From c467ba303f267838c67447f999d9998c2ed7a05c Mon Sep 17 00:00:00 2001
From: martinp876 <>
Date: Sat, 5 Jan 2019 23:21:58 +0000
Subject: [PATCH] 10_CUL_HM:introduce modelForce, correct message repeat for
burst devices
git-svn-id: https://svn.fhem.de/fhem/trunk@18153 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/FHEM/10_CUL_HM.pm | 219 ++++++++++++++++++++++++++++++-----------
1 file changed, 160 insertions(+), 59 deletions(-)
diff --git a/fhem/FHEM/10_CUL_HM.pm b/fhem/FHEM/10_CUL_HM.pm
index 6f8a14e4f..7e6a31686 100755
--- a/fhem/FHEM/10_CUL_HM.pm
+++ b/fhem/FHEM/10_CUL_HM.pm
@@ -154,6 +154,11 @@ my $evtDly = 0; # ugly switch to delay set readings if in parser - actually n
sub CUL_HM_Initialize($) {
my ($hash) = @_;
+ my @modellist;
+ foreach my $model (keys %{$culHmModel}){
+ push @modellist,$culHmModel->{$model}{name};
+ }
+
$hash->{Match} = "^A....................";
$hash->{DefFn} = "CUL_HM_Define";
$hash->{UndefFn} = "CUL_HM_Undef";
@@ -168,7 +173,7 @@ sub CUL_HM_Initialize($) {
."IODev IOList IOgrp "
."rssiLog:1,0 " # enable writing RSSI to Readings (device only)
."actCycle " # also for action detector
- ."hmKey hmKey2 hmKey3 "
+ ."hmKey hmKey2 hmKey3 "
."readingOnDead:multiple,noChange,state,periodValues,periodString,channels "
;
$hash->{Attr}{devPhy} = # -- physical device only attributes
@@ -176,9 +181,12 @@ sub CUL_HM_Initialize($) {
."actStatus "
."autoReadReg:0_off,1_restart,2_pon-restart,3_onChange,4_reqStatus,5_readMissing,8_stateOnly "
."burstAccess:0_off,1_auto "
- ."msgRepeat "
+ ."msgRepeat "
."hmProtocolEvents:0_off,1_dump,2_dumpFull,3_dumpTrigger "
."aesKey:5,4,3,2,1,0 "
+ ."modelForce:".join(",", sort @modellist)." "
+ .".mId "
+ ."subType:" .join(",",CUL_HM_noDup(map { $culHmModel->{$_}{st} } keys %{$culHmModel}))." "
;
$hash->{Attr}{chn} = "repPeers " # -- channel only attributes
."peerIDs "
@@ -193,6 +201,7 @@ sub CUL_HM_Initialize($) {
."readOnly:0,1 "
."actAutoTry:0_off,1_on "
."aesCommReq:1,0 " # IO will request AES if
+ ."model "
;
$hash->{AttrList} = $hash->{Attr}{glb}
.$hash->{Attr}{dev}
@@ -202,13 +211,9 @@ sub CUL_HM_Initialize($) {
;
CUL_HM_initRegHash();
- my @modellist;
- foreach my $model (keys %{$culHmModel}){
- push @modellist,$culHmModel->{$model}{name};
- }
- $hash->{AttrList} .= " model:" .join(",", sort @modellist);
- $hash->{AttrList} .= " subType:".join(",",
- CUL_HM_noDup(map { $culHmModel->{$_}{st} } keys %{$culHmModel}));
+# $hash->{AttrList} .= " model:" .join(",", sort @modellist);
+# $hash->{AttrList} .= " subType:".join(",",
+# CUL_HM_noDup(map { $culHmModel->{$_}{st} } keys %{$culHmModel}));
$hash->{prot}{rspPend} = 0;#count Pending responses
my @statQArr = ();
@@ -236,6 +241,7 @@ sub CUL_HM_Initialize($) {
}
sub CUL_HM_updateConfig($){
+ my $type = shift;
# this routine is called 5 sec after the last define of a restart
# this gives FHEM sufficient time to fill in attributes
# it will also be called after each manual definition
@@ -245,7 +251,17 @@ sub CUL_HM_updateConfig($){
InternalTimer(1,"CUL_HM_updateConfig", "updateConfig", 0);#start asap once FHEM is operational
return;
}
-
+ if ($type eq "startUp"){# only once after startup
+ foreach(devspec2array("TYPE=CUL_HM:FILTER=DEF=......:FILTER=subType!=virtual")){
+ if ($attr{$_}{".mId"} && $culHmModel->{$attr{$_}{".mId"}}){ #if mId is available set model to its original value -at least temporarliy
+ $attr{$_}{model} = $culHmModel->{$attr{$_}{name}};
+ }
+ else{#if mId is not available use attr model and assign it.
+ $attr{$_}{".mId"} = CUL_HM_getmIdFromModel($attr{$_}{model});
+ }
+ CUL_HM_updtDeviceModel($_,AttrVal($_,"modelForce",AttrVal($_,"model","")));
+ }
+ }
foreach my $name (@{$modules{CUL_HM}{helper}{updtCfgLst}}){
my $hash = $defs{$name};
next if (!$hash->{DEF}); # likely renamed
@@ -776,6 +792,7 @@ sub CUL_HM_Attr(@) {#################################
return;
}
elsif($attrName eq "model" && $hash->{helper}{role}{dev}){
+ return "$attrName must not be changed by User. \nUse modelForce instead" if ($init_done);
delete $hash->{helper}{rxType}; # needs new calculation
delete $hash->{helper}{mId};
if ($attrVal eq "CCU-FHEM"){
@@ -788,7 +805,27 @@ sub CUL_HM_Attr(@) {#################################
}
$attr{$name}{$attrName} = $attrVal if ($cmd eq "set");
}
+ elsif($attrName eq "modelForce"){
+ if ($init_done){# while init allow anything. Correct with CUL_HM_updateConfig after init_done
+ if ($cmd eq "set"){
+ if (!defined $attr{$name}{".mId"} && defined $attr{$name}{model}){ # set .mId in case it is missing
+ $attr{$name}{".mId"} = CUL_HM_getmIdFromModel($attr{$name}{model});
+ }
+
+ return "invalid model name:$attrVal. Please check options" if (!CUL_HM_getmIdFromModel($attrVal));
+ CUL_HM_updtDeviceModel($name,$attrVal);
+ }
+ else{
+ $attr{$name}{model} = $culHmModel->{$attr{$name}{".mId"}}{name};# return to old model name
+ CUL_HM_updtDeviceModel($name,$attr{$name}{model});
+ }
+ }
+ }
+ elsif($attrName eq ".mId"){
+ return "$attrName must not be changed by User. \nUse modelForce instead" if ($init_done);
+ }
elsif($attrName eq "subType"){
+ return "$attrName must not be changed by User. \nUse modelForce instead" if ($init_done);
$updtReq = 1;
}
elsif($attrName eq "aesCommReq" ){
@@ -1151,7 +1188,7 @@ sub CUL_HM_Parse($$) {#########################################################
my $sname = "HM_$mh{src}";
Log3 undef, 2, "CUL_HM Unknown device $sname is now defined";
DoTrigger("global","UNDEFINED $sname CUL_HM $mh{src}");
- $mh{devH} = CUL_HM_id2Hash($mh{src}); #sourcehash - changed to channel entity
+ $mh{devH} = CUL_HM_id2Hash($mh{src}); #sourcehash - changed to channel entity
$mh{devH}->{IODev} = $iohash;
$mh{devH}->{helper}{io}{nextSend} = $mh{rectm}+0.09 if(!defined($mh{devH}->{helper}{io}{nextSend}));# io couldn't set
}
@@ -4076,15 +4113,15 @@ sub CUL_HM_Set($@) {#+++++++++++++++++ set command+++++++++++++++++++++++++++++
if( $culHmFunctSets->{$fkt} && $roleC){foreach(keys %{$culHmFunctSets->{$fkt}} ){push @arr1,"$_:".${$culHmFunctSets->{$fkt}}{$_} }};
@arr1 = CUL_HM_noDup(@arr1);
foreach(@arr1){
- my ($cmd,$val) = split(":",$_,2);
+ my ($cmdS,$val) = split(":",$_,2);
if (!$val){ # no agruments possible
- $_ = "$cmd:noArg";
+ $_ = "$cmdS:noArg";
}
elsif($val !~ m/^\[.*\]$/ ||
$val =~ m/\[.*\[/ ||
$val =~ m/(\<|\>)]/
){
- $_ = $cmd;
+ $_ = $cmdS;
}
else{
$val =~ s/(\[|\])//g;
@@ -4096,7 +4133,7 @@ sub CUL_HM_Set($@) {#+++++++++++++++++ set command+++++++++++++++++++++++++++++
$_ = join(",",@list);
}
}
- $_ = "$cmd:".join(",",@vArr);
+ $_ = "$cmdS:".join(",",@vArr);
}
}
@arr1 = ("--") if (!scalar @arr1);
@@ -4495,7 +4532,7 @@ sub CUL_HM_Set($@) {#+++++++++++++++++ set command+++++++++++++++++++++++++++++
$state = "";
my $sn = ReadingsVal($name,"D-serialNr","");
return "serial number unknown" if (! $sn);
- CUL_HM_PushCmdStack($hash,'++8401'.$id.'000000010A'.uc(unpack('H*', $sn)));
+ CUL_HM_PushCmdStack($hash,'++8401'.$id.$dst.'010A'.uc(unpack('H*', $sn)));
}
elsif($cmd eq "getConfig") { ################################################
CUL_HM_unQEntity($name,"qReqConf");
@@ -6319,7 +6356,7 @@ sub CUL_HM_Set($@) {#+++++++++++++++++ set command+++++++++++++++++++++++++++++
my $ret = HMinfo_SetFn($defs{hm},$hm,"templateSet",$name,$tpl,"$tPeer$tTyp",@par);
return $ret;
}
- elsif($cmd =~ m/tplPara(..)(.)_.*/) { #####################################
+ elsif($cmd =~ m/tplPara(..)(.)_.*/) { #######################################
$state = "";
my ($tNo,$pNo) = ($1,$2);
my ($hm) = devspec2array("TYPE=HMinfo");
@@ -6484,21 +6521,24 @@ sub CUL_HM_weather(@) {#periodically send weather data
CUL_HM_SndCmd($hash,"++8470".$ioId."00000000".$hash->{helper}{weather});
InternalTimer(gettimeofday()+150,"CUL_HM_weather","weather:$name",0);
}
+
sub CUL_HM_infoUpdtDevData($$$) {#autoread config
my($name,$hash,$p) = @_;
my($fw1,$fw2,$mId,$serNo,$stc,$devInfo) = unpack('A1A1A4A20A2A*', $p);
- my $md = $culHmModel->{$mId}{name} ? $culHmModel->{$mId}{name}:"unknown";
+ my $md = $culHmModel->{$mId}{name} ? $culHmModel->{$mId}{name}:"unknown";# original model
my $serial = pack('H*',$serNo);
my $fw = sprintf("%d.%d", hex($fw1),hex($fw2));
- $attr{$name}{model} = $md;
+ $attr{$name}{".mId"} = $mId;
+ $attr{$name}{model} = AttrVal($name,"modelForce",$md); #may be overwritten by modelForce
$attr{$name}{subType} = $culHmModel->{$mId}{st};
$attr{$name}{serialNr} = $serial; # to be removed from attributes
$attr{$name}{firmware} = $fw; # to be removed from attributes
-# $attr{$name}{".devInfo"} = $devInfo; # to be removed from attributes
-# $attr{$name}{".stc"} = $stc; # to be removed from attributes
- CUL_HM_configUpdate($name) if(ReadingsVal($name,"D-firmware","") ne $fw
+
+ CUL_HM_updtDeviceModel($name,$attr{$name}{model});
+
+ CUL_HM_configUpdate($name) if(ReadingsVal($name,"D-firmware","") ne $fw # force read register
||ReadingsVal($name,"D-serialNr","") ne $serial
||ReadingsVal($name,".D-devInfo","") ne $devInfo
||ReadingsVal($name,".D-stc" ,"") ne $stc
@@ -6507,33 +6547,84 @@ sub CUL_HM_infoUpdtDevData($$$) {#autoread config
"D-serialNr:$serial",
".D-devInfo:$devInfo",
".D-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
+# my @chanTypesList = split(',',$culHmModel->{$mId}{chn});
+# 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 $chnName = $name."_".$chnTpName.(($chnStart == $chnEnd)?
+# '':'_'.sprintf("%02d",$chnNoTyp));
+#
+# CommandDefine(undef,$chnName.' CUL_HM '.$chnId);
+# $attr{CUL_HM_id2Name($chnId)}{model} = $attr{$name}{model};
+# }
+# $attr{CUL_HM_id2Name($chnId)}{model} = $attr{$name}{model};
+# $chnNoTyp++;
+# }
+# }
+# if ($culHmModel->{$mId}{cyc}){
+# CUL_HM_ActAdd($hash->{DEF},AttrVal($name,"actCycle",
+# $culHmModel->{$mId}{cyc}));
+# }
+}
+sub CUL_HM_updtDeviceModel($$) {#change the model for a device - obey overwrite modelForce
+ my($name,$model) = @_;
+ my $hash = $defs{$name};
+ $attr{$name}{model} = $model;
delete $hash->{helper}{rxType};
+ delete $hash->{helper}{mId};
CUL_HM_getRxType($hash); #will update rxType
- $mId = CUL_HM_getMId($hash);# set helper valiable and use result
+ my $mId = CUL_HM_getMId($hash);# set helper valiable and use result
+ $attr{$name}{subType} = $culHmModel->{$mId}{st};
# autocreate undefined channels
- my @chanTypesList = split(',',$culHmModel->{$mId}{chn});
- 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 $chnName = $name."_".$chnTpName.(($chnStart == $chnEnd)?
- '':'_'.sprintf("%02d",$chnNoTyp));
-
- CommandDefine(undef,$chnName.' CUL_HM '.$chnId);
- $attr{CUL_HM_id2Name($chnId)}{model} = $md;
- }
- $attr{CUL_HM_id2Name($chnId)}{model} = $md;
- $chnNoTyp++;
- }
+ my %chanExist;
+ %chanExist = map { $_ => 0 } CUL_HM_getAssChnIds($name);
+
+ if ($attr{$name}{subType} eq "virtual"){# du not apply all possible channels for virtual
+ $attr{CUL_HM_id2Name($_)}{model} = $model foreach(keys %chanExist);
}
- if ($culHmModel->{$mId}{cyc}){
- CUL_HM_ActAdd($hash->{DEF},AttrVal($name,"actCycle",
- $culHmModel->{$mId}{cyc}));
+ else{
+ my @chanTypesList = split(',',$culHmModel->{$mId}{chn});
+ foreach my $chantype (@chanTypesList){# check all regulat channels
+ 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}){# not existing by now - create
+ my $chnName = $name."_".$chnTpName.(($chnStart == $chnEnd)?''
+ :'_'.sprintf("%02d",$chnNoTyp));
+
+ CommandDefine(undef,$chnName.' CUL_HM '.$chnId);
+ Log3 $name,3,"CUL_HM_update: $name add channel ID: $chnId name: $chnName";
+ }
+ $attr{CUL_HM_id2Name($chnId)}{model} = $model;
+ $chanExist{$chnId} = 1; # mark this channel as required
+ $chnNoTyp++;
+ }
+ }
+ if (scalar @chanTypesList == 0){# we won't delete channel 01. This may be on purpose
+ $chanExist{$defs{$name}{DEF}."01"} = 1;
+ my $cn01 = CUL_HM_id2Name($defs{$name}{DEF}."01");
+ $attr{$cn01}{model} = $model if (defined $attr{$cn01});
+ }
+ foreach(keys %chanExist){
+ next if ($chanExist{$_} == 1);
+ CommandDelete(undef,CUL_HM_id2Name($_));
+ Log3 $name,3,"CUL_HM_update: $name delete channel name: $_";
+ }
+ CUL_HM_ActAdd($hash->{DEF},AttrVal($name,"actCycle", $culHmModel->{$mId}{cyc}))if ($culHmModel->{$mId}{cyc});
+ CUL_HM_queueUpdtCfg($name);
}
}
+
sub CUL_HM_getConfig($){
my $hash = shift;
my $flag = 'A0';
@@ -6829,7 +6920,7 @@ sub CUL_HM_responseSetup($$) {#store all we need to handle the response
if (($mFlg & 0x20) && ($dst ne '000000')){#msg wants ack
my $rss = $hash->{helper}{prt}{wuReSent}
? $hash->{helper}{prt}{wuReSent}
- :1;#resend count - may need preloaded for WU device
+ :1;#resend counter start value - may need preloaded for WU device
if ($mTp eq '01' && $sTp) {
if ($sTp eq "03"){ #PeerList-----------
@@ -7205,10 +7296,9 @@ sub CUL_HM_respPendTout($) {
}
else{# manage retries
$pHash->{rspWait}{reSent}++;
-
CUL_HM_eventP($hash,"Resnd");
Log3 $name,4,"CUL_HM_Resend: $name nr ".$pHash->{rspWait}{reSent};
- if ($hash->{protCondBurst}&&$hash->{protCondBurst} eq "on" ){
+ if ($hash->{protCondBurst} && $hash->{protCondBurst} eq "on" ){
#timeout while conditional burst was active. try re-wakeup
my $addr = CUL_HM_IoId($hash);
$pHash->{rspWaitSec}{$_} = $pHash->{rspWait}{$_}
@@ -7232,7 +7322,7 @@ sub CUL_HM_respPendTout($) {
CUL_HM_protState($hash,"CMDs_pending");
$pHash->{wuReSent} = $wuReSent;# restore'invalid' count after general delete
}
- else{# normal device resend
+ else{# normal/burst device resend
if ($rxt & 0x02){# type = burst - need to set burst-Bit for retry
if ($pHash->{mmcA}){#fillback multi-message command
unshift @{$hash->{cmdStack}},$_ foreach (reverse@{$pHash->{mmcA}});
@@ -7242,7 +7332,9 @@ sub CUL_HM_respPendTout($) {
my $cmd = shift @{$hash->{cmdStack}};
$cmd = sprintf("As%02X01%s", length($cmd)/2, substr($cmd,2));
$pHash->{rspWait}{cmd} = $cmd;
+ my $rss = $pHash->{rspWait}{reSent}; # rescue repeat counter (will be overwritten in responseSetup)
CUL_HM_responseSetup($hash,$cmd);
+ $pHash->{rspWait}{reSent} = $rss; # restore repeat counter
}
my ($pre,$tp,$tail) = unpack 'A6A2A*',$pHash->{rspWait}{cmd};
@@ -7603,22 +7695,20 @@ sub CUL_HM_getMId($) {#in: hash(chn or dev) out:model key (key for %culHmModel)
my $hash = shift;
$hash = CUL_HM_getDeviceHash($hash);
return "" if (!$hash->{NAME});
- if (!defined $hash->{helper}{mId} || !$hash->{helper}{mId}){
- my $model = AttrVal($hash->{NAME}, "model", "");
- $hash->{helper}{mId} = "";
- foreach my $mIdKey(keys%{$culHmModel}){
- next if (!$culHmModel->{$mIdKey}{name} ||
- $culHmModel->{$mIdKey}{name} ne $model);
- $hash->{helper}{mId} = $mIdKey;
- #--- mId is updated - now update the reglist
- foreach(CUL_HM_getAssChnNames($hash->{NAME})){
- $defs{$_}{helper}{regLst} = CUL_HM_getChnList($defs{$_});
- }
- last;
- }
+ if (!defined $hash->{helper}{mId} || !$hash->{helper}{mId}){# need to search
+ $hash->{helper}{mId} = CUL_HM_getmIdFromModel(AttrVal($hash->{NAME}, "model", ""));
+ $hash->{helper}{subType} = $hash->{helper}{mId} ? $culHmModel->{$hash->{helper}{mId}}{st}:"";
+ #--- mId is updated - now update the reglist
+ $defs{$_}{helper}{regLst} = CUL_HM_getChnList($defs{$_}) foreach(CUL_HM_getAssChnNames($hash->{NAME}));
}
return $hash->{helper}{mId};
}
+sub CUL_HM_getmIdFromModel($){# enter model and receive the corresponding ID
+ my $model = shift;
+ foreach (keys%{$culHmModel}){
+ return $_ if ($culHmModel->{$_}{name} && $culHmModel->{$_}{name} eq $model);
+ }
+}
sub CUL_HM_getRxType($) { #in:hash(chn or dev) out:binary coded Rx type
# Will store result in device helper
my ($hash) = @_;
@@ -11094,6 +11184,11 @@ sub CUL_HM_tempListTmpl(@) { ##################################################
attr myChannel levelRange 10,80
+