14_SD_WS09.pm 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. ##############################################
  2. # $Id: 14_SD_WS09.pm 16935 2018-07-02 20:22:34Z Sidey $
  3. #
  4. # The purpose of this module is to support serval
  5. # weather sensors like WS-0101 (Sender 868MHz ASK Epmfänger RX868SH-DV elv)
  6. # Sidey79 & pejonp 2015
  7. #
  8. # 22.09.2017: rainTotal --> rain_total
  9. # 23.09.2017: windDirAverage SabineT https://forum.fhem.de/index.php/topic,75225.msg669950.html#msg669950
  10. #
  11. #
  12. package main;
  13. use strict;
  14. use warnings;
  15. #use Math::Round qw/nearest/;
  16. # werden benötigt, aber im Programm noch extra abgetestet
  17. #use Digest::CRC qw(crc);
  18. #use Math::Trig;
  19. sub SD_WS09_Initialize($)
  20. {
  21. my ($hash) = @_;
  22. $hash->{Match} = "^P9#F[A-Fa-f0-9]+"; ## pos 7 ist aktuell immer 0xF
  23. $hash->{DefFn} = "SD_WS09_Define";
  24. $hash->{UndefFn} = "SD_WS09_Undef";
  25. $hash->{ParseFn} = "SD_WS09_Parse";
  26. $hash->{AttrFn} = "SD_WS09_Attr";
  27. $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 "
  28. ."model:CTW600,WH1080 ignore:0,1 "
  29. ."windKorrektur:-3,-2,-1,0,1,2,3 "
  30. ."Unit_of_Wind:m/s,km/h,ft/s,mph,bft,knot "
  31. ."WindDirAverageTime "
  32. ."WindDirAverageMinSpeed "
  33. ."WindDirAverageDecay "
  34. ."$readingFnAttributes ";
  35. $hash->{AutoCreate} =
  36. { "SD_WS09.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.* windKorrektur:.*:0 verbose:5" , FILTER => "%NAME", GPLOT => "WH1080wind4:windSpeed/windGust,", autocreateThreshold => "2:180"} };
  37. }
  38. #############################
  39. sub SD_WS09_Define($$)
  40. {
  41. my ($hash, $def) = @_;
  42. my @a = split("[ \t][ \t]*", $def);
  43. return "wrong syntax: define <name> SD_WS09 <code> ".int(@a)
  44. if(int(@a) < 3 );
  45. $hash->{CODE} = $a[2];
  46. $hash->{lastMSG} = "";
  47. $hash->{bitMSG} = "";
  48. $modules{SD_WS09}{defptr}{$a[2]} = $hash;
  49. $hash->{STATE} = "Defined";
  50. my $model = $a[2];
  51. $model =~ s/_.*$//;
  52. $hash->{MODEL} = $model;
  53. my $name= $hash->{NAME};
  54. return undef;
  55. }
  56. #####################################
  57. sub SD_WS09_Undef($$)
  58. {
  59. my ($hash, $name) = @_;
  60. delete($modules{SD_WS09}{defptr}{$hash->{CODE}})
  61. if(defined($hash->{CODE}) &&
  62. defined($modules{SD_WS09}{defptr}{$hash->{CODE}}));
  63. return undef;
  64. }
  65. ###################################
  66. sub SD_WS09_Parse($$)
  67. {
  68. my ($iohash, $msg) = @_;
  69. my $name = $iohash->{NAME};
  70. my (undef ,$rawData) = split("#",$msg);
  71. my @winddir_name=("N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW");
  72. my %uowind_unit= ("m/s",'1',"km/h",'3.6',"ft/s",'3.28',"bft",'-1',"mph",'2.24',"knot",'1.94');
  73. my %uowind_index = ("m/s",'0',"km/h",'1',"ft/s",'2',"mph",'3',"knot",'4',"bft",'5');
  74. my $hlen = length($rawData);
  75. my $blen = $hlen * 4;
  76. my $bitData = unpack("B$blen", pack("H$hlen", $rawData));
  77. my $bitData2;
  78. my $bitData20;
  79. my $rain = 0;
  80. my $deviceCode = 0;
  81. my $model = "undef"; # 0xFFA -> WS0101/WH1080 alles andere -> CTW600
  82. my $modelid;
  83. my $windSpeed;
  84. my $windSpeed_kmh;
  85. my $windSpeed_fts;
  86. my $windSpeed_bft;
  87. my $windSpeed_mph;
  88. my $windSpeed_kn;
  89. my $windguest;
  90. my $windguest_kmh;
  91. my $windguest_fts;
  92. my $windguest_bft;
  93. my $windguest_mph;
  94. my $windguest_kn;
  95. my $sensdata;
  96. my $id;
  97. my $bat = 0;
  98. my $temp = 0;
  99. my $hum = 1;
  100. my $windDirection = 1 ;
  101. my $windDirectionText = "N";
  102. my $windDirectionDegree = 0;
  103. my $FOuvo ; # UV data nybble ?
  104. my $FOlux ; # Lux High byte (full scale = 4,000,000?) # Lux Middle byte # Lux Low byte, Unit = 0.1 Lux (binary)
  105. my $rr2 ;
  106. my $state;
  107. my $msg_vor = 'P9#';
  108. my $minL1 = 70;
  109. my $minL2 = 60;
  110. my $whid;
  111. my $wh;
  112. my $rawData_merk;
  113. my $wfaktor = 1;
  114. my @windstat;
  115. my $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble
  116. Log3 $iohash, 4, "$name: SD_WS09_Parse0 msg=$rawData Bin=$bitData syncp=$syncpos length:".length($bitData) ;
  117. if ($syncpos ==-1 || length($bitData)-$syncpos < $minL2)
  118. {
  119. Log3 $iohash, 4, "$name: SD_WS09_Parse EXIT: msg=$rawData syncp=$syncpos length:".length($bitData) ;
  120. return undef;
  121. }
  122. my $crcwh1080 = AttrVal($iohash->{NAME},'WS09_CRCAUS',0);
  123. Log3 $iohash, 4, "$name: SD_WS09_Parse CRC_AUS:$crcwh1080 " ;
  124. $rawData_merk = $rawData;
  125. my $rc = eval
  126. {
  127. require Digest::CRC;
  128. Digest::CRC->import();
  129. 1;
  130. };
  131. if($rc) # test ob Digest::CRC geladen wurde
  132. {
  133. $rr2 = SD_WS09_CRCCHECK($rawData);
  134. if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) {
  135. # 1. OK
  136. $model = "WH1080";
  137. Log3 $iohash, 4, "$name: SD_WS09_SHIFT_0 OK rwa:$rawData" ;
  138. } else {
  139. # 1. nok
  140. $rawData = SD_WS09_SHIFT($rawData);
  141. Log3 $iohash, 4, "$name: SD_WS09_SHIFT_1 NOK rwa:$rawData" ;
  142. $rr2 = SD_WS09_CRCCHECK($rawData);
  143. if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) {
  144. # 2.ok
  145. $msg = $msg_vor.$rawData;
  146. $model = "WH1080";
  147. Log3 $iohash, 4, "$name: SD_WS09_SHIFT_2 OK rwa:$rawData msg:$msg" ;
  148. } else {
  149. # 2. nok
  150. $rawData = SD_WS09_SHIFT($rawData);
  151. Log3 $iohash, 4, "$name: SD_WS09_SHIFT_3 NOK rwa:$rawData" ;
  152. $rr2 = SD_WS09_CRCCHECK($rawData);
  153. if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) {
  154. # 3. ok
  155. $msg = $msg_vor.$rawData;
  156. $model = "WH1080";
  157. Log3 $iohash, 4, "$name: SD_WS09_SHIFT_4 OK rwa:$rawData msg:$msg" ;
  158. }else{
  159. # 3. nok
  160. $rawData = $rawData_merk;
  161. $msg = $msg_vor.$rawData;
  162. Log3 $iohash, 4, "$name: SD_WS09_SHIFT_5 NOK rwa:$rawData msg:$msg" ;
  163. }
  164. }
  165. }
  166. }else {
  167. Log3 $iohash, 1, "$name: SD_WS09 CRC_not_load: Modul Digest::CRC fehlt: cpan install Digest::CRC or sudo apt-get install libdigest-crc-perl" ;
  168. return "";
  169. }
  170. $hlen = length($rawData);
  171. $blen = $hlen * 4;
  172. $bitData = unpack("B$blen", pack("H$hlen", $rawData));
  173. Log3 $iohash, 4, "$name: SD_WS09_CRC_test2 rwa:$rawData msg:$msg CRC:".SD_WS09_CRCCHECK($rawData) ;
  174. if( $model eq "WH1080") {
  175. $sensdata = substr($bitData,8);
  176. $whid = substr($sensdata,0,4);
  177. if( $whid == "1010" ){ # A Wettermeldungen
  178. Log3 $iohash, 4, "$name: SD_WS09_Parse_1 msg=$sensdata length:".length($sensdata) ;
  179. $model = "WH1080";
  180. $id = SD_WS09_bin2dec(substr($sensdata,4,8));
  181. $bat = (SD_WS09_bin2dec((substr($sensdata,64,4))) == 0) ? 'ok':'low' ; # decode battery = 0 --> ok
  182. $temp = (SD_WS09_bin2dec(substr($sensdata,12,12)) - 400)/10;
  183. $hum = SD_WS09_bin2dec(substr($sensdata,24,8));
  184. $windDirection = SD_WS09_bin2dec(substr($sensdata,68,4));
  185. $windDirectionText = $winddir_name[$windDirection];
  186. $windDirectionDegree = $windDirection * 360 / 16;
  187. $windSpeed = round((SD_WS09_bin2dec(substr($sensdata,32,8))* 34)/100,01);
  188. Log3 $iohash, 4, "$name: SD_WS09_Parse_2 ".$model." id:$id, Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ;
  189. $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01);
  190. Log3 $iohash, 4, "$name: SD_WS09_Parse_3 ".$model." id:$id, Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ;
  191. $rain = SD_WS09_bin2dec(substr($sensdata,52,12)) * 0.3;
  192. Log3 $iohash, 4, "$name: SD_WS09_Parse_4 ".$model." id:$id, Rain bit: ".substr($sensdata,52,12)." Dec: " . $rain ;
  193. Log3 $iohash, 4, "$name: SD_WS09_Parse_5 ".$model." id:$id, bat:$bat, temp=$temp, hum=$hum, winddir=$windDirection:$windDirectionText wS=$windSpeed, wG=$windguest, rain=$rain";
  194. } elsif( $whid == "1011" ){ # B DCF-77 Zeitmeldungen vom Sensor
  195. my $hrs1 = substr($sensdata,16,8);
  196. my $hrs;
  197. my $mins;
  198. my $sec;
  199. my $mday;
  200. my $month;
  201. my $year;
  202. $id = SD_WS09_bin2dec(substr($sensdata,4,8));
  203. Log3 $iohash, 4, "$name: SD_WS09_Parse_6 Zeitmeldung0: HRS1=$hrs1 id:$id" ;
  204. $hrs = sprintf "%02d" , SD_WS09_BCD2bin(substr($sensdata,18,6) ) ; # Stunde
  205. $mins = sprintf "%02d" , SD_WS09_BCD2bin(substr($sensdata,24,8)); # Minute
  206. $sec = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,32,8)); # Sekunde
  207. #day month year
  208. $year = SD_WS09_BCD2bin(substr($sensdata,40,8)); # Jahr
  209. $month = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,51,5)); # Monat
  210. $mday = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,56,8)); # Tag
  211. Log3 $iohash, 4, "$name: SD_WS09_Parse_7 Zeitmeldung1: id:$id, msg=$rawData length:".length($bitData) ;
  212. Log3 $iohash, 4, "$name: SD_WS09_Parse_8 Zeitmeldung2: id:$id, HH:mm:ss - ".$hrs.":".$mins.":".$sec ;
  213. Log3 $iohash, 4, "$name: SD_WS09_Parse_9 Zeitmeldung3: id:$id, dd.mm.yy - ".$mday.".".$month.".".$year ;
  214. return $name;
  215. } elsif( $whid == "0111" ){ # 7 UV/Solar Meldungen vom Sensor
  216. # Fine Offset (Solar Data) message BYTE offsets (within receive buffer)
  217. # Examples= FF 75 B0 55 00 97 8E 0E *CRC*OK*
  218. # =FF 75 B0 55 00 8F BE 92 *CRC*OK*
  219. # symbol FOrunio = 0 ; Fine Offset Runin byte = FF
  220. # symbol FOsaddo = 1 ; Solar Pod address word
  221. # symbol FOuvo = 3 ; UV data nybble ?
  222. # symbol FOluxHo = 4 ; Lux High byte (full scale = 4,000,000?)
  223. # symbol FOluxMo = 5 ; Lux Middle byte
  224. # symbol FOluxLo = 6 ; Lux Low byte, Unit = 0.1 Lux (binary)
  225. # symbol FOcksumo= 7 ; CRC checksum (CRC-8 shifting left)
  226. $id = SD_WS09_bin2dec(substr($sensdata,4,8));
  227. $FOuvo = SD_WS09_bin2dec(substr($sensdata,12,4));
  228. $FOlux = SD_WS09_bin2dec(substr($sensdata,24,24))/10;
  229. Log3 $iohash, 4, "$name: SD_WS09_Parse_10 UV-Solar1: id:$id, UV:".$FOuvo." Lux:".$FOlux ;
  230. } else {
  231. Log3 $iohash, 4, "$name: SD_WS09_Parse_Ex Exit: msg=$rawData length:".length($sensdata) ;
  232. Log3 $iohash, 4, "$name: SD_WS09_WH10 Exit: Model=$model " ;
  233. return undef;
  234. }
  235. }else{
  236. # es wird eine CTW600 angenommen
  237. $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble
  238. $wh = substr($bitData,0,8);
  239. if ( $wh == "11111110" && length($bitData) > $minL1 )
  240. {
  241. Log3 $iohash, 4, "$name: SD_WS09_Parse_11 CTW600 EXIT: msg=$bitData wh:$wh length:".length($bitData) ;
  242. $sensdata = substr($bitData,$syncpos+8);
  243. Log3 $iohash, 4, "$name: SD_WS09_Parse_12 CTW WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ;
  244. $model = "CTW600";
  245. $whid = "0000";
  246. my $nn1 = substr($sensdata,10,2); # Keine Bedeutung
  247. my $nn2 = substr($sensdata,62,4); # Keine Bedeutung
  248. $modelid = substr($sensdata,0,4);
  249. Log3 $iohash, 4, "$name: SD_WS09_Parse_13 Id: ".$modelid." NN1:$nn1 NN2:$nn2" ;
  250. Log3 $iohash, 4, "$name: SD_WS09_Parse_14 Id: ".$modelid." Bin-Sync=$sensdata syncp=$syncpos length:".length($sensdata) ;
  251. $bat = SD_WS09_bin2dec((substr($sensdata,0,3))) ;
  252. $id = SD_WS09_bin2dec(substr($sensdata,4,6));
  253. $temp = (SD_WS09_bin2dec(substr($sensdata,12,10)) - 400)/10;
  254. $hum = SD_WS09_bin2dec(substr($sensdata,22,8));
  255. $windDirection = SD_WS09_bin2dec(substr($sensdata,66,4));
  256. $windDirectionText = $winddir_name[$windDirection];
  257. $windDirectionDegree = $windDirection * 360 / 16;
  258. $windSpeed = round(SD_WS09_bin2dec(substr($sensdata,30,16))/240,01);
  259. Log3 $iohash, 4, "$name: SD_WS09_Parse_15 ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ;
  260. $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01);
  261. Log3 $iohash, 4, "$name: SD_WS09_Parse_16 ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ;
  262. $rain = round(SD_WS09_bin2dec(substr($sensdata,46,16)) * 0.3,01);
  263. Log3 $iohash, 4, "$name: SD_WS09_Parse_17 ".$model." Rain bit: ".substr($sensdata,46,16)." Dec: " . $rain ;
  264. }else{
  265. Log3 $iohash, 4, "$name: SD_WS09_Parse_18 CTW600 EXIT: msg=$bitData length:".length($bitData) ;
  266. return undef;
  267. }
  268. }
  269. Log3 $iohash, 4, "$name: SD_WS09_Parse_19 ".$model." id:$id :$sensdata ";
  270. if($hum > 100 || $hum < 0) {
  271. Log3 $iohash, 4, "$name: SD_WS09_Parse HUM: hum=$hum msg=$rawData " ;
  272. return undef;
  273. }
  274. if($temp > 60 || $temp < -40) {
  275. Log3 $iohash, 4, "$name: SD_WS09_Parse TEMP: Temp=$temp msg=$rawData " ;
  276. return undef;
  277. }
  278. my $longids = AttrVal($iohash->{NAME},'longids',0);
  279. if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/)))
  280. {
  281. $deviceCode=$model."_".$id;
  282. Log3 $iohash,4, "$name: SD_WS09_Parse using longid: $longids model: $model";
  283. } else {
  284. $deviceCode = $model;
  285. }
  286. my $def = $modules{SD_WS09}{defptr}{$iohash->{NAME} . "." . $deviceCode};
  287. $def = $modules{SD_WS09}{defptr}{$deviceCode} if(!$def);
  288. if(!$def) {
  289. Log3 $iohash, 1, 'SD_WS09_Parse UNDEFINED sensor ' . $model . ' detected, code ' . $deviceCode;
  290. return "UNDEFINED $deviceCode SD_WS09 $deviceCode";
  291. }
  292. my $hash = $def;
  293. $name = $hash->{NAME};
  294. Log3 $name, 4, "SD_WS09_Parse_20: $name ($rawData)";
  295. if (!defined(AttrVal($name,"event-min-interval",undef)))
  296. {
  297. my $minsecs = AttrVal($iohash->{NAME},'minsecs',0);
  298. if($hash->{lastReceive} && (time() - $hash->{lastReceive} < $minsecs)) {
  299. Log3 $hash, 4, "SD_WS09_Parse_End $deviceCode Dropped due to short time. minsecs=$minsecs";
  300. return undef;
  301. }
  302. }
  303. my $windkorr = AttrVal($name,'windKorrektur',0);
  304. if ($windkorr != 0 )
  305. {
  306. my $oldwinddir = $windDirection;
  307. $windDirection = $windDirection + $windkorr;
  308. $windDirectionText = $winddir_name[$windDirection];
  309. Log3 $hash, 4, "SD_WS09_Parse_WK ".$model." Faktor:$windkorr wD:$oldwinddir Korrektur wD:$windDirection:$windDirectionText" ;
  310. }
  311. # "Unit_of_Wind:m/s,km/h,ft/s,bft,knot "
  312. # my %uowind_unit= ("m/s",'1',"km/h",'3.6',"ft/s",'3.28',"bft",'-1',"mph",'2.24',"knot",'1.94');
  313. # B = Wurzel aus ( 9 + 6 * V ) - 3
  314. # V = 17 Meter pro Sekunde ergibt: B = Wurzel aus( 9 + 6 * 17 ) - 3
  315. # Das ergibt : 7,53 Beaufort
  316. $windstat[0]= " Ws:$windSpeed Wg:$windguest m/s";
  317. Log3 $hash, 4, "SD_WS09_Wind $windstat[0] : Faktor:$wfaktor" ;
  318. $wfaktor = $uowind_unit{"km/h"};
  319. $windguest_kmh = round ($windguest * $wfaktor,01);
  320. $windSpeed_kmh = round ($windSpeed * $wfaktor,01);
  321. $windstat[1]= " Ws:$windSpeed_kmh Wg:$windguest_kmh km/h";
  322. Log3 $hash, 4, "SD_WS09_Wind $windstat[1] : Faktor:$wfaktor" ;
  323. $wfaktor = $uowind_unit{"ft/s"};
  324. $windguest_fts = round ($windguest * $wfaktor,01);
  325. $windSpeed_fts = round ($windSpeed * $wfaktor,01);
  326. $windstat[2]= " Ws:$windSpeed_fts Wg:$windguest_fts ft/s";
  327. Log3 $hash, 4, "SD_WS09_Wind $windstat[2] : Faktor:$wfaktor" ;
  328. $wfaktor = $uowind_unit{"mph"};
  329. $windguest_mph = round ($windguest * $wfaktor,01);
  330. $windSpeed_mph = round ($windSpeed * $wfaktor,01);
  331. $windstat[3]= " Ws:$windSpeed_mph Wg:$windguest_mph mph";
  332. Log3 $hash, 4, "SD_WS09_Wind $windstat[3] : Faktor:$wfaktor" ;
  333. $wfaktor = $uowind_unit{"knot"};
  334. $windguest_kn = round ($windguest * $wfaktor,01);
  335. $windSpeed_kn = round ($windSpeed * $wfaktor,01);
  336. $windstat[4]= " Ws:$windSpeed_kn Wg:$windguest_kn kn" ;
  337. Log3 $hash, 4, "SD_WS09_Wind $windstat[4] : Faktor:$wfaktor" ;
  338. $windguest_bft = round(sqrt( 9 + (6 * $windguest)) - 3,0) ;
  339. $windSpeed_bft = round(sqrt( 9 + (6 * $windSpeed)) - 3,0) ;
  340. $windstat[5]= " Ws:$windSpeed_bft Wg:$windguest_bft bft";
  341. Log3 $hash, 4, "SD_WS09_Wind $windstat[5] " ;
  342. # Resets des rain counters abfangen:
  343. # wenn der aktuelle Wert < letzter Wert ist, dann fand ein reset statt
  344. # die Differenz "letzer Wert - aktueller Wert" wird dann als offset für zukünftige Ausgaben zu rain addiert
  345. # offset wird auch im Reading ".rain_offset" gespeichert
  346. my $last_rain = ReadingsVal($name, "rain", 0);
  347. my $rain_offset = ReadingsVal($name, ".rainOffset", 0);
  348. $rain_offset += $last_rain if($rain < $last_rain);
  349. my $rain_total = $rain + $rain_offset;
  350. Log3 $hash, 4, "SD_WS09_Parse_rain_offset ".$model." rain:$rain raintotal:$rain_total rainoffset:$rain_offset " ;
  351. # windDirectionAverage berechnen
  352. my $average = SD_WS09_WindDirAverage($hash, $windSpeed, $windDirectionDegree);
  353. $hash->{lastReceive} = time();
  354. $def->{lastMSG} = $rawData;
  355. readingsBeginUpdate($hash);
  356. if($whid ne "0111")
  357. {
  358. #my $uowind = AttrVal($hash->{NAME},'Unit_of_Wind',0) ;
  359. my $uowind = AttrVal($name,'Unit_of_Wind',0) ;
  360. my $windex = $uowind_index{$uowind};
  361. if (!defined $windex) {
  362. $windex = 0;
  363. }
  364. $state = "T: $temp ". ($hum>0 ? " H: $hum ":" "). $windstat[$windex]." Wd: $windDirectionText "." R: $rain";
  365. readingsBulkUpdate($hash, "id", $id) if ($id ne "");
  366. readingsBulkUpdate($hash, "state", $state);
  367. readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne"");
  368. readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne "" && $hum != 0 );
  369. readingsBulkUpdate($hash, "battery", $bat) if ($bat ne "");
  370. readingsBulkUpdate($hash, "batteryState", $bat) if ($bat ne "");
  371. #zusätzlich Daten für Wetterstation
  372. readingsBulkUpdate($hash, "rain", $rain );
  373. readingsBulkUpdate($hash, ".rainOffset", $rain_offset ); # Zwischenspeicher für den offset
  374. readingsBulkUpdate($hash, "rain_total", $rain_total ); # monoton steigender Wert von rain
  375. readingsBulkUpdate($hash, "windGust", $windguest );
  376. readingsBulkUpdate($hash, "windSpeed", $windSpeed );
  377. readingsBulkUpdate($hash, "windGust_kmh", $windguest_kmh );
  378. readingsBulkUpdate($hash, "windSpeed_kmh", $windSpeed_kmh );
  379. readingsBulkUpdate($hash, "windGust_fts", $windguest_fts );
  380. readingsBulkUpdate($hash, "windSpeed_fts", $windSpeed_fts );
  381. readingsBulkUpdate($hash, "windGust_mph", $windguest_mph );
  382. readingsBulkUpdate($hash, "windSpeed_mph", $windSpeed_mph );
  383. readingsBulkUpdate($hash, "windGust_kn", $windguest_kn );
  384. readingsBulkUpdate($hash, "windSpeed_kn", $windSpeed_kn );
  385. readingsBulkUpdate($hash, "windDirectionAverage", $average );
  386. readingsBulkUpdate($hash, "windDirection", $windDirection );
  387. readingsBulkUpdate($hash, "windDirectionDegree", $windDirectionDegree);
  388. readingsBulkUpdate($hash, "windDirectionText", $windDirectionText );
  389. }
  390. if(($whid eq "0111") && ($model eq "WH1080"))
  391. {
  392. $state = "UV: $FOuvo Lux: $FOlux ";
  393. readingsBulkUpdate($hash, "id", $id) if ($id ne "");
  394. readingsBulkUpdate($hash, "state", $state);
  395. #zusätzliche Daten UV + Lux
  396. readingsBulkUpdate($hash, "UV", $FOuvo );
  397. readingsBulkUpdate($hash, "Lux", $FOlux );
  398. }
  399. readingsEndUpdate($hash, 1); # Notify is done by Dispatch
  400. return $name;
  401. }
  402. sub SD_WS09_Attr(@)
  403. {
  404. my @a = @_;
  405. # Make possible to use the same code for different logical devices when they
  406. # are received through different physical devices.
  407. return if($a[0] ne "set" || $a[2] ne "IODev");
  408. my $hash = $defs{$a[1]};
  409. my $iohash = $defs{$a[3]};
  410. my $cde = $hash->{CODE};
  411. delete($modules{SD_WS09}{defptr}{$cde});
  412. $modules{SD_WS09}{defptr}{$iohash->{NAME} . "." . $cde} = $hash;
  413. return undef;
  414. }
  415. sub SD_WS09_WindDirAverage($$$){
  416. ###############################################################################
  417. # übernommen von SabineT https://forum.fhem.de/index.php/topic,75225.msg669950.html#msg669950
  418. # WindDirAverage
  419. # z.B.: myWindDirAverage('WH1080','windSpeed','windDirectionDegree',900,0.75,0.5)
  420. # avtime ist optional, default ist 600 s Zeitspanne, die berücksichtig werden soll
  421. # decay ist optional, default ist 1 Parameter, um ältere Werte geringer zu gewichten
  422. # minspeed ist optional, default ist 0 m/s
  423. #
  424. # Als Ergebnis wird die Windrichtung zurück geliefert, die aus dem aktuellen und
  425. # vergangenen Werten über eine Art exponentiellen Mittelwert berechnet werden.
  426. # Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit berücksichtigt (höhere Geschwindigkeit
  427. # bedeutet höhere Gewichtung).
  428. #
  429. # decay: 1 -> alle Werte werden gleich gewichtet
  430. # 0 -> nur der aktuelle Wert wird verwendet.
  431. # in der Praxis wird man Werte so um 0.75 nehmen
  432. #
  433. # minspeed: da bei sehr geringer Windgeschwindigkeit die Windrichtung üblicherweise nicht
  434. # eindeutig ist, kann mit minspeed ein Schwellwert angegeben werden
  435. # Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zurück geliefert
  436. #
  437. ###############################################################################
  438. my ($hash, $ws, $wd) = @_;
  439. my $name = $hash->{NAME};
  440. Log3 $hash, 4, "SD_WS09_WindDirAverage --- OK ----" ;
  441. my $rc = eval
  442. {
  443. require Math::Trig;
  444. Math::Trig->import();
  445. 1;
  446. };
  447. if($rc) # test ob Math::Trig geladen wurde
  448. {
  449. Log3 $hash, 4, "SD_WS09_WindDirAverage Math::Trig:OK" ;
  450. }else
  451. {
  452. Log3 $hash, 1, "SD_WS09_WindDirAverage Math::Trig:fehlt : cpan install Math::Trig" ;
  453. return "";
  454. }
  455. my $avtime = AttrVal($name,'WindDirAverageTime',0);
  456. my $decay = AttrVal($name,'WindDirAverageDecay',0);
  457. my $minspeed = AttrVal($name,'WindDirAverageMinSpeed',0);
  458. # default Werte für die optionalen Parameter, falls nicht beim Aufruf mit angegeben
  459. $avtime = 600 if (!(defined $avtime) || $avtime == 0 );
  460. $decay = 1 if (!(defined $decay));
  461. $decay = 1 if ($decay > 1); # darf nicht >1 sein
  462. $decay = 0 if ($decay < 0); # darf nicht <0 sein
  463. $minspeed = 0 if (!(defined $minspeed));
  464. $wd = deg2rad($wd);
  465. my $ctime = time;
  466. my $time = FmtDateTime($ctime);
  467. my @new = ($ws,$wd,$time);
  468. Log3 $hash, 4,"SD_WS09_WindDirAverage_01 $name :Speed=".$ws." DirR=".round($wd,2)." Time=".$time;
  469. Log3 $hash, 4,"SD_WS09_WindDirAverage_02 $name :avtime=".$avtime." decay=".$decay." minspeed=".$minspeed;
  470. my $num;
  471. my $arr;
  472. #-- initialize if requested
  473. if( ($avtime eq "-1") ){
  474. $hash->{helper}{history}=undef;
  475. }
  476. #-- test for existence
  477. if(!$hash->{helper}{history}){
  478. Log3 $hash, 4,"SD_WS09_WindDirAverage_03 $name :ARRAY CREATED";
  479. push(@{$hash->{helper}{history}},\@new);
  480. $num = 1;
  481. $arr=\@{$hash->{helper}{history}};
  482. } else {
  483. $num = int(@{$hash->{helper}{history}});
  484. $arr=\@{$hash->{helper}{history}};
  485. my $stime = time_str2num($arr->[0][2]); # Zeitpunkt des ältesten Eintrags
  486. my $ltime = time_str2num($arr->[$num-1][2]); # Zeitpunkt des letzten Eintrags
  487. Log3 $hash,4,"SD_WS09_WindDirAverage_04 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." ctime=".$ctime." ltime=".$ltime." stime=".$stime." num=".$num;
  488. if((($ctime - $ltime) > 10) || ($num == 0)) {
  489. if(($num < 25) && (($ctime-$stime) < $avtime)){
  490. Log3 $hash,4,"SD_WS09_WindDirAverage_05 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." num=".$num;
  491. push(@{$hash->{helper}{history}},\@new);
  492. } else {
  493. shift(@{$hash->{helper}{history}});
  494. push(@{$hash->{helper}{history}},\@new);
  495. Log3 $hash,4,"SD_WS09_WindDirAverage_06 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." num=".$num;
  496. }
  497. } else {
  498. return undef;
  499. }
  500. }
  501. #-- output and average
  502. my ($anz, $sanz) = 0;
  503. $num = int(@{$hash->{helper}{history}});
  504. my ($sumSin, $sumCos, $sumSpeed, $age, $maxage, $weight) = 0;
  505. for(my $i=0; $i<$num; $i++){
  506. ($ws, $wd, $time) = @{ $arr->[$i] };
  507. $age = $ctime - time_str2num($time);
  508. if (($time eq "") || ($age > $avtime)) {
  509. #-- zu alte Einträge entfernen
  510. Log3 $hash,4,"SD_WS09_WindDirAverage_07 $name i=".$i." Speed=".round($ws,2)." Dir=".round($wd,2)." Time=".substr($time,11)." ctime=".$ctime." akt.=".time_str2num($time);
  511. shift(@{$hash->{helper}{history}});
  512. $i--;
  513. $num--;
  514. } else {
  515. #-- Werte aufsummieren, Windrichtung gewichtet über Geschwindigkeit und decay/"alter"
  516. $weight = $decay ** ($age / $avtime);
  517. #-- für die Mittelwertsbildung der Geschwindigkeit wird nur ein 10tel von avtime genommen
  518. if ($age < ($avtime / 10)) {
  519. $sumSpeed += $ws * $weight if ($age < ($avtime / 10));
  520. $sanz++;
  521. }
  522. $sumSin += sin($wd) * $ws * $weight;
  523. $sumCos += cos($wd) * $ws * $weight;
  524. $anz++;
  525. Log3 $hash,4,"SD_WS09_WindDirAverage_08 $name i=".$i." Speed=".round($ws,2)." Dir=".round($wd,2)." Time=".substr($time,11)." vec=".round($sumSin,2)."/".round($sumCos,2)." age=".$age." ".round($weight,2);
  526. }
  527. }
  528. my $average = int((rad2deg(atan2($sumSin, $sumCos)) + 360) % 360);
  529. Log3 $hash,4,"SD_WS09_WindDirAverage_09 $name Mittelwert über $anz Werte ist $average, avspeed=".round($sumSpeed/$num,1) if ($num > 0);
  530. #-- undef zurückliefern, wenn die durchschnittliche Geschwindigkeit zu gering oder gar keine Werte verfügbar
  531. return undef if (($anz == 0) || ($sanz == 0));
  532. return undef if (($sumSpeed / $sanz) < $minspeed);
  533. Log3 $hash,4,"SD_WS09_WindDirAverage_END $name Mittelwert=$average";
  534. return $average;
  535. }
  536. sub SD_WS09_bin2dec($)
  537. {
  538. my $h = shift;
  539. my $int = unpack("N", pack("B32",substr("0" x 32 . $h, -32)));
  540. return sprintf("%d", $int);
  541. }
  542. sub SD_WS09_binflip($)
  543. {
  544. my $h = shift;
  545. my $hlen = length($h);
  546. my $i = 0;
  547. my $flip = "";
  548. for ($i=$hlen-1; $i >= 0; $i--) {
  549. $flip = $flip.substr($h,$i,1);
  550. }
  551. return $flip;
  552. }
  553. sub SD_WS09_BCD2bin($) {
  554. my $binary = shift;
  555. my $int = unpack("N", pack("B32", substr("0" x 32 . $binary, -32)));
  556. my $BCD = sprintf("%x", $int );
  557. return $BCD;
  558. }
  559. sub SD_WS09_SHIFT($){
  560. my $rawData = shift;
  561. my $hlen = length($rawData);
  562. my $blen = $hlen * 4;
  563. my $bitData = unpack("B$blen", pack("H$hlen", $rawData));
  564. my $bitData2 = '1'.unpack("B$blen", pack("H$hlen", $rawData));
  565. my $bitData20 = substr($bitData2,0,length($bitData2)-1);
  566. $blen = length($bitData20);
  567. $hlen = $blen / 4;
  568. $rawData = uc(unpack("H$hlen", pack("B$blen", $bitData20)));
  569. $bitData = $bitData20;
  570. Log3 "SD_WS09_SHIFT", 4, "SD_WS09_SHIFT_0 raw: $rawData length:".length($bitData) ;
  571. Log3 "SD_WS09_SHIFT", 4, "SD_WS09_SHIFT_1 bitdata: $bitData" ;
  572. return $rawData;
  573. }
  574. sub SD_WS09_CRCCHECK($) {
  575. my $rawData = shift;
  576. my $datacheck1 = pack( 'H*', substr($rawData,2,length($rawData)-2) );
  577. my $crcmein1 = Digest::CRC->new(width => 8, poly => 0x31);
  578. my $rr3 = $crcmein1->add($datacheck1)->hexdigest;
  579. $rr3 = sprintf("%d", hex($rr3));
  580. Log3 "SD_WS09_CRCCHECK", 4, "SD_WS09_CRCCHECK : raw:$rawData CRC=$rr3 " ;
  581. return $rr3 ;
  582. }
  583. 1;
  584. =pod
  585. =item summary Supports weather sensors (WH1080/3080/CTW-600) protocol 9 from SIGNALduino
  586. =item summary_DE Unterstuetzt Wettersensoren (WH1080/3080/CTW-600) mit Protokol 9 vom SIGNALduino
  587. =begin html
  588. <a name="SD_WS09"></a>
  589. <h3>Wether Sensors protocol #9</h3>
  590. <ul>
  591. The SD_WS09 module interprets temperature sensor messages received by a Device like CUL, CUN, SIGNALduino etc.<br>
  592. Requires Perl-Modul Digest::CRC. <br>
  593. <br>
  594. cpan install Digest::CRC or sudo apt-get install libdigest-crc-perl <br>
  595. <br>
  596. <br>
  597. <b>Known models:</b>
  598. <ul>
  599. <li>WS-0101 --> Model: WH1080</li>
  600. <li>TFA 30.3189 / WH1080 --> Model: WH1080</li>
  601. <li>1073 (WS1080) --> Model: WH1080</li>
  602. <li>WH3080 --> Model: WH1080</li>
  603. <li>CTW600 --> Model: CTW600 (??) </li>
  604. </ul>
  605. <br>
  606. New received device are add in fhem with autocreate.
  607. <br><br>
  608. <a name="SD_WS09_Define"></a>
  609. <b>Define</b>
  610. <ul>The received devices created automatically.<br>
  611. The ID of the defice is the model or, if the longid attribute is specified, it is a combination of model and some random generated bits at powering the sensor.<br>
  612. If you want to use more sensors, you can use the longid option to differentiate them.
  613. </ul>
  614. <br>
  615. <a name="SD_WS09 Events"></a>
  616. <b>Generated readings:</b>
  617. <br>Some devices may not support all readings, so they will not be presented<br>
  618. <ul>
  619. <li>State (T: H: Ws: Wg: Wd: R: ) temperature, humidity, windSpeed, windGuest, windDirection, Rain</li>
  620. <li>Temperature (&deg;C)</li>
  621. <li>Humidity: (The humidity (1-100 if available)</li>
  622. <li>Battery: (low or ok)</li>
  623. <li>ID: (The ID-Number (number if)</li>
  624. <li>windSpeed/windGuest (Unit_of_Wind)) and windDirection (N-O-S-W)</li>
  625. <li>Rain (mm)</li>
  626. <li>windDirectionAverage<br>
  627. As a result, the wind direction is returned, which are calculated from the current and past values
  628. via a kind of exponential mean value.
  629. The respective wind speed is additionally taken into account (higher speed means higher weighting)</li>
  630. <b>WH3080:</b>
  631. <li>UV Index</li>
  632. <li>Lux</li>
  633. </ul>
  634. <br>
  635. <b>Attributes</b>
  636. <ul>
  637. <li><a href="#do_not_notify">do_not_notify</a></li>
  638. <li><a href="#ignore">ignore</a></li>
  639. <li><a href="#showtime">showtime</a></li>
  640. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  641. <li>Model: WH1080,CTW600
  642. </li>
  643. <li>windKorrektur: -3,-2,-1,0,1,2,3
  644. </li>
  645. <li>Unit_of_Wind<br>
  646. Unit of windSpeed and windGuest. State-Format: Value + Unit.
  647. <br>m/s,km/h,ft/s,mph,bft,knot
  648. </li><br>
  649. <li>WindDirAverageTime<br>
  650. default is 600s, time span to be considered for the calculation
  651. </li><br>
  652. <li>WindDirAverageMinSpeed<br>
  653. since the wind direction is usually not clear at very low wind speeds,
  654. minspeed can be used to specify a threshold value.
  655. <br>The (weighted) mean velocity < minspeed is returned undef
  656. </li><br>
  657. <li>WindDirAverageDecay<br>
  658. 1 -> all values ​​are weighted equally <br>
  659. 0 -> only the current value is used. <br>
  660. in practice, you will take values ​​around 0.75
  661. </li><br>
  662. <li>WS09_CRCAUS (set in Signalduino-Modul 00_SIGNALduino.pm)
  663. <br>0: CRC-Check WH1080 CRC-Summe = 0 on, default
  664. <br>2: CRC-Summe = 49 (x031) WH1080, set OK
  665. </li>
  666. </ul> <br>
  667. <a name="SD_WS09_Set"></a>
  668. <b>Set</b> <ul>N/A</ul><br>
  669. <a name="SD_WS09_Parse"></a>
  670. <b>Parse</b> <ul>N/A</ul><br>
  671. </ul>
  672. =end html
  673. =begin html_DE
  674. <a name="SD_WS09"></a>
  675. <h3>SD_WS09</h3>
  676. <ul>
  677. Das SD_WS09 Module verarbeitet von einem IO Gerät (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur-Sensoren.<br>
  678. <br>
  679. Perl-Modul Digest::CRC erforderlich. <br>
  680. <br>
  681. cpan install Digest::CRC oder auch <br>
  682. sudo apt-get install libdigest-crc-perl <br>
  683. <br>
  684. <br>
  685. <b>Unterstütze Modelle:</b>
  686. <ul>
  687. <li>WS-0101 --> Model: WH1080</li>
  688. <li>TFA 30.3189 / WH1080 --> Model: WH1080</li>
  689. <li>1073 (WS1080) --> Model: WH1080</li>
  690. <li>WH3080 --> Model: WH1080</li>
  691. <li>CTW600 --> Model: CTW600</li>
  692. </ul>
  693. <br>
  694. Neu empfangene Sensoren werden in FHEM per autocreate angelegt.
  695. <br><br>
  696. <a name="SD_WS09_Define"></a>
  697. <b>Define</b>
  698. <ul>Die empfangenen Sensoren werden automatisch angelegt.<br>
  699. Die ID der angelegten Sensoren wird nach jedem Batteriewechsel ge&aumlndert, welche der Sensor beim Einschalten zuf&aumlllig vergibt.<br>
  700. CRC Checksumme wird zur Zeit noch nicht überpr&uumlft, deshalb werden Sensoren bei denen die Luftfeuchte < 0 oder > 100 ist, nicht angelegt.<br>
  701. </ul>
  702. <br>
  703. <a name="SD_WS09 Events"></a>
  704. <b>Generierte Readings:</b>
  705. <ul>
  706. <li>State (T: H: Ws: Wg: Wd: R: ) temperature, humidity, windSpeed, windGuest, Einheit, windDirection, Rain</li>
  707. <li>Temperature (&deg;C)</li>
  708. <li>Humidity: (The humidity (1-100 if available)</li>
  709. <li>Battery: (low or ok)</li>
  710. <li>ID: (The ID-Number (number if)</li>
  711. <li>windSpeed/windgust (Einheit siehe Unit_of_Wind) and windDirection (N-O-S-W)</li>
  712. <li>Rain (mm)</li>
  713. <li>windDirectionAverage
  714. Als Ergebnis wird die Windrichtung zurück geliefert, die aus dem aktuellen und
  715. vergangenen Werten über eine Art exponentiellen Mittelwert berechnet werden.
  716. Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit berücksichtigt (höhere Geschwindigkeit
  717. bedeutet höhere Gewichtung).</li>
  718. <b>WH3080:</b>
  719. <li>UV Index</li>
  720. <li>Lux</li>
  721. </ul>
  722. <br>
  723. <b>Attribute</b>
  724. <ul>
  725. <li><a href="#do_not_notify">do_not_notify</a></li>
  726. <li><a href="#ignore">ignore</a></li>
  727. <li><a href="#showtime">showtime</a></li>
  728. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  729. <li>Model<br>
  730. WH1080, CTW600
  731. </li><br>
  732. <li>windKorrektur<br>
  733. Korrigiert die Nord-Ausrichtung des Windrichtungsmessers, wenn dieser nicht richtig nach Norden ausgerichtet ist.
  734. -3,-2,-1,0,1,2,3
  735. </li><br>
  736. <li>Unit_of_Wind<br>
  737. Hiermit wird der Einheit eingestellt und im State die entsprechenden Werte + Einheit angezeigt.
  738. <br>m/s,km/h,ft/s,mph,bft,knot
  739. </li><br>
  740. <li>WindDirAverageTime<br>
  741. default ist 600s, Zeitspanne die für die Berechung berücksichtig werden soll
  742. </li><br>
  743. <li>WindDirAverageMinSpeed<br>
  744. da bei sehr geringer Windgeschwindigkeit die Windrichtung üblicherweise nicht
  745. eindeutig ist, kann mit minspeed ein Schwellwert angegeben werden
  746. Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zurück geliefert
  747. </li><br>
  748. <li>WindDirAverageDecay<br>
  749. 1 -> alle Werte werden gleich gewichtet <br>
  750. 0 -> nur der aktuelle Wert wird verwendet.<br>
  751. in der Praxis wird man Werte so um 0.75 nehmen
  752. </li><br>
  753. <li>WS09_CRCAUS<br>
  754. Wird im Signalduino-Modul (00_SIGNALduino.pm) gesetzt
  755. <br>0: CRC-Prüfung bei WH1080 CRC-Summe = 0
  756. <br>2: CRC-Summe = 49 (x031) bei WH1080 wird als OK verarbeitet
  757. </li><br>
  758. </ul>
  759. <a name="SD_WS09_Set"></a>
  760. <b>Set</b> <ul>N/A</ul><br>
  761. <a name="SD_WS09_Parse"></a>
  762. <b>Parse</b> <ul>N/A</ul><br>
  763. </ul>
  764. =end html_DE
  765. =cut