From 62a234b4d905eb0deb22e4a93d22e4e140362de9 Mon Sep 17 00:00:00 2001 From: zap <> Date: Sun, 12 Dec 2021 13:01:01 +0000 Subject: [PATCH] HMCCU: Several improvements and bug fixes git-svn-id: https://svn.fhem.de/fhem/trunk@25336 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/88_HMCCU.pm | 320 ++++++++++++++++++++++++++--------- fhem/FHEM/88_HMCCUCHN.pm | 28 ++- fhem/FHEM/88_HMCCUDEV.pm | 8 +- fhem/FHEM/88_HMCCURPCPROC.pm | 2 +- fhem/FHEM/HMCCUConf.pm | 262 +++++++++++++++++----------- 6 files changed, 428 insertions(+), 193 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index f5d17dc27..bdca26b04 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - bugfix: 88_HMCCU: Several improvements and bug fixes - bugfix: 93_DbRep: fix some problems with reduceLog (Forum: #/topic,53584.msg1177799.html#msg1177799 - feature: 98_weekprofile: support HMCCUCHN with CCU diff --git a/fhem/FHEM/88_HMCCU.pm b/fhem/FHEM/88_HMCCU.pm index dc0a542ac..d208a0286 100755 --- a/fhem/FHEM/88_HMCCU.pm +++ b/fhem/FHEM/88_HMCCU.pm @@ -31,7 +31,7 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch'; use strict; use warnings; -# use Data::Dumper; +use Data::Dumper; use Encode qw(decode encode); use RPC::XML::Client; use RPC::XML::Server; @@ -57,7 +57,7 @@ my %HMCCU_CUST_CHN_DEFAULTS; my %HMCCU_CUST_DEV_DEFAULTS; # HMCCU version -my $HMCCU_VERSION = '5.0 213301607'; +my $HMCCU_VERSION = '5.0 213461309'; # Timeout for CCU requests (seconds) my $HMCCU_TIMEOUT_REQUEST = 4; @@ -4327,11 +4327,12 @@ sub HMCCU_GetParamDef ($$$;$) my $a = $devDesc->{ADDRESS}; my ($devAddr, $chnNo) = ($a =~ /:[0-9]{1,2}$/) ? HMCCU_SplitChnAddr ($a) : ($a, 'd'); - my $model = HMCCU_GetDeviceModel ($hash, $devDesc->{_model}, $devDesc->{_fw_ver}, $chnNo); if (!defined($paramset)) { HMCCU_Log ($hash, 2, "$a Paramset not defined ".stacktraceAsString(undef)); return undef; } + + my $model = HMCCU_GetDeviceModel ($hash, $devDesc->{_model}, $devDesc->{_fw_ver}, $chnNo); if (defined($model) && exists($model->{$paramset})) { if (defined($parameter)) { return exists($model->{$paramset}{$parameter}) ? $model->{$paramset}{$parameter} : undef; @@ -4378,6 +4379,46 @@ sub HMCCU_FindParamDef ($$$) return (undef, undef); } +###################################################################### +# Get values of ENUM datapoint +# object - Hash with parameter defintion or channel address +# dpt - Datapoint name +# value - Either a numeric value or an enumeration constant +# If value is not specified, a comma separated list of enumeration +# constants is returned. +# Return value or undef if datapoint is not of type ENUM. +###################################################################### + +sub HMCCU_GetEnumValues ($$$;$) +{ + my ($ioHash, $object, $dpt, $value) = @_; + + my $paramDef = ref($object) eq 'HASH' ? $object : HMCCU_GetParamDef ($ioHash, $object, 'VALUES', $dpt); + if (defined($paramDef) && defined($paramDef->{TYPE}) && $paramDef->{TYPE} eq 'ENUM' && defined($paramDef->{VALUE_LIST})) { + my $i = defined($paramDef->{MIN}) && HMCCU_IsIntNum($paramDef->{MIN}) ? $paramDef->{MIN} : 0; + my $j = $i; + my @valList = split(',',$paramDef->{VALUE_LIST}); + my %valIndex = map { $_ => $i++ } @valList; + if (defined($value)) { + if ($value eq '#') { + $j--; + return join(',', map { $j++; $_ ne '' ? "$_:$j" : () } @valList); + } + elsif (HMCCU_IsIntNum($value)) { + return $valList[$value] if ($value >= 0 && $value < scalar(@valList)); + } + else { + return $valIndex{$value} if (exists($valIndex{$value})); + } + } + else { + return $paramDef->{VALUE_LIST}; + } + } + + return $value; +} + ###################################################################### # Check if parameter exists # Parameters: @@ -4683,7 +4724,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) HMCCU_UpdateInternalValues ($clHash, $chKey, $ps, 'VAL', $v); # Modify value: scale, format, substitute - $sv = HMCCU_ScaleValue ($clHash, $c, $p, $v, 0); + $sv = HMCCU_ScaleValue ($clHash, $c, $p, $v, 0, $ps); HMCCU_UpdateInternalValues ($clHash, $chKey, $ps, 'NVAL', $sv); HMCCU_Trace ($clHash, 2, "$p: sv = $sv"); $fv = HMCCU_FormatReadingValue ($clHash, $sv, $p); @@ -5816,7 +5857,6 @@ sub HMCCU_IsDeviceActive ($) return 1 if ($disabled == 0 && exists($clHash->{ccuaddr}) && exists($clHash->{ccuif}) && $devstate ne 'inactive'); } - HMCCU_Log ($clHash, 2, "Device disabled or inactive and/or address or interface is missing"); return 0; } @@ -6803,8 +6843,8 @@ sub HMCCU_SetDefaultAttributes ($;$) } } else { - my ($sc, $sd, $cc, $cd) = HMCCU_GetSCDatapoints ($clHash); - $role = $parRef->{role} // HMCCU_GetChannelRole ($clHash, $parRef->{roleChn} // $cc); + my $scc = HMCCU_StateOrControlChannel ($clHash); + $role = $parRef->{role} // HMCCU_GetChannelRole ($clHash, $parRef->{roleChn} // $scc); } if (defined($role) && $role ne '') { @@ -6848,6 +6888,8 @@ sub HMCCU_GetStateValues ($;$$) $dpt //= ''; $ctrlChn //= ''; + my $ioHash = HMCCU_GetHash ($clHash); + my $sv = AttrVal ($clHash->{NAME}, 'statevals', ''); if ($sv eq '' && $dpt ne '' && $ctrlChn ne '') { my $role = HMCCU_GetChannelRole ($clHash, $ctrlChn); @@ -6855,7 +6897,9 @@ sub HMCCU_GetStateValues ($;$$) if ($role ne '' && exists($HMCCU_STATECONTROL->{$role}) && HMCCU_DetectSCDatapoint ($HMCCU_STATECONTROL->{$role}{C}, $clHash->{ccuif}) eq $dpt) { - return $HMCCU_STATECONTROL->{$role}{V}; + return $HMCCU_STATECONTROL->{$role}{V} eq '#' ? + HMCCU_GetEnumValues ($ioHash, HMCCU_GetChannelAddr ($clHash, $ctrlChn), $HMCCU_STATECONTROL->{$role}{C}, '#') : + $HMCCU_STATECONTROL->{$role}{V}; } } @@ -7038,6 +7082,9 @@ sub HMCCU_UpdateRoleCommands ($$;$) my %valList; if ($dpt eq $HMCCU_STATECONTROL->{$role}{C}) { # If parameter is control datapoint, use values/conversions from HMCCU_STATECONTROL + my $stVals = $HMCCU_STATECONTROL->{$role}{V} eq '#' ? + HMCCU_GetEnumValues ($ioHash, HMCCU_GetChannelAddr ($clHash, $chnNo), $HMCCU_STATECONTROL->{$role}{C}, '#') : + $HMCCU_STATECONTROL->{$role}{V}; foreach my $cv (split(',', $HMCCU_STATECONTROL->{$role}{V})) { my ($vn, $vv) = split(':', $cv); $valList{$vn} = $vv // $vn; @@ -7182,7 +7229,10 @@ sub HMCCU_UpdateAdditionalCommands ($$;$$) 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 %stateCmds = split (/[:,]/, $HMCCU_STATECONTROL->{$role}{V}); + my $stVals = $HMCCU_STATECONTROL->{$role}{V} eq '#' ? + HMCCU_GetEnumValues ($ioHash, HMCCU_GetChannelAddr ($clHash, $cc), $HMCCU_STATECONTROL->{$role}{C}, '#') : + $HMCCU_STATECONTROL->{$role}{V}; + my %stateCmds = split (/[:,]/, $stVals); my @states = keys %stateCmds; $clHash->{hmccu}{cmdlist}{set} .= $s.'toggle:noArg' if (scalar(@states) > 1); return; @@ -7431,6 +7481,7 @@ sub HMCCU_ExecuteSetDatapointCommand ($@) { my ($clHash, $a, $h) = @_; + my $ioHash = HMCCU_GetHash ($clHash); my $usage = "Usage: set $clHash->{NAME} datapoint [{channel-number}.]{datapoint} {value} [...]"; my %dpval; my $i = 0; @@ -7439,32 +7490,63 @@ sub HMCCU_ExecuteSetDatapointCommand ($@) my $stVals = HMCCU_GetStateValues ($clHash, $cd, $cc); push (@$a, %${h}) if (defined($h)); - while (my $dpt = shift @$a) { + while (my $cdpt = shift @$a) { my $value = shift @$a // return HMCCU_SetError ($clHash, $usage); $i++; + my $chnAddr = ''; + my $dpt = ''; if ($clHash->{TYPE} eq 'HMCCUDEV') { - if ($dpt =~ /^([0-9]+)\..+$/) { + if ($cdpt =~ /^([0-9]+)\.(.+)$/) { + $chnAddr = "$devAddr:$1"; + $dpt = $2; return HMCCU_SetError ($clHash, -7) if ($1 >= $clHash->{hmccu}{channels}); } else { return HMCCU_SetError ($clHash, -12) if ($cc eq ''); - $dpt = "$cc.$dpt"; + $dpt = $cdpt; + $cdpt = "$cc.$cdpt"; + $chnAddr = "$devAddr:$cc"; } } else { - if ($dpt =~ /^([0-9]+)\..+$/) { + if ($cdpt =~ /^([0-9]+)\.(.+)$/) { + $chnAddr = "$devAddr:$1"; + $dpt = $2; return HMCCU_SetError ($clHash, -7) if ($1 != $chnNo); } else { - $dpt = "$chnNo.$dpt"; + $dpt = $cdpt; + $cdpt = "$chnNo.$cdpt"; + $chnAddr = "$devAddr:$chnNo"; + } + } + + my $paramDef = HMCCU_GetParamDef ($ioHash, $chnAddr, 'VALUES', $dpt); + my $paramType = defined($paramDef) ? $paramDef->{TYPE} : ''; + + # Show values allowed for datapoint + if ($value eq '?' && defined($paramDef)) { + if ($paramDef->{OPERATIONS} & 2) { + if ($paramType ne 'ENUM') { + my $min = $paramDef->{MIN} // '?'; + my $max = $paramDef->{MAX} // '?'; + my $unit = $paramDef->{UNIT} // '?'; + return "Usage: set $clHash->{NAME} datapoint $cdpt {$paramType} # min=$min max=$max unit=$unit"; + } + else { + return "Usage: set $clHash->{NAME} datapoint $cdpt {$paramDef->{VALUE_LIST}}"; + } + } + else { + return "Datapoint $cdpt is not writeable"; } } $value = HMCCU_Substitute ($value, $stVals, 1, undef, '') if ($stVals ne '' && $dpt eq $cd); my $no = sprintf ("%03d", $i); - $dpval{"$no.$clHash->{ccuif}.$devAddr:$dpt"} = $value; + $dpval{"$no.$clHash->{ccuif}.$chnAddr.$dpt"} = $value; } return HMCCU_SetError ($clHash, $usage) if (scalar(keys %dpval) < 1); @@ -7526,9 +7608,13 @@ sub HMCCU_ExecuteSetParameterCommand ($@) return HMCCU_SetError ($clHash, "Paramset $paramset not supported by device or channel") if ($devDesc->{PARAMSETS} !~ /$paramset/); if (!HMCCU_IsValidParameter ($ioHash, $devDesc, $paramset, $h)) { - my @parList = HMCCU_GetParamDef ($ioHash, $devDesc, $paramset); - return HMCCU_SetError ($clHash, 'Invalid parameter specified. Valid parameters are '. - join(',', @parList)); + my $paramDef = HMCCU_GetParamDef ($ioHash, $devDesc, $paramset); + if (defined($paramDef)) { + my @parList = map { $paramDef->{$_}{OPERATIONS} == 2 ? $_ : () } keys %$paramDef; + return HMCCU_SetError ($clHash, 'Invalid parameter specified. Valid parameters: '. + join(',', @parList)) if (scalar(@parList) > 0); + } + return HMCCU_SetError ($clHash, 'Invalid parameter specified'); } if ($paramset eq 'VALUES' || $paramset eq 'MASTER') { @@ -7978,7 +8064,7 @@ sub HMCCU_SetDefaultSCDatapoints ($$;$$) $cc = $detect->{defCCh} if ($detect->{defCCh} != -1); $sd = $detect->{stateRole}{$sc}{datapoint} if ($sc ne '' && exists($detect->{stateRole}{$sc})); $cd = $detect->{controlRole}{$cc}{datapoint} if ($cc ne '' && exists($detect->{controlRole}{$cc})); -# HMCCU_Log ($clHash, 2, "Prio 4: s=$sc.$sd c=$cc.$sd"); +# HMCCU_Log ($clHash, 2, "Prio 4: s=$sc.$sd c=$cc.$cd"); } # Prio 3: Use information stored in device hash (HMCCUDEV only) @@ -7986,22 +8072,34 @@ sub HMCCU_SetDefaultSCDatapoints ($$;$$) # Support for level 5 devices ($sc, $sd) = HMCCU_SplitDatapoint ($clHash->{hmccu}{defSDP}) if (defined($clHash->{hmccu}{defSDP})); ($cc, $cd) = HMCCU_SplitDatapoint ($clHash->{hmccu}{defCDP}) if (defined($clHash->{hmccu}{defCDP})); -# HMCCU_Log ($clHash, 2, "Prio 3: s=$sc.$sd c=$cc.$sd"); +# HMCCU_Log ($clHash, 2, "Prio 3: s=$sc.$sd c=$cc.$cd"); } # Prio 2: Use attribute statechannel and controlchannel for HMCCUDEV and channel address for HMCCUCHN + my ($asc, $acc) = ('', ''); if ($clType eq 'HMCCUCHN') { # State and control channel of HMCCUCHN devices is defined by channel address my $da; - ($da, $sc) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); - $cc = $sc; + ($da, $asc) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); + $acc = $asc; } else { # Consider attributes statechannel and controlchannel for HMCCUDEV devices - $sc = AttrVal ($clName, 'statechannel', $sc); - $cc = AttrVal ($clName, 'controlchannel', $cc); + $asc = AttrVal ($clName, 'statechannel', $sc); + $acc = AttrVal ($clName, 'controlchannel', $cc); } -# HMCCU_Log ($clHash, 2, "Prio 2: s=$sc.$sd c=$cc.$sd"); + # Correct datapoints + if (defined($detect)) { + if ($asc ne '' && exists($detect->{stateRole}) && exists($detect->{stateRole}{$asc})) { + $sc = $asc; + $sd = $detect->{stateRole}{$asc}{datapoint}; + } + if ($acc ne '' && exists($detect->{controlRole}) && exists($detect->{controlRole}{$acc})) { + $cc = $acc; + $cd = $detect->{controlRole}{$acc}{datapoint}; + } + } +# HMCCU_Log ($clHash, 2, "Prio 2: s=$sc.$sd c=$cc.$cd"); # Prio 1: Use attributes statedatapoint and controldatapoint # Attributes are overriding attributes statechannel and controlchannel for HMCCUDEV @@ -8023,13 +8121,13 @@ sub HMCCU_SetDefaultSCDatapoints ($$;$$) } $cd = $ca[0]; } -# HMCCU_Log ($clHash, 2, "Prio 1: s=$sc.$sd c=$cc.$sd"); +# HMCCU_Log ($clHash, 2, "Prio 1: s=$sc.$sd c=$cc.$cd"); my $sr = $sc ne '' && defined($detect) && exists($detect->{stateRole}{$sc}) ? $detect->{stateRole}{$sc}{role} : ''; my $cr = $cc ne '' && defined($detect) && exists($detect->{controlRole}{$cc}) ? $detect->{controlRole}{$cc}{role} : ''; ($sc, $sd) = ('', '') if (!HMCCU_SetSCDatapoints ($clHash, 'statedatapoint', "$sc.$sd", $sr)); ($cc, $cd) = ('', '') if (!HMCCU_SetSCDatapoints ($clHash, 'controldatapoint', "$cc.$cd", $cr)); -# HMCCU_Log ($clHash, 2, "SetDC: s=$sc.$sd c=$cc.$sd sr=$sr cr=$cr"); +# HMCCU_Log ($clHash, 2, "SetDC: s=$sc.$sd c=$cc.$cd sr=$sr cr=$cr"); if ($cmd) { my $chn = $cc ne '' ? $cc : $sc; @@ -8047,46 +8145,44 @@ sub HMCCU_SetDefaultSCDatapoints ($$;$$) ###################################################################### # Get state and control channel and datapoint of a device. -# Priority depends on FHEM device type: -# -# HMCCUCHN: -# 1. Datapoints from attributes statedatapoint, controldatapoint -# 2. Datapoints by role -# -# HMCCUDEV: -# 1. Attributes statechannel, controlchannel -# 2. Channel from attributes statedatapoint, controldatapoint -# 3. Datapoints from attributes statedatapoint, controldatapoint -# 4. Channel datapoint by role -# -# If controldatapoint is not specified it is synchronized with -# statedatapoint. -# -# Return (sc, sd, cc, cd, sdCnt, cdCnt) -# If sdCnt > 1 or cdCnt > 1 more than 1 matching rules were found +# If neither statedatapoint nor controldatapoint is defined, try +# setting default values. ###################################################################### sub HMCCU_GetSCDatapoints ($) { my ($clHash) = @_; - my $ioHash = HMCCU_GetHash ($clHash); my $type = $clHash->{TYPE}; my ($sc, $sd) = HMCCU_StateDatapoint ($clHash); my ($cc, $cd) = HMCCU_ControlDatapoint ($clHash); - my $rsdCnt; - my $rcdCnt; + + my $rsdCnt = $sc ne '' && $sd ne '' ? 1 : 0; + my $rcdCnt = $cc ne '' && $cd ne '' ? 1 : 0; + + return ($sc, $sd, $cc, $cd, $rsdCnt, $rcdCnt) if ($rsdCnt > 0 || $rcdCnt > 0); # Detect by attributes # ($sc, $sd, $cc, $cd, $rsdCnt, $rcdCnt) = HMCCU_DetectSCAttr ($clHash, $sc, $sd, $cc, $cd); # return ($sc, $sd, $cc, $cd, $rsdCnt, $rcdCnt) if ($rsdCnt); - # HMCCU_Log ($clHash, 2, "GetSCDatapoints 2: $sc, $sd, $cc, $cd"); - + my $ioHash = HMCCU_GetHash ($clHash); return HMCCU_SetDefaultSCDatapoints ($ioHash, $clHash); } +sub HMCCU_StateOrControlChannel ($) +{ + my ($clHash) = @_; + + my ($sc, $sd) = HMCCU_StateDatapoint ($clHash); + my ($cc, $cd) = HMCCU_ControlDatapoint ($clHash); + + ($sc, $sd, $cc, $cd) = HMCCU_GetSCDatapoints ($clHash); + + return $cc eq '' ? $sc : $cc; +} + sub HMCCU_ControlDatapoint ($) { my ($clHash) = @_; @@ -9066,16 +9162,18 @@ sub HMCCU_SetMultipleParameters ($$$;$) $paramSet //= 'VALUES'; $address =~ s/:d$//; - my ($add, $chn) = HMCCU_SplitChnAddr ($address); - return (-1, undef) if ($paramSet eq 'VALUES' && !defined($chn)); + my ($add, $chn) = HMCCU_SplitChnAddr ($address, 'd'); + return (-1, undef) if ($paramSet eq 'VALUES' && $chn eq 'd'); foreach my $p (sort keys %$params) { - HMCCU_Trace ($clHash, 2, "Parameter=$address.$paramSet.$p Value=$params->{$p}"); + HMCCU_Trace ($clHash, 2, "Parameter=$address.$paramSet.$p chn=$chn Value=$params->{$p}"); return (-8, undef) if ( ($paramSet eq 'VALUES' && !HMCCU_IsValidParameter ($clHash, $address, 'VALUES', $p, 2)) || ($paramSet eq 'MASTER' && !HMCCU_IsValidParameter ($clHash, $address, 'MASTER', $p)) ); - $params->{$p} = HMCCU_ScaleValue ($clHash, $chn, $p, $params->{$p}, 1); + if ($params->{$p} !~ /:(STRING|BOOL|INTEGER|FLOAT|DOUBLE)$/) { + $params->{$p} = HMCCU_ScaleValue ($clHash, $chn, $p, $params->{$p}, 1, $paramSet); + } } return HMCCU_RPCRequest ($clHash, 'putParamset', $address, $paramSet, $params); @@ -9090,7 +9188,8 @@ sub HMCCU_SetMultipleParameters ($$$;$) # Return value < 0 on error. ###################################################################### -sub HMCCU_SetMultipleDatapoints ($$) { +sub HMCCU_SetMultipleDatapoints ($$) +{ my ($clHash, $params) = @_; my $mdFlag = $clHash->{TYPE} eq 'HMCCU' ? 1 : 0; my $ioHash; @@ -9154,8 +9253,19 @@ sub HMCCU_SetMultipleDatapoints ($$) { } } - my $dptType = HMCCU_GetDatapointAttr ($ioHash, $ccuType, $chn, $dpt, 'type'); - $v = "'".$v."'" if (defined($dptType) && $dptType == $HMCCU_TYPE_STRING); + my $paramDef = HMCCU_GetParamDef ($ioHash, "$add:$chn", 'VALUES', $dpt); + if (defined($paramDef)) { + if ($paramDef->{TYPE} eq 'STRING') { + $v = "'".$v."'"; + } + elsif ($paramDef->{TYPE} eq 'ENUM' && !HMCCU_IsIntNum($v)) { + HMCCU_Log ($clHash, 2, "Enum datapoint value = $v"); + $v = HMCCU_GetEnumValues ($ioHash, $paramDef, $dpt, $v); + HMCCU_Log ($clHash, 2, "Enum datapoint number = $v"); + } + } +# my $dptType = HMCCU_GetDatapointAttr ($ioHash, $ccuType, $chn, $dpt, 'type'); +# $v = "'".$v."'" if (defined($dptType) && $dptType == $HMCCU_TYPE_STRING); my $c = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$v.");\n"; if ($dpt =~ /$ccuChange/) { @@ -9197,15 +9307,19 @@ sub HMCCU_ScaleValue ($$$$$;$) my $name = $hash->{NAME}; my $ioHash = HMCCU_GetHash ($hash); - # Only numeric values allowed - return $value if (!HMCCU_IsFltNum ($value)); + my $boundsChecking = HMCCU_IsFlag ($name, 'noBoundsChecking') ? 0 : 1; # Get parameter definition and min/max values my $min; my $max; my $unit; my $ccuaddr = $hash->{ccuaddr}; - $ccuaddr .= ':'.$chnno if ($hash->{TYPE} eq 'HMCCUDEV' && $chnno ne ''); + if ($hash->{TYPE} eq 'HMCCUDEV' && $chnno ne '' && $chnno ne 'd') { + $ccuaddr .= ':'.$chnno; + } + elsif ($hash->{TYPE} eq 'HMCCUCHN' && $chnno eq 'd') { + ($ccuaddr, undef) = HMCCU_SplitChnAddr ($ccuaddr); + } my $paramDef = HMCCU_GetParamDef ($ioHash, $ccuaddr, $paramSet, $dpt); if (defined($paramDef)) { $min = $paramDef->{MIN} if (defined($paramDef->{MIN}) && $paramDef->{MIN} ne ''); @@ -9214,7 +9328,7 @@ sub HMCCU_ScaleValue ($$$$$;$) $unit = '100%' if ($dpt eq 'LEVEL' && !defined($unit)); } else { - HMCCU_Trace ($hash, 2, "Can't get parameter definion for addr=$hash->{ccuaddr} chn=$chnno"); + HMCCU_Trace ($hash, 2, "Can't get parameter definion for addr=$ccuaddr chn=$chnno dpt=$dpt"); } # Default values can be overriden by attribute @@ -9222,7 +9336,11 @@ sub HMCCU_ScaleValue ($$$$$;$) HMCCU_Trace ($hash, 2, "chnno=$chnno, dpt=$dpt, value=$value, mode=$mode"); + # Scale by attribute ccuscaleval if ($ccuscaleval ne '' && $mode != 2) { + # Only numeric or values allowed + return $value if (!HMCCU_IsFltNum ($value)); + HMCCU_Trace ($hash, 2, "ccuscaleval"); my @sl = split (',', $ccuscaleval); foreach my $sr (@sl) { @@ -9270,37 +9388,34 @@ sub HMCCU_ScaleValue ($$$$$;$) } # Align value with min/max boundaries for set mode - if ($mode == 1 && defined($min) && defined($max)) { + if ($mode == 1 && defined($min) && defined($max) && $boundsChecking) { $value = HMCCU_MinMax ($value, $min, $max); } HMCCU_Trace ($hash, 2, "Attribute scaled value of $dpt = $value"); - return int($value) == $value ? int($value) : $value; + return $mode == 0 && int($value) == $value ? int($value) : $value; } - - if ($dpt =~ /^RSSI_/) { + + # Auto scale + if ($dpt =~ /^RSSI_/ && $mode == 0) { # Subtract 256 from Rega value (Rega bug) $value = abs ($value) == 65535 || $value == 0 ? 'N/A' : ($value > 0 ? $value-256 : $value); } - elsif ($dpt =~ /^(P[0-9]_)?ENDTIME/) { - if ($mode == 0) { - my $hh = sprintf ("%02d", int($value/60)); - my $mm = sprintf ("%02d", $value%60); - $value = "$hh:$mm"; - } - else { - my ($hh, $mm) = split (':', $value); - $mm //= 0; - $value = $hh*60+$mm; - } + elsif (defined($unit) && ($unit eq 'minutes' || $unit eq 's')) { + $value = HMCCU_ConvertTime ($value, $unit, $mode); } elsif (defined($unit) && $unit =~ /^([0-9]+)%$/) { + # percentage values my $f = $1; $min //= 0; $max //= 1.0; - $value = ($mode == 0 || $mode == 2) ? HMCCU_MinMax ($value, $min, $max)*$f : - HMCCU_MinMax($value, $min*$f, $max*$f)/$f; + if ($mode == 0 || $mode == 2) { + $value = HMCCU_MinMax ($value, $min, $max)*$f; + } + else { + $value = $boundsChecking ? HMCCU_MinMax($value, $min*$f, $max*$f)/$f : $value/$f; + } } HMCCU_Trace ($hash, 2, "Auto scaled value of $dpt = $value"); @@ -9420,7 +9535,7 @@ sub HMCCU_GetUpdate ($$;$$) return -4 if ($type ne 'HMCCU' && $clHash->{ccudevstate} eq 'deleted'); my $nam = ''; - my $list = ''; + my @list = (); my $script = ''; $ccuget = HMCCU_GetAttribute ($ioHash, $clHash, 'ccuget', 'Value') if ($ccuget eq 'Attr'); @@ -9429,12 +9544,14 @@ sub HMCCU_GetUpdate ($$;$$) return -1 if ($nam eq ''); my ($stadd, $stchn) = split (':', $addr); my $stnam = HMCCU_GetChannelName ($ioHash, "$stadd:0"); - $list = $stnam eq '' ? $nam : $stnam . "," . $nam; + push @list, $stnam if ($stnam ne ''); + push @list, $nam; $script = '!GetDatapointsByChannel'; } elsif (HMCCU_IsValidDevice ($ioHash, $addr, $HMCCU_FL_ADDRESS)) { $nam = HMCCU_GetDeviceName ($ioHash, $addr); return -1 if ($nam eq ''); + push @list, $nam; $script = '!GetDatapointsByDevice'; # Consider members of group device @@ -9442,7 +9559,7 @@ sub HMCCU_GetUpdate ($$;$$) exists($clHash->{ccugroup}) && $clHash->{ccugroup} ne '') { foreach my $gd (split (',', $clHash->{ccugroup})) { $nam = HMCCU_GetDeviceName ($ioHash, $gd); - $list .= ','.$nam if ($nam ne ''); + push @list, $nam if ($nam ne ''); } } } @@ -9452,14 +9569,14 @@ sub HMCCU_GetUpdate ($$;$$) if (HMCCU_IsFlag ($ioHash->{NAME}, 'nonBlocking')) { # Non blocking request - HMCCU_HMScriptExt ($ioHash, $script, { list => $list, ccuget => $ccuget }, + HMCCU_HMScriptExt ($ioHash, $script, { list => join(',',@list), ccuget => $ccuget }, \&HMCCU_UpdateCB, { filter => $filter }); return 1; } # Blocking request my $response = HMCCU_HMScriptExt ($ioHash, $script, - { list => $list, ccuget => $ccuget }); + { list => join(',',@list), ccuget => $ccuget }); HMCCU_Trace ($clHash, 2, "Addr=$addr Name=$nam Script=$script
". "Script response = \n".$response); return -2 if ($response eq '' || $response =~ /^ERROR:.*/); @@ -9864,6 +9981,45 @@ sub HMCCU_GetTimeSpec ($) return ($s-$cs); } +###################################################################### +# Convert time values +# $value - Time value, format: +# $mode = 0: n +# $mode = 1, unit = s: [[hh:]mm:]ss +# $mode = 1, unit = minutes: [hh:]mm +# $unit - s or minutes +# $mode - 0 = Get, 1 = Set +###################################################################### + +sub HMCCU_ConvertTime ($$$) +{ + my ($value, $unit, $mode) = @_; + + return $value if ($unit ne 'minutes' && $unit ne 's'); + + if ($mode == 0) { + my $f = $unit eq 'minutes' ? 60 : 3600; + my @t = (); + while ($f >= 60) { + push @t, sprintf('%02d',int($value/$f)); + $value = $value%$f; + $f = $f/60; + } + push @t, sprintf('%02d',$value); + return join(':',@t); + } + else { + my @t = split(':',$value); + my $f = scalar(@t) == 1 ? 1 : ($unit eq 'minutes' ? 60 : (scalar(@t) == 3 ? 3600 : 60)); + my $r = 0; + foreach my $v (@t) { + $r = $r+$v*$f; + $f = $f/60; + } + return $r; + } +} + ###################################################################### # Get minimum or maximum of 2 values # Align value with boundaries diff --git a/fhem/FHEM/88_HMCCUCHN.pm b/fhem/FHEM/88_HMCCUCHN.pm index 8ced15091..c12d30c9b 100644 --- a/fhem/FHEM/88_HMCCUCHN.pm +++ b/fhem/FHEM/88_HMCCUCHN.pm @@ -30,7 +30,7 @@ sub HMCCUCHN_Set ($@); sub HMCCUCHN_Get ($@); sub HMCCUCHN_Attr ($@); -my $HMCCUCHN_VERSION = '5.0 213281908'; +my $HMCCUCHN_VERSION = '5.0 213461309'; ###################################################################### # Initialize module @@ -51,7 +51,7 @@ sub HMCCUCHN_Initialize ($) $hash->{parseParams} = 1; $hash->{AttrList} = 'IODev ccucalculate '. - 'ccuflags:multiple-strict,ackState,logCommand,noReadings,trace,showMasterReadings,showLinkReadings,showDeviceReadings,showServiceReadings '. + 'ccuflags:multiple-strict,noBoundsChecking,ackState,logCommand,noReadings,trace,showMasterReadings,showLinkReadings,showDeviceReadings,showServiceReadings '. 'ccureadingfilter:textField-long statedatapoint controldatapoint '. 'ccureadingformat:name,namelc,address,addresslc '. 'ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix '. @@ -212,7 +212,7 @@ sub HMCCUCHN_InitDevice ($$) $rc = -2; } - HMCCU_GetUpdate ($devHash, $da, 'Value'); + HMCCU_GetUpdate ($devHash, $da); } return $rc; @@ -357,7 +357,7 @@ sub HMCCUCHN_Set ($@) elsif ($lcopt =~ /^(config|values)$/) { return HMCCU_ExecuteSetParameterCommand ($ioHash, $hash, $lcopt, $a, $h); } - elsif ($lcopt =~ 'readingfilter') { + elsif ($lcopt eq 'readingfilter') { my $filter = shift @$a // return HMCCU_SetError ($hash, "Usage: set $name readingFilter {datapointList}"); $filter =~ s/,/\|/g; $filter = '^('.$filter.')$'; @@ -519,6 +519,9 @@ sub HMCCUCHN_Get ($@) Set