44_TEK603.pm 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. # $Id: 44_TEK603.pm 17651 2018-10-31 12:05:23Z eisler $
  2. ####################################################################################################
  3. #
  4. # 44_TEK603.pm
  5. #
  6. # Copyright: Stephan Eisler
  7. # Email: fhem.dev@hausautomatisierung.co
  8. #
  9. # This file is part of fhem.
  10. #
  11. # Fhem is free software: you can redistribute it and/or modify
  12. # it under the terms of the GNU General Public License as published by
  13. # the Free Software Foundation, either version 2 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # Fhem is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU General Public License
  22. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  23. #
  24. ####################################################################################################
  25. package main;
  26. use strict;
  27. use warnings;
  28. use Digest::CRC; # libdigest-crc-perl
  29. sub TEK603_Initialize($);
  30. sub TEK603_define($$);
  31. sub TEK603_doInit($);
  32. sub TEK603_undef($$);
  33. sub TEK603_ready($);
  34. sub TEK603_read($);
  35. sub TEK603_reconnect($);
  36. sub TEK603_Initialize($) {
  37. my ($hash) = @_;
  38. require $attr{global}{modpath} . '/FHEM/DevIo.pm';
  39. $hash->{ReadFn} = 'TEK603_read';
  40. $hash->{ReadyFn} = 'TEK603_ready';
  41. $hash->{DefFn} = 'TEK603_define';
  42. $hash->{UndefFn} = 'TEK603_undef';
  43. $hash->{AttrList} = 'do_not_notify:0,1 dummy:1,0 loglevel:0,1,2,3,4,5,6 ' .
  44. $readingFnAttributes;
  45. }
  46. sub TEK603_define($$) {
  47. my ($hash, $def) = @_;
  48. my @a = split('[ \t][ \t]*', $def);
  49. my $name = $a[0];
  50. my $dev = $a[2];
  51. $hash->{DEF} = $dev;
  52. my $msg = '';
  53. if( @a != 3) {
  54. $msg = 'wrong syntax: define <name> TEK603 {none | devicename}';
  55. Log3 $name, 3, $msg;
  56. return $msg;
  57. }
  58. DevIo_CloseDev($hash);
  59. $hash->{PORTSTATE} = $hash->{STATE};
  60. if($dev eq 'none') {
  61. Log3 $name, 3, "device is none, commands will be echoed only";
  62. $attr{$name}{dummy} = 1;
  63. return undef;
  64. }
  65. $hash->{DeviceName} = $dev;
  66. my $ret = DevIo_OpenDev($hash, 0, 'TEK603_doInit');
  67. return $ret;
  68. }
  69. sub TEK603_doInit($) {
  70. my ($hash) = @_;
  71. my $po = $hash->{USBDev};
  72. my $dev = $hash->{DeviceName};
  73. my $name = $hash->{NAME};
  74. # Parameter 115200, 8, 1, even, none
  75. $po->reset_error();
  76. $po->baudrate(115200);
  77. $po->databits(8);
  78. $po->stopbits(1);
  79. $po->parity('none');
  80. $po->handshake('none');
  81. $po->dtr_active(1);
  82. $po->rts_active(1);
  83. if (!$po->write_settings) {
  84. undef $po;
  85. $hash->{STATE} = $name . 'Error on write serial line settings on device ' . $dev;
  86. Log3 $name, 3, $hash->{STATE};
  87. return $hash->{STATE} . "\n";
  88. }
  89. Log3 $name, 3, "connected to device $dev";
  90. $hash->{STATE} = 'open';
  91. $hash->{PORTSTATE} = $hash->{STATE};
  92. return undef;
  93. }
  94. sub TEK603_undef($$) {
  95. my ($hash, $name) = @_;
  96. foreach my $d (sort keys %defs) {
  97. if(defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash) {
  98. Log3 $name, 4, "deleting port for $d";
  99. delete $defs{$d}{IODev};
  100. }
  101. }
  102. DevIo_CloseDev($hash);
  103. $hash->{PORTSTATE} = $hash->{STATE};
  104. return undef;
  105. }
  106. sub TEK603_ready($) {
  107. my ($hash) = @_;
  108. return DevIo_OpenDev($hash, 1, 'TEK603_doInit') if($hash->{STATE} eq 'disconnected');
  109. # This is relevant for windows/USB only
  110. my $po = $hash->{USBDev};
  111. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  112. return ($InBytes > 0);
  113. }
  114. sub TEK603_read($) {
  115. my ($hash) = @_;
  116. my $name = $hash->{NAME};
  117. my $buf = DevIo_SimpleRead($hash);
  118. return '' if(!defined($buf));
  119. # convert to hex string
  120. $hash->{buffer} = unpack ('H*', $buf);
  121. my $lenght = hex(substr($hash->{buffer},4,4))*2;
  122. #my $cmd = substr($hash->{buffer},8,2);
  123. #my $flags = substr($hash->{buffer},10,2);
  124. my $time = sprintf '%02d:%02d:%02d', hex(substr($hash->{buffer},12,2)), hex(substr($hash->{buffer},14,2)), hex(substr($hash->{buffer},16,2));
  125. #my $epromStart = hex(substr($hash->{buffer},18,4));
  126. #my $epromEnd = hex(substr($hash->{buffer},22,4));
  127. my $payloadlenght = $lenght - 26 - 4;
  128. my $payload = substr($hash->{buffer},26,$payloadlenght);
  129. my $crc = substr($hash->{buffer},26 + $payloadlenght,4);
  130. my $ctx = Digest::CRC->new(width=>16, init=>0x0, poly=>0x1021, refout=>0, xorout=>0);
  131. $ctx->add(pack 'H*',(substr($hash->{buffer},0,26 + $payloadlenght)));
  132. my $digest = $ctx->hexdigest;
  133. return '' if($crc ne $digest);
  134. # payload
  135. my $temp = sprintf '%.2f', ((hex(substr($payload, 0,2)) - 40 - 32) / 1.8);
  136. my $Ullage = hex(substr($payload,2,2)) * 256 + hex(substr($payload,4,2));
  137. my $RemainingUsableLevel = hex(substr($payload,6,2)) * 256 + hex(substr($payload,8,2));
  138. my $TotalUsableCapacity = hex(substr($payload,10,2)) * 256 + hex(substr($payload,12,2));
  139. return '' if($temp eq "-40.00" && $Ullage eq "0"); # TankLevel=NO_DATA
  140. # Calculations
  141. my $RemainingUsablePercent = round($RemainingUsableLevel / $TotalUsableCapacity * 100,01);
  142. #Log3 $name, 5, $hash->{buffer};
  143. Log3 $name, 5, "Time:$time Temp:$temp Ullage:$Ullage RemainingUsableLevel:$RemainingUsableLevel RemainingUsablePercent:$RemainingUsablePercent TotalUsableCapacity:$TotalUsableCapacity";
  144. readingsBeginUpdate($hash);
  145. readingsBulkUpdate($hash, "Time", $time);
  146. readingsBulkUpdate($hash, "Temperature", $temp);
  147. readingsBulkUpdate($hash, "Ullage", $Ullage);
  148. readingsBulkUpdate($hash, "RemainingUsableLevel", $RemainingUsableLevel);
  149. readingsBulkUpdate($hash, "RemainingUsablePercent", $RemainingUsablePercent);
  150. readingsBulkUpdate($hash, "TotalUsableCapacity", $TotalUsableCapacity);
  151. readingsEndUpdate($hash, 1);
  152. }
  153. sub TEK603_reconnect($) {
  154. my ($hash) = @_;
  155. my $name = $hash->{NAME};
  156. Log3 $name, 3, "Wrong Data received. We reconnect Device";
  157. # Sometime the device sends a lot of waste and we must reconnect.
  158. DevIo_CloseDev($hash);
  159. $hash->{PORTSTATE} = $hash->{STATE};
  160. select(undef, undef, undef, 0.1);
  161. DevIo_OpenDev($hash, 0, 'TEK603_doInit');
  162. }
  163. 1;
  164. =pod
  165. =item summary devices communicating with TEK603
  166. =begin html
  167. <a name="TEK603"></a>
  168. <h3>TEK603</h3>
  169. <ul>
  170. The TEK603 is a fhem module for the Tekelek TEK603 Eco Monitor a liquid level monitor designed for residential and small commercial applications.
  171. It works in conjunction with a TEK653 Sonic transmitter mounted on the top of the tank.
  172. <br /><br /><br />
  173. <b>Prerequisites</b><br>
  174. The module requires the perl module Digest::CRC<br />
  175. On a debian based system the module can be installed with<br />
  176. <code>
  177. sudo apt-get install libdigest-crc-perl<br />
  178. </code>
  179. <br /><br />
  180. <a name="TEK603_Define"></a>
  181. <b>Define</b>
  182. <ul>
  183. <code>define &lt;name&gt; TEK603 /dev/ttyUSBx</code><br />
  184. <br />
  185. Defines an TEK603 Eco Monitor device connected to USB.<br /><br />
  186. Examples:
  187. <ul>
  188. <code>define OelTank TEK603 /dev/ttyUSB0</code><br />
  189. </ul>
  190. </ul><br />
  191. <a name="TEK603_Readings"></a>
  192. <b>Readings</b>
  193. <ul>
  194. <li>Time<br />
  195. TEK603 internal Time</li>
  196. <li>Temperature<br />
  197. Sensor Temperature</li>
  198. <li>Ullage<br />
  199. Sensor Measured Ullage</li>
  200. <li>RemainingUsableLevel<br />
  201. This is the usable level, with deductions due to the sensor offset and outlet height. (Liters)</li>
  202. <li>RemainingUsablePercent<br />
  203. This is the usable level in percent (calculated from RemainingUsableLevel and TotalUsableCapacity)</li>
  204. <li>TotalUsableCapacity<br />
  205. This is the usable volume, with deductions due to the sensor offset and outlet height. (Liters)</li>
  206. </ul><br />
  207. </ul><br />
  208. =end html
  209. =cut