############################################## # $Id$ package main; use strict; use warnings; sub HMinfo_Initialize($$); sub HMinfo_Define($$); sub HMinfo_getParam(@); sub HMinfo_regCheck(@); sub HMinfo_peerCheck(@); sub HMinfo_peerCheck(@); sub HMinfo_getEntities(@); sub HMinfo_SetFn($@); sub HMinfo_SetFnDly($); sub HMinfo_post($); use Blocking; sub HMinfo_Initialize($$) {#################################################### my ($hash) = @_; $hash->{DefFn} = "HMinfo_Define"; $hash->{SetFn} = "HMinfo_SetFn"; $hash->{AttrFn} = "HMinfo_Attr"; $hash->{AttrList} = "loglevel:0,1,2,3,4,5,6 ". "sumStatus sumERROR ". "autoUpdate ". $readingFnAttributes; } sub HMinfo_Define($$){######################################################### my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my $name = $hash->{NAME}; $hash->{Version} = "01"; $attr{$name}{webCmd} = "update:protoEvents:rssi:peerXref:configCheck:models"; $attr{$name}{sumStatus} = "battery" .",sabotageError" .",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" ; 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"); 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; } sub HMinfo_autoUpdate($){#in:name, send status-request my $name = shift; (undef,$name)=split":",$name,2; HMinfo_SetFn($defs{$name},$name,"update") if ($name); return if (!defined $defs{$name}{helper}{autoUpdate}); InternalTimer(gettimeofday()+$defs{$name}{helper}{autoUpdate}, "HMinfo_autoUpdate","sUpdt:".$name,0); } sub HMinfo_getParam(@) { ###################################################### my ($id,@param) = @_; my @paramList; my $ehash = $modules{CUL_HM}{defptr}{$id}; my $eName = $ehash->{NAME}; my $found = 0; foreach (@param){ my $para = CUL_HM_Get($ehash,$eName,"param",$_); push @paramList,sprintf("%-20s",($para eq "undefined"?"-":$para)); $found = 1 if ($para ne "undefined") ; } return $found,sprintf("%-20s\t: %s",$eName,join "\t|",@paramList); } sub HMinfo_regCheck(@) { ###################################################### my @entities = @_; my @regIncompl; my @peerRegsFail; foreach my $eName (@entities){ my $ehash = $defs{$eName}; my $devId = substr($defs{$eName}{DEF},0,6); my @peerIdInReg; foreach my $rdEntry (keys %{$ehash->{READINGS}}){ next if ($rdEntry !~m /^[\.]?RegL_(.*)/); push @regIncompl,$eName.":".$rdEntry if ($ehash->{READINGS}{$rdEntry}{VAL} !~ m/00:00/); my $peer = $rdEntry; $peer =~ s/.*RegL_..://; $peer =~ s/^self/$devId/; next if (!$peer); push @peerIdInReg,CUL_HM_name2Id($peer); } #- - - - check whether peer is required - - - - my $st = CUL_HM_Get($defs{$eName},$eName,"param","subType"); if ($st !~ m/(thermostat|smokeDetector)/){ my $peerLinReg = (join ",",sort @peerIdInReg); $peerLinReg .= "," if ($peerLinReg); my $peerIDs = AttrVal($eName,"peerIDs",""); $peerIDs =~ s/00000000,//; push @peerRegsFail,$eName." - found:".$peerLinReg." expected:".$peerIDs if ($peerLinReg ne $peerIDs); } } return "\n incomplete register set\n " .(join "\n ",sort @regIncompl) ."\n missing Peer Registerset\n ".(join "\n ",sort @peerRegsFail) ; } sub HMinfo_peerCheck(@) { ##################################################### my @entities = @_; my @peerIDsFail; my @peerIDsEmpty; my @peerIDsNoPeer; my %th = CUL_HM_putHash("culHmModel"); foreach my $eName (@entities){ my $ehash = $defs{$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? next if (length($id) == 6 && $ehash->{channel_01});#device with channels - no peers on device level next if ($st eq "virtual"); # virtuals may not have peers my $list; foreach (keys %th){ $list = $th{$_}{lst} if ($th{$_}{name} eq $md); } # should not be empty for SD # should not be empty for entities with List 3 or 4 push @peerIDsEmpty,"empty critical: " .$eName if ($st eq "smokeDetector"); push @peerIDsEmpty,"empty: " .$eName if($list =~ m/[34]/); #those should have peers } elsif($peerIDs !~ m/00000000/ && $st ne "virtual"){#peerList incomplete push @peerIDsFail,"incomplete: ".$eName.":".$peerIDs; } else{# work on a valid list: foreach (split",",$peerIDs){ next if ($_ eq "00000000" ||$_ =~m /$devId/); 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/$id/); } } } return "\n incomplete list" ."\n ".(join "\n ",sort @peerIDsFail) ."\n empty list" ."\n ".(join "\n ",sort @peerIDsEmpty) ."\n peer not verified"."\n ".(join "\n ",sort @peerIDsNoPeer) ; } sub HMinfo_getEntities(@) { ################################################### my ($filter,$re) = @_; my @names; my ($doDev,$doChn,$noVrt,$noPhy,$noAct,$noSen,$doEmp); $doDev=$doChn=$doEmp= 1; $noVrt=$noPhy=$noAct=$noSen = 0; $filter .= "dc" if ($filter !~ m/d/ && $filter !~ m/c/); # add default $re = '.' if (!$re); if ($filter){# options provided $doDev=$doChn=$doEmp= 0;#change default no warnings; my @pl = split undef,$filter; use warnings; foreach (@pl){ $doDev = 1 if($_ eq 'd'); $doChn = 1 if($_ eq 'c'); $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 $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"); next if (!(($doDev && length($id) == 6) || ($doChn && $isChn))); 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; } return sort(@names); } 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 } if (@a && $a[0] =~ m/^-f$/){# options provided shift @a; #remove $filter = shift @a; } if (!$cmd ||$cmd eq "?" ) {##actionImmediate: clear parameter-------------- return "autoReadReg clear configCheck param peerCheck peerXref protoEvents models regCheck register rssi saveConfig update"; } elsif($cmd eq "clear" ) {##actionImmediate: clear parameter-------------- my ($type) = @a; $opt .= "d" if ($type ne "Readings");# readings apply to all, others device only my @entities; return "unknown parameter - use Protocol,readings or rssi" if ($type !~ m/^(Protocol|readings|rssi)$/); $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)); my @arr; if(!$modules{CUL_HM}{helper}{autoRdCfgLst}){ $modules{CUL_HM}{helper}{autoRdCfgLst} = \@arr; } push @{$modules{CUL_HM}{helper}{autoRdCfgLst}}, $dName; $defs{$dName}{autoRead} = "scheduled"; RemoveInternalTimer("autoRdCfg"); InternalTimer(gettimeofday()+5,"CUL_HM_autoReadConfig","autoRdCfg",0); push @entities,$dName; } return $cmd." done:" ."\n cleared" ."\n ".(join "\n ",sort @entities) ; } elsif($cmd eq "protoEvents"){##print protocol-events------------------------- 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" ,"protResndFail","protNack","protIOerr"); $para =~ s/( last_at|20..-|\|)//g; my @pl = split "\t",$para; $_ =~ s/\s+$|//g foreach (@pl); push @paramList, sprintf("%-20s%-22s|%-18s|%-18s|%-14s|%-18s|%-18s|%-18s|%-18s", $pl[0],$pl[1],$pl[2],$pl[3],$pl[4],$pl[5],$pl[6],$pl[7],$pl[8]); push @IOlist,$defs{$pl[0]}{IODev}->{NAME}; } my $hdr = sprintf("%-20s:%-21s|%-18s|%-18s|%-14s|%-18s|%-18s|%-18s|%-18s", ,"name" ,"protState","protCmdPend" ,"protSnd","protLastRcv","protResnd" ,"protResndFail","protNack","protIOerr"); $ret = $cmd." done:" ."\n ".$hdr ."\n ".(join "\n ",sort @paramList) ; $ret .= "\n\n CUL_HM queue:$modules{CUL_HM}{prot}{rspPend}"; $ret .= "\n autoRegRead pending:". join(",",@{$modules{CUL_HM}{helper}{autoRdCfgLst}}) if ($modules{CUL_HM}{helper}{autoRdCfgLst}); @IOlist = HMinfo_noDup(@IOlist); foreach(@IOlist){ $_ .= ":".$defs{$_}{STATE}. (defined $defs{$_}{helper}{q}{answerPend}? " pending=".$defs{$_}{helper}{q}{answerPend} : ""); } $ret .= "\n IODevs:".(join"\n ",HMinfo_noDup(@IOlist)); } elsif($cmd eq "rssi") {##print RSSI protocol-events-------------------- 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 %6.1f %6.1f %6.1f<%6.1f %3s" ,$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 "."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"; } delete $hash->{helper}{r}; } $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); push @paramList,$para if($found || $optEmpty); } $ret = $cmd." done:" ."\n param list" ."\n ".(join "\n ",sort @paramList) ; } elsif($cmd eq "regCheck") {##check register-------------------------------- my @entities = HMinfo_getEntities($opt."v",$filter); $ret = $cmd." done:" .HMinfo_regCheck(@entities); } elsif($cmd eq "peerCheck") {##check peers----------------------------------- my @entities = HMinfo_getEntities($opt."v",$filter); $ret = $cmd." done:" .HMinfo_peerCheck(@entities); } elsif($cmd eq "configCheck"){##check peers and register---------------------- my @entities = HMinfo_getEntities($opt."v",$filter); $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){ next if ($_ eq "00000000"); my $pName = CUL_HM_id2Name($_); my $pPlist = AttrVal($pName,"peerIDs",""); $pName =~ s/$dName\_chn:/self/; push @peerPairs,$dName." =>".$pName; } } $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/:/,/; $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 %-15s %-5s %-5s %s" ,$th{$_}{st} ,$th{$_}{name} ,$_ ,$mode ,$th{$_}{cyc} ,$list ,$chan ); } $ret = $cmd.($filter?" filtered":"").":$filter\n " .sprintf("%-16s %-24s %4s %-15s %-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; } return $repl; } elsif($cmd eq "templateList"){##template: list templates -------------------- return HMinfo_templateList($a[0]); } elsif($cmd eq "templateDef"){##template: define one ------------------------- return HMinfo_templateDef(@a); } elsif($cmd eq "cpRegs") {##copy register -------------------- return HMinfo_cpRegs(@a); } elsif($cmd eq "update") {##update hm counts ----------------------------- 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 [] # protocol status - names can be filtered" ."\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|Rssi]" ."\n Protocol # delete all protocol-events" ."\n Readings # delete all readings" ."\n Rssi # delete all rssi data" ."\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 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 $childName = "child_".$chCnt; return HMinfo_SetFnDly(join(",",($childName,$name,$cmd,$opt,$optEmpty,$filter,@a))); } return $ret; } sub HMinfo_SetFnDly($) {####################################################### my $in = shift; my ($childName,$name,$cmd,$opt,$optEmpty,$filter,@a) = split",",$in; my $ret; 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) ; } else{ return "autoReadReg clear configCheck param peerCheck peerXref " ."protoEvents models regCheck register rssi saveConfig update " ."cpRegs templateChk templateDef templateList templateSet"; } return $ret; } sub HMinfo_post($) {########################################################### my ($name,$childName) = (split":",$_); foreach (keys %{$defs{$name}{helper}{child}}){ Log 1,"General still running: $_ ".$defs{$name}{helper}{child}{$_}; } delete $defs{$name}{helper}{child}{$childName}; Log 1,"General deleted $childName now++++++++++++++"; return "finished"; } sub HMinfo_status($){########################################################## # - count defined HM entities, selected readings, errors on filtered readings # - display Assigned IO devices # - show ActionDetector status # - prot events if error # - rssi - eval minimum values my $hash = shift; my $name = $hash->{NAME}; my ($nbrE,$nbrD,$nbrC,$nbrV) = (0,0,0,0);# count entities and types #--- used for status my @info = split ",",$attr{$name}{sumStatus};#prepare event my %sum; #--- used for error counts my @erro = split ",",$attr{$name}{sumERROR}; my %errFlt; my %err; 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); } #--- used for IO, protocol and communication (e.g. rssi) my @IOdev; my %protE = (NACK =>0,IOerr =>0,ResndFail =>0,CmdDel =>0); my %protW = (Resnd =>0,CmdPend =>0); my @protNamesE; # devices with current protocol events my @protNamesW; # devices with current protocol events my @Anames; # devices with ActionDetector events my %rssiMin; my %rssiMinCnt = ("99>"=>0,"80>"=>0,"60>"=>0,"59<"=>0); my @rssiNames; #entities with ciritcal RSSI my @shdwNames; #entites with shadowRegs, i.e. unconfirmed register ->W_unconfRegs foreach my $id (keys%{$modules{CUL_HM}{defptr}}){#search/count for parameter my $ehash = $modules{CUL_HM}{defptr}{$id}; 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}); $sum{$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}++; push @errNames,$eName; } if ($ehash->{helper}{role}{dev}){#---restrict to devices $nbrD++; push @IOdev,$ehash->{IODev}{NAME} if($ehash->{IODev}); 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; } foreach (grep {$ehash->{"prot".$_}} keys %protW){#protocol events reported $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}); } } } #====== collection finished - start data preparation====== 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; } 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; } @errNames = grep !/^$/,HMinfo_noDup(@errNames); $hash->{ERR_names} = join",",@errNames if(@errNames);# and name entities push @updates,"C_sumDefined:"."entities:$nbrE device:$nbrD channel:$nbrC virtual:$nbrV"; # ------- 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; delete $tmp{""}; #remove empties if present @IOdev = sort keys %tmp; foreach (grep {$defs{$_}{READINGS}{cond}} @IOdev){ $_ .= " :".$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 my @tp; push @tp,"$_:$protE{$_}" foreach (grep {$protE{$_}} keys(%protE)); push @updates,"ERR__protocol:".join",",@tp if(@tp); my @tpw; push @tpw,"$_:$protW{$_}" foreach (grep {$protW{$_}} keys(%protW)); push @updates,"W__protocol:".join",",@tpw if(@tpw); @protNamesE = grep !/^$/,HMinfo_noDup(@protNamesE);; $hash->{ERR__protoNames} = join",",@protNamesE if(@protNamesE); @protNamesW = grep !/^$/,HMinfo_noDup(@protNamesW); $hash->{W__protoNames} = join",",@protNamesW if(@protNamesW); if (defined $modules{CUL_HM}{helper}{autoRdCfgLst} && @{$modules{CUL_HM}{helper}{autoRdCfgLst}}>0){ $hash->{I_autoReadPend} = join ",",@{$modules{CUL_HM}{helper}{autoRdCfgLst}}; push @updates,"I_autoReadPend:". scalar @{$modules{CUL_HM}{helper}{autoRdCfgLst}}; } 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,$_ ;} else {$rssiMinCnt{"80>"}++;} } my $d =""; $d .= "$_:$rssiMinCnt{$_} " foreach (sort keys %rssiMinCnt); push @updates,"I_rssiMinLevel:".$d; $hash->{ERR___rssiCrit} = join(",",@rssiNames) if (@rssiNames); # 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); # ------- 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 /$_/); } 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:"-")); } readingsEndUpdate($hash,1); return; } my %tpl = ( autoOff => {p=>"time" ,t=>"staircase - auto off after