2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 12:49:34 +00:00

HMCCU: Update for 4.4 Beta

git-svn-id: https://svn.fhem.de/fhem/trunk@21140 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
zap 2020-02-07 18:11:59 +00:00
parent 4ce630f310
commit 2451fcc7f3
3 changed files with 455 additions and 182 deletions

View File

@ -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.
</li><br/>
<li><b>get &lt;name&gt; devicelist create &lt;devexp&gt; [t={chn|<u>dev</u>|all}]
[p=&lt;prefix&gt;] [s=&lt;suffix&gt;] [f=&lt;format&gt;] [defattr] [duplicates]
[p=&lt;prefix&gt;] [s=&lt;suffix&gt;] [f=&lt;format&gt;] [defattr]
[save] [&lt;attr&gt;=&lt;value&gt; [...]]</b><br/>
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)

View File

@ -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 <at> t-online <dot> 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.
</li><br/>
<li><b>set &lt;name&gt; config [device] &lt;parameter&gt;=&lt;value[:&lt;type&gt;]&gt;</b><br/>
<li><b>set &lt;name&gt; config [device] &lt;parameter&gt;=&lt;value&gt;[:&lt;type&gt;]</b><br/>
Alias for command 'set paramset' for parameter set MASTER.
</li><br/>
<li><b>set &lt;name&gt; datapoint &lt;datapoint&gt; &lt;value&gt; | &lt;datapoint&gt=&lt;value&gt; [...]</b><br/>
@ -751,13 +756,15 @@ sub HMCCUCHN_Get ($@)
<li><b>set &lt;name&gt; pct &lt;value&gt; [&lt;ontime&gt; [&lt;ramptime&gt;]]</b><br/>
Alias for command 'set pct'.
</li><br/>
<li><b>set &lt;name&gt; link &lt;parameter&gt;=&lt;value[:&lt;type&gt;]&gt;</b><br/>
<li><b>set &lt;name&gt; link &lt;parameter&gt;=&lt;value&gt;[:&lt;type&gt;]</b><br/>
Alias for command 'set paramset' for parameter set LINK.
</li><br/>
<li><b>set &lt;name&gt; &lt;statevalue&gt;</b><br/>
Set state of a CCU device channel to <i>StateValue</i>. 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'.<br/>
If 'statedatapoint' or 'statevals' is not set, HMCCUCHN tries to detect the parameters
depending on the device type and the available datapoints.
<br/><br/>
Example: Turn switch on<br/>
<code>
@ -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.
</li><br/>
<li><b>set &lt;name&gt; paramset [device] [&lt;paramset&gt;] &lt;parameter&gt;=&lt;value[:&lt;type&gt;]&gt; [...]</b><br/>
Set multiple datapoints or config parameters by using RPC interface instead of Rega.
<li><b>set &lt;name&gt; paramset [device] [&lt;paramset&gt;] &lt;parameter&gt;=&lt;value&gt;[:&lt;type&gt;] [...]</b><br/>
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 <i>paramset</i> is a valid parameter set name (i.e. MASTER, LINK, ...). The
default parameter set is 'VALUES'.
@ -812,6 +819,10 @@ sub HMCCUCHN_Get ($@)
<li><b>set &lt;name&gt; rpcparamter</b><br/>
Alias for command 'set paramset'.
</li><br/>
<li><b>set &lt;name&gt; stop</b><br/>
Set datapoint STOP of a channel to true. This command is only available, if the
channel contains a writeable datapoint STOP.
</li><br/>
<li><b>set &lt;name&gt; toggle</b><br/>
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 <i>value</i> is 10.
</li><br/>
<li><b>set &lt;name&gt; values</b><br/>
<li><b>set &lt;name&gt; values &lt;parameter&gt;=&lt;value&gt;[:&lt;type&gt;]</b><br/>
Alias for command 'set paramset' for parameter set VALUES.
</li>
</ul>
@ -902,11 +913,11 @@ sub HMCCUCHN_Get ($@)
Example:<br/>
<code>dewpoint:taupunkt:1.TEMPERATURE,1.HUMIDITY</code>
</li><br/>
<li><b>ccuflags {ackState, logCommand, nochn0, noReadings, trace}</b><br/>
<li><b>ccuflags {ackState, logCommand, devState, noReadings, trace}</b><br/>
Control behaviour of device:<br/>
ackState: Acknowledge command execution by setting STATE to error or success.<br/>
logCommand: Write get and set commands to FHEM log with verbose level 3.<br/>
nochn0: Prevent update of status channel 0 datapoints / readings.<br/>
devState: Store channel 0 states in reading 'devstate'<br/>
noReadings: Do not update readings<br/>
trace: Write log file information for operations related to this device.
</li><br/>

View File

@ -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 <at> t-online <dot> 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) {