# $Id$
####################################################################################################
#
# 44_TEK603.pm
#
# Copyright: Stephan Eisler
# Email: stephan@eisler.de
#
# This file is part of fhem.
#
# Fhem is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Fhem is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with fhem. If not, see .
#
####################################################################################################
package main;
use strict;
use warnings;
use DevIo;
use Digest::CRC; # libdigest-crc-perl
sub TEK603_Initialize($) {
my ($hash) = @_;
$hash->{ReadFn} = 'TEK603_read';
$hash->{ReadyFn} = 'TEK603_ready';
$hash->{DefFn} = 'TEK603_define';
$hash->{UndefFn} = 'TEK603_undef';
$hash->{AttrList} = 'do_not_notify:0,1 dummy:1,0 disable:1,0 loglevel:0,1,2,3,4,5,6 ' .
$readingFnAttributes;
}
sub TEK603_define($$) {
my ($hash, $def) = @_;
my @a = split('[ \t][ \t]*', $def);
my $name = $a[0];
my $dev = $a[2];
$hash->{DEF} = $dev;
my $msg = '';
if( @a != 3) {
$msg = 'wrong syntax: define TEK603 {none | devicename | hostname:port}';
Log3 $name, 3, $msg;
return $msg;
}
DevIo_CloseDev($hash);
$hash->{PORTSTATE} = $hash->{STATE};
if($dev eq 'none') {
Log3 $name, 3, "device is none, commands will be echoed only";
$attr{$name}{dummy} = 1;
return undef;
}
$hash->{DeviceName} = $dev;
my $ret = DevIo_OpenDev($hash, 0, 'TEK603_doInit');
return $ret;
}
sub TEK603_doInit($) {
my ($hash) = @_;
my $po = $hash->{USBDev};
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
return if (IsDisabled($name));
# Wenn / enthalten ist ist es kein ser2net-Device, daher initialisieren
if ($dev =~ m/\//)
{
# Parameter 115200, 8, 1, even, none
$po->reset_error();
$po->baudrate(115200);
$po->databits(8);
$po->stopbits(1);
$po->parity('none');
$po->handshake('none');
$po->dtr_active(1);
$po->rts_active(1);
if (!$po->write_settings) {
undef $po;
$hash->{STATE} = $name . 'Error on write serial line settings on device ' . $dev;
Log3 $name, 3, $hash->{STATE};
return $hash->{STATE} . "\n";
}
}
Log3 $name, 3, "connected to device $dev";
$hash->{STATE} = 'open';
$hash->{PORTSTATE} = $hash->{STATE};
return undef;
}
sub TEK603_undef($$) {
my ($hash, $name) = @_;
foreach my $d (sort keys %defs) {
if(defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash) {
Log3 $name, 4, "deleting port for $d";
delete $defs{$d}{IODev};
}
}
DevIo_CloseDev($hash);
$hash->{PORTSTATE} = $hash->{STATE};
return undef;
}
sub TEK603_ready() {
my ($hash) = @_;
my $name = $hash->{NAME};
return if (IsDisabled($name));
return DevIo_OpenDev($hash, 1, 'TEK603_doInit') if($hash->{STATE} eq 'disconnected');
# This is relevant for windows/USB only
my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
my $po = $hash->{USBDev};
if ($po) {
($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
return ($InBytes > 0);
}
# Someone set us up the bomb
Log3($hash->{NAME}, 1, qq[Can't read from $hash->{DeviceName}]);
# disable device
Log3($hash->{NAME}, 1, qq[Disabled device due read errors]);
CommandAttr(undef, $hash->{NAME} . ' disable 1');
return;
}
sub TEK603_read($) {
my ($hash) = @_;
my $name = $hash->{NAME};
return if (IsDisabled($name));
my $buf = DevIo_SimpleRead($hash);
return '' if(!defined($buf));
# convert to hex string
$hash->{buffer} = unpack ('H*', $buf);
my $lenght = hex(substr($hash->{buffer},4,4))*2;
#my $cmd = substr($hash->{buffer},8,2);
#my $flags = substr($hash->{buffer},10,2);
my $time = sprintf '%02d:%02d:%02d', hex(substr($hash->{buffer},12,2)), hex(substr($hash->{buffer},14,2)), hex(substr($hash->{buffer},16,2));
#my $epromStart = hex(substr($hash->{buffer},18,4));
#my $epromEnd = hex(substr($hash->{buffer},22,4));
my $payloadlenght = $lenght - 26 - 4;
my $payload = substr($hash->{buffer},26,$payloadlenght);
my $crc = substr($hash->{buffer},26 + $payloadlenght,4);
my $ctx = Digest::CRC->new(width=>16, init=>0x0, poly=>0x1021, refout=>0, xorout=>0);
$ctx->add(pack 'H*',(substr($hash->{buffer},0,26 + $payloadlenght)));
my $digest = $ctx->hexdigest;
return '' if($crc ne $digest);
# payload
my $temp = sprintf '%.2f', ((hex(substr($payload, 0,2)) - 40 - 32) / 1.8);
my $Ullage = hex(substr($payload,2,2)) * 256 + hex(substr($payload,4,2));
my $RemainingUsableLevel = hex(substr($payload,6,2)) * 256 + hex(substr($payload,8,2));
my $TotalUsableCapacity = hex(substr($payload,10,2)) * 256 + hex(substr($payload,12,2));
return '' if($temp eq "-40.00" && $Ullage eq "0"); # TankLevel=NO_DATA
# Calculations
my $RemainingUsablePercent = round($RemainingUsableLevel / $TotalUsableCapacity * 100,01);
#Log3 $name, 5, $hash->{buffer};
Log3 $name, 5, "Time:$time Temp:$temp Ullage:$Ullage RemainingUsableLevel:$RemainingUsableLevel RemainingUsablePercent:$RemainingUsablePercent TotalUsableCapacity:$TotalUsableCapacity";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "Time", $time);
readingsBulkUpdate($hash, "Temperature", $temp);
readingsBulkUpdate($hash, "Ullage", $Ullage);
readingsBulkUpdate($hash, "RemainingUsableLevel", $RemainingUsableLevel);
readingsBulkUpdate($hash, "RemainingUsablePercent", $RemainingUsablePercent);
readingsBulkUpdate($hash, "TotalUsableCapacity", $TotalUsableCapacity);
readingsEndUpdate($hash, 1);
}
sub TEK603_reconnect($) {
my ($hash) = @_;
my $name = $hash->{NAME};
Log3 $name, 3, "Wrong Data received. We reconnect Device";
# Sometime the device sends a lot of waste and we must reconnect.
DevIo_CloseDev($hash);
$hash->{PORTSTATE} = $hash->{STATE};
select(undef, undef, undef, 0.1);
DevIo_OpenDev($hash, 0, 'TEK603_doInit');
}
1;
=pod
=item summary devices communicating with TEK603
=begin html
TEK603
The TEK603 is a fhem module for the Tekelek TEK603 Eco Monitor a liquid level monitor designed for residential and small commercial applications.
It works in conjunction with a TEK653 Sonic transmitter mounted on the top of the tank.
Prerequisites
The module requires the perl module Digest::CRC
On a debian based system the module can be installed with
sudo apt-get install libdigest-crc-perl
Define
define <name> TEK603 /dev/ttyUSBx
Defines an TEK603 Eco Monitor device connected to USB.
Examples:
define OelTank TEK603 /dev/ttyUSB0
define <name> TEK603 hostnameorip:port
Defines an TEK603 Eco Monitor device via ethernet on a remote host running ser2net.
Examples:
define OelTank TEK603 somehost:23000
Readings
- Time
TEK603 internal Time
- Temperature
Sensor Temperature
- Ullage
Sensor Measured Ullage
- RemainingUsableLevel
This is the usable level, with deductions due to the sensor offset and outlet height. (Liters)
- RemainingUsablePercent
This is the usable level in percent (calculated from RemainingUsableLevel and TotalUsableCapacity)
- TotalUsableCapacity
This is the usable volume, with deductions due to the sensor offset and outlet height. (Liters)
=end html
=cut