diff --git a/fhem/FHEM/00_SIGNALduino.pm b/fhem/FHEM/00_SIGNALduino.pm index 348940f02..ba3aae876 100644 --- a/fhem/FHEM/00_SIGNALduino.pm +++ b/fhem/FHEM/00_SIGNALduino.pm @@ -1,7 +1,7 @@ ############################################## # $Id$ # -# v3.3.1 (release 3.3) +# v3.3.1 (stable release 3.3) # The module is inspired by the FHEMduino project and modified in serval ways for processing the incomming messages # see http://www.fhemwiki.de/wiki/SIGNALDuino # It was modified also to provide support for raw message handling which can be send from the SIGNALduino @@ -10,7 +10,6 @@ # N. Butzek, S. Butzek, 2014-2015 # S.Butzek,Ralf9 2016-2017 - package main; use strict; @@ -25,7 +24,7 @@ no warnings 'portable'; use constant { - SDUINO_VERSION => "v3.3.1", + SDUINO_VERSION => "v3.3.2", SDUINO_INIT_WAIT_XQ => 1.5, # wait disable device SDUINO_INIT_WAIT => 2, SDUINO_INIT_MAXRETRY => 3, @@ -34,6 +33,8 @@ use constant { SDUINO_KEEPALIVE_MAXRETRY => 3, SDUINO_WRITEQUEUE_NEXT => 0.3, SDUINO_WRITEQUEUE_TIMEOUT => 2, + + SDUINO_DISPATCH_VERBOSE => 5, # default 5 }; @@ -55,10 +56,13 @@ my %gets = ( # Name, Data to send to the SIGNALduino, Regexp for the answer "raw" => ["", '.*'], "uptime" => ["t", '^[0-9]+' ], "cmds" => ["?", '.*Use one of[ 0-9A-Za-z]+[\r\n]*$' ], - "ITParms" => ["ip",'.*'], +# "ITParms" => ["ip",'.*'], "ping" => ["P",'^OK$'], "config" => ["CG",'^MS.*MU.*MC.*'], "protocolIDs" => ["none",'none'], + "ccconf" => ["C0DnF", 'C0Dn11.*'], + "ccreg" => ["C", '^C.* = .*'], + "ccpatable" => ["C3E", '^C3E = .*'], # "ITClock" => ["ic", '\d+'], # "FAParms" => ["fp", '.*' ], # "TCParms" => ["dp", '.*' ], @@ -72,19 +76,49 @@ my %sets = ( "reset" => 'noArg', "close" => 'noArg', #"disablereceiver" => "", - "ITClock" => 'slider,100,20,700', + #"ITClock" => 'slider,100,20,700', "enableMessagetype" => 'syncedMS,unsyncedMU,manchesterMC', "disableMessagetype" => 'syncedMS,unsyncedMU,manchesterMC', - 'sendMsg' => "", + "sendMsg" => "", + "cc1101_freq" => '', + "cc1101_bWidth" => '', + "cc1101_rAmpl" => '', + "cc1101_sens" => '', + "cc1101_patable_433" => '-10_dBm,-5_dBm,0_dBm,5_dBm,7_dBm,10_dBm', + "cc1101_patable_868" => '-10_dBm,-5_dBm,0_dBm,5_dBm,7_dBm,10_dBm', ); +my %patable = ( + "433" => + { + "-10_dBm" => '34', + "-5_dBm" => '68', + "0_dBm" => '60', + "5_dBm" => '84', + "7_dBm" => 'C8', + "10_dBm" => 'C0', + }, + "868" => + { + "-10_dBm" => '27', + "-5_dBm" => '67', + "0_dBm" => '50', + "5_dBm" => '81', + "7_dBm" => 'CB', + "10_dBm" => 'C2', + }, +); + + +my @ampllist = (24, 27, 30, 33, 36, 38, 40, 42); # rAmpl(dB) + ## Supported Clients per default my $clientsSIGNALduino = ":IT:" ."CUL_TCM97001:" -# ."SIGNALduino_RSL:" + ."SD_RSL:" ."OREGON:" ."CUL_TX:" -# ."SD_AS:" + ."SD_AS:" ."Hideki:" ."SD_WS07:" ."SD_WS09:" @@ -93,8 +127,17 @@ my $clientsSIGNALduino = ":IT:" ."RFXX10REC:" ."Dooya:" ."SOMFY:" -# ."SD_UT:" ## BELL 201.2 TXA + ."SD_UT:" ## BELL 201.2 TXA ."SD_WS_Maverick:" + ."FLAMINGO:" + ."CUL_WS:" + ."Revolt:" + ." :" # Zeilenumbruch + ."FS10:" + ."CUL_FHTTK:" + ."Siro:" + ."FHT:" + ."FS20:" ."SIGNALduino_un:" ; @@ -102,20 +145,28 @@ my $clientsSIGNALduino = ":IT:" my %matchListSIGNALduino = ( "1:IT" => "^i......", # Intertechno Format "2:CUL_TCM97001" => "^s[A-Fa-f0-9]+", # Any hex string beginning with s -# "3:SIGNALduino_RSL" => "^r[A-Fa-f0-9]+", # Any hex string beginning with r + "3:SD_RSL" => "^P1#[A-Fa-f0-9]{8}", "5:CUL_TX" => "^TX..........", # Need TX to avoid FHTTK -# "6:SD_AS" => "^P2#[A-Fa-f0-9]{7,8}", # Arduino based Sensors, should not be default + "6:SD_AS" => "^P2#[A-Fa-f0-9]{7,8}", # Arduino based Sensors, should not be default "4:OREGON" => "^(3[8-9A-F]|[4-6][0-9A-F]|7[0-8]).*", "7:Hideki" => "^P12#75[A-F0-9]+", - "10:SD_WS07" => "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}", - "11:SD_WS09" => "^P9#[A-Fa-f0-9]+", + "9:CUL_FHTTK" => "^T[A-F0-9]{8}", + "10:SD_WS07" => "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}(#R[A-F0-9][A-F0-9]){0,1}\$", + "11:SD_WS09" => "^P9#F[A-Fa-f0-9]+", "12:SD_WS" => '^W\d+x{0,1}#.*', "13:RFXX10REC" => '^(20|29)[A-Fa-f0-9]+', "14:Dooya" => '^P16#[A-Fa-f0-9]+', - "15:SOMFY" => '^YsA[0-9A-F]+', + "15:SOMFY" => '^Ys[0-9A-F]+', "16:SD_WS_Maverick" => '^P47#[A-Fa-f0-9]+', -# "17:SD_UT" => '^u30#.*', ## BELL 201.2 TXA - "X:SIGNALduino_un" => '^[uP]\d+#.*', + "17:SD_UT" => '^u30#.*', ## BELL 201.2 TXA + "18:FLAMINGO" => '^P13#[A-Fa-f0-9]+', ## Flamingo Smoke + "19:CUL_WS" => '^K[A-Fa-f0-9]{5,}', + "20:Revolt" => '^r[A-Fa-f0-9]{22}', + "21:FS10" => '^P61#[A-F0-9]+', + "22:Siro" => '^P72#[A-Fa-f0-9]+', + "23:FHT" => "^81..(04|09|0d)..(0909a001|83098301|c409c401)..", + "24:FS20" => "^81..(04|0c)..0101a001", + "X:SIGNALduino_un" => '^[u]\d+#.*', ); @@ -135,7 +186,7 @@ my %ProtocolListSIGNALduino = ( clientmodule => 'CUL_TCM97001', # not used now #modulematch => '^s[A-Fa-f0-9]+', # not used now length_min => '24', - length_max => '40', + length_max => '42', paddingbits => '8', # pad up to 8 bits, default is 4 }, "1" => @@ -147,20 +198,21 @@ my %ProtocolListSIGNALduino = ( sync => [1,-11], clockabs => '560', # not used now format => 'twostate', # not used now - preamble => 'r', # prepend to converted message + preamble => 'P1#', # prepend to converted message postamble => '', # Append to converted message - clientmodule => 'SIGNALduino_RSL', # not used now - modulematch => '^r[A-Fa-f0-9]+', # not used now - length_min => '23', - length_max => '24', + clientmodule => 'SD_RSL', # not used now + modulematch => '^P1#[A-Fa-f0-9]{8}', # not used now + length_min => '20', # 23 + length_max => '40', # 24 }, "2" => { - name => 'AS', # Self build arduino sensor - comment => 'Self build arduino sensor', + name => 'AS, Self build arduino sensor', + comment => 'developModule. SD_AS module is only in github available', + developId => 'm', id => '2', one => [1,-2], zero => [1,-1], @@ -191,6 +243,24 @@ my %ProtocolListSIGNALduino = ( length_min => '24', #length_max => '800', # Don't know maximal lenth of a valid message + }, + "3.1" => + { + name => 'itv1_sync40', + comment => 'IT remote Control PAR 1000', + id => '3', + one => [3,-1], + zero => [1,-3], + #float => [-1,3], # not full supported now later use + sync => [1,-40], + clockabs => -1, # -1=auto + format => 'twostate', # not used now + preamble => 'i', + clientmodule => 'IT', # not used now + modulematch => '^i......', # not used now + length_min => '24', + #length_max => '800', # Don't know maximal lenth of a valid message + }, "4" => { @@ -277,8 +347,8 @@ my %ProtocolListSIGNALduino = ( }, "9" => ## Funk Wetterstation CTW600 { - name => 'CTW 600', - comment => 'Funk Wetterstation CTW600', + name => 'CTW 600', + comment => 'FunkWS WH1080/WH3080/CTW600', id => '9', zero => [3,-2], one => [1,-2], @@ -289,7 +359,7 @@ my %ProtocolListSIGNALduino = ( preamble => 'P9#', # prepend to converted message clientmodule => 'SD_WS09', # not used now #modulematch => '^u9#.....', # not used now - length_min => '70', + length_min => '60', length_max => '120', }, @@ -328,27 +398,59 @@ my %ProtocolListSIGNALduino = ( preamble => 'P12#', # prepend to converted message clientmodule => 'hideki', # not used now modulematch => '^P12#75.+', # not used now - length_min => '72', - length_max => '104', + length_min => '71', + length_max => '128', method => \&SIGNALduino_Hideki, # Call to process this message polarity => 'invert', }, - "13" => ## FA21RF + "12.1" => ## hideki { - name => '21RF', + name => 'Hideki protocol not invert', + comment => 'only for test of the firmware dev-r33_fixmc', + id => '12', + clockrange => [420,510], # min, max better for Bresser Sensors, OK for hideki/Hideki/TFA too + format => 'manchester', + preamble => 'P12#', # prepend to converted message + clientmodule => 'hideki', # not used now + modulematch => '^P12#75.+', # not used now + length_min => '71', + length_max => '128', + method => \&SIGNALduino_Hideki, # Call to process this message + #polarity => 'invert', + + }, + "13" => ## FLAMINGO FA 21 + { + name => 'FLAMINGO FA21', id => '13', one => [1,-2], zero => [1,-4], - sync => [10,-1], + sync => [1,-20,10,-1], clockabs => 800, format => 'twostate', - preamble => 'u13#', # prepend to converted message - #clientmodule => '', # not used now - #modulematch => '', # not used now - length_min => '20', - length_max => '40', + preamble => 'P13#', # prepend to converted message + clientmodule => 'FLAMINGO', # not used now + #modulematch => 'P13#.*', # not used now + length_min => '24', + length_max => '26', }, + "13.1" => ## FLAMINGO FA20 + { + name => 'FLAMINGO FA21 b', + id => '13', + one => [1,-2], + zero => [1,-4], + start => [20,-1], + clockabs => 800, + format => 'twostate', + preamble => 'P13#', # prepend to converted message + clientmodule => 'FLAMINGO', # not used now + #modulematch => 'P13#.*', # not used now + length_min => '24', + length_max => '24', + }, + "14" => ## Heidemann HX { name => 'Heidemann HX', @@ -421,14 +523,15 @@ my %ProtocolListSIGNALduino = ( { name => 'OSV1', id => '18', - clockrange => [1550,1650], # min , max + clockrange => [1400,1500], # min , max format => 'manchester', # tristate can't be migrated from bin into hex! - #preamble => '', # prepend to converted message - #clientmodule => 'to be written', # not used now - modulematch => '^(3[8-9A-F]|[4-6][0-9A-F]|7[0-8]).*', - length_min => '8', - length_max => '8', - method => \&SIGNALduino_OSV1 # Call to process this message + preamble => '', + clientmodule => 'OREGON', + modulematch => '^[0-9A-F].*', + length_min => '31', + length_max => '32', + polarity => 'invert', # invert bits + method => \&SIGNALduino_OSV1 # Call to process this message }, #"19" => # nothing knowing about this 2015-09-28 01:25:40-MS;P0=-8916;P1=-19904;P2=390;P3=-535;P4=-1020;P5=12846;P6=1371;D=2120232323232324242423232323232323232320239;CP=2;SP=1; # @@ -609,7 +712,9 @@ my %ProtocolListSIGNALduino = ( "30" => # a unitec remote door reed switch { name => 'unitec47031', + comment => 'developModule. SD_UT module is only in github available', id => '30', + developId => 'm', one => [-1,2], zero => [-2,1], start => [-33,1], # Message is not provided as MS, worakround is start @@ -668,22 +773,22 @@ my %ProtocolListSIGNALduino = ( length_min => '42', length_max => '44', }, - "34" => - { - name => 'unknown34', - id => '34', - one => [2,-1], - zero => [1,-2], - start => [3,-3,3,-3,3,-3,3,-3], - clockabs => '240', - format => 'twostate', # not used now - preamble => 'u34#', # prepend to converted message - postamble => '', # Append to converted message - #clientmodule => '', # not used now - #modulematch => '', # not used now - length_min => '40', - length_max => '40', - }, +# "34" => # replaced by 37 +# { +# name => 'unknown34', +# id => '34', +# one => [2,-1], +# zero => [1,-2], +# start => [3,-3,3,-3,3,-3,3,-3], +# clockabs => '240', +# format => 'twostate', # not used now +# preamble => 'u34#', # prepend to converted message +# postamble => '', # Append to converted message +# #clientmodule => '', # not used now +# #modulematch => '', # not used now +# length_min => '40', +# length_max => '40', + # }, "35" => { name => 'socket35', @@ -716,21 +821,24 @@ my %ProtocolListSIGNALduino = ( length_min => '24', length_max => '24', }, - "37" => + "37" => ## Bresser 7009994 + # MU;P0=729;P1=-736;P2=483;P3=-251;P4=238;P5=-491;D=010101012323452323454523454545234523234545234523232345454545232345454545452323232345232340;CP=4; + # MU;P0=-790;P1=-255;P2=474;P4=226;P6=722;P7=-510;D=721060606060474747472121212147472121472147212121214747212147474721214747212147214721212147214060606060474747472121212140;CP=4;R=216; + # short pulse of 250 us followed by a 500 us gap is a 0 bit + # long pulse of 500 us followed by a 250 us gap is a 1 bit + # sync preamble of pulse, gap, 750 us each, repeated 4 times { - name => 'weather37', + name => 'Bresser 7009994', id => '37', one => [2,-1], zero => [1,-2], start => [3,-3,3,-3], - clockabs => '230', + clockabs => '250', format => 'twostate', # not used now preamble => 'W37#', # prepend to converted message - postamble => '', # Append to converted message - clientmodule => 'SD_WS', # not used now - #modulematch => '', # not used now + clientmodule => 'SD_WS', length_min => '40', - length_max => '44', + length_max => '40', }, "38" => { @@ -755,13 +863,13 @@ my %ProtocolListSIGNALduino = ( id => '39', one => [1,-3], zero => [1,-1], - start => [16,-4], - clockabs => 650, + start => [17,-7], + clockabs => 560, format => 'twostate', preamble => '', # prepend to converted message clientmodule => 'RFXX10REC', # not used now #modulematch => '^TX......', # not used now - length_min => '38', + length_min => '32', length_max => '44', paddingbits => '8', postDemodulation => \&SIGNALduino_lengtnPrefix, @@ -781,6 +889,8 @@ my %ProtocolListSIGNALduino = ( #clientmodule => '', # not used now #modulematch => '', # not used now length_min => '10', + length_min => '12', + }, "41" => ## Elro (Smartwares) Doorbell DB200 { @@ -795,39 +905,40 @@ my %ProtocolListSIGNALduino = ( #clientmodule => '', # not used now #modulematch => '', # not used now length_min => '20', - }, - "42" => ## MKT Multi Kon Trade - { - name => 'MKT motionsensor', - id => '42', - zero => [1,-3], - one => [3,-1], - start => [-28], - clockabs => 550, - preamble => 'u42#', # prepend to converted message - #clientmodule => '', # not used now - #modulematch => '', - length_min => '24', - }, + }, + #"42" => ## MKT Multi Kon Trade // Sollte eigentlich als MS ITv1 erkannt werden + # { + # name => 'MKT motionsensor', + # id => '42', + # zero => [1,-3], + # one => [3,-1], + # sync => [-28], + # clockabs => 550, + # preamble => 'u42#', # prepend to converted message + # #clientmodule => '', # not used now + # #modulematch => '', + # length_min => '24', + # }, "43" => ## Somfy RTS { name => 'Somfy RTS', id => '43', - clockrange => [610,670], # min , max + clockrange => [610,680], # min , max format => 'manchester', preamble => 'Ys', clientmodule => 'SOMFY', # not used now - modulematch => '^YsA[0-9A-F]{13}', + modulematch => '^Ys[0-9A-F]{14}', length_min => '56', - length_max => '56', + length_max => '57', method => \&SIGNALduino_SomfyRTS, # Call to process this message msgIntro => 'SR;P0=-2560;P1=2560;P3=-640;D=10101010101010113;', #msgOutro => 'SR;P0=-30415;D=0;', + frequency => '10AB85550A', }, "44" => ## Bresser Temeo Trend { name => 'BresserTemeo', - id => '44', + id => '44', clockabs => 500, zero => [4,-4], one => [4,-8], @@ -838,10 +949,10 @@ my %ProtocolListSIGNALduino = ( length_min => '64', length_max => '72', }, - "51" => ## Bresser Temeo Trend + "44.1" => ## Bresser Temeo Trend { name => 'BresserTemeo', - id => '44x', + id => '44', clockabs => 500, zero => [4,-4], one => [4,-8], @@ -853,21 +964,22 @@ my %ProtocolListSIGNALduino = ( length_max => '72', }, - "45" => - { - name => 'revolt', - id => '45', - one => [3,-1], - zero => [1,-3], - #float => [-1,3], # not full supported now later use - sync => [1,-24], - clockabs => -1, # -1=auto - format => 'twostate', # not used now - preamble => 'i', - clientmodule => 'IT', # not used now - modulematch => '^i......', # not used now - length_min => '24', - }, + "45" => # Revolt + # MU;P0=-8320;P1=9972;P2=-376;P3=117;P4=-251;P5=232;D=012345434345434345454545434345454545454543454343434343434343434343434543434345434343434545434345434343434343454343454545454345434343454345434343434343434345454543434343434345434345454543454343434543454345434545;CP=3;R=2 + { + name => 'Revolt', + id => '45', + one => [2,-2], + zero => [1,-2], + start => [83,-3], + clockabs => 120, + preamble => 'r', # prepend to converted message + clientmodule => 'Revolt', + modulematch => '^r[A-Fa-f0-9]{22}', + length_min => '84', + length_max => '120', + postDemodulation => sub { my ($name, @bit_msg) = @_; my @new_bitmsg = splice @bit_msg, 0,88; return 1,@new_bitmsg; }, + }, "46" => { name => 'EKX1BE', @@ -928,7 +1040,7 @@ my %ProtocolListSIGNALduino = ( }, "50" => ## Opus XT300 { - name => 'optus_XT300', + name => 'optus_XT300', id => '50', clockabs => 500, zero => [3,-2], @@ -936,11 +1048,450 @@ my %ProtocolListSIGNALduino = ( # start => [1,-25], # Wenn das startsignal empfangen wird, fehlt das 1 bit format => 'twostate', preamble => 'W50#', # prepend to converted message - clientmodule => 'SD_WS', # not used now + clientmodule => 'SD_WS', # not used now modulematch => '^W50#.*', # not used now length_min => '47', length_max => '48', + }, + "51" => + { + name => 'weather51', # Logilink, NC, WS, TCM97001 etc. + comment => 'IAN 275901 Wetterstation Lidl', + id => '51', + one => [1,-8], + zero => [1,-4], + sync => [1,-13], + clockabs => '560', # not used now + format => 'twostate', # not used now + preamble => 'W51#', # prepend to converted message + postamble => '', # Append to converted message + clientmodule => 'SD_WS', + modulematch => '^W51#.*', + length_min => '40', + length_max => '40', + }, + "52" => ## Oregon PIR Protocol + { + name => 'OS_PIR', + id => '52', + clockrange => [470,640], # min , max + format => 'manchester', # tristate can't be migrated from bin into hex! + clientmodule => 'OREGON', + modulematch => '^u52#F{3}|0{3}.*', + preamble => 'u52#', + length_min => '30', + length_max => '30', + method => \&SIGNALduino_OSPIR, # Call to process this message + polarity => 'invert', + }, + + "55" => ##quigg gt1000 + { + name => 'quigg_gt1000', + id => '55', + clockabs => 300, + zero => [1,-4], + one => [4,-2], + sync => [1,-8], + format => 'twostate', + preamble => 'i', # prepend to converted message + clientmodule => 'IT', # not used now + modulematch => '^i.*', # not used now + length_min => '24', + length_max => '24', + }, + "56" => ## Celexon + { + name => 'Celexon', + id => '56', + clockabs => 200, + zero => [1,-3], + one => [3,-1], + start => [25,-3], + format => 'twostate', + preamble => 'u56#', # prepend to converted message + #clientmodule => '' , # not used now + modulematch => '', # not used now + length_min => '56', + length_max => '68', + }, + "57" => ## m-e doorbell + { + name => 'm-e', + id => '57', + clockrange => [300,360], # min , max + format => 'manchester', # tristate can't be migrated from bin into hex! + clientmodule => '', + modulematch => '^u57*', + preamble => 'u57#', + length_min => '21', + length_max => '24', + method => \&SIGNALduino_MCRAW, # Call to process this message + polarity => 'invert', + }, + "58" => ## tfa 30.3208.0 + { + name => 'tfa 30.3208.0 ', + id => '58', + clockrange => [460,520], # min , max + format => 'manchester', # tristate can't be migrated from bin into hex! + clientmodule => '', + modulematch => '^W58*', + preamble => 'W58#', + length_min => '54', + length_max => '136', + method => \&SIGNALduino_MCTFA, # Call to process this message + polarity => 'invert', + }, + "59" => ## AK-HD-4 remote + { + name => 'AK-HD-4', + id => '59', + clockabs => 230, + zero => [-4,1], + one => [-1,4], + start => [-1,37], + format => 'twostate', # tristate can't be migrated from bin into hex! + preamble => 'u59#', # Append to converted message + postamble => '', # Append to converted message + #clientmodule => '', # not used now + modulematch => '', # not used now + length_min => '24', + length_max => '24', + }, + "60" => ## ELV, LA CROSSE (WS2000/WS7000) + { + # MU;P0=32001;P1=-381;P2=835;P3=354;P4=-857;D=01212121212121212121343421212134342121213434342121343421212134213421213421212121342121212134212121213421212121343421343430;CP=2;R=53; # tested sensors: WS-7000-20, AS2000, ASH2000, S2000, S2000I, S2001A, S2001IA, + # ASH2200, S300IA, S2001I, S2000ID, S2001ID, S2500H + # not tested: AS3, S2000W, S2000R, WS7000-15, WS7000-16, WS2500-19, S300TH, S555TH + # das letzte Bit 1 und 1 x 0 Preambel fehlt meistens + # ___ _ + # | |_ | |___ + # Bit 0 Bit 1 + # kurz 366 µSek / lang 854 µSek / gesamt 1220 µSek - Sollzeiten + name => 'WS2000', + id => '60', + one => [3,-7], + zero => [7,-3], + clockabs => 122, + preamble => 'K', # prepend to converted message + postamble => '', # Append to converted message + clientmodule => 'CUL_WS', + length_min => '44', # eigentlich 46 + length_max => '82', # eigentlich 81 + postDemodulation => \&SIGNALduino_postDemo_WS2000, + }, + + "61" => ## ELV FS10 + # tested transmitter: FS10-S8, FS10-S4, FS10-ZE + # tested receiver: FS10-ST, FS10-MS, WS3000-TV, PC-Wettersensor-Empfaenger + # das letzte Bit 1 und 1 x 0 Preambel fehlt immer + { + name => 'FS10', + id => '61', + one => [1,-2], + zero => [1,-1], + clockabs => 400, + format => 'twostate', + preamble => 'P61#', # prepend to converted message + postamble => '', # Append to converted message + clientmodule => 'FS10', + #modulematch => '', + length_min => '38', # eigentlich 41 oder 46 (Pruefsumme nicht bei allen) + length_max => '48', # eigentlich 46 + }, + "62" => ## Clarus_Switch + { #MU;P0=-5893;P4=-634;P5=498;P6=-257;P7=116;D=45656567474747474745656707456747474747456745674567456565674747474747456567074567474747474567456745674565656747474747474565670745674747474745674567456745656567474747474745656707456747474747456745674567456565674747474747456567074567474747474567456745674567;CP=7;O; + name => 'Clarus_Switch', + id => '62', + one => [3,-1], + zero => [1,-3], + start => [1,-35], # ca 30-40 + clockabs => 189, + preamble => 'i', # prepend to converted message + clientmodule => 'IT', + #modulematch => '', + length_min => '24', + length_max => '24', + }, + "63" => ## Warema MU + { #MU;P0=-2988;P1=1762;P2=-1781;P3=-902;P4=871;P5=6762;P6=5012;D=0121342434343434352434313434243521342134343436; + name => 'Warema', + comment => 'developId, is still experimental', + id => '63', + developId => 'y', + one => [1], + zero => [0], + clockabs => 800, + syncabs => '6700',# Special field for filterMC function + preamble => 'u63', # prepend to converted message + #clientmodule => '', + #modulematch => '', + length_min => '24', + filterfunc => 'SIGNALduino_filterMC', + }, + "64" => ## WH2 ############################################################################# + { + # MU;P0=-32001;P1=457;P2=-1064;P3=1438;D=0123232323212121232123232321212121212121212323212121232321;CP=1;R=63; + # MU;P0=-32001;P1=473;P2=-1058;P3=1454;D=0123232323212121232123232121212121212121212121232321212321;CP=1;R=51; + #MU;P0=134;P1=-113;P3=412;P4=-1062;P5=1379;D=01010101013434343434343454345454345454545454345454545454343434545434345454345454545454543454543454345454545434545454345;CP=3; + + name => 'WH2', + id => '64', + one => [1,-2], + zero => [3,-2], + clockabs => 490, + clientmodule => 'SD_WS', + modulematch => '^W64*', + preamble => 'W64#', # prepend to converted message + postamble => '', # Append to converted message + #clientmodule => '', + length_min => '48', + length_max => '54', + }, + "65" => ## Homeeasy + { + name => 'Homeeasy', + id => '65', + one => [1,-5], + zero => [1,-1], + start => [1,-40], + clockabs => 250, + format => 'twostate', # not used now + preamble => 'U65#', + length_min => '50', + #msgOutro => 'SR;P0=275;P1=-7150;D=01;', + postDemodulation => \&SIGNALduino_HE, + }, + "66" => ## TX2 Protocol (Remote Temp Transmitter & Remote Thermo Model 7035) + # MU;P0=13312;P1=-2785;P2=4985;P3=1124;P4=-6442;P5=3181;P6=-31980;D=0121345434545454545434545454543454545434343454543434545434545454545454343434545434343434545621213454345454545454345454545434545454343434545434345454345454545454543434345454343434345456212134543454545454543454545454345454543434345454343454543454545454545;CP=3;R=73;O; + { + name => 'WS7035', + id => '66', + one => [10,-52], + zero => [27,-52], + start => [-21,42,-21], + clockabs => 122, + format => 'pwm', # not used now + preamble => 'TX', + clientmodule => 'CUL_TX', + modulematch => '^TX......', + length_min => '43', + length_max => '44', + postDemodulation => \&SIGNALduino_postDemo_WS7035, + }, + "67" => ## TX2 Protocol (Remote Datalink & Remote Thermo Model 7053) + # MU;P0=3381;P1=-672;P2=-4628;P3=1142;P4=-30768;D=0102320232020202020232020232020202320232323202323202020202020202020401023202320202020202320202320202023202323232023232020202020202020200;CP=0;R=45; + # MU;P0=1148;P1=3421;P6=-664;P7=-4631;D=16170717071717171717071717071717171717070707170717171717070717171710;CP=1;R=29; + # MU;P0=3389;P3=2560;P4=-720;P5=1149;P7=-4616;D=345407570757070707070757070757070707070757570707075707070707570757575;CP=5;R=253; + # __ ____ + # ________| | ________| | + # Bit 1 Bit 0 + # 4630 1220 4630 3420 µSek - mit Oszi gemessene Zeiten + { + name => 'WS7053', + id => '67', + one => [-38,10], + zero => [-38,28], + clockabs => 122, + preamble => 'TX', # prepend to converted message + clientmodule => 'CUL_TX', + modulematch => '^TX......', + length_min => '32', + length_max => '34', + postDemodulation => \&SIGNALduino_postDemo_WS7053, + }, + "68" => ## PFR-130 ########################################################################### + { + # MS;P0=-3890;P1=386;P2=-2191;P3=-8184;D=1312121212121012121212121012121212101012101010121012121210121210101210101012;CP=1;SP=3;R=20;O; + # MS;P0=-2189;P1=371;P2=-3901;P3=-8158;D=1310101010101210101010101210101010121210121212101210101012101012121012121210;CP=1;SP=3;R=20;O; + name => 'PFR-130', + id => '68', + one => [1,-10], + zero => [1,-5], + sync => [1,-21], + clockabs => 380, # not used now + preamble => 's', # prepend to converted message + postamble => '00', # Append to converted message + clientmodule => 'CUL_TCM97001', # not used now + length_min => '24', + length_max => '42', + paddingbits => '8', # pad up to 8 bits, default is 4 + }, + "69" => ## Hoermann + # MU;P0=-508;P1=1029;P2=503;P3=-1023;P4=12388;D=01010232323232310104010101010101010102323231010232310231023232323231023101023101010231010101010232323232310104010101010101010102323231010232310231023232323231023101023101010231010101010232323232310104010101010101010102323231010232310231023232323231023101;CP=2;R=37;O; + { + name => 'Hoermann', + id => '69', + zero => [2,-1], + one => [1,-2], + start => [24,-1], + clockabs => 510, + format => 'twostate', # not used now + #clientmodule => '', + #modulematch => '^U69*', + preamble => 'U69#', + length_min => '40', + #length_max => '90', + postDemodulation => \&SIGNALduino_postDemo_Hoermann, # Call to process this message + }, + "70" => ## FHT80TF (Funk-Tuer-Fenster-Melder FHT 80TF und FHT 80TF-2) + # closed MU;P0=-24396;P1=417;P2=-376;P3=610;P4=-582;D=012121212121212121212121234123434121234341212343434121234123434343412343434121234341212121212341212341234341234123434;CP=1;R=35; + # open MU;P0=-21652;P1=429;P2=-367;P4=634;P5=-555;D=012121212121212121212121245124545121245451212454545121245124545454512454545121245451212121212124512451245451245121212;CP=1;R=38; + { + name => 'FHT80TF', + comment => 'Door/Window switch (868Mhz)', + id => '70', + one => [1.5,-1.5], # 600 + zero => [1,-1], # 400 + clockabs => 400, + format => 'twostate', # not used now + clientmodule => 'CUL_FHTTK', + preamble => 'T', + length_min => '50', + length_max => '58', + postDemodulation => \&SIGNALduino_postDemo_FHT80TF, + }, + "71" => ## PV-8644 infactory Poolthermometer + # MU;P0=1735;P1=-1160;P2=591;P3=-876;D=0123012323010101230101232301230123010101010123012301012323232323232301232323232323232323012301012;CP=2;R=97; + { + name => 'PV-8644', + comment => 'infactory Poolthermometer', + id => '71', + clockabs => 580, + zero => [3,-2], + one => [1,-1.5], + format => 'twostate', + preamble => 'W71#', # prepend to converted message + clientmodule => 'SD_WS', + #modulematch => '^W71#.*' + length_min => '48', + length_max => '48', + }, + # MU;P0=-760;P1=334;P2=693;P3=-399;P4=-8942;P5=4796;P6=-1540;D=01010102310232310101010102310232323101010102310101010101023102323102323102323102310101010102310232323101010102310101010101023102310231023102456102310232310232310231010101010231023232310101010231010101010102310231023102310245610231023231023231023101010101;CP=1;R=45;O; + # MU;P0=-8848;P1=4804;P2=-1512;P3=336;P4=-757;P5=695;P6=-402;D=0123456345656345656345634343434345634565656343434345634343434343456345634563456345;CP=3;R=49; + + "72" => # Siro blinds MU @Dr. Smag + { + name => 'Siro shutter', + comment => 'developModule. Siro is not in github or SVN available', + id => '72', + developId => 'm', + dispatchequals => 'true', + one => [2,-1.2], # 680, -400 + zero => [1,-2.2], # 340, -750 + start => [14,-4.4], # 4800,-1520 + clockabs => 340, + format => 'twostate', + preamble => 'P72#', # prepend to converted message + clientmodule => 'Siro', + #modulematch => '', + length_min => '39', + length_max => '40', + msgOutro => 'SR;P0=-8500;D=0;', + }, + + # MS;P0=4803;P1=-1522;P2=333;P3=-769;P4=699;P5=-393;P6=-9190;D=2601234523454523454523452323232323452345454523232323452323232323234523232345454545;CP=2;SP=6;R=61; + "72.1" => # Siro blinds MS @Dr. Smag + { + name => 'Siro shutter', + comment => 'developModule. Siro is not in github or SVN available', + id => '72', + developId => 'm', + dispatchequals => 'true', + one => [2,-1.2], # 680, -400 + zero => [1,-2.2], # 340, -750 + sync => [14,-4.4], # 4800,-1520 + clockabs => 340, + format => 'twostate', + preamble => 'P72#', # prepend to converted message + clientmodule => 'Siro', + #modulematch => '', + length_min => '39', + length_max => '40', + #msgOutro => 'SR;P0=-8500;D=0;', + }, + "73" => ## FHT80 - Raumthermostat (868Mhz), @HomeAutoUser + { + name => 'FHT80', + comment => 'Roomthermostat (868Mhz only receive)', + id => '73', + developId => 'y', + one => [1.5,-1.5], # 600 + zero => [1,-1], # 400 + clockabs => 400, + format => 'twostate', # not used now + clientmodule => 'FHT', + preamble => '810c04xx0909a001', + length_min => '59', + length_max => '67', + postDemodulation => \&SIGNALduino_postDemo_FHT80, + }, + "74" => ## FS20 - 'Remote Control (868Mhz), @HomeAutoUser + { + name => 'FS20', + comment => 'Remote Control (868Mhz only receive)', + id => '74', + developId => 'y', + one => [1.5,-1.5], # 600 + zero => [1,-1], # 400 + clockabs => 400, + format => 'twostate', # not used now + clientmodule => 'FS20', + preamble => '810b04f70101a001', + length_min => '50', + length_max => '67', + postDemodulation => \&SIGNALduino_postDemo_FS20, + }, + "75" => ## ConradRSL2 @litronics https://github.com/RFD-FHEM/SIGNALDuino/issues/69 + # MU;P0=-1365;P1=477;P2=1145;P3=-734;P4=-6332;D=01023202310102323102423102323102323101023232323101010232323231023102323102310102323102423102323102323101023232323101010232323231023102323102310102323102;CP=1;R=12; + { + name => 'ConradRSL2', + id => '75', + one => [3,-1], + zero => [1,-3], + clockabs => 500, + format => 'twostate', + clientmodule => 'SD_RSL', + preamble => 'P1#', + modulematch => '^P1#[A-Fa-f0-9]{8}', + length_min => '32', + length_max => '40', + }, + "76" => ## Kabellose LED-Weihnachtskerzen XM21-0 + { + name => 'xm21', + comment => 'reserviert, LED Lichtrekette on', + id => '76', + developId => 'p', + one => [1.2,-2], # 120,-200 + zero => [], # existiert nicht + start => [4.5,-2], # 450,-200 Starsequenz + clockabs => 100, + format => 'twostate', # not used now + clientmodule => '', + preamble => 'P76', + length_min => 64, + length_max => 64, + }, + "76.1" => ## Kabellose LED-Weihnachtskerzen XM21-0 + { + name => 'xm21', + comment => 'reserviert, LED Lichtrekette off', + id => '76.1', + developId => 'p', + one => [1.2,-2], # 120,-200 + zero => [], # existiert nicht + start => [4.5,-2], # 450,-200 Starsequenz + clockabs => 100, + format => 'twostate', # not used now + clientmodule => '', + preamble => 'P76', + length_min => 58, + length_max => 58, + }, + ); @@ -971,17 +1522,28 @@ SIGNALduino_Initialize($) ." hexFile" ." initCommands" ." flashCommand" - ." hardware:nano328,uno,promini328" + ." hardware:nano328,uno,promini328,nanoCC1101" ." debug:0,1" ." longids" ." minsecs" ." whitelist_IDs" - ." WS09_WSModel:undef,WH1080,CTW600" - ." WS09_CRCAUS:0,1" + ." blacklist_IDs" + ." WS09_CRCAUS:0,1,2" + ." addvaltrigger" + ." rawmsgEvent:1,0" + ." cc1101_frequency" + ." doubleMsgCheck_IDs" + ." suppressDeviceRawmsg:1,0" + ." development" + ." noMsgVerbose:0,1,2,3,4,5" ." $readingFnAttributes"; $hash->{ShutdownFn} = "SIGNALduino_Shutdown"; - + + $hash->{msIdList} = (); + $hash->{muIdList} = (); + $hash->{mcIdList} = (); + } sub @@ -1047,14 +1609,14 @@ SIGNALduino_Define($$) if( !defined( $attr{$name}{flashCommand} ) ) { # $attr{$name}{flashCommand} = "avrdude -p atmega328P -c arduino -P [PORT] -D -U flash:w:[HEXFILE] 2>[LOGFILE]" - $attr{$name}{flashCommand} = "avrdude -c arduino -b 57600 -P [PORT] -p atmega328p -vv -U flash:w:[HEXFILE] 2>[LOGFILE]" + $attr{$name}{flashCommand} = "avrdude -c arduino -b [BAUDRATE] -P [PORT] -p atmega328p -vv -U flash:w:[HEXFILE] 2>[LOGFILE]"; + } $hash->{DeviceName} = $dev; my $ret=undef; - - my $whitelistIDs = AttrVal($name,"whitelist_IDs",""); - SIGNALduino_IdList($hash ,$name, $whitelistIDs); + + InternalTimer(gettimeofday(), 'SIGNALduino_IdList',"sduino_IdList:$name",0); # verzoegern bis alle Attribute eingelesen sind if($dev ne "none") { $ret = DevIo_OpenDev($hash, 0, "SIGNALduino_DoInit", 'SIGNALduino_Connect'); @@ -1064,6 +1626,7 @@ SIGNALduino_Define($$) } $hash->{DMSG}="nothing"; + $hash->{LASTDMSG} = "nothing"; $hash->{TIME}=time(); @@ -1091,6 +1654,9 @@ SIGNALduino_Undef($$) { my ($hash, $arg) = @_; my $name = $hash->{NAME}; + + + foreach my $d (sort keys %defs) { if(defined($defs{$d}) && @@ -1129,9 +1695,27 @@ SIGNALduino_Set($@) my ($hash, @a) = @_; return "\"set SIGNALduino\" needs at least one parameter" if(@a < 2); + + #Log3 $hash, 3, "SIGNALduino_Set called with params @a"; + + + my $hasCC1101 = 0; + my $CC1101Frequency; + if ($hash->{version} && $hash->{version} =~ m/cc1101/) { + $hasCC1101 = 1; + if (!defined($hash->{cc1101_frequency})) { + $CC1101Frequency = "433"; + } else { + $CC1101Frequency = $hash->{cc1101_frequency}; + } + } if (!defined($sets{$a[1]})) { my $arguments = ' '; foreach my $arg (sort keys %sets) { + next if ($arg =~ m/cc1101/ && $hasCC1101 == 0); + if ($arg =~ m/patable/) { + next if (substr($arg, -3) ne $CC1101Frequency); + } $arguments.= $arg . ($sets{$arg} ? (':' . $sets{$arg}) : '') . ' '; } #Log3 $hash, 3, "set arg = $arguments"; @@ -1142,8 +1726,16 @@ SIGNALduino_Set($@) my $cmd = shift @a; my $arg = join(" ", @a); + if ($cmd =~ m/cc1101/ && $hasCC1101 == 0) { + return "This command is only available with a cc1101 receiver"; + } + return "$name is not active, may firmware is not suppoted, please flash or reset" if ($cmd ne 'reset' && $cmd ne 'flash' && exists($hash->{DevState}) && $hash->{DevState} ne 'initialized'); + if ($cmd =~ m/^cc1101_/) { + $cmd = substr($cmd,7); + } + if($cmd eq "raw") { Log3 $name, 4, "set $name $cmd $arg"; #SIGNALduino_SimpleWrite($hash, $arg); @@ -1155,7 +1747,7 @@ SIGNALduino_Set($@) my @deviceName = split('@', $hash->{DeviceName}); my $port = $deviceName[0]; my $hardware=AttrVal($name,"hardware","nano328"); - + my $baudrate=$hardware eq "uno" ? 115200 : 57600; my $defaultHexFile = "./FHEM/firmware/$hash->{TYPE}_$hardware.hex"; my $logFile = AttrVal("global", "logdir", "./log/") . "$hash->{TYPE}-Flash.log"; @@ -1165,10 +1757,23 @@ SIGNALduino_Set($@) $hexFile = $defaultHexFile; } } - else { + elsif ($args[0] =~ m/^https?:\/\// ) { + my $http_param = { + url => $args[0], + timeout => 5, + hash => $hash, # Muss gesetzt werden, damit die Callback funktion wieder $hash hat + method => "GET", # Lesen von Inhalten + callback => \&SIGNALduino_ParseHttpResponse, # Diese Funktion soll das Ergebnis dieser HTTP Anfrage bearbeiten + command => 'flash', + }; + + HttpUtils_NonblockingGet($http_param); + return; + } else { $hexFile = $args[0]; } - + Log3 $name, 3, "$name: filename $hexFile provided, trying to flash"; + return "Usage: set $name flash [filename]\n\nor use the hexFile attribute" if($hexFile !~ m/^(\w|\/|.)+$/); $log .= "flashing Arduino $name\n"; @@ -1189,6 +1794,7 @@ SIGNALduino_Set($@) my $avrdude = $flashCommand; $avrdude =~ s/\Q[PORT]\E/$port/g; + $avrdude =~ s/\Q[BAUDRATE]\E/$baudrate/g; $avrdude =~ s/\Q[HEXFILE]\E/$hexFile/g; $avrdude =~ s/\Q[LOGFILE]\E/$logFile/g; @@ -1224,17 +1830,6 @@ SIGNALduino_Set($@) } elsif( $cmd eq "close" ) { $hash->{DevState} = 'closed'; return SIGNALduino_CloseDevice($hash); - } elsif( $cmd eq "ITClock" ) { - Log3 $name, 4, "set $name $cmd $arg"; - my $clock = shift @a; - - $clock=250 if ($clock eq "" ); - return "argument $arg is not numeric" if($clock !~ /^\d+$/); - Log3 $name, 3, "$name: Setting ITClock to $clock (sending $arg)"; - $arg="ic$clock"; - #SIGNALduino_SimpleWrite($hash, $arg); - SIGNALduino_AddSendQueue($hash,$arg); - $hash->{$cmd}=$clock; } elsif( $cmd eq "disableMessagetype" ) { my $argm = 'CD' . substr($arg,-1,1); #SIGNALduino_SimpleWrite($hash, $argm); @@ -1245,12 +1840,70 @@ SIGNALduino_Set($@) #SIGNALduino_SimpleWrite($hash, $argm); SIGNALduino_AddSendQueue($hash,$argm); Log3 $name, 4, "set $name $cmd $arg $argm"; + } elsif( $cmd eq "freq" ) { + if ($arg eq "") { + $arg = AttrVal($name,"cc1101_frequency", 433.92); + } + my $f = $arg/26*65536; + my $f2 = sprintf("%02x", $f / 65536); + my $f1 = sprintf("%02x", int($f % 65536) / 256); + my $f0 = sprintf("%02x", $f % 256); + $arg = sprintf("%.3f", (hex($f2)*65536+hex($f1)*256+hex($f0))/65536*26); + Log3 $name, 3, "$name: Setting FREQ2..0 (0D,0E,0F) to $f2 $f1 $f0 = $arg MHz"; + SIGNALduino_AddSendQueue($hash,"W0F$f2"); + SIGNALduino_AddSendQueue($hash,"W10$f1"); + SIGNALduino_AddSendQueue($hash,"W11$f0"); + SIGNALduino_WriteInit($hash); + } elsif( $cmd eq "bWidth" ) { + SIGNALduino_AddSendQueue($hash,"C10"); + $hash->{getcmd}->{cmd} = "bWidth"; + $hash->{getcmd}->{arg} = $arg; + } elsif( $cmd eq "rAmpl" ) { + return "a numerical value between 24 and 42 is expected" if($arg !~ m/^\d+$/ || $arg < 24 || $arg > 42); + my ($v, $w); + for($v = 0; $v < @ampllist; $v++) { + last if($ampllist[$v] > $arg); + } + $v = sprintf("%02d", $v-1); + $w = $ampllist[$v]; + Log3 $name, 3, "$name: Setting AGCCTRL2 (1B) to $v / $w dB"; + SIGNALduino_AddSendQueue($hash,"W1D$v"); + SIGNALduino_WriteInit($hash); + } elsif( $cmd eq "sens" ) { + return "a numerical value between 4 and 16 is expected" if($arg !~ m/^\d+$/ || $arg < 4 || $arg > 16); + my $w = int($arg/4)*4; + my $v = sprintf("9%d",$arg/4-1); + Log3 $name, 3, "$name: Setting AGCCTRL0 (1D) to $v / $w dB"; + SIGNALduino_AddSendQueue($hash,"W1F$v"); + SIGNALduino_WriteInit($hash); + } elsif( substr($cmd,0,7) eq "patable" ) { + my $paFreq = substr($cmd,8); + my $pa = "x" . $patable{$paFreq}{$arg}; + Log3 $name, 3, "$name: Setting patable $paFreq $arg $pa"; + SIGNALduino_AddSendQueue($hash,$pa); + SIGNALduino_WriteInit($hash); } elsif( $cmd eq "sendMsg" ) { - my ($protocol,$data,$repeats,$clock) = split("#",$arg); + Log3 $name, 5, "$name: sendmsg msg=$arg"; + my ($protocol,$data,$repeats,$clock,$frequency) = split("#",$arg); $protocol=~ s/[Pp](\d+)/$1/; # extract protocol num $repeats=~ s/[rR](\d+)/$1/; # extract repeat num - $clock=~ s/[Cc](\d+)/$1/ if (defined($clock)); # extract ITClock num $repeats=1 if (!defined($repeats)); + if (defined($clock) && substr($clock,0,1) eq "F") { # wenn es kein clock gibt, pruefen ob im clock eine frequency ist + $clock=~ s/[F]([0-9a-fA-F]+$)/$1/; + $frequency = $clock; + $clock = undef; + } else { + $clock=~ s/[Cc](\d+)/$1/ if (defined($clock)); # extract ITClock num + $frequency=~ s/[Ff]([0-9a-fA-F]+$)/$1/ if (defined($frequency)); + } + if (exists($ProtocolListSIGNALduino{$protocol}{frequency}) && $hasCC1101 && !defined($frequency)) { + $frequency = $ProtocolListSIGNALduino{$protocol}{frequency}; + } + if (defined($frequency) && $hasCC1101) { + $frequency="F=$frequency;"; + } else { + $frequency=""; + } return "$name: sendmsg, unknown protocol: $protocol" if (!exists($ProtocolListSIGNALduino{$protocol})); @@ -1262,8 +1915,6 @@ SIGNALduino_Set($@) my %patternHash; my $pattern=""; my $cnt=0; - - my $sendData; if ($ProtocolListSIGNALduino{$protocol}{format} eq 'manchester') @@ -1288,7 +1939,7 @@ SIGNALduino_Set($@) $repeats = 0; } - $sendData = $intro . "SM;" . ($repeats > 0 ? "R=$repeats;" : "") . "C=$clock;D=$data;" . $outro; # SM;R=2;C=400;D=AFAFAF; + $sendData = $intro . "SM;" . ($repeats > 0 ? "R=$repeats;" : "") . "C=$clock;D=$data;" . $outro . $frequency; # SM;R=2;C=400;D=AFAFAF; Log3 $name, 5, "$name: sendmsg Preparing manchester protocol=$protocol, repeats=$repeats, clock=$clock data=$data"; } else { if ($protocol == 3 || substr($data,0,2) eq "is") { @@ -1337,7 +1988,7 @@ SIGNALduino_Set($@) #Log3 $name, 5, "encoding $bit"; $SignalData.=$signalHash{$bitconv{$bit}}; ## Add the signal to our data string } - $sendData = "SR;R=$repeats;$pattern$SignalData;"; + $sendData = "SR;R=$repeats;$pattern$SignalData;$frequency"; } @@ -1367,13 +2018,17 @@ SIGNALduino_Get($@) Log3 $name, 5, "\"get $type\" needs at least one parameter" if(@a < 2); return "\"get $type\" needs at least one parameter" if(@a < 2); if(!defined($gets{$a[1]})) { - my @cList = map { $_ =~ m/^(file|raw)$/ ? $_ : "$_:noArg" } sort keys %gets; + my @cList = map { $_ =~ m/^(file|raw|ccreg)$/ ? $_ : "$_:noArg" } sort keys %gets; return "Unknown argument $a[1], choose one of " . join(" ", @cList); } my $arg = ($a[2] ? $a[2] : ""); return "no command to send, get aborted." if (length($gets{$a[1]}[0]) == 0 && length($arg) == 0); + if (($a[1] eq "ccconf" || $a[1] eq "ccreg" || $a[1] eq "ccpatable") && $hash->{version} && $hash->{version} !~ m/cc1101/) { + return "This command is only available with a cc1101 receiver"; + } + my ($msg, $err); if (IsDummy($name)) @@ -1518,9 +2173,10 @@ SIGNALduino_Get($@) $ret .= "\n"; } - $moduleId =~ s/,$//; + #$moduleId =~ s/,$//; - return "$a[1]: \n\n$ret\nIds with modules: $moduleId"; + return "$a[1]: \n\n$ret\n"; + #return "$a[1]: \n\n$ret\nIds with modules: $moduleId"; } #SIGNALduino_SimpleWrite($hash, $gets{$a[1]}[0] . $arg); @@ -1551,8 +2207,73 @@ sub SIGNALduino_parseResponse($$$) { # decode it #$msg = hex($msg); # /125; only for col or coc $msg = sprintf("%d %02d:%02d:%02d", $msg/86400, ($msg%86400)/3600, ($msg%3600)/60, $msg%60); - } - + } + elsif($cmd eq "ccregAll") + { + $msg =~ s/ /\n/g; + $msg = "\n\n" . $msg + } + elsif($cmd eq "ccconf") + { + my (undef,$str) = split('=', $msg); + my $var; + my %r = ( "0D"=>1,"0E"=>1,"0F"=>1,"10"=>1,"11"=>1,"1B"=>1,"1D"=>1 ); + $msg = ""; + foreach my $a (sort keys %r) { + $var = substr($str,(hex($a)-13)*2, 2); + $r{$a} = hex($var); + } + $msg = sprintf("freq:%.3fMHz bWidth:%dKHz rAmpl:%ddB sens:%ddB (DataRate:%.2fBaud)", + 26*(($r{"0D"}*256+$r{"0E"})*256+$r{"0F"})/65536, #Freq + 26000/(8 * (4+(($r{"10"}>>4)&3)) * (1 << (($r{"10"}>>6)&3))), #Bw + $ampllist[$r{"1B"}&7], #rAmpl + 4+4*($r{"1D"}&3), #Sens + ((256+$r{"11"})*(2**($r{"10"} & 15 )))*26000000/(2**28) #DataRate + ); + } + elsif($cmd eq "bWidth") { + my $val = hex(substr($msg,6)); + my $arg = $hash->{getcmd}->{arg}; + my $ob = $val & 0x0f; + + my ($bits, $bw) = (0,0); + OUTERLOOP: + for (my $e = 0; $e < 4; $e++) { + for (my $m = 0; $m < 4; $m++) { + $bits = ($e<<6)+($m<<4); + $bw = int(26000/(8 * (4+$m) * (1 << $e))); # KHz + last OUTERLOOP if($arg >= $bw); + } + } + + $ob = sprintf("%02x", $ob+$bits); + $msg = "Setting MDMCFG4 (10) to $ob = $bw KHz"; + Log3 $name, 3, "$name/msg parseResponse bWidth: Setting MDMCFG4 (10) to $ob = $bw KHz"; + delete($hash->{getcmd}); + SIGNALduino_AddSendQueue($hash,"W12$ob"); + SIGNALduino_WriteInit($hash); + } + elsif($cmd eq "ccpatable") { + my $CC1101Frequency = "433"; + if (defined($hash->{cc1101_frequency})) { + $CC1101Frequency = $hash->{cc1101_frequency}; + } + my $dBn = substr($msg,9,2); + Log3 $name, 3, "$name/msg parseResponse patable: $dBn"; + foreach my $dB (keys %{ $patable{$CC1101Frequency} }) { + if ($dBn eq $patable{$CC1101Frequency}{$dB}) { + Log3 $name, 5, "$name/msg parseResponse patable: $dB"; + $msg .= " => $dB"; + last; + } + } + # $msg .= "\n\n$CC1101Frequency MHz\n\n"; + # foreach my $dB (keys $patable{$CC1101Frequency}) + # { + # $msg .= "$patable{$CC1101Frequency}{$dB} $dB\n"; + # } + } + return $msg; } @@ -1603,7 +2324,10 @@ SIGNALduino_DoInit($) RemoveInternalTimer("HandleWriteQueue:$name"); @{$hash->{QUEUE}} = (); - if (($hash->{DEF} !~ m/\@DirectIO/) and ($hash->{DEF} !~ m/none/) ) + $hash->{sendworking} = 0; + + # if (($hash->{DEF} !~ m/\@DirectIO/) and ($hash->{DEF} !~ m/none/) ) + if (($hash->{DEF} !~ m/\@directio/) and ($hash->{DEF} !~ m/none/) ) { Log3 $hash, 1, "$name/init: ".$hash->{DEF}; $hash->{initretry} = 0; @@ -1618,7 +2342,7 @@ SIGNALduino_DoInit($) delete($hash->{XMIT_TIME}); delete($hash->{NR_CMD_LAST_H}); return; - #return undef; + return undef; } # Disable receiver @@ -1773,13 +2497,12 @@ sub SIGNALduino_AddSendQueue($$) my ($hash, $msg) = @_; my $name = $hash->{NAME}; - #Log3 $hash, 3,"AddSendQueue: " . $hash->{NAME} . ": $msg"; - push(@{$hash->{QUEUE}}, $msg); #Log3 $hash , 5, Dumper($hash->{QUEUE}); - InternalTimer(gettimeofday() + 0.1, "SIGNALduino_HandleWriteQueue", "HandleWriteQueue:$name") if (@{$hash->{QUEUE}} == 1); + Log3 $hash, 5,"AddSendQueue: " . $hash->{NAME} . ": $msg (" . @{$hash->{QUEUE}} . ")"; + InternalTimer(gettimeofday() + 0.1, "SIGNALduino_HandleWriteQueue", "HandleWriteQueue:$name") if (@{$hash->{QUEUE}} == 1 && $hash->{sendworking} == 0); } @@ -1792,10 +2515,14 @@ SIGNALduino_SendFromQueue($$) if($msg ne "") { SIGNALduino_XmitLimitCheck($hash,$msg); #DevIo_SimpleWrite($hash, $msg . "\n", 2); + $hash->{sendworking} = 1; SIGNALduino_SimpleWrite($hash,$msg); if ($msg =~ m/^S(R|C|M);/) { $hash->{getcmd}->{cmd} = 'sendraw'; - Log3 $hash, 4, "$name SendFromQueue: msg=$msg"; # zu testen der Queue, kann wenn es funktioniert auskommentiert werden + Log3 $hash, 4, "$name SendrawFromQueue: msg=$msg"; # zu testen der Queue, kann wenn es funktioniert auskommentiert werden + } + elsif ($msg eq "C99") { + $hash->{getcmd}->{cmd} = 'ccregAll'; } } @@ -1820,6 +2547,8 @@ SIGNALduino_HandleWriteQueue($) #my @arr = @{$hash->{QUEUE}}; + $hash->{sendworking} = 0; # es wurde gesendet + if (defined($hash->{getcmd}->{cmd}) && $hash->{getcmd}->{cmd} eq 'sendraw') { Log3 $name, 4, "$name/HandleWriteQueue: sendraw no answer (timeout)"; delete($hash->{getcmd}); @@ -1859,13 +2588,90 @@ SIGNALduino_Read($) my $rmsg; ($rmsg,$SIGNALduinodata) = split("\n", $SIGNALduinodata, 2); $rmsg =~ s/\r//; - Log3 $name, 4, "$name/msg READ: $rmsg"; + + if ($rmsg =~ m/^\002(M(s|u);.*;)\003/) { + $rmsg =~ s/^\002//; # \002 am Anfang entfernen + my @msg_parts = split(";",$rmsg); + my $m0; + my $mnr0; + my $m1; + my $mL; + my $mH; + my $part = ""; + my $partD; + + foreach my $msgPart (@msg_parts) { + $m0 = substr($msgPart,0,1); + $mnr0 = ord($m0); + $m1 = substr($msgPart,1); + if ($m0 eq "M") { + $part .= "M" . uc($m1) . ";"; + } + elsif ($mnr0 > 127) { + $part .= "P" . sprintf("%u", ($mnr0 & 7)) . "="; + if (length($m1) == 2) { + $mL = ord(substr($m1,0,1)) & 127; # Pattern low + $mH = ord(substr($m1,1,1)) & 127; # Pattern high + if (($mnr0 & 0b00100000) != 0) { # Vorzeichen 0b00100000 = 32 + $part .= "-"; + } + if ($mnr0 & 0b00010000) { # Bit 7 von Pattern low + $mL += 128; + } + $part .= ($mH * 256) + $mL; + } + $part .= ";"; + } + elsif (($m0 eq "D" || $m0 eq "d") && length($m1) > 0) { + my @arrayD = split(//, $m1); + $part .= "D="; + $partD = ""; + foreach my $D (@arrayD) { + $mH = ord($D) >> 4; + $mL = ord($D) & 7; + $partD .= "$mH$mL"; + } + #Log3 $name, 3, "$name/msg READredu1$m0: $partD"; + if ($m0 eq "d") { + $partD =~ s/.$//; # letzte Ziffer entfernen wenn Anzahl der Ziffern ungerade + } + $partD =~ s/^8//; # 8 am Anfang entfernen + #Log3 $name, 3, "$name/msg READredu2$m0: $partD"; + $part = $part . $partD . ';'; + } + elsif (($m0 eq "C" || $m0 eq "S") && length($m1) == 1) { + $part .= "$m0" . "P=$m1;"; + } + elsif ($m1 =~ m/^[0-9A-Z]{1,2}$/) { # bei 1 oder 2 Hex Ziffern nach Dez wandeln + $part .= "$m0=" . hex($m1) . ";"; + } + elsif ($m0 =~m/[0-9a-zA-Z]/) { + $part .= "$m0"; + if ($m1 ne "") { + $part .= "=$m1"; + } + $part .= ";"; + } + } + Log3 $name, 4, "$name/msg READredu: $part"; + $rmsg = "\002$part\003"; + } + else { + Log3 $name, 4, "$name/msg READ: $rmsg"; + } + if ( $rmsg && !SIGNALduino_Parse($hash, $hash, $name, $rmsg) && defined($hash->{getcmd}) && defined($hash->{getcmd}->{cmd})) { my $regexp; if ($hash->{getcmd}->{cmd} eq 'sendraw') { $regexp = '^S(R|C|M);'; } + elsif ($hash->{getcmd}->{cmd} eq 'ccregAll') { + $regexp = '^ccreg 00:'; + } + elsif ($hash->{getcmd}->{cmd} eq 'bWidth') { + $regexp = '^C.* = .*'; + } else { $regexp = $gets{$hash->{getcmd}->{cmd}}[1]; } @@ -1897,7 +2703,9 @@ SIGNALduino_Read($) } else { $rmsg = SIGNALduino_parseResponse($hash,$hash->{getcmd}->{cmd},$rmsg); - readingsSingleUpdate($hash, $hash->{getcmd}->{cmd}, $rmsg, 0); + if (defined($hash->{getcmd}) && $hash->{getcmd}->{cmd} ne 'ccregAll') { + readingsSingleUpdate($hash, $hash->{getcmd}->{cmd}, $rmsg, 0); + } if (defined($hash->{getcmd}->{asyncOut})) { #Log3 $name, 4, "$name/msg READ: asyncOutput"; my $ao = asyncOutput( $hash->{getcmd}->{asyncOut}, $hash->{getcmd}->{cmd}.": " . $rmsg ); @@ -1920,24 +2728,30 @@ sub SIGNALduino_KeepAlive($){ return if ($hash->{DevState} eq 'disconnected'); - Log3 $name,4 , "$name/KeepAliveOk: " . $hash->{keepalive}{ok}; + #Log3 $name,4 , "$name/KeepAliveOk: " . $hash->{keepalive}{ok}; if (!$hash->{keepalive}{ok}) { delete($hash->{getcmd}); if ($hash->{keepalive}{retry} >= SDUINO_KEEPALIVE_MAXRETRY) { - Log3 $name,4 , "$name/keepalive retry count reached. Reset"; + Log3 $name,3 , "$name/keepalive not ok, retry count reached. Reset"; $hash->{DevState} = 'INACTIVE'; SIGNALduino_ResetDevice($hash); return; } else { + my $logLevel = 3; $hash->{keepalive}{retry} ++; - Log3 $name,3 , "$name/KeepAliveOk: " . $hash->{keepalive}{ok} . " retry = " . $hash->{keepalive}{retry} . " -> get ping"; + if ($hash->{keepalive}{retry} == 1) { + $logLevel = 4; + } + Log3 $name, $logLevel, "$name/KeepAlive not ok, retry = " . $hash->{keepalive}{retry} . " -> get ping"; $hash->{getcmd}->{cmd} = "ping"; SIGNALduino_AddSendQueue($hash, "P"); #SIGNALduino_SimpleWrite($hash, "P"); } } - Log3 $name,4 , "$name/keepalive retry = " . $hash->{keepalive}{retry}; + else { + Log3 $name,4 , "$name/keepalive ok, retry = " . $hash->{keepalive}{retry}; + } $hash->{keepalive}{ok} = 0; InternalTimer(gettimeofday() + SDUINO_KEEPALIVE_TIMEOUT, "SIGNALduino_KeepAlive", $hash); @@ -1946,6 +2760,55 @@ sub SIGNALduino_KeepAlive($){ ### Helper Subs >>> + +## Parses a HTTP Response for example for flash via http download +sub SIGNALduino_ParseHttpResponse +{ + + my ($param, $err, $data) = @_; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; + + if($err ne "") # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist + { + Log3 $name, 3, "error while requesting ".$param->{url}." - $err"; # Eintrag fürs Log + } + elsif($param->{code} eq "200" && $data ne "") # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes) + { + + Log3 $name, 3, "url ".$param->{url}." returned: ".length($data)." bytes Data"; # Eintrag fürs Log + + if ($param->{command} eq "flash") + { + my $filename; + + if ($param->{httpheader} =~ /Content-Disposition: attachment;filename=\"?([-+.\w]+)?\"/) + { + $filename = $1; + } else { # Filename via path if not specifyied via Content-Disposition + ($filename = $param->{path}) =~s/.*\///; + } + + Log3 $name, 3, "$name: Downloaded $filename firmware from ".$param->{host}; + Log3 $name, 5, "$name: Header = ".$param->{httpheader}; + + + $filename = "FHEM/firmware/" . $filename; + open(my $file, ">", $filename) or die $!; + print $file $data; + close $file; + + # Den Flash Befehl mit der soebene heruntergeladenen Datei ausführen + #Log3 $name, 3, "calling set ".$param->{command}." $filename"; # Eintrag fürs Log + + SIGNALduino_Set($hash,$name,$param->{command},$filename); # $hash->{SetFn} + + } + } else { + Log3 $name, 3, "undefined error while requesting ".$param->{url}." - $err - code=".$param->{code}; # Eintrag fürs Log + } +} + sub SIGNALduino_splitMsg { my $txt = shift; @@ -1980,6 +2843,8 @@ sub SIGNALduino_PatternExists #Debug "plist: ".Dumper($patternList) if($debug); #Debug "searchlist: ".Dumper($search) if($debug); + + my $searchpattern; my $valid=1; my @pstr; @@ -2129,6 +2994,7 @@ sub SIGNALduino_Split_Message($$) my $rawData; my $clockabs; my $mcbitnum; + my $rssi; my @msg_parts = SIGNALduino_splitMsg($rmsg,';'); ## Split message parts by ";" my %ret; @@ -2188,6 +3054,13 @@ sub SIGNALduino_Split_Message($$) $clockabs = $_ ; Debug "$name: extracted absolute clock $clockabs \n" if ($debug); $ret{clockabs} = $clockabs; + } + elsif($_ =~ m/^R=\d+/) ### RSSI ### + { + $_ =~ s/R=//; + $rssi = $_ ; + Debug "$name: extracted RSSI $rssi \n" if ($debug); + $ret{rssi} = $rssi; } else { Debug "$name: unknown Message part $_" if ($debug);; } @@ -2200,36 +3073,66 @@ sub SIGNALduino_Split_Message($$) # Function which dispatches a message if needed. -sub SIGNALduno_Dispatch($$$) +sub SIGNALduno_Dispatch($$$$$) { - my ($hash, $rmsg, $dmsg) = @_; + my ($hash, $rmsg, $dmsg, $rssi, $id) = @_; my $name = $hash->{NAME}; if (!defined($dmsg)) { - Log3 $name, 5, "$name: (SIGNALduno_Dispatch) dmsg is undef. Skipping dispatch call"; + Log3 $name, 5, "$name Dispatch: dmsg is undef. Skipping dispatch call"; return; } - Log3 $name, 5, "$name: converted Data to ($dmsg)"; + #Log3 $name, 5, "$name: Dispatch DMSG: $dmsg"; + + my $DMSGgleich = 1; + if ($dmsg eq $hash->{LASTDMSG}) { + Log3 $name, SDUINO_DISPATCH_VERBOSE, "$name Dispatch: $dmsg, test gleich"; + } else { + if (defined($hash->{DoubleMsgIDs}{$id})) { + $DMSGgleich = 0; + Log3 $name, SDUINO_DISPATCH_VERBOSE, "$name Dispatch: $dmsg, test ungleich"; + } + else { + Log3 $name, SDUINO_DISPATCH_VERBOSE, "$name Dispatch: $dmsg, test ungleich: disabled"; + } + $hash->{LASTDMSG} = $dmsg; + } - #Dispatch only if $dmsg is different from last $dmsg, or if 2 seconds are between transmits - if ( ($hash->{DMSG} ne $dmsg) || ($hash->{TIME}+1 < time()) ) { + if ($DMSGgleich) { + #Dispatch if dispatchequals is provided in protocol definition or only if $dmsg is different from last $dmsg, or if 2 seconds are between transmits + if ( (SIGNALduino_getProtoProp($id,'dispatchequals',0) eq 'true') || ($hash->{DMSG} ne $dmsg) || ($hash->{TIME}+2 < time() ) ) { $hash->{MSGCNT}++; $hash->{TIME} = time(); $hash->{DMSG} = $dmsg; - my $event = 0; - if (substr($dmsg,0,1) eq 'u') { - $event = 1; + #my $event = 0; + if (substr(ucfirst($dmsg),0,1) eq 'U') { + #$event = 1; + DoTrigger($name, "DMSG " . $dmsg); } - readingsSingleUpdate($hash, "state", $hash->{READINGS}{state}{VAL}, $event); + #readingsSingleUpdate($hash, "state", $hash->{READINGS}{state}{VAL}, $event); + $hash->{RAWMSG} = $rmsg; - my %addvals = (RAWMSG => $rmsg, DMSG => $dmsg); + my %addvals = (DMSG => $dmsg); + if (AttrVal($name,"suppressDeviceRawmsg",0) == 0) { + $addvals{RAWMSG} = $rmsg + } + if(defined($rssi)) { + $hash->{RSSI} = $rssi; + $addvals{RSSI} = $rssi; + $rssi .= " dB," + } + else { + $rssi = ""; + } + Log3 $name, SDUINO_DISPATCH_VERBOSE, "$name Dispatch: $dmsg, $rssi dispatch"; Dispatch($hash, $dmsg, \%addvals); ## Dispatch to other Modules } else { - Log3 $name, 4, "$name: Dropped ($dmsg) due to short time or equal msg"; - } + Log3 $name, 4, "$name Dispatch: $dmsg, Dropped due to short time or equal msg"; + } + } } sub @@ -2240,9 +3143,14 @@ SIGNALduino_Parse_MS($$$$%) my $protocolid; my $syncidx=$msg_parts{syncidx}; my $clockidx=$msg_parts{clockidx}; + my $rawRssi=$msg_parts{rssi}; my $protocol=undef; my $rawData=$msg_parts{rawData}; my %patternList; + my $rssi; + if (defined($rawRssi)) { + $rssi = ($rawRssi>=128 ? (($rawRssi-256)/2-74) : ($rawRssi/2-74)); # todo: passt dies so? habe ich vom 00_cul.pm + } #$patternList{$_} = $msg_parts{rawData}{$_] for keys %msg_parts{rawData}; #$patternList = \%msg_parts{pattern}; @@ -2294,8 +3202,8 @@ SIGNALduino_Parse_MS($$$$%) #Check calculated max length $valid = $valid && $ProtocolListSIGNALduino{$id}{length_max} >= $bit_length if (exists $ProtocolListSIGNALduino{$id}{length_max}); + Debug "expecting $bit_length bits in signal" if ($debug); next if (!$valid) ; - Debug "expecting $bit_length bits in signal" if ($debug && $valid); #Debug Dumper(@{$ProtocolListSIGNALduino{$id}{sync}}); Debug "Searching in patternList: ".Dumper(\%patternList) if($debug); @@ -2366,7 +3274,7 @@ SIGNALduino_Parse_MS($$$$%) Debug "$name: decoded message raw (@bit_msg), ".@bit_msg." bits\n" if ($debug);; my ($rcode,@retvalue) = SIGNALduino_callsub('postDemodulation',$ProtocolListSIGNALduino{$id}{postDemodulation},$name,@bit_msg); - next if (!$rcode); + next if ($rcode < 1 ); #Log3 $name, 5, "$name: postdemodulation value @retvalue"; @bit_msg = @retvalue; @@ -2391,25 +3299,43 @@ SIGNALduino_Parse_MS($$$$%) #my $dmsg = sprintf "%02x", oct "0b" . join "", @bit_msg; ## Array -> String -> bin -> hex my $dmsg = SIGNALduino_b2h(join "", @bit_msg); - $dmsg = "$dmsg"."$ProtocolListSIGNALduino{$id}{postamble}" if (defined($ProtocolListSIGNALduino{$id}{postamble})); + my $postamble = $ProtocolListSIGNALduino{$id}{postamble}; + #if (defined($rawRssi)) { + #if (defined($ProtocolListSIGNALduino{$id}{preamble}) && $ProtocolListSIGNALduino{$id}{preamble} eq "s") { + # $postamble = sprintf("%02X", $rawRssi); + #} elsif ($id eq "7") { + # $postamble = "#R" . sprintf("%02X", $rawRssi); + #} + #} + $dmsg = "$dmsg".$postamble if (defined($postamble)); $dmsg = "$ProtocolListSIGNALduino{$id}{preamble}"."$dmsg" if (defined($ProtocolListSIGNALduino{$id}{preamble})); - Log3 $name, 4, "$name: Decoded MS Protocol id $id dmsg $dmsg length " . scalar @bit_msg; - + if (defined($rssi)) { + Log3 $name, 4, "$name: Decoded MS Protocol id $id dmsg $dmsg length " . scalar @bit_msg . " RSSI = $rssi"; + } else { + Log3 $name, 4, "$name: Decoded MS Protocol id $id dmsg $dmsg length " . scalar @bit_msg; + } #my ($rcode,@retvalue) = SIGNALduino_callsub('preDispatchfunc',$ProtocolListSIGNALduino{$id}{preDispatchfunc},$name,$dmsg); #next if (!$rcode); #$dmsg = @retvalue; #undef(@retvalue); undef($rcode); - - my $modulematch; + my $modulematch = undef; if (defined($ProtocolListSIGNALduino{$id}{modulematch})) { $modulematch = $ProtocolListSIGNALduino{$id}{modulematch}; } if (!defined($modulematch) || $dmsg =~ m/$modulematch/) { Debug "$name: dispatching now msg: $dmsg" if ($debug); - SIGNALduno_Dispatch($hash,$rmsg,$dmsg); + if (defined($ProtocolListSIGNALduino{$id}{developId}) && substr($ProtocolListSIGNALduino{$id}{developId},0,1) eq "m") { + my $devid = "m$id"; + my $develop = lc(AttrVal($name,"development","")); + if ($develop !~ m/$devid/) { # kein dispatch wenn die Id nicht im Attribut development steht + Log3 $name, 3, "$name: ID=$devid skiped dispatch (developId=m). To use, please add m$id to the attr development"; + next; + } + } + SIGNALduno_Dispatch($hash,$rmsg,$dmsg,$rssi,$id); $message_dispatched=1; } } @@ -2437,18 +3363,21 @@ sub SIGNALduino_padbits(\@$) # - - - - - - - - - - - - #=item SIGNALduino_getProtoProp() -#This functons, will return a value from the Protocolist and check if it is defined +#This functons, will return a value from the Protocolist and check if it is defined optional you can specify a optional default value that will be reurned # # returns "" if the var is not defined # =cut # $id, $propertyname, -sub SIGNALduino_getProtoProp($$) +sub SIGNALduino_getProtoProp { - my $id = shift; - my $propNameLst = shift; + my ($id,$propNameLst,$default) = @_; + + #my $id = shift; + #my $propNameLst = shift; return $ProtocolListSIGNALduino{$id}{$propNameLst} if defined($ProtocolListSIGNALduino{$id}{$propNameLst}); - return undef; + return $default; # Will return undef if $default is not provided + #return undef; } sub SIGNALduino_Parse_MU($$$$@) @@ -2456,17 +3385,21 @@ sub SIGNALduino_Parse_MU($$$$@) my ($hash, $iohash, $name, $rmsg,%msg_parts) = @_; my $protocolid; - my $clockidx=$msg_parts{clockidx}; + my $clockidx=$msg_parts{clockidx}; + my $rssi=$msg_parts{rssi}; my $protocol=undef; my $rawData; my %patternListRaw; my $message_dispatched=0; - my $debug = AttrVal($iohash->{NAME},"debug",0); + if (defined($rssi)) { + $rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74)); # todo: passt dies so? habe ich vom 00_cul.pm + } + Debug "$name: processing unsynced message\n" if ($debug); - #my $clockabs; #Clock will be fetched from Protocol + my $clockabs = 1; #Clock will be fetched from Protocol if possible #$patternListRaw{$_} = floor($msg_parts{pattern}{$_}/$clockabs) for keys $msg_parts{pattern}; $patternListRaw{$_} = $msg_parts{pattern}{$_} for keys %{$msg_parts{pattern}}; @@ -2482,7 +3415,7 @@ sub SIGNALduino_Parse_MU($$$$@) foreach $id (@{$hash->{muIdList}}) { my $valid=1; - my $clockabs= $ProtocolListSIGNALduino{$id}{clockabs}; + $clockabs= $ProtocolListSIGNALduino{$id}{clockabs}; my %patternList; $rawData=$msg_parts{rawData}; if (exists($ProtocolListSIGNALduino{$id}{filterfunc})) @@ -2540,17 +3473,18 @@ sub SIGNALduino_Parse_MU($$$$@) my $pstr=""; $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{one}},\%patternList,\$rawData)) >=0; Debug "Found matched one" if ($debug && $valid); - - my $oneStr=$pstr if ($valid); $patternLookupHash{$pstr}="1" if ($valid); ## Append one to our lookuptable Debug "added $pstr " if ($debug && $valid); - $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList,\$rawData)) >=0; - Debug "Found matched zero" if ($debug && $valid); - my $zeroStr=$pstr if ($valid); - $patternLookupHash{$pstr}="0" if ($valid); ## Append zero to our lookuptable - Debug "added $pstr " if ($debug && $valid); + my $zeroStr =""; + if (scalar @{$ProtocolListSIGNALduino{$id}{zero}} >0) { + $valid = $valid && ($pstr=SIGNALduino_PatternExists($hash,\@{$ProtocolListSIGNALduino{$id}{zero}},\%patternList,\$rawData)) >=0; + Debug "Found matched zero" if ($debug && $valid); + $zeroStr=$pstr if ($valid); + $patternLookupHash{$pstr}="0" if ($valid); ## Append zero to our lookuptable + Debug "added $pstr " if ($debug && $valid); + } if (defined($ProtocolListSIGNALduino{$id}{float})) { @@ -2583,17 +3517,21 @@ sub SIGNALduino_Parse_MU($$$$@) if (@msgStartLst = SIGNALduino_getProtoProp($id,"start")) { Debug "msgStartLst: ".Dumper(@msgStartLst) if ($debug); - - if ( ($startStr=SIGNALduino_PatternExists($hash,@msgStartLst,\%patternList,\$rawData)) eq -1) - { - Log3 $name, 5, "$name: start pattern for MU Protocol id $id -> $ProtocolListSIGNALduino{$id}{name} mismatches, aborting" ; - $valid=0; - next; - }; - } - $start_regex="$startStr($oneStr|$zeroStr)"; - Debug "Regex is: $start_regex" if ($debug); + if ( ($startStr=SIGNALduino_PatternExists($hash,@msgStartLst,\%patternList,\$rawData)) eq -1) + { + Log3 $name, 5, "$name: start pattern for MU Protocol id $id -> $ProtocolListSIGNALduino{$id}{name} mismatches, aborting" ; + $valid=0; + next; + }; + } + + + if (length ($zeroStr) > 0 ){ $start_regex = "$startStr\($oneStr\|$zeroStr\)";} + else {$start_regex = $startStr.$oneStr; } + + Debug "Regex is: $start_regex" if ($debug); + $rawData =~ /$start_regex/; if (defined($-[0] && $-[0] > 0)) { $message_start=$-[0]+ length($startStr); @@ -2639,7 +3577,8 @@ sub SIGNALduino_Parse_MU($$$$@) if ($valid) { my ($rcode,@retvalue) = SIGNALduino_callsub('postDemodulation',$ProtocolListSIGNALduino{$id}{postDemodulation},$name,@bit_msg); - next if (!$rcode); + next if ($rcode < 1 ); + #Log3 $name, 5, "$name: postdemodulation value @retvalue"; @bit_msg = @retvalue; @@ -2658,7 +3597,11 @@ sub SIGNALduino_Parse_MU($$$$@) $dmsg = "$dmsg"."$ProtocolListSIGNALduino{$id}{postamble}" if (defined($ProtocolListSIGNALduino{$id}{postamble})); $dmsg = "$ProtocolListSIGNALduino{$id}{preamble}"."$dmsg" if (defined($ProtocolListSIGNALduino{$id}{preamble})); - Log3 $name, 4, "$name: decoded matched MU Protocol id $id dmsg $dmsg length " . scalar @bit_msg; + if (defined($rssi)) { + Log3 $name, 4, "$name: decoded matched MU Protocol id $id dmsg $dmsg length " . scalar @bit_msg . " RSSI = $rssi"; + } else { + Log3 $name, 4, "$name: decoded matched MU Protocol id $id dmsg $dmsg length " . scalar @bit_msg; + } my $modulematch; if (defined($ProtocolListSIGNALduino{$id}{modulematch})) { @@ -2666,7 +3609,16 @@ sub SIGNALduino_Parse_MU($$$$@) } if (!defined($modulematch) || $dmsg =~ m/$modulematch/) { Debug "$name: dispatching now msg: $dmsg" if ($debug); - SIGNALduno_Dispatch($hash,$rmsg,$dmsg); + if (defined($ProtocolListSIGNALduino{$id}{developId}) && substr($ProtocolListSIGNALduino{$id}{developId},0,1) eq "m") { + my $devid = "m$id"; + my $develop = lc(AttrVal($name,"development","")); + if ($develop !~ m/$devid/) { # kein dispatch wenn die Id nicht im Attribut development steht + Log3 $name, 3, "$name: ID=$devid skiped dispatch (developId=m). To use, please add m$id to the attr development"; + next; + } + } + + SIGNALduno_Dispatch($hash,$rmsg,$dmsg,$rssi,$id); $message_dispatched=1; } } else { @@ -2691,7 +3643,6 @@ sub SIGNALduino_Parse_MU($$$$@) if (defined($-[0]) && ($-[0] > 0)) { #$i=$-[0]+ $i+ length($startStr); $i=$-[0]+ $i; - $i=$i-$signal_width if ($i>0 && length($startStr) == 0); #Todo: Debug "$name: found restart at Position $i ($regex)\n" if ($debug); } else { @@ -2728,10 +3679,15 @@ SIGNALduino_Parse_MC($$$$@) my ($hash, $iohash, $name, $rmsg,%msg_parts) = @_; my $clock=$msg_parts{clockabs}; ## absolute clock my $rawData=$msg_parts{rawData}; + my $rssi=$msg_parts{rssi}; + my $mcbitnum=$msg_parts{mcbitnum}; my $bitData; my $dmsg; my $message_dispatched=0; my $debug = AttrVal($iohash->{NAME},"debug",0); + if (defined($rssi)) { + $rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74)); # todo: passt dies so? habe ich vom 00_cul.pm + } return undef if (!$clock); #my $protocol=undef; @@ -2740,7 +3696,12 @@ SIGNALduino_Parse_MC($$$$@) Debug "$name: processing manchester messag len:".length($rawData) if ($debug); my $hlen = length($rawData); - my $blen = $hlen * 4; + my $blen; + #if (defined($mcbitnum)) { + # $blen = $mcbitnum; + #} else { + $blen = $hlen * 4; + #} my $id; my $rawDataInverted; @@ -2748,12 +3709,17 @@ SIGNALduino_Parse_MC($$$$@) foreach $id (@{$hash->{mcIdList}}) { - + #next if ($blen < $ProtocolListSIGNALduino{$id}{length_min} || $blen > $ProtocolListSIGNALduino{$id}{length_max}); + #if ( $clock >$ProtocolListSIGNALduino{$id}{clockrange}[0] and $clock <$ProtocolListSIGNALduino{$id}{clockrange}[1]); if ( $clock >$ProtocolListSIGNALduino{$id}{clockrange}[0] and $clock <$ProtocolListSIGNALduino{$id}{clockrange}[1] and length($rawData)*4 >= $ProtocolListSIGNALduino{$id}{length_min} ) { Debug "clock and min length matched" if ($debug); - Log3 $name, 4, "$name: Found manchester Protocol id $id clock $clock -> $ProtocolListSIGNALduino{$id}{name}"; + if (defined($rssi)) { + Log3 $name, 4, "$name: Found manchester Protocol id $id clock $clock RSSI $rssi -> $ProtocolListSIGNALduino{$id}{name}"; + } else { + Log3 $name, 4, "$name: Found manchester Protocol id $id clock $clock -> $ProtocolListSIGNALduino{$id}{name}"; + } if (exists($ProtocolListSIGNALduino{$id}{polarity}) && ($ProtocolListSIGNALduino{$id}{polarity} eq 'invert') && (!defined($hash->{version}) || substr($hash->{version},0,6) ne 'V 3.2.')) # todo && substr($hash->{version},0,6) ne 'V 3.2.') # bei version V 3.2. nicht invertieren @@ -2770,7 +3736,7 @@ SIGNALduino_Parse_MC($$$$@) { Log3 $name, 5, "$name: Error: Unknown function=$method. Please define it in file $0"; } else { - my ($rcode,$res) = $method->($name,$bitData,$id); + my ($rcode,$res) = $method->($name,$bitData,$id,$mcbitnum); if ($rcode != -1) { $dmsg = $res; $dmsg=$ProtocolListSIGNALduino{$id}{preamble}.$dmsg if (defined($ProtocolListSIGNALduino{$id}{preamble})); @@ -2779,11 +3745,20 @@ SIGNALduino_Parse_MC($$$$@) $modulematch = $ProtocolListSIGNALduino{$id}{modulematch}; } if (!defined($modulematch) || $dmsg =~ m/$modulematch/) { - SIGNALduno_Dispatch($hash,$rmsg,$dmsg); + if (defined($ProtocolListSIGNALduino{$id}{developId}) && substr($ProtocolListSIGNALduino{$id}{developId},0,1) eq "m") { + my $devid = "m$id"; + my $develop = lc(AttrVal($name,"development","")); + if ($develop !~ m/$devid/) { # kein dispatch wenn die Id nicht im Attribut development steht + Log3 $name, 3, "$name: ID=$devid skiped dispatch (developId=m). To use, please add m$id to the attr development"; + next; + } + } + SIGNALduno_Dispatch($hash,$rmsg,$dmsg,$rssi,$id); $message_dispatched=1; } } else { - Log3 $name, 5, "$name: protocol does not match return from method: ($res)" if ($debug); + $res="undef" if (!defined($res)); + Log3 $name, 5, "$name: protocol does not match return from method: ($res)" ; } } @@ -2801,9 +3776,14 @@ SIGNALduino_Parse($$$$@) my ($hash, $iohash, $name, $rmsg, $initstr) = @_; #print Dumper(\%ProtocolListSIGNALduino); + - return undef if !($rmsg=~ s/^\002(M.;.*;)\003/$1/); ## Check if a Data Message arrived and if it's complete (start & end control char are received) - # cut off start end end character from message for further processing they are not needed + if (!($rmsg=~ s/^\002(M.;.*;)\003/$1/)) # Check if a Data Message arrived and if it's complete (start & end control char are received) + { # cut off start end end character from message for further processing they are not needed + Log3 $name, AttrVal($name,"noMsgVerbose",5), "$name/noMsg Parse: $rmsg"; + return undef; + } + if (defined($hash->{keepalive})) { $hash->{keepalive}{ok} = 1; $hash->{keepalive}{retry} = 0; @@ -2814,6 +3794,10 @@ SIGNALduino_Parse($$$$@) Debug "$name: incomming message: ($rmsg)\n" if ($debug); + if (AttrVal($name, "rawmsgEvent", 0)) { + DoTrigger($name, "RAWMSG " . $rmsg); + } + my %signal_parts=SIGNALduino_Split_Message($rmsg,$name); ## Split message and save anything in an hash %signal_parts #Debug "raw data ". $signal_parts{rawData}; @@ -2882,6 +3866,17 @@ SIGNALduino_Ready($) return ($InBytes && $InBytes>0); } + +sub +SIGNALduino_WriteInit($) +{ + my ($hash) = @_; + + # todo: ist dies so ausreichend, damit die Aenderungen uebernommen werden? + SIGNALduino_AddSendQueue($hash,"WS36"); # SIDLE, Exit RX / TX, turn off frequency synthesizer + SIGNALduino_AddSendQueue($hash,"WS34"); # SRX, Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1. +} + ######################## sub SIGNALduino_SimpleWrite(@) @@ -2949,27 +3944,95 @@ SIGNALduino_Attr(@) } elsif ($aName eq "whitelist_IDs") { - SIGNALduino_IdList($hash, $name, $aVal); + Log3 $name, 3, "$name Attr: whitelist_IDs"; + if ($init_done) { # beim fhem Start wird das SIGNALduino_IdList nicht aufgerufen, da es beim define aufgerufen wird + SIGNALduino_IdList("x:$name",$aVal); + } + } + elsif ($aName eq "blacklist_IDs") + { + Log3 $name, 3, "$name Attr: blacklist_IDs"; + if ($init_done) { # beim fhem Start wird das SIGNALduino_IdList nicht aufgerufen, da es beim define aufgerufen wird + SIGNALduino_IdList("x:$name",undef,$aVal); + } + } + elsif ($aName eq "development") + { + Log3 $name, 3, "$name Attr: development"; + if ($init_done) { # beim fhem Start wird das SIGNALduino_IdList nicht aufgerufen, da es beim define aufgerufen wird + SIGNALduino_IdList("x:$name",undef,undef,$aVal); + } + } + elsif ($aName eq "doubleMsgCheck_IDs") + { + if (defined($aVal)) { + if (length($aVal)>0) { + if (substr($aVal,0 ,1) eq '#') { + Log3 $name, 3, "$name Attr: doubleMsgCheck_IDs disabled: $aVal"; + delete $hash->{DoubleMsgIDs}; + } + else { + Log3 $name, 3, "$name Attr: doubleMsgCheck_IDs enabled: $aVal"; + my %DoubleMsgiD = map { $_ => 1 } split(",", $aVal); + $hash->{DoubleMsgIDs} = \%DoubleMsgiD; + #print Dumper $hash->{DoubleMsgIDs}; + } + } + else { + Log3 $name, 3, "$name delete Attr: doubleMsgCheck_IDs"; + delete $hash->{DoubleMsgIDs}; + } + } + } + elsif ($aName eq "cc1101_frequency") + { + if ($aVal eq "" || $aVal < 800) { + Log3 $name, 3, "$name: delete cc1101_frequeny"; + delete ($hash->{cc1101_frequency}) if (defined($hash->{cc1101_frequency})); + } else { + Log3 $name, 3, "$name: setting cc1101_frequency to 868"; + $hash->{cc1101_frequency} = 868; + } } return undef; } -sub SIGNALduino_IdList($$$) +sub SIGNALduino_IdList($@) { - my ($hash, $name, $aVal) = @_; + my ($param, $aVal, $blacklist, $develop) = @_; + my (undef,$name) = split(':', $param); + my $hash = $defs{$name}; my @msIdList = (); my @muIdList = (); my @mcIdList = (); + if (!defined($aVal)) { + $aVal = AttrVal($name,"whitelist_IDs",""); + } + Log3 $name, 3, "$name sduinoIdList: whitelistIds=$aVal"; + + if (!defined($blacklist)) { + $blacklist = AttrVal($name,"blacklist_IDs",""); + } + Log3 $name, 3, "$name sduinoIdList: blacklistIds=$blacklist"; + + if (!defined($develop)) { + $develop = AttrVal($name,"development",""); + } + $develop = lc($develop); + Log3 $name, 3, "$name sduinoIdList: development=$develop"; + my %WhitelistIDs; - my $wflag = 0; + my %BlacklistIDs; + my $wflag = 0; # whitelist flag, 0=disabled + my $bflag = 0; # blacklist flag, 0=disabled if (defined($aVal) && length($aVal)>0) { if (substr($aVal,0 ,1) eq '#') { - Log3 $name, 3, "Attr whitelist deaktiviert: $aVal"; + Log3 $name, 3, "$name Attr whitelist disabled: $aVal"; } else { %WhitelistIDs = map { $_ => 1 } split(",", $aVal); @@ -2978,6 +4041,15 @@ sub SIGNALduino_IdList($$$) $wflag = 1; } } + if ($wflag == 0) { # whitelist disabled + if (defined($blacklist) && length($blacklist)>0) { + %BlacklistIDs = map { $_ => 1 } split(",", $blacklist); + my $w = join ', ' => map "$_" => keys %BlacklistIDs; + Log3 $name, 3, "$name Attr blacklist $w"; + $bflag = 1; + } + } + my $id; foreach $id (keys %ProtocolListSIGNALduino) { @@ -2985,8 +4057,28 @@ sub SIGNALduino_IdList($$$) if ($wflag == 1 && !defined($WhitelistIDs{$id})) { #Log3 $name, 3, "skip ID $id"; - next; - } + next; + } + if ($bflag == 1 && defined($BlacklistIDs{$id})) + { + Log3 $name, 3, "$name skip Blacklist ID $id"; + next; + } + + if (defined($ProtocolListSIGNALduino{$id}{developId}) && substr($ProtocolListSIGNALduino{$id}{developId},0,1) eq "p") { + my $devid = "p$id"; + if ($develop !~ m/$devid/) { # skip wenn die Id nicht im Attribut development steht + Log3 $name, 3, "$name: ID=$devid skiped (developId=p)"; + next; + } + } + + if (defined($ProtocolListSIGNALduino{$id}{developId}) && substr($ProtocolListSIGNALduino{$id}{developId},0,1) eq "y") { + if ($develop !~ m/y/) { # skip wenn y nicht im Attribut development steht + Log3 $name, 3, "$name: ID=$id skiped (developId=y)"; + next; + } + } if (exists ($ProtocolListSIGNALduino{$id}{format}) && $ProtocolListSIGNALduino{$id}{format} eq "manchester") { @@ -3026,13 +4118,14 @@ sub SIGNALduino_callsub if ( defined $method && defined &$method ) { - Log3 $name, 5, "$name: applying $funcname method $method"; + #my $subname = @{[eval {&$method}, $@ =~ /.*/]}; + Log3 $name, 5, "$name: applying $funcname"; # method $subname"; #Log3 $name, 5, "$name: value bevore $funcname: @args"; - my @returnvalues = $method->(@args) ; + my ($rcode, @returnvalues) = $method->($name, @args) ; Log3 $name, 5, "$name: modified value after $funcname: @returnvalues"; - return (1,@returnvalues); + return ($rcode, @returnvalues); } elsif (defined $method ) { Log3 $name, 5, "$name: Error: Unknown method $funcname Please check definition"; return (0,undef); @@ -3046,23 +4139,26 @@ sub SIGNALduino_callsub # output = @list sub SIGNALduino_lengtnPrefix { - my $msg = join("",@_); + my ($name, @bit_msg) = @_; + + my $msg = join("",@bit_msg); #$msg = unpack("B8", pack("N", length($msg))).$msg; $msg=sprintf('%08b', length($msg)).$msg; - return split("",$msg); + return (1,split("",$msg)); } sub SIGNALduino_bit2Arctec { - my $msg = join("",@_); + my ($name, @bit_msg) = @_; + my $msg = join("",@bit_msg); # Convert 0 -> 01 1 -> 10 to be compatible with IT Module $msg =~ s/0/z/g; $msg =~ s/1/10/g; $msg =~ s/z/01/g; - return split("",$msg); + return (1,split("",$msg)); } @@ -3075,13 +4171,427 @@ sub SIGNALduino_ITV1_tristateToBit($) $msg =~ s/F/01/g; $msg =~ s/D/10/g; - return $msg; + return (1,$msg); +} + +sub SIGNALduino_HE($@) { + my ($name, @bit_msg) = @_; + my $msg = join("",@bit_msg); + + #Log3 $name, 4, "$name HE: $msg"; + Log3 $name, 4, "$name HE: " . substr($msg,0,11) ." ". substr($msg,11,32) ." ". substr($msg,43,4) ." ". substr($msg,47,2) ." ". substr($msg,49,2) ." ". substr($msg,51); + + return (1,split("",$msg)); +} + +sub SIGNALduino_postDemo_Hoermann($@) { + my ($name, @bit_msg) = @_; + my $msg = join("",@bit_msg); + + if (substr($msg,0,9) ne "000000001") { # check ident + Log3 $name, 4, "$name: Hoermann ERROR - Ident not 000000001"; + return 0, undef; + } else { + Log3 $name, 5, "$name: Hoermann $msg"; + $msg = substr($msg,9); + return (1,split("",$msg)); + } +} + +sub SIGNALduino_postDemo_FS20($@) { + my ($name, @bit_msg) = @_; + my $datastart = 0; + my $protolength = scalar @bit_msg; + my $sum = 0; + my $b = 0; + my $i = 0; + for ($datastart = 0; $datastart < $protolength; $datastart++) { # Start bei erstem Bit mit Wert 1 suchen + last if $bit_msg[$datastart] eq "1"; + } + if ($datastart == $protolength) { # all bits are 0 + Log3 $name, 3, "$name: FS20 - ERROR message all bit are zeros"; + return 0, undef; + } + splice(@bit_msg, 0, $datastart + 1); # delete preamble + 1 bit + $protolength = scalar @bit_msg; + + if ($protolength == 45) { ### FS20 length 45 or 54 + for(my $b = 0; $b < 36; $b += 9) { # build sum over first 4 bytes + $sum += oct( "0b".(join "", @bit_msg[$b .. $b + 7])); + } + my $checksum = oct( "0b".(join "", @bit_msg[36 .. 43])); # Checksum Byte 5 + if (((($sum + 6) & 0xFF) - $checksum) == 0) { ## FHT80TF Tuer-/Fensterkontakt + for(my $b = 0; $b < 45; $b += 9) { # check parity over 5 byte + my $parity = 0; # Parity even + for(my $i = $b; $i < $b + 9; $i++) { # Parity over 1 byte + 1 bit + $parity += $bit_msg[$i]; + } + if ($parity % 2 != 0) { + Log3 $name, 3, "$name: FS20 ERROR - Parity not even"; + return 0, undef; + } + } # parity ok + for(my $b = 44; $b > 0; $b -= 9) { # delete 5 parity bits + splice(@bit_msg, $b, 1); + } + splice(@bit_msg, 32, 8); # delete checksum + splice(@bit_msg, 24, 0, (0,0,0,0,0,0,0,0)); # insert Byte 3 + Log3 $name, 4, "$name: FS20 - remote control protolength $protolength"; + return (1, @bit_msg); ## FHT80TF ok + } + } + + if ($protolength == 54) { ### FS20 length 45 or 54 + for($b = 0; $b < 45; $b += 9) { # build sum over first 5 bytes + $sum += oct( "0b".(join "", @bit_msg[$b .. $b + 7])); + } + my $checksum = oct( "0b".(join "", @bit_msg[45 .. 52])); # Checksum Byte 6 + if (((($sum + 6) & 0xFF) - $checksum) == 0) { ## FHT80 Raumthermostat + for($b = 0; $b < 55; $b += 9) { # check parity over 6 byte + my $parity = 0; # Parity even + for($i = $b; $i < $b + 9; $i++) { # Parity over 1 byte + 1 bit + $parity += $bit_msg[$i]; + } + if ($parity % 2 != 0) { + Log3 $name, 3, "$name: FHT80 ERROR - Parity not even"; + return 0, undef; + } + } # parity ok + for($b = 53; $b > 0; $b -= 9) { # delete 6 parity bits + splice(@bit_msg, $b, 1); + } + splice(@bit_msg, 40, 8); # delete checksum + Log3 $name, 4, "$name: FS20 - remote control protolength $protolength"; + return (1, @bit_msg); ## FHT80 ok + } + } + return 0, undef; +} + +sub SIGNALduino_postDemo_FHT80($@) { + my ($name, @bit_msg) = @_; + my $datastart = 0; + my $protolength = scalar @bit_msg; + my $sum = 0; + my $b = 0; + my $i = 0; + # if ($protolength < 66) { # min 6 bytes + 6 bits + # Log3 $name, 3, "$name: FHT80 - ERROR lenght of message < 66"; + # return 0, undef; + # } + for ($datastart = 0; $datastart < $protolength; $datastart++) { # Start bei erstem Bit mit Wert 1 suchen + last if $bit_msg[$datastart] eq "1"; + } + if ($datastart == $protolength) { # all bits are 0 + Log3 $name, 3, "$name: FHT80 - ERROR message all bit are zeros"; + return 0, undef; + } + splice(@bit_msg, 0, $datastart + 1); # delete preamble + 1 bit + $protolength = scalar @bit_msg; + if ($protolength == 54) { ### FHT80 fixed length + for($b = 0; $b < 45; $b += 9) { # build sum over first 5 bytes + $sum += oct( "0b".(join "", @bit_msg[$b .. $b + 7])); + } + my $checksum = oct( "0b".(join "", @bit_msg[45 .. 52])); # Checksum Byte 6 + if (((($sum + 12) & 0xFF) - $checksum) == 0) { ## FHT80 Raumthermostat + for($b = 0; $b < 55; $b += 9) { # check parity over 6 byte + my $parity = 0; # Parity even + for($i = $b; $i < $b + 9; $i++) { # Parity over 1 byte + 1 bit + $parity += $bit_msg[$i]; + } + if ($parity % 2 != 0) { + Log3 $name, 3, "$name: FHT80 ERROR - Parity not even"; + return 0, undef; + } + } # parity ok + for($b = 53; $b > 0; $b -= 9) { # delete 6 parity bits + splice(@bit_msg, $b, 1); + } + if ($bit_msg[26] != 1) { # Bit 5 Byte 3 must 1 + Log3 $name, 3, "$name: FHT80 ERROR - byte 3 bit 5 not 1"; + return 0, undef; + } + splice(@bit_msg, 40, 8); # delete checksum + splice(@bit_msg, 24, 0, (0,0,0,0,0,0,0,0)); # insert Byte 3 + Log3 $name, 4, "$name: FHT80 - roomthermostat protolength $protolength"; + return (1, @bit_msg); ## FHT80 ok + } + } + return 0, undef; +} + +sub SIGNALduino_postDemo_FHT80TF($@) { + my ($name, @bit_msg) = @_; + my $datastart = 0; + my $protolength = scalar @bit_msg; + my $sum = 0; + my $b = 0; + if ($protolength < 46) { # min 5 bytes + 6 bits + Log3 $name, 4, "$name: FHT80TF or FS20 - ERROR lenght of message < 46"; + return 0, undef; + } + for ($datastart = 0; $datastart < $protolength; $datastart++) { # Start bei erstem Bit mit Wert 1 suchen + last if $bit_msg[$datastart] eq "1"; + } + if ($datastart == $protolength) { # all bits are 0 + Log3 $name, 3, "$name: FHTTF or FS20 - ERROR message all bit are zeros"; + return 0, undef; + } + splice(@bit_msg, 0, $datastart + 1); # delete preamble + 1 bit + $protolength = scalar @bit_msg; + if ($protolength == 45) { ### FHT80TF fixed length + for(my $b = 0; $b < 36; $b += 9) { # build sum over first 4 bytes + $sum += oct( "0b".(join "", @bit_msg[$b .. $b + 7])); + } + my $checksum = oct( "0b".(join "", @bit_msg[36 .. 43])); # Checksum Byte 5 + if (((($sum + 12) & 0xFF) - $checksum) == 0) { ## FHT80TF Tuer-/Fensterkontakt + for(my $b = 0; $b < 45; $b += 9) { # check parity over 5 byte + my $parity = 0; # Parity even + for(my $i = $b; $i < $b + 9; $i++) { # Parity over 1 byte + 1 bit + $parity += $bit_msg[$i]; + } + if ($parity % 2 != 0) { + Log3 $name, 4, "$name: FHT80TF ERROR - Parity not even"; + return 0, undef; + } + } # parity ok + for(my $b = 44; $b > 0; $b -= 9) { # delete 5 parity bits + splice(@bit_msg, $b, 1); + } + if ($bit_msg[26] != 0) { # Bit 5 Byte 3 must 0 + Log3 $name, 3, "$name: FHT80 ERROR - byte 3 bit 5 not 0"; + return 0, undef; + } + splice(@bit_msg, 32, 8); # delete checksum + Log3 $name, 4, "$name: FHT80TF - door/window switch protolength $protolength"; + return (1, @bit_msg); ## FHT80TF ok + } + } + return 0, undef; +} + +sub SIGNALduino_postDemo_WS7035($@) { + my ($name, @bit_msg) = @_; + my $msg = join("",@bit_msg); + my $parity = 0; # Parity even + + Log3 $name, 4, "$name: WS7035 $msg"; + if (substr($msg,0,8) ne "10100000") { # check ident + Log3 $name, 3, "$name: WS7035 ERROR - Ident not 1010 0000"; + return 0, undef; + } else { + for(my $i = 15; $i < 28; $i++) { # Parity over bit 15 and 12 bit temperature + $parity += substr($msg, $i, 1); + } + if ($parity % 2 != 0) { + Log3 $name, 3, "$name: WS7035 ERROR - Parity not even"; + return 0, undef; + } else { + Log3 $name, 4, "$name: WS7035 " . substr($msg,0,4) ." ". substr($msg,4,4) ." ". substr($msg,8,4) ." ". substr($msg,12,4) ." ". substr($msg,16,4) ." ". substr($msg,20,4) ." ". substr($msg,24,4) ." ". substr($msg,28,4) ." ". substr($msg,32,4) ." ". substr($msg,36,4) ." ". substr($msg,40); + substr($msg, 27, 4, ''); # delete nibble 8 + return (1,split("",$msg)); + } + } +} + +sub SIGNALduino_postDemo_WS2000($@) { + my ($name, @bit_msg) = @_; + my $debug = AttrVal($name,"debug",0); + my @new_bit_msg = ""; + my $protolength = scalar @bit_msg; + my @datalenghtws = (35,50,35,50,70,40,40,85); + my $datastart = 0; + my $datalength = 0; + my $datalength1 = 0; + my $index = 0; + my $data = 0; + my $dataindex = 0; + my $error = 0; + my $check = 0; + my $sum = 5; + my $typ = 0; + my $adr = 0; + my @sensors = ( + "Thermo", + "Thermo/Hygro", + "Rain", + "Wind", + "Thermo/Hygro/Baro", + "Brightness", + "Pyrano", + "Kombi" + ); + + for ($datastart = 0; $datastart < $protolength; $datastart++) { # Start bei erstem Bit mit Wert 1 suchen + last if $bit_msg[$datastart] eq "1"; + } + if ($datastart == $protolength) { # all bits are 0 + Log3 $name, 3, "$name: WS2000 - ERROR message all bit are zeros"; + return 0, undef; + } + $datalength = $protolength - $datastart; + $datalength1 = $datalength - ($datalength % 5); # modulo 5 + Log3 $name, 5, "$name: WS2000 protolength: $protolength, datastart: $datastart, datalength $datalength"; + $typ = oct( "0b".(join "", reverse @bit_msg[$datastart + 1.. $datastart + 4])); # Sensortyp + if ($typ > 7) { + Log3 $name, 4, "$name: WS2000 Sensortyp $typ - ERROR typ to big"; + return 0, undef; + } + if ($typ == 1 && ($datalength == 45 || $datalength == 46)) {$datalength1 += 5;} # Typ 1 ohne Summe + if ($datalenghtws[$typ] != $datalength1) { # check lenght of message + Log3 $name, 4, "$name: WS2000 Sensortyp $typ - ERROR lenght of message $datalength1 ($datalenghtws[$typ])"; + return 0, undef; + } elsif ($datastart > 10) { # max 10 Bit preamble + Log3 $name, 4, "$name: WS2000 ERROR preamble > 10 ($datastart)"; + return 0, undef; + } else { + do { + $error += !$bit_msg[$index + $datastart]; # jedes 5. Bit muss 1 sein + $dataindex = $index + $datastart + 1; + $data = oct( "0b".(join "", reverse @bit_msg[$dataindex .. $dataindex + 3])); + if ($index == 5) {$adr = ($data & 0x07)} # Sensoradresse + if ($datalength == 45 || $datalength == 46) { # Typ 1 ohne Summe + if ($index <= $datalength - 5) { + $check = $check ^ $data; # Check - Typ XOR Adresse XOR bis XOR Check muss 0 ergeben + } + } else { + if ($index <= $datalength - 10) { + $check = $check ^ $data; # Check - Typ XOR Adresse XOR bis XOR Check muss 0 ergeben + $sum += $data; + } + } + $index += 5; + } until ($index >= $datalength); + } + if ($error != 0) { + Log3 $name, 4, "$name: WS2000 Sensortyp $typ Adr $adr - ERROR examination bit"; + return (0, undef); + } elsif ($check != 0) { + Log3 $name, 4, "$name: WS2000 Sensortyp $typ Adr $adr - ERROR check XOR"; + return (0, undef); + } else { + if ($datalength < 45 || $datalength > 46) { # Summe prüfen, außer Typ 1 ohne Summe + $data = oct( "0b".(join "", reverse @bit_msg[$dataindex .. $dataindex + 3])); + if ($data != ($sum & 0x0F)) { + Log3 $name, 4, "$name: WS2000 Sensortyp $typ Adr $adr - ERROR sum"; + return (0, undef); + } + } + Log3 $name, 4, "$name: WS2000 Sensortyp $typ Adr $adr - $sensors[$typ]"; + $datastart += 1; # [x] - 14_CUL_WS + @new_bit_msg[4 .. 7] = reverse @bit_msg[$datastart .. $datastart+3]; # [2] Sensortyp + @new_bit_msg[0 .. 3] = reverse @bit_msg[$datastart+5 .. $datastart+8]; # [1] Sensoradresse + @new_bit_msg[12 .. 15] = reverse @bit_msg[$datastart+10 .. $datastart+13]; # [4] T 0.1, R LSN, Wi 0.1, B 1, Py 1 + @new_bit_msg[8 .. 11] = reverse @bit_msg[$datastart+15 .. $datastart+18]; # [3] T 1, R MID, Wi 1, B 10, Py 10 + if ($typ == 0 || $typ == 2) { # Thermo (AS3), Rain (S2000R, WS7000-16) + @new_bit_msg[16 .. 19] = reverse @bit_msg[$datastart+20 .. $datastart+23]; # [5] T 10, R MSN + } else { + @new_bit_msg[20 .. 23] = reverse @bit_msg[$datastart+20 .. $datastart+23]; # [6] T 10, Wi 10, B 100, Py 100 + @new_bit_msg[16 .. 19] = reverse @bit_msg[$datastart+25 .. $datastart+28]; # [5] H 0.1, Wr 1, B Fak, Py Fak + if ($typ == 1 || $typ == 3 || $typ == 4 || $typ == 7) { # Thermo/Hygro, Wind, Thermo/Hygro/Baro, Kombi + @new_bit_msg[28 .. 31] = reverse @bit_msg[$datastart+30 .. $datastart+33]; # [8] H 1, Wr 10 + @new_bit_msg[24 .. 27] = reverse @bit_msg[$datastart+35 .. $datastart+38]; # [7] H 10, Wr 100 + if ($typ == 4) { # Thermo/Hygro/Baro (S2001I, S2001ID) + @new_bit_msg[36 .. 39] = reverse @bit_msg[$datastart+40 .. $datastart+43]; # [10] P 1 + @new_bit_msg[32 .. 35] = reverse @bit_msg[$datastart+45 .. $datastart+48]; # [9] P 10 + @new_bit_msg[44 .. 47] = reverse @bit_msg[$datastart+50 .. $datastart+53]; # [12] P 100 + @new_bit_msg[40 .. 43] = reverse @bit_msg[$datastart+55 .. $datastart+58]; # [11] P Null + } + } + } + return (1, @new_bit_msg); + } + +} + + +sub SIGNALduino_postDemo_WS7053($@) { + my ($name, @bit_msg) = @_; + my $msg = join("",@bit_msg); + my $new_msg =""; + my $parity = 0; # Parity even + if (length($msg) > 32) { # start not correct + $msg = substr($msg,1) + } + Log3 $name, 4, "$name: WS7053 MSG = $msg"; + if (substr($msg,0,8) ne "10100000") { # check ident + Log3 $name, 3, "$name: WS7053 ERROR - Ident not 1010 0000 - " . substr($msg,0,8); + return 0, undef; + } else { + for(my $i = 15; $i < 28; $i++) { # Parity over bit 15 and 12 bit temperature + $parity += substr($msg, $i, 1); + } + if ($parity % 2 != 0) { + Log3 $name, 3, "$name: WS7053 ERROR - Parity not even"; + return 0, undef; + } else { + Log3 $name, 5, "$name: WS7053 before: " . substr($msg,0,4) ." ". substr($msg,4,4) ." ". substr($msg,8,4) ." ". substr($msg,12,4) ." ". substr($msg,16,4) ." ". substr($msg,20,4) ." ". substr($msg,24,4) ." ". substr($msg,28,4); + # Format from 7053: Bit 0-7 Ident, Bit 8-15 Rolling Code/Parity, Bit 16-27 Temperature (12.3), Bit 28-31 Zero + $new_msg = substr($msg,0,28) . substr($msg,16,8) . substr($msg,28,4); + # Format for CUL_TX: Bit 0-7 Ident, Bit 8-15 Rolling Code/Parity, Bit 16-27 Temperature (12.3), Bit 28 - 35 Temperature (12), Bit 36-39 Zero + Log3 $name, 5, "$name: WS7053 after: " . substr($new_msg,0,4) ." ". substr($new_msg,4,4) ." ". substr($new_msg,8,4) ." ". substr($new_msg,12,4) ." ". substr($new_msg,16,4) ." ". substr($new_msg,20,4) ." ". substr($new_msg,24,4) ." ". substr($new_msg,28,4) ." ". substr($new_msg,32,4) ." ". substr($new_msg,36,4); + return (1,split("",$new_msg)); + } + } +} + + +# manchester method + +sub SIGNALduino_MCTFA +{ + my ($name,$bitData,$id,$mcbitnum) = @_; + + my $preamble_pos; + my $message_end; + my $message_length; + + #if ($bitData =~ m/^.?(1){16,24}0101/) { + if ($bitData =~ m/(1{10}101)/ ) + { + $preamble_pos=$+[1]; + Log3 $name, 4, "$name: TFA 30.3208.0 preamble_pos = $preamble_pos"; + return return (-1," sync not found") if ($preamble_pos <=0); + my @messages; + + do + { + $message_end = index($bitData,"1111111111101",$preamble_pos); + if ($message_end < $preamble_pos) + { + $message_end=length($bitData); + } + $message_length = ($message_end - $preamble_pos); + + my $part_str=substr($bitData,$preamble_pos,$message_length); + $part_str = substr($part_str,0,52) if (length($part_str)) > 52; + + Log3 $name, 4, "$name: TFA message start=$preamble_pos end=$message_end with length".$message_length; + Log3 $name, 5, "$name: part $part_str"; + my $hex=SIGNALduino_b2h($part_str); + push (@messages,$hex); + Log3 $name, 4, "$name: ".$hex; + $preamble_pos=index($bitData,"1101",$message_end)+4; + } while ( $message_end < length($bitData) ); + + my %seen; + my @dupmessages = map { 1==$seen{$_}++ ? $_ : () } @messages; + + if (scalar(@dupmessages) > 0 ) { + Log3 $name, 4, "$name: repeated hex ".$dupmessages[0]." found ".$seen{$dupmessages[0]}." times"; + return (1,$dupmessages[0]); + } else { + return (-1," no duplicate found"); + } + } + return (-1,undef); + } sub SIGNALduino_OSV2() { - my ($name,$bitData,$id) = @_; + my ($name,$bitData,$id,$mcbitnum) = @_; my $preamble_pos; my $message_end; @@ -3211,26 +4721,66 @@ sub SIGNALduino_OSV2() sub SIGNALduino_OSV1() { - my ($name,$bitData,$rawData) = @_; + my ($name,$bitData,$id,$mcbitnum) = @_; - my $idx=0; + return (-1," message is to short") if (defined($ProtocolListSIGNALduino{$id}{length_min}) && $mcbitnum < $ProtocolListSIGNALduino{$id}{length_min} ); + return (-1," message is to long") if (defined($ProtocolListSIGNALduino{$id}{length_max}) && $mcbitnum > $ProtocolListSIGNALduino{$id}{length_max} ); - my $osv1hex ;# ~hex('0x'.$rawData); - #my $osv1bit = $bitData =~ tr/10/01/r; - my $osv1bit; - ($osv1bit = $bitData) =~ tr/10/01/; - #Log3 $name, 5, "$name: OSV1 protocol converted from ($bitData) to bit: ($osv1bit)" ; - $osv1hex=sprintf("%02X", length($rawData)*4, $osv1hex).SIGNALduino_b2h($osv1bit); + + my $calcsum = oct( "0b" . reverse substr($bitData,0,8)); + $calcsum += oct( "0b" . reverse substr($bitData,8,8)); + $calcsum += oct( "0b" . reverse substr($bitData,16,8)); + $calcsum = ($calcsum & 0xFF) + ($calcsum >> 8); + my $checksum = oct( "0b" . reverse substr($bitData,24,8)); + if ($calcsum != $checksum) { # Checksum + return (-1,"OSV1 - ERROR checksum not equal: $calcsum != $checksum"); + } else { + Log3 $name, 4, "$name: OSV1 input data: $bitData"; + my $newBitData = "00001010"; # Byte 0: Id1 = 0x0A + $newBitData .= "01001101"; # Byte 1: Id2 = 0x4D + # Todo: Sensortyp automtisch erkennen und Premable damit setzen. + + # preamble => '50B208', # THR128 ohne Checksumme + # 50 - Length + # B2 - Byte 0: Id1 + # 08 - Byte 1: Id2 + my $channel = substr($bitData,6,2); # Byte 2 h: Channel + if ($channel == "00") { # in 0 LSB first + $newBitData .= "0001"; # out 1 MSB first + } elsif ($channel == "10") { # in 4 LSB first + $newBitData .= "0010"; # out 2 MSB first + } else { # in 8 LSB first + $newBitData .= "0100"; # out 4 MSB first + } + $newBitData .= "0000"; # Byte 2 l: ???? + $newBitData .= "0000"; # Byte 3 h: address + $newBitData .= reverse substr($bitData,0,4); # Byte 3 l: address (Rolling Code) + $newBitData .= reverse substr($bitData,8,4); # Byte 4 h: T 0,1 + $newBitData .= "0" . substr($bitData,23,1) . "00"; # Byte 4 l: Bit 2 - Batterie 0=ok, 1=low (< 2,5 Volt) + $newBitData .= reverse substr($bitData,16,4); # Byte 5 h: T 10 + $newBitData .= reverse substr($bitData,12,4); # Byte 5 l: T 1 + $newBitData .= "0000"; # Byte 6 h: immer 0000 + $newBitData .= substr($bitData,21,1) . "000"; # Byte 6 l: Bit 3 - Temperatur 0=pos | 1=neg, Rest 0 + $newBitData .= "00000000"; # Byte 7: immer 0000 0000 + # calculate new checksum over first 16 nibbles + $checksum = 0; + for (my $i = 0; $i < 64; $i = $i + 4) { + $checksum += oct( "0b" . substr($newBitData, $i, 4)); + } + $checksum = ($checksum - 0xa) & 0xff; + $newBitData .= sprintf("%08b",$checksum); # Byte 8: new Checksum + $newBitData .= "00000000"; # Byte 9: immer 0000 0000 - - Log3 $name, 5, "$name: OSV1 protocol converted to hex: ($osv1hex) with length (".(length($rawData)*4).") bits \n"; + my $osv1hex = "50" . SIGNALduino_b2h($newBitData); # output with length before #todo: Länge berechnen + Log3 $name, 4, "$name: OSV1 protocol id ($id) translated to RFXSensor format"; + Log3 $name, 4, "$name: converted to hex: ($osv1hex)"; return (1,$osv1hex); - +} } sub SIGNALduino_AS() { - my ($name,$bitData,$id) = @_; + my ($name,$bitData,$id,$mcbitnum) = @_; my $debug = AttrVal($name,"debug",0); if(index($bitData,"1100",16) >= 0) # $rawData =~ m/^A{2,3}/) @@ -3258,7 +4808,7 @@ sub SIGNALduino_AS() sub SIGNALduino_Hideki() { - my ($name,$bitData,$id) = @_; + my ($name,$bitData,$id,$mcbitnum) = @_; my $debug = AttrVal($name,"debug",0); Debug "$name: search in $bitData \n" if ($debug); @@ -3294,13 +4844,13 @@ sub SIGNALduino_Hideki() return (1,$hidekihex); ## Return only the original bits, include length } - return (-1,""); + return (-1,"Start pattern (10101110) not found"); } sub SIGNALduino_Maverick() { - my ($name,$bitData,$id) = @_; + my ($name,$bitData,$id,$mcbitnum) = @_; my $debug = AttrVal($name,"debug",0); @@ -3318,17 +4868,127 @@ sub SIGNALduino_Maverick() } } +sub SIGNALduino_OSPIR() +{ + my ($name,$bitData,$id,$mcbitnum) = @_; + my $debug = AttrVal($name,"debug",0); + + + if ($bitData =~ m/^.*(1{14}|0{14}).*/) + { # Valid Oregon PIR detected + my $header_pos=$+[1]; + + Log3 $name, 4, "$name: Oregon PIR protocol detected: header_pos = $header_pos"; + + my $hex=SIGNALduino_b2h($bitData); + + return (1,$hex); ## Return the bits unchanged in hex + } else { + return return (-1," header not found"); + } +} +sub SIGNALduino_MCRAW() +{ + my ($name,$bitData,$id,$mcbitnum) = @_; + my $debug = AttrVal($name,"debug",0); + + + my $hex=SIGNALduino_b2h($bitData); + return (1,$hex); ## Return the bits unchanged in hex +} + + + sub SIGNALduino_SomfyRTS() { - my ($name, $bitData, $rawData) = @_; + my ($name, $bitData,$id,$mcbitnum) = @_; #(my $negBits = $bitData) =~ tr/10/01/; # Todo: eventuell auf pack umstellen + + if (defined($mcbitnum)) { + Log3 $name, 4, "$name: Somfy bitdata: $bitData ($mcbitnum)"; + if ($mcbitnum == 57) { + $bitData = substr($bitData, 1, 56); + Log3 $name, 4, "$name: Somfy bitdata: _$bitData (" . length($bitData) . "). Bit am Anfang entfernt"; + } + } my $encData = SIGNALduino_b2h($bitData); #Log3 $name, 4, "$name: Somfy RTS protocol enc: $encData"; return (1, $encData); } +# - - - - - - - - - - - - +#=item SIGNALduino_filterMC() +#This functons, will act as a filter function. It will decode MU data via Manchester encoding +# +# Will return $count of ???, modified $rawData , modified %patternListRaw, +# =cut + + +sub SIGNALduino_filterMC($$$%) +{ + + ## Warema Implementierung : Todo variabel gestalten + my ($name,$id,$rawData,%patternListRaw) = @_; + my $debug = AttrVal($name,"debug",0); + + my ($ht, $hasbit, $value) = 0; + $value=1 if (!$debug); + my @bitData; + my @sigData = split "",$rawData; + + foreach my $pulse (@sigData) + { + next if (!defined($patternListRaw{$pulse})); + #Log3 $name, 4, "$name: pulese: ".$patternListRaw{$pulse}; + + if (SIGNALduino_inTol($ProtocolListSIGNALduino{$id}{clockabs},abs($patternListRaw{$pulse}),$ProtocolListSIGNALduino{$id}{clockabs}*0.5)) + { + # Short + $hasbit=$ht; + $ht = $ht ^ 0b00000001; + $value='S' if($debug); + #Log3 $name, 4, "$name: filter S "; + } elsif ( SIGNALduino_inTol($ProtocolListSIGNALduino{$id}{clockabs}*2,abs($patternListRaw{$pulse}),$ProtocolListSIGNALduino{$id}{clockabs}*0.5)) { + # Long + $hasbit=1; + $ht=1; + $value='L' if($debug); + #Log3 $name, 4, "$name: filter L "; + } elsif ( SIGNALduino_inTol($ProtocolListSIGNALduino{$id}{syncabs}+(2*$ProtocolListSIGNALduino{$id}{clockabs}),abs($patternListRaw{$pulse}),$ProtocolListSIGNALduino{$id}{clockabs}*0.5)) { + $hasbit=1; + $ht=1; + $value='L' if($debug); + #Log3 $name, 4, "$name: sync L "; + + } else { + # No Manchester Data + $ht=0; + $hasbit=0; + #Log3 $name, 4, "$name: filter n "; + } + + if ($hasbit && $value) { + $value = lc($value) if($debug && $patternListRaw{$pulse} < 0); + my $bit=$patternListRaw{$pulse} > 0 ? 1 : 0; + #Log3 $name, 5, "$name: adding value: ".$bit; + + push @bitData, $bit ; + } + } + + my %patternListRawFilter; + + $patternListRawFilter{0} = 0; + $patternListRawFilter{1} = $ProtocolListSIGNALduino{$id}{clockabs}; + + #Log3 $name, 5, "$name: filterbits: ".@bitData; + $rawData = join "", @bitData; + return (undef ,$rawData, %patternListRawFilter); + +} + # - - - - - - - - - - - - #=item SIGNALduino_filterSign() #This functons, will act as a filter function. It will remove the sign from the pattern, and compress message and pattern @@ -3345,7 +5005,7 @@ sub SIGNALduino_filterSign($$$%) my %buckets; # Remove Sign - %patternListRaw = map { $_ => abs($patternListRaw{$_})} keys %patternListRaw; ## remove sing from all + %patternListRaw = map { $_ => abs($patternListRaw{$_})} keys %patternListRaw; ## remove sign from all my $intol=0; my $cnt=0; @@ -3503,8 +5163,9 @@ sub SIGNALduino_compPattern($$$%) Wireless switches
- ITv1 & ITv3/Elro and other brands using pt2263 or arctech protocol--> uses IT.pm
+ ITv1 & ITv3/Elro and other brands using pt2263 or arctech protocol--> uses IT.pm

+ In the ITv1 protocol is used to sent a default ITclock from 250 and it may be necessary in the IT-Modul to define the attribute ITclock


Temperatur / humidity senso -
- + + Attributes + + + Get @@ -3639,20 +5338,24 @@ With a # at the beginnging whitelistIDs can be deactivated. SIGNALduino to interpret the response of this command. See also the raw- command.
-
  • ITParms
    - For sending IT Signals for wireless switches, the number of repeats and the base duration can be set. - With the get command, you can verify what is programmed into the uC. -

  • protocolIDs
    - display a list of the protocol IDs -

  • + display a list of the protocol IDs +
    +
  • ccconf
    + Only with cc1101 receiver. + Read some CUL radio-chip (cc1101) registers (frequency, bandwidth, etc.), + and display them in human readable form. +

  • +
  • ccpatable
    + read cc1101 PA table (power amplification for RF sending) +

  • +
  • ccreg
    + read cc1101 registers (99 reads all cc1101 registers) +

  • SET - - =end html diff --git a/fhem/FHEM/14_Hideki.pm b/fhem/FHEM/14_Hideki.pm index c594c54a5..703c2fc08 100644 --- a/fhem/FHEM/14_Hideki.pm +++ b/fhem/FHEM/14_Hideki.pm @@ -4,7 +4,7 @@ # see http://www.fhemwiki.de/wiki/SIGNALduino # and was modified by a few additions # to support Hideki Sensors -# S. Butzek & HJGode & Ralf9 2015-2016 +# S. Butzek, HJGode, Ralf9 2015-2017 # package main; @@ -27,8 +27,9 @@ Hideki_Initialize($) $hash->{UndefFn} = "Hideki_Undef"; $hash->{AttrFn} = "Hideki_Attr"; $hash->{ParseFn} = "Hideki_Parse"; - $hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1 " - ."ignore:0,1 " + $hash->{AttrList} = "IODev do_not_notify:0,1 showtime:0,1" + ." ignore:0,1" + ." windDirCorr windSpeedCorr" ." $readingFnAttributes"; $hash->{AutoCreate}= @@ -53,7 +54,7 @@ Hideki_Define($$) my $name= $hash->{NAME}; $modules{Hideki}{defptr}{$a[2]} = $hash; - $hash->{STATE} = "Defined"; + #$hash->{STATE} = "Defined"; #AssignIoPort($hash); return undef; @@ -102,25 +103,64 @@ Hideki_Parse($$) } my $id=substr($decodedString,2,2); # get the random id from the data - my $channel=0; - my $temp=0; + my $channel=0; + my $temp=""; my $hum=0; my $rain=0; + my $unknown=0; + my $windchill=0; + my $windspeed=0; + my $windgust=0; + my $winddir=0; + my $winddirdeg=0; + my $winddirtext; my $rc; my $val; my $bat; my $deviceCode; my $model= "Hideki_$sensorTyp"; - + my $count=0; + my $comfort=0; ## 1. Detect what type of sensor we have, then call specific function to decode - if ($sensorTyp==0x1E){ - ($channel, $temp, $hum) = decodeThermoHygro(\@decodedBytes); # decodeThermoHygro($decodedString); + if ($sensorTyp==30){ + ($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString); + $hum = 10 * ($decodedBytes[6] >> 4) + ($decodedBytes[6] & 0x0f); $bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery - $val = "T: $temp H: $hum Bat: $bat"; - }elsif($sensorTyp==0x0E){ + $count = $decodedBytes[3] >> 6; # verifiziert, MSG_Counter + $comfort = ($decodedBytes[7] >> 2 & 0x03); # comfort level + + if ($comfort == 0) { $comfort = 'Hum. OK. Temp. uncomfortable (>24.9 or <20)' } + elsif ($comfort == 1) { $comfort = 'Wet. More than 69% RH' } + elsif ($comfort == 2) { $comfort = 'Dry. Less than 40% RH' } + elsif ($comfort == 3) { $comfort = 'Temp. and Hum. comfortable' } + $val = "T: $temp H: $hum"; + Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp, humidity=$hum, comfort=$comfort"; + }elsif($sensorTyp==31){ + ($channel, $temp) = decodeThermo(\@decodedBytes); + $bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery + $count = $decodedBytes[3] >> 6; # verifiziert, MSG_Counter + $val = "T: $temp"; + Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp"; + }elsif($sensorTyp==14){ ($channel, $rain) = decodeRain(\@decodedBytes); # decodeThermoHygro($decodedString); $bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery - $val = "R: $rain Bat: $bat"; + $count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter + $val = "R: $rain"; + Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, rain=$rain, unknown=$unknown"; + }elsif($sensorTyp==12){ + ($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString); + ($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext) = wind(\@decodedBytes); + $bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery + $count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter + $val = "T: $temp Ws: $windspeed Wg: $windgust Wd: $winddirtext"; + Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp, Wc=$windchill, Ws=$windspeed, Wg=$windgust, Wd=$winddir, WdDeg=$winddirdeg, Wdtxt=$winddirtext"; + }elsif($sensorTyp==13){ + ($channel, $temp) = decodeThermo(\@decodedBytes); # decodeThermoHygro($decodedString); + $bat = ($decodedBytes[2] >> 6 == 3) ? 'ok' : 'low'; # decode battery + $count = $decodedBytes[3] >> 6; # UNVERIFIZIERT, MSG_Counter + $val = "T: $temp"; + Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, cnt=$count, bat=$bat, temp=$temp"; + Log3 $iohash, 4, "$name Sensor Typ $sensorTyp currently not full supported, please report sensor information!"; } else{ Log3 $iohash, 4, "$name Sensor Typ $sensorTyp not supported, please report sensor information!"; @@ -135,14 +175,13 @@ Hideki_Parse($$) $deviceCode = $model . "_" . $channel; } - Log3 $iohash, 4, "$name decoded Hideki protocol model=$model, sensor id=$id, channel=$channel, temp=$temp, humidity=$hum, bat=$bat, rain=$rain"; Log3 $iohash, 5, "deviceCode: $deviceCode"; my $def = $modules{Hideki}{defptr}{$iohash->{NAME} . "." . $deviceCode}; $def = $modules{Hideki}{defptr}{$deviceCode} if(!$def); if(!$def) { - Log3 $iohash, 1, "Hideki: UNDEFINED sensor $sensorTyp detected, code $deviceCode"; + Log3 $iohash, 1, "$name Hideki: UNDEFINED sensor $sensorTyp detected, code $deviceCode"; return "UNDEFINED $deviceCode Hideki $deviceCode"; } @@ -152,6 +191,12 @@ Hideki_Parse($$) #Log3 $name, 4, "Hideki: $name ($msg)"; + my $WindSpeedCorr = AttrVal($name,"windSpeedCorr",0); + if ($WindSpeedCorr > 0 && $sensorTyp == 12) { + $windspeed = sprintf("%.2f", $windspeed * $WindSpeedCorr); + $windgust = sprintf("%.2f", $windgust * $WindSpeedCorr); + Log3 $name, 4, "$name Hideki_Parse: WindSpeedCorr=$WindSpeedCorr, WindSpeed=$windspeed, WindGust=$windgust"; + } if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef))) { @@ -171,13 +216,24 @@ Hideki_Parse($$) readingsBulkUpdate($hash, "state", $val); readingsBulkUpdate($hash, "battery", $bat) if ($bat ne ""); readingsBulkUpdate($hash, "channel", $channel) if ($channel ne ""); - if ($sensorTyp==0x1E){ - readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne ""); - readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne ""); + readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne ""); + readingsBulkUpdate($hash, "package_number", $count) if ($count ne ""); + if ($sensorTyp == 30) { # temperature, humidity + readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne ""); + readingsBulkUpdate($hash, "comfort_level", $comfort) if ($comfort ne ""); } - elsif($sensorTyp==0x0E){ - readingsBulkUpdate($hash, "rain", $rain) if ($rain ne ""); + elsif ($sensorTyp == 14) { # rain + readingsBulkUpdate($hash, "rain", $rain); } + elsif ($sensorTyp == 12) { # wind + readingsBulkUpdate($hash, "windChill", $windchill); + readingsBulkUpdate($hash, "windGust", $windgust); + readingsBulkUpdate($hash, "windSpeed", $windspeed); + readingsBulkUpdate($hash, "windDirection", $winddir); + readingsBulkUpdate($hash, "windDirectionDegree", $winddirdeg); + readingsBulkUpdate($hash, "windDirectionText", $winddirtext); + } + readingsEndUpdate($hash, 1); # Notify is done by Dispatch return $name; @@ -227,6 +283,7 @@ sub Hideki_crc{ # 0x0D UV sensor # 0x0E Rain level meter # 0x1E Thermo/hygro-sensor +# 0x1F Thermo sensor sub getSensorType{ return $_[0] & 0x1F; } @@ -254,11 +311,11 @@ sub Hideki_decryptByte{ return $ret2; } -# decode byte array and return channel, temperature and hunidity +# decode byte array and return channel, temperature # input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array); -# output , , , +# output , , # was unable to get this working with an array ref as input, so switched to hex string input -sub decodeThermoHygro { +sub decodeThermo { my @Hidekibytes = @{$_[0]}; #my $Hidekihex = shift; @@ -269,7 +326,6 @@ sub decodeThermoHygro { #} my $channel=0; my $temp=0; - my $humi=0; $channel = $Hidekibytes[1] >> 5; # //Internally channel 4 is used for the other sensor types (rain, uv, anemo). @@ -285,12 +341,11 @@ sub decodeThermoHygro { $temp = -$temp; } - $humi = 10 * ($Hidekibytes[6] >> 4) + ($Hidekibytes[6] & 0x0f); - $temp = $temp / 10; - return ($channel, $temp, $humi); + return ($channel, $temp); } + # decode byte array and return channel and total rain in mm # input: decrypted byte array starting with 0x75, passed by reference as in mysub(\@array); # output , , @@ -306,18 +361,20 @@ sub decodeRain { #} my $channel=0; my $rain=0; + my $unknown; - my $tests=0; + #my $tests=0; #additional checks? - if($Hidekibytes[2]==0xCC){ - $tests+=1; - } - if($Hidekibytes[6]==0x66){ - $tests+=1; - } + #if($Hidekibytes[2]==0xCC){ + # $tests+=1; + #} + #if($Hidekibytes[6]==0x66){ + # $tests+=1; + #} # possibly test if $tests==2 for sanity check #printf("SANITY CHECK tests=%i\n", $tests); + $unknown = $Hidekibytes[6]; $channel = $Hidekibytes[1] >> 5; # //Internally channel 4 is used for the other sensor types (rain, uv, anemo). # //Therefore, if channel is decoded 5 or 6, the real value set on the device itself is 4 resp 5. @@ -328,7 +385,33 @@ sub decodeRain { $rain = ($Hidekibytes[4] + $Hidekibytes[5]*0xff)*0.7; - return ($channel, $rain); + return ($channel, $rain, $unknown); +} + +# P12#758BB244074007400F00001C6E7A01 +sub wind { + my @Hidekibytes = @{$_[0]}; + my @winddir_name=("N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"); + my $windspeed; + my $windchill; + my $windgust; + my $winddir; + my $winddirdeg; + my $winddirtext; + + $windchill = 100 * ($Hidekibytes[7] & 0x0f) + 10 * ($Hidekibytes[6] >> 4) + ($Hidekibytes[6] & 0x0f); + ## windchill is negative? + if (!($Hidekibytes[7] & 0x80)) { + $windchill = -$windchill; + } + $windchill = $windchill / 10; + $windspeed = ($Hidekibytes[9] & 0x0f ) * 100 + ($Hidekibytes[8] >> 4) * 10 + ($Hidekibytes[8] & 0x0f); + $windgust = ($Hidekibytes[10] >> 4) * 100 + ($Hidekibytes[10] & 0x0f) * 10 + ($Hidekibytes[9] >> 4); + $winddir = ($Hidekibytes[11] >> 4); + $winddirtext = $winddir_name[$winddir]; + $winddirdeg = $winddir * 22.5; + + return ($windchill,$windspeed,$windgust,$winddir,$winddirdeg,$winddirtext); } sub @@ -369,7 +452,7 @@ Hideki_Attr(@)
  • TFA Dostman
  • Arduinos with remote Sensor lib from Randy Simons
  • Cresta
  • -
  • Hideki
  • +
  • Hideki (Anemometer | UV sensor | Rain level meter | Thermo/hygro-sensor)
  • all other devices, which use the Hideki protocol
  • Please note, currently temp/hum devices are implemented. Please report data for other sensortypes. @@ -395,6 +478,9 @@ Hideki_Attr(@)
  • humidity (0-100)
  • battery (ok or low)
  • channel (The Channelnumber (number if)
  • +
    - Hideki only - +
  • comfort_level (Status: Humidity OK... , Wet. More than 69% RH, Dry. Less than 40% RH, Temperature and humidity comfortable)
  • +
  • package_number (reflect the package number in the stream starting at 1)

  • @@ -434,7 +520,7 @@ Hideki_Attr(@)
  • TFA Dostman
  • Arduinos with remote Sensor lib from Randy Simons
  • Cresta
  • -
  • Hideki
  • +
  • Hideki (Anemometer | UV sensor | Rain level meter | Thermo/hygro-sensor)
  • Alle anderen, welche das Hideki Protokoll verwenden
  • Hinweis, Aktuell sind nur temp/feuchte Sensoren implementiert. Bitte sendet uns Daten zu anderen Sensoren. @@ -454,13 +540,16 @@ Hideki_Attr(@)
    - Erzeugte Readings + Generated Readings
    • state (T:x H:y B:z)
    • temperature (°C)
    • humidity (0-100)
    • -
    • battery (ok or low)
    • +
    • battery (ok oder low)
    • channel (Der Sensor Kanal)
    • +
      - Hideki spezifisch - +
    • comfort_level (Status: Humidity OK... , Wet größer 69% RH, Dry weiniger als 40% RH, Temperature and humidity comfortable)
    • +
    • package_number (Paketnummer in der letzten Signalfolge, startet bei 1)

    Set
      N/A

    diff --git a/fhem/FHEM/14_SD_WS.pm b/fhem/FHEM/14_SD_WS.pm index 3fa4f48d7..8fed2405f 100644 --- a/fhem/FHEM/14_SD_WS.pm +++ b/fhem/FHEM/14_SD_WS.pm @@ -3,16 +3,19 @@ # # The purpose of this module is to support serval # weather sensors which use various protocol -# Sidey79 & Ralf9 2016 -# - +# Sidey79 & Ralf9 2016 - 2017 +# Joerg 2017 +# 17.04.2017 WH2 (TFA 30.3157 nur Temp, Hum = 255),es wird das Perlmodul Digest:CRC benoetigt fuer CRC-Pruefung benoetigt +# 29.05.2017 Test ob Digest::CRC installiert +# 22.07.2017 WH2 angepasst +# 21.08.2017 WH2 Abbruch wenn kein "FF" am Anfang package main; use strict; use warnings; - -#use Data::Dumper; +# use Digest::CRC qw(crc); +# use Data::Dumper; sub SD_WS_Initialize($) @@ -25,13 +28,16 @@ sub SD_WS_Initialize($) $hash->{ParseFn} = "SD_WS_Parse"; $hash->{AttrFn} = "SD_WS_Attr"; $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 " . - "$readingFnAttributes "; + "$readingFnAttributes "; $hash->{AutoCreate} = { "SD_WS37_TH.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"}, "SD_WS50_SM.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"}, - "BresserTemeo.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"} - + "BresserTemeo.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"}, + "SD_WS51_TH.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"}, + "SD_WS58_TH.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:90"}, + "SD_WH2.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:90"}, + "SD_WS71_T.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.*", FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"}, }; } @@ -63,7 +69,7 @@ sub SD_WS_Undef($$) { my ($hash, $name) = @_; delete($modules{SD_WS}{defptr}{$hash->{CODE}}) - if(defined($hash->{CODE}) && defined($modules{SD_WS}{defptr}{$hash->{CODE}})); + if(defined($hash->{CODE}) && defined($modules{SD_WS}{defptr}{$hash->{CODE}})); return undef; } @@ -77,8 +83,7 @@ sub SD_WS_Parse($$) my ($protocol,$rawData) = split("#",$msg); $protocol=~ s/^[WP](\d+)/$1/; # extract protocol - - + my $dummyreturnvalue= "Unknown, please report"; my $hlen = length($rawData); my $blen = $hlen * 4; @@ -113,74 +118,166 @@ sub SD_WS_Parse($$) prematch => sub {my $msg = shift; return 1 if ($msg =~ /^FF5[0-9A-F]{5}FF[0-9A-F]{2}/); }, # prematch crcok => sub {my $msg = shift; return 1 if ((hex(substr($msg,2,2))+hex(substr($msg,4,2))+hex(substr($msg,6,2))+hex(substr($msg,8,2))&0xFF) == (hex(substr($msg,10,2))) ); }, # crc id => sub {my $msg = shift; return (hex(substr($msg,2,2)) &0x03 ); }, #id - #temp => sub {my $msg = shift; return (sprintf('%x',((hex(substr($msg,6,2)) <<4)/2/10))); }, #temp - #temphex => sub {my $msg = shift; return sprintf("%04X",((hex(substr($msg,6,2)))<<4)/2); }, #temp temp => sub {my $msg = shift; return ((hex(substr($msg,6,2)))-40) }, #temp - #hum => sub {my $msg = shift; return (printf('%02x',hex(substr($msg,4,2)))); }, #hum hum => sub {my $msg = shift; return hex(substr($msg,4,2)); }, #hum channel => sub {my (undef,$bitData) = @_; return ( SD_WS_binaryToNumber($bitData,12,15)&0x03 ); }, #channel + bat => sub { return "";}, }, + 71 => + # 5C2A909F792F + # 589A829FDFF4 + # PiiTTTK?CCCC + # P = Preamble (immer 5 ?) + # i = ID + # T = Temperatur + # K = Kanal (B/A/9) + # ? = immer F ? + # C = Checksum ? + { + sensortype => 'PV-8644', + model => 'SD_WS71_T', + prematch => sub {my $msg = shift; return 1 if ($msg =~ /^5[A-F0-9]{6}F[A-F0-9]{2}/); }, # prematch + crcok => sub {return 1; }, # crc is unknown + id => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,4,11); }, # id + temp => sub {my (undef,$bitData) = @_; return ((SD_WS_binaryToNumber($bitData,12,23) - 2448) / 10); }, #temp + channel => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,26,27); }, #channel + hum => sub {return undef;}, + bat => sub {return undef;}, + }, 33 => { sensortype => 's014/TFA 30.3200/TCM/Conrad', model => 'SD_WS_33_TH', prematch => sub {my $msg = shift; return 1 if ($msg =~ /^[0-9A-F]{10,11}/); }, # prematch - crcok => sub {return SD_WS_binaryToNumber($bitData,36,39); }, # crc + crcok => sub {return SD_WS_binaryToNumber($bitData,36,39)+1; }, # crc currently not calculated id => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,0,9); }, # id # sendmode => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,10,11) eq "1" ? "manual" : "auto"; } temp => sub {my (undef,$bitData) = @_; return (((SD_WS_binaryToNumber($bitData,22,25)*256 + SD_WS_binaryToNumber($bitData,18,21)*16 + SD_WS_binaryToNumber($bitData,14,17)) *10 -12200) /18)/10; }, #temp hum => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,30,33)*16 + SD_WS_binaryToNumber($bitData,26,29)); }, #hum channel => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,12,13)+1 ); }, #channel - bat => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,34) eq "1" ? "ok" : "critical");}, + bat => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,34) eq "1" ? "ok" : "critical";}, # sync => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,35,35) eq "1" ? "true" : "false");}, - } - - + } , + 51 => + { + sensortype => 'Lidl Wetterstation 2759001/IAN114324', + model => 'SD_WS_51_TH', + prematch => sub {my $msg = shift; return 1 if ($msg =~ /^[0-9A-F]{10}/); }, # prematch + crcok => sub {return 1; }, # crc is unknown + id => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,0,12); }, # random id? + # sendmode => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,10,11) eq "1" ? "manual" : "auto"; } + temp => sub {my (undef,$bitData) = @_; return round(((SD_WS_binaryToNumber($bitData,16,27)) -1220) *5 /90.0,1); }, #temp + hum => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,28,31)*10) + (SD_WS_binaryToNumber($bitData,32,35)); }, #hum + channel => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,36,39) ); }, #channel + bat => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,13) eq "1" ? "crititcal" : "ok";}, + trend => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,15,16) eq "01" ? "rising" : SD_WS_binaryToNumber($bitData,14,15) eq "00" ? "neutral" : "rising";}, + # sync => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,35,35) eq "1" ? "true" : "false");}, + } , + 58 => + { + sensortype => 'TFA 3032080', + model => 'SD_WS_58_TH', + prematch => sub {my $msg = shift; return 1 if ($msg =~ /^45[0-9A-F]{11}/); }, # prematch + crcok => sub { my $msg = shift; + my @buff = split(//,substr($msg,index($msg,"45"),10)); + my $crc_check = substr($msg,index($msg,"45")+10,2); + my $mask = 0x7C; + my $checksum = 0x64; + my $data; + my $nibbleCount; + for ( $nibbleCount=0; $nibbleCount < scalar @buff; $nibbleCount+=2) + { + my $bitCnt; + if ($nibbleCount+1 = 0 ; $bitCnt-- ) + { + my $bit; + # Rotate mask right + $bit = $mask & 1; + $mask = ($mask >> 1 ) | ($mask << 7) & 0xFF; + if ( $bit ) + { + $mask ^= 0x18 & 0xFF; + } + # XOR mask into checksum if data bit is 1 + if ( $data & 0x80 ) + { + $checksum ^= $mask & 0xFF; + } + $data <<= 1 & 0xFF; + } + } + if ($checksum == hex($crc_check)) { + return 1; + } else { + return 0; + } + }, + id => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,8,15); }, # random id + bat => sub {my (undef,$bitData) = @_; return SD_WS_binaryToNumber($bitData,16) eq "1" ? "crititcal" : "ok";}, # bat? + channel => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,17,19)+1 ); }, # channel + temp => sub {my (undef,$bitData) = @_; return round((SD_WS_binaryToNumber($bitData,20,31)-720)*0.0556,1); }, # temp + hum => sub {my (undef,$bitData) = @_; return (SD_WS_binaryToNumber($bitData,32,39)); }, # hum + } , ); Log3 $name, 4, "SD_WS_Parse: Protocol: $protocol, rawData: $rawData"; - if ($protocol eq "37") # Bresser 7009994 - { - # 0 7 8 9 10 12 22 25 31 - # 01011010 0 0 01 01100001110 10 0111101 11001010 - # ID B? T Kan Temp ?? Hum Pruefsumme? - - # MU;P0=729;P1=-736;P2=483;P3=-251;P4=238;P5=-491;D=010101012323452323454523454545234523234545234523232345454545232345454545452323232345232340;CP=4; - + if ($protocol eq "37") { # Bresser 7009994 + # Protokollbeschreibung: + # https://github.com/merbanan/rtl_433_tests/tree/master/tests/bresser_3ch + # The data is grouped in 5 bytes / 10 nibbles + # ------------------------------------------------------------------------ + # 0 | 8 12 | 16 | 24 | 32 + # 1111 1100 | 0001 0110 | 0001 0000 | 0011 0111 | 0101 1001 0 65.1 F 55 % + # iiii iiii | bscc tttt | tttt tttt | hhhh hhhh | xxxx xxxx + # i: 8 bit random id (changes on power-loss) + # b: battery indicator (0=>OK, 1=>LOW) + # s: Test/Sync (0=>Normal, 1=>Test-Button pressed / Sync) + # c: Channel (MSB-first, valid channels are 1-3) + # t: Temperature (MSB-first, Big-endian) + # 12 bit unsigned fahrenheit offset by 90 and scaled by 10 + # h: Humidity (MSB-first) 8 bit relative humidity percentage + # x: checksum (byte1 + byte2 + byte3 + byte4) % 256 + # Check with e.g. (byte1 + byte2 + byte3 + byte4 - byte5) % 256) = 0 $model = "SD_WS37_TH"; $SensorTyp = "Bresser 7009994"; - - $id = SD_WS_binaryToNumber($bitData,0,7); - #$bat = int(substr($bitData,8,1)) eq "1" ? "ok" : "low"; - $channel = SD_WS_binaryToNumber($bitData,10,11); - $rawTemp = SD_WS_binaryToNumber($bitData,12,22); - $hum = SD_WS_binaryToNumber($bitData,25,31); - - $id = sprintf('%02X', $id); # wandeln nach hex - $temp = ($rawTemp - 609.93) / 9.014; - $temp = sprintf("%.1f", $temp); - - if ($hum < 10 || $hum > 99 || $temp < -30 || $temp > 70) { + my $checksum = (SD_WS_binaryToNumber($bitData,0,7) + SD_WS_binaryToNumber($bitData,8,15) + SD_WS_binaryToNumber($bitData,16,23) + SD_WS_binaryToNumber($bitData,24,31)) & 0xFF; + if ($checksum != SD_WS_binaryToNumber($bitData,32,39)) { + Log3 $name, 3, "$name: SD_WS37 ERROR - checksum $checksum != ".SD_WS_binaryToNumber($bitData,32,39); return ""; + } else { + Log3 $name, 4, "$name: SD_WS37 checksum ok $checksum = ".SD_WS_binaryToNumber($bitData,32,39); + $id = SD_WS_binaryToNumber($bitData,0,7); + $id = sprintf('%02X', $id); # wandeln nach hex + $bat = int(substr($bitData,8,1)) eq "0" ? "ok" : "low"; # Batterie-Bit konnte nicht geprueft werden + $channel = SD_WS_binaryToNumber($bitData,10,11); + $rawTemp = SD_WS_binaryToNumber($bitData,12,23); + $hum = SD_WS_binaryToNumber($bitData,24,31); + my $tempFh = $rawTemp / 10 - 90; # Grad Fahrenheit + $temp = (($tempFh - 32) * 5 / 9); # Grad Celsius + $temp = sprintf("%.1f", $temp + 0.05); # round + Log3 $name, 4, "$name: SD_WS37 tempraw = $rawTemp, temp = $tempFh F, temp = $temp C, Hum = $hum"; + Log3 $name, 4, "$name: SD_WS37 decoded protocol = $protocol ($SensorTyp), sensor id = $id, channel = $channel"; } - - $bitData2 = substr($bitData,0,8) . ' ' . substr($bitData,8,4) . ' ' . substr($bitData,12,11); - $bitData2 = $bitData2 . ' ' . substr($bitData,23,2) . ' ' . substr($bitData,25,7) . ' ' . substr($bitData,32,8); - Log3 $iohash, 4, "$name converted to bits: " . $bitData2; - Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) sensor id=$id, channel=$channel, rawTemp=$rawTemp, temp=$temp, hum=$hum"; } elsif ($protocol eq "44" || $protocol eq "44x") # BresserTemeo { # 0 4 8 12 20 24 28 32 36 40 44 52 56 60 # 0101 0111 1001 00010101 0010 0100 0001 1010 1000 0110 11101010 1101 1011 1110 110110010 - # hhhh hhhh ?bcc iiiiiiii sttt tttt tttt xxxx xxxx ?BCC IIIIIIII Syyy yyyy yyyy + # hhhh hhhh ?bcc viiiiiii sttt tttt tttt xxxx xxxx ?BCC VIIIIIII Syyy yyyy yyyy # - h humidity / -x checksum # - t temp / -y checksum - # - c Channel / C checksum - # - i 8 bit random id (aendert sich beim Batterie- und Kanalwechsel) / - I checksum + # - c Channel / -C checksum + # - V sign / -V checksum + # - i 7 bit random id (aendert sich beim Batterie- und Kanalwechsel) / - I checksum # - b battery indicator (0=>OK, 1=>LOW) / - B checksum # - s Test/Sync (0=>Normal, 1=>Test-Button pressed) / - S checksum @@ -250,6 +347,22 @@ sub SD_WS_Parse($$) return ""; } + my $sign = substr($binvalue,12,1); + my $checkSign = substr($binvalue,44,1) ^ 0b1; + + if ($sign != $checkSign) + { + Log3 $iohash, 4, "SD_WS_Parse BresserTemeo: checksum error in Sign"; + $checksumOkay = 0; + } + else + { + if ($sign) + { + $temp = 0 - $temp + } + } + $bat = substr($binvalue,9,1); my $checkBat = substr($binvalue,41,1) ^ 0b1; @@ -265,8 +378,8 @@ sub SD_WS_Parse($$) $channel = SD_WS_binaryToNumber($binvalue, 10, 11); my $checkChannel = SD_WS_binaryToNumber($binvalue, 42, 43) ^ 0b11; - $id = SD_WS_binaryToNumber($binvalue, 12, 19); - my $checkId = SD_WS_binaryToNumber($binvalue, 44, 51) ^ 0b11111111; + $id = SD_WS_binaryToNumber($binvalue, 13, 19); + my $checkId = SD_WS_binaryToNumber($binvalue, 45, 51) ^ 0b1111111; if ($channel != $checkChannel || $id != $checkId) { @@ -283,15 +396,145 @@ sub SD_WS_Parse($$) $id = sprintf('%02X', $id); # wandeln nach hex Log3 $iohash, 4, "$name SD_WS_Parse: model=$model, temp=$temp, hum=$hum, channel=$channel, id=$id, bat=$bat"; - } + } elsif ($protocol eq "64") # WH2 + { + #* Fine Offset Electronics WH2 Temperature/Humidity sensor protocol + #* aka Agimex Rosenborg 66796 (sold in Denmark) + #* aka ClimeMET CM9088 (Sold in UK) + #* aka TFA Dostmann/Wertheim 30.3157 (Temperature only!) (sold in Germany) + #* aka ... + #* + #* The sensor sends two identical packages of 48 bits each ~48s. The bits are PWM modulated with On Off Keying + # * The data is grouped in 6 bytes / 12 nibbles + #* [pre] [pre] [type] [id] [id] [temp] [temp] [temp] [humi] [humi] [crc] [crc] + #* + #* pre is always 0xFF + #* type is always 0x4 (may be different for different sensor type?) + #* id is a random id that is generated when the sensor starts + #* temp is 12 bit signed magnitude scaled by 10 celcius + #* humi is 8 bit relative humidity percentage + #* Based on reverse engineering with gnu-radio and the nice article here: + #* http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ + # 0x4A/74 0x70/112 0xEF/239 0xFF/255 0x97/151 | Sensor ID: 0x4A7 | 255% | 239 | OK + #{ Dispatch($defs{sduino}, "W64#FF48D0C9FFBA", undef) } + + #* Message Format: + #* .- [0] -. .- [1] -. .- [2] -. .- [3] -. .- [4] -. + #* | | | | | | | | | | + #* SSSS.DDDD DDN_.TTTT TTTT.TTTT WHHH.HHHH CCCC.CCCC + #* | | | || | | | | | | || | | | + #* | | | || | | | | | | || | `--------- CRC + #* | | | || | | | | | | |`-------- Humidity + #* | | | || | | | | | | | + #* | | | || | | | | | | `---- weak battery + #* | | | || | | | | | | + #* | | | || | | | | `----- Temperature T * 0.1 + #* | | | || | | | | + #* | | | || | | `---------- Temperature T * 1 + #* | | | || | | + #* | | | || `--------------- Temperature T * 10 + #* | | | | `--- new battery + #* | | `---------- ID + #* `---- START = 9 + #* + #*/ + $msg = substr($msg,0,16); + my (undef ,$rawData) = split("#",$msg); + my $hlen = length($rawData); + my $blen = $hlen * 4; + my $msg_vor ="W64#"; + my $bitData20; + my $sign = 0; + my $rr2; + my $vorpre = -1; + my $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + + my $temptyp = substr($bitData,0,8); + if( $temptyp == "11111110" ) { + $rawData = SD_WS_WH2SHIFT($rawData); + $msg = $msg_vor.$rawData; + $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + Log3 $iohash, 4, "$name: SD_WS_WH2_1 msg=$msg length:".length($bitData) ; + Log3 $iohash, 4, "$name: SD_WS_WH2_1 bitdata: $bitData" ; + } else{ + if ( $temptyp == "11111101" ) { + $rawData = SD_WS_WH2SHIFT($rawData); + $rawData = SD_WS_WH2SHIFT($rawData); + $msg = $msg_vor.$rawData; + $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + Log3 $iohash, 4, "$name: SD_WS_WH2_2 msg=$msg length:".length($bitData) ; + Log3 $iohash, 4, "$name: SD_WS_WH2_2 bitdata: $bitData" ; + } + } + + if( $temptyp == "11111111" ) { + $vorpre = 8; + }else{ + Log3 $iohash, 4, "$name: SD_WS_WH2_4 Error kein WH2: Typ: $temptyp" ; + return ""; + } + + my $rc = eval + { + require Digest::CRC; + Digest::CRC->import(); + 1; + }; + + if($rc) + { + # Digest::CRC loaded and imported successfully + Log3 $iohash, 4, "$name: SD_WS_WH2_1 msg: $msg raw: $rawData " ; + $rr2 = SD_WS_WH2CRCCHECK($rawData); + if ($rr2 == 0 ){ + # 1.CRC OK + Log3 $iohash, 4, "$name: SD_WS_WH2_1 CRC_OK : CRC=$rr2 msg: $msg check:".$rawData ; + }else{ + Log3 $iohash, 4, "$name: SD_WS_WH2_4 CRC_Error: CRC=$rr2 msg: $msg check:".$rawData ; + return ""; + } + }else { + Log3 $iohash, 1, "$name: SD_WS_WH2_3 CRC_not_load: Modul Digest::CRC fehlt" ; + return ""; + } + + $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + Log3 $iohash, 4, "$name converted to bits: WH2 " . $bitData; + $model = "SD_WS_WH2"; + $SensorTyp = "WH2"; + $id = SD_WS_bin2dec(substr($bitData,$vorpre + 4,6)); + $id = sprintf('%03X', $id); + $channel = 0; + $bat = SD_WS_bin2dec(substr($bitData,$vorpre + 20,1)); + $sign = SD_WS_bin2dec(substr($bitData,$vorpre + 12,1)); + + if ($sign == 0) { + # Temp positiv + $temp = (SD_WS_bin2dec(substr($bitData,$vorpre + 13,11))) / 10; + }else{ + # Temp negativ + $temp = -(SD_WS_bin2dec(substr($bitData,$vorpre + 13,11))) / 10; + } + Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) sensor id=$id, Data:".substr($bitData,$vorpre + 12,12)." temp=$temp"; + $hum = SD_WS_bin2dec(substr($bitData,$vorpre + 24,8)); # TFA 30.3157 nur Temp, Hum = 255 + Log3 $iohash, 4, "$name SD_WS_WH2_8: $protocol ($SensorTyp) sensor id=$id, Data:".substr($bitData,$vorpre + 24,8)." hum=$hum"; + Log3 $iohash, 4, "$name SD_WS_WH2_9: $protocol ($SensorTyp) sensor id=$id, channel=$channel, temp=$temp, hum=$hum"; + + } + elsif (defined($decodingSubs{$protocol})) # durch den hash decodieren { - $SensorTyp=$decodingSubs{$protocol}{sensortype}; - - return "" && Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) prematch error" if (!$decodingSubs{$protocol}{prematch}->( $rawData )); - return "" && Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) crc error" if (!$decodingSubs{$protocol}{crcok}->( $rawData )); - + if (!$decodingSubs{$protocol}{prematch}->( $rawData )) + { + Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) prematch error" ; + return ""; + } + my $retcrc=$decodingSubs{$protocol}{crcok}->( $rawData ); + if (!$retcrc) { + Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) crc error: $retcrc"; + return ""; + } $id=$decodingSubs{$protocol}{id}->( $rawData,$bitData ); #my $temphex=$decodingSubs{$protocol}{temphex}->( $rawData,$bitData ); @@ -299,13 +542,14 @@ sub SD_WS_Parse($$) $hum=$decodingSubs{$protocol}{hum}->( $rawData,$bitData ); $channel=$decodingSubs{$protocol}{channel}->( $rawData,$bitData ); $model = $decodingSubs{$protocol}{model}; - $bat = $decodingSubs{$protocol}{bat}; + $bat = $decodingSubs{$protocol}{bat}->( $rawData,$bitData ); + $trend = $decodingSubs{$protocol}{trend}->( $rawData,$bitData ) if (defined($decodingSubs{$protocol}{trend})); - Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) sensor id=$id, channel=$channel, temp=$temp, hum=$hum"; + Log3 $iohash, 4, "$name decoded protocolid: $protocol ($SensorTyp) sensor id=$id, channel=$channel, temp=$temp, hum=$hum, bat=$bat"; } else { - Log3 $iohash, 4, "SD_WS_Parse: unknown message, please report. converted to bits: $bitData"; + Log3 $iohash, 2, "SD_WS_WH2: unknown message, please report. converted to bits: $bitData"; return undef; } @@ -334,8 +578,7 @@ sub SD_WS_Parse($$) Log3 $iohash, 1, 'SD_WS: UNDEFINED sensor ' . $model . ' detected, code ' . $deviceCode; return "UNDEFINED $deviceCode SD_WS $deviceCode"; } - #Log3 $iohash, 3, 'SD_WS: ' . $def->{NAME} . ' ' . $id; - + my $hash = $def; $name = $hash->{NAME}; return "" if(IsIgnored($name)); @@ -358,16 +601,16 @@ sub SD_WS_Parse($$) } else { $hash->{bitMSG} = $bitData; } - - my $state = "T: $temp" . ($hum > 0 ? " H: $hum":""); + + my $state = (($temp > -60 && $temp < 70) ? "T: $temp":"T: xx") . (($hum > 0 && $hum < 100) ? " H: $hum":""); readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", $state); - readingsBulkUpdate($hash, "temperature", $temp) if (defined($temp)); - readingsBulkUpdate($hash, "humidity", $hum) if (defined($hum) && $hum > 0); - readingsBulkUpdate($hash, "battery", $bat) if (defined($bat)); - readingsBulkUpdate($hash, "channel", $channel) if (defined($channel)); - readingsBulkUpdate($hash, "trend", $trend) if (defined($trend)); + readingsBulkUpdate($hash, "temperature", $temp) if (defined($temp)&& ($temp > -60 && $temp < 70 )); + readingsBulkUpdate($hash, "humidity", $hum) if (defined($hum) && ($hum > 0 && $hum < 100 )) ; + readingsBulkUpdate($hash, "battery", $bat) if (defined($bat) && length($bat) > 0) ; + readingsBulkUpdate($hash, "channel", $channel) if (defined($channel)&& length($channel) > 0); + readingsBulkUpdate($hash, "trend", $trend) if (defined($trend) && length($trend) > 0); readingsEndUpdate($hash, 1); # Notify is done by Dispatch @@ -390,6 +633,13 @@ sub SD_WS_Attr(@) return undef; } +sub SD_WS_bin2dec($) + { + my $h = shift; + my $int = unpack("N", pack("B32",substr("0" x 32 . $h, -32))); + return sprintf("%d", $int); + } + sub SD_WS_binaryToNumber { @@ -401,6 +651,31 @@ sub SD_WS_binaryToNumber return oct("0b".substr($binstr,$fbit,($lbit-$fbit)+1)); } + sub SD_WS_WH2CRCCHECK($) { + my $rawData = shift; + my $datacheck1 = pack( 'H*', substr($rawData,2,length($rawData)-2) ); + my $crcmein1 = Digest::CRC->new(width => 8, poly => 0x31); + my $rr3 = $crcmein1->add($datacheck1)->hexdigest; + $rr3 = sprintf("%d", hex($rr3)); + Log3 "SD_WS_CRCCHECK", 4, "SD_WS_WH2CRCCHECK : raw:$rawData CRC=$rr3 " ; + return $rr3 ; + } +sub SD_WS_WH2SHIFT($){ + my $rawData = shift; + my $hlen = length($rawData); + my $blen = $hlen * 4; + my $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + my $bitData2 = '1'.unpack("B$blen", pack("H$hlen", $rawData)); + my $bitData20 = substr($bitData2,0,length($bitData2)-1); + $blen = length($bitData20); + $hlen = $blen / 4; + $rawData = uc(unpack("H$hlen", pack("B$blen", $bitData20))); + $bitData = $bitData20; + Log3 "SD_WS_WH2SHIFT", 4, "SD_WS_WH2SHIFT_0 raw: $rawData length:".length($bitData) ; + Log3 "SD_WS_WH2SHIFT", 4, "SD_WS_WH2SHIFT_1 bitdata: $bitData" ; + return $rawData; + } + 1; =pod @@ -417,6 +692,9 @@ sub SD_WS_binaryToNumber
    • Bresser 7009994
    • Opus XT300
    • +
    • BresserTemeo
    • +
    • WH2 (TFA Dostmann/Wertheim 30.3157(Temperature only!) (sold in Germany), Agimex Rosenborg 66796 (sold in Denmark),ClimeMET CM9088 (Sold in UK)
    • +
    • PV-8644 infactory Poolthermometer

    New received device are add in fhem with autocreate. @@ -470,6 +748,8 @@ sub SD_WS_binaryToNumber
  • Bresser 7009994
  • Opus XT300
  • BresserTemeo
  • +
  • WH2 (TFA Dostmann/Wertheim 30.3157(Temperatur!) (Deutschland), Agimex Rosenborg 66796 (Denmark),ClimeMET CM9088 (UK)
  • +
  • PV-8644 infactory Poolthermometer

  • Neu empfangene Sensoren werden in FHEM per autocreate angelegt. diff --git a/fhem/FHEM/14_SD_WS07.pm b/fhem/FHEM/14_SD_WS07.pm index 3576f887d..fc7be722b 100644 --- a/fhem/FHEM/14_SD_WS07.pm +++ b/fhem/FHEM/14_SD_WS07.pm @@ -3,7 +3,7 @@ # # The purpose of this module is to support serval eurochron # weather sensors like eas8007 which use the same protocol -# Sidey79 & Ralf9 2015-2016 +# Sidey79, Ralf9 2015-2017 # package main; @@ -20,11 +20,11 @@ SD_WS07_Initialize($) { my ($hash) = @_; - $hash->{Match} = "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}"; ## pos 7 ist aktuell immer 0xF + $hash->{Match} = "^P7#[A-Fa-f0-9]{6}F[A-Fa-f0-9]{2}(#R[A-F0-9][A-F0-9]){0,1}\$"; ## pos 7 ist aktuell immer 0xF $hash->{DefFn} = "SD_WS07_Define"; $hash->{UndefFn} = "SD_WS07_Undef"; $hash->{ParseFn} = "SD_WS07_Parse"; - $hash->{AttrFn} = "SD_WS07_Attr"; + $hash->{AttrFn} = "SD_WS07_Attr"; $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 " . "$readingFnAttributes "; $hash->{AutoCreate} = @@ -73,7 +73,11 @@ SD_WS07_Parse($$) my ($iohash, $msg) = @_; #my $rawData = substr($msg, 2); my $name = $iohash->{NAME}; - my (undef ,$rawData) = split("#",$msg); + my (undef ,$rawData, $rssi) = split("#",$msg); + if (defined($rssi)) { + $rssi = hex(substr($rssi,1)); + $rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74)); + } #$protocol=~ s/^P(\d+)/$1/; # extract protocol my $model = "SD_WS07"; @@ -81,7 +85,12 @@ SD_WS07_Parse($$) my $blen = $hlen * 4; my $bitData = unpack("B$blen", pack("H$hlen", $rawData)); - Log3 $name, 4, "SD_WS07_Parse $model ($msg) length: $hlen"; + if (defined($rssi)) { + Log3 $name, 4, "$name SD_WS07_Parse $model ($msg) length: $hlen RSSI = $rssi"; + } else { + Log3 $name, 4, "$name SD_WS07_Parse $model ($msg) length: $hlen"; + } + # 4 8 9 12 24 28 36 # 0011 0110 1 010 000100000010 1111 00111000 0000 eas8007 @@ -110,12 +119,9 @@ SD_WS07_Parse($$) $model=$model."_T"; } else { $model=$model."_TH"; - - - } - - if ($hum > 100) { - return ''; # Eigentlich muesste sowas wie ein skip rein, damit ggf. spaeter noch weitre Sensoren dekodiert werden koennen. + if ($hum < 10 || $hum > 99) { + return ''; + } } if ($temp > 700 && $temp < 3840) { @@ -125,7 +131,7 @@ SD_WS07_Parse($$) } $temp /= 10; - Log3 $iohash, 4, "$model decoded protocolid: 7 sensor id=$id, channel=$channel, temp=$temp, hum=$hum, bat=$bat"; + Log3 $iohash, 4, "$name $model decoded protocolid: 7 sensor id=$id, channel=$channel, temp=$temp, hum=$hum, bat=$bat"; my $deviceCode; @@ -144,7 +150,7 @@ SD_WS07_Parse($$) $def = $modules{SD_WS07}{defptr}{$deviceCode} if(!$def); if(!$def) { - Log3 $iohash, 1, 'SD_WS07: UNDEFINED sensor ' . $model . ' detected, code ' . $deviceCode; + Log3 $iohash, 1, "$name SD_WS07: UNDEFINED sensor $model detected, code $deviceCode"; return "UNDEFINED $deviceCode SD_WS07 $deviceCode"; } #Log3 $iohash, 3, 'SD_WS07: ' . $def->{NAME} . ' ' . $id; @@ -153,7 +159,7 @@ SD_WS07_Parse($$) $name = $hash->{NAME}; return "" if(IsIgnored($name)); - Log3 $name, 4, "SD_WS07: $name ($rawData)"; + Log3 $name, 4, "$iohash->{NAME} SD_WS07: $name ($rawData)"; if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef))) { @@ -179,6 +185,10 @@ SD_WS07_Parse($$) readingsEndUpdate($hash, 1); # Notify is done by Dispatch + if(defined($rssi)) { + $hash->{RSSI} = $rssi; + } + return $name; } @@ -208,7 +218,7 @@ sub SD_WS07_Attr(@) =begin html -

    Wether Sensors protocol #7

    +

    Weather Sensors protocol #7

      The SD_WS07 module interprets temperature sensor messages received by a Device like CUL, CUN, SIGNALduino etc.

      @@ -218,12 +228,12 @@ sub SD_WS07_Attr(@)
    • Technoline WS6750/TX70DTH

    - New received device are add in fhem with autocreate. + New received devices are added in FHEM with autocreate.

    Define -
      The received devices created automatically.
      +
        The received devices are created automatically.
        The ID of the defice is the cannel or, if the longid attribute is specified, it is a combination of channel and some random generated bits at powering the sensor and the channel.
        If you want to use more sensors, than channels available, you can use the longid option to differentiate them.
      @@ -232,18 +242,17 @@ sub SD_WS07_Attr(@) Generated readings:
      Some devices may not support all readings, so they will not be presented
        -
      • State (T: H:)
      • +
      • state (T: H:)
      • temperature (°C)
      • -
      • humidity: (The humidity (1-100 if available)
      • +
      • humidity: (the humidity 1-100)
      • battery: (low or ok)
      • -
      • channel: (The Channelnumber (number if)
      • +
      • channel: (the channelnumberf)

      Attributes @@ -251,8 +260,8 @@ sub SD_WS07_Attr(@) Set
        N/A

      - - Set
        N/A

      + + Get
        N/A

    @@ -263,7 +272,7 @@ sub SD_WS07_Attr(@)

    SD_WS07

      - Das SD_WS07 Module verarbeitet von einem IO Geraet (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur-Sensoren.
      + Das SD_WS07 Modul verarbeitet von einem IO Geraet (CUL, CUN, SIGNALDuino, etc.) empfangene Nachrichten von Temperatur-Sensoren.

      Unterstützte Modelle:
        @@ -279,14 +288,14 @@ sub SD_WS07_Attr(@) Define
          Die empfangenen Sensoren werden automatisch angelegt.
          - Die ID der angelgten Sensoren ist entweder der Kanal des Sensors, oder wenn das Attribut longid gesetzt ist, dann wird die ID aus dem Kanal und einer Reihe von Bits erzeugt, welche der Sensor beim Einschalten zufaellig vergibt.
          + Die ID der angelegten Sensoren ist entweder der Kanal des Sensors, oder wenn das Attribut longid gesetzt ist, dann wird die ID aus dem Kanal und einer Reihe von Bits erzeugt, welche der Sensor beim Einschalten zufaellig vergibt.

        Generierte Readings:
          -
        • State (T: H:)
        • -
        • temperature (°C)
        • +
        • state: (T: H:)
        • +
        • temperature: (°C)
        • humidity: (Luftfeuchte (1-100)
        • battery: (low oder ok)
        • channel: (Der Sensor Kanal)
        • @@ -296,7 +305,6 @@ sub SD_WS07_Attr(@) @@ -304,8 +312,9 @@ sub SD_WS07_Attr(@) Set
            N/A

          - - Set
            N/A

          + + Get
            N/A

          +
        diff --git a/fhem/FHEM/14_SD_WS09.pm b/fhem/FHEM/14_SD_WS09.pm index 45e7e13c7..bdcc9f37b 100644 --- a/fhem/FHEM/14_SD_WS09.pm +++ b/fhem/FHEM/14_SD_WS09.pm @@ -1,33 +1,44 @@ ############################################## - ############################################## # $Id$ # # The purpose of this module is to support serval # weather sensors like WS-0101 (Sender 868MHz ASK Epmfänger RX868SH-DV elv) # Sidey79 & pejonp 2015 # + # 22.09.2017: rainTotal --> rain_total + # 23.09.2017: windDirAverage SabineT https://forum.fhem.de/index.php/topic,75225.msg669950.html#msg669950 + # + # + package main; use strict; use warnings; - use Digest::CRC qw(crc); - #use Math::Round qw/nearest/; + # werden benötigt, aber im Programm noch extra abgetestet + #use Digest::CRC qw(crc); + #use Math::Trig; + sub SD_WS09_Initialize($) { my ($hash) = @_; - $hash->{Match} = "^P9#[A-Fa-f0-9]+"; ## pos 7 ist aktuell immer 0xF + $hash->{Match} = "^P9#F[A-Fa-f0-9]+"; ## pos 7 ist aktuell immer 0xF $hash->{DefFn} = "SD_WS09_Define"; $hash->{UndefFn} = "SD_WS09_Undef"; $hash->{ParseFn} = "SD_WS09_Parse"; - $hash->{AttrFn} = "SD_WS09_Attr"; + $hash->{AttrFn} = "SD_WS09_Attr"; $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 " + ."model:CTW600,WH1080 ignore:0,1 " ."windKorrektur:-3,-2,-1,0,1,2,3 " + ."Unit_of_Wind:m/s,km/h,ft/s,mph,bft,knot " + ."WindDirAverageTime " + ."WindDirAverageMinSpeed " + ."WindDirAverageDecay " ."$readingFnAttributes "; $hash->{AutoCreate} = - { "SD_WS09.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.* windKorrektur:.*:0 " , FILTER => "%NAME", GPLOT => "temp4hum4:Temp/Hum,", autocreateThreshold => "2:180"} }; + { "SD_WS09.*" => { ATTR => "event-min-interval:.*:300 event-on-change-reading:.* windKorrektur:.*:0 verbose:5" , FILTER => "%NAME", GPLOT => "WH1080wind4:windSpeed/windGust,", autocreateThreshold => "2:180"} }; } @@ -48,6 +59,10 @@ $modules{SD_WS09}{defptr}{$a[2]} = $hash; $hash->{STATE} = "Defined"; + my $model = $a[2]; + $model =~ s/_.*$//; + $hash->{MODEL} = $model; + my $name= $hash->{NAME}; return undef; } @@ -70,6 +85,8 @@ my $name = $iohash->{NAME}; my (undef ,$rawData) = split("#",$msg); my @winddir_name=("N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"); + my %uowind_unit= ("m/s",'1',"km/h",'3.6',"ft/s",'3.28',"bft",'-1',"mph",'2.24',"knot",'1.94'); + my %uowind_index = ("m/s",'0',"km/h",'1',"ft/s",'2',"mph",'3',"knot",'4',"bft",'5'); my $hlen = length($rawData); my $blen = $hlen * 4; my $bitData = unpack("B$blen", pack("H$hlen", $rawData)); @@ -78,175 +95,216 @@ my $rain = 0; my $deviceCode = 0; my $model = "undef"; # 0xFFA -> WS0101/WH1080 alles andere -> CTW600 - my $modelattr ; my $modelid; - my $windSpeed = 0; - my $windguest =0; + my $windSpeed; + my $windSpeed_kmh; + my $windSpeed_fts; + my $windSpeed_bft; + my $windSpeed_mph; + my $windSpeed_kn; + my $windguest; + my $windguest_kmh; + my $windguest_fts; + my $windguest_bft; + my $windguest_mph; + my $windguest_kn; my $sensdata; my $id; my $bat = 0; - my $temp; - my $hum; - my $windDirection ; - my $windDirectionText; + my $temp = 0; + my $hum = 1; + my $windDirection = 1 ; + my $windDirectionText = "N"; + my $windDirectionDegree = 0; + my $FOuvo ; # UV data nybble ? + my $FOlux ; # Lux High byte (full scale = 4,000,000?) # Lux Middle byte # Lux Low byte, Unit = 0.1 Lux (binary) + my $rr2 ; + my $state; + my $msg_vor = 'P9#'; + my $minL1 = 70; + my $minL2 = 60; + my $whid; + my $wh; + my $rawData_merk; + my $wfaktor = 1; + my @windstat; - - $modelattr = AttrVal($iohash->{NAME},'WS09_WSModel',0); - if ($modelattr eq '0'){ - $modelattr = "undef"; - } + my $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble + Log3 $iohash, 4, "$name: SD_WS09_Parse0 msg=$rawData Bin=$bitData syncp=$syncpos length:".length($bitData) ; + + if ($syncpos ==-1 || length($bitData)-$syncpos < $minL2) + { + Log3 $iohash, 4, "$name: SD_WS09_Parse EXIT: msg=$rawData syncp=$syncpos length:".length($bitData) ; + return undef; + } my $crcwh1080 = AttrVal($iohash->{NAME},'WS09_CRCAUS',0); - Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_AUS:$crcwh1080 Model=$modelattr" ; - - my $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble - Log3 $iohash, 3, "$name: SD_WS09_Parse0 Bin=$bitData syncp=$syncpos length:".length($bitData) ; + Log3 $iohash, 4, "$name: SD_WS09_Parse CRC_AUS:$crcwh1080 " ; + $rawData_merk = $rawData; + + my $rc = eval + { + require Digest::CRC; + Digest::CRC->import(); + 1; + }; - if ($syncpos ==-1 || length($bitData)-$syncpos < 78) - { - Log3 $iohash, 3, "$name: SD_WS09_Parse EXIT: msg=$rawData syncp=$syncpos length:".length($bitData) ; - return undef; - } - - my $wh = substr($bitData,0,8); - #CRC-Check bei WH1080/WS0101 WS09_CRCAUS=0 und WS09_WSModel = undef oder Wh1080 - if(($crcwh1080 == 0) && ($modelattr ne "CTW600")) { - if($wh == "11111111") { - if ($syncpos == 0) - { - $hlen = length($rawData); - $blen = $hlen * 4; - $bitData2 = '11'.unpack("B$blen", pack("H$hlen", $rawData)); - $bitData20 = substr($bitData2,0,length($bitData2)-2); - $blen = length($bitData20); - $hlen = $blen / 4; - $msg = 'P9#'.unpack("H$hlen", pack("B$blen", $bitData20)); - $bitData = $bitData20; - Log3 $iohash, 3, "$name: SD_WS09_Parse sync1 msg=$msg syncp=$syncpos length:".length($bitData) ; - } - - if ($syncpos == 1) - { - $hlen = length($rawData); - $blen = $hlen * 4; - $bitData2 = '1'.unpack("B$blen", pack("H$hlen", $rawData)); - $bitData20 = substr($bitData2,0,length($bitData2)-1); - $blen = length($bitData20); - $hlen = $blen / 4; - $msg = 'P9#'.unpack("H$hlen", pack("B$blen", $bitData20)); - $bitData = $bitData20; - Log3 $iohash, 3, "$name: SD_WS09_Parse sync2 msg=$msg syncp=$syncpos length:".length($bitData) ; - } - - - my $datacheck = pack( 'H*', substr($msg,5,length($msg)-5) ); - my $crcmein = Digest::CRC->new(width => 8, poly => 0x31); - my $rr2 = $crcmein->add($datacheck)->hexdigest; - if ($rr2 eq "0"){ - $model = "WH1080"; - Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_OK: CRC=$rr2 Model=$model attr=$modelattr" ; - }else{ - Log3 $iohash, 3, "$name: SD_WS09_Parse CRC_Error: msg=$msg CRC=$rr2 " ; - return undef; - } - }else{ - $model = "CTW600"; - Log3 $iohash, 3, "$name: SD_WS09_Parse CTW600: Model=$model attr=$modelattr" ; - } - }; - - if( ($wh == "11111111") || ($model eq "WH1080")) { - if ($modelattr eq "CTW600"){ - Log3 $iohash, 3, "$name: SD_WS09_WH1080 off=$modelattr Model=$model " ; - return undef; - } - $sensdata = substr($bitData,8); - my $whid = substr($sensdata,0,4); - if( $whid == "1010" ){ # A - Log3 $iohash, 3, "$name: SD_WS09_Parse WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ; + if($rc) # test ob Digest::CRC geladen wurde + { + $rr2 = SD_WS09_CRCCHECK($rawData); + if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { + # 1. OK + $model = "WH1080"; + Log3 $iohash, 4, "$name: SD_WS09_SHIFT_0 OK rwa:$rawData" ; + } else { + # 1. nok + $rawData = SD_WS09_SHIFT($rawData); + Log3 $iohash, 4, "$name: SD_WS09_SHIFT_1 NOK rwa:$rawData" ; + $rr2 = SD_WS09_CRCCHECK($rawData); + if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { + # 2.ok + $msg = $msg_vor.$rawData; $model = "WH1080"; - $id = SD_WS09_bin2dec(substr($sensdata,4,8)); - $bat = (SD_WS09_bin2dec((substr($sensdata,64,4))) == 0) ? 'ok':'low' ; # decode battery = 0 --> ok - $temp = (SD_WS09_bin2dec(substr($sensdata,12,12)) - 400)/10; - $hum = SD_WS09_bin2dec(substr($sensdata,24,8)); - $windDirection = SD_WS09_bin2dec(substr($sensdata,68,4)); - $windDirectionText = $winddir_name[$windDirection]; - $windSpeed = round((SD_WS09_bin2dec(substr($sensdata,32,8))* 34)/100,01); - Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ; - $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01); - Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ; - $rain = SD_WS09_bin2dec(substr($sensdata,56,8)) * 0.3; - Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Rain bit: ".substr($sensdata,56,8)." Dec: " . $rain ; + Log3 $iohash, 4, "$name: SD_WS09_SHIFT_2 OK rwa:$rawData msg:$msg" ; + } else { + # 2. nok + $rawData = SD_WS09_SHIFT($rawData); + Log3 $iohash, 4, "$name: SD_WS09_SHIFT_3 NOK rwa:$rawData" ; + $rr2 = SD_WS09_CRCCHECK($rawData); + if ($rr2 == 0 || (($rr2 == 49) && ($crcwh1080 == 2)) ) { + # 3. ok + $msg = $msg_vor.$rawData; + $model = "WH1080"; + Log3 $iohash, 4, "$name: SD_WS09_SHIFT_4 OK rwa:$rawData msg:$msg" ; + }else{ + # 3. nok + $rawData = $rawData_merk; + $msg = $msg_vor.$rawData; + Log3 $iohash, 4, "$name: SD_WS09_SHIFT_5 NOK rwa:$rawData msg:$msg" ; + } + } + } + }else { + Log3 $iohash, 1, "$name: SD_WS09 CRC_not_load: Modul Digest::CRC fehlt: cpan install Digest::CRC or sudo apt-get install libdigest-crc-perl" ; + return ""; + } + + $hlen = length($rawData); + $blen = $hlen * 4; + $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + Log3 $iohash, 4, "$name: SD_WS09_CRC_test2 rwa:$rawData msg:$msg CRC:".SD_WS09_CRCCHECK($rawData) ; + + if( $model eq "WH1080") { + $sensdata = substr($bitData,8); + $whid = substr($sensdata,0,4); + + if( $whid == "1010" ){ # A Wettermeldungen + Log3 $iohash, 4, "$name: SD_WS09_Parse_1 msg=$sensdata length:".length($sensdata) ; + $model = "WH1080"; + $id = SD_WS09_bin2dec(substr($sensdata,4,8)); + $bat = (SD_WS09_bin2dec((substr($sensdata,64,4))) == 0) ? 'ok':'low' ; # decode battery = 0 --> ok + $temp = (SD_WS09_bin2dec(substr($sensdata,12,12)) - 400)/10; + $hum = SD_WS09_bin2dec(substr($sensdata,24,8)); + $windDirection = SD_WS09_bin2dec(substr($sensdata,68,4)); + $windDirectionText = $winddir_name[$windDirection]; + $windDirectionDegree = $windDirection * 360 / 16; + $windSpeed = round((SD_WS09_bin2dec(substr($sensdata,32,8))* 34)/100,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_2 ".$model." id:$id, Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ; + $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_3 ".$model." id:$id, Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ; + $rain = SD_WS09_bin2dec(substr($sensdata,52,12)) * 0.3; + Log3 $iohash, 4, "$name: SD_WS09_Parse_4 ".$model." id:$id, Rain bit: ".substr($sensdata,52,12)." Dec: " . $rain ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_5 ".$model." id:$id, bat:$bat, temp=$temp, hum=$hum, winddir=$windDirection:$windDirectionText wS=$windSpeed, wG=$windguest, rain=$rain"; + } elsif( $whid == "1011" ){ # B DCF-77 Zeitmeldungen vom Sensor + my $hrs1 = substr($sensdata,16,8); + my $hrs; + my $mins; + my $sec; + my $mday; + my $month; + my $year; + $id = SD_WS09_bin2dec(substr($sensdata,4,8)); + Log3 $iohash, 4, "$name: SD_WS09_Parse_6 Zeitmeldung0: HRS1=$hrs1 id:$id" ; + $hrs = sprintf "%02d" , SD_WS09_BCD2bin(substr($sensdata,18,6) ) ; # Stunde + $mins = sprintf "%02d" , SD_WS09_BCD2bin(substr($sensdata,24,8)); # Minute + $sec = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,32,8)); # Sekunde + #day month year + $year = SD_WS09_BCD2bin(substr($sensdata,40,8)); # Jahr + $month = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,51,5)); # Monat + $mday = sprintf "%02d" ,SD_WS09_BCD2bin(substr($sensdata,56,8)); # Tag + Log3 $iohash, 4, "$name: SD_WS09_Parse_7 Zeitmeldung1: id:$id, msg=$rawData length:".length($bitData) ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_8 Zeitmeldung2: id:$id, HH:mm:ss - ".$hrs.":".$mins.":".$sec ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_9 Zeitmeldung3: id:$id, dd.mm.yy - ".$mday.".".$month.".".$year ; + return $name; + } elsif( $whid == "0111" ){ # 7 UV/Solar Meldungen vom Sensor + # Fine Offset (Solar Data) message BYTE offsets (within receive buffer) + # Examples= FF 75 B0 55 00 97 8E 0E *CRC*OK* + # =FF 75 B0 55 00 8F BE 92 *CRC*OK* + # symbol FOrunio = 0 ; Fine Offset Runin byte = FF + # symbol FOsaddo = 1 ; Solar Pod address word + # symbol FOuvo = 3 ; UV data nybble ? + # symbol FOluxHo = 4 ; Lux High byte (full scale = 4,000,000?) + # symbol FOluxMo = 5 ; Lux Middle byte + # symbol FOluxLo = 6 ; Lux Low byte, Unit = 0.1 Lux (binary) + # symbol FOcksumo= 7 ; CRC checksum (CRC-8 shifting left) + $id = SD_WS09_bin2dec(substr($sensdata,4,8)); + $FOuvo = SD_WS09_bin2dec(substr($sensdata,12,4)); + $FOlux = SD_WS09_bin2dec(substr($sensdata,24,24))/10; + Log3 $iohash, 4, "$name: SD_WS09_Parse_10 UV-Solar1: id:$id, UV:".$FOuvo." Lux:".$FOlux ; } else { - if( $whid == "1011" ){ # B DCF-77 Zeitmeldungen vom Sensor - my $hrs1 = substr($sensdata,16,8); - my $hrs; - my $mins; - my $sec; - my $mday; - my $month; - my $year; - $id = SD_WS09_bin2dec(substr($sensdata,4,8)); - Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung0: HRS1=$hrs1 id:$id" ; - $hrs = SD_WS09_BCD2bin(substr($sensdata,18,6) ) ; # Stunde - $mins = SD_WS09_BCD2bin(substr($sensdata,24,8)); # Minute - $sec = SD_WS09_BCD2bin(substr($sensdata,32,8)); # Sekunde - #day month year - $year = SD_WS09_BCD2bin(substr($sensdata,40,8)); # Jahr - $month = SD_WS09_BCD2bin(substr($sensdata,51,5)); # Monat - $mday = SD_WS09_BCD2bin(substr($sensdata,56,8)); # Tag - Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung1: msg=$rawData syncp=$syncpos length:".length($bitData) ; - Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung2: HH:mm:ss - ".$hrs.":".$mins.":".$sec ; - Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung3: dd.mm.yy - ".$mday.":".$month.":".$year ; - return $name; - } - Log3 $iohash, 3, "$name: SD_WS09_Parse Zeitmeldung4: msg=$rawData syncp=$syncpos length:".length($sensdata) ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_Ex Exit: msg=$rawData length:".length($sensdata) ; + Log3 $iohash, 4, "$name: SD_WS09_WH10 Exit: Model=$model " ; return undef; } }else{ - if ($modelattr eq "WH1080"){ - Log3 $iohash, 3, "$name: SD_WS09_CTW600 off=$modelattr Model=$model " ; - return undef; - } else { - # eine CTW600 wurde erkannt - $sensdata = substr($bitData,$syncpos+8); - Log3 $iohash, 3, "$name: SD_WS09_Parse CTW WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ; - $model = "CTW600"; - my $nn1 = substr($sensdata,10,2); # Keine Bedeutung - my $nn2 = substr($sensdata,62,4); # Keine Bedeutung - $modelid = substr($sensdata,0,4); - Log3 $iohash, 3, "$name: SD_WS09_Parse Id: ".$modelid." NN1:$nn1 NN2:$nn2" ; - Log3 $iohash, 3, "$name: SD_WS09_Parse Id: ".$modelid." Bin-Sync=$sensdata syncp=$syncpos length:".length($sensdata) ; - $bat = SD_WS09_bin2dec((substr($sensdata,0,3))) ; - $id = SD_WS09_bin2dec(substr($sensdata,4,6)); - $temp = (SD_WS09_bin2dec(substr($sensdata,12,10)) - 400)/10; - $hum = SD_WS09_bin2dec(substr($sensdata,22,8)); - $windDirection = SD_WS09_bin2dec(substr($sensdata,66,4)); - $windDirectionText = $winddir_name[$windDirection]; - $windSpeed = round(SD_WS09_bin2dec(substr($sensdata,30,16))/240,01); - Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ; - $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01); - Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ; - $rain = round(SD_WS09_bin2dec(substr($sensdata,46,16)) * 0.3,01); - Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." Rain bit: ".substr($sensdata,46,16)." Dec: " . $rain ; + # es wird eine CTW600 angenommen + $syncpos= index($bitData,"11111110"); #7x1 1x0 preamble + $wh = substr($bitData,0,8); + if ( $wh == "11111110" && length($bitData) > $minL1 ) + { + Log3 $iohash, 4, "$name: SD_WS09_Parse_11 CTW600 EXIT: msg=$bitData wh:$wh length:".length($bitData) ; + $sensdata = substr($bitData,$syncpos+8); + Log3 $iohash, 4, "$name: SD_WS09_Parse_12 CTW WH=$wh msg=$sensdata syncp=$syncpos length:".length($sensdata) ; + $model = "CTW600"; + $whid = "0000"; + my $nn1 = substr($sensdata,10,2); # Keine Bedeutung + my $nn2 = substr($sensdata,62,4); # Keine Bedeutung + $modelid = substr($sensdata,0,4); + Log3 $iohash, 4, "$name: SD_WS09_Parse_13 Id: ".$modelid." NN1:$nn1 NN2:$nn2" ; + Log3 $iohash, 4, "$name: SD_WS09_Parse_14 Id: ".$modelid." Bin-Sync=$sensdata syncp=$syncpos length:".length($sensdata) ; + $bat = SD_WS09_bin2dec((substr($sensdata,0,3))) ; + $id = SD_WS09_bin2dec(substr($sensdata,4,6)); + $temp = (SD_WS09_bin2dec(substr($sensdata,12,10)) - 400)/10; + $hum = SD_WS09_bin2dec(substr($sensdata,22,8)); + $windDirection = SD_WS09_bin2dec(substr($sensdata,66,4)); + $windDirectionText = $winddir_name[$windDirection]; + $windDirectionDegree = $windDirection * 360 / 16; + $windSpeed = round(SD_WS09_bin2dec(substr($sensdata,30,16))/240,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_15 ".$model." Windspeed bit: ".substr($sensdata,32,8)." Dec: " . $windSpeed ; + $windguest = round((SD_WS09_bin2dec(substr($sensdata,40,8)) * 34)/100,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_16 ".$model." Windguest bit: ".substr($sensdata,40,8)." Dec: " . $windguest ; + $rain = round(SD_WS09_bin2dec(substr($sensdata,46,16)) * 0.3,01); + Log3 $iohash, 4, "$name: SD_WS09_Parse_17 ".$model." Rain bit: ".substr($sensdata,46,16)." Dec: " . $rain ; + }else{ + Log3 $iohash, 4, "$name: SD_WS09_Parse_18 CTW600 EXIT: msg=$bitData length:".length($bitData) ; + return undef; } - } - - Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." id:$id :$sensdata "; - Log3 $iohash, 3, "$name: SD_WS09_Parse ".$model." id:$id, bat:$bat, temp=$temp, hum=$hum, winddir=$windDirection:$windDirectionText wS=$windSpeed, wG=$windguest, rain=$rain"; + } + + + Log3 $iohash, 4, "$name: SD_WS09_Parse_19 ".$model." id:$id :$sensdata "; if($hum > 100 || $hum < 0) { - Log3 $iohash, 3, "$name: SD_WS09_Parse HUM: hum=$hum msg=$rawData " ; + Log3 $iohash, 4, "$name: SD_WS09_Parse HUM: hum=$hum msg=$rawData " ; return undef; } if($temp > 60 || $temp < -40) { - Log3 $iohash, 3, "$name: SD_WS09_Parse TEMP: Temp=$temp msg=$rawData " ; + Log3 $iohash, 4, "$name: SD_WS09_Parse TEMP: Temp=$temp msg=$rawData " ; return undef; } - - my $longids = AttrVal($iohash->{NAME},'longids',0); - if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))) + my $longids = AttrVal($iohash->{NAME},'longids',0); + if ( ($longids ne "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))) { $deviceCode=$model."_".$id; Log3 $iohash,4, "$name: SD_WS09_Parse using longid: $longids model: $model"; @@ -261,58 +319,135 @@ Log3 $iohash, 1, 'SD_WS09_Parse UNDEFINED sensor ' . $model . ' detected, code ' . $deviceCode; return "UNDEFINED $deviceCode SD_WS09 $deviceCode"; } - + my $hash = $def; $name = $hash->{NAME}; - Log3 $name, 4, "SD_WS09_Parse: $name ($rawData)"; + Log3 $name, 4, "SD_WS09_Parse_20: $name ($rawData)"; - - my $windkorr = AttrVal($hash->{NAME},'windKorrektur',0); - if ($windkorr != 0 ) - { - my $oldwinddir = $windDirection; - $windDirection = $windDirection + $windkorr; - $windDirectionText = $winddir_name[$windDirection]; - Log3 $iohash, 3, "SD_WS09_Parse ".$model." Faktor:$windkorr wD:$oldwinddir Korrektur wD:$windDirection:$windDirectionText" ; - } - - if (!defined(AttrVal($hash->{NAME},"event-min-interval",undef))) + if (!defined(AttrVal($name,"event-min-interval",undef))) { my $minsecs = AttrVal($iohash->{NAME},'minsecs',0); if($hash->{lastReceive} && (time() - $hash->{lastReceive} < $minsecs)) { - Log3 $hash, 4, "SD_WS09_Parse $deviceCode Dropped due to short time. minsecs=$minsecs"; - return ""; + Log3 $hash, 4, "SD_WS09_Parse_End $deviceCode Dropped due to short time. minsecs=$minsecs"; + return undef; } } - $def->{lastMSG} = $rawData; - - my $state = "T: $temp ". ($hum>0 ? " H: $hum ":" ")." Ws: $windSpeed "." Wg: $windguest "." Wd: $windDirectionText "." R: $rain"; + my $windkorr = AttrVal($name,'windKorrektur',0); + if ($windkorr != 0 ) + { + my $oldwinddir = $windDirection; + $windDirection = $windDirection + $windkorr; + $windDirectionText = $winddir_name[$windDirection]; + Log3 $hash, 4, "SD_WS09_Parse_WK ".$model." Faktor:$windkorr wD:$oldwinddir Korrektur wD:$windDirection:$windDirectionText" ; + } + + # "Unit_of_Wind:m/s,km/h,ft/s,bft,knot " + # my %uowind_unit= ("m/s",'1',"km/h",'3.6',"ft/s",'3.28',"bft",'-1',"mph",'2.24',"knot",'1.94'); + # B = Wurzel aus ( 9 + 6 * V ) - 3 + # V = 17 Meter pro Sekunde ergibt: B = Wurzel aus( 9 + 6 * 17 ) - 3 + # Das ergibt : 7,53 Beaufort - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "state", $state); - readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne""); - readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne "" && $hum != 0 ); - readingsBulkUpdate($hash, "battery", $bat) if ($bat ne ""); - readingsBulkUpdate($hash, "id", $id) if ($id ne ""); + $windstat[0]= " Ws:$windSpeed Wg:$windguest m/s"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[0] : Faktor:$wfaktor" ; + + $wfaktor = $uowind_unit{"km/h"}; + $windguest_kmh = round ($windguest * $wfaktor,01); + $windSpeed_kmh = round ($windSpeed * $wfaktor,01); + $windstat[1]= " Ws:$windSpeed_kmh Wg:$windguest_kmh km/h"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[1] : Faktor:$wfaktor" ; - #zusätzlich Daten für Wetterstation - readingsBulkUpdate($hash, "rain", $rain ); - readingsBulkUpdate($hash, "windGust", $windguest ); - readingsBulkUpdate($hash, "windSpeed", $windSpeed ); - readingsBulkUpdate($hash, "windDirection", $windDirection ); - readingsBulkUpdate($hash, "windDirectionDegree", $windDirection * 360 / 16); - readingsBulkUpdate($hash, "windDirectionText", $windDirectionText ); + $wfaktor = $uowind_unit{"ft/s"}; + $windguest_fts = round ($windguest * $wfaktor,01); + $windSpeed_fts = round ($windSpeed * $wfaktor,01); + $windstat[2]= " Ws:$windSpeed_fts Wg:$windguest_fts ft/s"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[2] : Faktor:$wfaktor" ; + + $wfaktor = $uowind_unit{"mph"}; + $windguest_mph = round ($windguest * $wfaktor,01); + $windSpeed_mph = round ($windSpeed * $wfaktor,01); + $windstat[3]= " Ws:$windSpeed_mph Wg:$windguest_mph mph"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[3] : Faktor:$wfaktor" ; + + $wfaktor = $uowind_unit{"knot"}; + $windguest_kn = round ($windguest * $wfaktor,01); + $windSpeed_kn = round ($windSpeed * $wfaktor,01); + $windstat[4]= " Ws:$windSpeed_kn Wg:$windguest_kn kn" ; + Log3 $hash, 4, "SD_WS09_Wind $windstat[4] : Faktor:$wfaktor" ; + + $windguest_bft = round(sqrt( 9 + (6 * $windguest)) - 3,0) ; + $windSpeed_bft = round(sqrt( 9 + (6 * $windSpeed)) - 3,0) ; + $windstat[5]= " Ws:$windSpeed_bft Wg:$windguest_bft bft"; + Log3 $hash, 4, "SD_WS09_Wind $windstat[5] " ; + + # Resets des rain counters abfangen: + # wenn der aktuelle Wert < letzter Wert ist, dann fand ein reset statt + # die Differenz "letzer Wert - aktueller Wert" wird dann als offset für zukünftige Ausgaben zu rain addiert + # offset wird auch im Reading ".rain_offset" gespeichert + my $last_rain = ReadingsVal($name, "rain", 0); + my $rain_offset = ReadingsVal($name, ".rainOffset", 0); + $rain_offset += $last_rain if($rain < $last_rain); + my $rain_total = $rain + $rain_offset; + Log3 $hash, 4, "SD_WS09_Parse_rain_offset ".$model." rain:$rain raintotal:$rain_total rainoffset:$rain_offset " ; + + # windDirectionAverage berechnen + my $average = SD_WS09_WindDirAverage($hash, $windSpeed, $windDirectionDegree); + + $hash->{lastReceive} = time(); + $def->{lastMSG} = $rawData; + readingsBeginUpdate($hash); + + if($whid ne "0111") + { + #my $uowind = AttrVal($hash->{NAME},'Unit_of_Wind',0) ; + my $uowind = AttrVal($name,'Unit_of_Wind',0) ; + my $windex = $uowind_index{$uowind}; + if (!defined $windex) { + $windex = 0; + } + + $state = "T: $temp ". ($hum>0 ? " H: $hum ":" "). $windstat[$windex]." Wd: $windDirectionText "." R: $rain"; + readingsBulkUpdate($hash, "id", $id) if ($id ne ""); + readingsBulkUpdate($hash, "state", $state); + readingsBulkUpdate($hash, "temperature", $temp) if ($temp ne""); + readingsBulkUpdate($hash, "humidity", $hum) if ($hum ne "" && $hum != 0 ); + readingsBulkUpdate($hash, "battery", $bat) if ($bat ne ""); + #zusätzlich Daten für Wetterstation + readingsBulkUpdate($hash, "rain", $rain ); + readingsBulkUpdate($hash, ".rainOffset", $rain_offset ); # Zwischenspeicher für den offset + readingsBulkUpdate($hash, "rain_total", $rain_total ); # monoton steigender Wert von rain + readingsBulkUpdate($hash, "windGust", $windguest ); + readingsBulkUpdate($hash, "windSpeed", $windSpeed ); + readingsBulkUpdate($hash, "windGust_kmh", $windguest_kmh ); + readingsBulkUpdate($hash, "windSpeed_kmh", $windSpeed_kmh ); + readingsBulkUpdate($hash, "windGust_fts", $windguest_fts ); + readingsBulkUpdate($hash, "windSpeed_fts", $windSpeed_fts ); + readingsBulkUpdate($hash, "windGust_mph", $windguest_mph ); + readingsBulkUpdate($hash, "windSpeed_mph", $windSpeed_mph ); + readingsBulkUpdate($hash, "windGust_kn", $windguest_kn ); + readingsBulkUpdate($hash, "windSpeed_kn", $windSpeed_kn ); + readingsBulkUpdate($hash, "windDirectionAverage", $average ); + readingsBulkUpdate($hash, "windDirection", $windDirection ); + readingsBulkUpdate($hash, "windDirectionDegree", $windDirectionDegree); + readingsBulkUpdate($hash, "windDirectionText", $windDirectionText ); + } + if(($whid eq "0111") && ($model eq "WH1080")) + { + $state = "UV: $FOuvo Lux: $FOlux "; + readingsBulkUpdate($hash, "id", $id) if ($id ne ""); + readingsBulkUpdate($hash, "state", $state); + #zusätzliche Daten UV + Lux + readingsBulkUpdate($hash, "UV", $FOuvo ); + readingsBulkUpdate($hash, "Lux", $FOlux ); + } readingsEndUpdate($hash, 1); # Notify is done by Dispatch return $name; - } sub SD_WS09_Attr(@) { my @a = @_; - # Make possible to use the same code for different logical devices when they # are received through different physical devices. return if($a[0] ne "set" || $a[2] ne "IODev"); @@ -323,7 +458,138 @@ $modules{SD_WS09}{defptr}{$iohash->{NAME} . "." . $cde} = $hash; return undef; } - + + sub SD_WS09_WindDirAverage($$$){ + ############################################################################### + # übernommen von SabineT https://forum.fhem.de/index.php/topic,75225.msg669950.html#msg669950 + # WindDirAverage + # z.B.: myWindDirAverage('WH1080','windSpeed','windDirectionDegree',900,0.75,0.5) + # avtime ist optional, default ist 600 s Zeitspanne, die berücksichtig werden soll + # decay ist optional, default ist 1 Parameter, um ältere Werte geringer zu gewichten + # minspeed ist optional, default ist 0 m/s + # + # Als Ergebnis wird die Windrichtung zurück geliefert, die aus dem aktuellen und + # vergangenen Werten über eine Art exponentiellen Mittelwert berechnet werden. + # Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit berücksichtigt (höhere Geschwindigkeit + # bedeutet höhere Gewichtung). + # + # decay: 1 -> alle Werte werden gleich gewichtet + # 0 -> nur der aktuelle Wert wird verwendet. + # in der Praxis wird man Werte so um 0.75 nehmen + # + # minspeed: da bei sehr geringer Windgeschwindigkeit die Windrichtung üblicherweise nicht + # eindeutig ist, kann mit minspeed ein Schwellwert angegeben werden + # Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zurück geliefert + # + ############################################################################### + + my ($hash, $ws, $wd) = @_; + my $name = $hash->{NAME}; + Log3 $hash, 4, "SD_WS09_WindDirAverage --- OK ----" ; + my $rc = eval + { + require Math::Trig; + Math::Trig->import(); + 1; + }; + + if($rc) # test ob Math::Trig geladen wurde + { + Log3 $hash, 4, "SD_WS09_WindDirAverage Math::Trig:OK" ; + }else + { + Log3 $hash, 1, "SD_WS09_WindDirAverage Math::Trig:fehlt : cpan install Math::Trig" ; + return ""; + } + + my $avtime = AttrVal($name,'WindDirAverageTime',0); + my $decay = AttrVal($name,'WindDirAverageDecay',0); + my $minspeed = AttrVal($name,'WindDirAverageMinSpeed',0); + + # default Werte für die optionalen Parameter, falls nicht beim Aufruf mit angegeben + $avtime = 600 if (!(defined $avtime) || $avtime == 0 ); + $decay = 1 if (!(defined $decay)); + $decay = 1 if ($decay > 1); # darf nicht >1 sein + $decay = 0 if ($decay < 0); # darf nicht <0 sein + $minspeed = 0 if (!(defined $minspeed)); + + $wd = deg2rad($wd); + my $ctime = time; + my $time = FmtDateTime($ctime); + my @new = ($ws,$wd,$time); + + Log3 $hash, 4,"SD_WS09_WindDirAverage_01 $name :Speed=".$ws." DirR=".round($wd,2)." Time=".$time; + Log3 $hash, 4,"SD_WS09_WindDirAverage_02 $name :avtime=".$avtime." decay=".$decay." minspeed=".$minspeed; + + my $num; + my $arr; + + #-- initialize if requested + if( ($avtime eq "-1") ){ + $hash->{helper}{history}=undef; + } + + #-- test for existence + if(!$hash->{helper}{history}){ + Log3 $hash, 4,"SD_WS09_WindDirAverage_03 $name :ARRAY CREATED"; + push(@{$hash->{helper}{history}},\@new); + $num = 1; + $arr=\@{$hash->{helper}{history}}; + } else { + $num = int(@{$hash->{helper}{history}}); + $arr=\@{$hash->{helper}{history}}; + my $stime = time_str2num($arr->[0][2]); # Zeitpunkt des ältesten Eintrags + my $ltime = time_str2num($arr->[$num-1][2]); # Zeitpunkt des letzten Eintrags + Log3 $hash,4,"SD_WS09_WindDirAverage_04 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." ctime=".$ctime." ltime=".$ltime." stime=".$stime." num=".$num; + + if((($ctime - $ltime) > 10) || ($num == 0)) { + if(($num < 25) && (($ctime-$stime) < $avtime)){ + Log3 $hash,4,"SD_WS09_WindDirAverage_05 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." num=".$num; + push(@{$hash->{helper}{history}},\@new); + } else { + shift(@{$hash->{helper}{history}}); + push(@{$hash->{helper}{history}},\@new); + Log3 $hash,4,"SD_WS09_WindDirAverage_06 $name :Speed=".$ws." Dir=".round($wd,2)." Time=".$time." minspeed=".$minspeed." num=".$num; + } + } else { + return undef; + } + } + #-- output and average + my ($anz, $sanz) = 0; + $num = int(@{$hash->{helper}{history}}); + my ($sumSin, $sumCos, $sumSpeed, $age, $maxage, $weight) = 0; + for(my $i=0; $i<$num; $i++){ + ($ws, $wd, $time) = @{ $arr->[$i] }; + $age = $ctime - time_str2num($time); + if (($time eq "") || ($age > $avtime)) { + #-- zu alte Einträge entfernen + Log3 $hash,4,"SD_WS09_WindDirAverage_07 $name i=".$i." Speed=".round($ws,2)." Dir=".round($wd,2)." Time=".substr($time,11)." ctime=".$ctime." akt.=".time_str2num($time); + shift(@{$hash->{helper}{history}}); + $i--; + $num--; + } else { + #-- Werte aufsummieren, Windrichtung gewichtet über Geschwindigkeit und decay/"alter" + $weight = $decay ** ($age / $avtime); + #-- für die Mittelwertsbildung der Geschwindigkeit wird nur ein 10tel von avtime genommen + if ($age < ($avtime / 10)) { + $sumSpeed += $ws * $weight if ($age < ($avtime / 10)); + $sanz++; + } + $sumSin += sin($wd) * $ws * $weight; + $sumCos += cos($wd) * $ws * $weight; + $anz++; + Log3 $hash,4,"SD_WS09_WindDirAverage_08 $name i=".$i." Speed=".round($ws,2)." Dir=".round($wd,2)." Time=".substr($time,11)." vec=".round($sumSin,2)."/".round($sumCos,2)." age=".$age." ".round($weight,2); + } + } + my $average = int((rad2deg(atan2($sumSin, $sumCos)) + 360) % 360); + Log3 $hash,4,"SD_WS09_WindDirAverage_09 $name Mittelwert über $anz Werte ist $average, avspeed=".round($sumSpeed/$num,1) if ($num > 0); + #-- undef zurückliefern, wenn die durchschnittliche Geschwindigkeit zu gering oder gar keine Werte verfügbar + return undef if (($anz == 0) || ($sanz == 0)); + return undef if (($sumSpeed / $sanz) < $minspeed); + Log3 $hash,4,"SD_WS09_WindDirAverage_END $name Mittelwert=$average"; + return $average; +} sub SD_WS09_bin2dec($) { @@ -331,6 +597,7 @@ my $int = unpack("N", pack("B32",substr("0" x 32 . $h, -32))); return sprintf("%d", $int); } + sub SD_WS09_binflip($) { my $h = shift; @@ -341,11 +608,9 @@ for ($i=$hlen-1; $i >= 0; $i--) { $flip = $flip.substr($h,$i,1); } - return $flip; } - sub SD_WS09_BCD2bin($) { my $binary = shift; my $int = unpack("N", pack("B32", substr("0" x 32 . $binary, -32))); @@ -353,14 +618,38 @@ return $BCD; } + sub SD_WS09_SHIFT($){ + my $rawData = shift; + my $hlen = length($rawData); + my $blen = $hlen * 4; + my $bitData = unpack("B$blen", pack("H$hlen", $rawData)); + my $bitData2 = '1'.unpack("B$blen", pack("H$hlen", $rawData)); + my $bitData20 = substr($bitData2,0,length($bitData2)-1); + $blen = length($bitData20); + $hlen = $blen / 4; + $rawData = uc(unpack("H$hlen", pack("B$blen", $bitData20))); + $bitData = $bitData20; + Log3 "SD_WS09_SHIFT", 4, "SD_WS09_SHIFT_0 raw: $rawData length:".length($bitData) ; + Log3 "SD_WS09_SHIFT", 4, "SD_WS09_SHIFT_1 bitdata: $bitData" ; + return $rawData; + } + sub SD_WS09_CRCCHECK($) { + my $rawData = shift; + my $datacheck1 = pack( 'H*', substr($rawData,2,length($rawData)-2) ); + my $crcmein1 = Digest::CRC->new(width => 8, poly => 0x31); + my $rr3 = $crcmein1->add($datacheck1)->hexdigest; + $rr3 = sprintf("%d", hex($rr3)); + Log3 "SD_WS09_CRCCHECK", 4, "SD_WS09_CRCCHECK : raw:$rawData CRC=$rr3 " ; + return $rr3 ; + } 1; =pod -=item summary Supports weather sensors protocl 9 from SIGNALduino -=item summary_DE Unterstützt Wettersensoren mit Protokol 9 vom SIGNALduino +=item summary Supports weather sensors (WH1080/3080/CTW-600) protocol 9 from SIGNALduino +=item summary_DE Unterstuetzt Wettersensoren (WH1080/3080/CTW-600) mit Protokol 9 vom SIGNALduino =begin html @@ -377,6 +666,7 @@
      • WS-0101 --> Model: WH1080
      • TFA 30.3189 / WH1080 --> Model: WH1080
      • 1073 (WS1080) --> Model: WH1080
      • +
      • WH3080 --> Model: WH1080
      • CTW600 --> Model: CTW600 (??)

      @@ -399,8 +689,16 @@
    • Humidity: (The humidity (1-100 if available)
    • Battery: (low or ok)
    • ID: (The ID-Number (number if)
    • -
    • windSpeed (m/s) and windDirection (N-O-S-W)
    • +
    • windSpeed/windGuest (Unit_of_Wind)) and windDirection (N-O-S-W)
    • Rain (mm)
    • +
    • windDirectionAverage
      + As a result, the wind direction is returned, which are calculated from the current and past values + via a kind of exponential mean value. + The respective wind speed is additionally taken into account (higher speed means higher weighting)
    • + WH3080: +
    • UV Index
    • +
    • Lux
    • +

    Attributes @@ -409,19 +707,41 @@
  • ignore
  • showtime
  • readingFnAttributes
  • -
  • Model
    - WH1080, CTW600 +
  • Model: WH1080,CTW600 +
  • +
  • windKorrektur: -3,-2,-1,0,1,2,3 +
  • +
  • Unit_of_Wind
    + Unit of windSpeed and windGuest. State-Format: Value + Unit. +
    m/s,km/h,ft/s,mph,bft,knot

  • -
  • windKorrektur
    - -3,-2,-1,0,1,2,3 + +
  • WindDirAverageTime
    + default is 600s, time span to be considered for the calculation

  • - - + +
  • WindDirAverageMinSpeed
    + since the wind direction is usually not clear at very low wind speeds, + minspeed can be used to specify a threshold value. +
    The (weighted) mean velocity < minspeed is returned undef +

  • + +
  • WindDirAverageDecay
    + 1 -> all values ​​are weighted equally
    + 0 -> only the current value is used.
    + in practice, you will take values ​​around 0.75 +

  • + +
  • WS09_CRCAUS (set in Signalduino-Modul 00_SIGNALduino.pm) +
    0: CRC-Check WH1080 CRC-Summe = 0 on, default +
    2: CRC-Summe = 49 (x031) WH1080, set OK +
  • +
    Set
      N/A

    - Set
      N/A

    + Parse
      N/A

    @@ -444,7 +764,8 @@
  • WS-0101 --> Model: WH1080
  • TFA 30.3189 / WH1080 --> Model: WH1080
  • 1073 (WS1080) --> Model: WH1080
  • -
  • CTW600 --> Model: CTW600 (nicht getestet)
  • +
  • WH3080 --> Model: WH1080
  • +
  • CTW600 --> Model: CTW600

  • Neu empfangene Sensoren werden in FHEM per autocreate angelegt. @@ -460,13 +781,22 @@ Generierte Readings:
      -
    • State (T: H: Ws: Wg: Wd: R: ) temperature, humidity, windSpeed, windGuest, windDirection, Rain
    • +
    • State (T: H: Ws: Wg: Wd: R: ) temperature, humidity, windSpeed, windGuest, Einheit, windDirection, Rain
    • Temperature (°C)
    • Humidity: (The humidity (1-100 if available)
    • Battery: (low or ok)
    • ID: (The ID-Number (number if)
    • -
    • windSpeed (m/s) and windDirection (N-O-S-W)
    • +
    • windSpeed/windgust (Einheit siehe Unit_of_Wind) and windDirection (N-O-S-W)
    • Rain (mm)
    • +
    • windDirectionAverage + Als Ergebnis wird die Windrichtung zurück geliefert, die aus dem aktuellen und + vergangenen Werten über eine Art exponentiellen Mittelwert berechnet werden. + Dabei wird zusätzlich die jeweilige Windgeschwindigkeit mit berücksichtigt (höhere Geschwindigkeit + bedeutet höhere Gewichtung).
    • + WH3080: +
    • UV Index
    • +
    • Lux
    • +

    Attribute @@ -482,15 +812,43 @@ Korrigiert die Nord-Ausrichtung des Windrichtungsmessers, wenn dieser nicht richtig nach Norden ausgerichtet ist. -3,-2,-1,0,1,2,3
    +
  • Unit_of_Wind
    + Hiermit wird der Einheit eingestellt und im State die entsprechenden Werte + Einheit angezeigt. +
    m/s,km/h,ft/s,mph,bft,knot +

  • + +
  • WindDirAverageTime
    + default ist 600s, Zeitspanne die für die Berechung berücksichtig werden soll +

  • + +
  • WindDirAverageMinSpeed
    + da bei sehr geringer Windgeschwindigkeit die Windrichtung üblicherweise nicht + eindeutig ist, kann mit minspeed ein Schwellwert angegeben werden + Ist die (gewichtetete) mittlere Geschwindigkeit < minspeed wird undef zurück geliefert +

  • + +
  • WindDirAverageDecay
    + 1 -> alle Werte werden gleich gewichtet
    + 0 -> nur der aktuelle Wert wird verwendet.
    + in der Praxis wird man Werte so um 0.75 nehmen +

  • + +
  • WS09_CRCAUS
    + Wird im Signalduino-Modul (00_SIGNALduino.pm) gesetzt +
    0: CRC-Prüfung bei WH1080 CRC-Summe = 0 +
    2: CRC-Summe = 49 (x031) bei WH1080 wird als OK verarbeitet +

  • + Set
      N/A

    - Set
      N/A

    + Parse
      N/A

    =end html_DE =cut +