From be9bc8d368ab1bf60e5970803010d2fa398c0be9 Mon Sep 17 00:00:00 2001 From: zap <> Date: Wed, 15 Jan 2020 18:17:54 +0000 Subject: [PATCH] HMCCU: Update for version 4.4 beta git-svn-id: https://svn.fhem.de/fhem/trunk@20988 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/HMCCU/FHEM/88_HMCCU.pm | 320 ++++++++++++++++----- fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm | 211 ++++++++------ fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm | 128 +++++---- fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm | 14 +- 4 files changed, 457 insertions(+), 216 deletions(-) diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm index f4483a37e..b1509b446 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm @@ -4,7 +4,7 @@ # # $Id: 88_HMCCU.pm 18745 2019-02-26 17:33:23Z zap $ # -# Version 4.4.000 +# Version 4.4.002 # # Module for communication between FHEM and Homematic CCU2/3. # @@ -52,7 +52,7 @@ my %HMCCU_CUST_CHN_DEFAULTS; my %HMCCU_CUST_DEV_DEFAULTS; # HMCCU version -my $HMCCU_VERSION = '4.4.000'; +my $HMCCU_VERSION = '4.4.002'; # Constants and default values my $HMCCU_MAX_IOERRORS = 100; @@ -222,9 +222,9 @@ sub HMCCU_SetRPCState ($@); # Filter and modify readings sub HMCCU_FilterReading ($$$); sub HMCCU_FormatReadingValue ($$$); -sub HMCCU_GetReadingName ($$$$$$$); +sub HMCCU_GetReadingName ($$$$$$$;$); sub HMCCU_ScaleValue ($$$$$); -sub HMCCU_Substitute ($$$$$); +sub HMCCU_Substitute ($$$$$;$); sub HMCCU_SubstRule ($$$); sub HMCCU_SubstVariables ($$$); @@ -284,6 +284,7 @@ sub HMCCU_AddDeviceModel ($$$$$$); sub HMCCU_CreateDevice ($$$$$); sub HMCCU_DeleteDevice ($); sub HMCCU_ExistsDeviceModel ($$$;$); +sub HMCCU_FindParamDef ($$$); sub HMCCU_FormatDeviceInfo ($); sub HMCCU_GetAddress ($$$$); sub HMCCU_GetAffectedAddresses ($); @@ -406,7 +407,7 @@ sub HMCCU_Initialize ($) " ccudef-hmstatevals:textField-long ccudef-substitute:textField-long". " ccudef-readingname:textField-long ccudef-readingfilter:textField-long". " ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc". - " ccudef-stripnumber". + " ccudef-stripnumber ccuReadingPrefix". " ccuflags:multiple-strict,procrpc,dptnocheck,logCommand,noagg,nohmstate,". "logEvents,noEvents,noInitialUpdate,noReadings,nonBlocking,reconnect,logPong,trace". " ccuReqTimeout ccuGetVars rpcinterval:2,3,5,7,10 rpcqueue rpcPingCCU". @@ -2365,24 +2366,25 @@ sub HMCCU_FilterReading ($$$) my $hmccu_hash = HMCCU_GetHash ($hash); return 1 if (!defined ($hmccu_hash)); - my $grf = AttrVal ($hmccu_hash->{NAME}, 'ccudef-readingfilter', '.*'); - my $rf = AttrVal ($name, 'ccureadingfilter', $grf); - $rf = $grf.";".$rf if ($rf ne $grf && $grf ne '.*'); +# my $grf = AttrVal ($hmccu_hash->{NAME}, 'ccudef-readingfilter', '.*'); +# my $rf = AttrVal ($name, 'ccureadingfilter', $grf); +# $rf = $grf.";".$rf if ($rf ne $grf && $grf ne '.*'); + my $rf = AttrVal ($name, 'ccureadingfilter', '.*'); my $chnnam = ''; - my $chnnum = ''; - my $devadd = ''; +# my $chnnum = ''; +# my $devadd = ''; - # Get channel name and channel number - if (HMCCU_IsValidChannel ($hmccu_hash, $chn, $HMCCU_FL_ADDRESS)) { + my ($devadd, $chnnum) = HMCCU_SplitChnAddr ($chn); + if ($chnnum ne 'd') { + # Get channel name and channel number $chnnam = HMCCU_GetChannelName ($hmccu_hash, $chn, ''); - ($devadd, $chnnum) = HMCCU_SplitChnAddr ($chn); + if ($chnnam eq '') { + ($devadd, $chnnum) = HMCCU_GetAddress ($hash, $chn, '', ''); + $chnnam = $chn; + } } - else { - ($devadd, $chnnum) = HMCCU_GetAddress ($hash, $chn, '', ''); - $chnnam = $chn; - } - + HMCCU_Trace ($hash, 2, $fnc, "chn=$chn, chnnam=$chnnam chnnum=$chnnum dpt=$dpt, rules=$rf"); foreach my $r (split (';', $rf)) { @@ -2420,11 +2422,12 @@ sub HMCCU_FilterReading ($$$) ( ($cn ne '' && "$chnnum" eq "$cn") || ($c ne '' && $chnnam =~ /$c/) || - ($cn eq '' && $c eq '') + ($cn eq '' && $c eq '') || + ($chnnum eq 'd') ) && $dpt =~ /$f/ ) ); - # Negate filter + # Negative filter return 1 if ( !$rm && ( ($cn ne '' && "$chnnum" ne "$cn") || @@ -2443,7 +2446,7 @@ sub HMCCU_FilterReading ($$$) # # Parameters: # -# Interface,Address,ChannelNo,Datapoint,ChannelNam,Format +# Interface,Address,ChannelNo,Datapoint,ChannelNam,Format,Paramset # Format := { name[lc] | datapoint[lc] | address[lc] | formatStr } # formatStr := Any text containing at least one format pattern # pattern := { %a, %c, %n, %d, %A, %C, %N, %D } @@ -2456,17 +2459,20 @@ sub HMCCU_FilterReading ($$$) # # Reading names can be modified or new readings can be added by # setting attribut ccureadingname. -# Returns list of readings names. +# Returns list of readings names. Return empty list on error. ###################################################################### -sub HMCCU_GetReadingName ($$$$$$$) +sub HMCCU_GetReadingName ($$$$$$$;$) { - my ($hash, $i, $a, $c, $d, $n, $rf) = @_; + my ($hash, $i, $a, $c, $d, $n, $rf, $ps) = @_; my $name = $hash->{NAME}; + + my %prefix = ( 'MASTER' => 'R-', 'LINK' => 'L-', 'VALUES' => '', 'SERVICE' => 'S-' ); my $ioHash = HMCCU_GetHash ($hash); - return '' if (!defined ($ioHash)); + return () if (!defined ($ioHash) || !defined($d) || $d eq ''); + $ps = 'VALUES' if (!defined($ps)); my $rn = ''; my @rnlist; @@ -2477,8 +2483,13 @@ sub HMCCU_GetReadingName ($$$$$$$) my $sr = AttrVal ($name, 'ccureadingname', $gsr); $sr .= ";".$gsr if ($sr ne $gsr && $gsr ne ''); - # Datapoint is mandatory - return '' if ($d eq ''); + # Get reading prefix definitions + my $readingPrefix = HMCCU_GetAttribute ($ioHash, $hash, 'ccuReadingPrefix', ''); + foreach my $pd (split (',', $readingPrefix)) { + my ($rSet, $rPre) = split (':', $pd); + $prefix{$rSet} = $rPre if (defined($rPre) && exists($prefix{$rSet})); + } + my $rpf = exists($prefix{$ps}) ? $prefix{$ps} : ''; # Complete missing values $c = '' if (!defined ($c)); @@ -2499,11 +2510,11 @@ sub HMCCU_GetReadingName ($$$$$$$) $rn = $c ne '' ? $c.'.'.$d : $d; } elsif ($rf eq 'name' || $rf =~ /^name(lc|uc)$/) { - return '' if ($n eq ''); + return () if ($n eq ''); $rn = $n.'.'.$d; } elsif ($rf eq 'address' || $rf =~ /^address(lc|uc)$/) { - return '' if ($a eq ''); + return () if ($a eq ''); my $t = $a; $t = $i.'.'.$t if ($i ne ''); $t = $t.'.'.$c if ($c ne ''); @@ -2521,7 +2532,7 @@ sub HMCCU_GetReadingName ($$$$$$$) $rn =~ s/\%D/uc($d)/ge; } - push (@rnlist, $rn); + push (@rnlist, $rpf.$rn); # Rename and/or add reading names if ($sr ne '') { @@ -2837,9 +2848,9 @@ sub HMCCU_SetRPCState ($@) # mode: 0=Substitute regular expression, 1=Substitute text ###################################################################### -sub HMCCU_Substitute ($$$$$) +sub HMCCU_Substitute ($$$$$;$) { - my ($value, $substrule, $mode, $chn, $dpt) = @_; + my ($value, $substrule, $mode, $chn, $dpt, $type) = @_; my $rc = 0; my $newvalue; @@ -2853,26 +2864,34 @@ sub HMCCU_Substitute ($$$$$) my @rulelist = split (';', $substrule); foreach my $rule (@rulelist) { my @ruletoks = split ('!', $rule); - if (@ruletoks == 2 && $dpt ne '' && $mode == 0) { - my @dptlist = split (',', $ruletoks[0]); - foreach my $d (@dptlist) { - my $c = -1; - if ($d =~ /^([0-9]{1,2})\.(.+)$/) { - ($c, $d) = ($1, $2); - } - if ($d eq $dpt && ($c == -1 || !defined($chn) || $c == $chn)) { - ($rc, $newvalue) = HMCCU_SubstRule ($value, $ruletoks[1], $mode); - return $newvalue; + if (scalar(@ruletoks) == 2 && $dpt ne '' && $mode == 0) { + my ($r, $f) = split (':', $ruletoks[0], 2); + if (!defined($f)) { + $f = $r; + $r = undef; + } + if (!defined($r) || (defined($type) && $r eq $type)) { + my @dptlist = split (',', $f); + foreach my $d (@dptlist) { + my $c = -1; + if ($d =~ /^([0-9]{1,2})\.(.+)$/) { + ($c, $d) = ($1, $2); + } + if ($d eq $dpt && ($c == -1 || !defined($chn) || $c == $chn)) { + ($rc, $newvalue) = HMCCU_SubstRule ($value, $ruletoks[1], $mode); + return $newvalue; + } } } } - elsif (@ruletoks == 1) { + elsif (scalar(@ruletoks) == 1) { return $value if ($value !~ /^[+-]?\d+$/ && $value =~ /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/); ($rc, $newvalue) = HMCCU_SubstRule ($value, $ruletoks[0], $mode); return $newvalue if ($rc == 1); } } + # Original value not modified by rules. Use default conversion return $value; } @@ -3365,7 +3384,9 @@ sub HMCCU_AddDeviceDesc ($$$$) $hash->{hmccu}{device}{$iface}{$k}{$p} = join(',', @{$desc->{$p}}); } else { - $hash->{hmccu}{device}{$iface}{$k}{$p} = $desc->{$p}; + my $d = $desc->{$p}; + $d =~ s/ /,/g; + $hash->{hmccu}{device}{$iface}{$k}{$p} = $d; } } @@ -3392,7 +3413,8 @@ sub HMCCU_AddDeviceDesc ($$$$) # Get device description. # Parameters: # $hash - Hash reference of IO device. -# $address - Address of device or channel. +# $address - Address of device or channel. Accepts a channel address +# with channel number 'd' as an alias for device address. # $iface - Interface name. # Return hash reference for device description or undef on error. ###################################################################### @@ -3409,6 +3431,7 @@ sub HMCCU_GetDeviceDesc ($$;$) push (@ifaceList, keys %${$hash->{hmccu}{device}}); } + $address =~ s/:d//; foreach my $i (@ifaceList) { return $hash->{hmccu}{device}{$i}{$address} if (exists($hash->{hmccu}{device}{$i}{$address})); @@ -3589,19 +3612,12 @@ sub HMCCU_GetParamDef ($$$$) { my ($hash, $object, $paramset, $parameter) = @_; - my $devDesc; - if (ref($object) eq 'HASH') { - $devDesc = $object; - } - else { - $devDesc = HMCCU_GetDeviceDesc ($hash, $object); - } + my $devDesc = ref($object) eq 'HASH' ? $object : HMCCU_GetDeviceDesc ($hash, $object); if (defined($devDesc)) { # Build device address and channel number - my $address = $devDesc->{ADDRESS}; - my ($devAddr, $chnNo) = ($address =~ /:[0-9]{1,2}$/) ? - HMCCU_SplitChnAddr ($address) : ($address, 'd'); + 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($model) && exists($model->{$paramset}) && exists($model->{$paramset}{$parameter})) { @@ -3612,6 +3628,39 @@ sub HMCCU_GetParamDef ($$$$) return undef; } +###################################################################### +# Find parameter defintion of device model +# Parameters: +# $hash - Hash reference of IO device. +# $object - Device or channel address or device description +# reference. +# $parameter - Parameter name. +# Returns (undef,undef) on error. Otherwise parameter set name and +# reference to the parameter definition. +###################################################################### + +sub HMCCU_FindParamDef ($$$) +{ + my ($hash, $object, $parameter) = @_; + + my $devDesc = ref($object) eq 'HASH' ? $object : HMCCU_GetDeviceDesc ($hash, $object); + + if (defined($devDesc)) { + # Build device address and channel number + 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($model)) { + foreach my $ps (keys %$model) { + return ($ps, $model->{$ps}{$parameter}) if (exists($model->{$ps}{$parameter})); + } + } + } + + return (undef, undef); +} + ###################################################################### # Convert parameter value # Parameters: @@ -3635,7 +3684,6 @@ sub HMCCU_GetParamValue ($$$$$) my $paramDef = HMCCU_GetParamDef ($hash, $object, $paramset, $parameter); if (defined($paramDef)) { my $type = $paramDef->{TYPE}; - HMCCU_Log ($hash, 2, "Checking type $type"); return $ct{$type}{$value} if (exists($ct{$type}) && exists($ct{$type}{$value})); @@ -3715,6 +3763,138 @@ sub HMCCU_UpdateSingleDatapoint ($$$$) return (ref ($rc)) ? $rc->{$devaddr}{$chn}{$dpt} : $value; } +###################################################################### +# Update readings of client device. +# Parameter objects is a hash reference which contains updated data +# for devices: +# {devaddr}{channelno}{paramset}{parameter} = value +# channelno = 'd' for device parameters. +# Return hash reference for results or undef on error. +###################################################################### + +sub HMCCU_UpdateParamsetReadings ($$$;$) +{ + my ($ioHash, $clHash, $objects, $addListRef) = @_; + + my $clName = $clHash->{NAME}; + my $clType = $clHash->{TYPE}; + + return undef if (!defined($clHash->{IODev}) || !defined($clHash->{ccuaddr}) || + $clHash->{IODev} != $ioHash); + + # Store the resulting readings + my %results; + + # Updated internal values + my @chKeys = (); + + # Check if update of device allowed + my $disable = AttrVal ($clName, 'disable', 0); + my $update = AttrVal ($clName, 'ccureadings', 1); + return undef if ($update == 0 || $disable == 1 || $clHash->{ccudevstate} ne 'active'); + + # Build list of affected addresses + my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); + my @addList = defined ($addListRef) ? @$addListRef : ($devAddr); + + # Determine virtual device flag + my $vg = (($clHash->{ccuif} eq 'VirtualDevices' || $clHash->{ccuif} eq 'fhem') && + exists($clHash->{ccugroup})) ? 1 : 0; + + # Get attributes considering default attributes in IO device + my $substitute = HMCCU_GetAttrSubstitute ($clHash, $ioHash); + + # Get client device attributes + my $clFlags = HMCCU_GetFlags ($clName); + my $clRF = HMCCU_GetAttrReadingFormat ($clHash, $ioHash); + my $peer = AttrVal ($clName, 'peer', 'null'); + my $clInt = $clHash->{ccuif}; + my ($sc, $sd, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clHash, '', 'STATE', '', ''); + + readingsBeginUpdate ($clHash); + + # Loop over all addresses + foreach my $a (@addList) { + # Loop over all channels of device, including channel 'd' + foreach my $c (keys %{$objects->{$a}}) { + next if (($clType eq 'HMCCUCHN' && "$c" ne "$chnNo" && "$c" ne "0" && "$c" ne "d") || + ("$c" eq "0" && $clFlags =~ /nochn0/)); + + my $chnAddr = "$devAddr:$c"; + my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $chnAddr, $clHash->{ccuif}); + my $chnType = defined($devDesc) ? $devDesc->{TYPE} : undef; + + # Loop over all parameter sets + foreach my $ps (keys %{$objects->{$a}{$c}}) { + # Loop over all parameters + foreach my $p (keys %{$objects->{$a}{$c}{$ps}}) { + my $v = $objects->{$a}{$c}{$ps}{$p}; + next if (!defined($v) || !HMCCU_FilterReading ($clHash, $chnAddr, $p)); + my $fv = $v; + my $cv = $v; + my $sv; + + # Key for storing values in client device hash. Indirect updates of virtual + # devices are stored with device address in key. + my $chKey = $devAddr ne $a ? "$chnAddr.$p" : "$c.$p"; + + # Store raw value in client device hash + HMCCU_UpdateInternalValues ($clHash, $chKey, 'VAL', $v) if ($ps eq 'VALUES'); + + # Store the resulting value after scaling, formatting and substitution + if ($ps eq 'VALUES') { + # Modify value: scale, format, substitute + $sv = HMCCU_ScaleValue ($clHash, $c, $p, $v, 0); + $fv = HMCCU_FormatReadingValue ($clHash, $sv, $p); + $cv = HMCCU_Substitute ($fv, $substitute, 0, $c, $p, $chnType); + if ("$cv" eq "$fv") { + # If value has not been changed by rules, use default conversion + $cv = HMCCU_GetParamValue ($ioHash, $devDesc, $ps, $p, $fv); + } + + HMCCU_UpdateInternalValues ($clHash, $chKey, 'SVAL', $cv); + push @chKeys, $chKey; + + # Update 'state' and 'control' + HMCCU_BulkUpdate ($clHash, 'control', $fv, $cv) + if ($cd ne '' && $p eq $cd && $c eq $cc); + HMCCU_BulkUpdate ($clHash, 'state', $fv, $cv) + if ($p eq $sd && ($sc eq '' || $sc eq $c)); + + # Update peers + HMCCU_UpdatePeers ($clHash, "$c.$p", $cv, $peer) if (!$vg && $peer ne 'null'); + } + else { + $fv = $v; + $cv = HMCCU_GetParamValue ($ioHash, $devDesc, $ps, $p, $v); + } + + # Store result, but not for indirect updates of virtual devices + $results{$devAddr}{$c}{$p} = $cv if ($devAddr eq $a); + + # Update readings + my @rnList = HMCCU_GetReadingName ($clHash, $clInt, $a, $c, $p, '', $clRF, $ps); + foreach my $rn (@rnList) { + HMCCU_BulkUpdate ($clHash, $rn, $fv, $cv); + } + } + } + } + } + + # Calculate additional readings + if (scalar (@chKeys) > 0) { + my %calc = HMCCU_CalculateReading ($clHash, \@chKeys); + foreach my $cr (keys %calc) { + HMCCU_BulkUpdate ($clHash, $cr, $calc{$cr}, $calc{$cr}); + } + } + + readingsEndUpdate ($clHash, 1); + + return \%results; +} + ###################################################################### # Update readings of client device. # Parameter objects is a hash reference which contains updated data @@ -3744,7 +3924,7 @@ sub HMCCU_UpdateSingleDevice ($$$$) # Check if update of device allowed my $disable = AttrVal ($cltname, 'disable', 0); my $update = AttrVal ($cltname, 'ccureadings', 1); - next if ($update == 0 || $disable == 1 || $clthash->{ccudevstate} ne 'active'); + return 0 if ($update == 0 || $disable == 1 || $clthash->{ccudevstate} ne 'active'); # Get device parameters and attributes my $ccuflags = HMCCU_GetFlags ($ccuname); @@ -5991,10 +6171,10 @@ sub HMCCU_GetHash ($@) sub HMCCU_GetAttribute ($$$$) { - my ($hmccu_hash, $cl_hash, $attr_name, $attr_def) = @_; + my ($ioHash, $clHash, $attrName, $attrDefault) = @_; - my $value = AttrVal ($cl_hash->{NAME}, $attr_name, ''); - $value = AttrVal ($hmccu_hash->{NAME}, $attr_name, $attr_def) if ($value eq ''); + my $value = AttrVal ($clHash->{NAME}, $attrName, ''); + $value = AttrVal ($ioHash->{NAME}, $attrName, $attrDefault) if ($value eq ''); return $value; } @@ -7403,7 +7583,8 @@ sub HMCCU_RPCRequest ($$$$$;$) my $type = $clHash->{TYPE}; my $fnc = "RPCRequest"; - my $reqMethod = $method eq 'listParamset' || $method eq 'listRawParamset' ? 'getParamset' : $method; + my $reqMethod = $method eq 'listParamset' || $method eq 'listRawParamset' || + $method eq 'getRawParamset' ? 'getParamset' : $method; $filter = '.*' if (!defined ($filter)); my $addr = ''; my $result = ''; @@ -7437,10 +7618,19 @@ sub HMCCU_RPCRequest ($$$$$;$) # Build parameter array: (Address, Paramset [, Parameter ...]) # Paramset := VALUE | MASTER | LINK or any paramset supported by device - # Parameter := Name=Value + # Parameter := Name=Value[:Type] my @parArray = ($addr, $paramset); - if (defined ($parref)) { - foreach my $k (keys %{$parref}) { push @parArray, "$k=$parref->{$k}"; }; + if (defined($parref)) { + foreach my $k (keys %{$parref}) { + my ($pv, $pt) = split (':', $parref->{$k}); + if (!defined($pt)) { + my $paramDef = HMCCU_GetParamDef ($ioHash, $addr, $paramset, $k); + $pt = defined($paramDef) && defined($paramDef->{TYPE}) && $paramDef->{TYPE} ne '' ? + $paramDef->{TYPE} : "STRING"; + } + $pv .= ":$pt"; + push @parArray, "$k=$pv"; + }; } # Submit RPC request @@ -7469,7 +7659,7 @@ sub HMCCU_RPCRequest ($$$$$;$) if ($method eq 'listParamset') { $result = join ("\n", map { $_ =~ /$filter/ ? $_.'='.$reqResult->{$_} : () } keys %$reqResult); } - elsif ($method eq 'listRawParamset') { + elsif ($method eq 'listRawParamset' || $method eq 'getRawParamset') { $result = $reqResult; } elsif ($method eq 'getDeviceDescription') { diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm index 1d64d8865..9ac25d9ad 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm @@ -4,7 +4,7 @@ # # $Id: 88_HMCCUCHN.pm 18552 2019-02-10 11:52:28Z zap $ # -# Version 4.4.001 +# Version 4.4.002 # # (c) 2020 zap (zap01 t-online de) # @@ -45,7 +45,7 @@ sub HMCCUCHN_Initialize ($) $hash->{AttrList} = "IODev ccucalculate ". "ccuflags:multiple-strict,ackState,logCommand,nochn0,trace ccureadingfilter ". "ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ". - "ccureadingname:textField-long ccuSetOnChange ". + "ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix". "ccureadings:0,1 ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint ". "disable:0,1 hmstatevals:textField-long statedatapoint statevals substitute:textField-long ". "substexcl stripnumber peer:textField-long ". $readingFnAttributes; @@ -67,7 +67,7 @@ sub HMCCUCHN_Define ($@) my $devtype = shift @$a; my $devspec = shift @$a; - my $hmccu_hash = undef; + my $ioHash = undef; # Store some definitions for delayed initialization $hash->{hmccu}{devspec} = $devspec; @@ -93,16 +93,16 @@ sub HMCCUCHN_Define ($@) return "Specified IO Device ".$h->{iodev}." does not exist" if (!exists ($defs{$h->{iodev}})); return "Specified IO Device ".$h->{iodev}." is not a HMCCU device" if ($defs{$h->{iodev}}->{TYPE} ne 'HMCCU'); - $hmccu_hash = $defs{$h->{iodev}}; + $ioHash = $defs{$h->{iodev}}; } else { # The following call will fail during FHEM start if CCU is not ready - $hmccu_hash = HMCCU_FindIODevice ($devspec); + $ioHash = HMCCU_FindIODevice ($devspec); } if ($init_done) { # Interactive define command while CCU not ready or no IO device defined - if (!defined ($hmccu_hash)) { + if (!defined ($ioHash)) { my ($ccuactive, $ccuinactive) = HMCCU_IODeviceStates (); if ($ccuinactive > 0) { return "CCU and/or IO device not ready. Please try again later"; @@ -114,7 +114,7 @@ sub HMCCUCHN_Define ($@) } else { # CCU not ready during FHEM start - if (!defined ($hmccu_hash) || $hmccu_hash->{ccustate} ne 'active') { + if (!defined ($ioHash) || $ioHash->{ccustate} ne 'active') { Log3 $name, 2, "HMCCUCHN: [$devname] Cannot detect IO device, maybe CCU not ready. Trying later ..."; # readingsSingleUpdate ($hash, "state", "Pending", 1); $hash->{ccudevstate} = 'pending'; @@ -123,9 +123,9 @@ sub HMCCUCHN_Define ($@) } # Initialize FHEM device, set IO device - my $rc = HMCCUCHN_InitDevice ($hmccu_hash, $hash); + my $rc = HMCCUCHN_InitDevice ($ioHash, $hash); return "Invalid or unknown CCU channel name or address" if ($rc == 1); - return "Can't assign I/O device ".$hmccu_hash->{NAME} if ($rc == 2); + return "Can't assign I/O device ".$ioHash->{NAME} if ($rc == 2); return undef; } @@ -223,7 +223,7 @@ sub HMCCUCHN_Set ($@) return "No set command specified" if (!defined ($opt)); my $rocmds = "clear defaults:noArg"; - my $rwcmds = "clear config control datapoint defaults:noArg paramset devstate"; + my $rwcmds = "clear config control datapoint defaults:noArg paramset link devstate values"; # Get I/O device, check device state return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' || @@ -234,9 +234,9 @@ sub HMCCUCHN_Set ($@) my $disable = AttrVal ($name, "disable", 0); return undef if ($disable == 1); - my $hmccu_hash = $hash->{IODev}; - my $hmccu_name = $hmccu_hash->{NAME}; - if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) { + my $ioHash = $hash->{IODev}; + my $hmccu_name = $ioHash->{NAME}; + if (HMCCU_IsRPCStateBlocking ($ioHash)) { return undef if ($opt eq '?'); return "HMCCUCHN: CCU busy"; } @@ -419,20 +419,31 @@ sub HMCCUCHN_Set ($@) HMCCU_DeleteReadings ($hash, $rnexp); return HMCCU_SetState ($hash, "OK"); } - elsif ($opt eq 'paramset' || $opt eq 'rpcparameter' || $opt eq 'config') { - return HMCCU_SetError ($hash, "Usage: set $name rpcparameter [device] [paramset] {parameter}={value}[:{type}] [...]") + elsif ($opt =~ /^(paramset|rpcparameter|config|link|values)$/) { + my %parSets = ('config' => 'MASTER', 'links' => 'LINK', 'values' => 'VALUES'); + my $paramset = ''; + + return HMCCU_SetError ($hash, "Usage: set $name $opt [device] [paramset] {parameter}={value}[:{type}] [...]") if ((scalar keys %{$h}) < 1); - + + if (exists($parSets{$opt})) { + $paramset = $parSets{$opt}; + } + my $ccuobj = $ccuaddr; my $p = shift @$a; if (defined($p) && $p eq 'device') { ($ccuobj, undef) = HMCCU_SplitChnAddr ($ccuaddr); $p = shift @$a; } - - my $paramset = defined($p) ? uc($p) : ($opt eq 'config' ? 'MASTER' : 'VALUES'); - my $devDesc = HMCCU_GetDeviceDesc ($hmccu_hash, $ccuobj, $ccuif); + if ($opt =~ /(paramset|rpcparameter)/) { + return HMCCU_SetError ($hash, "Command $opt requires a parameter set") + if (!defined($p)); + $paramset = $p; + } + + my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $ccuobj, $ccuif); return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc)); return HMCCU_SetError ($hash, "Paramset $paramset not supported by device") @@ -492,9 +503,9 @@ sub HMCCUCHN_Get ($@) my $disable = AttrVal ($name, "disable", 0); return undef if ($disable == 1); - my $hmccu_hash = $hash->{IODev}; - my $hmccu_name = $hmccu_hash->{NAME}; - if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) { + my $ioHash = $hash->{IODev}; + my $hmccu_name = $ioHash->{NAME}; + if (HMCCU_IsRPCStateBlocking ($ioHash)) { return undef if ($opt eq '?'); return "HMCCUCHN: CCU busy"; } @@ -552,67 +563,77 @@ sub HMCCUCHN_Get ($@) return HMCCU_SetError ($hash, -2) if ($result eq ''); return HMCCU_FormatDeviceInfo ($result); } - elsif ($opt eq 'config' || $opt eq 'configlist') { - my $ccuobj = $ccuaddr; - my $method = $opt eq 'config' ? 'getParamset' : 'listRawParamset'; + elsif ($opt =~ /^(paramset|paramsetlist|config|links|values)$/) { + my $defParamset = ''; + my $method = $opt =~ /list$/ ? 'listRawParamset' : 'getRawParamset'; + my %parSets = ('config' => 'MASTER', 'links' => 'LINK', 'values' => 'VALUES', + 'configlist' => 'MASTER'); my ($devAddr, undef) = HMCCU_SplitChnAddr ($ccuaddr); - my @addList = (); + my @addList = ($devAddr, "$devAddr:0", $ccuaddr); my $par = shift @$a; - if (defined ($par)) { - if ($par eq 'device') { - push (@addList, $devAddr); - $par = shift @$a; - } - elsif ($par eq 'channel') { - $par = shift @$a; - } - } - if (scalar(@addList) == 0) { - push (@addList, "$devAddr:0", $ccuaddr); + if (exists($parSets{$opt})) { + $defParamset = $parSets{$opt}; } - - my $paramset = 'ALL'; - if (defined($par)) { - $paramset = $par; - $par = shift @$a; + else { + if (defined($par)) { + $defParamset = $par; + $par = shift @$a; + } } $par = '.*' if (!defined ($par)); my $res = ''; + my %objects; foreach my $a (@addList) { - my $devDesc = HMCCU_GetDeviceDesc ($hmccu_hash, $a, $ccuif); + my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $a, $ccuif); return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc)); + + my $paramset = $defParamset eq '' ? $devDesc->{PARAMSETS} : $defParamset; + my ($da, $dc) = HMCCU_SplitChnAddr ($a); + $dc = 'd' if ($dc eq ''); $res .= $a eq $devAddr ? "Device $a\n" : "Channel $a\n"; - foreach my $ps (split (',', $devDesc->{PARAMSETS})) { - next if (($ps ne $paramset && $paramset ne 'ALL') || ($ps ne 'MASTER' && $ps ne 'LINK')); + foreach my $ps (split (',', $paramset)) { + next if ($devDesc->{PARAMSETS} !~ /$ps/); + $res .= " Paramset $ps\n"; ($rc, $result) = HMCCU_RPCRequest ($hash, $method, $a, $ps, undef, $par); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0); - $res .= $opt eq 'config' ? - $result : - join ("\n", map { $_ =~ /$par/ ? - " ".$_.' = '.HMCCU_GetParamValue ($hmccu_hash, $devDesc, $ps, $_, $result->{$_}) : () - } sort keys %$result)."\n"; + + if ($opt =~ /list$/) { + $res .= join ("\n", map { $_ =~ /$par/ ? + " ".$_.' = '.HMCCU_GetParamValue ($ioHash, $devDesc, $ps, $_, $result->{$_}) : () + } sort keys %$result)."\n"; + } + else { + foreach my $p (keys %$result) { $objects{$da}{$dc}{$ps}{$p} = $result->{$p}; } + } } } - return $ccureadings && $opt eq 'config' ? undef : $res; + if ($opt !~ /list$/ && scalar(keys %objects) > 0) { + my $convRes = HMCCU_UpdateParamsetReadings ($ioHash, $hash, \%objects); + if (defined($convRes)) { + $res .= join ("\n", map { $_ =~ /$par/ ? + " ".$_.' = '.$convRes->{$_} : () + } sort keys %$convRes)."\n"; + } + } + + return (!$ccureadings || $opt =~ /list$/) ? $res : undef; } elsif ($opt eq 'paramsetdesc') { - my $ccuobj = $ccuaddr; - my $par = shift @$a; my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($ccuaddr); - $chnNo = 'd' if (defined ($par) && $par eq 'device'); - my $model = HMCCU_GetClientDeviceModel ($hash); return HMCCU_SetError ($hash, "Can't get device model") if (!defined($model)); my $res = ''; - foreach my $c (sort keys %{$model}) { - next if (($chnNo eq 'd' && $c ne 'd') || ($chnNo ne 'd' && $c ne '0' && $c ne $chnNo)); +# my @chnList = sort keys %{$model}; + my @chnList = ('d', 0, $chnNo); +# unshift (@chnList, pop(@chnList)) if (exists($model->{'d'})); + foreach my $c (@chnList) { $res .= $c eq 'd' ? "Device\n" : "Channel $c\n"; foreach my $ps (sort keys %{$model->{$c}}) { $res .= " Paramset $ps\n"; @@ -633,26 +654,20 @@ sub HMCCUCHN_Get ($@) elsif ($opt eq 'devicedesc') { my $ccuobj = $ccuaddr; my $par = shift @$a; - my @addList = (); my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($ccuaddr); - - if (defined($par) && $par eq 'device') { - push (@addList, $devAddr); - } - else { - push (@addList, "$devAddr:0"); - push (@addList, $ccuaddr); - } + my @addList = ($devAddr, "$devAddr:0", $ccuaddr); $result = ''; foreach my $a (@addList) { - my $devDesc = HMCCU_GetDeviceDesc ($hmccu_hash, $a, $ccuif); + my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $a, $ccuif); return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc)); - $result .= $a eq $devAddr ? "Device $a\n" : "Channel $a\n"; + $result .= $a eq $devAddr ? "Device $a" : "Channel $a"; + $result .= " $devDesc->{_name} [$devDesc->{TYPE}]\n"; foreach my $n (sort keys %{$devDesc}) { - next if ($n =~ /^_/); + next if ($n =~ /^_/ || $n eq 'ADDRESS' || $n eq 'TYPE' || + !defined($devDesc->{$n}) || $devDesc->{$n} eq ''); $result .= " $n: ".HMCCU_FlagsToStr ('device', $n, $devDesc->{$n}, ',', '')."\n"; } } @@ -669,8 +684,8 @@ sub HMCCUCHN_Get ($@) my @valuelist; my $valuecount = HMCCU_GetValidDatapoints ($hash, $hash->{ccutype}, $c, 1, \@valuelist); $retmsg .= ":".join(",",@valuelist) if ($valuecount > 0); - $retmsg .= " update:noArg deviceinfo:noArg config configlist:channel,device". - " devicedesc:channel,device paramsetdesc:channel,device"; + $retmsg .= " update:noArg deviceinfo:noArg config links". + " devicedesc:noArg paramset paramsetlist paramsetdesc:noArg values"; return $retmsg; } @@ -720,7 +735,7 @@ sub HMCCUCHN_Get ($@) Readings 'state' and 'control' are not deleted.
  • set <name> config
    - Alias for command 'set paramset' with default value for parameter set = 'MASTER'. + Alias for command 'set paramset' for parameter set MASTER.

  • set <name> datapoint <datapoint> <value> [...]
    Set datapoint values of a CCU channel. If parameter value contains special @@ -745,6 +760,9 @@ sub HMCCUCHN_Get ($@) Decrement value of datapoint LEVEL. This command is only available if channel contains a datapoint LEVEL. Default for value is 10.

  • +
  • set <name> link
    + Alias for command 'set paramset' for parameter set LINK. +

  • set <name> <statevalue>
    Set state of a CCU device channel to StateValue. The state datapoint of a channel must be defined by setting attribute 'statedatapoint'. The available state values must @@ -781,7 +799,8 @@ sub HMCCUCHN_Get ($@) Parameter paramset is a valid parameter set name (i.e. MASTER, LINK, ...). The default parameter set is 'VALUES'. Supports attribute 'ccuscaleval' for datapoints. Parameter parameter must be a valid - datapoint or config parameter name. The default type is STRING. + datapoint or config parameter name. If type is not specified, it's taken from + parameter set definition. The default type is STRING. Valid types are STRING, BOOL, INTEGER, FLOAT, DOUBLE.

  • set <name> pct <value> [<ontime> [<ramptime>]]
    @@ -818,21 +837,17 @@ sub HMCCUCHN_Get ($@) Increment value of datapoint LEVEL. This command is only available if channel contains a datapoint LEVEL. Default for value is 10.

  • +
  • set <name> values
    + Alias for command 'set paramset' for parameter set VALUES. +

  • Get


    @@ -955,6 +990,9 @@ sub HMCCUCHN_Get ($@) attr mydev ccureadingname [1-4].PRESSED_SHORT:+pressed
    +
  • ccuReadingPrefix <paramset>:<prefix>[,...]
    + Set reading name prefix for parameter sets. +

  • ccuscaleval <[channelno.]datapoint>:<factor>[,...]
    ccuscaleval <[!][channelno.]datapoint>:<min>:<max>:<minn>:<maxn>[,...]
    @@ -1082,8 +1120,9 @@ sub HMCCUCHN_Get ($@)

  • substitute <subst-rule>[;...]
    Define substitutions for datapoint/reading values. Syntax of subst-rule is

    - [[<channelno>.]<datapoint>[,...]!]<{#n1-m1|regexp}>:<text>[,...] + [[<type>:][<channelno>.]<datapoint>[,...]!]<{#n1-m1|regexp}>:<text>[,...]

    + Parameter type is a valid channel type, i.e. "SHUTTER_CONTACT". Parameter text can contain variables in format ${varname}. The variable ${value} is substituted by the original datapoint value. All other variables must match with a valid diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm index 74c24cc8a..377c4fe3b 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm @@ -83,7 +83,7 @@ sub HMCCUDEV_Define ($@) my $devtype = shift @$a; my $devspec = shift @$a; - my $hmccu_hash = undef; + my $ioHash = undef; # Store some definitions for delayed initialization $hash->{hmccu}{devspec} = $devspec; @@ -120,16 +120,16 @@ sub HMCCUDEV_Define ($@) return "Specified IO Device ".$h->{iodev}." does not exist" if (!exists ($defs{$h->{iodev}})); return "Specified IO Device ".$h->{iodev}." is not a HMCCU device" if ($defs{$h->{iodev}}->{TYPE} ne 'HMCCU'); - $hmccu_hash = $defs{$h->{iodev}}; + $ioHash = $defs{$h->{iodev}}; } else { # The following call will fail for non virtual devices during FHEM start if CCU is not ready - $hmccu_hash = $devspec eq 'virtual' ? HMCCU_GetHash (0) : HMCCU_FindIODevice ($devspec); + $ioHash = $devspec eq 'virtual' ? HMCCU_GetHash (0) : HMCCU_FindIODevice ($devspec); } if ($init_done) { # Interactive define command while CCU not ready - if (!defined ($hmccu_hash)) { + if (!defined ($ioHash)) { my ($ccuactive, $ccuinactive) = HMCCU_IODeviceStates (); if ($ccuinactive > 0) { return "CCU and/or IO device not ready. Please try again later"; @@ -141,7 +141,7 @@ sub HMCCUDEV_Define ($@) } else { # CCU not ready during FHEM start - if (!defined ($hmccu_hash) || $hmccu_hash->{ccustate} ne 'active') { + if (!defined ($ioHash) || $ioHash->{ccustate} ne 'active') { Log3 $name, 2, "HMCCUDEV: [$devname] Cannot detect IO device, maybe CCU not ready. Trying later ..."; # readingsSingleUpdate ($hash, "state", "Pending", 1); $hash->{ccudevstate} = 'pending'; @@ -150,7 +150,7 @@ sub HMCCUDEV_Define ($@) } # Initialize FHEM device, set IO device - my $rc = HMCCUDEV_InitDevice ($hmccu_hash, $hash); + my $rc = HMCCUDEV_InitDevice ($ioHash, $hash); return $errmsg[$rc] if ($rc > 0); return undef; @@ -171,7 +171,7 @@ sub HMCCUDEV_Define ($@) sub HMCCUDEV_InitDevice ($$) { - my ($hmccu_hash, $dev_hash) = @_; + my ($ioHash, $dev_hash) = @_; my $name = $dev_hash->{NAME}; my $devspec = $dev_hash->{hmccu}{devspec}; my $gdcount = 0; @@ -187,7 +187,7 @@ sub HMCCUDEV_InitDevice ($$) # Search for free address. Maximum of 10000 virtual devices allowed. for (my $i=1; $i<=10000; $i++) { my $va = sprintf ("VIR%07d", $i); - if (!HMCCU_IsValidDevice ($hmccu_hash, $va, 1)) { + if (!HMCCU_IsValidDevice ($ioHash, $va, 1)) { $no = $i; last; } @@ -200,9 +200,9 @@ sub HMCCUDEV_InitDevice ($$) $dev_hash->{ccuname} = $name; } else { - return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $devspec, 7)); + return 1 if (!HMCCU_IsValidDevice ($ioHash, $devspec, 7)); - my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec); + my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($ioHash, $devspec); return 1 if (!defined ($da)); $gdname = $dn; @@ -218,7 +218,7 @@ sub HMCCUDEV_InitDevice ($$) my @devlist = (); if (exists ($dev_hash->{hmccu}{groupexp})) { # Group devices specified by name expression - $gdcount = HMCCU_GetMatchingDevices ($hmccu_hash, $dev_hash->{hmccu}{groupexp}, 'dev', \@devlist); + $gdcount = HMCCU_GetMatchingDevices ($ioHash, $dev_hash->{hmccu}{groupexp}, 'dev', \@devlist); return 4 if ($gdcount == 0); } elsif (exists ($dev_hash->{hmccu}{group})) { @@ -228,9 +228,9 @@ sub HMCCUDEV_InitDevice ($$) foreach my $gd (@gdevlist) { my ($gda, $gdc, $gdo) = ('', '', '', ''); - return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $gd, 7)); + return 1 if (!HMCCU_IsValidDevice ($ioHash, $gd, 7)); - ($gda, $gdc) = HMCCU_GetAddress ($hmccu_hash, $gd, '', ''); + ($gda, $gdc) = HMCCU_GetAddress ($ioHash, $gd, '', ''); $gdo = $gda; $gdo .= ':'.$gdc if ($gdc ne ''); push @devlist, $gdo; @@ -239,7 +239,7 @@ sub HMCCUDEV_InitDevice ($$) } else { # Group specified by CCU virtual group name - @devlist = HMCCU_GetGroupMembers ($hmccu_hash, $gdname); + @devlist = HMCCU_GetGroupMembers ($ioHash, $gdname); $gdcount = scalar (@devlist); } @@ -248,10 +248,10 @@ sub HMCCUDEV_InitDevice ($$) $dev_hash->{ccugroup} = join (',', @devlist); if ($devspec eq 'virtual') { my $dev = shift @devlist; - my $devtype = HMCCU_GetDeviceType ($hmccu_hash, $dev, 'n/a'); + my $devtype = HMCCU_GetDeviceType ($ioHash, $dev, 'n/a'); my $devna = $devtype eq 'n/a' ? 1 : 0; for my $d (@devlist) { - if (HMCCU_GetDeviceType ($hmccu_hash, $d, 'n/a') ne $devtype) { + if (HMCCU_GetDeviceType ($ioHash, $d, 'n/a') ne $devtype) { $devna = 1; last; } @@ -261,11 +261,11 @@ sub HMCCUDEV_InitDevice ($$) if ($devna) { $dev_hash->{ccutype} = 'n/a'; $dev_hash->{statevals} = 'readonly'; - $rc = HMCCU_CreateDevice ($hmccu_hash, $dev_hash->{ccuaddr}, $name, undef, $dev); + $rc = HMCCU_CreateDevice ($ioHash, $dev_hash->{ccuaddr}, $name, undef, $dev); } else { $dev_hash->{ccutype} = $devtype; - $rc = HMCCU_CreateDevice ($hmccu_hash, $dev_hash->{ccuaddr}, $name, $devtype, $dev); + $rc = HMCCU_CreateDevice ($ioHash, $dev_hash->{ccuaddr}, $name, $devtype, $dev); } return $rc+4 if ($rc > 0); @@ -275,7 +275,7 @@ sub HMCCUDEV_InitDevice ($$) } # Inform HMCCU device about client device - return 2 if (!HMCCU_AssignIODevice ($dev_hash, $hmccu_hash->{NAME}, undef)); + return 2 if (!HMCCU_AssignIODevice ($dev_hash, $ioHash->{NAME}, undef)); # readingsSingleUpdate ($dev_hash, "state", "Initialized", 1); $dev_hash->{ccudevstate} = 'active'; @@ -347,8 +347,8 @@ sub HMCCUDEV_Set ($@) # Get I/O device, check device state return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' || !defined ($hash->{IODev})); - my $hmccu_hash = $hash->{IODev}; - my $hmccu_name = $hmccu_hash->{NAME}; + my $ioHash = $hash->{IODev}; + my $hmccu_name = $ioHash->{NAME}; # Handle read only and disabled devices return undef if ($hash->{statevals} eq 'readonly' && $opt ne '?' @@ -357,7 +357,7 @@ sub HMCCUDEV_Set ($@) return undef if ($disable == 1); # Check if CCU is busy - if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) { + if (HMCCU_IsRPCStateBlocking ($ioHash)) { return undef if ($opt eq '?'); return "HMCCUDEV: CCU busy"; } @@ -670,15 +670,15 @@ sub HMCCUDEV_Get ($@) # Get I/O device return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' || !defined ($hash->{IODev})); - my $hmccu_hash = $hash->{IODev}; - my $hmccu_name = $hmccu_hash->{NAME}; + my $ioHash = $hash->{IODev}; + my $hmccu_name = $ioHash->{NAME}; # Handle disabled devices my $disable = AttrVal ($name, "disable", 0); return undef if ($disable == 1); # Check if CCU is busy - if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) { + if (HMCCU_IsRPCStateBlocking ($ioHash)) { return undef if ($opt eq '?'); return "HMCCUDEV: CCU busy"; } @@ -816,43 +816,55 @@ sub HMCCUDEV_Get ($@) } return $res; } - elsif ($opt eq 'configdesc') { - my $ccuobj = $ccuaddr; - my $par = shift @$a; - if (defined ($par)) { - if ($par =~ /^([0-9]{1,2})$/) { - return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels}); - $ccuobj .= ':'.$1; - } - else { - return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels}); + elsif ($opt eq 'paramsetdesc') { + my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($ccuaddr); + my $model = HMCCU_GetClientDeviceModel ($hash); + return HMCCU_SetError ($hash, "Can't get device model") if (!defined($model)); + + my $res = ''; + my @chnList = sort keys %{$model}; + unshift (@chnList, pop(@chnList)) if (exists($model->{'d'})); + foreach my $c (@chnList) { + $res .= $c eq 'd' ? "Device\n" : "Channel $c\n"; + foreach my $ps (sort keys %{$model->{$c}}) { + $res .= " Paramset $ps\n"; + $result = join ("\n", map { + " ".$_.": ". + $model->{$c}{$ps}{$_}{TYPE}. + " [".HMCCU_FlagsToStr ('model', 'OPERATIONS', $model->{$c}{$ps}{$_}{OPERATIONS}, ',', '')."]". + " [".HMCCU_FlagsToStr ('model', 'FLAGS', $model->{$c}{$ps}{$_}{FLAGS}, ',', '')."]". + " RANGE=".$model->{$c}{$ps}{$_}{MIN}."-".$model->{$c}{$ps}{$_}{MAX}. + " DFLT=".$model->{$c}{$ps}{$_}{DEFAULT}. + " UNIT=".$model->{$c}{$ps}{$_}{UNIT} + } sort keys %{$model->{$c}{$ps}}); + $res .= "$result\n"; } } - - my $res = "MASTER:\n"; - ($rc, $result) = HMCCU_RPCRequest ($hash, "getParamsetDescription", $ccuobj, "MASTER", undef); - return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0); - $res .= "$result\nLINK:\n"; - ($rc, $result) = HMCCU_RPCRequest ($hash, "getParamsetDescription", $ccuobj, "MASTER", undef); - return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0); - HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error"); - return $res.$result; + return $res; } elsif ($opt eq 'devicedesc') { - my $ccuobj = $ccuaddr; - my $par = shift @$a; - if (defined ($par)) { - if ($par =~ /^([0-9]{1,2})$/) { - return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels}); - $ccuobj .= ':'.$1; - } - else { - return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels}); - } - } + my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $ccuaddr, $ccuif); + return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc)); + + my @addList = (); + push (@addList, split (',', $devDesc->{CHILDREN})) + if (defined($devDesc->{CHILDREN}) && $devDesc->{CHILDREN} ne ''); - ($rc, $result) = HMCCU_RPCRequest ($hash, "getDeviceDescription", $ccuobj, "MASTER", undef); - return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0); + my $a = $ccuaddr; + $result = ''; + while (1) { + $result .= $a eq $ccuaddr ? "Device $a" : "Channel $a"; + $result .= " $devDesc->{_name} [$devDesc->{TYPE}]\n"; + foreach my $n (sort keys %{$devDesc}) { + next if ($n =~ /^_/ || $n eq 'ADDRESS' || $n eq 'TYPE' || + !defined($devDesc->{$n}) || $devDesc->{$n} eq ''); + $result .= " $n: ".HMCCU_FlagsToStr ('device', $n, $devDesc->{$n}, ',', '')."\n"; + } + $a = shift (@addList); + last if (!defined($a)); + $devDesc = HMCCU_GetDeviceDesc ($ioHash, $a, $ccuif); + return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc)); + } return $result; } elsif ($opt eq 'defaults') { @@ -866,7 +878,7 @@ sub HMCCUDEV_Get ($@) my $valuecount = HMCCU_GetValidDatapoints ($hash, $ccutype, -1, 1, \@valuelist); $retmsg .= ":".join(",", @valuelist) if ($valuecount > 0); - $retmsg .= " defaults:noArg update:noArg config configlist configdesc devicedesc". + $retmsg .= " defaults:noArg update:noArg config configlist paramsetdesc:noArg devicedesc:noArg". " deviceinfo:noArg"; $retmsg .= ' devstate:noArg' if ($sc ne ''); diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm index d7c392d95..b9d861761 100755 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm @@ -404,13 +404,13 @@ sub HMCCURPCPROC_InitDevice ($$) { } # Read RPC device descriptions - if ($dev_hash->{rpcinterface} ne 'CUxD') { - HMCCU_Log ($dev_hash, 1, "Updating internal device tables"); - HMCCU_ResetDeviceTables ($hmccu_hash, $dev_hash->{rpcinterface}); - my $cd = HMCCURPCPROC_GetDeviceDesc ($dev_hash); - my $cm = HMCCURPCPROC_GetParamsetDesc ($dev_hash); - HMCCU_Log ($dev_hash, 1, "Read $cd channel and device descriptions and $cm device models from CCU"); - } + if ($dev_hash->{rpcinterface} ne 'CUxD') { + HMCCU_Log ($dev_hash, 1, "Updating internal device tables"); + HMCCU_ResetDeviceTables ($hmccu_hash, $dev_hash->{rpcinterface}); + my $cd = HMCCURPCPROC_GetDeviceDesc ($dev_hash); + my $cm = HMCCURPCPROC_GetParamsetDesc ($dev_hash); + HMCCU_Log ($dev_hash, 1, "Read $cd channel and device descriptions and $cm device models from CCU"); + } # RPC device ready HMCCURPCPROC_ResetRPCState ($dev_hash);