14_CUL_WS.pm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. ##############################################
  2. # $Id: 14_CUL_WS.pm 15603 2017-12-13 20:53:47Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. # Supports following devices:
  7. # KS300TH (this is redirected to the more sophisticated 14_KS300 by 00_CUL)
  8. # S300TH
  9. # WS2000/WS7000
  10. #
  11. #####################################
  12. sub
  13. CUL_WS_Initialize($)
  14. {
  15. my ($hash) = @_;
  16. # Message is like
  17. # K41350270
  18. $hash->{Match} = "^K.....";
  19. $hash->{DefFn} = "CUL_WS_Define";
  20. $hash->{UndefFn} = "CUL_WS_Undef";
  21. $hash->{AttrFn} = "CUL_WS_Attr";
  22. $hash->{ParseFn} = "CUL_WS_Parse";
  23. $hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1 ".
  24. "model:S300TH,KS300,ASH2200 ignore:0,1 ".
  25. $readingFnAttributes;
  26. $hash->{AutoCreate}=
  27. { "CUL_WS.*" => { GPLOT => "temp4hum6:Temp/Hum,", FILTER=>"%NAME:T:.*" } };
  28. }
  29. #####################################
  30. sub
  31. CUL_WS_Define($$)
  32. {
  33. my ($hash, $def) = @_;
  34. my @a = split("[ \t][ \t]*", $def);
  35. return "wrong syntax: define <name> CUL_WS <code> [corr1...corr4]"
  36. if(int(@a) < 3 || int(@a) > 7);
  37. return "Define $a[0]: wrong CODE format: valid is 1-8"
  38. if($a[2] !~ m/^[1-8]$/);
  39. $hash->{CODE} = $a[2];
  40. $hash->{corr1} = ((int(@a) > 3) ? $a[3] : 0);
  41. $hash->{corr2} = ((int(@a) > 4) ? $a[4] : 0);
  42. $hash->{corr3} = ((int(@a) > 5) ? $a[5] : 0);
  43. $hash->{corr4} = ((int(@a) > 6) ? $a[6] : 0);
  44. $modules{CUL_WS}{defptr}{$a[2]} = $hash;
  45. AssignIoPort($hash);
  46. return undef;
  47. }
  48. #####################################
  49. sub
  50. CUL_WS_Undef($$)
  51. {
  52. my ($hash, $name) = @_;
  53. delete($modules{CUL_WS}{defptr}{$hash->{CODE}}) if($hash && $hash->{CODE});
  54. return undef;
  55. }
  56. #####################################
  57. sub
  58. CUL_WS_Parse($$)
  59. {
  60. my ($hash,$msg) = @_;
  61. my %tlist = ("0"=>"temp",
  62. "1"=>"temp/hum",
  63. "2"=>"rain",
  64. "3"=>"wind",
  65. "4"=>"temp/hum/press",
  66. "5"=>"brightness",
  67. "6"=>"pyro",
  68. "7"=>"temp/hum");
  69. # -wusel, 2010-01-24: *sigh* No READINGS set, bad for other modules. Trying
  70. # to add setting READINGS as well as STATE ...
  71. my $NotifyType;
  72. my $NotifyHumidity;
  73. my $NotifyTemperature;
  74. my $NotifyRain;
  75. my $NotifyIsRaining;
  76. my $NotifyWind;
  77. my $NotifyWindDir;
  78. my $NotifyWindSwing;
  79. my $NotifyBrightness;
  80. my $NotifyPressure;
  81. my %NotifyMappings = (
  82. "T" => "temperature",
  83. "H" => "humidity",
  84. "R" => "rain",
  85. "IR" => "is_raining",
  86. "W" => "wind",
  87. "WD" => "wind_direction",
  88. "WS" => "wind_swing",
  89. "B" => "brightness",
  90. "P" => "pressure",
  91. );
  92. my @a = split("", $msg);
  93. my $firstbyte = hex($a[1]);
  94. my $cde = ($firstbyte&7) + 1;
  95. my $type = $tlist{$a[2]} ? $tlist{$a[2]} : "unknown";
  96. # There are only 8 S300 devices. In order to enable more, we try to look up
  97. # the name in connection with the receiver's name ("CUL868.1", "CUL433.1")
  98. # See attr <name> IODev XX
  99. my $def = $modules{CUL_WS}{defptr}{$hash->{NAME} . "." . $cde};
  100. $def = $modules{CUL_WS}{defptr}{$cde} if(!$def);
  101. if(!$def) {
  102. Log3 $hash, 1, "CUL_WS UNDEFINED $type sensor detected, code $cde";
  103. return "UNDEFINED CUL_WS_$cde CUL_WS $cde";
  104. }
  105. $hash = $def;
  106. my $name = $hash->{NAME};
  107. return "" if(IsIgnored($name));
  108. my $typbyte = hex($a[2]) & 7;
  109. my $sfirstbyte = $firstbyte & 7;
  110. my $val = "";
  111. my $devtype = "unknown";
  112. my $family = "unknown";
  113. my ($sgn, $tmp, $rain, $hum, $prs, $wnd);
  114. if($sfirstbyte == 7) {
  115. if($typbyte == 0 && int(@a) > 6) { # temp
  116. $sgn = ($firstbyte&8) ? -1 : 1;
  117. $tmp = $sgn * ($a[6].$a[3].".".$a[4]) + $hash->{corr1};
  118. $val = "T: $tmp";
  119. $devtype = "Temp";
  120. $NotifyType="T";
  121. $NotifyTemperature=$tmp;
  122. }
  123. if($typbyte == 1 && int(@a) > 8) { # temp/hum
  124. $sgn = ($firstbyte&8) ? -1 : 1;
  125. $tmp = $sgn * ($a[6].$a[3].".".$a[4]) + $hash->{corr1};
  126. $hum = ($a[7].$a[8].".".$a[5]) + $hash->{corr2};
  127. $val = "T: $tmp H: $hum";
  128. $devtype = "PS50";
  129. $family = "WS300";
  130. $NotifyType="T H";
  131. $NotifyTemperature=$tmp;
  132. $NotifyHumidity=$hum;
  133. }
  134. if($typbyte == 2 && int(@a) > 5) { # rain
  135. #my $more = ($firstbyte&8) ? 0 : 1000;
  136. my $c = $hash->{corr1} ? $hash->{corr1} : 1;
  137. $rain = hex($a[5].$a[3].$a[4]) * $c;
  138. $val = "R: $rain";
  139. $devtype = "Rain";
  140. $family = "WS7000";
  141. $NotifyType="R";
  142. $NotifyRain=$rain;
  143. }
  144. if($typbyte == 3 && int(@a) > 8) { # wind
  145. my $hun = ($firstbyte&8) ? 100 : 0;
  146. $wnd = ($a[6].$a[3].".".$a[4])+$hun;
  147. my $dir = ((hex($a[7])&3).$a[8].$a[5])+0;
  148. my $swing = (hex($a[7])&6) >> 2;
  149. $val = "W: $wnd D: $dir A: $swing";
  150. $devtype = "Wind";
  151. $family = "WS7000";
  152. $NotifyType="W WD WS";
  153. $NotifyWind=$wnd;
  154. $NotifyWindDir=$dir;
  155. $NotifyWindSwing=$swing;
  156. }
  157. if($typbyte == 4 && int(@a) > 10) { # temp/hum/press
  158. $sgn = ($firstbyte&8) ? -1 : 1;
  159. $tmp = $sgn * ($a[6].$a[3].".".$a[4]) + $hash->{corr1};
  160. $hum = ($a[7].$a[8].".".$a[5]) + $hash->{corr2};
  161. $prs = ($a[9].$a[10])+ 900 + $hash->{corr3};
  162. if($prs < 930) {
  163. $prs = $prs + 100;
  164. }
  165. $val = "T: $tmp H: $hum P: $prs";
  166. $devtype = "Indoor";
  167. $family = "WS7000";
  168. $NotifyType="T H P";
  169. $NotifyTemperature=$tmp;
  170. $NotifyHumidity=$hum;
  171. $NotifyPressure=$prs;
  172. }
  173. if($typbyte == 5 && int(@a) > 5) { # brightness
  174. my $fakt = 1;
  175. my $rawfakt = ($a[6])+0;
  176. if($rawfakt == 1) { $fakt = 10; }
  177. if($rawfakt == 2) { $fakt = 100; }
  178. if($rawfakt == 3) { $fakt = 1000; }
  179. my $br = (hex($a[5].$a[4].$a[3])*$fakt) + $hash->{corr1};
  180. $val = "B: $br";
  181. $devtype = "Brightness";
  182. $family = "WS7000";
  183. $NotifyType="B";
  184. $NotifyBrightness=$br;
  185. }
  186. if($typbyte == 6 && int(@a) > 0) { # Pyro: wurde nie gebaut
  187. $devtype = "Pyro";
  188. $family = "WS7000";
  189. }
  190. if($typbyte == 7 && int(@a) > 8) { # Temp/hum
  191. $sgn = ($firstbyte&8) ? -1 : 1;
  192. $tmp = $sgn * ($a[6].$a[3].".".$a[4]) + $hash->{corr1};
  193. $hum = ($a[7].$a[8].".".$a[5]) + $hash->{corr2};
  194. $val = "T: $tmp H: $hum";
  195. $devtype = "Temp/Hum";
  196. $family = "WS7000";
  197. $NotifyType="T H";
  198. $NotifyTemperature=$tmp;
  199. $NotifyHumidity=$hum;
  200. }
  201. } else { # $firstbyte not 7
  202. if(@a == 9 && int(@a) > 8) { # S300TH
  203. # Sanity check
  204. if (!($msg =~ /^K[0-9A-F]\d\d\d\d\d\d\d$/ )) {
  205. Log3 $name, 1,
  206. "Error: S300TH CUL_WS Cannot decode $msg (sanitycheck). Malformed";
  207. return "";
  208. }
  209. $sgn = ($firstbyte&8) ? -1 : 1;
  210. $tmp = sprintf("%0.1f", $sgn * ($a[6].$a[3].".".$a[4]) + $hash->{corr1});
  211. $hum = ($a[7].$a[8].".".$a[5]) + $hash->{corr2};
  212. $val = "T: $tmp H: $hum";
  213. $devtype = "S300TH";
  214. $family = "WS300";
  215. $NotifyType="T H";
  216. $NotifyTemperature=$tmp;
  217. $NotifyHumidity=$hum;
  218. } elsif(@a == 15 && int(@a) > 14) { # KS300/2
  219. my $c = $hash->{corr4} ? $hash->{corr4} : 255;
  220. $rain = sprintf("%0.1f", hex("$a[14]$a[11]$a[12]") * $c / 1000);
  221. $wnd = sprintf("%0.1f", "$a[9]$a[10].$a[7]" + $hash->{corr3});
  222. $hum = sprintf( "%02d", "$a[8]$a[5]" + $hash->{corr2});
  223. $tmp = sprintf("%0.1f", ("$a[6]$a[3].$a[4]"+ $hash->{corr1}),
  224. (($a[1] & 0xC) ? -1 : 1));
  225. my $ir = ((hex($a[1]) & 2)) ? "yes" : "no";
  226. $val = "T: $tmp H: $hum W: $wnd R: $rain IR: $ir";
  227. $devtype = "KS300/2";
  228. $family = "WS300";
  229. $NotifyType="T H W R IR";
  230. $NotifyTemperature=$tmp;
  231. $NotifyHumidity=$hum;
  232. $NotifyWind=$wnd;
  233. $NotifyRain=$rain;
  234. $NotifyIsRaining=$ir;
  235. } elsif(int(@a) > 8) { # WS7000 Temp/Hum sensors
  236. if(join("", @a[3..8]) =~ m/^\d*$/) { # Forum 49125
  237. $sgn = ($firstbyte&8) ? -1 : 1;
  238. $tmp = $sgn * ($a[6].$a[3].".".$a[4]) + $hash->{corr1};
  239. $hum = ($a[7].$a[8].".".$a[5]) + $hash->{corr2};
  240. $val = "T: $tmp H: $hum";
  241. $devtype = "TH".$sfirstbyte;
  242. $family = "WS7000";
  243. $NotifyType="T H";
  244. $NotifyTemperature=$tmp;
  245. $NotifyHumidity=$hum;
  246. }
  247. }
  248. }
  249. if(!$val) {
  250. Log3 $name, 1, "CUL_WS Cannot decode $msg";
  251. return "";
  252. }
  253. Log3 $name, 4, "CUL_WS $devtype $name: $val";
  254. # Sanity checks
  255. if($NotifyTemperature && ReadingsVal($name, "temperature", undef)) {
  256. my $tval = ReadingsVal($name, "strangetemp",
  257. ReadingsVal($name, "temperature", undef));
  258. my $diff = ($NotifyTemperature - $tval)+0;
  259. if($diff < -15.0 || $diff > 15.0) {
  260. Log3 $name, 2,
  261. "$name: Temp difference ($diff) too large: $val, skipping it";
  262. readingsSingleUpdate($hash, "strangetemp", $NotifyTemperature, 0);
  263. return "";
  264. }
  265. }
  266. delete $hash->{READINGS}{strangetemp} if($hash->{READINGS});
  267. if($NotifyPressure && ReadingsVal($name, "pressure", undef)) {
  268. my $tval = ReadingsVal($name, "strangepress",
  269. ReadingsVal($name, "pressure", undef));
  270. my $diff = ($NotifyPressure - $tval)+0;
  271. if($diff < -10.0 || $diff > 10.0) {
  272. Log3 $name, 2,
  273. "$name: Pressure difference ($diff) too large: $val, skipping it";
  274. readingsSingleUpdate($hash, "strangepress", $NotifyPressure, 0);
  275. return "";
  276. }
  277. }
  278. delete $hash->{READINGS}{strangepress} if($hash->{READINGS});
  279. if(defined($hum) && ($hum < 0 || $hum > 100)) {
  280. Log3 $name, 1, "BOGUS: $name reading: $val, skipping it";
  281. return "";
  282. }
  283. readingsBeginUpdate($hash);
  284. readingsBulkUpdate($hash, "state", $val);
  285. my $i=1;
  286. my $j;
  287. my @Notifies=split(" ", $NotifyType);
  288. for($j=0; $j<int(@Notifies); $j++) {
  289. my $val = "";
  290. if($Notifies[$j] eq "T") { $val = $NotifyTemperature;
  291. } elsif($Notifies[$j] eq "H") { $val = $NotifyHumidity;
  292. } elsif($Notifies[$j] eq "R") { $val = $NotifyRain;
  293. } elsif($Notifies[$j] eq "W") { $val = $NotifyWind;
  294. } elsif($Notifies[$j] eq "WD") { $val = $NotifyWindDir;
  295. } elsif($Notifies[$j] eq "WS") { $val = $NotifyWindSwing;
  296. } elsif($Notifies[$j] eq "IR") { $val = $NotifyIsRaining;
  297. } elsif($Notifies[$j] eq "B") { $val = $NotifyBrightness;
  298. } elsif($Notifies[$j] eq "P") { $val = $NotifyPressure;
  299. }
  300. my $nm = $NotifyMappings{$Notifies[$j]};
  301. readingsBulkUpdate($hash, $nm, $val);
  302. }
  303. readingsBulkUpdate($hash, "DEVTYPE", $devtype, 0);
  304. readingsBulkUpdate($hash, "DEVFAMILY", $family, 0);
  305. readingsEndUpdate($hash, 1); # Notify is done by Dispatch
  306. return $name;
  307. }
  308. sub
  309. CUL_WS_Attr(@)
  310. {
  311. my @a = @_;
  312. # Make possible to use the same code for different logical devices when they
  313. # are received through different physical devices.
  314. return if($a[0] ne "set" || $a[2] ne "IODev");
  315. my $hash = $defs{$a[1]};
  316. my $iohash = $defs{$a[3]};
  317. my $cde = $hash->{CODE};
  318. delete($modules{CUL_WS}{defptr}{$cde});
  319. $modules{CUL_WS}{defptr}{$iohash->{NAME} . "." . $cde} = $hash;
  320. return undef;
  321. }
  322. 1;
  323. =pod
  324. =item summary devices communicating via the ELV WS protocol (S300TH, etc)
  325. =item summary_DE Anbindung von ELV Ger&auml;ten mit dem WS Protokoll (S300TH, usw.)
  326. =begin html
  327. <a name="CUL_WS"></a>
  328. <h3>CUL_WS</h3>
  329. <ul>
  330. The CUL_WS module interprets S300 type of messages received by the CUL.
  331. <br><br>
  332. <a name="CUL_WSdefine"></a>
  333. <b>Define</b>
  334. <ul>
  335. <code>define &lt;name&gt; CUL_WS &lt;code&gt; [corr1...corr4]</code> <br>
  336. <br>
  337. &lt;code&gt; is the code which must be set on the S300 device. Valid values
  338. are 1 through 8.<br>
  339. corr1..corr4 are up to 4 numerical correction factors, which will be added
  340. to the respective value to calibrate the device. Note: rain-values will be
  341. multiplied and not added to the correction factor.
  342. </ul>
  343. <br>
  344. <a name="CUL_WSset"></a>
  345. <b>Set</b> <ul>N/A</ul><br>
  346. <a name="CUL_WSget"></a>
  347. <b>Get</b> <ul>N/A</ul><br>
  348. <a name="CUL_WSattr"></a>
  349. <b>Attributes</b>
  350. <ul>
  351. <li><a href="#IODev">IODev</a>
  352. Note: by setting this attribute you can define different sets of 8
  353. devices in FHEM, each set belonging to a CUL. It is important, however,
  354. that a device is only received by the CUL defined, e.g. by using
  355. different Frquencies (433MHz vs 868MHz)
  356. </li>
  357. <li><a href="#do_not_notify">do_not_notify</a></li>
  358. <li><a href="#eventMap">eventMap</a></li>
  359. <li><a href="#ignore">ignore</a></li>
  360. <li><a href="#model">model</a> (S300,KS300,ASH2200)</li>
  361. <li><a href="#showtime">showtime</a></li>
  362. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  363. </ul>
  364. <br>
  365. </ul>
  366. =end html
  367. =begin html_DE
  368. <a name="CUL_WS"></a>
  369. <h3>CUL_WS</h3>
  370. <ul>
  371. Das CUL_WS-Modul entschl&uuml;sselt die Nachrichten des Types S300, die von
  372. dem CUL empfangen wurden.
  373. <br><br>
  374. <a name="CUL_WSdefine"></a>
  375. <b>Define</b>
  376. <ul>
  377. <code>define &lt;name&gt; CUL_WS &lt;code&gt; [corr1...corr4]</code> <br>
  378. <br>
  379. &lt;code&gt; ist der Code, der an dem S300 eingestellt werden muss.
  380. G&uuml;ltige Werte sind 1 bis 8
  381. <br>
  382. corr1..corr4 entsprechen vier m&ouml;glichen Korrekturwerten, die den
  383. jeweiligen Werten hinzuaddiert werden, um die Ger&auml;te zu kalibrieren.
  384. Hinweis: Bei den Werten f&uuml;r Regenmengen werden die Korrekturwerte
  385. nicht hinzuaddiert, sondern als Faktor mit dem Regenwert multipliziert.
  386. </ul>
  387. <br>
  388. <a name="CUL_WSset"></a>
  389. <b>Set</b> <ul>N/A</ul><br>
  390. <a name="CUL_WSget"></a>
  391. <b>Get</b> <ul>N/A</ul><br>
  392. <a name="CUL_WSattr"></a>
  393. <b>Attribute</b>
  394. <ul>
  395. <li><a href="#IODev">IODev (!)</a>
  396. Achtung: mit diesem Attribut ist es m&ouml;glich mehrere 8-er Sets an
  397. S300-er in FHEM zu definieren. Wichtige Voraussetzung allerdings ist,
  398. dass nur das spezifizierte CUL das S300 empfangen kann, z.Bsp. durch
  399. Frequenztrennung (433MHz vs. 868MHz).
  400. </li>
  401. <li><a href="#do_not_notify">do_not_notify</a></li>
  402. <li><a href="#eventMap">eventMap</a></li>
  403. <li><a href="#ignore">ignore</a></li>
  404. <li><a href="#model">model</a> (S300,KS300,ASH2200)</li>
  405. <li><a href="#showtime">showtime</a></li>
  406. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  407. </ul>
  408. <br>
  409. </ul>
  410. =end html_DE
  411. =cut