1
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-05-04 22:19:38 +00:00
fhem-mirror/FHEM/98_HMinfo.pm
martinp876 46ea23f5da correct autoReadReg
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@3503 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2013-07-25 16:16:09 +00:00

1260 lines
54 KiB
Perl

##############################################
# $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->{AttrList} = "loglevel:0,1,2,3,4,5,6 ".
"sumStatus sumERROR ".
$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_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: %s",$eName,join "\t|",@paramList);
return sprintf("%-20s: %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 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;
foreach my $dName (HMinfo_getEntities($opt."dv",$filter)){
my $id = $defs{$dName}{DEF};
my ($found,$para) = HMinfo_getParam($id,"protState","protCmdPend","protSnd",
"protLastRcv","protResndFail","protResnd","protNack");
$para =~ s/ last_at//g;
push @paramList,$para;
}
my $hdr = sprintf("%-20s:%-23s|%-23s|%-23s|%-23s|%-23s|%-23s|%-23s",
"name","protState","protCmdPend","protSnd",
"protLastRcv","protResndFail","protResnd","protNack");
$ret = $cmd." done:" ."\n ".$hdr ."\n ".(join "\n ",sort @paramList)
;
}
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<max count"
."\n ".(join "\n ",sort @rssiList)
;
}
elsif($cmd eq "register") {##print register--------------------------------
# devicenameFilter
my $RegReply = "";
my @noReg;
foreach my $dName (HMinfo_getEntities($opt."v",$filter)){
my $regs = CUL_HM_Get(CUL_HM_name2Hash($dName),$dName,"reg","all");
if ($regs !~ m/[0-6]:/){
push @noReg,$dName;
next;
}
my ($peerOld,$ptOld,$ptLine,$peerLine) = ("",""," "," ");
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;
}
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/:/,/;
$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 %-13s %-5s %-5s %s"
,$th{$_}{st}
,$th{$_}{name}
,$_
,$mode
,$th{$_}{cyc}
,$list
,$chan
);
}
$ret = $cmd.($filter?" filtered":"").":$filter\n "
.sprintf("%-16s %-24s %4s %-13s %-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 [<typeFilter>] # perform regCheck and regCheck"
."\n regCheck [<typeFilter>] # find incomplete or inconsistant register readings"
."\n peerCheck [<typeFilter>] # find incomplete or inconsistant peer lists"
."\n ---actions---"
."\n saveConfig [<typeFilter>] <file> # stores peers and register with saveConfig"
."\n autoReadReg [<typeFilter>] # trigger update readings if attr autoReadReg is set"
."\n ---infos---"
."\n update # update HMindfo counts"
."\n register [<typeFilter>] # devicefilter parse devicename. Partial strings supported"
."\n peerXref [<typeFilter>] # peer cross-reference"
."\n models [<typeFilter>] # list of models incl native parameter"
."\n protoEvents [<typeFilter>] # protocol status - names can be filtered"
."\n param [<typeFilter>] [<param1>] [<param2>] ... # displays params for all entities as table"
."\n rssi [<typeFilter>] # 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 [<typeFilter>] [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 [<nameFilter>] : only matiching names are processed - partial names are possible"
."\n [<modelsFilter>] : any match in the output are searched. "
."\n"
."\n cpRegs <src:peer> <dst:peer>"
."\n copy register for a channel or behavior of channel/peer"
."\n templateChk [<typeFilter>] <templateName> <peer:[long|short]> [<param1> ...] "
."\n compare whether register match the template values"
."\n templateDef <entity> <templateName> <param1[:<param2>...] <description> <reg1>:<val1> [<reg2>:<val2>] ... "
."\n define a template"
."\n templateList [<templateName>] # 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 <entity> <templateName> <peer:[long|short]> [<param1> ...] "
."\n write register according to a given template"
."\n ======= typeFilter options: supress class of devices ===="
."\n set <name> <cmd> [-dcasev] [-f <filter>] [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 @crit = 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 %prot = (NACK =>0,IOerr =>0,ResendFail =>0,CmdDel =>0,CmdPend =>0);
my @protNames; # 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}{$_}} @crit){ #---- 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 %prot){#protocol events reported
$prot{$_}++;
push @protNames,$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_sum_)/,keys%{$hash}));# remove old
foreach my $read(grep {defined $sum{$_}} @crit){ #--- disp crt count
$hash->{"W_sum_".$read} = "";
$hash->{"W_sum_".$read} .= "$_:$sum{$read}{$_};"foreach(keys %{$sum{$read}});
}
foreach my $read(grep {defined $err{$_}} keys %errFlt){#--- disp err count
$hash->{"ERR_".$read} = "";
$hash->{"ERR_".$read} .= "$_:$err{$read}{$_};"foreach(keys %{$err{$read}});
}
my %allE; # remove duplicates
$allE{$_}=0 foreach (grep !//, @errNames);
@errNames = sort keys %allE;
$hash->{ERR_names} = join",",@errNames if(@errNames);# and name entities
$hash->{C_sumDefined} = "entities:$nbrE device:$nbrD channel:$nbrC virtual:$nbrV";
# ------- display status of action detector ------
$hash->{I_actTotal} = $modules{CUL_HM}{defptr}{"000000"}{STATE};
$hash->{ERRactNames} = join",",@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,"$_:$prot{$_}" foreach (grep {$prot{$_}} keys(%prot));
$hash->{ERR__protocol} = join",",@tp if(@tp);
my %all; # remove duplicates
$all{$_}=0 foreach (grep !//,@protNames);
@protNames = sort keys %all;
$hash->{ERR__protoNames} = join",",@protNames if(@protNames);
if (defined $modules{CUL_HM}{helper}{autoRdCfgLst} &&
@{$modules{CUL_HM}{helper}{autoRdCfgLst}}>0){
$hash->{I_autoReadPend} = join ",",@{$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<"}++;}
}
$hash->{I_rssiMinLevel} = "";
$hash->{I_rssiMinLevel} .= "$_:$rssiMinCnt{$_} " foreach (sort keys %rssiMinCnt);
$hash->{ERR___rssiCrit} = join(",",@rssiNames) if (@rssiNames);
# ------- what about others ------
$hash->{W_unConfRegs} = join(",",@shdwNames) if (@shdwNames > 0);
# ------- update own status ------
$hash->{STATE} = "updated:".TimeNow();
return;
}
my %tpl = (
autoOff => {p=>"time" ,t=>"staircase - auto off after <time>, extend time with each trigger"
,reg=>{ OnTime =>"p0"
,OffTime =>111600
}}
,motionOnDim => {p=>"ontime brightness",t=>"Dimmer:on for time if MDIR-brightness below level"
,reg=>{ CtDlyOn =>"ltLo"
,CtDlyOff =>"ltLo"
,CtOn =>"ltLo"
,CtOff =>"ltLo"
,CtValLo =>"p1"
,CtRampOn =>"ltLo"
,CtRampOff =>"ltLo"
,OffTime =>111600
,OnTime =>"p0"
,ActionTypeDim =>"jmpToTarget"
,DimJtOn =>"on"
,DimJtOff =>"dlyOn"
,DimJtDlyOn =>"rampOn"
,DimJtDlyOff =>"dlyOn"
,DimJtRampOn =>"on"
,DimJtRampOff =>"dlyOn"
}}
,motionOnSw => {p=>"ontime brightness",t=>"Switch:on for time if MDIR-brightness below level"
,reg=>{ CtDlyOn =>"ltLo"
,CtDlyOff =>"ltLo"
,CtOn =>"ltLo"
,CtOff =>"ltLo"
,CtValLo =>"p1"
,OffTime =>111600
,OnTime =>"p0"
,ActionType =>"jmpToTarget"
,SwJtOn =>"on"
,SwJtOff =>"dlyOn"
,SwJtDlyOn =>"on"
,SwJtDlyOff =>"dlyOn"
}}
,SwCondAbove => {p=>"condition" ,t=>"Switch:execute only if condition level is above limit"
,reg=>{ CtDlyOn =>"geLo"
,CtDlyOff =>"geLo"
,CtOn =>"geLo"
,CtOff =>"geLo"
,CtValLo =>"p0"
}}
,SwCondBelow => {p=>"condition" ,t=>"Switch:execute only if condition level is below limit"
,reg=>{ CtDlyOn =>"ltLo"
,CtDlyOff =>"ltLo"
,CtOn =>"ltLo"
,CtOff =>"ltLo"
,CtValLo =>"p0"
}}
,SwOnCond => {p=>"level cond" ,t=>"switch:execute only if condition [geLo|ltLo] level is below limit"
,reg=>{ CtDlyOn =>"p1"
,CtDlyOff =>"p1"
,CtOn =>"p1"
,CtOff =>"p1"
,CtValLo =>"p0"
}}
,BlStopDnLg => {p=>"" ,t=>"Blind: stop drive on any key - for long drive down"
,reg=>{ ActionType =>"jmpToTarget"
,BlJtDlyOff =>"refOff"
,BlJtDlyOn =>"dlyOff"
,BlJtOff =>"dlyOff"
,BlJtOn =>"dlyOff"
,BlJtRampOff =>"rampOff"
,BlJtRampOn =>"on"
,BlJtRefOff =>"rampOff"
,BlJtRefOn =>"on"
}}
,BlStopDnSh => {p=>"" ,t=>"Blind: stop drive on any key - for short drive down"
,reg=>{ ActionType =>"jmpToTarget"
,BlJtDlyOff =>"refOff"
,BlJtDlyOn =>"dlyOff"
,BlJtOff =>"dlyOff"
,BlJtOn =>"dlyOff"
,BlJtRampOff =>"off"
,BlJtRampOn =>"on"
,BlJtRefOff =>"rampOff"
,BlJtRefOn =>"on"
}}
,BlStopUpLg => {p=>"" ,t=>"Blind: stop drive on any key - for long drive up"
,reg=>{ ActionType =>"jmpToTarget"
,BlJtDlyOff =>"dlyOn"
,BlJtDlyOn =>"refOn"
,BlJtOff =>"dlyOn"
,BlJtOn =>"dlyOn"
,BlJtRampOff =>"off"
,BlJtRampOn =>"rampOn"
,BlJtRefOff =>"off"
,BlJtRefOn =>"rampOn"
}}
,BlStopUpSh => {p=>"" ,t=>"Blind: stop drive on"
,reg=>{ ActionType =>"jmpToTarget"
,BlJtDlyOff =>"dlyOn"
,BlJtDlyOn =>"refOn"
,BlJtOff =>"dlyOn"
,BlJtOn =>"dlyOn"
,BlJtRampOff =>"off"
,BlJtRampOn =>"on"
,BlJtRefOff =>"off"
,BlJtRefOn =>"rampOn"
}}
);
sub HMinfo_templateDef(@){#####################################################
my ($name,$param,$desc,@regs) = @_;
return "insufficient parameter" if(!defined $param);
if ($param eq "del"){
delete $tpl{$name};
return;
}
# get description if marked wir ""
if ($desc =~ m/^"/){
foreach (@regs){
$desc .= " ".(shift @regs);
last if ($desc =~ m/"$/);
}
$desc =~ s/"//g;
}
return "$name already defined, delete it first" if($tpl{$name});
return "insufficient parameter" if(@regs < 1);
$tpl{$name}{p} = "";
$tpl{$name}{p} = join(" ",split(":",$param)) if($param ne "0");
$tpl{$name}{t} = $desc;
my $paramNo = split(":",$param);
foreach (@regs){
my ($r,$v)=split":",$_;
if (!defined $v){
delete $tpl{$name};
return " empty reg value for $r";
}
elsif($v =~ m/^p(.)/){
return ($1+1)." params are necessary, only $paramNo aregiven"
if (($1+1)>$paramNo);
}
$tpl{$name}{reg}{$r} = $v;
}
}
sub HMinfo_templateSet(@){#####################################################
my ($aName,$tmpl,$pSet,@p) = @_;
$pSet = "" if (!$pSet);
my ($pName,$pTyp) = split(":",$pSet);
return "template undefined $tmpl" if(!$tpl{$tmpl});
return "aktor $aName unknown" if(!$defs{$aName});
return "exec set $aName getConfig first" if(!(grep /RegL_/,keys%{$defs{$aName}{READINGS}}));
return "give <peer>:[short|long] with peer, not $pSet" if($pName && $pTyp !~ m/(short|long)/);
$pSet = $pTyp eq "long"?"lg":"sh";
my $aHash = $defs{$aName};
my @regCh;
foreach (keys%{$tpl{$tmpl}{reg}}){
my $regN = $pSet.$_;
my $regV = $tpl{$tmpl}{reg}{$_};
if ($regV =~m /^p(.)$/) {#replace with User parameter
return "insufficient values - at least ".$tpl{p}." are $1 necessary" if (@p < ($1+1));
$regV = $p[$1];
}
my ($ret,undef) = CUL_HM_Set($aHash,$aName,"regSet",$regN,"?",$pName);
return "Device doesn't support $regN - template $tmpl not applicable" if ($ret =~ m/failed:/);
return "peer necessary for template" if ($ret =~ m/peer required/ && !$pName);
return "Device doesn't support literal $regV for reg $regN" if ($ret =~ m/literal:/ && $ret !~ m/\b$regV\b/);
my ($min,$max) = ($1,$2) if ($ret =~ m/range:(.*) to (._?) /);
return "$regV out of range: $min to $max" if ($min && ($regV < $min || $regV > $max));
push @regCh,"$regN,$regV";
}
foreach (@regCh){#Finally write to shadow register.
my ($ret,undef) = CUL_HM_Set($aHash,$aName,"regSet",split(",",$_),$pName,"prep");
return $ret if ($ret);
}
foreach my $regl (keys %{$aHash->{helper}{shadowReg}}){#write any existing shadowreg for this entity
my @new;
my $cur = $aHash->{READINGS}{$regl}{VAL};
foreach (split(" ",$aHash->{helper}{shadowReg}{$regl})){
push @new, $_ if ($cur !~ m/$_/);
}
my ($ret,undef) = CUL_HM_Set($aHash,$aName,"regBulk",$regl,@new);
return $ret if ($ret);
}
return "";
}
sub HMinfo_templateChk(@){#####################################################
my ($aName,$tmpl,$pSet,@p) = @_;
$pSet = "" if (!$pSet || $pSet eq "none");
my ($pName,$pTyp) = split(":",$pSet);
return "template undefined $tmpl\n" if(!$tpl{$tmpl});
return "aktor $aName unknown\n" if(!$defs{$aName});
return "give <peer>:[short|long|all] wrong:$pTyp\n" if($pTyp && $pTyp !~ m/(short|long|all)/);
my @pNames;
if ($pName eq "all"){
my $dId = substr(CUL_HM_name2Id($aName),0,6);
foreach (grep !/00000000/,split(",",AttrVal($aName,"peerIDs",""))){
push @pNames,CUL_HM_peerChName($_,$dId,"").":long" if (!$pTyp || $pTyp ne "short");
push @pNames,CUL_HM_peerChName($_,$dId,"").":short" if (!$pTyp || $pTyp ne "long");
}
}
elsif(($pName && !$pTyp) || $pTyp eq "all"){
push @pNames,$pName.":long";
push @pNames,$pName.":short";
}
else{
push @pNames,$pSet;
}
my $repl = "";
foreach my $pS (@pNames){
($pName,$pTyp) = split(":",$pS);
my $replPeer="";
if($pName && (grep !/$pName/,ReadingsVal($aName,"peerList" ,undef))){
$replPeer=" no peer:$pName\n";
}
else{
my $pRnm = $pName?($pName."-".($pTyp eq "long"?"lg":"sh")):"";
foreach my $rn (keys%{$tpl{$tmpl}{reg}}){
my $regV = ReadingsVal($aName,"R-$pRnm$rn" ,undef);
$regV = ReadingsVal($aName,".R-$pRnm$rn",undef) if (!defined $regV);
$regV = ReadingsVal($aName,"R-".$rn ,undef) if (!defined $regV);
$regV = ReadingsVal($aName,".R-".$rn ,undef) if (!defined $regV);
if (defined $regV){
$regV =~s/ .*//;#strip unit
my $tplV = $tpl{$tmpl}{reg}{$rn};
if ($tplV =~m /^p(.)$/) {#replace with User parameter
return "insufficient data - at least ".$tpl{p}." are $1 necessary"
if (@p < ($1+1));
$tplV = $p[$1];
}
$replPeer .= " $rn :$regV should $tplV \n" if ($regV ne $tplV);
}
else{
$replPeer .= " reg not found: $rn\n";
}
}
}
$repl .= "$aName $pS-> ".($replPeer?"failed\n$replPeer":"match\n");
}
return ($repl?$repl:"template $tmpl match actor:$aName peer:$pSet");
}
sub HMinfo_templateList($){####################################################
my $templ = shift;
my $reply = "";
# if(!$templ || !(grep /$templ/,keys%tpl)){# list all templates
if(!($templ && (grep /$templ/,keys%tpl))){# list all templates
foreach (sort keys%tpl){
$reply .= sprintf("%-16s params:%-24s Info:%s\n"
,$_
,$tpl{$_}{p}
,$tpl{$_}{t}
);
}
}
else{#details about one template
$reply = sprintf("%-16s params:%-24s Info:%s\n",$templ,$tpl{$templ}{p},$tpl{$templ}{t});
foreach (sort keys %{$tpl{$templ}{reg}}){
my $val = $tpl{$templ}{reg}{$_};
if ($val =~m /^p(.)$/){
my @a = split(" ",$tpl{$templ}{p});
$val = $a[$1];
}
$reply .= sprintf(" %-16s :%s\n",$_,$val);
}
}
return $reply;
}
sub HMinfo_cpRegs(@){#########################################################
my ($srcCh,$dstCh) = @_;
my ($srcP,$dstP,$srcPid,$dstPid,$srcRegLn,$dstRegLn);
($srcCh,$srcP) = split(":",$srcCh,2);
($dstCh,$dstP) = split(":",$dstCh,2);
return "source channel $srcCh undefined" if (!$defs{$srcCh});
return "destination channel $srcCh undefined" if (!$defs{$dstCh});
#compare source and destination attributes
# return "model not compatible" if (CUL_HM_Get($ehash,$eName,"param","model") ne
# CUL_HM_Get($ehash,$eName,"param","model"));
if ($srcP){# will be peer related copy
if ($srcP =~ m/self(.*)/) {$srcPid = $defs{$srcCh}{DEF}.sprintf("%02X",$1)}
elsif($srcP =~ m/^[A-F0-9]{8}$/i){$srcPid = $srcP;}
elsif($srcP =~ m/(.*)_chn:(..)/) {$srcPid = $defs{$1}->{DEF}.$2;}
elsif($defs{$srcP}) {$srcPid = $defs{$srcP}{DEF}.$2;}
if ($dstP =~ m/self(.*)/) {$dstPid = $defs{$dstCh}{DEF}.sprintf("%02X",$1)}
elsif($dstP =~ m/^[A-F0-9]{8}$/i){$dstPid = $dstP;}
elsif($dstP =~ m/(.*)_chn:(..)/) {$dstPid = $defs{$1}->{DEF}.$2;}
elsif($defs{$dstP}) {$dstPid = $defs{$dstP}{DEF}.$2;}
return "invalid peers src:$srcP dst:$dstP" if(!$srcPid || !$dstPid);
return "sourcepeer not in peerlist" if ($attr{$srcCh}{peerIDs} !~ m/$srcPid/);
return "destination peer not in peerlist" if ($attr{$dstCh}{peerIDs} !~ m/$dstPid/);
if ($defs{$srcCh}{READINGS}{"RegL_03:".$srcP}) {$srcRegLn = "RegL_03:".$srcP}
elsif($defs{$srcCh}{READINGS}{".RegL_03:".$srcP}) {$srcRegLn = ".RegL_03:".$srcP}
elsif($defs{$srcCh}{READINGS}{"RegL_04:".$srcP}) {$srcRegLn = "RegL_04:".$srcP}
elsif($defs{$srcCh}{READINGS}{".RegL_04:".$srcP}) {$srcRegLn = ".RegL_04:".$srcP}
$dstRegLn = $srcRegLn;
$dstRegLn =~ s/:.*/:/;
$dstRegLn .= $dstP;
}
else{
if ($defs{$srcCh}{READINGS}{"RegL_01:"}) {$srcRegLn = "RegL_01:"}
elsif($defs{$srcCh}{READINGS}{".RegL_01:"}) {$srcRegLn = ".RegL_01:"}
$dstRegLn = $srcRegLn;
}
return "source register not available" if (!$srcRegLn);
return "regList incomplete" if ($defs{$srcCh}{READINGS}{$srcRegLn}{VAL} !~ m/00:00/);
# we habe a reglist with termination, source and destination peer is checked. Go copy
my $srcData = $defs{$srcCh}{READINGS}{$srcRegLn}{VAL};
$srcData =~ s/00:00//; # remove termination
my ($ret,undef) = CUL_HM_Set($defs{$dstCh},$dstCh,"regBulk",$srcRegLn,split(" ",$srcData));
return $ret;
}
1;
=pod
=begin html
<a name="HMinfo"></a>
<h3>HMinfo</h3>
<ul>
<tr><td>
HMinfo is a module that shall support in getting an overview of
eQ-3 HomeMatic devices as defines in <a href="#CUL_HM">CUL_HM</a>. <br><br>
<B>Status information and counter</B><br>
hminfo tries to give an overview on the CUL_HM installed base including current conditions.
Readings and counter will not be updates automatically due to performance issues. <br>
Command <a href="#HMinfoupdate">update</a> must be used to refresh the values.
<ul><code>
set hm update<br>
</code></ul>
Webview of HMinfo will provide details, mainly based counter drivern, on how
many CUL_HM entities experience certain conditions. Areas provided are
<li>Action Detector status</li>
<li>CUL_HM related IO devices with their condition</li>
<li>Device protocol events which are related to communication errors</li>
<li>count of certain readings (e.g. batterie) with their condition - <a href="HMinfoattr">attribut controlled</a></li>
<li>count of error condition in readings (e.g. overheat, motorError) - <a href="HMinfoattr">attribut controlled</a></li>
<br>
It also allows some HM wide commands such
as store all collected register settings.<br><br>
Commands will be executed on all HM entities of the installation.
If applicable and evident execution is restricted to related entities.
This means that rssi is executed only on devices, never channels since
they never have support rssi values.<br><br>
<a name="HMinfoFilter"><b>Filter</b></a>
<ul> can be applied as following:<br><br>
<code>set &lt;name&gt; &lt;cmd&gt; &lt;filter&gt; [&lt;param&gt;]</code><br>
whereby filter has two segments, typefilter and name filter<br>
[-dcasev] [-f &lt;filter&gt;]<br><br>
filter for <b>types</b> <br>
<ul>
<li>d - device :include devices</li>
<li>c - channels :include channels</li>
<li>v - virtual :supress fhem virtual</li>
<li>p - physical :supress physical</li>
<li>a - aktor :supress actor</li>
<li>s - sensor :supress sensor</li>
<li>e - empty :include results even if requested fields are empty</li>
</ul>
and/or a filter for <b>names</b>:<br>
<ul>
<li>-f - filter :regexp to filter entity names </li>
</ul>
Example:<br>
<ul><code>
set hm param -d -f dim state # display param 'state' for all devices whos name contains dim<br>
set hm param -c -f ^dimUG$ peerList # display param 'peerList' for all channels whos name is dimUG<br>
set hm param -dcv expert # get attribut expert for all channels,devices or virtuals<br>
</code></ul>
</ul>
<br>
<a name="HMinfodefine"><b>Define</b></a>
<ul>
<code>define &lt;name&gt; HMinfo</code><br>
Just one entity needs to be defines, no parameter are necessary.<br>
</ul>
<br>
<a name="HMinfoset"><b>Set</b></a>
<ul>
even though the commands are more a get funktion they are implemented
as set to allow simple web interface usage<br>
<ul>
<li><a name="#HMinfoupdate">update</a><br>
updates HM status counter.
</li>
<li><a name="#HMinfomodels">models</a><br>
list all HM models that are supported in FHEM
</li>
<li><a name="#HMinfoparam">param</a> <a href="HMinfoFilter">[filter]</a> &lt;name&gt; &lt;name&gt;...<br>
returns a table parameter values (attribute, readings,...)
for all entities as a table
</li>
<li><a name="#HMinfopeerXref">peerXref</a> <a href="HMinfoFilter">[filter]</a><br>
provides a cross-reference on peerings, a kind of who-with-who summary over HM
</li>
<li><a name="#HMinforegister">register</a> <a href="HMinfoFilter">[filter]</a><br>
provides a tableview of register of an entity
</li>
<li><a name="#HMinfoconfigCheck">configCheck</a> <a href="HMinfoFilter">[filter]</a><br>
performs a consistancy check of HM settings. It includes regCheck and peerCheck
</li>
<li><a name="#HMinfopeerCheck">peerCheck</a> <a href="HMinfoFilter">[filter]</a><br>
performs a consistancy check on peers. If a peer is set in one channel
this funktion will search wether the peer also exist on the opposit side.
</li>
<li><a name="#HMinforegCheck">regCheck</a> <a href="HMinfoFilter">[filter]</a><br>
performs a consistancy check on register readings for completeness
</li>
<li><a name="#HMinfoautoReadReg">autoReadReg</a> <a href="HMinfoFilter">[filter]</a><br>
schedules a read of the configuration for the CUL_HM devices with attribut autoReadReg set to 1 or higher.
</li>
<li><a name="#HMinfoclear">clear [Protocol|Readings|Rssi]</a> <a href="HMinfoFilter">[filter]</a><br>
executes a set clear ... on all HM entities<br>
<ul>
<li>Protocol relates to set clear msgEvents</li>
<li>Readings relates to set clear readings</li>
<li>Rssi clears all rssi counters </li>
</ul>
</li>
<li><a name="#HMinfocpRegs">cpRegs &lt;src:peer&gt; &lt;dst:peer&gt; </a><br>
allows to copy register, setting and behavior of a channel to
another or for peers from the same or different channels. Copy therefore is allowed
intra/inter device and intra/inter channel.
<ul>
<li>src:peer is the source entity. Peer needs to be given if a peer behabior beeds to be copied </li>
<li>dst:peer is the destination entity.</li>
</ul>
Examples are
<code>
set hm cpRegs blindR blindL # will copy all general register (list 1)for this channel from the blindR to the blindL entity.
This includes items like drive times. It does not include peers related register (list 3/4) <br>
set hm cpRegs blindR:Btn1 blindL:Btn2 # copy behavior of Btn1/blindR relation to Btn2/blindL<br>
set hm cpRegs blindR:Btn1 blindR:Btn2 # copy behavior of Btn1/blindR relation to Btn2/blindR, i.e. inside the same Actor<br>
</code>
<br>
Restrictions:<br>
cpRegs will not add any peers or read from the devices. It is up to the user to read register in advance<br>
cpRegs is only allowed between identical models<br>
peerings of devices must exist. cpRegs will terminate if peers cannot be identified<br>
cpRegs estimates that all readings are up-to-date. It is up to the user to ensure and check data consistancy. <br>
<br>
</li>
<li><a name="#HMinfosaveConfig">saveConfig</a> <a href="HMinfoFilter">[filter]</a><br>
performs a save for all HM register setting and peers. See <a href="#CUL_HMsaveConfig">CUL_HM saveConfig</a>.
</li>
<li><a name="#HMinfotemplateDef">templateDef &lt;name&gt; &lt;param&gt; &lt;desc&gt; &lt;reg1:val1&gt; [&lt;reg2:val2&gt;] ...</a><br>
define a template.<br>
<b>param</b> gives the names of parameter necesary to execute the template. It is template dependant
and coule be an onTime or brightnesslevel. a list of parameter needs to be separated with colon<br>
param1:param2:param3<br>
if del is given as parameter the template is removed<br>
<b>desc</b> shall give a description of the template<br>
<b>reg:val</b> is the registername to be written and the value it needs to be set to.<br>
In case the register is from link set and can destinguist between long and short it is necessary to leave the
leading sh or lg off. <br>
if parameter are used it is necessary to enter p. as value with p0 first, p1 second parameter
Examples <br>
<code>
set hm templateDef SwOnCond level:cond "my description" CtValLo:p0 CtDlyOn:p1 CtOn:geLo<br>
</code>
<br>
</li>
<li><a name="#HMinfotemplateList">templateList [&lt;name&gt;]</a><br>
list defined templates. If no name is given all templates will be listed<br>
</li>
<li><a name="#HMinfotemplateChk">templateChk <a href="HMinfoFilter">[filter]</a> &lt;template&gt; &lt;peer:[long|short]&gt; [&lt;param1&gt; ...]</a><br>
verifies if the register-readings comply to the template <br>
Parameter are identical to <a href="#HMinfotemplateSet">templateSet</a><br>
The procedure will check if the register values match the ones provided by the template<br>
If no peer is necessary use <B>none</B> to skip this entry<br>
Examples <br>
<code>
set hm templateChk -f RolloNord BlStopUpLg none 1 2 # verify RolloNord, no peer, parameter 1 and 2 given<br>
set hm templateChk -f RolloNord BlStopUpLg peerName:long # verify RolloNord peerName, long match template?<br>
set hm templateChk -f RolloNord BlStopUpLg peerName # verify RolloNord peerName, long and short match template?<br>
set hm templateChk -f RolloNord BlStopUpLg peerName:all # verify RolloNord peerName, long and short match template?<br>
set hm templateChk -f RolloNord BlStopUpLg all:long # verify RolloNord all peers,long match template?<br>
set hm templateChk -f RolloNord BlStopUpLg all # verify RolloNord all peers,long and short match template?<br>
set hm templateChk -f Rollo.* BlStopUpLg all # verify each Rollo* all peers,long and short match template?<br>
set hm templateChk BlStopUpLg # verify each entities against this template<br>
</code>
<br>
</li>
<li><a name="#HMinfotemplateSet">templateSet &lt;entity&gt; &lt;template&gt; &lt;peer:[long|short]&gt; [&lt;param1&gt; ...]</a><br>
sets a bunch of register accroding to a given template. Parameter may be added depending on
the template setup. <br>
templateSet will collect and accumulate all changes. Finally the results are written streamlined.
<li>entity: peer is the source entity. Peer needs to be given if a peer behabior beeds to be copied <\li>
<li>template: one of the programmed template<\li>
<li>peer: [long|short]:if necessary a peer needs to be given. If no peer is used enter '0'.
with a peer it should be given whether it is for long or short keypress
<\li>
<li>param: number and meaning of parameter depends on the given template<\li>
Examples could be (templates not provided, just theoretical)<br>
set hm templateSet Licht1 staircase FB1:short 20 <br>
set hm templateSet Licht1 staircase FB1:long 100 <br>
<br>
Restrictions:<br>
User must ensure to read configuration prior to execution.<br>
templateSet may not setup a complete register block but only a part if it. This is up to template design.<br>
<br>
</li>
</ul>
</ul>
<br>
<a name="HMinfoget"></a>
<b>Get</b>
<ul> N/A </ul>
<br><br>
<a name="HMinfoattr"><b>Attributes</b></a>
<ul>
<li><a name="#HMinfosumStatus">sumStatus</a><br>
Warnings: list of readings that shall be screend and counted based on current presence.
I.e. counter is the number of entities with this reading and the same value.
Readings to be searched are separated by comma. <br>
Example: <br>
<code>
attr hm sumStatus battery,sabotageError<br>
</code>
will cause a reading like<br>
W_sum_batterie ok:5 low:3<br>
W_sum_sabotageError on:1<br>
<br>
Note: counter with '0' value will not be reported. HMinfo will find all present values autonomously<br>
Setting is meant to give user a fast overview of parameter that are expected to be system critical<br>
</li>
<li><a name="#HMinfosumERROR">sumERROR</a>
Similar to sumStatus but with a focus on error conditions in the system.
Here user can add reading<b>values</b> that are <b>not displayed</b>. I.e. the value is the
good-condition that will not be counted.<br>
This way user must not know all error values but it is sufficient to supress known non-ciritical ones.
Example: <br>
<code>
attr hm sumERROR battery:ok,sabotageError:off,overheat:off,Activity:alive:unknown<br>
</code>
will cause a reading like<br>
ERR_batterie low:3<br>
ERR_sabotageError on:1<br>
ERR_overheat on:3<br>
ERR_Activity dead:5<br>
</li>
</ul>
<br>
<a name="HMinfovariables"><b>Variables</b></a>
<ul>
<li><b>I_autoReadPend:</b> Info:list of entities which are queued to retrieve config and status.
This is typically scheduled thru autoReadReg</li>
<li><b>ERR___rssiCrit:</b> Error:list of devices with RSSI reading n min level </li>
<li><b>W_unConfRegs:</b> Warning:list of entities with unconfirmed register changes. Execute getConfig to clear this.</li>
<li><b>I_rssiMinLevel:</b> Info:counts of rssi min readings per device, clustered in blocks</li>
<li><b>ERR__protocol:</b> Error:count of non-recoverable protocol events per device.
Those events are NACK, IOerr, ResendFail, CmdDel, CmdPend.<br>
Coutned are the number of device with those events, not the number of events!</li>
<li><b>ERR__protoNames:</b> Error:name-list of devices with non-recoverable protocol events</li>
<li><b>I_HM_IOdevices:</b> Info:list of IO devices used by CUL_HM entities</li>
<li><b>I_actTotal:</b> Info:action detector state, count of devices with ceratin states</li>
<li><b>ERRactNames:</b> Error:names of devices that are not alive according to ActionDetector</li>
<li><b>C_sumDefined:</b> Count:defined entities in CUL_HM. Entites might be count as
device AND channel if channel funtion is covered by the device itself. Similar to virtual</li>
<li><b>ERR_&lt;reading&gt;:</b> Error:count of readings as defined in attribut
<a href="#HMinfosumERROR">sumERROR</a>
that do not match the good-content. </li>
<li><b>ERR_names:</b> Error:name-list of entities that are counted in any ERR_&lt;reading&gt;
W_sum_&lt;reading&gt;: count of readings as defined in attribut
<a href="#HMinfosumStatus">sumStatus</a>. </li>
Example:<br>
<li><code>
ERR___rssiCrit LightKittchen,WindowDoor,Remote12
ERR__protocol NACK:2 ResendFail:5 CmdDel:2 CmdPend:1
ERR__protoNames LightKittchen,WindowDoor,Remote12,Ligth1,Light5
ERR_battery: low:2;
ERR_names: remote1,buttonClara,
I_rssiMinLevel 99&gt;:3 80&lt;:0 60&lt;:7 59&lt;:4
W_sum_battery: ok:5;low:2;
W_sum_overheat: off:7;
C_sumDefined: entities:23 device:11 channel:16 virtual:5;
</code></li>
</ul>
</ul>
=end html
=cut