diff --git a/fhem/FHEM/00_HMLAN.pm b/fhem/FHEM/00_HMLAN.pm index 1f77137c2..e6a7e3828 100755 --- a/fhem/FHEM/00_HMLAN.pm +++ b/fhem/FHEM/00_HMLAN.pm @@ -33,16 +33,16 @@ my %sets = ( "hmPairForSec" => "HomeMatic" ); my %HMcond = ( 0 =>'ok' ,2 =>'Warning-HighLoad' - ,4 =>'ERROR-Overload' - ,252=>'timeout' - ,253=>'disconnected' - ,254=>'Overload-released' - ,255=>'init'); - + ,4 =>'ERROR-Overload' + ,252=>'timeout' + ,253=>'disconnected' + ,254=>'Overload-released' + ,255=>'init'); + #my %HM STATE= ( =>'opened' # =>'disconnected' -# =>'overload'); - +# =>'overload'); + my $HMOvLdRcvr = 6*60;# time HMLAN needs to recover from overload my $HMmlSlice = 6; # number of messageload slices per hour (10 = 6min) @@ -68,15 +68,15 @@ sub HMLAN_Initialize($) { $hash->{DefFn} = "HMLAN_Define"; $hash->{UndefFn} = "HMLAN_Undef"; $hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 " . - "addvaltrigger " . + "addvaltrigger " . "hmId hmKey hmKey2 hmKey3 " . "respTime " . - "hmProtocolEvents:0_off,1_dump,2_dumpFull,3_dumpTrigger ". - "hmMsgLowLimit ". - "hmLanQlen:1_min,2_low,3_normal,4_high,5_critical ". - "wdTimer:5,10,15,20,25 ". - "logIDs ". - $readingFnAttributes; + "hmProtocolEvents:0_off,1_dump,2_dumpFull,3_dumpTrigger ". + "hmMsgLowLimit ". + "hmLanQlen:1_min,2_low,3_normal,4_high,5_critical ". + "wdTimer:5,10,15,20,25 ". + "logIDs ". + $readingFnAttributes; } sub HMLAN_Define($$) {######################################################### my ($hash, $def) = @_; @@ -101,20 +101,20 @@ sub HMLAN_Define($$) {######################################################### $attr{$name}{hmLanQlen} = "1_min"; #max message queue length in HMLan no warnings 'numeric'; - $hash->{helper}{q}{hmLanQlen} = int($attr{$name}{hmLanQlen})+0; + $hash->{helper}{q}{hmLanQlen} = int($attr{$name}{hmLanQlen})+0; use warnings 'numeric'; $hash->{DeviceName} = $dev; $hash->{msgKeepAlive} = ""; # delay of trigger Alive messages $hash->{helper}{k}{DlyMax} = 0; $hash->{helper}{k}{BufMin} = 30; - + $hash->{helper}{q}{answerPend} = 0;#pending answers from LANIf my @arr = (); @{$hash->{helper}{q}{apIDs}} = \@arr; - + $hash->{helper}{q}{cap}{$_} = 0 for (0..($HMmlSlice-1)); $hash->{helper}{q}{cap}{last} = 0; - $hash->{helper}{q}{cap}{sum} = 0; + $hash->{helper}{q}{cap}{sum} = 0; HMLAN_UpdtMsgCnt("UpdtMsg:".$name); $defs{$name}{helper}{log}{all} = 0;# selective log support $defs{$name}{helper}{log}{sys} = 0; @@ -140,7 +140,7 @@ sub HMLAN_Undef($$) {########################################################## delete $defs{$d}{IODev}; } } - DevIo_CloseDev($hash); + DevIo_CloseDev($hash); return undef; } sub HMLAN_RemoveHMPair($) {#################################################### @@ -152,11 +152,11 @@ sub HMLAN_RemoveHMPair($) {#################################################### sub HMLAN_Notify(@) {########################################################## my ($hash,$dev) = @_; if ($dev->{NAME} eq "global" && grep (m/^INITIALIZED$/,@{$dev->{CHANGED}})){ - if ($hash->{helper}{attrPend}){ - my $aVal = AttrVal($hash->{NAME},"logIDs",""); - HMLAN_Attr("set",$hash->{NAME},"logIDs",$aVal) if($aVal); - delete $hash->{helper}{attrPend}; - } + if ($hash->{helper}{attrPend}){ + my $aVal = AttrVal($hash->{NAME},"logIDs",""); + HMLAN_Attr("set",$hash->{NAME},"logIDs",$aVal) if($aVal); + delete $hash->{helper}{attrPend}; + } } elsif ($dev->{NAME} eq $hash->{NAME}){ foreach (grep (m/CONNECTED$/,@{$dev->{CHANGED}})) { # connect/disconnect @@ -171,104 +171,104 @@ sub HMLAN_Attr(@) {############################################################ if ($aName eq "wdTimer" && $cmd eq "set"){#allow between 5 and 25 second return "select wdTimer between 5 and 25 seconds" if ($aVal>30 || $aVal<5); $attr{$name}{wdTimer} = $aVal; - $defs{$name}{helper}{k}{Start} = 0; + $defs{$name}{helper}{k}{Start} = 0; } elsif($aName eq "hmLanQlen"){ - if ($cmd eq "set"){ + if ($cmd eq "set"){ no warnings 'numeric'; - $defs{$name}{helper}{q}{hmLanQlen} = int($aVal)+0; + $defs{$name}{helper}{q}{hmLanQlen} = int($aVal)+0; use warnings 'numeric'; - } - else{ - $defs{$name}{helper}{q}{hmLanQlen} = 1; - } + } + else{ + $defs{$name}{helper}{q}{hmLanQlen} = 1; + } } elsif($aName =~ m /^hmKey/){ my $retVal= ""; if ($cmd eq "set"){ - my $kno = ($aName eq "hmKey")?1:substr($aName,5,1); - my ($no,$val) = (sprintf("%02X",$kno),$aVal); - if ($aVal =~ m/:/){#number given - ($no,$val) = split ":",$aVal; - return "illegal number:$no" if (hex($no) < 1 || hex($no) > 255 || length($no) != 2); - } - $attr{$name}{$aName} = "$no:". - (($val =~ m /^[0-9A-Fa-f]{32}$/ )? - $val: - unpack('H*', md5($val))); - $retVal = "$aName set to $attr{$name}{$aName}"; - } - else{ - delete $attr{$name}{$aName}; - } - my ($k1no,$k1,$k2no,$k2,$k3no,$k3) - =( "01",AttrVal($name,"hmKey","") - ,"02",AttrVal($name,"hmKey2","") - ,"03",AttrVal($name,"hmKey3","") - ); - - if ($k1 =~ m/:/){($k1no,$k1) = split(":",$k1);} - if ($k2 =~ m/:/){($k2no,$k2) = split(":",$k2);} - if ($k3 =~ m/:/){($k3no,$k3) = split(":",$k3);} - - HMLAN_SimpleWrite($defs{$name}, "Y01,".($k1?"$k1no,$k1":"00,")); - HMLAN_SimpleWrite($defs{$name}, "Y02,".($k2?"$k2no,$k2":"00,")); - HMLAN_SimpleWrite($defs{$name}, "Y03,".($k3?"$k3no,$k3":"00,")); - return $retVal; + my $kno = ($aName eq "hmKey")?1:substr($aName,5,1); + my ($no,$val) = (sprintf("%02X",$kno),$aVal); + if ($aVal =~ m/:/){#number given + ($no,$val) = split ":",$aVal; + return "illegal number:$no" if (hex($no) < 1 || hex($no) > 255 || length($no) != 2); + } + $attr{$name}{$aName} = "$no:". + (($val =~ m /^[0-9A-Fa-f]{32}$/ )? + $val: + unpack('H*', md5($val))); + $retVal = "$aName set to $attr{$name}{$aName}"; + } + else{ + delete $attr{$name}{$aName}; + } + my ($k1no,$k1,$k2no,$k2,$k3no,$k3) + =( "01",AttrVal($name,"hmKey","") + ,"02",AttrVal($name,"hmKey2","") + ,"03",AttrVal($name,"hmKey3","") + ); + + if ($k1 =~ m/:/){($k1no,$k1) = split(":",$k1);} + if ($k2 =~ m/:/){($k2no,$k2) = split(":",$k2);} + if ($k3 =~ m/:/){($k3no,$k3) = split(":",$k3);} + + HMLAN_SimpleWrite($defs{$name}, "Y01,".($k1?"$k1no,$k1":"00,")); + HMLAN_SimpleWrite($defs{$name}, "Y02,".($k2?"$k2no,$k2":"00,")); + HMLAN_SimpleWrite($defs{$name}, "Y03,".($k3?"$k3no,$k3":"00,")); + return $retVal; } elsif($aName eq "hmMsgLowLimit"){ if ($cmd eq "set"){ - return "hmMsgLowLimit:please add integer between 10 and 120" - if ( $aVal !~ m/^(\d+)$/ - ||$aVal<10 - ||$aVal >120 ); - } + return "hmMsgLowLimit:please add integer between 10 and 120" + if ( $aVal !~ m/^(\d+)$/ + ||$aVal<10 + ||$aVal >120 ); + } } elsif($aName eq "hmId"){ if ($cmd eq "set"){ - return "wrong syntax: hmId must be 6-digit-hex-code (3 byte)" - if ($aVal !~ m/^[A-F0-9]{6}$/i); - } + return "wrong syntax: hmId must be 6-digit-hex-code (3 byte)" + if ($aVal !~ m/^[A-F0-9]{6}$/i); + } } elsif($aName eq "logIDs"){ if ($cmd eq "set"){ - if ($init_done){ - my @ids = split",",$aVal; - my @idName; - if (grep /sys/,@ids){ - push @idName,"sys"; - $defs{$name}{helper}{log}{sys}=1; - } - else{ - $defs{$name}{helper}{log}{sys}=0; - } - if (grep /all/,@ids){ - push @idName,"all"; - $defs{$name}{helper}{log}{all}=1; - } - else{ - $defs{$name}{helper}{log}{all}=0; - $_=substr(CUL_HM_name2Id($_),0,6) foreach(grep !/^$/,@ids); - $_="" foreach(grep !/^[A-F0-9]{6}$/,@ids); - @ids = HMLAN_noDup(@ids); - push @idName,CUL_HM_id2Name($_) foreach(@ids); - } - $attr{$name}{$aName} = join(",",@idName); - @{$defs{$name}{helper}{log}{ids}}=@ids; - } - else{ - $defs{$name}{helper}{attrPend} = 1; - return; - } - } - else{ - my @ids = (); - $defs{$name}{helper}{log}{sys}=0; - $defs{$name}{helper}{log}{all}=0; - @{$defs{$name}{helper}{log}{ids}}=@ids; + if ($init_done){ + my @ids = split",",$aVal; + my @idName; + if (grep /sys/,@ids){ + push @idName,"sys"; + $defs{$name}{helper}{log}{sys}=1; + } + else{ + $defs{$name}{helper}{log}{sys}=0; + } + if (grep /all/,@ids){ + push @idName,"all"; + $defs{$name}{helper}{log}{all}=1; + } + else{ + $defs{$name}{helper}{log}{all}=0; + $_=substr(CUL_HM_name2Id($_),0,6) foreach(grep !/^$/,@ids); + $_="" foreach(grep !/^[A-F0-9]{6}$/,@ids); + @ids = HMLAN_noDup(@ids); + push @idName,CUL_HM_id2Name($_) foreach(@ids); + } + $attr{$name}{$aName} = join(",",@idName); + @{$defs{$name}{helper}{log}{ids}}=@ids; + } + else{ + $defs{$name}{helper}{attrPend} = 1; + return; + } } - return "logging set to $attr{$name}{$aName}" - if ($attr{$name}{$aName} ne $aVal); + else{ + my @ids = (); + $defs{$name}{helper}{log}{sys}=0; + $defs{$name}{helper}{log}{all}=0; + @{$defs{$name}{helper}{log}{ids}}=@ids; + } + return "logging set to $attr{$name}{$aName}" + if ($attr{$name}{$aName} ne $aVal); } return; } @@ -277,7 +277,7 @@ sub HMLAN_UpdtMsgCnt($) {###################################################### # update HMLAN capacity counter # HMLAN will raise high-load after ~610 msgs per hour # overload with send-stop after 670 msgs - # this count is an approximation and best guess - nevertheless it cannot + # this count is an approximation and best guess - nevertheless it cannot # read the real values of HMLAN that might persist e.g. after FHEM reboot my($in ) = shift; my(undef,$name) = split(':',$in); @@ -290,28 +290,28 @@ sub HMLAN_UpdtMsgLoad($$) {#################################################### my($name,$incr) = @_; my $hash = $defs{$name}; my $hCap = $hash->{helper}{q}{cap}; - + my $t = int(gettimeofday()/(3600/$HMmlSlice))%$HMmlSlice; if ($hCap->{last} != $t){ $hCap->{last} = $t; $hCap->{$t} = 0; - # try to release high-load condition with a dummy message - # one a while + # try to release high-load condition with a dummy message + # one a while if (ReadingsVal($name,"cond","") =~ m /(Warning-HighLoad|ERROR-Overload)/){ - $hash->{helper}{recoverTest} = 1; + $hash->{helper}{recoverTest} = 1; HMLAN_Write($hash,"","As09998112" - .AttrVal($name,"hmId","999999") - ."000001"); - } + .AttrVal($name,"hmId","999999") + ."000001"); + } } $hCap->{$hCap->{last}}+=$incr if ($incr); my @tl; $hCap->{sum} = 0; for (($t-$HMmlSlice+1)..$t){# we have 6 slices push @tl,int($hCap->{$_%$HMmlSlice}/16.8); - $hCap->{sum} += $hCap->{$_%$HMmlSlice}; # need to recalc incase a slice was removed + $hCap->{sum} += $hCap->{$_%$HMmlSlice}; # need to recalc incase a slice was removed } - + $hash->{msgLoadEst} = "1hour:".int($hCap->{sum}/16.8)."% " .(60/$HMmlSlice)."min steps: ".join("/",reverse @tl); #testing only ." :".$hCap->{sum} @@ -323,7 +323,7 @@ sub HMLAN_Set($@) {############################################################ return "\"set HMLAN\" needs at least one parameter" if(@a < 2); return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets) - if(!defined($sets{$a[1]})); + if(!defined($sets{$a[1]})); my $name = shift @a; my $type = shift @a; @@ -333,7 +333,7 @@ sub HMLAN_Set($@) {############################################################ if(!$arg || $arg !~ m/^\d+$/); $hash->{hmPair} = 1; InternalTimer(gettimeofday()+$arg, "HMLAN_RemoveHMPair", "hmPairForSec:".$name, 1); - } + } elsif($type eq "hmPairSerial") { ################################ return "Usage: set $name hmPairSerial <10-character-serialnumber>" if(!$arg || $arg !~ m/^.{10}$/); @@ -368,7 +368,7 @@ sub HMLAN_ReadAnswer($$$) {# This is a direct read for commands like get next if ($! == EAGAIN() || $! == EINTR() || $! == 0); my $err = $!; DevIo_Disconnected($hash); - HMLAN_condUpdate($hash,253); + HMLAN_condUpdate($hash,253); return("HMLAN_ReadAnswer $arg: $err", undef); } return ("Timeout reading answer for get $arg", undef) if($nfound == 0); @@ -382,8 +382,8 @@ sub HMLAN_ReadAnswer($$$) {# This is a direct read for commands like get if($mdata =~ m/\r\n/) { if($regexp && $mdata !~ m/$regexp/) { HMLAN_Parse($hash, $mdata); - } - else { + } + else { return (undef, $mdata); } } @@ -397,35 +397,35 @@ sub HMLAN_Write($$$) {######################################################### if (length($msg)>22){ my ($mtype,$src,$dst) = (substr($msg, 8, 2), substr($msg, 10, 6), - substr($msg, 16, 6)); + substr($msg, 16, 6)); - if ( $mtype eq "02" && $src eq $hash->{owner} && length($msg) == 24 - && $hash->{assignIDs} =~ m/$dst/){ + if ( $mtype eq "02" && $src eq $hash->{owner} && length($msg) == 24 + && $hash->{assignIDs} =~ m/$dst/){ # Acks are generally send by HMLAN autonomously - # Special + # Special Log3 $hash, 5, "HMLAN: Skip ACK" if (!$debug); - return; + return; } # my $IDHM = '+'.$dst.',01,00,F1EF'; #used by HMconfig - meanning?? -# my $IDadd = '+'.$dst; # guess: add ID? +# my $IDadd = '+'.$dst; # guess: add ID? # my $IDack = '+'.$dst.',02,00,'; # guess: ID acknowledge # my $IDack = '+'.$dst.',FF,00,'; # guess: ID acknowledge # my $IDsub = '-'.$dst; # guess: ID remove? # my $IDnew = '+'.$dst.',00,01,'; # newChannel- trailing 01 to be sent if talk to neu channel - my $IDadd = '+'.$dst.',00,00,'; # guess: add ID? - + my $IDadd = '+'.$dst.',00,00,'; # guess: add ID? + if (!$lhash{$dst} && $dst ne "000000"){ HMLAN_SimpleWrite($hash, $IDadd); -# delete $hash->{helper}{$dst}; - my $dN = CUL_HM_id2Name($dst); - if (!($dN eq $dst) && # name not found - !(CUL_HM_Get(CUL_HM_id2Hash($dst),$dN,"param","rxType") & ~0x04)){#config only - $hash->{helper}{$dst}{newChn} = '+'.$dst.",01,01,FE1F"; +# delete $hash->{helper}{$dst}; + my $dN = CUL_HM_id2Name($dst); + if (!($dN eq $dst) && # name not found + !(CUL_HM_Get(CUL_HM_id2Hash($dst),$dN,"param","rxType") & ~0x04)){#config only + $hash->{helper}{$dst}{newChn} = '+'.$dst.",01,01,FE1F"; } - else{ - $hash->{helper}{$dst}{newChn} = '+'.$dst.',00,01,'; - } - $hash->{helper}{$dst}{name} = CUL_HM_id2Name($dst); + else{ + $hash->{helper}{$dst}{newChn} = '+'.$dst.',00,01,'; + } + $hash->{helper}{$dst}{name} = CUL_HM_id2Name($dst); $lhash{$dst} = 1; $hash->{assignIDs}=join(',',keys %lhash); $hash->{assignIDsCnt}=scalar(keys %lhash); @@ -441,7 +441,7 @@ sub HMLAN_Read($) {############################################################ my $buf = DevIo_SimpleRead($hash); return "" if(!defined($buf)); my $name = $hash->{NAME}; - + my $hmdata = $hash->{PARTIAL}; Log3 $hash, 5, "HMLAN/RAW: $hmdata/$buf" if (!$debug); $hmdata .= $buf; @@ -460,26 +460,26 @@ sub HMLAN_uptime($@) {######################################################### $hmtC = hex($hmtC); if ($hash && $hash->{helper}{ref}){ #will calculate new ref-time - my $ref = $hash->{helper}{ref};#shortcut + my $ref = $hash->{helper}{ref};#shortcut my $sysC = int(time()*1000); #current systime in ms - my $offC = $sysC - $hmtC; #offset calc between time and HM-stamp - if ($ref->{hmtL} && ($hmtC > $ref->{hmtL})){ - if (($sysC - $ref->{kTs})<20){ #if delay is more then 20ms, we dont trust - if ($ref->{sysL}){ - $ref->{drft} = ($offC - $ref->{offL})/($sysC - $ref->{sysL}); - } + my $offC = $sysC - $hmtC; #offset calc between time and HM-stamp + if ($ref->{hmtL} && ($hmtC > $ref->{hmtL})){ + if (($sysC - $ref->{kTs})<20){ #if delay is more then 20ms, we dont trust + if ($ref->{sysL}){ + $ref->{drft} = ($offC - $ref->{offL})/($sysC - $ref->{sysL}); + } $ref->{sysL} = $sysC; $ref->{offL} = $offC; - } - } - else{# hm had a skip in time, start over calculation - delete $hash->{helper}{ref}; - } - $hash->{helper}{ref}{hmtL} = $hmtC; - $hash->{helper}{ref}{kTs} = 0; + } + } + else{# hm had a skip in time, start over calculation + delete $hash->{helper}{ref}; + } + $hash->{helper}{ref}{hmtL} = $hmtC; + $hash->{helper}{ref}{kTs} = 0; } - my $sec = int($hmtC/1000); + my $sec = int($hmtC/1000); return sprintf("%03d %02d:%02d:%02d.%03d", int($hmtC/86400000), int($sec/3600), int(($sec%3600)/60), $sec%60, $hmtC % 1000); @@ -489,141 +489,141 @@ sub HMLAN_Parse($$) {########################################################## my $name = $hash->{NAME}; my @mFld = split(',', $rmsg); my $letter = substr($mFld[0],0,1); # get leading char - + if ($letter =~ m/^[ER]/){#@mFld=($src, $status, $msec, $d2, $rssi, $msg) # max speed for devices is 100ms after receive - example:TC - my ($mNo,$flg,$type,$src,$dst,$p) = unpack('A2A2A2A6A6A*',$mFld[5]); + my ($mNo,$flg,$type,$src,$dst,$p) = unpack('A2A2A2A6A6A*',$mFld[5]); my $CULinfo = ""; - - my @logIds = ("150B94","172A85"); + + my @logIds = ("150B94","172A85"); Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") - , "HMLAN_Parse: $name R:".$mFld[0] - .(($mFld[0] =~ m/^E/)?' ':'') - .' stat:' .$mFld[1] - .' t:' .$mFld[2] - .' d:' .$mFld[3] - .' r:' .$mFld[4] + , "HMLAN_Parse: $name R:".$mFld[0] + .(($mFld[0] =~ m/^E/)?' ':'') + .' stat:' .$mFld[1] + .' t:' .$mFld[2] + .' d:' .$mFld[3] + .' r:' .$mFld[4] .' m:'.$mNo .' '.$flg.$type .' '.$src .' '.$dst .' '.$p; - - # handle status. - #HMcnd stat - # 00 00= msg without relation - # 00 01= ack that HMLAN waited for - # 00 02= msg send, no ack requested - # 00 08= nack - ack was requested, msg repeated 3 times, still no ack - # 00 21= ??(seen with 'R') - see below - # 00 2x= should: AES was accepted, here is the response - # 00 30= should: AES response failed - # 00 40= ??(seen with 'E') after 0100 - # 00 41= ??(seen with 'R') - # 00 50= ??(seen with 'R') - # 00 81= ?? - # 01 xx= ?? 0100 AES response send (gen autoMsgSent) - # 02 xx= prestate to 04xx. Message is still sent. This is a warning - # 04 xx= nothing sent anymore. Any restart unsuccessful except power - # - # parameter 'cond'- condition of the IO device - # Cond text - # 0 ok - # 2 Warning-HighLoad - # 4 Overload condition - no send anymore - # + + # handle status. + #HMcnd stat + # 00 00= msg without relation + # 00 01= ack that HMLAN waited for + # 00 02= msg send, no ack requested + # 00 08= nack - ack was requested, msg repeated 3 times, still no ack + # 00 21= ??(seen with 'R') - see below + # 00 2x= should: AES was accepted, here is the response + # 00 30= should: AES response failed + # 00 40= ??(seen with 'E') after 0100 + # 00 41= ??(seen with 'R') + # 00 50= ??(seen with 'R') + # 00 81= ?? + # 01 xx= ?? 0100 AES response send (gen autoMsgSent) + # 02 xx= prestate to 04xx. Message is still sent. This is a warning + # 04 xx= nothing sent anymore. Any restart unsuccessful except power + # + # parameter 'cond'- condition of the IO device + # Cond text + # 0 ok + # 2 Warning-HighLoad + # 4 Overload condition - no send anymore + # my $stat = hex($mFld[1]); my $HMcnd =$stat >>8; #high = HMLAN cond - $stat &= 0xff; # low byte related to message format + $stat &= 0xff; # low byte related to message format - if ($HMcnd == 0x01){#HMLAN responded to AES request - $CULinfo = "AESKey-".$mFld[3]; - } - - if ($stat){# message with status information - HMLAN_condUpdate($hash,$HMcnd)if ($hash->{helper}{q}{HMcndN} != $HMcnd); - - if ($stat & 0x03 && $dst eq $attr{$name}{hmId}){HMLAN_qResp($hash,$src,0);} - elsif ($stat & 0x08 && $src eq $attr{$name}{hmId}){HMLAN_qResp($hash,$dst,0);} - - HMLAN_UpdtMsgLoad($name,((hex($flg)&0x10)?34 #burst=17units *2 - :2)) #ACK=1 unit *2 - if (($stat & 0x48) == 8);# reject - but not from repeater - - - $hash->{helper}{$dst}{flg} = 0;#got response => unblock sending - if ($stat & 0x0A){#08 and 02 dont need to go to CUL, internal ack only - Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") - , "HMLAN_Parse: $name no ACK from $dst" if($stat & 0x08); - return; - }elsif (($stat & 0x70) == 0x30){Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") - , "HMLAN_Parse: $name AES code rejected for $dst $stat"; - $CULinfo = "AESerrReject"; - HMLAN_qResp($hash,$src,0); - }elsif (($stat & 0x70) == 0x20){$CULinfo = "AESok"; - }elsif (($stat & 0x70) == 0x40){;#$CULinfo = "???"; - } + if ($HMcnd == 0x01){#HMLAN responded to AES request + $CULinfo = "AESKey-".$mFld[3]; + } + + if ($stat){# message with status information + HMLAN_condUpdate($hash,$HMcnd)if ($hash->{helper}{q}{HMcndN} != $HMcnd); + + if ($stat & 0x03 && $dst eq $attr{$name}{hmId}){HMLAN_qResp($hash,$src,0);} + elsif ($stat & 0x08 && $src eq $attr{$name}{hmId}){HMLAN_qResp($hash,$dst,0);} + + HMLAN_UpdtMsgLoad($name,((hex($flg)&0x10)?34 #burst=17units *2 + :2)) #ACK=1 unit *2 + if (($stat & 0x48) == 8);# reject - but not from repeater + + + $hash->{helper}{$dst}{flg} = 0;#got response => unblock sending + if ($stat & 0x0A){#08 and 02 dont need to go to CUL, internal ack only + Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") + , "HMLAN_Parse: $name no ACK from $dst" if($stat & 0x08); + return; + }elsif (($stat & 0x70) == 0x30){Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") + , "HMLAN_Parse: $name AES code rejected for $dst $stat"; + $CULinfo = "AESerrReject"; + HMLAN_qResp($hash,$src,0); + }elsif (($stat & 0x70) == 0x20){$CULinfo = "AESok"; + }elsif (($stat & 0x70) == 0x40){;#$CULinfo = "???"; + } + } + else{ + HMLAN_UpdtMsgLoad($name,1) + if ( $letter eq "E" + && (hex($flg)&0x60) == 0x20 # ack but not from repeater + && $dst eq $attr{$name}{hmId}); } - else{ - HMLAN_UpdtMsgLoad($name,1) - if ( $letter eq "E" - && (hex($flg)&0x60) == 0x20 # ack but not from repeater - && $dst eq $attr{$name}{hmId}); - } my $rssi = hex($mFld[4])-65536; #update some User information ------ - $hash->{uptime} = HMLAN_uptime($mFld[2]); - $hash->{RSSI} = $rssi; + $hash->{uptime} = HMLAN_uptime($mFld[2]); + $hash->{RSSI} = $rssi; $hash->{RAWMSG} = $rmsg; $hash->{"${name}_MSGCNT"}++; $hash->{"${name}_TIME"} = TimeNow(); - my $dly = 0; #--------- calc messageDelay ---------- + my $dly = 0; #--------- calc messageDelay ---------- if ($hash->{helper}{ref} && $hash->{helper}{ref}{drft}){ my $ref = $hash->{helper}{ref};#shortcut my $sysC = int(time()*1000); #current systime in ms $dly = int($sysC - (hex($mFld[2]) + $ref->{offL} + $ref->{drft}*($sysC - $ref->{sysL}))); - $hash->{helper}{dly}{lst} = $dly; - my $dlyP = $hash->{helper}{dly}; - $dlyP->{min} = $dly if (!$dlyP->{min} || $dlyP->{min}>$dly); - $dlyP->{max} = $dly if (!$dlyP->{max} || $dlyP->{max}<$dly); - if ($dlyP->{cnt}) {$dlyP->{cnt}++} else {$dlyP->{cnt} = 1} ; - - $hash->{msgParseDly} = "min:" .$dlyP->{min} - ." max:" .$dlyP->{max} - ." last:".$dlyP->{lst} - ." cnt:" .$dlyP->{cnt}; - $dly = 0 if ($dly<0); + $hash->{helper}{dly}{lst} = $dly; + my $dlyP = $hash->{helper}{dly}; + $dlyP->{min} = $dly if (!$dlyP->{min} || $dlyP->{min}>$dly); + $dlyP->{max} = $dly if (!$dlyP->{max} || $dlyP->{max}<$dly); + if ($dlyP->{cnt}) {$dlyP->{cnt}++} else {$dlyP->{cnt} = 1} ; + + $hash->{msgParseDly} = "min:" .$dlyP->{min} + ." max:" .$dlyP->{max} + ." last:".$dlyP->{lst} + ." cnt:" .$dlyP->{cnt}; + $dly = 0 if ($dly<0); + } + + # HMLAN sends ACK for flag 'A0' but not for 'A4'(config mode)- + # we ack ourself an long as logic is uncertain - also possible is 'A6' for RHS + if (hex($flg)&0x2){#not sure: 4 oder 2 ? 02 works for VD! + my $wait = 0.100 - $dly/1000; + $hash->{helper}{$src}{nextSend} = gettimeofday() + $wait if ($wait > 0); + } + if (hex($flg)&0xA4 == 0xA4 && $hash->{owner} eq $dst){ + Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") + , "HMLAN_Parse: $name ACK config"; + HMLAN_Write($hash,undef, "As15".$mNo."8002".$dst.$src."00"); } - # HMLAN sends ACK for flag 'A0' but not for 'A4'(config mode)- - # we ack ourself an long as logic is uncertain - also possible is 'A6' for RHS - if (hex($flg)&0x2){#not sure: 4 oder 2 ? 02 works for VD! - my $wait = 0.100 - $dly/1000; - $hash->{helper}{$src}{nextSend} = gettimeofday() + $wait if ($wait > 0); - } - if (hex($flg)&0xA4 == 0xA4 && $hash->{owner} eq $dst){ - Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") - , "HMLAN_Parse: $name ACK config"; - HMLAN_Write($hash,undef, "As15".$mNo."8002".$dst.$src."00"); - } - if ($letter eq 'R' && $hash->{helper}{$src}{flg}){ - $hash->{helper}{$src}{flg} = 0; #release send-holdoff - if ($hash->{helper}{$src}{msg}){ #send delayed msg if any - Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") - ,"HMLAN_SdDly: $name $src"; - HMLAN_SimpleWrite($hash, $hash->{helper}{$src}{msg}); - } - $hash->{helper}{$src}{msg} = ""; #clear message - } - # prepare dispatch----------- + $hash->{helper}{$src}{flg} = 0; #release send-holdoff + if ($hash->{helper}{$src}{msg}){ #send delayed msg if any + Log3 $hash, HMLAN_getVerbLvl ($hash,$src,$dst,"5") + ,"HMLAN_SdDly: $name $src"; + HMLAN_SimpleWrite($hash, $hash->{helper}{$src}{msg}); + } + $hash->{helper}{$src}{msg} = ""; #clear message + } + # prepare dispatch----------- # HM format A::: Info is not used anymore - my $dmsg = sprintf("A%02X%s:$CULinfo:$rssi:$name", - length($mFld[5])/2, uc($mFld[5])); + my $dmsg = sprintf("A%02X%s:$CULinfo:$rssi:$name", + length($mFld[5])/2, uc($mFld[5])); my %addvals = (RAWMSG => $rmsg, RSSI => hex($mFld[4])-65536); Dispatch($hash, $dmsg, \%addvals); } @@ -632,16 +632,16 @@ sub HMLAN_Parse($$) {########################################################## $hash->{firmware} = sprintf("%d.%d", (hex($mFld[1])>>12)&0xf, hex($mFld[1]) & 0xffff); $hash->{owner} = $mFld[4]; $hash->{uptime} = HMLAN_uptime($mFld[5],$hash); - $hash->{assignIDsReport}=hex($mFld[6]); + $hash->{assignIDsReport}=hex($mFld[6]); $hash->{helper}{q}{keepAliveRec} = 1; $hash->{helper}{q}{keepAliveRpt} = 0; Log3 $hash, ($hash->{helper}{log}{sys}?0:5) - , 'HMLAN_Parse: '.$name. ' V:'.$mFld[1] - .' sNo:'.$mFld[2].' d:'.$mFld[3] - .' O:' .$mFld[4].' t:'.$mFld[5].' IDcnt:'.$mFld[6]; + , 'HMLAN_Parse: '.$name. ' V:'.$mFld[1] + .' sNo:'.$mFld[2].' d:'.$mFld[3] + .' O:' .$mFld[4].' t:'.$mFld[5].' IDcnt:'.$mFld[6]; my $myId = AttrVal($name, "hmId", ""); - $myId = $attr{$name}{hmId} = $mFld[4] if (!$myId); - + $myId = $attr{$name}{hmId} = $mFld[4] if (!$myId); + if($mFld[4] ne $myId && !AttrVal($name, "dummy", 0)) { Log3 $hash, 1, 'HMLAN setting owner to '.$myId.' from '.$mFld[4]; HMLAN_SimpleWrite($hash, "A$myId"); @@ -649,7 +649,7 @@ sub HMLAN_Parse($$) {########################################################## } elsif($rmsg =~ m/^I00.*/) {; # Ack from the HMLAN - } + } else { Log3 $hash, 5, "$name Unknown msg >$rmsg<"; } @@ -662,74 +662,74 @@ sub HMLAN_SimpleWrite(@) {##################################################### my ($hash, $msg, $nonl) = @_; return if(!$hash || AttrVal($hash->{NAME}, "dummy", undef)); - + my $name = $hash->{NAME}; my $len = length($msg); - + # It is not possible to answer befor 100ms if ($len>51){ if($hash->{helper}{q}{HMcndN}){ - my $HMcnd = $hash->{helper}{q}{HMcndN}; - return if ( ($HMcnd == 4 || $HMcnd == 253) - && !$hash->{helper}{recoverTest});# no send if overload or disconnect - delete $hash->{helper}{recoverTest}; # test done + my $HMcnd = $hash->{helper}{q}{HMcndN}; + return if ( ($HMcnd == 4 || $HMcnd == 253) + && !$hash->{helper}{recoverTest});# no send if overload or disconnect + delete $hash->{helper}{recoverTest}; # test done } $msg =~ m/(.{9}).(..).(.{8}).(..).(.{8}).(..)(..)(..)(.{6})(.{6})(.*)/; - my ($s,$stat,$t,$d,$r,$no,$flg,$typ,$src,$dst,$p) = - ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11); + my ($s,$stat,$t,$d,$r,$no,$flg,$typ,$src,$dst,$p) = + ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11); - my $hmId = AttrVal($name,"hmId",""); - my $hDst = $hash->{helper}{$dst};# shortcut - my $tn = gettimeofday(); + my $hmId = AttrVal($name,"hmId",""); + my $hDst = $hash->{helper}{$dst};# shortcut + my $tn = gettimeofday(); if ($hDst->{nextSend}){ my $DevDelay = $hDst->{nextSend} - $tn; select(undef, undef, undef, (($DevDelay > 0.1)?0.1:$DevDelay)) - if ($DevDelay > 0.01); - delete $hDst->{nextSend}; + if ($DevDelay > 0.01); + delete $hDst->{nextSend}; } - if ($dst ne $hmId){ #delay send if answer is pending - if ( $hDst->{flg} && #HMLAN's ack pending + if ($dst ne $hmId){ #delay send if answer is pending + if ( $hDst->{flg} && #HMLAN's ack pending ($hDst->{to} > $tn)){#won't wait forever! check timeout - $hDst->{msg} = $msg; #postpone message - Log3 $hash, HMLAN_getVerbLvl($hash,$src,$dst,"5"),"HMLAN_Delay: $name $dst"; - return; - } - if ($src eq $hmId){ - $hDst->{flg} = (hex($flg)&0x20)?1:0;# answer expected? + $hDst->{msg} = $msg; #postpone message + Log3 $hash, HMLAN_getVerbLvl($hash,$src,$dst,"5"),"HMLAN_Delay: $name $dst"; + return; + } + if ($src eq $hmId){ + $hDst->{flg} = (hex($flg)&0x20)?1:0;# answer expected? $hDst->{to} = $tn + 2;# flag timeout after 2 sec - $hDst->{msg} = ""; - HMLAN_qResp($hash,$dst,1) if ($hDst->{flg} == 1); - } - } + $hDst->{msg} = ""; + HMLAN_qResp($hash,$dst,1) if ($hDst->{flg} == 1); + } + } if ($len > 52){#channel information included, send sone kind of clearance - my $chn = substr($msg,52,2); - if ($hDst->{chn} && $hDst->{chn} ne $chn){ - my $updt = $hDst->{newChn}; + my $chn = substr($msg,52,2); + if ($hDst->{chn} && $hDst->{chn} ne $chn){ + my $updt = $hDst->{newChn}; Log3 $hash, HMLAN_getVerbLvl($hash,$src,$dst,"5") - , 'HMLAN_Send: '.$name.' S:'.$updt; - syswrite($hash->{TCPDev}, $updt."\r\n") if($hash->{TCPDev}); - } - $hDst->{chn} = $chn; - } - Log3 $hash, HMLAN_getVerbLvl($hash,$src,$dst,"5") - , 'HMLAN_Send: '.$name.' S:'.$s + , 'HMLAN_Send: '.$name.' S:'.$updt; + syswrite($hash->{TCPDev}, $updt."\r\n") if($hash->{TCPDev}); + } + $hDst->{chn} = $chn; + } + Log3 $hash, HMLAN_getVerbLvl($hash,$src,$dst,"5") + , 'HMLAN_Send: '.$name.' S:'.$s .' stat: ' .$stat .' t:' .$t .' d:' .$d - .' r:' .$r + .' r:' .$r .' m:' .$no .' ' .$flg.$typ .' ' .$src .' ' .$dst .' ' .$p; - HMLAN_UpdtMsgLoad($name,(hex($flg)&0x10)?17:1);#burst counts + HMLAN_UpdtMsgLoad($name,(hex($flg)&0x10)?17:1);#burst counts } - else{ - Log3 $hash, ($hash->{helper}{log}{sys}?0:5), 'HMLAN_Send: '.$name.' I:'.$msg; + else{ + Log3 $hash, ($hash->{helper}{log}{sys}?0:5), 'HMLAN_Send: '.$name.' I:'.$msg; } - + $msg .= "\r\n" unless($nonl); syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev}); } @@ -738,19 +738,19 @@ sub HMLAN_DoInit($) {########################################################## my $name = $hash->{NAME}; my $id = AttrVal($name, "hmId", "999999"); - my ($k1no,$k1,$k2no,$k2,$k3no,$k3) - =( "01",AttrVal($name,"hmKey","") - ,"02",AttrVal($name,"hmKey2","") - ,"03",AttrVal($name,"hmKey3","") - ); - + my ($k1no,$k1,$k2no,$k2,$k3no,$k3) + =( "01",AttrVal($name,"hmKey","") + ,"02",AttrVal($name,"hmKey2","") + ,"03",AttrVal($name,"hmKey3","") + ); + if ($k1 =~ m/:/){($k1no,$k1) = split(":",$k1);} if ($k2 =~ m/:/){($k2no,$k2) = split(":",$k2);} if ($k3 =~ m/:/){($k3no,$k3) = split(":",$k3);} - + my $s2000 = sprintf("%02X", HMLAN_secSince2000()); delete $hash->{READINGS}{state}; - + HMLAN_SimpleWrite($hash, "A$id") if($id ne "999999"); HMLAN_SimpleWrite($hash, "C"); HMLAN_SimpleWrite($defs{$name}, "Y01,".($k1?"$k1no,$k1":"00,")); @@ -758,19 +758,19 @@ sub HMLAN_DoInit($) {########################################################## HMLAN_SimpleWrite($defs{$name}, "Y03,".($k3?"$k3no,$k3":"00,")); HMLAN_SimpleWrite($hash, "T$s2000,04,00,00000000"); delete $hash->{helper}{ref}; - + HMLAN_condUpdate($hash,0xff); $hash->{helper}{q}{cap}{$_}=0 foreach (keys %{$hash->{helper}{q}{cap}}); - foreach (keys %lhash){delete ($lhash{$_})};# clear IDs - HMLAN might have a reset + foreach (keys %lhash){delete ($lhash{$_})};# clear IDs - HMLAN might have a reset $hash->{helper}{q}{keepAliveRec} = 1; # ok for first time $hash->{helper}{q}{keepAliveRpt} = 0; # ok for first time - + my $tn = gettimeofday(); my $wdTimer = AttrVal($name,"wdTimer",25); $hash->{helper}{k}{Start} = $tn; $hash->{helper}{k}{Next} = $tn + $wdTimer; - + RemoveInternalTimer( "keepAliveCk:".$name);# avoid duplicate timer RemoveInternalTimer( "keepAlive:".$name);# avoid duplicate timer InternalTimer($tn+$wdTimer, "HMLAN_KeepAlive", "keepAlive:".$name, 0); @@ -792,15 +792,15 @@ sub HMLAN_KeepAlive($) {####################################################### my $kDly = int(($tn - $hash->{helper}{k}{Next})*1000)/1000; $hash->{helper}{k}{DlyMax} = $kDly if($hash->{helper}{k}{DlyMax} < $kDly); - + if ($hash->{helper}{k}{Start}){ my $kBuf = int($hash->{helper}{k}{Start} + 30 - $tn); $hash->{helper}{k}{BufMin} = $kBuf if($hash->{helper}{k}{BufMin} > $kBuf); - } + } else{ $hash->{helper}{k}{BufMin} = 30; } - + $hash->{msgKeepAlive} = "dlyMax:".$hash->{helper}{k}{DlyMax} ." bufferMin:". $hash->{helper}{k}{BufMin}; $hash->{helper}{k}{Start} = $tn; @@ -818,12 +818,12 @@ sub HMLAN_KeepAliveCheck($) {################################################## my $hash = $defs{$name}; if ($hash->{helper}{q}{keepAliveRec} != 1){# no answer if ($hash->{helper}{q}{keepAliveRpt} >2){# give up here - HMLAN_condUpdate($hash,252);# trigger timeout event + HMLAN_condUpdate($hash,252);# trigger timeout event DevIo_Disconnected($hash); } else{ $hash->{helper}{q}{keepAliveRpt}++; - HMLAN_KeepAlive("keepAlive:".$name);#repeat + HMLAN_KeepAlive("keepAlive:".$name);#repeat } } else{ @@ -836,7 +836,7 @@ sub HMLAN_secSince2000() {##################################################### my $t = time(); my @l = localtime($t); my @g = gmtime($t); - $t += 60*(($l[2]-$g[2] + ((($l[5]<<9)|$l[7]) <=> (($g[5]<<9)|$g[7])) * 24) * 60 + $l[1]-$g[1]) + $t += 60*(($l[2]-$g[2] + ((($l[5]<<9)|$l[7]) <=> (($g[5]<<9)|$g[7])) * 24) * 60 + $l[1]-$g[1]) # timezone and daylight saving... - 946684800 # seconds between 01.01.2000, 00:00 and THE EPOCH (1970) - 7200; # HM Special @@ -847,17 +847,17 @@ sub HMLAN_qResp($$$) {#response-waiting queue################################## my $hashQ = $hash->{helper}{q}; if ($cmd){ $hashQ->{answerPend} ++; - push @{$hashQ->{apIDs}},$id; - $hash->{XmitOpen} = 0 if ($hashQ->{answerPend} >= $hashQ->{hmLanQlen}); + push @{$hashQ->{apIDs}},$id; + $hash->{XmitOpen} = 0 if ($hashQ->{answerPend} >= $hashQ->{hmLanQlen}); } else{ $hashQ->{answerPend}-- if ($hashQ->{answerPend}>0); - @{$hashQ->{apIDs}}=grep !/$id/,@{$hashQ->{apIDs}}; - $hash->{XmitOpen} = 1 - if (($hashQ->{answerPend} < $hashQ->{hmLanQlen}) && - !($hashQ->{HMcndN} == 4 || - $hashQ->{HMcndN} == 253) - ); + @{$hashQ->{apIDs}}=grep !/$id/,@{$hashQ->{apIDs}}; + $hash->{XmitOpen} = 1 + if (($hashQ->{answerPend} < $hashQ->{hmLanQlen}) && + !($hashQ->{HMcndN} == 4 || + $hashQ->{HMcndN} == 253) + ); } } @@ -884,25 +884,25 @@ sub HMLAN_condUpdate($$) {##################################################### readingsBulkUpdate($hash,"prot_".$HMcndTxt,"last"); $hashQ->{HMcndN} = $HMcnd; - + if ($HMcnd == 4 || $HMcnd == 253) {#transmission down $hashQ->{answerPend} = 0; - @{$hashQ->{apIDs}} = (); #clear Q-status + @{$hashQ->{apIDs}} = (); #clear Q-status $hash->{XmitOpen} = 0; #deny transmit readingsBulkUpdate($hash,"prot_keepAlive","last") - if ( $HMcnd == 253 - && $hash->{helper}{k}{Start} - &&(gettimeofday() - 29) > $hash->{helper}{k}{Start}); + if ( $HMcnd == 253 + && $hash->{helper}{k}{Start} + &&(gettimeofday() - 29) > $hash->{helper}{k}{Start}); } elsif ($HMcnd == 255) {#reset counter after init $hashQ->{answerPend} = 0; - @{$hashQ->{apIDs}} = (); #clear Q-status + @{$hashQ->{apIDs}} = (); #clear Q-status $hash->{XmitOpen} = 1; #allow transmit } else{ - $hash->{XmitOpen} = 1 - if($hashQ->{answerPend} < $hashQ->{hmLanQlen});#allow transmit - } + $hash->{XmitOpen} = 1 + if($hashQ->{answerPend} < $hashQ->{hmLanQlen});#allow transmit + } readingsEndUpdate($hash,1); } @@ -916,7 +916,7 @@ sub HMLAN_noDup(@) {#return list with no duplicates sub HMLAN_getVerbLvl ($$$$){#get verboseLevel for message my ($hash,$src,$dst,$def) = @_; return ($hash->{helper}{log}{all}|| - (grep /($src|$dst)/,@{$hash->{helper}{log}{ids}}))?0:$def; + (grep /($src|$dst)/,@{$hash->{helper}{log}{ids}}))?0:$def; } 1; @@ -927,121 +927,121 @@ sub HMLAN_getVerbLvl ($$$$){#get verboseLevel for message

HMLAN

    - The HMLAN is the fhem module for the eQ-3 HomeMatic LAN Configurator.
    - A description on how to use hmCfgUsb can be found follwing the link.
    -
    - The fhem module will emulate a CUL device, so the CUL_HM module can be used to define HomeMatic devices.
    -
    - In order to use it with fhem you must disable the encryption first with the "HomeMatic Lan Interface Configurator"
    - (which is part of the supplied Windows software), by selecting the device, "Change IP Settings", and deselect "AES Encrypt Lan Communication".
    -
    - This device can be used in parallel with a CCU and (readonly) with fhem. To do this: -
      -
    • start the fhem/contrib/tcptee.pl program
    • -
    • redirect the CCU to the local host
    • -
    • disable the LAN-Encryption on the CCU for the Lan configurator
    • -
    • set the dummy attribute for the HMLAN device in fhem
    • -
    -

    + The HMLAN is the fhem module for the eQ-3 HomeMatic LAN Configurator.
    + A description on how to use hmCfgUsb can be found follwing the link.
    +
    + The fhem module will emulate a CUL device, so the CUL_HM module can be used to define HomeMatic devices.
    +
    + In order to use it with fhem you must disable the encryption first with the "HomeMatic Lan Interface Configurator"
    + (which is part of the supplied Windows software), by selecting the device, "Change IP Settings", and deselect "AES Encrypt Lan Communication".
    +
    + This device can be used in parallel with a CCU and (readonly) with fhem. To do this: +
      +
    • start the fhem/contrib/tcptee.pl program
    • +
    • redirect the CCU to the local host
    • +
    • disable the LAN-Encryption on the CCU for the Lan configurator
    • +
    • set the dummy attribute for the HMLAN device in fhem
    • +
    +

    - Define -
      - define <name> HMLAN <ip-address>[:port]
      -
      - port is 1000 by default.
      - If the ip-address is called none, then no device will be opened, so you can experiment without hardware attached. -
    -

    + Define +
      + define <name> HMLAN <ip-address>[:port]
      +
      + port is 1000 by default.
      + If the ip-address is called none, then no device will be opened, so you can experiment without hardware attached. +
    +

    - Set - -

    + Set + +

    - Get -
      - N/A -
    -

    + Get +
      + N/A +
    +

    - Attributes -
      -
    • do_not_notify

    • -
    • dummy

    • -
    • addvaltrigger

    • -
    • logIDs
      - enables selective logging of HMLAN messages. A list of HMIds or names can be - entered, comma separated, which shall be logged.
      - The attribute only allows device-IDs, not channel IDs. - Channel-IDs will be modified to device-IDs automatically. - all will log raw messages for all HMIds
      - sys will log system related messages like keep-alive
      - in order to enable all messages set "all,sys"
      -
    • -
    • hmMsgLowLimit
      - max messages level of HMLAN allowed for low-level message queue - to be executed. Above this level processing will be postponed.
      - HMLAN will allow a max of messages per hour, it will block sending otherwise. - After about 90% messages the low-priority queue (currently only CUL_HM autoReadReg) - will be delayed until the condition is cleared.
      - hmMsgLowLimit allowes to reduce this level further.
      - Note that HMLAN transmitt-level calculation is based on some estimations and + Attributes +
        +
      • do_not_notify

      • +
      • dummy

      • +
      • addvaltrigger

      • +
      • logIDs
        + enables selective logging of HMLAN messages. A list of HMIds or names can be + entered, comma separated, which shall be logged.
        + The attribute only allows device-IDs, not channel IDs. + Channel-IDs will be modified to device-IDs automatically. + all will log raw messages for all HMIds
        + sys will log system related messages like keep-alive
        + in order to enable all messages set "all,sys"
        +
      • +
      • hmMsgLowLimit
        + max messages level of HMLAN allowed for low-level message queue + to be executed. Above this level processing will be postponed.
        + HMLAN will allow a max of messages per hour, it will block sending otherwise. + After about 90% messages the low-priority queue (currently only CUL_HM autoReadReg) + will be delayed until the condition is cleared.
        + hmMsgLowLimit allowes to reduce this level further.
        + Note that HMLAN transmitt-level calculation is based on some estimations and has some tolerance.
        -

      • -
      • hmId

      • -
      • hmKey

      • -
      • hmKey2

      • -
      • hmKey3
        - AES keys for the HMLAN adapter.
        - The key is converted to a hash. If a hash is given directly it is not converted but taken directly. - Therefore the original key cannot be converted back
        -
      • -
      • hmProtocolEvents

      • -
      • respTime
        - Define max response time of the HMLAN adapter in seconds. Default is 1 sec.
        - Longer times may be used as workaround in slow/instable systems or LAN configurations.
      • -
      • wdTimer
        - Time in sec to trigger HMLAN. Values between 5 and 25 are allowed, 25 is default.
        - It is not recommended to change this timer. If problems are detected with
        - HLMLAN disconnection it is advisable to resolve the root-cause of the problem and not symptoms.
      • -
      • hmLanQlen
        - defines queuelength of HMLAN interface. This is therefore the number of - simultanously send messages. increasing values may cause higher transmission speed. - It may also cause retransmissions up to data loss.
        - Effects can be observed by watching protocol events
        - 1 - is a conservatibe value, and is default
        - 5 - is critical length, likely cause message loss
      • -
      - parameter +

    • +
    • hmId

    • +
    • hmKey

    • +
    • hmKey2

    • +
    • hmKey3
      + AES keys for the HMLAN adapter.
      + The key is converted to a hash. If a hash is given directly it is not converted but taken directly. + Therefore the original key cannot be converted back
      +
    • +
    • hmProtocolEvents

    • +
    • respTime
      + Define max response time of the HMLAN adapter in seconds. Default is 1 sec.
      + Longer times may be used as workaround in slow/instable systems or LAN configurations.
    • +
    • wdTimer
      + Time in sec to trigger HMLAN. Values between 5 and 25 are allowed, 25 is default.
      + It is not recommended to change this timer. If problems are detected with
      + HLMLAN disconnection it is advisable to resolve the root-cause of the problem and not symptoms.
    • +
    • hmLanQlen
      + defines queuelength of HMLAN interface. This is therefore the number of + simultanously send messages. increasing values may cause higher transmission speed. + It may also cause retransmissions up to data loss.
      + Effects can be observed by watching protocol events
      + 1 - is a conservatibe value, and is default
      + 5 - is critical length, likely cause message loss
    • +
    + parameter
    • assignedIDs
      - HMIds that are assigned to HMLAN and will be handled. e.g. ACK will be generated internally
    • + HMIds that are assigned to HMLAN and will be handled. e.g. ACK will be generated internally
    • assignedIDsCnt
      - number of IDs that are assigned to HMLAN by FHEM
    • + number of IDs that are assigned to HMLAN by FHEM
    • assignedIDsReport
      - number of HMIds that HMLAN reports are assigned. This should be identical - to assignedIDsCnt
    • + number of HMIds that HMLAN reports are assigned. This should be identical + to assignedIDsCnt
    • msgKeepAlive
      - performance of keep-alive messages.
      - dlyMax: maximum delay of sheduled message-time to actual message send.
      - bufferMin: minimal buffer left to before HMLAN would likely disconnect - due to missing keepAlive message. bufferMin will be reset to 30sec if - attribut wdTimer is changed.
      - if dlyMax is high (several seconds) or bufferMin goes to "0" (normal is 4) the system - suffers on internal delays. Reasons for the delay might be explored. As a quick solution - wdTimer could be decreased to trigger HMLAN faster.
    • + performance of keep-alive messages.
      + dlyMax: maximum delay of sheduled message-time to actual message send.
      + bufferMin: minimal buffer left to before HMLAN would likely disconnect + due to missing keepAlive message. bufferMin will be reset to 30sec if + attribut wdTimer is changed.
      + if dlyMax is high (several seconds) or bufferMin goes to "0" (normal is 4) the system + suffers on internal delays. Reasons for the delay might be explored. As a quick solution + wdTimer could be decreased to trigger HMLAN faster.
    • msgLoadEst
      - estimation of load of HMLAN. As HMLAN has a max capacity of message transmit per hour - FHEM tries to estimate usage - see also - hmMsgLowLimit
    • + estimation of load of HMLAN. As HMLAN has a max capacity of message transmit per hour + FHEM tries to estimate usage - see also + hmMsgLowLimit
    • msgParseDly
      - calculates the delay of messages in ms from send in HMLAN until processing in FHEM. - It therefore gives an indication about FHEM system performance. -
    • + calculates the delay of messages in ms from send in HMLAN until processing in FHEM. + It therefore gives an indication about FHEM system performance. +
    - parameter and readings + parameter and readings
    • prot_disconnect
      recent HMLAN disconnect
    • prot_init
      recent HMLAN init
    • diff --git a/fhem/FHEM/10_CUL_HM.pm b/fhem/FHEM/10_CUL_HM.pm index 757643240..7bf711efd 100755 --- a/fhem/FHEM/10_CUL_HM.pm +++ b/fhem/FHEM/10_CUL_HM.pm @@ -138,16 +138,16 @@ sub CUL_HM_Initialize($) { "rawToReadable unit ".#"KFM-Sensor" only "peerIDs repPeers ". "actCycle actStatus ". - "autoReadReg:0_off,1_restart,2_pon-restart,3_onChange,4_reqStatus,8_stateOnly ". - "expert:0_off,1_on,2_full ". - "burstAccess:0_off,1_auto ". + "autoReadReg:0_off,1_restart,2_pon-restart,3_onChange,4_reqStatus,8_stateOnly ". + "expert:0_off,1_on,2_full ". + "burstAccess:0_off,1_auto ". "param msgRepeat ". - ".stc .devInfo ". + ".stc .devInfo ". $readingFnAttributes; - #autoReadReg: - # ,6_allForce - # ,4_backUpdt - + #autoReadReg: + # ,6_allForce + # ,4_backUpdt + my @modellist; foreach my $model (keys %culHmModel){ push @modellist,$culHmModel{$model}{name}; @@ -183,126 +183,126 @@ sub CUL_HM_updateConfig($){ # Purpose is to parse attributes and read config foreach my $name (@{$modules{CUL_HM}{helper}{updtCfgLst}}){ my $hash = $defs{$name}; - my $id = $hash->{DEF}; + my $id = $hash->{DEF}; my $chn = substr($id."00",6,2); - - if ($id ne $K_actDetID){# if not action detector - CUL_HM_ID2PeerList($name,"",1); # update peerList out of peerIDs + + if ($id ne $K_actDetID){# if not action detector + CUL_HM_ID2PeerList($name,"",1); # update peerList out of peerIDs my $actCycle = AttrVal($name,"actCycle",undef); - CUL_HM_ActAdd($id,$actCycle) if ($actCycle);# add to ActionDetect? - # --- set default attrubutes if missing --- + CUL_HM_ActAdd($id,$actCycle) if ($actCycle);# add to ActionDetect? + # --- set default attrubutes if missing --- if ( $hash->{helper}{role}{dev} - && AttrVal($name,"subType","") ne "virtual"){ - $attr{$name}{expert} = AttrVal($name,"expert" ,"2_full"); - $attr{$name}{autoReadReg}= AttrVal($name,"autoReadReg","4_reqStatus"); - } - CUL_HM_Attr("attr",$name,"expert",$attr{$name}{expert});#need update after readings are available - } - else{# Action Detector only - $attr{$name}{"event-on-change-reading"} = AttrVal($name, "event-on-change-reading", ".*"); - delete $hash->{helper}{role}; - $hash->{helper}{role}{vrt} = 1; - next; - #delete $attr{$name}{peerIDs}; # remove historical data - } + && AttrVal($name,"subType","") ne "virtual"){ + $attr{$name}{expert} = AttrVal($name,"expert" ,"2_full"); + $attr{$name}{autoReadReg}= AttrVal($name,"autoReadReg","4_reqStatus"); + } + CUL_HM_Attr("attr",$name,"expert",$attr{$name}{expert});#need update after readings are available + } + else{# Action Detector only + $attr{$name}{"event-on-change-reading"} = AttrVal($name, "event-on-change-reading", ".*"); + delete $hash->{helper}{role}; + $hash->{helper}{role}{vrt} = 1; + next; + #delete $attr{$name}{peerIDs}; # remove historical data + } my $st = CUL_HM_Get($hash,$name,"param","subType"); my $md = CUL_HM_Get($hash,$name,"param","model"); if ($md =~ /(HM-CC-TC)/){ - $hash->{helper}{role}{chn} = 1 if (length($id) == 6); #tc special - $attr{$name}{stateFormat} = "last:trigLast" if ($chn eq "03"); - } + $hash->{helper}{role}{chn} = 1 if (length($id) == 6); #tc special + $attr{$name}{stateFormat} = "last:trigLast" if ($chn eq "03"); + } elsif ($md =~ m/HM-CC-RT-DN/){ - $attr{$name}{stateFormat} = "last:trigLast" if ($chn eq "03"); - } - elsif ("dimmer" eq $st) {#setup virtual dimmer channels - my $mId = CUL_HM_getMId($hash); - #configure Dimmer virtual channel assotiation - if ($hash->{helper}{role}{chn}){ - my $chn = (length($id) == 8)?substr($id,6,2):"01"; - my $devId = substr($id,0,6); - if ($culHmModel{$mId} && $culHmModel{$mId}{chn} =~ m/Sw._V/){#virtual? - my @chnPh = (grep{$_ =~ m/Sw:/ } split ',',$culHmModel{$mId}{chn}); - @chnPh = split ':',$chnPh[0] if (@chnPh); - my $chnPhyMax = $chnPh[2]?$chnPh[2]:1; # max Phys channels - my $chnPhy = int(($chn-$chnPhyMax+1)/2); # assotiated phy chan - my $idPhy = $devId.sprintf("%02X",$chnPhy);# ID assot phy chan - my $pHash = CUL_HM_id2Hash($idPhy); # hash assot phy chan - $idPhy = $pHash->{DEF}; # could be device!!! - if ($pHash){ - $pHash->{helper}{vDim}{idPhy} = $idPhy; - my $vHash = CUL_HM_id2Hash($devId.sprintf("%02X",$chnPhyMax+2*$chnPhy-1)); - if ($vHash){ - $pHash->{helper}{vDim}{idV2} = $vHash->{DEF}; - $vHash->{helper}{vDim}{idPhy} = $idPhy; - } - else{ - delete $pHash->{helper}{vDim}{idV2}; - } - $vHash = CUL_HM_id2Hash($devId.sprintf("%02X",$chnPhyMax+2*$chnPhy)); - if ($vHash){ - $pHash->{helper}{vDim}{idV3} = $vHash->{DEF}; - $vHash->{helper}{vDim}{idPhy} = $idPhy; - } - else{ - delete $pHash->{helper}{vDim}{idV3}; - } - } - } - } - } - elsif ("virtual" eq $st) {#setup virtuals - $hash->{helper}{role}{vrt} = 1; - } + $attr{$name}{stateFormat} = "last:trigLast" if ($chn eq "03"); + } + elsif ("dimmer" eq $st) {#setup virtual dimmer channels + my $mId = CUL_HM_getMId($hash); + #configure Dimmer virtual channel assotiation + if ($hash->{helper}{role}{chn}){ + my $chn = (length($id) == 8)?substr($id,6,2):"01"; + my $devId = substr($id,0,6); + if ($culHmModel{$mId} && $culHmModel{$mId}{chn} =~ m/Sw._V/){#virtual? + my @chnPh = (grep{$_ =~ m/Sw:/ } split ',',$culHmModel{$mId}{chn}); + @chnPh = split ':',$chnPh[0] if (@chnPh); + my $chnPhyMax = $chnPh[2]?$chnPh[2]:1; # max Phys channels + my $chnPhy = int(($chn-$chnPhyMax+1)/2); # assotiated phy chan + my $idPhy = $devId.sprintf("%02X",$chnPhy);# ID assot phy chan + my $pHash = CUL_HM_id2Hash($idPhy); # hash assot phy chan + $idPhy = $pHash->{DEF}; # could be device!!! + if ($pHash){ + $pHash->{helper}{vDim}{idPhy} = $idPhy; + my $vHash = CUL_HM_id2Hash($devId.sprintf("%02X",$chnPhyMax+2*$chnPhy-1)); + if ($vHash){ + $pHash->{helper}{vDim}{idV2} = $vHash->{DEF}; + $vHash->{helper}{vDim}{idPhy} = $idPhy; + } + else{ + delete $pHash->{helper}{vDim}{idV2}; + } + $vHash = CUL_HM_id2Hash($devId.sprintf("%02X",$chnPhyMax+2*$chnPhy)); + if ($vHash){ + $pHash->{helper}{vDim}{idV3} = $vHash->{DEF}; + $vHash->{helper}{vDim}{idPhy} = $idPhy; + } + else{ + delete $pHash->{helper}{vDim}{idV3}; + } + } + } + } + } + elsif ("virtual" eq $st) {#setup virtuals + $hash->{helper}{role}{vrt} = 1; + } if ( $hash->{helper}{role}{dev} && CUL_HM_getRxType($hash)&0x02){#burst dev - #burst devices must restrict retries! - $attr{$name}{msgRepeat} = 1 if (!$attr{$name}{msgRepeat}); - } - # -+-+-+-+-+ add default web-commands + #burst devices must restrict retries! + $attr{$name}{msgRepeat} = 1 if (!$attr{$name}{msgRepeat}); + } + # -+-+-+-+-+ add default web-commands my $webCmd; $webCmd = AttrVal($name,"webCmd",undef); if(!defined $webCmd){ if ($st eq "virtual" ){$webCmd="press short:press long"; - }elsif((!$hash->{helper}{role}{chn} && - $md ne "HM-CC-TC") - ||$st eq "repeater" - ||$md eq "HM-CC-VD" ){$webCmd="getConfig"; - }elsif($st eq "blindActuator"){$webCmd="toggle:on:off:up:down:stop:statusRequest"; - }elsif($st eq "dimmer" ){$webCmd="toggle:on:off:up:down:statusRequest"; - }elsif($st eq "switch" ){$webCmd="toggle:on:off:statusRequest"; - }elsif($st eq "smokeDetector"){$webCmd="test:alarmOn:alarmOff"; - }elsif($st eq "keyMatic" ){$webCmd="lock:inhibit on:inhibit off"; - }elsif($md eq "HM-OU-CFM-PL" ){$webCmd="press short:press long" - .($chn eq "02"?":playTone replay":""); - } - if (!$hash->{helper}{role}{chn} - && $md =~ m/HM-CC-RT-DN/) {$webCmd.=":burstXmit";} - - if ($webCmd){ - my $eventMap = AttrVal($name,"eventMap",undef); - - my @wc; - push @wc,ReplaceEventMap($name, $_, 1) foreach (split ":",$webCmd); - $webCmd = join ":",@wc; - } - } - $attr{$name}{webCmd} = $webCmd if ($webCmd); + }elsif((!$hash->{helper}{role}{chn} && + $md ne "HM-CC-TC") + ||$st eq "repeater" + ||$md eq "HM-CC-VD" ){$webCmd="getConfig"; + }elsif($st eq "blindActuator"){$webCmd="toggle:on:off:up:down:stop:statusRequest"; + }elsif($st eq "dimmer" ){$webCmd="toggle:on:off:up:down:statusRequest"; + }elsif($st eq "switch" ){$webCmd="toggle:on:off:statusRequest"; + }elsif($st eq "smokeDetector"){$webCmd="test:alarmOn:alarmOff"; + }elsif($st eq "keyMatic" ){$webCmd="lock:inhibit on:inhibit off"; + }elsif($md eq "HM-OU-CFM-PL" ){$webCmd="press short:press long" + .($chn eq "02"?":playTone replay":""); + } + if (!$hash->{helper}{role}{chn} + && $md =~ m/HM-CC-RT-DN/) {$webCmd.=":burstXmit";} - CUL_HM_qStateUpdatIfEnab($name); + if ($webCmd){ + my $eventMap = AttrVal($name,"eventMap",undef); + + my @wc; + push @wc,ReplaceEventMap($name, $_, 1) foreach (split ":",$webCmd); + $webCmd = join ":",@wc; + } + } + $attr{$name}{webCmd} = $webCmd if ($webCmd); + + CUL_HM_qStateUpdatIfEnab($name); next if (0 == (0x07 & CUL_HM_getAttrInt($name,"autoReadReg"))); - if(!CUL_HM_peersValid($name)){ - CUL_HM_qAutoRead($name,1); - } - else{ - foreach(CUL_HM_reglUsed($name)){ - next if (!$_); - if(ReadingsVal($name,$_,"x") !~ m/00:00/){ - CUL_HM_qAutoRead($name,1); - last; - } - } - } + if(!CUL_HM_peersValid($name)){ + CUL_HM_qAutoRead($name,1); + } + else{ + foreach(CUL_HM_reglUsed($name)){ + next if (!$_); + if(ReadingsVal($name,$_,"x") !~ m/00:00/){ + CUL_HM_qAutoRead($name,1); + last; + } + } + } } delete $modules{CUL_HM}{helper}{updtCfgLst}; } @@ -312,46 +312,46 @@ sub CUL_HM_Define($$) {############################## my $HMid = uc($a[2]); return "wrong syntax: define CUL_HM 6-digit-hex-code [Raw-Message]" if(!(int(@a)==3 || int(@a)==4) || $HMid !~ m/^[A-F0-9]{6,8}$/i); - return "HMid DEF already used by " . CUL_HM_id2Name($HMid) - if ($modules{CUL_HM}{defptr}{$HMid}); + return "HMid DEF already used by " . CUL_HM_id2Name($HMid) + if ($modules{CUL_HM}{defptr}{$HMid}); my $name = $hash->{NAME}; if(length($HMid) == 8) {# define a channel my $devHmId = substr($HMid, 0, 6); my $chn = substr($HMid, 6, 2); my $devHash = $modules{CUL_HM}{defptr}{$devHmId}; - return "please define a device with hmId:".$devHmId." first" if(!$devHash); - + return "please define a device with hmId:".$devHmId." first" if(!$devHash); + my $devName = $devHash->{NAME}; - $hash->{device} = $devName; #readable ref to device name + $hash->{device} = $devName; #readable ref to device name $hash->{chanNo} = $chn; #readable ref to Channel - $devHash->{"channel_$chn"} = $name; #reference in device as well + $devHash->{"channel_$chn"} = $name; #reference in device as well $attr{$name}{model} = AttrVal($devName, "model", undef); $hash->{helper}{role}{chn}=1; - if($chn eq "01"){ - $attr{$name}{peerIDs} = AttrVal($devName, "peerIDs", ""); - $hash->{READINGS}{peerList}{VAL} = ReadingsVal($devName,"peerList",""); - $hash->{peerList} = $devHash->{peerList} if($devHash->{peerList}); - - delete $devHash->{helper}{role}{chn};#device no longer - delete $devHash->{peerList}; - delete $devHash->{READINGS}{peerList}; - } + if($chn eq "01"){ + $attr{$name}{peerIDs} = AttrVal($devName, "peerIDs", ""); + $hash->{READINGS}{peerList}{VAL} = ReadingsVal($devName,"peerList",""); + $hash->{peerList} = $devHash->{peerList} if($devHash->{peerList}); + + delete $devHash->{helper}{role}{chn};#device no longer + delete $devHash->{peerList}; + delete $devHash->{READINGS}{peerList}; + } } else{# define a device $hash->{helper}{role}{dev}=1; $hash->{helper}{role}{chn}=1;# take role of chn 01 until it is defined $hash->{helper}{q}{qReqConf}=""; # queue autoConfig requests for this device $hash->{helper}{q}{qReqStat}=""; # queue autoConfig requests for this device - CUL_HM_prtInit ($hash); + CUL_HM_prtInit ($hash); AssignIoPort($hash); } $modules{CUL_HM}{defptr}{$HMid} = $hash; - + #- - - - create auto-update - - - - - - CUL_HM_ActGetCreateHash() if($HMid eq '000000');#startTimer $hash->{DEF} = $HMid; CUL_HM_Parse($hash, $a[3]) if(int(@a) == 4); - + CUL_HM_queueUpdtCfg($name); return undef; } @@ -361,12 +361,12 @@ sub CUL_HM_Undef($$) {############################### my $HMid = $hash->{DEF}; my $chn = substr($HMid,6,2); if ($chn){# delete a channel - my $devHash = $defs{$devName}; + my $devHash = $defs{$devName}; delete $devHash->{"channel_$chn"} if ($devName); $devHash->{helper}{role}{chn}=1 if($chn eq "01");# return chan 01 role } else{# delete a device - CommandDelete(undef,$hash->{$_}) foreach (grep(/^channel_/,keys %{$hash})); + CommandDelete(undef,$hash->{$_}) foreach (grep(/^channel_/,keys %{$hash})); } delete($modules{CUL_HM}{defptr}{$HMid}); return undef; @@ -377,33 +377,33 @@ sub CUL_HM_Rename($$$) {############################# my $hash = $defs{$name}; if (length($HMid) == 8){# we are channel, inform the device $hash->{chanNo} = substr($HMid,6,2); - my $devHash = CUL_HM_id2Hash(substr($HMid,0,6)); + my $devHash = CUL_HM_id2Hash(substr($HMid,0,6)); $hash->{device} = $devHash->{NAME}; - $devHash->{"channel_".$hash->{chanNo}} = $name; + $devHash->{"channel_".$hash->{chanNo}} = $name; } else{# we are a device - inform channels if exist foreach (grep (/^channel_/, keys%{$hash})){ - my $chnHash = $defs{$hash->{$_}}; - $chnHash->{device} = $name; - } + my $chnHash = $defs{$hash->{$_}}; + $chnHash->{device} = $name; + } } if ($hash->{helper}{role}{chn}){ my $HMidCh = substr($HMid."01",0,8); - foreach my $pId (keys %{$modules{CUL_HM}{defptr}}){ - my $pH = $modules{CUL_HM}{defptr}{$pId}; + foreach my $pId (keys %{$modules{CUL_HM}{defptr}}){ + my $pH = $modules{CUL_HM}{defptr}{$pId}; my $pN = $pH->{NAME}; - my $pPeers = AttrVal($pN, "peerIDs", ""); - if ($pPeers =~ m/$HMidCh/){ - CUL_HM_ID2PeerList ($pN,"x",0); - foreach my $pR (grep /-$oldName-/,keys%{$pH->{READINGS}}){ - my $pRn = $pR; - $pRn =~ s/$oldName/$name/; - $pH->{READINGS}{$pRn}{VAL} = $pH->{READINGS}{$pR}{VAL}; - $pH->{READINGS}{$pRn}{TIME} = $pH->{READINGS}{$pR}{TIME}; - delete $pH->{READINGS}{$pR}; - } - } - } + my $pPeers = AttrVal($pN, "peerIDs", ""); + if ($pPeers =~ m/$HMidCh/){ + CUL_HM_ID2PeerList ($pN,"x",0); + foreach my $pR (grep /-$oldName-/,keys%{$pH->{READINGS}}){ + my $pRn = $pR; + $pRn =~ s/$oldName/$name/; + $pH->{READINGS}{$pRn}{VAL} = $pH->{READINGS}{$pR}{VAL}; + $pH->{READINGS}{$pRn}{TIME} = $pH->{READINGS}{$pR}{TIME}; + delete $pH->{READINGS}{$pR}; + } + } + } } return; } @@ -414,69 +414,69 @@ sub CUL_HM_Attr(@) {################################# my $hash = CUL_HM_name2Hash($name); if ($attrName eq "expert"){#[0,1,2] $attr{$name}{expert} = $attrVal; - my $eHash = $defs{$name}; - foreach my $chId (CUL_HM_getAssChnIds($name)){ - my $cHash = CUL_HM_id2Hash($chId); - push(@hashL,$cHash) if ($eHash ne $cHash); - } + my $eHash = $defs{$name}; + foreach my $chId (CUL_HM_getAssChnIds($name)){ + my $cHash = CUL_HM_id2Hash($chId); + push(@hashL,$cHash) if ($eHash ne $cHash); + } push(@hashL,$eHash); foreach my $hash (@hashL){ - my $exLvl = CUL_HM_getAttrInt($hash->{NAME},"expert"); - if ($exLvl eq "0"){# off + my $exLvl = CUL_HM_getAttrInt($hash->{NAME},"expert"); + if ($exLvl eq "0"){# off foreach my $rdEntry (grep /^RegL_/,keys %{$hash->{READINGS}}){ $hash->{READINGS}{".".$rdEntry} = $hash->{READINGS}{$rdEntry}; delete $hash->{READINGS}{$rdEntry}; - } + } foreach my $rdEntry (grep /^R-/ ,keys %{$hash->{READINGS}}){ - my $reg = $rdEntry; - $reg =~ s/.*-//; + my $reg = $rdEntry; + $reg =~ s/.*-//; next if(!$culHmRegDefine{$reg} || $culHmRegDefine{$reg}{d} eq '1'); $hash->{READINGS}{".".$rdEntry} = $hash->{READINGS}{$rdEntry}; delete $hash->{READINGS}{$rdEntry}; - } - } - elsif ($exLvl eq "1"){# on: Only register values, no raw data - # move register to visible if available + } + } + elsif ($exLvl eq "1"){# on: Only register values, no raw data + # move register to visible if available foreach my $rdEntry (grep /^RegL_/,keys %{$hash->{READINGS}}){ $hash->{READINGS}{".".$rdEntry} = $hash->{READINGS}{$rdEntry}; delete $hash->{READINGS}{$rdEntry}; - } + } foreach my $rdEntry (grep /^\.R-/ ,keys %{$hash->{READINGS}}){ $hash->{READINGS}{substr($rdEntry,1)} = $hash->{READINGS}{$rdEntry}; delete $hash->{READINGS}{$rdEntry}; - } - } - elsif ($exLvl eq "2"){# full - incl raw data + } + } + elsif ($exLvl eq "2"){# full - incl raw data foreach my $rdEntry (grep /^\.R(egL_|-)/,keys %{$hash->{READINGS}}){ $hash->{READINGS}{substr($rdEntry,1)} = $hash->{READINGS}{$rdEntry}; delete $hash->{READINGS}{$rdEntry}; - } - } - else{; - } + } + } + else{; + } } } elsif($attrName eq "actCycle"){#"000:00" or 'off' return if (CUL_HM_name2Id($name) eq $K_actDetID); - return "attribut not allowed for channels" - if (!$hash->{helper}{role}{dev}); - $updtReq = 1; + return "attribut not allowed for channels" + if (!$hash->{helper}{role}{dev}); + $updtReq = 1; } elsif($attrName eq "param"){ my $md = CUL_HM_Get($hash,$name,"param","model"); - my $chn = substr(CUL_HM_hash2Id($hash),6,2); + my $chn = substr(CUL_HM_hash2Id($hash),6,2); if ($md eq "HM-Sen-RD-O" && $chn eq "02"){ - delete $hash->{helper}{param}; - my @param = split ",",$attrVal; - foreach (@param){ + delete $hash->{helper}{param}; + my @param = split ",",$attrVal; + foreach (@param){ if ($_ eq "offAtPon"){$hash->{helper}{param}{offAtPon} = 1} elsif ($_ eq "onAtRain"){$hash->{helper}{param}{onAtRain} = 1} - else {return "param $_ unknown, use offAtPon or onAtRain";} - } - } - else{ - return "attribut param not defined for this entity"; - } + else {return "param $_ unknown, use offAtPon or onAtRain";} + } + } + else{ + return "attribut param not defined for this entity"; + } } elsif($attrName eq "peerIDs" &&!$hash->{helper}{role}{chn}){#only for chan return "$attrName not usable for devices"; @@ -484,20 +484,20 @@ sub CUL_HM_Attr(@) {################################# elsif($attrName eq "msgRepeat"){ return if ($cmd ne "set"); return "$attrName not usable for channels" if(!$hash->{helper}{role}{dev});#only for device - return "value $attrVal ignored, must be an integer" if ($attrVal !~ m/^(\d+)$/); + return "value $attrVal ignored, must be an integer" if ($attrVal !~ m/^(\d+)$/); } elsif($attrName eq "model" && $hash->{helper}{role}{dev}){ delete $hash->{helper}{rxType}; # needs new calculation - delete $hash->{helper}{mId}; + delete $hash->{helper}{mId}; } elsif($attrName eq "burstAccess"){ if ($cmd eq "set"){ return "use burstAccess only for device" if (!$hash->{helper}{role}{dev}); - return "$attrVal not a valid option for burstAccess" if ($attrVal !~ m/^[01]/); - if ($attrVal =~ m/^0/){$hash->{protCondBurst} = "forced_off";} - else {$hash->{protCondBurst} = "unknown";} - } - else{ $hash->{protCondBurst} = "forced_off";} + return "$attrVal not a valid option for burstAccess" if ($attrVal !~ m/^[01]/); + if ($attrVal =~ m/^0/){$hash->{protCondBurst} = "forced_off";} + else {$hash->{protCondBurst} = "unknown";} + } + else{ $hash->{protCondBurst} = "forced_off";} delete $hash->{helper}{rxType}; # needs new calculation } CUL_HM_queueUpdtCfg($name) if ($updtReq); @@ -507,16 +507,16 @@ sub CUL_HM_Attr(@) {################################# #+++++++++++++++++ msg receive, parsing++++++++++++++++++++++++++++++++++++++++ # translate level to readable my %lvlStr = ( md =>{ "HM-SEC-WDS" =>{"00"=>"dry" ,"64"=>"damp" ,"C8"=>"wet" } - ,"HM-CC-SCD" =>{"00"=>"normal" ,"64"=>"added" ,"C8"=>"addedStrong"} - ,"HM-Sen-RD-O" =>{"00"=>"dry" ,"C8"=>"rain"} - } + ,"HM-CC-SCD" =>{"00"=>"normal" ,"64"=>"added" ,"C8"=>"addedStrong"} + ,"HM-Sen-RD-O" =>{"00"=>"dry" ,"C8"=>"rain"} + } ,mdCh=>{ "HM-Sen-RD-O01" =>{"00"=>"dry" ,"C8"=>"rain"} - ,"HM-Sen-RD-O02" =>{"00"=>"off" ,"C8"=>"on"} - } - ,st =>{ "smokeDetector" =>{"01"=>"no alarm","C7"=>"tone off","C8"=>"Smoke Alarm"} - ,"threeStateSensor"=>{"00"=>"closed" ,"64"=>"tilted" ,"C8"=>"open"} - } - ); + ,"HM-Sen-RD-O02" =>{"00"=>"off" ,"C8"=>"on"} + } + ,st =>{ "smokeDetector" =>{"01"=>"no alarm","C7"=>"tone off","C8"=>"Smoke Alarm"} + ,"threeStateSensor"=>{"00"=>"closed" ,"64"=>"tilted" ,"C8"=>"open"} + } + ); sub CUL_HM_Parse($$) {############################## my ($iohash, $msgIn) = @_; my $id = CUL_HM_Id($iohash); @@ -531,20 +531,20 @@ sub CUL_HM_Parse($$) {############################## return "" if( ($msgStat && $msgStat eq 'NACK')# lowlevel error ||($src eq $id)); # mirrored messages # $shash will be replaced for multichannel commands - my $shash = $modules{CUL_HM}{defptr}{$src}; + my $shash = $modules{CUL_HM}{defptr}{$src}; my $dhash = $modules{CUL_HM}{defptr}{$dst}; CUL_HM_statCnt($ioName,"r"); - + $respRemoved = 0; #set to 'no response in this message' at start if(!$shash) { # Unknown source # Generate an UNKNOWN event for pairing requests, ignore everything else if($mTp eq "00") { my $md = substr($p, 2, 4); - $md = $culHmModel{$md}{name} ? - $culHmModel{$md}{name} : - "ID_".$md; - my $sname = "CUL_HM_".$md."_$src"; - $sname =~ s/-/_/g; + $md = $culHmModel{$md}{name} ? + $culHmModel{$md}{name} : + "ID_".$md; + my $sname = "CUL_HM_".$md."_$src"; + $sname =~ s/-/_/g; Log3 undef, 3, "CUL_HM Unknown device $sname, please define it"; return "UNDEFINED $sname CUL_HM $src $msg"; } @@ -552,7 +552,7 @@ sub CUL_HM_Parse($$) {############################## } my @event; #events to be posted for main entity my @entities; #additional entities with events to be notifies - + my $name = $shash->{NAME}; return $name if (CUL_HM_getAttrInt($name,"ignore")); @@ -563,9 +563,9 @@ sub CUL_HM_Parse($$) {############################## CUL_HM_eventP($shash,"Evt_$msgStat")if ($msgStat);#log io-events CUL_HM_eventP($shash,"Rcv"); my $dname = ($dst eq "000000") ? "broadcast" : - ($dhash ? $dhash->{NAME} : - ($dst eq $id ? $ioName : - $dst)); + ($dhash ? $dhash->{NAME} : + ($dst eq $id ? $ioName : + $dst)); my $target = " (to $dname)"; my $st = AttrVal($name, "subType", ""); my $md = AttrVal($name, "model" , ""); @@ -580,51 +580,51 @@ sub CUL_HM_Parse($$) {############################## && $shash->{helper}{rpt}{IO} eq $ioName #from same IO && $shash->{helper}{rpt}{flg} eq substr($msg,5,1) #not from repeater && $shash->{helper}{rpt}{ts} < gettimeofday()-0.24 # again if older then 240ms (typ repeat time) - #todo: hack since HMLAN sends duplicate status messages - ){ - my $ack = $shash->{helper}{rpt}{ack};#shorthand - my $i=0; - $shash->{helper}{rpt}{ts} = gettimeofday(); + #todo: hack since HMLAN sends duplicate status messages + ){ + my $ack = $shash->{helper}{rpt}{ack};#shorthand + my $i=0; + $shash->{helper}{rpt}{ts} = gettimeofday(); CUL_HM_SndCmd(${$ack}[$i++],${$ack}[$i++]) while ($i<@{$ack}); Log3 $name,4,"CUL_HM $name dup: repeat ack, dont process"; - } - else{ + } + else{ Log3 $name,4,"CUL_HM $name dup: dont process"; - } + } return $name; #return something to please dispatcher } $shash->{lastMsg} = $msgX; delete $shash->{helper}{rpt};# new message, rm recent ack my @ack; # ack and responses, might be repeated - + CUL_HM_DumpProtocol("RCV",$iohash,$len,$mNo,$mFlg,$mTp,$src,$dst,$p); #----------start valid messages parsing --------- my $parse = CUL_HM_parseCommon($mNo,$mFlg,$mTp,$src,$dst,$p,$st,$md); push @event, "powerOn" if($parse eq "powerOn"); push @event, "" if($parse eq "parsed"); # msg is parsed but may - # be processed further + # be processed further foreach my $r (split";",$parse){ if ($r =~ s/entities://){#common generated trigger for some entities push @entities,split(",",$r); } - else{ - $parse = $r; - } + else{ + $parse = $r; + } } if ($parse eq "ACK"){# remember - ACKinfo will be passed on push @event, ""; } elsif($parse eq "NACK"){ - push @event, "state:NACK"; + push @event, "state:NACK"; } elsif($parse eq "done"){ push @event, ""; - } + } elsif($mTp eq "12") {#$lcm eq "09A112" Another fhem request (HAVE_DATA) ; - } + } elsif($md =~ m/^(KS550|KS888|HM-WDS100-C6-O)/) { ############################ if($mTp eq "70" && $p =~ m/^(....)(..)(....)(....)(..)(..)(..)/) { @@ -651,220 +651,220 @@ sub CUL_HM_Parse($$) {############################## push @event, "isRaining:$ir"; push @event, "sunshine:$s"; push @event, "brightness:$b"; - } - else { + } + else { push @event, "unknown:$p"; } - } + } elsif($md eq "HM-CC-TC") { ################################################## my ($sType,$chn) = ($1,$2) if($p && $p =~ m/^(..)(..)/); if($mTp eq "70" && $p =~ m/^(....)(..)/) { # weather event - $chn = '01'; # fix definition + $chn = '01'; # fix definition my ( $t, $h) = (hex($1), hex($2));# temp is 15 bit signed $t = sprintf("%2.1f",($t & 0x3fff)/10*(($t & 0x4000)?-1:1)); - my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; - push @entities,CUL_HM_UpdtReadBulk($chnHash,1,"state:T: $t H: $h", - "measured-temp:$t", - "humidity:$h") - if ($chnHash); + my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; + push @entities,CUL_HM_UpdtReadBulk($chnHash,1,"state:T: $t H: $h", + "measured-temp:$t", + "humidity:$h") + if ($chnHash); push @event, "state:T: $t H: $h"; push @event, "measured-temp:$t"; push @event, "humidity:$h"; } elsif($mTp eq "58" && $p =~ m/^(..)(..)/) {# climate event - $chn = '02'; # fix definition + $chn = '02'; # fix definition my ( $d1, $vp) = # adjust_command[0..4] adj_data[0..250] ( $1, hex($2)); $vp = int($vp/2.56+0.5); # valve position in % - my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; - if($chnHash){ - push @entities,CUL_HM_UpdtReadSingle($chnHash,"state","$vp %",1); - if ($chnHash->{helper}{needUpdate}){ - if ($chnHash->{helper}{needUpdate} == 1){ - $chnHash->{helper}{needUpdate}++; - } - else{ - CUL_HM_qStateUpdatIfEnab(":".$chnHash->{NAME}); - delete $chnHash->{helper}{needUpdate}; - } - } - } + my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; + if($chnHash){ + push @entities,CUL_HM_UpdtReadSingle($chnHash,"state","$vp %",1); + if ($chnHash->{helper}{needUpdate}){ + if ($chnHash->{helper}{needUpdate} == 1){ + $chnHash->{helper}{needUpdate}++; + } + else{ + CUL_HM_qStateUpdatIfEnab(":".$chnHash->{NAME}); + delete $chnHash->{helper}{needUpdate}; + } + } + } push @event, "actuator:$vp %"; # Set the valve state too, without an extra trigger - push @entities,CUL_HM_UpdtReadBulk($dhash,1, - "state:set_$vp %", - "ValveDesired:$vp %") + push @entities,CUL_HM_UpdtReadBulk($dhash,1, + "state:set_$vp %", + "ValveDesired:$vp %") if($dhash); } elsif(($mTp eq '02' &&$sType eq '01')|| # ackStatus - ($mTp eq '10' &&$sType eq '06')){ # infoStatus - $chn = substr($p,2,2); - my $dTemp = hex(substr($p,4,2))/2; - $dTemp = ($dTemp < 6 )?'off': - ($dTemp >30 )?'on' :sprintf("%0.1f", $dTemp); - my $err = hex(substr($p,6,2)); - my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; - if($chnHash){ - my $chnName = $chnHash->{NAME}; - my $mode = ReadingsVal($chnName,"R-controlMode",""); - push @entities,CUL_HM_UpdtReadSingle($chnHash,"desired-temp",$dTemp,1); - CUL_HM_UpdtReadSingle($chnHash,"desired-temp-manu",$dTemp,1) if($mode =~ m /manual/ && $mTp eq '10'); -# readingsSingleUpdate($chnHash,"desired-temp-cent",$dTemp,1) if($mode =~ m /central/ && $mTp eq '02'); -# removed - shall not be changed automatically - change is only temporary + ($mTp eq '10' &&$sType eq '06')){ # infoStatus + $chn = substr($p,2,2); + my $dTemp = hex(substr($p,4,2))/2; + $dTemp = ($dTemp < 6 )?'off': + ($dTemp >30 )?'on' :sprintf("%0.1f", $dTemp); + my $err = hex(substr($p,6,2)); + my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; + if($chnHash){ + my $chnName = $chnHash->{NAME}; + my $mode = ReadingsVal($chnName,"R-controlMode",""); + push @entities,CUL_HM_UpdtReadSingle($chnHash,"desired-temp",$dTemp,1); + CUL_HM_UpdtReadSingle($chnHash,"desired-temp-manu",$dTemp,1) if($mode =~ m /manual/ && $mTp eq '10'); +# readingsSingleUpdate($chnHash,"desired-temp-cent",$dTemp,1) if($mode =~ m /central/ && $mTp eq '02'); +# removed - shall not be changed automatically - change is only temporary # CUL_HM_Set($chnHash,$chnName,"desired-temp",$dTemp) if($mode =~ m /central/ && $mTp eq '10'); $chnHash->{helper}{needUpdate} = 1 if($mode =~ m /central/ && $mTp eq '10'); } push @event, "desired-temp:" .$dTemp; - push @event, "battery:".($err&0x80?"low":"ok"); + push @event, "battery:".($err&0x80?"low":"ok"); } elsif($mTp eq "10" && # Config change report ($p =~ m/^0402000000000501/)) { # paramchanged L5 - my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; + my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; my $dTemp; - if($chnHash){ - my $chnName = $chnHash->{NAME}; - my $mode = ReadingsVal($chnName,"R-controlMode",""); + if($chnHash){ + my $chnName = $chnHash->{NAME}; + my $mode = ReadingsVal($chnName,"R-controlMode",""); $dTemp = ReadingsVal($chnName,"desired-temp","21.0"); - if (!$chnHash->{helper}{oldMode} || $chnHash->{helper}{oldMode} ne $mode){ - $dTemp = ReadingsVal($chnName,"desired-temp-manu",$dTemp)if ($mode =~ m /manual/); - $dTemp = ReadingsVal($chnName,"desired-temp-cent",$dTemp)if ($mode =~ m /central/); - $chnHash->{helper}{oldMode} = $mode; - } - push @entities,CUL_HM_UpdtReadSingle($chnHash,"desired-temp",$dTemp,1); + if (!$chnHash->{helper}{oldMode} || $chnHash->{helper}{oldMode} ne $mode){ + $dTemp = ReadingsVal($chnName,"desired-temp-manu",$dTemp)if ($mode =~ m /manual/); + $dTemp = ReadingsVal($chnName,"desired-temp-cent",$dTemp)if ($mode =~ m /central/); + $chnHash->{helper}{oldMode} = $mode; + } + push @entities,CUL_HM_UpdtReadSingle($chnHash,"desired-temp",$dTemp,1); } push @event, "desired-temp:" .$dTemp; - } + } elsif($mTp eq "01"){ # status reports if($p =~ m/^010809(..)0A(..)/) { # TC set valve for VD => post events to VD my ( $of, $vep) = (hex($1), hex($2)); push @event, "ValveErrorPosition_for_$dname: $vep %"; push @event, "ValveOffset_for_$dname: $of %"; - push @entities, - CUL_HM_UpdtReadBulk($dhash,1,'ValveErrorPosition:set_'.$vep.' %', - 'ValveOffset:set_'.$of.' %'); - } - elsif($p =~ m/^010[56]/){ # 'prepare to set' or 'end set' - push @event,""; # - } + push @entities, + CUL_HM_UpdtReadBulk($dhash,1,'ValveErrorPosition:set_'.$vep.' %', + 'ValveOffset:set_'.$of.' %'); + } + elsif($p =~ m/^010[56]/){ # 'prepare to set' or 'end set' + push @event,""; # + } } elsif($mTp eq "3F" && $id eq $dst) { # Timestamp request my $s2000 = sprintf("%02X", CUL_HM_secSince2000()); push @ack,$shash,"++803F$id${src}0204$s2000"; push @event, "time-request"; - } - } + } + } elsif($md eq "HM-CC-VD") { ################################################## if($mTp eq "02" && $p =~ m/^(..)(..)(..)(..)/) {#subtype+chn+value+err my ($chn,$vp, $err) = (hex($2),hex($3), hex($4)); - $chn = sprintf("%02X",$chn&0x3f); + $chn = sprintf("%02X",$chn&0x3f); $vp = int($vp)/2; # valve position in % - push @event, "ValvePosition:$vp %"; + push @event, "ValvePosition:$vp %"; push @event, "state:$vp %"; - $shash = $modules{CUL_HM}{defptr}{"$src$chn"} - if($modules{CUL_HM}{defptr}{"$src$chn"}); + $shash = $modules{CUL_HM}{defptr}{"$src$chn"} + if($modules{CUL_HM}{defptr}{"$src$chn"}); - my $stErr = ($err >>1) & 0x7; # Status-Byte Evaluation - push @event,"battery:".(($stErr == 4)?"critical":($err&0x80?"low":"ok")); - if (!$stErr){#remove both conditions + my $stErr = ($err >>1) & 0x7; # Status-Byte Evaluation + push @event,"battery:".(($stErr == 4)?"critical":($err&0x80?"low":"ok")); + if (!$stErr){#remove both conditions push @event, "motorErr:ok"; - } - else{ + } + else{ push @event, "motorErr:blocked" if($stErr == 1); push @event, "motorErr:loose" if($stErr == 2); push @event, "motorErr:adjusting range too small" if($stErr == 3); -# push @event, "battery:critical" if($stErr == 4); - } +# push @event, "battery:critical" if($stErr == 4); + } push @event, "motor:opening" if(($err&0x30) == 0x10); push @event, "motor:closing" if(($err&0x30) == 0x20); push @event, "motor:stop" if(($err&0x30) == 0x00); - - #VD hang detection - my $des = ReadingsVal($name, "ValveDesired", ""); - $des =~ s/ .*//; # remove unit - if ($des ne $vp && ($err&0x30) == 0x00){ - push @event, "operState:errorTargetNotMet"; - push @event, "operStateErrCnt:". - (ReadingsVal($name,"operStateErrCnt","0")+1); - } - else{ - push @event, "operState:".((($err&0x30) == 0x00)?"onTarget":"adjusting"); - } + + #VD hang detection + my $des = ReadingsVal($name, "ValveDesired", ""); + $des =~ s/ .*//; # remove unit + if ($des ne $vp && ($err&0x30) == 0x00){ + push @event, "operState:errorTargetNotMet"; + push @event, "operStateErrCnt:". + (ReadingsVal($name,"operStateErrCnt","0")+1); + } + else{ + push @event, "operState:".((($err&0x30) == 0x00)?"onTarget":"adjusting"); + } } - } + } elsif($md =~ m/HM-CC-RT-DN/) { ############################################## my %ctlTbl=( 0=>"auto", 1=>"manu", 2=>"party",3=>"boost"); if ($mTp eq "10" && $p =~ m/^0A(....)(..)(..)(..)/) {#info-level - my ($chn,$setTemp,$actTemp,$err,$bat,$vp,$ctrlMode) = - ("04",hex($1),hex($1),hex($2),hex($2),hex($3), hex($4)); - $setTemp =(($setTemp >>10) & 0x3f )/2; - $actTemp =(($actTemp ) & 0x3ff)/10; - $err = ($err >> 5) & 0x7 ; - $bat =(($bat ) & 0x1f)/10+1.5; - $vp = ($vp ) & 0x7f ; - my $uk0 = ($ctrlMode ) & 0x3f ;#unknown - $ctrlMode = ($ctrlMode >> 6) & 0x3 ; + my ($chn,$setTemp,$actTemp,$err,$bat,$vp,$ctrlMode) = + ("04",hex($1),hex($1),hex($2),hex($2),hex($3), hex($4)); + $setTemp =(($setTemp >>10) & 0x3f )/2; + $actTemp =(($actTemp ) & 0x3ff)/10; + $err = ($err >> 5) & 0x7 ; + $bat =(($bat ) & 0x1f)/10+1.5; + $vp = ($vp ) & 0x7f ; + my $uk0 = ($ctrlMode ) & 0x3f ;#unknown + $ctrlMode = ($ctrlMode >> 6) & 0x3 ; $actTemp = sprintf("%2.1f",$actTemp); - $setTemp = ($setTemp < 5 )?'off': - ($setTemp >30 )?'on' :$setTemp; - - my $dHash = $shash; - $shash = $modules{CUL_HM}{defptr}{"$src$chn"} - if($modules{CUL_HM}{defptr}{"$src$chn"}); + $setTemp = ($setTemp < 5 )?'off': + ($setTemp >30 )?'on' :$setTemp; + + my $dHash = $shash; + $shash = $modules{CUL_HM}{defptr}{"$src$chn"} + if($modules{CUL_HM}{defptr}{"$src$chn"}); my %errTbl=( 0=>"ok", 1=>"ValveTight", 2=>"adjustRangeTooLarge" - ,3=>"adjustRangeTooSmall" , 4=>"communicationERR" + ,3=>"adjustRangeTooSmall" , 4=>"communicationERR" ,5=>"unknown" , 6=>"lowBat" , 7=>"ValveErrorPosition" ); push @event, "motorErr:$errTbl{$err}"; push @event, "measured-temp:$actTemp"; push @event, "desired-temp:$setTemp"; - push @event, "ValvePosition:$vp %"; + push @event, "ValvePosition:$vp %"; push @event, "mode:$ctlTbl{$ctrlMode}"; push @event, "unknown0:$uk0"; push @event, "unknown1:".$2 if ($p =~ m/^0A(.10)(.*)/); push @event, "state:T: $actTemp desired: $setTemp valve: $vp %"; push @entities,CUL_HM_UpdtReadBulk($dHash,1 - ,"battery:".($err&0x80?"low":"ok") - ,"batteryLevel:$bat V" + ,"battery:".($err&0x80?"low":"ok") + ,"batteryLevel:$bat V" ,"measured-temp:$actTemp" # values to please HCS ,"desired-temp:$setTemp" - ,"actuator:$vp %" - ); + ,"actuator:$vp %" + ); } elsif($mTp eq "59" && $p =~ m/^(..)/) {#inform team about new value - my $setTemp = int(hex($1)/4)/2; - my $ctrlMode = hex($1)&0x3; + my $setTemp = int(hex($1)/4)/2; + my $ctrlMode = hex($1)&0x3; push @event, "desired-temp:$setTemp"; push @event, "mode:$ctlTbl{$ctrlMode}"; - my $tHash = $modules{CUL_HM}{defptr}{$dst."04"}; + my $tHash = $modules{CUL_HM}{defptr}{$dst."04"}; push @entities,CUL_HM_UpdtReadBulk($tHash,1 - ,"desired-temp:$setTemp" - ,"mode:$ctlTbl{$ctrlMode}" - ) - if ($tHash); - } + ,"desired-temp:$setTemp" + ,"mode:$ctlTbl{$ctrlMode}" + ) + if ($tHash); + } elsif($mTp eq "3F" && $id eq $dst) { # Timestamp request my $s2000 = sprintf("%02X", CUL_HM_secSince2000()); push @ack,$shash,"++803F$id${src}0204$s2000"; push @event, "time-request"; - } - } + } + } elsif($md =~ m/^(HM-Sen-Wa-Od|HM-CC-SCD)$/){ ################################ if (($mTp eq "02" && $p =~ m/^01/) || # handle Ack_Status - ($mTp eq "10" && $p =~ m/^06/) || #or Info_Status message here - ($mTp eq "41")) { - my $lvl = substr($p,4,2); - my $err = hex(substr($p,6,2)); - if ($lvlStr{md}{$md}){$lvl = $lvlStr{md}{$md}{$lvl}} - elsif ($lvlStr{st}{$st}){$lvl = $lvlStr{st}{$st}{$lvl} } - else {$lvl = hex($lvl)/2} + ($mTp eq "10" && $p =~ m/^06/) || #or Info_Status message here + ($mTp eq "41")) { + my $lvl = substr($p,4,2); + my $err = hex(substr($p,6,2)); + if ($lvlStr{md}{$md}){$lvl = $lvlStr{md}{$md}{$lvl}} + elsif ($lvlStr{st}{$st}){$lvl = $lvlStr{st}{$st}{$lvl} } + else {$lvl = hex($lvl)/2} - push @event, "level:$lvl %" if($md eq "HM-Sen-Wa-Od"); - push @event, "state:$lvl %"; - push @event, "battery:".($err&0x80?"low":"ok") if (defined $err); - } + push @event, "level:$lvl %" if($md eq "HM-Sen-Wa-Od"); + push @event, "state:$lvl %"; + push @event, "battery:".($err&0x80?"low":"ok") if (defined $err); + } } elsif($md eq "KFM-Sensor") { ################################################ if ($mTp eq "53"){ @@ -875,7 +875,7 @@ sub CUL_HM_Parse($$) {############################## push @event, "rawValue:$v"; my $nextSeq = (ReadingsVal($name,"Sequence","") %15)+1; push @event, "Sequence:$seq".($nextSeq ne $seq?"_seqMiss":""); - + my $r2r = AttrVal($name, "rawToReadable", undef); if($r2r) { my @r2r = split("[ :]", $r2r); @@ -893,257 +893,257 @@ sub CUL_HM_Parse($$) {############################## push @event, "state:$v"; } } - } - } + } + } elsif($st eq "THSensor") { ################################################## if ($mTp eq "70"){ - my $chn = 1; - $chn = 10 if ($md =~ m/^(WS550|WS888|HM-WDC7000)/);#todo use channel correct + my $chn = 1; + $chn = 10 if ($md =~ m/^(WS550|WS888|HM-WDC7000)/);#todo use channel correct my $t = hex(substr($p,0,4)); $t -= 32768 if($t > 1638.4); $t = sprintf("%0.1f", $t/10); my $statemsg = "state:T: $t"; push @event, "temperature:$t";#temp is always there - if ($md eq "HM-WDS30-OT2-SM"){ - $chn = 5; - my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; - push @entities,CUL_HM_UpdtReadBulk($chnHash,1,$statemsg, - "temperature:$t") - if ($chnHash); - } - elsif ($md !~ m/^(S550IA|HM-WDS30-T-O)$/){#skip temp-only sens + if ($md eq "HM-WDS30-OT2-SM"){ + $chn = 5; + my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; + push @entities,CUL_HM_UpdtReadBulk($chnHash,1,$statemsg, + "temperature:$t") + if ($chnHash); + } + elsif ($md !~ m/^(S550IA|HM-WDS30-T-O)$/){#skip temp-only sens my $h = hex(substr($p,4,2)); $statemsg .= " H: $h"; push @event, "humidity:$h"; - if ($md eq "HM-WDC7000"){#airpressure sensor + if ($md eq "HM-WDC7000"){#airpressure sensor my $ap = hex(substr($p,6,4)); $statemsg .= " AP: $ap"; push @event, "airpress:$ap"; - } - } + } + } push @event, $statemsg; - } + } elsif ($mTp eq "53"){ my ($mChn,@dat) = unpack 'A2(A6)*',$p; - foreach (@dat){ - my ($a,$d) = unpack 'A2A4',$_; - $d = hex($d); - $d -= 0x10000 if($d & 0xC000); - $d = sprintf("%0.1f",$d/10); - my $chId = sprintf("%02X",hex($a) & 0x3f); - my $chnHash = $modules{CUL_HM}{defptr}{$src.$chId}; - if ($chnHash){ - push @entities,CUL_HM_UpdtReadBulk($chnHash,1 - ,"state:T: $d" - ,"temperature:$d"); - } - else{ - push @event, "Chan_$chId:T: $d"; - } - } - } - } + foreach (@dat){ + my ($a,$d) = unpack 'A2A4',$_; + $d = hex($d); + $d -= 0x10000 if($d & 0xC000); + $d = sprintf("%0.1f",$d/10); + my $chId = sprintf("%02X",hex($a) & 0x3f); + my $chnHash = $modules{CUL_HM}{defptr}{$src.$chId}; + if ($chnHash){ + push @entities,CUL_HM_UpdtReadBulk($chnHash,1 + ,"state:T: $d" + ,"temperature:$d"); + } + else{ + push @event, "Chan_$chId:T: $d"; + } + } + } + } elsif($st eq "sensRain") {################################################### my $hHash = CUL_HM_id2Hash($src."02");# hash for heating - my $pon = 0;# power on if mNo == 0 and heating status plus second msg - # status or trigger from rain channel - my $devHash = $shash; + my $pon = 0;# power on if mNo == 0 and heating status plus second msg + # status or trigger from rain channel + my $devHash = $shash; if (($mTp eq "02" && $p =~ m/^01/) || #Ack_Status - ($mTp eq "10" && $p =~ m/^06/)) { #Info_Status + ($mTp eq "10" && $p =~ m/^06/)) { #Info_Status + + my ($subType,$chn,$val,$err) = ($1,hex($2),$3,hex($4)) + if($p =~ m/^(..)(..)(..)(..)/); + $chn = sprintf("%02X",$chn&0x3f); + my $chId = $src.$chn; + $shash = $modules{CUL_HM}{defptr}{$chId} + if($modules{CUL_HM}{defptr}{$chId}); - my ($subType,$chn,$val,$err) = ($1,hex($2),$3,hex($4)) - if($p =~ m/^(..)(..)(..)(..)/); - $chn = sprintf("%02X",$chn&0x3f); - my $chId = $src.$chn; - $shash = $modules{CUL_HM}{defptr}{$chId} - if($modules{CUL_HM}{defptr}{$chId}); - push @event, "timedOn:".(($err&0x40 && $chn eq "02")?"running":"off"); - - my $mdCh = $md.$chn; - if($lvlStr{mdCh}{$mdCh} && $lvlStr{mdCh}{$mdCh}{$val}){ - $val = $lvlStr{mdCh}{$mdCh}{$val}; + + my $mdCh = $md.$chn; + if($lvlStr{mdCh}{$mdCh} && $lvlStr{mdCh}{$mdCh}{$val}){ + $val = $lvlStr{mdCh}{$mdCh}{$val}; } - else{ - $val = hex($val)/2; - } - push @event, "state:$val"; + else{ + $val = hex($val)/2; + } + push @event, "state:$val"; - if ($val eq "rain"){#--- handle lastRain--- + if ($val eq "rain"){#--- handle lastRain--- $shash->{helper}{lastRain} = $tn; - } - elsif ($val eq "dry" && $shash->{helper}{lastRain}){ - CUL_HM_UpdtReadSingle($shash,'lastRain',$shash->{helper}{lastRain},0); + } + elsif ($val eq "dry" && $shash->{helper}{lastRain}){ + CUL_HM_UpdtReadSingle($shash,'lastRain',$shash->{helper}{lastRain},0); delete $shash->{helper}{lastRain}; - } + } - CUL_HM_UpdtReadSingle($shash,'.level',#store level invisible - ($val eq "off"?"0 %":"100 %"),0); - - if ($mNo eq "00" && $chn eq "02" && $val eq "on"){ - $hHash->{helper}{pOn} = 1; - } - elsif ($mNo eq "01" && $chn eq "01" && - $hHash->{helper}{pOn} && $hHash->{helper}{pOn} == 1){ - $pon = 1; - } - else{ - delete $hHash->{helper}{pOn}; - my $hHash = CUL_HM_id2Hash($src."02");# hash for heating - if ($chn eq "01" && - $hHash->{helper}{param} && $hHash->{helper}{param}{onAtRain}){ - CUL_HM_Set($hHash,$hHash->{NAME},$val eq "rain"?"on":"off"); - } - } - } - elsif ($mTp eq "41") { #eventonAtRain + CUL_HM_UpdtReadSingle($shash,'.level',#store level invisible + ($val eq "off"?"0 %":"100 %"),0); + + if ($mNo eq "00" && $chn eq "02" && $val eq "on"){ + $hHash->{helper}{pOn} = 1; + } + elsif ($mNo eq "01" && $chn eq "01" && + $hHash->{helper}{pOn} && $hHash->{helper}{pOn} == 1){ + $pon = 1; + } + else{ + delete $hHash->{helper}{pOn}; + my $hHash = CUL_HM_id2Hash($src."02");# hash for heating + if ($chn eq "01" && + $hHash->{helper}{param} && $hHash->{helper}{param}{onAtRain}){ + CUL_HM_Set($hHash,$hHash->{NAME},$val eq "rain"?"on":"off"); + } + } + } + elsif ($mTp eq "41") { #eventonAtRain my ($chn,$bno,$val) = unpack('(A2)*',$p); - my $mdCh = $md.$chn; - $chn = sprintf("%02X",hex($chn)&0x3f); - my $chId = $src.$chn; - $shash = $modules{CUL_HM}{defptr}{$chId} - if($modules{CUL_HM}{defptr}{$chId}); - push @event,"trigger:".hex($bno).":".$lvlStr{mdCh}{$mdCh}{$val}.$target; - if ($mNo eq "01" && $bno eq "01" && - $hHash->{helper}{pOn} && $hHash->{helper}{pOn} == 1){ - $pon = 1; - } - delete $shash->{helper}{pOn}; - } - if ($pon){# we have power ON, perform action - push @entities,CUL_HM_UpdtReadSingle($devHash,'powerOn',"-",1); - CUL_HM_Set($hHash,$hHash->{NAME},"off") - if ($hHash && $hHash->{helper}{param}{offAtPon}); - } - } + my $mdCh = $md.$chn; + $chn = sprintf("%02X",hex($chn)&0x3f); + my $chId = $src.$chn; + $shash = $modules{CUL_HM}{defptr}{$chId} + if($modules{CUL_HM}{defptr}{$chId}); + push @event,"trigger:".hex($bno).":".$lvlStr{mdCh}{$mdCh}{$val}.$target; + if ($mNo eq "01" && $bno eq "01" && + $hHash->{helper}{pOn} && $hHash->{helper}{pOn} == 1){ + $pon = 1; + } + delete $shash->{helper}{pOn}; + } + if ($pon){# we have power ON, perform action + push @entities,CUL_HM_UpdtReadSingle($devHash,'powerOn',"-",1); + CUL_HM_Set($hHash,$hHash->{NAME},"off") + if ($hHash && $hHash->{helper}{param}{offAtPon}); + } + } elsif($st =~ m /^(switch|dimmer|blindActuator)$/) {########################## if (($mTp eq "02" && $p =~ m/^01/) || # handle Ack_Status - ($mTp eq "10" && $p =~ m/^06/)) { # or Info_Status message here + ($mTp eq "10" && $p =~ m/^06/)) { # or Info_Status message here - my $rSUpdt = 0;# require status update - my ($subType,$chn,$val,$err) = ($1,hex($2),hex($3)/2,hex($4)) - if($p =~ m/^(..)(..)(..)(..)/); - $chn = sprintf("%02X",$chn&0x3f); - my $chId = $src.$chn; - $shash = $modules{CUL_HM}{defptr}{$chId} - if($modules{CUL_HM}{defptr}{$chId}); - my $physLvl; #store phys level if available - if( $p =~ m/^........(..)(..)$/ #message with physical level? - && $st eq "dimmer"){ - my $pl = hex($2)/2; - my $vDim = $shash->{helper}{vDim}; #shortcut - if ($vDim->{idPhy} && - CUL_HM_id2Hash($vDim->{idPhy})){ #has virt chan - RemoveInternalTimer("sUpdt:".$chId); - if ($mTp eq "10"){ #valid PhysLevel - foreach my $tmpKey ("idPhy","idV2","idV3",){#update all virtuals - my $vh = CUL_HM_id2Hash($vDim->{$tmpKey}) if ($vDim->{$tmpKey}); - next if (!$vh || $vDim->{$tmpKey} eq $chId); - my $vl = ReadingsVal($vh->{NAME},"level","???"); - my $vs = ($vl eq "100 %"?"on":($vl eq "0 %"?"off":"$vl")); - $vs = (($pl." %") ne $vl)?"chn:$vs phys:$pl %":$vs; - push @entities,CUL_HM_UpdtReadBulk($vh ,1,"state:$vs", - "phyLevel:$pl %"); - } - push @event,"phyLevel:$pl %"; #phys level - $physLvl = $pl." %"; - } - else{ #invalid PhysLevel - $rSUpdt = 1; - CUL_HM_stateUpdatDly($shash->{NAME},5); # update to get level - } - } - } - $physLvl = ReadingsVal($name,"phyLevel",$val." %") - if(!$physLvl); #not updated? use old or ignore + my $rSUpdt = 0;# require status update + my ($subType,$chn,$val,$err) = ($1,hex($2),hex($3)/2,hex($4)) + if($p =~ m/^(..)(..)(..)(..)/); + $chn = sprintf("%02X",$chn&0x3f); + my $chId = $src.$chn; + $shash = $modules{CUL_HM}{defptr}{$chId} + if($modules{CUL_HM}{defptr}{$chId}); + my $physLvl; #store phys level if available + if( $p =~ m/^........(..)(..)$/ #message with physical level? + && $st eq "dimmer"){ + my $pl = hex($2)/2; + my $vDim = $shash->{helper}{vDim}; #shortcut + if ($vDim->{idPhy} && + CUL_HM_id2Hash($vDim->{idPhy})){ #has virt chan + RemoveInternalTimer("sUpdt:".$chId); + if ($mTp eq "10"){ #valid PhysLevel + foreach my $tmpKey ("idPhy","idV2","idV3",){#update all virtuals + my $vh = CUL_HM_id2Hash($vDim->{$tmpKey}) if ($vDim->{$tmpKey}); + next if (!$vh || $vDim->{$tmpKey} eq $chId); + my $vl = ReadingsVal($vh->{NAME},"level","???"); + my $vs = ($vl eq "100 %"?"on":($vl eq "0 %"?"off":"$vl")); + $vs = (($pl." %") ne $vl)?"chn:$vs phys:$pl %":$vs; + push @entities,CUL_HM_UpdtReadBulk($vh ,1,"state:$vs", + "phyLevel:$pl %"); + } + push @event,"phyLevel:$pl %"; #phys level + $physLvl = $pl." %"; + } + else{ #invalid PhysLevel + $rSUpdt = 1; + CUL_HM_stateUpdatDly($shash->{NAME},5); # update to get level + } + } + } + $physLvl = ReadingsVal($name,"phyLevel",$val." %") + if(!$physLvl); #not updated? use old or ignore my $vs = ($val==100 ? "on":($val==0 ? "off":"$val %")); # user string... - push @event,"level:$val %"; - push @event,"pct:$val"; # duplicate to level - necessary for "slider" + push @event,"level:$val %"; + push @event,"pct:$val"; # duplicate to level - necessary for "slider" push @event,"deviceMsg:$vs$target" if($chn ne "00"); - push @event,"state:".(($physLvl ne $val." %")?"chn:$vs phys:$physLvl": - $vs); + push @event,"state:".(($physLvl ne $val." %")?"chn:$vs phys:$physLvl": + $vs); my $eventName = "unknown"; # different names for events $eventName = "switch" if($st eq "switch"); $eventName = "motor" if($st eq "blindActuator"); - $eventName = "dim" if($st eq "dimmer"); - my $action; #determine action + $eventName = "dim" if($st eq "dimmer"); + my $action; #determine action push @event, "timedOn:".(($err&0x40)?"running":"off"); - if ($st ne "switch"){ - my $dir = $err&0x30; + if ($st ne "switch"){ + my $dir = $err&0x30; if ($dir == 0x10){push @event, "$eventName:up:$vs" ;} elsif($dir == 0x20){push @event, "$eventName:down:$vs";} elsif($dir == 0x00){push @event, "$eventName:stop:$vs";} elsif($dir == 0x30){push @event, "$eventName:err:$vs";} - if (!$rSUpdt){#dont touch if necessary for dimmer - if($dir != 0x00){CUL_HM_stateUpdatDly($shash->{NAME},120);} - else {CUL_HM_unQEntity($shash->{NAME},"qReqStat");} - } - } - if ($st eq "dimmer"){ + if (!$rSUpdt){#dont touch if necessary for dimmer + if($dir != 0x00){CUL_HM_stateUpdatDly($shash->{NAME},120);} + else {CUL_HM_unQEntity($shash->{NAME},"qReqStat");} + } + } + if ($st eq "dimmer"){ push @event,"overload:".(($err&0x02)?"on":"off"); push @event,"overheat:".(($err&0x04)?"on":"off"); push @event,"reduced:" .(($err&0x08)?"on":"off"); - #hack for blind - other then behaved devices blind does not send - # a status info for chan 0 at power on - # chn3 (virtual chan) and not used up to now - # info from it is likely a power on! - push @event,"powerOn" if($chn eq "03"); - } - elsif ($md eq "HM-SEC-SFA-SM"){ # && $chn eq "00") - push @entities, - CUL_HM_UpdtReadBulk(CUL_HM_getDeviceHash($shash),1, - "powerError:" .(($err&0x02) ? "on":"off"), - "sabotageError:".(($err&0x04) ? "on":"off"), - "battery:".(($err&0x08)?"critical":($err&0x80?"low":"ok"))); - } - elsif ($md eq "HM-LC-SW1-BA-PCB"){ - push @event,"battery:" . (($err&0x80) ? "low" : "ok" ); - } + #hack for blind - other then behaved devices blind does not send + # a status info for chan 0 at power on + # chn3 (virtual chan) and not used up to now + # info from it is likely a power on! + push @event,"powerOn" if($chn eq "03"); + } + elsif ($md eq "HM-SEC-SFA-SM"){ # && $chn eq "00") + push @entities, + CUL_HM_UpdtReadBulk(CUL_HM_getDeviceHash($shash),1, + "powerError:" .(($err&0x02) ? "on":"off"), + "sabotageError:".(($err&0x04) ? "on":"off"), + "battery:".(($err&0x08)?"critical":($err&0x80?"low":"ok"))); + } + elsif ($md eq "HM-LC-SW1-BA-PCB"){ + push @event,"battery:" . (($err&0x80) ? "low" : "ok" ); + } } - } + } elsif($st =~ m /^(remote|pushButton|swi)$/) { ############################### if($mTp =~ m/^4./ && $p =~ m/^(..)(..)/) { my ($chn, $bno) = (hex($1), hex($2));# button number/event count - my $buttonID = $chn&0x3f;# only 6 bit are valid - my $btnName; - my $state = ""; - my $chnHash = $modules{CUL_HM}{defptr}{$src.sprintf("%02X",$buttonID)}; - - if ($chnHash){# use userdefined name - ignore this irritating on-off naming - $btnName = $chnHash->{NAME}; - } - else{# Button not defined, use default naming - $chnHash = $shash; - if ($st eq "swi"){#maintain history for event naming - $btnName = "Btn$chn"; - } - else{ - my $btn = int((($chn&0x3f)+1)/2); - $btnName = "Btn$btn"; - $state = ($chn&1 ? "off" : "on") - } - } + my $buttonID = $chn&0x3f;# only 6 bit are valid + my $btnName; + my $state = ""; + my $chnHash = $modules{CUL_HM}{defptr}{$src.sprintf("%02X",$buttonID)}; + + if ($chnHash){# use userdefined name - ignore this irritating on-off naming + $btnName = $chnHash->{NAME}; + } + else{# Button not defined, use default naming + $chnHash = $shash; + if ($st eq "swi"){#maintain history for event naming + $btnName = "Btn$chn"; + } + else{ + my $btn = int((($chn&0x3f)+1)/2); + $btnName = "Btn$btn"; + $state = ($chn&1 ? "off" : "on") + } + } my $trigType; if($chn & 0x40){ - if(!$shash->{BNO} || $shash->{BNO} ne $bno){#bno = event counter - $shash->{BNO}=$bno; - $shash->{BNOCNT}=0; # message counter reest - } - $shash->{BNOCNT}+=1; + if(!$shash->{BNO} || $shash->{BNO} ne $bno){#bno = event counter + $shash->{BNO}=$bno; + $shash->{BNOCNT}=0; # message counter reest + } + $shash->{BNOCNT}+=1; $state .= "Long" .($mFlg eq "A0" ? "Release" : ""). - " ".$shash->{BNOCNT}."-".$mFlg.$mTp."-"; - $trigType = "Long"; + " ".$shash->{BNOCNT}."-".$mFlg.$mTp."-"; + $trigType = "Long"; } - else{ + else{ $state .= ($st eq "swi")?"toggle":"Short";#swi only support toggle - $trigType = "Short"; + $trigType = "Short"; } - $shash->{helper}{addVal} = $chn; #store to handle changesFread - push @entities,CUL_HM_UpdtReadBulk($chnHash,1, - ,"state:".$state.$target - ,"trigger:".$trigType."_".$bno); + $shash->{helper}{addVal} = $chn; #store to handle changesFread + push @entities,CUL_HM_UpdtReadBulk($chnHash,1, + ,"state:".$state.$target + ,"trigger:".$trigType."_".$bno); push @event,"battery:". (($chn&0x80)?"low":"ok"); push @event,"state:$btnName $state$target"; } @@ -1157,275 +1157,275 @@ sub CUL_HM_Parse($$) {############################## push @event, "battery:". (($err&0x80)?"low" :"ok" ); my $flag = ($err>>4) &0x7; push @event, "flags:". (($flag)?"none" :$flag ); - } + } } elsif($st eq "virtual"){ #################################################### - # possibly add code to count all acks that are paired. + # possibly add code to count all acks that are paired. if($mTp eq "02") {# this must be a reflection from what we sent, ignore push @event, ""; - } + } } elsif($st eq "outputUnit"){ ################################################# if($mTp eq "40" && @mI == 2){ my ($button, $bno) = (hex($mI[0]), hex($mI[1])); if(!(exists($shash->{BNO})) || $shash->{BNO} ne $bno){ $shash->{BNO}=$bno; - $shash->{BNOCNT}=1; + $shash->{BNOCNT}=1; } - else{ + else{ $shash->{BNOCNT}+=1; } my $btn = int($button&0x3f); push @event, "state:Btn$btn on$target"; } - elsif(($mTp eq "02" && $mI[0] eq "01") || # handle Ack_Status - ($mTp eq "10" && $mI[0] eq "06")){ # or Info_Status message + elsif(($mTp eq "02" && $mI[0] eq "01") || # handle Ack_Status + ($mTp eq "10" && $mI[0] eq "06")){ # or Info_Status message my ($msgChn,$msgState) = ((hex($mI[1])&0x1f),$mI[2]) if (@mI > 2); - my $chnHash = $modules{CUL_HM}{defptr}{$src.sprintf("%02X",$msgChn)}; - if ($md eq "HM-OU-LED16") { - #special: all LEDs map to device state + my $chnHash = $modules{CUL_HM}{defptr}{$src.sprintf("%02X",$msgChn)}; + if ($md eq "HM-OU-LED16") { + #special: all LEDs map to device state my $devState = ReadingsVal($name,"color","00000000"); - if($parse eq "powerOn"){# reset LEDs after power on - CUL_HM_PushCmdStack($shash,'++A011'.$id.$src."8100".$devState); - CUL_HM_ProcessCmdStack($shash); - # no event necessary, all the same as before - } - else {# just update datafields in storage + if($parse eq "powerOn"){# reset LEDs after power on + CUL_HM_PushCmdStack($shash,'++A011'.$id.$src."8100".$devState); + CUL_HM_ProcessCmdStack($shash); + # no event necessary, all the same as before + } + else {# just update datafields in storage if (@mI > 8){#status for all channel included - # open to decode byte $mI[4] - related to backlight? seen 20 and 21 - my $lStat = join("",@mI[5..8]); # all LED status in one long - my %colTbl=("00"=>"off","01"=>"red","10"=>"green","11"=>"orange"); - my @leds = reverse(unpack('(A2)*',sprintf("%032b",hex($lStat)))); - $_ = $colTbl{$_} foreach (@leds); - for(my $cCnt = 0;$cCnt<16;$cCnt++){# go for all channels - my $cH = $modules{CUL_HM}{defptr}{$src.sprintf("%02X",$cCnt+1)}; - next if (!$cH); - if (ReadingsVal($cH->{NAME},"state","") ne $leds[$cCnt]) { + # open to decode byte $mI[4] - related to backlight? seen 20 and 21 + my $lStat = join("",@mI[5..8]); # all LED status in one long + my %colTbl=("00"=>"off","01"=>"red","10"=>"green","11"=>"orange"); + my @leds = reverse(unpack('(A2)*',sprintf("%032b",hex($lStat)))); + $_ = $colTbl{$_} foreach (@leds); + for(my $cCnt = 0;$cCnt<16;$cCnt++){# go for all channels + my $cH = $modules{CUL_HM}{defptr}{$src.sprintf("%02X",$cCnt+1)}; + next if (!$cH); + if (ReadingsVal($cH->{NAME},"state","") ne $leds[$cCnt]) { push @entities,CUL_HM_UpdtReadBulk($cH,1,"color:".$leds[$cCnt], - "state:".$leds[$cCnt]); - } - } - push @entities,CUL_HM_UpdtReadBulk($shash,1,"color:".$lStat, - "state:".$lStat); - } - else{# branch can be removed if message is always that long - my $bitLoc = ($msgChn-1)*2;#calculate bit location - my $mask = 3<<$bitLoc; - my $value = sprintf("%08X",(hex($devState) &~$mask)|($msgState<<$bitLoc)); - push @entities,CUL_HM_UpdtReadBulk($shash,1,"color:".$value, - "state:".$value); - if ($chnHash){ - $shash = $chnHash; - my %colorTable=("00"=>"off","01"=>"red","02"=>"green","03"=>"orange"); - my $actColor = $colorTable{$msgState}; - $actColor = "unknown" if(!$actColor); - push @event, "color:$actColor"; + "state:".$leds[$cCnt]); + } + } + push @entities,CUL_HM_UpdtReadBulk($shash,1,"color:".$lStat, + "state:".$lStat); + } + else{# branch can be removed if message is always that long + my $bitLoc = ($msgChn-1)*2;#calculate bit location + my $mask = 3<<$bitLoc; + my $value = sprintf("%08X",(hex($devState) &~$mask)|($msgState<<$bitLoc)); + push @entities,CUL_HM_UpdtReadBulk($shash,1,"color:".$value, + "state:".$value); + if ($chnHash){ + $shash = $chnHash; + my %colorTable=("00"=>"off","01"=>"red","02"=>"green","03"=>"orange"); + my $actColor = $colorTable{$msgState}; + $actColor = "unknown" if(!$actColor); + push @event, "color:$actColor"; push @event, "state:$actColor"; - } - } - } - } - elsif ($md eq "HM-OU-CFM-PL"){ - if ($chnHash){ - $shash = $chnHash; - my $val = hex($mI[2])/2; + } + } + } + } + elsif ($md eq "HM-OU-CFM-PL"){ + if ($chnHash){ + $shash = $chnHash; + my $val = hex($mI[2])/2; $val = ($val == 100 ? "on" : ($val == 0 ? "off" : "$val %")); - push @event, "state:$val"; - } - } - } - } + push @event, "state:$val"; + } + } + } + } elsif($st eq "motionDetector") { ############################################ # Code with help of Bassem my $state; if(($mTp eq "10" ||$mTp eq "02") && $p =~ m/^0601(..)(..)/) { - my $err; + my $err; ($state, $err) = ($1, hex($2)); - my $bright = hex($state); + my $bright = hex($state); push @event, "brightness:".$bright; - if ($md eq "HM-Sec-MDIR"){ - push @event, "sabotageError:".(($err&0x0E)?"on":"off"); - } - else{ - push @event, "cover:". (($err&0x0E)?"open" :"closed"); - } - push @event, "battery:". (($err&0x80)?"low" :"ok" ); + if ($md eq "HM-Sec-MDIR"){ + push @event, "sabotageError:".(($err&0x0E)?"on":"off"); + } + else{ + push @event, "cover:". (($err&0x0E)?"open" :"closed"); + } + push @event, "battery:". (($err&0x80)?"low" :"ok" ); } elsif($mTp eq "41" && $p =~ m/^01(..)(..)(..)/) {#01 is channel - my($cnt,$bright,$nextTr); - ($cnt,$state,$nextTr) = (hex($1),$2,(hex($3)>>4)); - $bright = hex($state); - my @nextVal = ("0x0","0x1","0x2","0x3","15" ,"30" ,"60" ,"120", - "240","0x9","0xa","0xb","0xc","0xd","0xe","0xf"); + my($cnt,$bright,$nextTr); + ($cnt,$state,$nextTr) = (hex($1),$2,(hex($3)>>4)); + $bright = hex($state); + my @nextVal = ("0x0","0x1","0x2","0x3","15" ,"30" ,"60" ,"120", + "240","0x9","0xa","0xb","0xc","0xd","0xe","0xf"); push @event, "state:motion"; push @event, "motion:on$target"; #added peterp push @event, "motionCount:".$cnt."_next:".$nextTr."-".$nextVal[$nextTr]; push @event, "brightness:".$bright; } elsif($mTp eq "70" && $p =~ m/^7F(..)(.*)/) { - my($d1, $d2) = ($1, $2); + my($d1, $d2) = ($1, $2); push @event, 'devState_raw'.$d1.':'.$d2; } - - if($id eq $dst && hex($mFlg)&0x20 && $state){ - push @ack,$shash,$mNo."8002".$id.$src."0101${state}00"; - } - } - elsif($st eq "smokeDetector") { ############################################# - #Info Level: mTp=0x10 p(..)(..)(..) subtype=06, channel, state (1 byte) - #Event: mTp=0x41 p(..)(..)(..) channel , unknown, state (1 byte) - if ($mTp eq "10" && $p =~ m/^06..(..)/) { - my $state = hex($1); + if($id eq $dst && hex($mFlg)&0x20 && $state){ + push @ack,$shash,$mNo."8002".$id.$src."0101${state}00"; + } + } + elsif($st eq "smokeDetector") { ############################################# + #Info Level: mTp=0x10 p(..)(..)(..) subtype=06, channel, state (1 byte) + #Event: mTp=0x41 p(..)(..)(..) channel , unknown, state (1 byte) + + if ($mTp eq "10" && $p =~ m/^06..(..)/) { + my $state = hex($1); push @event, "battery:". (($state&0x04)?"low" :"ok" ); push @event, "state:alive"; - } + } elsif ($mTp eq "40"){ #autonomous event - if($dhash){ # the source is in dst - my ($state,$trgCnt) = (hex(substr($p,0,2)),hex(substr($p,2,2))); - push @entities,CUL_HM_UpdtReadSingle($dhash,'test',"from $dname:$state",1) - if (!($state & 1)); - push @entities,CUL_HM_UpdtReadSingle($dhash,'battery',(($state & 0x04)?"low":"ok"),1) - if($state&0x80); - } + if($dhash){ # the source is in dst + my ($state,$trgCnt) = (hex(substr($p,0,2)),hex(substr($p,2,2))); + push @entities,CUL_HM_UpdtReadSingle($dhash,'test',"from $dname:$state",1) + if (!($state & 1)); + push @entities,CUL_HM_UpdtReadSingle($dhash,'battery',(($state & 0x04)?"low":"ok"),1) + if($state&0x80); + } push @event, ""; } elsif ($mTp eq "41"){ #Alarm detected - #C8: Smoke Alarm - #C7: tone off - #01: no alarm - my ($No,$state) = (substr($p,2,2),substr($p,4,2)); - if(($dhash && $dname ne $name) && # update source(ID reported in $dst) - (!$dhash->{helper}{alarmNo} || $dhash->{helper}{alarmNo} ne $No)){ - $dhash->{helper}{alarmNo} = $No; - push @entities, - CUL_HM_UpdtReadBulk($dhash,1, - 'state:'.(($state eq "01")?"off":"smoke-Alarm_".$No), - "eventNo:".$No - ); - } - # - - - - - - now handle the team - - - - - - - my @alarmArry; - @alarmArry = split(",",$shash->{helper}{alarmList}) - if ($shash->{helper}{alarmList}); - if ($state eq "01") { # clear alarm for sensor - @alarmArry = grep !/$dst/,@alarmArry; - } - else{ # add alarm for Sensor - @alarmArry = CUL_HM_noDup(@alarmArry,$dst); - } - my $alarmList; # make alarm ID list readable - foreach(@alarmArry){ - $alarmList .= CUL_HM_id2Name($_)."," if ($_); - } - $shash->{helper}{alarmList} = join",",@alarmArry; + #C8: Smoke Alarm + #C7: tone off + #01: no alarm + my ($No,$state) = (substr($p,2,2),substr($p,4,2)); + if(($dhash && $dname ne $name) && # update source(ID reported in $dst) + (!$dhash->{helper}{alarmNo} || $dhash->{helper}{alarmNo} ne $No)){ + $dhash->{helper}{alarmNo} = $No; + push @entities, + CUL_HM_UpdtReadBulk($dhash,1, + 'state:'.(($state eq "01")?"off":"smoke-Alarm_".$No), + "eventNo:".$No + ); + } + # - - - - - - now handle the team - - - - - - + my @alarmArry; + @alarmArry = split(",",$shash->{helper}{alarmList}) + if ($shash->{helper}{alarmList}); + if ($state eq "01") { # clear alarm for sensor + @alarmArry = grep !/$dst/,@alarmArry; + } + else{ # add alarm for Sensor + @alarmArry = CUL_HM_noDup(@alarmArry,$dst); + } + my $alarmList; # make alarm ID list readable + foreach(@alarmArry){ + $alarmList .= CUL_HM_id2Name($_)."," if ($_); + } + $shash->{helper}{alarmList} = join",",@alarmArry; push @event,"state:" .($alarmList?"smoke-Alarm":"off" ); push @event,"smoke_detect:" .($alarmList?$alarmList :"none"); - #--- check out teamstatus, members might be shy --- - my $peerList = $shash->{peerList}?$shash->{peerList}:""; - foreach my $pNm (split(",",$peerList)){ - CUL_HM_qStateUpdatIfEnab($pNm,1)if ($pNm); - } - } + #--- check out teamstatus, members might be shy --- + my $peerList = $shash->{peerList}?$shash->{peerList}:""; + foreach my $pNm (split(",",$peerList)){ + CUL_HM_qStateUpdatIfEnab($pNm,1)if ($pNm); + } + } elsif ($mTp eq "01"){ #Configs - my $sType = substr($p,0,2); - if($sType eq "01"){#add peer to group - push @event,"SDteam:add_".$dname; - } - elsif($sType eq "02"){# remove from group - push @event,"SDteam:remove_".$dname; - } - elsif($sType eq "05"){# set param List 3 and 4 - push @event,""; - } - } - else{ + my $sType = substr($p,0,2); + if($sType eq "01"){#add peer to group + push @event,"SDteam:add_".$dname; + } + elsif($sType eq "02"){# remove from group + push @event,"SDteam:remove_".$dname; + } + elsif($sType eq "05"){# set param List 3 and 4 + push @event,""; + } + } + else{ push @event, "SDunknownMsg:$p" if(!@event); - } - - if($id eq $dst && (hex($mFlg)&0x20)){ # Send Ack/Nack - push @ack,$shash,$mNo."8002".$id.$src.($mFlg.$mTp eq "A001" ? "80":"00"); - } - } - elsif($st eq "threeStateSensor") { ########################################## - #Event: mTp=0x41 p(..)(..)(..) channel , unknown, state - #Info Level: mTp=0x10 p(..)(..)(..)(..) subty=06, chn, state,err (3bit) - #AckStatus: mTp=0x02 p(..)(..)(..)(..) subty=01, chn, state,err (3bit) - my ($chn,$state,$err,$cnt); #define locals - if(($mTp eq "10" && $p =~ m/^06/) || - ($mTp eq "02" && $p =~ m/^01/)) { - $p =~ m/^..(..)(..)(..)?$/; - ($chn,$state,$err) = (hex($1), $2, hex($3)); - $chn = sprintf("%02X",$chn&0x3f); - $shash = $modules{CUL_HM}{defptr}{"$src$chn"} - if($modules{CUL_HM}{defptr}{"$src$chn"}); - push @event, "alive:yes"; - push @event, "battery:". (($err&0x80)?"low" :"ok" ); - if ( $md eq "HM-SEC-SC" || - $md eq "HM-Sec-RHS"){push @event, "sabotageError:".(($err&0x0E)?"on":"off"); - }elsif($md ne "HM-SEC-WDS"){push @event, "cover:" .(($err&0x0E)?"open" :"closed"); - } - } - elsif($mTp eq "41"){ - ($chn,$cnt,$state)=(hex($1),$2,$3) if($p =~ m/^(..)(..)(..)/); - $chn = sprintf("%02X",$chn&0x3f); - $shash = $modules{CUL_HM}{defptr}{"$src$chn"} - if($modules{CUL_HM}{defptr}{"$src$chn"}); - } - if (defined($state)){# if state was detected post events - my $txt; - if ($lvlStr{md}{$md}){$txt = $lvlStr{md}{$md}{$state}} - elsif ($lvlStr{st}{$st}){$txt = $lvlStr{st}{$st}{$state}} - else {$txt = "unknown:$state"} + } - push @event, "state:$txt"; - push @event, "contact:$txt$target"; + if($id eq $dst && (hex($mFlg)&0x20)){ # Send Ack/Nack + push @ack,$shash,$mNo."8002".$id.$src.($mFlg.$mTp eq "A001" ? "80":"00"); + } + } + elsif($st eq "threeStateSensor") { ########################################## + #Event: mTp=0x41 p(..)(..)(..) channel , unknown, state + #Info Level: mTp=0x10 p(..)(..)(..)(..) subty=06, chn, state,err (3bit) + #AckStatus: mTp=0x02 p(..)(..)(..)(..) subty=01, chn, state,err (3bit) + my ($chn,$state,$err,$cnt); #define locals + if(($mTp eq "10" && $p =~ m/^06/) || + ($mTp eq "02" && $p =~ m/^01/)) { + $p =~ m/^..(..)(..)(..)?$/; + ($chn,$state,$err) = (hex($1), $2, hex($3)); + $chn = sprintf("%02X",$chn&0x3f); + $shash = $modules{CUL_HM}{defptr}{"$src$chn"} + if($modules{CUL_HM}{defptr}{"$src$chn"}); + push @event, "alive:yes"; + push @event, "battery:". (($err&0x80)?"low" :"ok" ); + if ( $md eq "HM-SEC-SC" || + $md eq "HM-Sec-RHS"){push @event, "sabotageError:".(($err&0x0E)?"on":"off"); + }elsif($md ne "HM-SEC-WDS"){push @event, "cover:" .(($err&0x0E)?"open" :"closed"); + } + } + elsif($mTp eq "41"){ + ($chn,$cnt,$state)=(hex($1),$2,$3) if($p =~ m/^(..)(..)(..)/); + $chn = sprintf("%02X",$chn&0x3f); + $shash = $modules{CUL_HM}{defptr}{"$src$chn"} + if($modules{CUL_HM}{defptr}{"$src$chn"}); + } + if (defined($state)){# if state was detected post events + my $txt; + if ($lvlStr{md}{$md}){$txt = $lvlStr{md}{$md}{$state}} + elsif ($lvlStr{st}{$st}){$txt = $lvlStr{st}{$st}{$state}} + else {$txt = "unknown:$state"} + + push @event, "state:$txt"; + push @event, "contact:$txt$target"; } else{push @event, "3SSunknownMsg:$p" if(!@event);} - } + } elsif($st eq "winMatic") { ################################################# my($sType,$chn,$lvl,$stat) = ($1,$2,$3,$4) if ($p =~ m/^(..)(..)(..)(..)/); if(($mTp eq "10" && $sType eq "06") || - ($mTp eq "02" && $sType eq "01")){ - $shash = $modules{CUL_HM}{defptr}{"$src$chn"} - if($modules{CUL_HM}{defptr}{"$src$chn"}); - # stateflag meaning unknown - push @event, "state:".(($lvl eq "FF")?"locked":((hex($lvl)/2)." %")); - if ($chn eq "01"){ - my %err = (0=>"no",1=>"TurnError",2=>"TiltError"); - my %dir = (0=>"no",1=>"up",2=>"down",3=>"undefined"); - push @event, "motorError:".$err{(hex($stat)>>1)&0x02}; - push @event, "direction:".$dir{(hex($stat)>>4)&0x02}; - } - else{ #should be akku - my %statF = (0=>"trickleCharge",1=>"charge",2=>"dischange",3=>"unknown"); - push @event, "charge:".$statF{(hex($stat)>>4)&0x02}; - } - } - if ($p =~ m/^0287(..)89(..)8B(..)/) { - my ($air, undef, $course) = ($1, $2, $3); + ($mTp eq "02" && $sType eq "01")){ + $shash = $modules{CUL_HM}{defptr}{"$src$chn"} + if($modules{CUL_HM}{defptr}{"$src$chn"}); + # stateflag meaning unknown + push @event, "state:".(($lvl eq "FF")?"locked":((hex($lvl)/2)." %")); + if ($chn eq "01"){ + my %err = (0=>"no",1=>"TurnError",2=>"TiltError"); + my %dir = (0=>"no",1=>"up",2=>"down",3=>"undefined"); + push @event, "motorError:".$err{(hex($stat)>>1)&0x02}; + push @event, "direction:".$dir{(hex($stat)>>4)&0x02}; + } + else{ #should be akku + my %statF = (0=>"trickleCharge",1=>"charge",2=>"dischange",3=>"unknown"); + push @event, "charge:".$statF{(hex($stat)>>4)&0x02}; + } + } + if ($p =~ m/^0287(..)89(..)8B(..)/) { + my ($air, undef, $course) = ($1, $2, $3); push @event, "airing:".($air eq "FF" ? "inactiv" : CUL_HM_decodeTime8($air)); push @event, "course:".($course eq "FF" ? "tilt" : "close"); - } - elsif($p =~ m/^0201(..)03(..)04(..)05(..)07(..)09(..)0B(..)0D(..)/) { + } + elsif($p =~ m/^0201(..)03(..)04(..)05(..)07(..)09(..)0B(..)0D(..)/) { my ($flg1, $flg2, $flg3, $flg4, $flg5, $flg6, $flg7, $flg8) = ($1, $2, $3, $4, $5, $6, $7, $8); push @event, "airing:".($flg5 eq "FF" ? "inactiv" : CUL_HM_decodeTime8($flg5)); push @event, "contact:tesed"; - } + } } elsif($st eq "keyMatic") { ################################################# - #Info Level: mTp=0x10 p(..)(..)(..)(..) subty=06, chn, state,err (3bit) - #AckStatus: mTp=0x02 p(..)(..)(..)(..) subty=01, chn, state,err (3bit) + #Info Level: mTp=0x10 p(..)(..)(..)(..) subty=06, chn, state,err (3bit) + #AckStatus: mTp=0x02 p(..)(..)(..)(..) subty=01, chn, state,err (3bit) if(($mTp eq "10" && $p =~ m/^06/) || - ($mTp eq "02" && $p =~ m/^01/)) { - $p =~ m/^..(..)(..)(..)/; + ($mTp eq "02" && $p =~ m/^01/)) { + $p =~ m/^..(..)(..)(..)/; my ($chn,$val, $err) = ($1,hex($2), hex($3)); - $shash = $modules{CUL_HM}{defptr}{"$src$chn"} - if($modules{CUL_HM}{defptr}{"$src$chn"}); + $shash = $modules{CUL_HM}{defptr}{"$src$chn"} + if($modules{CUL_HM}{defptr}{"$src$chn"}); - my $stErr = ($err >>1) & 0x7; + my $stErr = ($err >>1) & 0x7; my $error = 'unknown_'.$stErr; $error = 'motor aborted' if ($stErr == 2); $error = 'clutch failure' if ($stErr == 1); @@ -1433,15 +1433,15 @@ sub CUL_HM_Parse($$) {############################## my %dir = (0=>"none",1=>"up",2=>"down",3=>"undef"); push @event, "unknown:40" if($err&0x40); - push @event, "battery:" .(($err&0x80) ? "low":"ok"); + push @event, "battery:" .(($err&0x80) ? "low":"ok"); push @event, "uncertain:" .(($err&0x30) ? "yes":"no"); push @event, "direction:" .$dir{($err>>4)&3}; push @event, "error:" . ($error); my $state = ($err & 0x30) ? " (uncertain)" : ""; - push @event, "lock:" . (($val == 1) ? "unlocked" : "locked"); - push @event, "state:" . (($val == 1) ? "unlocked" : "locked") . $state; + push @event, "lock:" . (($val == 1) ? "unlocked" : "locked"); + push @event, "state:" . (($val == 1) ? "unlocked" : "locked") . $state; } - } + } else{######################################################################## ; # no one wants the message } @@ -1451,83 +1451,83 @@ sub CUL_HM_Parse($$) {############################## if(AttrVal($dname, "subType", "none") eq "virtual"){# see if need for answer if($mTp =~ m/^4/ && @mI > 1) { #Push Button event my ($recChn,$trigNo) = (hex($mI[0]),hex($mI[1]));# button number/event count - my $longPress = ($recChn & 0x40)?"long":"short"; - my $recId = $src.sprintf("%02X",($recChn&0x3f)); - foreach my $dChId (CUL_HM_getAssChnIds($dname)){# need to check all chan - next if (!$modules{CUL_HM}{defptr}{$dChId}); - my $dChNo = substr($dChId,6,2); - my $dChName = CUL_HM_id2Name($dChId); + my $longPress = ($recChn & 0x40)?"long":"short"; + my $recId = $src.sprintf("%02X",($recChn&0x3f)); + foreach my $dChId (CUL_HM_getAssChnIds($dname)){# need to check all chan + next if (!$modules{CUL_HM}{defptr}{$dChId}); + my $dChNo = substr($dChId,6,2); + my $dChName = CUL_HM_id2Name($dChId); - if(($attr{$dChName}{peerIDs}?$attr{$dChName}{peerIDs}:"") =~m/$recId/){ - my $dChHash = $defs{$dChName}; - $dChHash->{helper}{trgLgRpt} = 0 - if (!defined($dChHash->{helper}{trgLgRpt})); - $dChHash->{helper}{trgLgRpt} +=1; - my $trgLgRpt = $dChHash->{helper}{trgLgRpt}; - - my $state = ReadingsVal($dChName,"virtActState","OFF"); - my $tNoOld = ReadingsVal($dChName,"virtActTrigNo","0"); - $state = ($state eq "OFF")?"ON":"OFF" if ($trigNo ne $tNoOld); - if (hex($mFlg)&0x20){ - $longPress .= "_Release"; - $dChHash->{helper}{trgLgRpt}=0; + if(($attr{$dChName}{peerIDs}?$attr{$dChName}{peerIDs}:"") =~m/$recId/){ + my $dChHash = $defs{$dChName}; + $dChHash->{helper}{trgLgRpt} = 0 + if (!defined($dChHash->{helper}{trgLgRpt})); + $dChHash->{helper}{trgLgRpt} +=1; + my $trgLgRpt = $dChHash->{helper}{trgLgRpt}; + + my $state = ReadingsVal($dChName,"virtActState","OFF"); + my $tNoOld = ReadingsVal($dChName,"virtActTrigNo","0"); + $state = ($state eq "OFF")?"ON":"OFF" if ($trigNo ne $tNoOld); + if (hex($mFlg)&0x20){ + $longPress .= "_Release"; + $dChHash->{helper}{trgLgRpt}=0; push @ack,$dhash,$mNo."8002".$dst.$src.'01'.$dChNo. - (($state eq "ON")?"C8":"00")."00"; - } - push @entities, - CUL_HM_UpdtReadBulk($dChHash,1,"state:".$state, - "virtActState:".$state, - "virtActTrigger:".CUL_HM_id2Name($recId), - "virtActTrigType:".$longPress, - "virtActTrigRpt:".$trgLgRpt, - "virtActTrigNo:".$trigNo ); - } - } - } - elsif($mTp eq "58" && $p =~ m/^(..)(..)/) {# climate event + (($state eq "ON")?"C8":"00")."00"; + } + push @entities, + CUL_HM_UpdtReadBulk($dChHash,1,"state:".$state, + "virtActState:".$state, + "virtActTrigger:".CUL_HM_id2Name($recId), + "virtActTrigType:".$longPress, + "virtActTrigRpt:".$trgLgRpt, + "virtActTrigNo:".$trigNo); + } + } + } + elsif($mTp eq "58" && $p =~ m/^(..)(..)/) {# climate event my ($d1,$vp) =($1,hex($2)); # adjust_command[0..4] adj_data[0..250] $vp = int($vp/2.56+0.5); # valve position in % - my $chnHash = $modules{CUL_HM}{defptr}{$dst."01"}; - push @entities, CUL_HM_UpdtReadBulk($chnHash,1,"ValvePosition:$vp %", - "ValveAdjCmd:".$d1); + my $chnHash = $modules{CUL_HM}{defptr}{$dst."01"}; + push @entities, CUL_HM_UpdtReadBulk($chnHash,1,"ValvePosition:$vp %", + "ValveAdjCmd:".$d1); push @ack,$chnHash,$mNo."8002".$dst.$src.'0101'. - sprintf("%02X",$vp*2)."0000"; - } - elsif($mTp eq "02"){ - if ($dhash->{helper}{prt}{rspWait}{mNo} && - $dhash->{helper}{prt}{rspWait}{mNo} eq $mNo ){ - #ack we waited for - stop Waiting - CUL_HM_respPendRm($dhash); - } - } + sprintf("%02X",$vp*2)."0000"; + } + elsif($mTp eq "02"){ + if ($dhash->{helper}{prt}{rspWait}{mNo} && + $dhash->{helper}{prt}{rspWait}{mNo} eq $mNo ){ + #ack we waited for - stop Waiting + CUL_HM_respPendRm($dhash); + } + } push @ack,$dhash,$mNo."8002".$dst.$src."00" if (hex($mFlg)&0x20 && (!@ack)); } elsif($id eq $dst){# if fhem is destination check if we need to react if($mTp =~ m/^4./ && $p =~ m/^(..)/ && #Push Button event - (hex($mFlg)&0x20)){ #response required Flag + (hex($mFlg)&0x20)){ #response required Flag my ($recChn) = (hex($1));# button number/event count - # fhem CUL shall ack a button press + # fhem CUL shall ack a button press push @ack,$shash,$mNo."8002".$dst.$src."0101".(($recChn&1)?"C8":"00")."00"; - } + } } - + #------------ send default ACK if not applicable------------------ # ack if we are destination, anyone did accept the message (@event) - # parser did not supress + # parser did not supress push @ack,$shash, $mNo."8002".$id.$src."00" - if( ($id eq $dst) #are we adressee - && (hex($mFlg)&0x20) #response required Flag - && @event #only ack if we identified it - && (!@ack) #sender requested ACK - ); + if( ($id eq $dst) #are we adressee + && (hex($mFlg)&0x20) #response required Flag + && @event #only ack if we identified it + && (!@ack) #sender requested ACK + ); - if (@ack) {# send acks and store for repeat + if (@ack) {# send acks and store for repeat my $sRptHash = $modules{CUL_HM}{defptr}{$src}{helper}{rpt}; $sRptHash->{IO} = $ioName; $sRptHash->{flg} = substr($msg,5,1); $sRptHash->{ack} = \@ack; - $sRptHash->{ts} = gettimeofday(); - my $i=0; + $sRptHash->{ts} = gettimeofday(); + my $i=0; CUL_HM_SndCmd($ack[$i++],$ack[$i++])while ($i<@ack); Log3 $name,6,"CUL_HM $name sent ACK:".(int(@ack)); } @@ -1537,382 +1537,381 @@ sub CUL_HM_Parse($$) {############################## push @event, "noReceiver:src:$src ".$mFlg.$mTp." $p" if(!@event && !@entities); CUL_HM_UpdtReadBulk($shash,1,@event); #events to the channel $defs{$shash->{NAME}}{EVENTS}++; # count events for channel - push @entities,$shash->{NAME}; - foreach (CUL_HM_noDup(@entities)){ - DoTrigger($_, undef) if ($_ ne $name); - } + + @entities = CUL_HM_noDup(@entities,$shash->{NAME}); + $defs{$_}{".noDispatchVars"} = 1 foreach (grep !/$name/,@entities); - return $name ; + return @entities; } sub CUL_HM_parseCommon(@){##################################################### # parsing commands that are device independent my ($mNo,$mFlg,$mTp,$src,$dst,$p,$st,$md) = @_; - my $shash = $modules{CUL_HM}{defptr}{$src}; + my $shash = $modules{CUL_HM}{defptr}{$src}; my $dhash = $modules{CUL_HM}{defptr}{$dst}; - return "" if(!$shash->{DEF});# this should be from ourself + return "" if(!$shash->{DEF});# this should be from ourself my $ret = ""; - my $pendType = $shash->{helper}{prt}{rspWait}{Pending}? + my $pendType = $shash->{helper}{prt}{rspWait}{Pending}? $shash->{helper}{prt}{rspWait}{Pending}:""; #------------ parse message flag for start processing command Stack # TC wakes up with 8270, not with A258 - # VD wakes up with 8202 + # VD wakes up with 8202 # 9610 if( ((hex($mFlg) & 0xA2) == 0x82) && - (CUL_HM_getRxType($shash) & 0x08)){ #wakeup and process stack - CUL_HM_appFromQ($shash->{NAME},"wu");# stack cmds if waiting - if ($shash->{cmdStack}){ + (CUL_HM_getRxType($shash) & 0x08)){ #wakeup and process stack + CUL_HM_appFromQ($shash->{NAME},"wu");# stack cmds if waiting + if ($shash->{cmdStack}){ CUL_HM_SndCmd($shash, '++A112'.CUL_HM_IOid($shash).$src); - CUL_HM_ProcessCmdStack($shash); - } + CUL_HM_ProcessCmdStack($shash); + } } my $repeat; if ($mTp eq "02"){# Ack/Nack ########################### - #see if the channel is defined separate - otherwise go for chief - my $subType = substr($p,0,2); - my $reply; - my $success; + #see if the channel is defined separate - otherwise go for chief + my $subType = substr($p,0,2); + my $reply; + my $success; - if ($shash->{helper}{prt}{rspWait}{wakeup}){ - if ($shash->{helper}{prt}{rspWait}{mNo} eq $mNo && - $subType eq "00" ){ - if ($shash->{helper}{prt}{awake} && $shash->{helper}{prt}{awake}==4){#re-wakeup - delete $shash->{helper}{prt}{rspWait};#clear wakeup values - $shash->{helper}{prt}{rspWait}{$_} = $shash->{helper}{prt}{rspWaitSec}{$_} - foreach (keys%{$shash->{helper}{prt}{rspWaitSec}}); #back to original message - delete $shash->{helper}{prt}{rspWaitSec}; - IOWrite($shash, "", $shash->{helper}{prt}{rspWait}{cmd}); # and send - CUL_HM_statCnt($shash->{IODev}{NAME},"s"); - #General set timer - return "done" - } - $shash->{protCondBurst} = "on" if ( $shash->{protCondBurst} - && $shash->{protCondBurst} !~ m/forced/); - $shash->{helper}{prt}{awake}=2;#awake - } - else{ - $shash->{protCondBurst} = "off" if ($shash->{protCondBurst} !~ m/forced/); - $shash->{helper}{prt}{awake}=3;#reject - return "done"; - } - } - - if ($subType =~ m/^8/){#NACK - #84 : request undefined register - $success = "no"; - CUL_HM_eventP($shash,"Nack"); - $reply = "NACK"; - } - elsif($subType eq "01"){ #ACKinfo################# + if ($shash->{helper}{prt}{rspWait}{wakeup}){ + if ($shash->{helper}{prt}{rspWait}{mNo} eq $mNo && + $subType eq "00"){ + if ($shash->{helper}{prt}{awake} && $shash->{helper}{prt}{awake}==4){#re-wakeup + delete $shash->{helper}{prt}{rspWait};#clear wakeup values + $shash->{helper}{prt}{rspWait}{$_} = $shash->{helper}{prt}{rspWaitSec}{$_} + foreach (keys%{$shash->{helper}{prt}{rspWaitSec}}); #back to original message + delete $shash->{helper}{prt}{rspWaitSec}; + IOWrite($shash, "", $shash->{helper}{prt}{rspWait}{cmd}); # and send + CUL_HM_statCnt($shash->{IODev}{NAME},"s"); + #General set timer + return "done" + } + $shash->{protCondBurst} = "on" if ( $shash->{protCondBurst} + && $shash->{protCondBurst} !~ m/forced/); + $shash->{helper}{prt}{awake}=2;#awake + } + else{ + $shash->{protCondBurst} = "off" if ($shash->{protCondBurst} !~ m/forced/); + $shash->{helper}{prt}{awake}=3;#reject + return "done"; + } + } + + if ($subType =~ m/^8/){#NACK + #84 : request undefined register + $success = "no"; + CUL_HM_eventP($shash,"Nack"); + $reply = "NACK"; + } + elsif($subType eq "01"){ #ACKinfo################# $success = "yes"; - my $rssi = substr($p,8,2);# --calculate RSSI + my $rssi = substr($p,8,2);# --calculate RSSI CUL_HM_storeRssi($shash->{NAME}, ($dhash?$dhash->{NAME}:$shash->{IODev}{NAME}), - (-1)*(hex($rssi))) - if ($rssi && $rssi ne '00' && $rssi ne'80'); - $reply = "ACKStatus"; - if ($shash->{helper}{tmdOn}){ - if (not hex(substr($p,6,2))&0x40){# not timedOn, we have to repeat + (-1)*(hex($rssi))) + if ($rssi && $rssi ne '00' && $rssi ne'80'); + $reply = "ACKStatus"; + if ($shash->{helper}{tmdOn}){ + if (not hex(substr($p,6,2))&0x40){# not timedOn, we have to repeat my ($pre,$nbr,$msg) = unpack 'A4A2A*',$shash->{helper}{prt}{rspWait}{cmd}; $shash->{helper}{prt}{rspWait}{cmd} = sprintf("%s%02X%s", - $pre,hex($nbr)+1,$msg); - CUL_HM_eventP($shash,"TimedOn"); - $success = "no"; - $repeat = 1; - $reply = "NACK"; - } - } - } - elsif($subType eq "04"){ #ACK-AES, interim######## + $pre,hex($nbr)+1,$msg); + CUL_HM_eventP($shash,"TimedOn"); + $success = "no"; + $repeat = 1; + $reply = "NACK"; + } + } + } + elsif($subType eq "04"){ #ACK-AES, interim######## #$success = ""; #result not final, another response should come - $reply = "done"; - } - else{ #ACK - $success = "yes"; - $reply = "ACK"; - } + $reply = "done"; + } + else{ #ACK + $success = "yes"; + $reply = "ACK"; + } - if ( $shash->{helper}{prt}{mmcS} - && $shash->{helper}{prt}{mmcS} == 3){ - # after write device might need a break - # allow for wake types only - and if commands are pending - $shash->{helper}{prt}{try} = 1 if (CUL_HM_getRxType($shash) & 0x08 #wakeup - && $shash->{cmdStack}); - if ($success eq 'yes'){ - delete $shash->{helper}{prt}{mmcA}; - delete $shash->{helper}{prt}{mmcS}; - } - }; - - if($success){#do we have a final ack? + if ( $shash->{helper}{prt}{mmcS} + && $shash->{helper}{prt}{mmcS} == 3){ + # after write device might need a break + # allow for wake types only - and if commands are pending + $shash->{helper}{prt}{try} = 1 if (CUL_HM_getRxType($shash) & 0x08 #wakeup + && $shash->{cmdStack}); + if ($success eq 'yes'){ + delete $shash->{helper}{prt}{mmcA}; + delete $shash->{helper}{prt}{mmcS}; + } + }; + + if($success){#do we have a final ack? #mark timing on the channel, not the device my $chn = sprintf("%02X",hex(substr($p,2,2))&0x3f); my $chnhash = $modules{CUL_HM}{defptr}{$chn?$src.$chn:$src}; $chnhash = $shash if(!$chnhash); - readingsSingleUpdate($chnhash,"CommandAccepted",$success,0); + readingsSingleUpdate($chnhash,"CommandAccepted",$success,0); CUL_HM_ProcessCmdStack($shash) if(CUL_HM_IOid($shash) eq $dst); - } - $ret = $reply; + } + $ret = $reply; } elsif($mTp eq "00"){###################################### my $iohash = $shash->{IODev}; my $id = CUL_HM_Id($iohash); - CUL_HM_infoUpdtDevData($shash->{NAME}, $shash,$p) - if (!$modules{CUL_HM}{helper}{hmManualOper}#no autoaction - ||$iohash->{hmPair} + CUL_HM_infoUpdtDevData($shash->{NAME}, $shash,$p) + if (!$modules{CUL_HM}{helper}{hmManualOper}#no autoaction + ||$iohash->{hmPair} ||$iohash->{hmPairSerial} ); if( $dst =~ m /(000000|$id)/ #--- see if we need to pair - &&($iohash->{hmPair} - ||( $iohash->{hmPairSerial} - && $iohash->{hmPairSerial} eq $attr{$shash->{NAME}}{serialNr})) - &&( $mFlg.$mTp ne "0400") ) { - #-- try to pair + &&($iohash->{hmPair} + ||( $iohash->{hmPairSerial} + && $iohash->{hmPairSerial} eq $attr{$shash->{NAME}}{serialNr})) + &&( $mFlg.$mTp ne "0400") ) { + #-- try to pair Log3 $shash,3, "CUL_HM pair: $shash->{NAME} " - ."$attr{$shash->{NAME}}{subType}, " - ."model $attr{$shash->{NAME}}{model} " - ."serialNr $attr{$shash->{NAME}}{serialNr}"; - delete $iohash->{hmPairSerial}; - CUL_HM_respPendRm($shash); # remove all pending messages - delete $shash->{cmdStack}; - delete $shash->{EVENTS}; - delete $shash->{helper}{prt}{rspWait}; - delete $shash->{helper}{prt}{rspWaitSec}; - + ."$attr{$shash->{NAME}}{subType}, " + ."model $attr{$shash->{NAME}}{model} " + ."serialNr $attr{$shash->{NAME}}{serialNr}"; + delete $iohash->{hmPairSerial}; + CUL_HM_respPendRm($shash); # remove all pending messages + delete $shash->{cmdStack}; + delete $shash->{EVENTS}; + delete $shash->{helper}{prt}{rspWait}; + delete $shash->{helper}{prt}{rspWaitSec}; + my ($idstr, $s) = ($id, 0xA); $idstr =~ s/(..)/sprintf("%02X%s",$s++,$1)/ge; CUL_HM_pushConfig($shash, $id, $src,0,0,0,0, "0201$idstr"); CUL_HM_ProcessCmdStack($shash); # start processing immediately - CUL_HM_qAutoRead($shash->{NAME},0); - CUL_HM_appFromQ($shash->{NAME},"cf");# stack cmds if waiting - } - elsif(CUL_HM_getRxType($shash) & 0x04){# nothing to pair - maybe send config - CUL_HM_qAutoRead($shash->{NAME},0); - CUL_HM_appFromQ($shash->{NAME},"cf"); # stack cmds if waiting - if (hex($mFlg)&0x20){CUL_HM_SndCmd($shash,$mNo."8002".$id.$src."00");} - else{ CUL_HM_ProcessCmdStack($shash);} ;#config - } - $ret = "done"; - } + CUL_HM_qAutoRead($shash->{NAME},0); + CUL_HM_appFromQ($shash->{NAME},"cf");# stack cmds if waiting + } + elsif(CUL_HM_getRxType($shash) & 0x04){# nothing to pair - maybe send config + CUL_HM_qAutoRead($shash->{NAME},0); + CUL_HM_appFromQ($shash->{NAME},"cf"); # stack cmds if waiting + if (hex($mFlg)&0x20){CUL_HM_SndCmd($shash,$mNo."8002".$id.$src."00");} + else{ CUL_HM_ProcessCmdStack($shash);} ;#config + } + $ret = "done"; + } elsif($mTp eq "10"){###################################### my $subType = substr($p,0,2); - if ($subType eq "00"){ #SerialRead==================================== - $attr{$shash->{NAME}}{serialNr} = pack("H*",substr($p,2,20)); - if ($pendType eq "SerialRead"){ - CUL_HM_respPendRm($shash); - } - $ret = "done"; - } - elsif($subType eq "01"){ #storePeerList================================= + if ($subType eq "00"){ #SerialRead==================================== + $attr{$shash->{NAME}}{serialNr} = pack("H*",substr($p,2,20)); + if ($pendType eq "SerialRead"){ + CUL_HM_respPendRm($shash); + } + $ret = "done"; + } + elsif($subType eq "01"){ #storePeerList================================= my $msgValid = 0; - if ($pendType eq "PeerList"){ - if($shash->{helper}{prt}{rspWait}{mNo} == hex($mNo)){#next message - $shash->{helper}{prt}{rspWait}{mNo}++; - $shash->{helper}{prt}{rspWait}{mNo} &= 0xff; - $msgValid = 1; - } - } - if ($msgValid){ - my $chn = $shash->{helper}{prt}{rspWait}{forChn}; - my $chnhash = $modules{CUL_HM}{defptr}{$src.$chn}; - $chnhash = $shash if (!$chnhash); - my $chnName = $chnhash->{NAME}; - my (undef,@peers) = unpack 'A2(A8)*',$p; + if ($pendType eq "PeerList"){ + if($shash->{helper}{prt}{rspWait}{mNo} == hex($mNo)){#next message + $shash->{helper}{prt}{rspWait}{mNo}++; + $shash->{helper}{prt}{rspWait}{mNo} &= 0xff; + $msgValid = 1; + } + } + if ($msgValid){ + my $chn = $shash->{helper}{prt}{rspWait}{forChn}; + my $chnhash = $modules{CUL_HM}{defptr}{$src.$chn}; + $chnhash = $shash if (!$chnhash); + my $chnName = $chnhash->{NAME}; + my (undef,@peers) = unpack 'A2(A8)*',$p; $_ = '00000000' foreach (grep /^000000/,@peers);#correct bad term(6 chars) from rain sens) - $chnhash->{helper}{peerIDsRaw}.= ",".join",",@peers; + $chnhash->{helper}{peerIDsRaw}.= ",".join",",@peers; - CUL_HM_ID2PeerList ($chnName,$_,1) foreach (@peers); - if (grep /00000000/,@peers) {# last entry, peerList is complete - # check for request to get List3 data - my $reqPeer = $chnhash->{helper}{getCfgList}; - if ($reqPeer){ - my $flag = CUL_HM_getFlag($shash); - my $id = CUL_HM_IOid($shash); - my $listNo = "0".$chnhash->{helper}{getCfgListNo}; - my @peerID = split(",",($attr{$chnName}{peerIDs}? - $attr{$chnName}{peerIDs}:"")); - foreach my $peer (grep (!/00000000/,@peerID)){ - $peer .="01" if (length($peer) == 6); # add the default - if ($peer &&($peer eq $reqPeer || $reqPeer eq "all")){ - CUL_HM_PushCmdStack($shash,sprintf("++%s01%s%s%s04%s%s", - $flag,$id,$src,$chn,$peer,$listNo));# List3 or 4 - } - } - } - CUL_HM_respPendRm($shash); - delete $chnhash->{helper}{getCfgList}; - delete $chnhash->{helper}{getCfgListNo}; - CUL_HM_rmOldRegs($chnName); - } - else{ - CUL_HM_respPendToutProlong($shash);#wasn't last - reschedule timer - } - $ret = "done"; - } - else{#response without request - discard - $ret = "done"; - } - } - elsif($subType eq "02" ||$subType eq "03"){ #ParamResp================== + CUL_HM_ID2PeerList ($chnName,$_,1) foreach (@peers); + if (grep /00000000/,@peers) {# last entry, peerList is complete + # check for request to get List3 data + my $reqPeer = $chnhash->{helper}{getCfgList}; + if ($reqPeer){ + my $flag = CUL_HM_getFlag($shash); + my $id = CUL_HM_IOid($shash); + my $listNo = "0".$chnhash->{helper}{getCfgListNo}; + my @peerID = split(",",($attr{$chnName}{peerIDs}? + $attr{$chnName}{peerIDs}:"")); + foreach my $peer (grep (!/00000000/,@peerID)){ + $peer .="01" if (length($peer) == 6); # add the default + if ($peer &&($peer eq $reqPeer || $reqPeer eq "all")){ + CUL_HM_PushCmdStack($shash,sprintf("++%s01%s%s%s04%s%s", + $flag,$id,$src,$chn,$peer,$listNo));# List3 or 4 + } + } + } + CUL_HM_respPendRm($shash); + delete $chnhash->{helper}{getCfgList}; + delete $chnhash->{helper}{getCfgListNo}; + CUL_HM_rmOldRegs($chnName); + } + else{ + CUL_HM_respPendToutProlong($shash);#wasn't last - reschedule timer + } + $ret = "done"; + } + else{#response without request - discard + $ret = "done"; + } + } + elsif($subType eq "02" ||$subType eq "03"){ #ParamResp================== my $msgValid = 0; - if ($pendType eq "RegisterRead"){ - if($shash->{helper}{prt}{rspWait}{mNo} == hex($mNo)){#next message - $shash->{helper}{prt}{rspWait}{mNo}++; - $shash->{helper}{prt}{rspWait}{mNo} &= 0xff; - $msgValid = 1; - } - } - if ($msgValid){ - my $chnSrc = $src.$shash->{helper}{prt}{rspWait}{forChn}; - my $chnHash = $modules{CUL_HM}{defptr}{$chnSrc}; - $chnHash = $shash if (!$chnHash); - my $chnName = $chnHash->{NAME}; - my ($format,$data) = ($1,$2) if ($p =~ m/^(..)(.*)/); - my $list = $shash->{helper}{prt}{rspWait}{forList}; - $list = "00" if (!$list); #use the default - if ($format eq "02"){ # list 2: format aa:dd aa:dd ... - $data =~ s/(..)(..)/ $1:$2/g; - } - elsif ($format eq "03"){ # list 3: format aa:dddd - my $addr; - my @dataList; - ($addr,$data) = (hex($1),$2) if ($data =~ m/(..)(.*)/); - if ($addr == 0){ - $data = "00:00"; - } - else{ - $data =~s/(..)/$1:/g; - foreach my $d1 (split(":",$data)){ - push (@dataList,sprintf("%02X:%s",$addr++,$d1)); - } - $data = join(" ",@dataList); - } - } + if ($pendType eq "RegisterRead"){ + if($shash->{helper}{prt}{rspWait}{mNo} == hex($mNo)){#next message + $shash->{helper}{prt}{rspWait}{mNo}++; + $shash->{helper}{prt}{rspWait}{mNo} &= 0xff; + $msgValid = 1; + } + } + if ($msgValid){ + my $chnSrc = $src.$shash->{helper}{prt}{rspWait}{forChn}; + my $chnHash = $modules{CUL_HM}{defptr}{$chnSrc}; + $chnHash = $shash if (!$chnHash); + my $chnName = $chnHash->{NAME}; + my ($format,$data) = ($1,$2) if ($p =~ m/^(..)(.*)/); + my $list = $shash->{helper}{prt}{rspWait}{forList}; + $list = "00" if (!$list); #use the default + if ($format eq "02"){ # list 2: format aa:dd aa:dd ... + $data =~ s/(..)(..)/ $1:$2/g; + } + elsif ($format eq "03"){ # list 3: format aa:dddd + my $addr; + my @dataList; + ($addr,$data) = (hex($1),$2) if ($data =~ m/(..)(.*)/); + if ($addr == 0){ + $data = "00:00"; + } + else{ + $data =~s/(..)/$1:/g; + foreach my $d1 (split(":",$data)){ + push (@dataList,sprintf("%02X:%s",$addr++,$d1)); + } + $data = join(" ",@dataList); + } + } - my $peer = $shash->{helper}{prt}{rspWait}{forPeer}; - my $regLNp = "RegL_$list:$peer";# pure, no expert - my $regLN = ((CUL_HM_getAttrInt($chnName,"expert") == 2)?"":".").$regLNp; - readingsSingleUpdate($chnHash,$regLN, - ReadingsVal($chnName,$regLN,"")." ".$data,0); - if ($data =~m/00:00$/){ # this was the last message in the block - if($list eq "00"){ - my $name = CUL_HM_id2Name($src); - readingsSingleUpdate($shash,"PairedTo", - CUL_HM_getRegFromStore($name,"pairCentral",0,""),0); - } - CUL_HM_respPendRm($shash); - delete $chnHash->{helper}{shadowReg}{$regLNp}; #rm shadow - # peer Channel name from/for user entry. - CUL_HM_updtRegDisp($chnHash,$list, - CUL_HM_peerChId($peer, - substr($chnHash->{DEF},0,6),"00000000")); - $ret = "done;entities:$chnName"; - } - else{ - CUL_HM_respPendToutProlong($shash);#wasn't last - reschedule timer - $ret = "done"; - } - } - else{#response without request - discard - $ret = "done"; - } - } - elsif($subType eq "04"){ #ParamChange=================================== - my($chn,$peerID,$list,$data) = ($1,$2,$3,$4) if($p =~ m/^04(..)(........)(..)(.*)/); - my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; - $chnHash = $shash if(!$chnHash); # will add param to dev if no chan - my $regLNp = "RegL_$list:".CUL_HM_id2Name($peerID); + my $peer = $shash->{helper}{prt}{rspWait}{forPeer}; + my $regLNp = "RegL_$list:$peer";# pure, no expert + my $regLN = ((CUL_HM_getAttrInt($chnName,"expert") == 2)?"":".").$regLNp; + readingsSingleUpdate($chnHash,$regLN, + ReadingsVal($chnName,$regLN,"")." ".$data,0); + if ($data =~m/00:00$/){ # this was the last message in the block + if($list eq "00"){ + my $name = CUL_HM_id2Name($src); + readingsSingleUpdate($shash,"PairedTo", + CUL_HM_getRegFromStore($name,"pairCentral",0,""),0); + } + CUL_HM_respPendRm($shash); + delete $chnHash->{helper}{shadowReg}{$regLNp}; #rm shadow + # peer Channel name from/for user entry. + CUL_HM_updtRegDisp($chnHash,$list, + CUL_HM_peerChId($peer, + substr($chnHash->{DEF},0,6),"00000000")); + $ret = "done;entities:$chnName"; + } + else{ + CUL_HM_respPendToutProlong($shash);#wasn't last - reschedule timer + $ret = "done"; + } + } + else{#response without request - discard + $ret = "done"; + } + } + elsif($subType eq "04"){ #ParamChange=================================== + my($chn,$peerID,$list,$data) = ($1,$2,$3,$4) if($p =~ m/^04(..)(........)(..)(.*)/); + my $chnHash = $modules{CUL_HM}{defptr}{$src.$chn}; + $chnHash = $shash if(!$chnHash); # will add param to dev if no chan + my $regLNp = "RegL_$list:".CUL_HM_id2Name($peerID); $regLNp =~ s/broadcast//; - $regLNp =~ s/ /_/g; #remove blanks - my $regLN = ((CUL_HM_getAttrInt($chnHash->{NAME},"expert") == 2)?"":".").$regLNp; + $regLNp =~ s/ /_/g; #remove blanks + my $regLN = ((CUL_HM_getAttrInt($chnHash->{NAME},"expert") == 2)?"":".").$regLNp; - $data =~ s/(..)(..)/ $1:$2/g; - - my $lN = ReadingsVal($chnHash->{NAME},$regLN,""); - my $shdwReg = $chnHash->{helper}{shadowReg}{$regLNp}; - foreach my $entry(split(' ',$data)){ - my ($a,$d) = split(":",$entry); - last if ($a eq "00"); - if ($lN =~m/$a:/){$lN =~ s/$a:../$a:$d/; - }else{ $lN .= " ".$entry;} - $shdwReg =~ s/ $a:..// if ($shdwReg);# confirmed: remove from shadow - } - $chnHash->{helper}{shadowReg}{$regLNp} = $shdwReg; - $lN = join(' ',sort(split(' ',$lN)));# re-order - if ($lN =~ s/00:00//){$lN .= " 00:00"}; + $data =~ s/(..)(..)/ $1:$2/g; + + my $lN = ReadingsVal($chnHash->{NAME},$regLN,""); + my $shdwReg = $chnHash->{helper}{shadowReg}{$regLNp}; + foreach my $entry(split(' ',$data)){ + my ($a,$d) = split(":",$entry); + last if ($a eq "00"); + if ($lN =~m/$a:/){$lN =~ s/$a:../$a:$d/; + }else{ $lN .= " ".$entry;} + $shdwReg =~ s/ $a:..// if ($shdwReg);# confirmed: remove from shadow + } + $chnHash->{helper}{shadowReg}{$regLNp} = $shdwReg; + $lN = join(' ',sort(split(' ',$lN)));# re-order + if ($lN =~ s/00:00//){$lN .= " 00:00"}; readingsSingleUpdate($chnHash,$regLN,$lN,0); - CUL_HM_updtRegDisp($chnHash,$list,$peerID); - $ret= "parsed"; - } - elsif($subType eq "06"){ #reply to status request======================= - my $rssi = substr($p,8,2);# --calculate RSSI + CUL_HM_updtRegDisp($chnHash,$list,$peerID); + $ret= "parsed"; + } + elsif($subType eq "06"){ #reply to status request======================= + my $rssi = substr($p,8,2);# --calculate RSSI CUL_HM_storeRssi($shash->{NAME}, ($dhash?$dhash->{NAME}:$shash->{IODev}{NAME}), - (-1)*(hex($rssi))) - if ($rssi && $rssi ne '00' && $rssi ne'80'); - @{$modules{CUL_HM}{helper}{qReqStat}} = grep { $_ ne $shash->{NAME} } + (-1)*(hex($rssi))) + if ($rssi && $rssi ne '00' && $rssi ne'80'); + @{$modules{CUL_HM}{helper}{qReqStat}} = grep { $_ ne $shash->{NAME} } @{$modules{CUL_HM}{helper}{qReqStat}}; if ($pendType eq "StatusReq"){#it is the answer to our request - my $chnSrc = $src.$shash->{helper}{prt}{rspWait}{forChn}; - my $chnhash = $modules{CUL_HM}{defptr}{$chnSrc}; - $chnhash = $shash if (!$chnhash); - CUL_HM_respPendRm($shash); - $ret = "STATresp"; - } - else{ - my ($chn) = ($1) if($p =~ m/^..(..)/); - if ($chn eq "00"){# this is power on - my $name = $shash->{NAME}; - CUL_HM_qStateUpdatIfEnab($name); - CUL_HM_qAutoRead($name,2); - $ret = "powerOn" ;# check dst eq "000000" as well? - } - } - } + my $chnSrc = $src.$shash->{helper}{prt}{rspWait}{forChn}; + my $chnhash = $modules{CUL_HM}{defptr}{$chnSrc}; + $chnhash = $shash if (!$chnhash); + CUL_HM_respPendRm($shash); + $ret = "STATresp"; + } + else{ + my ($chn) = ($1) if($p =~ m/^..(..)/); + if ($chn eq "00"){# this is power on + my $name = $shash->{NAME}; + CUL_HM_qStateUpdatIfEnab($name); + CUL_HM_qAutoRead($name,2); + $ret = "powerOn" ;# check dst eq "000000" as well? + } + } + } } elsif($mTp =~ m /^4[01]/){ #someone is triggered########## - CUL_HM_qStateUpdatIfEnab($dst)if (hex($mFlg) & 0x20 && $dhash); - my $chn = hex(substr($p,0,2)); - my $long = ($chn & 0x40)?"long":"short"; - $chn = $chn & 0x3f; + CUL_HM_qStateUpdatIfEnab($dst)if (hex($mFlg) & 0x20 && $dhash); + my $chn = hex(substr($p,0,2)); + my $long = ($chn & 0x40)?"long":"short"; + $chn = $chn & 0x3f; my $cName = CUL_HM_id2Hash($src.sprintf("%02X",$chn)); - $cName = $cName->{NAME}; - my $level = "-"; - - if (length($p)>5){ - my $l = substr($p,4,2); - if ($lvlStr{md}{$md} && $lvlStr{md}{$md}{$l}){$level = $lvlStr{md}{$md}{$l}} - elsif ($lvlStr{st}{$st} && $lvlStr{st}{$st}{$l}){$level = $lvlStr{st}{$st}{$l}} - else {$level = hex($l)}; - } - elsif($mTp eq "40"){ - $level = $long; - } + $cName = $cName->{NAME}; + my $level = "-"; - my @peers = split(",",AttrVal($cName,"peerIDs","")); - my @entities; - foreach my $peer (@peers){ - my $pName = CUL_HM_id2Name($peer); - $pName = CUL_HM_id2Name(substr($peer,0,6)) if (!$defs{$pName}); - next if (!$defs{$pName});#||substr($peer,0,6) ne $dst - push @entities,CUL_HM_UpdtReadBulk($defs{$pName},1 - ,"trig_$cName:$level" - ,"trigLast:$cName ".(($level ne "-")?":$level":"")); - } + if (length($p)>5){ + my $l = substr($p,4,2); + if ($lvlStr{md}{$md} && $lvlStr{md}{$md}{$l}){$level = $lvlStr{md}{$md}{$l}} + elsif ($lvlStr{st}{$st} && $lvlStr{st}{$st}{$l}){$level = $lvlStr{st}{$st}{$l}} + else {$level = hex($l)}; + } + elsif($mTp eq "40"){ + $level = $long; + } - return "entities:".join(",",@entities); + my @peers = split(",",AttrVal($cName,"peerIDs","")); + my @entities; + foreach my $peer (@peers){ + my $pName = CUL_HM_id2Name($peer); + $pName = CUL_HM_id2Name(substr($peer,0,6)) if (!$defs{$pName}); + next if (!$defs{$pName});#||substr($peer,0,6) ne $dst + push @entities,CUL_HM_UpdtReadBulk($defs{$pName},1 + ,"trig_$cName:$level" + ,"trigLast:$cName ".(($level ne "-")?":$level":"")); + } + + return "entities:".join(",",@entities); } elsif($mTp eq "70"){ #Time to trigger TC################## #send wakeup and process command stack -# CUL_HM_SndCmd($shash, '++A112'.CUL_HM_IOid($shash).$src); -# CUL_HM_ProcessCmdStack($shash); +# CUL_HM_SndCmd($shash, '++A112'.CUL_HM_IOid($shash).$src); +# CUL_HM_ProcessCmdStack($shash); } - if ($shash->{helper}{prt}{rspWait}{mNo} && - $shash->{helper}{prt}{rspWait}{mNo} eq $mNo && - !$repeat){ - #response we waited for - stop Waiting - CUL_HM_respPendRm($shash); + if ($shash->{helper}{prt}{rspWait}{mNo} && + $shash->{helper}{prt}{rspWait}{mNo} eq $mNo && + !$repeat){ + #response we waited for - stop Waiting + CUL_HM_respPendRm($shash); } return $ret; @@ -1921,7 +1920,7 @@ sub CUL_HM_queueUpdtCfg($){ my $name = shift; if ($modules{CUL_HM}{helper}{hmManualOper}){ # no update when manual operation delete $modules{CUL_HM}{helper}{updtCfgLst}; - return; + return; } my @arr; if ($modules{CUL_HM}{helper}{updtCfgLst}){ @@ -1968,12 +1967,12 @@ sub CUL_HM_Get($@) { my @arr = keys %culHmGlobalGets; push @arr, keys %{$culHmSubTypeGets{$st}} if($culHmSubTypeGets{$st}); push @arr, keys %{$culHmModelGets{$md}} if($culHmModelGets{$md}); - my $usg = "Unknown argument $cmd, choose one of ".join(" ",sort @arr); + my $usg = "Unknown argument $cmd, choose one of ".join(" ",sort @arr); return $usg; }elsif($h eq "" && @a != 2) { return "$cmd requires no parameters"; - + } elsif($h !~ m/\.\.\./ && @h != @a-2) { return "$cmd requires parameter: $h"; } @@ -1982,135 +1981,135 @@ sub CUL_HM_Get($@) { #----------- now start processing -------------- if($cmd eq "param") { ###################################################### - return $attr{$name}{$a[2]} if ($attr{$name}{$a[2]}); - return $hash->{READINGS}{$a[2]}{VAL} if ($hash->{READINGS}{$a[2]}); - return $attr{$devName}{$a[2]} if ($attr{$devName}{$a[2]}); - return $hash->{$a[2]} if ($hash->{$a[2]}); - return $hash->{helper}{$a[2]} if ($hash->{helper}{$a[2]} && ref($hash->{helper}{$a[2]}) ne "HASH"); - return "undefined"; + return $attr{$name}{$a[2]} if ($attr{$name}{$a[2]}); + return $hash->{READINGS}{$a[2]}{VAL} if ($hash->{READINGS}{$a[2]}); + return $attr{$devName}{$a[2]} if ($attr{$devName}{$a[2]}); + return $hash->{$a[2]} if ($hash->{$a[2]}); + return $hash->{helper}{$a[2]} if ($hash->{helper}{$a[2]} && ref($hash->{helper}{$a[2]}) ne "HASH"); + return "undefined"; } elsif($cmd eq "reg") { ##################################################### my (undef,undef,$regReq,$list,$peerId) = @a; - if ($regReq eq 'all'){ - my @regArr = keys %culHmRegGeneral; - push @regArr, keys %{$culHmRegType{$st}} if($culHmRegType{$st}); - push @regArr, keys %{$culHmRegModel{$md}} if($culHmRegModel{$md}); - push @regArr, keys %{$culHmRegChan{$md.$chn}} if($culHmRegChan{$md.$chn}); + if ($regReq eq 'all'){ + my @regArr = keys %culHmRegGeneral; + push @regArr, keys %{$culHmRegType{$st}} if($culHmRegType{$st}); + push @regArr, keys %{$culHmRegModel{$md}} if($culHmRegModel{$md}); + push @regArr, keys %{$culHmRegChan{$md.$chn}} if($culHmRegChan{$md.$chn}); - my @peers; # get all peers we have a reglist - my @listWp; # list that require peers - foreach my $readEntry (keys %{$hash->{READINGS}}){ - if ($readEntry =~m /^[\.]?RegL_(.*)/){ #reg Reading "RegL_:peerN - my $peer = substr($1,3); - next if (!$peer); - push(@peers,$peer); - push(@listWp,substr($1,1,1)); - } - } - my @regValList; #storage of results - my $regHeader = "list:peer\tregister :value\n"; - foreach my $regName (@regArr){ - my $regL = $culHmRegDefine{$regName}->{l}; - my @peerExe = (grep (/$regL/,@listWp))?@peers:("00000000"); - foreach my $peer(@peerExe){ - next if($peer eq ""); - my $regVal= CUL_HM_getRegFromStore($name,$regName,0,$peer);#determine - my $peerN = CUL_HM_id2Name($peer); - $peerN = " " if ($peer eq "00000000"); - push @regValList,sprintf(" %d:%s\t%-16s :%s\n", - $regL,$peerN,$regName,$regVal) - if ($regVal ne 'invalid'); - } - } - my $addInfo = ""; - if ($md eq "HM-CC-TC" && $chn eq "02"){$addInfo = CUL_HM_TCtempReadings($hash)} + my @peers; # get all peers we have a reglist + my @listWp; # list that require peers + foreach my $readEntry (keys %{$hash->{READINGS}}){ + if ($readEntry =~m /^[\.]?RegL_(.*)/){ #reg Reading "RegL_:peerN + my $peer = substr($1,3); + next if (!$peer); + push(@peers,$peer); + push(@listWp,substr($1,1,1)); + } + } + my @regValList; #storage of results + my $regHeader = "list:peer\tregister :value\n"; + foreach my $regName (@regArr){ + my $regL = $culHmRegDefine{$regName}->{l}; + my @peerExe = (grep (/$regL/,@listWp))?@peers:("00000000"); + foreach my $peer(@peerExe){ + next if($peer eq ""); + my $regVal= CUL_HM_getRegFromStore($name,$regName,0,$peer);#determine + my $peerN = CUL_HM_id2Name($peer); + $peerN = " " if ($peer eq "00000000"); + push @regValList,sprintf(" %d:%s\t%-16s :%s\n", + $regL,$peerN,$regName,$regVal) + if ($regVal ne 'invalid'); + } + } + my $addInfo = ""; + if ($md eq "HM-CC-TC" && $chn eq "02"){$addInfo = CUL_HM_TCtempReadings($hash)} elsif ($md =~ m/HM-CC-RT-DN/ && $chn eq "04"){$addInfo = CUL_HM_RTtempReadings($hash)} elsif ($md eq "HM-PB-4DIS-WM") {$addInfo = CUL_HM_4DisText($hash)} - elsif ($md eq "HM-Sys-sRP-Pl") {$addInfo = CUL_HM_repReadings($hash)} + elsif ($md eq "HM-Sys-sRP-Pl") {$addInfo = CUL_HM_repReadings($hash)} - return $name." type:".$st." - \n". - $regHeader.join("",sort(@regValList)). - $addInfo; - } - else{ + return $name." type:".$st." - \n". + $regHeader.join("",sort(@regValList)). + $addInfo; + } + else{ my $regVal = CUL_HM_getRegFromStore($name,$regReq,$list,$peerId); - return ($regVal eq "invalid")? "Value not captured" - : $regVal; - } + return ($regVal eq "invalid")? "Value not captured" + : $regVal; + } } elsif($cmd eq "regList") { ################################################# my @regArr = keys %culHmRegGeneral ; - push @regArr, keys %{$culHmRegType{$st}} if($culHmRegType{$st}); - push @regArr, keys %{$culHmRegModel{$md}} if($culHmRegModel{$md}); - - if ($isChannel){ - push @regArr, keys %{$culHmRegChan{$md.$chn}} if($culHmRegChan{$md.$chn}); - } - else{# add all ugly channel register to device view - for my $chnId (CUL_HM_getAssChnIds($name)){ - my $chnN = substr($chnId,6,2); - push @regArr, keys %{$culHmRegChan{$md.$chnN}} - if($culHmRegChan{$md.$chnN}); - } - } + push @regArr, keys %{$culHmRegType{$st}} if($culHmRegType{$st}); + push @regArr, keys %{$culHmRegModel{$md}} if($culHmRegModel{$md}); - my @rI; - foreach my $regName (@regArr){ - my $reg = $culHmRegDefine{$regName}; - my $help = $reg->{t}; - my ($min,$max) = ($reg->{min},"to ".$reg->{max}); - if (defined($reg->{lit})){ - $help .= " options:".join(",",keys%{$reg->{lit}}); - $min = ""; - $max = "literal"; - } - push @rI,sprintf("%4d: %-16s | %3s %-14s | %8s | %s\n", - $reg->{l},$regName,$min,$max.$reg->{u}, + if ($isChannel){ + push @regArr, keys %{$culHmRegChan{$md.$chn}} if($culHmRegChan{$md.$chn}); + } + else{# add all ugly channel register to device view + for my $chnId (CUL_HM_getAssChnIds($name)){ + my $chnN = substr($chnId,6,2); + push @regArr, keys %{$culHmRegChan{$md.$chnN}} + if($culHmRegChan{$md.$chnN}); + } + } + + my @rI; + foreach my $regName (@regArr){ + my $reg = $culHmRegDefine{$regName}; + my $help = $reg->{t}; + my ($min,$max) = ($reg->{min},"to ".$reg->{max}); + if (defined($reg->{lit})){ + $help .= " options:".join(",",keys%{$reg->{lit}}); + $min = ""; + $max = "literal"; + } + push @rI,sprintf("%4d: %-16s | %3s %-14s | %8s | %s\n", + $reg->{l},$regName,$min,$max.$reg->{u}, ((($reg->{l} == 3)||($reg->{l} == 4))?"required":""), - $help) - if (($roleD && $reg->{l} == 0)|| - ($roleC && $reg->{l} != 0)); - } - + $help) + if (($roleD && $reg->{l} == 0)|| + ($roleC && $reg->{l} != 0)); + } + my $info = sprintf("list: %16s | %-18s | %-8s | %s\n", - "register","range","peer","description"); - foreach(sort(@rI)){$info .= $_;} - return $info; + "register","range","peer","description"); + foreach(sort(@rI)){$info .= $_;} + return $info; } elsif($cmd eq "saveConfig"){ ############################################### my $fName = $a[2]; - open(aSave, ">>$fName") || return("Can't open $fName: $!"); - - print aSave "\n\n#======== store device data:".$devName." === from: ".TimeNow(); - my @eNames; - push @eNames,$devName; - foreach my $e (CUL_HM_getAssChnIds($name)){ - my $eName = CUL_HM_id2Name($e); - push @eNames, $eName if($eName !~ m/_chn:/); - } + open(aSave, ">>$fName") || return("Can't open $fName: $!"); - foreach my $eName (@eNames){ - print aSave "\n#--- entity:".$eName; - my $pIds = AttrVal($eName, "peerIDs", ""); - my $timestamps = "\n# timestamp of the readings for reference"; - if ($pIds){ - print aSave "\n# Peer Names:" - .($defs{$name}{peerList}?$defs{$name}{peerList}:""); - $timestamps .= "\n# " - .($defs{$eName}{peerList}?$defs{$eName}{peerList}:"") - ." :peerList"; - print aSave "\nset ".$eName." peerBulk ".$pIds; - } - my $ehash = $defs{$eName}; - foreach my $read (sort grep(/^[\.]?RegL_/,keys %{$ehash->{READINGS}})){ - print aSave "\nset ".$eName." regBulk ".$read." " - .ReadingsVal($eName,$read,""); - $timestamps .= "\n# ".ReadingsTimestamp($eName,$read,"")." :".$read; - } - print aSave $timestamps; - } - print aSave "\n======= finished ===\n"; - close(aSave); + print aSave "\n\n#======== store device data:".$devName." === from: ".TimeNow(); + my @eNames; + push @eNames,$devName; + foreach my $e (CUL_HM_getAssChnIds($name)){ + my $eName = CUL_HM_id2Name($e); + push @eNames, $eName if($eName !~ m/_chn:/); + } + + foreach my $eName (@eNames){ + print aSave "\n#--- entity:".$eName; + my $pIds = AttrVal($eName, "peerIDs", ""); + my $timestamps = "\n# timestamp of the readings for reference"; + if ($pIds){ + print aSave "\n# Peer Names:" + .($defs{$name}{peerList}?$defs{$name}{peerList}:""); + $timestamps .= "\n# " + .($defs{$eName}{peerList}?$defs{$eName}{peerList}:"") + ." :peerList"; + print aSave "\nset ".$eName." peerBulk ".$pIds; + } + my $ehash = $defs{$eName}; + foreach my $read (sort grep(/^[\.]?RegL_/,keys %{$ehash->{READINGS}})){ + print aSave "\nset ".$eName." regBulk ".$read." " + .ReadingsVal($eName,$read,""); + $timestamps .= "\n# ".ReadingsTimestamp($eName,$read,"")." :".$read; + } + print aSave $timestamps; + } + print aSave "\n======= finished ===\n"; + close(aSave); } Log3 $name,4,"CUL_HM get $name " . join(" ", @a[1..$#a]); @@ -2126,7 +2125,7 @@ sub CUL_HM_Set($@) { my $ret; return "no set value specified" if(@a < 2); my $name = $hash->{NAME}; - return "device ignored due to attr 'ignore'" + return "device ignored due to attr 'ignore'" if (CUL_HM_getAttrInt($name,"ignore")); my $devName = $hash->{device}?$hash->{device}:$name; my $st = AttrVal($devName, "subType", ""); @@ -2155,14 +2154,14 @@ sub CUL_HM_Set($@) { my @h; @h = split(" ", $h) if($h); my @postCmds=(); #Commands to be appended after regSet (ugly...) - + if(!defined($h) && defined($culHmSubTypeSets{$st}{pct}) && $cmd =~ m/^\d+/) { - splice @a, 1, 0,"pct";#insert the actual command - } + splice @a, 1, 0,"pct";#insert the actual command + } elsif(!defined($h)) { my @arr1 = (); - - if( $st ne "virtual") {foreach(keys %culHmGlobalSets ){push @arr1,"$_:$culHmGlobalSets{$_}" }}; + + if( $st ne "virtual") {foreach(keys %culHmGlobalSets ){push @arr1,"$_:$culHmGlobalSets{$_}" }}; if(($st eq "virtual"||!$st) && $roleD){foreach(keys %culHmGlobalSetsVrtDev ){push @arr1,"$_:$culHmGlobalSetsVrtDev{$_}" }}; if( $st ne "virtual" && $roleD){foreach(keys %culHmGlobalSetsDevice ){push @arr1,"$_:$culHmGlobalSetsDevice{$_}" }}; if( $st ne "virtual" && $roleD){foreach(keys %{$culHmSubTypeDevSets{$st}}){push @arr1,"$_:${$culHmSubTypeDevSets{$st}}{$_}"}}; @@ -2171,38 +2170,38 @@ sub CUL_HM_Set($@) { if( $culHmModelSets{$md}) {foreach(keys %{$culHmModelSets{$md}} ){push @arr1,"$_:${$culHmModelSets{$md}}{$_}" }}; if( $culHmChanSets{$md."00"} && $roleD){foreach(keys %{$culHmChanSets{$md."00"}} ){push @arr1,"$_:".${$culHmChanSets{$md."00"}}{$_}}}; if( $culHmChanSets{$md.$chn} && $roleC){foreach(keys %{$culHmChanSets{$md.$chn}} ){push @arr1,"$_:".${$culHmChanSets{$md.$chn}}{$_}}}; - @arr1 = CUL_HM_noDup(@arr1); - foreach(@arr1){ - my ($cmd,$val) = split(":",$_,2); - if (!$val || - $val !~ m/^\[.*\]$/ || - $val =~ m/\[.*\[/ || - $val =~ m/(\<|\>)]/ - ){ - $_ = $cmd; - } - else{ - $val =~ s/(\[|\])//g; - my @vArr = split('\|',$val); - foreach (@vArr){ - if ($_ =~ m/(.*)\.\.(.*)/ ){ - my @list = map { ($_.".0", $_+0.5) } ($1..$2); - pop @list; - $_ = join(",",@list); - } - } - $_ = "$cmd:".join(",",@vArr); - } - } - my $usg = "Unknown argument $cmd, choose one of ".join(" ",sort @arr1); + @arr1 = CUL_HM_noDup(@arr1); + foreach(@arr1){ + my ($cmd,$val) = split(":",$_,2); + if (!$val || + $val !~ m/^\[.*\]$/ || + $val =~ m/\[.*\[/ || + $val =~ m/(\<|\>)]/ + ){ + $_ = $cmd; + } + else{ + $val =~ s/(\[|\])//g; + my @vArr = split('\|',$val); + foreach (@vArr){ + if ($_ =~ m/(.*)\.\.(.*)/ ){ + my @list = map { ($_.".0", $_+0.5) } ($1..$2); + pop @list; + $_ = join(",",@list); + } + } + $_ = "$cmd:".join(",",@vArr); + } + } + my $usg = "Unknown argument $cmd, choose one of ".join(" ",sort @arr1); $usg =~ s/ pct/ pct:slider,0,1,100/; $usg =~ s/ virtual/ virtual:slider,1,1,40/; return $usg; - } + } elsif($h eq "" && @a != 2) { return "$cmd requires no parameters"; - } + } elsif($h !~ m/\.\.\./ && @h != @a-2) { return "$cmd requires parameter: $h"; } @@ -2210,29 +2209,29 @@ sub CUL_HM_Set($@) { #convert 'old' commands to current methodes like regSet and regBulk... # Unify the interface if( $cmd eq "sign"){ - splice @a,1,0,"regSet";# make hash,regSet,reg,value + splice @a,1,0,"regSet";# make hash,regSet,reg,value } elsif($cmd eq "unpair"){ - splice @a,1,3, ("regSet","pairCentral","000000"); + splice @a,1,3, ("regSet","pairCentral","000000"); } elsif($cmd eq "ilum") { ################################################# reg - return "$a[2] not specified. choose 0-15 for brightness" if ($a[2]>15); - return "$a[3] not specified. choose 0-127 for duration" if ($a[3]>127); - return "unsupported for channel, use $devName" if (!$roleD); - splice @a,1,3, ("regBulk","RegL_00:",sprintf("04:%02X",$a[2]),sprintf("08:%02X",$a[3]*2)); - } + return "$a[2] not specified. choose 0-15 for brightness" if ($a[2]>15); + return "$a[3] not specified. choose 0-127 for duration" if ($a[3]>127); + return "unsupported for channel, use $devName" if (!$roleD); + splice @a,1,3, ("regBulk","RegL_00:",sprintf("04:%02X",$a[2]),sprintf("08:%02X",$a[3]*2)); + } elsif($cmd eq "text") { ################################################# reg my ($bn,$l1, $l2) = ($chn,$a[2],$a[3]); # Create CONFIG_WRITE_INDEX string - if ($roleD){# if used on device. + if ($roleD){# if used on device. return "$a[2] is not a button number" if($a[2] !~ m/^\d*$/ || $a[2] < 1); return "$a[3] is not on or off" if($a[3] !~ m/^(on|off)$/); $bn = $a[2]*2-($a[3] eq "on" ? 0 : 1); - ($l1, $l2) = ($a[4],$a[5]); - $chn = sprintf("%02X",$bn) - } - else{ - return "to many parameter. Try set $a[0] text $a[2] $a[3]" if($a[4]); - } + ($l1, $l2) = ($a[4],$a[5]); + $chn = sprintf("%02X",$bn) + } + else{ + return "to many parameter. Try set $a[0] text $a[2] $a[3]" if($a[4]); + } my $s = 54; $l1 = substr($l1."\x00", 0, 13); @@ -2242,47 +2241,47 @@ sub CUL_HM_Set($@) { $l2 = substr($l2."\x00", 0, 13); $l2 =~ s/(.)/sprintf(" %02X:%02X",$s++,ord($1))/ge; - @a = ($a[0],"regBulk","RegL_01:",split(" ",$l1.$l2)); + @a = ($a[0],"regBulk","RegL_01:",split(" ",$l1.$l2)); } elsif($cmd =~ m /(displayMode|displayTemp|displayTempUnit|controlMode)/) { if ($md eq "HM-CC-TC"){#controlMode different for RT - splice @a,1,3, ("regSet",$a[1],$a[2]); - push @postCmds,"++803F$id${dst}0204".sprintf("%02X",CUL_HM_secSince2000()); - } + splice @a,1,3, ("regSet",$a[1],$a[2]); + push @postCmds,"++803F$id${dst}0204".sprintf("%02X",CUL_HM_secSince2000()); + } } elsif($cmd eq "partyMode") { ################################################ - my ($eH,$eM,$days,$prep) = ("","","",""); + my ($eH,$eM,$days,$prep) = ("","","",""); if ($a[2] =~ m/^(prep|exec)$/){ - $prep = $a[2]; - splice @a,2,1;#remove prep - } - $days = $a[3]; - ($eH,$eM) = split(':',$a[2]); + $prep = $a[2]; + splice @a,2,1;#remove prep + } + $days = $a[3]; + ($eH,$eM) = split(':',$a[2]); - return "$eM illegal - use 00 or 30 minutes only" if ($eM !~ m/^(00|30)$/); - return "$eH illegal - hour must be between 0 and 23" if ($eH < 0 || $eH > 23); - return "$days illegal - days must be between 0 and 200" if ($days < 0 || $days > 200); - $eH += 128 if ($eM eq "30"); - my $cHash = CUL_HM_id2Hash($dst."02"); - $cHash->{helper}{partyReg} = sprintf("61%02X62%02X0000",$eH,$days); - $cHash->{helper}{partyReg} =~ s/(..)(..)/ $1:$2/g; - if ($cHash->{READINGS}{"RegL_06:"}){#remove old settings - $cHash->{READINGS}{"RegL_06:"}{VAL} =~ s/ 61:.*//; - $cHash->{READINGS}{"RegL_06:"}{VAL} =~ s/ 00:00//; - $cHash->{READINGS}{"RegL_06:"}{VAL} .= $cHash->{helper}{partyReg}; - } - else{ - $cHash->{READINGS}{"RegL_06:"}{VAL} = $cHash->{helper}{partyReg}; - } - CUL_HM_pushConfig($hash,$id,$dst,2,"000000","00",6, - sprintf("61%02X62%02X",$eH,$days),$prep); + return "$eM illegal - use 00 or 30 minutes only" if ($eM !~ m/^(00|30)$/); + return "$eH illegal - hour must be between 0 and 23" if ($eH < 0 || $eH > 23); + return "$days illegal - days must be between 0 and 200" if ($days < 0 || $days > 200); + $eH += 128 if ($eM eq "30"); + my $cHash = CUL_HM_id2Hash($dst."02"); + $cHash->{helper}{partyReg} = sprintf("61%02X62%02X0000",$eH,$days); + $cHash->{helper}{partyReg} =~ s/(..)(..)/ $1:$2/g; + if ($cHash->{READINGS}{"RegL_06:"}){#remove old settings + $cHash->{READINGS}{"RegL_06:"}{VAL} =~ s/ 61:.*//; + $cHash->{READINGS}{"RegL_06:"}{VAL} =~ s/ 00:00//; + $cHash->{READINGS}{"RegL_06:"}{VAL} .= $cHash->{helper}{partyReg}; + } + else{ + $cHash->{READINGS}{"RegL_06:"}{VAL} = $cHash->{helper}{partyReg}; + } + CUL_HM_pushConfig($hash,$id,$dst,2,"000000","00",6, + sprintf("61%02X62%02X",$eH,$days),$prep); splice @a,1,3, ("regSet","controlMode","party"); splice @a,2,0, ($prep) if ($prep); - push @postCmds,"++803F$id${dst}0204".sprintf("%02X",CUL_HM_secSince2000()); - } + push @postCmds,"++803F$id${dst}0204".sprintf("%02X",CUL_HM_secSince2000()); + } $cmd = $a[1];# get converted command - + #if chn cmd is executed on device but refers to a channel? my $chnHash = (!$isChannel && $modules{CUL_HM}{defptr}{$dst."01"})? $modules{CUL_HM}{defptr}{$dst."01"}:$hash; @@ -2291,426 +2290,426 @@ sub CUL_HM_Set($@) { if ($cmd eq "raw") { ##################################################### return "Usage: set $a[0] $cmd data [data ...]" if(@a < 3); - $state = ""; + $state = ""; foreach (@a[2..$#a]) { - CUL_HM_PushCmdStack($hash, $_); + CUL_HM_PushCmdStack($hash, $_); } - } + } elsif($cmd eq "clear") { #################################################### my (undef,undef,$sect) = @a; - if ($sect eq "readings"){ - my @cH = ($hash); - push @cH,$defs{$hash->{$_}} foreach(grep /^channel/,keys %{$hash}); + if ($sect eq "readings"){ + my @cH = ($hash); + push @cH,$defs{$hash->{$_}} foreach(grep /^channel/,keys %{$hash}); - delete $_->{READINGS}foreach (@cH); - } - elsif($sect eq "register"){ - my @cH = ($hash); - push @cH,$defs{$hash->{$_}} foreach(grep /^channel/,keys %{$hash}); + delete $_->{READINGS}foreach (@cH); + } + elsif($sect eq "register"){ + my @cH = ($hash); + push @cH,$defs{$hash->{$_}} foreach(grep /^channel/,keys %{$hash}); - foreach my $h(@cH){ - delete $h->{READINGS}{$_} - foreach (grep /^(\.?)(R-|RegL)/,keys %{$h->{READINGS}}); - } - } - elsif($sect eq "msgEvents"){ - CUL_HM_respPendRm($hash); + foreach my $h(@cH){ + delete $h->{READINGS}{$_} + foreach (grep /^(\.?)(R-|RegL)/,keys %{$h->{READINGS}}); + } + } + elsif($sect eq "msgEvents"){ + CUL_HM_respPendRm($hash); - $hash->{helper}{prt}{bErr}=0; - delete $hash->{cmdStack}; - delete $hash->{EVENTS}; - delete $hash->{helper}{prt}{rspWait}; - delete $hash->{helper}{prt}{rspWaitSec}; - delete $hash->{helper}{prt}{mmcA}; - delete $hash->{helper}{prt}{mmcS}; - #rescue "protLastRcv" for action detector. - my $protLastRcv = $hash->{protLastRcv} if ($hash->{protLastRcv}); + $hash->{helper}{prt}{bErr}=0; + delete $hash->{cmdStack}; + delete $hash->{EVENTS}; + delete $hash->{helper}{prt}{rspWait}; + delete $hash->{helper}{prt}{rspWaitSec}; + delete $hash->{helper}{prt}{mmcA}; + delete $hash->{helper}{prt}{mmcS}; + #rescue "protLastRcv" for action detector. + my $protLastRcv = $hash->{protLastRcv} if ($hash->{protLastRcv}); delete ($hash->{$_}) foreach (grep(/^prot/,keys %{$hash})); - $hash->{protLastRcv} = $protLastRcv if ($protLastRcv); - if ($hash->{IODev}{NAME} && - $modules{CUL_HM}{$hash->{IODev}{NAME}} && - $modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}){ - @{$modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}} = - grep !/$name/,@{$modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}}; - } - CUL_HM_unQEntity($name,"qReqConf"); - CUL_HM_unQEntity($name,"qReqStat"); - CUL_HM_protState($hash,"Info_Cleared"); - } - elsif($sect eq "rssi"){ - delete $defs{$name}{helper}{rssi}; - delete ($hash->{$_}) foreach (grep(/^rssi/,keys %{$hash})) - } - else{ - return "unknown section. User readings, msgEvents or rssi"; - } - $state = ""; - } + $hash->{protLastRcv} = $protLastRcv if ($protLastRcv); + if ($hash->{IODev}{NAME} && + $modules{CUL_HM}{$hash->{IODev}{NAME}} && + $modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}){ + @{$modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}} = + grep !/$name/,@{$modules{CUL_HM}{$hash->{IODev}{NAME}}{pendDev}}; + } + CUL_HM_unQEntity($name,"qReqConf"); + CUL_HM_unQEntity($name,"qReqStat"); + CUL_HM_protState($hash,"Info_Cleared"); + } + elsif($sect eq "rssi"){ + delete $defs{$name}{helper}{rssi}; + delete ($hash->{$_}) foreach (grep(/^rssi/,keys %{$hash})) + } + else{ + return "unknown section. User readings, msgEvents or rssi"; + } + $state = ""; + } elsif($cmd eq "reset") { #################################################### - CUL_HM_PushCmdStack($hash,"++".$flag."11".$id.$dst."0400"); - } + CUL_HM_PushCmdStack($hash,"++".$flag."11".$id.$dst."0400"); + } elsif($cmd eq "burstXmit") { ################################################ - $state = ""; - $hash->{helper}{prt}{wakeup}=1;# start wakeup + $state = ""; + $hash->{helper}{prt}{wakeup}=1;# start wakeup CUL_HM_SndCmd($hash,"++B112$id$dst"); - } + } elsif($cmd eq "pair") { ##################################################### - $state = ""; + $state = ""; my $serialNr = AttrVal($name, "serialNr", undef); return "serialNr is not set" if(!$serialNr); CUL_HM_PushCmdStack($hash,"++A401".$id."000000010A".uc( unpack("H*",$serialNr))); $hash->{hmPairSerial} = $serialNr; - } + } elsif($cmd eq "statusRequest") { ############################################ my @chnIdList = CUL_HM_getAssChnIds($name); foreach my $channel (@chnIdList){ - my $chnNo = substr($channel,6,2); - CUL_HM_PushCmdStack($hash,"++".$flag.'01'.$id.$dst.$chnNo.'0E'); + my $chnNo = substr($channel,6,2); + CUL_HM_PushCmdStack($hash,"++".$flag.'01'.$id.$dst.$chnNo.'0E'); } - $state = ""; - } + $state = ""; + } elsif($cmd eq "getSerial") { ################################################ CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.'0009'); - $state = ""; - } + $state = ""; + } elsif($cmd eq "getConfig") { ################################################ CUL_HM_unQEntity($name,"qReqConf"); - CUL_HM_getConfig($hash); - $state = ""; - } + CUL_HM_getConfig($hash); + $state = ""; + } elsif($cmd eq "peerBulk") { ################################################# - $state = ""; - my $pL = $a[2]; - return "unknown action: $a[3] - use set or unset" - if ($a[3] && $a[3] !~ m/^(set|unset)/); - my $set = ($a[3] eq "unset")?"02":"01"; - foreach my $peer (grep(!/^self/,split(',',$pL))){ - my $pID = CUL_HM_peerChId($peer,$dst,$id); - return "unknown peer".$peer if (length($pID) != 8);# peer only to channel - my $pCh1 = substr($pID,6,2); - my $pCh2 = $pCh1; + $state = ""; + my $pL = $a[2]; + return "unknown action: $a[3] - use set or unset" + if ($a[3] && $a[3] !~ m/^(set|unset)/); + my $set = ($a[3] eq "unset")?"02":"01"; + foreach my $peer (grep(!/^self/,split(',',$pL))){ + my $pID = CUL_HM_peerChId($peer,$dst,$id); + return "unknown peer".$peer if (length($pID) != 8);# peer only to channel + my $pCh1 = substr($pID,6,2); + my $pCh2 = $pCh1; if(($culHmSubTypeSets{$st} &&$culHmSubTypeSets{$st}{peerChan} )|| ($culHmModelSets{$md} &&$culHmModelSets{$md}{peerChan} )|| ($culHmChanSets{$md.$chn} &&$culHmChanSets{$md.$chn}{peerChan}) ){ - $pCh2 = "00"; # button behavior + $pCh2 = "00"; # button behavior } - CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chn.$set. - substr($pID,0,6).$pCh1.$pCh2); - } - CUL_HM_qAutoRead($name,3); - } + CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chn.$set. + substr($pID,0,6).$pCh1.$pCh2); + } + CUL_HM_qAutoRead($name,3); + } elsif($cmd =~ m/^(regBulk|getRegRaw)$/) { ############################### reg my ($list,$addr,$data,$peerID); - $state = ""; - if ($cmd eq "regBulk"){ - $list = $a[2]; - $list =~ s/[\.]?RegL_//; - ($list,$peerID) = split(":",$list); - return "unknown list Number:".$list if(hex($list)>6); - } + $state = ""; + if ($cmd eq "regBulk"){ + $list = $a[2]; + $list =~ s/[\.]?RegL_//; + ($list,$peerID) = split(":",$list); + return "unknown list Number:".$list if(hex($list)>6); + } elsif ($cmd eq "getRegRaw"){ - ($list,$peerID) = ($a[2],$a[3]); - return "Enter valid List0-6" if ($list !~ m/^List([0-6])$/); - $list ='0'.$1; - } - # as of now only hex value allowed check range and convert + ($list,$peerID) = ($a[2],$a[3]); + return "Enter valid List0-6" if ($list !~ m/^List([0-6])$/); + $list ='0'.$1; + } + # as of now only hex value allowed check range and convert - $peerID = CUL_HM_peerChId(($peerID?$peerID:"00000000"),$dst,$id); - my $peerChn = ((length($peerID) == 8)?substr($peerID,6,2):"01");# have to split chan and id - $peerID = substr($peerID,0,6); + $peerID = CUL_HM_peerChId(($peerID?$peerID:"00000000"),$dst,$id); + my $peerChn = ((length($peerID) == 8)?substr($peerID,6,2):"01");# have to split chan and id + $peerID = substr($peerID,0,6); - if($cmd eq "getRegRaw"){ - if ($list eq "00"){ - CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.'00040000000000'); - } - else{# other lists are per channel - my @chnIdList = CUL_HM_getAssChnIds($name); + if($cmd eq "getRegRaw"){ + if ($list eq "00"){ + CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.'00040000000000'); + } + else{# other lists are per channel + my @chnIdList = CUL_HM_getAssChnIds($name); foreach my $channel (@chnIdList){ - my $chnNo = substr($channel,6,2); - if ($list =~m /0[34]/){#getPeers to see if list3 is available - CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chnNo.'03'); - my $chnHash = CUL_HM_id2Hash($channel); - $chnHash->{helper}{getCfgList} = $peerID.$peerChn;#list3 regs - $chnHash->{helper}{getCfgListNo} = int($list); - } - else{ - CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chnNo.'04' - .$peerID.$peerChn.$list); - } - } - } - } - elsif($cmd eq "regBulk"){; - my @adIn = @a; - shift @adIn;shift @adIn;shift @adIn; - my $adList; - foreach my $ad (sort @adIn){ - ($addr,$data) = split(":",$ad); - $adList .= sprintf("%02X%02X",hex($addr),hex($data)) if ($addr ne "00"); - return "wrong addr or data:".$ad if (hex($addr)>255 || hex($data)>255); - } - $chn = 0 if ($list == 0); + my $chnNo = substr($channel,6,2); + if ($list =~m /0[34]/){#getPeers to see if list3 is available + CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chnNo.'03'); + my $chnHash = CUL_HM_id2Hash($channel); + $chnHash->{helper}{getCfgList} = $peerID.$peerChn;#list3 regs + $chnHash->{helper}{getCfgListNo} = int($list); + } + else{ + CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.$chnNo.'04' + .$peerID.$peerChn.$list); + } + } + } + } + elsif($cmd eq "regBulk"){; + my @adIn = @a; + shift @adIn;shift @adIn;shift @adIn; + my $adList; + foreach my $ad (sort @adIn){ + ($addr,$data) = split(":",$ad); + $adList .= sprintf("%02X%02X",hex($addr),hex($data)) if ($addr ne "00"); + return "wrong addr or data:".$ad if (hex($addr)>255 || hex($data)>255); + } + $chn = 0 if ($list == 0); CUL_HM_pushConfig($hash,$id,$dst,hex($chn),$peerID,$peerChn,$list,$adList); - } - } + } + } elsif($cmd eq "regSet") { ############################################### reg - #set regSet [prep] [] - #prep is internal use only. It allowes to prepare shadowReg only but supress - #writing. Application necessarily needs to execute writing subsequent. - my $prep = ""; + #set regSet [prep] [] + #prep is internal use only. It allowes to prepare shadowReg only but supress + #writing. Application necessarily needs to execute writing subsequent. + my $prep = ""; if ($a[2] =~ m/^(prep|exec)$/){ - $prep = $a[2]; - splice @a,2,1;#remove prep - } + $prep = $a[2]; + splice @a,2,1;#remove prep + } - my (undef,undef,$regName,$data,$peerChnIn) = @a; - $state = ""; - if (!$culHmRegType{$st}{$regName} && - !$culHmRegGeneral{$regName} && - !$culHmRegModel{$md}{$regName} && - !$culHmRegChan{$md.$chn}{$regName} ){ + my (undef,undef,$regName,$data,$peerChnIn) = @a; + $state = ""; + if (!$culHmRegType{$st}{$regName} && + !$culHmRegGeneral{$regName} && + !$culHmRegModel{$md}{$regName} && + !$culHmRegChan{$md.$chn}{$regName} ){ my @regArr = keys %culHmRegGeneral ; - push @regArr, keys %{$culHmRegType{$st}} if($culHmRegType{$st}); - push @regArr, keys %{$culHmRegModel{$md}} if($culHmRegModel{$md}); - push @regArr, keys %{$culHmRegChan{$md.$chn}} if($culHmRegChan{$md.$chn}); - return "$regName failed: supported register are ".join(" ",sort @regArr); - } - + push @regArr, keys %{$culHmRegType{$st}} if($culHmRegType{$st}); + push @regArr, keys %{$culHmRegModel{$md}} if($culHmRegModel{$md}); + push @regArr, keys %{$culHmRegChan{$md.$chn}} if($culHmRegChan{$md.$chn}); + return "$regName failed: supported register are ".join(" ",sort @regArr); + } + my $reg = $culHmRegDefine{$regName}; - return $st." - ".$regName # give some help - .($reg->{lit}? " literal:".join(",",keys%{$reg->{lit}})." " - : " range:". $reg->{min}." to ".$reg->{max}.$reg->{u} - ) - .(($reg->{l} == 3)?" peer required":"")." : ".$reg->{t}."\n" - if ($data eq "?"); - return "value:".$data." out of range $reg->{min} to $reg->{max} for Reg \"" - .$regName."\"" - if (!($reg->{c} =~ m/^(lit|hex|min2time)$/)&& - ($data < $reg->{min} ||$data > $reg->{max})); # none number - return"invalid value. use:". join(",",sort keys%{$reg->{lit}}) - if ($reg->{c} eq 'lit' && !defined($reg->{lit}{$data})); - - $data *= $reg->{f} if($reg->{f});# obey factor befor possible conversion - my $conversion = $reg->{c}; - if (!$conversion){;# do nothing - }elsif($conversion eq "fltCvT" ){$data = CUL_HM_fltCvT($data); - }elsif($conversion eq "fltCvT60"){$data = CUL_HM_fltCvT60($data); - }elsif($conversion eq "min2time"){$data = CUL_HM_time2min($data); - }elsif($conversion eq "m10s3") {$data = $data*10-3; - }elsif($conversion eq "hex") {$data = hex($data); - }elsif($conversion eq "lit") {$data = $reg->{lit}{$data}; - }else{return " conversion undefined - please contact admin"; - } - - my $addr = int($reg->{a}); # bit location later - my $list = $reg->{l}; - my $bit = ($reg->{a}*10)%10; # get fraction - - my $dLen = $reg->{s}; # datalength in bit - $dLen = int($dLen)*8+(($dLen*10)%10); - # only allow it level if length less then one byte!! - return "partial Word error: ".$dLen if($dLen != 8*int($dLen/8) && $dLen>7); - no warnings qw(overflow portable); - my $mask = (0xffffffff>>(32-$dLen)); - use warnings qw(overflow portable); - my $dataStr = substr(sprintf("%08X",($data & $mask) << $bit), - 8-int($reg->{s}+0.99)*2,); - + return $st." - ".$regName # give some help + .($reg->{lit}? " literal:".join(",",keys%{$reg->{lit}})." " + : " range:". $reg->{min}." to ".$reg->{max}.$reg->{u} + ) + .(($reg->{l} == 3)?" peer required":"")." : ".$reg->{t}."\n" + if ($data eq "?"); + return "value:".$data." out of range $reg->{min} to $reg->{max} for Reg \"" + .$regName."\"" + if (!($reg->{c} =~ m/^(lit|hex|min2time)$/)&& + ($data < $reg->{min} ||$data > $reg->{max})); # none number + return"invalid value. use:". join(",",sort keys%{$reg->{lit}}) + if ($reg->{c} eq 'lit' && !defined($reg->{lit}{$data})); + + $data *= $reg->{f} if($reg->{f});# obey factor befor possible conversion + my $conversion = $reg->{c}; + if (!$conversion){;# do nothing + }elsif($conversion eq "fltCvT" ){$data = CUL_HM_fltCvT($data); + }elsif($conversion eq "fltCvT60"){$data = CUL_HM_fltCvT60($data); + }elsif($conversion eq "min2time"){$data = CUL_HM_time2min($data); + }elsif($conversion eq "m10s3") {$data = $data*10-3; + }elsif($conversion eq "hex") {$data = hex($data); + }elsif($conversion eq "lit") {$data = $reg->{lit}{$data}; + }else{return " conversion undefined - please contact admin"; + } + + my $addr = int($reg->{a}); # bit location later + my $list = $reg->{l}; + my $bit = ($reg->{a}*10)%10; # get fraction + + my $dLen = $reg->{s}; # datalength in bit + $dLen = int($dLen)*8+(($dLen*10)%10); + # only allow it level if length less then one byte!! + return "partial Word error: ".$dLen if($dLen != 8*int($dLen/8) && $dLen>7); + no warnings qw(overflow portable); + my $mask = (0xffffffff>>(32-$dLen)); + use warnings qw(overflow portable); + my $dataStr = substr(sprintf("%08X",($data & $mask) << $bit), + 8-int($reg->{s}+0.99)*2,); + my ($lChn,$peerId,$peerChn) = ($chn,"000000","00"); - if (($list == 3) ||($list == 4)){ # peer is necessary for list 3/4 - return "Peer not specified" if ($peerChnIn eq ""); - $peerId = CUL_HM_peerChId($peerChnIn,$dst,$id); - $peerChn = ((length($peerId) == 8)?substr($peerId,6,2):"01"); + if (($list == 3) ||($list == 4)){ # peer is necessary for list 3/4 + return "Peer not specified" if ($peerChnIn eq ""); + $peerId = CUL_HM_peerChId($peerChnIn,$dst,$id); + $peerChn = ((length($peerId) == 8)?substr($peerId,6,2):"01"); $peerId = substr($peerId,0,6); - return "Peer not valid" if (!$peerId); - } - elsif($list == 0){ + return "Peer not valid" if (!$peerId); + } + elsif($list == 0){ $lChn = "00"; - } - else{ #if($list == 1/5/6){ + } + else{ #if($list == 1/5/6){ $lChn = "01" if ($chn eq "00"); #by default select chan 01 for device - } + } my $addrData; - if ($dLen < 8){# fractional byte see whether we have stored the register - #read full 8 bit!!! - my $rName = CUL_HM_id2Name($dst.$lChn); - $rName =~ s/_chn:.*//; - my $curVal = CUL_HM_getRegFromStore($rName,$addr,$list,$peerId.$peerChn); - return "cannot calculate value. Please issue set $name getConfig first - $curVal" - if ($curVal !~ m/^(set_|)(\d+)$/); - $curVal = $2; # we expect one byte in int, strap 'set_' possibly - $data = ($curVal & (~($mask<<$bit)))|($data<<$bit); - $addrData.=sprintf("%02X%02X",$addr,$data); - } - else{ - for (my $cnt = 0;$cnt{s}+0.99);$cnt++){ - $addrData.=sprintf("%02X",$addr+$cnt).substr($dataStr,$cnt*2,2); - } - } + if ($dLen < 8){# fractional byte see whether we have stored the register + #read full 8 bit!!! + my $rName = CUL_HM_id2Name($dst.$lChn); + $rName =~ s/_chn:.*//; + my $curVal = CUL_HM_getRegFromStore($rName,$addr,$list,$peerId.$peerChn); + return "cannot calculate value. Please issue set $name getConfig first - $curVal" + if ($curVal !~ m/^(set_|)(\d+)$/); + $curVal = $2; # we expect one byte in int, strap 'set_' possibly + $data = ($curVal & (~($mask<<$bit)))|($data<<$bit); + $addrData.=sprintf("%02X%02X",$addr,$data); + } + else{ + for (my $cnt = 0;$cnt{s}+0.99);$cnt++){ + $addrData.=sprintf("%02X",$addr+$cnt).substr($dataStr,$cnt*2,2); + } + } $lChn = "00" if($list == 7);#face to send - my $cHash = CUL_HM_id2Hash($dst.($lChn eq '00'?"":$lChn)); - $cHash = $hash if (!$cHash); + my $cHash = CUL_HM_id2Hash($dst.($lChn eq '00'?"":$lChn)); + $cHash = $hash if (!$cHash); CUL_HM_pushConfig($cHash,$id,$dst,$lChn,$peerId,hex($peerChn),$list - ,$addrData,$prep); + ,$addrData,$prep); CUL_HM_PushCmdStack($hash,$_) foreach(@postCmds);#ugly commands after regSet - } + } elsif($cmd eq "level") { #################################################### - #level =>" ..." - my (undef,undef,$lvl,$rLocDly,$speed) = @a; - return "please enter level 0 to 100" if (!defined($lvl) || $lvl>100); - return "reloclDelay range 0..65535 or ignore" - if (defined($rLocDly) && - ($rLocDly > 65535 || - ($rLocDly < 0.1 && $rLocDly ne 'ignore' && $rLocDly ne '0' ))); - return "select speed range 0 to 100" if (defined($speed) && $speed>100); + #level =>" ..." + my (undef,undef,$lvl,$rLocDly,$speed) = @a; + return "please enter level 0 to 100" if (!defined($lvl) || $lvl>100); + return "reloclDelay range 0..65535 or ignore" + if (defined($rLocDly) && + ($rLocDly > 65535 || + ($rLocDly < 0.1 && $rLocDly ne 'ignore' && $rLocDly ne '0' ))); + return "select speed range 0 to 100" if (defined($speed) && $speed>100); $rLocDly = 111600 if (!defined($rLocDly)||$rLocDly eq "ignore");# defaults $speed = 30 if (!defined($rLocDly)); - $rLocDly = CUL_HM_encodeTime8($rLocDly);# calculate hex value + $rLocDly = CUL_HM_encodeTime8($rLocDly);# calculate hex value CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'81'.$chn. - sprintf("%02X%02s%02X",$lvl*2,$rLocDly,$speed*2)); - } + sprintf("%02X%02s%02X",$lvl*2,$rLocDly,$speed*2)); + } elsif($cmd =~ m/^(on|off|toggle)$/) { ####################################### my $lvl = ($cmd eq 'on') ? 'C8': - (($cmd eq 'off') ? '00':(CUL_HM_getChnLvl($name) != 0 ?"00":"C8")); + (($cmd eq 'off') ? '00':(CUL_HM_getChnLvl($name) != 0 ?"00":"C8")); CUL_HM_PushCmdStack($hash,"++$flag"."11$id$dst"."02$chn$lvl".'0000'); - $hash = $chnHash; # report to channel if defined + $hash = $chnHash; # report to channel if defined } elsif($cmd =~ m/^(on-for-timer|on-till)$/) { ################################ my (undef,undef,$duration,$edate) = @a; #date prepared extention to entdate if ($cmd eq "on-till"){ - # to be extended to handle end date as well - my ($eH,$eM,$eSec) = split(':',$duration); - return "please enter time informat hh:mm:ss" if (!$eSec); - $eSec += $eH*3600 + $eM*60; - my @lt = localtime; - my $ltSec = $lt[2]*3600+$lt[1]*60+$lt[0];# actually strip of date - $eSec += 3600*24 if ($ltSec > $eSec); # go for the next day - $duration = $eSec - $ltSec; + # to be extended to handle end date as well + my ($eH,$eM,$eSec) = split(':',$duration); + return "please enter time informat hh:mm:ss" if (!$eSec); + $eSec += $eH*3600 + $eM*60; + my @lt = localtime; + my $ltSec = $lt[2]*3600+$lt[1]*60+$lt[0];# actually strip of date + $eSec += 3600*24 if ($ltSec > $eSec); # go for the next day + $duration = $eSec - $ltSec; } - return "please enter the duration in seconds" - if (!defined $duration || $duration !~ m/^[+-]?\d+(\.\d+)?$/); + return "please enter the duration in seconds" + if (!defined $duration || $duration !~ m/^[+-]?\d+(\.\d+)?$/); my $tval = CUL_HM_encodeTime16($duration);# onTime 0.0..85825945.6, 0=forever - return "timer value to low" if ($tval eq "0000"); + return "timer value to low" if ($tval eq "0000"); CUL_HM_PushCmdStack($hash,"++$flag"."11$id$dst"."02$chn"."C80000$tval"); - $hash = $chnHash; # report to channel if defined - } + $hash = $chnHash; # report to channel if defined + } elsif($cmd eq "lock") { ##################################################### - CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'800100FF'); # LEVEL_SET + CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'800100FF'); # LEVEL_SET } elsif($cmd eq "unlock") { ################################################### - my $tval = (@a > 2) ? int($a[2]) : 0; - my $delay = ($tval > 0) ? CUL_HM_encodeTime8($tval) : "FF"; # RELOCK_DELAY (FF=never) + my $tval = (@a > 2) ? int($a[2]) : 0; + my $delay = ($tval > 0) ? CUL_HM_encodeTime8($tval) : "FF"; # RELOCK_DELAY (FF=never) CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'800101'.$delay);# LEVEL_SET } elsif($cmd eq "open") { ##################################################### - my $tval = (@a > 2) ? int($a[2]) : 0; - my $delay = ($tval > 0) ? CUL_HM_encodeTime8($tval) : "FF"; # RELOCK_DELAY (FF=never) + my $tval = (@a > 2) ? int($a[2]) : 0; + my $delay = ($tval > 0) ? CUL_HM_encodeTime8($tval) : "FF"; # RELOCK_DELAY (FF=never) CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'8001C8'.$delay);# OPEN } elsif($cmd eq "inhibit") { ################################################## - return "$a[2] is not on or off" if($a[2] !~ m/^(on|off)$/); - my $val = ($a[2] eq "on") ? "01" : "00"; - CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.$val.$chn); # SET_LOCK + return "$a[2] is not on or off" if($a[2] !~ m/^(on|off)$/); + my $val = ($a[2] eq "on") ? "01" : "00"; + CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.$val.$chn); # SET_LOCK } elsif($cmd =~ m/^(up|down|pct)$/) { ######################################### - my ($lvl,$tval,$rval) = ($a[2],"",""); - if ($cmd ne "pct"){#dim [] ... [ontime] [ramptime] - $lvl = 10 if (!defined $a[2]); #set default step - $lvl = -1*$lvl if ($cmd eq "down"); - $lvl += CUL_HM_getChnLvl($name); - } + my ($lvl,$tval,$rval) = ($a[2],"",""); + if ($cmd ne "pct"){#dim [] ... [ontime] [ramptime] + $lvl = 10 if (!defined $a[2]); #set default step + $lvl = -1*$lvl if ($cmd eq "down"); + $lvl += CUL_HM_getChnLvl($name); + } $lvl = ($lvl > 100)?100:(($lvl < 0)?0:$lvl); - if ($st eq "dimmer"){# at least blind cannot stand ramp time... + if ($st eq "dimmer"){# at least blind cannot stand ramp time... $tval = $a[3]?CUL_HM_encodeTime16($a[3]):"FFFF";# onTime 0.05..85825945.6, 0=forever $rval = CUL_HM_encodeTime16((@a > 4)?$a[4]:2.5);# rampTime 0.0..85825945.6, 0=immediate - } + } CUL_HM_PushCmdStack($hash,sprintf("++%s11%s%s02%s%02X%s%s", - $flag,$id,$dst,$chn,$lvl*2,$rval,$tval)); + $flag,$id,$dst,$chn,$lvl*2,$rval,$tval)); readingsSingleUpdate($hash,"level","set_".$lvl,1); - $state = "set_".$lvl; - } + $state = "set_".$lvl; + } elsif($cmd eq "stop") { ##################################################### CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'03'.$chn); - } + } elsif($cmd eq "setRepeat") { ################################################ # setRepeat => "[no1..36] [bdcast-yes|no]"} $state = ""; - my (undef,undef,$eNo,$sId,$rId,$bCst) = @a; - my ($pattern,$cnt); - my $repPeers = AttrVal($name,"repPeers",undef); - my @rPeer; - @rPeer = split ",",$repPeers; - if ($eNo eq "setAll"){ - return " too many entries in repPeer" if (int(@rPeer) > 36); - return "setAll: attr repPeers undefined" if (!defined $repPeers); - my $entry = 0; - foreach my $repData (@rPeer){ - $entry++; - my ($s,$d,$b) =split":",$repData; - $s = CUL_HM_name2Id($s); - $d = CUL_HM_name2Id($d); - return "attr repPeers entry $entry irregular:$repData" - if (!$s || !$d || !$b - || $s !~ m/(^[0-9A-F]{6})$/ - || $d !~ m/(^[0-9A-F]{6})$/ - || $b !~ m/^[yn]$/ - ); - $pattern .= $s.$d.(($b eq "n")?"00":"01"); - } - while ($entry < 36){ - $entry++; - $pattern .= "000000"."000000"."00"; - } - $cnt = 1;# set first address byte - } - else{ - return "entry must be between 1 and 36" if ($eNo < 1 || $eNo > 36); - my $sndID = CUL_HM_name2Id($sId); - my $recID = CUL_HM_name2Id($rId); - if ($sndID !~ m/(^[0-9A-F]{6})$/){$sndID = AttrVal($sId,"hmId","");}; - if ($recID !~ m/(^[0-9A-F]{6})$/){$recID = AttrVal($rId,"hmId","");}; - return "sender ID $sId unknown:".$sndID if ($sndID !~ m/(^[0-9A-F]{6})$/); - return "receiver ID $rId unknown:".$recID if ($recID !~ m/(^[0-9A-F]{6})$/); + my (undef,undef,$eNo,$sId,$rId,$bCst) = @a; + my ($pattern,$cnt); + my $repPeers = AttrVal($name,"repPeers",undef); + my @rPeer; + @rPeer = split ",",$repPeers; + if ($eNo eq "setAll"){ + return " too many entries in repPeer" if (int(@rPeer) > 36); + return "setAll: attr repPeers undefined" if (!defined $repPeers); + my $entry = 0; + foreach my $repData (@rPeer){ + $entry++; + my ($s,$d,$b) =split":",$repData; + $s = CUL_HM_name2Id($s); + $d = CUL_HM_name2Id($d); + return "attr repPeers entry $entry irregular:$repData" + if (!$s || !$d || !$b + || $s !~ m/(^[0-9A-F]{6})$/ + || $d !~ m/(^[0-9A-F]{6})$/ + || $b !~ m/^[yn]$/ + ); + $pattern .= $s.$d.(($b eq "n")?"00":"01"); + } + while ($entry < 36){ + $entry++; + $pattern .= "000000"."000000"."00"; + } + $cnt = 1;# set first address byte + } + else{ + return "entry must be between 1 and 36" if ($eNo < 1 || $eNo > 36); + my $sndID = CUL_HM_name2Id($sId); + my $recID = CUL_HM_name2Id($rId); + if ($sndID !~ m/(^[0-9A-F]{6})$/){$sndID = AttrVal($sId,"hmId","");}; + if ($recID !~ m/(^[0-9A-F]{6})$/){$recID = AttrVal($rId,"hmId","");}; + return "sender ID $sId unknown:".$sndID if ($sndID !~ m/(^[0-9A-F]{6})$/); + return "receiver ID $rId unknown:".$recID if ($recID !~ m/(^[0-9A-F]{6})$/); return "broadcast must be yes or now" if ($bCst !~ m/^(yes|no)$/); - $pattern = $sndID.$recID.(($bCst eq "no")?"00":"01"); + $pattern = $sndID.$recID.(($bCst eq "no")?"00":"01"); $cnt = ($eNo-1)*7+1; - $rPeer[$eNo-1] = "$sId:$rId:".(($bCst eq "no")?"n":"y"); - $attr{$name}{repPeers} = join",",@rPeer; - } - my $addrData; + $rPeer[$eNo-1] = "$sId:$rId:".(($bCst eq "no")?"n":"y"); + $attr{$name}{repPeers} = join",",@rPeer; + } + my $addrData; foreach ($pattern =~ /(.{2})/g){ $addrData .= sprintf("%02X%s",$cnt++,$_); } CUL_HM_pushConfig($hash, $id, $dst, 1,0,0,2, $addrData); } - elsif($cmd eq "display") { ################################################## - my (undef,undef,undef,$t,$c,$u,$snd,$blk,$symb) = @_; - return "cmd only possible for device or its display channel" - if ($isChannel && $chn ne "12"); - my %symbol=(off => 0x0000, - bulb =>0x0100,switch =>0x0200,window =>0x0400,door=>0x0800, + elsif($cmd eq "display") { ################################################## + my (undef,undef,undef,$t,$c,$u,$snd,$blk,$symb) = @_; + return "cmd only possible for device or its display channel" + if ($isChannel && $chn ne "12"); + my %symbol=(off => 0x0000, + bulb =>0x0100,switch =>0x0200,window =>0x0400,door=>0x0800, blind=>0x1000,scene =>0x2000,phone =>0x4000,bell=>0x8000, clock=>0x0001,arrowUp=>0x0002,arrowDown=>0x0004); my %light=(off=>0,on=>1,slow=>2,fast=>3); my %unit=(off =>0,Proz=>1,Watt=>2,x3=>3,C=>4,x5=>5,x6=>6,x7=>7, - F=>8,x9=>9,x10=>10,x11=>11,x12=>12,x13=>13,x14=>14,x15=>15); - + F=>8,x9=>9,x10=>10,x11=>11,x12=>12,x13=>13,x14=>14,x15=>15); + my @symbList = split(',',$symb); my $symbAdd = ""; foreach my $symb (@symbList){ if (!defined($symbol{$symb})){# wrong parameter - return "'$symb ' unknown. Select one of ".join(" ",sort keys(%symbol)); - } + return "'$symb ' unknown. Select one of ".join(" ",sort keys(%symbol)); + } $symbAdd |= $symbol{$symb}; } - - return "$c not specified. Select one of [comma|no]" - if ($c ne "comma" && $c ne "no"); - return "'$u' unknown. Select one of ".join(" ",sort keys(%unit)) - if (!defined($unit{$u})); + + return "$c not specified. Select one of [comma|no]" + if ($c ne "comma" && $c ne "no"); + return "'$u' unknown. Select one of ".join(" ",sort keys(%unit)) + if (!defined($unit{$u})); return "'$snd' unknown. Select one of [off|1|2|3]" if ($snd ne "off" && $snd > 3); return "'$blk' unknown. Select one of ".join(" ",sort keys(%light)) @@ -2718,215 +2717,215 @@ sub CUL_HM_Set($@) { my $beepBack = $snd | $light{$blk}*4; $symbAdd |= 0x0004 if ($c eq "comma"); - $symbAdd |= $unit{$u}; - - my $text = sprintf("%5.5s",$t);#pad left with space - $text = uc(unpack("H*",$text)); + $symbAdd |= $unit{$u}; + + my $text = sprintf("%5.5s",$t);#pad left with space + $text = uc(unpack("H*",$text)); CUL_HM_PushCmdStack($hash,sprintf("++%s11%s%s8012%s%04X%02X", - $flag,$id,$dst,$text,$symbAdd,$beepBack)); - } + $flag,$id,$dst,$text,$symbAdd,$beepBack)); + } elsif($cmd =~ m/^(alarm|service)$/) { ####################################### - return "$a[2] must be below 255" if ($a[2] >255 ); - $chn = 18 if ($chn eq "01"); - my $subtype = ($cmd eq "alarm")?"81":"82"; + return "$a[2] must be below 255" if ($a[2] >255 ); + $chn = 18 if ($chn eq "01"); + my $subtype = ($cmd eq "alarm")?"81":"82"; CUL_HM_PushCmdStack($hash, sprintf("++%s11%s%s%s%s%02X",$flag,$id,$dst,$subtype,$chn, $a[2])); } - elsif($cmd eq "led") { ###################################################### - if ($md eq "HM-OU-LED16"){ - my %color=(off=>0,red=>1,green=>2,orange=>3); - if (length($hash->{DEF}) == 6){# command called for a device, not a channel - my $col4all; - if (defined($color{$a[2]})){ - $col4all = sprintf("%02X",$color{$a[2]}*85);#Color for 4 LEDS - $col4all = $col4all.$col4all.$col4all.$col4all;#and now for 16 - } - elsif ($a[2] =~ m/^[A-Fa-f0-9]{1,8}$/i){ - $col4all = sprintf("%08X",hex($a[2])); - } + elsif($cmd eq "led") { ###################################################### + if ($md eq "HM-OU-LED16"){ + my %color=(off=>0,red=>1,green=>2,orange=>3); + if (length($hash->{DEF}) == 6){# command called for a device, not a channel + my $col4all; + if (defined($color{$a[2]})){ + $col4all = sprintf("%02X",$color{$a[2]}*85);#Color for 4 LEDS + $col4all = $col4all.$col4all.$col4all.$col4all;#and now for 16 + } + elsif ($a[2] =~ m/^[A-Fa-f0-9]{1,8}$/i){ + $col4all = sprintf("%08X",hex($a[2])); + } else{ - return "$a[2] unknown. use hex or: ".join(" ",sort keys(%color)); - } - CUL_HM_UpdtReadBulk($hash,1,"color:".$col4all, - "state:set_".$col4all); + return "$a[2] unknown. use hex or: ".join(" ",sort keys(%color)); + } + CUL_HM_UpdtReadBulk($hash,1,"color:".$col4all, + "state:set_".$col4all); CUL_HM_PushCmdStack($hash,"++".$flag."11".$id.$dst."8100".$col4all); - } - else{# operating on a channel - return "$a[2] unknown. use: ".join(" ",sort keys(%color)) - if (!defined($color{$a[2]}) ); + } + else{# operating on a channel + return "$a[2] unknown. use: ".join(" ",sort keys(%color)) + if (!defined($color{$a[2]}) ); CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'80'.$chn.'0'.$color{$a[2]}); - } - } - elsif($md eq "HM-OU-CFM-PL"){ - my %color = (redL =>18,greenL =>34,orangeL =>50, - redS =>17,greenS =>33,orangeS =>49, - pause=>01); - my @itemList = split(',',$a[2]); - my $repeat = (defined $a[3] && $a[3] =~ m/^(\d+)$/)?$a[3]:1; + } + } + elsif($md eq "HM-OU-CFM-PL"){ + my %color = (redL =>18,greenL =>34,orangeL =>50, + redS =>17,greenS =>33,orangeS =>49, + pause=>01); + my @itemList = split(',',$a[2]); + my $repeat = (defined $a[3] && $a[3] =~ m/^(\d+)$/)?$a[3]:1; my $itemCnt = int(@itemList); - return "no more then 10 entries please" if ($itemCnt>10); - return "at least one entry must be entered" if ($itemCnt<1); - return "repetition $repeat out of range [1..255]" - if($repeat < 1 || $repeat > 255); + return "no more then 10 entries please" if ($itemCnt>10); + return "at least one entry must be entered" if ($itemCnt<1); + return "repetition $repeat out of range [1..255]" + if($repeat < 1 || $repeat > 255); # - my $msgBytes = sprintf("01%02X",$repeat); - foreach my $led (@itemList){ + my $msgBytes = sprintf("01%02X",$repeat); + foreach my $led (@itemList){ if (!$color{$led} ){# wrong parameter - return "'$led' unknown. use: ".join(" ",sort keys(%color)); - } + return "'$led' unknown. use: ".join(" ",sort keys(%color)); + } $msgBytes .= sprintf("%02X",$color{$led}); - } - $msgBytes .= "01" if ($itemCnt == 1 && $repeat == 1);#add pause to term LED - # need to fill up empty locations for LED channel - $msgBytes = substr($msgBytes."000000000000000000",0,(10+2)*2); + } + $msgBytes .= "01" if ($itemCnt == 1 && $repeat == 1);#add pause to term LED + # need to fill up empty locations for LED channel + $msgBytes = substr($msgBytes."000000000000000000",0,(10+2)*2); CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'80'.$chn.$msgBytes); - } - else{ - return "device for command cannot be identified"; - } - } + } + else{ + return "device for command cannot be identified"; + } + } elsif($cmd eq "playTone") { ################################################# - my $msg; - if ($a[2] eq 'replay'){ - $msg = ReadingsVal($chnHash->{NAME},".lastTone",""); - } - else{ - my @itemList = split(',',$a[2]); - my $repeat = (defined $a[3] && $a[3] =~ m/^(\d+)$/)?$a[3]:1; + my $msg; + if ($a[2] eq 'replay'){ + $msg = ReadingsVal($chnHash->{NAME},".lastTone",""); + } + else{ + my @itemList = split(',',$a[2]); + my $repeat = (defined $a[3] && $a[3] =~ m/^(\d+)$/)?$a[3]:1; my $itemCnt = int(@itemList); - return "no more then 12 entries please" if ($itemCnt>12); - return "repetition $repeat out of range [1..255]" - if($repeat < 1 || $repeat > 255); + return "no more then 12 entries please" if ($itemCnt>12); + return "repetition $repeat out of range [1..255]" + if($repeat < 1 || $repeat > 255); # - my $msgBytes = sprintf("%02X%02X",$itemCnt,$repeat); - foreach my $mp3 (@itemList){ + my $msgBytes = sprintf("%02X%02X",$itemCnt,$repeat); + foreach my $mp3 (@itemList){ $msgBytes .= sprintf("%02X",$mp3); - } - $msg = '++'.$flag.'11'.$id.$dst.'80'.$chn.$msgBytes; - CUL_HM_UpdtReadSingle($chnHash,".lastTone",$msg,0); - } + } + $msg = '++'.$flag.'11'.$id.$dst.'80'.$chn.$msgBytes; + CUL_HM_UpdtReadSingle($chnHash,".lastTone",$msg,0); + } CUL_HM_PushCmdStack($hash,$msg) if ($msg); - } + } elsif($cmd =~ m/^(controlMode|controlManu|controlParty)$/) { ################ my $mode = $a[2]; if ($cmd ne "controlMode"){ - $mode = substr($a[1],7); + $mode = substr($a[1],7); $a[2] = ($a[2] eq "off")?4.5:($a[2] eq "on"?30.5:$a[2]); - } - $mode = lc $mode; + } + $mode = lc $mode; return "invalid $mode:select of mode [auto|boost|day|night] or" - ." controlManu,controlParty" + ." controlManu,controlParty" if ($mode !~ m/^(auto|manu|party|boost|day|night)$/); my ($temp,$party); - if ($mode =~ m/^(auto|boost|day|night)$/){ - return "no additional params for $mode" if ($a[3]); - } - if($mode eq "manu"){ - return "temperatur for manu 4.5 to 30.5 C" - if (!$a[2] || $a[2] < 4.5 || $a[2] > 30.5); - $temp = $a[2]*2; - } - elsif($mode eq "party"){ - return "use party \n" + if ($mode =~ m/^(auto|boost|day|night)$/){ + return "no additional params for $mode" if ($a[3]); + } + if($mode eq "manu"){ + return "temperatur for manu 4.5 to 30.5 C" + if (!$a[2] || $a[2] < 4.5 || $a[2] > 30.5); + $temp = $a[2]*2; + } + elsif($mode eq "party"){ + return "use party \n" ."temperatur: 5 to 30 C\n" - ."date format: party 10 03.8.13 11:30 5.8.13 12:00" - if (!$a[2] || $a[2] < 5 || $a[2] > 30 || !$a[6] ); - $temp = $a[2]*2; - # party format 03.8.13 11:30 5.8.13 12:00 - my ($sd,$sm,$sy) = split('\.',$a[3]); - my ($sh,$smin) = split(':' ,$a[4]); - my ($ed,$em,$ey) = split('\.',$a[5]); - my ($eh,$emin) = split(':' ,$a[6]); - - return "wrong start day $sd" if ($sd < 0 || $sd > 31); - return "wrong start month $sm" if ($sm < 0 || $sm > 12); - return "wrong start year $sy" if ($sy < 0 || $sy > 99); - return "wrong start hour $sh" if ($sh < 0 || $sh > 23); - return "wrong start minute $smin, ony 00 or 30" if ($smin != 0 && $smin != 30); + ."date format: party 10 03.8.13 11:30 5.8.13 12:00" + if (!$a[2] || $a[2] < 5 || $a[2] > 30 || !$a[6] ); + $temp = $a[2]*2; + # party format 03.8.13 11:30 5.8.13 12:00 + my ($sd,$sm,$sy) = split('\.',$a[3]); + my ($sh,$smin) = split(':' ,$a[4]); + my ($ed,$em,$ey) = split('\.',$a[5]); + my ($eh,$emin) = split(':' ,$a[6]); + + return "wrong start day $sd" if ($sd < 0 || $sd > 31); + return "wrong start month $sm" if ($sm < 0 || $sm > 12); + return "wrong start year $sy" if ($sy < 0 || $sy > 99); + return "wrong start hour $sh" if ($sh < 0 || $sh > 23); + return "wrong start minute $smin, ony 00 or 30" if ($smin != 0 && $smin != 30); $sh = $sh * 2 + $smin/30; - - return "wrong end day $ed" if ($ed < 0 || $ed > 31); - return "wrong end month $em" if ($em < 0 || $em > 12); - return "wrong end year $ey" if ($ey < 0 || $ey > 99); - return "wrong end hour $eh" if ($eh < 0 || $eh > 23); - return "wrong end minute $emin, ony 00 or 30" if ($emin != 0 && $emin != 30); + + return "wrong end day $ed" if ($ed < 0 || $ed > 31); + return "wrong end month $em" if ($em < 0 || $em > 12); + return "wrong end year $ey" if ($ey < 0 || $ey > 99); + return "wrong end hour $eh" if ($eh < 0 || $eh > 23); + return "wrong end minute $emin, ony 00 or 30" if ($emin != 0 && $emin != 30); $eh = $eh * 2 + $emin/30; - - $party = sprintf("%02X%02X%02X%02X%02X%02X%02X", - $sh,$sd,$sy,$eh,$ed,$ey,($sm*16+$em)); - } - my %mCmd = (auto=>0,manu=>1,party=>2,boost=>3,day=>4,night=>5); + + $party = sprintf("%02X%02X%02X%02X%02X%02X%02X", + $sh,$sd,$sy,$eh,$ed,$ey,($sm*16+$em)); + } + my %mCmd = (auto=>0,manu=>1,party=>2,boost=>3,day=>4,night=>5); readingsSingleUpdate($hash,"mode","set_".$mode,1); - my $msg = '8'.($mCmd{$mode}).$chn; - $msg .= sprintf("%02X",$temp) if ($temp); - $msg .= $party if ($party); + my $msg = '8'.($mCmd{$mode}).$chn; + $msg .= sprintf("%02X",$temp) if ($temp); + $msg .= $party if ($party); CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.$msg); } elsif($cmd eq "desired-temp") { ############################################# - if ($md =~ m/HM-CC-RT-DN/){ - my $temp = ($a[2] eq "off")?9:($a[2] eq "on"?61:$a[2]*2); - return "invalid temp:$a[2]" if($temp <9 ||$temp > 61); - $temp = sprintf ("%02X",$temp); + if ($md =~ m/HM-CC-RT-DN/){ + my $temp = ($a[2] eq "off")?9:($a[2] eq "on"?61:$a[2]*2); + return "invalid temp:$a[2]" if($temp <9 ||$temp > 61); + $temp = sprintf ("%02X",$temp); CUL_HM_PushCmdStack($hash,'++'.$flag."11$id$dst"."8604$temp"); - } - else{ - my $temp = CUL_HM_convTemp($a[2]); - return $temp if($temp =~ m/Invalid/); + } + else{ + my $temp = CUL_HM_convTemp($a[2]); + return $temp if($temp =~ m/Invalid/); CUL_HM_PushCmdStack($hash,'++'.$flag.'11'.$id.$dst.'0202'.$temp); my $chnHash = CUL_HM_id2Hash($dst."02"); - my $mode = ReadingsVal($chnHash->{NAME},"R-controlMode",""); - $mode =~ s/set_//;#consider set as given - readingsSingleUpdate($chnHash,"desired-temp-cent",$a[2],1) - if($mode =~ m/central/); - } - } + my $mode = ReadingsVal($chnHash->{NAME},"R-controlMode",""); + $mode =~ s/set_//;#consider set as given + readingsSingleUpdate($chnHash,"desired-temp-cent",$a[2],1) + if($mode =~ m/central/); + } + } elsif($cmd =~ m/^tempList(...)/) { ###################################### reg my $wd = $1; - $state= ""; + $state= ""; my ($list,$addr,$prgChn); - if ($md =~ m/HM-CC-RT-DN/){ - my %day2off = ( "Sat"=>"20", "Sun"=>"46", "Mon"=>"72", "Tue"=>"98", - "Wed"=>"124","Thu"=>"150","Fri"=>"176"); - ($list,$addr,$prgChn) = (7,$day2off{$wd},0); - } - else{ + if ($md =~ m/HM-CC-RT-DN/){ + my %day2off = ( "Sat"=>"20", "Sun"=>"46", "Mon"=>"72", "Tue"=>"98", + "Wed"=>"124","Thu"=>"150","Fri"=>"176"); + ($list,$addr,$prgChn) = (7,$day2off{$wd},0); + } + else{ my %day2off = ( "Sat"=>"5 0B", "Sun"=>"5 3B", "Mon"=>"5 6B", "Tue"=>"5 9B", "Wed"=>"5 CB", "Thu"=>"6 01", "Fri"=>"6 31"); ($list,$addr) = split(" ", $day2off{$wd},2); - $prgChn = 2; + $prgChn = 2; $addr = hex($addr); - } + } - my $prep = ""; + my $prep = ""; if ($a[2] =~ m/^(prep|exec)$/){ - $prep = $a[2]; - splice @a,2,1;#remove prep - } + $prep = $a[2]; + splice @a,2,1;#remove prep + } return "To few arguments" if(@a < 4); return "To many arguments, max 24 pairs" if(@a > (($md =~ m/HM-CC-RT-DN/)?28:50)); return "Bad format, use HH:MM TEMP ..." if(@a % 2); return "Last time spec must be 24:00" if($a[@a-2] ne "24:00"); - + my ($data,$msg) = ("",""); for(my $idx = 2; $idx < @a; $idx += 2) { return "$a[$idx] is not in HH:MM format" if($a[$idx] !~ m/^([0-2]\d):([0-5]\d)/); my ($h, $m) = ($1, $2); - my ($hByte,$lByte); - my $temp = $a[$idx+1]; - if ($md =~ m/HM-CC-RT-DN/){ - $temp = (int($temp*2)<<9) + ($h*12+($m/5)); - $hByte = $temp>>8; - $lByte = $temp & 0xff; - } - else{ + my ($hByte,$lByte); + my $temp = $a[$idx+1]; + if ($md =~ m/HM-CC-RT-DN/){ + $temp = (int($temp*2)<<9) + ($h*12+($m/5)); + $hByte = $temp>>8; + $lByte = $temp & 0xff; + } + else{ $temp = CUL_HM_convTemp($temp); - return $temp if($temp =~ m/Invalid/); - $hByte = $h*6+($m/10); - $lByte = hex($temp); - } + return $temp if($temp =~ m/Invalid/); + $hByte = $h*6+($m/10); + $lByte = hex($temp); + } $data .= sprintf("%02X%02X%02X%02X", $addr, $hByte, $addr+1,$lByte); $addr += 2; @@ -2936,40 +2935,40 @@ sub CUL_HM_Set($@) { $msg .= sprintf(" %02d:%02d %.1f", $h, $m, $a[$idx+1]); } CUL_HM_pushConfig($hash, $id, $dst, $prgChn,0,0,$list, $data,$prep); - } + } elsif($cmd eq "sysTime") { ################################################## $state = ""; - my $s2000 = sprintf("%02X", CUL_HM_secSince2000()); + my $s2000 = sprintf("%02X", CUL_HM_secSince2000()); CUL_HM_PushCmdStack($hash,"++A03F$id${dst}0204$s2000"); - } + } elsif($cmd eq "valvePos") { ################################################# - return "only number <= 100 or 'off' allowed" - if (!($a[2] eq "off" ||$a[2]+0 ne $a[2] ||$a[2] <100 )); + return "only number <= 100 or 'off' allowed" + if (!($a[2] eq "off" ||$a[2]+0 ne $a[2] ||$a[2] <100 )); if ($a[2] eq "off"){ - $state = "ValveAdjust:stopped"; - RemoveInternalTimer("valvePos:$dst$chn");# remove responsePending timer - delete($hash->{helper}{virtTC}); - } - else { - my $vp = $a[2]; - readingsSingleUpdate($hash,"valvePosTC","$vp %",0); - CUL_HM_valvePosUpdt("valvePos:$dst$chn") if (!$hash->{helper}{virtTC}); - $hash->{helper}{virtTC} = "03"; - $state = "ValveAdjust:$vp %"; - } - } + $state = "ValveAdjust:stopped"; + RemoveInternalTimer("valvePos:$dst$chn");# remove responsePending timer + delete($hash->{helper}{virtTC}); + } + else { + my $vp = $a[2]; + readingsSingleUpdate($hash,"valvePosTC","$vp %",0); + CUL_HM_valvePosUpdt("valvePos:$dst$chn") if (!$hash->{helper}{virtTC}); + $hash->{helper}{virtTC} = "03"; + $state = "ValveAdjust:$vp %"; + } + } elsif($cmd eq "matic") { #################################################### # Trigger pre-programmed action in the winmatic. These actions must be # programmed via the original software. CUL_HM_PushCmdStack($hash, sprintf("++%s3E%s%s%s40%02X%s", $flag,$id, $dst, $id, $a[2], $chn)); - } + } elsif($cmd eq "create") { ################################################### - CUL_HM_PushCmdStack($hash, + CUL_HM_PushCmdStack($hash, sprintf("++%s01%s%s0101%s%02X%s",$flag,$id, $dst, $id, $a[2], $chn)); CUL_HM_PushCmdStack($hash, sprintf("++A001%s%s0104%s%02X%s", $id, $dst, $id, $a[2], $chn)); - } + } elsif($cmd eq "keydef") { ############################################### reg if ( $a[3] eq "tilt") {CUL_HM_pushConfig($hash,$id,$dst,1,$id,$a[2],3,"0B220D838B228D83");#JT_ON/OFF/RAMPON/RAMPOFF short and long } elsif ($a[3] eq "close") {CUL_HM_pushConfig($hash,$id,$dst,1,$id,$a[2],3,"0B550D838B558D83");#JT_ON/OFF/RAMPON/RAMPOFF short and long @@ -2981,223 +2980,223 @@ sub CUL_HM_Set($@) { } else { return 'unknown argument '.$a[3]; } - } + } elsif($cmd eq "test") { ##################################################### my $testnr = $hash->{TESTNR} ? ($hash->{TESTNR} +1) : 1; $hash->{TESTNR} = $testnr; - my $msg = sprintf("++9440%s%s00%02X",$dst,$dst,$testnr); + my $msg = sprintf("++9440%s%s00%02X",$dst,$dst,$testnr); CUL_HM_PushCmdStack($hash, $msg);# repeat non-ack messages 3 times CUL_HM_PushCmdStack($hash, $msg); CUL_HM_PushCmdStack($hash, $msg); - } + } elsif($cmd =~ m/alarm(.*)/) { ############################################### my $msg = sprintf("++9441%s%s01%s",$dst,$dst,(($1 eq "On")?"0BC8":"0C01")); CUL_HM_PushCmdStack($hash, $msg);# repeat non-ack messages 3 times CUL_HM_PushCmdStack($hash, $msg); CUL_HM_PushCmdStack($hash, $msg); - } + } elsif($cmd eq "virtual") { ################################################## - $state = ""; + $state = ""; my (undef,undef,$maxBtnNo) = @a; - return "please give a number between 1 and 255" - if ($maxBtnNo < 1 ||$maxBtnNo > 255);# arbitrary - 255 should be max + return "please give a number between 1 and 255" + if ($maxBtnNo < 1 ||$maxBtnNo > 255);# arbitrary - 255 should be max return $name." already defines as ".$attr{$name}{subType} - if ($attr{$name}{subType} && $attr{$name}{subType} ne "virtual"); + if ($attr{$name}{subType} && $attr{$name}{subType} ne "virtual"); $attr{$name}{subType} = "virtual"; $attr{$name}{model} = "virtual_".$maxBtnNo; my $devId = $hash->{DEF}; for (my $btn=1;$btn <= $maxBtnNo;$btn++){ - my $chnName = $name."_Btn".$btn; - my $chnId = $devId.sprintf("%02X",$btn); - DoTrigger("global", "UNDEFINED $chnName CUL_HM $chnId") - if (!$modules{CUL_HM}{defptr}{$chnId}); - } - foreach my $channel (keys %{$hash}){# remove higher numbers - my $chNo = $1 if($channel =~ m/^channel_(.*)/); - next if (!defined($chNo)); - CommandDelete(undef,$hash->{$channel}) - if (hex($chNo) > $maxBtnNo); - } + my $chnName = $name."_Btn".$btn; + my $chnId = $devId.sprintf("%02X",$btn); + DoTrigger("global", "UNDEFINED $chnName CUL_HM $chnId") + if (!$modules{CUL_HM}{defptr}{$chnId}); + } + foreach my $channel (keys %{$hash}){# remove higher numbers + my $chNo = $1 if($channel =~ m/^channel_(.*)/); + next if (!defined($chNo)); + CommandDelete(undef,$hash->{$channel}) + if (hex($chNo) > $maxBtnNo); + } } elsif($cmd eq "press") { #################################################### my (undef,undef,$mode,$vChn) = @a; - my $pressCnt = (!$hash->{helper}{count}?1:$hash->{helper}{count}+1)%256; - $hash->{helper}{count}=$pressCnt;# remember for next round + my $pressCnt = (!$hash->{helper}{count}?1:$hash->{helper}{count}+1)%256; + $hash->{helper}{count}=$pressCnt;# remember for next round - my @peerList; - if ($st eq 'virtual'){#serve all peers of virtual button - foreach my $peer (sort(split(',',AttrVal($name,"peerIDs","")))) { - push (@peerList,substr($peer,0,6)); - } - @peerList = CUL_HM_noDup(@peerList); - push @peerList,'00000000' if (!@peerList);#send to broadcast if no peer - foreach my $peer (sort @peerList){ - my $peerFlag = $peer eq '00000000'?'A4': - CUL_HM_getFlag(CUL_HM_id2Hash($peer)); - $peerFlag =~ s/0/4/;# either 'A4' or 'B4' + my @peerList; + if ($st eq 'virtual'){#serve all peers of virtual button + foreach my $peer (sort(split(',',AttrVal($name,"peerIDs","")))) { + push (@peerList,substr($peer,0,6)); + } + @peerList = CUL_HM_noDup(@peerList); + push @peerList,'00000000' if (!@peerList);#send to broadcast if no peer + foreach my $peer (sort @peerList){ + my $peerFlag = $peer eq '00000000'?'A4': + CUL_HM_getFlag(CUL_HM_id2Hash($peer)); + $peerFlag =~ s/0/4/;# either 'A4' or 'B4' CUL_HM_PushCmdStack($hash, sprintf("++%s40%s%s%02X%02X", - $peerFlag,$dst,$peer, - $chn+(($mode && $mode eq "long")?64:0), - $pressCnt)); - } - } - else{#serve internal channels for actor - my $pChn = $chn; # simple device, only one button per channel - $pChn = (($vChn && $vChn eq "off")?-1:0) + $chn*2 - if($st eq 'blindActuator'||$st eq 'dimmer'); + $peerFlag,$dst,$peer, + $chn+(($mode && $mode eq "long")?64:0), + $pressCnt)); + } + } + else{#serve internal channels for actor + my $pChn = $chn; # simple device, only one button per channel + $pChn = (($vChn && $vChn eq "off")?-1:0) + $chn*2 + if($st eq 'blindActuator'||$st eq 'dimmer'); CUL_HM_PushCmdStack($hash, sprintf("++%s3E%s%s%s40%02X%02X",$flag, - $id,$dst,$dst, + $id,$dst,$dst, $pChn+(($mode && $mode eq "long")?64:0), - $pressCnt)); - } - } + $pressCnt)); + } + } elsif($cmd eq "postEvent") { ################################################ #General add thermal event simulator - my (undef,undef,$cond) = @a; - if ($cond =~ m/[+-]?\d+/){ - return "condition value:$cond above 200 illegal" if ($cond > 200); - } - else{ - my $val; - my @keys; - foreach my $tp (keys %lvlStr){ - foreach my $mk (keys %{$lvlStr{$tp}}){ - foreach (keys %{$lvlStr{$tp}{$mk}}){ - $val = hex($_) if ($cond eq $lvlStr{$tp}{$mk}{$_}); - push @keys,$lvlStr{$tp}{$mk}{$_}; - } - } - } - return "cond:$cond not allowed. choose one of:[0..200]," - .join(",",sort @keys) - if (!defined $val); - $cond = $val; - } - my $pressCnt = (!$hash->{helper}{count}?1:$hash->{helper}{count}+1)%256; - $hash->{helper}{count}=$pressCnt;# remember for next round - - my @peerLChn = split(',',AttrVal($name,"peerIDs","")); - my @peerDev; - push (@peerDev,substr($_,0,6)) foreach (@peerLChn); - @peerDev = CUL_HM_noDup(@peerDev);#only once per device! - - push @peerDev,'000000' if (!@peerDev);#send to broadcast if no peer - foreach my $peer (@peerDev){ - my $pHash = CUL_HM_id2Hash($peer); - my $peerFlag = $peer eq '00000000'?'A4':CUL_HM_getFlag($pHash); - $peerFlag =~ s/0/4/;# either 'A4' or 'B4' - CUL_HM_SndCmd($pHash, "++B112$id".substr($peer,0,6)) - if (CUL_HM_getRxType($pHash) & 0x80); - CUL_HM_SndCmd($pHash, sprintf("++%s41%s%s%02X%02X%02X" - ,$peerFlag,$dst,$peer - ,$chn - ,$pressCnt - ,$cond)); - } + my (undef,undef,$cond) = @a; + if ($cond =~ m/[+-]?\d+/){ + return "condition value:$cond above 200 illegal" if ($cond > 200); + } + else{ + my $val; + my @keys; + foreach my $tp (keys %lvlStr){ + foreach my $mk (keys %{$lvlStr{$tp}}){ + foreach (keys %{$lvlStr{$tp}{$mk}}){ + $val = hex($_) if ($cond eq $lvlStr{$tp}{$mk}{$_}); + push @keys,$lvlStr{$tp}{$mk}{$_}; + } + } + } + return "cond:$cond not allowed. choose one of:[0..200]," + .join(",",sort @keys) + if (!defined $val); + $cond = $val; + } + my $pressCnt = (!$hash->{helper}{count}?1:$hash->{helper}{count}+1)%256; + $hash->{helper}{count}=$pressCnt;# remember for next round - foreach my $peer (@peerLChn){#inform each channel - my $pName = CUL_HM_id2Name($peer); - $pName = CUL_HM_id2Name(substr($peer,0,6)) if (!$defs{$pName}); - next if (!$defs{$pName}); - CUL_HM_UpdtReadBulk($defs{$pName},1 - ,"trig_$name:$cond" - ,"trigLast:$name:$cond"); - } - } + my @peerLChn = split(',',AttrVal($name,"peerIDs","")); + my @peerDev; + push (@peerDev,substr($_,0,6)) foreach (@peerLChn); + @peerDev = CUL_HM_noDup(@peerDev);#only once per device! + + push @peerDev,'000000' if (!@peerDev);#send to broadcast if no peer + foreach my $peer (@peerDev){ + my $pHash = CUL_HM_id2Hash($peer); + my $peerFlag = $peer eq '00000000'?'A4':CUL_HM_getFlag($pHash); + $peerFlag =~ s/0/4/;# either 'A4' or 'B4' + CUL_HM_SndCmd($pHash, "++B112$id".substr($peer,0,6)) + if (CUL_HM_getRxType($pHash) & 0x80); + CUL_HM_SndCmd($pHash, sprintf("++%s41%s%s%02X%02X%02X" + ,$peerFlag,$dst,$peer + ,$chn + ,$pressCnt + ,$cond)); + } + + foreach my $peer (@peerLChn){#inform each channel + my $pName = CUL_HM_id2Name($peer); + $pName = CUL_HM_id2Name(substr($peer,0,6)) if (!$defs{$pName}); + next if (!$defs{$pName}); + CUL_HM_UpdtReadBulk($defs{$pName},1 + ,"trig_$name:$cond" + ,"trigLast:$name:$cond"); + } + } elsif($cmd eq "peerChan") { ############################################# reg #peerChan ... [single|dual] [set|unset] [actor|remote|both] - my ($bNo,$peerN,$single,$set,$target) = ($a[2],$a[3],$a[4],$a[5],$a[6]); - $state = ""; - return "$bNo is not a button number" if(($bNo < 1) && !$roleC); - my $peerId = CUL_HM_name2Id($peerN); + my ($bNo,$peerN,$single,$set,$target) = ($a[2],$a[3],$a[4],$a[5],$a[6]); + $state = ""; + return "$bNo is not a button number" if(($bNo < 1) && !$roleC); + my $peerId = CUL_HM_name2Id($peerN); return "please enter peer" if(!$peerId); - $peerId .= "01" if( length($peerId)==6); - - my ($peerChn,$peerBtn,$peerHash,$myBtn); - my $peerDst = substr($peerId,0,6); + $peerId .= "01" if( length($peerId)==6); - if ($md =~ m/HM-CC-RT-DN/ && $chn eq "05" ){# rt team peers cross from 05 to 04 - $myBtn = $peerBtn = "04"; - $peerChn = "05"; - } - else{ # normal devices - $peerBtn = $peerChn = substr($peerId,6,2); # chan peeredd to remote - $myBtn = $chn; - } - $peerHash = $modules{CUL_HM}{defptr}{$peerDst.$peerChn}if ($modules{CUL_HM}{defptr}{$peerDst.$peerChn}); + my ($peerChn,$peerBtn,$peerHash,$myBtn); + my $peerDst = substr($peerId,0,6); + + if ($md =~ m/HM-CC-RT-DN/ && $chn eq "05" ){# rt team peers cross from 05 to 04 + $myBtn = $peerBtn = "04"; + $peerChn = "05"; + } + else{ # normal devices + $peerBtn = $peerChn = substr($peerId,6,2); # chan peeredd to remote + $myBtn = $chn; + } + $peerHash = $modules{CUL_HM}{defptr}{$peerDst.$peerChn}if ($modules{CUL_HM}{defptr}{$peerDst.$peerChn}); $peerHash = $modules{CUL_HM}{defptr}{$peerDst} if (!$peerHash); - - return "$peerN not a CUL_HM device" if($target && ($target ne "remote") &&(!$peerHash ||$peerHash->{TYPE} ne "CUL_HM")); + + return "$peerN not a CUL_HM device" if($target && ($target ne "remote") &&(!$peerHash ||$peerHash->{TYPE} ne "CUL_HM")); return "$single must be single or dual" if(defined($single) && ($single !~ m/^(single|dual)$/)); return "$set must be set or unset" if(defined($set) && ($set !~ m/^(set|unset)$/)); return "$target must be [actor|remote|both]" if(defined($target) && ($target !~ m/^(actor|remote|both)$/)); - return "use climate chan to pair TC" if( $md eq "HM-CC-TC" && $myBtn ne "02"); - return "use - single [set|unset] actor - for smoke detector" if( $st eq "smokeDetector" && (!$single || $single ne "single" || $target ne "actor")); - return "use - single - for ".$st if(($st =~ m/(threeStateSensor|thermostat|motionDetector)/) - && (!$single || $single ne "single")); - my $pSt = CUL_HM_Get($peerHash,$peerHash->{NAME},"param","subType"); - - $single = ($single eq "single")?1:"";#default to dual - $set = ($set && $set eq "unset")?0:1; + return "use climate chan to pair TC" if( $md eq "HM-CC-TC" && $myBtn ne "02"); + return "use - single [set|unset] actor - for smoke detector" if( $st eq "smokeDetector" && (!$single || $single ne "single" || $target ne "actor")); + return "use - single - for ".$st if(($st =~ m/(threeStateSensor|thermostat|motionDetector)/) + && (!$single || $single ne "single")); + my $pSt = CUL_HM_Get($peerHash,$peerHash->{NAME},"param","subType"); - my ($b1,$b2,$nrCh2Pair); - $b1 = ($roleC) ? hex($myBtn) : ($single?$bNo : ($bNo*2 - 1)); - if ($single){ - $b2 = $b1; - $b1 = 0 if ($st eq "smokeDetector" ||$pSt eq "smokeDetector"); - $nrCh2Pair = 1; - } - else{ - $b2 = $b1 + 1; - $nrCh2Pair = 2; - } - $target = "both" if ($st eq "virtual" && $pSt eq "smokeDetector"); - my $cmdB = ($set)?"01":"02";# do we set or remove? + $single = ($single eq "single")?1:"";#default to dual + $set = ($set && $set eq "unset")?0:1; + + my ($b1,$b2,$nrCh2Pair); + $b1 = ($roleC) ? hex($myBtn) : ($single?$bNo : ($bNo*2 - 1)); + if ($single){ + $b2 = $b1; + $b1 = 0 if ($st eq "smokeDetector" ||$pSt eq "smokeDetector"); + $nrCh2Pair = 1; + } + else{ + $b2 = $b1 + 1; + $nrCh2Pair = 2; + } + $target = "both" if ($st eq "virtual" && $pSt eq "smokeDetector"); + my $cmdB = ($set)?"01":"02";# do we set or remove? # First the remote (one loop for on, one for off) - if (!$target || $target =~ m/^(remote|both)$/){ - my $burst = ($pSt eq "thermostat"?"0101":"0100");#set burst for target - my $pnb = 1 if ($culHmRegModel{$md}{peerNeedsBurst}|| #supported? - $culHmRegType{$st}{peerNeedsBurst}); + if (!$target || $target =~ m/^(remote|both)$/){ + my $burst = ($pSt eq "thermostat"?"0101":"0100");#set burst for target + my $pnb = 1 if ($culHmRegModel{$md}{peerNeedsBurst}|| #supported? + $culHmRegType{$st}{peerNeedsBurst}); for(my $i = 1; $i <= $nrCh2Pair; $i++) { - my $b = ($i==1 ? $b1 : $b2); - if ($st eq "virtual"){ - my $btnName = CUL_HM_id2Name($dst.sprintf("%02X",$b)); - return "button ".$b." not defined for virtual remote ".$name - if (!defined $attr{$btnName}); - CUL_HM_ID2PeerList ($btnName,$peerDst.$peerBtn,$set); #upd. peerlist - } - else{ - my $bStr = sprintf("%02X",$b); - CUL_HM_PushCmdStack($hash, - "++".$flag."01${id}${dst}${bStr}$cmdB${peerDst}${peerBtn}00"); - CUL_HM_pushConfig($hash,$id, $dst,$b,$peerDst,hex($peerBtn),4,$burst) - if($pnb && $cmdB eq "01"); # only if set - CUL_HM_qAutoRead($name,3); - } + my $b = ($i==1 ? $b1 : $b2); + if ($st eq "virtual"){ + my $btnName = CUL_HM_id2Name($dst.sprintf("%02X",$b)); + return "button ".$b." not defined for virtual remote ".$name + if (!defined $attr{$btnName}); + CUL_HM_ID2PeerList ($btnName,$peerDst.$peerBtn,$set); #upd. peerlist + } + else{ + my $bStr = sprintf("%02X",$b); + CUL_HM_PushCmdStack($hash, + "++".$flag."01${id}${dst}${bStr}$cmdB${peerDst}${peerBtn}00"); + CUL_HM_pushConfig($hash,$id, $dst,$b,$peerDst,hex($peerBtn),4,$burst) + if($pnb && $cmdB eq "01"); # only if set + CUL_HM_qAutoRead($name,3); + } } - } - if (!$target || $target =~ m/^(actor|both)$/ ){ - if ($pSt eq "virtual"){ - CUL_HM_ID2PeerList ($peerN,$dst.sprintf("%02X",$b2),$set); #update peerlist - CUL_HM_ID2PeerList ($peerN,$dst.sprintf("%02X",$b1),$set) if ($b1 & !$single); - } - else{ - my $peerFlag = CUL_HM_getFlag($peerHash); + } + if (!$target || $target =~ m/^(actor|both)$/ ){ + if ($pSt eq "virtual"){ + CUL_HM_ID2PeerList ($peerN,$dst.sprintf("%02X",$b2),$set); #update peerlist + CUL_HM_ID2PeerList ($peerN,$dst.sprintf("%02X",$b1),$set) if ($b1 & !$single); + } + else{ + my $peerFlag = CUL_HM_getFlag($peerHash); CUL_HM_PushCmdStack($peerHash, sprintf("++%s01%s%s%s%s%s%02X%02X", $peerFlag,$id,$peerDst,$peerChn,$cmdB,$dst,$b2,$b1 )); - CUL_HM_pushConfig($peerHash,$id,$peerDst,0,0,0,0,"0101")#set burstRx - if(CUL_HM_getRxType($peerHash) & 0x80); #if conBurst - CUL_HM_qAutoRead($peerHash->{NAME},3); - } - } - return ("",1) if ($target && $target eq "remote");#Nothing to transmit for actor + CUL_HM_pushConfig($peerHash,$id,$peerDst,0,0,0,0,"0101")#set burstRx + if(CUL_HM_getRxType($peerHash) & 0x80); #if conBurst + CUL_HM_qAutoRead($peerHash->{NAME},3); + } + } + return ("",1) if ($target && $target eq "remote");#Nothing to transmit for actor $devHash = $peerHash; # Exchange the hash, as the switch is always alive. } else{ return "$cmd not impelmented - contact sysop"; } - + readingsSingleUpdate($hash,"state",$state,1) if($state); my $rxType = CUL_HM_getRxType($devHash); @@ -3206,10 +3205,10 @@ sub CUL_HM_Set($@) { CUL_HM_ProcessCmdStack($devHash); } elsif(($rxType & 0x80) && #burstConditional - have a try - $devHash->{cmdStack} && - $devHash->{helper}{prt}{sProc} != 1 # not pocessing - ){ - $hash->{helper}{prt}{wakeup}=1;# start auto-wakeup + $devHash->{cmdStack} && + $devHash->{helper}{prt}{sProc} != 1 # not pocessing + ){ + $hash->{helper}{prt}{wakeup}=1;# start auto-wakeup CUL_HM_SndCmd($devHash,"++B112$id$dst"); } return ("",1);# no not generate trigger outof command @@ -3233,12 +3232,12 @@ sub CUL_HM_valvePosUpdt(@) {#update valve position periodically to please valve my $vp = ReadingsVal($name,"valvePosTC","15 %"); $vp =~ s/ %//; $vp *=2.56; - foreach my $peer (sort(split(',',AttrVal($name,"peerIDs","")))) { - next if (length($peer) != 8); - $peer = substr($peer,0,6); - CUL_HM_PushCmdStack($hash,sprintf("++A258%s%s%s%02X",$vDevId - ,$peer,$hash->{helper}{virtTC},$vp)); - } + foreach my $peer (sort(split(',',AttrVal($name,"peerIDs","")))) { + next if (length($peer) != 8); + $peer = substr($peer,0,6); + CUL_HM_PushCmdStack($hash,sprintf("++A258%s%s%s%02X",$vDevId + ,$peer,$hash->{helper}{virtTC},$vp)); + } # } $hash->{helper}{virtTC} = "00"; CUL_HM_ProcessCmdStack($hash); @@ -3246,43 +3245,43 @@ sub CUL_HM_valvePosUpdt(@) {#update valve position periodically to please valve } sub CUL_HM_infoUpdtDevData($$$) {#autoread config my($name,$hash,$p) = @_; - my($fw,$mId,$serNo,$stc,$devInfo) = ($1,$2,$3,$4,$5) - if($p =~ m/(..)(.{4})(.{20})(.{2})(.*)/); + my($fw,$mId,$serNo,$stc,$devInfo) = ($1,$2,$3,$4,$5) + if($p =~ m/(..)(.{4})(.{20})(.{2})(.*)/); my $md = $culHmModel{$mId}{name} ? $culHmModel{$mId}{name}:"unknown"; $attr{$name}{model} = $md; $attr{$name}{subType} = $culHmModel{$mId}{st}; $attr{$name}{serialNr} = pack('H*',$serNo); #expert level attributes - $attr{$name}{firmware} = + $attr{$name}{firmware} = sprintf("%d.%d", hex(substr($p,0,1)),hex(substr($p,1,1))); $attr{$name}{".devInfo"} = $devInfo; $attr{$name}{".stc"} = $stc; - + delete $hash->{helper}{rxType}; CUL_HM_getRxType($hash); #will update rxType - $mId = CUL_HM_getMId($hash);# set helper valiable and use result - - # autocreate undefined channels + $mId = CUL_HM_getMId($hash);# set helper valiable and use result + + # autocreate undefined channels my @chanTypesList = split(',',$culHmModel{$mId}{chn}); my $startime = gettimeofday()+1; foreach my $chantype (@chanTypesList){ my ($chnTpName,$chnStart,$chnEnd) = split(':',$chantype); - my $chnNoTyp = 1; - for (my $chnNoAbs = $chnStart; $chnNoAbs <= $chnEnd;$chnNoAbs++){ - my $chnId = $hash->{DEF}.sprintf("%02X",$chnNoAbs); - if (!$modules{CUL_HM}{defptr}{$chnId}){ + my $chnNoTyp = 1; + for (my $chnNoAbs = $chnStart; $chnNoAbs <= $chnEnd;$chnNoAbs++){ + my $chnId = $hash->{DEF}.sprintf("%02X",$chnNoAbs); + if (!$modules{CUL_HM}{defptr}{$chnId}){ my $chnName = $name."_".$chnTpName.(($chnStart == $chnEnd)? - '':'_'.sprintf("%02d",$chnNoTyp)); - InternalTimer($startime++,"CUL_HM_infoUpdtChanData", - "$chnName,$chnId,$md",0); + '':'_'.sprintf("%02d",$chnNoTyp)); + InternalTimer($startime++,"CUL_HM_infoUpdtChanData", + "$chnName,$chnId,$md",0); } - $attr{CUL_HM_id2Name($chnId)}{model} = $md; - $chnNoTyp++; - } + $attr{CUL_HM_id2Name($chnId)}{model} = $md; + $chnNoTyp++; + } } if ($culHmModel{$mId}{cyc}){ CUL_HM_ActAdd($hash->{DEF},AttrVal($name,"actCycle", - $culHmModel{$mId}{cyc})); + $culHmModel{$mId}{cyc})); } } sub CUL_HM_infoUpdtChanData(@) {# verify attributes after reboot @@ -3297,9 +3296,9 @@ sub CUL_HM_getConfig($){ my $id = CUL_HM_IOid($hash); my $dst = substr($hash->{DEF},0,6); my $name = $hash->{NAME}; - + CUL_HM_PushCmdStack($hash,'++'.$flag.'01'.$id.$dst.'00040000000000') - if ($hash->{helper}{role}{dev}); + if ($hash->{helper}{role}{dev}); my @chnIdList = CUL_HM_getAssChnIds($name); foreach my $channel (@chnIdList){ my $cHash = CUL_HM_id2Hash($channel); @@ -3312,7 +3311,7 @@ sub CUL_HM_getConfig($){ my $pReq = 0; # Peer request not issued, do only once for channel foreach my$listEntry (@list){# each list that is define for this channel my ($peerReq,$chnValid)= (0,0); - my ($listNo,$chnLst1) = split(":",$listEntry); + my ($listNo,$chnLst1) = split(":",$listEntry); if (!$chnLst1){ $chnValid = 1; #if no entry go for all channels $peerReq = 1 if($listNo eq 'p' || $listNo==3 ||$listNo==4); #default @@ -3321,29 +3320,29 @@ sub CUL_HM_getConfig($){ my @chnLst = split('\.',$chnLst1); foreach my $lchn (@chnLst){ $peerReq = 1 if ($lchn =~ m/p/); - no warnings;#know that lchan may be followed by 'p' causing a warning + no warnings;#know that lchan may be followed by 'p' causing a warning $chnValid = 1 if (int($lchn) == hex($chn)); - use warnings; + use warnings; last if ($chnValid); } } if ($chnValid){# yes, we will go for a list if ($peerReq){# need to get the peers first if($listNo ne 'p'){# not if 'only peers'! - $cHash->{helper}{getCfgList} = "all"; + $cHash->{helper}{getCfgList} = "all"; $cHash->{helper}{getCfgListNo} = $listNo; - } - if (!$pReq){#get peers first, but only once per channel - CUL_HM_PushCmdStack($cHash,sprintf("++%s01%s%s%s03" - ,$flag,$id,$dst,$chn)); - $pReq = 1; - } + } + if (!$pReq){#get peers first, but only once per channel + CUL_HM_PushCmdStack($cHash,sprintf("++%s01%s%s%s03" + ,$flag,$id,$dst,$chn)); + $pReq = 1; + } } else{ CUL_HM_PushCmdStack($cHash,sprintf("++%s01%s%s%s0400000000%02X" - ,$flag,$id,$dst,$chn,$listNo)); + ,$flag,$id,$dst,$chn,$listNo)); } - } + } } } } @@ -3373,7 +3372,7 @@ sub CUL_HM_pushConfig($$$$$$$$@) {#generate messages to config data to register my $rRd = ReadingsVal($chnhash->{NAME},$regLN,""); if (!$chnhash->{helper}{shadowReg} || !$chnhash->{helper}{shadowReg}{$regLNp}){ - $chnhash->{helper}{shadowReg}{$regLNp} = $rRd; + $chnhash->{helper}{shadowReg}{$regLNp} = $rRd; } #--- update with ne value my $regs = $chnhash->{helper}{shadowReg}{$regLNp}; @@ -3387,45 +3386,45 @@ sub CUL_HM_pushConfig($$$$$$$$@) {#generate messages to config data to register $chnhash->{helper}{shadowReg}{$regLNp} = $regs; # update shadow my @changeList; if ($prep eq "exec"){#update complete registerset - @changeList = keys%{$chnhash->{helper}{shadowReg}}; + @changeList = keys%{$chnhash->{helper}{shadowReg}}; } elsif ($prep eq "prep"){ - return; #prepare shadowReg only. More data expected. + return; #prepare shadowReg only. More data expected. } else{ - push @changeList,$regLNp; + push @changeList,$regLNp; } my $changed = 0;# did we write foreach my $nrn(@changeList){ my $change; - my $nrRd = ReadingsVal($chnhash->{NAME},$regPre.$nrn,""); + my $nrRd = ReadingsVal($chnhash->{NAME},$regPre.$nrn,""); foreach (sort split " ",$chnhash->{helper}{shadowReg}{$nrn}){ - $change .= $_." " if ($nrRd !~ m /$_/);# filter only changes - } - next if (!$change);#no changes - $change =~ s/00:00//; - $change =~ s/(\ |:)//g; - my $peerN; - $changed = 1;# yes, we did - ($list,$peerN) = ($1,$2) if($nrn =~ m/RegL_(..):(.*)/); - if ($peerN){($peerAddr,$peerChn) = unpack('A6A2', CUL_HM_name2Id($peerN,$hash));} - else {($peerAddr,$peerChn) = ('000000','00');} + $change .= $_." " if ($nrRd !~ m /$_/);# filter only changes + } + next if (!$change);#no changes + $change =~ s/00:00//; + $change =~ s/(\ |:)//g; + my $peerN; + $changed = 1;# yes, we did + ($list,$peerN) = ($1,$2) if($nrn =~ m/RegL_(..):(.*)/); + if ($peerN){($peerAddr,$peerChn) = unpack('A6A2', CUL_HM_name2Id($peerN,$hash));} + else {($peerAddr,$peerChn) = ('000000','00');} CUL_HM_updtRegDisp($hash,$list,$peerAddr.$peerChn); - ############partition -# my @chSplit = unpack('(A28)*',$change); - my @chSplit = unpack('(A1120)*',$change);# makes max 40 lines, 280 byte - foreach my $chSpl(@chSplit){ + ############partition +# my @chSplit = unpack('(A28)*',$change); + my @chSplit = unpack('(A1120)*',$change);# makes max 40 lines, 280 byte + foreach my $chSpl(@chSplit){ CUL_HM_PushCmdStack($hash, "++".$flag.'01'.$src.$dst.$chn.'05'. $peerAddr.$peerChn.$list); $tl = length($chSpl); - for(my $l = 0; $l < $tl; $l+=28) { + for(my $l = 0; $l < $tl; $l+=28) { my $ml = $tl-$l < 28 ? $tl-$l : 28; CUL_HM_PushCmdStack($hash, "++A001".$src.$dst.$chn."08". - substr($chSpl,$l,$ml)); + substr($chSpl,$l,$ml)); } CUL_HM_PushCmdStack($hash,"++A001".$src.$dst.$chn."06"); - } - ######### + } + ######### } CUL_HM_qAutoRead($hash->{NAME},3) if ($changed); } @@ -3436,7 +3435,7 @@ sub CUL_HM_PushCmdStack($$) { my $name = $hash->{NAME}; if(!$hash->{cmdStack}){# this is a new 'burst' of messages $hash->{cmdStack} = \@arr; - $hash->{helper}{prt}{bErr}=0 if ($hash->{helper}{prt}{sProc} != 1);# not processing + $hash->{helper}{prt}{bErr}=0 if ($hash->{helper}{prt}{sProc} != 1);# not processing } push(@{$hash->{cmdStack}}, $cmd); my $entries = scalar @{$hash->{cmdStack}}; @@ -3451,9 +3450,9 @@ sub CUL_HM_ProcessCmdStack($) { CUL_HM_SndCmd($hash, shift @{$hash->{cmdStack}}); } elsif(!@{$hash->{cmdStack}}) { - #-- update info --- - CUL_HM_protState($hash,"CMDs_done".($hash->{helper}{prt}{bErr}? - ("_Errors:".$hash->{helper}{prt}{bErr}):"")); + #-- update info --- + CUL_HM_protState($hash,"CMDs_done".($hash->{helper}{prt}{bErr}? + ("_Errors:".$hash->{helper}{prt}{bErr}):"")); } } return; @@ -3468,7 +3467,7 @@ sub CUL_HM_respWaitSu($@){ #setup response for multi-message response # single commands # cmd: single msg that needs to be ACKed # mNo: number of message (needs to be in ACK) - # mNoWu: number of message if wakeup + # mNoWu: number of message if wakeup # reSent: number of resends already done - usually init with 1 # wakeup: was wakeup message (burst devices) # @@ -3482,121 +3481,121 @@ sub CUL_HM_respWaitSu($@){ #setup response for multi-message response my $mHsh = $hash->{helper}{prt}; $modules{CUL_HM}{prot}{rspPend}++ if(!$mHsh->{rspWait}{cmd}); foreach (@a){ - my ($f,$d)=split ":=",$_; - $mHsh->{rspWait}{$f}=$d; + my ($f,$d)=split ":=",$_; + $mHsh->{rspWait}{$f}=$d; } my $to = gettimeofday() + (($mHsh->{rspWait}{Pending})?rand(20)/10+4: - rand(40)/10+1); - InternalTimer($to,"CUL_HM_respPendTout","respPend:$hash->{DEF}", 0); + rand(40)/10+1); + InternalTimer($to,"CUL_HM_respPendTout","respPend:$hash->{DEF}", 0); } sub CUL_HM_responseSetup($$) {#store all we need to handle the response #setup repeatTimer and cmdStackControll my ($hash,$cmd) = @_; my ($mNo,$mFlg,$mTp,$dst,$p) = ($2,hex($3),$4,$6,$7) if ($cmd =~ m/As(..)(..)(..)(..)(......)(......)(.*)/); - my ($chn,$subType) = ($1,$2) if($p =~ m/^(..)(..)/); + my ($chn,$subType) = ($1,$2) if($p =~ m/^(..)(..)/); if (($mFlg & 0x20) && ($dst ne '000000')){ - if ($mTp eq "01" && $subType){ + if ($mTp eq "01" && $subType){ if ($subType eq "03"){ #PeerList----------- - #--- remember request params in device level - CUL_HM_respWaitSu ($hash,"Pending:=PeerList" - ,"cmd:=$cmd" ,"forChn:=".substr($p,0,2) - ,"mNo:=".hex($mNo) - ,"reSent:=1"); - - #--- remove readings in channel - my $chnhash = $modules{CUL_HM}{defptr}{"$dst$chn"}; - $chnhash = $hash if (!$chnhash); - delete $chnhash->{READINGS}{peerList};#empty old list - delete $chnhash->{peerList};#empty old list - delete $chnhash->{helper}{peerIDsRaw}; - $attr{$chnhash->{NAME}}{peerIDs} = ''; + #--- remember request params in device level + CUL_HM_respWaitSu ($hash,"Pending:=PeerList" + ,"cmd:=$cmd" ,"forChn:=".substr($p,0,2) + ,"mNo:=".hex($mNo) + ,"reSent:=1"); + + #--- remove readings in channel + my $chnhash = $modules{CUL_HM}{defptr}{"$dst$chn"}; + $chnhash = $hash if (!$chnhash); + delete $chnhash->{READINGS}{peerList};#empty old list + delete $chnhash->{peerList};#empty old list + delete $chnhash->{helper}{peerIDsRaw}; + $attr{$chnhash->{NAME}}{peerIDs} = ''; } elsif($subType eq "04"){ #RegisterRead------- my ($peer, $list) = ($1,$2) if ($p =~ m/..04(........)(..)/); - $peer = ($peer ne "00000000")?CUL_HM_peerChName($peer,$dst,""):""; - #--- set messaging items - CUL_HM_respWaitSu ($hash,"Pending:=RegisterRead" - ,"cmd:=$cmd" ,"forChn:=$chn" - ,"forList:=$list","forPeer:=$peer" - ,"mNo:=".hex($mNo) - ,"reSent:=1"); - #--- remove channel entries that will be replaced + $peer = ($peer ne "00000000")?CUL_HM_peerChName($peer,$dst,""):""; + #--- set messaging items + CUL_HM_respWaitSu ($hash,"Pending:=RegisterRead" + ,"cmd:=$cmd" ,"forChn:=$chn" + ,"forList:=$list","forPeer:=$peer" + ,"mNo:=".hex($mNo) + ,"reSent:=1"); + #--- remove channel entries that will be replaced my $chnhash = $modules{CUL_HM}{defptr}{"$dst$chn"}; - $chnhash = $hash if(!$chnhash); - - $peer ="" if($list !~ m/^0[34]$/); - #empty val since reading will be cumulative - my $rlName = ((CUL_HM_getAttrInt($chnhash->{NAME},"expert") == 2)? - "":".")."RegL_".$list.":".$peer; - $chnhash->{READINGS}{$rlName}{VAL}=""; - delete ($chnhash->{READINGS}{$rlName}{TIME}); + $chnhash = $hash if(!$chnhash); + + $peer ="" if($list !~ m/^0[34]$/); + #empty val since reading will be cumulative + my $rlName = ((CUL_HM_getAttrInt($chnhash->{NAME},"expert") == 2)? + "":".")."RegL_".$list.":".$peer; + $chnhash->{READINGS}{$rlName}{VAL}=""; + delete ($chnhash->{READINGS}{$rlName}{TIME}); } elsif($subType eq "09"){ #SerialRead------- - CUL_HM_respWaitSu ($hash,"Pending:=SerialRead" - ,"cmd:=$cmd" ,"reSent:=1"); + CUL_HM_respWaitSu ($hash,"Pending:=SerialRead" + ,"cmd:=$cmd" ,"reSent:=1"); + } + else{ + CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=1"); } - else{ - CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=1"); - } } elsif($mTp eq '11' && $chn =~ m/^(02|81)$/){#!!! chn is subtype!!! # CUL_HM_qStateUpdatIfEnab($dst.$subType);# subtype actually is channel if ($p =~ m/02..(..)....(....)/){#lvl ne 0 and timer on - $hash->{helper}{tmdOn} = $2 if ($1 ne "00" && $2 !~ m/(0000|FFFF)/); - CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo" - ,"reSent:=1","timedOn:=1"); - } + $hash->{helper}{tmdOn} = $2 if ($1 ne "00" && $2 !~ m/(0000|FFFF)/); + CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo" + ,"reSent:=1","timedOn:=1"); + } + } + elsif($mTp eq '12' && $mFlg & 0x10){#wakeup with burst + # response setup - do not repeat, set counter to 250 + CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=1","wakeup:=1"); + } + else{ + CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=1"); } - elsif($mTp eq '12' && $mFlg & 0x10){#wakeup with burst - # response setup - do not repeat, set counter to 250 - CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=1","wakeup:=1"); - } - else{ - CUL_HM_respWaitSu ($hash,"cmd:=$cmd","mNo:=$mNo","reSent:=1"); - } - CUL_HM_protState($hash,"CMDs_processing..."); + CUL_HM_protState($hash,"CMDs_processing..."); } else{# no answer expected - if($hash->{cmdStack} && scalar @{$hash->{cmdStack}}){ - CUL_HM_protState($hash,"CMDs_processing..."); + if($hash->{cmdStack} && scalar @{$hash->{cmdStack}}){ + CUL_HM_protState($hash,"CMDs_processing..."); InternalTimer(gettimeofday()+.5, "CUL_HM_ProcessCmdStack", $hash, 0); - } - elsif(!$hash->{helper}{prt}{rspWait}{cmd}){ - CUL_HM_protState($hash,"CMDs_done".($hash->{helper}{prt}{bErr}? - ("_Errors:".$hash->{helper}{prt}{bErr}):"")); - } + } + elsif(!$hash->{helper}{prt}{rspWait}{cmd}){ + CUL_HM_protState($hash,"CMDs_done".($hash->{helper}{prt}{bErr}? + ("_Errors:".$hash->{helper}{prt}{bErr}):"")); + } } my $mmcS = $hash->{helper}{prt}{mmcS}?$hash->{helper}{prt}{mmcS}:0; if ($mTp eq '01'){ my ($chn,$sTp) = unpack 'A2A2',$p; - my $oCmd = "++".substr($cmd,6); - if ($sTp eq "05"){ - my @arr = ($oCmd); - $hash->{helper}{prt}{mmcA}=\@arr; - $hash->{helper}{prt}{mmcS} = 1; - } - elsif ($sTp =~ m/(07|08)/ && ($mmcS == 1||$mmcS == 2)){ - push @{$hash->{helper}{prt}{mmcA}},$oCmd; - $hash->{helper}{prt}{mmcS} = 2; - } - elsif ($sTp eq "06" && ($mmcS == 2)){ - push @{$hash->{helper}{prt}{mmcA}},$oCmd; - $hash->{helper}{prt}{mmcS} = 3; - } + my $oCmd = "++".substr($cmd,6); + if ($sTp eq "05"){ + my @arr = ($oCmd); + $hash->{helper}{prt}{mmcA}=\@arr; + $hash->{helper}{prt}{mmcS} = 1; + } + elsif ($sTp =~ m/(07|08)/ && ($mmcS == 1||$mmcS == 2)){ + push @{$hash->{helper}{prt}{mmcA}},$oCmd; + $hash->{helper}{prt}{mmcS} = 2; + } + elsif ($sTp eq "06" && ($mmcS == 2)){ + push @{$hash->{helper}{prt}{mmcA}},$oCmd; + $hash->{helper}{prt}{mmcS} = 3; + } elsif ($mmcS){ # - delete $hash->{helper}{prt}{mmcA}; - delete $hash->{helper}{prt}{mmcS}; + delete $hash->{helper}{prt}{mmcA}; + delete $hash->{helper}{prt}{mmcS}; } } elsif($mmcS){ delete $hash->{helper}{prt}{mmcA}; - delete $hash->{helper}{prt}{mmcS}; + delete $hash->{helper}{prt}{mmcS}; } - + if($hash->{cmdStack} && scalar @{$hash->{cmdStack}}){ $hash->{protCmdPend} = scalar @{$hash->{cmdStack}}." CMDs pending"; } @@ -3609,92 +3608,92 @@ sub CUL_HM_sndIfOpen($) { my(undef,$io) = split(':',$_[0]); RemoveInternalTimer("sndIfOpen:$io");# should not be necessary, but my $ioHash = $defs{$io}; - if ( $ioHash->{STATE} !~ m/^(opened|Initialized)$/ - ||(defined $ioHash->{XmitOpen} && $ioHash->{XmitOpen} == 0) -# ||$modules{CUL_HM}{prot}{rspPend}>=$maxPendCmds - ){#still no send allowed + if ( $ioHash->{STATE} !~ m/^(opened|Initialized)$/ + ||(defined $ioHash->{XmitOpen} && $ioHash->{XmitOpen} == 0) +# ||$modules{CUL_HM}{prot}{rspPend}>=$maxPendCmds + ){#still no send allowed if ($modules{CUL_HM}{$io}{tmrStart} < gettimeofday() - $modules{CUL_HM}{hmIoMaxDly}){ - # we need to clean up - this is way to long Stop delay - if ($modules{CUL_HM}{$io}{pendDev}) { + # we need to clean up - this is way to long Stop delay + if ($modules{CUL_HM}{$io}{pendDev}) { while(@{$modules{CUL_HM}{$io}{pendDev}}){ - my $name = shift(@{$modules{CUL_HM}{$io}{pendDev}}); + my $name = shift(@{$modules{CUL_HM}{$io}{pendDev}}); CUL_HM_eventP($defs{$name},"IOerr"); - } - } - $modules{CUL_HM}{$io}{tmr} = 0; - } - else{ - InternalTimer(gettimeofday()+$IOpoll,"CUL_HM_sndIfOpen", - "sndIfOpen:$io", 0); - } + } + } + $modules{CUL_HM}{$io}{tmr} = 0; + } + else{ + InternalTimer(gettimeofday()+$IOpoll,"CUL_HM_sndIfOpen", + "sndIfOpen:$io", 0); + } } else{ $modules{CUL_HM}{$io}{tmr} = 0; - my $name = shift(@{$modules{CUL_HM}{$io}{pendDev}}); - CUL_HM_ProcessCmdStack($defs{$name}); - if (@{$modules{CUL_HM}{$io}{pendDev}}){#tmr = 0, clearing queue slowly - InternalTimer(gettimeofday()+$IOpoll,"CUL_HM_sndIfOpen", - "sndIfOpen:$io", 0); - } + my $name = shift(@{$modules{CUL_HM}{$io}{pendDev}}); + CUL_HM_ProcessCmdStack($defs{$name}); + if (@{$modules{CUL_HM}{$io}{pendDev}}){#tmr = 0, clearing queue slowly + InternalTimer(gettimeofday()+$IOpoll,"CUL_HM_sndIfOpen", + "sndIfOpen:$io", 0); + } } } sub CUL_HM_SndCmd($$) { my ($hash, $cmd) = @_; - $hash = CUL_HM_getDeviceHash($hash); + $hash = CUL_HM_getDeviceHash($hash); my $io = $hash->{IODev}; return if( AttrVal($hash->{NAME},"ignore","") - || AttrVal($hash->{NAME},"dummy","")); + || AttrVal($hash->{NAME},"dummy","")); my $ioName = $io->{NAME}; if(!$io || !$ioName){ - CUL_HM_eventP($hash,"IOerr"); + CUL_HM_eventP($hash,"IOerr"); CUL_HM_UpdtReadSingle($hash,"state","ERR_IOdev_undefined",1); - return; - }; + return; + }; if ( $io->{STATE} !~ m/^(opened|Initialized)$/ # we need to queue ||(hex substr($cmd,2,2) & 0x20) && ( # check for commands with resp-req $modules{CUL_HM}{$ioName}{tmr} # queue already running ||(defined $io->{XmitOpen} && $io->{XmitOpen} == 0)#overload, dont send - ) - ){ + ) + ){ - # shall we delay commands if IO device is not present? - # it could cause trouble if light switches on after a long period - # repetition will be stopped after 1min forsecurity reason. + # shall we delay commands if IO device is not present? + # it could cause trouble if light switches on after a long period + # repetition will be stopped after 1min forsecurity reason. my @arr = (); $hash->{cmdStack} = \@arr if(!$hash->{cmdStack}); unshift (@{$hash->{cmdStack}}, $cmd);#pushback cmd, wait for opportunity - # push device to list - if (!defined $modules{CUL_HM}{$ioName}{tmr}){ - # some setup work for this timer - $modules{CUL_HM}{$ioName}{tmr} = 0; - if (!$modules{CUL_HM}{$ioName}{pendDev}){# generate if not exist - my @arr2 = (); + # push device to list + if (!defined $modules{CUL_HM}{$ioName}{tmr}){ + # some setup work for this timer + $modules{CUL_HM}{$ioName}{tmr} = 0; + if (!$modules{CUL_HM}{$ioName}{pendDev}){# generate if not exist + my @arr2 = (); $modules{CUL_HM}{$ioName}{pendDev} = \@arr2; - } - } - @{$modules{CUL_HM}{$ioName}{pendDev}} = - CUL_HM_noDup(@{$modules{CUL_HM}{$ioName}{pendDev}},$hash->{NAME}); - CUL_HM_respPendRm($hash);#rm timer - we are out - if ($modules{CUL_HM}{$ioName}{tmr} != 1){# need to start timer - my $tn = gettimeofday(); - InternalTimer($tn+$IOpoll, "CUL_HM_sndIfOpen", "sndIfOpen:$ioName", 0); + } + } + @{$modules{CUL_HM}{$ioName}{pendDev}} = + CUL_HM_noDup(@{$modules{CUL_HM}{$ioName}{pendDev}},$hash->{NAME}); + CUL_HM_respPendRm($hash);#rm timer - we are out + if ($modules{CUL_HM}{$ioName}{tmr} != 1){# need to start timer + my $tn = gettimeofday(); + InternalTimer($tn+$IOpoll, "CUL_HM_sndIfOpen", "sndIfOpen:$ioName", 0); $modules{CUL_HM}{$ioName}{tmr} = 1; $modules{CUL_HM}{$ioName}{tmrStart} = $tn; # abort if to long - } - return; + } + return; } - + $cmd =~ m/^(..)(.*)$/; my ($mn, $cmd2) = ($1, $2); if($mn eq "++") { $mn = $io->{HM_CMDNR} ? (($io->{HM_CMDNR} +1)&0xff) : 1; $io->{HM_CMDNR} = $mn; - } + } elsif($cmd =~ m/^[+-]/){; #continue pure IOWrite($hash, "", $cmd); - return; + return; } else { $mn = hex($mn); @@ -3702,8 +3701,8 @@ sub CUL_HM_SndCmd($$) { $cmd = sprintf("As%02X%02X%s", length($cmd2)/2+1, $mn, $cmd2); IOWrite($hash, "", $cmd); CUL_HM_statCnt($ioName,"s"); - CUL_HM_eventP($hash,"Snd"); - CUL_HM_responseSetup($hash,$cmd); + CUL_HM_eventP($hash,"Snd"); + CUL_HM_responseSetup($hash,$cmd); $cmd =~ m/As(..)(..)(..)(..)(......)(......)(.*)/; CUL_HM_DumpProtocol("SND", $io, ($1,$2,$3,$4,$5,$6,$7)); } @@ -3722,18 +3721,18 @@ sub CUL_HM_statCnt($$) {# set msg statistics for (r)ecive (s)end or (u)pdate if ($l[2] != $stat->{$ioName}{last}){#next field my $end = $l[2]; if ($l[2] < $stat->{$ioName}{last}){#next day - $end += 24; - my $recentD = ($l[6]+6)%7; - foreach my $ud ("r","s"){ - $stat->{$ud}{$ioName}{d}{$recentD} = 0; - $stat->{$ud}{$ioName}{d}{$recentD} += $stat->{$ud}{$ioName}{h}{$_} - foreach (0..23); - } - } - foreach (($stat->{$ioName}{last}+1)..$end){ + $end += 24; + my $recentD = ($l[6]+6)%7; + foreach my $ud ("r","s"){ + $stat->{$ud}{$ioName}{d}{$recentD} = 0; + $stat->{$ud}{$ioName}{d}{$recentD} += $stat->{$ud}{$ioName}{h}{$_} + foreach (0..23); + } + } + foreach (($stat->{$ioName}{last}+1)..$end){ $stat->{r}{$ioName}{h}{$_%24} = 0; $stat->{s}{$ioName}{h}{$_%24} = 0; - } + } $stat->{$ioName}{last} = $l[2]; } $stat->{$dir}{$ioName}{h}{$l[2]}++ if ($dir ne "u"); @@ -3742,20 +3741,20 @@ sub CUL_HM_statCntRfresh($) {# update statistic once a day my ($ioName,$dir) = @_; foreach (keys %{$modules{CUL_HM}{stat}{r}}){ if (!$defs{$ioName}){#IO device is deleted, clear counts - delete $modules{CUL_HM}{stat}{$ioName}; - delete $modules{CUL_HM}{stat}{r}{$ioName}{h}; - delete $modules{CUL_HM}{stat}{r}{$ioName}{d}; - delete $modules{CUL_HM}{stat}{s}{$ioName}{h}; - delete $modules{CUL_HM}{stat}{s}{$ioName}{d}; - next; - } + delete $modules{CUL_HM}{stat}{$ioName}; + delete $modules{CUL_HM}{stat}{r}{$ioName}{h}; + delete $modules{CUL_HM}{stat}{r}{$ioName}{d}; + delete $modules{CUL_HM}{stat}{s}{$ioName}{h}; + delete $modules{CUL_HM}{stat}{s}{$ioName}{d}; + next; + } CUL_HM_statCnt($_,"u") if ($_ ne "dummy"); } InternalTimer(gettimeofday()+3600*20,"CUL_HM_statCntRfresh","StatCntRfrh",0); } sub CUL_HM_respPendRm($) {#del response related entries in messageing entity - my ($hash) = @_; + my ($hash) = @_; $modules{CUL_HM}{prot}{rspPend}-- if($hash->{helper}{prt}{rspWait}{cmd}); delete ($hash->{helper}{prt}{rspWait}); delete $hash->{helper}{tmdOn}; @@ -3766,65 +3765,65 @@ sub CUL_HM_respPendRm($) {#del response related entries in messageing entity $respRemoved = 1; } sub CUL_HM_respPendTout($) { - my ($HMid) = @_; - (undef,$HMid) = split(":",$HMid,2); - my $hash = $modules{CUL_HM}{defptr}{$HMid}; + my ($HMid) = @_; + (undef,$HMid) = split(":",$HMid,2); + my $hash = $modules{CUL_HM}{defptr}{$HMid}; my $pHash = $hash->{helper}{prt};#shortcut if ($hash && $hash->{DEF} ne '000000'){# we know the device my $name = $hash->{NAME}; $pHash->{awake} = 0 if (defined $pHash->{awake});# set to asleep return if(!$pHash->{rspWait}{reSent}); # Double timer? if ($pHash->{rspWait}{wakeup}){ - CUL_HM_respPendRm($hash);# do not count problems with wakeup try, just wait - $hash->{protCondBurst} = "off" if (!$hash->{protCondBurst}|| - $hash->{protCondBurst} !~ m/forced/);; - $pHash->{wakeup} = 0;# finished - $pHash->{awake} = 0;# set to asleep - CUL_HM_protState($hash,"CMDs_pending"); - } - elsif ($pHash->{try}){# send was a try - revert and wait for wakeup - unshift (@{$hash->{cmdStack}}, "++".substr($pHash->{rspWait}{cmd},6)); + CUL_HM_respPendRm($hash);# do not count problems with wakeup try, just wait + $hash->{protCondBurst} = "off" if (!$hash->{protCondBurst}|| + $hash->{protCondBurst} !~ m/forced/);; + $pHash->{wakeup} = 0;# finished + $pHash->{awake} = 0;# set to asleep + CUL_HM_protState($hash,"CMDs_pending"); + } + elsif ($pHash->{try}){# send was a try - revert and wait for wakeup + unshift (@{$hash->{cmdStack}}, "++".substr($pHash->{rspWait}{cmd},6)); delete $pHash->{try}; - CUL_HM_respPendRm($hash);# do not count problems with wakeup try, just wait - CUL_HM_protState($hash,"CMDs_pending"); - } - elsif ($hash->{IODev}->{STATE} !~ m/^(opened|Initialized)$/){#IO errors + CUL_HM_respPendRm($hash);# do not count problems with wakeup try, just wait + CUL_HM_protState($hash,"CMDs_pending"); + } + elsif ($hash->{IODev}->{STATE} !~ m/^(opened|Initialized)$/){#IO errors CUL_HM_eventP($hash,"IOdly"); - CUL_HM_ProcessCmdStack($hash) if(CUL_HM_getRxType($hash) & 0x03); - } - elsif ($pHash->{rspWait}{reSent} > AttrVal($hash->{NAME},"msgRepeat",3) # too much - ||((CUL_HM_getRxType($hash) & 0x83) == 0)){ #to slow + CUL_HM_ProcessCmdStack($hash) if(CUL_HM_getRxType($hash) & 0x03); + } + elsif ($pHash->{rspWait}{reSent} > AttrVal($hash->{NAME},"msgRepeat",3) # too much + ||((CUL_HM_getRxType($hash) & 0x83) == 0)){ #to slow my $pendCmd = ($pHash->{rspWait}{Pending} - ?"RESPONSE TIMEOUT:".$pHash->{rspWait}{Pending} - :"MISSING ACK");# save before remove - CUL_HM_eventP($hash,"ResndFail"); - readingsSingleUpdate($hash,"state",$pendCmd,1); - CUL_HM_ProcessCmdStack($hash); # continue processing commands if any - } - else{# manage retries - if ($hash->{protCondBurst}&&$hash->{protCondBurst} eq "on" ){ - #timeout while conditional burst was active. try re-wakeup + ?"RESPONSE TIMEOUT:".$pHash->{rspWait}{Pending} + :"MISSING ACK");# save before remove + CUL_HM_eventP($hash,"ResndFail"); + readingsSingleUpdate($hash,"state",$pendCmd,1); + CUL_HM_ProcessCmdStack($hash); # continue processing commands if any + } + else{# manage retries + if ($hash->{protCondBurst}&&$hash->{protCondBurst} eq "on" ){ + #timeout while conditional burst was active. try re-wakeup $pHash->{rspWait}{reSent}++; - my (undef,$addr,$msg) = unpack 'A10A12A*',$hash->{helper}{prt}{rspWait}{cmd}; - $pHash->{rspWaitSec}{$_} = $pHash->{rspWait}{$_} - foreach (keys%{$pHash->{rspWait}}); - CUL_HM_SndCmd($hash,"++B112$addr"); - $hash->{helper}{prt}{awake}=4;# start re-wakeup - } - else{# normal device resend - CUL_HM_eventP($hash,"Resnd"); + my (undef,$addr,$msg) = unpack 'A10A12A*',$hash->{helper}{prt}{rspWait}{cmd}; + $pHash->{rspWaitSec}{$_} = $pHash->{rspWait}{$_} + foreach (keys%{$pHash->{rspWait}}); + CUL_HM_SndCmd($hash,"++B112$addr"); + $hash->{helper}{prt}{awake}=4;# start re-wakeup + } + else{# normal device resend + CUL_HM_eventP($hash,"Resnd"); IOWrite($hash, "", $pHash->{rspWait}{cmd}); - CUL_HM_statCnt($hash->{IODev}{NAME},"s"); + CUL_HM_statCnt($hash->{IODev}{NAME},"s"); $pHash->{rspWait}{reSent}++; Log3 $name,4,"CUL_HM_Resend: ".$name. " nr ".$pHash->{rspWait}{reSent}; - InternalTimer(gettimeofday()+rand(20)/10+4,"CUL_HM_respPendTout","respPend:$hash->{DEF}", 0); - } - } + InternalTimer(gettimeofday()+rand(20)/10+4,"CUL_HM_respPendTout","respPend:$hash->{DEF}", 0); + } + } } } sub CUL_HM_respPendToutProlong($) {#used when device sends part responses - my ($hash) = @_; + my ($hash) = @_; RemoveInternalTimer("respPend:$hash->{DEF}"); InternalTimer(gettimeofday()+2, "CUL_HM_respPendTout", "respPend:$hash->{DEF}", 0); } @@ -3836,34 +3835,34 @@ sub CUL_HM_eventP($$) {#handle protocol events my $nAttr = $hash; if ($evntType eq "Rcv"){ $nAttr->{"protLastRcv"} = TimeNow(); - return; + return; } - + my $evnt = $nAttr->{"prot".$evntType}?$nAttr->{"prot".$evntType}:"0 > x"; my ($evntCnt,undef) = split(' last_at:',$evnt); $nAttr->{"prot".$evntType} = ++$evntCnt." last_at:".TimeNow(); - + if ($evntType =~ m/(Nack|ResndFail|IOerr)/){# unrecoverable Error - readingsSingleUpdate($hash,"state",$evntType,1); + readingsSingleUpdate($hash,"state",$evntType,1); $hash->{helper}{prt}{bErr}++; $nAttr->{protCmdDel} = 0 if(!$nAttr->{protCmdDel}); $nAttr->{protCmdDel} += scalar @{$hash->{cmdStack}} + 1 - if ($hash->{cmdStack}); - CUL_HM_protState($hash,"CMDs_done".($hash->{helper}{prt}{bErr}? - ("_Errors:".$hash->{helper}{prt}{bErr}):"")); - CUL_HM_respPendRm($hash); + if ($hash->{cmdStack}); + CUL_HM_protState($hash,"CMDs_done".($hash->{helper}{prt}{bErr}? + ("_Errors:".$hash->{helper}{prt}{bErr}):"")); + CUL_HM_respPendRm($hash); } - elsif($evntType eq "IOdly"){ # IO problem - will see whether it recovers + elsif($evntType eq "IOdly"){ # IO problem - will see whether it recovers my $pHash = $hash->{helper}{prt}; - if ($pHash->{mmcA}){ - unshift @{$hash->{cmdStack}},$_ foreach (reverse@{$pHash->{mmcA}}); - delete $pHash->{mmcA}; - delete $pHash->{mmcS}; - } - else{ + if ($pHash->{mmcA}){ + unshift @{$hash->{cmdStack}},$_ foreach (reverse@{$pHash->{mmcA}}); + delete $pHash->{mmcA}; + delete $pHash->{mmcS}; + } + else{ unshift @{$hash->{cmdStack}}, $pHash->{rspWait}{cmd};#pushback - } - CUL_HM_respPendRm($hash); + } + CUL_HM_respPendRm($hash); } } sub CUL_HM_protState($$){ @@ -3874,15 +3873,15 @@ sub CUL_HM_protState($$){ Log3 $name,6,"CUL_HM $name protEvent:$state". ($hash->{cmdStack}?" pending:".scalar @{$hash->{cmdStack}}:""); if ($state =~ m/processing/) {$hash->{helper}{prt}{sProc} = 1; - } + } elsif($state =~ m/^CMDs_done/) {DoTrigger($name, undef); delete($hash->{cmdStack}); - delete($hash->{protCmdPend}); - $hash->{helper}{prt}{sProc} = 0; - $hash->{helper}{prt}{awake} = 0 if (defined$hash->{helper}{prt}{awake}); # asleep + delete($hash->{protCmdPend}); + $hash->{helper}{prt}{sProc} = 0; + $hash->{helper}{prt}{awake} = 0 if (defined$hash->{helper}{prt}{awake}); # asleep } elsif($state eq "Info_Cleared"){$hash->{helper}{prt}{sProc} = 0; - $hash->{helper}{prt}{awake} = 0 if (defined$hash->{helper}{prt}{awake}); # asleep + $hash->{helper}{prt}{awake} = 0 if (defined$hash->{helper}{prt}{awake}); # asleep } elsif($state eq "CMDs_pending"){$hash->{helper}{prt}{sProc} = 2; } @@ -3904,9 +3903,9 @@ sub CUL_HM_ID2PeerList ($$$) { my $dId = substr(CUL_HM_name2Id($name),0,6); #get own device ID foreach my $pId (sort(keys %tmpHash)){ next if ($pId !~ m/^[0-9A-F]{8}$/); #ignore non-channel IDs - $peerIDs .= $pId.","; #append ID + $peerIDs .= $pId.","; #append ID next if ($pId eq "00000000"); # and end detection - $peerNames .= CUL_HM_peerChName($pId,$dId,"").","; + $peerNames .= CUL_HM_peerChName($pId,$dId,"").","; } $attr{$name}{peerIDs} = $peerIDs; # make it public if ($peerNames){ @@ -3914,7 +3913,7 @@ sub CUL_HM_ID2PeerList ($$$) { readingsSingleUpdate($hash,"peerList",$peerNames,0) ; $hash->{peerList} = $peerNames; } - else{ + else{ delete $hash->{READINGS}{peerList}; delete $hash->{peerList}; } @@ -3927,33 +3926,33 @@ sub CUL_HM_peerChId($$$) {# in: , out:channelID return "all" if ($pId eq 'all');#used by getRegList my $repID = CUL_HM_name2Id($pId); $repID .= '01' if (length( $repID) == 6);# add default 01 if this is a device - return $repID; + return $repID; } sub CUL_HM_peerChName($$$) {#in: , out:name my($pId,$dId,$iId)=@_; my($pDev,$pChn) = unpack'A6A2',$pId; return 'self'.$pChn if ($pDev eq $dId); return 'fhem'.$pChn if ($pDev eq $iId); - return CUL_HM_id2Name($pId); + return CUL_HM_id2Name($pId); } sub CUL_HM_getMId($) {#in: hash(chn or dev) out:model key (key for %culHmModel) # Will store result in device helper my $hash = shift; $hash = CUL_HM_getDeviceHash($hash); my $mId = $hash->{helper}{mId}; - if (!$mId){ + if (!$mId){ my $model = AttrVal($hash->{NAME}, "model", ""); foreach my $mIdKey(keys%culHmModel){ - next if (!$culHmModel{$mIdKey}{name} || - $culHmModel{$mIdKey}{name} ne $model); - $hash->{helper}{mId} = $mIdKey ; - return $mIdKey; + next if (!$culHmModel{$mIdKey}{name} || + $culHmModel{$mIdKey}{name} ne $model); + $hash->{helper}{mId} = $mIdKey ; + return $mIdKey; } - return ""; + return ""; } return $mId; } -sub CUL_HM_getRxType($) { #in:hash(chn or dev) out:binary coded Rx type +sub CUL_HM_getRxType($) { #in:hash(chn or dev) out:binary coded Rx type # Will store result in device helper my ($hash) = @_; $hash = CUL_HM_getDeviceHash($hash); @@ -3963,18 +3962,18 @@ sub CUL_HM_getRxType($) { #in:hash(chn or dev) out:binary coded Rx type if (!$rxtEntity){ #at least one bit must be set my $MId = CUL_HM_getMId($hash); my $rxtOfModel = $culHmModel{$MId}{rxt} if ($MId && $culHmModel{$MId}{rxt}); - if ($rxtOfModel){ + if ($rxtOfModel){ $rxtEntity |= ($rxtOfModel =~ m/b/)?0x02:0;#burst $rxtEntity |= ($rxtOfModel =~ m/c/)?0x04:0;#config $rxtEntity |= ($rxtOfModel =~ m/w/)?0x08:0;#wakeup $rxtEntity |= ($rxtOfModel =~ m/l/)?0x10:0;#lazyConfig $rxtEntity |= ($rxtOfModel =~ m/f/)?0x80:0 #burstConditional - if(CUL_HM_getAttrInt($hash->{NAME},"burstAccess",0)); - } - $rxtEntity = 1 if (!$rxtEntity);#always - $hash->{helper}{rxType} = $rxtEntity; + if(CUL_HM_getAttrInt($hash->{NAME},"burstAccess",0)); + } + $rxtEntity = 1 if (!$rxtEntity);#always + $hash->{helper}{rxType} = $rxtEntity; } - return $rxtEntity; + return $rxtEntity; } sub CUL_HM_getFlag($) {#mFlg 'A0' or 'B0' for burst/normal devices # currently not supported is the wakeupflag since it is hardly used @@ -3983,13 +3982,13 @@ sub CUL_HM_getFlag($) {#mFlg 'A0' or 'B0' for burst/normal devices } sub CUL_HM_getAssChnIds($) { #in: name out:ID list of assotiated channels # if it is a channel only return itself - # if device and no channel + # if device and no channel my ($name) = @_; my @chnIdList; my $hash = $defs{$name}; foreach my $channel (grep /^channel_/, keys %{$hash}){ - my $chnHash = $defs{$hash->{$channel}}; - push @chnIdList,$chnHash->{DEF} if ($chnHash); + my $chnHash = $defs{$hash->{$channel}}; + push @chnIdList,$chnHash->{DEF} if ($chnHash); } my $dId = CUL_HM_name2Id($name); @@ -4000,25 +3999,25 @@ sub CUL_HM_getAssChnIds($) { #in: name out:ID list of assotiated channels #+++++++++++++++++ Conversions names, hashes, ids++++++++++++++++++++++++++++++ #Performance opti: subroutines may consume up to 5 times the performance -# +# #get Attr: $val = $attr{$hash->{NAME}}{$attrName}?$attr{$hash->{NAME}}{$attrName} :""; # $val = $attr{$name}{$attrName} ?$attr{$name}{$attrName} :""; -#getRead: $val = $hash->{READINGS}{$rlName} ?$hash->{READINGS}{$rlName}{VAL} :""; -# $val = $defs{$name}{READINGS}{$rlName}?$defs{$name}{READINGS}{$rlName}{VAL} :""; -# $time = $hash->{READINGS}{$rlName} ?$hash->{READINGS}{$rlName}{time} :""; +#getRead: $val = $hash->{READINGS}{$rlName} ?$hash->{READINGS}{$rlName}{VAL} :""; +# $val = $defs{$name}{READINGS}{$rlName}?$defs{$name}{READINGS}{$rlName}{VAL} :""; +# $time = $hash->{READINGS}{$rlName} ?$hash->{READINGS}{$rlName}{time} :""; -sub CUL_HM_Id($) {#in: ioHash out: ioHMid +sub CUL_HM_Id($) {#in: ioHash out: ioHMid my ($io) = @_; my $fhtid = defined($io->{FHTID}) ? $io->{FHTID} : "0000"; return $attr{$io->{NAME}}{hmId}?$attr{$io->{NAME}}{hmId}:"F1$fhtid"; } -sub CUL_HM_IOid($) {#in: hash out: id of IO device +sub CUL_HM_IOid($) {#in: hash out: id of IO device my ($hash) = @_; my $dHash = CUL_HM_getDeviceHash($hash); my $ioHash = $dHash->{IODev}; my $fhtid = defined($ioHash->{FHTID}) ? $ioHash->{FHTID} : "0000"; return "" if (!$ioHash->{NAME}); - return AttrVal($ioHash->{NAME},"hmId","F1$fhtid"); + return AttrVal($ioHash->{NAME},"hmId","F1$fhtid"); return $attr{$ioHash->{NAME}}{hmId}?$attr{$ioHash->{NAME}}{hmId}:"F1$fhtid"; } sub CUL_HM_hash2Id($) {#in: id, out:hash @@ -4040,7 +4039,7 @@ sub CUL_HM_name2Id(@) { #in: name or HMid ==>out: HMid, "" if no match return "000000" if($name eq "broadcast"); #broadcast return $defs{$1}->{DEF}.$2 if($name =~ m/(.*)_chn:(..)/); # chn:xx return $name if($name =~ m/^[A-F0-9]{6,8}$/i);#was already HMid - return substr($idHash->{DEF},0,6).sprintf("%02X",$1) + return substr($idHash->{DEF},0,6).sprintf("%02X",$1) if($idHash && ($name =~ m/self(.*)/)); return AttrVal($name,"hmId",""); # could be IO device } @@ -4048,13 +4047,13 @@ sub CUL_HM_id2Name($) { #in: name or HMid out: name my ($p) = @_; return $p if($defs{$p}||$p =~ m/_chn:/); my $devId= substr($p, 0, 6); - return "broadcast" if($devId eq "000000"); + return "broadcast" if($devId eq "000000"); my $defPtr = $modules{CUL_HM}{defptr}; if (length($p) == 8){ - return $defPtr->{$p}{NAME} if($defPtr->{$p});#channel - return $defPtr->{$devId}{NAME}."_chn:".substr($p,6,2) - if($defPtr->{$devId});#dev, add chn + return $defPtr->{$p}{NAME} if($defPtr->{$p});#channel + return $defPtr->{$devId}{NAME}."_chn:".substr($p,6,2) + if($defPtr->{$devId});#dev, add chn return $p; #not defined, return ID only } else{ @@ -4136,21 +4135,21 @@ sub CUL_HM_getRegFromStore($$$$@) {#read a register from backup data my $reg = $culHmRegDefine{$regName}; if ($reg) { # get the register's information $addr = $reg->{a}; - $pos = ($addr*10)%10; - $addr = int($addr); + $pos = ($addr*10)%10; + $addr = int($addr); $list = $reg->{l}; - $size = $reg->{s}; - $size = int($size)*8 + ($size*10)%10; - $conversion = $reg->{c}; #unconvert formula - $factor = $reg->{f}; - $unit = " ".$reg->{u}; + $size = $reg->{s}; + $size = int($size)*8 + ($size*10)%10; + $conversion = $reg->{c}; #unconvert formula + $factor = $reg->{f}; + $unit = " ".$reg->{u}; } if(!$regLN){ $regLN = ((CUL_HM_getAttrInt($name,"expert") == 2)?"":".") .sprintf("RegL_%02X:",$list) - .($peerId?CUL_HM_peerChName($peerId, - substr(CUL_HM_name2Id($name),0,6), - CUL_HM_IOid($hash)):""); + .($peerId?CUL_HM_peerChName($peerId, + substr(CUL_HM_name2Id($name),0,6), + CUL_HM_IOid($hash)):""); } $regLN =~ s/broadcast//; my $regLNp = $regLN; @@ -4160,23 +4159,23 @@ sub CUL_HM_getRegFromStore($$$$@) {#read a register from backup data my $convFlg = "";# confirmation flag - indicates data not confirmed by device for (my $size2go = $size;$size2go>0;$size2go -=8){ my $addrS = sprintf("%02X",$addr); - my ($dReadS,$dReadR) = (undef,""); + my ($dReadS,$dReadR) = (undef,""); $dReadS = $1 if( $hash->{helper}{shadowReg} - && $hash->{helper}{shadowReg}{$regLNp} - && $hash->{helper}{shadowReg}{$regLNp} =~ m/$addrS:(..)/); + && $hash->{helper}{shadowReg}{$regLNp} + && $hash->{helper}{shadowReg}{$regLNp} =~ m/$addrS:(..)/); $dReadR = $1 if( $hash->{READINGS}{$regLN} - &&$hash->{READINGS}{$regLN}{VAL} =~ m/$addrS:(..)/); + &&$hash->{READINGS}{$regLN}{VAL} =~ m/$addrS:(..)/); my $dRead = $dReadR; - if (defined $dReadS){ - $convFlg = "set_" if ($dReadR ne $dReadS); - $dRead = $dReadS; - } - else{ - return "invalid" if (!defined($dRead) || $dRead eq ""); - } - - $data = ($data<< 8)+hex($dRead); - $addr++; + if (defined $dReadS){ + $convFlg = "set_" if ($dReadR ne $dReadS); + $dRead = $dReadS; + } + else{ + return "invalid" if (!defined($dRead) || $dRead eq ""); + } + + $data = ($data<< 8)+hex($dRead); + $addr++; } $data = ($data>>$pos) & (0xffffffff>>(32-$size)); @@ -4188,7 +4187,7 @@ sub CUL_HM_getRegFromStore($$$$@) {#read a register from backup data } elsif($conversion eq "m10s3" ){$data = ($data+3)/10; } elsif($conversion eq "hex" ){$data = sprintf("0x%X",$data); } else { return " conversion undefined - please contact admin"; - } + } $data /= $factor if ($factor);# obey factor after possible conversion return $convFlg.$data.$unit; } @@ -4206,30 +4205,30 @@ sub CUL_HM_updtRegDisp($$$) { my $chn = $hash->{DEF}; $chn = (length($chn) == 8)?substr($chn,6,2):""; my @regArr = keys %culHmRegGeneral; - push @regArr, keys %{$culHmRegType{$st}} if($culHmRegType{$st}); - push @regArr, keys %{$culHmRegModel{$md}} if($culHmRegModel{$md}); - push @regArr, keys %{$culHmRegChan{$md.$chn}} if($culHmRegChan{$md.$chn}); + push @regArr, keys %{$culHmRegType{$st}} if($culHmRegType{$st}); + push @regArr, keys %{$culHmRegModel{$md}} if($culHmRegModel{$md}); + push @regArr, keys %{$culHmRegChan{$md.$chn}} if($culHmRegChan{$md.$chn}); my @changedRead; my $expL = CUL_HM_getAttrInt($name,"expert"); my $expLvl = ($expL != 0)?1:0; my $regLN = (($expL == 2)?"":".") .sprintf("RegL_%02X:",$listNo) - .($peerId?CUL_HM_peerChName($peerId, - substr(CUL_HM_name2Id($name),0,6), - CUL_HM_IOid($hash)):""); + .($peerId?CUL_HM_peerChName($peerId, + substr(CUL_HM_name2Id($name),0,6), + CUL_HM_IOid($hash)):""); foreach my $rgN (@regArr){ next if ($culHmRegDefine{$rgN}->{l} ne $listNo); - my $rgVal = CUL_HM_getRegFromStore($name,$rgN,$list,$peerId,$regLN); - next if (!$rgVal || $rgVal eq "invalid"); - my $rdN = ((!$expLvl && !$culHmRegDefine{$rgN}->{d})?".":"").$pReg.$rgN; - push (@changedRead,$rdN.":".$rgVal) - if (ReadingsVal($name,$rdN,"") ne $rgVal); + my $rgVal = CUL_HM_getRegFromStore($name,$rgN,$list,$peerId,$regLN); + next if (!$rgVal || $rgVal eq "invalid"); + my $rdN = ((!$expLvl && !$culHmRegDefine{$rgN}->{d})?".":"").$pReg.$rgN; + push (@changedRead,$rdN.":".$rgVal) + if (ReadingsVal($name,$rdN,"") ne $rgVal); } CUL_HM_UpdtReadBulk($hash,1,@changedRead) if (@changedRead); # --- handle specifics - Devices with abnormal or long register if ($md eq "HM-CC-TC"){#handle temperature readings - CUL_HM_TCtempReadings($hash) if (($list == 5 ||$list == 6) && + CUL_HM_TCtempReadings($hash) if (($list == 5 ||$list == 6) && substr($hash->{DEF},6,2) eq "02"); } elsif ($md =~ m/HM-CC-RT-DN/){#handle temperature readings @@ -4256,8 +4255,8 @@ sub CUL_HM_rmOldRegs($){ # remove register i outdated @rpList = CUL_HM_noDup(@rpList); return if (!@rpList); foreach my $peer(@rpList){ - next if($hash->{peerList} =~ m /\b$peer\b/); - delete $hash->{READINGS}{$_} foreach (grep /^R-$peer-/,keys %{$hash->{READINGS}}) + next if($hash->{peerList} =~ m /\b$peer\b/); + delete $hash->{READINGS}{$_} foreach (grep /^R-$peer-/,keys %{$hash->{READINGS}}) } } @@ -4326,7 +4325,7 @@ sub CUL_HM_secSince2000() {##################### my @l = localtime($t); my @g = gmtime($t); - my $t2 = $t + 60*(($l[2]-$g[2] + ((($l[5]<<9)|$l[7]) <=> (($g[5]<<9)|$g[7])) * 24) * 60 + $l[1]-$g[1]) + my $t2 = $t + 60*(($l[2]-$g[2] + ((($l[5]<<9)|$l[7]) <=> (($g[5]<<9)|$g[7])) * 24) * 60 + $l[1]-$g[1]) # timezone and daylight saving... - 946684800 # seconds between 01.01.2000, 00:00 and THE EPOCH (1970) - 7200; # HM Special @@ -4342,45 +4341,45 @@ sub CUL_HM_getChnLvl($){# in: name out: vit or phys level } #--------------- Conversion routines for register settings--------------------- -sub CUL_HM_initRegHash() { #duplicate short and long press register +sub CUL_HM_initRegHash() { #duplicate short and long press register foreach my $reg (keys %culHmRegDefShLg){ #update register list %{$culHmRegDefine{"sh".$reg}} = %{$culHmRegDefShLg{$reg}}; %{$culHmRegDefine{"lg".$reg}} = %{$culHmRegDefShLg{$reg}}; - $culHmRegDefine{"lg".$reg}{a} +=0x80; + $culHmRegDefine{"lg".$reg}{a} +=0x80; } foreach my $rN (keys %culHmRegDefine){#create literal inverse for fast search if ($culHmRegDefine{$rN}{lit}){# literal assigned => create inverse - foreach my $lit (keys %{$culHmRegDefine{$rN}{lit}}){ - $culHmRegDefine{$rN}{litInv}{$culHmRegDefine{$rN}{lit}{$lit}}=$lit; - } - } + foreach my $lit (keys %{$culHmRegDefine{$rN}{lit}}){ + $culHmRegDefine{$rN}{litInv}{$culHmRegDefine{$rN}{lit}{$lit}}=$lit; + } + } } foreach my $type(sort(keys %culHmRegType)){ #update references to register foreach my $reg (sort(keys %{$culHmRegType{$type}})){ if ($culHmRegDefShLg{$reg}){ - delete $culHmRegType{$type}{$reg}; - $culHmRegType{$type}{"sh".$reg} = 1; - $culHmRegType{$type}{"lg".$reg} = 1; - } + delete $culHmRegType{$type}{$reg}; + $culHmRegType{$type}{"sh".$reg} = 1; + $culHmRegType{$type}{"lg".$reg} = 1; + } } } foreach my $type(sort(keys %culHmRegModel)){ #update references to register foreach my $reg (sort(keys %{$culHmRegModel{$type}})){ if ($culHmRegDefShLg{$reg}){ - delete $culHmRegModel{$type}{$reg}; - $culHmRegModel{$type}{"sh".$reg} = 1; - $culHmRegModel{$type}{"lg".$reg} = 1; - } + delete $culHmRegModel{$type}{$reg}; + $culHmRegModel{$type}{"sh".$reg} = 1; + $culHmRegModel{$type}{"lg".$reg} = 1; + } } } foreach my $type(sort(keys %culHmRegChan)){ #update references to register foreach my $reg (sort(keys %{$culHmRegChan{$type}})){ if ($culHmRegDefShLg{$reg}){ - delete $culHmRegChan{$type}{$reg}; - $culHmRegChan{$type}{"sh".$reg} = 1; - $culHmRegChan{$type}{"lg".$reg} = 1; - } + delete $culHmRegChan{$type}{$reg}; + $culHmRegChan{$type}{"sh".$reg} = 1; + $culHmRegChan{$type}{"lg".$reg} = 1; + } } } } @@ -4392,8 +4391,8 @@ sub CUL_HM_fltCvT60($) { # float -> config time my $div2; foreach my $div(sort{$a <=> $b} keys %fltCvT60){ $div2 = $div; - last if ($inValue < $fltCvT60{$div}); - $exp++; + last if ($inValue < $fltCvT60{$div}); + $exp++; } return ($exp << 7)+int($inValue/$div2+.1); } @@ -4410,8 +4409,8 @@ sub CUL_HM_fltCvT($) { # float -> config time my $div2; foreach my $div(sort{$a <=> $b} keys %fltCvT){ $div2 = $div; - last if ($inValue < $fltCvT{$div}); - $exp++; + last if ($inValue < $fltCvT{$div}); + $exp++; } return ($exp << 5)+int($inValue/$div2+.1); } @@ -4448,15 +4447,15 @@ sub CUL_HM_4DisText($) { # convert text for 4dis my %txt; foreach my $sAddr (54,70){ my $txtHex = $reg1; #one row - my $sStr = sprintf("%02X:",$sAddr); + my $sStr = sprintf("%02X:",$sAddr); $txtHex =~ s/.* $sStr//; #remove reg prior to string - $sStr = sprintf("%02X:",$sAddr+11); + $sStr = sprintf("%02X:",$sAddr+11); $txtHex =~ s/$sStr(..).*/,$1/; #remove reg after string - $txtHex =~ s/ ..:/,/g; #remove addr + $txtHex =~ s/ ..:/,/g; #remove addr $txtHex =~ s/,00.*//; #remove trailing string my @ch = split(",",$txtHex,12); foreach (@ch){$txt{$sAddr}.=chr(hex($_))}; - } + } CUL_HM_UpdtReadBulk($hash,1,"text1:".$pref.$txt{54}, "text2:".$pref.$txt{70}); return "text1:".$txt{54}."\n". @@ -4471,7 +4470,7 @@ sub CUL_HM_TCtempReadings($) {# parse TC temperature readings if (ReadingsVal($name,"R-controlMode","") =~ m/^party/){ if ( $reg6 # ugly handling to add vanishing party register - && $reg6 !~ m/ 61:/ + && $reg6 !~ m/ 61:/ && $hash->{helper}{partyReg}){ $hash->{READINGS}{"RegL_06:"}{VAL} =~s/ 00:00/$hash->{helper}{partyReg}/; } @@ -4496,26 +4495,26 @@ sub CUL_HM_TCtempReadings($) {# parse TC temperature readings my @changedRead; push (@changedRead,"tempList_State:". (($hash->{helper}{shadowReg}{"RegL_05:"} || - $hash->{helper}{shadowReg}{"RegL_06:"} )?"set":"verified")); + $hash->{helper}{shadowReg}{"RegL_06:"} )?"set":"verified")); for (my $day = 0;$day<7;$day++){ my $tSpan = 0; - my $dayRead = ""; + my $dayRead = ""; for (my $entry = 0;$entry<24;$entry++){ - my $reg = $day *24 + $entry; + my $reg = $day *24 + $entry; last if ($tSpan > 1430); - $tSpan = $time[$reg]; - my $entry = sprintf("%02d:%02d %3.01f",($tSpan/60),($tSpan%60),$temp[$reg]); - $setting .= "Temp set: ".$days[$day]." ".$entry." C\n"; - $dayRead .= " ".$entry; - $tSpan = $time[$reg]; + $tSpan = $time[$reg]; + my $entry = sprintf("%02d:%02d %3.01f",($tSpan/60),($tSpan%60),$temp[$reg]); + $setting .= "Temp set: ".$days[$day]." ".$entry." C\n"; + $dayRead .= " ".$entry; + $tSpan = $time[$reg]; } - push (@changedRead,"tempList".$days[$day].":".$dayRead); - } + push (@changedRead,"tempList".$days[$day].":".$dayRead); + } CUL_HM_UpdtReadBulk($hash,1,@changedRead) if (@changedRead); { #update readings in device - oldfashioned style, copy from Readings my @histVals; foreach my $var ("displayMode","displayTemp","controlMode","decalcDay","displayTempUnit","day-temp","night-temp","party-temp"){ - push @histVals,$var.":".ReadingsVal($name,"R-".$var,"???"); + push @histVals,$var.":".ReadingsVal($name,"R-".$var,"???"); } CUL_HM_UpdtReadBulk(CUL_HM_getDeviceHash($hash),1,@histVals) if (@histVals); } @@ -4528,55 +4527,55 @@ sub CUL_HM_RTtempReadings($) {# parse RT temperature readings my $tempRegs = ReadingsVal($name,$regPre."RegL_07:",""); my $stmpRegs = ($hash->{helper}{shadowReg}{"RegL_07:"})? # need to compare actual data ($hash->{helper}{shadowReg}{"RegL_07:"}) - :$tempRegs; + :$tempRegs; return "reglist incomplete\n" if ($tempRegs !~ m/00:00/); - + my @days = ("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"); $tempRegs =~ s/.* 14://; #remove register up to addr 20 from list $tempRegs =~ s/ 00:00/ /g; #remove regline termination $tempRegs =~ s/ ..://g; #remove addr Info $tempRegs =~ s/ //g; #blank - - $stmpRegs =~ s/.* 14://; - $stmpRegs =~ s/ 00:00/ /g; - $stmpRegs =~ s/ ..://g; - $stmpRegs =~ s/ //g; + + $stmpRegs =~ s/.* 14://; + $stmpRegs =~ s/ 00:00/ /g; + $stmpRegs =~ s/ ..://g; + $stmpRegs =~ s/ //g; my $setting; my @changedRead; push (@changedRead,"tempList_State:". ($hash->{helper}{shadowReg}{"RegL_07:"} ?"set":"verified")); for (my $day = 0;$day<7;$day++){ - my $dayRead = ""; - my $pre =""; + my $dayRead = ""; + my $pre =""; my @time; my @temp; my $str; - if (substr($stmpRegs,$day *13*4,13*4) eq - substr($tempRegs,$day *13*4,13*4) ){ - $str = substr($tempRegs,$day *13*4,13*4); - } - else{ - $str = substr($stmpRegs,$day *13*4,13*4); - $pre = "set_"; - } + if (substr($stmpRegs,$day *13*4,13*4) eq + substr($tempRegs,$day *13*4,13*4) ){ + $str = substr($tempRegs,$day *13*4,13*4); + } + else{ + $str = substr($stmpRegs,$day *13*4,13*4); + $pre = "set_"; + } foreach (unpack '(A4)*',$str){ my $h = hex($_); - push @temp,($h >> 9)/2; - $h = ($h & 0x1ff) * 5; - $h = sprintf("%02d:%02d",int($h / 60),($h%60)); + push @temp,($h >> 9)/2; + $h = ($h & 0x1ff) * 5; + $h = sprintf("%02d:%02d",int($h / 60),($h%60)); push @time,$h; } for (my $idx = 0;$idx<13;$idx++){ - my $entry = sprintf(" %s %3.01f",$time[$idx],$temp[$idx]); - $setting .= "Temp set: ".$days[$day].$entry." C\n"; - $dayRead .= $entry; + my $entry = sprintf(" %s %3.01f",$time[$idx],$temp[$idx]); + $setting .= "Temp set: ".$days[$day].$entry." C\n"; + $dayRead .= $entry; last if ($time[$idx] eq "24:00"); } - push (@changedRead,"tempList".$days[$day].":".$pre." ".$dayRead); - } + push (@changedRead,"tempList".$days[$day].":".$pre." ".$dayRead); + } CUL_HM_UpdtReadBulk($hash,1,@changedRead) if (@changedRead); # transport some readings to relevant channels (window receivce here) @@ -4585,52 +4584,52 @@ sub CUL_HM_RTtempReadings($) {# parse RT temperature readings "winOpnTemp:" .ReadingsVal($name,"R-winOpnTemp" ,"unknown"), "winOpnPeriod:" .ReadingsVal($name,"R-winOpnPeriod" ,"unknown"), "winOpnBoost:" .ReadingsVal($name,"R-winOpnBoost" ,"unknown"), - "winOpnMode:" .ReadingsVal($name,"R-winOpnMode" ,"unknown"), - "winOpnDetFall:".ReadingsVal($name,"R-winOpnDetFall" ,"unknown"),); + "winOpnMode:" .ReadingsVal($name,"R-winOpnMode" ,"unknown"), + "winOpnDetFall:".ReadingsVal($name,"R-winOpnDetFall" ,"unknown"),); return $setting; } -sub CUL_HM_repReadings($) { # parse repeater +sub CUL_HM_repReadings($) { # parse repeater my ($hash)=@_; my %pCnt; my $cnt = 0; return "" if (!$hash->{helper}{peerIDsRaw}); foreach my$pId(split',',$hash->{helper}{peerIDsRaw}){ next if (!$pId || $pId eq "00000000"); - $pCnt{$pId.$cnt}{cnt}=$cnt++; + $pCnt{$pId.$cnt}{cnt}=$cnt++; } my @pS; my @pD; - my @pB; + my @pB; foreach (split",",(AttrVal($hash->{NAME},"repPeers",undef))){ my ($s,$d,$b) = split":",$_; - push @pS,$s; - push @pD,$d; - push @pB,$b; + push @pS,$s; + push @pD,$d; + push @pB,$b; } my @readList; push @readList,"repPeer_$_:undefined" for(0..35);#set default empty my @retL; foreach my$pId(sort keys %pCnt){ - my ($pdID,$bdcst,$no) = unpack('A6A2A2',$pId); - my $fNo = $no-1;#shorthand field number, often used - my $sName = CUL_HM_id2Name($pdID); - - if ($sName eq $pdID && $pD[$fNo]){ - $sName = $defs{$pD[$fNo]}->{IODev}{NAME} - if($attr{$defs{$pD[$fNo]}->{IODev}{NAME}}{hmId} eq $pdID); - } - my $eS = sprintf("%02d %-15s %-15s %-3s %-4s", - $no - ,$sName - ,((!$pS[$fNo] || $pS[$fNo] ne $sName)?"unknown":" dst>$pD[$fNo]") - ,($bdcst eq "01"?"yes":"no ") - ,($pB[$fNo] && ( ($bdcst eq "01" && $pB[$fNo] eq "y") - ||($bdcst eq "00" && $pB[$fNo] eq "n")) ?"ok":"fail") - ); - push @retL, $eS; - $readList[$fNo]=sprintf("repPeer_%02d:%s",$no,$eS); + my ($pdID,$bdcst,$no) = unpack('A6A2A2',$pId); + my $fNo = $no-1;#shorthand field number, often used + my $sName = CUL_HM_id2Name($pdID); + + if ($sName eq $pdID && $pD[$fNo]){ + $sName = $defs{$pD[$fNo]}->{IODev}{NAME} + if($attr{$defs{$pD[$fNo]}->{IODev}{NAME}}{hmId} eq $pdID); + } + my $eS = sprintf("%02d %-15s %-15s %-3s %-4s", + $no + ,$sName + ,((!$pS[$fNo] || $pS[$fNo] ne $sName)?"unknown":" dst>$pD[$fNo]") + ,($bdcst eq "01"?"yes":"no ") + ,($pB[$fNo] && ( ($bdcst eq "01" && $pB[$fNo] eq "y") + ||($bdcst eq "00" && $pB[$fNo] eq "n")) ?"ok":"fail") + ); + push @retL, $eS; + $readList[$fNo]=sprintf("repPeer_%02d:%s",$no,$eS); } CUL_HM_UpdtReadBulk($hash,0,@readList); return "No Source Dest Bcast\n". join"\n", sort @retL; @@ -4639,25 +4638,25 @@ sub CUL_HM_dimLog($) {# dimmer readings - support virtual chan - unused so far my ($hash)=@_; my $lComb = CUL_HM_Get($hash,$hash->{NAME},"reg","logicCombination"); return if (!$lComb); - my %logicComb=( - inactive=>{calc=>'$val=$val' ,txt=>'unused'}, + my %logicComb=( + inactive=>{calc=>'$val=$val' ,txt=>'unused'}, or =>{calc=>'$val=$in>$val?$in:$val' ,txt=>'max(state,chan)'}, - and =>{calc=>'$val=$in<$val?$in:$val' ,txt=>'min(state,chan)'}, - xor =>{calc=>'$val=!($in!=0&&$val!=0)?($in>$val?$in:$val): 0' ,txt=>'0 if both are != 0, else max'}, - nor =>{calc=>'$val=100-($in>$val?$in : $val)' ,txt=>'100-max(state,chan)'}, - nand =>{calc=>'$val=100-($in<$val?$in : $val)' ,txt=>'100-min(state,chan)'}, - orinv =>{calc=>'$val=(100-$in)>$val?(100-$in) : $val' ,txt=>'max((100-chn),state)'}, - andinv =>{calc=>'$val=(100-$in)<$val?(100-$in) : $val' ,txt=>'min((100-chn),state)'}, - plus =>{calc=>'$val=($in + $val)<100?($in + $val) : 100' ,txt=>'state + chan'}, - minus =>{calc=>'$val=($in - $val)>0?($in + $val) : 0' ,txt=>'state - chan'}, - mul =>{calc=>'$val=($in * $val)<100?($in + $val) : 100' ,txt=>'state * chan'}, - plusinv =>{calc=>'$val=($val+100-$in)<100?($val+100-$in) : 100' ,txt=>'state + 100 - chan'}, - minusinv=>{calc=>'$val=($val-100+$in)>0?($val-100+$in) : 0' ,txt=>'state - 100 + chan'}, - mulinv =>{calc=>'$val=((100-$in)*$val)<100?(100-$in)*$val) : 100',txt=>'state * (100 - chan)'}, - invPlus =>{calc=>'$val=(100-$val-$in)>0?(100-$val-$in) : 0' ,txt=>'100 - state - chan'}, - invMinus=>{calc=>'$val=(100-$val+$in)<100?(100-$val-$in) : 100' ,txt=>'100 - state + chan'}, - invMul =>{calc=>'$val=(100-$val*$in)>0?(100-$val*$in) : 0' ,txt=>'100 - state * chan'}, - ); + and =>{calc=>'$val=$in<$val?$in:$val' ,txt=>'min(state,chan)'}, + xor =>{calc=>'$val=!($in!=0&&$val!=0)?($in>$val?$in:$val): 0' ,txt=>'0 if both are != 0, else max'}, + nor =>{calc=>'$val=100-($in>$val?$in : $val)' ,txt=>'100-max(state,chan)'}, + nand =>{calc=>'$val=100-($in<$val?$in : $val)' ,txt=>'100-min(state,chan)'}, + orinv =>{calc=>'$val=(100-$in)>$val?(100-$in) : $val' ,txt=>'max((100-chn),state)'}, + andinv =>{calc=>'$val=(100-$in)<$val?(100-$in) : $val' ,txt=>'min((100-chn),state)'}, + plus =>{calc=>'$val=($in + $val)<100?($in + $val) : 100' ,txt=>'state + chan'}, + minus =>{calc=>'$val=($in - $val)>0?($in + $val) : 0' ,txt=>'state - chan'}, + mul =>{calc=>'$val=($in * $val)<100?($in + $val) : 100' ,txt=>'state * chan'}, + plusinv =>{calc=>'$val=($val+100-$in)<100?($val+100-$in) : 100' ,txt=>'state + 100 - chan'}, + minusinv=>{calc=>'$val=($val-100+$in)>0?($val-100+$in) : 0' ,txt=>'state - 100 + chan'}, + mulinv =>{calc=>'$val=((100-$in)*$val)<100?(100-$in)*$val) : 100',txt=>'state * (100 - chan)'}, + invPlus =>{calc=>'$val=(100-$val-$in)>0?(100-$val-$in) : 0' ,txt=>'100 - state - chan'}, + invMinus=>{calc=>'$val=(100-$val+$in)<100?(100-$val-$in) : 100' ,txt=>'100 - state + chan'}, + invMul =>{calc=>'$val=(100-$val*$in)>0?(100-$val*$in) : 0' ,txt=>'100 - state * chan'}, + ); readingsSingleUpdate($hash,"R-logicCombTxt" ,$logicComb{$lComb}{txt},0); readingsSingleUpdate($hash,"R-logicCombCalc",$logicComb{$lComb}{calc},0); return ""; @@ -4665,27 +4664,27 @@ sub CUL_HM_dimLog($) {# dimmer readings - support virtual chan - unused so far #+++++++++++++++++ Action Detector ++++++++++++++++++++++++++++++++++++++++++++ # verify that devices are seen in a certain period of time -# It will generate events if no message is seen sourced by the device during +# It will generate events if no message is seen sourced by the device during # that period. # ActionDetector will use the fixed HMid 000000 sub CUL_HM_ActGetCreateHash() {# get ActionDetector - create if necessary if (!$modules{CUL_HM}{defptr}{"000000"}){ DoTrigger("global", "UNDEFINED ActionDetector CUL_HM 000000"); - $attr{ActionDetector}{actCycle} = 600; - $attr{ActionDetector}{"event-on-change-reading"} = ".*"; + $attr{ActionDetector}{actCycle} = 600; + $attr{ActionDetector}{"event-on-change-reading"} = ".*"; } my $actHash = $modules{CUL_HM}{defptr}{"000000"}; my $actName = $actHash->{NAME} if($actHash); - + if (!$actHash->{helper}{actCycle} || $actHash->{helper}{actCycle} != $attr{$actName}{actCycle}){ - $attr{$actName}{actCycle} = 30 if(!$attr{$actName}{actCycle} || - $attr{$actName}{actCycle}<30); - $actHash->{helper}{actCycle} = $attr{$actName}{actCycle}; - RemoveInternalTimer("ActionDetector"); - $actHash->{STATE} = "active"; - InternalTimer(gettimeofday()+$attr{$actName}{actCycle}, - "CUL_HM_ActCheck", "ActionDetector", 0); + $attr{$actName}{actCycle} = 30 if(!$attr{$actName}{actCycle} || + $attr{$actName}{actCycle}<30); + $actHash->{helper}{actCycle} = $attr{$actName}{actCycle}; + RemoveInternalTimer("ActionDetector"); + $actHash->{STATE} = "active"; + InternalTimer(gettimeofday()+$attr{$actName}{actCycle}, + "CUL_HM_ActCheck", "ActionDetector", 0); } return $actHash; } @@ -4706,8 +4705,8 @@ sub CUL_HM_ActAdd($$) {# add an HMid to list for activity supervision my ($cycleString,undef)=CUL_HM_time2sec($timeout); my $devName = CUL_HM_id2Name($devId); my $devHash = $defs{$devName}; - - $attr{$devName}{actCycle} = $cycleString; + + $attr{$devName}{actCycle} = $cycleString; $attr{$devName}{actStatus}=""; # force trigger # get last reading timestamp------- my $recent = ""; @@ -4719,17 +4718,17 @@ sub CUL_HM_ActAdd($$) {# add an HMid to list for activity supervision next if (!defined $ehash->{NAME}); use strict; my $eName = $ehash->{NAME}; - next if (!$eName); + next if (!$eName); foreach my $rName (keys %{$ehash->{READINGS}}){ next if (!$rName || - $rName eq "PairedTo" || # derived - $rName eq "peerList" || # derived - $rName eq "Activity" || # derived - $rName =~ m/^[.]?R-/ || # no Regs - those are derived from Reg - ReadingsVal($eName,$rName,"") =~ m/^set_/); # ignore setting - my $ts = ReadingsTimestamp($eName,$rName,""); - $recent = $ts if ($ts gt $recent); - } + $rName eq "PairedTo" || # derived + $rName eq "peerList" || # derived + $rName eq "Activity" || # derived + $rName =~ m/^[.]?R-/ || # no Regs - those are derived from Reg + ReadingsVal($eName,$rName,"") =~ m/^set_/); # ignore setting + my $ts = ReadingsTimestamp($eName,$rName,""); + $recent = $ts if ($ts gt $recent); + } } my $actHash = CUL_HM_ActGetCreateHash(); $actHash->{helper}{$devId}{start} = TimeNow(); @@ -4745,7 +4744,7 @@ sub CUL_HM_ActAdd($$) {# add an HMid to list for activity supervision return; } sub CUL_HM_ActDel($) {# delete HMid for activity supervision - my ($devId) = @_; + my ($devId) = @_; my $devName = CUL_HM_id2Name($devId); CUL_HM_setAttrIfCh($devName,"actStatus","deleted","Activity");#post trigger delete $attr{$devName}{actCycle}; @@ -4755,7 +4754,7 @@ sub CUL_HM_ActDel($) {# delete HMid for activity supervision delete ($actHash->{helper}{$devId}); my $peerIDs = $actHash->{helper}{peers}; - $peerIDs =~ s/$devId//g if($peerIDs); + $peerIDs =~ s/$devId//g if($peerIDs); $actHash->{helper}{peers} = CUL_HM_noDupInString($peerIDs); Log3 $actHash,3,"Device ".$devName." removed from ActionDetector"; RemoveInternalTimer("ActionDetector"); @@ -4769,55 +4768,55 @@ sub CUL_HM_ActCheck() {# perform supervision my $peerIDs = $actHash->{helper}{peers}?$actHash->{helper}{peers}:""; my @event; my ($cntUnkn,$cntAlive,$cntDead,$cntOff) =(0,0,0,0); - + foreach my $devId (split(",",$peerIDs)){ next if (!$devId); my $devName = CUL_HM_id2Name($devId); - if(!$devName || !defined($attr{$devName}{actCycle})){ - CUL_HM_ActDel($devId); - next; - } + if(!$devName || !defined($attr{$devName}{actCycle})){ + CUL_HM_ActDel($devId); + next; + } my $devHash = $defs{$devName}; - my $state; - my $oldState = AttrVal($devName,"actStatus","unset"); + my $state; + my $oldState = AttrVal($devName,"actStatus","unset"); my (undef,$tSec)=CUL_HM_time2sec($attr{$devName}{actCycle}); - if ($tSec == 0){# detection switched off - $cntOff++; - $state = "switchedOff"; - } - else{ - $actHash->{helper}{$devId}{recent} = ($devHash->{"protLastRcv"})?#update recent - $devHash->{"protLastRcv"} - :0; - my $tLast = $actHash->{helper}{$devId}{recent}; + if ($tSec == 0){# detection switched off + $cntOff++; + $state = "switchedOff"; + } + else{ + $actHash->{helper}{$devId}{recent} = ($devHash->{"protLastRcv"})?#update recent + $devHash->{"protLastRcv"} + :0; + my $tLast = $actHash->{helper}{$devId}{recent}; my @t = localtime($tod - $tSec); #time since when a trigger is expected - my $tSince = sprintf("%04d-%02d-%02d %02d:%02d:%02d", + my $tSince = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]); - - if (!$tLast){ #cannot determine time - if ($actHash->{helper}{$devId}{start} lt $tSince){ - $state = "dead"; - $cntDead++; - } - else{ - $state = "unknown"; - $cntUnkn++; - } - } - elsif ($tSince gt $tLast){ #no message received in window - $cntDead++; - $state = "dead"; - } - else{ #message in time - $cntAlive++; - $state = "alive"; - } - } - if ($oldState ne $state){ - readingsSingleUpdate($devHash,"Activity",$state,1); - $attr{$devName}{actStatus} = $state; - Log3 $actHash,4,"Device ".$devName." is ".$state; - } + + if (!$tLast){ #cannot determine time + if ($actHash->{helper}{$devId}{start} lt $tSince){ + $state = "dead"; + $cntDead++; + } + else{ + $state = "unknown"; + $cntUnkn++; + } + } + elsif ($tSince gt $tLast){ #no message received in window + $cntDead++; + $state = "dead"; + } + else{ #message in time + $cntAlive++; + $state = "alive"; + } + } + if ($oldState ne $state){ + readingsSingleUpdate($devHash,"Activity",$state,1); + $attr{$devName}{actStatus} = $state; + Log3 $actHash,4,"Device ".$devName." is ".$state; + } push @event, "status_".$devName.":".$state; } push @event, "state:"."alive:".$cntAlive @@ -4831,37 +4830,37 @@ sub CUL_HM_ActCheck() {# perform supervision } CUL_HM_UpdtReadBulk($actHash,1,@event); - - $attr{$actName}{actCycle} = 600 if($attr{$actName}{actCycle}<30); + + $attr{$actName}{actCycle} = 600 if($attr{$actName}{actCycle}<30); $actHash->{helper}{actCycle} = $attr{$actName}{actCycle}; - InternalTimer(gettimeofday()+$attr{$actName}{actCycle}, - "CUL_HM_ActCheck", "ActionDetector", 0); + InternalTimer(gettimeofday()+$attr{$actName}{actCycle}, + "CUL_HM_ActCheck", "ActionDetector", 0); } #+++++++++++++++++ helper +++++++++++++++++++++++++++++++++++++++++++++++++++++ sub CUL_HM_UpdtReadBulk(@) { #update a bunch of readings and trigger the events - my ($hash,$doTrg,@readings) = @_; + my ($hash,$doTrg,@readings) = @_; return if (!@readings); readingsBeginUpdate($hash); foreach my $rd (@readings){ next if (!$rd); my ($rdName, $rdVal) = split(":",$rd, 2); - readingsBulkUpdate($hash,$rdName, - ((defined($rdVal) && $rdVal ne "")?$rdVal:"-")); + readingsBulkUpdate($hash,$rdName, + ((defined($rdVal) && $rdVal ne "")?$rdVal:"-")); } readingsEndUpdate($hash,$doTrg); return $hash->{NAME}; } sub CUL_HM_UpdtReadSingle(@) { #update single reading and trigger the event - my ($hash,$rName,$val,$updt) = @_; + my ($hash,$rName,$val,$updt) = @_; readingsSingleUpdate($hash,$rName,$val,$updt); return $hash->{NAME}; } sub CUL_HM_setAttrIfCh($$$$) { - my ($name,$att,$val,$trig) = @_; + my ($name,$att,$val,$trig) = @_; if($attr{$name}{$att} ne $val){ DoTrigger($name,$trig.":".$val) if($trig); - $attr{$name}{$att} = $val; + $attr{$name}{$att} = $val; } } sub CUL_HM_noDup(@) {#return list with no duplicates @@ -4893,8 +4892,8 @@ sub CUL_HM_storeRssi(@){ my $rssi; foreach (keys %{$rssiP}){ my $val = $rssiP->{$_}?$rssiP->{$_}:0; - $rssi .= $_.":".(int($val*100)/100)." "; - } + $rssi .= $_.":".(int($val*100)/100)." "; + } $hash->{"rssi_".$peerName} = $rssi; return ; } @@ -4922,42 +4921,42 @@ sub CUL_HM_qAutoRead($$){ ||$lvl >= (0x07 & CUL_HM_getAttrInt($name,"autoReadReg"))); CUL_HM_qEntity($name,"qReqConf"); } -sub CUL_HM_unQEntity($$){# remove entity from q +sub CUL_HM_unQEntity($$){# remove entity from q my ($name,$q) = @_; - + my $devN = CUL_HM_getDeviceName($name); return if (AttrVal($devN,"subType","") eq "virtual"); my $dq = $defs{$devN}{helper}{q}; return if ($dq->{$q} eq ""); if ($devN eq $name){#all channels included - $dq->{$q}=""; + $dq->{$q}=""; } else{ my @chns = split(",",$dq->{$q}); - my $chn = substr(CUL_HM_name2Id($name),6,2); - @chns = grep !/$chn/,@chns; - $dq->{$q} = join",",@chns; + my $chn = substr(CUL_HM_name2Id($name),6,2); + @chns = grep !/$chn/,@chns; + $dq->{$q} = join",",@chns; } my $mQ = $q."Wu" if (CUL_HM_getRxType($defs{$name}) & 0x1C); - $mQ = $modules{CUL_HM}{helper}{$q}; + $mQ = $modules{CUL_HM}{helper}{$q}; @{$mQ} = grep !/^$devN$/,@{$mQ} if ($dq->{$q} eq ""); RemoveInternalTimer("sUpdt:$name") if ($q eq "qReqStat");#remove delayed } sub CUL_HM_qEntity($$){ # add to queue my ($name,$q) = @_; return if ($modules{CUL_HM}{helper}{hmManualOper});#no autoaction when manual - + my $devN = CUL_HM_getDeviceName($name); return if (AttrVal($devN,"subType","") eq "virtual"); return if ($defs{$devN}{helper}{q}{$q} eq "00"); #already requesting all if ($devN eq $name){#config for all device - $defs{$devN}{helper}{q}{$q}="00"; + $defs{$devN}{helper}{q}{$q}="00"; } else{ - $defs{$devN}{helper}{q}{$q} = CUL_HM_noDupInString( - $defs{$devN}{helper}{q}{$q} - .",".substr(CUL_HM_name2Id($name),6,2)); + $defs{$devN}{helper}{q}{$q} = CUL_HM_noDupInString( + $defs{$devN}{helper}{q}{$q} + .",".substr(CUL_HM_name2Id($name),6,2)); } $q .= "Wu" if (CUL_HM_getRxType($defs{$name}) & 0x1C);#normal or wakeup q? @@ -4966,7 +4965,7 @@ sub CUL_HM_qEntity($$){ # add to queue my $wT = (@{$modules{CUL_HM}{helper}{qReqStat}})? "1": - $modules{CUL_HM}{hmAutoReadScan}; + $modules{CUL_HM}{hmAutoReadScan}; RemoveInternalTimer("CUL_HM_procQs"); InternalTimer(gettimeofday()+ $wT,"CUL_HM_procQs","CUL_HM_procQs", 0); } @@ -4977,89 +4976,89 @@ sub CUL_HM_procQs($){#process non-wakeup queues my $mq = $modules{CUL_HM}{helper}; foreach my $q ("qReqStat","qReqConf"){ if (@{$mq->{$q}}){ - my $devN = ${$mq->{$q}}[0]; - my $ioName = $defs{$devN}{IODev}{NAME}; - if ( ( $ioName + my $devN = ${$mq->{$q}}[0]; + my $ioName = $defs{$devN}{IODev}{NAME}; + if ( ( $ioName && ReadingsVal($ioName,"cond","") =~ m /^(ok|Overload-released|init)$/ - && $q eq "qReqStat") - ||( CUL_HM_autoReadReady($ioName) - && $q eq "qReqConf")){ + && $q eq "qReqStat") + ||( CUL_HM_autoReadReady($ioName) + && $q eq "qReqConf")){ my $dq = $defs{$devN}{helper}{q}; my @chns = split(",",$dq->{$q}); - my $nOpen = scalar @chns; - if (@chns > 1){$dq->{$q} = join ",",@chns[1..$nOpen-1];} - else{ $dq->{$q} = ""; - @{$mq->{$q}} = grep !/^$devN$/,@{$mq->{$q}}; - } - my $dId = CUL_HM_name2Id($devN); - my $eN=($chns[0]ne "00")?CUL_HM_id2Name($dId.$chns[0]):$devN; - if ($q eq "qReqConf"){ + my $nOpen = scalar @chns; + if (@chns > 1){$dq->{$q} = join ",",@chns[1..$nOpen-1];} + else{ $dq->{$q} = ""; + @{$mq->{$q}} = grep !/^$devN$/,@{$mq->{$q}}; + } + my $dId = CUL_HM_name2Id($devN); + my $eN=($chns[0]ne "00")?CUL_HM_id2Name($dId.$chns[0]):$devN; + if ($q eq "qReqConf"){ $mq->{autoRdActive} = $devN; - CUL_HM_Set($defs{$eN},$eN,"getConfig"); - } - else{ - CUL_HM_Set($defs{$eN},$eN,"statusRequest"); - } - } - last; # execute only one! + CUL_HM_Set($defs{$eN},$eN,"getConfig"); + } + else{ + CUL_HM_Set($defs{$eN},$eN,"statusRequest"); + } + } + last; # execute only one! } } - delete $mq->{autoRdActive} + delete $mq->{autoRdActive} if ($mq->{autoRdActive} && $defs{$mq->{autoRdActive}}{helper}{prt}{sProc} != 1); my $next;# how long to wait for next timer if (@{$mq->{qReqStat}}){$next = 1} elsif (@{$mq->{qReqConf}}){$next = $modules{CUL_HM}{hmAutoReadScan}} - + InternalTimer(gettimeofday()+$next,"CUL_HM_procQs","CUL_HM_procQs",0) - if ($next); + if ($next); } -sub CUL_HM_appFromQ($$){#stack commands if pend in WuQ +sub CUL_HM_appFromQ($$){#stack commands if pend in WuQ my ($name,$reason) = @_; - my $devN = CUL_HM_getDeviceName($name); + my $devN = CUL_HM_getDeviceName($name); my $dId = CUL_HM_name2Id($devN); my $dq = $defs{$devN}{helper}{q}; if ($reason eq "cf"){# reason is config. add all since User has control foreach my $q ("qReqStat","qReqConf"){ if ($dq->{$q} ne ""){# need update - my @eName; - if ($dq->{$q} eq "00"){ - push @eName,$devN; - } - else{ + my @eName; + if ($dq->{$q} eq "00"){ + push @eName,$devN; + } + else{ my @chns = split(",",$dq->{$q}); - push @eName,CUL_HM_id2Name($dId.$_)foreach (@chns); - } - $dq->{$q} = ""; - @{$modules{CUL_HM}{helper}{$q."Wu"}} = - grep !/^$devN$/,@{$modules{CUL_HM}{helper}{$q."Wu"}}; + push @eName,CUL_HM_id2Name($dId.$_)foreach (@chns); + } + $dq->{$q} = ""; + @{$modules{CUL_HM}{helper}{$q."Wu"}} = + grep !/^$devN$/,@{$modules{CUL_HM}{helper}{$q."Wu"}}; foreach my $eN(@eName){ next if (!$eN); - CUL_HM_Set($defs{$eN},$eN,"getConfig") if ($q eq "qReqConf"); - CUL_HM_Set($defs{$eN},$eN,"statusRequest") if ($q eq "qReqStat"); + CUL_HM_Set($defs{$eN},$eN,"getConfig") if ($q eq "qReqConf"); + CUL_HM_Set($defs{$eN},$eN,"statusRequest") if ($q eq "qReqStat"); } - } - } + } + } } elsif($reason eq "wu"){#wakeup - just add one step my $ioName = $defs{$devN}{IODev}{NAME}; - return if (!CUL_HM_autoReadReady($ioName));# no sufficient performance + return if (!CUL_HM_autoReadReady($ioName));# no sufficient performance foreach my $q ("qReqStat","qReqConf"){ - if ($dq->{$q} ne ""){# need update + if ($dq->{$q} ne ""){# need update my @chns = split(",",$dq->{$q}); - my $nOpen = scalar @chns; - if ($nOpen > 1){$dq->{$q} = join ",",@chns[1..$nOpen-1];} - else{ $dq->{$q} = ""; - @{$modules{CUL_HM}{helper}{$q."Wu"}} = - grep !/^$devN$/,@{$modules{CUL_HM}{helper}{$q."Wu"}}; - } - my $eN=($chns[0]ne "00")?CUL_HM_id2Name($dId.$chns[0]):$devN; - CUL_HM_Set($defs{$eN},$eN,"getConfig") if ($q eq "qReqConf"); - CUL_HM_Set($defs{$eN},$eN,"statusRequest") if ($q eq "qReqStat"); - return;# Only one per step - very defensive. - } - } + my $nOpen = scalar @chns; + if ($nOpen > 1){$dq->{$q} = join ",",@chns[1..$nOpen-1];} + else{ $dq->{$q} = ""; + @{$modules{CUL_HM}{helper}{$q."Wu"}} = + grep !/^$devN$/,@{$modules{CUL_HM}{helper}{$q."Wu"}}; + } + my $eN=($chns[0]ne "00")?CUL_HM_id2Name($dId.$chns[0]):$devN; + CUL_HM_Set($defs{$eN},$eN,"getConfig") if ($q eq "qReqConf"); + CUL_HM_Set($defs{$eN},$eN,"statusRequest") if ($q eq "qReqStat"); + return;# Only one per step - very defensive. + } + } } } sub CUL_HM_autoReadReady($){# capacity for autoread available? @@ -5069,12 +5068,12 @@ sub CUL_HM_autoReadReady($){# capacity for autoread available? && $defs{$mHlp->{autoRdActive}}){ return 0 if ($defs{$mHlp->{autoRdActive}}{helper}{prt}{sProc} == 1); # predecessor still on } - if ( !$ioName + if ( !$ioName || ReadingsVal($ioName,"cond","") !~ m /^(ok|Overload-released|init)$/ - || ( $defs{$ioName}{helper}{q} - && ($defs{$ioName}{helper}{q}{cap}{sum}/16.8)> - AttrVal($ioName,"hmMsgLowLimit",40))){ - return 0; + || ( $defs{$ioName}{helper}{q} + && ($defs{$ioName}{helper}{q}{cap}{sum}/16.8)> + AttrVal($ioName,"hmMsgLowLimit",40))){ + return 0; } return 1; } @@ -5085,7 +5084,7 @@ sub CUL_HM_getAttrInt($@){#return attrValue as integer no warnings 'numeric'; my $devN = $defs{$name}{device}?$defs{$name}{device}:$name; $default = 0 if (!defined $default); - $val = int($attr{$devN}{$attrName}?$attr{$devN}{$attrName}:$default)+0 + $val = int($attr{$devN}{$attrName}?$attr{$devN}{$attrName}:$default)+0 if($val eq ""); use warnings 'numeric'; return substr($val,0,1); @@ -5103,24 +5102,24 @@ sub CUL_HM_peerUsed($) {# are peers expected? return 0 if (!$hash->{helper}{role}{chn});#device has no channels my $devId = substr($hash->{DEF},0,6); my $peerIDs = AttrVal($name,"peerIDs",undef); - return 0 if (AttrVal(CUL_HM_id2Name($devId),"subType","") eq "virtual"); - + return 0 if (AttrVal(CUL_HM_id2Name($devId),"subType","") eq "virtual"); + my $mId = CUL_HM_getMId($hash); my $cNo = hex(substr($hash->{DEF}."01",6,2))."p"; #default to channel 01 return 0 if (!$mId || !$culHmModel{$mId}); foreach my $ls (split ",",$culHmModel{$mId}{lst}){ - my ($l,$c) = split":",$ls; + my ($l,$c) = split":",$ls; if ( ($l =~ m/^(p|3|4)$/ && !$c ) # 3,4,p without chanspec - ||($c && $c =~ m/$cNo/ )){ - return 1; - } + ||($c && $c =~ m/$cNo/ )){ + return 1; + } } } sub CUL_HM_peersValid($) {# is list valid? my $name = shift; if (CUL_HM_peerUsed($name) && AttrVal($name,"peerIDs","") !~ m/00000000/){ - return 0; + return 0; } return 1; } @@ -5130,12 +5129,12 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo my $hash = $defs{$name}; my $devId = substr($hash->{DEF},0,6); my $chn = substr($hash->{DEF}."01",6,2); - return undef if (AttrVal(CUL_HM_id2Name($devId),"subType","") eq "virtual"); + return undef if (AttrVal(CUL_HM_id2Name($devId),"subType","") eq "virtual"); my @pNames; - push @pNames,CUL_HM_peerChName($_,$devId,"") + push @pNames,CUL_HM_peerChName($_,$devId,"") foreach (grep !/00000000/,split(",",AttrVal($name,"peerIDs",""))); - + my @lsNo; my $mId = CUL_HM_getMId($hash); return undef if (!$mId || !$culHmModel{$mId}); @@ -5143,24 +5142,24 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo push @lsNo,"0:"; } elsif ($hash->{helper}{role}{chn}){ - - foreach my $ls (split ",",$culHmModel{$mId}{lst}){ - my ($l,$c) = split":",$ls; - if ($l ne "p"){# ignore peer-only entries - if ($c){ - my $chNo = hex($chn); - if ($c =~ m/($chNo)p/){push @lsNo,"$l:$_" foreach (@pNames);} - elsif($c =~ m/$chNo/ ){push @lsNo,"$l:";} - } - else{ - if ($l == 3 || $l == 4){push @lsNo,"$l:$_" foreach (@pNames); - }else{ push @lsNo,"$l:" ;} - } - } - } + + foreach my $ls (split ",",$culHmModel{$mId}{lst}){ + my ($l,$c) = split":",$ls; + if ($l ne "p"){# ignore peer-only entries + if ($c){ + my $chNo = hex($chn); + if ($c =~ m/($chNo)p/){push @lsNo,"$l:$_" foreach (@pNames);} + elsif($c =~ m/$chNo/ ){push @lsNo,"$l:";} + } + else{ + if ($l == 3 || $l == 4){push @lsNo,"$l:$_" foreach (@pNames); + }else{ push @lsNo,"$l:" ;} + } + } + } } my $pre = (CUL_HM_getAttrInt($name,"expert") == 2)?"":"."; - + $_ = $pre."RegL_0".$_ foreach (@lsNo); return @lsNo; } @@ -5197,10 +5196,10 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo well.
      An example for a full definition of a 2 channel switch is given below:
      -
        - define livingRoomSwitch CUL_HM 123456
        - define LivingroomMainLight CUL_HM 12345601
        - define LivingroomBackLight CUL_HM 12345602

        +
          + define livingRoomSwitch CUL_HM 123456
          + define LivingroomMainLight CUL_HM 12345601
          + define LivingroomBackLight CUL_HM 12345602

        livingRoomSwitch is the device managing communication. This device is @@ -5295,24 +5294,24 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo Universal commands (available to most hm devices):
          -
        • clear <[readings|register|msgEvents]>
          +
        • clear <[readings|register|msgEvents]>
          A set of variables can be removed.
          -
            - readings: all readings will be deleted. Any new reading will be added usual. May be used to eliminate old data
            - register: all captured register-readings in FHEM will be removed. This has NO impact to the values in the device.
            - msgEvents: all message event counter will be removed. Also commandstack will be cleared.
            - rssi: collected rssi values will be cleared.
            -
          -
        • -
        • getConfig
          +
            + readings: all readings will be deleted. Any new reading will be added usual. May be used to eliminate old data
            + register: all captured register-readings in FHEM will be removed. This has NO impact to the values in the device.
            + msgEvents: all message event counter will be removed. Also commandstack will be cleared.
            + rssi: collected rssi values will be cleared.
            +
          +
        • +
        • getConfig
          Will read major configuration items stored in the HM device. Executed on a channel it will read pair Inforamtion, List0, List1 and List3 of the 1st internal peer. Furthermore the peerlist will be retrieved for teh given channel. If executed on a device the command will get the above info or all assotated channels. Not included will be the configuration for additional peers.
          The command is a shortcut - for a selection of other commands. -
        • + for a selection of other commands. +
        • getRegRaw [List0|List1|List2|List3|List4|List5|List6] <peerChannel>
          @@ -5332,13 +5331,13 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo data!. It controlls actions taken upon receive of a trigger from the peer.
          - List4: settings for channel (button) of a remote

          + List4: settings for channel (button) of a remote

          <PeerChannel> paired HMid+ch, i.e. 4 byte (8 digit) value like '12345601'. It is mendatory for List 3 and 4 and can be left out for List 0 and 1.
          - 'all' can be used to get data of each paired link of the channel.
          + 'all' can be used to get data of each paired link of the channel.
          'selfxx' can be used to address data for internal channels (associated with the build-in switches if any). xx is the number of the channel in @@ -5358,98 +5357,98 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo Note4: the direct buttons on a HM device are hidden by default. Nevertheless those are implemented as links as well. To get access to the 'internal links' it is necessary to issue
          - 'set <name> regSet intKeyVisib visib'
          - or
          - 'set <name> regBulk RegL_0: 2:81'
          - - Reset it by replacing '81' with '01'
          example:
          + 'set <name> regSet intKeyVisib visib'
          + or
          + 'set <name> regBulk RegL_0: 2:81'
          -
            - set mydimmer getRegRaw List1
            - set mydimmer getRegRaw List3 all
            -
          -
        • -
        • getSerial
          + Reset it by replacing '81' with '01'
          example:
          + +
            + set mydimmer getRegRaw List1
            + set mydimmer getRegRaw List3 all
            +
          +
        • +
        • getSerial
          Read serial number from device and write it to attribute serialNr. -
        • -
        • inhibit [on|off]
          - Block / unblock all changes to the actor channel, i.e. actor state is frozen - until inhibit is set off again. Inhibit can be executed on any actor channel - but obviously not on sensors - would not make any sense.
          - Practically it can be used to suspend any notifies as well as peered channel action - temporarily without the need to delete them.
          +
        • +
        • inhibit [on|off]
          + Block / unblock all changes to the actor channel, i.e. actor state is frozen + until inhibit is set off again. Inhibit can be executed on any actor channel + but obviously not on sensors - would not make any sense.
          + Practically it can be used to suspend any notifies as well as peered channel action + temporarily without the need to delete them.
          Examples:
            # Block operation
            set keymatic inhibit on

            -
          +
      • pair
        - Pair the device with a known serialNumber (e.g. after a device reset) - to FHEM Central unit. FHEM Central is usualy represented by CUL/CUNO, - HMLAN,... - If paired, devices will report status information to + Pair the device with a known serialNumber (e.g. after a device reset) + to FHEM Central unit. FHEM Central is usualy represented by CUL/CUNO, + HMLAN,... + If paired, devices will report status information to FHEM. If not paired, the device won't respond to some requests, and certain status information is also not reported. Paring is on device - level. Channels cannot be paired to central separate from the device. - See also getPair and - unpair.
        - Don't confuse pair (to a central) with peer (channel to channel) with - peerChan.
        -
      • + level. Channels cannot be paired to central separate from the device. + See also getPair and + unpair.
        + Don't confuse pair (to a central) with peer (channel to channel) with + peerChan.
        +
      • peerBulk <peerch1,peerch2,...> [set|unset]
        - peerBulk will add peer channels to the channel. All peers in the - list will be added.
        - peering sets the configuration of this link to its defaults. As peers are not - added in pairs default will be as defined for 'single' by HM for this device.
        - More suffisticated funktionality is provided by - peerChan.
        - peerBulk will not delete existing peers, just handle the given peerlist. - Other already installed peers will not be touched.
        - peerBulk may be used to remove peers using unset option while default ist set.
        - - Main purpose of this command is to re-store data to a device. - It is recommended to restore register configuration utilising - regBulk subsequent.
        - Example:
        -
          - set myChannel peerBulk 12345601,
          - set myChannel peerBulk self01,self02,FB_Btn_04,FB_Btn_03,
          - set myChannel peerBulk 12345601 unset # remove peer 123456 channel 01
          -
        -
      • + peerBulk will add peer channels to the channel. All peers in the + list will be added.
        + peering sets the configuration of this link to its defaults. As peers are not + added in pairs default will be as defined for 'single' by HM for this device.
        + More suffisticated funktionality is provided by + peerChan.
        + peerBulk will not delete existing peers, just handle the given peerlist. + Other already installed peers will not be touched.
        + peerBulk may be used to remove peers using unset option while default ist set.
        + + Main purpose of this command is to re-store data to a device. + It is recommended to restore register configuration utilising + regBulk subsequent.
        + Example:
        +
          + set myChannel peerBulk 12345601,
          + set myChannel peerBulk self01,self02,FB_Btn_04,FB_Btn_03,
          + set myChannel peerBulk 12345601 unset # remove peer 123456 channel 01
          +
        +
      • regBulk <reg List>:<peer> <addr1:data1> <addr2:data2>... -
        - This command will replace the former regRaw. It allows to set register - in raw format. Its main purpose is to restore a complete register list - to values secured before.
        - Values may be read by getConfig. The - resulting readings can be used directly for this command.
        - <reg List> is the list data should be written to. Format could be - '00', 'RegL_00', '01'...
        - <peer> is an optional adder in case the list requires a peer. - The peer can be given as channel name or the 4 byte (8 chars) HM - channel ID.
        - <addr1:data1> is the list of register to be written in hex - format.
        - Example:
        -
          - set myChannel regBulk RegL_00: 02:01 0A:17 0B:43 0C:BF 15:FF 00:00
          - RegL_03:FB_Btn_07 - 01:00 02:00 03:00 04:32 05:64 06:00 07:FF 08:00 09:FF 0A:01 0B:44 0C:54 0D:93 0E:00 0F:00 11:C8 12:00 13:00 14:00 15:00 16:00 17:00 18:00 19:00 1A:00 1B:00 1C:00 1D:FF 1E:93 1F:00 81:00 82:00 83:00 84:32 85:64 86:00 87:FF 88:00 89:FF 8A:21 8B:44 8C:54 8D:93 8E:00 8F:00 91:C8 92:00 93:00 94:00 95:00 96:00 97:00 98:00 99:00 9A:00 9B:00 9C:00 9D:05 9E:93 9F:00 00:00
          - set myblind regBulk 01 0B:10
          - set myblind regBulk 01 0C:00
          -
        - myblind will set the max drive time up for a blind actor to 25,6sec
      • +
        + This command will replace the former regRaw. It allows to set register + in raw format. Its main purpose is to restore a complete register list + to values secured before.
        + Values may be read by getConfig. The + resulting readings can be used directly for this command.
        + <reg List> is the list data should be written to. Format could be + '00', 'RegL_00', '01'...
        + <peer> is an optional adder in case the list requires a peer. + The peer can be given as channel name or the 4 byte (8 chars) HM + channel ID.
        + <addr1:data1> is the list of register to be written in hex + format.
        + Example:
        +
          + set myChannel regBulk RegL_00: 02:01 0A:17 0B:43 0C:BF 15:FF 00:00
          + RegL_03:FB_Btn_07 + 01:00 02:00 03:00 04:32 05:64 06:00 07:FF 08:00 09:FF 0A:01 0B:44 0C:54 0D:93 0E:00 0F:00 11:C8 12:00 13:00 14:00 15:00 16:00 17:00 18:00 19:00 1A:00 1B:00 1C:00 1D:FF 1E:93 1F:00 81:00 82:00 83:00 84:32 85:64 86:00 87:FF 88:00 89:FF 8A:21 8B:44 8C:54 8D:93 8E:00 8F:00 91:C8 92:00 93:00 94:00 95:00 96:00 97:00 98:00 99:00 9A:00 9B:00 9C:00 9D:05 9E:93 9F:00 00:00
          + set myblind regBulk 01 0B:10
          + set myblind regBulk 01 0C:00
          +
        + myblind will set the max drive time up for a blind actor to 25,6sec
      • regSet [prep|exec] <regName> <value> <peerChannel>
        For some major register a readable version is implemented supporting register names <regName> and value conversionsing. Only a subset of register can be supproted.
        - Optional parameter [prep|exec] allowes to pack the messages and therefore greatly - improve data transmission. - Usage is to send the commands with paramenter "prep". The data will be accumulated for send. - The last command must have the parameter "exec" in order to transmitt the information.
        + Optional parameter [prep|exec] allowes to pack the messages and therefore greatly + improve data transmission. + Usage is to send the commands with paramenter "prep". The data will be accumulated for send. + The last command must have the parameter "exec" in order to transmitt the information.
        <value> is the data in human readable manner that will be written to the register.
        @@ -5471,7 +5470,7 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo href="#HMAES">note above). Warning: if the device is attached via a CUL, you won't be able to switch it (or deactivate signing) from fhem before you reset the device directly. -
      • +
      • statusRequest
        Update device status. For multichannel devices it should be issued on an per channel base @@ -5492,7 +5491,7 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo set vRemote_Btn5 press long
      see also press - +

    @@ -5508,27 +5507,27 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo off-for-timer like FS20 is not supported. It may to be programmed thru channel register.
  • on-till <time> - set the switch on for the given end time.
    -
      set <name> on-till 20:32:10
    - Currently a max of 24h is supported with endtime.
    -
  • +
      set <name> on-till 20:32:10
    + Currently a max of 24h is supported with endtime.
    +
  • press <[short|long]><[on|off]> - simulate a press of the local button or direct connected switch of the actor.
    - [short|long] choose whether to simulate a short or long press of the button.
    - [on|off] is relevant only for devices with direct buttons per channel. - Those are available for dimmer and blind-actor, usually not for switches
    -
  • -
  • toggle - toggle the Actor. It will switch from any current - level to off or from off to 100%
  • + simulate a press of the local button or direct connected switch of the actor.
    + [short|long] choose whether to simulate a short or long press of the button.
    + [on|off] is relevant only for devices with direct buttons per channel. + Those are available for dimmer and blind-actor, usually not for switches
    + +
  • toggle - toggle the Actor. It will switch from any current + level to off or from off to 100%

- +
  • dimmer, blindActuator
    - Dimmer may support virtual channels. Those are autocrated if applicable. Usually there are 2 virtual channels - in addition to the primary channel. Virtual dimmer channels are inactive by default but can be used in - in parallel to the primay channel to control light.
    - Virtual channels have default naming SW<channel>_V<no>. e.g. Dimmer_SW1_V1 and Dimmer_SW1_V2.
    - Dimmer virtual channels are completely different from FHEM virtual buttons and actors but - are part of the HM device. Documentation and capabilities for virtual channels is out of scope.
    + Dimmer may support virtual channels. Those are autocrated if applicable. Usually there are 2 virtual channels + in addition to the primary channel. Virtual dimmer channels are inactive by default but can be used in + in parallel to the primay channel to control light.
    + Virtual channels have default naming SW<channel>_V<no>. e.g. Dimmer_SW1_V1 and Dimmer_SW1_V2.
    + Dimmer virtual channels are completely different from FHEM virtual buttons and actors but + are part of the HM device. Documentation and capabilities for virtual channels is out of scope.
    • 0 - 100 [on-time] [ramp-time]
      set the actuator to the given value (in percent) @@ -5544,17 +5543,17 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
    • on-for-timer <sec> - Dimmer only!
    • on-till <time> - Dimmer only!
    • stop - stop motion (blind) or dim ramp
    • -
    • pct <level> [<ontime>] [<ramptime>] - set actor to a desired absolut level. - Optional ontime and ramptime could be given for dimmer.
    • +
    • pct <level> [<ontime>] [<ramptime>] - set actor to a desired absolut level. + Optional ontime and ramptime could be given for dimmer.
    • up [changeValue] [<ontime>] [<ramptime>] dim up one step
    • down [changeValue] [<ontime>] [<ramptime>] dim up one step
      - changeValue is optional an gives the level to be changed up or down in percent. Granularity is 0.5%, default is 10%.
      - ontime is optional an gives the duration of the level to be kept. '0' means forever and is default.
      - ramptime is optional an defines the change speed to reach the new level. It is meaningful only for dimmer. -
    • + changeValue is optional an gives the level to be changed up or down in percent. Granularity is 0.5%, default is 10%.
      + ontime is optional an gives the duration of the level to be kept. '0' means forever and is default.
      + ramptime is optional an defines the change speed to reach the new level. It is meaningful only for dimmer. +

    -
  • +
  • remotes, pushButton
    This class of devices does not react on requests unless they are put to learn mode. FHEM obeys this behavior by stacking all requests until @@ -5566,25 +5565,25 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo [set|unset] [both|actor|remote]
    peerChan will establish a connection between a sender-channel and - an actuator-channel called link in HM nomenclatur. Peering must not be - confused with pairing.
    - Pairing refers to assign a device to the central.
    - Peering refers to virtally connect two channels.
    - Peering allowes direkt interaction between sender and aktor without - the necessity of a CCU
    - Peering a sender-channel causes the sender to expect an ack from -each- - of its peers after sending a trigger. It will give positive feedback (e.g. LED green) - only if all peers acknowledged.
    - Peering an aktor-channel will setup a parameter set which defines the action to be - taken once a trigger from -this- peer arrived. In other words an aktor will
    - - process trigger from peers only
    - - define the action to be taken dedicated for each peer's trigger
    - An actor channel will setup a default action upon peering - which is actor dependant. - It may also depend whether one or 2 buttons are peered in one command. - A swich may setup oen button for 'on' and the other for 'off' if 2 button are - peered. If only one button is peered the funktion will likely be 'toggle'.
    - The funtion can be modified by programming the register (aktor dependant).
    - + an actuator-channel called link in HM nomenclatur. Peering must not be + confused with pairing.
    + Pairing refers to assign a device to the central.
    + Peering refers to virtally connect two channels.
    + Peering allowes direkt interaction between sender and aktor without + the necessity of a CCU
    + Peering a sender-channel causes the sender to expect an ack from -each- + of its peers after sending a trigger. It will give positive feedback (e.g. LED green) + only if all peers acknowledged.
    + Peering an aktor-channel will setup a parameter set which defines the action to be + taken once a trigger from -this- peer arrived. In other words an aktor will
    + - process trigger from peers only
    + - define the action to be taken dedicated for each peer's trigger
    + An actor channel will setup a default action upon peering - which is actor dependant. + It may also depend whether one or 2 buttons are peered in one command. + A swich may setup oen button for 'on' and the other for 'off' if 2 button are + peered. If only one button is peered the funktion will likely be 'toggle'.
    + The funtion can be modified by programming the register (aktor dependant).
    + Even though the command is executed on a remote or push-button it will as well take effect on the actuator directly. Both sides' peering is virtually independant and has different impact on sender and receiver @@ -5602,14 +5601,14 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo 3rd button pair correcponding to button 5 and 6 in single mode.
    If the command is executed on a channel the btn_no is ignored. - It needs to be set, should be 0
    + It needs to be set, should be 0
    - [single|dual]: this mode impacts the default behavior of the - Actuator upon using this button. E.g. a dimmer can be learned to a + [single|dual]: this mode impacts the default behavior of the + Actuator upon using this button. E.g. a dimmer can be learned to a single button or to a button pair.
    - Defaults to dual.
    + Defaults to dual.
    - 'dual' (default) Button pairs two buttons to one actuator. With a + 'dual' (default) Button pairs two buttons to one actuator. With a dimmer this means one button for dim-up and one for dim-down.
    'single' uses only one button of the sender. It is useful for e.g. for @@ -5617,18 +5616,18 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo be learned to only one button.
    [set|unset]: selects either enter a peering or remove it.
    - Defaults to set.
    + Defaults to set.
    'set' will setup peering for the channels
    'unset' will remove the peering for the channels
    [actor|remote|both] limits the execution to only actor or only remote. This gives the user the option to redo the peering on the remote channel while the settings in the actor will not be removed.
    - Defaults to both.
    + Defaults to both.
    Example: -
      - +
        + set myRemote peerChan 2 mySwActChn single set #peer second button to an actuator channel
        set myRmtBtn peerChan 0 mySwActChn single set #myRmtBtn is a button of the remote. '0' is not processed here
        set myRemote peerChan 2 mySwActChn dual set #peer button 3 and 4
        @@ -5636,10 +5635,10 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo set myRemote peerChan 3 mySwActChn dual unset aktor #remove peering for button 5 and 6 in actor only
        set myRemote peerChan 3 mySwActChn dual set remote #peer button 5 and 6 on remote only. Link settings il mySwActChn will be maintained
        -
      +
  • - +
  • virtual
    • peerChan see remote
    • @@ -5647,25 +5646,25 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo simulates a button press short (default) or long. Note that the current implementation will not specify the duration for long. Only one trigger will be sent of type "long". - +
    • postEvent <[0..200]> simulates an event. A value must be given between 0 and 200.
      - Alternally a literal ca be given as used by other sensors. enter
      - postEvent ?
      - to get options
      -
    • + Alternally a literal ca be given as used by other sensors. enter
      + postEvent ?
      + to get options
      +
    -
  • +
  • smokeDetector
    Note: All these commands work right now only if you have more then one smoekDetector, and you peered them to form a group. For issuing the commands you have to use the master of this group, and currently you have to guess which of the detectors is the master.
    - smokeDetector can be setup to teams using - peerChan. You need to peer all - team-members to the master. Don't forget to also peerChan the master - itself to the team - i.e. peer it to itself! doing that you have full - controll over the team and don't need to guess.
    + smokeDetector can be setup to teams using + peerChan. You need to peer all + team-members to the master. Don't forget to also peerChan the master + itself to the team - i.e. peer it to itself! doing that you have full + controll over the team and don't need to guess.
    • test - execute a network test
    • alarmOn - initiate an alarm
    • @@ -5678,18 +5677,18 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo Set the text on the display of the device. To this purpose issue this set command first (or a number of them), and then choose from the teach-in menu of the 4Dis the "Central" to transmit the data.
      - If used on a channel btn_no and on|off must not be given but only pure text. + If used on a channel btn_no and on|off must not be given but only pure text. Example:
        - + set 4Dis text 1 on On Lamp
        set 4Dis text 1 off Kitchen Off
        -
        +
        set 4Dis_chn4 text Kitchen Off
        -
      - -
    + +
  • +
  • Climate-Control (HM-CC-TC)
      @@ -5706,33 +5705,33 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo Specify a list of temperature intervals. Up to 24 intervals can be specified for each week day, the resolution is 10 Minutes. The last time spec must always be 24:00.
      - Example: until 6:00 temperature shall be 19, from then until 23:00 temperature shall be + Example: until 6:00 temperature shall be 19, from then until 23:00 temperature shall be 22.5, thereafter until midnight, 19 degrees celsius is desired.
      - set th tempListSat 06:00 19 23:00 22.5 24:00 19
      + set th tempListSat 06:00 19 23:00 22.5 24:00 19
    • partyMode <HH:MM><durationDays>
      - set control mode to party and device ending time. Add the time it ends - and the number of days it shall last. If it shall end next day '1' - must be entered
    • + set control mode to party and device ending time. Add the time it ends + and the number of days it shall last. If it shall end next day '1' + must be entered
    • systime
      set time in climate channel to system time

    -
  • - -
  • Climate-Control (HM-CC-RT-DN|HM-CC-RT-DN-BoM) +
  • + +
  • Climate-Control (HM-CC-RT-DN|HM-CC-RT-DN-BoM)
      -
    • controlMode <auto|boost|day|night>
    • -
    • controlManu <temp>
    • -
    • controlParty <temp><startDate><startTime><endDate><endTime>
      - set control mode to party, define temp and timeframe.
      - example:
      - set controlParty 15 03.8.13 20:30 5.8.13 11:30
    • +
    • controlMode <auto|boost|day|night>
    • +
    • controlManu <temp>
    • +
    • controlParty <temp><startDate><startTime><endDate><endTime>
      + set control mode to party, define temp and timeframe.
      + example:
      + set controlParty 15 03.8.13 20:30 5.8.13 11:30
    • systime
      set time in climate channel to system time
    • -
    • desired-temp <temp>
      +
    • desired-temp <temp>
      Set different temperatures. <temp> must be between 6 and 30 Celsius, and precision is half a degree.
    • -
    • tempListSat [prep|exec] HH:MM temp ... 24:00 temp
    • +
    • tempListSat [prep|exec] HH:MM temp ... 24:00 temp
    • tempListSun [prep|exec] HH:MM temp ... 24:00 temp
    • tempListMon [prep|exec] HH:MM temp ... 24:00 temp
    • tempListTue [prep|exec] HH:MM temp ... 24:00 temp
    • @@ -5742,28 +5741,28 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo Specify a list of temperature intervals. Up to 24 intervals can be specified for each week day, the resolution is 10 Minutes. The last time spec must always be 24:00.
      - Optional parameter [prep|exec] allowes to pack the messages and therefore greatly - improve data transmission. This is especially helpful if device is operated in wakeup mode. - Usage is to send the commands with paramenter "prep". The data will be accumulated for send. - The last command must have the parameter "exec" in order to transmitt the information.
      - Example: until 6:00 temperature shall be 19, from then until 23:00 temperature shall be + Optional parameter [prep|exec] allowes to pack the messages and therefore greatly + improve data transmission. This is especially helpful if device is operated in wakeup mode. + Usage is to send the commands with paramenter "prep". The data will be accumulated for send. + The last command must have the parameter "exec" in order to transmitt the information.
      + Example: until 6:00 temperature shall be 19, from then until 23:00 temperature shall be 22.5, thereafter until midnight, 19 degrees celsius is desired.
      - set th tempListSat 06:00 19 23:00 22.5 24:00 19
      -
      - set th tempListSat prep 06:00 19 23:00 22.5 24:00 19
      - set th tempListSun prep 06:00 19 23:00 22.5 24:00 19
      - set th tempListMon prep 06:00 19 23:00 22.5 24:00 19
      - set th tempListTue exec 06:00 19 23:00 22.5 24:00 19
      - + set th tempListSat 06:00 19 23:00 22.5 24:00 19
      +
      + set th tempListSat prep 06:00 19 23:00 22.5 24:00 19
      + set th tempListSun prep 06:00 19 23:00 22.5 24:00 19
      + set th tempListMon prep 06:00 19 23:00 22.5 24:00 19
      + set th tempListTue exec 06:00 19 23:00 22.5 24:00 19
      +

    -
  • +
  • OutputUnit (HM-OU-LED16)
    • led [off|red|green|yellow]
      switches the LED of the channel to the color. If the command is executed on a device it will set all LEDs to the specified color.
      - For Expert all LEDs can be set individual by providing a 8-digit hex number to the device.
    • + For Expert all LEDs can be set individual by providing a 8-digit hex number to the device.
    • ilum <brightness><duration>
      <brightness> [0-15] of backlight.
      <duration> [0-127] in sec. 0 is permanent 'on'.
    • @@ -5776,26 +5775,26 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo White spaces must not be used in the list. 'S' indicates short and 'L' long ilumination.
      repeat defines how often the sequence shall be executed. Defaults to 1.
      - +
    • playTone <MP3No>[,<MP3No>..] [<repeat>..]
      Play a series of tones. List is to be entered separated by ','. White spaces must not be used in the list.
      replay can be entered to repeat the last sound played once more.
      repeat defines how often the sequence shall be played. Defaults to 1.
      - Example: -
        + Example: +
          # "hello" in display, symb bulb on, backlight, beep
          set cfm_Mp3 playTone 3 # MP3 title 3 once
          set cfm_Mp3 playTone 3 3 # MP3 title 3 3 times
          set cfm_Mp3 playTone 3,6,8,3,4 # MP3 title list 3,6,8,3,4 once
          set cfm_Mp3 playTone 3,6,8,3,4 255# MP3 title list 3,6,8,3,4 255 times
          set cfm_Mp3 playTone replay # repeat last sequence
          -
          +
          set cfm_Led led redL 4 # led red blink 3 times long
          set cfm_Led led redS,redS,redS,redL,redL,redL,redS,redS,redS 255 # SOS 255 times
          -
        +
      -
    • +

  • HM-RC-19xxx
      @@ -5807,7 +5806,7 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo activate a symbol as available on the remote.
    • beep [off|1|2|3]
      activate tone
    • -
    • backlight [off|on|slow|fast]
      +
    • backlight [off|on|slow|fast]
      activate backlight
    • display <text> comma unit tone backlight <symbol(s)>
      @@ -5824,7 +5823,7 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo <symbol(s)> activate symbol display. Multople symbols can be acticated at the same time, concatinating them comma separated. Don't - use spaces here. Possiblesymbols are + use spaces here. Possiblesymbols are [bulb|switch|window|door|blind|scene|phone|bell|clock|arrowUp|arrowDown]

      Example: @@ -5834,10 +5833,10 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo # "1234,5" in display with unit 'W'. Symbols scene,phone,bell and # clock are active. Backlight flashing fast, Beep is second tone
      set FB1 display 12345 comma Watt 2 fast scene,phone,bell,clock -
    +

  • - +
  • keyMatic

      The Keymatic uses the AES signed communication. Therefore the control of the Keymatic is only together with the HM-LAN adapter possible. But @@ -5848,52 +5847,52 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo The lock bolt moves to the locking position
    • unlock [sec]
      The lock bolt moves to the unlocking position.
      - [sec]: Sets the delay in seconds after the lock automatically locked again.
      - 0 - 65535 seconds
    • + [sec]: Sets the delay in seconds after the lock automatically locked again.
      + 0 - 65535 seconds
    • open [sec]
      Unlocked the door so that the door can be opened.
      [sec]: Sets the delay in seconds after the lock automatically locked again.
      0 - 65535 seconds
    • -
  • -
  • winMatic

    +
  • +
  • winMatic

      winMatic provides 2 channels, one for the window control and a second - for the accumulator.

    + for the accumulator.
    • level <level> <relockDelay> <speed>
      set the level.
      - <level>: range is 0 to 100%
      - <relockDelay>: range 0 to 65535 sec. 'ignore' can be used to igneore the value alternaly
      - <speed>: range is 0 to 100%
      -
    • + <level>: range is 0 to 100%
      + <relockDelay>: range 0 to 65535 sec. 'ignore' can be used to igneore the value alternaly
      + <speed>: range is 0 to 100%
      +
    • stop
      stop movement
      -
    • +
  • HM-Sys-sRP-Pl

    - setup the repeater's entries. Up to 36entries can be applied. + setup the repeater's entries. Up to 36entries can be applied.
    • setRepeat <entry> <sender> <receiver> <broadcast>
      <entry> [1..36] entry number in repeater table. The repeater can handle up to 36 entries.
      <sender> name or HMID of the sender or source which shall be repeated
      <receiver> name or HMID of the receiver or destination which shall be repeated
      <broadcast> [yes|no] determines whether broadcast from this ID shall be repeated
      -
      - short application:
      - setRepeat setAll 0 0 0
      - will rewrite the complete list to the deivce. Data will be taken from attribut repPeer.
      - attribut repPeer is formated:
      - src1:dst1:[y/n],src2:dst2:[y/n],src2:dst2:[y/n],...
      -
      - Reading repPeer is formated:
      - Number src dst broadcast verify
      - number: entry sequence number
      - src: message source device - read from repeater
      - dst: message destination device - assembled from attributes
      - broadcast: shall broadcast be repeated for this source - read from repeater
      - verify: do attributes and readings match?
      +
      + short application:
      + setRepeat setAll 0 0 0
      + will rewrite the complete list to the deivce. Data will be taken from attribut repPeer.
      + attribut repPeer is formated:
      + src1:dst1:[y/n],src2:dst2:[y/n],src2:dst2:[y/n],...
      +
      + Reading repPeer is formated:
      + Number src dst broadcast verify
      + number: entry sequence number
      + src: message source device - read from repeater
      + dst: message destination device - assembled from attributes
      + broadcast: shall broadcast be repeated for this source - read from repeater
      + verify: do attributes and readings match?
      +
    • +
  • - -
    Debugging: @@ -5913,43 +5912,43 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
    - + Get
    • configSave <filename>
      Saves the configuration of an entity into a file. Data is stored in a - format to be executed from fhem command prompt.
      - The file is located in the fhem home directory aside of fhem.cfg. Data - will be stored cumulative - i.e. new data will be appended to the - file. It is up to the user to avoid duplicate storage of the same - entity.
      - Target of the data is ONLY the HM-device information which is located - IN the HM device. Explicitely this is the peer-list and the register. - With the register also the peering is included.
      - The file is readable and editable by the user. Additionaly timestamps - are stored to help user to validate.
      - Restrictions:
      - Even though all data of the entity will be secured to the file FHEM - stores the data that is avalilable to FHEM at time of save!. It is up - to the user to read the data from the HM-hardware prior to execution. - See recommended flow below.
      - This command will not store any FHEM attributes o device definitions. - This continues to remain in fhem.cfg.
      - Furthermore the secured data will not automatically be reloaded to the - HM-hardware. It is up to the user to perform a restore.

      - As with other commands also 'configSave' is best executed on a device - rather then on a channel. If executed on a device also the assotiated - channel data will be secured.

      - - Recommended work-order for device 'HMdev':
      - set HMdev clear msgEvents # clear old events to better check flow
      - set HMdev getConfig # read device & channel inforamtion
      - # wait until operation is complete
      - # protState should be CMDs_done
      - # there shall be no warnings amongst prot... variables
      - get configSave myActorFile
      -
      + format to be executed from fhem command prompt.
      + The file is located in the fhem home directory aside of fhem.cfg. Data + will be stored cumulative - i.e. new data will be appended to the + file. It is up to the user to avoid duplicate storage of the same + entity.
      + Target of the data is ONLY the HM-device information which is located + IN the HM device. Explicitely this is the peer-list and the register. + With the register also the peering is included.
      + The file is readable and editable by the user. Additionaly timestamps + are stored to help user to validate.
      + Restrictions:
      + Even though all data of the entity will be secured to the file FHEM + stores the data that is avalilable to FHEM at time of save!. It is up + to the user to read the data from the HM-hardware prior to execution. + See recommended flow below.
      + This command will not store any FHEM attributes o device definitions. + This continues to remain in fhem.cfg.
      + Furthermore the secured data will not automatically be reloaded to the + HM-hardware. It is up to the user to perform a restore.

      + As with other commands also 'configSave' is best executed on a device + rather then on a channel. If executed on a device also the assotiated + channel data will be secured.

      + + Recommended work-order for device 'HMdev':
      + set HMdev clear msgEvents # clear old events to better check flow
      + set HMdev getConfig # read device & channel inforamtion
      + # wait until operation is complete
      + # protState should be CMDs_done
      + # there shall be no warnings amongst prot... variables
      + get configSave myActorFile
      +
    • param <paramName>
      returns the content of the relevant parameter for the entity.
      @@ -5965,20 +5964,20 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
    • regList
      returns a list of register that are decoded by FHEM for this device.
      - Note that there could be more register implemented for a device.
      + Note that there could be more register implemented for a device.
    • - +
    • saveConfig <file>
      stores peers and register to the file.
      - Stored will be the data as available in fhem. It is necessary to read the information from the device prior to the save.
      - The command supports device-level action. I.e. if executed on a device also all related channel entities will be stored implicitely.
      - Storage to the file will be cumulative. If an entity is stored multiple times to the same file data will be appended. User can identify time of storage in the file if necessary.
      - Content of the file can be used to restore device configuration. It will restore all peers and all register to the entity.
      - Constrains/Restrictions:
      - prior to rewrite data to an entity it is necessary to pair the device with FHEM.
      - restore will not delete any peered channels, it will just add peer channels.
      + Stored will be the data as available in fhem. It is necessary to read the information from the device prior to the save.
      + The command supports device-level action. I.e. if executed on a device also all related channel entities will be stored implicitely.
      + Storage to the file will be cumulative. If an entity is stored multiple times to the same file data will be appended. User can identify time of storage in the file if necessary.
      + Content of the file can be used to restore device configuration. It will restore all peers and all register to the entity.
      + Constrains/Restrictions:
      + prior to rewrite data to an entity it is necessary to pair the device with FHEM.
      + restore will not delete any peered channels, it will just add peer channels.
    • -
    +
    Attributes @@ -5990,34 +5989,34 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
  • showtime
  • readingFnAttributes
  • actCycle - actCycle <[hhh:mm]|off>
    + actCycle <[hhh:mm]|off>
    Supports 'alive' or better 'not alive' detection for devices. [hhh:mm] is the maximum silent time for the device. Upon no message received in this period an event will be raised "<device> is dead". If the device sends again another notification is posted "<device> is alive".
    - This actiondetect will be autocreated for each device with build in cyclic status report.
    - Controlling entity is a pseudo device "ActionDetector" with HMId "000000".
    - Due to performance considerations the report latency is set to 600sec (10min). It can be controlled by the attribute "actCycle" of "ActionDetector".
    - Once entered to the supervision the HM device has 2 attributes:
    -
      - actStatus: activity status of the device
      - actCycle: detection period [hhh:mm]
      -
    - The overall function can be viewed checking out the "ActionDetector" entity. The status of all entities is present in the READING section.
    - Note: This function can be enabled for devices with non-cyclic messages as well. It is up to the user to enter a reasonable cycletime. -
  • + This actiondetect will be autocreated for each device with build in cyclic status report.
    + Controlling entity is a pseudo device "ActionDetector" with HMId "000000".
    + Due to performance considerations the report latency is set to 600sec (10min). It can be controlled by the attribute "actCycle" of "ActionDetector".
    + Once entered to the supervision the HM device has 2 attributes:
    +
      + actStatus: activity status of the device
      + actCycle: detection period [hhh:mm]
      +
    + The overall function can be viewed checking out the "ActionDetector" entity. The status of all entities is present in the READING section.
    + Note: This function can be enabled for devices with non-cyclic messages as well. It is up to the user to enter a reasonable cycletime. +
  • expert
    - This attribut controls the visibility of the readings. This attibute controlls - the presentation of device parameter in the readings.
    - 3 level can be choosen:
    -
      - 0_off: standart level. Display commonly used parameter
      - 1_on: enhanced level. Display all decoded device parameter
      - 2_full: display all parameter plus raw register information as well.
      -
    - If expert is applied a device it is used for assotiated channels. - It can be overruled if expert attibute is also applied to the channel device.
    - Make sure to check out attribut showInternalValues in the global values as well. - extert takes benefit of the implementation. - Nevertheless - by definition - showInternalValues overrules expert. -
  • + This attribut controls the visibility of the readings. This attibute controlls + the presentation of device parameter in the readings.
    + 3 level can be choosen:
    +
      + 0_off: standart level. Display commonly used parameter
      + 1_on: enhanced level. Display all decoded device parameter
      + 2_full: display all parameter plus raw register information as well.
      +
    + If expert is applied a device it is used for assotiated channels. + It can be overruled if expert attibute is also applied to the channel device.
    + Make sure to check out attribut showInternalValues in the global values as well. + extert takes benefit of the implementation. + Nevertheless - by definition - showInternalValues overrules expert. +
  • model, subType
    These attributes are set automatically after a successful pairing. @@ -6028,12 +6027,12 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
  • msgRepeat
    defines number of repetitions if a device doesn't answer in time
  • burstAccess
    - can be set for the device entity if the model allowes conditionalBurst. - The attribut will switch off burst operations (0_off) which causes less message load - on HMLAN and therefore reduces the chance of HMLAN overload.
    - Setting it on (1_auto) allowes shorter reaction time of the device. User does not - need to wait for the device to wake up.
    - Note that also the register burstRx needs to be set in the device.
  • + can be set for the device entity if the model allowes conditionalBurst. + The attribut will switch off burst operations (0_off) which causes less message load + on HMLAN and therefore reduces the chance of HMLAN overload.
    + Setting it on (1_auto) allowes shorter reaction time of the device. User does not + need to wait for the device to wake up.
    + Note that also the register burstRx needs to be set in the device.
  • rawToReadable
    Used to convert raw KFM100 values to readable data, based on measured values. E.g. fill slowly your container, while monitoring the @@ -6054,21 +6053,21 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
  • autoReadReg
    '0' autoReadReg will be ignored.
    '1' will execute a getConfig for the device automatically after each reboot of FHEM.
    - '2' like '1' plus execute after power_on.
    - '3' includes '2' plus updates on writes to the device
    - '4' includes '3' plus tries to request status if it seems to be missing
    - '8_stateOnly' will only update status information but not configuration - data like register and peer
    - Execution will be delayed in order to prevent congestion at startup. Therefore the update - of the readings and the display will be delayed depending on the size of the database.
    - Recommendations and constrains upon usage:
    + '2' like '1' plus execute after power_on.
    + '3' includes '2' plus updates on writes to the device
    + '4' includes '3' plus tries to request status if it seems to be missing
    + '8_stateOnly' will only update status information but not configuration + data like register and peer
    + Execution will be delayed in order to prevent congestion at startup. Therefore the update + of the readings and the display will be delayed depending on the size of the database.
    + Recommendations and constrains upon usage:
      - use this attribute on the device or channel 01. Do not use it separate on each channel - of a multi-channel device to avoid duplicate execution
      - usage on devices which only react to 'config' mode is not recommended since executen will - not start until config is triggered by the user
      - usage on devices which support wakeup-mode is usefull. But consider that execution is delayed - until the device "wakes up".
      + use this attribute on the device or channel 01. Do not use it separate on each channel + of a multi-channel device to avoid duplicate execution
      + usage on devices which only react to 'config' mode is not recommended since executen will + not start until config is triggered by the user
      + usage on devices which support wakeup-mode is usefull. But consider that execution is delayed + until the device "wakes up".
  • @@ -6079,91 +6078,91 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo offAtPon: heat channel only: force heating off after powerOn
    onAtRain: heat channel only: force heating on while status changes to 'rain' and off when it changes to 'dry'
    - + Generated events:
    • HM-CC-TC
      T: $t H: $h
      - battery:[low|ok]
      + battery:[low|ok]
      measured-temp $t
      humidity $h
      actuator $vp %
      - desired-temp $dTemp
      - desired-temp-manu $dTemp #temperature if switchen to manual mode
      - desired-temp-cent $dTemp #temperature if switchen to central mode
      - windowopen-temp-%d %.1f (sensor:%s)
      - tempList$wd hh:mm $t hh:mm $t ...
      + desired-temp $dTemp
      + desired-temp-manu $dTemp #temperature if switchen to manual mode
      + desired-temp-cent $dTemp #temperature if switchen to central mode
      + windowopen-temp-%d %.1f (sensor:%s)
      + tempList$wd hh:mm $t hh:mm $t ...
      displayMode temp-[hum|only]
      displayTemp [setpoint|actual]
      displayTempUnit [fahrenheit|celsius]
      controlMode [manual|auto|central|party]
      tempValveMode [Auto|Closed|Open|unknown]
      - param-change offset=$o1, value=$v1
      + param-change offset=$o1, value=$v1
      ValveErrorPosition_for_$dname $vep %
      ValveOffset_for_$dname : $of %
      - ValveErrorPosition $vep %
      - ValveOffset $of %
      + ValveErrorPosition $vep %
      + ValveOffset $of %
      time-request
      - trig_<src> <value> #channel was triggered by <src> channel. - This event relies on complete reading of channels configuration, otherwise Data can be - incomplete or incorrect.
      - trigLast <channel> #last receiced trigger
      + trig_<src> <value> #channel was triggered by <src> channel. + This event relies on complete reading of channels configuration, otherwise Data can be + incomplete or incorrect.
      + trigLast <channel> #last receiced trigger
    • HM-CC-RT-DN and HM-CC-RT-DN-BoM
      state:T: $actTemp desired: $setTemp valve: $vp %
      motorErr: [ok|ValveTight|adjustRangeTooLarge|adjustRangeTooSmall|communicationERR|unknown|lowBat|ValveErrorPosition] measured-temp $actTemp
      desired-temp $setTemp
      - ValvePosition $vp %
      + ValvePosition $vp %
      mode [auto|manu|party|boost]
      battery [low|ok]
      - batteryLevel $bat V
      + batteryLevel $bat V
      measured-temp $actTemp
      desired-temp $setTemp
      - actuator $vp %
      + actuator $vp %
      time-request
      - trig_<src> <value> #channel was triggered by <src> channel. + trig_<src> <value> #channel was triggered by <src> channel.
    • HM-CC-VD
      $vp %
      - battery:[critical|low|ok]
      + battery:[critical|low|ok]
      motorErr:[ok|blocked|loose|adjusting range too small|opening|closing|stop]
      - ValvePosition:$vp %
      + ValvePosition:$vp %
      ValveErrorPosition:$vep %
      ValveOffset:$of %
      - ValveDesired:$vp % # set by TC
      - operState:[errorTargetNotMet|onTarget|adjusting] # operational condition
      - operStateErrCnt:$cnt # number of failed settings
      + ValveDesired:$vp % # set by TC
      + operState:[errorTargetNotMet|onTarget|adjusting] # operational condition
      + operStateErrCnt:$cnt # number of failed settings
    • HM-CC-SCD
      - [normal|added|addedStrong]
      + [normal|added|addedStrong]
      battery [low|ok]
    • HM-SEC-SFA-SM
      - powerError [on|off]
      - sabotageError [on|off]
      - battery: [critical|low|ok]
      + powerError [on|off]
      + sabotageError [on|off]
      + battery: [critical|low|ok]
    • HM-LC-BL1-PB-FM
      motor: [opening|closing]
    • HM-LC-SW1-BA-PCB
      - battery: [low|ok]
      + battery: [low|ok]
    • HM-OU-LED16
      - color $value # hex - for device only
      - $value # hex - for device only
      - color [off|red|green|orange] # for channel
      - [off|red|green|orange] # for channel
      + color $value # hex - for device only
      + $value # hex - for device only
      + color [off|red|green|orange] # for channel
      + [off|red|green|orange] # for channel
    • HM-OU-CFM-PL
      - [on|off|$val]
      + [on|off|$val]
    • HM-Sen-Wa-Od
      - $level%
      - level $level%
      + $level%
      + level $level%
    • KFM100
      $v
      @@ -6187,7 +6186,7 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
    • HM-Sen-RD-O
      lastRain: timestamp # no trigger generated. Begin of previous Rain - - timestamp of the reading is the end of rain.
      + timestamp of the reading is the end of rain.
    • THSensor and HM-WDC7000
      T: $t H: $h AP: $ap
      @@ -6207,13 +6206,13 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo motion on (to $dest)
      motionCount $cnt _next:$nextTr"-"[0x0|0x1|0x2|0x3|15|30|60|120|240|0x9|0xa|0xb|0xc|0xd|0xe|0xf]
      cover [closed|open] # not for HM-Sec-MDIR
      - sabotageError [on|off] # only HM-Sec-MDIR
      + sabotageError [on|off] # only HM-Sec-MDIR
      battery [low|ok]
      - devState_raw.$d1 $d2
      + devState_raw.$d1 $d2
    • remote/pushButton/outputUnit
      -
        (to $dest) is added if the button is peered and does not send to broadcast
        - Release is provided for peered channels only
      +
        (to $dest) is added if the button is peered and does not send to broadcast
        + Release is provided for peered channels only
      Btn$x onShort
      Btn$x offShort
      Btn$x onLong $counter
      @@ -6229,7 +6228,7 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
    • remote/pushButton
      battery [low|ok]
      - trigger [Long|Short]_$no trigger event from channel
      + trigger [Long|Short]_$no trigger event from channel
    • swi
      Btn$x toggle
      @@ -6237,23 +6236,23 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo battery: [low|ok]
    • switch/dimmer/blindActuator
      - $val
      - powerOn [on|off|$val]
      + $val
      + powerOn [on|off|$val]
      [unknown|motor|dim] [up|down|stop]:$val
      - timedOn [running|off]
      # on is temporary - e.g. started with on-for-timer + timedOn [running|off]
      # on is temporary - e.g. started with on-for-timer
    • sensRain
      - $val
      - powerOn
      + $val
      + powerOn
      level
      - timedOn [running|off]
      # on is temporary - e.g. started with on-for-timer - trigger [Long|Short]_$no trigger event from channel
      + timedOn [running|off]
      # on is temporary - e.g. started with on-for-timer + trigger [Long|Short]_$no trigger event from channel
    • smokeDetector
      [off|smoke-Alarm|alive] # for team leader
      - [off|smoke-forward|smoke-alarm] # for team members
      - [normal|added|addedStrong] #HM-CC-SCD
      - SDteam [add|remove]_$dname
      + [off|smoke-forward|smoke-alarm] # for team members
      + [normal|added|addedStrong] #HM-CC-SCD
      + SDteam [add|remove]_$dname
      battery [low|ok]
      smoke_detect [none|<src>]
      test:from $src
      @@ -6261,18 +6260,18 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
    • threeStateSensor
      [open|tilted|closed]
      [wet|damp|dry] #HM-SEC-WDS only
      - cover [open|closed] #HM-SEC-WDS and HM-Sec-RHS
      - alive yes
      + cover [open|closed] #HM-SEC-WDS and HM-Sec-RHS
      + alive yes
      battery [low|ok]
      contact [open|tilted|closed]
      - contact [wet|damp|dry] #HM-SEC-WDS only
      - sabotageError [on|off] #HM-SEC-SC only
      + contact [wet|damp|dry] #HM-SEC-WDS only
      + sabotageError [on|off] #HM-SEC-SC only
    • winMatic
      - [locked|$value]
      - motorError [no|TurnError|TiltError]
      - direction [no|up|down|undefined]
      - charge [trickleCharge|charge|dischange|unknown]
      + [locked|$value]
      + motorError [no|TurnError|TiltError]
      + direction [no|up|down|undefined]
      + charge [trickleCharge|charge|dischange|unknown]
      airing [inactiv|$air]
      course [tilt|close]
      airing [inactiv|$value]
      diff --git a/fhem/FHEM/98_HMinfo.pm b/fhem/FHEM/98_HMinfo.pm index 2131eeef3..545a9e649 100644 --- a/fhem/FHEM/98_HMinfo.pm +++ b/fhem/FHEM/98_HMinfo.pm @@ -24,10 +24,10 @@ sub HMinfo_Initialize($$) {#################################################### $hash->{SetFn} = "HMinfo_SetFn"; $hash->{AttrFn} = "HMinfo_Attr"; $hash->{AttrList} = "loglevel:0,1,2,3,4,5,6 " - ."sumStatus sumERROR " - ."autoUpdate " - ."hmAutoReadScan hmIoMaxDly " - ."hmManualOper:0_auto,1_manual " + ."sumStatus sumERROR " + ."autoUpdate " + ."hmAutoReadScan hmIoMaxDly " + ."hmManualOper:0_auto,1_manual " .$readingFnAttributes; } @@ -39,73 +39,73 @@ sub HMinfo_Define($$){######################################################### $attr{$name}{webCmd} = "update:protoEvents short:rssi:peerXref:configCheck:models"; $attr{$name}{sumStatus} = "battery" .",sabotageError" - .",powerError" - .",motor"; + .",powerError" + .",motor"; $attr{$name}{sumERROR} = "battery:ok" .",sabotageError:off" - .",powerError:ok" - .",overload:off" - .",overheat:off" - .",reduced:off" - .",motorError:no" - .",error:none" - .",uncertain:yes" - .",smoke_detect:none" - .",cover:closed" - ; + .",powerError:ok" + .",overload:off" + .",overheat:off" + .",reduced:off" + .",motorError:no" + .",error:none" + .",uncertain:yes" + .",smoke_detect:none" + .",cover:closed" + ; return; } sub HMinfo_Attr(@) {################################# my ($cmd,$name, $attrName,$attrVal) = @_; my @hashL; my $hash = $defs{$name}; - + if ($attrName eq "autoUpdate"){# 00:00 hh:mm - delete $hash->{helper}{autoUpdate}; - return if ($cmd eq "del"); + delete $hash->{helper}{autoUpdate}; + return if ($cmd eq "del"); my ($h,$m) = split":",$attrVal; - return "please enter time [hh:mm]" if (!defined $h||!defined $m); - my $sec = $h*3600+$m*60; - return "give at least one minute" if ($sec < 60); - $hash->{helper}{autoUpdate} = $sec; - InternalTimer(gettimeofday()+$sec,"HMinfo_autoUpdate","sUpdt:".$name,0); + return "please enter time [hh:mm]" if (!defined $h||!defined $m); + my $sec = $h*3600+$m*60; + return "give at least one minute" if ($sec < 60); + $hash->{helper}{autoUpdate} = $sec; + InternalTimer(gettimeofday()+$sec,"HMinfo_autoUpdate","sUpdt:".$name,0); } elsif($attrName eq "hmAutoReadScan"){# 00:00 hh:mm - if ($cmd eq "del"){ - $modules{CUL_HM}{hmAutoReadScan} = 4;# return to default - } + if ($cmd eq "del"){ + $modules{CUL_HM}{hmAutoReadScan} = 4;# return to default + } else{ - return "please add plain integer between 1 and 300" - if ( $attrVal !~ m/^(\d+)$/ - ||$attrVal<0 - ||$attrVal >300 ); - ## implement new timer to CUL_HM + return "please add plain integer between 1 and 300" + if ( $attrVal !~ m/^(\d+)$/ + ||$attrVal<0 + ||$attrVal >300 ); + ## implement new timer to CUL_HM $modules{CUL_HM}{hmAutoReadScan}=$attrVal; - CUL_HM_queueAutoRead(""); #will restart timer - } + CUL_HM_queueAutoRead(""); #will restart timer + } } - elsif($attrName eq "hmIoMaxDly"){# - if ($cmd eq "del"){ - $modules{CUL_HM}{hmIoMaxDly} = 60;# return to default - } + elsif($attrName eq "hmIoMaxDly"){# + if ($cmd eq "del"){ + $modules{CUL_HM}{hmIoMaxDly} = 60;# return to default + } else{ - return "please add plain integer between 0 and 3600" - if ( $attrVal !~ m/^(\d+)$/ - ||$attrVal<0 - ||$attrVal >3600 ); - ## implement new timer to CUL_HM + return "please add plain integer between 0 and 3600" + if ( $attrVal !~ m/^(\d+)$/ + ||$attrVal<0 + ||$attrVal >3600 ); + ## implement new timer to CUL_HM $modules{CUL_HM}{hmIoMaxDly}=$attrVal; - } + } } elsif($attrName eq "hmManualOper"){# 00:00 hh:mm - if ($cmd eq "del"){ - $modules{CUL_HM}{helper}{hmManualOper} = 0;# default automode - } + if ($cmd eq "del"){ + $modules{CUL_HM}{helper}{hmManualOper} = 0;# default automode + } else{ - return "please set 0 or 1" if ($attrVal !~ m/^(0|1)/); - ## implement new timer to CUL_HM + return "please set 0 or 1" if ($attrVal !~ m/^(0|1)/); + ## implement new timer to CUL_HM $modules{CUL_HM}{helper}{hmManualOper} = substr($attrVal,0,1); - } + } } return; } @@ -127,7 +127,7 @@ sub HMinfo_getParam(@) { ###################################################### foreach (@param){ my $para = CUL_HM_Get($ehash,$eName,"param",$_); push @paramList,sprintf("%-15s",($para eq "undefined"?" -":$para)); - $found = 1 if ($para ne "undefined") ; + $found = 1 if ($para ne "undefined") ; } return $found,sprintf("%-20s\t: %s",$eName,join "\t|",@paramList); } @@ -136,23 +136,23 @@ sub HMinfo_regCheck(@) { ###################################################### my @regIncompl; my @regMissing; my @peerRegsFail; - + my %th = CUL_HM_putHash("culHmModel"); foreach my $eName (@entities){ my $ehash = $defs{$eName}; my @lsNo = CUL_HM_reglUsed($eName); - my @mReg = (); - my @iReg = (); + my @mReg = (); + my @iReg = (); foreach my $rNm (@lsNo){# check non-peer lists - next if (!$rNm || $rNm eq ""); - if (!$ehash->{READINGS}{$rNm}){ push @mReg, $rNm;} - elsif ( $ehash->{READINGS}{$rNm}{VAL} !~ m/00:00/){push @iReg, $rNm;} - } - push @regMissing,$eName.":\t".join(",",@mReg) if (scalar @mReg); - push @regIncompl,$eName.":\t".join(",",@iReg) if (scalar @iReg); + next if (!$rNm || $rNm eq ""); + if (!$ehash->{READINGS}{$rNm}){ push @mReg, $rNm;} + elsif ( $ehash->{READINGS}{$rNm}{VAL} !~ m/00:00/){push @iReg, $rNm;} + } + push @regMissing,$eName.":\t".join(",",@mReg) if (scalar @mReg); + push @regIncompl,$eName.":\t".join(",",@iReg) if (scalar @iReg); } return "\n\n missing register list\n " .(join "\n ",sort @regMissing) ."\n\n incomplete register list\n ".(join "\n ",sort @regIncompl) @@ -165,36 +165,36 @@ sub HMinfo_peerCheck(@) { ##################################################### my @peerIDsNoPeer; my %th = CUL_HM_putHash("culHmModel"); foreach my $eName (@entities){ - next if (!$defs{$eName}{helper}{role}{chn});#device has no channels - next if (!CUL_HM_peerUsed($eName)); - - my $id = $defs{$eName}{DEF}; - my $devId = substr($id,0,6); - my $st = AttrVal(CUL_HM_id2Name($devId),"subType","");# from Master + next if (!$defs{$eName}{helper}{role}{chn});#device has no channels + next if (!CUL_HM_peerUsed($eName)); + + my $id = $defs{$eName}{DEF}; + my $devId = substr($id,0,6); + my $st = AttrVal(CUL_HM_id2Name($devId),"subType","");# from Master my $md = AttrVal(CUL_HM_id2Name($devId),"model",""); - my $peerIDs = AttrVal($eName,"peerIDs",undef); - - if (!$peerIDs){ # no peers - is this correct? - push @peerIDsEmpty,"empty: ".$eName; - } - elsif($peerIDs !~ m/00000000/){#peerList incomplete - push @peerIDsFail,"incomplete: ".$eName.":".$peerIDs; - } - else{# work on a valid list: - next if ($st eq "repeater"); - foreach (split",",$peerIDs){ - next if ($_ eq "00000000" ||$_ =~m /$devId/); - my $cId = $id; - if ($md eq "HM-CC-RT-DN" && $id =~ m/05$/){ # special RT climate - $_ =~ s/04$/05/; # have to compare with clima_team, not clima - $cId =~ s/05$/04/;# will find 04 in peerlist, not 05 - } - my $pName = CUL_HM_id2Name($_); - $pName =~s/_chn:01//; #channel 01 could be covered by device - my $pPlist = AttrVal($pName,"peerIDs",""); - push @peerIDsNoPeer,$eName." p:".$pName if ($pPlist !~ m/$cId/); - } - } + my $peerIDs = AttrVal($eName,"peerIDs",undef); + + if (!$peerIDs){ # no peers - is this correct? + push @peerIDsEmpty,"empty: ".$eName; + } + elsif($peerIDs !~ m/00000000/){#peerList incomplete + push @peerIDsFail,"incomplete: ".$eName.":".$peerIDs; + } + else{# work on a valid list: + next if ($st eq "repeater"); + foreach (split",",$peerIDs){ + next if ($_ eq "00000000" ||$_ =~m /$devId/); + my $cId = $id; + if ($md eq "HM-CC-RT-DN" && $id =~ m/05$/){ # special RT climate + $_ =~ s/04$/05/; # have to compare with clima_team, not clima + $cId =~ s/05$/04/;# will find 04 in peerlist, not 05 + } + my $pName = CUL_HM_id2Name($_); + $pName =~s/_chn:01//; #channel 01 could be covered by device + my $pPlist = AttrVal($pName,"peerIDs",""); + push @peerIDsNoPeer,$eName." p:".$pName if ($pPlist !~ m/$cId/); + } + } } return "\n\n peer list not read" ."\n ".(join "\n ",sort @peerIDsEmpty) ."\n\n peer list incomplete"."\n ".(join "\n ",sort @peerIDsFail) @@ -212,39 +212,39 @@ sub HMinfo_getEntities(@) { ################################################### if ($filter){# options provided $doDev=$doChn=$doEmp= 0;#change default no warnings; - my @pl = split undef,$filter; + my @pl = split undef,$filter; use warnings; - foreach (@pl){ - $doDev = 1 if($_ eq 'd'); - $doChn = 1 if($_ eq 'c'); - $doIgn = 1 if($_ eq 'i'); - $noVrt = 1 if($_ eq 'v'); - $noPhy = 1 if($_ eq 'p'); - $noAct = 1 if($_ eq 'a'); - $noSen = 1 if($_ eq 's'); - $doEmp = 1 if($_ eq 'e'); - } + foreach (@pl){ + $doDev = 1 if($_ eq 'd'); + $doChn = 1 if($_ eq 'c'); + $doIgn = 1 if($_ eq 'i'); + $noVrt = 1 if($_ eq 'v'); + $noPhy = 1 if($_ eq 'p'); + $noAct = 1 if($_ eq 'a'); + $noSen = 1 if($_ eq 's'); + $doEmp = 1 if($_ eq 'e'); + } } # generate entity list foreach my $id (sort(keys%{$modules{CUL_HM}{defptr}})){ next if ($id eq "000000"); - my $eHash = $modules{CUL_HM}{defptr}{$id}; + my $eHash = $modules{CUL_HM}{defptr}{$id}; my $eName = $eHash->{NAME}; my $isChn = (length($id) != 6 || CUL_HM_Get($eHash,$eName,"param","channel_01") eq "undefined")?1:0; - my $eMd = CUL_HM_Get($eHash,$eName,"param","model"); - my $eIg = CUL_HM_Get($eHash,$eName,"param","ignore"); - $eIg = "" if ($eIg eq "undefined"); - next if (!(($doDev && length($id) == 6) || - ($doChn && $isChn))); - next if (!$doIgn && $eIg); - next if ( $noVrt && $eMd =~ m/^virtual/); - next if ( $noPhy && $eMd !~ m/^virtual/); - my $eSt = CUL_HM_Get($eHash,$eName,"param","subType"); - + my $eMd = CUL_HM_Get($eHash,$eName,"param","model"); + my $eIg = CUL_HM_Get($eHash,$eName,"param","ignore"); + $eIg = "" if ($eIg eq "undefined"); + next if (!(($doDev && length($id) == 6) || + ($doChn && $isChn))); + next if (!$doIgn && $eIg); + next if ( $noVrt && $eMd =~ m/^virtual/); + next if ( $noPhy && $eMd !~ m/^virtual/); + my $eSt = CUL_HM_Get($eHash,$eName,"param","subType"); + next if ( $noSen && $eSt =~ m/^(THSensor|remote|pushButton|threeStateSensor|sensor|motionDetector|swi)$/); next if ( $noAct && $eSt =~ m/^(switch|blindActuator|dimmer|thermostat|smokeDetector|KFM100|outputUnit)$/); - next if ( $eName !~ m/$re/); - push @names,$eName; + next if ( $eName !~ m/$re/); + push @names,$eName; } return sort(@names); } @@ -260,239 +260,239 @@ sub HMinfo_getMsgStat() { ##################################################### $dr .= sprintf("|%4s",$_) foreach ("Mon","Tue","Wed","Thu","Fri","Sat","Sun","# tdy"); $ds .= sprintf("|%4s",$_) foreach ("Mon","Tue","Wed","Thu","Fri","Sat","Sun","# tdy"); foreach my $ioD(keys %{$modules{CUL_HM}{stat}{r}}){ - next if ($ioD eq "dummy"); - $hr .= sprintf("\n %-10s:",$ioD); - $hs .= sprintf("\n %-10s:",$ioD); - $dr .= sprintf("\n %-10s:",$ioD); - $ds .= sprintf("\n %-10s:",$ioD); - $hr .= sprintf("|%3d",$modules{CUL_HM}{stat}{r}{$ioD}{h}{$_}) foreach (0..23); - $hs .= sprintf("|%3d",$modules{CUL_HM}{stat}{s}{$ioD}{h}{$_}) foreach (0..23); - $dr .= sprintf("|%4d",$modules{CUL_HM}{stat}{r}{$ioD}{d}{$_}) foreach (0..6); - $ds .= sprintf("|%4d",$modules{CUL_HM}{stat}{s}{$ioD}{d}{$_}) foreach (0..6); + next if ($ioD eq "dummy"); + $hr .= sprintf("\n %-10s:",$ioD); + $hs .= sprintf("\n %-10s:",$ioD); + $dr .= sprintf("\n %-10s:",$ioD); + $ds .= sprintf("\n %-10s:",$ioD); + $hr .= sprintf("|%3d",$modules{CUL_HM}{stat}{r}{$ioD}{h}{$_}) foreach (0..23); + $hs .= sprintf("|%3d",$modules{CUL_HM}{stat}{s}{$ioD}{h}{$_}) foreach (0..23); + $dr .= sprintf("|%4d",$modules{CUL_HM}{stat}{r}{$ioD}{d}{$_}) foreach (0..6); + $ds .= sprintf("|%4d",$modules{CUL_HM}{stat}{s}{$ioD}{d}{$_}) foreach (0..6); - my ($tdr,$tds); - $tdr += $modules{CUL_HM}{stat}{r}{$ioD}{h}{$_} foreach (0..23); - $tds += $modules{CUL_HM}{stat}{s}{$ioD}{h}{$_} foreach (0..23); - $dr .= sprintf("|#%4d",$tdr); - $ds .= sprintf("|#%4d",$tds); + my ($tdr,$tds); + $tdr += $modules{CUL_HM}{stat}{r}{$ioD}{h}{$_} foreach (0..23); + $tds += $modules{CUL_HM}{stat}{s}{$ioD}{h}{$_} foreach (0..23); + $dr .= sprintf("|#%4d",$tdr); + $ds .= sprintf("|#%4d",$tds); } my @l = localtime(gettimeofday()); my $tsts = "\n |"; - $tsts .= "----" foreach (1..$l[2]); - $tsts .= ">*" ; + $tsts .= "----" foreach (1..$l[2]); + $tsts .= ">*" ; return "msg statistics\n" .$tsts - .$hr.$hs - .$tsts - .$dr.$ds - ; + .$hr.$hs + .$tsts + .$dr.$ds + ; } sub HMinfo_SetFn($@) {######################################################### my ($hash,$name,$cmd,@a) = @_; my ($opt,$optEmpty,$filter) = ("",1,""); my $ret; - + if (@a && ($a[0] =~ m/^-/) && ($a[0] !~ m/^-f$/)){# options provided $opt = $a[0]; - $optEmpty = ($opt =~ m/e/)?1:0; - shift @a; #remove + $optEmpty = ($opt =~ m/e/)?1:0; + shift @a; #remove } if (@a && $a[0] =~ m/^-f$/){# options provided - shift @a; #remove - $filter = shift @a; + shift @a; #remove + $filter = shift @a; } if (!$cmd ||$cmd eq "?" ) {##actionImmediate: clear parameter-------------- - return "autoReadReg " - ."clear " - ."configCheck param peerCheck peerXref " - ."protoEvents " + return "autoReadReg " + ."clear " + ."configCheck param peerCheck peerXref " + ."protoEvents " ."models regCheck register rssi saveConfig update " ."templateSet templateChk templateList templateDef cpRegs update"; } elsif($cmd eq "clear" ) {##actionImmediate: clear parameter-------------- my ($type) = @a; - if ($type eq "msgStat"){ - foreach (keys %{$modules{CUL_HM}{stat}{r}}){ + if ($type eq "msgStat"){ + foreach (keys %{$modules{CUL_HM}{stat}{r}}){ next if ($_ ne "dummy"); - delete $modules{CUL_HM}{stat}{$_}; - delete $modules{CUL_HM}{stat}{r}{$_}; - delete $modules{CUL_HM}{stat}{s}{$_}; + delete $modules{CUL_HM}{stat}{$_}; + delete $modules{CUL_HM}{stat}{r}{$_}; + delete $modules{CUL_HM}{stat}{s}{$_}; } - return; - } - else{ - return "unknown parameter - use Protocol, readings, msgStat, register or rssi" - if ($type !~ m/^(Protocol|readings|register|rssi)$/); - $opt .= "d" if ($type !~ m/(readings|register)/);# readings apply to all, others device only - my @entities; - $type = "msgEvents" if ($type eq "Protocol");# translate parameter - foreach my $dName (HMinfo_getEntities($opt."v",$filter)){ - push @entities,$dName; - CUL_HM_Set($defs{$dName},$dName,"clear",$type); - } - return $cmd.$type." done:" ."\n cleared" ."\n ".(join "\n ",sort @entities) - ; - } + return; + } + else{ + return "unknown parameter - use Protocol, readings, msgStat, register or rssi" + if ($type !~ m/^(Protocol|readings|register|rssi)$/); + $opt .= "d" if ($type !~ m/(readings|register)/);# readings apply to all, others device only + my @entities; + $type = "msgEvents" if ($type eq "Protocol");# translate parameter + foreach my $dName (HMinfo_getEntities($opt."v",$filter)){ + push @entities,$dName; + CUL_HM_Set($defs{$dName},$dName,"clear",$type); + } + return $cmd.$type." done:" ."\n cleared" ."\n ".(join "\n ",sort @entities) + ; + } } elsif($cmd eq "autoReadReg"){##actionImmediate: re-issue register Read------- - my @entities; - foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){ - next if (!substr(AttrVal($dName,"autoReadReg","0"),0,1)); - CUL_HM_qAutoRead($dName,1); - push @entities,$dName; - } - return $cmd." done:" ."\n triggered:" ."\n ".(join "\n ",sort @entities) - ; + my @entities; + foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){ + next if (!substr(AttrVal($dName,"autoReadReg","0"),0,1)); + CUL_HM_qAutoRead($dName,1); + push @entities,$dName; + } + return $cmd." done:" ."\n triggered:" ."\n ".(join "\n ",sort @entities) + ; } elsif($cmd eq "protoEvents"){##print protocol-events------------------------- - my ($type) = @a; - $type = "long" if(!$type); - my @paramList; - my @IOlist; - foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){ - my $id = $defs{$dName}{DEF}; + my ($type) = @a; + $type = "long" if(!$type); + my @paramList; + my @IOlist; + foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){ + my $id = $defs{$dName}{DEF}; my ($found,$para) = HMinfo_getParam($id, - ,"protState","protCmdPend" - ,"protSnd","protLastRcv","protResnd" - ,"protCmdDel","protResndFail","protNack","protIOerr"); - $para =~ s/( last_at|20..-|\|)//g; + ,"protState","protCmdPend" + ,"protSnd","protLastRcv","protResnd" + ,"protCmdDel","protResndFail","protNack","protIOerr"); + $para =~ s/( last_at|20..-|\|)//g; my @pl = split "\t",$para; - foreach (@pl){ - $_ =~ s/\s+$|//g ; - $_ =~ s/CMDs_//; - $_ =~ s/..-.. ..:..:..//g if ($type eq "short"); - $_ =~ s/CMDs // if ($type eq "short"); - } - if ($type eq "short"){ - push @paramList, sprintf("%-20s%-17s|%-10s|%-10s|%-10s#%-10s|%-10s|%-10s|%-10s", - $pl[0],$pl[1],$pl[2],$pl[3],$pl[5],$pl[6],$pl[7],$pl[8],$pl[9]); - } - else{ - push @paramList, sprintf("%-20s%-17s|%-18s|%-18s|%-14s|%-18s#%-18s|%-18s|%-18s|%-18s", - $pl[0],$pl[1],$pl[2],$pl[3],$pl[4],$pl[5],$pl[6],$pl[7],$pl[8],$pl[9]); - } - push @IOlist,$defs{$pl[0]}{IODev}->{NAME}; - } - - my $hdr = sprintf("%-20s:%-16s|%-18s|%-18s|%-14s|%-18s#%-18s|%-18s|%-18s|%-18s", - ,"name" - ,"State","CmdPend" - ,"Snd","LastRcv","Resnd" - ,"CmdDel","ResndFail","Nack","IOerr"); - $hdr = sprintf("%-20s:%-16s|%-10s|%-10s|%-10s#%-10s|%-10s|%-10s|%-10s", - ,"name" - ,"State","CmdPend" - ,"Snd","Resnd" - ,"CmdDel","ResndFail","Nack","IOerr") if ($type eq "short"); - $ret = $cmd." done:" ."\n ".$hdr ."\n ".(join "\n ",sort @paramList) - ; - $ret .= "\n\n CUL_HM queue:$modules{CUL_HM}{prot}{rspPend}"; - $ret .= "\n"; - $ret .= "\n autoReadReg pending:" .join(",",@{$modules{CUL_HM}{helper}{qReqConf}}) + foreach (@pl){ + $_ =~ s/\s+$|//g ; + $_ =~ s/CMDs_//; + $_ =~ s/..-.. ..:..:..//g if ($type eq "short"); + $_ =~ s/CMDs // if ($type eq "short"); + } + if ($type eq "short"){ + push @paramList, sprintf("%-20s%-17s|%-10s|%-10s|%-10s#%-10s|%-10s|%-10s|%-10s", + $pl[0],$pl[1],$pl[2],$pl[3],$pl[5],$pl[6],$pl[7],$pl[8],$pl[9]); + } + else{ + push @paramList, sprintf("%-20s%-17s|%-18s|%-18s|%-14s|%-18s#%-18s|%-18s|%-18s|%-18s", + $pl[0],$pl[1],$pl[2],$pl[3],$pl[4],$pl[5],$pl[6],$pl[7],$pl[8],$pl[9]); + } + push @IOlist,$defs{$pl[0]}{IODev}->{NAME}; + } + + my $hdr = sprintf("%-20s:%-16s|%-18s|%-18s|%-14s|%-18s#%-18s|%-18s|%-18s|%-18s", + ,"name" + ,"State","CmdPend" + ,"Snd","LastRcv","Resnd" + ,"CmdDel","ResndFail","Nack","IOerr"); + $hdr = sprintf("%-20s:%-16s|%-10s|%-10s|%-10s#%-10s|%-10s|%-10s|%-10s", + ,"name" + ,"State","CmdPend" + ,"Snd","Resnd" + ,"CmdDel","ResndFail","Nack","IOerr") if ($type eq "short"); + $ret = $cmd." done:" ."\n ".$hdr ."\n ".(join "\n ",sort @paramList) + ; + $ret .= "\n\n CUL_HM queue:$modules{CUL_HM}{prot}{rspPend}"; + $ret .= "\n"; + $ret .= "\n autoReadReg pending:" .join(",",@{$modules{CUL_HM}{helper}{qReqConf}}) .($modules{CUL_HM}{helper}{autoRdActive}?" recent:".$modules{CUL_HM}{helper}{autoRdActive}:" recent:none"); - $ret .= "\n status request pending:" .join(",",@{$modules{CUL_HM}{helper}{qReqStat}}) ; - $ret .= "\n autoReadReg wakeup pending:" .join(",",@{$modules{CUL_HM}{helper}{qReqConfWu}}); - $ret .= "\n status request wakeup pending:".join(",",@{$modules{CUL_HM}{helper}{qReqStatWu}}); - $ret .= "\n"; - @IOlist = HMinfo_noDup(@IOlist); - foreach(@IOlist){ - $_ .= ":".$defs{$_}{STATE} - .(defined $defs{$_}{helper}{q}{answerPend}? - " pending=".$defs{$_}{helper}{q}{answerPend} : - "") - ." condition:".ReadingsVal($_,"cond","-") - ."\n msgLoadEst: ".$defs{$_}{msgLoadEst}; - } - $ret .= "\n IODevs:".(join"\n ",HMinfo_noDup(@IOlist)); + $ret .= "\n status request pending:" .join(",",@{$modules{CUL_HM}{helper}{qReqStat}}) ; + $ret .= "\n autoReadReg wakeup pending:" .join(",",@{$modules{CUL_HM}{helper}{qReqConfWu}}); + $ret .= "\n status request wakeup pending:".join(",",@{$modules{CUL_HM}{helper}{qReqStatWu}}); + $ret .= "\n"; + @IOlist = HMinfo_noDup(@IOlist); + foreach(@IOlist){ + $_ .= ":".$defs{$_}{STATE} + .(defined $defs{$_}{helper}{q}{answerPend}? + " pending=".$defs{$_}{helper}{q}{answerPend} : + "") + ." condition:".ReadingsVal($_,"cond","-") + ."\n msgLoadEst: ".$defs{$_}{msgLoadEst}; + } + $ret .= "\n IODevs:".(join"\n ",HMinfo_noDup(@IOlist)); } elsif($cmd eq "msgStat") {##print message statistics---------------------- $ret = HMinfo_getMsgStat(); } elsif($cmd eq "rssi") {##print RSSI protocol-events-------------------- - my @rssiList; - foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){ + my @rssiList; + foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){ foreach my $dest (keys %{$defs{$dName}{helper}{rssi}}){ - my $dispName = $dName; - my $dispDest = $dest; - if ($dest =~ m/^at_(.*)/){ - $dispName = $1; -# $dispName =~ s/^rpt_//; - $dispDest = (($dest =~ m/^to_rpt_/)?"rep_":"").$dName; - } - push @rssiList,sprintf("%-15s:%-15s %-15s %6.1f %6.1f %6.1f<%6.1f %5s" - ,$dName,$dispName,$dispDest - ,$defs{$dName}{helper}{rssi}{$dest}{lst} - ,$defs{$dName}{helper}{rssi}{$dest}{avg} - ,$defs{$dName}{helper}{rssi}{$dest}{min} - ,$defs{$dName}{helper}{rssi}{$dest}{max} - ,$defs{$dName}{helper}{rssi}{$dest}{cnt} - ); - } - } - $ret = $cmd." done:"."\n "."Device :receive from last avg min{helper}{r}{$rN} = "" if (!defined($hash->{helper}{r}{$rN})); - $hash->{helper}{r}{$rN} .= sprintf("%16s",$val); - if ($pt ne $ptOld){ - $ptLine .= sprintf("%16s",$pt); - $ptOld = $pt; - } - if ($peer ne $peerOld){ - $peerLine .= sprintf("%32s",$peer); - $peerOld = $peer; - } - } - $RegReply .= $peerLine."\n".$ptLine."\n"; - foreach my $rN (sort keys %{$hash->{helper}{r}}){ - $RegReply .= $rN.$hash->{helper}{r}{$rN}."\n"; - } + foreach my $reg (split("\n",$regs)){ + my ($peer,$h1) = split ("\t",$reg); + $peer =~s/ //g; + if ($peer !~ m/3:/){ + $RegReply .= $reg."\n"; + next; + } + $peer =~s/3://; + next if (!$h1); + my ($regN,$h2) = split (":",$h1); + my ($val,$unit) = split (" ",$h2); + $unit = $unit?("[".$unit."]"):" "; + my ($pt,$rN) = ($1,$2) if ($regN =~m/(..)(.*)/); + $rN .= $unit; + $hash->{helper}{r}{$rN} = "" if (!defined($hash->{helper}{r}{$rN})); + $hash->{helper}{r}{$rN} .= sprintf("%16s",$val); + if ($pt ne $ptOld){ + $ptLine .= sprintf("%16s",$pt); + $ptOld = $pt; + } + if ($peer ne $peerOld){ + $peerLine .= sprintf("%32s",$peer); + $peerOld = $peer; + } + } + $RegReply .= $peerLine."\n".$ptLine."\n"; + foreach my $rN (sort keys %{$hash->{helper}{r}}){ + $RegReply .= $rN.$hash->{helper}{r}{$rN}."\n"; + } delete $hash->{helper}{r}; - } - $ret = "No regs found for:".join(",",sort @noReg)."\n\n" - .$RegReply; + } + $ret = "No regs found for:".join(",",sort @noReg)."\n\n" + .$RegReply; } elsif($cmd eq "param") {##print param ---------------------------------- - my @paramList; - foreach my $dName (HMinfo_getEntities($opt,$filter)){ - my $id = $defs{$dName}{DEF}; - my ($found,$para) = HMinfo_getParam($id,@a); + my @paramList; + foreach my $dName (HMinfo_getEntities($opt,$filter)){ + my $id = $defs{$dName}{DEF}; + my ($found,$para) = HMinfo_getParam($id,@a); push @paramList,$para if($found || $optEmpty); - } + } my $prtHdr = "entity \t: "; $prtHdr .= sprintf("%-20s \t|",$_)foreach (@a); - $ret = $cmd." done:" - ."\n param list" ."\n " - .$prtHdr ."\n " - .(join "\n ",sort @paramList) - ; + $ret = $cmd." done:" + ."\n param list" ."\n " + .$prtHdr ."\n " + .(join "\n ",sort @paramList) + ; } elsif($cmd eq "regCheck") {##check register-------------------------------- my @entities = HMinfo_getEntities($opt."v",$filter); @@ -504,76 +504,76 @@ sub HMinfo_SetFn($@) {######################################################### } elsif($cmd eq "configCheck"){##check peers and register---------------------- my @entities = HMinfo_getEntities($opt."v",$filter); - $ret = $cmd." done:" .HMinfo_regCheck(@entities) - .HMinfo_peerCheck(@entities); + $ret = $cmd." done:" .HMinfo_regCheck(@entities) + .HMinfo_peerCheck(@entities); } elsif($cmd eq "peerXref") {##print cross-references------------------------ - my @peerPairs; - foreach my $dName (HMinfo_getEntities($opt,$filter)){ - my $peerIDs = AttrVal($dName,"peerIDs",undef); - foreach (split",",$peerIDs){ + my @peerPairs; + foreach my $dName (HMinfo_getEntities($opt,$filter)){ + my $peerIDs = AttrVal($dName,"peerIDs",undef); + foreach (split",",$peerIDs){ next if ($_ eq "00000000"); - my $pName = CUL_HM_id2Name($_); + my $pName = CUL_HM_id2Name($_); my $pPlist = AttrVal($pName,"peerIDs",""); $pName =~ s/$dName\_chn:/self/; - push @peerPairs,$dName." =>".$pName; + push @peerPairs,$dName." =>".$pName; } - } - $ret = $cmd." done:" ."\n x-ref list" ."\n ".(join "\n ",sort @peerPairs) - ; + } + $ret = $cmd." done:" ."\n x-ref list" ."\n ".(join "\n ",sort @peerPairs) + ; } elsif($cmd eq "models") {##print capability, models---------------------- my %th = CUL_HM_putHash("culHmModel"); - my @model; - foreach (keys %th){ - my $mode = $th{$_}{rxt}; - $mode =~ s/c/config/; - $mode =~ s/w/wakeup/; - $mode =~ s/b/burst/; - $mode =~ s/l/lazyConf/; - $mode =~ s/\bf\b/burstCond/; - $mode =~ s/:/,/g; - $mode = "normal" if (!$mode); - my $list = $th{$_}{lst}; - $list =~ s/.://g; - $list =~ s/p//; - my $chan = ""; - foreach (split",",$th{$_}{chn}){ - my ($n,$s,$e) = split(":",$_); - $chan .= $s.(($s eq $e)?"":("-".$e))." ".$n.", "; - } - push @model,sprintf("%-16s %-24s %4s %-24s %-5s %-5s %s" - ,$th{$_}{st} - ,$th{$_}{name} - ,$_ - ,$mode - ,$th{$_}{cyc} - ,$list - ,$chan - ); - } - $ret = $cmd.($filter?" filtered":"").":$filter\n " - .sprintf("%-16s %-24s %4s %-24s %-5s %-5s %s\n " - ,"subType" - ,"name" - ,"ID" - ,"supportedMode" - ,"Info" - ,"List" - ,"channels" - ) - .join"\n ",grep(/$filter/,sort @model); + my @model; + foreach (keys %th){ + my $mode = $th{$_}{rxt}; + $mode =~ s/c/config/; + $mode =~ s/w/wakeup/; + $mode =~ s/b/burst/; + $mode =~ s/l/lazyConf/; + $mode =~ s/\bf\b/burstCond/; + $mode =~ s/:/,/g; + $mode = "normal" if (!$mode); + my $list = $th{$_}{lst}; + $list =~ s/.://g; + $list =~ s/p//; + my $chan = ""; + foreach (split",",$th{$_}{chn}){ + my ($n,$s,$e) = split(":",$_); + $chan .= $s.(($s eq $e)?"":("-".$e))." ".$n.", "; + } + push @model,sprintf("%-16s %-24s %4s %-24s %-5s %-5s %s" + ,$th{$_}{st} + ,$th{$_}{name} + ,$_ + ,$mode + ,$th{$_}{cyc} + ,$list + ,$chan + ); + } + $ret = $cmd.($filter?" filtered":"").":$filter\n " + .sprintf("%-16s %-24s %4s %-24s %-5s %-5s %s\n " + ,"subType" + ,"name" + ,"ID" + ,"supportedMode" + ,"Info" + ,"List" + ,"channels" + ) + .join"\n ",grep(/$filter/,sort @model); } elsif($cmd eq "templateSet"){##template: set of register -------------------- return HMinfo_templateSet(@a); } elsif($cmd eq "templateChk"){##template: see if it applies ------------------ - my $repl; - foreach my $dName (HMinfo_getEntities($opt."v",$filter)){ - unshift @a, $dName; - $repl .= HMinfo_templateChk(@a); - shift @a; - } + my $repl; + foreach my $dName (HMinfo_getEntities($opt."v",$filter)){ + unshift @a, $dName; + $repl .= HMinfo_templateChk(@a); + shift @a; + } return $repl; } elsif($cmd eq "templateList"){##template: list templates -------------------- @@ -589,71 +589,71 @@ sub HMinfo_SetFn($@) {######################################################### return HMinfo_status($hash); } elsif($cmd eq "help") { - $ret = " Unknown argument $cmd, choose one of " - ."\n ---checks---" - ."\n configCheck [] # perform regCheck and regCheck" - ."\n regCheck [] # find incomplete or inconsistant register readings" - ."\n peerCheck [] # find incomplete or inconsistant peer lists" - ."\n ---actions---" - ."\n saveConfig [] # stores peers and register with saveConfig" - ."\n autoReadReg [] # trigger update readings if attr autoReadReg is set" - ."\n ---infos---" - ."\n update # update HMindfo counts" - ."\n register [] # devicefilter parse devicename. Partial strings supported" - ."\n peerXref [] # peer cross-reference" - ."\n models [] # list of models incl native parameter" - ."\n protoEvents [] [short|long] # protocol status - names can be filtered" - ."\n msgStat # view message statistic" - ."\n param [] [] [] ... # displays params for all entities as table" - ."\n rssi [] # displays receive level of the HM devices" - ."\n last: most recent" - ."\n avg: average overall" - ."\n range: min to max value" - ."\n count: number of events in calculation" - ."\n ---clear status---" - ."\n clear [] [Protocol|readings|msgStat|register|rssi]" - ."\n Protocol # delete all protocol-events" - ."\n readings # delete all readings" - ."\n register # delete all register-readings" - ."\n rssi # delete all rssi data" - ."\n msgStat # delete message statistics" - ."\n ---help---" - ."\n help #" - ."\n ***footnote***" - ."\n [] : only matiching names are processed - partial names are possible" - ."\n [] : any match in the output are searched. " - ."\n" - ."\n cpRegs " - ."\n copy register for a channel or behavior of channel/peer" - ."\n templateChk [] [ ...] " - ."\n compare whether register match the template values" - ."\n templateDef ...] : [:] ... " - ."\n define a template" - ."\n templateList [] # gives a list of templates or a description of the named template" - ."\n list all currently defined templates or the structure of a given template" - ."\n templateSet [ ...] " - ."\n write register according to a given template" - ."\n ======= typeFilter options: supress class of devices ====" - ."\n set [-dcasev] [-f ] [params]" - ."\n entities according to list will be processed" - ."\n d - device :include devices" - ."\n c - channels :include channels" - ."\n i - ignore :include devices marked as ignore" - ."\n v - virtual :supress fhem virtual" - ."\n p - physical :supress physical" - ."\n a - aktor :supress actor" - ."\n s - sensor :supress sensor" - ."\n e - empty :include results even if requested fields are empty" - ."\n " - ."\n -f - filter :regexp to filter entity names " - ."\n " - ; + $ret = " Unknown argument $cmd, choose one of " + ."\n ---checks---" + ."\n configCheck [] # perform regCheck and regCheck" + ."\n regCheck [] # find incomplete or inconsistant register readings" + ."\n peerCheck [] # find incomplete or inconsistant peer lists" + ."\n ---actions---" + ."\n saveConfig [] # stores peers and register with saveConfig" + ."\n autoReadReg [] # trigger update readings if attr autoReadReg is set" + ."\n ---infos---" + ."\n update # update HMindfo counts" + ."\n register [] # devicefilter parse devicename. Partial strings supported" + ."\n peerXref [] # peer cross-reference" + ."\n models [] # list of models incl native parameter" + ."\n protoEvents [] [short|long] # protocol status - names can be filtered" + ."\n msgStat # view message statistic" + ."\n param [] [] [] ... # displays params for all entities as table" + ."\n rssi [] # displays receive level of the HM devices" + ."\n last: most recent" + ."\n avg: average overall" + ."\n range: min to max value" + ."\n count: number of events in calculation" + ."\n ---clear status---" + ."\n clear [] [Protocol|readings|msgStat|register|rssi]" + ."\n Protocol # delete all protocol-events" + ."\n readings # delete all readings" + ."\n register # delete all register-readings" + ."\n rssi # delete all rssi data" + ."\n msgStat # delete message statistics" + ."\n ---help---" + ."\n help #" + ."\n ***footnote***" + ."\n [] : only matiching names are processed - partial names are possible" + ."\n [] : any match in the output are searched. " + ."\n" + ."\n cpRegs " + ."\n copy register for a channel or behavior of channel/peer" + ."\n templateChk [] [ ...] " + ."\n compare whether register match the template values" + ."\n templateDef ...] : [:] ... " + ."\n define a template" + ."\n templateList [] # gives a list of templates or a description of the named template" + ."\n list all currently defined templates or the structure of a given template" + ."\n templateSet [ ...] " + ."\n write register according to a given template" + ."\n ======= typeFilter options: supress class of devices ====" + ."\n set [-dcasev] [-f ] [params]" + ."\n entities according to list will be processed" + ."\n d - device :include devices" + ."\n c - channels :include channels" + ."\n i - ignore :include devices marked as ignore" + ."\n v - virtual :supress fhem virtual" + ."\n p - physical :supress physical" + ."\n a - aktor :supress actor" + ."\n s - sensor :supress sensor" + ."\n e - empty :include results even if requested fields are empty" + ."\n " + ."\n -f - filter :regexp to filter entity names " + ."\n " + ; } else {## go for delayed action $hash->{helper}{childCnt} = 0 if (!$hash->{helper}{childCnt}); - my $chCnt = ($hash->{helper}{childCnt}+1)%1000; + my $chCnt = ($hash->{helper}{childCnt}+1)%1000; my $childName = "child_".$chCnt; - + return HMinfo_SetFnDly(join(",",($childName,$name,$cmd,$opt,$optEmpty,$filter,@a))); } return $ret; @@ -666,24 +666,24 @@ sub HMinfo_SetFnDly($) {####################################################### my $hash = $defs{$name}; if ($cmd eq "saveConfig") {##action: saveConfig---------------------------- my ($file) = @a; - my @entities; - foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){ - CUL_HM_Get($defs{$dName},$dName,"saveConfig",$file); - push @entities,$dName; - foreach my $chnId (CUL_HM_getAssChnIds($dName)){ - my $dName = CUL_HM_id2Name($chnId); - push @entities, $dName if($dName !~ m/_chn:/); - } - } - $ret = $cmd." done:" ."\n saved" ."\n ".(join "\n ",sort @entities) - ; + my @entities; + foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){ + CUL_HM_Get($defs{$dName},$dName,"saveConfig",$file); + push @entities,$dName; + foreach my $chnId (CUL_HM_getAssChnIds($dName)){ + my $dName = CUL_HM_id2Name($chnId); + push @entities, $dName if($dName !~ m/_chn:/); + } + } + $ret = $cmd." done:" ."\n saved" ."\n ".(join "\n ",sort @entities) + ; } else{ - return "autoReadReg clear " + return "autoReadReg clear " ."configCheck param peerCheck peerXref " - ."protoEvents msgStat:view,clear models regCheck register rssi saveConfig update " + ."protoEvents msgStat:view,clear models regCheck register rssi saveConfig update " ."cpRegs templateChk templateDef templateList templateSet"; - } + } return $ret; } sub HMinfo_post($) {########################################################### @@ -714,8 +714,8 @@ sub HMinfo_status($){########################################################## my @errNames; foreach (@erro){ #prepare reading filter for error counts my ($p,@a) = split ":",$_; - $errFlt{$p}{x}=1; # add at least one reading - $errFlt{$p}{$_}=1 foreach (@a); + $errFlt{$p}{x}=1; # add at least one reading + $errFlt{$p}{$_}=1 foreach (@a); } #--- used for IO, protocol and communication (e.g. rssi) my @IOdev; @@ -731,54 +731,54 @@ sub HMinfo_status($){########################################################## foreach my $id (keys%{$modules{CUL_HM}{defptr}}){#search/count for parameter my $ehash = $modules{CUL_HM}{defptr}{$id}; - my $eName = $ehash->{NAME}; - $nbrE++; + my $eName = $ehash->{NAME}; + $nbrE++; $nbrC++ if ($ehash->{helper}{role}{chn}); $nbrV++ if ($ehash->{helper}{role}{vrt}); - push @shdwNames,$eName if (keys %{$ehash->{helper}{shadowReg}}); - foreach my $read (grep {$ehash->{READINGS}{$_}} @info){ #---- count critical readings - my $val = $ehash->{READINGS}{$read}{VAL}; - $sum{$read}{$val} =0 if (!$sum{$read}{$val}); + push @shdwNames,$eName if (keys %{$ehash->{helper}{shadowReg}}); + foreach my $read (grep {$ehash->{READINGS}{$_}} @info){ #---- count critical readings + my $val = $ehash->{READINGS}{$read}{VAL}; + $sum{$read}{$val} =0 if (!$sum{$read}{$val}); $sum{$read}{$val}++; - } - foreach my $read (grep {$ehash->{READINGS}{$_}} keys %errFlt){#---- count error readings - my $val = $ehash->{READINGS}{$read}{VAL}; + } + foreach my $read (grep {$ehash->{READINGS}{$_}} keys %errFlt){#---- count error readings + my $val = $ehash->{READINGS}{$read}{VAL}; next if (grep (/$val/,(keys%{$errFlt{$read}})));# filter non-Error - $err{$read}{$val} =0 if (!$err{$read}{$val}); + $err{$read}{$val} =0 if (!$err{$read}{$val}); $err{$read}{$val}++; push @errNames,$eName; - } + } if ($ehash->{helper}{role}{dev}){#---restrict to devices - $nbrD++; - push @IOdev,$ehash->{IODev}{NAME} if($ehash->{IODev} && $ehash->{IODev}{NAME}); - push @Anames,$eName if ($attr{$eName}{actStatus} && $attr{$eName}{actStatus} ne "alive"); + $nbrD++; + push @IOdev,$ehash->{IODev}{NAME} if($ehash->{IODev} && $ehash->{IODev}{NAME}); + push @Anames,$eName if ($attr{$eName}{actStatus} && $attr{$eName}{actStatus} ne "alive"); foreach (grep {$ehash->{"prot".$_}} keys %protE){#protocol events reported - $protE{$_}++; - push @protNamesE,$eName; - } + $protE{$_}++; + push @protNamesE,$eName; + } foreach (grep {$ehash->{"prot".$_}} keys %protW){#protocol events reported - $protW{$_}++; - push @protNamesW,$eName; - } - $rssiMin{$eName} = 0; + $protW{$_}++; + push @protNamesW,$eName; + } + $rssiMin{$eName} = 0; foreach (keys %{$ehash->{helper}{rssi}}){ $rssiMin{$eName} = $ehash->{helper}{rssi}{$_}{min} - if ($rssiMin{$eName} > $ehash->{helper}{rssi}{$_}{min}); + if ($rssiMin{$eName} > $ehash->{helper}{rssi}{$_}{min}); } - } + } } #====== collection finished - start data preparation====== - delete $hash->{$_} foreach (grep(/^(ERR|W_|I_|C_)/,keys%{$hash}));# remove old + delete $hash->{$_} foreach (grep(/^(ERR|W_|I_|C_)/,keys%{$hash}));# remove old my @updates; foreach my $read(grep {defined $sum{$_}} @info){ #--- disp crt count - my $d; - $d .= "$_:$sum{$read}{$_};"foreach(keys %{$sum{$read}}); - push @updates,"I_sum_$read:".$d; + my $d; + $d .= "$_:$sum{$read}{$_};"foreach(keys %{$sum{$read}}); + push @updates,"I_sum_$read:".$d; } foreach my $read(grep {defined $err{$_}} keys %errFlt){#--- disp err count my $d; - $d .= "$_:$err{$read}{$_};"foreach(keys %{$err{$read}}); - push @updates,"ERR_$read:".$d; + $d .= "$_:$err{$read}{$_};"foreach(keys %{$err{$read}}); + push @updates,"ERR_$read:".$d; } @errNames = grep !/^$/,HMinfo_noDup(@errNames); @@ -788,7 +788,7 @@ sub HMinfo_status($){########################################################## # ------- display status of action detector ------ push @updates,"I_actTotal:".$modules{CUL_HM}{defptr}{"000000"}{STATE}; $hash->{ERRactNames} = join",",@Anames if (@Anames); - + # ------- what about IO devices??? ------ my %tmp; # remove duplicates $tmp{$_}=0 for @IOdev; @@ -798,7 +798,7 @@ sub HMinfo_status($){########################################################## $_ .= " :".$defs{$_}{READINGS}{cond}{VAL}; } $hash->{I_HM_IOdevices}= join",",@IOdev; - + # ------- what about protocol events ------ # Current Events are Rcv,NACK,IOerr,Resend,ResendFail,Snd # additional variables are protCmdDel,protCmdPend,protState,protLastRcv @@ -823,16 +823,16 @@ sub HMinfo_status($){########################################################## else{ # delete $hash->{I_autoReadPend}; } - + # ------- what about rssi low readings ------ foreach (grep {$rssiMin{$_} != 0}keys %rssiMin){ if ($rssiMin{$_}> -60) {$rssiMinCnt{"59<"}++;} elsif ($rssiMin{$_}> -80) {$rssiMinCnt{"60>"}++;} elsif ($rssiMin{$_}< -99) {$rssiMinCnt{"99>"}++; - push @rssiNames,$_ ;} + push @rssiNames,$_ ;} else {$rssiMinCnt{"80>"}++;} } - + my $d =""; $d .= "$_:$rssiMinCnt{$_} " foreach (sort keys %rssiMinCnt); push @updates,"I_rssiMinLevel:".$d; @@ -840,21 +840,21 @@ sub HMinfo_status($){########################################################## # push @updates,":".$hash->{ERR___rssiCrit} if(@rssiNames); # ------- what about others ------ $hash->{W_unConfRegs} = join(",",@shdwNames) if (@shdwNames > 0); -# push @updates,":".$hash->{W_unConfRegs} if(@shdwNames > 0); +# push @updates,":".$hash->{W_unConfRegs} if(@shdwNames > 0); # ------- update own status ------ $hash->{STATE} = "updated:".TimeNow(); my $updt = join",",@updates; foreach (grep /^(W_|I_|ERR)/,keys%{$hash->{READINGS}}){ - delete $hash->{READINGS}{$_} if ($updt !~ m /$_/); + delete $hash->{READINGS}{$_} if ($updt !~ m /$_/); } readingsBeginUpdate($hash); foreach my $rd (@updates){ next if (!$rd); my ($rdName, $rdVal) = split(":",$rd, 2); - next if (defined $hash->{READINGS}{$rdName} && - $hash->{READINGS}{$rdName}{VAL} eq $rdVal); - readingsBulkUpdate($hash,$rdName, - ((defined($rdVal) && $rdVal ne "")?$rdVal:"-")); + next if (defined $hash->{READINGS}{$rdName} && + $hash->{READINGS}{$rdName}{VAL} eq $rdVal); + readingsBulkUpdate($hash,$rdName, + ((defined($rdVal) && $rdVal ne "")?$rdVal:"-")); } readingsEndUpdate($hash,1); return; @@ -863,63 +863,63 @@ sub HMinfo_status($){########################################################## my %tpl = ( autoOff => {p=>"time" ,t=>"staircase - auto off after