2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

HMCCU: Update for version 4.4 beta

git-svn-id: https://svn.fhem.de/fhem/trunk@21058 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
zap 2020-01-26 15:11:19 +00:00
parent 310a170c32
commit d1c7c00389
4 changed files with 493 additions and 285 deletions

View File

@ -4,7 +4,7 @@
#
# $Id: 88_HMCCU.pm 18745 2019-02-26 17:33:23Z zap $
#
# Version 4.4.002
# Version 4.4.005
#
# Module for communication between FHEM and Homematic CCU2/3.
#
@ -52,7 +52,7 @@ my %HMCCU_CUST_CHN_DEFAULTS;
my %HMCCU_CUST_DEV_DEFAULTS;
# HMCCU version
my $HMCCU_VERSION = '4.4.002';
my $HMCCU_VERSION = '4.4.005';
# Constants and default values
my $HMCCU_MAX_IOERRORS = 100;
@ -197,6 +197,7 @@ sub HMCCU_Attr ($@);
sub HMCCU_AttrInterfacesPorts ($$$);
sub HMCCU_Notify ($$);
sub HMCCU_Detail ($$$$);
sub HMCCU_PostInit ($);
# Aggregation
sub HMCCU_AggregateReadings ($$);
@ -281,8 +282,10 @@ sub HMCCU_IsFlag ($$);
# Handle interfaces, devices and channels
sub HMCCU_AddDeviceDesc ($$$$);
sub HMCCU_AddDeviceModel ($$$$$$);
sub HMCCU_AddPeers ($$$);
sub HMCCU_CreateDevice ($$$$$);
sub HMCCU_DeleteDevice ($);
sub HMCCU_DeviceDescToStr ($);
sub HMCCU_ExistsDeviceModel ($$$;$);
sub HMCCU_FindParamDef ($$$);
sub HMCCU_FormatDeviceInfo ($);
@ -294,7 +297,9 @@ sub HMCCU_GetClientDeviceModel ($;$);
sub HMCCU_GetDefaultInterface ($);
sub HMCCU_GetDeviceAddresses ($;$$);
sub HMCCU_GetDeviceChannels ($$$);
sub HMCCU_GetDeviceConfig ($);
sub HMCCU_GetDeviceDesc ($$;$);
sub HMCCU_GetDeviceIdentifier ($$;$);
sub HMCCU_GetDeviceInfo ($$;$);
sub HMCCU_GetDeviceInterface ($$$);
sub HMCCU_GetDeviceList ($);
@ -309,6 +314,7 @@ sub HMCCU_GetParamValue ($$$$$);
sub HMCCU_IsValidChannel ($$$);
sub HMCCU_IsValidDevice ($$$);
sub HMCCU_IsValidDeviceOrChannel ($$$);
sub HMCCU_ParamsetDescToStr ($);
sub HMCCU_ResetDeviceTables ($;$$);
sub HMCCU_UpdateDeviceTable ($$);
@ -557,6 +563,34 @@ sub HMCCU_InitDevice ($)
}
}
######################################################################
# Tasks to be executed after all devices have been defined. Executed
# as timer function.
# Read device configuration from CCU
# Start RPC servers
######################################################################
sub HMCCU_PostInit ($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
if ($hash->{ccustate} eq 'active') {
my $rpcServer = AttrVal ($name, 'rpcserver', 'off');
HMCCU_Log ($hash, 1, "Reading device config from CCU. This may take a couple of seconds ...");
my ($cDev, $cPar, $cLnk) = HMCCU_GetDeviceConfig ($hash);
HMCCU_Log ($hash, 2, "Read device configuration: devices/channels=$cDev parametersets=$cPar links=$cLnk");
if ($rpcServer eq 'on') {
HMCCU_StartExtRPCServer ($hash);
}
}
else {
HMCCU_Log ($hash, 1, "CCU not active. Post FHEM start initialization failed");
}
}
######################################################################
# Set or delete attribute
######################################################################
@ -1133,7 +1167,6 @@ sub HMCCU_Notify ($$)
my $devtype = $devhash->{TYPE};
my $disable = AttrVal ($name, 'disable', 0);
my $rpcserver = AttrVal ($name, 'rpcserver', 'off');
my $ccuflags = HMCCU_GetFlags ($name);
return if ($disable);
@ -1145,16 +1178,10 @@ sub HMCCU_Notify ($$)
foreach my $event (@{$events}) {
if ($devname eq 'global') {
if ($event eq 'INITIALIZED') {
return if ($rpcserver eq 'off');
my $delay = $hash->{ccustate} eq 'active' && $hash->{hmccu}{ccu}{delayed} == 0 ?
$HMCCU_INIT_INTERVAL0 : $hash->{hmccu}{ccu}{delay}+$HMCCU_CCU_RPC_OFFSET;
HMCCU_Log ($hash, 0, "Start of RPC server after FHEM initialization in $delay seconds");
# if ($ccuflags =~ /(extrpc|procrpc)/) {
InternalTimer (gettimeofday()+$delay, "HMCCU_StartExtRPCServer", $hash, 0);
# }
# else {
# InternalTimer (gettimeofday()+$delay, "HMCCU_StartIntRPCServer", $hash, 0);
# }
HMCCU_Log ($hash, 0, "Scheduling post FHEM initialization tasks in $delay seconds");
InternalTimer (gettimeofday()+$delay, "HMCCU_PostInit", $hash, 0);
}
}
else {
@ -1593,7 +1620,7 @@ sub HMCCU_Set ($@)
return HMCCU_SetError ($hash, "Missing channel number for device $devName")
if (!defined ($t2));
return HMCCU_SetError ($hash, "Invalid channel number specified for device $devName")
if ($t1 !~ /^[0-9]+$/ || $t1 > $dh->{channels});
if ($t1 !~ /^[0-9]+$/ || $t1 > $dh->{hmccu}{channels});
$adr = $dh->{ccuaddr};
$chn = $t1;
$dpt = $t2;
@ -1822,10 +1849,11 @@ sub HMCCU_Get ($@)
my $opt = shift @$a;
return "No get command specified" if (!defined ($opt));
$opt = lc($opt);
my $options = "defaults:noArg exportdefaults devicelist dump dutycycle:noArg vars update".
" updateccu configdesc firmware rpcevents:noArg rpcstate:noArg deviceinfo".
" ccumsg:alarm,service";
my $options = "defaults:noArg exportDefaults deviceList dump dutycycle:noArg vars update".
" updateccu configdesc firmware rpcEvents:noArg rpcState:noArg deviceInfo".
" ccuMsg:alarm,service ccuConfig:noArg";
my $usage = "HMCCU: Unknown argument $opt, choose one of $options";
my $host = $hash->{host};
@ -1916,30 +1944,19 @@ sub HMCCU_Get ($@)
return HMCCU_FormatDeviceInfo ($result);
}
elsif ($opt eq 'rpcevents') {
# if ($ccuflags =~ /(extrpc|procrpc)/) {
$result = '';
my @iflist = HMCCU_GetRPCInterfaceList ($hash);
foreach my $ifname (@iflist) {
my ($rpcdev, $save) = HMCCU_GetRPCDevice ($hash, 0, $ifname);
if ($rpcdev eq '') {
HMCCU_Log ($hash, 2, "Can't find HMCCURPCPROC device for interface $ifname");
next;
}
my $res = AnalyzeCommandChain (undef, "get $rpcdev rpcevents");
$result .= $res if (defined ($result));
$result = '';
my @iflist = HMCCU_GetRPCInterfaceList ($hash);
foreach my $ifname (@iflist) {
my ($rpcdev, $save) = HMCCU_GetRPCDevice ($hash, 0, $ifname);
if ($rpcdev eq '') {
HMCCU_Log ($hash, 2, "Can't find HMCCURPCPROC device for interface $ifname");
next;
}
return HMCCU_SetState ($hash, "OK", $result) if ($result ne '');
return HMCCU_SetError ($hash, "No event statistics available");
# }
# else {
# return HMCCU_SetError ($hash, "No event statistics available")
# if (!exists ($hash->{hmccu}{evs}) || !exists ($hash->{hmccu}{evr}));
# foreach my $stkey (sort keys %{$hash->{hmccu}{evr}}) {
# $result .= "S: ".$stkey." = ".$hash->{hmccu}{evs}{$stkey}."\n";
# $result .= "R: ".$stkey." = ".$hash->{hmccu}{evr}{$stkey}."\n";
# }
# return HMCCU_SetState ($hash, "OK", $result);
# }
my $res = AnalyzeCommandChain (undef, "get $rpcdev rpcevents");
$result .= $res if (defined ($result));
}
return HMCCU_SetState ($hash, "OK", $result) if ($result ne '');
return HMCCU_SetError ($hash, "No event statistics available");
}
elsif ($opt eq 'rpcstate') {
my @hm_pids = ();
@ -1955,6 +1972,11 @@ sub HMCCU_Get ($@)
return HMCCU_SetState ($hash, "OK", $result);
}
elsif ($opt eq 'ccuconfig') {
HMCCU_ResetDeviceTables ($hash);
my ($cDev, $cPar, $cLnk) = HMCCU_GetDeviceConfig ($hash);
return "Device descriptions: $cDev\nParameter set descriptions: $cPar\nLinks/Peerings: $cLnk";
}
elsif ($opt eq 'devicelist') {
my ($devcount, $chncount, $ifcount, $prgcount, $gcount) = HMCCU_GetDeviceList ($hash);
return HMCCU_SetError ($hash, -2) if ($devcount < 0);
@ -2467,7 +2489,8 @@ sub HMCCU_GetReadingName ($$$$$$$;$)
my ($hash, $i, $a, $c, $d, $n, $rf, $ps) = @_;
my $name = $hash->{NAME};
my %prefix = ( 'MASTER' => 'R-', 'LINK' => 'L-', 'VALUES' => '', 'SERVICE' => 'S-' );
my %prefix = ( 'MASTER' => 'R-', 'LINK' => 'L-', 'VALUES' => '', 'SERVICE' => 'S-',
'PEER' => 'P-' );
my $ioHash = HMCCU_GetHash ($hash);
return () if (!defined ($ioHash) || !defined($d) || $d eq '');
@ -2703,7 +2726,7 @@ sub HMCCU_SetError ($@)
-3 => 'Cannot detect IO device',
-4 => 'Device deleted in CCU',
-5 => 'No response from CCU',
-6 => 'Update of readings disabled. Set attribute ccureadings first',
-6 => 'Update of readings disabled. Remove ccuflag noReadings',
-7 => 'Invalid channel number',
-8 => 'Invalid datapoint',
-9 => 'Interface does not support RPC calls',
@ -3002,7 +3025,7 @@ sub HMCCU_SubstVariables ($$$)
######################################################################
# Update all datapoint/readings of all client devices matching
# specified regular expression. Update will fail if device is deleted
# or disabled or if attribute ccureadings of a device is set to 0.
# or disabled or if ccuflag noReadings is set.
# If fromccu is 1 regular expression is compared to CCU device name.
# Otherwise it's compared to FHEM device name. If ifname is specified
# only devices belonging to interface ifname are updated.
@ -3138,7 +3161,7 @@ sub HMCCU_DeleteDevice ($)
my $hmccu_hash = $clthash->{IODev};
my $devaddr = $clthash->{ccuaddr};
my $channels = exists ($clthash->{channels}) ? $clthash->{channels} : 0;
my $channels = exists ($clthash->{hmccu}{channels}) ? $clthash->{hmccu}{channels} : 0;
# Delete device address entries
if (exists ($hmccu_hash->{hmccu}{dev}{$devaddr})) {
@ -3311,7 +3334,7 @@ sub HMCCU_UpdateDeviceTable ($$)
if (defined ($hash->{hmccu}{dev}{$ca}{name}));
$ch->{ccuif} = $hash->{hmccu}{dev}{$ca}{interface}
if (defined ($devices->{$ca}{interface}));
$ch->{channels} = $hash->{hmccu}{dev}{$ca}{channels}
$ch->{hmccu}{channels} = $hash->{hmccu}{dev}{$ca}{channels}
if (defined ($hash->{hmccu}{dev}{$ca}{channels}));
}
elsif ($devices->{$ca}{flag} eq 'D') {
@ -3356,9 +3379,35 @@ sub HMCCU_ResetDeviceTables ($;$$)
else {
$hash->{hmccu}{device} = ();
$hash->{hmccu}{model} = ();
$hash->{hmccu}{snd} = ();
$hash->{hmccu}{rcv} = ();
}
}
######################################################################
# Get device configuration for all interfaces from CCU
# Currently binary interfaces like CUxD are not supported
######################################################################
sub HMCCU_GetDeviceConfig ($)
{
my ($ioHash) = @_;
my ($cDev, $cPar, $cLnk) = (0, 0, 0);
foreach my $i (keys %{$ioHash->{hmccu}{interfaces}}) {
next if ($ioHash->{hmccu}{interfaces}{$i}{type} eq 'B');
if (exists($ioHash->{hmccu}{interfaces}{$i}{device})) {
my $rpcHash = $defs{$ioHash->{hmccu}{interfaces}{$i}{device}};
$cDev += HMCCURPCPROC_GetDeviceDesc ($rpcHash);
$cPar += HMCCURPCPROC_GetParamsetDesc ($rpcHash);
$cLnk += HMCCURPCPROC_GetPeers ($rpcHash);
}
}
return ($cDev, $cPar, $cLnk);
}
######################################################################
# Add new device.
# Arrays are converted to a comma separated string. Device description
@ -3405,7 +3454,13 @@ sub HMCCU_AddDeviceDesc ($$$$)
$hash->{hmccu}{device}{$iface}{$k}{_model} = $desc->{TYPE};
$hash->{hmccu}{device}{$iface}{$k}{_name} = HMCCU_GetDeviceName ($hash, $k, '');
}
# Get defined FHEM devices
my @devList = HMCCU_FindClientDevices ($hash, "(HMCCUDEV|HMCCUCHN)", undef,
"ccuaddr=$k");
$hash->{hmccu}{device}{$iface}{$k}{_fhem} = join(',', @devList)
if (scalar(@devList) > 0);
return 1;
}
@ -3415,13 +3470,15 @@ sub HMCCU_AddDeviceDesc ($$$$)
# $hash - Hash reference of IO device.
# $address - Address of device or channel. Accepts a channel address
# with channel number 'd' as an alias for device address.
# $iface - Interface name.
# $iface - Interface name (optional).
# Return hash reference for device description or undef on error.
######################################################################
sub HMCCU_GetDeviceDesc ($$;$)
{
my ($hash, $address, $iface) = @_;
return undef if (!exists($hash->{hmccu}{device}));
my @ifaceList = ();
if (defined($iface)) {
@ -3440,6 +3497,114 @@ sub HMCCU_GetDeviceDesc ($$;$)
return undef;
}
######################################################################
# Get device identifier(s)
######################################################################
sub HMCCU_GetDeviceIdentifier ($$$)
{
my ($ioHash, $address, $iface) = @_;
my $identifier = $address;
my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $address, $iface);
if (defined($devDesc)) {
if (exists($devDesc->{_fhem})) {
$identifier = $devDesc->{_fhem};
}
elsif (exists($devDesc->{PARENT})) {
$identifier = HMCCU_GetDeviceIdentifier ($ioHash, $devDesc->{PARENT}, $iface);
}
elsif (exists($devDesc->{_name})) {
$identifier = "ccu:".$devDesc->{_name};
}
}
return $identifier;
}
######################################################################
# Convert device description to string
######################################################################
sub HMCCU_DeviceDescToStr ($)
{
my ($clHash) = @_;
my $result = '';
my $ioHash = HMCCU_GetHash ($clHash);
return undef if (!defined($ioHash));
my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($clHash->{ccuaddr});
my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $devAddr, $clHash->{ccuif});
return undef if (!defined($devDesc));
my @addList = ($devAddr);
push (@addList, split (',', $devDesc->{CHILDREN}))
if (defined($devDesc->{CHILDREN}) && $devDesc->{CHILDREN} ne '');
foreach my $a (@addList) {
my ($d, $c) = HMCCU_SplitChnAddr ($a);
next if ($clHash->{TYPE} eq 'HMCCUCHN' && "$c" ne '0' && "$c" ne "$chnNo" && $c ne '');
$devDesc = HMCCU_GetDeviceDesc ($ioHash, $a, $clHash->{ccuif});
return undef if (!defined($devDesc));
$result .= $a eq $devAddr ? "Device $a" : "Channel $a";
$result .= " $devDesc->{_name} [$devDesc->{TYPE}]\n";
foreach my $n (sort keys %{$devDesc}) {
next if ($n =~ /^_/ || $n =~ /^(ADDRESS|TYPE|INDEX|VERSION)$/ ||
!defined($devDesc->{$n}) || $devDesc->{$n} eq '');
$result .= " $n: ".HMCCU_FlagsToStr ('device', $n, $devDesc->{$n}, ',', '')."\n";
}
}
return $result;
}
######################################################################
# Convert parameter set description to string
######################################################################
sub HMCCU_ParamsetDescToStr ($)
{
my ($clHash) = @_;
my $result = '';
my $ioHash = HMCCU_GetHash ($clHash);
return undef if (!defined($ioHash));
my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($clHash->{ccuaddr});
my $model = HMCCU_GetClientDeviceModel ($clHash);
return undef if (!defined($model));
my @chnList = ();
if ($clHash->{TYPE} eq 'HMCCUDEV') {
push @chnList, sort keys %{$model};
unshift (@chnList, pop(@chnList)) if (exists($model->{'d'}));
}
else {
push @chnList, 'd', 0, $chnNo;
}
foreach my $c (@chnList) {
$result .= $c eq 'd' ? "Device\n" : "Channel $c\n";
foreach my $ps (sort keys %{$model->{$c}}) {
$result .= " Paramset $ps\n";
$result .= join ("\n", map {
" ".$_.": ".
$model->{$c}{$ps}{$_}{TYPE}.
" [".HMCCU_FlagsToStr ('model', 'OPERATIONS', $model->{$c}{$ps}{$_}{OPERATIONS}, ',', '')."]".
" [".HMCCU_FlagsToStr ('model', 'FLAGS', $model->{$c}{$ps}{$_}{FLAGS}, ',', '')."]".
" RANGE=".$model->{$c}{$ps}{$_}{MIN}."-".$model->{$c}{$ps}{$_}{MAX}.
" DFLT=".$model->{$c}{$ps}{$_}{DEFAULT}.
" UNIT=".$model->{$c}{$ps}{$_}{UNIT}
} sort keys %{$model->{$c}{$ps}})."\n";
}
}
return $result;
}
######################################################################
# Get device addresses.
# Parameters:
@ -3458,6 +3623,8 @@ sub HMCCU_GetDeviceAddresses ($;$$)
my @addList = ();
my @ifaceList = ();
return @addList if (!exists($hash->{hmccu}{device}));
if (defined($iface)) {
push (@ifaceList, $iface);
}
@ -3506,6 +3673,8 @@ sub HMCCU_ExistsDeviceModel ($$$;$)
{
my ($hash, $type, $fw_ver, $chnNo) = @_;
return 0 if (!exists($hash->{hmccu}{model}));
if (defined($chnNo)) {
return (exists($hash->{hmccu}{model}{$type}) && exists($hash->{hmccu}{model}{$type}{$fw_ver}) &&
exists($hash->{hmccu}{model}{$type}{$fw_ver}{$chnNo}) ? 1 : 0);
@ -3565,6 +3734,8 @@ sub HMCCU_GetDeviceModel ($$$;$)
{
my ($hash, $type, $fw_ver, $chnNo) = @_;
return undef if (!exists($hash->{hmccu}{model}));
if (defined($chnNo)) {
return (exists($hash->{hmccu}{model}{$type}{$fw_ver}{$chnNo}) ?
$hash->{hmccu}{model}{$type}{$fw_ver}{$chnNo} : undef);
@ -3698,10 +3869,70 @@ sub HMCCU_GetParamValue ($$$$$)
return $value;
}
######################################################################
# Update client devices with peering information
# In addition peering information is stored in hash of IO device.
######################################################################
sub HMCCU_AddPeers ($$$)
{
my ($ioHash, $peerList, $iface) = @_;
my @devList = HMCCU_FindClientDevices ($ioHash, "(HMCCUDEV|HMCCUCHN)", undef,
"ccudevstate=active");
foreach my $p (@$peerList) {
my ($sd, $sc) = HMCCU_SplitChnAddr ($p->{SENDER});
my ($rd, $rc) = HMCCU_SplitChnAddr ($p->{RECEIVER});
$ioHash->{hmccu}{snd}{$iface}{$sd}{$sc}{$p->{RECEIVER}}{NAME} = $p->{NAME};
$ioHash->{hmccu}{snd}{$iface}{$sd}{$sc}{$p->{RECEIVER}}{DESCRIPTION} = $p->{DESCRIPTION};
$ioHash->{hmccu}{snd}{$iface}{$sd}{$sc}{$p->{RECEIVER}}{FLAGS} = $p->{FLAGS};
$ioHash->{hmccu}{rcv}{$iface}{$rd}{$rc}{$p->{SENDER}}{NAME} = $p->{NAME};
$ioHash->{hmccu}{rcv}{$iface}{$rd}{$rc}{$p->{SENDER}}{DESCRIPTION} = $p->{DESCRIPTION};
}
foreach my $d (@devList) {
my $clHash = $defs{$d};
my $clType = $clHash->{TYPE};
my ($da, $dc) = HMCCU_SplitChnAddr ($clHash->{ccuaddr});
next if (!exists($ioHash->{hmccu}{snd}{$iface}{$da}));
readingsBeginUpdate ($clHash);
foreach my $c (sort keys %{$ioHash->{hmccu}{snd}{$iface}{$da}}) {
next if ($clType eq 'HMCCUCHN' && "$c" ne "$dc");
my $key = "L-S-".$c;
my @rcvList = ();
foreach my $r (keys %{$ioHash->{hmccu}{snd}{$iface}{$da}{$c}}) {
my $rcvName = HMCCU_GetDeviceIdentifier ($ioHash, $r, $iface);
push @rcvList, $rcvName;
my $rn = HMCCU_CorrectName ($key."-".$rcvName);
my $rv = HMCCU_FlagsToStr ('peer', 'FLAGS', $ioHash->{hmccu}{snd}{$iface}{$da}{$c}{$r}{FLAGS});
readingsBulkUpdate ($clHash, $rn, $rv);
}
$clHash->{$key} = scalar(@rcvList) > 0 ? join (',', @rcvList) : "NO_RECEIVERS";
}
readingsEndUpdate ($clHash, 1);
foreach my $c (sort keys %{$ioHash->{hmccu}{rcv}{$iface}{$da}}) {
next if ($clType eq 'HMCCUCHN' && "$c" ne "$dc");
my $key = "L-R-".$c;
my @sndList = ();
foreach my $s (keys %{$ioHash->{hmccu}{rcv}{$iface}{$da}{$c}}) {
my $sndName = HMCCU_GetDeviceIdentifier ($ioHash, $s, $iface);
push @sndList, $sndName;
}
$clHash->{$key} = scalar(@sndList) > 0 ? join (',', @sndList) : "NO_SENDERS";
}
}
return scalar(@$peerList);
}
#######################################################################
# Convert bitmask to text
# Parameters:
# $set - 'device' or 'model'.
# $set - 'device', 'model' or 'peer'.
# $flag - Name of parameter.
# $value - Value of parameter.
# $sep - String separator. Default = ''.
@ -3714,29 +3945,45 @@ sub HMCCU_FlagsToStr ($$$;$$)
{
my ($set, $flag, $value, $sep, $default) = @_;
$value = 0 if (!defined($value));
$default = '' if (!defined($default));
$sep = '' if (!defined($sep));
my %bitmasks = (
'device' => {
'FLAGS' => { 1 => "Visible", 2 => "Internal", 8 => "DontDelete" },
'DIRECTION' => { 0 => "NONE", 1 => "SENDER", 2 => "RECEIVER" },
'RX_MODE' => { 1 => "ALWAYS", 2 => "BURST", 4 => "CONFIG", 8 => "WAKEUP", 16 => "LAZY_CONFIG" }
},
'model' => {
'FLAGS' => { 1 => "Visible", 2 => "Internal", 4 => "Transform", 8 => "Service", 16 => "Sticky" },
'OPERATIONS' => { 1 => 'R', 2 => 'W', 4 => 'E' }
},
);
my %mappings = (
'device' => {
'DIRECTION' => { 0 => "NONE", 1 => "SENDER", 2 => "RECEIVER" }
},
'peer' => {
'FLAGS' => { 0 => "OK", 1 => "SENDER_BROKEN", 2 => "RECEIVER_BROKEN" }
}
);
return $value if (!exists($bitmasks{$set}{$flag}));
return $value if (!exists($bitmasks{$set}{$flag}) && !exists($mappings{$set}{$flag}));
my @list = ();
foreach my $b (sort keys %{$bitmasks{$set}{$flag}}) {
push (@list, $bitmasks{$set}{$flag}{$b}) if ($value & $b);
if (exists($bitmasks{$set}{$flag})) {
my @list = ();
foreach my $b (sort keys %{$bitmasks{$set}{$flag}}) {
push (@list, $bitmasks{$set}{$flag}{$b}) if ($value & $b);
}
return scalar(@list) == 0 ? $default : join($sep, @list);
}
elsif ($mappings{$set}{$flag}) {
return exists($mappings{$set}{$flag}{$value}) ? $mappings{$set}{$flag}{$value} : $value;
}
return scalar(@list) == 0 ? $default : join($sep, @list);
return $value;
}
######################################################################
@ -3790,7 +4037,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$)
# Check if update of device allowed
my $disable = AttrVal ($clName, 'disable', 0);
my $update = AttrVal ($clName, 'ccureadings', 1);
my $update = AttrVal ($clName, 'ccureadings', HMCCU_IsFlag ($clName, 'noReadings') ? 0 : 1);
return undef if ($update == 0 || $disable == 1 || $clHash->{ccudevstate} ne 'active');
# Build list of affected addresses
@ -3923,7 +4170,7 @@ sub HMCCU_UpdateSingleDevice ($$$$)
# Check if update of device allowed
my $disable = AttrVal ($cltname, 'disable', 0);
my $update = AttrVal ($cltname, 'ccureadings', 1);
my $update = AttrVal ($cltname, 'ccureadings', HMCCU_IsFlag ($cltname, 'noReadings') ? 0 : 1);
return 0 if ($update == 0 || $disable == 1 || $clthash->{ccudevstate} ne 'active');
# Get device parameters and attributes
@ -5926,7 +6173,7 @@ sub HMCCU_FindClientDevices ($$$$)
if (defined ($internal)) {
foreach my $intspec (split (',', $internal)) {
my ($i, $v) = split ('=', $intspec);
if (defined ($v) && exists ($ch->{$i}) && $ch->{$i} !~ /$v/) {
if (!exists($ch->{$i}) || (defined ($v) && exists ($ch->{$i}) && $ch->{$i} !~ /$v/)) {
$m = 0;
last;
}
@ -7102,7 +7349,12 @@ sub HMCCU_SetMultipleDatapoints ($$) {
$v = HMCCU_EncodeEPDisplay ($v);
}
else {
$v = HMCCU_ScaleValue ($clHash, $chn, $dpt, $v, 1);
if ($v =~ /^[\$\%]{1,2}([0-9]{1,2}\.[A-Z0-9_]+)$/ && exists($clHash->{hmccu}{dp}{"$1"})) {
$v = HMCCU_SubstVariables ($clHash, $v, undef);
}
else {
$v = HMCCU_ScaleValue ($clHash, $chn, $dpt, $v, 1);
}
}
my $dptType = HMCCU_GetDatapointAttr ($ioHash, $ccuType, $chn, $dpt, 'type');
@ -7568,8 +7820,10 @@ sub HMCCU_UpdateCB ($$$)
# $method - RPC request method. Use listParamset or listRawParamset
# as an alias for getParamset if readings should not be updated.
# $address - Device address.
# $paramset - paramset name: VALUE, MASTER, LINK, ...
# $parref - Hash reference with parameter/value pairs (optional).
# $paramset - paramset name: VALUE, MASTER, LINK, ... If not defined
# request does not affect a parameter set
# $parref - Hash reference with parameter/value pairs or array
# reference with parameter values (optional).
# $filter - Regular expression for filtering response (default = .*).
# Return (retCode, result).
# retCode = 0 - Success
@ -7619,18 +7873,24 @@ sub HMCCU_RPCRequest ($$$$$;$)
# Build parameter array: (Address, Paramset [, Parameter ...])
# Paramset := VALUE | MASTER | LINK or any paramset supported by device
# Parameter := Name=Value[:Type]
my @parArray = ($addr, $paramset);
my @parArray = ($addr);
push (@parArray, $paramset) if (defined($paramset));
if (defined($parref)) {
foreach my $k (keys %{$parref}) {
my ($pv, $pt) = split (':', $parref->{$k});
if (!defined($pt)) {
my $paramDef = HMCCU_GetParamDef ($ioHash, $addr, $paramset, $k);
$pt = defined($paramDef) && defined($paramDef->{TYPE}) && $paramDef->{TYPE} ne '' ?
$paramDef->{TYPE} : "STRING";
if (ref($parref) eq 'HASH') {
foreach my $k (keys %{$parref}) {
my ($pv, $pt) = split (':', $parref->{$k});
if (!defined($pt)) {
my $paramDef = HMCCU_GetParamDef ($ioHash, $addr, $paramset, $k);
$pt = defined($paramDef) && defined($paramDef->{TYPE}) && $paramDef->{TYPE} ne '' ?
$paramDef->{TYPE} : "STRING";
}
$pv .= ":$pt";
push @parArray, "$k=$pv";
}
$pv .= ":$pt";
push @parArray, "$k=$pv";
};
}
elsif (ref($parref) eq 'ARRAY') {
push @parArray, @$parref;
}
}
# Submit RPC request
@ -7707,6 +7967,9 @@ sub HMCCU_RPCRequest ($$$$$;$)
readingsEndUpdate ($clHash, 1) if ($ccureadings);
}
else {
$result = $reqResult;
}
return (0, $result);
}

View File

@ -4,7 +4,7 @@
#
# $Id: 88_HMCCUCHN.pm 18552 2019-02-10 11:52:28Z zap $
#
# Version 4.4.002
# Version 4.4.004
#
# (c) 2020 zap (zap01 <at> t-online <dot> de)
#
@ -18,6 +18,7 @@ package main;
use strict;
use warnings;
use SetExtensions;
# use Data::Dumper;
require "$attr{global}{modpath}/FHEM/88_HMCCU.pm";
@ -43,10 +44,10 @@ sub HMCCUCHN_Initialize ($)
$hash->{parseParams} = 1;
$hash->{AttrList} = "IODev ccucalculate ".
"ccuflags:multiple-strict,ackState,logCommand,nochn0,trace ccureadingfilter ".
"ccuflags:multiple-strict,ackState,logCommand,nochn0,noReadings,trace ccureadingfilter ".
"ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ".
"ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix".
"ccureadings:0,1 ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint ".
"ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix ".
"ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint ".
"disable:0,1 hmstatevals:textField-long statedatapoint statevals substitute:textField-long ".
"substexcl stripnumber peer:textField-long ". $readingFnAttributes;
}
@ -73,7 +74,7 @@ sub HMCCUCHN_Define ($@)
$hash->{hmccu}{devspec} = $devspec;
# Defaults
$hash->{channels} = 1;
$hash->{hmccu}{channels} = 1;
$hash->{statevals} = 'devstate';
# Parse optional command line parameters
@ -221,6 +222,7 @@ sub HMCCUCHN_Set ($@)
my $opt = shift @$a;
return "No set command specified" if (!defined ($opt));
$opt = lc($opt);
my $rocmds = "clear defaults:noArg";
my $rwcmds = "clear config control datapoint defaults:noArg paramset link devstate values";
@ -496,6 +498,7 @@ sub HMCCUCHN_Get ($@)
my $opt = shift @$a;
return "No get command specified" if (!defined ($opt));
$opt = lc($opt);
return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' ||
!defined ($hash->{IODev}));
@ -515,7 +518,6 @@ sub HMCCUCHN_Get ($@)
my $ccuif = $hash->{ccuif};
my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', '');
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
my $ccureadings = AttrVal ($name, "ccureadings", 1);
my $result = '';
my $rc;
@ -532,7 +534,7 @@ sub HMCCUCHN_Get ($@)
my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd;
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 0);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
return $ccureadings ? undef : $result;
return $result;
}
elsif ($opt eq 'datapoint') {
my $objname = shift @$a;
@ -545,7 +547,7 @@ sub HMCCUCHN_Get ($@)
$objname = $ccuif.'.'.$ccuaddr.'.'.$objname;
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 0);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
return $ccureadings ? undef : $result;
return $result;
}
elsif ($opt eq 'update') {
my $ccuget = shift @$a;
@ -563,11 +565,9 @@ sub HMCCUCHN_Get ($@)
return HMCCU_SetError ($hash, -2) if ($result eq '');
return HMCCU_FormatDeviceInfo ($result);
}
elsif ($opt =~ /^(paramset|paramsetlist|config|links|values)$/) {
elsif ($opt =~ /^(paramset|config|values)$/) {
my $defParamset = '';
my $method = $opt =~ /list$/ ? 'listRawParamset' : 'getRawParamset';
my %parSets = ('config' => 'MASTER', 'links' => 'LINK', 'values' => 'VALUES',
'configlist' => 'MASTER');
my %parSets = ('config' => 'MASTER,LINK', 'values' => 'VALUES');
my ($devAddr, undef) = HMCCU_SplitChnAddr ($ccuaddr);
my @addList = ($devAddr, "$devAddr:0", $ccuaddr);
my $par = shift @$a;
@ -584,7 +584,6 @@ sub HMCCUCHN_Get ($@)
$par = '.*' if (!defined ($par));
my $res = '';
my %objects;
foreach my $a (@addList) {
my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $a, $ccuif);
@ -594,85 +593,41 @@ sub HMCCUCHN_Get ($@)
my ($da, $dc) = HMCCU_SplitChnAddr ($a);
$dc = 'd' if ($dc eq '');
$res .= $a eq $devAddr ? "Device $a\n" : "Channel $a\n";
foreach my $ps (split (',', $paramset)) {
next if ($devDesc->{PARAMSETS} !~ /$ps/);
$res .= " Paramset $ps\n";
($rc, $result) = HMCCU_RPCRequest ($hash, $method, $a, $ps, undef, $par);
($rc, $result) = HMCCU_RPCRequest ($hash, 'getRawParamset', $a, $ps, undef, $par);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
if ($opt =~ /list$/) {
$res .= join ("\n", map { $_ =~ /$par/ ?
" ".$_.' = '.HMCCU_GetParamValue ($ioHash, $devDesc, $ps, $_, $result->{$_}) : ()
} sort keys %$result)."\n";
}
else {
foreach my $p (keys %$result) { $objects{$da}{$dc}{$ps}{$p} = $result->{$p}; }
}
foreach my $p (keys %$result) { $objects{$da}{$dc}{$ps}{$p} = $result->{$p}; }
}
}
if ($opt !~ /list$/ && scalar(keys %objects) > 0) {
my $convRes = HMCCU_UpdateParamsetReadings ($ioHash, $hash, \%objects);
if (defined($convRes)) {
$res .= join ("\n", map { $_ =~ /$par/ ?
" ".$_.' = '.$convRes->{$_} : ()
} sort keys %$convRes)."\n";
}
}
return (!$ccureadings || $opt =~ /list$/) ? $res : undef;
}
elsif ($opt eq 'paramsetdesc') {
my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($ccuaddr);
my $model = HMCCU_GetClientDeviceModel ($hash);
return HMCCU_SetError ($hash, "Can't get device model") if (!defined($model));
my $res = '';
# my @chnList = sort keys %{$model};
my @chnList = ('d', 0, $chnNo);
# unshift (@chnList, pop(@chnList)) if (exists($model->{'d'}));
foreach my $c (@chnList) {
$res .= $c eq 'd' ? "Device\n" : "Channel $c\n";
foreach my $ps (sort keys %{$model->{$c}}) {
$res .= " Paramset $ps\n";
$result = join ("\n", map {
" ".$_.": ".
$model->{$c}{$ps}{$_}{TYPE}.
" [".HMCCU_FlagsToStr ('model', 'OPERATIONS', $model->{$c}{$ps}{$_}{OPERATIONS}, ',', '')."]".
" [".HMCCU_FlagsToStr ('model', 'FLAGS', $model->{$c}{$ps}{$_}{FLAGS}, ',', '')."]".
" RANGE=".$model->{$c}{$ps}{$_}{MIN}."-".$model->{$c}{$ps}{$_}{MAX}.
" DFLT=".$model->{$c}{$ps}{$_}{DEFAULT}.
" UNIT=".$model->{$c}{$ps}{$_}{UNIT}
} sort keys %{$model->{$c}{$ps}});
$res .= "$result\n";
if (scalar(keys %objects) > 0) {
my $convRes = HMCCU_UpdateParamsetReadings ($ioHash, $hash, \%objects);
if (defined($convRes)) {
foreach my $da (sort keys %$convRes) {
$res .= "Device $da\n";
foreach my $dc (sort keys %{$convRes->{$da}}) {
$res .= " Channel $dc\n";
$res .= join ("\n", map { $_ =~ /$par/ ?
" ".$_.' = '.$convRes->{$da}{$dc}{$_} : ()
} sort keys %{$convRes->{$da}{$dc}})."\n";
}
}
}
}
return $res;
return $res eq '' ? "No data found" : $res;
}
elsif ($opt eq 'paramsetdesc') {
$result = HMCCU_ParamsetDescToStr ($hash);
return defined($result) ? $result : HMCCU_SetError ($hash, "Can't get device model");
}
elsif ($opt eq 'devicedesc') {
my $ccuobj = $ccuaddr;
my $par = shift @$a;
my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($ccuaddr);
my @addList = ($devAddr, "$devAddr:0", $ccuaddr);
$result = '';
foreach my $a (@addList) {
my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $a, $ccuif);
return HMCCU_SetError ($hash, "Can't get device description")
if (!defined($devDesc));
$result .= $a eq $devAddr ? "Device $a" : "Channel $a";
$result .= " $devDesc->{_name} [$devDesc->{TYPE}]\n";
foreach my $n (sort keys %{$devDesc}) {
next if ($n =~ /^_/ || $n eq 'ADDRESS' || $n eq 'TYPE' ||
!defined($devDesc->{$n}) || $devDesc->{$n} eq '');
$result .= " $n: ".HMCCU_FlagsToStr ('device', $n, $devDesc->{$n}, ',', '')."\n";
}
}
return $result;
$result = HMCCU_DeviceDescToStr ($hash);
return defined($result) ? $result : HMCCU_SetError ($hash, "Can't get device description");
}
elsif ($opt eq 'defaults') {
return HMCCU_GetDefaults ($hash, 0);
@ -684,8 +639,8 @@ sub HMCCUCHN_Get ($@)
my @valuelist;
my $valuecount = HMCCU_GetValidDatapoints ($hash, $hash->{ccutype}, $c, 1, \@valuelist);
$retmsg .= ":".join(",",@valuelist) if ($valuecount > 0);
$retmsg .= " update:noArg deviceinfo:noArg config links".
" devicedesc:noArg paramset paramsetlist paramsetdesc:noArg values";
$retmsg .= " update:noArg deviceInfo:noArg config:noArg".
" deviceDesc:noArg paramset paramsetDesc:noArg values:noArg";
return $retmsg;
}
@ -871,7 +826,7 @@ sub HMCCUCHN_Get ($@)
</li><br/>
<li><b>get &lt;name&gt; paramset [&lt;paramset&gt;[,...]] [&lt;filter-expr&gt;]</b><br/>
Get parameters from all or specific parameter sets of device and channel.
If attribute 'ccureadings' is 0 results are displayed in browser window. Otherwise
If ccuflag noReadings is set results are displayed in browser window. Otherwise
they are stored as readings beginning with "R-" for MASTER parameters and "L-" for
LINK parameters. Values from other parameter sets are stored without prefix.
Prefixes can be modified with attribute 'ccuReadingPrefix'. If no <i>paramset</i>
@ -882,8 +837,12 @@ sub HMCCUCHN_Get ($@)
<li><b>get &lt;name&gt; paramsetdesc</b><br/>
Display description of parameter sets of channel and device.
</li><br/>
<li><b>get &lt;name&gt; paramsetlist [&lt;paramset&gt;[,...]] [&lt;filter-expr&gt;]</b><br/>
Same as 'get paramset' without storing parameters as readings.
<li><b>get &lt;name&gt; peers</b><br/>
Read links for current channel and store them as readings beginning with "P-".
This prefix can be modified with attribut 'ccuReadingPrefix'.
</li><br/>
<li><b>get &lt;name&gt; peerlist</b><br/>
Read links for current channel and list them in browser window.
</li><br/>
<li><b>get &lt;name&gt; update [{State | <u>Value</u>}]</b><br/>
Update all datapoints / readings of channel. With option 'State' the device is queried.
@ -919,11 +878,12 @@ sub HMCCUCHN_Get ($@)
Example:<br/>
<code>dewpoint:taupunkt:1.TEMPERATURE,1.HUMIDITY</code>
</li><br/>
<li><b>ccuflags {ackState, logCommand, nochn0, trace}</b><br/>
<li><b>ccuflags {ackState, logCommand, nochn0, 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/>
noReadings: Do not update readings<br/>
trace: Write log file information for operations related to this device.
</li><br/>
<li><b>ccuget {State | <u>Value</u>}</b><br/>
@ -931,9 +891,6 @@ sub HMCCUCHN_Get ($@)
because each request is sent to the device. With method 'Value' only CCU is queried.
Default is 'Value'.
</li><br/>
<li><b>ccureadings {0 | <u>1</u>}</b><br/>
If set to 1 values read from CCU will be stored as readings. Default is 1.
</li><br/>
<li><b>ccureadingfilter &lt;filter-rule[;...]&gt;</b><br/>
Only datapoints matching specified expression <i>RegExp</i> are stored as readings.<br/>
Syntax for <i>filter-rule</i> is either:<br/>
@ -991,7 +948,8 @@ sub HMCCUCHN_Get ($@)
</code>
</li><br/>
<li><b>ccuReadingPrefix &lt;paramset&gt;:&lt;prefix&gt;[,...]</b><br/>
Set reading name prefix for parameter sets.
Set reading name prefix for parameter sets. The special parameter set 'peer' can
be used for link readings.
</li><br/>
<li><b>ccuscaleval &lt;[channelno.]datapoint&gt;:&lt;factor&gt;[,...]</b><br/>
<b>ccuscaleval &lt;[!][channelno.]datapoint&gt;:&lt;min&gt;:&lt;max&gt;:&lt;minn&gt;:&lt;maxn&gt;[,...]

View File

@ -4,7 +4,7 @@
#
# $Id: 88_HMCCUDEV.pm 18552 2019-02-10 11:52:28Z zap $
#
# Version 4.4.000
# Version 4.4.001
#
# (c) 2020 zap (zap01 <at> t-online <dot> de)
#
@ -45,10 +45,10 @@ sub HMCCUDEV_Initialize ($)
$hash->{parseParams} = 1;
$hash->{AttrList} = "IODev ccuaggregate:textField-long ccucalculate:textField-long ".
"ccuflags:multiple-strict,ackState,logCommand,nochn0,trace ccureadingfilter:textField-long ".
"ccuflags:multiple-strict,ackState,logCommand,nochn0,noReadings,trace ccureadingfilter:textField-long ".
"ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ".
"ccureadingname:textField-long ".
"ccureadings:0,1 ccuget:State,Value ccuscaleval ccuSetOnChange ccuverify:0,1,2 disable:0,1 ".
"ccuget:State,Value ccuscaleval ccuSetOnChange ccuverify:0,1,2 disable:0,1 ".
"hmstatevals:textField-long statevals substexcl substitute:textField-long statechannel ".
"statedatapoint controldatapoint stripnumber peer:textField-long ".
$readingFnAttributes;
@ -210,7 +210,7 @@ sub HMCCUDEV_InitDevice ($$)
$dev_hash->{ccuaddr} = $da;
$dev_hash->{ccuname} = $dn;
$dev_hash->{ccutype} = $dt;
$dev_hash->{channels} = $dc;
$dev_hash->{hmccu}{channels} = $dc;
}
# Parse group options
@ -402,7 +402,7 @@ sub HMCCUDEV_Set ($@)
if ($objname =~ /^([0-9]+)\..+$/) {
my $chn = $1;
return HMCCU_SetError ($hash, -7) if ($chn >= $hash->{channels});
return HMCCU_SetError ($hash, -7) if ($chn >= $hash->{hmccu}{channels});
}
else {
return HMCCU_SetError ($hash, -11) if ($sc eq '');
@ -422,7 +422,7 @@ sub HMCCUDEV_Set ($@)
elsif ($opt eq 'control') {
return HMCCU_SetError ($hash, -12) if ($cc eq '');
return HMCCU_SetError ($hash, -14) if ($cd eq '');
return HMCCU_SetError ($hash, -7) if ($cc >= $hash->{channels});
return HMCCU_SetError ($hash, -7) if ($cc >= $hash->{hmccu}{channels});
my $objvalue = shift @$a;
return HMCCU_SetError ($hash, "Usage: set $name control {value}") if (!defined ($objvalue));
@ -583,7 +583,7 @@ sub HMCCUDEV_Set ($@)
# Channel number is optional because parameter can be related to device or channel
if ((scalar @$a) > 0 && $$a[0] =~ /^([0-9]{1,2})$/) {
return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{hmccu}{channels});
$objname .= ':'.$1;
}
@ -602,7 +602,7 @@ sub HMCCUDEV_Set ($@)
$key = uc($p);
}
elsif ($p =~ /^([0-9]+)$/ && !defined ($chn)) {
HMCCU_SetError ($hash, -7) if ($p >= $hash->{channels});
HMCCU_SetError ($hash, -7) if ($p >= $hash->{hmccu}{channels});
$chn = $p;
}
}
@ -688,7 +688,6 @@ sub HMCCUDEV_Get ($@)
my $ccuaddr = $hash->{ccuaddr};
my $ccuif = $hash->{ccuif};
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
my $ccureadings = AttrVal ($name, 'ccureadings', 1);
my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', '');
my $result = '';
@ -712,7 +711,7 @@ sub HMCCUDEV_Get ($@)
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 0);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
return $ccureadings ? undef : $result;
return $result;
}
elsif ($opt eq 'datapoint') {
my $objname = shift @$a;
@ -721,7 +720,7 @@ sub HMCCUDEV_Get ($@)
if ($objname =~ /^([0-9]+)\..+$/) {
my $chn = $1;
return HMCCU_SetError ($hash, -7) if ($chn >= $hash->{channels});
return HMCCU_SetError ($hash, -7) if ($chn >= $hash->{hmccu}{channels});
}
else {
return HMCCU_SetError ($hash, -11) if ($sc eq '');
@ -736,7 +735,7 @@ sub HMCCUDEV_Get ($@)
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
return $ccureadings ? undef : $result;
return $result;
}
elsif ($opt eq 'update') {
my $ccuget = shift @$a;
@ -770,102 +769,74 @@ sub HMCCUDEV_Get ($@)
return HMCCU_SetError ($hash, -2) if ($result eq '');
return HMCCU_FormatDeviceInfo ($result);
}
elsif ($opt eq 'config') {
my $ccuobj = $ccuaddr;
elsif ($opt =~ /^(paramset|config|values)$/) {
my $par = shift @$a;
if (defined ($par)) {
if ($par =~ /^([0-9]{1,2})$/) {
return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
$ccuobj .= ':'.$1;
$par = shift @$a;
}
}
$par = '.*' if (!defined ($par));
my $defParamset = '';
my %parSets = ('config' => 'MASTER,LINK', 'values' => 'VALUES');
my ($devAddr, undef) = HMCCU_SplitChnAddr ($ccuaddr);
my @addList = ($ccuaddr);
($rc, $result) = HMCCU_RPCRequest ($hash, "getParamset", $ccuobj, "MASTER", undef, $par);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
my $res = $result;
($rc, $result) = HMCCU_RPCRequest ($hash, "getParamset", $ccuobj, "LINK", undef, $par);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
return $ccureadings ? undef : $res.$result;
}
elsif ($opt eq 'configlist') {
my $ccuobj = $ccuaddr;
my $chnNo = 'd';
my $par = shift @$a;
if (defined ($par)) {
if ($par =~ /^([0-9]{1,2})$/) {
return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
$chnNo = $1;
$ccuobj .= ':'.$chnNo;
$par = shift @$a;
}
}
$par = '.*' if (!defined ($par));
my $devDesc = HMCCU_GetDeviceDesc ($hash, undef, $ccuobj);
my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $ccuaddr, $ccuif);
return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc));
push @addList, split (',', $devDesc->{CHILDREN});
my $res = '';
foreach my $paramset (split (',', $devDesc->{PARAMSETS})) {
next if ($paramset ne 'MASTER' && $paramset ne 'LINK');
$res .= "$paramset:\n";
($rc, $result) = HMCCU_RPCRequest ($hash, "listParamset", $ccuobj, $paramset, undef, $par);
$res .= "$result\n";
if (exists($parSets{$opt})) {
$defParamset = $parSets{$opt};
}
else {
if (defined($par)) {
$defParamset = $par;
$par = shift @$a;
}
}
$par = '.*' if (!defined ($par));
my $res = '';
my %objects;
foreach my $a (@addList) {
my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $a, $ccuif);
return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc));
my $paramset = $defParamset eq '' ? $devDesc->{PARAMSETS} : $defParamset;
my ($da, $dc) = HMCCU_SplitChnAddr ($a);
$dc = 'd' if ($dc eq '');
$res .= $a eq $devAddr ? "Device $a\n" : "Channel $a\n";
foreach my $ps (split (',', $paramset)) {
next if ($devDesc->{PARAMSETS} !~ /$ps/);
($rc, $result) = HMCCU_RPCRequest ($hash, 'getRawParamset', $a, $ps, undef, $par);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
my @paramsetPars = keys %$result;
next if (scalar(@paramsetPars) == 0);
$res .= " Paramset $ps\n".join ("\n", map { $_ =~ /$par/ ?
" ".$_.' = '.HMCCU_GetParamValue ($ioHash, $devDesc, $ps, $_, $result->{$_}) : ()
} sort @paramsetPars)."\n";
foreach my $p (@paramsetPars) { $objects{$da}{$dc}{$ps}{$p} = $result->{$p}; }
}
}
if (scalar(keys %objects) > 0) {
my $convRes = HMCCU_UpdateParamsetReadings ($ioHash, $hash, \%objects);
if (defined($convRes)) {
$res .= join ("\n", map { $_ =~ /$par/ ?
" ".$_.' = '.$convRes->{$_} : ()
} sort keys %$convRes)."\n";
}
}
return $res;
}
elsif ($opt eq 'paramsetdesc') {
my ($devAddr, $chnNo) = HMCCU_SplitChnAddr ($ccuaddr);
my $model = HMCCU_GetClientDeviceModel ($hash);
return HMCCU_SetError ($hash, "Can't get device model") if (!defined($model));
my $res = '';
my @chnList = sort keys %{$model};
unshift (@chnList, pop(@chnList)) if (exists($model->{'d'}));
foreach my $c (@chnList) {
$res .= $c eq 'd' ? "Device\n" : "Channel $c\n";
foreach my $ps (sort keys %{$model->{$c}}) {
$res .= " Paramset $ps\n";
$result = join ("\n", map {
" ".$_.": ".
$model->{$c}{$ps}{$_}{TYPE}.
" [".HMCCU_FlagsToStr ('model', 'OPERATIONS', $model->{$c}{$ps}{$_}{OPERATIONS}, ',', '')."]".
" [".HMCCU_FlagsToStr ('model', 'FLAGS', $model->{$c}{$ps}{$_}{FLAGS}, ',', '')."]".
" RANGE=".$model->{$c}{$ps}{$_}{MIN}."-".$model->{$c}{$ps}{$_}{MAX}.
" DFLT=".$model->{$c}{$ps}{$_}{DEFAULT}.
" UNIT=".$model->{$c}{$ps}{$_}{UNIT}
} sort keys %{$model->{$c}{$ps}});
$res .= "$result\n";
}
}
return $res;
$result = HMCCU_ParamsetDescToStr ($hash);
return defined($result) ? $result : HMCCU_SetError ($hash, "Can't get device model");
}
elsif ($opt eq 'devicedesc') {
my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $ccuaddr, $ccuif);
return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc));
my @addList = ();
push (@addList, split (',', $devDesc->{CHILDREN}))
if (defined($devDesc->{CHILDREN}) && $devDesc->{CHILDREN} ne '');
my $a = $ccuaddr;
$result = '';
while (1) {
$result .= $a eq $ccuaddr ? "Device $a" : "Channel $a";
$result .= " $devDesc->{_name} [$devDesc->{TYPE}]\n";
foreach my $n (sort keys %{$devDesc}) {
next if ($n =~ /^_/ || $n eq 'ADDRESS' || $n eq 'TYPE' ||
!defined($devDesc->{$n}) || $devDesc->{$n} eq '');
$result .= " $n: ".HMCCU_FlagsToStr ('device', $n, $devDesc->{$n}, ',', '')."\n";
}
$a = shift (@addList);
last if (!defined($a));
$devDesc = HMCCU_GetDeviceDesc ($ioHash, $a, $ccuif);
return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc));
}
return $result;
$result = HMCCU_DeviceDescToStr ($hash);
return defined($result) ? $result : HMCCU_SetError ($hash, "Can't get device description");
}
elsif ($opt eq 'defaults') {
$result = HMCCU_GetDefaults ($hash, 0);
@ -878,10 +849,10 @@ sub HMCCUDEV_Get ($@)
my $valuecount = HMCCU_GetValidDatapoints ($hash, $ccutype, -1, 1, \@valuelist);
$retmsg .= ":".join(",", @valuelist) if ($valuecount > 0);
$retmsg .= " defaults:noArg update:noArg config configlist paramsetdesc:noArg devicedesc:noArg".
" deviceinfo:noArg";
$retmsg .= " defaults:noArg update:noArg config paramset".
" paramsetdesc:noArg devicedesc:noArg deviceinfo:noArg";
$retmsg .= ' devstate:noArg' if ($sc ne '');
return $retmsg;
}
}
@ -1044,18 +1015,11 @@ sub HMCCUDEV_Get ($@)
<a name="HMCCUDEVget"></a>
<b>Get</b><br/><br/>
<ul>
<li><b>get &lt;name&gt; config [&lt;channel-number&gt;] [&lt;filter-expr&gt;]</b><br/>
Get configuration parameters of CCU device. If attribute 'ccureadings' is set to 0
<li><b>get &lt;name&gt; config [&lt;filter-expr&gt;]</b><br/>
Get configuration parameters of CCU device and all its channels. If ccuflag noReadings is set
parameters are displayed in browser window (no readings set). Parameters can be filtered
by <i>filter-expr</i>.
</li><br/>
<li><b>get &lt;name&gt; configdesc [&lt;channel-number&gt;]</b><br/>
Display description of configuration parameters for CCU device.
</li><br/>
<li><b>get &lt;name&gt; configlist [&lt;channel-number&gt;] [&lt;filter-expr&gt;]</b><br/>
Display configuration parameters of CCU device. Parameters can be filtered by
<i>filter-expr</i>.
</li><br/>
<li><b>get &lt;name&gt; datapoint [&lt;channel-number&gt;.]&lt;datapoint&gt;</b><br/>
Get value of a CCU device datapoint. If <i>channel-number</i> is not specified state
channel is used.
@ -1073,6 +1037,9 @@ sub HMCCUDEV_Get ($@)
Get state of CCU device. Attribute 'statechannel' must be set. Default state datapoint
STATE can be modified by attribute 'statedatapoint'.
</li><br/>
<li><b>get &lt;name&gt; paramset [&lt;paramset&gt;[,...]] [&lt;filter-expr&gt;]</b><br/>
Read parameter set values.
</li><br/>
<li><b>get &lt;name&gt; update [{State | <u>Value</u>}]</b><br/>
<a href="#HMCCUCHNget">see HMCCUCHN</a>
</li><br/>
@ -1093,9 +1060,6 @@ sub HMCCUDEV_Get ($@)
<li><b>ccuget {State | <u>Value</u>}</b><br/>
<a href="#HMCCUCHNattr">see HMCCUCHN</a>
</li><br/>
<li><b>ccureadings {0 | <u>1</u>}</b><br/>
<a href="#HMCCUCHNattr">see HMCCUCHN</a>
</li><br/>
<li><b>ccureadingfilter &lt;filter-rule[,...]&gt;</b><br/>
<a href="#HMCCUCHNattr">see HMCCUCHN</a>
</li><br/>

View File

@ -4,7 +4,7 @@
#
# $Id: 88_HMCCURPCPROC.pm 18745 2019-02-26 17:33:23Z zap $
#
# Version 4.4.000
# Version 4.4.002
#
# Subprocess based RPC Server module for HMCCU.
#
@ -38,7 +38,7 @@ require "$attr{global}{modpath}/FHEM/88_HMCCU.pm";
######################################################################
# HMCCURPC version
my $HMCCURPCPROC_VERSION = '4.4.000';
my $HMCCURPCPROC_VERSION = '4.4.001';
# Maximum number of events processed per call of Read()
my $HMCCURPCPROC_MAX_EVENTS = 100;
@ -143,6 +143,7 @@ sub HMCCURPCPROC_ProcessEvent ($$);
# RPC information
sub HMCCURPCPROC_GetDeviceDesc ($;$);
sub HMCCURPCPROC_GetParamsetDesc ($;$);
sub HMCCURPCPROC_GetPeers ($);
# RPC server control functions
sub HMCCURPCPROC_CheckProcessState ($$);
@ -403,15 +404,6 @@ sub HMCCURPCPROC_InitDevice ($$) {
$attr{$name}{verbose} = 2;
}
# Read RPC device descriptions
if ($dev_hash->{rpcinterface} ne 'CUxD') {
HMCCU_Log ($dev_hash, 1, "Updating internal device tables");
HMCCU_ResetDeviceTables ($hmccu_hash, $dev_hash->{rpcinterface});
my $cd = HMCCURPCPROC_GetDeviceDesc ($dev_hash);
my $cm = HMCCURPCPROC_GetParamsetDesc ($dev_hash);
HMCCU_Log ($dev_hash, 1, "Read $cd channel and device descriptions and $cm device models from CCU");
}
# RPC device ready
HMCCURPCPROC_ResetRPCState ($dev_hash);
HMCCURPCPROC_SetState ($dev_hash, 'Initialized');
@ -630,7 +622,7 @@ sub HMCCURPCPROC_Get ($@)
return "No get command specified" if (!defined ($opt));
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
my $options = "deviceDesc rpcevents:noArg rpcstate:noArg";
my $options = "deviceDesc rpcevents:noArg rpcstate:noArg peers:noArg";
return "HMCCURPCPROC: CCU busy, choose one of rpcstate:noArg"
if ($opt ne 'rpcstate' && HMCCURPCPROC_IsRPCStateBlocking ($hash));
@ -658,6 +650,10 @@ sub HMCCURPCPROC_Get ($@)
my $cm = HMCCURPCPROC_GetParamsetDesc ($hash, $address);
return "Read $cd channel and device descriptions and $cm device models from CCU";
}
elsif ($opt eq 'peers') {
my $cp = HMCCURPCPROC_GetPeers ($hash);
return "Read $cp links from CCU";
}
elsif ($opt eq 'rpcevents') {
my @eventtypes = ("EV", "ND", "DD", "RD", "RA", "UD", "IN", "EX", "SL", "TO");
my $clkey = 'CB'.$hash->{rpcport}.$hash->{rpcid};
@ -1135,6 +1131,33 @@ sub HMCCURPCPROC_GetAttribute ($$$$)
return $default;
}
######################################################################
# Get links (sender and receiver) from CCU.
######################################################################
sub HMCCURPCPROC_GetPeers ($)
{
my ($hash) = @_;
my $ioHash = $hash->{IODev};
my $c = 0;
my $rd = HMCCURPCPROC_SendRequest ($hash, "getLinks");
return HMCCU_Log ($hash, 2, "Can't get peers", 0) if (!defined($rd));
if (ref($rd) eq 'HASH' && exists($rd->{faultString})) {
return HMCCU_Log ($hash, 2, "Can't get peers. ".$rd->{faultString}, 0);
}
elsif (ref($rd) eq 'ARRAY') {
$c = HMCCU_AddPeers ($ioHash, $rd, $hash->{rpcinterface});
}
else {
return HMCCU_Log ($hash, 2, "Unexpected response from getLinks", 0);
}
return $c;
}
######################################################################
# Get RPC device descriptions from CCU
# Return number of devices and channels read from CCU.
@ -1876,7 +1899,7 @@ sub HMCCURPCPROC_SendRequest ($@)
if ($RPC_METHODS{$request} == 1) {
# Build a parameter hash
while (my $p = shift @param) {
my $pt = "STRING";
my $pt;
if ($p =~ /${re}/) {
$pt = $1;
$p =~ s/${re}//;
@ -1903,7 +1926,7 @@ sub HMCCURPCPROC_SendRequest ($@)
# Build a parameter array
while (my $p = shift @param) {
my $pt = "STRING";
my $pt;
if ($p =~ /${re}/) {
$pt = $1;
$p =~ s/${re}//;