14_CUL_TCM97001.pm 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890
  1. # From dancer0705
  2. #
  3. # Receive temperature sensor
  4. # Supported models:
  5. # - "TCM97..."
  6. # - "ABS700"
  7. # - "TCM21...."
  8. # - "Prologue"
  9. # - "Rubicson"
  10. # - "NC_WS"
  11. # - "GT_WT_02"
  12. # - "AURIOL"
  13. # - "KW9010"
  14. #
  15. # Unsupported models are saved in a device named CUL_TCM97001_Unknown
  16. #
  17. # Copyright (C) 2016 Bjoern Hempel
  18. #
  19. # This program is free software; you can redistribute it and/or modify it under
  20. # the terms of the GNU General Public License as published by the Free Software
  21. # Foundation; either version 2 of the License, or (at your option) any later
  22. # version.
  23. #
  24. # This program is distributed in the hope that it will be useful, but
  25. # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  26. # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  27. # more details.
  28. #
  29. # You should have received a copy of the GNU General Public License along with
  30. # this program; if not, write to the
  31. # Free Software Foundation, Inc.,
  32. # 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
  33. #
  34. # $Id: 14_CUL_TCM97001.pm 15367 2017-10-31 17:44:02Z bjoernh $
  35. #
  36. #
  37. # 14.06.2017 W155(TCM21...) wind/rain pejonp
  38. # 25.06.2017 W155(TCM21...) wind/rain pejonp
  39. # 04.07.2017 PFR-130 rain pejonp
  40. # 04.07.2017 TFA 30.3161 temp/rain pejonp
  41. # 22.10.2017 W174 rain elektron-bbs/HomeAutoUser
  42. ##############################################
  43. package main;
  44. use strict;
  45. use warnings;
  46. use SetExtensions;
  47. use constant { TRUE => 1, FALSE => 0 };
  48. #
  49. # All suported models
  50. #
  51. my %models = (
  52. "TCM97..." => 'TCM97...',
  53. "ABS700" => 'ABS700',
  54. "TCM21...." => 'TCM21....',
  55. "Prologue" => 'Prologue',
  56. "Rubicson" => 'Rubicson',
  57. "NC_WS" => 'NC_WS',
  58. "GT_WT_02" => 'GT_WT_02',
  59. "AURIOL" => 'AURIOL',
  60. "PFR_130" => 'PFR_130',
  61. "Type1" => 'Type1',
  62. "Mebus" => 'Mebus',
  63. "Eurochron" => 'Eurochron',
  64. "KW9010" => 'KW9010',
  65. "Unknown" => 'Unknown',
  66. "W174" => 'W174',
  67. );
  68. sub
  69. CUL_TCM97001_Initialize($)
  70. {
  71. my ($hash) = @_;
  72. $hash->{Match} = "^s.....";
  73. $hash->{DefFn} = "CUL_TCM97001_Define";
  74. $hash->{UndefFn} = "CUL_TCM97001_Undef";
  75. $hash->{ParseFn} = "CUL_TCM97001_Parse";
  76. $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 " .
  77. "$readingFnAttributes " .
  78. "model:".join(",", sort keys %models);
  79. $hash->{AutoCreate}=
  80. {
  81. "CUL_TCM97001_Unknown.*" => { GPLOT => "", FILTER => "%NAME", autocreateThreshold => "2:10" },
  82. "CUL_TCM97001.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  83. "Prologue_.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  84. "Mebus_.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  85. "NC_WS.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  86. "ABS700.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  87. "Eurochron.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  88. "TCM21....*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  89. "TCM97..._.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  90. "GT_WT_02.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  91. "Type1.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  92. "Rubicson.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  93. "AURIOL.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  94. "PFR_130.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  95. "KW9010.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  96. "TCM97001.*" => { ATTR => "event-on-change-reading:.*", GPLOT => "temp4hum4:Temp/Hum,", FILTER => "%NAME", autocreateThreshold => "2:340"},
  97. "W174.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", GPLOT => "rain4:Rain,", FILTER => "%NAME", autocreateThreshold => "2:180"},
  98. "Unknown_.*" => { autocreateThreshold => "2:10"}
  99. };
  100. }
  101. #############################
  102. sub
  103. CUL_TCM97001_Define($$)
  104. {
  105. my ($hash, $def) = @_;
  106. my @a = split("[ \t][ \t]*", $def);
  107. return "wrong syntax: define <name> CUL_TCM97001 <code>"
  108. if(int(@a) < 3 || int(@a) > 5);
  109. $hash->{CODE} = $a[2];
  110. $hash->{lastT} = 0;
  111. $hash->{lastH} = 0;
  112. $modules{CUL_TCM97001}{defptr}{$a[2]} = $hash;
  113. $hash->{STATE} = "Defined";
  114. return undef;
  115. }
  116. #####################################
  117. sub
  118. CUL_TCM97001_Undef($$)
  119. {
  120. my ($hash, $name) = @_;
  121. delete($modules{CUL_TCM97001}{defptr}{$hash->{CODE}})
  122. if(defined($hash->{CODE}) &&
  123. defined($modules{CUL_TCM97001}{defptr}{$hash->{CODE}}));
  124. return undef;
  125. }
  126. ### inserted by elektron-bbs for rain gauge Ventus W174
  127. # Checksum for Rain Gauge VENTUS W174 Protocol Auriol
  128. # n8 = ( 0x7 + n0 + n1 + n2 + n3 + n4 + n5 + n6 + n7 ) & 0xf
  129. sub checksum_W174 {
  130. my $msg = shift;
  131. Log3 "CUL_TCM97001: ", 4 , "CUL_TCM97001: W174 checksum calc for: $msg";
  132. my @a = split("", $msg);
  133. my $bitReverse = undef;
  134. my $x = undef;
  135. foreach $x (@a) {
  136. my $bin3=sprintf("%04b",hex($x));
  137. $bitReverse = $bitReverse . reverse($bin3);
  138. }
  139. my $hexReverse = unpack("H*", pack ("B*", $bitReverse));
  140. my @aReverse = split("", $hexReverse); # Split reversed a again
  141. my $CRC = (7 + hex($aReverse[0])+hex($aReverse[1])+hex($aReverse[2])+hex($aReverse[3])+hex($aReverse[4])+hex($aReverse[5])+hex($aReverse[6])+hex($aReverse[7])) & 15;
  142. if ($CRC == hex($aReverse[8])) {
  143. Log3 "CUL_TCM97001: ", 4 , "CUL_TCM97001: W174 checksum ok $CRC == ".hex($aReverse[8]);
  144. return TRUE;
  145. } else {
  146. return FALSE;
  147. }
  148. }
  149. #
  150. # CRC Check for TCM 21....
  151. #
  152. sub checkCRC {
  153. my $msg = shift;
  154. my @a = split("", $msg);
  155. my $bitReverse = undef;
  156. my $x = undef;
  157. foreach $x (@a) {
  158. my $bin3=sprintf("%04b",hex($x));
  159. $bitReverse = $bitReverse . reverse($bin3);
  160. }
  161. my $hexReverse = unpack("H*", pack ("B*", $bitReverse));
  162. #Split reversed a again
  163. my @aReverse = split("", $hexReverse);
  164. my $CRC = (hex($aReverse[0])+hex($aReverse[1])+hex($aReverse[2])+hex($aReverse[3])
  165. +hex($aReverse[4])+hex($aReverse[5])+hex($aReverse[6])+hex($aReverse[7])) & 15;
  166. if ($CRC + hex($aReverse[8]) == 15) {
  167. return TRUE;
  168. }
  169. return FALSE;
  170. }
  171. #
  172. # CRC 4 check for PFR-130
  173. # xor 4 bits of nibble 0 to 7
  174. #
  175. sub checkCRC4 {
  176. my $msg = shift;
  177. my @a = split("", $msg);
  178. if(scalar(@a)<9){
  179. Log3 "checkCRC4", 5, "CUL_TCM97001 failed for msg=($msg) length<9";
  180. return FALSE;
  181. }
  182. # xor nibbles 0 to 7 and compare to n8, if more nibble they might have been added to fill gap
  183. my $CRC = ( (hex($a[0])) ^ (hex($a[1])) ^ (hex($a[2])) ^ (hex($a[3])) ^
  184. (hex($a[4])) ^ (hex($a[5])) ^ (hex($a[6])) ^ (hex($a[7])) );
  185. if ($CRC == (hex($a[8]))) {
  186. Log3 "checkCRC4", 5, "CUL_TCM97001 OK for msg=($msg)";
  187. return TRUE;
  188. }
  189. Log3 "checkCRC4", 5, "CUL_TCM97001 FAILED for msg=($msg)";
  190. return FALSE;
  191. }
  192. #
  193. # CRC 4 check for PFR-130
  194. # xor 4 bits of nibble 0 to 7
  195. #
  196. sub isRain {
  197. my $msg = shift;
  198. my @a = split("", $msg);
  199. if(scalar(@a)<9){
  200. Log3 "isRain", 5, "isRain: CUL_TCM97001 failed for msg=($msg) length<9";
  201. return FALSE;
  202. }
  203. # if bit 0 of nibble 2 is 1 then this is no rain data
  204. my $isRainData = ( (hex($a[2]) & 1) );
  205. if ($isRainData == 1) {
  206. Log3 "isRain", 5, "isRain: CUL_TCM97001 for msg=($msg) = FALSE";
  207. return FALSE;
  208. }
  209. Log3 "isRain", 5, "isRain: CUL_TCM97001 for msg=($msg) = TRUE";
  210. return TRUE;
  211. }
  212. #
  213. # CRC Check for KW9010....
  214. #
  215. sub checkCRCKW9010 {
  216. my $msg = shift;
  217. Log3 "CUL_TCM97001", 5 , "crc calc for: $msg";
  218. my @a = split("", $msg);
  219. my $bitReverse = undef;
  220. my $x = undef;
  221. foreach $x (@a) {
  222. my $bin3=sprintf("%04b",hex($x));
  223. $bitReverse = $bitReverse . reverse($bin3);
  224. }
  225. my $hexReverse = unpack("H*", pack ("B*", $bitReverse));
  226. #Split reversed a again
  227. my @aReverse = split("", $hexReverse);
  228. my $CRC = (hex($aReverse[0])+hex($aReverse[1])+hex($aReverse[2])+hex($aReverse[3])
  229. +hex($aReverse[4])+hex($aReverse[5])+hex($aReverse[6])+hex($aReverse[7])) & 15;
  230. Log3 "CUL_TCM97001", 5 , "calc crc is: $CRC";
  231. Log3 "CUL_TCM97001", 5 , "ref crc is :".hex($aReverse[8]);
  232. if ($CRC == hex($aReverse[8])) {
  233. return TRUE;
  234. }
  235. return FALSE;
  236. }
  237. #
  238. # CRC Check for Mebus
  239. #
  240. sub checkCRC_Mebus {
  241. my $msg = shift;
  242. my @a = split("", $msg);
  243. my $CRC = ((hex($a[1])+hex($a[2])+hex($a[3])
  244. +hex($a[4])+hex($a[5])+hex($a[6])) -1) & 15;
  245. my $CRCCHECKVAL= (hex($a[0]));
  246. if ($CRC == $CRCCHECKVAL) {
  247. return TRUE;
  248. }
  249. return FALSE;
  250. }
  251. #
  252. # CRC Check for GT_WT_02
  253. ### edited by elektron-bbs for GT_WT_02
  254. sub checkCRC_GTWT02 {
  255. my $msg = shift;
  256. my @a = split("", $msg);
  257. my $CRC = (hex($a[0])+hex($a[1])+hex($a[2])+hex($a[3])
  258. +hex($a[4])+hex($a[5])+hex($a[6])+(hex($a[7]) & 0xE));
  259. # my $CRC = (hex($a[0])+hex($a[1])+hex($a[2])+hex($a[3])
  260. # +hex($a[4])+hex($a[5])+hex($a[6])+hex($a[7])) -1;
  261. my $CRCCHECKVAL= (hex($a[7].$a[8].$a[9]) & 0x1F8) >> 3;
  262. if ($CRC % 64 == $CRCCHECKVAL) {
  263. return TRUE;
  264. }
  265. return FALSE;
  266. }
  267. #
  268. # CRC Check for Sensor-Type1
  269. #
  270. sub checkCRC_Type1 {
  271. my $msg = shift;
  272. my @a = split("", $msg);
  273. my $CRC = (hex($a[0])+hex($a[1])+hex($a[2])+hex($a[3])
  274. +hex($a[4])+hex($a[5])+hex($a[6])+hex($a[7]));
  275. my $CRCCHECKVAL= (hex($a[7].$a[8].$a[9]) & 0x1F8) >> 3;
  276. if ($CRC == $CRCCHECKVAL) {
  277. return TRUE;
  278. }
  279. return FALSE;
  280. }
  281. sub checkValues {
  282. my $temp = shift;
  283. my $humidy = shift;
  284. if (!defined($humidy)) {
  285. $humidy = 50;
  286. }
  287. if ($temp <= 60 && $temp >= -30
  288. && $humidy >= 0 && $humidy <= 100) {
  289. return TRUE;
  290. }
  291. return FALSE;
  292. }
  293. ###################################
  294. sub
  295. CUL_TCM97001_Parse($$)
  296. {
  297. my $enableLongIDs = TRUE; # Disable short ID support, enable longIDs
  298. my ($hash, $msg) = @_;
  299. $msg = substr($msg, 1);
  300. my @a = split("", $msg);
  301. my $id3 = hex($a[0] . $a[1]);
  302. #my $id4 = hex($a[0] . $a[1] . $a[2] . (hex($a[3]) & 0x3));
  303. my $def = $modules{CUL_TCM97001}{defptr}{$id3}; # test for already defined devices use old naming convention
  304. #my $def2 = $modules{CUL_TCM97001}{defptr}{$idType2};
  305. if(!$def) {
  306. $def = $modules{CUL_TCM97001}{defptr}{'CUL_TCM97001_'.$id3}; # use new naming convention
  307. }
  308. my $now = time();
  309. my $name = "Unknown";
  310. if($def) {
  311. $name = $def->{NAME};
  312. }
  313. my $readedModel = AttrVal($name, "model", "Unknown");
  314. my $syncTimeIndex = rindex($msg,";");
  315. my @syncBit;
  316. if ($syncTimeIndex != -1) {
  317. my $syncTimeMsg = substr($msg, $syncTimeIndex + 1);
  318. @syncBit = split(":", $syncTimeMsg);
  319. $msg = substr($msg, 0, $syncTimeIndex);
  320. } else {
  321. $syncBit[0] = 0;
  322. $syncBit[1] = 4000;
  323. }
  324. my $rssi;
  325. my $l = length($msg);
  326. $rssi = substr($msg, $l-2, 2);
  327. undef($rssi) if ($rssi eq "00");
  328. if (defined($rssi))
  329. {
  330. $rssi = hex($rssi);
  331. $rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74)) if defined($rssi);
  332. Log3 $name, 4, "CUL_TCM97001 $name $id3 ($msg) length: $l RSSI: $rssi";
  333. } else {
  334. Log3 $name, 4, "CUL_TCM97001 $name $id3 ($msg) length: $l";
  335. }
  336. my ($msgtype, $msgtypeH);
  337. my $packageOK = FALSE;
  338. my $batbit=undef;
  339. my $mode=undef;
  340. my $trend=undef;
  341. my $hashumidity = FALSE;
  342. my $hasbatcheck = FALSE;
  343. my $hastrend = FALSE;
  344. my $haschannel = FALSE;
  345. my $hasmode = FALSE;
  346. my $model="Unknown";
  347. my $temp = undef;
  348. my $humidity=undef;
  349. my $channel = undef;
  350. # für zusätzliche Sensoren
  351. my @winddir_name=("N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW");
  352. my $windSpeed = 0;
  353. my $windDirection = 0 ;
  354. my $windDirectionText = "N";
  355. my $windgrad = 0 ;
  356. my $windGuest = 0;
  357. my $rain = 0;
  358. my $haswindspeed = FALSE;
  359. my $haswind = FALSE;
  360. my $hasrain = FALSE;
  361. my $rainticks = undef;
  362. my $rainMM = undef;
  363. my $longids = AttrVal($hash->{NAME},'longids',1);
  364. if (length($msg) == 8) {
  365. # Only tmp TCM device
  366. #eg. 1000 1111 0100 0011 0110 1000 = 21.8C
  367. #eg. --> shift2 0100 0011 0110 10
  368. my $tcm97id = hex($a[0] . $a[1]);
  369. $def = $modules{CUL_TCM97001}{defptr}{$tcm97id};
  370. if($def) {
  371. $name = $def->{NAME};
  372. }
  373. $readedModel = AttrVal($name, "model", "Unknown");
  374. if ($readedModel eq "Unknown" || $readedModel eq "TCM97...") {
  375. $temp = (hex($a[3].$a[4].$a[5]) >> 2) & 0xFFFF;
  376. my $negative = (hex($a[2]) >> 0) & 0x3;
  377. if ($negative == 0x3) {
  378. $temp = (~$temp & 0x03FF) + 1;
  379. $temp = -$temp;
  380. }
  381. $temp = $temp / 10;
  382. if (checkValues($temp, 50)) {
  383. $model="TCM97...";
  384. # I think bit 3 on byte 3 is battery warning
  385. $batbit = (hex($a[2]) >> 0) & 0x4;
  386. $batbit = ~$batbit & 0x1; # Bat bit umdrehen
  387. $mode = (hex($a[5]) >> 0) & 0x1;
  388. my $unknown = (hex($a[4]) >> 0) & 0x2;
  389. if ($mode) {
  390. Log3 $name, 5, "CUL_TCM97001 Mode: manual triggert";
  391. } else {
  392. Log3 $name, 5, "CUL_TCM97001 Mode: auto triggert";
  393. }
  394. if ($unknown) {
  395. Log3 $name, 5, "CUL_TCM97001 Unknown Bit: $unknown";
  396. }
  397. my $deviceCode;
  398. if (!defined($modules{CUL_TCM97001}{defptr}{$tcm97id}))
  399. {
  400. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  401. {
  402. $deviceCode="CUL_TCM97001_".$tcm97id;
  403. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  404. } else {
  405. $deviceCode="CUL_TCM97001_" . $model;
  406. }
  407. } else {
  408. $deviceCode=$tcm97id;
  409. }
  410. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  411. if($def) {
  412. $name = $def->{NAME};
  413. }
  414. if(!$def) {
  415. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  416. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  417. }
  418. $packageOK = TRUE;
  419. $hasbatcheck = TRUE;
  420. $hasmode = TRUE;
  421. $readedModel=$model;
  422. }
  423. }
  424. if ($readedModel eq "Unknown" || $readedModel eq "ABS700") {
  425. $temp = (hex($a[2].$a[3]) & 0x7F)+(hex($a[5])/10);
  426. if ((hex($a[2]) & 0x8) == 0x8) {
  427. $temp = -$temp;
  428. }
  429. if (checkValues($temp, 50)) {
  430. $model="ABS700";
  431. $batbit = ((hex($a[4]) & 0x8) != 0x8);
  432. $mode = (hex($a[4]) & 0x4) >> 2;
  433. my $deviceCode;
  434. if (!defined($modules{CUL_TCM97001}{defptr}{$tcm97id}))
  435. {
  436. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  437. {
  438. $deviceCode="CUL_TCM97001_".$tcm97id;
  439. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  440. } else {
  441. $deviceCode="CUL_TCM97001_" . $model;
  442. }
  443. } else {
  444. $deviceCode=$tcm97id;
  445. }
  446. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  447. if($def) {
  448. $name = $def->{NAME};
  449. }
  450. if(!$def) {
  451. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  452. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  453. }
  454. $hasbatcheck = TRUE;
  455. $packageOK = TRUE;
  456. $hasmode = TRUE;
  457. $readedModel=$model;
  458. }
  459. }
  460. } elsif (length($msg) == 10) {
  461. #Log3 $name, 2, "CUL_TCM97001 10er msg: " . $msg;
  462. my $idType2 = hex($a[1] . $a[2]);
  463. my $deviceCode = $idType2;
  464. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode}; # test for already defined devices use old naming convention
  465. if(!$def) {
  466. $deviceCode = "CUL_TCM97001_" . $idType2; # use new naming convention
  467. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  468. }
  469. if($def) {
  470. $name = $def->{NAME};
  471. }
  472. $readedModel = AttrVal($name, "model", "Unknown");
  473. if (checkCRC_Mebus($msg) == TRUE && ($readedModel eq "Unknown" || $readedModel eq "Mebus")) {
  474. # Protocol mebus start everytime with 1001
  475. # Sync bit 9700ms, bit 0 = 350ms, bit 1 = 2000ms
  476. # e.g. 8250ED70 1000 0010 0101 0000 1110 1101 0111
  477. # A B C D E F G
  478. # A = CRC ((B+C+D+E+F+G)-1)
  479. # B+C = Random Address
  480. # D+E+F temp (/10)
  481. # G Bit 4,3 = Channel, Bit 2 = Battery, Bit 1 = Force sending
  482. $temp = (hex($a[3].$a[4].$a[5])) & 0x3FF;
  483. my $negative = (hex($a[3])) & 0xC;
  484. if ($negative == 0xC) {
  485. $temp = (~$temp & 0x03FF) + 1;
  486. $temp = -$temp;
  487. }
  488. $temp = $temp / 10;
  489. if (checkValues($temp, 50)) {
  490. $batbit = (hex($a[6]) & 0x2) >> 1;
  491. #$batbit = ~$batbit & 0x1; # Bat bit umdrehen
  492. $mode = (hex($a[6]) & 0x1);
  493. $channel = (hex($a[6]) & 0xC) >> 2;
  494. $model="Mebus";
  495. my $deviceCode;
  496. if (!defined($modules{CUL_TCM97001}{defptr}{$idType2}))
  497. {
  498. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  499. {
  500. $deviceCode="CUL_TCM97001_".$idType2;
  501. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  502. } else {
  503. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  504. }
  505. } else { # Fallback for already defined devices use old naming convention
  506. $deviceCode=$idType2;
  507. }
  508. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  509. if($def) {
  510. $name = $def->{NAME};
  511. }
  512. if(!$def) {
  513. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  514. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  515. }
  516. $packageOK = TRUE;
  517. $readedModel=$model;
  518. $hasmode = TRUE;
  519. $hasbatcheck = TRUE;
  520. $haschannel = TRUE;
  521. $id3 = $idType2
  522. } else {
  523. $name = "Unknown";
  524. }
  525. }
  526. if ($packageOK == FALSE) {
  527. my $idType1 = hex($a[0] . $a[1]);
  528. $deviceCode = $idType1;
  529. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode}; # test for already defined devices use old naming convention
  530. #my $def2 = $modules{CUL_TCM97001}{defptr}{$idType2};
  531. #my $def3 = $modules{CUL_TCM97001}{defptr}{$idType3};
  532. if(!$def) {
  533. $deviceCode = "CUL_TCM97001_" . $idType1; # use new naming convention
  534. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  535. }
  536. if($def) {
  537. $name = $def->{NAME};
  538. #} elsif($def2) {
  539. # $def = $def2;
  540. # $name = $def->{NAME};
  541. #} elsif($def3) {
  542. # $def = $def3;
  543. # $name = $def->{NAME};
  544. }
  545. $readedModel = AttrVal($name, "model", "Unknown");
  546. if (($readedModel eq "AURIOL" || $readedModel eq "Unknown")) {
  547. # Implementation from Femduino
  548. # AURIOL (Lidl Version: 09/2013)
  549. # /--------------------------------- Channel, changes after every battery change
  550. # / / ------------------------ Battery state 1 == Ok
  551. # / / /------------------------ Battery changed, Sync startet
  552. # / / / ----------------------- Unknown
  553. # / / / / /--------------------- neg Temp: if 1 then temp = temp - 4096
  554. # / / / / /---------------------- 12 Bit Temperature
  555. # / / / / / /---------- ??? CRC
  556. # / / / / / / /---- Trend 10 == rising, 01 == falling
  557. # 0101 0101 1 0 00 0001 0000 1011 1100 01 00
  558. # Bit 0 8 9 10 12 24 30
  559. $def = $modules{CUL_TCM97001}{defptr}{$idType1};
  560. if($def) {
  561. $name = $def->{NAME};
  562. }
  563. $temp = (hex($a[3].$a[4].$a[5])) & 0x7FF;
  564. my $negative = (hex($a[3])) & 0x8;
  565. if ($negative == 0x8) {
  566. $temp = (~$temp & 0x07FF) + 1;
  567. $temp = -$temp;
  568. }
  569. $temp = $temp / 10;
  570. if (checkValues($temp, 50)) {
  571. $batbit = (hex($a[2]) & 0x8) >> 3;
  572. $batbit = ~$batbit & 0x1; # Bat bit umdrehen
  573. $mode = (hex($a[2]) & 0x4) >> 2;
  574. $trend = (hex($a[7]) & 0x3);
  575. $model="AURIOL";
  576. if ($deviceCode ne $idType1) # new naming convention
  577. {
  578. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  579. {
  580. $deviceCode="CUL_TCM97001_".$idType1;
  581. } else {
  582. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  583. }
  584. }
  585. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  586. if($def) {
  587. $name = $def->{NAME};
  588. }
  589. if(!$def) {
  590. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  591. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  592. }
  593. $hasbatcheck = TRUE;
  594. $hastrend = TRUE;
  595. $packageOK = TRUE;
  596. $hasmode = TRUE;
  597. $readedModel=$model;
  598. } else {
  599. $name = "Unknown";
  600. }
  601. }
  602. }
  603. } elsif (length($msg) == 12) {
  604. my $bin = undef;
  605. my $deviceCode;
  606. my $idType1 = hex($a[0] . $a[1]);
  607. #my $idType2 = hex($a[1] . $a[2]);
  608. #my $idType3 = hex($a[0] . $a[1] . $a[2] . (hex($a[3]) & 0x3));
  609. $deviceCode = $idType1;
  610. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode}; # test for already defined devices use old naming convention
  611. #my $def2 = $modules{CUL_TCM97001}{defptr}{$idType2};
  612. #my $def3 = $modules{CUL_TCM97001}{defptr}{$idType3};
  613. if(!$def) {
  614. $deviceCode = "CUL_TCM97001_" . $idType1; # use new naming convention
  615. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  616. }
  617. if($def) {
  618. $name = $def->{NAME};
  619. #} elsif($def2) {
  620. # $def = $def2;
  621. # $name = $def->{NAME};
  622. #} elsif($def3) {
  623. # $def = $def3;
  624. # $name = $def->{NAME};
  625. }
  626. $readedModel = AttrVal($name, "model", "Unknown");
  627. Log3 $name, 4, "CUL_TCM97001 Parse Name: $name , devicecode: $deviceCode , Model defined: $readedModel";
  628. if (($readedModel eq "Eurochron" || (hex($a[6]) == 0xF && $readedModel eq "Unknown") && $syncBit[1] < 5000)) {
  629. # EAS 800
  630. # G is every time 1111
  631. #
  632. # 0100 1110 1001 0000 1010 0001 1111 0100 1001
  633. # A B C D E F G H I
  634. #
  635. # A+B = ID = 4E
  636. # C Bit 0 = Bat (1) OK
  637. # C Bit 1-3 = Channel 001 = 1
  638. # D-F = Temp (0000 1010 0001) = 161 ~ 16,1°
  639. # G = Unknown
  640. # H+I = hum (0100 1001) = 73
  641. $def = $modules{CUL_TCM97001}{defptr}{$idType1};
  642. if($def) {
  643. $name = $def->{NAME};
  644. } else {
  645. # Redirect to
  646. my $SD_WS07_ClientMatch=index($hash->{Clients},"SD_WS07");
  647. if ($SD_WS07_ClientMatch == -1) {
  648. # Append Clients and MatchList for CUL
  649. $hash->{Clients} = $hash->{Clients}.":SD_WS07:";
  650. $hash->{MatchList}{"C:SD_WS07"} = "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}";
  651. }
  652. my $dmsg = "P7#" . substr($msg, 0, $l-2, 2);
  653. $hash->{RAWMSG} = $msg;
  654. my %addvals = (RAWMSG => $msg, DMSG => $dmsg);
  655. Log3 $name, 5, "CUL_TCM97001 Dispatch $dmsg to other modul";
  656. Dispatch($hash, $dmsg, \%addvals); ## Dispatch to other Modules
  657. return "";
  658. }
  659. $temp = (hex($a[3].$a[4].$a[5])) & 0x7FF;
  660. my $negative = (hex($a[3])) & 0x8;
  661. if ($negative == 0x8) {
  662. $temp = (~$temp & 0x07FF) + 1;
  663. $temp = -$temp;
  664. }
  665. $temp = $temp / 10;
  666. $humidity = hex($a[7].$a[8]) & 0x7F;
  667. if (checkValues($temp, $humidity)) {
  668. $batbit = (hex($a[2]) & 0x8) >> 3;
  669. #$batbit = ~$batbit & 0x1; # Bat bit umdrehen
  670. $mode = (hex($a[2]) & 0x4) >> 2;
  671. $channel = ((hex($a[2])) & 0x3) + 1;
  672. $model="Eurochron";
  673. if ($deviceCode ne $idType1) # new naming convention
  674. {
  675. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  676. {
  677. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  678. } else {
  679. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  680. }
  681. }
  682. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  683. if($def) {
  684. $name = $def->{NAME};
  685. }
  686. if(!$def) {
  687. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  688. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  689. }
  690. if (defined($humidity)) {
  691. if ($humidity >= 20) {
  692. $hashumidity = TRUE;
  693. }
  694. }
  695. $hasbatcheck = TRUE;
  696. $haschannel = TRUE;
  697. $packageOK = TRUE;
  698. $hasmode = TRUE;
  699. $readedModel=$model;
  700. } else {
  701. $name = "Unknown";
  702. }
  703. }
  704. ### inserted by elektron-bbs for rain gauge Ventus W174
  705. if (checksum_W174($msg) == TRUE && ($readedModel eq "Unknown" || $readedModel eq "W174")) {
  706. # VENTUS W174 Rain gauge
  707. # Documentation also at http://www.tfd.hu/tfdhu/files/wsprotocol/auriol_protocol_v20.pdf
  708. # send interval 36 seconds
  709. # * Format for Rain
  710. # * AAAAAAAA vXXB CCCC DDDD DDDD DDDD DDDD EEEE FFFF FFFF
  711. # * RC Type Rain Checksum
  712. # * A = Rolling Code /Device ID
  713. # * B = Message type (xyyx = NON temp/humidity data if yy = '11')
  714. # * v = 0: Sensor's battery voltage is normal, 1: Battery voltage is below ~2.6 V.
  715. # * XX = 11: Non temperature/humidity data. All other type data packets have this value in this field.
  716. # * C = fixed to 1100 for rain gauge
  717. # * D = Rain (bitvalue * 0.25 mm)
  718. # * E = Checksum
  719. # * F = 0000 0000 (W174!!!)
  720. my @a = split("", $msg);
  721. my $bitReverse = undef;
  722. my $bitUnreverse = undef;
  723. my $x = undef;
  724. my $bin3;
  725. foreach $x (@a) {
  726. $bin3=sprintf("%024b",hex($x));
  727. $bitReverse = $bitReverse . substr(reverse($bin3),0,4);
  728. $bitUnreverse = $bitUnreverse . sprintf( "%b", hex( substr($bin3,0,4) ) );
  729. }
  730. my $hexReverse = unpack("H*", pack ("B*", $bitReverse));
  731. my @aReverse = split("", $hexReverse); # Split reversed a again
  732. Log3 $hash,5, "CUL_TCM97001: $name original-msg: $msg , reversed nibbles: $hexReverse";
  733. Log3 $hash,5, "CUL_TCM97001: $name nibble 2: $aReverse[2] , nibble 3: $aReverse[3]";
  734. # Nibble 2 must be x110 for rain gauge
  735. # Nibble 3 must be 0x03 for rain gauge
  736. if ((hex($aReverse[2]) >> 1) == 3 && $aReverse[3] == 0x03) {
  737. Log3 $hash,4, "CUL_TCM97001: $name detected rain gauge message ok";
  738. $batbit = $aReverse[2] & 0b0001; # Bat bit normal=0, low=1
  739. Log3 $hash,4, "CUL_TCM97001: $name battery bit: $batbit";
  740. $batbit = ~$batbit & 0x1; # Bat bit negation
  741. $hasbatcheck = TRUE;
  742. my $rainticks = hex($aReverse[4]) + hex($aReverse[5]) * 16 + hex($aReverse[6]) * 256 + hex($aReverse[7]) * 4096;
  743. Log3 $hash,5, "CUL_TCM97001: $name rain gauge swing count: $rainticks";
  744. $rain = ($rainticks + ($rainticks & 1)) / 4; # 1 tick = 0,5 l/qm
  745. $model="W174";
  746. $hasrain = TRUE;
  747. # Sanity check
  748. if($def) {
  749. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  750. my $hash = $def;
  751. my $timeSinceLastUpdate = ReadingsAge($hash->{NAME}, "state", 0);
  752. $name = $def->{NAME};
  753. if (defined($hash->{READINGS}{rain}{VAL})) {
  754. my $diffRain = 0;
  755. my $oldRain = $hash->{READINGS}{rain}{VAL};
  756. if ($rain > $oldRain) {
  757. $diffRain = ($rain - $oldRain);
  758. } else {
  759. $diffRain = ($oldRain - $rain);
  760. }
  761. $diffRain = sprintf("%.1f", $diffRain);
  762. Log3 $hash, 4, "CUL_TCM97001: $name old rain $oldRain, age $timeSinceLastUpdate, new rain $rain, diff rain $diffRain";
  763. my $maxDiffRain = $timeSinceLastUpdate / 60 + 1; # 1.0 Liter/Minute + 1.0
  764. $maxDiffRain = sprintf("%.1f", $maxDiffRain + 0.05); # round 0.1
  765. Log3 $hash, 4, "CUL_TCM97001: $name max difference rain $maxDiffRain l";
  766. if ($diffRain > $maxDiffRain) {
  767. Log3 $hash, 3, "CUL_TCM97001: $name ERROR - Rain diff too large (old $oldRain, new $rain, diff $diffRain)";
  768. return "";
  769. }
  770. }
  771. } else {
  772. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  773. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  774. }
  775. Log3 $hash,4, "CUL_TCM97001: $name rain total: $rain l/qm";
  776. $readedModel=$model;
  777. $packageOK = TRUE;
  778. }
  779. }
  780. if (checkCRC($msg) == TRUE && ($readedModel eq "Unknown" || $readedModel eq "TCM21....")) {
  781. # Long with tmp
  782. # All nibbles must be reversed
  783. # e.g. 154E800480 0001 0101 0100 1110 1000 0000 0000 0100 1000 0000
  784. # A B C D E F G H I
  785. # A+B = Addess
  786. # C Bit 1 Battery
  787. # D+E+F Temp
  788. # G+H Hum
  789. # I CRC
  790. #/* Documentation also at http://www.tfd.hu/tfdhu/files/wsprotocol/auriol_protocol_v20.pdf
  791. # * Message Format: (9 nibbles, 36 bits):
  792. # * Please note that bytes need to be reversed before processing!
  793. # *
  794. # * Format for Temperature Humidity
  795. # * AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
  796. # * RC Type Temperature___ Humidity Checksum
  797. # * A = Rolling Code / Device ID
  798. # * Device ID: AAAABBAA BB is used for channel, base channel is 01
  799. # * When channel selector is used, channel can be 10 (2) and 11 (3)
  800. # * B = Message type (xyyz = temp/humidity if yy <> '11') else wind/rain sensor
  801. # * x indicates battery status (0 normal, 1 voltage is below ~2.6 V)
  802. # * z 0 indicates regular transmission, 1 indicates requested by pushbutton
  803. # * C = Temperature (two's complement)
  804. # * D = Humidity BCD format
  805. # * E = Checksum
  806. # *
  807. # * Format for Rain
  808. # * AAAAAAAA BBBB CCCC DDDD DDDD DDDD DDDD EEEE
  809. # * RC Type Rain Checksum
  810. # * A = Rolling Code /Device ID
  811. # * B = Message type (xyyx = NON temp/humidity data if yy = '11')
  812. # * C = fixed to 1100 (C)
  813. # * D = Rain (bitvalue * 0.25 mm)
  814. # * E = Checksum
  815. # *
  816. # * Format for Windspeed
  817. # * AAAAAAAA BBBB CCCC CCCC CCCC DDDDDDDD EEEE
  818. # * RC Type Windspd Checksum
  819. # * A = Rolling Code
  820. # * B = Message type (xyyx = NON temp/humidity data if yy = '11')
  821. # * C = Fixed to 1000 0000 0000 (8)
  822. # * D = Windspeed (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
  823. # * E = Checksum
  824. # *
  825. # * Format for Winddirection & Windgust
  826. # * AAAAAAAA BBBB CCCD DDDD DDDD EEEEEEEE FFFF
  827. # * RC Type Winddir Windgust Checksum
  828. # * A = Rolling Code
  829. # * B = Message type (xyyx = NON temp/humidity data if yy = '11')
  830. # * C = Fixed to 111 (E)
  831. # * D = Wind direction
  832. # * E = Windgust (bitvalue * 0.2 m/s, correction for webapp = 3600/1000 * 0.2 * 100 = 72)
  833. # * F = Checksum
  834. # *********************************************************************************************
  835. # */
  836. my @a = split("", $msg);
  837. my $bitReverse = undef;
  838. my $bitUnreverse = undef;
  839. my $x = undef;
  840. my $bin3;
  841. foreach $x (@a) {
  842. $bin3=sprintf("%024b",hex($x));
  843. $bitReverse = $bitReverse . substr(reverse($bin3),0,4);
  844. $bitUnreverse = $bitUnreverse . sprintf( "%b", hex( substr($bin3,0,4) ) );
  845. }
  846. my $hexReverse = unpack("H*", pack ("B*", $bitReverse));
  847. #Split reversed a again
  848. my @aReverse = split("", $hexReverse);
  849. Log3 $hash,4, "CUL_TCM97001 hex:$hexReverse msg:$msg aR:@aReverse ";
  850. # Message type (xyyz = temp/humidity if yy <> '11') else wind/rain sensor
  851. # Message type (xyyx = NON temp/humidity data if yy = '11')
  852. $msgtype = substr(sprintf( "%04b", hex( substr( $msg,2,1))),1,2);
  853. Log3 $hash,4, "CUL_TCM97001 nib2:$msgtype aRev: $aReverse[2]";
  854. $msgtype = substr(sprintf( "%04b", hex( $aReverse[2])),1,2);
  855. Log3 $hash,4, "CUL_TCM97001 nib2R:$msgtype hexR: $hexReverse";
  856. if ( $msgtype ne "11") {
  857. Log3 $hash,4, "CUL_TCM97001 Temp/Hum Msgype: $msgtype nib3:$aReverse[3] ";
  858. if (hex($aReverse[5]) > 3) {
  859. # negative temp
  860. $temp = ((hex($aReverse[3]) + hex($aReverse[4]) * 16 + hex($aReverse[5]) * 256));
  861. $temp = (~$temp & 0x03FF) + 1;
  862. $temp = -$temp/10;
  863. } else {
  864. # positive temp
  865. $temp = (hex($aReverse[3]) + hex($aReverse[4]) * 16 + hex($aReverse[5]) * 256)/10;
  866. }
  867. $humidity = hex($aReverse[7]).hex($aReverse[6]);
  868. $hashumidity = TRUE;
  869. } else {
  870. # Wind/Rain/Guest
  871. Log3 $hash,4, "CUL_TCM97001 Wind/Rain/Guest Msgype: $msgtype nib3:$aReverse[3] ";
  872. # C = Fixed to 1000 0000 0000 Reverse 0001 0000 0000
  873. if ((hex($aReverse[3])== 0x1) && (hex($aReverse[4])== 0x0)) { # Windspeed
  874. $windSpeed = hex($aReverse[6]) + hex($aReverse[7]);
  875. $haswindspeed = TRUE;
  876. Log3 $hash,4, "CUL_TCM97001 windSpeed: $windSpeed ";
  877. }
  878. if ((hex($aReverse[3])== 0xF)) { # Windguest Reverse
  879. $windGuest = hex($aReverse[6]) + hex($aReverse[7]);
  880. $windDirection = hex($aReverse[4]) + hex($aReverse[5]) ;
  881. $windDirectionText = $winddir_name[$windDirection];
  882. $haswind = TRUE;
  883. Log3 $hash,4, "CUL_TCM97001 windGuest: $windGuest ";
  884. }
  885. if ((hex($aReverse[3])== 0x3)) { # Rain
  886. $rain = (hex($aReverse[4]) + hex($aReverse[5]) + hex($aReverse[6]) + hex($aReverse[7])) * 0.25;
  887. $hasrain = TRUE;
  888. Log3 $hash,4, "CUL_TCM97001 rain: $rain ";
  889. }
  890. }
  891. #if (checkValues($temp, $humidity)) {
  892. if (1) {
  893. $batbit = (hex($a[2]) & 0x8) >> 3;
  894. #$mode = (hex($a[2]) & 0x4) >> 2;
  895. $model="TCM21....";
  896. if ($deviceCode ne $idType1) # new naming convention
  897. {
  898. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  899. {
  900. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  901. } else {
  902. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  903. }
  904. }
  905. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  906. if($def) {
  907. $name = $def->{NAME};
  908. }
  909. if(!$def) {
  910. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  911. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  912. }
  913. $packageOK = TRUE;
  914. $hasbatcheck = TRUE;
  915. $readedModel=$model;
  916. } else {
  917. $name = "Unknown";
  918. }
  919. }
  920. if (checkCRC_GTWT02($msg) == TRUE && ($readedModel eq "GT_WT_02" || $readedModel eq "Type1" || $readedModel eq "Unknown")
  921. || checkCRC_Type1($msg) == TRUE && ($readedModel eq "Type1" || $readedModel eq "GT_WT_02" || $readedModel eq "Unknown")) {
  922. ### edited by elektron-bbs for Checksum
  923. #http://www.ludwich.de/ludwich/Temperatur.html
  924. #https://github.com/merbanan/rtl_433/issues/117
  925. # F F 0 0 F 9 5 5 F
  926. # 1111 1111 0000 0000 1111 1001 0101 0101 1111
  927. # A B C D E F G H I
  928. # A+B = Zufällige Code wechelt beim Batteriewechsel
  929. # C Bit 4 Battery, 3 Manual, 2+1 Channel
  930. # D+E+F Temperatur, wenn es negativ wird muss man negieren und dann 1 addieren, wie im ersten Post beschrieben.
  931. # G+H Hum - bit 0-7
  932. # I CRC
  933. #$def = $modules{CUL_TCM97001}{defptr}{$idType3};
  934. $temp = (hex($a[3].$a[4].$a[5])) & 0x3FF;
  935. my $negative = (hex($a[3])) & 0xC;
  936. if ($negative == 0xC) {
  937. $temp = (~$temp & 0x03FF) + 1;
  938. $temp = -$temp;
  939. }
  940. $temp = $temp / 10;
  941. $humidity = (hex($a[6].$a[7]) & 0x0FE) >> 1; # only the first 7 bits are the humidity
  942. if ($humidity > 100) {
  943. # HH - Workaround
  944. $humidity = 100;
  945. } elsif ($humidity < 20) {
  946. # LL - Workaround
  947. $humidity = 20;
  948. }
  949. if (checkValues($temp, $humidity)) {
  950. $channel = ((hex($a[2])) & 0x3) + 1;
  951. $batbit = ((hex($a[2]) & 0x8) != 0x8);
  952. $mode = (hex($a[2]) & 0x4) >> 2;
  953. if (checkCRC_GTWT02($msg) == TRUE) {
  954. $model="GT_WT_02";
  955. } else {
  956. $model="Type1";
  957. }
  958. if ($deviceCode ne $idType1) # new naming convention
  959. {
  960. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  961. {
  962. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  963. } else {
  964. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  965. }
  966. }
  967. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  968. if($def) {
  969. $name = $def->{NAME};
  970. }
  971. if(!$def) {
  972. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  973. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  974. }
  975. $hashumidity = TRUE;
  976. $hasbatcheck = TRUE;
  977. $haschannel = TRUE;
  978. $hasmode = TRUE;
  979. $packageOK = TRUE;
  980. $readedModel=$model;
  981. } else {
  982. $name = "Unknown";
  983. }
  984. }
  985. #Log3 $name, 4, "CUL_TCM97001: CRC for TCM21.... Failed, checking other protocolls";
  986. # Check for Prologue
  987. if ($readedModel eq "Prologue" || (hex($a[0]) == 0x9 && $readedModel eq "Unknown")) {
  988. # Protocol prologue start everytime with 1001
  989. # e.g. 91080F614C 1001 0001 0000 1000 0000 1111 0110 0001 0100 1100
  990. # A B C D E F G H I
  991. # A = Startbit 1001
  992. # B+C = Random Address
  993. # D Bit 4 Battery, 3 Manual, 2+1 Channel
  994. # E+F+G Bit 15+16 negativ temp, 14-0 temp
  995. # H+I Hum
  996. #$def = $modules{CUL_TCM97001}{defptr}{$idType3};
  997. #$def = $modules{CUL_TCM97001}{defptr}{$idType1};
  998. $temp = (hex($a[4].$a[5].$a[6])) & 0x3FFF;
  999. my $negative = (hex($a[4])) & 0xC;
  1000. if ($negative == 0xC) {
  1001. $temp = (~$temp & 0x03FF) + 1;
  1002. $temp = -$temp;
  1003. }
  1004. $temp = $temp / 10;
  1005. if (!(hex($a[7]) == 0xC && hex($a[8]) == 0xC)) {
  1006. $humidity = hex($a[7].$a[8]);
  1007. }
  1008. if (checkValues($temp, $humidity)) {
  1009. $channel = (hex($a[3])) & 0x3;
  1010. $batbit = (hex($a[3]) & 0x8) >> 3;
  1011. $batbit = ~$batbit & 0x1; # Bat bit umdrehen
  1012. $mode = (hex($a[3]) & 0x4) >> 2;
  1013. $model="Prologue";
  1014. if ($deviceCode ne $idType1) # new naming convention
  1015. {
  1016. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  1017. {
  1018. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  1019. } else {
  1020. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  1021. }
  1022. }
  1023. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  1024. if($def) {
  1025. $name = $def->{NAME};
  1026. }
  1027. if(!$def) {
  1028. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  1029. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  1030. }
  1031. if (defined($humidity)) {
  1032. if ($humidity >= 20) {
  1033. $hashumidity = TRUE;
  1034. }
  1035. }
  1036. $hasbatcheck = TRUE;
  1037. $hasmode = TRUE;
  1038. $packageOK = TRUE;
  1039. $haschannel = TRUE;
  1040. $readedModel=$model;
  1041. } else {
  1042. $name = "Unknown";
  1043. }
  1044. }
  1045. if ($readedModel eq "NC_WS" || (hex($a[0]) == 0x5 && $readedModel eq "Unknown")) {
  1046. # Implementation from Femduino
  1047. # PEARL NC7159, LogiLink WS0002
  1048. # /--------------------------------- Sensdortype
  1049. # / / ---------------------------- ID, changes after every battery change
  1050. # / / /--------------------- Battery state 0 == Ok
  1051. # / / / / ------------------ forced send
  1052. # / / / / / ---------------- Channel (0..2)
  1053. # / / / / / / -------------- neg Temp: if 1 then temp = temp - 2048
  1054. # / / / / / / / ----------- Temp
  1055. # / / / / / / / /-- unknown
  1056. # / / / / / / / / / Humidity
  1057. # 0101 0010 1001 0 0 00 0 010 0011 0000 1 101 1101
  1058. # Bit 0 4 12 13 14 16 17 28 29 36
  1059. #$def = $modules{CUL_TCM97001}{defptr}{$idType3};
  1060. $temp = (hex($a[4].$a[5].$a[6])) & 0x7FFF;
  1061. my $negative = (hex($a[4])) & 0x8;
  1062. if ($negative == 0x8) {
  1063. $temp = (~$temp & 0x07FF) + 1;
  1064. $temp = -$temp;
  1065. }
  1066. $temp = $temp / 10;
  1067. $humidity = hex($a[7].$a[8]) & 0x7F;
  1068. if (checkValues($temp, $humidity)) {
  1069. $model="NC_WS";
  1070. $channel = (hex($a[3])) & 0x3;
  1071. $batbit = (hex($a[3]) & 0x8) >> 3;
  1072. $batbit = ~$batbit & 0x1; # Bat bit umdrehen
  1073. $mode = (hex($a[3]) & 0x4) >> 2;
  1074. if ($deviceCode ne $idType1) # new naming convention
  1075. {
  1076. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  1077. {
  1078. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  1079. } else {
  1080. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  1081. }
  1082. }
  1083. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  1084. if($def) {
  1085. $name = $def->{NAME};
  1086. }
  1087. if(!$def) {
  1088. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  1089. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  1090. }
  1091. $hashumidity = TRUE;
  1092. $hasbatcheck = TRUE;
  1093. $hasmode = TRUE;
  1094. $packageOK = TRUE;
  1095. $haschannel = TRUE;
  1096. $readedModel=$model;
  1097. } else {
  1098. $name = "Unknown";
  1099. }
  1100. }
  1101. if ($readedModel eq "Rubicson" || (hex($a[2]) == 0x8 && $readedModel eq "Unknown")) {
  1102. # Protocol Rubicson has as nibble C every time 1000
  1103. # e.g. F4806B8E14 1111 0100 1000 0000 0110 1011 1000 1110 0001 0100
  1104. # A B C D E F G H I
  1105. # A+B = Random Address
  1106. # C = Rubicson = 1000
  1107. # D+E+F 12 bit temp
  1108. # G+H+I Unknown
  1109. $def = $modules{CUL_TCM97001}{defptr}{$idType1};
  1110. if($def) {
  1111. $name = $def->{NAME};
  1112. }
  1113. $temp = (hex($a[3].$a[4].$a[5])) & 0x3FF;
  1114. my $negative = (hex($a[3])) & 0xC;
  1115. if ($negative == 0xC) {
  1116. $temp = (~$temp & 0x03FF) + 1;
  1117. $temp = -$temp;
  1118. }
  1119. $temp = $temp / 10;
  1120. if (checkValues($temp, 50)) {
  1121. $model="Rubicson";
  1122. if ($deviceCode ne $idType1) # new naming convention
  1123. {
  1124. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  1125. {
  1126. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  1127. } else {
  1128. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  1129. }
  1130. }
  1131. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  1132. if($def) {
  1133. $name = $def->{NAME};
  1134. }
  1135. if(!$def) {
  1136. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  1137. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  1138. }
  1139. $packageOK = TRUE;
  1140. $readedModel=$model;
  1141. } else {
  1142. $name = "Unknown";
  1143. }
  1144. }
  1145. if ( (checkCRC4($msg) == TRUE) && (isRain($msg)==TRUE) &&($readedModel eq "PFR_130" || $readedModel eq "Unknown")) {
  1146. # Implementation from Femduino
  1147. # Pollin PFR_130)
  1148. # nibbles n2, n6 and n7 hold the Rain fall ticks
  1149. # /--------------------------------- Channel, changes after every battery change
  1150. # / / ------------------------ Battery state 0 == Ok
  1151. # / / /------------------------ ??Battery changed, Sync startet
  1152. # / / / ----------------------- n2 lower two bits ->rain ticks
  1153. # / / / / /------------------- neg Temp: if 1 then temp = temp - 4096
  1154. # / / / / /-------------------- 12 Bit Temperature
  1155. # / / / / / /----- n6,n7 rain ticks 8 bit (n2 & 0x03 n6 n7)
  1156. # / / / / / / /---- n8, CRC (xor n0 to n7)
  1157. # 0101 0101 1 0 00 0001 0000 1011 11000100 xxxx
  1158. # Bit 0 8 9 10 12 24 32
  1159. $def = $modules{CUL_TCM97001}{defptr}{$idType1};
  1160. if($def) {
  1161. $name = $def->{NAME};
  1162. }
  1163. $temp = (hex($a[3].$a[4].$a[5])) & 0x7FF;
  1164. my $negative = (hex($a[3])) & 0x8;
  1165. if ($negative == 0x8) {
  1166. $temp = (~$temp & 0x07FF) + 1;
  1167. $temp = -$temp;
  1168. }
  1169. $temp = $temp / 10;
  1170. Log3 $name, 5, "CUL_TCM97001: PFR_130 Temp=$temp";
  1171. # rain values Pollin PFR_130
  1172. $rainticks = (hex($a[2].$a[6].$a[7])) & 0x3FF; #mask n2 n6 n7 for rain ticks
  1173. Log3 $name, 5, "CUL_TCM97001: PFR_130 rainticks=$rainticks";
  1174. $rainMM = $rainticks / 25 * .5; # rain height in mm/qm, verified against sensor receiver display
  1175. Log3 $name, 5, "CUL_TCM97001: PFR_130 rain mm=$rainMM";
  1176. if (checkValues($temp, 50)) {
  1177. $batbit = (hex($a[2]) & 0x8) >> 3; # in auriol_protocol_v20.pdf bat bit is n2 & 0x08, same
  1178. $batbit = ~$batbit & 0x1; # Bat bit umdrehen
  1179. $mode = (hex($a[2]) & 0x4) >> 2; # in auriol_protocol_v20.pdf mode is: n2 & 0x01, different
  1180. $trend = (hex($a[7]) & 0x3); # in auriol_protocol_v20.pdf there is no trend bit
  1181. $model="PFR_130";
  1182. my $deviceCode;
  1183. if (!defined($modules{CUL_TCM97001}{defptr}{$idType1}))
  1184. {
  1185. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  1186. {
  1187. $deviceCode="CUL_TCM97001_".$idType1;
  1188. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  1189. } else {
  1190. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  1191. }
  1192. } else { # Fallback for already defined devices use old naming convention
  1193. $deviceCode=$idType1;
  1194. }
  1195. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  1196. if($def) {
  1197. $name = $def->{NAME};
  1198. }
  1199. if(!$def) {
  1200. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  1201. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  1202. }
  1203. $hasbatcheck = TRUE;
  1204. $hastrend = TRUE;
  1205. $packageOK = TRUE;
  1206. $hasmode = TRUE;
  1207. $readedModel=$model;
  1208. } else {
  1209. $name = "Unknown";
  1210. }
  1211. }
  1212. if ( (isRain($msg)!=TRUE) && ($readedModel eq "AURIOL" || $readedModel eq "Unknown") && ($readedModel ne "PFR_130")) {
  1213. # Implementation from Femduino
  1214. # AURIOL (Lidl Version: 09/2013)
  1215. # /--------------------------------- Channel, changes after every battery change
  1216. # / / ------------------------ Battery state 1 == Ok
  1217. # / / /------------------------ Battery changed, Sync startet
  1218. # / / / ----------------------- Unknown
  1219. # / / / / /--------------------- neg Temp: if 1 then temp = temp - 4096
  1220. # / / / / /---------------------- 12 Bit Temperature
  1221. # / / / / / /---------- ??? CRC
  1222. # / / / / / / /---- Trend 10 == rising, 01 == falling
  1223. # 0101 0101 1 0 00 0001 0000 1011 1100 01 00
  1224. # Bit 0 8 9 10 12 24 30
  1225. $def = $modules{CUL_TCM97001}{defptr}{$idType1};
  1226. if($def) {
  1227. $name = $def->{NAME};
  1228. }
  1229. $temp = (hex($a[3].$a[4].$a[5])) & 0x7FF;
  1230. my $negative = (hex($a[3])) & 0x8;
  1231. if ($negative == 0x8) {
  1232. $temp = (~$temp & 0x07FF) + 1;
  1233. $temp = -$temp;
  1234. }
  1235. $temp = $temp / 10;
  1236. if (checkValues($temp, 50)) {
  1237. $batbit = (hex($a[2]) & 0x8) >> 3;
  1238. $batbit = ~$batbit & 0x1; # Bat bit umdrehen
  1239. $mode = (hex($a[2]) & 0x4) >> 2;
  1240. $trend = (hex($a[7]) & 0x3);
  1241. $model="AURIOL";
  1242. my $deviceCode;
  1243. if (!defined($modules{CUL_TCM97001}{defptr}{$idType1}))
  1244. {
  1245. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  1246. {
  1247. $deviceCode="CUL_TCM97001_".$idType1;
  1248. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  1249. } else {
  1250. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  1251. }
  1252. } else { # Fallback for already defined devices use old naming convention
  1253. $deviceCode=$idType1;
  1254. }
  1255. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  1256. if($def) {
  1257. $name = $def->{NAME};
  1258. }
  1259. if(!$def) {
  1260. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  1261. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  1262. }
  1263. $hasbatcheck = TRUE;
  1264. $hastrend = TRUE;
  1265. $packageOK = TRUE;
  1266. $hasmode = TRUE;
  1267. $readedModel=$model;
  1268. } else {
  1269. $name = "Unknown";
  1270. }
  1271. }
  1272. #if (($readedModel eq "Unknown" || $readedModel eq "KW9010")) {
  1273. if (checkCRCKW9010($msg) == TRUE && ($readedModel eq "Unknown" || $readedModel eq "KW9010")) {
  1274. # Re: Tchibo Wetterstation 433 MHz - KW9010
  1275. # See also http://forum.arduino.cc/index.php?PHPSESSID=ffoeoe9qeuv7rf4fh0d637hd74&topic=136836.msg1536416#msg1536416
  1276. # /------------------------------------- Random ID part one
  1277. # / / -------------------------------- Channel switch
  1278. # / / /------------------------------- Random ID part two
  1279. # / / / / ---------------------------- Battery state 0 == Ok
  1280. # / / / / / --------------------------- Trend (continous, rising, falling
  1281. # / / / / / / ------------------------- forced send
  1282. # / / / / / / / ----------------------- Temperature
  1283. # / / / / / / / /-------------- Temperature sign bit. if 1 then temp = temp - 4096
  1284. # / / / / / / / / /------------ Humidity
  1285. # / / / / / / / / / /----- Checksum
  1286. # 0110 00 10 1 00 1 000000100011 00001101 1101
  1287. # 0110 01 00 0 10 1 100110001001 00001011 0101
  1288. # Bit 0 4 6 8 9 11 12 24 32
  1289. #
  1290. #5922B07BC0 42 21.2 66
  1291. # 0101 10 01 0 01 0 001010110000 01111011 1100 0000
  1292. # 000011010100 11011110
  1293. # 212 222-156=66
  1294. my @a = split("", $msg);
  1295. my $bitReverse = undef;
  1296. my $x = undef;
  1297. foreach $x (@a) {
  1298. $bitReverse = $bitReverse . reverse(sprintf("%04b",hex($x)));
  1299. }
  1300. Log3 $hash, 5 , "KW9010 CRC Matched: ($bitReverse)";
  1301. my $hexReverse = unpack("H*", pack ("B*", $bitReverse));
  1302. Log3 $hash, 5 , "KW9010 CRC Hex Matched: $hexReverse";
  1303. #Split reversed a again
  1304. my @aReverse = split("", $hexReverse);
  1305. if (hex($aReverse[5]) > 3) {
  1306. # negative temp
  1307. $temp = ((hex($aReverse[3]) + hex($aReverse[4]) * 16 + hex($aReverse[5]) * 256));
  1308. $temp = (~$temp & 0x03FF) + 1;
  1309. $temp = -$temp/10;
  1310. } else {
  1311. # positive temp
  1312. $temp = (hex($aReverse[3]) + hex($aReverse[4]) * 16 + hex($aReverse[5]) * 256)/10;
  1313. }
  1314. $humidity = hex($aReverse[7].$aReverse[6]) - 156;
  1315. #if (checkValues($temp, $humidity)) {
  1316. if (1) {
  1317. Log3 $hash, 5 , "KW9010 values are matching";
  1318. $batbit = (hex($a[2]) & 0x8) >> 3;
  1319. #$mode = (hex($a[2]) & 0x4) >> 2;
  1320. $channel = ((hex($a[1])) & 0xC) >> 2;
  1321. $mode = (hex($a[2]) & 0x1);
  1322. $trend = (hex($a[2]) & 0x6) >> 1;
  1323. $model="KW9010";
  1324. if ($deviceCode ne $idType1) # new naming convention
  1325. {
  1326. if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
  1327. {
  1328. Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
  1329. } else {
  1330. $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
  1331. }
  1332. }
  1333. $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
  1334. if($def) {
  1335. $name = $def->{NAME};
  1336. }
  1337. if(!$def) {
  1338. Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
  1339. return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode";
  1340. }
  1341. $hashumidity = TRUE;
  1342. $packageOK = TRUE;
  1343. $hasbatcheck = TRUE;
  1344. $hastrend = TRUE;
  1345. $haschannel = TRUE;
  1346. $readedModel=$model;
  1347. } else {
  1348. Log3 $hash, 5 , "KW9010 t:$temp / h:$humidity mismatch";
  1349. $name = "Unknown";
  1350. }
  1351. }
  1352. }
  1353. if ($packageOK == TRUE) {
  1354. # save lastT, calc rainMM sum for day and hour
  1355. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  1356. my $lastDay=$mday;
  1357. my $lastHour=$hour;
  1358. my $rainSumDay=0;
  1359. my $rainSumHour=0;
  1360. if($def) {
  1361. $def->{lastT} = $now;
  1362. }
  1363. readingsBeginUpdate($def);
  1364. my ($val, $valH, $state);
  1365. if (defined($temp)) {
  1366. $msgtype = "temperature";
  1367. $val = sprintf("%2.1f", ($temp) );
  1368. $state="T: $val";
  1369. # if ($hashumidity == TRUE) {
  1370. # if ($model eq "Prologue") {
  1371. # # plausibility check
  1372. # my $oldhumidity = ReadingsVal($name, "humidity", "unknown");
  1373. # if ($oldhumidity eq "unknown" || ($humidity+15 > $oldhumidity && $humidity-15 < $oldhumidity)) {
  1374. # $hashumidity = TRUE;
  1375. # } else {
  1376. # $hashumidity = FALSE;
  1377. # }
  1378. # }
  1379. # }
  1380. }
  1381. if ($model eq "PFR_130") {
  1382. $lastDay = ReadingsVal($name, "lastDay", $lastDay);
  1383. $lastHour = ReadingsVal($name, "lastHour", $lastHour);
  1384. $rainSumDay=ReadingsVal($name, "RainD", $rainSumDay);
  1385. $rainSumHour=ReadingsVal($name, "RainH", $rainSumHour);
  1386. $msgtype = "temperature";
  1387. $val = sprintf("%2.1f", ($temp) );
  1388. $state="T: $val";
  1389. Log3 $name, 5, "CUL_TCM97001 1. $lastDay : $lastHour : $rainSumDay : $rainSumHour";
  1390. #rain Pollin PFR-130
  1391. if($mday==$lastDay){
  1392. #same day add rainMM
  1393. $rainSumDay+=$rainMM;
  1394. }else {
  1395. #new day, start over
  1396. $rainSumDay=$rainMM;
  1397. }
  1398. if($hour==$lastHour){
  1399. $rainSumHour+=$rainMM;
  1400. }else{
  1401. $rainSumHour=$rainMM;
  1402. }
  1403. readingsBulkUpdate($def, "lastDay", $lastDay );
  1404. readingsBulkUpdate($def, "lastHour", $lastHour );
  1405. readingsBulkUpdate($def, "RainD", $rainSumDay );
  1406. readingsBulkUpdate($def, "RainH", $rainSumHour );
  1407. Log3 $name, 5, "CUL_TCM97001 2. $lastDay : $lastHour : $rainSumDay : $rainSumHour";
  1408. $state="$state RainH: $rainSumHour RainD: $rainSumDay R: $rainticks Rmm: $rainMM";
  1409. Log3 $name, 5, "CUL_TCM97001 $msgtype $name $id3 state: $state";
  1410. } else {
  1411. ### edited by elektron-bbs
  1412. # W174 has no temperature
  1413. if ($model ne "W174") {
  1414. $msgtype = "temperature";
  1415. $val = sprintf("%2.1f", ($temp) );
  1416. $state="T: $val";
  1417. # if ($hashumidity == TRUE) {
  1418. # if ($model eq "Prologue") {
  1419. # # plausibility check
  1420. # my $oldhumidity = ReadingsVal($name, "humidity", "unknown");
  1421. # if ($oldhumidity eq "unknown" || ($humidity+15 > $oldhumidity && $humidity-15 < $oldhumidity)) {
  1422. # $hashumidity = TRUE;
  1423. # } else {
  1424. # $hashumidity = FALSE;
  1425. # }
  1426. # }
  1427. # }
  1428. }
  1429. }
  1430. #zusätzlich Daten für Wetterstation
  1431. if ($hasrain == TRUE) {
  1432. ### inserted by elektron-bbs
  1433. #my $rain_old = ReadingsVal($name, "rain", "unknown");
  1434. my $rain_old = ReadingsVal($name, "rain", 0);
  1435. if ($rain != $rain_old) {
  1436. readingsBulkUpdate($def, "israining", "yes");
  1437. } else {
  1438. readingsBulkUpdate($def, "israining", "no");
  1439. }
  1440. readingsBulkUpdate($def, "rain", $rain );
  1441. $state = "R: $rain";
  1442. $hasrain = FALSE;
  1443. }
  1444. if ($haswind == TRUE) {
  1445. readingsBulkUpdate($def, "windGust", $windGuest );
  1446. readingsBulkUpdate($def, "windDirection", $windDirection );
  1447. #readingsBulkUpdate($def, "windDirectionDegree", $windDirection * 360 / 16);
  1448. readingsBulkUpdate($def, "windDirectionText", $windDirectionText );
  1449. $state = "Wg: $windGuest "." Wd: $windDirectionText ";
  1450. $haswind = FALSE;
  1451. }
  1452. if ($haswindspeed == TRUE) {
  1453. readingsBulkUpdate($def, "windSpeed", $windSpeed );
  1454. $state = "Ws: $windSpeed ";
  1455. $haswindspeed = FALSE;
  1456. }
  1457. if ($hashumidity == TRUE) {
  1458. $msgtypeH = "humidity";
  1459. $valH = $humidity;
  1460. $state="$state H: $valH";
  1461. Log3 $name, 4, "CUL_TCM97001 $msgtype $name $id3 T: $val H: $valH";
  1462. } else {
  1463. $msgtype = "other";
  1464. Log3 $name, 4, "CUL_TCM97001 $msgtype $name $id3";
  1465. #Log3 $name, 4, "CUL_TCM97001 $msgtype $name $id3 ";
  1466. }
  1467. if($hastrend) {
  1468. my $readTrend = ReadingsVal($name, "trend", "unknown");
  1469. if ($trend == 1) {
  1470. if ($readTrend ne "falling") { readingsBulkUpdate($def, "trend", "falling"); }
  1471. } else {
  1472. if ($readTrend ne "rising") { readingsBulkUpdate($def, "trend", "rising"); }
  1473. }
  1474. }
  1475. if ($hasbatcheck) {
  1476. my $battery = ReadingsVal($name, "battery", "unknown");
  1477. if ($batbit) {
  1478. if ($battery ne "ok") { readingsBulkUpdate($def, "battery", "ok"); }
  1479. } else {
  1480. if ($battery ne "low") { readingsBulkUpdate($def, "battery", "low"); }
  1481. }
  1482. }
  1483. if ($hasmode) {
  1484. my $modeVal = ReadingsVal($name, "mode", "unknown");
  1485. if ($mode) {
  1486. if ($modeVal ne "forced") { readingsBulkUpdate($def, "mode", "forced"); }
  1487. } else {
  1488. if ($modeVal ne "normal") { readingsBulkUpdate($def, "mode", "normal"); }
  1489. }
  1490. }
  1491. if ($haschannel) {
  1492. my $readChannel = ReadingsVal($name, "channel", "");
  1493. if (defined($readChannel) && $readChannel ne $channel) { readingsBulkUpdate($def, "channel", $channel); }
  1494. }
  1495. # if ($model eq "Prologue" || $model eq "Eurochron") {
  1496. # # plausibility check
  1497. # my $oldtemp = ReadingsVal($name, "temperature", "unknown");
  1498. # if ($oldtemp eq "unknown" || ($val+5 > $oldtemp && $val-5 < $oldtemp)) {
  1499. # readingsBulkUpdate($def, $msgtype, $val);
  1500. # }
  1501. # } else {
  1502. readingsBulkUpdate($def, $msgtype, $val);
  1503. # }
  1504. if ($hashumidity == TRUE) {
  1505. readingsBulkUpdate($def, $msgtypeH, $valH);
  1506. }
  1507. readingsBulkUpdate($def, "state", $state);
  1508. # for testing only
  1509. #my $rawlen = length($msg);
  1510. #my $rawVal = substr($msg, 0, $rawlen-2);
  1511. #readingsBulkUpdate($def, "RAW", $rawVal);
  1512. readingsEndUpdate($def, 1);
  1513. if(defined($rssi)) {
  1514. $def->{RSSI} = $rssi;
  1515. }
  1516. $attr{$name}{model} = $model;
  1517. return $name;
  1518. } else {
  1519. if (length($msg) == 8 || length($msg) == 10 || length($msg) == 12 || length($msg) == 14) {
  1520. my $defUnknown = $modules{CUL_TCM97001}{defptr}{"CUL_TCM97001_Unknown"};
  1521. if (!$defUnknown) {
  1522. Log3 "Unknown", 2, "CUL_TCM97001 Unknown device Unknown, please define it";
  1523. return "UNDEFINED Unknown CUL_TCM97001 CUL_TCM97001_Unknown";
  1524. }
  1525. $name = $defUnknown->{NAME};
  1526. Log3 $name, 4, "CUL_TCM97001 Device not implemented yet name Unknown msg $msg";
  1527. my $rawlen = length($msg);
  1528. my $rawVal = substr($msg, 0, $rawlen-2);
  1529. my $state="Code: $rawVal";
  1530. if ($defUnknown) {
  1531. $defUnknown->{lastT} = $now;
  1532. }
  1533. $attr{$name}{model} = $model;
  1534. readingsBeginUpdate($defUnknown);
  1535. readingsBulkUpdate($defUnknown, "state", $state);
  1536. # for testing only
  1537. #readingsBulkUpdate($defUnknown, "RAW", $rawVal);
  1538. readingsEndUpdate($defUnknown, 1);
  1539. if(defined($rssi)) {
  1540. $defUnknown->{RSSI} = $rssi;
  1541. }
  1542. #my $defSvg = $defs{"SVG_CUL_TCM97001_Unknown"};
  1543. #if ($defSvg) {
  1544. # CommandDelete(undef, $defSvg->{NAME});
  1545. #}
  1546. return $name;
  1547. }
  1548. }
  1549. return undef;
  1550. }
  1551. 1;
  1552. =pod
  1553. =item summary This module interprets temperature sensor messages.
  1554. =item summary_DE Module verarbeitet empfangene Nachrichten von Temp-Sensoren.
  1555. =begin html
  1556. <a name="CUL_TCM97001"></a>
  1557. <h3>CUL_TCM97001</h3>
  1558. <ul>
  1559. The CUL_TCM97001 module interprets temperature sensor messages received by a Device like CUL, CUN, SIGNALduino etc.<br>
  1560. <br>
  1561. <b>Supported models:</b>
  1562. <ul>
  1563. <li>TCM97...</li>
  1564. <li>ABS700</li>
  1565. <li>TCM21....</li>
  1566. <li>Prologue</li>
  1567. <li>Rubicson</li>
  1568. <li>NC_WS</li>
  1569. <li>GT_WT_02</li>
  1570. <li>AURIOL</li>
  1571. <li>Eurochron</li>
  1572. <li>KW9010</li>
  1573. </ul>
  1574. <br>
  1575. New received device packages are add in fhem category CUL_TCM97001 with autocreate.
  1576. <br><br>
  1577. <a name="CUL_TCM97001_Define"></a>
  1578. <b>Define</b>
  1579. <ul>The received devices created automatically.<br>
  1580. The ID of the defive are the first two Hex values of the package as dezimal.<br>
  1581. </ul>
  1582. <br>
  1583. <a name="CUL_TCM97001 Events"></a>
  1584. <b>Generated events:</b>
  1585. <ul>
  1586. <li>temperature: The temperature</li>
  1587. <li>humidity: The humidity (if available)</li>
  1588. <li>battery: The battery state: low or ok (if available)</li>
  1589. <li>channel: The Channelnumber (if available)</li>
  1590. <li>trend: The temperature trend (if available)</li>
  1591. </ul>
  1592. <br>
  1593. <b>Attributes</b>
  1594. <ul>
  1595. <li><a href="#IODev">IODev</a>
  1596. Note: by setting this attribute you can define different sets of 8
  1597. devices in FHEM, each set belonging to a Device which is capable of receiving the signals. It is important, however,
  1598. that a device is only received by the defined IO Device, e.g. by using
  1599. different Frquencies (433MHz vs 868MHz)
  1600. </li>
  1601. <li><a href="#do_not_notify">do_not_notify</a></li>
  1602. <li><a href="#ignore">ignore</a></li>
  1603. <li><a href="#model">model</a> (TCM97..., ABS700, TCM21...., Prologue, Rubicson, NC_WS, GT_WT_02, AURIOL, KW9010, Unknown)</li>
  1604. <li><a href="#showtime">showtime</a></li>
  1605. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1606. </ul>
  1607. </ul>
  1608. =end html
  1609. =begin html_DE
  1610. <a name="CUL_TCM97001"></a>
  1611. <h3>CUL_TCM97001</h3>
  1612. <ul>
  1613. Das CUL_TCM97001 Module verarbeitet von einem IO Gerät (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur \ Wind \ Rain - Sensoren.<br>
  1614. <br>
  1615. <b>Unterstütze Modelle:</b>
  1616. <ul>
  1617. <li>TCM97...</li>
  1618. <li>ABS700</li>
  1619. <li>TCM21....</li>
  1620. <li>Prologue</li>
  1621. <li>Rubicson</li>
  1622. <li>NC_WS</li>
  1623. <li>GT_WT_02</li>
  1624. <li>AURIOL</li>
  1625. <li>Eurochron</li>
  1626. <li>KW9010</li>
  1627. <li>W155 (wind/rain)</li>
  1628. <li>PFR-130 (rain)</li>
  1629. <li>W174 (rain)</li>
  1630. </ul>
  1631. <br>
  1632. Neu empfangene Sensoren werden in der fhem Kategory CUL_TCM97001 per autocreate angelegt.
  1633. <br><br>
  1634. <a name="CUL_TCM97001_Define"></a>
  1635. <b>Define</b>
  1636. <ul>Die empfangenen Sensoren werden automatisch angelegt.<br>
  1637. Die ID der angelgten Sensoren sind die ersten zwei HEX Werte des empfangenen Paketes in dezimaler Schreibweise.<br>
  1638. </ul>
  1639. <br>
  1640. <a name="CUL_TCM97001 Events"></a>
  1641. <b>Generierte Events:</b>
  1642. <ul>
  1643. <li>temperature: Die aktuelle Temperatur</li>
  1644. <li>humidity: Die aktuelle Luftfeutigkeit (falls verfügbar)</li>
  1645. <li>battery: Der Batteriestatus: low oder ok (falls verfügbar)</li>
  1646. <li>channel: Kanalnummer (falls verfügbar)</li>
  1647. <li>trend: Der Temperaturtrend (falls verfügbar)</li>
  1648. <li>israining: Aussage Regen zwichen zwei Messungen (falls verfügbar)</li>
  1649. <li>rain: Der Regenwert, eine fortlaufende Zahl bis zum Batteriewechsel (falls verfügbar)</li>
  1650. </ul>
  1651. <br>
  1652. <b>Attribute</b>
  1653. <ul>
  1654. <li><a href="#IODev">IODev</a>
  1655. Spezifiziert das physische Ger&auml;t, das die Ausstrahlung der Befehle f&uuml;r das
  1656. "logische" Ger&auml;t ausf&uuml;hrt. Ein Beispiel f&uuml;r ein physisches Ger&auml;t ist ein CUL.<br>
  1657. </li>
  1658. <li><a href="#do_not_notify">do_not_notify</a></li>
  1659. <li><a href="#ignore">ignore</a></li>
  1660. <li><a href="#model">model</a> (ABS700, AURIOL, GT_WT_02, KW9010, NC_WS, PFR-130, Prologue, Rubicson, TCM21...., TCM97…, Unknown, W174)</li>
  1661. <li><a href="#showtime">showtime</a></li>
  1662. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1663. </ul>
  1664. </ul>
  1665. =end html_DE
  1666. =cut