14_CUL_TCM97001.pm 71 KB

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