19_Revolt.pm 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #
  2. # Written by Martin Paulat, 2013
  3. # mainted by Gernot Hillier (yoda_gh), 2018-
  4. # <gernot@hillier.de>
  5. #
  6. # $Id: 19_Revolt.pm 17447 2018-10-01 19:13:48Z yoda_gh $
  7. package main;
  8. use strict;
  9. use warnings;
  10. use Date::Parse;
  11. #####################################
  12. sub Revolt_Initialize($)
  13. {
  14. my ($hash) = @_;
  15. $hash->{Match} = "^r......................\$";
  16. $hash->{DefFn} = "Revolt_Define";
  17. $hash->{UndefFn} = "Revolt_Undef";
  18. $hash->{ParseFn} = "Revolt_Parse";
  19. $hash->{AttrList} = "IODev ".
  20. "EnergyAdjustValue ".
  21. $readingFnAttributes;
  22. }
  23. #####################################
  24. sub Revolt_Define($$)
  25. {
  26. my ($hash, $def) = @_;
  27. my @a = split("[ \t][ \t]*", $def);
  28. return "wrong syntax: define <name> Revolt <id>" if(int(@a) != 3);
  29. $a[2] = lc($a[2]);
  30. return "Define $a[0]: wrong <id> format: specify a 4 digit hex value" if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/);
  31. $hash->{ID} = $a[2];
  32. #$hash->{STATE} = "Initialized";
  33. $modules{REVOLT}{defptr}{$a[2]} = $hash;
  34. AssignIoPort($hash);
  35. my $name = $a[0];
  36. #$attr{$name}{"event-aggregator"} = "power::none:median:120,energy::none:median:120" if(!defined($attr{$name}{"event-aggregator"}));
  37. $attr{$name}{"stateFormat"} = "P: power E: energy V: voltage C: current Pf: pf" if(!defined($attr{$name}{"stateFormat"}));
  38. return undef;
  39. }
  40. #####################################
  41. sub Revolt_Undef($$)
  42. {
  43. my ($hash, $name) = @_;
  44. delete($modules{REVOLT}{defptr}{$hash->{ID}}) if(defined($hash->{ID}) &&
  45. defined($modules{REVOLT}{defptr}{$hash->{ID}}));
  46. return undef;
  47. }
  48. #####################################
  49. sub Revolt_Parse($$)
  50. {
  51. my ($hash, $msg) = @_;
  52. $msg = lc($msg);
  53. my $seq = substr($msg, 1, 2);
  54. my $dev = substr($msg, 3, 4);
  55. my $cde = substr($msg, 7, 4);
  56. my $val = substr($msg, 11, 22);
  57. my $id = substr($msg, 1, 4);
  58. my $voltage = hex(substr($msg, 5, 2));
  59. my $current = hex(substr($msg, 7, 4)) * 0.01;
  60. my $freq = hex(substr($msg, 11, 2));
  61. my $power = hex(substr($msg, 13, 4)) * 0.1;
  62. my $pf = hex(substr($msg, 17, 2)) * 0.01;
  63. my $energy = hex(substr($msg, 19, 4)) * 0.01;
  64. my $lastval = 0.0;
  65. my $type = "";
  66. my $energyAdj = $energy;
  67. if(!defined($modules{REVOLT}{defptr}{$id})) {
  68. Log3 undef,3, "Unknown Revolt device $id, please define it";
  69. $type = "Revolt" if(!$type);
  70. return "UNDEFINED ${type}_$id Revolt $id";
  71. }
  72. my $def = $modules{REVOLT}{defptr}{$id};
  73. my $name = $def->{NAME};
  74. return "" if(IsIgnored($name));
  75. # check if data is invalid
  76. if (defined($def->{READINGS}{".lastenergy"})) {
  77. $lastval = $def->{READINGS}{".lastenergy"}{VAL};
  78. }
  79. else {
  80. readingsSingleUpdate($def,".lastenergy", $energy, 1);
  81. }
  82. # adjust energy value
  83. $energy -= AttrVal($name, "EnergyAdjustValue", 0);
  84. my $isInvalid = 0;
  85. #my $energydiff = 0;
  86. #my $maxenergy = 0;
  87. #TODO: This plausability check will stop accepting values forever after
  88. #receiving a very small outlier once. We could use abs(...), but what about
  89. #changing energyadjustvalue etc.? Do we really need this at all?
  90. #
  91. #if (defined($def->{READINGS}{"energy"})) {
  92. # my $timediff = gettimeofday() - str2time($def->{READINGS}{"energy"}{TIME});
  93. # $energydiff = $energy - $def->{READINGS}{"energy"}{VAL};
  94. # $maxenergy = 3.65 * ($timediff / 3600.0);
  95. #}
  96. #if ($energydiff > $maxenergy) {
  97. # $isInvalid = 1;
  98. #}
  99. if (0 == $pf) {
  100. $pf = 0.0001;
  101. }
  102. # plausability check partly taken from http://www.sknorrell.de/blog/energiemesssung-mit-revolt-nc-5462/
  103. if (($voltage < 80) || ($freq > 65) || ($power > 3650) || ($current > 16) ||
  104. ((($power / $voltage / $pf) > 0.00999) && (0 == $current))) {
  105. $isInvalid = 1;
  106. }
  107. if (0 == $isInvalid) {
  108. #my $state = "P: ".sprintf("%5.1f", $power)." E: ".sprintf("%6.2f", $energy)." V: ".sprintf("%3d", $voltage)." C: ".sprintf("%6.2f", $current)." F: $freq Pf: ".sprintf("%4.2f", $pf);
  109. readingsBeginUpdate($def);
  110. my $timediff = gettimeofday() - str2time($def->{READINGS}{".lastenergy"}{TIME});
  111. if (($lastval != $energy) && (($energy - $lastval) < (3.65 * ($timediff / 3600.0)))) {
  112. readingsBulkUpdate($def, ".lastenergy", $energy, 1);
  113. }
  114. readingsBulkUpdate($def, "state", "active", 0);
  115. readingsBulkUpdate($def, "voltage", $voltage, 1);
  116. readingsBulkUpdate($def, "current", $current, 1);
  117. readingsBulkUpdate($def, "frequency", $freq, 1);
  118. readingsBulkUpdate($def, "power", $power, 1);
  119. readingsBulkUpdate($def, "pf", $pf, 1);
  120. readingsBulkUpdate($def, "energy", $energy, 1);
  121. readingsEndUpdate($def, 1);
  122. }
  123. return $name;
  124. }
  125. 1;
  126. =pod
  127. =item device
  128. =item summary Receive power, energy, voltage etc. from Revolt NC-5462 433MHz devices
  129. =item summary_DE Energieverbrauch von Revolt NC-5462 über 433MHz empfangen
  130. =begin html
  131. <a name="Revolt"></a>
  132. <h3>Revolt NC-5462</h3>
  133. <ul>
  134. Provides energy consumption readings for Revolt NC-5462 devices via CUL or
  135. SIGNALduino (433MHz).
  136. <br><br>
  137. These devices send telegrams with measured values every few seconds. Many
  138. users saw problems with cheap 433MHz receivers - and even with good
  139. receivers, wrong values are received quite often, leading to outliers
  140. in plots and sporadic autocreation of devices with wrong IDs.
  141. <br><br>
  142. This module now ignores implausible telegrams (e.g. voltage below 80),
  143. but to further reduce outliers and event frequency, you should use the common
  144. attributes described below. You also might want to disable autocreation of
  145. Revolt devices once all of yours are detected.
  146. <br><br>
  147. <a name="RevoltDefine"></a>
  148. <b>Define</b>
  149. <ul>
  150. <code>define &lt;name&gt; Revolt &lt;id&gt;</code>
  151. <br><br>
  152. &lt;id&gt; is a 4 digit hex number to identify the NC-5462 device.<br>
  153. Note: devices are autocreated on reception of the first message.<br>
  154. </ul>
  155. <br>
  156. <a name="RevoltAttributes"></a>
  157. <b>Attributes</b>
  158. <ul>
  159. <li>EnergyAdjustValue: adjust the energy reading (energy = energy - EnergyAdjustValue)</li>
  160. </ul>
  161. <br>
  162. <b>Common attributes</b>
  163. <ul>
  164. <li><a href="#event-aggregator">event-aggregator</a>: To reduce the high sending frequency and filter reception errors, you can use FHEM's event-aggregator. Common examples:
  165. <ul>
  166. <li><tt>power:60:linear:mean,energy:36000:none:v</tt> reduce sampling frequency to one power event per minute and one energy value per 10 hours, other events can be disabled via <tt>event-on-change-reading</tt> or <tt>event-on-update-reading</tt>.</li>
  167. <li><tt>power::none:median:120,energy::none:median:120</tt> keep sampling frequency untouched, but filter outliers using statistical median</li>
  168. </ul>
  169. </li>
  170. <li><a href="#event-min-interval">event-min-interval</a></li>
  171. <li><a href="#event-on-change-reading">event-on-change-reading</a>: can be used to disable events for readings you don't need. See documentation for details.</li>
  172. <li><a href="#event-on-update-reading">event-on-update-reading</a>: can be used to disable events for readings you don't need. See documentatino for details.</li>
  173. </ul>
  174. <a name="RevoltReadings"></a>
  175. <b>Readings</b>
  176. <ul>
  177. <li>energy [kWh]: Total energy consumption summed up in the NC-5462
  178. devices. It seems you can't reset this counter and it will wrap around
  179. to 0 at 655.35 kWh. The EnergyAdjustValue attribute (see above) allows
  180. to adapt the reading, e.g. when you connect a new device.</li>
  181. <li>power [W]</li>
  182. <li>voltage [V]</li>
  183. <li>current [A]</li>
  184. <li>frequency [Hz]</li>
  185. <li>Pf: Power factor</li>
  186. </ul>
  187. </ul>
  188. =end html
  189. =cut