2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-28 23:14:10 +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_FormatReadingValue ($$$);
sub HMCCU_GetReadingName ($$$$$$$;$); sub HMCCU_GetReadingName ($$$$$$$;$);
sub HMCCU_ScaleValue ($$$$$); sub HMCCU_ScaleValue ($$$$$);
sub HMCCU_StripNumber ($$);
sub HMCCU_Substitute ($$$$$;$$); sub HMCCU_Substitute ($$$$$;$$);
sub HMCCU_SubstRule ($$$); sub HMCCU_SubstRule ($$$);
sub HMCCU_SubstVariables ($$$); sub HMCCU_SubstVariables ($$$);
@ -269,6 +270,7 @@ sub HMCCU_SplitDatapoint ($;$);
# FHEM device handling functions # FHEM device handling functions
sub HMCCU_AssignIODevice ($$$); sub HMCCU_AssignIODevice ($$$);
sub HMCCU_ExistsClientDevice ($$);
sub HMCCU_FindClientDevices ($$$$); sub HMCCU_FindClientDevices ($$$$);
sub HMCCU_FindIODevice ($); sub HMCCU_FindIODevice ($);
sub HMCCU_GetHash ($@); sub HMCCU_GetHash ($@);
@ -319,8 +321,10 @@ sub HMCCU_IsValidDevice ($$$);
sub HMCCU_IsValidDeviceOrChannel ($$$); sub HMCCU_IsValidDeviceOrChannel ($$$);
sub HMCCU_ParamsetDescToStr ($$); sub HMCCU_ParamsetDescToStr ($$);
sub HMCCU_RemoveDevice ($$$;$); sub HMCCU_RemoveDevice ($$$;$);
sub HMCCU_RenameDevice ($$$);
sub HMCCU_ResetDeviceTables ($;$$); sub HMCCU_ResetDeviceTables ($;$$);
sub HMCCU_UpdateDevice ($$); sub HMCCU_UpdateDevice ($$);
sub HMCCU_UpdateDeviceRoles ($$;$$);
sub HMCCU_UpdateDeviceTable ($$); sub HMCCU_UpdateDeviceTable ($$);
# Handle datapoints # Handle datapoints
@ -330,6 +334,7 @@ sub HMCCU_GetDatapointAttr ($$$$$);
sub HMCCU_GetDatapointCount ($$$); sub HMCCU_GetDatapointCount ($$$);
sub HMCCU_GetDatapointList ($$$); sub HMCCU_GetDatapointList ($$$);
sub HMCCU_GetSpecialDatapoints ($$$$$); sub HMCCU_GetSpecialDatapoints ($$$$$);
sub HMCCU_GetStateValues ($$;$);
sub HMCCU_GetSwitchDatapoint ($$$); sub HMCCU_GetSwitchDatapoint ($$$);
sub HMCCU_GetValidDatapoints ($$$$$); sub HMCCU_GetValidDatapoints ($$$$$);
sub HMCCU_IsValidDatapoint ($$$$$); sub HMCCU_IsValidDatapoint ($$$$$);
@ -365,10 +370,12 @@ sub HMCCU_CalculateReading ($$);
sub HMCCU_CorrectName ($); sub HMCCU_CorrectName ($);
sub HMCCU_Encrypt ($); sub HMCCU_Encrypt ($);
sub HMCCU_Decrypt ($); sub HMCCU_Decrypt ($);
sub HMCCU_DefStr ($;$$);
sub HMCCU_DeleteReadings ($$); sub HMCCU_DeleteReadings ($$);
sub HMCCU_EncodeEPDisplay ($); sub HMCCU_EncodeEPDisplay ($);
sub HMCCU_ExprMatch ($$$); sub HMCCU_ExprMatch ($$$);
sub HMCCU_ExprNotMatch ($$$); sub HMCCU_ExprNotMatch ($$$);
sub HMCCU_GetDeviceStates ($);
sub HMCCU_GetDutyCycle ($); sub HMCCU_GetDutyCycle ($);
sub HMCCU_GetHMState ($$$); sub HMCCU_GetHMState ($$$);
sub HMCCU_GetIdFromIP ($$); sub HMCCU_GetIdFromIP ($$);
@ -2009,9 +2016,8 @@ sub HMCCU_Get ($@)
} }
elsif ($optcmd eq 'create') { elsif ($optcmd eq 'create') {
$usage = "Usage: get $name create {devexp|chnexp} [t={'chn'|'dev'|'all'}] [s=suffix] ". $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 $devdefaults = 0;
my $duplicates = 0;
my $savedef = 0; my $savedef = 0;
my $newcount = 0; my $newcount = 0;
@ -2025,7 +2031,6 @@ sub HMCCU_Get ($@)
if ($devtype !~ /^(dev|chn|all)$/ || !defined ($devspec)); if ($devtype !~ /^(dev|chn|all)$/ || !defined ($devspec));
foreach my $defopt (@$a) { foreach my $defopt (@$a) {
if ($defopt eq 'defattr') { $devdefaults = 1; } if ($defopt eq 'defattr') { $devdefaults = 1; }
elsif ($defopt eq 'duplicates') { $duplicates = 1; }
elsif ($defopt eq 'save') { $savedef = 1; } elsif ($defopt eq 'save') { $savedef = 1; }
else { return HMCCU_SetError ($hash, $usage); } else { return HMCCU_SetError ($hash, $usage); }
} }
@ -2049,7 +2054,6 @@ sub HMCCU_Get ($@)
$devname =~ s/[^A-Za-z\d_\.]+/_/g; $devname =~ s/[^A-Za-z\d_\.]+/_/g;
# Check for duplicate device definitions # Check for duplicate device definitions
if (!$duplicates) {
next if (exists ($defs{$devname})); next if (exists ($defs{$devname}));
my $devexists = 0; my $devexists = 0;
foreach my $exdev (@devlist) { foreach my $exdev (@devlist) {
@ -2059,7 +2063,6 @@ sub HMCCU_Get ($@)
} }
} }
next if ($devexists); next if ($devexists);
}
# Define new client device # Define new client device
my $ret = CommandDefine (undef, $devname." $defmod ".$add); my $ret = CommandDefine (undef, $devname." $defmod ".$add);
@ -2501,6 +2504,7 @@ sub HMCCU_GetReadingName ($$$$$$$;$)
{ {
my ($hash, $i, $a, $c, $d, $n, $rf, $ps) = @_; my ($hash, $i, $a, $c, $d, $n, $rf, $ps) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $type = $hash->{TYPE};
my %prefix = ( 'MASTER' => 'R-', 'LINK' => 'L-', 'VALUES' => '', 'SERVICE' => 'S-', my %prefix = ( 'MASTER' => 'R-', 'LINK' => 'L-', 'VALUES' => '', 'SERVICE' => 'S-',
'PEER' => 'P-' ); 'PEER' => 'P-' );
@ -2543,7 +2547,7 @@ sub HMCCU_GetReadingName ($$$$$$$;$)
} }
if ($rf eq 'datapoint' || $rf =~ /^datapoint(lc|uc)$/) { 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)$/) { elsif ($rf eq 'name' || $rf =~ /^name(lc|uc)$/) {
return () if ($n eq ''); return () if ($n eq '');
@ -2571,7 +2575,6 @@ sub HMCCU_GetReadingName ($$$$$$$;$)
push (@rnlist, $rpf.$rn); push (@rnlist, $rpf.$rn);
# Rename and/or add reading names # Rename and/or add reading names
if ($sr ne '') {
my @rules = split (';', $sr); my @rules = split (';', $sr);
foreach my $rr (@rules) { foreach my $rr (@rules) {
my ($rold, $rnew) = split (':', $rr); my ($rold, $rnew) = split (':', $rr);
@ -2592,7 +2595,6 @@ sub HMCCU_GetReadingName ($$$$$$$;$)
} }
} }
} }
}
# Convert to lower or upper case # Convert to lower or upper case
$rnlist[0] = lc($rnlist[0]) if ($rf =~ /^(datapoint|name|address)lc$/); $rnlist[0] = lc($rnlist[0]) if ($rf =~ /^(datapoint|name|address)lc$/);
@ -2620,6 +2622,10 @@ sub HMCCU_FormatReadingValue ($$$)
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $fnc = "FormatReadingValue"; my $fnc = "FormatReadingValue";
if (!defined($value)) {
HMCCU_Trace ($hash, 2, $fnc, "Value undefined for datapoint $dpt");
return $value;
}
my $stripnumber = HMCCU_GetAttrStripNumber ($hash); my $stripnumber = HMCCU_GetAttrStripNumber ($hash);
if ($stripnumber ne 'null' && $value =~ /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/) { if ($stripnumber ne 'null' && $value =~ /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/) {
@ -2651,6 +2657,27 @@ sub HMCCU_FormatReadingValue ($$$)
return $value; 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. # Log message if trace flag is set.
# Will output multiple log file entries if parameter msg is separated # 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. # Substitute first occurrence of regular expression or fixed string.
# Floating point values are ignored without datapoint specification. # Floating point values are ignored without datapoint specification.
# Integer values are compared with complete value. # Integer values are compared with complete value.
# $hashOrRule -
# $mode - 0=Substitute regular expression, 1=Substitute text # $mode - 0=Substitute regular expression, 1=Substitute text
# $chn - A channel number. Ignored if $dpt contains a Channel # $chn - A channel number. Ignored if $dpt contains a Channel
# number. # number.
@ -2892,28 +2920,48 @@ sub HMCCU_SetRPCState ($@)
sub HMCCU_Substitute ($$$$$;$$) 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 $rc = 0;
my $newvalue; my $newvalue;
my %conversion = ( my %conversion = (
'SHUTTER_CONTACT' => { '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' => { 'BLIND' => {
'LEVEL' => { '0' => 'closed', '100' => 'open' } 'LEVEL' => { '0' => 'closed', '100' => 'open', 'close' => '0', 'open' => '100' }
}, },
'DIMMER' => { 'DIMMER' => {
'LEVEL' => { '0' => 'off', '100' => 'on' } 'LEVEL' => { '0' => 'off', '100' => 'on', 'off' => '0', 'on' => '100' }
}, },
'DEFAULT' => { '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 # Remove channel number from datapoint if specified
if ($dpt =~ /^([0-9]{1,2})\.(.+)$/) { if ($dpt =~ /^([0-9]{1,2})\.(.+)$/) {
@ -2952,20 +3000,35 @@ sub HMCCU_Substitute ($$$$$;$$)
# Original value not modified by rules. Use default conversion depending on type/role # 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 # 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})) { if ((!defined($type) || $type eq '') && defined($devDesc) && defined($devDesc->{INDEX})) {
$type = $devDesc->{TYPE}; $type = $devDesc->{TYPE};
} }
$type = 'DEFAULT' if (!defined($type) || $type eq '');
if (defined($type) && $type ne '') { if (exists($conversion{$type}{$dpt}{$value})) {
if (exists($conversion{$type})) {
if (exists($conversion{$type}{$dpt}) && exists($conversion{$type}{$dpt}{$value})) {
return $conversion{$type}{$dpt}{$value}; return $conversion{$type}{$dpt}{$value};
} }
} elsif (exists($conversion{DEFAULT}{$dpt}{$value})) {
elsif (exists($conversion{DEFAULT}{$dpt}) && exists($conversion{DEFAULT}{$dpt}{$value})) {
return $conversion{DEFAULT}{$dpt}{$value}; return $conversion{DEFAULT}{$dpt}{$value};
} }
}
return $value; return $value;
} }
@ -3522,7 +3585,7 @@ sub HMCCU_UpdateDevice ($$)
my @rcvNames = HMCCU_GetDeviceIdentifier ($ioHash, $r, $iface); my @rcvNames = HMCCU_GetDeviceIdentifier ($ioHash, $r, $iface);
my $rcvFlags = HMCCU_FlagsToStr ('peer', 'FLAGS', my $rcvFlags = HMCCU_FlagsToStr ('peer', 'FLAGS',
$ioHash->{hmccu}{snd}{$iface}{$da}{$c}{$r}{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 @sndNames = HMCCU_GetDeviceIdentifier ($ioHash, $s, $iface);
my $sndFlags = HMCCU_FlagsToStr ('peer', 'FLAGS', my $sndFlags = HMCCU_FlagsToStr ('peer', 'FLAGS',
$ioHash->{hmccu}{snd}{$iface}{$da}{$c}{$s}{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->{sender} = join (',', @sndList) if (scalar(@sndList) > 0);
$clHash->{receiver} = join (',', @rcvList) if (scalar(@rcvList) > 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); my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $address, $iface);
if (defined($devDesc)) { if (defined($devDesc)) {
if ($clType eq 'HMCCUCHN' && defined($devDesc->{TYPE})) { if ($clType eq 'HMCCUCHN' && defined($devDesc->{TYPE})) {
@ -3559,6 +3637,38 @@ sub HMCCU_UpdateDevice ($$)
} }
} }
######################################################################
# 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;
}
###################################################################### ######################################################################
# Return role of a channel as stored in device hash # Return role of a channel as stored in device hash
###################################################################### ######################################################################
@ -3617,6 +3727,7 @@ sub HMCCU_GetDeviceConfig ($)
# Update FHEM devices # Update FHEM devices
foreach my $d (@devList) { foreach my $d (@devList) {
HMCCU_UpdateDevice ($ioHash, $defs{$d}); HMCCU_UpdateDevice ($ioHash, $defs{$d});
HMCCU_UpdateDeviceRoles ($ioHash, $defs{$d});
} }
return ($cDev, $cPar, $cLnk); return ($cDev, $cPar, $cLnk);
@ -3718,12 +3829,13 @@ sub HMCCU_GetDeviceIdentifier ($$;$$)
my @idList = (); my @idList = ();
my ($da, $dc) = HMCCU_SplitChnAddr ($address); 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); my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $address, $iface);
if (defined($devDesc)) { if (defined($devDesc)) {
if (defined($devDesc->{_fhem})) { 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 '') { elsif (defined($devDesc->{PARENT}) && $devDesc->{PARENT} ne '') {
push @idList, HMCCU_GetDeviceIdentifier ($ioHash, $devDesc->{PARENT}, $iface, $dc); push @idList, HMCCU_GetDeviceIdentifier ($ioHash, $devDesc->{PARENT}, $iface, $dc);
@ -3786,6 +3898,7 @@ sub HMCCU_DeviceDescToStr ($$)
###################################################################### ######################################################################
# Convert parameter set description to string # Convert parameter set description to string
# Parameter $object can be an address or a reference to a client hash.
###################################################################### ######################################################################
sub HMCCU_ParamsetDescToStr ($$) sub HMCCU_ParamsetDescToStr ($$)
@ -3806,7 +3919,9 @@ sub HMCCU_ParamsetDescToStr ($$)
my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($address); 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)); return undef if (!defined($devDesc));
my $model = HMCCU_GetDeviceModel ($ioHash, $devDesc->{_model}, $devDesc->{_fw_ver}); my $model = HMCCU_GetDeviceModel ($ioHash, $devDesc->{_model}, $devDesc->{_fw_ver});
return undef if (!defined($model)); return undef if (!defined($model));
@ -3829,9 +3944,11 @@ sub HMCCU_ParamsetDescToStr ($$)
$model->{$c}{$ps}{$_}{TYPE}. $model->{$c}{$ps}{$_}{TYPE}.
" [".HMCCU_FlagsToStr ('model', 'OPERATIONS', $model->{$c}{$ps}{$_}{OPERATIONS}, ',', '')."]". " [".HMCCU_FlagsToStr ('model', 'OPERATIONS', $model->{$c}{$ps}{$_}{OPERATIONS}, ',', '')."]".
" [".HMCCU_FlagsToStr ('model', 'FLAGS', $model->{$c}{$ps}{$_}{FLAGS}, ',', '')."]". " [".HMCCU_FlagsToStr ('model', 'FLAGS', $model->{$c}{$ps}{$_}{FLAGS}, ',', '')."]".
" RANGE=".$model->{$c}{$ps}{$_}{MIN}."-".$model->{$c}{$ps}{$_}{MAX}. " RANGE=".HMCCU_StripNumber ($model->{$c}{$ps}{$_}{MIN}, 2).
" DFLT=".$model->{$c}{$ps}{$_}{DEFAULT}. "...".HMCCU_StripNumber ($model->{$c}{$ps}{$_}{MAX}, 2).
" UNIT=".$model->{$c}{$ps}{$_}{UNIT} " 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"; } sort keys %{$model->{$c}{$ps}})."\n";
} }
} }
@ -3993,6 +4110,7 @@ sub HMCCU_GetClientDeviceModel ($;$)
my ($clHash, $chnNo) = @_; my ($clHash, $chnNo) = @_;
return undef if ($clHash->{TYPE} ne 'HMCCUCHN' && $clHash->{TYPE} ne 'HMCCUDEV'); return undef if ($clHash->{TYPE} ne 'HMCCUCHN' && $clHash->{TYPE} ne 'HMCCUDEV');
return undef if (!defined($clHash->{ccuaddr}));
my $ioHash = HMCCU_GetHash ($clHash); my $ioHash = HMCCU_GetHash ($clHash);
my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $clHash->{ccuaddr}, $clHash->{ccuif}); my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $clHash->{ccuaddr}, $clHash->{ccuif});
@ -4222,6 +4340,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$)
{ {
my ($ioHash, $clHash, $objects, $addListRef) = @_; my ($ioHash, $clHash, $objects, $addListRef) = @_;
my $ioName = $ioHash->{NAME};
my $clName = $clHash->{NAME}; my $clName = $clHash->{NAME};
my $clType = $clHash->{TYPE}; my $clType = $clHash->{TYPE};
@ -4235,6 +4354,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$)
my @chKeys = (); my @chKeys = ();
# Check if update of device allowed # Check if update of device allowed
my $ccuflags = HMCCU_GetFlags ($ioName);
my $disable = AttrVal ($clName, 'disable', 0); my $disable = AttrVal ($clName, 'disable', 0);
my $update = AttrVal ($clName, 'ccureadings', HMCCU_IsFlag ($clName, 'noReadings') ? 0 : 1); my $update = AttrVal ($clName, 'ccureadings', HMCCU_IsFlag ($clName, 'noReadings') ? 0 : 1);
return undef if ($update == 0 || $disable == 1 || $clHash->{ccudevstate} ne 'active'); 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') && my $vg = (($clHash->{ccuif} eq 'VirtualDevices' || $clHash->{ccuif} eq 'fhem') &&
exists($clHash->{ccugroup})) ? 1 : 0; exists($clHash->{ccugroup})) ? 1 : 0;
# Get attributes considering default attributes in IO device
my $substitute = HMCCU_GetAttrSubstitute ($clHash, $ioHash);
# Get client device attributes # Get client device attributes
my $clFlags = HMCCU_GetFlags ($clName); my $clFlags = HMCCU_GetFlags ($clName);
my $clRF = HMCCU_GetAttrReadingFormat ($clHash, $ioHash); my $clRF = HMCCU_GetAttrReadingFormat ($clHash, $ioHash);
@ -4275,7 +4392,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$)
# Loop over all parameters # Loop over all parameters
foreach my $p (keys %{$objects->{$a}{$c}{$ps}}) { foreach my $p (keys %{$objects->{$a}{$c}{$ps}}) {
my $v = $objects->{$a}{$c}{$ps}{$p}; my $v = $objects->{$a}{$c}{$ps}{$p};
next if (!defined($v) || !HMCCU_FilterReading ($clHash, $chnAddr, $p)); next if (!defined($v));
my $fv = $v; my $fv = $v;
my $cv = $v; my $cv = $v;
my $sv; my $sv;
@ -4292,7 +4409,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$)
# Modify value: scale, format, substitute # Modify value: scale, format, substitute
$sv = HMCCU_ScaleValue ($clHash, $c, $p, $v, 0); $sv = HMCCU_ScaleValue ($clHash, $c, $p, $v, 0);
$fv = HMCCU_FormatReadingValue ($clHash, $sv, $p); $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"); $cv = HMCCU_GetParamValue ($ioHash, $devDesc, $ps, $p, $fv) if ("$fv" eq "$cv");
HMCCU_UpdateInternalValues ($clHash, $chKey, 'SVAL', $cv); HMCCU_UpdateInternalValues ($clHash, $chKey, 'SVAL', $cv);
@ -4315,7 +4432,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$)
# Store result, but not for indirect updates of virtual devices # Store result, but not for indirect updates of virtual devices
$results{$devAddr}{$c}{$p} = $cv if ($devAddr eq $a); $results{$devAddr}{$c}{$p} = $cv if ($devAddr eq $a);
# Update readings if (HMCCU_FilterReading ($clHash, $chnAddr, $p) && ("$c" ne "0" || $clFlags =~ /devState/)) {
my @rnList = HMCCU_GetReadingName ($clHash, $clInt, $a, $c, $p, '', $clRF, $ps); my @rnList = HMCCU_GetReadingName ($clHash, $clInt, $a, $c, $p, '', $clRF, $ps);
foreach my $rn (@rnList) { foreach my $rn (@rnList) {
HMCCU_BulkUpdate ($clHash, $rn, $fv, $cv); HMCCU_BulkUpdate ($clHash, $rn, $fv, $cv);
@ -4324,6 +4441,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$)
} }
} }
} }
}
# Calculate additional readings # Calculate additional readings
if (scalar (@chKeys) > 0) { if (scalar (@chKeys) > 0) {
@ -4333,6 +4451,18 @@ sub HMCCU_UpdateParamsetReadings ($$$;$)
} }
} }
# 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); readingsEndUpdate ($clHash, 1);
return \%results; return \%results;
@ -4374,7 +4504,6 @@ sub HMCCU_UpdateSingleDevice ($$$$)
my $cf = HMCCU_GetFlags ($cltname); my $cf = HMCCU_GetFlags ($cltname);
my $peer = AttrVal ($cltname, 'peer', 'null'); my $peer = AttrVal ($cltname, 'peer', 'null');
my $crf = HMCCU_GetAttrReadingFormat ($clthash, $ccuhash); my $crf = HMCCU_GetAttrReadingFormat ($clthash, $ccuhash);
my $substitute = HMCCU_GetAttrSubstitute ($clthash, $ccuhash);
my ($sc, $st, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clthash, '', 'STATE', '', ''); my ($sc, $st, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clthash, '', 'STATE', '', '');
# Virtual device flag # Virtual device flag
@ -4426,7 +4555,7 @@ sub HMCCU_UpdateSingleDevice ($$$$)
my @readings = HMCCU_GetReadingName ($clthash, '', $addr, $chnnum, $dpt, '', $crf); my @readings = HMCCU_GetReadingName ($clthash, '', $addr, $chnnum, $dpt, '', $crf);
my $svalue = HMCCU_ScaleValue ($clthash, $chnnum, $dpt, $value, 0); my $svalue = HMCCU_ScaleValue ($clthash, $chnnum, $dpt, $value, 0);
my $fvalue = HMCCU_FormatReadingValue ($clthash, $svalue, $dpt); 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"); $cvalue = HMCCU_GetParamValue ($ccuhash, $devDesc, 'VALUES', $dpt, $fvalue) if ("$fvalue" eq "$cvalue");
# my %calcs = HMCCU_CalculateReading ($clthash, $chkey); # my %calcs = HMCCU_CalculateReading ($clthash, $chkey);
@ -4442,9 +4571,11 @@ sub HMCCU_UpdateSingleDevice ($$$$)
", orgvalue=$value value=$cvalue peer=$peer"); ", orgvalue=$value value=$cvalue peer=$peer");
# Update readings # Update readings
if ("$chnnum" ne "0" || $cf =~ /devState/) {
foreach my $rn (@readings) { foreach my $rn (@readings) {
HMCCU_BulkUpdate ($clthash, $rn, $fvalue, $cvalue) if ($rn ne ''); HMCCU_BulkUpdate ($clthash, $rn, $fvalue, $cvalue) if ($rn ne '');
} }
}
# foreach my $clcr (keys %calcs) { # foreach my $clcr (keys %calcs) {
# HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr}); # HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr});
# } # }
@ -4467,6 +4598,12 @@ sub HMCCU_UpdateSingleDevice ($$$$)
} }
} }
# 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 # Calculate and update HomeMatic state
if ($ccuflags !~ /nohmstate/) { if ($ccuflags !~ /nohmstate/) {
my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cltname, $ccuname, undef); my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cltname, $ccuname, undef);
@ -6385,6 +6522,25 @@ sub HMCCU_FindClientDevices ($$$$)
return @devlist; 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. # Get name of assigned client device of type HMCCURPCPROC.
# Create a RPC device of type HMCCURPCPROC if none is found and # 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, # Get channels and datapoints from attributes statechannel,
# statedatapoint and controldatapoint. # statedatapoint and controldatapoint.
@ -6628,14 +6813,15 @@ sub HMCCU_GetDatapointCount ($$$)
# datapoint name. # datapoint name.
# If controldatapoint is not specified it will synchronized with # If controldatapoint is not specified it will synchronized with
# statedatapoint. # statedatapoint.
# Return (sc, sd, cc, cd)
###################################################################### ######################################################################
sub HMCCU_GetSpecialDatapoints ($$$$$) sub HMCCU_GetSpecialDatapoints ($$$$$)
{ {
# my ($hash, $defsc, $defsd, $defcc, $defcd) = @_;
my ($hash, $sc, $sd, $cc, $cd) = @_; my ($hash, $sc, $sd, $cc, $cd) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $type = $hash->{TYPE}; my $type = $hash->{TYPE};
my $ccutype = $hash->{ccutype};
my $statedatapoint = AttrVal ($name, 'statedatapoint', ''); my $statedatapoint = AttrVal ($name, 'statedatapoint', '');
my $statechannel = AttrVal ($name, 'statechannel', ''); my $statechannel = AttrVal ($name, 'statechannel', '');
@ -6665,20 +6851,25 @@ sub HMCCU_GetSpecialDatapoints ($$$$$)
$sc = $hash->{ccuaddr}; $sc = $hash->{ccuaddr};
$sc =~ s/^[\*]*[0-9A-Z]+://; $sc =~ s/^[\*]*[0-9A-Z]+://;
$cc = $sc; $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'; }
} }
}
else {
# Try to find state channel # Try to find state channel
my $c = -1; my $c = -1;
if ($sc eq '' && $sd ne '') { if ($sc eq '' && $sd ne '') {
$c = HMCCU_FindDatapoint ($hash, $hash->{ccutype}, -1, $sd, 3); $c = HMCCU_FindDatapoint ($hash, $type, -1, $sd, 3);
$sc = $c if ($c >= 0); $sc = $c if ($c >= 0);
} }
# Try to find control channel # Try to find control channel
if ($cc eq '' && $cd ne '') { if ($cc eq '' && $cd ne '') {
$c = HMCCU_FindDatapoint ($hash, $hash->{ccutype}, -1, $cd, 3); $c = HMCCU_FindDatapoint ($hash, $type, -1, $cd, 3);
$cc = $c if ($c >= 0); $cc = $c if ($c >= 0);
} }
}
# By default set control channel and datapoint to state channel and datapoint # By default set control channel and datapoint to state channel and datapoint
$cc = $sc if ($cc eq ''); $cc = $sc if ($cc eq '');
@ -6748,16 +6939,16 @@ sub HMCCU_GetAttrStripNumber ($)
my ($hash) = @_; my ($hash) = @_;
my $fnc = "GetAttrStripNumber"; my $fnc = "GetAttrStripNumber";
my $snDef = 'null'; my $snDef = '1';
if ($hash->{TYPE} ne 'HMCCU') { if ($hash->{TYPE} ne 'HMCCU') {
my $ioHash = HMCCU_GetHash ($hash); my $ioHash = HMCCU_GetHash ($hash);
if (defined ($ioHash)) { if (defined ($ioHash)) {
$snDef = AttrVal ($ioHash->{NAME}, 'ccudef-stripnumber', 'null'); $snDef = AttrVal ($ioHash->{NAME}, 'ccudef-stripnumber', $snDef);
} }
} }
else { else {
$snDef = AttrVal ($hash->{NAME}, 'ccudef-stripnumber', 'null'); $snDef = AttrVal ($hash->{NAME}, 'ccudef-stripnumber', $snDef);
} }
my $stripnumber = AttrVal ($hash->{NAME}, 'stripnumber', $snDef); my $stripnumber = AttrVal ($hash->{NAME}, 'stripnumber', $snDef);
@ -8307,6 +8498,57 @@ sub HMCCU_QueueDeq ($)
# *** HELPER FUNCTIONS *** # *** 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 # Determine HomeMatic state considering datapoint values specified
# in attributes ccudef-hmstatevals and hmstatevals. # 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. which reacts with execution of command 'get devicelist' on these events.
</li><br/> </li><br/>
<li><b>get &lt;name&gt; devicelist create &lt;devexp&gt; [t={chn|<u>dev</u>|all}] <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/> [save] [&lt;attr&gt;=&lt;value&gt; [...]]</b><br/>
With option 'create' HMCCU will automatically create client devices for all CCU devices 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) 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 $ # $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) # (c) 2020 zap (zap01 <at> t-online <dot> de)
# #
@ -26,6 +26,7 @@ sub HMCCUCHN_Initialize ($);
sub HMCCUCHN_Define ($@); sub HMCCUCHN_Define ($@);
sub HMCCUCHN_InitDevice ($$); sub HMCCUCHN_InitDevice ($$);
sub HMCCUCHN_Undef ($$); sub HMCCUCHN_Undef ($$);
sub HMCCUCHN_Rename ($$);
sub HMCCUCHN_Set ($@); sub HMCCUCHN_Set ($@);
sub HMCCUCHN_Get ($@); sub HMCCUCHN_Get ($@);
sub HMCCUCHN_Attr ($@); sub HMCCUCHN_Attr ($@);
@ -40,13 +41,14 @@ sub HMCCUCHN_Initialize ($)
$hash->{DefFn} = "HMCCUCHN_Define"; $hash->{DefFn} = "HMCCUCHN_Define";
$hash->{UndefFn} = "HMCCUCHN_Undef"; $hash->{UndefFn} = "HMCCUCHN_Undef";
$hash->{RenameFn} = "HMCCUCHN_Rename";
$hash->{SetFn} = "HMCCUCHN_Set"; $hash->{SetFn} = "HMCCUCHN_Set";
$hash->{GetFn} = "HMCCUCHN_Get"; $hash->{GetFn} = "HMCCUCHN_Get";
$hash->{AttrFn} = "HMCCUCHN_Attr"; $hash->{AttrFn} = "HMCCUCHN_Attr";
$hash->{parseParams} = 1; $hash->{parseParams} = 1;
$hash->{AttrList} = "IODev ccucalculate ". $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 ". "ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ".
"ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix ". "ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix ".
"ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint ". "ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint ".
@ -72,19 +74,22 @@ sub HMCCUCHN_Define ($@)
my $ioHash = undef; 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 # Store some definitions for delayed initialization
$hash->{hmccu}{devspec} = $devspec; $hash->{hmccu}{devspec} = $devspec;
# Defaults # Defaults
$hash->{readonly} = "no";
$hash->{hmccu}{channels} = 1; $hash->{hmccu}{channels} = 1;
$hash->{statevals} = 'devstate';
# Parse optional command line parameters # Parse optional command line parameters
my $n = 0; my $n = 0;
my $arg = shift @$a; my $arg = shift @$a;
while (defined ($arg)) { while (defined ($arg)) {
return $usage if ($n == 3); 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); } elsif ($arg eq 'defaults') { HMCCU_SetDefaults ($hash) if ($init_done); }
else { return $usage; } else { return $usage; }
$n++; $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 # Set attribute
###################################################################### ######################################################################
@ -202,19 +221,7 @@ sub HMCCUCHN_Attr ($@)
$hash->{IODev} = $defs{$attrval}; $hash->{IODev} = $defs{$attrval};
} }
elsif ($attrname eq 'statevals') { elsif ($attrname eq 'statevals') {
return "Device is read only" if ($hash->{statevals} eq 'readonly'); return "Device is read only" if ($hash->{readonly} eq 'yes');
$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";
} }
} }
@ -248,7 +255,7 @@ sub HMCCUCHN_Set ($@)
# Get I/O device, check device state # Get I/O device, check device state
return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' || return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' ||
!defined ($hash->{IODev})); !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)$/); $opt !~ /^(clear|config|defaults)$/);
my $disable = AttrVal ($name, "disable", 0); my $disable = AttrVal ($name, "disable", 0);
@ -265,8 +272,10 @@ sub HMCCUCHN_Set ($@)
my $ccuaddr = $hash->{ccuaddr}; my $ccuaddr = $hash->{ccuaddr};
my $ccuif = $hash->{ccuif}; my $ccuif = $hash->{ccuif};
my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
my $statevals = AttrVal ($name, 'statevals', ''); my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', '', '', '');
my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', ''); my $stateVals = HMCCU_GetStateValues ($hash, $cd, 2);
my %stateCmds = split (/[:,]/, $stateVals);
my @states = keys %stateCmds;
my $result = ''; my $result = '';
my $rc; my $rc;
@ -290,7 +299,9 @@ sub HMCCUCHN_Set ($@)
my $no = sprintf ("%03d", $i); my $no = sprintf ("%03d", $i);
$objvalue =~ s/\\_/%20/g; $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)) { if (defined($h)) {
@ -301,7 +312,9 @@ sub HMCCUCHN_Set ($@)
return HMCCU_SetError ($hash, -8) return HMCCU_SetError ($hash, -8)
if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $objname, 2)); if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $objname, 2));
$objvalue =~ s/\\_/%20/g; $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; $objvalue =~ s/\\_/%20/g;
$rc = HMCCU_SetMultipleDatapoints ($hash, $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)); return HMCCU_SetError ($hash, min(0, $rc));
} }
elsif ($opt =~ /^($hash->{statevals})$/) { elsif (exists($stateCmds{$opt})) {
my $cmd = $1;
my $objvalue = ($cmd ne 'devstate') ? $cmd : shift @$a;
return HMCCU_SetError ($hash, -13) if ($sd eq ''); return HMCCU_SetError ($hash, -13) if ($sd eq '');
return HMCCU_SetError ($hash, -8) 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, "Usage: set $name devstate {value}") if (!defined ($objvalue));
$objvalue =~ s/\\_/%20/g;
$rc = HMCCU_SetMultipleDatapoints ($hash, $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)); return HMCCU_SetError ($hash, min(0, $rc));
} }
elsif ($opt eq 'toggle') { elsif ($opt eq 'toggle') {
return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals})); return HMCCU_SetError ($hash, -15) if ($stateVals eq '');
return HMCCU_SetError ($hash, -13) if ($sd eq ''); return HMCCU_SetError ($hash, -13) if ($cd eq '');
return HMCCU_SetError ($hash, -8) 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 $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; my $newState = '';
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 1);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
my $objvalue = '';
my $st = 0; my $st = 0;
while ($st < $stc) { while ($st < $stc) {
if ($states[$st] eq $result) { if ($states[$st] eq $curState) {
$objvalue = ($st == $stc-1) ? $states[0] : $states[$st+1]; $newState = ($st == $stc-1) ? $states[0] : $states[$st+1];
last; last;
} }
else { else {
@ -365,15 +368,15 @@ sub HMCCUCHN_Set ($@)
} }
} }
return HMCCU_SetError ($hash, "Current device state doesn't match statevals") return HMCCU_SetError ($hash, "Current device state doesn't match any state value")
if ($objvalue eq ''); if ($newState eq '');
$rc = HMCCU_SetMultipleDatapoints ($hash, $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)); 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") return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype")
if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "LEVEL", 2)); if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "LEVEL", 2));
@ -421,13 +424,20 @@ sub HMCCUCHN_Set ($@)
return HMCCU_SetError ($hash, min(0, $rc)); 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') { 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") return HMCCU_SetError ($hash, "No state value for 'on' defined")
if ("on" !~ /($hash->{statevals})/); if (!exists($stateCmds{"on"}));
return HMCCU_SetError ($hash, -13) if ($sd eq ''); return HMCCU_SetError ($hash, -13) if ($cd eq '');
return HMCCU_SetError ($hash, -8) 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") return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type")
if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "ON_TIME", 2)); if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "ON_TIME", 2));
@ -442,7 +452,7 @@ sub HMCCUCHN_Set ($@)
$rc = HMCCU_SetMultipleDatapoints ($hash, { $rc = HMCCU_SetMultipleDatapoints ($hash, {
"001.$ccuif.$ccuaddr.ON_TIME" => $timespec, "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)); return HMCCU_SetError ($hash, min(0, $rc));
} }
@ -498,22 +508,17 @@ sub HMCCUCHN_Set ($@)
} }
else { else {
my $retmsg = "clear defaults:noArg"; my $retmsg = "clear defaults:noArg";
if ($hash->{statevals} ne 'readonly') { if ($hash->{readonly} ne 'yes') {
$retmsg .= " config control datapoint rpcparameter devstate"; $retmsg .= " config control datapoint rpcparameter";
if (scalar(@states) > 0) {
if ($hash->{statevals} ne '') { $retmsg .= ' toggle:noArg '.join (' ', map { $_.':noArg' } @states);
my @cmdlist = split /\|/,$hash->{statevals};
shift @cmdlist;
$retmsg .= ':'.join(',',@cmdlist) if (scalar(@cmdlist) > 0);
foreach my $sv (@cmdlist) {
$retmsg .= ' '.$sv.':noArg';
}
$retmsg .= " toggle:noArg";
$retmsg .= " on-for-timer on-till" $retmsg .= " on-for-timer on-till"
if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "ON_TIME", 2)); if (HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "ON_TIME", 2));
$retmsg .= " pct up down level"
if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $cc, "LEVEL", 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); return AttrTemplate_Set ($hash, $retmsg, $name, $opt, @$a);
} }
@ -629,7 +634,7 @@ sub HMCCUCHN_Get ($@)
foreach my $ps (split (',', $paramset)) { foreach my $ps (split (',', $paramset)) {
next if ($devDesc->{PARAMSETS} !~ /$ps/); 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); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
foreach my $p (keys %$result) { $objects{$da}{$dc}{$ps}{$p} = $result->{$p}; } 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 '.*'. Delete readings matching specified reading name expression. Default expression is '.*'.
Readings 'state' and 'control' are not deleted. Readings 'state' and 'control' are not deleted.
</li><br/> </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. Alias for command 'set paramset' for parameter set MASTER.
</li><br/> </li><br/>
<li><b>set &lt;name&gt; datapoint &lt;datapoint&gt; &lt;value&gt; | &lt;datapoint&gt=&lt;value&gt; [...]</b><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/> <li><b>set &lt;name&gt; pct &lt;value&gt; [&lt;ontime&gt; [&lt;ramptime&gt;]]</b><br/>
Alias for command 'set pct'. Alias for command 'set pct'.
</li><br/> </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. Alias for command 'set paramset' for parameter set LINK.
</li><br/> </li><br/>
<li><b>set &lt;name&gt; &lt;statevalue&gt;</b><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 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 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/> <br/><br/>
Example: Turn switch on<br/> Example: Turn switch on<br/>
<code> <code>
@ -784,8 +791,8 @@ sub HMCCUCHN_Get ($@)
ON_TIME. The attribute 'statevals' must contain at least a value for 'on'. The Attribute ON_TIME. The attribute 'statevals' must contain at least a value for 'on'. The Attribute
'statedatapoint' must be set to a writeable datapoint. 'statedatapoint' must be set to a writeable datapoint.
</li><br/> </li><br/>
<li><b>set &lt;name&gt; paramset [device] [&lt;paramset&gt;] &lt;parameter&gt;=&lt;value[:&lt;type&gt;]&gt; [...]</b><br/> <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. 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. 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 Parameter <i>paramset</i> is a valid parameter set name (i.e. MASTER, LINK, ...). The
default parameter set is 'VALUES'. default parameter set is 'VALUES'.
@ -812,6 +819,10 @@ sub HMCCUCHN_Get ($@)
<li><b>set &lt;name&gt; rpcparamter</b><br/> <li><b>set &lt;name&gt; rpcparamter</b><br/>
Alias for command 'set paramset'. Alias for command 'set paramset'.
</li><br/> </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/> <li><b>set &lt;name&gt; toggle</b><br/>
Toggle state datapoint between values defined by attribute 'statevals'. This command is 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 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 Increment value of datapoint LEVEL. This command is only available if channel contains
a datapoint LEVEL. Default for <i>value</i> is 10. a datapoint LEVEL. Default for <i>value</i> is 10.
</li><br/> </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. Alias for command 'set paramset' for parameter set VALUES.
</li> </li>
</ul> </ul>
@ -902,11 +913,11 @@ sub HMCCUCHN_Get ($@)
Example:<br/> Example:<br/>
<code>dewpoint:taupunkt:1.TEMPERATURE,1.HUMIDITY</code> <code>dewpoint:taupunkt:1.TEMPERATURE,1.HUMIDITY</code>
</li><br/> </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/> Control behaviour of device:<br/>
ackState: Acknowledge command execution by setting STATE to error or success.<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/> 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/> noReadings: Do not update readings<br/>
trace: Write log file information for operations related to this device. trace: Write log file information for operations related to this device.
</li><br/> </li><br/>

View File

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