diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm index 47c9311c0..51c2293f3 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm @@ -226,6 +226,7 @@ sub HMCCU_FilterReading ($$$); sub HMCCU_FormatReadingValue ($$$); sub HMCCU_GetReadingName ($$$$$$$;$); sub HMCCU_ScaleValue ($$$$$); +sub HMCCU_StripNumber ($$); sub HMCCU_Substitute ($$$$$;$$); sub HMCCU_SubstRule ($$$); sub HMCCU_SubstVariables ($$$); @@ -269,6 +270,7 @@ sub HMCCU_SplitDatapoint ($;$); # FHEM device handling functions sub HMCCU_AssignIODevice ($$$); +sub HMCCU_ExistsClientDevice ($$); sub HMCCU_FindClientDevices ($$$$); sub HMCCU_FindIODevice ($); sub HMCCU_GetHash ($@); @@ -319,8 +321,10 @@ sub HMCCU_IsValidDevice ($$$); sub HMCCU_IsValidDeviceOrChannel ($$$); sub HMCCU_ParamsetDescToStr ($$); sub HMCCU_RemoveDevice ($$$;$); +sub HMCCU_RenameDevice ($$$); sub HMCCU_ResetDeviceTables ($;$$); sub HMCCU_UpdateDevice ($$); +sub HMCCU_UpdateDeviceRoles ($$;$$); sub HMCCU_UpdateDeviceTable ($$); # Handle datapoints @@ -330,6 +334,7 @@ sub HMCCU_GetDatapointAttr ($$$$$); sub HMCCU_GetDatapointCount ($$$); sub HMCCU_GetDatapointList ($$$); sub HMCCU_GetSpecialDatapoints ($$$$$); +sub HMCCU_GetStateValues ($$;$); sub HMCCU_GetSwitchDatapoint ($$$); sub HMCCU_GetValidDatapoints ($$$$$); sub HMCCU_IsValidDatapoint ($$$$$); @@ -365,10 +370,12 @@ sub HMCCU_CalculateReading ($$); sub HMCCU_CorrectName ($); sub HMCCU_Encrypt ($); sub HMCCU_Decrypt ($); +sub HMCCU_DefStr ($;$$); sub HMCCU_DeleteReadings ($$); sub HMCCU_EncodeEPDisplay ($); sub HMCCU_ExprMatch ($$$); sub HMCCU_ExprNotMatch ($$$); +sub HMCCU_GetDeviceStates ($); sub HMCCU_GetDutyCycle ($); sub HMCCU_GetHMState ($$$); sub HMCCU_GetIdFromIP ($$); @@ -2009,9 +2016,8 @@ sub HMCCU_Get ($@) } elsif ($optcmd eq 'create') { $usage = "Usage: get $name create {devexp|chnexp} [t={'chn'|'dev'|'all'}] [s=suffix] ". - "[p=prefix] [f=format] ['defattr'] ['duplicates'] [save] [attr=val [...]]"; + "[p=prefix] [f=format] ['defattr'] [save] [attr=val [...]]"; my $devdefaults = 0; - my $duplicates = 0; my $savedef = 0; my $newcount = 0; @@ -2025,7 +2031,6 @@ sub HMCCU_Get ($@) if ($devtype !~ /^(dev|chn|all)$/ || !defined ($devspec)); foreach my $defopt (@$a) { if ($defopt eq 'defattr') { $devdefaults = 1; } - elsif ($defopt eq 'duplicates') { $duplicates = 1; } elsif ($defopt eq 'save') { $savedef = 1; } else { return HMCCU_SetError ($hash, $usage); } } @@ -2049,17 +2054,15 @@ sub HMCCU_Get ($@) $devname =~ s/[^A-Za-z\d_\.]+/_/g; # Check for duplicate device definitions - if (!$duplicates) { - next if (exists ($defs{$devname})); - my $devexists = 0; - foreach my $exdev (@devlist) { - if ($defs{$exdev}->{ccuaddr} eq $add) { - $devexists = 1; - last; - } + next if (exists ($defs{$devname})); + my $devexists = 0; + foreach my $exdev (@devlist) { + if ($defs{$exdev}->{ccuaddr} eq $add) { + $devexists = 1; + last; } - next if ($devexists); } + next if ($devexists); # Define new client device my $ret = CommandDefine (undef, $devname." $defmod ".$add); @@ -2501,6 +2504,7 @@ sub HMCCU_GetReadingName ($$$$$$$;$) { my ($hash, $i, $a, $c, $d, $n, $rf, $ps) = @_; my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; my %prefix = ( 'MASTER' => 'R-', 'LINK' => 'L-', 'VALUES' => '', 'SERVICE' => 'S-', 'PEER' => 'P-' ); @@ -2543,7 +2547,7 @@ sub HMCCU_GetReadingName ($$$$$$$;$) } if ($rf eq 'datapoint' || $rf =~ /^datapoint(lc|uc)$/) { - $rn = $c ne '' ? $c.'.'.$d : $d; + $rn = $c ne '' && $type ne 'HMCCUCHN' ? $c.'.'.$d : $d; } elsif ($rf eq 'name' || $rf =~ /^name(lc|uc)$/) { return () if ($n eq ''); @@ -2571,24 +2575,22 @@ sub HMCCU_GetReadingName ($$$$$$$;$) push (@rnlist, $rpf.$rn); # Rename and/or add reading names - if ($sr ne '') { - my @rules = split (';', $sr); - foreach my $rr (@rules) { - my ($rold, $rnew) = split (':', $rr); - next if (!defined ($rnew)); - my @rnewList = split (',', $rnew); - next if (scalar (@rnewList) < 1); - if ($rnlist[0] =~ /$rold/) { - foreach my $rnew (@rnewList) { - if ($rnew =~ /^\+(.+)$/) { - my $radd = $1; - $radd =~ s/$rold/$radd/; - push (@rnlist, $radd); - } - else { - $rnlist[0] =~ s/$rold/$rnew/; - last; - } + my @rules = split (';', $sr); + foreach my $rr (@rules) { + my ($rold, $rnew) = split (':', $rr); + next if (!defined ($rnew)); + my @rnewList = split (',', $rnew); + next if (scalar (@rnewList) < 1); + if ($rnlist[0] =~ /$rold/) { + foreach my $rnew (@rnewList) { + if ($rnew =~ /^\+(.+)$/) { + my $radd = $1; + $radd =~ s/$rold/$radd/; + push (@rnlist, $radd); + } + else { + $rnlist[0] =~ s/$rold/$rnew/; + last; } } } @@ -2620,6 +2622,10 @@ sub HMCCU_FormatReadingValue ($$$) my $name = $hash->{NAME}; my $fnc = "FormatReadingValue"; + if (!defined($value)) { + HMCCU_Trace ($hash, 2, $fnc, "Value undefined for datapoint $dpt"); + return $value; + } my $stripnumber = HMCCU_GetAttrStripNumber ($hash); if ($stripnumber ne 'null' && $value =~ /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/) { @@ -2651,6 +2657,27 @@ sub HMCCU_FormatReadingValue ($$$) return $value; } +###################################################################### +# Format number +###################################################################### + +sub HMCCU_StripNumber ($$) +{ + my ($value, $strip) = @_; + + if ($value =~ /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/) { + my $isint = $value =~ /^[+-]?[0-9]+$/ ? 1 : 0; + + if ($strip eq '0' && !$isint) { return sprintf ("%d", $value); } + elsif ($strip eq '1' && !$isint) { return sprintf ("%.1f", $value); } + elsif ($strip eq '2' && !$isint) { return sprintf ("%g", $value); } + elsif ($strip =~ /^-([0-9])$/ && !$isint) { my $f = '%.'.$1.'f'; return sprintf ($f, $value); } + elsif ($strip =~ /^%.+$/) { return sprintf ($strip, $value); } + } + + return $value; +} + ###################################################################### # Log message if trace flag is set. # Will output multiple log file entries if parameter msg is separated @@ -2881,6 +2908,7 @@ sub HMCCU_SetRPCState ($@) # Substitute first occurrence of regular expression or fixed string. # Floating point values are ignored without datapoint specification. # Integer values are compared with complete value. +# $hashOrRule - # $mode - 0=Substitute regular expression, 1=Substitute text # $chn - A channel number. Ignored if $dpt contains a Channel # number. @@ -2892,28 +2920,48 @@ sub HMCCU_SetRPCState ($@) sub HMCCU_Substitute ($$$$$;$$) { - my ($value, $substrule, $mode, $chn, $dpt, $type, $devDesc) = @_; + my ($value, $hashOrRule, $mode, $chn, $dpt, $type, $devDesc) = @_; + + my $substrule = ''; + my $ioHash; + + if (defined($hashOrRule)) { + if (ref($hashOrRule) eq 'HASH') { + $ioHash = HMCCU_GetHash ($hashOrRule); + my $substitute = HMCCU_GetAttrSubstitute ($hashOrRule, $ioHash) + if (defined($ioHash)); + } + else { + $substrule = $hashOrRule; + } + } - $substrule = '' if (!defined($substrule)); my $rc = 0; my $newvalue; my %conversion = ( 'SHUTTER_CONTACT' => { - 'STATE' => { '0' => 'open', '1' => 'closed', 'false' => 'open', 'true' => 'closed' } + 'STATE' => { '0' => 'closed', '1' => 'open', 'false' => 'closed', 'true' => 'open' } + }, + 'SWITCH' => { + 'STATE' => { '0' => 'off', 'false' => 'off', '1' => 'on', 'true' => 'on', 'off' => '0', 'on' => '1' }, }, 'BLIND' => { - 'LEVEL' => { '0' => 'closed', '100' => 'open' } + 'LEVEL' => { '0' => 'closed', '100' => 'open', 'close' => '0', 'open' => '100' } }, 'DIMMER' => { - 'LEVEL' => { '0' => 'off', '100' => 'on' } + 'LEVEL' => { '0' => 'off', '100' => 'on', 'off' => '0', 'on' => '100' } }, 'DEFAULT' => { - 'STATE' => { '0' => 'false', '1' => 'true' } + 'AES_KEY' => { '0' => 'off', 'false' => 'off', '1' => 'on', 'true' => 'on' }, + 'LOW_BAT' => { '0' => 'ok', 'false' => 'ok', '1' => 'low', 'true' => 'low' }, + 'LOWBAT' => { '0' => 'ok', 'false' => 'ok', '1' => 'low', 'true' => 'low' }, + 'STATE' => { '0' => 'false', '1' => 'true' }, + 'UNREACH' => { '0' => 'alive', 'false' => 'alive', '1' => 'dead', 'true' => 'dead' } } ); - return $value if (!defined ($substrule) || $substrule eq ''); +# return $value if (!defined ($substrule) || $substrule eq ''); # Remove channel number from datapoint if specified if ($dpt =~ /^([0-9]{1,2})\.(.+)$/) { @@ -2952,19 +3000,34 @@ sub HMCCU_Substitute ($$$$$;$$) # Original value not modified by rules. Use default conversion depending on type/role # Default conversion can be overriden by attribute ccudef-substitute in I/O device + + # Substitute enumerations + if (defined($devDesc) && defined($ioHash)) { + my $paramDef = HMCCU_GetParamDef ($ioHash, $devDesc, 'VALUES', $dpt); + if (!defined($paramDef) && $paramDef->{TYPE} eq 'ENUM' && defined($paramDef->{VALUE_LIST})) { + my $i = defined($paramDef->{MIN}) ? $paramDef->{MIN} : 0; + if ($mode) { + my %enumVals = map { $_ => $i++ } split(',', $paramDef->{VALUE_LIST}); + return $enumVals{$value} if (exists($enumVals{$value})); + } + else { + my @enumList = split(',', $paramDef->{VALUE_LIST}); + my $idx = $value-$i; + return $enumList[$idx] if ($idx >= 0 && $idx < scalar(@enumList)); + } + } + } + if ((!defined($type) || $type eq '') && defined($devDesc) && defined($devDesc->{INDEX})) { $type = $devDesc->{TYPE}; } + $type = 'DEFAULT' if (!defined($type) || $type eq ''); - if (defined($type) && $type ne '') { - if (exists($conversion{$type})) { - if (exists($conversion{$type}{$dpt}) && exists($conversion{$type}{$dpt}{$value})) { - return $conversion{$type}{$dpt}{$value}; - } - } - elsif (exists($conversion{DEFAULT}{$dpt}) && exists($conversion{DEFAULT}{$dpt}{$value})) { - return $conversion{DEFAULT}{$dpt}{$value}; - } + if (exists($conversion{$type}{$dpt}{$value})) { + return $conversion{$type}{$dpt}{$value}; + } + elsif (exists($conversion{DEFAULT}{$dpt}{$value})) { + return $conversion{DEFAULT}{$dpt}{$value}; } return $value; @@ -3522,7 +3585,7 @@ sub HMCCU_UpdateDevice ($$) my @rcvNames = HMCCU_GetDeviceIdentifier ($ioHash, $r, $iface); my $rcvFlags = HMCCU_FlagsToStr ('peer', 'FLAGS', $ioHash->{hmccu}{snd}{$iface}{$da}{$c}{$r}{FLAGS}, ','); - push @rcvList, map { $_." [".$rcvFlags."]" } @rcvNames; + push @rcvList, map { $_.($rcvFlags ne 'OK' ? " [".$rcvFlags."]" : '') } @rcvNames; } } @@ -3532,15 +3595,30 @@ sub HMCCU_UpdateDevice ($$) my @sndNames = HMCCU_GetDeviceIdentifier ($ioHash, $s, $iface); my $sndFlags = HMCCU_FlagsToStr ('peer', 'FLAGS', $ioHash->{hmccu}{snd}{$iface}{$da}{$c}{$s}{FLAGS}, ','); - push @sndList, map { $_." [".$sndFlags."]" } @sndNames; + push @sndList, map { $_.($sndFlags ne 'OK' ? " [".$sndFlags."]" : '') } @sndNames; } } $clHash->{sender} = join (',', @sndList) if (scalar(@sndList) > 0); $clHash->{receiver} = join (',', @rcvList) if (scalar(@rcvList) > 0); } +} + +###################################################################### +# Update device roles +###################################################################### + +sub HMCCU_UpdateDeviceRoles ($$;$$) +{ + my ($ioHash, $clHash, $iface, $address) = @_; + + my $clType = $clHash->{TYPE}; + return if ($clType ne 'HMCCUCHN'); + + $iface = $clHash->{ccuif} if (!defined($iface)); + $address = $clHash->{ccuaddr} if (!defined($address)); + return if (!defined($address)); - # Update channel roles my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $address, $iface); if (defined($devDesc)) { if ($clType eq 'HMCCUCHN' && defined($devDesc->{TYPE})) { @@ -3556,7 +3634,39 @@ sub HMCCU_UpdateDevice ($$) } $clHash->{ccurole} = join(',', @roles) if (scalar(@roles) > 0); } + } +} + +###################################################################### +# Rename a client device +###################################################################### + +sub HMCCU_RenameDevice ($$$) +{ + my ($ioHash, $clHash, $oldName) = @_; + + return 0 if (!defined($ioHash) || !defined($clHash) || !exists($clHash->{ccuif}) || + !exists($clHash->{ccuaddr})); + + my $name = $clHash->{NAME}; + my $iface = $clHash->{ccuif}; + my $address = $clHash->{ccuaddr}; + + return 0 if (!exists($ioHash->{hmccu}{device}{$iface}{$address})); + + if (exists($ioHash->{hmccu}{device}{$iface}{$address}{_fhem})) { + my @devList = grep { $_ ne $oldName } split(',', $ioHash->{hmccu}{device}{$iface}{$address}{_fhem}); + push @devList, $name; + $ioHash->{hmccu}{device}{$iface}{$address}{_fhem} = join(',', @devList); } + else { + $ioHash->{hmccu}{device}{$iface}{$address}{_fhem} = $name; + } + + # Update links, but not the roles + HMCCU_UpdateDevice ($ioHash, $clHash); + + return 1; } ###################################################################### @@ -3617,6 +3727,7 @@ sub HMCCU_GetDeviceConfig ($) # Update FHEM devices foreach my $d (@devList) { HMCCU_UpdateDevice ($ioHash, $defs{$d}); + HMCCU_UpdateDeviceRoles ($ioHash, $defs{$d}); } return ($cDev, $cPar, $cLnk); @@ -3718,12 +3829,13 @@ sub HMCCU_GetDeviceIdentifier ($$;$$) my @idList = (); my ($da, $dc) = HMCCU_SplitChnAddr ($address); - my $c = defined($chnNo) ? $chnNo : ''; +# my $c = defined($chnNo) ? ' #'.$chnNo : ''; + my $c = ''; my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $address, $iface); if (defined($devDesc)) { if (defined($devDesc->{_fhem})) { - push @idList, map { $_." #".$c } split(',', $devDesc->{_fhem}); + push @idList, map { $_.$c } split(',', $devDesc->{_fhem}); } elsif (defined($devDesc->{PARENT}) && $devDesc->{PARENT} ne '') { push @idList, HMCCU_GetDeviceIdentifier ($ioHash, $devDesc->{PARENT}, $iface, $dc); @@ -3786,6 +3898,7 @@ sub HMCCU_DeviceDescToStr ($$) ###################################################################### # Convert parameter set description to string +# Parameter $object can be an address or a reference to a client hash. ###################################################################### sub HMCCU_ParamsetDescToStr ($$) @@ -3806,7 +3919,9 @@ sub HMCCU_ParamsetDescToStr ($$) my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($address); - my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $address, $iface); +# BUG? +# my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $address, $iface); + my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $devAddr, $iface); return undef if (!defined($devDesc)); my $model = HMCCU_GetDeviceModel ($ioHash, $devDesc->{_model}, $devDesc->{_fw_ver}); return undef if (!defined($model)); @@ -3829,9 +3944,11 @@ sub HMCCU_ParamsetDescToStr ($$) $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} + " RANGE=".HMCCU_StripNumber ($model->{$c}{$ps}{$_}{MIN}, 2). + "...".HMCCU_StripNumber ($model->{$c}{$ps}{$_}{MAX}, 2). + " DFLT=".HMCCU_StripNumber ($model->{$c}{$ps}{$_}{DEFAULT}, 2). + HMCCU_DefStr ($model->{$c}{$ps}{$_}{UNIT}, " UNIT="). + HMCCU_DefStr ($model->{$c}{$ps}{$_}{VALUE_LIST}, " VALUES=") } sort keys %{$model->{$c}{$ps}})."\n"; } } @@ -3993,6 +4110,7 @@ sub HMCCU_GetClientDeviceModel ($;$) my ($clHash, $chnNo) = @_; return undef if ($clHash->{TYPE} ne 'HMCCUCHN' && $clHash->{TYPE} ne 'HMCCUDEV'); + return undef if (!defined($clHash->{ccuaddr})); my $ioHash = HMCCU_GetHash ($clHash); my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $clHash->{ccuaddr}, $clHash->{ccuif}); @@ -4222,6 +4340,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) { my ($ioHash, $clHash, $objects, $addListRef) = @_; + my $ioName = $ioHash->{NAME}; my $clName = $clHash->{NAME}; my $clType = $clHash->{TYPE}; @@ -4235,6 +4354,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) my @chKeys = (); # Check if update of device allowed + my $ccuflags = HMCCU_GetFlags ($ioName); my $disable = AttrVal ($clName, 'disable', 0); my $update = AttrVal ($clName, 'ccureadings', HMCCU_IsFlag ($clName, 'noReadings') ? 0 : 1); return undef if ($update == 0 || $disable == 1 || $clHash->{ccudevstate} ne 'active'); @@ -4247,9 +4367,6 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) 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); @@ -4275,7 +4392,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) # 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)); + next if (!defined($v)); my $fv = $v; my $cv = $v; my $sv; @@ -4292,7 +4409,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) # 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, $devDesc); + $cv = HMCCU_Substitute ($fv, $clHash, 0, $c, $p, $chnType, $devDesc); $cv = HMCCU_GetParamValue ($ioHash, $devDesc, $ps, $p, $fv) if ("$fv" eq "$cv"); HMCCU_UpdateInternalValues ($clHash, $chKey, 'SVAL', $cv); @@ -4315,10 +4432,11 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) # 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); + if (HMCCU_FilterReading ($clHash, $chnAddr, $p) && ("$c" ne "0" || $clFlags =~ /devState/)) { + my @rnList = HMCCU_GetReadingName ($clHash, $clInt, $a, $c, $p, '', $clRF, $ps); + foreach my $rn (@rnList) { + HMCCU_BulkUpdate ($clHash, $rn, $fv, $cv); + } } } } @@ -4332,6 +4450,18 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) HMCCU_BulkUpdate ($clHash, $cr, $calc{$cr}, $calc{$cr}); } } + + # Update device states + my ($devState, $battery, $activity) = HMCCU_GetDeviceStates ($clHash); + HMCCU_BulkUpdate ($clHash, 'battery', $battery, $battery); + HMCCU_BulkUpdate ($clHash, 'activity', $activity, $activity); + HMCCU_BulkUpdate ($clHash, 'devstate', $devState, $devState); + + # Calculate and update HomeMatic state + if ($ccuflags !~ /nohmstate/) { + my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($clName, $ioName, undef); + HMCCU_BulkUpdate ($clHash, $hms_read, $hms_val, $hms_val) if (defined ($hms_val)); + } readingsEndUpdate ($clHash, 1); @@ -4374,7 +4504,6 @@ sub HMCCU_UpdateSingleDevice ($$$$) my $cf = HMCCU_GetFlags ($cltname); my $peer = AttrVal ($cltname, 'peer', 'null'); my $crf = HMCCU_GetAttrReadingFormat ($clthash, $ccuhash); - my $substitute = HMCCU_GetAttrSubstitute ($clthash, $ccuhash); my ($sc, $st, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clthash, '', 'STATE', '', ''); # Virtual device flag @@ -4426,7 +4555,7 @@ sub HMCCU_UpdateSingleDevice ($$$$) my @readings = HMCCU_GetReadingName ($clthash, '', $addr, $chnnum, $dpt, '', $crf); my $svalue = HMCCU_ScaleValue ($clthash, $chnnum, $dpt, $value, 0); my $fvalue = HMCCU_FormatReadingValue ($clthash, $svalue, $dpt); - my $cvalue = HMCCU_Substitute ($fvalue, $substitute, 0, $chnnum, $dpt, $chnType, $devDesc); + my $cvalue = HMCCU_Substitute ($fvalue, $clthash, 0, $chnnum, $dpt, $chnType, $devDesc); $cvalue = HMCCU_GetParamValue ($ccuhash, $devDesc, 'VALUES', $dpt, $fvalue) if ("$fvalue" eq "$cvalue"); # my %calcs = HMCCU_CalculateReading ($clthash, $chkey); @@ -4442,8 +4571,10 @@ sub HMCCU_UpdateSingleDevice ($$$$) ", orgvalue=$value value=$cvalue peer=$peer"); # Update readings - foreach my $rn (@readings) { - HMCCU_BulkUpdate ($clthash, $rn, $fvalue, $cvalue) if ($rn ne ''); + if ("$chnnum" ne "0" || $cf =~ /devState/) { + foreach my $rn (@readings) { + HMCCU_BulkUpdate ($clthash, $rn, $fvalue, $cvalue) if ($rn ne ''); + } } # foreach my $clcr (keys %calcs) { # HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr}); @@ -4466,6 +4597,12 @@ sub HMCCU_UpdateSingleDevice ($$$$) HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr}); } } + + # Update device states + my ($devState, $battery, $activity) = HMCCU_GetDeviceStates ($clthash); + HMCCU_BulkUpdate ($clthash, 'battery', $battery, $battery); + HMCCU_BulkUpdate ($clthash, 'activity', $activity, $activity); + HMCCU_BulkUpdate ($clthash, 'devstate', $devState, $devState); # Calculate and update HomeMatic state if ($ccuflags !~ /nohmstate/) { @@ -6385,6 +6522,25 @@ sub HMCCU_FindClientDevices ($$$$) return @devlist; } +###################################################################### +# Check if client device already exists +###################################################################### + +sub HMCCU_ExistsClientDevice ($$) +{ + my ($devSpec, $type) = @_; + + foreach my $d (keys %defs) { + my $clHash = $defs{$d}; + return $clHash->{NAME} if (defined($clHash->{TYPE}) && $clHash->{TYPE} eq $type && + (defined($clHash->{ccuaddr}) && $clHash->{ccuaddr} eq $devSpec || + defined($clHash->{ccuname}) && $clHash->{ccuname} eq $devSpec) + ); + } + + return undef; +} + ###################################################################### # Get name of assigned client device of type HMCCURPCPROC. # Create a RPC device of type HMCCURPCPROC if none is found and @@ -6619,6 +6775,35 @@ sub HMCCU_GetDatapointCount ($$$) } } +###################################################################### +# Get state values of client device +# Return '' if no state values available +###################################################################### + +sub HMCCU_GetStateValues ($$;$) +{ + my ($clHash, $dpt, $oper) = @_; + $oper = 2 if (!defined($oper)); + + my %stateValues = ( + "STATE:BOOL" => "on:true,off:false", + "LEVEL:FLOAT" => "on:100,off:0", + "PRESS_SHORT:ACTION" => "on:true,press:true" + ); + + my $ioHash = HMCCU_GetHash ($clHash); + return '' if (!defined($ioHash)); + + my $sv = AttrVal ($clHash->{NAME}, 'statevals', ''); + if ($sv eq '') { + my $paramDef = HMCCU_GetParamDef ($ioHash, $clHash->{ccuaddr}, 'VALUES', $dpt); + return $stateValues{"$dpt:$paramDef->{TYPE}"} if (defined($paramDef) && + $paramDef->{OPERATIONS} & $oper && exists($stateValues{"$dpt:$paramDef->{TYPE}"})); + } + + return $sv; +} + ###################################################################### # Get channels and datapoints from attributes statechannel, # statedatapoint and controldatapoint. @@ -6628,15 +6813,16 @@ sub HMCCU_GetDatapointCount ($$$) # datapoint name. # If controldatapoint is not specified it will synchronized with # statedatapoint. +# Return (sc, sd, cc, cd) ###################################################################### sub HMCCU_GetSpecialDatapoints ($$$$$) { -# my ($hash, $defsc, $defsd, $defcc, $defcd) = @_; my ($hash, $sc, $sd, $cc, $cd) = @_; my $name = $hash->{NAME}; my $type = $hash->{TYPE}; - + my $ccutype = $hash->{ccutype}; + my $statedatapoint = AttrVal ($name, 'statedatapoint', ''); my $statechannel = AttrVal ($name, 'statechannel', ''); my $controldatapoint = AttrVal ($name, 'controldatapoint', $statedatapoint); @@ -6659,25 +6845,30 @@ sub HMCCU_GetSpecialDatapoints ($$$$$) $cd = $controldatapoint; } } - + # For devices of type HMCCUCHN extract channel numbers from CCU device address if ($type eq 'HMCCUCHN') { $sc = $hash->{ccuaddr}; $sc =~ s/^[\*]*[0-9A-Z]+://; $cc = $sc; + if ($sd eq '') { + if (HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, 'STATE', 3)) { $sd = 'STATE'; } + elsif (HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, 'LEVEL', 3)) { $sd = 'LEVEL'; } + elsif (HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, 'PRESS_SHORT', 3)) { $sd = 'PRESS_SHORT'; } + } } - - # Try to find state channel - my $c = -1; - if ($sc eq '' && $sd ne '') { - $c = HMCCU_FindDatapoint ($hash, $hash->{ccutype}, -1, $sd, 3); - $sc = $c if ($c >= 0); - } - - # Try to find control channel - if ($cc eq '' && $cd ne '') { - $c = HMCCU_FindDatapoint ($hash, $hash->{ccutype}, -1, $cd, 3); - $cc = $c if ($c >= 0); + else { + # Try to find state channel + my $c = -1; + if ($sc eq '' && $sd ne '') { + $c = HMCCU_FindDatapoint ($hash, $type, -1, $sd, 3); + $sc = $c if ($c >= 0); + } + # Try to find control channel + if ($cc eq '' && $cd ne '') { + $c = HMCCU_FindDatapoint ($hash, $type, -1, $cd, 3); + $cc = $c if ($c >= 0); + } } # By default set control channel and datapoint to state channel and datapoint @@ -6748,16 +6939,16 @@ sub HMCCU_GetAttrStripNumber ($) my ($hash) = @_; my $fnc = "GetAttrStripNumber"; - my $snDef = 'null'; + my $snDef = '1'; if ($hash->{TYPE} ne 'HMCCU') { my $ioHash = HMCCU_GetHash ($hash); if (defined ($ioHash)) { - $snDef = AttrVal ($ioHash->{NAME}, 'ccudef-stripnumber', 'null'); + $snDef = AttrVal ($ioHash->{NAME}, 'ccudef-stripnumber', $snDef); } } else { - $snDef = AttrVal ($hash->{NAME}, 'ccudef-stripnumber', 'null'); + $snDef = AttrVal ($hash->{NAME}, 'ccudef-stripnumber', $snDef); } my $stripnumber = AttrVal ($hash->{NAME}, 'stripnumber', $snDef); @@ -8307,6 +8498,57 @@ sub HMCCU_QueueDeq ($) # *** HELPER FUNCTIONS *** ###################################################################### +sub HMCCU_DefStr ($;$$) +{ + my ($v, $p, $d) = @_; + + $p = '' if (!defined($p)); + $d = '' if (!defined($d)); + return defined($v) && $v ne '' ? $p.$v : $d; +} + +###################################################################### +# Get device state +# Return values for readings (devState, battery, alive) +###################################################################### + +sub HMCCU_GetDeviceStates ($) +{ + my ($clHash) = @_; + + my %stName = ( + '0.AES_KEY' => 'sign', + '0.CONFIG_PENDING' => 'cfgPending', '0.DEVICE_IN_BOOTLOADER' => 'boot', + '0.STICKY_UNREACH' => 'stickyUnreach', '0.UPDATE_PENDING' => 'updPending' + ); + + my %stVal = ( + '0.LOWBAT' => '1|true:low,0|false:ok', '0.UNREACH' => '1|true:dead,0|false:alive' + ); + + my @state = (); + my @values = (); + + if (exists($clHash->{hmccu}{dp})) { + foreach my $dp (keys %stName) { + push @state, $stName{$dp} if (exists($clHash->{hmccu}{dp}{$dp}) && + defined($clHash->{hmccu}{dp}{$dp}{VAL}) && + $clHash->{hmccu}{dp}{$dp}{VAL} =~ /^(1|true)$/); + } + push @values, scalar(@state) > 0 ? join(',', @state) : 'ok'; + foreach my $dp (keys %stVal) { + if (exists($clHash->{hmccu}{dp}{$dp}) && defined($clHash->{hmccu}{dp}{$dp}{SVAL})) { + push @values, $clHash->{hmccu}{dp}{SVAL}; + } + else { + push @values, 'unknown'; + } + } + } + + return @values; +} + ###################################################################### # Determine HomeMatic state considering datapoint values specified # in attributes ccudef-hmstatevals and hmstatevals. @@ -9491,7 +9733,7 @@ sub HMCCU_CCURPC_ListDevicesCB ($$) which reacts with execution of command 'get devicelist' on these events.
  • get <name> devicelist create <devexp> [t={chn|dev|all}] - [p=<prefix>] [s=<suffix>] [f=<format>] [defattr] [duplicates] + [p=<prefix>] [s=<suffix>] [f=<format>] [defattr] [save] [<attr>=<value> [...]]
    With option 'create' HMCCU will automatically create client devices for all CCU devices and channels matching specified regular expression. With option t=chn or t=dev (default) diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm index 084acf297..34c454aa6 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.007 +# Version 4.4.008 # # (c) 2020 zap (zap01 t-online de) # @@ -26,6 +26,7 @@ sub HMCCUCHN_Initialize ($); sub HMCCUCHN_Define ($@); sub HMCCUCHN_InitDevice ($$); sub HMCCUCHN_Undef ($$); +sub HMCCUCHN_Rename ($$); sub HMCCUCHN_Set ($@); sub HMCCUCHN_Get ($@); sub HMCCUCHN_Attr ($@); @@ -40,13 +41,14 @@ sub HMCCUCHN_Initialize ($) $hash->{DefFn} = "HMCCUCHN_Define"; $hash->{UndefFn} = "HMCCUCHN_Undef"; + $hash->{RenameFn} = "HMCCUCHN_Rename"; $hash->{SetFn} = "HMCCUCHN_Set"; $hash->{GetFn} = "HMCCUCHN_Get"; $hash->{AttrFn} = "HMCCUCHN_Attr"; $hash->{parseParams} = 1; $hash->{AttrList} = "IODev ccucalculate ". - "ccuflags:multiple-strict,ackState,logCommand,nochn0,noReadings,trace ccureadingfilter ". + "ccuflags:multiple-strict,ackState,logCommand,devState,noReadings,trace ccureadingfilter ". "ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ". "ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix ". "ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint ". @@ -71,20 +73,23 @@ sub HMCCUCHN_Define ($@) my $devspec = shift @$a; my $ioHash = undef; - + + my $existDev = HMCCU_ExistsClientDevice ($devspec, $devtype); + return "FHEM device $existDev for CCU device $devspec already exists" if (defined($existDev)); + # Store some definitions for delayed initialization $hash->{hmccu}{devspec} = $devspec; # Defaults + $hash->{readonly} = "no"; $hash->{hmccu}{channels} = 1; - $hash->{statevals} = 'devstate'; # Parse optional command line parameters my $n = 0; my $arg = shift @$a; while (defined ($arg)) { return $usage if ($n == 3); - if ($arg eq 'readonly') { $hash->{statevals} = $arg; } + if ($arg eq 'readonly') { $hash->{readonly} = "yes"; } elsif ($arg eq 'defaults') { HMCCU_SetDefaults ($hash) if ($init_done); } else { return $usage; } $n++; @@ -182,6 +187,20 @@ sub HMCCUCHN_Undef ($$) } } +###################################################################### +# Rename device +###################################################################### + +sub HMCCUCHN_Rename ($$) +{ + my ($newName, $oldName) = @_; + + my $clHash = $defs{$newName}; + my $ioHash = defined($clHash) ? $clHash->{IODev} : undef; + + HMCCU_RenameDevice ($ioHash, $clHash, $oldName); +} + ###################################################################### # Set attribute ###################################################################### @@ -202,19 +221,7 @@ sub HMCCUCHN_Attr ($@) $hash->{IODev} = $defs{$attrval}; } elsif ($attrname eq 'statevals') { - return "Device is read only" if ($hash->{statevals} eq 'readonly'); - $hash->{statevals} = "devstate"; - my @states = split /,/,$attrval; - foreach my $st (@states) { - my @statesubs = split /:/,$st; - return "value := text:substext[,...]" if (@statesubs != 2); - $hash->{statevals} .= '|'.$statesubs[0]; - } - } - } - elsif ($cmd eq "del") { - if ($attrname eq 'statevals') { - $hash->{statevals} = "devstate"; + return "Device is read only" if ($hash->{readonly} eq 'yes'); } } @@ -248,7 +255,7 @@ sub HMCCUCHN_Set ($@) # Get I/O device, check device state return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' || !defined ($hash->{IODev})); - return undef if ($hash->{statevals} eq 'readonly' && $opt ne '?' && + return undef if ($hash->{readonly} eq 'yes' && $opt ne '?' && $opt !~ /^(clear|config|defaults)$/); my $disable = AttrVal ($name, "disable", 0); @@ -265,8 +272,10 @@ sub HMCCUCHN_Set ($@) my $ccuaddr = $hash->{ccuaddr}; my $ccuif = $hash->{ccuif}; my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); - my $statevals = AttrVal ($name, 'statevals', ''); - my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', ''); + my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', '', '', ''); + my $stateVals = HMCCU_GetStateValues ($hash, $cd, 2); + my %stateCmds = split (/[:,]/, $stateVals); + my @states = keys %stateCmds; my $result = ''; my $rc; @@ -290,7 +299,9 @@ sub HMCCUCHN_Set ($@) my $no = sprintf ("%03d", $i); $objvalue =~ s/\\_/%20/g; - $dpval{"$no.$ccuif.$ccuaddr.$objname"} = HMCCU_Substitute ($objvalue, $statevals, 1, undef, ''); + $objvalue = HMCCU_Substitute ($objvalue, $stateVals, 1, undef, '') + if ($stateVals ne '' && $objname eq $cd); + $dpval{"$no.$ccuif.$ccuaddr.$objname"} = $objvalue; } if (defined($h)) { @@ -301,7 +312,9 @@ sub HMCCUCHN_Set ($@) return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $objname, 2)); $objvalue =~ s/\\_/%20/g; - $dpval{"$no.$ccuif.$ccuaddr.$objname"} = HMCCU_Substitute ($objvalue, $statevals, 1, undef, ''); + $objvalue = HMCCU_Substitute ($objvalue, $stateVals, 1, undef, '') + if ($stateVals ne '' && $objname eq $cd); + $dpval{"$no.$ccuif.$ccuaddr.$objname"} = $objvalue; } } @@ -319,45 +332,35 @@ sub HMCCUCHN_Set ($@) $objvalue =~ s/\\_/%20/g; $rc = HMCCU_SetMultipleDatapoints ($hash, - { "001.$ccuif.$ccuaddr.$cd" => HMCCU_Substitute ($objvalue, $statevals, 1, undef, '') } + { "001.$ccuif.$ccuaddr.$cd" => HMCCU_Substitute ($objvalue, $stateVals, 1, undef, '') } ); return HMCCU_SetError ($hash, min(0, $rc)); } - elsif ($opt =~ /^($hash->{statevals})$/) { - my $cmd = $1; - my $objvalue = ($cmd ne 'devstate') ? $cmd : shift @$a; - + elsif (exists($stateCmds{$opt})) { return HMCCU_SetError ($hash, -13) if ($sd eq ''); return HMCCU_SetError ($hash, -8) - if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $sd, 2)); - return HMCCU_SetError ($hash, "Usage: set $name devstate {value}") if (!defined ($objvalue)); + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $cd, 2)); - $objvalue =~ s/\\_/%20/g; $rc = HMCCU_SetMultipleDatapoints ($hash, - { "001.$ccuif.$ccuaddr.$sd" => HMCCU_Substitute ($objvalue, $statevals, 1, undef, '') } + { "001.$ccuif.$ccuaddr.$cd" => $stateCmds{$opt} } ); return HMCCU_SetError ($hash, min(0, $rc)); } elsif ($opt eq 'toggle') { - return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals})); - return HMCCU_SetError ($hash, -13) if ($sd eq ''); + return HMCCU_SetError ($hash, -15) if ($stateVals eq ''); + return HMCCU_SetError ($hash, -13) if ($cd eq ''); return HMCCU_SetError ($hash, -8) - if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $sd, 2)); + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $cd, 2)); - my $tstates = $hash->{statevals}; - $tstates =~ s/devstate\|//; - my @states = split /\|/, $tstates; my $stc = scalar (@states); + my $curState = defined($hash->{hmccu}{dp}{"$cc.$cd"}{SVAL}) ? + $hash->{hmccu}{dp}{"$cc.$cd"}{SVAL} : $states[0]; - my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd; - ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 1); - return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0); - - my $objvalue = ''; + my $newState = ''; my $st = 0; while ($st < $stc) { - if ($states[$st] eq $result) { - $objvalue = ($st == $stc-1) ? $states[0] : $states[$st+1]; + if ($states[$st] eq $curState) { + $newState = ($st == $stc-1) ? $states[0] : $states[$st+1]; last; } else { @@ -365,15 +368,15 @@ sub HMCCUCHN_Set ($@) } } - return HMCCU_SetError ($hash, "Current device state doesn't match statevals") - if ($objvalue eq ''); + return HMCCU_SetError ($hash, "Current device state doesn't match any state value") + if ($newState eq ''); $rc = HMCCU_SetMultipleDatapoints ($hash, - { "001.$objname" => HMCCU_Substitute ($objvalue, $statevals, 1, undef, '') } + { "001.$ccuif.$ccuaddr.$cd" => $stateCmds{$newState} } ); return HMCCU_SetError ($hash, min(0, $rc)); } - elsif ($opt eq 'pct' || $opt eq 'up' || $opt eq 'down') { + elsif ($opt =~ /^(pct|level|up|down)$/) { return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype") if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "LEVEL", 2)); @@ -421,13 +424,20 @@ sub HMCCUCHN_Set ($@) return HMCCU_SetError ($hash, min(0, $rc)); } + elsif ($opt eq 'stop') { + return HMCCU_SetError ($hash, "Can't find STOP datapoint for device type $ccutype") + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "STOP", 2)); + + $rc = HMCCU_SetMultipleDatapoints ($hash, { "001.$ccuif.$ccuaddr.STOP" => 1 }); + return HMCCU_SetError ($hash, min(0, $rc)); + } elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') { - return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals})); + return HMCCU_SetError ($hash, -15) if ($stateVals eq ''); return HMCCU_SetError ($hash, "No state value for 'on' defined") - if ("on" !~ /($hash->{statevals})/); - return HMCCU_SetError ($hash, -13) if ($sd eq ''); + if (!exists($stateCmds{"on"})); + return HMCCU_SetError ($hash, -13) if ($cd eq ''); return HMCCU_SetError ($hash, -8) - if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $sd, 2)); + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $cd, 2)); return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type") if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "ON_TIME", 2)); @@ -442,7 +452,7 @@ sub HMCCUCHN_Set ($@) $rc = HMCCU_SetMultipleDatapoints ($hash, { "001.$ccuif.$ccuaddr.ON_TIME" => $timespec, - "002.$ccuif.$ccuaddr.$sd" => HMCCU_Substitute ("on", $statevals, 1, undef, '') + "002.$ccuif.$ccuaddr.$cd" => $stateCmds{"on"} }); return HMCCU_SetError ($hash, min(0, $rc)); } @@ -498,22 +508,17 @@ sub HMCCUCHN_Set ($@) } else { my $retmsg = "clear defaults:noArg"; - if ($hash->{statevals} ne 'readonly') { - $retmsg .= " config control datapoint rpcparameter devstate"; - - if ($hash->{statevals} ne '') { - my @cmdlist = split /\|/,$hash->{statevals}; - shift @cmdlist; - $retmsg .= ':'.join(',',@cmdlist) if (scalar(@cmdlist) > 0); - foreach my $sv (@cmdlist) { - $retmsg .= ' '.$sv.':noArg'; - } - $retmsg .= " toggle:noArg"; + if ($hash->{readonly} ne 'yes') { + $retmsg .= " config control datapoint rpcparameter"; + if (scalar(@states) > 0) { + $retmsg .= ' toggle:noArg '.join (' ', map { $_.':noArg' } @states); $retmsg .= " on-for-timer on-till" - if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "ON_TIME", 2)); - $retmsg .= " pct up down level" - if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $cc, "LEVEL", 2)); + if (HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "ON_TIME", 2)); } + $retmsg .= " pct up down level" + if (HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "LEVEL", 2)); + $retmsg .= " stop" + if (HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "STOP", 2)); } return AttrTemplate_Set ($hash, $retmsg, $name, $opt, @$a); } @@ -629,7 +634,7 @@ sub HMCCUCHN_Get ($@) foreach my $ps (split (',', $paramset)) { next if ($devDesc->{PARAMSETS} !~ /$ps/); - ($rc, $result) = HMCCU_RPCRequest ($hash, 'getRawParamset', $a, $ps, undef, $par); + ($rc, $result) = HMCCU_RPCRequest ($hash, 'getRawParamset', $a, $ps, undef, undef); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0); foreach my $p (keys %$result) { $objects{$da}{$dc}{$ps}{$p} = $result->{$p}; } @@ -722,7 +727,7 @@ sub HMCCUCHN_Get ($@) Delete readings matching specified reading name expression. Default expression is '.*'. Readings 'state' and 'control' are not deleted.

  • -
  • set <name> config [device] <parameter>=<value[:<type>]>
    +
  • set <name> config [device] <parameter>=<value>[:<type>]
    Alias for command 'set paramset' for parameter set MASTER.

  • set <name> datapoint <datapoint> <value> | <datapoint>=<value> [...]
    @@ -751,13 +756,15 @@ sub HMCCUCHN_Get ($@)
  • set <name> pct <value> [<ontime> [<ramptime>]]
    Alias for command 'set pct'.

  • -
  • set <name> link <parameter>=<value[:<type>]>
    +
  • set <name> link <parameter>=<value>[:<type>]
    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 - be defined by setting attribute 'statevals'. + be defined by setting attribute 'statevals'.
    + If 'statedatapoint' or 'statevals' is not set, HMCCUCHN tries to detect the parameters + depending on the device type and the available datapoints.

    Example: Turn switch on
    @@ -784,8 +791,8 @@ sub HMCCUCHN_Get ($@) ON_TIME. The attribute 'statevals' must contain at least a value for 'on'. The Attribute 'statedatapoint' must be set to a writeable datapoint.

  • -
  • set <name> paramset [device] [<paramset>] <parameter>=<value[:<type>]> [...]
    - Set multiple datapoints or config parameters by using RPC interface instead of Rega. +
  • set <name> paramset [device] [<paramset>] <parameter>=<value>[:<type>] [...]
    + Set multiple datapoints or config parameters by using RPC interface instead of ReGa. With option 'device' a parameter set of the device is set instead of the current channel. Parameter paramset is a valid parameter set name (i.e. MASTER, LINK, ...). The default parameter set is 'VALUES'. @@ -812,6 +819,10 @@ sub HMCCUCHN_Get ($@)
  • set <name> rpcparamter
    Alias for command 'set paramset'.

  • +
  • set <name> stop
    + Set datapoint STOP of a channel to true. This command is only available, if the + channel contains a writeable datapoint STOP. +

  • set <name> toggle
    Toggle state datapoint between values defined by attribute 'statevals'. This command is only available if attribute 'statevals' is set. Toggling supports more than two state @@ -828,7 +839,7 @@ 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
    +
  • set <name> values <parameter>=<value>[:<type>]
    Alias for command 'set paramset' for parameter set VALUES.
  • @@ -902,11 +913,11 @@ sub HMCCUCHN_Get ($@) Example:
    dewpoint:taupunkt:1.TEMPERATURE,1.HUMIDITY
    -
  • ccuflags {ackState, logCommand, nochn0, noReadings, trace}
    +
  • ccuflags {ackState, logCommand, devState, noReadings, trace}
    Control behaviour of device:
    ackState: Acknowledge command execution by setting STATE to error or success.
    logCommand: Write get and set commands to FHEM log with verbose level 3.
    - nochn0: Prevent update of status channel 0 datapoints / readings.
    + devState: Store channel 0 states in reading 'devstate'
    noReadings: Do not update readings
    trace: Write log file information for operations related to this device.

  • diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm index 13896861b..0750e482c 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm @@ -4,7 +4,7 @@ # # $Id: 88_HMCCUDEV.pm 18552 2019-02-10 11:52:28Z zap $ # -# Version 4.4.002 +# Version 4.4.008 # # (c) 2020 zap (zap01 t-online de) # @@ -25,6 +25,7 @@ sub HMCCUDEV_Initialize ($); sub HMCCUDEV_Define ($@); sub HMCCUDEV_InitDevice ($$); sub HMCCUDEV_Undef ($$); +sub HMCCUDEV_Rename ($$); sub HMCCUDEV_Set ($@); sub HMCCUDEV_Get ($@); sub HMCCUDEV_Attr ($@); @@ -39,6 +40,7 @@ sub HMCCUDEV_Initialize ($) $hash->{DefFn} = "HMCCUDEV_Define"; $hash->{UndefFn} = "HMCCUCHN_Undef"; + $hash->{RenameFn} = "HMCCUDEV_Rename"; $hash->{SetFn} = "HMCCUDEV_Set"; $hash->{GetFn} = "HMCCUDEV_Get"; $hash->{AttrFn} = "HMCCUDEV_Attr"; @@ -85,7 +87,11 @@ sub HMCCUDEV_Define ($@) my $ioHash = undef; +# my $existDev = HMCCU_ExistsClientDevice ($devspec, $devtype); +# return "FHEM device $existDev for CCU device $devspec already exists" if (defined($existDev)); + # Store some definitions for delayed initialization + $hash->{readonly} = 'no'; $hash->{hmccu}{devspec} = $devspec; $hash->{hmccu}{groupexp} = $h->{groupexp} if (exists ($h->{groupexp})); $hash->{hmccu}{group} = $h->{group} if (exists ($h->{group})); @@ -103,11 +109,11 @@ sub HMCCUDEV_Define ($@) } # Defaults - $hash->{statevals} = 'devstate'; + $hash->{hmccu}{statevals} = 'devstate'; # Parse optional command line parameters foreach my $arg (@$a) { - if ($arg eq 'readonly') { $hash->{statevals} = $arg; } + if ($arg eq 'readonly') { $hash->{readonly} = 'yes'; $hash->{hmccu}{statevals} = '' } elsif ($arg eq 'defaults') { HMCCU_SetDefaults ($hash) if ($init_done); } @@ -260,7 +266,7 @@ sub HMCCUDEV_InitDevice ($$) my $rc = 0; if ($devna) { $dev_hash->{ccutype} = 'n/a'; - $dev_hash->{statevals} = 'readonly'; + $dev_hash->{readonly} = 'yes'; $rc = HMCCU_CreateDevice ($ioHash, $dev_hash->{ccuaddr}, $name, undef, $dev); } else { @@ -299,6 +305,20 @@ sub HMCCUDEV_Undef ($$) } } +###################################################################### +# Rename device +###################################################################### + +sub HMCCUDEV_Rename ($$) +{ + my ($newName, $oldName) = @_; + + my $clHash = $defs{$newName}; + my $ioHash = defined($clHash) ? $clHash->{IODev} : undef; + + HMCCU_RenameDevice ($ioHash, $clHash, $oldName); +} + ###################################################################### # Set attribute ###################################################################### @@ -314,19 +334,19 @@ sub HMCCUDEV_Attr ($@) $hash->{IODev} = $defs{$attrval}; } elsif ($attrname eq "statevals") { - return "Device is read only" if ($hash->{statevals} eq 'readonly'); - $hash->{statevals} = 'devstate'; + return "Device is read only" if ($hash->{readonly} eq 'yes'); + $hash->{hmccu}{statevals} = 'devstate'; my @states = split /,/,$attrval; foreach my $st (@states) { my @statesubs = split /:/,$st; return "value := text:substext[,...]" if (@statesubs != 2); - $hash->{statevals} .= '|'.$statesubs[0]; + $hash->{hmccu}{statevals} .= '|'.$statesubs[0]; } } } elsif ($cmd eq "del") { if ($attrname eq "statevals") { - $hash->{statevals} = "devstate"; + $hash->{hmccu}{statevals} = $hash->{readonly} eq 'yes' ? '' : "devstate"; } } @@ -352,7 +372,7 @@ sub HMCCUDEV_Set ($@) my $hmccu_name = $ioHash->{NAME}; # Handle read only and disabled devices - return undef if ($hash->{statevals} eq 'readonly' && $opt ne '?' + return undef if ($hash->{readonly} eq 'yes' && $opt ne '?' && $opt !~ /^(clear|config|defaults)$/); my $disable = AttrVal ($name, "disable", 0); return undef if ($disable == 1); @@ -435,7 +455,7 @@ sub HMCCUDEV_Set ($@) ); return HMCCU_SetError ($hash, min(0, $rc)); } - elsif ($opt =~ /^($hash->{statevals})$/) { + elsif ($opt =~ /^($hash->{hmccu}{statevals})$/) { my $cmd = $1; my $objvalue = ($cmd ne 'devstate') ? $cmd : shift @$a; @@ -450,12 +470,12 @@ sub HMCCUDEV_Set ($@) return HMCCU_SetError ($hash, min(0, $rc)); } elsif ($opt eq 'toggle') { - return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals})); + return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{hmccu}{statevals})); return HMCCU_SetError ($hash, -11) if ($sc eq ''); return HMCCU_SetError ($hash, -13) if ($sd eq ''); return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, $sd, 2)); - my $tstates = $hash->{statevals}; + my $tstates = $hash->{hmccu}{statevals}; $tstates =~ s/devstate\|//; my @states = split /\|/, $tstates; my $stc = scalar (@states); @@ -549,9 +569,9 @@ sub HMCCUDEV_Set ($@) return HMCCU_SetError ($hash, min(0, $rc)); } elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') { - return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals})); + return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{hmccu}{statevals})); return HMCCU_SetError ($hash, "No state value for 'on' defined") - if ("on" !~ /($hash->{statevals})/); + if ("on" !~ /($hash->{hmccu}{statevals})/); return HMCCU_SetError ($hash, -11) if ($sc eq ''); return HMCCU_SetError ($hash, -13) if ($sd eq ''); return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type") @@ -632,12 +652,12 @@ sub HMCCUDEV_Set ($@) else { my $retmsg = "clear config defaults:noArg"; - if ($hash->{statevals} ne 'readonly') { + if ($hash->{readonly} ne 'yes') { $retmsg .= " control datapoint rpcparameter"; if ($sc ne '') { $retmsg .= " devstate"; - if ($hash->{statevals} ne '') { - my @cmdlist = split /\|/,$hash->{statevals}; + if ($hash->{hmccu}{statevals} ne '') { + my @cmdlist = split /\|/,$hash->{hmccu}{statevals}; shift @cmdlist; $retmsg .= ':'.join(',',@cmdlist) if (@cmdlist > 0); foreach my $sv (@cmdlist) {