18_JME.pm 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. ################################################################################
  2. # FHEM-Modul see www.fhem.de
  3. # 18_JME.pm
  4. # JeeMeterNode
  5. #
  6. # Usage: define <Name> JME <Node-Nr>
  7. ################################################################################
  8. # This program is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. ################################################################################
  21. # Autor: Axel Rieger
  22. # Version: 1.0
  23. # Datum: 07.2011
  24. # Kontakt: fhem [bei] anax [punkt] info
  25. ################################################################################
  26. # READINGs
  27. # MeterBase = MeterBase abgelesener Zaehlerstand...default 0
  28. # MeterNow = aktueller Zaehlerstand...wird hochgezŠhlt
  29. # Wenn MeterBase gesetzt ist, wird von dan an hochgezaehlt
  30. # Wenn MeterBase gesetzt wird, werden
  31. # AVG_Hour, AVG_Day, AVG_Month
  32. ################################################################################
  33. package main;
  34. use strict;
  35. use warnings;
  36. use POSIX;
  37. use Data::Dumper;
  38. use vars qw(%defs);
  39. use vars qw(%attr);
  40. use vars qw(%data);
  41. use vars qw(%modules);
  42. ################################################################################
  43. sub JME_Initialize($)
  44. {
  45. my ($hash) = @_;
  46. # Match/Prefix
  47. my $match = "JME";
  48. $hash->{Match} = "^JME";
  49. $hash->{DefFn} = "JME_Define";
  50. $hash->{UndefFn} = "JME_Undef";
  51. $hash->{SetFn} = "JME_Set";
  52. $hash->{ParseFn} = "JME_Parse";
  53. $hash->{AttrList} = "do_not_notify:0,1 loglevel:0,5 disable:0,1 TicksPerUnit avg_data_day avg_data_month";
  54. #-----------------------------------------------------------------------------
  55. # Arduino/JeeNodes-Variables:
  56. # http://arduino.cc/en/Reference/HomePage
  57. # Integer = 2 Bytes -> form -32,768 to 32,767
  58. # Long (unsigned) = 4 Bytes -> from 0 to 4,294,967,295
  59. # Long (signed) = 4 Bytes -> from -2,147,483,648 to 2,147,483,647
  60. #
  61. # JeeConf
  62. # $data{JEECONF}{<SensorType>}{ReadingName}
  63. # $data{JEECONF}{<SensorType>}{DataBytes}
  64. # $data{JEECONF}{<SensorType>}{Prefix}
  65. # $data{JEECONF}{<SensorType>}{CorrFactor}
  66. # $data{JEECONF}{<SensorType>}{Function}
  67. # <SensorType>: 0-9 -> Reserved/not Used
  68. # <SensorType>: 10-99 -> Default
  69. # <SensorType>: 100-199 -> Userdifined
  70. # <SensorType>: 200-255 -> Internal/Test
  71. # Counter --------------------------------------------------------------------
  72. $data{JEECONF}{14}{ReadingName} = "counter";
  73. $data{JEECONF}{14}{DataBytes} = 2;
  74. $data{JEECONF}{14}{Prefix} = $match;
  75. }
  76. ################################################################################
  77. sub JME_Define($){
  78. # define J001 JME <Node-Nr>
  79. # hash = New Device
  80. # defs = $a[0] <DEVICE-NAME> $a[1] DEVICE-TYPE $a[2]<Parameter-1-> $a[3]<Parameter-2->
  81. my ($hash, $def) = @_;
  82. my @a = split(/\s+/, $def);
  83. return "JME: Unknown argument count " . int(@a) . " , usage define <NAME>
  84. NodeID [<Path_to_User_Conf_File>]" if(int(@a) != 3);
  85. my $NodeID = $a[2];
  86. if(defined($modules{JME}{defptr}{$NodeID})) {
  87. return "Node $NodeID allready define";
  88. }
  89. $hash->{CODE} = $NodeID;
  90. $hash->{STATE} = "NEW: " . TimeNow();
  91. $hash->{OrderID} = ord($NodeID);
  92. $modules{JME}{defptr}{ord($NodeID)} = $hash;
  93. # Init
  94. #$hash->{READINGS}{MeterBase}{TIME} = TimeNow();
  95. #$hash->{READINGS}{MeterBase}{VAL} = 0;
  96. #$hash->{READINGS}{MeterNow}{TIME} = TimeNow();
  97. #$hash->{READINGS}{MeterNow}{VAL} = 0;
  98. #$hash->{READINGS}{consumption}{TIME} = TimeNow();
  99. #$hash->{READINGS}{consumption}{VAL} = 0;
  100. #$hash->{READINGS}{current}{TIME} = TimeNow();
  101. #$hash->{READINGS}{current}{VAL} = 0;
  102. #$hash->{cnt_old} = 0;
  103. return undef;
  104. }
  105. ################################################################################
  106. sub JME_Undef($$){
  107. my ($hash, $name) = @_;
  108. Log 4, "JME Undef: " . Dumper(@_);
  109. my $NodeID = $hash->{NodeID};
  110. if(defined($modules{JME}{defptr}{$NodeID})) {
  111. delete $modules{JME}{defptr}{$NodeID}
  112. }
  113. return undef;
  114. }
  115. ################################################################################
  116. sub JME_Set($)
  117. {
  118. my ($hash, @a) = @_;
  119. my $fields = "MeterBase MeterNow counter avg_day avg_month";
  120. return "Unknown argument $a[1], choose one of $fields" if($a[1] eq "?");
  121. if($fields =~ m/$a[1]/){
  122. $hash->{READINGS}{$a[1]}{VAL} = sprintf("%0.3f",$a[2]);
  123. $hash->{READINGS}{$a[1]}{TIME} = TimeNow();
  124. }
  125. return "";
  126. }
  127. ################################################################################
  128. sub JME_Parse($$) {
  129. my ($iodev, $rawmsg) = @_;
  130. # $rawmsg = JeeNodeID + SensorType + SensorData
  131. # rawmsg = JME 03 252 03 65
  132. Log 4, "JME PARSE RAW-MSG: " . $rawmsg . " IODEV:" . $iodev->{NAME};
  133. #
  134. my @data = split(/\s+/,$rawmsg);
  135. my $NodeID = $data[1];
  136. # my $NodeID = sprintf("%02x" ,($data[1]));
  137. # $NodeID = hex($NodeID);
  138. # my $NodeID = chr(ord($data[1]));
  139. my $SType = $data[2];
  140. my $data_bytes = $data{JEECONF}{$SType}{DataBytes};
  141. my $data_end = int(@data) - 1;
  142. # $array[$#array];
  143. Log 4, "JME PARSE N:$NodeID S:$SType B:$data_bytes CNT:" . @data . " END:" . $data_end;
  144. my @SData = @data[3..$data_end];
  145. my ($hash,$name);
  146. if(defined($modules{JME}{defptr}{ord($NodeID)})) {
  147. $hash = $modules{JME}{defptr}{ord($NodeID)};
  148. $name = $hash->{NAME};
  149. }
  150. else {
  151. return "UNDEFINED JME_$NodeID JME $NodeID";};
  152. my %readings;
  153. # LogLevel
  154. my $ll = 5;
  155. if(defined($attr{$name}{loglevel})) {
  156. $ll = $attr{$name}{loglevel};
  157. }
  158. # Sensor-Data Bytes to Values
  159. # lowBit HighBit reverse ....
  160. @SData = reverse(@SData);
  161. my $raw_value = join("",@SData);
  162. my $value = "";
  163. map {$value .= sprintf "%02x",$_} @SData;
  164. $value = hex($value);
  165. Log $ll, "$name/JME-PARSE: $NodeID - $SType - " . join(" " , @SData) . " -> " . $value;
  166. my $TicksPerUnit = 0.1;
  167. if(defined($attr{$name}{TicksPerUnit})){
  168. $TicksPerUnit = $attr{$name}{TicksPerUnit};
  169. }
  170. my $counter = 0;
  171. if(defined($defs{$name}{READINGS}{counter})){
  172. $counter = $defs{$name}{READINGS}{counter}{VAL};
  173. }
  174. # Counter Reset at 100 to 0
  175. if($counter > 100) {
  176. $readings{counter} = 0;
  177. }
  178. else {$readings{counter} = $value;}
  179. my ($current,$cnt_delta);
  180. $cnt_delta = $value - $counter;
  181. $current = sprintf("%0.3f", ($cnt_delta * $TicksPerUnit));
  182. $readings{current} = $current;
  183. # Update only on Changes
  184. my ($MeterNow,$consumption,$MeterBase);
  185. $MeterNow = $defs{$name}{READINGS}{MeterNow}{VAL};
  186. if($current > 0 ){
  187. $MeterBase = $defs{$name}{READINGS}{MeterBase}{VAL};
  188. $readings{MeterNow} = sprintf("%0.3f", ($MeterNow + $current));
  189. $consumption = ($MeterNow + $current) - $MeterBase;
  190. $readings{consumption} = sprintf("%0.3f", $consumption);
  191. }
  192. #-----------------------------------------------------------------------------
  193. # Caculate AVG Day and Month
  194. #-----------------------------------------------------------------------------
  195. my $tsecs= time();
  196. my $d_now = (localtime($tsecs))[3];
  197. my $m_now = (localtime($tsecs))[4] + 1;
  198. # avg_data_day = Day | Day_MeterNow
  199. # avg_data_month = Month | Month_MeterNow
  200. my ($d, $d_mn,$m,$m_mn);
  201. if(defined($attr{$name}{avg_data_day})){
  202. ($d, $d_mn) = split(/\|/,$attr{$name}{avg_data_day});
  203. ($m,$m_mn) = split(/\|/,$attr{$name}{avg_data_month});
  204. }
  205. else {
  206. # INIT
  207. $defs{$name}{READINGS}{avg_day}{VAL} = 0.000;
  208. $defs{$name}{READINGS}{avg_day}{TIME} = TimeNow();
  209. $defs{$name}{READINGS}{avg_month}{VAL} = 0.000;
  210. $defs{$name}{READINGS}{avg_month}{TIME} = TimeNow();
  211. $attr{$name}{avg_data_day} = "$d_now|$MeterNow";
  212. $attr{$name}{avg_data_month} = "$m_now|$MeterNow";
  213. ($d, $d_mn) = split(/\|/,$attr{$name}{avg_data_day});
  214. ($m,$m_mn) = split(/\|/,$attr{$name}{avg_data_month});
  215. }
  216. Log $ll, "$name/JME-PARSE: D:NOW:$d_now/OLD:$d M:NOW:$m_now/OLD:$m";
  217. # AVG DAY
  218. if($d_now ne $d) {
  219. $consumption = ($MeterNow - $d_mn) + $defs{$name}{READINGS}{avg_day}{VAL} ;
  220. $consumption = $consumption / 2;
  221. $readings{avg_day} = sprintf("%0.3f", $consumption);
  222. $attr{$name}{avg_data_day} = "$d_now|$MeterNow";
  223. }
  224. # AVG Month
  225. if($m_now ne $m) {
  226. $consumption = ($MeterNow - $d_mn) + $defs{$name}{READINGS}{avg_month}{VAL} ;
  227. $consumption = $consumption / 2;
  228. $readings{avg_month} = sprintf("%0.3f", $consumption);
  229. $attr{$name}{avg_data_month} = "$m_now|$MeterNow";
  230. }
  231. #-----------------------------------------------------------------------------
  232. # Readings
  233. my $i = 0;
  234. foreach my $r (sort keys %readings) {
  235. Log 4, "JME $name $r:" . $readings{$r};
  236. $defs{$name}{READINGS}{$r}{VAL} = $readings{$r};
  237. $defs{$name}{READINGS}{$r}{TIME} = TimeNow();
  238. # Changed for Notify and Logs
  239. $defs{$name}{CHANGED}[$i] = $r . ": " . $readings{$r};
  240. $i++;
  241. }
  242. $defs{$name}{STATE} = "M:" . $defs{$name}{READINGS}{MeterNow}{VAL} . " C:$value";
  243. return $name;
  244. }
  245. ################################################################################
  246. 1;