From 3c5872e59309b4b6e056dd40738c66c5337a5ced Mon Sep 17 00:00:00 2001 From: zap <> Date: Fri, 29 Mar 2024 15:15:33 +0000 Subject: [PATCH] HMCCU: bugfixes git-svn-id: https://svn.fhem.de/fhem/trunk@28721 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/88_HMCCU.pm | 337 ++++++++++++++++++++++------------------- fhem/FHEM/HMCCUConf.pm | 82 ++++++---- 2 files changed, 235 insertions(+), 184 deletions(-) diff --git a/fhem/FHEM/88_HMCCU.pm b/fhem/FHEM/88_HMCCU.pm index 94154802c..ffb763373 100755 --- a/fhem/FHEM/88_HMCCU.pm +++ b/fhem/FHEM/88_HMCCU.pm @@ -307,7 +307,6 @@ sub HMCCU_UpdateDevice ($$); sub HMCCU_UpdateDeviceRoles ($$;$$); sub HMCCU_UpdateDeviceTable ($$); sub HMCCU_UpdateRoleCommands ($$); -sub HMCCU_UpdateAdditionalCommands ($$;$$); # Handle datapoints sub HMCCU_GetSCDatapoints ($); @@ -483,9 +482,10 @@ sub HMCCU_Define ($$$) $hash->{hmccu}{postInit} = 0; # Check if authentication is active - my ($erruser, $encuser) = getKeyValue ($name.'_username'); - my ($errpass, $encpass) = getKeyValue ($name.'_password'); - $hash->{authentication} = (defined($encuser) && defined($encpass)) ? 'on' : 'off'; + my ($username, $password) = HMCCU_GetCredentials ($hash); + $hash->{authentication} = $username ne '' && $password ne '' ? 'on' : 'off'; + ($username, $password) = HMCCU_GetCredentials ($hash, '_json_'); + $hash->{json} = $username ne '' && $password ne '' ? 'on' : 'off'; HMCCU_Log ($hash, 1, "Initialized version $HMCCU_VERSION"); @@ -1329,10 +1329,8 @@ sub HMCCU_Undef ($$) } # Delete CCU credentials - my ($erruser, $encuser) = getKeyValue ($name.'_username'); - my ($errpass, $encpass) = getKeyValue ($name.'_password'); - setKeyValue ($name."_username", undef) if (!defined($erruser) && defined($encuser)); - setKeyValue ($name."_password", undef) if (!defined($errpass) && defined($encpass)); + HMCCU_SetCredentials ($hash); + HMCCU_SetCredentials ($hash, '_json_'); return undef; } @@ -1345,15 +1343,15 @@ sub HMCCU_Rename ($$) { my ($oldName, $newName); - my ($erruser, $encuser) = getKeyValue ($oldName.'_username'); - my ($errpass, $encpass) = getKeyValue ($oldName.'_password'); - if (!defined($erruser) && defined($encuser)) { - setKeyValue ($newName."_username", $encuser); - setKeyValue ($oldName."_username", undef); + my ($username, $password) = HMCCU_GetCredentials ($oldName); + if ($username ne '' && $password ne '') { + HMCCU_SetCredentials ($oldName); + HMCCU_SetCredentials ($newName, '_', $username, $password); } - if (!defined($errpass) && defined($encpass)) { - setKeyValue ($newName."_password", $encpass); - setKeyValue ($oldName."_password", undef); + ($username, $password) = HMCCU_GetCredentials ($oldName, '_json_'); + if ($username ne '' && $password ne '') { + HMCCU_SetCredentials ($oldName, '_json_'); + HMCCU_SetCredentials ($newName, '_json_', $username, $password); } } @@ -1495,29 +1493,35 @@ sub HMCCU_Set ($@) return HMCCU_SetState ($hash, 'OK'); } elsif ($opt eq 'authentication') { + my $json = 0; + my $credKey = '_'; + my $credInt = 'authentication'; my $username = shift @$a; + if (defined($username) && $username eq 'json') { + $json = 1; + $credKey = '_json_'; + $credInt = 'json'; + $username = shift @$a; + } my $password = shift @$a; - $usage = "set $name $opt username password"; + $usage = "set $name $opt ['json'] username password"; if (!defined($username)) { - setKeyValue ($name."_username", undef); - setKeyValue ($name."_password", undef); - $hash->{authentication} = 'off'; - return 'Credentials for CCU authentication deleted'; + my $err = HMCCU_SetCredentials ($hash, $credKey); + if (!defined($err)) { + $hash->{$credInt} = 'off'; + return 'Credentials for CCU authentication deleted'; + } + else { + return HMCCU_SetError ($hash, "Can't delete credentials. $err"); + } } return HMCCU_SetError ($hash, $usage) if (!defined($password)); - my $encuser = HMCCU_Encrypt ($username); - my $encpass = HMCCU_Encrypt ($password); - return HMCCU_SetError ($hash, 'Encryption of credentials failed') if ($encuser eq '' || $encpass eq ''); - - my $err = setKeyValue ($name."_username", $encuser); - return HMCCU_SetError ($hash, "Can't store credentials. $err") if (defined ($err)); - $err = setKeyValue ($name."_password", $encpass); - return HMCCU_SetError ($hash, "Can't store credentials. $err") if (defined ($err)); + my $err = HMCCU_SetCredentials ($hash, $credKey, $username, $password); + return HMCCU_SetError ($hash, "Can't store credentials. $err") if (defined($err)); - $hash->{authentication} = 'on'; - + $hash->{$credInt} = 'on'; return 'Credentials for CCU authentication stored'; } elsif ($opt eq 'clear') { @@ -2251,22 +2255,35 @@ sub HMCCU_GetReadingName ($$$$$;$$$) my $rn = ''; my @rnlist = (); + # Get reading prefix definitions + $ps = 'DEVICE' if (($c eq '0' && $ps eq 'MASTER') || $c eq 'd'); + my $readingPrefix = HMCCU_GetAttribute ($ioHash, $hash, 'ccuReadingPrefix', ''); + foreach my $pd (split (',', $readingPrefix)) { + my ($rSet, $rPre) = split (':', $pd); + if (exists($prefix{$rSet})) { + $prefix{$rSet} = defined($rPre) && $rPre ne '' ? $rPre : ''; + } + } + my $rpf = exists($prefix{$ps}) ? $prefix{$ps} : ''; + # Add device state reading if (exists($newReadings{$d}) && ($c eq '' || $c eq '0')) { push @rnlist, $newReadings{$d}; } - # Build list of reading name rul + # Build list of reading name rules my @srl = (); my $crn = AttrVal ($name, 'ccureadingname', ''); push @srl, split(';', $crn) if ($crn ne ''); - if (!$hideStandard && - (exists($hash->{hmccu}{control}{chn}) && "$c" eq $hash->{hmccu}{control}{chn}) || - (exists($hash->{hmccu}{state}{chn}) && "$c" eq $hash->{hmccu}{state}{chn}) - ) { - my $role = HMCCU_GetChannelRole ($hash, $c); +# if (!$hideStandard && +# (exists($hash->{hmccu}{control}{chn}) && "$c" eq $hash->{hmccu}{control}{chn}) || +# (exists($hash->{hmccu}{state}{chn}) && "$c" eq $hash->{hmccu}{state}{chn}) +# ) { + if (!$hideStandard) { + my $role = $c ne '' && $c ne 'd' ? HMCCU_GetChannelRole ($hash, $c) : ''; $crn = $role ne '' && exists($HMCCU_READINGS->{$role}) ? $HMCCU_READINGS->{$role} : $HMCCU_READINGS->{DEFAULT}; $crn =~ s/C#\\/$c\\/g; + $crn =~ s/P#/$rpf/g; push @srl, map { $replaceStandard ? $_ =~ s/\+//g : $_ } split(';',$crn); } @@ -2281,17 +2298,6 @@ sub HMCCU_GetReadingName ($$$$$;$$$) $i = HMCCU_GetDeviceInterface ($ioHash, $a); } - # Get reading prefix definitions - $ps = 'DEVICE' if (($c eq '0' && $ps eq 'MASTER') || $c eq 'd'); - my $readingPrefix = HMCCU_GetAttribute ($ioHash, $hash, 'ccuReadingPrefix', ''); - foreach my $pd (split (',', $readingPrefix)) { - my ($rSet, $rPre) = split (':', $pd); - if (exists($prefix{$rSet})) { - $prefix{$rSet} = defined($rPre) && $rPre ne '' ? $rPre : ''; - } - } - my $rpf = exists($prefix{$ps}) ? $prefix{$ps} : ''; - # Format reading name if (!$h) { $rf //= HMCCU_GetAttrReadingFormat ($hash, $ioHash); @@ -4381,6 +4387,16 @@ sub HMCCU_GetEnumValues ($$$$;$$) } } } + elsif (defined($paramDef) && + defined($paramDef->{MIN}) && HMCCU_IsIntNum($paramDef->{MIN}) && + defined($paramDef->{MAX}) && HMCCU_IsIntNum($paramDef->{MAX}) && + $paramDef->{MAX}-$paramDef->{MIN} < 10 + ) { + for (my $i=$paramDef->{MIN}; $i<=$paramDef->{MAX}; $i++) { + $valList{$i} = $i; + $valIndex{$i} = $i; + } + } if (defined($value)) { if ($value eq '#') { @@ -6708,35 +6724,43 @@ sub HMCCU_UpdateRoleCommands ($$) $cmdChn = 'd' if ($ps eq 'D'); - # Allow different datapoint/config parameter names for same command, if name depends on firmware revision of device type my $dptValid = 0; my $dpt = ''; - # Find supported datapoint/config parameter - foreach my $d (split /,/, $dptList) { - next if (!defined($d) || $d eq ''); - if ($combDpt ne '') { - next if (!exists($HMCCU_ROLECMDS->{$role}{$combDpt}{$d})); - $dpt = $HMCCU_ROLECMDS->{$role}{$combDpt}{$d}; - push @combArgs, $d; + my $paramDef = { }; + + if ($dptList ne '*') { + # Allow different datapoint/config parameter names for same command, if name depends on firmware revision of device type + # Find supported datapoint/config parameter + foreach my $d (split /,/, $dptList) { + next if (!defined($d) || $d eq ''); + if ($combDpt ne '') { + next if (!exists($HMCCU_ROLECMDS->{$role}{$combDpt}{$d})); + $dpt = $HMCCU_ROLECMDS->{$role}{$combDpt}{$d}; + push @combArgs, $d; + } + else { + $dpt = $d; + } + if (HMCCU_IsValidParameter ($clHash, "$addr:$cmdChn", $psName, $dpt, $parAccess)) { + $dptValid = 1; + last; + } } - else { - $dpt = $d; + if (!$dptValid) { + HMCCU_Log ($clHash, 4, "HMCCUConf: Unsupported parameter $addr:$cmdChn $psName $dpt $parAccess. Ignoring sub command $subCmd in role $role for $devType device $devName"); + next URCSUB; } - if (HMCCU_IsValidParameter ($clHash, "$addr:$cmdChn", $psName, $dpt, $parAccess)) { - $dptValid = 1; - last; + + $paramDef = HMCCU_GetParamDef ($ioHash, "$addr:$cmdChn", $psName, $dpt); + if (!defined($paramDef)) { + HMCCU_Log ($ioHash, 4, "HMCCUConf: Can't get definition of datapoint $addr:$cmdChn.$dpt. Ignoring command $cmd in role $role for $devType device $devName"); + next URCCMD; } } - if (!$dptValid) { - HMCCU_Log ($clHash, 4, "HMCCUConf: Unsupported parameter $addr:$cmdChn $psName $dpt $parAccess. Ignoring sub command $subCmd in role $role for $devType device $devName"); - next URCSUB; - } - - my $paramDef = HMCCU_GetParamDef ($ioHash, "$addr:$cmdChn", $psName, $dpt); - if (!defined($paramDef)) { - HMCCU_Log ($ioHash, 4, "HMCCUConf: Can't get definition of datapoint $addr:$cmdChn.$dpt. Ignoring command $cmd in role $role for $devType device $devName"); - next URCCMD; + else { + $dpt = '_any_'; } + $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{scn} = sprintf("%03d", $subCmdNo); $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{type} = $paramDef->{TYPE} // ''; $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{min} = $paramDef->{MIN}; @@ -6753,7 +6777,19 @@ sub HMCCU_UpdateRoleCommands ($$) # Build lookup table my $argList = ''; - my $el = HMCCU_GetEnumValues ($ioHash, $paramDef, $dpt, $role, '#', $pv); + my $el = ''; + + if (defined($pv) && $pv =~ /^[A-Z0-9_]+$/) { + $paramDef = HMCCU_GetParamDef ($ioHash, "$addr:$cmdChn", 'VALUES', $pv); + if (defined($paramDef)) { + $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{min} = $paramDef->{MIN}; + $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{max} = $paramDef->{MAX}; + $el = HMCCU_GetEnumValues ($ioHash, $paramDef, $pv, $role, '#'); + } + } + else { + $el = HMCCU_GetEnumValues ($ioHash, $paramDef, $dpt, $role, '#', $pv); + } if ($el ne '') { my $min; my $max; @@ -6776,6 +6812,9 @@ sub HMCCU_UpdateRoleCommands ($$) $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{min} = $min; $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{max} = $max; } + else { + HMCCU_Log ($clHash, 2, "$cmdType $cmd: Cannot find enum values for parameter $pn, pv = $pv"); + } # Parameter list $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{parname} = $pn; @@ -6833,7 +6872,7 @@ sub HMCCU_UpdateRoleCommands ($$) if ($parTypes[1] == 1 && $parTypes[2] == 0 && $cmdArgList ne '') { # Only one variable argument. Argument belongs to a predefined value list # If values contain blanks, substitute blanks by # and enclose strings in quotes - $cmdDef .= ':'.join(',', map { $_ =~ / / ? '"'.(s/ /#/gr).'"' : $_ } split(',', $cmdArgList)); + $cmdDef .= ':'.join(',', sort map { $_ =~ / / ? '"'.(s/ /#/gr).'"' : $_ } split(',', $cmdArgList)); } elsif ($parTypes[1] == 0 && $parTypes[2] == 0) { $cmdDef .= ':noArg'; @@ -6873,72 +6912,6 @@ sub HMCCU_UpdateRoleCommands ($$) return; } -###################################################################### -# Update additional commands which depend on device state -###################################################################### - -sub HMCCU_UpdateAdditionalCommands ($$;$$) -{ - my ($ioHash, $clHash, $cc, $cd) = @_; - $cc //= ''; - $cd //= ''; - - # No controldatapoint available (read only device) - if ($cd eq '' || $cc eq '') { - HMCCU_Log ($clHash, 4, "No control datapoint. Maybe device is read only or attribute will be set later"); - return; - } - - my $s = exists($clHash->{hmccu}{cmdlist}{set}) && $clHash->{hmccu}{cmdlist}{set} ne '' ? ' ' : ''; - my ($addr, $chn) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); - - # Check if role of control channel is supported by HMCCU - my $role = HMCCU_GetChannelRole ($clHash, $cc); - if ($role ne '' && exists($HMCCU_STATECONTROL->{$role}) && - HMCCU_DetectSCDatapoint ($HMCCU_STATECONTROL->{$role}{C}, $clHash->{ccuif}) eq $cd) { - # Only add toggle command, ignore attribute statevals - my $stVals = $HMCCU_STATECONTROL->{$role}{V} eq '#' ? - HMCCU_GetEnumValues ($ioHash, HMCCU_GetChannelAddr ($clHash, $cc), $HMCCU_STATECONTROL->{$role}{C}, $role, '#') : - $HMCCU_STATECONTROL->{$role}{V}; - my %stateCmds = split (/[:,]/, $stVals); - my @states = keys %stateCmds; - $clHash->{hmccu}{cmdlist}{set} .= $s.'toggle:noArg' if (scalar(@states) > 1); - return; - } - - my $sv = AttrVal ($clHash->{NAME}, 'statevals', ''); - if ($sv ne '') { - my %stateCmds = split (/[:,]/, $sv); - my @states = keys %stateCmds; - - my $paramDef = HMCCU_GetParamDef ($ioHash, "$addr:$cc", 'VALUES', $cd); - if (defined($paramDef)) { - foreach my $cmd (@states) { - $clHash->{hmccu}{roleCmds}{set}{$cmd}{channel} = $cc; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{role} = $role; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcount} = 1; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{syntax} = "V:$cd:".$stateCmds{$cmd}; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{usage} = $cmd; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{partype} = 3; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{args} = $stateCmds{$cmd}; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{min} = $paramDef->{MIN}; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{max} = $paramDef->{MAX}; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{unit} = $paramDef->{UNIT} // ''; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{ps} = 'VALUES'; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{dpt} = $cd; - $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{fnc} = ''; - } - $clHash->{hmccu}{cmdlist}{set} .= $s.join(' ', map { $_ . ':noArg' } @states) - if (scalar(@states) > 0); - $clHash->{hmccu}{cmdlist}{set} .= ' toggle:noArg' - if (scalar(@states) > 1); - } - else { - HMCCU_Log ($clHash, 3, "Can't get definition of datapoint $addr:$cc.$cd. Ignoring commands ".join(',',@states)." for device $clHash->{NAME}"); - } - } -} - ###################################################################### # Execute command related to role # Parameters: @@ -6988,7 +6961,7 @@ sub HMCCU_ExecuteRoleCommand ($@) my @par = (); my $autoscale = 0; - if ($cmd->{ps} ne 'INTERNAL' && !HMCCU_IsValidParameter ($clHash, $chnAddr, $cmd->{ps}, $cmd->{dpt})) { + if ($cmd->{ps} ne 'INTERNAL' && $cmd->{dpt} ne '_any_' && !HMCCU_IsValidParameter ($clHash, $chnAddr, $cmd->{ps}, $cmd->{dpt})) { HMCCU_Trace ($clHash, 2, "Invalid parameter $cmd->{ps}.$cmd->{dpt} for command $command"); return HMCCU_SetError ($clHash, -8, "$cmd->{ps}.$cmd->{dpt}"); } @@ -7671,9 +7644,9 @@ sub HMCCU_DisplayWeekProgram ($$$;$$) my $pn = $programName ne 'all' ? $programName : $w+1; $s .= '
Week Program '.$pn.'
'.$weekDay[$d].' | '; + $s .= '||
'.$weekDay[$d].' | '; foreach my $h (sort { $a <=> $b } keys %{$p->{ENDTIME}{$d}}) { - $s .= ''.$p->{ENDTIME}{$d}{$h}.' / '.$p->{TEMPERATURE}{$d}{$h}.' | '; + $s .= ''.$p->{ENDTIME}{$d}{$h}.' / '.$p->{TEMPERATURE}{$d}{$h}.' | '; last if ($p->{ENDTIME}{$d}{$h} eq '24:00'); } $s .= '