mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-04 22:19:38 +00:00
HMCCU: Version 3.6
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@12748 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
8c70a7969a
commit
ed44e17f10
553
FHEM/88_HMCCU.pm
553
FHEM/88_HMCCU.pm
@ -4,7 +4,7 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Version 3.5
|
||||
# Version 3.6
|
||||
#
|
||||
# Module for communication between FHEM and Homematic CCU2.
|
||||
# Supports BidCos-RF, BidCos-Wired, HmIP-RF, virtual CCU channels,
|
||||
@ -23,12 +23,13 @@
|
||||
# set <name> rpcserver {on|off|restart}
|
||||
# set <name> var <value> [...]
|
||||
#
|
||||
# get <name> aggregation {<rule>|all}
|
||||
# get <name> configdesc {<device>|<channel>}
|
||||
# get <name> defaults
|
||||
# get <name> deviceinfo <device>
|
||||
# get <name> devicelist [dump]
|
||||
# get <name> devicelist create <devexp> [s=<suffix>] [p=<prefix>] [f=<format>] [defattr]
|
||||
# [<attr>=<val> [...]]}]
|
||||
# get <name> devicelist create <devexp> [t={chn|dev|all}] [s=<suffix>] [p=<prefix>] [f=<format>]
|
||||
# [defattr] [<attr>=<val> [...]]}]
|
||||
# get <name> dump {devtypes|datapoints} [<filter>]
|
||||
# get <name> exportdefaults {filename}
|
||||
# get <name> parfile [<parfile>]
|
||||
@ -41,7 +42,7 @@
|
||||
# attr <name> ccuackstate { 0 | 1 }
|
||||
# attr <name> ccuaggregate <rules>
|
||||
# attr <name> ccudefaults <filename>
|
||||
# attr <name> ccuflags { singlerpc,intrpc,extrpc,dptnocheck }
|
||||
# attr <name> ccuflags { intrpc,extrpc,dptnocheck,noagg }
|
||||
# attr <name> ccuget { State | Value }
|
||||
# attr <name> ccureadingfilter <filter_rule>
|
||||
# attr <name> ccureadingformat { name[lc] | address[lc] | datapoint[lc] }
|
||||
@ -53,6 +54,8 @@
|
||||
# attr <name> rpcport <ccu_rpc_port>
|
||||
# attr <name> rpcqueue <file>
|
||||
# attr <name> rpcserver { on | off }
|
||||
# attr <name> rpcserveraddr <ip-or-name>
|
||||
# attr <name> rpcserverport <base_port>
|
||||
# attr <name> rpctimeout <read>[,<write>]
|
||||
# attr <name> stripchar <character>
|
||||
# attr <name> stripnumber { 0 | 1 | 2 }
|
||||
@ -93,7 +96,7 @@ my %HMCCU_CUST_CHN_DEFAULTS;
|
||||
my %HMCCU_CUST_DEV_DEFAULTS;
|
||||
|
||||
# HMCCU version
|
||||
my $HMCCU_VERSION = '3.5';
|
||||
my $HMCCU_VERSION = '3.6';
|
||||
|
||||
# RPC Ports and URL extensions
|
||||
my %HMCCU_RPC_PORT = (
|
||||
@ -132,12 +135,9 @@ my %rpcevent_snapshot;
|
||||
|
||||
# CCU Device names, key = CCU device address
|
||||
my %HMCCU_Devices;
|
||||
|
||||
# CCU Device addresses, key = CCU device name
|
||||
my %HMCCU_Addresses;
|
||||
# Last update of device list
|
||||
# my $HMCCU_UpdateTime = 0;
|
||||
# Last event from CCU
|
||||
# my $HMCCU_EventTime = 0;
|
||||
|
||||
# Datapoint operations
|
||||
my $HMCCU_OPER_READ = 1;
|
||||
@ -191,7 +191,7 @@ sub HMCCU_FindDefaults ($);
|
||||
sub HMCCU_SetDefaults ($);
|
||||
sub HMCCU_GetDefaults ($$);
|
||||
sub HMCCU_Notify ($$);
|
||||
sub HMCCU_AggregateReadings ($$$);
|
||||
sub HMCCU_AggregateReadings ($$);
|
||||
sub HMCCU_ParseObject ($$);
|
||||
sub HMCCU_FilterReading ($$$);
|
||||
sub HMCCU_GetReadingName ($$$$$$$);
|
||||
@ -250,7 +250,7 @@ sub HMCCU_SetVariable ($$$);
|
||||
sub HMCCU_GetUpdate ($$$);
|
||||
sub HMCCU_UpdateDeviceReadings ($$);
|
||||
sub HMCCU_GetChannel ($$);
|
||||
sub HMCCU_RPCGetConfig ($$$);
|
||||
sub HMCCU_RPCGetConfig ($$$$);
|
||||
sub HMCCU_RPCSetConfig ($$$);
|
||||
|
||||
# File queue functions
|
||||
@ -277,6 +277,7 @@ sub HMCCU_CCURPC_ReplaceDeviceCB ($$$$);
|
||||
sub HMCCU_CCURPC_ReaddDevicesCB ($$$);
|
||||
sub HMCCU_CCURPC_EventCB ($$$$$);
|
||||
sub HMCCU_CCURPC_ListDevicesCB ($$);
|
||||
sub HMCCU_CCURPC_GetEventsCB ($$);
|
||||
|
||||
|
||||
#####################################
|
||||
@ -296,7 +297,7 @@ sub HMCCU_Initialize ($)
|
||||
$hash->{NotifyFn} = "HMCCU_Notify";
|
||||
$hash->{ShutdownFn} = "HMCCU_Shutdown";
|
||||
|
||||
$hash->{AttrList} = "stripchar stripnumber ccuackstate:0,1 ccuaggregate:textField-long ccudefaults ccuflags:multiple-strict,singlerpc,extrpc,intrpc,dptnocheck ccureadings:0,1 ccureadingfilter ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc rpcinterval:3,5,7,10 rpcqueue rpcport:multiple-strict,2000,2001,2010,9292 rpcserver:on,off rpctimeout rpcevtimeout parfile substitute ccutrace ccuget:Value,State ". $readingFnAttributes;
|
||||
$hash->{AttrList} = "stripchar stripnumber ccuackstate:0,1 ccuaggregate:textField-long ccudefaults ccuflags:multiple-strict,extrpc,intrpc,dptnocheck,noagg ccureadings:0,1 ccureadingfilter ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc rpcinterval:2,3,5,7,10 rpcqueue rpcport:multiple-strict,2000,2001,2010,9292 rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute ccutrace ccuget:Value,State ". $readingFnAttributes;
|
||||
}
|
||||
|
||||
#####################################
|
||||
@ -310,10 +311,18 @@ sub HMCCU_Define ($$)
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
return "Define CCU hostname or IP address as a parameter" if(@a < 3);
|
||||
|
||||
|
||||
$hash->{host} = $a[2];
|
||||
$hash->{Clients} = ':HMCCUDEV:HMCCUCHN:';
|
||||
|
||||
# Find existing CCU devices
|
||||
my $ccucount = 0;
|
||||
foreach my $d (keys %defs) {
|
||||
my $ch = $defs{$d};
|
||||
next if (!exists ($ch->{TYPE}));
|
||||
$ccucount++ if ($ch->{TYPE} eq 'HMCCU');
|
||||
}
|
||||
$hash->{CCUNum} = $ccucount;
|
||||
$hash->{version} = $HMCCU_VERSION;
|
||||
$hash->{ccutype} = 'CCU2';
|
||||
$hash->{DevCount} = HMCCU_GetDeviceList ($hash);
|
||||
@ -331,6 +340,8 @@ sub HMCCU_Define ($$)
|
||||
readingsBulkUpdate ($hash, "rpcstate", "stopped");
|
||||
readingsEndUpdate ($hash, 1);
|
||||
|
||||
$attr{$name}{stateFormat} = "rpcstate/state";
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
@ -359,60 +370,87 @@ sub HMCCU_Attr ($@)
|
||||
return HMCCU_SetError ($hash, "Syntax error in attribute ccuaggregate") if ($rc == 0);
|
||||
}
|
||||
}
|
||||
elsif ($cmd eq 'del') {
|
||||
if ($attrname eq 'ccuaggregate') {
|
||||
HMCCU_AggregationRules ($hash, '');
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
########################################
|
||||
######################################################
|
||||
# Parse aggregation rules for readings.
|
||||
# Syntax of aggregation rule is:
|
||||
# Filter:Reading:Cond:Else:Prefix:Coll
|
||||
# Filter := {d|t|g|r}=List
|
||||
# Cond := {any|all}=Value
|
||||
# Else := Value
|
||||
# Prefix := Prefix for result readings
|
||||
# Coll := Attribute
|
||||
########################################
|
||||
# FilterSpec[;...]
|
||||
# FilterSpec := {Name|Filt|Read|Cond|Else|Pref|Coll}[,...]
|
||||
# Name := name:Name
|
||||
# Filt := filter:{name|type|group|room|alias}=Regexp[!Regexp]
|
||||
# Read := read:Regexp
|
||||
# Cond := if:{any|all|min|max|sum|avg|gt|lt|ge|le}=Value
|
||||
# Else := else:Value
|
||||
# Pref := prefix:{RULE|Prefix}
|
||||
# Coll := coll:{NAME|Attribute}
|
||||
######################################################
|
||||
|
||||
sub HMCCU_AggregationRules ($$)
|
||||
{
|
||||
my ($hash, $rulestr) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
# Delete existing aggregation rules
|
||||
if (exists ($hash->{hmccu}{agg})) {
|
||||
delete $hash->{hmccu}{agg};
|
||||
}
|
||||
return if ($rulestr eq '');
|
||||
|
||||
my @opts = ('name', 'filter', 'if', 'else');
|
||||
|
||||
# Extract aggregation rules
|
||||
my @rules = split (';', $rulestr);
|
||||
my @rules = split (/[;\n]+/, $rulestr);
|
||||
foreach my $r (@rules) {
|
||||
# Extract parts of aggregation rule
|
||||
my @spec = split (':', $r);
|
||||
return 0 if (scalar (@spec) < 5 || scalar (@spec) > 6);
|
||||
|
||||
# Extract filter type and value list
|
||||
return 0 if ($spec[0] !~ /^(d|t|g|r)=(.+)$/);
|
||||
my ($ft, $vl) = ($1, $2);
|
||||
|
||||
# Create a hash entry for each filter value
|
||||
my @values = split (',', $vl);
|
||||
foreach my $v (@values) {
|
||||
# Extract condition and value
|
||||
return 0 if ($spec[2] !~ /^(any|all)=(.+)$/);
|
||||
my ($ct, $cv) = ($1, $2);
|
||||
|
||||
# Store aggregation rule in hash
|
||||
$hash->{hmccu}{agg}{$ft}{$v}{reading} = $spec[1];
|
||||
$hash->{hmccu}{agg}{$ft}{$v}{cond} = $ct;
|
||||
$hash->{hmccu}{agg}{$ft}{$v}{if} = $cv;
|
||||
$hash->{hmccu}{agg}{$ft}{$v}{else} = $spec[3];
|
||||
$hash->{hmccu}{agg}{$ft}{$v}{prefix} = $spec[4];
|
||||
if (scalar (@spec) == 6) {
|
||||
$hash->{hmccu}{agg}{$ft}{$v}{coll} = $spec[5];
|
||||
} else {
|
||||
$hash->{hmccu}{agg}{$ft}{$v}{coll} = 'NAME';
|
||||
# Set default rule parameters
|
||||
my %opt = (
|
||||
'read' => 'state',
|
||||
prefix => 'RULE',
|
||||
coll => 'NAME'
|
||||
);
|
||||
|
||||
# Parse aggregation rule
|
||||
my @specs = split (',', $r);
|
||||
foreach my $spec (@specs) {
|
||||
if ($spec =~ /^(name|filter|read|if|else|prefix|coll):(.+)$/) {
|
||||
$opt{$1} = $2;
|
||||
}
|
||||
}
|
||||
|
||||
# Check if rule syntax is correct
|
||||
foreach my $o (@opts) {
|
||||
if (!exists ($opt{$o})) {
|
||||
Log3 $name, 1, "HMCCU: Parameter $o is missing in aggregation rule.";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
my $fname = $opt{name};
|
||||
my ($fincl, $fexcl) = split ('!', $opt{filter});
|
||||
my ($ftype, $fexpr) = split ('=', $fincl);
|
||||
return 0 if (!defined ($fexpr));
|
||||
my ($fcond, $fval) = split ('=', $opt{if});
|
||||
return 0 if (!defined ($fval));
|
||||
my ($fcoll, $fdflt) = split ('!', $opt{coll});
|
||||
$fdflt = 'no match' if (!defined ($fdflt));
|
||||
|
||||
$hash->{hmccu}{agg}{$fname}{ftype} = $ftype;
|
||||
$hash->{hmccu}{agg}{$fname}{fexpr} = $fexpr;
|
||||
$hash->{hmccu}{agg}{$fname}{fexcl} = (defined ($fexcl) ? $fexcl : '');
|
||||
$hash->{hmccu}{agg}{$fname}{fread} = $opt{'read'};
|
||||
$hash->{hmccu}{agg}{$fname}{fcond} = $fcond;
|
||||
$hash->{hmccu}{agg}{$fname}{ftrue} = $fval;
|
||||
$hash->{hmccu}{agg}{$fname}{felse} = $opt{'else'};
|
||||
$hash->{hmccu}{agg}{$fname}{fpref} = $opt{prefix} eq 'RULE' ? $fname : $opt{prefix};
|
||||
$hash->{hmccu}{agg}{$fname}{fcoll} = $fcoll;
|
||||
$hash->{hmccu}{agg}{$fname}{fdflt} = $fdflt;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -633,46 +671,173 @@ sub HMCCU_GetDefaults ($$)
|
||||
|
||||
sub HMCCU_Notify ($$)
|
||||
{
|
||||
my ($hash, $dev) = @_;
|
||||
my ($hash, $devhash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $devname = $devhash->{NAME};
|
||||
my $devtype = $devhash->{TYPE};
|
||||
|
||||
my $disable = AttrVal ($name, 'disable', 0);
|
||||
my $rpcserver = AttrVal ($name, 'rpcserver', 'off');
|
||||
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
|
||||
|
||||
return if ($dev->{NAME} ne "global" || $disable);
|
||||
# return if (!grep (m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}));
|
||||
return if (!grep (m/^INITIALIZED$/, @{$dev->{CHANGED}}));
|
||||
return if ($disable);
|
||||
|
||||
my $events = deviceEvents ($devhash, 1);
|
||||
return if (! $events);
|
||||
|
||||
if ($rpcserver eq 'on') {
|
||||
my $delay = $HMCCU_INIT_INTERVAL0;
|
||||
Log3 $name, 0, "HMCCU: Autostart of RPC server after FHEM initialization in $delay seconds";
|
||||
if ($ccuflags =~ /extrpc/) {
|
||||
InternalTimer (gettimeofday()+$delay, "HMCCU_StartExtRPCServer", $hash, 0);
|
||||
# Process events
|
||||
foreach my $event (@{$events}) {
|
||||
if ($devname eq 'global') {
|
||||
if ($event eq 'INITIALIZED') {
|
||||
return if ($rpcserver eq 'on');
|
||||
my $delay = $HMCCU_INIT_INTERVAL0;
|
||||
Log3 $name, 0, "HMCCU: Start of RPC server after FHEM initialization in $delay seconds";
|
||||
if ($ccuflags =~ /extrpc/) {
|
||||
InternalTimer (gettimeofday()+$delay, "HMCCU_StartExtRPCServer", $hash, 0);
|
||||
}
|
||||
else {
|
||||
InternalTimer (gettimeofday()+$delay, "HMCCU_StartIntRPCServer", $hash, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
InternalTimer (gettimeofday()+$delay, "HMCCU_StartIntRPCServer", $hash, 0);
|
||||
return if ($devtype ne 'HMCCUDEV' && $devtype ne 'HMCCUCHN');
|
||||
my ($r, $v) = split (": ", $event);
|
||||
return if (!defined ($v));
|
||||
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
|
||||
return if ($ccuflags =~ /noagg/);
|
||||
|
||||
foreach my $rule (keys %{$hash->{hmccu}{agg}}) {
|
||||
my $ftype = $hash->{hmccu}{agg}{$rule}{ftype};
|
||||
my $fexpr = $hash->{hmccu}{agg}{$rule}{fexpr};
|
||||
my $fread = $hash->{hmccu}{agg}{$rule}{fread};
|
||||
next if ($r !~ $fread);
|
||||
next if ($ftype eq 'name' && $devname !~ /$fexpr/);
|
||||
next if ($ftype eq 'type' && $devhash->{ccutype} !~ /$fexpr/);
|
||||
next if ($ftype eq 'group' && AttrVal ($devname, 'group', 'null') !~ /$fexpr/);
|
||||
next if ($ftype eq 'room' && AttrVal ($devname, 'room', 'null') !~ /$fexpr/);
|
||||
next if ($ftype eq 'alias' && AttrVal ($devname, 'alias', 'null') !~ /$fexpr/);
|
||||
|
||||
HMCCU_AggregateReadings ($hash, $rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Calculate reading aggregation
|
||||
#####################################
|
||||
|
||||
sub HMCCU_AggregateReadings ($$$)
|
||||
sub HMCCU_AggregateReadings ($$)
|
||||
{
|
||||
my ($hash, $ftype, $fkey) = @_;
|
||||
my ($hash, $rule) = @_;
|
||||
|
||||
my $dc = 0;
|
||||
my $mc = 0;
|
||||
my $result = '';
|
||||
my $rl = '';
|
||||
|
||||
# Get rule parameters
|
||||
my $ftype = $hash->{hmccu}{agg}{$rule}{ftype};
|
||||
my $fexpr = $hash->{hmccu}{agg}{$rule}{fexpr};
|
||||
my $fexcl = $hash->{hmccu}{agg}{$rule}{fexcl};
|
||||
my $fread = $hash->{hmccu}{agg}{$rule}{fread};
|
||||
# my $fcoll = $hash->{hmccu}{agg}{$rule}{fcoll};
|
||||
my $fcond = $hash->{hmccu}{agg}{$rule}{fcond};
|
||||
my $ftrue = $hash->{hmccu}{agg}{$rule}{ftrue};
|
||||
my $felse = $hash->{hmccu}{agg}{$rule}{felse};
|
||||
my $fpref = $hash->{hmccu}{agg}{$rule}{fpref};
|
||||
|
||||
my $resval;
|
||||
$resval = $ftrue if ($fcond =~ /^(max|min|sum|avg)$/);
|
||||
|
||||
# Update reading in matching client devices
|
||||
foreach my $d (keys %defs) {
|
||||
# Get hash and name of client device
|
||||
# Get device parameters and check device type
|
||||
my $ch = $defs{$d};
|
||||
my $ct = $ch->{TYPE};
|
||||
next if (!exists ($ch->{NAME}) || !exists ($ch->{TYPE}));
|
||||
my $cn = $ch->{NAME};
|
||||
my $ct = $ch->{TYPE};
|
||||
next if ($ct ne 'HMCCUCHN' && $ct ne 'HMCCUDEV');
|
||||
|
||||
my $fmatch = '';
|
||||
$fmatch = $cn if ($ftype eq 'name');
|
||||
$fmatch = $ch->{ccutype} if ($ftype eq 'type');
|
||||
$fmatch = AttrVal ($cn, 'group', '') if ($ftype eq 'group');
|
||||
$fmatch = AttrVal ($cn, 'room', '') if ($ftype eq 'room');
|
||||
$fmatch = AttrVal ($cn, 'alias', '') if ($ftype eq 'alias');
|
||||
next if ($fmatch eq '' || $fmatch !~ /$fexpr/ || ($fexcl ne '' && $fmatch =~ /$fexcl/));
|
||||
|
||||
my $fcoll = $hash->{hmccu}{agg}{$rule}{fcoll} eq 'NAME' ?
|
||||
$cn : AttrVal ($cn, $hash->{hmccu}{agg}{$rule}{fcoll}, $cn);
|
||||
|
||||
# Compare readings
|
||||
my $f = 0;
|
||||
foreach my $r (keys %{$ch->{READINGS}}) {
|
||||
next if ($r !~ /$fread/);
|
||||
my $rv = $ch->{READINGS}{$r}{VAL};
|
||||
if (($fcond eq 'any' || $fcond eq 'all') && $rv eq $ftrue) {
|
||||
$mc++;
|
||||
$rl .= ($mc > 1 ? ",$fcoll" : $fcoll);
|
||||
last;
|
||||
}
|
||||
if ($fcond eq 'max' && $rv > $resval) {
|
||||
$resval = $rv;
|
||||
$mc = 1;
|
||||
$rl = $fcoll;
|
||||
last;
|
||||
}
|
||||
if ($fcond eq 'min' && $rv < $resval) {
|
||||
$resval = $rv;
|
||||
$mc = 1;
|
||||
$rl = $fcoll;
|
||||
last;
|
||||
}
|
||||
if ($fcond eq 'sum' || $fcond eq 'avg') {
|
||||
$resval += $rv;
|
||||
$mc++;
|
||||
$f = 1;
|
||||
last;
|
||||
}
|
||||
if (($fcond eq 'gt' && $rv > $ftrue) ||
|
||||
($fcond eq 'lt' && $rv < $ftrue) ||
|
||||
($fcond eq 'ge' && $rv >= $ftrue) ||
|
||||
($fcond eq 'le' && $rv <= $ftrue)) {
|
||||
$mc++;
|
||||
$rl .= ($mc > 1 ? ",$fcoll" : $fcoll);
|
||||
last;
|
||||
}
|
||||
}
|
||||
$dc++;
|
||||
}
|
||||
|
||||
$rl = $hash->{hmccu}{agg}{$rule}{fdflt} if ($rl eq '');
|
||||
if ($fcond eq 'any') {
|
||||
$result = $mc > 0 ? $ftrue : $felse;
|
||||
}
|
||||
elsif ($fcond eq 'all') {
|
||||
$result = $mc == $dc ? $ftrue : $felse;
|
||||
}
|
||||
elsif ($fcond eq 'min' || $fcond eq 'max' || $fcond eq 'sum') {
|
||||
$result = $mc > 0 ? $resval : $felse;
|
||||
}
|
||||
elsif ($fcond eq 'avg') {
|
||||
$result = $mc > 0 ? $resval/$mc : $felse;
|
||||
}
|
||||
elsif ($fcond =~ /^(gt|lt|ge|le)$/) {
|
||||
$result = $mc;
|
||||
}
|
||||
|
||||
# Set readings
|
||||
readingsBeginUpdate ($hash);
|
||||
readingsBulkUpdate ($hash, $fpref.'state', $result);
|
||||
readingsBulkUpdate ($hash, $fpref.'match', $mc);
|
||||
readingsBulkUpdate ($hash, $fpref.'count', $dc);
|
||||
readingsBulkUpdate ($hash, $fpref.'list', $rl);
|
||||
readingsEndUpdate ($hash, 1);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
#####################################
|
||||
@ -1039,14 +1204,25 @@ sub HMCCU_Get ($@)
|
||||
my $optcmd = shift @a;
|
||||
if (defined ($optcmd)) {
|
||||
if ($optcmd eq 'dump') {
|
||||
$result .= "\n-----------------------------------------\n";
|
||||
my $n = 0;
|
||||
foreach my $add (sort keys %HMCCU_Devices) {
|
||||
$result .= $HMCCU_Devices{$add}{name}."\n";
|
||||
if ($HMCCU_Devices{$add}{addtype} eq 'dev') {
|
||||
$result .= "Device ".'"'.$HMCCU_Devices{$add}{name}.'"'." [".$add."] ".
|
||||
"Type=".$HMCCU_Devices{$add}{type}."\n";
|
||||
$n = 0;
|
||||
}
|
||||
else {
|
||||
$result .= " Channel $n ".'"'.$HMCCU_Devices{$add}{name}.'"'." [".$add."]\n";
|
||||
$n++;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
elsif ($optcmd eq 'create') {
|
||||
my $devprefix = '';
|
||||
my $devsuffix = '';
|
||||
my $devtype = 'dev';
|
||||
my $devformat = '%n';
|
||||
my $devdefaults = 0;
|
||||
my $newcount = 0;
|
||||
@ -1066,6 +1242,9 @@ sub HMCCU_Get ($@)
|
||||
elsif ($defopt =~ /^f=(.+)$/) {
|
||||
$devformat = $1;
|
||||
}
|
||||
elsif ($defopt =~ /^t=(chn|dev|all)$/) {
|
||||
$devtype = $1;
|
||||
}
|
||||
elsif ($defopt eq 'defattr') {
|
||||
$devdefaults = 1;
|
||||
}
|
||||
@ -1077,7 +1256,8 @@ sub HMCCU_Get ($@)
|
||||
foreach my $add (sort keys %HMCCU_Devices) {
|
||||
my $defmod = $HMCCU_Devices{$add}{addtype} eq 'dev' ? 'HMCCUDEV' : 'HMCCUCHN';
|
||||
my $ccuname = $HMCCU_Devices{$add}{name};
|
||||
my $ccudevname = HMCCU_GetDeviceName ($add, $ccuname);
|
||||
my $ccudevname = HMCCU_GetDeviceName ($add, $ccuname);
|
||||
next if ($devtype ne 'all' && $devtype ne $HMCCU_Devices{$add}{addtype});
|
||||
next if ($ccuname !~ /$devspec/);
|
||||
my $devname = $devformat;
|
||||
$devname = $devprefix.$devname.$devsuffix;
|
||||
@ -1085,7 +1265,7 @@ sub HMCCU_Get ($@)
|
||||
$devname =~ s/%d/$ccudevname/g;
|
||||
$devname =~ s/%a/$add/g;
|
||||
$devname =~ s/[^A-Za-z\d_\.]+/_/g;
|
||||
my $ret = CommandDefine (undef, $devname." $defmod ".$ccuname);
|
||||
my $ret = CommandDefine (undef, $devname." $defmod ".$add);
|
||||
if ($ret) {
|
||||
Log3 $name, 2, "HMCCU: Define command failed $devname $defmod $ccuname";
|
||||
Log3 $name, 2, "$defmod: $ret";
|
||||
@ -1123,19 +1303,45 @@ sub HMCCU_Get ($@)
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return "Default attributes written to $filename";
|
||||
}
|
||||
elsif ($opt eq 'aggregation') {
|
||||
my $rule = shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, "Usage: get $name aggregagtion {all|rule}")
|
||||
if (!defined ($rule));
|
||||
|
||||
if ($rule eq 'all') {
|
||||
foreach my $r (keys %{$hash->{hmccu}{agg}}) {
|
||||
my $rc = HMCCU_AggregateReadings ($hash, $r);
|
||||
$result .= "$r = $rc\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
return HMCCU_SetError ($hash, "HMCCU: Aggregation rule does not exist")
|
||||
if (!exists ($hash->{hmccu}{agg}{$rule}));
|
||||
$result = HMCCU_AggregateReadings ($hash, $rule);
|
||||
$result = "$rule = $result";
|
||||
}
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return $ccureadings ? undef : $result;
|
||||
}
|
||||
elsif ($opt eq 'configdesc') {
|
||||
my $ccuobj = shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, "Usage: get $name configdesc {device|channel}")
|
||||
if (!defined ($ccuobj));
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription");
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return $res;
|
||||
}
|
||||
else {
|
||||
if (exists ($hash->{hmccu}{agg})) {
|
||||
my @rules = keys %{$hash->{hmccu}{agg}};
|
||||
$options .= " aggregation:all,".join (',', @rules) if (scalar (@rules) > 0);
|
||||
}
|
||||
return "HMCCU: Unknown argument $opt, choose one of ".$options;
|
||||
}
|
||||
}
|
||||
@ -1579,6 +1785,7 @@ sub HMCCU_UpdateClients ($$$$)
|
||||
|
||||
foreach my $d (keys %defs) {
|
||||
my $ch = $defs{$d};
|
||||
next if (!exists ($ch->{NAME}) || !exists ($ch->{TYPE}));
|
||||
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});
|
||||
@ -1604,6 +1811,7 @@ sub HMCCU_UpdateClients ($$$$)
|
||||
foreach my $d (keys %defs) {
|
||||
# Get hash of client device
|
||||
my $ch = $defs{$d};
|
||||
next if (!exists ($ch->{NAME}) || !exists ($ch->{TYPE}));
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV' && $ch->{TYPE} ne 'HMCCUCHN');
|
||||
next if ($ch->{ccudevstate} ne 'Active');
|
||||
next if ($ch->{NAME} !~ /$devexp/);
|
||||
@ -1676,6 +1884,7 @@ sub HMCCU_UpdateClientReading ($@)
|
||||
foreach my $d (keys %defs) {
|
||||
# Get hash and name of client device
|
||||
my $ch = $defs{$d};
|
||||
next if (!exists ($ch->{NAME}) || !exists ($ch->{TYPE}));
|
||||
my $ct = $ch->{TYPE};
|
||||
my $cn = $ch->{NAME};
|
||||
|
||||
@ -1729,9 +1938,6 @@ sub HMCCU_UpdateClientReading ($@)
|
||||
|
||||
# Update datapoint reading and control/state readings
|
||||
readingsBeginUpdate ($ch);
|
||||
# readingsBulkUpdate ($ch, $clreading, $cl_value);
|
||||
# readingsBulkUpdate ($ch, 'control', $cl_value) if ($cd ne '' && $dpt eq $cd && $chn eq $cc);
|
||||
# readingsBulkUpdate ($ch, 'state', $cl_value) if ($dpt eq $st && ($sc eq '' || $sc eq $chn));
|
||||
HMCCU_BulkUpdate ($ch, $clreading, $value, $cl_value);
|
||||
HMCCU_BulkUpdate ($ch, 'control', $value, $cl_value)
|
||||
if ($cd ne '' && $dpt eq $cd && $chn eq $cc);
|
||||
@ -1761,6 +1967,7 @@ sub HMCCU_DeleteDevices ($)
|
||||
}
|
||||
foreach my $d (keys %defs) {
|
||||
my $ch = $defs{$d};
|
||||
next if (!exists ($ch->{NAME}) || !exists ($ch->{TYPE}));
|
||||
if ($ch->{TYPE} eq 'HMCCUDEV' && $ch->{ccuaddr} eq $a) {
|
||||
$ch->{ccudevstate} = 'Deleted';
|
||||
readingsSingleUpdate ($ch, 'state', 'Deleted', 1);
|
||||
@ -1787,6 +1994,7 @@ sub HMCCU_RPCRegisterCallback ($)
|
||||
|
||||
my $rpcport = AttrVal ($name, 'rpcport', 2001);
|
||||
my $rpcinterval = AttrVal ($name, 'rpcinterval', $HMCCU_INIT_INTERVAL2);
|
||||
my $rpcserveraddr = AttrVal ($name, 'rpcserveraddr', $localaddr);
|
||||
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
|
||||
|
||||
foreach my $port (split (',', $rpcport)) {
|
||||
@ -1868,6 +2076,7 @@ sub HMCCU_StartExtRPCServer ($)
|
||||
my $logfile = $modpath."/log/ccurpcd";
|
||||
my $rpcqueue = AttrVal ($name, 'rpcqueue', '/tmp/ccuqueue');
|
||||
my $rpcport = AttrVal ($name, 'rpcport', 2001);
|
||||
my $rpcserverport = AttrVal ($name, 'rpcserverport', 5400);
|
||||
my $rpcinterval = AttrVal ($name, 'rpcinterval', $HMCCU_INIT_INTERVAL1);
|
||||
my $verbose = AttrVal ($name, 'verbose', -1);
|
||||
$verbose = AttrVal ('global', 'verbose', 0) if ($verbose == -1);
|
||||
@ -1896,14 +2105,16 @@ sub HMCCU_StartExtRPCServer ($)
|
||||
|
||||
my $fork_cnt = 0;
|
||||
my $callbackport = 0;
|
||||
my $ccunum = $hash->{CCUNum};
|
||||
|
||||
# Fork child process(es)
|
||||
foreach my $port (split (',', $rpcport)) {
|
||||
my $clkey = 'CB'.$port;
|
||||
my $rpcqueueport = $rpcqueue."_".$port;
|
||||
my $logfileport = $logfile."_".$port.".log";
|
||||
my $rpcqueueport = $rpcqueue."_".$port."_".$ccunum;
|
||||
my $logfileport = $logfile."_".$port."_".$ccunum.".log";
|
||||
|
||||
$callbackport = 5400+$port if ($callbackport == 0 || $ccuflags !~ /singlerpc/);
|
||||
$callbackport = $rpcserverport+$port+($ccunum*10)
|
||||
if ($callbackport == 0);
|
||||
|
||||
# Detect local IP
|
||||
if ($localaddr eq '') {
|
||||
@ -1997,6 +2208,7 @@ sub HMCCU_StartIntRPCServer ($)
|
||||
# Address and ports
|
||||
my $rpcqueue = AttrVal ($name, 'rpcqueue', '/tmp/ccuqueue');
|
||||
my $rpcport = AttrVal ($name, 'rpcport', 2001);
|
||||
my $rpcserverport = AttrVal ($name, 'rpcserverport', 5400);
|
||||
my $rpcinterval = AttrVal ($name, 'rpcinterval', $HMCCU_INIT_INTERVAL1);
|
||||
my @rpcportlist = split (",", $rpcport);
|
||||
my $serveraddr = $hash->{host};
|
||||
@ -2024,11 +2236,13 @@ sub HMCCU_StartIntRPCServer ($)
|
||||
my $localaddr = $socket->sockhost ();
|
||||
close ($socket);
|
||||
|
||||
my $ccunum = $hash->{CCUNum};
|
||||
|
||||
# Fork child processes
|
||||
foreach my $port (@rpcportlist) {
|
||||
my $clkey = 'CB'.$port;
|
||||
my $rpcqueueport = $rpcqueue."_".$port;
|
||||
my $callbackport = 5400+$port;
|
||||
my $rpcqueueport = $rpcqueue."_".$port."_".$ccunum;
|
||||
my $callbackport = $rpcserverport+$port+($ccunum*10);
|
||||
|
||||
# Clear event queue
|
||||
HMCCU_ResetRPCQueue ($hash, $port);
|
||||
@ -2401,6 +2615,15 @@ foreach(devid, root.Devices().EnumUsedIDs()) {
|
||||
$HMCCU_Addresses{$hmdata[3]}{address} = $hmdata[2];
|
||||
$HMCCU_Addresses{$hmdata[3]}{addtype} = 'dev';
|
||||
$HMCCU_Addresses{$hmdata[3]}{valid} = 1;
|
||||
$hash->{hmccu}{dev}{$hmdata[2]}{name} = $hmdata[3];
|
||||
$hash->{hmccu}{dev}{$hmdata[2]}{type} = ($hmdata[2] =~ /^CUX/) ? "CUX-".$hmdata[4] : $hmdata[4];
|
||||
$hash->{hmccu}{dev}{$hmdata[2]}{interface} = $hmdata[1];
|
||||
$hash->{hmccu}{dev}{$hmdata[2]}{channels} = $hmdata[5];
|
||||
$hash->{hmccu}{dev}{$hmdata[2]}{addtype} = 'dev';
|
||||
$hash->{hmccu}{dev}{$hmdata[2]}{valid} = 1;
|
||||
$hash->{hmccu}{adr}{$hmdata[3]}{address} = $hmdata[2];
|
||||
$hash->{hmccu}{adr}{$hmdata[3]}{addtype} = 'dev';
|
||||
$hash->{hmccu}{adr}{$hmdata[3]}{valid} = 1;
|
||||
$count++;
|
||||
}
|
||||
elsif ($hmdata[0] eq 'C') {
|
||||
@ -2412,6 +2635,13 @@ foreach(devid, root.Devices().EnumUsedIDs()) {
|
||||
$HMCCU_Addresses{$hmdata[2]}{address} = $hmdata[1];
|
||||
$HMCCU_Addresses{$hmdata[2]}{addtype} = 'chn';
|
||||
$HMCCU_Addresses{$hmdata[2]}{valid} = 1;
|
||||
$hash->{hmccu}{dev}{$hmdata[1]}{name} = $hmdata[2];
|
||||
$hash->{hmccu}{dev}{$hmdata[1]}{channels} = 1;
|
||||
$hash->{hmccu}{dev}{$hmdata[1]}{addtype} = 'chn';
|
||||
$hash->{hmccu}{dev}{$hmdata[1]}{valid} = 1;
|
||||
$hash->{hmccu}{adr}{$hmdata[2]}{address} = $hmdata[1];
|
||||
$hash->{hmccu}{adr}{$hmdata[2]}{addtype} = 'chn';
|
||||
$hash->{hmccu}{adr}{$hmdata[2]}{valid} = 1;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
@ -2422,6 +2652,7 @@ foreach(devid, root.Devices().EnumUsedIDs()) {
|
||||
foreach my $d (keys %defs) {
|
||||
# Get hash of client device
|
||||
my $ch = $defs{$d};
|
||||
next if (!exists ($ch->{NAME}) || !exists ($ch->{TYPE}));
|
||||
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" &&
|
||||
@ -3070,7 +3301,7 @@ sub HMCCU_ResetRPCQueue ($$)
|
||||
my $rpcqueue = AttrVal ($name, 'rpcqueue', '/tmp/ccuqueue');
|
||||
my $clkey = 'CB'.$port;
|
||||
|
||||
if (HMCCU_QueueOpen ($hash, $rpcqueue."_".$port)) {
|
||||
if (HMCCU_QueueOpen ($hash, $rpcqueue."_".$port."_".$hash->{CCUNum})) {
|
||||
HMCCU_QueueReset ($hash);
|
||||
while (defined (HMCCU_QueueDeq ($hash))) { }
|
||||
HMCCU_QueueClose ($hash);
|
||||
@ -3454,9 +3685,6 @@ sub HMCCU_UpdateSingleReading ($$$$$)
|
||||
my $cl_value = HMCCU_Substitute ($value, $substitute, 0, $chn, $dpt);
|
||||
|
||||
readingsBeginUpdate ($hash);
|
||||
# readingsBulkUpdate ($hash, $reading, $value);
|
||||
# readingsBulkUpdate ($hash, 'control', $value) if ($cd ne '' && $dpt eq $cd && $chn eq $cc);
|
||||
# readingsBulkUpdate ($hash, 'state', $value) if ($dpt eq $sd && ($sc eq '' || $sc eq $chn));
|
||||
HMCCU_BulkUpdate ($hash, $reading, $value, $cl_value);
|
||||
HMCCU_BulkUpdate ($hash, 'control', $value, $cl_value)
|
||||
if ($cd ne '' && $dpt eq $cd && $chn eq $cc);
|
||||
@ -3464,7 +3692,7 @@ sub HMCCU_UpdateSingleReading ($$$$$)
|
||||
if ($dpt eq $sd && ($sc eq '' || $sc eq $chn));
|
||||
readingsEndUpdate ($hash, 1);
|
||||
|
||||
return $value;
|
||||
return $cl_value;
|
||||
}
|
||||
|
||||
####################################################
|
||||
@ -3838,6 +4066,7 @@ if (odev) {
|
||||
my ($da, $cno) = HMCCU_SplitChnAddr ($cl_hash->{ccuaddr});
|
||||
foreach my $dn (sort keys %defs) {
|
||||
my $ch = $defs{$dn};
|
||||
next if (!exists ($ch->{NAME}) || !exists ($ch->{TYPE}));
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV');
|
||||
next if ($ch->{ccuif} ne "VirtualDevices" || !exists ($ch->{ccugroup}));
|
||||
my @vdevs = split (",", $ch->{ccugroup});
|
||||
@ -4037,11 +4266,12 @@ foreach (sChannel, sChnList.Split(",")) {
|
||||
# Get RPC paramSet or paramSetDescription
|
||||
####################################################
|
||||
|
||||
sub HMCCU_RPCGetConfig ($$$)
|
||||
sub HMCCU_RPCGetConfig ($$$$)
|
||||
{
|
||||
my ($hash, $param, $mode) = @_;
|
||||
my ($hash, $param, $mode, $filter) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $type = $hash->{TYPE};
|
||||
my $method = $mode eq 'listParamset' ? 'getParamset' : $mode;
|
||||
|
||||
my $addr;
|
||||
my $result = '';
|
||||
@ -4065,7 +4295,7 @@ sub HMCCU_RPCGetConfig ($$$)
|
||||
$url .= $HMCCU_RPC_URL{$port} if (exists ($HMCCU_RPC_URL{$port}));
|
||||
my $client = RPC::XML::Client->new ($url);
|
||||
|
||||
my $res = $client->simple_request ($mode, $addr, "MASTER");
|
||||
my $res = $client->simple_request ($method, $addr, "MASTER");
|
||||
if (! defined ($res)) {
|
||||
return (-5, "Function not available");
|
||||
}
|
||||
@ -4091,29 +4321,36 @@ sub HMCCU_RPCGetConfig ($$$)
|
||||
$oper .= 'E' if ($res->{$key}->{OPERATIONS} & 4);
|
||||
$result .= $key.": ".$res->{$key}->{TYPE}." [".$oper."]\n";
|
||||
}
|
||||
|
||||
return (0, $result);
|
||||
}
|
||||
|
||||
readingsBeginUpdate ($hash) if ($ccureadings);
|
||||
|
||||
foreach my $key (sort keys %$res) {
|
||||
my $value = $res->{$key};
|
||||
$result .= "$key=$value\n";
|
||||
|
||||
if ($ccureadings) {
|
||||
my $reading = HMCCU_GetReadingName ($hash, $int, $add, $chn, $key, $nam,
|
||||
$readingformat);
|
||||
if ($reading ne '') {
|
||||
$value = HMCCU_FormatReadingValue ($hash, $value);
|
||||
$value = HMCCU_Substitute ($value, $substitute, 0, $chn, $reading);
|
||||
$reading = "R-".$reading;
|
||||
readingsBulkUpdate ($hash, $reading, $value);
|
||||
}
|
||||
elsif ($mode eq 'listParamset') {
|
||||
foreach my $key (sort keys %$res) {
|
||||
next if ($key !~ /$filter/);
|
||||
my $value = $res->{$key};
|
||||
$result .= "$key=$value\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
readingsBeginUpdate ($hash) if ($ccureadings);
|
||||
|
||||
readingsEndUpdate ($hash, 1) if ($ccureadings);
|
||||
foreach my $key (sort keys %$res) {
|
||||
next if ($key !~ /$filter/);
|
||||
my $value = $res->{$key};
|
||||
$result .= "$key=$value\n";
|
||||
|
||||
if ($ccureadings) {
|
||||
my $reading = HMCCU_GetReadingName ($hash, $int, $add, $chn, $key, $nam,
|
||||
$readingformat);
|
||||
if ($reading ne '') {
|
||||
$value = HMCCU_FormatReadingValue ($hash, $value);
|
||||
$value = HMCCU_Substitute ($value, $substitute, 0, $chn, $reading);
|
||||
$reading = "R-".$reading;
|
||||
readingsBulkUpdate ($hash, $reading, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readingsEndUpdate ($hash, 1) if ($ccureadings);
|
||||
}
|
||||
|
||||
return (0, $result);
|
||||
}
|
||||
@ -4498,7 +4735,7 @@ sub HMCCU_EncodeEPDisplay ($)
|
||||
|
||||
|
||||
####################################################
|
||||
# *** Subprocess process part ***
|
||||
# *** Subprocess part ***
|
||||
####################################################
|
||||
|
||||
# Child process. Must be global to allow access by RPC callbacks
|
||||
@ -4509,6 +4746,10 @@ my $queue;
|
||||
my %child_queue;
|
||||
my $cpqueue = \%child_queue;
|
||||
|
||||
# In-Memory queue
|
||||
my @evqueue;
|
||||
my $evq_lock = 0;
|
||||
|
||||
# Statistic data of child process
|
||||
my %child_hash = (
|
||||
"total", 0,
|
||||
@ -4541,6 +4782,12 @@ sub HMCCU_CCURPC_Write ($$)
|
||||
|
||||
# SUBPROCESS
|
||||
HMCCU_QueueEnq ($cpqueue, $et."|".$msg);
|
||||
|
||||
if (!$evq_lock) {
|
||||
$evq_lock = 1;
|
||||
push (@evqueue, $et."|".$msg);
|
||||
$evq_lock = 0;
|
||||
}
|
||||
|
||||
# SUBPROCESS
|
||||
# Log3 $name, 1, "CCURPC: Write $et $msg";
|
||||
@ -4658,6 +4905,15 @@ sub HMCCU_CCURPC_OnRun ($)
|
||||
}
|
||||
);
|
||||
|
||||
# Callback for event queue query
|
||||
Log3 $name, 1, "CCURPC: $clkey Adding callback for event query";
|
||||
$ccurpc_server->add_method (
|
||||
{ name=>"getEvents",
|
||||
signature=>["array string"],
|
||||
code=>\&HMCCU_CCURPC_GetEventsCB
|
||||
}
|
||||
);
|
||||
|
||||
# Enter server loop
|
||||
# SUBPROCESS
|
||||
# sleep (5);
|
||||
@ -4826,6 +5082,32 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
return RPC::XML::array->new();
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Subprocess
|
||||
# Callback for event query
|
||||
#####################################
|
||||
|
||||
sub HMCCU_CCURPC_GetEventsCB ($$)
|
||||
{
|
||||
my ($server, $cb) = @_;
|
||||
my @result;
|
||||
my $name = $hmccu_child->{devname};
|
||||
|
||||
$cb = "unknown" if (!defined ($cb));
|
||||
Log3 $name, 1, "CCURPC: $cb GetEvents";
|
||||
|
||||
if (scalar (@evqueue) > 0 && !$evq_lock) {
|
||||
$evq_lock = 1;
|
||||
@result = @evqueue;
|
||||
@evqueue = ();
|
||||
$evq_lock = 0;
|
||||
|
||||
return \@result;
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
@ -4909,6 +5191,9 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
<a name="HMCCUget"></a>
|
||||
<b>Get</b><br/><br/>
|
||||
<ul>
|
||||
<li><b>get <name> aggregation {<rule>|all}</b><br/>
|
||||
Process aggregation rule defined with attribute ccuaggregation.
|
||||
</li><br/>
|
||||
<li><b>get <name> configdesc {<device>|<channel>}</b><br/>
|
||||
Get configuration parameter description of CCU device or channel (similar
|
||||
to device settings in CCU). Not every CCU device or channel provides a configuration
|
||||
@ -4922,17 +5207,21 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
queried directly. Otherwise device information from CCU is listed.
|
||||
</li><br/>
|
||||
<li><b>get <name> devicelist [dump]</b><br/>
|
||||
<b>get <name> devicelist create <devexp> [p=<prefix>] [s=<suffix>]
|
||||
[f=<format>] [defattr] [<attr>=<value> [...]]</b><br/>
|
||||
Read list of devices and channels from CCU. This command is executed automatically
|
||||
after the definition of an I/O device. It must be executed manually after
|
||||
module HMCCU is reloaded or after devices have changed in CCU (added, removed or
|
||||
renamed). With option 'dump' devices are displayed in browser window. If a RPC
|
||||
server is running HMCCU will raise events "<i>count</i> devices added in CCU" or
|
||||
"<i>count</i> devices deleted in CCU". It's recommended to set up a notification
|
||||
which reacts with execution of command 'get devicelist' on these events.<br/>
|
||||
which reacts with execution of command 'get devicelist' on these events.
|
||||
</li><br/>
|
||||
<li><b>get <name> devicelist create <devexp> [t={chn|<u>dev</u>|all}]
|
||||
[p=<prefix>] [s=<suffix>] [f=<format>] [defattr]
|
||||
[<attr>=<value> [...]]</b><br/>
|
||||
With option 'create' HMCCU will automatically create client devices for all CCU devices
|
||||
and channels matching specified regular expression. Optionally a <i>prefix</i> and/or a
|
||||
and channels matching specified regular expression. With option t=chn or t=dev (default)
|
||||
the creation of devices is limited to CCU channels or devices.<br/>
|
||||
Optionally a <i>prefix</i> and/or a
|
||||
<i>suffix</i> for the FHEM device name can be specified. The parameter <i>format</i>
|
||||
defines a template for the FHEM device names. Prefix, suffix and format can contain
|
||||
format identifiers which are substituted by corresponding values of the CCU device or
|
||||
@ -4982,15 +5271,44 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
<li><b>ccuackstate {0 | <u>1</u>}</b><br/>
|
||||
If set to 1 state will be set to result of command (i.e. 'OK').
|
||||
</li><br/>
|
||||
<li><b>ccuaggregate <rule>[;...]</b><br/>
|
||||
Define aggregation rules for client device readings. With an aggregation rule
|
||||
it's easy to detect if some or all client device readings are set to a specific
|
||||
value, i.e. detect all devices with low battery or detect all open windows.<br/>
|
||||
Aggregation rules are automatically executed as a reaction on reading events of
|
||||
HMCCU client devices. An aggregation rule consists of several parameters separated
|
||||
by comma:<br/><br/>
|
||||
<ul>
|
||||
<li><b>name:<rule-name></b><br/>
|
||||
Name of aggregation rule</li>
|
||||
<li><b>filter:{name|alias|group|room|type}=<incl-expr>[!<excl-expr>]</b><br/>
|
||||
Filter criteria, i.e. "type=^HM-Sec-SC.*"</li>
|
||||
<li><b>read:<read-expr></b><br/>
|
||||
Expression for reading names, i.e. "STATE"</li>
|
||||
<li><b>if:{any|all|min|max|sum|avg|lt|gt|le|ge}=<value></b><br/>
|
||||
Condition, i.e. "any=open" or initial value, i.e. max=0</li>
|
||||
<li><b>else:<value></b><br/>
|
||||
Complementary value, i.e. "closed"</li>
|
||||
<li><b>prefix:{<text>|RULE}</b><br/>
|
||||
Prefix for reading names with aggregation results</li>
|
||||
<li><b>coll:{<attribute>|NAME}[!<default-text>]</b><br/>
|
||||
Attribute of matching devices stored in aggregation results. Default text in case
|
||||
of no matching devices found is optional.</li>
|
||||
</ul><br/>
|
||||
Aggregation results will be stored in readings <i>prefix</i>count, <i>prefix</i>
|
||||
list, <i>prefix</i>match and <i>prefix</i>state<br/><br/>
|
||||
Example: Find open windows<br/>
|
||||
name=lock,filter:type=^HM-Sec-SC.*,read:STATE,if:any=open,else:closed,prefix:lock_,coll:NAME!All windows closed<br/><br/>
|
||||
Example: Find devices with low batteries<br/>
|
||||
name=battery,filter:name=.*,read:(LOWBAT|LOW_BAT),if:any=yes,else:no,prefix:batt_,coll:NAME<br/>
|
||||
</li><br/>
|
||||
<li><b>ccudefaults <filename></b><br/>
|
||||
Load default attributes for HMCCUCHN and HMCCUDEV devices from specified file. Best
|
||||
practice for creating a custom default attribute file is by exporting predefined default
|
||||
attributes from HMCCU with command 'get exportdefaults'.
|
||||
</li><br/>
|
||||
<li><b>ccuflags {singlerpc, extrpc, <u>intrpc</u>}</b><br/>
|
||||
<li><b>ccuflags {extrpc, <u>intrpc</u>}</b><br/>
|
||||
Control RPC server process and datapoint validation:<br/>
|
||||
singlerpc - Start only one external RPC server for all Homematic interfaces (not
|
||||
recommended!). Requires flag extrpc.<br/>
|
||||
intrpc - Use internal RPC server instead of ccurpcd.pl. This is the default.<br/>
|
||||
extrpc - Use ccurpcd.pl instead of internal RPC server (depricated)<br/>
|
||||
dptnocheck - Do not check within set or get commands if datapoint is valid<br/>
|
||||
@ -5038,6 +5356,19 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
<li><b>rpcserver {on | <u>off</u>}</b><br/>
|
||||
Specify if RPC server is automatically started on FHEM startup.
|
||||
</li><br/>
|
||||
<li><b>rpcserveraddr <ip-or-name></b><br/>
|
||||
Specify network interface by IP address or DNS name where RPC server should listen
|
||||
on. By default HMCCU automatically detects the IP address. This attribute should be used
|
||||
if the FHEM server has more than one network interface.
|
||||
</li><br/>
|
||||
<li><b>rpcserverport <base-port></b><br/>
|
||||
Specify base port for RPC server. The real listening port of an RPC server is
|
||||
calculated by the formula: base-port + rpc-port + (10 * ccu-number). Default
|
||||
value for <i>base-port</i> is 5400.<br/>
|
||||
The value ccu-number is only relevant if more than one CCU is connected to FHEM.
|
||||
Example: If <i>base-port</i> is 5000, protocol is BidCos (rpc-port 2001) and only
|
||||
one CCU is connected the resulting RPC server port is 5000+2001+(10*0) = 7001.
|
||||
</li><br/>
|
||||
<li><b>substitute <expression>:<substext>[,...]</b><br/>
|
||||
Define substitions for reading values. Substitutions for parfile values must
|
||||
be specified in parfiles.
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Version 3.5
|
||||
# Version 3.6
|
||||
#
|
||||
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
||||
#
|
||||
@ -23,8 +23,9 @@
|
||||
# set <name> pct <level> [{ <ontime> | 0 } [<ramptime>]]
|
||||
# set <name> toggle
|
||||
#
|
||||
# get <name> config
|
||||
# get <name> config [<filter-expr>]
|
||||
# get <name> configdesc
|
||||
# get <name> configlist
|
||||
# get <name> datapoint <datapoint>
|
||||
# get <name> defaults
|
||||
# get <name> devstate
|
||||
@ -89,7 +90,8 @@ sub HMCCUCHN_Define ($@)
|
||||
my $name = $hash->{NAME};
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
return "Specifiy the CCU device name or address as parameters" if (@a < 3);
|
||||
my $usage = "Usage: define $name HMCCUCHN {device} ['readonly'] ['defaults']";
|
||||
return $usage if (@a < 3);
|
||||
|
||||
my $devname = shift @a;
|
||||
my $devtype = shift @a;
|
||||
@ -124,9 +126,27 @@ sub HMCCUCHN_Define ($@)
|
||||
$hash->{channels} = 1;
|
||||
$hash->{statevals} = 'devstate';
|
||||
|
||||
# my $arg = shift @a;
|
||||
# if (defined ($arg) && $arg eq 'readonly') {
|
||||
# $hash->{statevals} = $arg;
|
||||
# }
|
||||
|
||||
# Parse optional command line parameters
|
||||
my $n = 0;
|
||||
my $arg = shift @a;
|
||||
if (defined ($arg) && $arg eq 'readonly') {
|
||||
$hash->{statevals} = $arg;
|
||||
while (defined ($arg)) {
|
||||
return $usage if ($n == 3);
|
||||
if ($arg eq 'readonly') {
|
||||
$hash->{statevals} = $arg;
|
||||
}
|
||||
elsif ($arg eq 'defaults') {
|
||||
HMCCU_SetDefaults ($hash);
|
||||
}
|
||||
else {
|
||||
return $usage;
|
||||
}
|
||||
$n++;
|
||||
$arg = shift @a;
|
||||
}
|
||||
|
||||
# Inform HMCCU device about client device
|
||||
@ -498,15 +518,26 @@ sub HMCCUCHN_Get ($@)
|
||||
}
|
||||
elsif ($opt eq 'config') {
|
||||
my $ccuobj = $ccuaddr;
|
||||
my $par = shift @a;
|
||||
$par = '.*' if (!defined ($par));
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset");
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return $ccureadings ? undef : $res;
|
||||
}
|
||||
elsif ($opt eq 'configlist') {
|
||||
my $ccuobj = $ccuaddr;
|
||||
my $par = shift @a;
|
||||
$par = '.*' if (!defined ($par));
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return $res;
|
||||
}
|
||||
elsif ($opt eq 'configdesc') {
|
||||
my $ccuobj = $ccuaddr;
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription");
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return $res;
|
||||
}
|
||||
@ -521,7 +552,7 @@ sub HMCCUCHN_Get ($@)
|
||||
my @valuelist;
|
||||
my $valuecount = HMCCU_GetValidDatapoints ($hash, $hash->{ccutype}, $c, 1, \@valuelist);
|
||||
$retmsg .= ":".join(",",@valuelist) if ($valuecount > 0);
|
||||
$retmsg .= " update:noArg config:noArg configdesc:noArg";
|
||||
$retmsg .= " update:noArg config configlist configdesc:noArg";
|
||||
|
||||
return $retmsg;
|
||||
}
|
||||
@ -576,10 +607,12 @@ sub HMCCUCHN_SetError ($$)
|
||||
<b>Define</b><br/><br/>
|
||||
<ul>
|
||||
<code>define <name> HMCCUCHN {<channel-name> | <channel-address>}
|
||||
[readonly]</code>
|
||||
[readonly] [defaults]</code>
|
||||
<br/><br/>
|
||||
If option 'readonly' is specified no set command will be available. Define command
|
||||
accepts a CCU2 channel name or channel address as parameter.
|
||||
If option 'readonly' is specified no set command will be available. With option 'defaults'
|
||||
some default attributes depending on CCU device type will be set. Default attributes are only
|
||||
available for some device types.<br/>
|
||||
The define command accepts a CCU2 channel name or channel address as parameter.
|
||||
<br/><br/>
|
||||
Examples:<br/>
|
||||
<code>define window_living HMCCUCHN WIN-LIV-1 readonly</code><br/>
|
||||
@ -684,13 +717,17 @@ sub HMCCUCHN_SetError ($$)
|
||||
<a name="HMCCUCHNget"></a>
|
||||
<b>Get</b><br/><br/>
|
||||
<ul>
|
||||
<li><b>get <name> config</b><br/>
|
||||
<li><b>get <name> config [<filter-expr>]</b><br/>
|
||||
Get configuration parameters of CCU channel. If attribute 'ccureadings' is 0 results
|
||||
are displayed in browser window.
|
||||
are displayed in browser window. Parameters can be filtered by <i>filter-expr</i>.
|
||||
</li><br/>
|
||||
<li><b>get <name> configdesc</b><br/>
|
||||
Get description of configuration parameters of CCU channel.
|
||||
</li><br/>
|
||||
<li><b>get <name> configlist [<filter-expr>]</b><br/>
|
||||
Get configuration parameters of CCU channel. Parameters can be filtered by
|
||||
<i>filter-expr</i>.
|
||||
</li><br/>
|
||||
<li><b>get <name> datapoint <datapoint></b><br/>
|
||||
Get value of a CCU channel datapoint.
|
||||
</li><br/>
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Version 3.5
|
||||
# Version 3.6
|
||||
#
|
||||
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
||||
#
|
||||
@ -25,8 +25,9 @@
|
||||
# set <name> <stateval_cmds>
|
||||
# set <name> toggle
|
||||
#
|
||||
# get <name> config [<channel-number>]
|
||||
# get <name> config [<channel-number>] [<filter-expr>]
|
||||
# get <name> configdesc [<channel-number>]
|
||||
# get <name> configlist [<channel-number>]
|
||||
# get <name> datapoint [<channel-number>.]<datapoint>
|
||||
# get <name> defaults
|
||||
# get <name> devstate
|
||||
@ -158,13 +159,10 @@ sub HMCCUDEV_Define ($@)
|
||||
}
|
||||
|
||||
# Parse optional command line parameters
|
||||
my $n = 0;
|
||||
my $arg = shift @a;
|
||||
while (defined ($arg)) {
|
||||
return $usage if ($n == 3);
|
||||
if ($arg eq 'readonly') {
|
||||
$hash->{statevals} = $arg;
|
||||
$n++;
|
||||
}
|
||||
elsif ($arg eq 'defaults') {
|
||||
HMCCU_SetDefaults ($hash);
|
||||
@ -210,7 +208,6 @@ sub HMCCUDEV_Define ($@)
|
||||
}
|
||||
elsif ($arg =~ /^[0-9]+$/) {
|
||||
$attr{$name}{statechannel} = $arg;
|
||||
$n++;
|
||||
}
|
||||
else {
|
||||
return $usage;
|
||||
@ -652,17 +649,34 @@ sub HMCCUDEV_Get ($@)
|
||||
if ($par =~ /^([0-9]{1,2})$/) {
|
||||
return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
|
||||
$ccuobj .= ':'.$1;
|
||||
}
|
||||
else {
|
||||
return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
|
||||
$par = shift @a;
|
||||
}
|
||||
}
|
||||
$par = '.*' if (!defined ($par));
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset");
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
|
||||
return $ccureadings ? undef : $res;
|
||||
}
|
||||
elsif ($opt eq 'configlist') {
|
||||
my $channel = undef;
|
||||
my $ccuobj = $ccuaddr;
|
||||
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 ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
|
||||
return $res;
|
||||
}
|
||||
elsif ($opt eq 'configdesc') {
|
||||
my $channel = undef;
|
||||
my $ccuobj = $ccuaddr;
|
||||
@ -677,7 +691,7 @@ sub HMCCUDEV_Get ($@)
|
||||
}
|
||||
}
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription");
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
|
||||
return $res;
|
||||
@ -693,7 +707,7 @@ sub HMCCUDEV_Get ($@)
|
||||
my $valuecount = HMCCU_GetValidDatapoints ($hash, $ccutype, -1, 1, \@valuelist);
|
||||
|
||||
$retmsg .= ":".join(",", @valuelist) if ($valuecount > 0);
|
||||
$retmsg .= " defaults:noArg update:noArg config configdesc deviceinfo:noArg";
|
||||
$retmsg .= " defaults:noArg update:noArg config configlist configdesc deviceinfo:noArg";
|
||||
$retmsg .= ' devstate:noArg' if ($sc ne '');
|
||||
|
||||
return $retmsg;
|
||||
@ -841,14 +855,19 @@ sub HMCCUDEV_Get ($@)
|
||||
<a name="HMCCUDEVget"></a>
|
||||
<b>Get</b><br/><br/>
|
||||
<ul>
|
||||
<li><b>get <name> config [<channel-number>]</b><br/>
|
||||
<li><b>get <name> config [<channel-number>] [<filter-expr>]</b><br/>
|
||||
Get configuration parameters of CCU device. If attribute 'ccureadings' is set to 0
|
||||
parameters are displayed in browser window (no readings set).
|
||||
parameters are displayed in browser window (no readings set). Parameters can be filtered
|
||||
by <i>filter-expr</i>.
|
||||
</li><br/>
|
||||
<li><b>get <name> configdesc [<channel-number>] [<rpcport>]</b><br/>
|
||||
Get description of configuration parameters for CCU device. Default value for Parameter
|
||||
<i>rpcport</i> is 2001 (BidCos-RF). Other valid values are 2000 (wired) and 2010 (HMIP).
|
||||
</li><br/>
|
||||
<li><b>get <name> configlist [<channel-number>] [<filter-expr>]</b><br/>
|
||||
Display configuration parameters of CCU device. Parameters can be filtered by
|
||||
<i>filter-expr</i>.
|
||||
</li><br/>
|
||||
<li><b>get <name> datapoint [<channel-number>.]<datapoint></b><br/>
|
||||
Get value of a CCU device datapoint. If <i>channel-number</i> is not specified state
|
||||
channel is used.
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Version 3.5
|
||||
# Version 3.6
|
||||
#
|
||||
# Configuration parameters for Homematic devices.
|
||||
#
|
||||
@ -76,6 +76,30 @@ use vars qw(%HMCCU_DEV_DEFAULTS);
|
||||
statevals => "on:true,off:false",
|
||||
substitute => "STATE!(1|true):on,(0|false):off;LOWBAT,UNREACH!(1|true):yes,(0|false):no"
|
||||
},
|
||||
"HM-LC-Sw1-Pl|HM-LC-Sw1-Pl-2|HM-LC-Sw1-SM|HM-LC-Sw1-FM|HM-LC-Sw1-PB-FM" => {
|
||||
_description => "1 Kanal Funk-Schaltaktor",
|
||||
_channels => "1",
|
||||
ccureadingfilter => "(STATE|LOWBAT|^UNREACH)",
|
||||
statedatapoint => "STATE",
|
||||
statevals => "on:true,off:false",
|
||||
substitute => "STATE!(1|true):on,(0|false):off;LOWBAT,UNREACH!(1|true):yes,(0|false):no"
|
||||
},
|
||||
"HM-LC-Sw2-SM|HM-LC-Sw2-FM|HM-LC-Sw2-PB-FM|HM-LC-Sw2-DR" => {
|
||||
_description => "2 Kanal Funk-Schaltaktor",
|
||||
_channels => "1,2",
|
||||
ccureadingfilter => "(STATE|LOWBAT|^UNREACH)",
|
||||
statedatapoint => "STATE",
|
||||
statevals => "on:true,off:false",
|
||||
substitute => "STATE!(1|true):on,(0|false):off;LOWBAT,UNREACH!(1|true):yes,(0|false):no"
|
||||
},
|
||||
"HM-LC-Sw4-DR|HM-LC-Sw4-WM|HM-LC-Sw4-PCB|HM-LC-Sw4-SM" => {
|
||||
_description => "4 Kanal Funk-Schaltaktor",
|
||||
_channels => "1,2,3,4",
|
||||
ccureadingfilter => "(STATE|LOWBAT|^UNREACH)",
|
||||
statedatapoint => "STATE",
|
||||
statevals => "on:true,off:false",
|
||||
substitute => "STATE!(1|true):on,(0|false):off;LOWBAT,UNREACH!(1|true):yes,(0|false):no"
|
||||
},
|
||||
"HM-Sen-LI-O" => {
|
||||
_description => "Lichtsensor",
|
||||
_channels => "1",
|
||||
|
Loading…
x
Reference in New Issue
Block a user