mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-03 10:46:53 +00:00
HMCCU: new version
git-svn-id: https://svn.fhem.de/fhem/trunk@10975 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
035d7e57df
commit
9092b63c77
@ -1,14 +1,14 @@
|
||||
####################################################################
|
||||
#########################################################################
|
||||
#
|
||||
# 88_HMCCU.pm
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Version 2.7
|
||||
# Version 2.8
|
||||
#
|
||||
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
||||
#
|
||||
####################################################################
|
||||
#########################################################################
|
||||
#
|
||||
# define <name> HMCCU <host_or_ip> [<read_interval>]
|
||||
#
|
||||
@ -30,10 +30,11 @@
|
||||
# get <name> configdesc {<device>|<channel>} [<rpcport>]
|
||||
# get <name> deviceinfo <device>
|
||||
# get <name> rpcstate
|
||||
# get <name> update [<devexp> [{ State | Value }]]
|
||||
# get <name> update [<fhemdevexp> [{ State | Value }]]
|
||||
# get <name> updateccu [<devexp> [{ State | Value }]]
|
||||
#
|
||||
# attr <name> ccuget { State | Value }
|
||||
# attr <name> ccureadingfilter <datapoint_exp>
|
||||
# attr <name> ccureadingfilter <filter_rule>
|
||||
# attr <name> ccureadingformat { name | address }
|
||||
# attr <name> ccureadings { 0 | 1 }
|
||||
# attr <name> ccutrace {<ccudevname_exp>|<ccudevaddr_exp>}
|
||||
@ -49,8 +50,9 @@
|
||||
# attr <name> substitute <subst_rule>
|
||||
# attr <name> updatemode { client | both | hmccu }
|
||||
#
|
||||
# subst_rule := [datapoint[,...]]!<regexp1>:<subtext1>[,...][;...]
|
||||
####################################################################
|
||||
# filter_rule := [channel-regexp!]datapoint-regexp[,...]
|
||||
# subst_rule := [datapoint[,...]!]<regexp>:<subtext>[,...][;...]
|
||||
#########################################################################
|
||||
|
||||
package main;
|
||||
|
||||
@ -102,13 +104,14 @@ sub HMCCU_Set ($@);
|
||||
sub HMCCU_Get ($@);
|
||||
sub HMCCU_Attr ($@);
|
||||
sub HMCCU_ParseObject ($$);
|
||||
sub HMCCU_FilterReading ($$$);
|
||||
sub HMCCU_GetReadingName ($$$$$$);
|
||||
sub HMCCU_FormatReadingValue ($$);
|
||||
sub HMCCU_SetError ($$);
|
||||
sub HMCCU_SetState ($$);
|
||||
sub HMCCU_Substitute ($$$$);
|
||||
sub HMCCU_SubstRule ($$$);
|
||||
sub HMCCU_UpdateClients ($$$);
|
||||
sub HMCCU_UpdateClients ($$$$);
|
||||
sub HMCCU_UpdateClientReading ($@);
|
||||
sub HMCCU_DeleteDevices ($);
|
||||
sub HMCCU_StartRPCServer ($);
|
||||
@ -119,16 +122,21 @@ sub HMCCU_CheckProcess ($$);
|
||||
sub HMCCU_GetDeviceInfo ($$$);
|
||||
sub HMCCU_GetDeviceList ($);
|
||||
sub HMCCU_GetAddress ($$$);
|
||||
sub HMCCU_IsDevAddr ($$);
|
||||
sub HMCCU_IsChnAddr ($$);
|
||||
sub HMCCU_SplitChnAddr ($);
|
||||
sub HMCCU_GetCCUObjectAttribute ($$);
|
||||
sub HMCCU_GetHash ($@);
|
||||
sub HMCCU_GetAttribute ($$$$);
|
||||
sub HMCCU_GetSpecialDatapoints ($$$$$);
|
||||
sub HMCCU_IsValidDevice ($);
|
||||
sub HMCCU_GetMatchingDevices ($$$$);
|
||||
sub HMCCU_GetDeviceName ($$);
|
||||
sub HMCCU_GetChannelName ($$);
|
||||
sub HMCCU_GetDeviceType ($$);
|
||||
sub HMCCU_GetDeviceChannels ($);
|
||||
sub HMCCU_GetDeviceInterface ($$);
|
||||
sub HMCCU_ResetRPCQueue ($);
|
||||
sub HMCCU_ReadRPCQueue ($);
|
||||
sub HMCCU_HMScript ($$);
|
||||
sub HMCCU_GetDatapoint ($@);
|
||||
@ -136,10 +144,11 @@ sub HMCCU_SetDatapoint ($$$);
|
||||
sub HMCCU_GetVariables ($$);
|
||||
sub HMCCU_SetVariable ($$$);
|
||||
sub HMCCU_GetUpdate ($$$);
|
||||
sub HMCCU_UpdateDeviceReadings ($$);
|
||||
sub HMCCU_GetChannel ($$);
|
||||
sub HMCCU_RPCGetConfig ($$$$);
|
||||
sub HMCCU_RPCSetConfig ($$$);
|
||||
sub HMCCU_State ($);
|
||||
sub HMCCU_AggReadings ($$$$$);
|
||||
sub HMCCU_Dewpoint ($$$$);
|
||||
|
||||
|
||||
@ -159,6 +168,16 @@ sub HMCCU_Initialize ($)
|
||||
$hash->{ShutdownFn} = "HMCCU_Shutdown";
|
||||
|
||||
$hash->{AttrList} = "stripchar stripnumber:0,1,2 ccureadings:0,1 ccureadingfilter ccureadingformat:name,address rpcinterval:3,5,10 rpcqueue rpcport rpcserver:on,off parfile statedatapoint statevals substitute updatemode:client,both,hmccu ccutrace ccuget:Value,State ". $readingFnAttributes;
|
||||
|
||||
# Get list of CCU devices
|
||||
# foreach my $d (keys %defs) {
|
||||
# my $h = $defs{$d};
|
||||
# if ($h->{TYPE} eq "HMCCU") {
|
||||
# HMCCU_GetDeviceList ($h);
|
||||
# DoTrigger ($h->{NAME}, "Module HMCCU reloaded");
|
||||
# last;
|
||||
# }
|
||||
# }
|
||||
}
|
||||
|
||||
#####################################
|
||||
@ -398,7 +417,7 @@ sub HMCCU_Get ($@)
|
||||
my ($hash, @a) = @_;
|
||||
my $name = shift @a;
|
||||
my $opt = shift @a;
|
||||
my $options = "devicelist:noArg devstate datapoint vars channel update parfile config configdesc rpcstate:noArg deviceinfo";
|
||||
my $options = "devicelist:noArg devstate datapoint vars channel update updateccu parfile config configdesc rpcstate:noArg deviceinfo";
|
||||
my $host = $hash->{host};
|
||||
|
||||
if (HMCCU_IsRPCStateBlocking ($hash)) {
|
||||
@ -477,16 +496,16 @@ sub HMCCU_Get ($@)
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return $ccureadings ? undef : $result;
|
||||
}
|
||||
elsif ($opt eq 'update') {
|
||||
elsif ($opt eq 'update' || $opt eq 'updateccu') {
|
||||
my $devexp = shift @a;
|
||||
$devexp = '.*' if (!defined ($devexp));
|
||||
my $ccuget = shift @a;
|
||||
$ccuget = 'Attr' if (!defined ($ccuget));
|
||||
if ($ccuget !~ /^(Attr|State|Value)$/) {
|
||||
return HMCCU_SetError ($hash, "Usage: get $name update [device-expr [{'State'|'Value'}]]");
|
||||
return HMCCU_SetError ($hash, "Usage: get $name $opt [device-expr [{'State'|'Value'}]]");
|
||||
}
|
||||
|
||||
my ($c_ok, $c_err) = HMCCU_UpdateClients ($hash, $devexp, $ccuget);
|
||||
my ($c_ok, $c_err) = HMCCU_UpdateClients ($hash, $devexp, $ccuget, ($opt eq 'updateccu') ? 1 : 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return "$c_ok client devices successfully updated. Update for $c_err client devices failed";
|
||||
@ -701,6 +720,36 @@ sub HMCCU_ParseObject ($$)
|
||||
return ($i, $a, $c, $d, $n, $f);
|
||||
}
|
||||
|
||||
##################################################################
|
||||
# Filter reading by datapoint and optionally by channel name
|
||||
##################################################################
|
||||
|
||||
sub HMCCU_FilterReading ($$$)
|
||||
{
|
||||
my ($hash, $chn, $dpt) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $rf = AttrVal ($name, 'ccureadingfilter', '.*');
|
||||
return 1 if ($rf eq '.*');
|
||||
|
||||
my $chnnam = HMCCU_GetChannelName ($chn, '');
|
||||
|
||||
my @rules = split (",", $rf);
|
||||
foreach my $r (@rules) {
|
||||
my ($c, $f) = split ("!", $r);
|
||||
if (defined ($f) && $chnnam ne '') {
|
||||
if ($chnnam =~ /$c/) {
|
||||
return ($dpt =~ /$f/) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 1 if ($dpt =~ /$r/);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
##################################################################
|
||||
# Build reading name
|
||||
#
|
||||
@ -898,25 +947,49 @@ sub HMCCU_SubstRule ($$$)
|
||||
# will fail if attribute ccureadings of a device is set to 0.
|
||||
##################################################################
|
||||
|
||||
sub HMCCU_UpdateClients ($$$)
|
||||
sub HMCCU_UpdateClients ($$$$)
|
||||
{
|
||||
my ($hash, $devexp, $ccuget) = @_;
|
||||
my ($hash, $devexp, $ccuget, $fromccu) = @_;
|
||||
my $c_ok = 0;
|
||||
my $c_err = 0;
|
||||
|
||||
foreach my $d (keys %defs) {
|
||||
# Get hash of client device
|
||||
my $ch = $defs{$d};
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV' && $ch->{TYPE} ne 'HMCCUCHN');
|
||||
next if ($ch->{NAME} !~ /$devexp/);
|
||||
next if (!defined ($ch->{IODev}) || !defined ($ch->{ccuaddr}));
|
||||
if ($fromccu) {
|
||||
foreach my $name (sort keys %HMCCU_Addresses) {
|
||||
next if ($name !~ /$devexp/ || !($HMCCU_Addresses{$name}{valid}));
|
||||
|
||||
my $rc = HMCCU_GetUpdate ($ch, $ch->{ccuaddr}, $ccuget);
|
||||
if ($rc <= 0) {
|
||||
$c_err++;
|
||||
foreach my $d (keys %defs) {
|
||||
my $ch = $defs{$d};
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV' && $ch->{TYPE} ne 'HMCCUCHN');
|
||||
next if ($ch->{ccudevstate} ne 'Active');
|
||||
next if ($ch->{ccuaddr} ne $HMCCU_Addresses{$name}{address});
|
||||
next if (!defined ($ch->{IODev}) || !defined ($ch->{ccuaddr}));
|
||||
|
||||
my $rc = HMCCU_GetUpdate ($ch, $HMCCU_Addresses{$name}{address}, $ccuget);
|
||||
if ($rc <= 0) {
|
||||
$c_err++;
|
||||
}
|
||||
else {
|
||||
$c_ok++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$c_ok++;
|
||||
}
|
||||
else {
|
||||
foreach my $d (keys %defs) {
|
||||
# Get hash of client device
|
||||
my $ch = $defs{$d};
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV' && $ch->{TYPE} ne 'HMCCUCHN');
|
||||
next if ($ch->{ccudevstate} ne 'Active');
|
||||
next if ($ch->{NAME} !~ /$devexp/);
|
||||
next if (!defined ($ch->{IODev}) || !defined ($ch->{ccuaddr}));
|
||||
|
||||
my $rc = HMCCU_GetUpdate ($ch, $ch->{ccuaddr}, $ccuget);
|
||||
if ($rc <= 0) {
|
||||
$c_err++;
|
||||
}
|
||||
else {
|
||||
$c_ok++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -930,7 +1003,7 @@ sub HMCCU_UpdateClients ($$$)
|
||||
# hash, devadd, channelno, reading, value, [mode]
|
||||
#
|
||||
# Parameter devadd can be a device or a channel address. If
|
||||
# devadd is a channel address parameter channelno should be ''.
|
||||
# devadd is a channel address parameter channel should be ''.
|
||||
# Valid modes are: hmccu, rpcevent, client.
|
||||
# Reading values are substituted if attribute substitute is set
|
||||
# in client device.
|
||||
@ -943,7 +1016,7 @@ sub HMCCU_UpdateClientReading ($@)
|
||||
|
||||
my $hmccu_substitute = AttrVal ($name, 'substitute', '');
|
||||
my $hmccu_updreadings = AttrVal ($name, 'ccureadings', 1);
|
||||
my $hmccu_flt = AttrVal ($name, 'ccureadingfilter', '.*');
|
||||
# my $hmccu_flt = AttrVal ($name, 'ccureadingfilter', '.*');
|
||||
my $updatemode = AttrVal ($name, 'updatemode', 'hmccu');
|
||||
|
||||
# Update mode can be: client, hmccu, both, rpcevent
|
||||
@ -963,7 +1036,9 @@ sub HMCCU_UpdateClientReading ($@)
|
||||
if ($hmccu_updreadings && $updatemode ne 'client') {
|
||||
$hmccu_value = HMCCU_Substitute ($value, $hmccu_substitute, 0, $reading);
|
||||
$hmccu_value = HMCCU_FormatReadingValue ($hash, $hmccu_value);
|
||||
if ($updatemode ne 'rpcevent' && ($dpt eq '' || $dpt =~ /$hmccu_flt/)) {
|
||||
# if ($updatemode ne 'rpcevent' && ($dpt eq '' || $dpt =~ /$hmccu_flt/)) {
|
||||
if ($updatemode ne 'rpcevent' && ($dpt eq '' ||
|
||||
HMCCU_FilterReading ($hash, $chnadd, $dpt))) {
|
||||
readingsSingleUpdate ($hash, $reading, $hmccu_value, 1);
|
||||
}
|
||||
return $hmccu_value if ($updatemode eq 'hmccu');
|
||||
@ -978,16 +1053,24 @@ sub HMCCU_UpdateClientReading ($@)
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV' && $ch->{TYPE} ne 'HMCCUCHN');
|
||||
next if (!defined ($ch->{IODev}) || !defined ($ch->{ccuaddr}));
|
||||
next if ($ch->{IODev} != $hash);
|
||||
next if ($ch->{ccuaddr} ne $devadd && $ch->{ccuaddr} ne $chnadd);
|
||||
if ($ch->{ccuif} eq "VirtualDevices" && exists ($ch->{ccugroup})) {
|
||||
my @gdevs = split (",", $ch->{ccugroup});
|
||||
next if (!($devadd ~~ @gdevs) && !($chnadd ~~ @gdevs));
|
||||
}
|
||||
else {
|
||||
next if ($ch->{ccuaddr} ne $devadd && $ch->{ccuaddr} ne $chnadd);
|
||||
}
|
||||
|
||||
# Get attributes of client device
|
||||
my $upd = AttrVal ($cn, 'ccureadings', 1);
|
||||
my $crf = AttrVal ($cn, 'ccureadingformat', 'name');
|
||||
my $flt = AttrVal ($cn, 'ccureadingfilter', '.*');
|
||||
# my $flt = AttrVal ($cn, 'ccureadingfilter', '.*');
|
||||
my $mapdatapoints = AttrVal ($cn, 'mapdatapoints', '');
|
||||
my $substitute = AttrVal ($cn, 'substitute', '');
|
||||
my ($sc, $st, $cc, $cd) = HMCCU_GetSpecialDatapoints ($ch, 'STATE', '', '', '');
|
||||
last if ($upd == 0);
|
||||
next if ($dpt eq '' || $dpt !~ /$flt/);
|
||||
next if (!HMCCU_FilterReading ($ch, $chnadd, $dpt));
|
||||
# next if ($dpt eq '' || $dpt !~ /$flt/);
|
||||
|
||||
my $clreading = HMCCU_GetReadingName ('', $devadd, $channel, $dpt, '', $crf);
|
||||
next if ($clreading eq '');
|
||||
@ -1002,6 +1085,7 @@ sub HMCCU_UpdateClientReading ($@)
|
||||
}
|
||||
$cl_value = HMCCU_FormatReadingValue ($ch, $cl_value);
|
||||
|
||||
# Update reading and control/state readings
|
||||
readingsSingleUpdate ($ch, $clreading, $cl_value, 1);
|
||||
if ($cd ne '' && $dpt eq $cd && $channel eq $cc) {
|
||||
readingsSingleUpdate ($ch, 'control', $cl_value, 1);
|
||||
@ -1009,6 +1093,30 @@ sub HMCCU_UpdateClientReading ($@)
|
||||
if ($clreading =~ /\.$st$/ && ($sc eq '' || $sc eq $channel)) {
|
||||
HMCCU_SetState ($ch, $cl_value);
|
||||
}
|
||||
|
||||
# Map datapoints for virtual devices (groups)
|
||||
if ($mapdatapoints ne '') {
|
||||
foreach my $m (split (",", $mapdatapoints)) {
|
||||
my @mr = split ("=", $m);
|
||||
next if (@mr != 2);
|
||||
my ($i1, $a1, $c1, $d1, $n1, $f1) =
|
||||
HMCCU_ParseObject ($mr[0], $HMCCU_FLAG_FULLADDR);
|
||||
my ($i2, $a2, $c2, $d2, $n2, $f2) =
|
||||
HMCCU_ParseObject ($mr[1], $HMCCU_FLAG_FULLADDR);
|
||||
next if (($f1 & $HMCCU_FLAGS_AC) != $HMCCU_FLAGS_AC ||
|
||||
($f2 & $HMCCU_FLAGS_AC) != $HMCCU_FLAGS_AC);
|
||||
next if ($devadd ne $a1 || $channel ne $c1 || $dpt ne $d1);
|
||||
my $mreading = HMCCU_GetReadingName ('', $a2, $c2, $d2, '', $crf);
|
||||
next if ($mreading eq '');
|
||||
readingsSingleUpdate ($ch, $mreading, $cl_value, 1);
|
||||
if ($cd ne '' && $d2 eq $cd && $c2 eq $cc) {
|
||||
readingsSingleUpdate ($ch, 'control', $cl_value, 1);
|
||||
}
|
||||
if ($mreading =~ /\.$st/ && ($sc eq '' || $sc eq $c2)) {
|
||||
HMCCU_SetState ($ch, $cl_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $hmccu_value;
|
||||
@ -1077,6 +1185,9 @@ sub HMCCU_StartRPCServer ($)
|
||||
return 0;
|
||||
}
|
||||
|
||||
# Clear RPC queues
|
||||
HMCCU_ResetRPCQueue ($hash);
|
||||
|
||||
# Fork child process(es)
|
||||
foreach my $port (split (',', $rpcport)) {
|
||||
my $rpcqueueport = $rpcqueue."_".$port;
|
||||
@ -1210,6 +1321,7 @@ sub HMCCU_IsRPCServerRunning ($$$)
|
||||
sub HMCCU_CheckProcess ($$)
|
||||
{
|
||||
my ($hash, $port) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $modpath = AttrVal ('global', 'modpath', '/opt/fhem');
|
||||
my $rpcserver = $modpath."/FHEM/ccurpcd.pl";
|
||||
@ -1345,7 +1457,10 @@ foreach(devid, root.Devices().EnumUsedIDs()) {
|
||||
foreach my $d (keys %defs) {
|
||||
# Get hash of client device
|
||||
my $ch = $defs{$d};
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV' && $ch->{TYPE} ne 'HMCCUCHN');
|
||||
next if (!defined ($ch->{IODev}) || !defined ($ch->{ccuaddr}));
|
||||
next if ($ch->{TYPE} eq 'HMCCUDEV' && $ch->{ccuif} eq "VirtualDevices" &&
|
||||
$ch->{ccuname} eq 'none');
|
||||
my $add = $ch->{ccuaddr};
|
||||
my $dadd = $add;
|
||||
$dadd =~ s/:[0-9]+$//;
|
||||
@ -1386,6 +1501,28 @@ sub HMCCU_IsValidDevice ($)
|
||||
}
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Get list of device or channel addresses with
|
||||
# device or channel name matching regular expression.
|
||||
# Parameter mode can be dev or chn.
|
||||
# Return number of matching entries.
|
||||
####################################################
|
||||
|
||||
sub HMCCU_GetMatchingDevices ($$$$)
|
||||
{
|
||||
my ($hash, $regexp, $mode, $listref) = @_;
|
||||
my $c = 0;
|
||||
|
||||
foreach my $name (sort keys %HMCCU_Addresses) {
|
||||
next if ($name !~/$regexp/ || $HMCCU_Addresses{$name}{addtype} ne $mode ||
|
||||
$HMCCU_Addresses{$name}{valid} == 0);
|
||||
push (@$listref, $HMCCU_Addresses{$name}{address});
|
||||
$c++;
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Get name of a CCU device by address.
|
||||
# Channel number will be removed if specified.
|
||||
@ -1519,6 +1656,59 @@ sub HMCCU_GetAddress ($$$)
|
||||
return ($add, $chn);
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Check if parameter is a channel address (syntax)
|
||||
# f=1: Interface required.
|
||||
####################################################
|
||||
|
||||
sub HMCCU_IsChnAddr ($$)
|
||||
{
|
||||
my ($id, $f) = @_;
|
||||
|
||||
if ($f) {
|
||||
return ($id =~ /^.+\.[A-Z]{3,3}[0-9]{7,7}:[0-9]+$/) ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
return ($id =~ /^[A-Z]{3,3}[0-9]{7,7}:[0-9]+$/) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Check if parameter is a device address (syntax)
|
||||
# f=1: Interface required.
|
||||
####################################################
|
||||
|
||||
sub HMCCU_IsDevAddr ($$)
|
||||
{
|
||||
my ($id, $f) = @_;
|
||||
|
||||
if ($f) {
|
||||
return ($id =~ /^.+\.[A-Z]{3,3}[0-9]{7,7}$/) ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
return ($id =~ /^[A-Z]{3,3}[0-9]{7,7}$/) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Split channel address into device address and
|
||||
# channel number
|
||||
####################################################
|
||||
|
||||
sub HMCCU_SplitChnAddr ($)
|
||||
{
|
||||
my ($addr) = @_;
|
||||
|
||||
if (HMCCU_IsChnAddr ($addr, 0)) {
|
||||
return split (":", $addr);
|
||||
}
|
||||
elsif (HMCCU_IsDevAddr ($addr, 0)) {
|
||||
return ($addr, '');
|
||||
}
|
||||
|
||||
return ('', '');
|
||||
}
|
||||
|
||||
sub HMCCU_GetCCUObjectAttribute ($$)
|
||||
{
|
||||
my ($object, $attr) = @_;
|
||||
@ -1605,6 +1795,26 @@ sub HMCCU_GetSpecialDatapoints ($$$$$)
|
||||
return ($sc, $sd, $cc, $cd);
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Clear RPC queue
|
||||
####################################################
|
||||
|
||||
sub HMCCU_ResetRPCQueue ($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $rpcqueue = AttrVal ($name, 'rpcqueue', '/tmp/ccuqueue');
|
||||
my $rpcport = AttrVal ($name, 'rpcport', '2001');
|
||||
|
||||
my @portlist = split (',', $rpcport);
|
||||
foreach my $port (@portlist) {
|
||||
my $queue = new RPCQueue (File => $rpcqueue."_".$port, Mode => 0666);
|
||||
$queue->reset();
|
||||
$queue->close();
|
||||
}
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Timer function for reading RPC queue
|
||||
####################################################
|
||||
@ -1618,6 +1828,7 @@ sub HMCCU_ReadRPCQueue ($)
|
||||
my $f = 0;
|
||||
my @newdevices;
|
||||
my @deldevices;
|
||||
my @termpids;
|
||||
my $newcount = 0;
|
||||
my $delcount = 0;
|
||||
|
||||
@ -1625,6 +1836,7 @@ sub HMCCU_ReadRPCQueue ($)
|
||||
my $ccureadingformat = AttrVal ($name, 'ccureadingformat', 'name');
|
||||
my $rpcqueue = AttrVal ($name, 'rpcqueue', '/tmp/ccuqueue');
|
||||
my $rpcport = AttrVal ($name, 'rpcport', '2001');
|
||||
my $rpctimeout = AttrVal ($name, 'rpctimeout', 300);
|
||||
|
||||
my @portlist = split (',', $rpcport);
|
||||
foreach my $port (@portlist) {
|
||||
@ -1664,6 +1876,7 @@ sub HMCCU_ReadRPCQueue ($)
|
||||
}
|
||||
elsif ($Tokens[0] eq 'EX') {
|
||||
#### RPC Server shut down ####
|
||||
push (@termpids, $Tokens[2]);
|
||||
Log3 $name, 0, "HMCCU: Received EX event. RPC server terminated.";
|
||||
if ($hash->{RPCState} ne "restarting") {
|
||||
$hash->{RPCState} = "stopped";
|
||||
@ -1680,11 +1893,14 @@ sub HMCCU_ReadRPCQueue ($)
|
||||
|
||||
$element = $queue->deq();
|
||||
}
|
||||
|
||||
$queue->close();
|
||||
}
|
||||
|
||||
if ($HMCCU_EventTime > 0 && time()-$HMCCU_EventTime > 300) {
|
||||
Log3 $name, 2, "HMCCU: Received no events from CCU since 300 seconds";
|
||||
DoTrigger ($name, "No events from CCU since 300 seconds");
|
||||
# Check if RPC server still running if events from CCU timed out
|
||||
if ($HMCCU_EventTime > 0 && time()-$HMCCU_EventTime > $rpctimeout) {
|
||||
Log3 $name, 2, "HMCCU: Received no events from CCU since $rpctimeout seconds";
|
||||
DoTrigger ($name, "No events from CCU since $rpctimeout seconds");
|
||||
}
|
||||
|
||||
# CCU devices deleted
|
||||
@ -1704,39 +1920,42 @@ sub HMCCU_ReadRPCQueue ($)
|
||||
my @hm_pids;
|
||||
my @ex_pids;
|
||||
HMCCU_IsRPCServerRunning ($hash, \@hm_pids, \@ex_pids);
|
||||
if ($f == 0 && @hm_pids > 0) {
|
||||
if (scalar (@hm_pids) != scalar (@portlist)) {
|
||||
Log3 $name, 1, "HMCCU: Number of RPC server process differs from number of CCU destination ports";
|
||||
my $nhm_pids = scalar (@hm_pids);
|
||||
my $nex_pids = scalar (@ex_pids);
|
||||
|
||||
if ($nex_pids > 0) {
|
||||
Log3 $name, 1, "HMCCU: Externally launched RPC server(s) detected. Kill process(es) manually with command kill -SIGINT pid for pids ".join (',', @ex_pids)." f=$f";
|
||||
}
|
||||
|
||||
if ($f > 0) {
|
||||
# At least one RPC server has been stopped. Update PID list
|
||||
$hash->{RPCPID} = $nhm_pids > 0 ? join(',',@hm_pids) : '0';
|
||||
Log3 $name, 0, "HMCCU: RPC server(s) with PID(s) ".join(',',@termpids)." shut down. f=$f";
|
||||
}
|
||||
|
||||
if ($f == 2 && $nhm_pids == 0) {
|
||||
# All RPC servers terminated and restart flag set
|
||||
$HMCCU_EventTime = 0;
|
||||
if (HMCCU_StartRPCServer ($hash)) {
|
||||
InternalTimer (gettimeofday()+60, 'HMCCU_ReadRPCQueue', $hash, 0);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Log3 $name, 0, "HMCCU: Restart of RPC server failed";
|
||||
}
|
||||
}
|
||||
|
||||
if ($nhm_pids > 0) {
|
||||
# Reschedule reading of RPC queues if at least one RPC server is running
|
||||
InternalTimer (gettimeofday()+$rpcinterval, 'HMCCU_ReadRPCQueue', $hash, 0);
|
||||
}
|
||||
else {
|
||||
my $nex_pids = scalar @ex_pids;
|
||||
if ($nex_pids > 0) {
|
||||
Log3 $name, 1, "HMCCU: Externally launched RPC server(s) detected. Kill process(es) manually with command kill -SIGINT pid for pids ".join (',', @ex_pids)." f=$f";
|
||||
}
|
||||
else {
|
||||
Log3 $name, 0, "HMCCU: RPC server has been shut down. f=$f";
|
||||
}
|
||||
|
||||
$HMCCU_EventTime = 0;
|
||||
|
||||
if ($f == 2) {
|
||||
if ($nex_pids == 0 && HMCCU_StartRPCServer ($hash)) {
|
||||
InternalTimer (gettimeofday()+60,
|
||||
'HMCCU_ReadRPCQueue', $hash, 0);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Log3 $name, 0, "HMCCU: Restart of RPC server failed";
|
||||
}
|
||||
}
|
||||
|
||||
# No more RPC servers active
|
||||
$hash->{RPCPID} = '0';
|
||||
$hash->{RPCPRC} = 'none';
|
||||
$hash->{RPCState} = "stopped";
|
||||
DoTrigger ($name, "RPC server stopped");
|
||||
# $attr{$name}{rpcserver} = "off";
|
||||
Log3 $name, 0, "HMCCU: All RPC servers stopped";
|
||||
DoTrigger ($name, "All RPC servers stopped");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1964,18 +2183,10 @@ sub HMCCU_GetUpdate ($$$)
|
||||
my $nam = '';
|
||||
my $script;
|
||||
|
||||
my $cn = $cl_hash->{NAME};
|
||||
my $ccureadings = AttrVal ($cn, 'ccureadings', 1);
|
||||
return -6 if ($ccureadings == 0);
|
||||
$ccuget = HMCCU_GetAttribute ($hmccu_hash, $cl_hash, 'ccuget', 'Value') if ($ccuget eq 'Attr');
|
||||
my $ccutrace = AttrVal ($hmccu_hash->{NAME}, 'ccutrace', '');
|
||||
my $ccureadingfilter = AttrVal ($cn, 'ccureadingfilter', '.*');
|
||||
my $readingformat = AttrVal ($cn, 'ccureadingformat', 'name');
|
||||
my $substitute = AttrVal ($cn, 'substitute', '');
|
||||
my ($statechn, $statedpt, $controlchn, $controldpt) = HMCCU_GetSpecialDatapoints (
|
||||
$cl_hash, 'STATE', '', '', '');
|
||||
|
||||
if ($addr =~ /^[A-Z]{3,3}[0-9]{7,7}:[0-9]{1,2}$/) {
|
||||
if (HMCCU_IsChnAddr ($addr, 0)) {
|
||||
$nam = HMCCU_GetChannelName ($addr, '');
|
||||
return -1 if ($nam eq '');
|
||||
|
||||
@ -1993,7 +2204,7 @@ if (oChannel) {
|
||||
}
|
||||
);
|
||||
}
|
||||
elsif ($addr =~ /^[A-Z]{3,3}[0-9]{7,7}$/) {
|
||||
elsif (HMCCU_IsDevAddr ($addr, 0)) {
|
||||
$nam = HMCCU_GetDeviceName ($addr, '');
|
||||
return -1 if ($nam eq '');
|
||||
|
||||
@ -2028,16 +2239,59 @@ if (odev) {
|
||||
}
|
||||
return -2 if ($response eq '');
|
||||
|
||||
my @dpdef = split /\n/, $response;
|
||||
|
||||
# Update client device
|
||||
my $rc = HMCCU_UpdateDeviceReadings ($cl_hash, \@dpdef);
|
||||
return $rc if ($rc < 0);
|
||||
|
||||
# Update virtual devices
|
||||
my ($da, $cno) = HMCCU_SplitChnAddr ($cl_hash->{ccuaddr});
|
||||
foreach my $dn (sort keys %defs) {
|
||||
my $ch = $defs{$dn};
|
||||
my @vdevs = split (",", $ch->{ccugroup});
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV');
|
||||
next if ($ch->{ccuif} ne "VirtualDevices" || !exists ($ch->{ccugroup}));
|
||||
if ($da ~~ @vdevs || ($cno ne '' && $cl_hash->{ccuaddr} ~~ @vdevs)) {
|
||||
HMCCU_UpdateDeviceReadings ($ch, \@dpdef);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Update readings of client device. Parameter dp
|
||||
# is a reference to an array of datapoint=value
|
||||
# pairs. Returns number of updated readings.
|
||||
####################################################
|
||||
|
||||
sub HMCCU_UpdateDeviceReadings ($$)
|
||||
{
|
||||
my ($cl_hash, $dp) = @_;
|
||||
|
||||
my $uc = 0;
|
||||
|
||||
my $cn = $cl_hash->{NAME};
|
||||
my $ccureadings = AttrVal ($cn, 'ccureadings', 1);
|
||||
return -6 if ($ccureadings == 0);
|
||||
# my $ccureadingfilter = AttrVal ($cn, 'ccureadingfilter', '.*');
|
||||
my $readingformat = AttrVal ($cn, 'ccureadingformat', 'name');
|
||||
my $substitute = AttrVal ($cn, 'substitute', '');
|
||||
my ($statechn, $statedpt, $controlchn, $controldpt) = HMCCU_GetSpecialDatapoints (
|
||||
$cl_hash, 'STATE', '', '', '');
|
||||
|
||||
readingsBeginUpdate ($cl_hash);
|
||||
|
||||
foreach my $dpdef (split /\n/, $response) {
|
||||
foreach my $dpdef (@$dp) {
|
||||
my @dpdata = split /=/, $dpdef;
|
||||
next if (@dpdata < 2);
|
||||
my @adrtoks = split /\./, $dpdata[1];
|
||||
next if (@adrtoks != 3);
|
||||
next if ($adrtoks[2] !~ /$ccureadingfilter/);
|
||||
# next if ($adrtoks[2] !~ /$ccureadingfilter/);
|
||||
|
||||
my ($add, $chn) = split /:/, $adrtoks[1];
|
||||
next if (!HMCCU_FilterReading ($cl_hash, $adrtoks[1], $adrtoks[2]));
|
||||
my $reading = HMCCU_GetReadingName ($adrtoks[0], $add, $chn, $adrtoks[2],
|
||||
$dpdata[0], $readingformat);
|
||||
next if ($reading eq '');
|
||||
@ -2052,11 +2306,12 @@ if (odev) {
|
||||
if ($reading =~ /\.$statedpt$/ && ($statechn eq '' || $statechn eq $chn)) {
|
||||
readingsBulkUpdate ($cl_hash, "state", $value);
|
||||
}
|
||||
$uc++;
|
||||
}
|
||||
|
||||
readingsEndUpdate ($cl_hash, 1);
|
||||
|
||||
return 1;
|
||||
return $uc;
|
||||
}
|
||||
|
||||
####################################################
|
||||
@ -2304,30 +2559,41 @@ sub HMCCU_RPCSetConfig ($$$)
|
||||
}
|
||||
|
||||
####################################################
|
||||
# Return string for internal STATE. This function
|
||||
# can be used in attribute stateFormat.
|
||||
# Aggregate readings. Valid operations are 'and',
|
||||
# 'or' or 'cnt'.
|
||||
# and: return v1 if all readings matching v1,
|
||||
# otherwise return v2.
|
||||
# or: return v1 if at least 1 reading matches v1,
|
||||
# otherwise return v2.
|
||||
# cnt: return number of readings matching v1.
|
||||
# Ex 1: number of open windows: state, "cnt", "open", ""
|
||||
# Ex 2: Status of windows: state, "and", "close", "open"
|
||||
####################################################
|
||||
|
||||
sub HMCCU_State ($)
|
||||
sub HMCCU_AggReadings ($$$$$)
|
||||
{
|
||||
my ($name) = @_;
|
||||
my ($name, $readexp, $oper, $v1, $v2) = @_;
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $sf = AttrVal ($name, 'ccustate', '');
|
||||
return undef if (!exists ($defs{$name}));
|
||||
|
||||
return ReadingsVal ($name, 'state', '') if ($sf eq '');
|
||||
my $mc = 0;
|
||||
my $c = 0;
|
||||
|
||||
my $st = $sf;
|
||||
my $r = $hash->{READINGS};
|
||||
foreach my $r (keys %{$defs{$name}{READINGS}}) {
|
||||
next if ($r !~ /$readexp/);
|
||||
$c++;
|
||||
$mc++ if ($defs{$name}{READINGS}{$r}{VAL} eq $v1);
|
||||
}
|
||||
|
||||
if ($r->{state}{VAL} ne "Error") {
|
||||
$st =~ s/\b([A-Za-z\d_\.\:-]+)\b/($r->{$1} ? $r->{$1}{VAL} : $1)/ge;
|
||||
if ($oper eq 'and') {
|
||||
return ($mc < $c) ? $v2 : $v1;
|
||||
}
|
||||
elsif ($oper eq 'or') {
|
||||
return ($mc > 0) ? $v1 : $v2;
|
||||
}
|
||||
else {
|
||||
$st = "Error";
|
||||
return $mc;
|
||||
}
|
||||
|
||||
return $st;
|
||||
}
|
||||
|
||||
####################################################
|
||||
@ -2485,7 +2751,10 @@ sub HMCCU_Dewpoint ($$$$)
|
||||
Check if RPC server process is running.
|
||||
</li><br/>
|
||||
<li>get <name> update [<devexp> [<'State'|'Value'>]]<br/>
|
||||
Update all datapoint / readings of client devices with device name matching <devexp>
|
||||
Update all datapoints / readings of client devices with FHEM device name matching <devexp>
|
||||
</li><br/>
|
||||
<li>get <name> updateccu [<devexp> [<'State'|'Value'>]]<br/>
|
||||
Update all datapoints / readings of client devices with CCU device name matching <devexp>
|
||||
</li>
|
||||
</ul>
|
||||
<br/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user