HMCCU: Firmware downloads, advanced scripting

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@14103 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
zap 2017-04-25 15:40:51 +00:00
parent ca9b3b3fdb
commit 5994d53ce9
6 changed files with 904 additions and 366 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- update: 88_HMCCU: Homematic firmware download, advanced scripting
- feature: 98_TRAFFIC: v1.3.2, stroke styles, warnings reduced, bugfix - feature: 98_TRAFFIC: v1.3.2, stroke styles, warnings reduced, bugfix
- update: 98_DOIFtools: more precise regexp for INITIALIZED event because - update: 98_DOIFtools: more precise regexp for INITIALIZED event because
there is more than one initialisation event now there is more than one initialisation event now

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
# #
# $Id$ # $Id$
# #
# Version 4.0 # Version 4.0.001
# #
# (c) 2017 zap (zap01 <at> t-online <dot> de) # (c) 2017 zap (zap01 <at> t-online <dot> de)
# #
@ -66,7 +66,6 @@ sub HMCCUCHN_Define ($@);
sub HMCCUCHN_Set ($@); sub HMCCUCHN_Set ($@);
sub HMCCUCHN_Get ($@); sub HMCCUCHN_Get ($@);
sub HMCCUCHN_Attr ($@); sub HMCCUCHN_Attr ($@);
sub HMCCUCHN_SetError ($$);
################################################## ##################################################
# Initialize module # Initialize module
@ -286,7 +285,7 @@ sub HMCCUCHN_Set ($@)
my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd; my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd;
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname); ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
my $objvalue = ''; my $objvalue = '';
my $st = 0; my $st = 0;
@ -487,7 +486,7 @@ sub HMCCUCHN_Get ($@)
my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd; my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd;
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname); ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
return $ccureadings ? undef : $result; return $ccureadings ? undef : $result;
} }
elsif ($opt eq 'datapoint') { elsif ($opt eq 'datapoint') {
@ -500,7 +499,7 @@ sub HMCCUCHN_Get ($@)
$objname = $ccuif.'.'.$ccuaddr.'.'.$objname; $objname = $ccuif.'.'.$ccuaddr.'.'.$objname;
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname); ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
return $ccureadings ? undef : $result; return $ccureadings ? undef : $result;
} }
elsif ($opt eq 'update') { elsif ($opt eq 'update') {
@ -536,7 +535,7 @@ sub HMCCUCHN_Get ($@)
$par = '.*' if (!defined ($par)); $par = '.*' if (!defined ($par));
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par); my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
return $ccureadings ? undef : $res; return $ccureadings ? undef : $res;
} }
elsif ($opt eq 'configlist') { elsif ($opt eq 'configlist') {
@ -551,7 +550,7 @@ sub HMCCUCHN_Get ($@)
$par = '.*' if (!defined ($par)); $par = '.*' if (!defined ($par));
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par); my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
return $res; return $res;
} }
elsif ($opt eq 'configdesc') { elsif ($opt eq 'configdesc') {
@ -562,7 +561,7 @@ sub HMCCUCHN_Get ($@)
} }
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef); my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
return $res; return $res;
} }
elsif ($opt eq 'defaults') { elsif ($opt eq 'defaults') {
@ -582,36 +581,6 @@ sub HMCCUCHN_Get ($@)
} }
} }
#####################################
# Set error status
#####################################
sub HMCCUCHN_SetError ($$)
{
my ($hash, $text) = @_;
my $name = $hash->{NAME};
my $msg;
my %errlist = (
-1 => 'Channel name or address invalid',
-2 => 'Execution of CCU script failed',
-3 => 'Cannot detect IO device',
-4 => 'Device deleted in CCU',
-5 => 'No response from CCU',
-6 => 'Update of readings disabled. Set attribute ccureadings first'
);
if (exists ($errlist{$text})) {
$msg = $errlist{$text};
}
else {
$msg = $text;
}
$msg = "HMCCUCHN: ".$name." ". $msg;
readingsSingleUpdate ($hash, "state", "Error", 1);
Log3 $name, 1, $msg;
return $msg;
}
1; 1;

View File

@ -4,7 +4,7 @@
# #
# $Id$ # $Id$
# #
# Version 4.0 # Version 4.0.001
# #
# (c) 2017 zap (zap01 <at> t-online <dot> de) # (c) 2017 zap (zap01 <at> t-online <dot> de)
# #
@ -383,7 +383,7 @@ sub HMCCUDEV_Set ($@)
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname); ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
Log3 $name, 2, "HMCCU: set toggle: GetDatapoint returned $rc, $result" Log3 $name, 2, "HMCCU: set toggle: GetDatapoint returned $rc, $result"
if ($ccuflags =~ /trace/); if ($ccuflags =~ /trace/);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
my $objvalue = ''; my $objvalue = '';
my $st = 0; my $st = 0;
@ -588,7 +588,7 @@ sub HMCCUDEV_Get ($@)
my $objname = $ccuif.'.'.$ccuaddr.':'.$sc.'.'.$sd; my $objname = $ccuif.'.'.$ccuaddr.':'.$sc.'.'.$sd;
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname); ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
return $ccureadings ? undef : $result; return $ccureadings ? undef : $result;
} }
elsif ($opt eq 'datapoint') { elsif ($opt eq 'datapoint') {
@ -611,7 +611,7 @@ sub HMCCUDEV_Get ($@)
$objname = $ccuif.'.'.$ccuaddr.':'.$objname; $objname = $ccuif.'.'.$ccuaddr.':'.$objname;
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname); ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error"); HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
return $ccureadings ? undef : $result; return $ccureadings ? undef : $result;
@ -663,7 +663,7 @@ sub HMCCUDEV_Get ($@)
$par = '.*' if (!defined ($par)); $par = '.*' if (!defined ($par));
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par); my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error"); HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
return $ccureadings ? undef : $res; return $ccureadings ? undef : $res;
} }
@ -681,7 +681,7 @@ sub HMCCUDEV_Get ($@)
$par = '.*' if (!defined ($par)); $par = '.*' if (!defined ($par));
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par); my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error"); HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
return $res; return $res;
} }
@ -700,7 +700,7 @@ sub HMCCUDEV_Get ($@)
} }
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef); my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error"); HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
return $res; return $res;
} }

View File

@ -4,7 +4,7 @@
# #
# $Id$ # $Id$
# #
# Version 0.94 beta # Version 0.95 beta
# #
# Thread based RPC Server module for HMCCU. # Thread based RPC Server module for HMCCU.
# #
@ -40,7 +40,7 @@ use SetExtensions;
###################################################################### ######################################################################
# HMCCURPC version # HMCCURPC version
my $HMCCURPC_VERSION = '0.94 beta'; my $HMCCURPC_VERSION = '0.95 beta';
# Maximum number of events processed per call of Read() # Maximum number of events processed per call of Read()
my $HMCCURPC_MAX_EVENTS = 50; my $HMCCURPC_MAX_EVENTS = 50;
@ -162,7 +162,7 @@ sub HMCCURPC_StartRPCServer ($);
sub HMCCURPC_CleanupThreads ($$$); sub HMCCURPC_CleanupThreads ($$$);
sub HMCCURPC_CleanupThreadIO ($); sub HMCCURPC_CleanupThreadIO ($);
sub HMCCURPC_TerminateThreads ($$); sub HMCCURPC_TerminateThreads ($$);
sub HMCCURPC_CheckThreadState ($$$); sub HMCCURPC_CheckThreadState ($$$$);
sub HMCCURPC_IsRPCServerRunning ($); sub HMCCURPC_IsRPCServerRunning ($);
sub HMCCURPC_Housekeeping ($); sub HMCCURPC_Housekeeping ($);
sub HMCCURPC_StopRPCServer ($); sub HMCCURPC_StopRPCServer ($);
@ -234,7 +234,7 @@ sub HMCCURPC_Initialize ($)
$hash->{parseParams} = 1; $hash->{parseParams} = 1;
$hash->{AttrList} = "rpcInterfaces:multiple-strict,".join(',',sort keys %HMCCURPC_RPC_PORT). $hash->{AttrList} = "rpcInterfaces:multiple-strict,".join(',',sort keys %HMCCURPC_RPC_PORT).
" ccuflags:multiple-strict,expert rpcMaxEvents rpcQueueSize rpcTriggerTime". " ccuflags:multiple-strict,expert,keepThreads rpcMaxEvents rpcQueueSize rpcTriggerTime".
" rpcServer:on,off rpcServerAddr rpcServerPort rpcWriteTimeout rpcAcceptTimeout". " rpcServer:on,off rpcServerAddr rpcServerPort rpcWriteTimeout rpcAcceptTimeout".
" rpcConnTimeout rpcWaitTime rpcStatistics ". " rpcConnTimeout rpcWaitTime rpcStatistics ".
$readingFnAttributes; $readingFnAttributes;
@ -352,7 +352,7 @@ sub HMCCURPC_Attr ($@)
my $rc = 0; my $rc = 0;
if ($attrname eq 'rpcInterfaces') { if ($attrname eq 'rpcInterfaces') {
my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, 'running'); my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, 'running', undef);
return 'Stop RPC server before modifying rpcInterfaces' if ($run > 0); return 'Stop RPC server before modifying rpcInterfaces' if ($run > 0);
} }
@ -415,26 +415,26 @@ sub HMCCURPC_Set ($@)
return HMCCURPC_SetError ($hash, "RPC request failed") if (!defined ($response)); return HMCCURPC_SetError ($hash, "RPC request failed") if (!defined ($response));
my $result = ''; my $result = HMCCU_RefToString ($response);
if (ref ($response) eq 'ARRAY') { # if (ref ($response) eq 'ARRAY') {
$result = join "\n", @$response; # $result = join "\n", @$response;
} # }
elsif (ref ($response) eq 'HASH') { # elsif (ref ($response) eq 'HASH') {
foreach my $k (keys %$response) { # foreach my $k (keys %$response) {
$result .= "$k = ".$response->{$k}."\n"; # $result .= "$k = ".$response->{$k}."\n";
} # }
} # }
elsif (ref ($response) eq 'SCALAR') { # elsif (ref ($response) eq 'SCALAR') {
$result = $$response; # $result = $$response;
} # }
else { # else {
if (ref ($response)) { # if (ref ($response)) {
$result = "Unknown response from CCU of type ".ref ($response); # $result = "Unknown response from CCU of type ".ref ($response);
} # }
else { # else {
$result = ($response eq '') ? 'Request returned void' : $response; # $result = ($response eq '') ? 'Request returned void' : $response;
} # }
} # }
return $result; return $result;
} }
@ -470,6 +470,8 @@ sub HMCCURPC_Get ($@)
my ($hash, $a, $h) = @_; my ($hash, $a, $h) = @_;
my $name = shift @$a; my $name = shift @$a;
my $opt = shift @$a; my $opt = shift @$a;
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
my $options = "rpcevents:noArg rpcstate:noArg"; my $options = "rpcevents:noArg rpcstate:noArg";
if ($opt ne 'rpcstate' && HMCCURPC_IsRPCStateBlocking ($hash)) { if ($opt ne 'rpcstate' && HMCCURPC_IsRPCStateBlocking ($hash)) {
@ -866,8 +868,8 @@ sub HMCCURPC_ProcessEvent ($$)
if ($t[0] == $rh->{$clkey}{tid}) { if ($t[0] == $rh->{$clkey}{tid}) {
Log3 $name, 1, "HMCCURPC: Received SL event. RPC server $clkey enters server loop"; Log3 $name, 1, "HMCCURPC: Received SL event. RPC server $clkey enters server loop";
$rh->{$clkey}{state} = $clkey eq 'DATA' ? 'running' : 'working'; $rh->{$clkey}{state} = $clkey eq 'DATA' ? 'running' : 'working';
my ($run, $alld) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_DATA, "running"); my ($run, $alld) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_DATA, 'running', undef);
my ($work, $alls) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_SERVER, 'working'); my ($work, $alls) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_SERVER, 'working', undef);
if ($work == $alls && $run == $alld) { if ($work == $alls && $run == $alld) {
Log3 $name, 1, "HMCCURPC: All threads working"; Log3 $name, 1, "HMCCURPC: All threads working";
HMCCURPC_RegisterCallback ($hash); HMCCURPC_RegisterCallback ($hash);
@ -893,7 +895,7 @@ sub HMCCURPC_ProcessEvent ($$)
$rh->{$clkey}{state} = "running"; $rh->{$clkey}{state} = "running";
# Set binary RPC interfaces to 'running' if all ascii interfaces are in state 'running' # Set binary RPC interfaces to 'running' if all ascii interfaces are in state 'running'
my ($runa, $alla) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ASCII, 'running'); my ($runa, $alla) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ASCII, 'running', undef);
if ($runa == $alla) { if ($runa == $alla) {
foreach my $sn (keys %{$rh}) { foreach my $sn (keys %{$rh}) {
$rh->{$sn}{state} = "running" $rh->{$sn}{state} = "running"
@ -902,7 +904,7 @@ sub HMCCURPC_ProcessEvent ($$)
} }
# Check if all RPC servers were initialized. Set overall status # Check if all RPC servers were initialized. Set overall status
my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, 'running'); my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, 'running', undef);
if ($run == $all) { if ($run == $all) {
$hash->{hmccu}{rpcstarttime} = 0; $hash->{hmccu}{rpcstarttime} = 0;
HMCCURPC_SetRPCState ($hash, "running", "All RPC servers running"); HMCCURPC_SetRPCState ($hash, "running", "All RPC servers running");
@ -934,17 +936,16 @@ sub HMCCURPC_ProcessEvent ($$)
if ($clkey ne 'DATA') { if ($clkey ne 'DATA') {
($stopped, $all) = HMCCURPC_CleanupThreads ($hash, $HMCCURPC_THREAD_SERVER, 'stopped'); ($stopped, $all) = HMCCURPC_CleanupThreads ($hash, $HMCCURPC_THREAD_SERVER, 'stopped');
if ($stopped == $all) { if ($stopped == $all) {
# Terminate data processing thread # Terminate data processing thread if all server threads stopped
Log3 $name, 2, "HMCCURPC: All RPC servers stopped. Terminating data processing thread"; Log3 $name, 2, "HMCCURPC: All RPC servers stopped. Terminating data processing thread";
HMCCURPC_TerminateThreads ($hash, $HMCCURPC_THREAD_DATA); HMCCURPC_TerminateThreads ($hash, $HMCCURPC_THREAD_DATA);
sleep (1); sleep (1);
} }
} }
else { else {
# Vielleicht besser außerhalb von Read() löschen
HMCCURPC_CleanupThreadIO ($hash);
($stopped, $all) = HMCCURPC_CleanupThreads ($hash, $HMCCURPC_THREAD_DATA, '.*'); ($stopped, $all) = HMCCURPC_CleanupThreads ($hash, $HMCCURPC_THREAD_DATA, '.*');
if ($stopped == $all) { if ($stopped == $all) {
HMCCURPC_CleanupThreadIO ($hash);
HMCCURPC_ResetRPCState ($hash, "OK"); HMCCURPC_ResetRPCState ($hash, "OK");
RemoveInternalTimer ($hash); RemoveInternalTimer ($hash);
Log3 $name, 1, "HMCCURPC: All threads stopped"; Log3 $name, 1, "HMCCURPC: All threads stopped";
@ -1159,8 +1160,6 @@ sub HMCCURPC_DeRegisterCallback ($)
if (exists ($rpchash->{cburl}) && $rpchash->{cburl} ne '') { if (exists ($rpchash->{cburl}) && $rpchash->{cburl} ne '') {
Log3 $name, 1, "HMCCURPC: Deregistering RPC server ".$rpchash->{cburl}. Log3 $name, 1, "HMCCURPC: Deregistering RPC server ".$rpchash->{cburl}.
" with ID $clkey at ".$rpchash->{clurl}; " with ID $clkey at ".$rpchash->{clurl};
# my $rpcclient = RPC::XML::Client->new ($rpchash->{clurl});
# $rpcclient->send_request ("init", $rpchash->{cburl});
if (HMCCURPC_IsAscRPCPort ($rpchash->{port})) { if (HMCCURPC_IsAscRPCPort ($rpchash->{port})) {
HMCCURPC_SendRequest ($hash, $rpchash->{port}, "init", $rpchash->{cburl}); HMCCURPC_SendRequest ($hash, $rpchash->{port}, "init", $rpchash->{cburl});
} }
@ -1406,7 +1405,7 @@ sub HMCCURPC_StartRPCServer ($)
sleep (1); sleep (1);
# Cleanup if one or more threads are not initialized (ignore thread state) # Cleanup if one or more threads are not initialized (ignore thread state)
my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, '.*'); my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, '.*', undef);
if ($run != $all) { if ($run != $all) {
Log3 $name, 0, "HMCCURPC: Only $run from $all threads are running. Cleaning up"; Log3 $name, 0, "HMCCURPC: Only $run from $all threads are running. Cleaning up";
HMCCURPC_Housekeeping ($hash); HMCCURPC_Housekeeping ($hash);
@ -1439,8 +1438,8 @@ sub HMCCURPC_CleanupThreadIO ($)
my $pid = $$; my $pid = $$;
if (exists ($selectlist{"RPC.$name.$pid"})) { if (exists ($selectlist{"RPC.$name.$pid"})) {
Log3 $name, 2, "HMCCURPC: Stop I/O handling"; Log3 $name, 2, "HMCCURPC: Stop I/O handling";
delete $hash->{FD};
delete $selectlist{"RPC.$name.$pid"}; delete $selectlist{"RPC.$name.$pid"};
delete $hash->{FD} if (defined ($hash->{FD}));
} }
if (defined ($hash->{hmccu}{sockchild})) { if (defined ($hash->{hmccu}{sockchild})) {
Log3 $name, 2, "HMCCURPC: Close child socket"; Log3 $name, 2, "HMCCURPC: Close child socket";
@ -1498,6 +1497,8 @@ sub HMCCURPC_CleanupThreads ($$$)
my ($hash, $mode, $state) = @_; my ($hash, $mode, $state) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
my $count = 0; my $count = 0;
my $all = 0; my $all = 0;
@ -1515,9 +1516,15 @@ sub HMCCURPC_CleanupThreads ($$$)
" still running. Can't delete it"; " still running. Can't delete it";
next; next;
} }
if ($hash->{hmccu}{rpc}{$clkey}{state} eq 'stopped' && $ccuflags !~ /keepThreads/) {
Log3 $name, 2, "HMCCURPC: Thread $clkey with TID=".$thr->tid (). Log3 $name, 2, "HMCCURPC: Thread $clkey with TID=".$thr->tid ().
" has been stopped. Deleting it"; " has been stopped. Deleting it";
# undef $hash->{hmccu}{rpc}{$clkey}{child}; undef $hash->{hmccu}{rpc}{$clkey}{child};
}
else {
Log3 $name, 2, "HMCCURPC: Thread $clkey with TID=".$thr->tid ().
" is in state ".$hash->{hmccu}{rpc}{$clkey}{state}.". Can't delete it";
}
# delete $hash->{hmccu}{rpc}{$clkey}; # delete $hash->{hmccu}{rpc}{$clkey};
} }
} }
@ -1531,21 +1538,22 @@ sub HMCCURPC_CleanupThreads ($$$)
# Count threads in specified state. # Count threads in specified state.
# Parameter state is a regular expression. # Parameter state is a regular expression.
# Parameter mode specifies which threads should be counted: # Parameter mode specifies which threads should be counted:
# 1 - Count data processing thread
# 2 - Count server threads
# 3 - Count all threads
# If state is empty thread state is ignored and only running threads # If state is empty thread state is ignored and only running threads
# are counted by calling thread function is_running(). # are counted by calling thread function is_running().
# Return number of threads in specified state and total number of # Return number of threads in specified state and total number of
# threads. # threads. Also return IDs of running threads if parameter tids is
# defined and parameter state is 'running' or '.*'.
###################################################################### ######################################################################
sub HMCCURPC_CheckThreadState ($$$) sub HMCCURPC_CheckThreadState ($$$$)
{ {
my ($hash, $mode, $state) = @_; my ($hash, $mode, $state, $tids) = @_;
my $count = 0; my $count = 0;
my $all = 0; my $all = 0;
$mode = $HMCCURPC_THREAD_ALL if (!defined ($mode));
$state = '' if (!defined ($state));
foreach my $clkey (keys %{$hash->{hmccu}{rpc}}) { foreach my $clkey (keys %{$hash->{hmccu}{rpc}}) {
next if ($hash->{hmccu}{rpc}{$clkey}{state} eq 'inactive'); next if ($hash->{hmccu}{rpc}{$clkey}{state} eq 'inactive');
next if (!($hash->{hmccu}{rpc}{$clkey}{type} & $mode)); next if (!($hash->{hmccu}{rpc}{$clkey}{type} & $mode));
@ -1553,8 +1561,11 @@ sub HMCCURPC_CheckThreadState ($$$)
if ($state eq 'running' || $state eq '.*') { if ($state eq 'running' || $state eq '.*') {
next if (!exists ($hash->{hmccu}{rpc}{$clkey}{child})); next if (!exists ($hash->{hmccu}{rpc}{$clkey}{child}));
my $thr = $hash->{hmccu}{rpc}{$clkey}{child}; my $thr = $hash->{hmccu}{rpc}{$clkey}{child};
$count++ if (defined ($thr) && $thr->is_running () && if (defined ($thr) && $thr->is_running () &&
$hash->{hmccu}{rpc}{$clkey}{state} =~ /$state/); ($state eq '' || $hash->{hmccu}{rpc}{$clkey}{state} =~ /$state/)) {
$count++;
push (@$tids, $thr->tid()) if (defined ($tids));
}
} }
else { else {
$count++ if ($hash->{hmccu}{rpc}{$clkey}{state} =~ /$state/); $count++ if ($hash->{hmccu}{rpc}{$clkey}{state} =~ /$state/);
@ -1574,7 +1585,7 @@ sub HMCCURPC_IsRPCServerRunning ($)
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3 $name, 2, "HMCCURPC: Checking if all threads are running"; Log3 $name, 2, "HMCCURPC: Checking if all threads are running";
my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, 'running'); my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, 'running', undef);
if ($run != $all) { if ($run != $all) {
Log3 $name, 1, "HMCCURPC: Only $run of $all threads are running. Cleaning up"; Log3 $name, 1, "HMCCURPC: Only $run of $all threads are running. Cleaning up";
HMCCURPC_Housekeeping ($hash); HMCCURPC_Housekeeping ($hash);
@ -1597,7 +1608,10 @@ sub HMCCURPC_Housekeeping ($)
Log3 $name, 1, "HMCCURPC: Housekeeping called. Cleaning up RPC environment"; Log3 $name, 1, "HMCCURPC: Housekeeping called. Cleaning up RPC environment";
# I/O Handling beenden # Deregister callback URLs in CCU
HMCCURPC_DeRegisterCallback ($hash);
# Stop I/O handling
HMCCURPC_CleanupThreadIO ($hash); HMCCURPC_CleanupThreadIO ($hash);
my $count = HMCCURPC_TerminateThreads ($hash, $HMCCURPC_THREAD_ALL); my $count = HMCCURPC_TerminateThreads ($hash, $HMCCURPC_THREAD_ALL);
@ -1623,7 +1637,7 @@ sub HMCCURPC_StopRPCServer ($)
my ($hash) = @_; my ($hash) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, 'running'); my ($run, $all) = HMCCURPC_CheckThreadState ($hash, $HMCCURPC_THREAD_ALL, 'running', undef);
if ($run > 0) { if ($run > 0) {
HMCCURPC_SetRPCState ($hash, "stopping", "Found $run threads. Stopping ..."); HMCCURPC_SetRPCState ($hash, "stopping", "Found $run threads. Stopping ...");
@ -1889,6 +1903,8 @@ sub HMCCURPC_HandleConnection ($$$$)
foreach my $et (@eventtypes) { foreach my $et (@eventtypes) {
Log3 $name, 4, "CCURPC: $clkey event type = $et: ".$rpcsrv->{hmccu}{rec}{$et}; Log3 $name, 4, "CCURPC: $clkey event type = $et: ".$rpcsrv->{hmccu}{rec}{$et};
} }
return;
} }
###################################################################### ######################################################################
@ -2085,9 +2101,9 @@ sub HMCCURPC_HexDump ($$)
# Callback functions # Callback functions
###################################################################### ######################################################################
################################################## ######################################################################
# Callback for new devices # Callback for new devices
################################################## ######################################################################
sub HMCCURPC_NewDevicesCB ($$$) sub HMCCURPC_NewDevicesCB ($$$)
{ {
@ -2095,15 +2111,17 @@ sub HMCCURPC_NewDevicesCB ($$$)
my $name = $server->{hmccu}{name}; my $name = $server->{hmccu}{name};
my $devcount = scalar (@$a); my $devcount = scalar (@$a);
Log3 $name, 2, "CCURPC: $cb NewDevice received $devcount device specifications"; Log3 $name, 2, "CCURPC: $cb NewDevice received $devcount device and channel specifications";
foreach my $dev (@$a) { foreach my $dev (@$a) {
my $msg = ''; my $msg = '';
if ($dev->{ADDRESS} =~ /:[0-9]{1,2}$/) { if ($dev->{ADDRESS} =~ /:[0-9]{1,2}$/) {
$msg = "C|".$dev->{ADDRESS}."|".$dev->{TYPE}."|".$dev->{VERSION}."|null|null"; $msg = "C|".$dev->{ADDRESS}."|".$dev->{TYPE}."|".$dev->{VERSION}."|null|null";
} }
else { else {
# Wired devices do not have a RX_MODE attribute
my $rx = exists ($dev->{RX_MODE}) ? $dev->{RX_MODE} : 'null';
$msg = "D|".$dev->{ADDRESS}."|".$dev->{TYPE}."|".$dev->{VERSION}."|". $msg = "D|".$dev->{ADDRESS}."|".$dev->{TYPE}."|".$dev->{VERSION}."|".
$dev->{FIRMWARE}."|".$dev->{RX_MODE}; $dev->{FIRMWARE}."|".$rx;
} }
HMCCURPC_Write ($server, "ND", $cb, $msg); HMCCURPC_Write ($server, "ND", $cb, $msg);
} }
@ -2727,6 +2745,7 @@ sub HMCCURPC_DecodeResponse ($)
<li><b>ccuflags { expert }</b><br/> <li><b>ccuflags { expert }</b><br/>
Set flags for controlling device behaviour. Meaning of flags is:<br/> Set flags for controlling device behaviour. Meaning of flags is:<br/>
expert - Activate expert mode<br/> expert - Activate expert mode<br/>
keepThreads - Do not delete thread objects after RPC server has been stopped<br/>
</li><br/> </li><br/>
<li><b>rpcAcceptTimeout &lt;seconds&gt;</b><br/> <li><b>rpcAcceptTimeout &lt;seconds&gt;</b><br/>
Specify timeout for accepting incoming connections. Default is 1 second. Increase this Specify timeout for accepting incoming connections. Default is 1 second. Increase this

View File

@ -4,17 +4,19 @@
# #
# $Id$ # $Id$
# #
# Version 4.0 # Version 4.0.001
# #
# Configuration parameters for Homematic devices. # Configuration parameters for HomeMatic devices.
# #
# (c) 2016 zap (zap01 <at> t-online <dot> de) # (c) 2017 by zap (zap01 <at> t-online <dot> de)
# #
# Datapoints LOWBAT, LOW_BAT, UNREACH, ERROR.*, SABOTAGE and FAULT.* must # Datapoints LOWBAT, LOW_BAT, UNREACH, ERROR.*, SABOTAGE and FAULT.*
# not be specified in ccureadingfilter. They are always stored as readings. # must not be specified in attribute ccureadingfilter. They are always
# stored as readings.
# Datapoints LOWBAT, LOW_BAT and UNREACH must not be specified in # Datapoints LOWBAT, LOW_BAT and UNREACH must not be specified in
# substitute because they are substituted by default. # attribute substitute because they are substituted by default.
# See attributes ccudef-readingname and ccudef-substitute in module HMCCU. # See also documentation of attributes ccudef-readingname and
# ccudef-substitute in module HMCCU.
# #
######################################################################### #########################################################################
@ -135,6 +137,14 @@ use vars qw(%HMCCU_SCRIPTS);
statevals => "press:true", statevals => "press:true",
substitute => "PRESS_SHORT,PRESS_LONG,PRESS_CONT!(1|true):pressed,(0|false):released;PRESS_LONG_RELEASE!(0|false):no,(1|true):yes" substitute => "PRESS_SHORT,PRESS_LONG,PRESS_CONT!(1|true):pressed,(0|false):released;PRESS_LONG_RELEASE!(0|false):no,(1|true):yes"
}, },
"HM-SwI-3-FM" => {
_description => "Funk-Schalterschnittstelle",
_channels => "1,2,3",
ccureadingfilter => "PRESS",
statedatapoint => "PRESS",
statevals => "press:true",
substitute => "PRESS!(1|true):pressed,(0|false):released"
},
"HM-LC-Sw1PBU-FM" => { "HM-LC-Sw1PBU-FM" => {
_description => "Unterputz Schaltaktor für Markenschalter", _description => "Unterputz Schaltaktor für Markenschalter",
_channels => "1", _channels => "1",
@ -154,6 +164,14 @@ use vars qw(%HMCCU_SCRIPTS);
statevals => "on:true,off:false", statevals => "on:true,off:false",
substitute => "STATE!(1|true):on,(0|false):off" substitute => "STATE!(1|true):on,(0|false):off"
}, },
"HM-MOD-Re-8" => {
_description => "8 Kanal Empfangsmodul",
_channels => "1,2,3,4,5,6,7,8",
ccureadingfilter => "(STATE|WORKING)",
statedatapoint => "STATE",
statevals => "on:true,off:false",
substitute => "STATE!(1|true):on,(0|false):off;WORKING!(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" => { "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", _description => "1 Kanal Funk-Schaltaktor",
_channels => "1", _channels => "1",
@ -200,6 +218,14 @@ use vars qw(%HMCCU_SCRIPTS);
statedatapoint => "TEMPERATURE", statedatapoint => "TEMPERATURE",
stripnumber => 1 stripnumber => 1
}, },
"HM-WDS100-C6-O-2" => {
_description => "Funk-Kombisensor",
_channels => "1",
ccureadingfilter => "(HUMIDITY|TEMPERATURE|WIND|RAIN|SUNSHINE|BRIGHTNESS)",
statedatapoint => "TEMPERATURE",
stripnumber => 1,
substitute => "RAINING!(1|true):yes,(0|false):no"
},
"HM-Sec-MD|HM-Sec-MDIR|HM-Sec-MDIR-2|HM-Sec-MDIR-3" => { "HM-Sec-MD|HM-Sec-MDIR|HM-Sec-MDIR-2|HM-Sec-MDIR-3" => {
_description => "Bewegungsmelder", _description => "Bewegungsmelder",
_channels => "1", _channels => "1",
@ -421,6 +447,12 @@ use vars qw(%HMCCU_SCRIPTS);
ccureadingfilter => "PRESS", ccureadingfilter => "PRESS",
substitute => "PRESS_SHORT,PRESS_LONG,PRESS_CONT!(1|true):pressed,(0|false):released;PRESS_LONG_RELEASE!(0|false):no,(1|true):yes" substitute => "PRESS_SHORT,PRESS_LONG,PRESS_CONT!(1|true):pressed,(0|false):released;PRESS_LONG_RELEASE!(0|false):no,(1|true):yes"
}, },
"HM-SwI-3-FM" => {
_description => "Funk-Schalterschnittstelle",
ccureadingfilter => "PRESS",
statevals => "press:true",
substitute => "PRESS!(1|true):pressed,(0|false):released"
},
"HM-LC-Sw1PBU-FM" => { "HM-LC-Sw1PBU-FM" => {
_description => "Unterputz Schaltaktor für Markenschalter", _description => "Unterputz Schaltaktor für Markenschalter",
ccureadingfilter => "STATE", ccureadingfilter => "STATE",
@ -437,6 +469,12 @@ use vars qw(%HMCCU_SCRIPTS);
statevals => "on:true,off:false", statevals => "on:true,off:false",
substitute => "STATE!(1|true):on,(0|false):off" substitute => "STATE!(1|true):on,(0|false):off"
}, },
"HM-MOD-Re-8" => {
_description => "8 Kanal Empfangsmodul",
ccureadingfilter => "(STATE|WORKING)",
statevals => "on:true,off:false",
substitute => "STATE!(1|true):on,(0|false):off;WORKING!(1|true):yes,(0|false):no"
},
"HM-LC-Bl1PBU-FM|HM-LC-Bl1-FM|HM-LC-Bl1-SM|HM-LC-BlX|HM-LC-Bl1-SM-2|HM-LC-Bl1-FM-2" => { "HM-LC-Bl1PBU-FM|HM-LC-Bl1-FM|HM-LC-Bl1-SM|HM-LC-BlX|HM-LC-Bl1-SM-2|HM-LC-Bl1-FM-2" => {
_description => "Jalousienaktor", _description => "Jalousienaktor",
ccureadingfilter => "(LEVEL|INHIBIT|DIRECTION|WORKING)", ccureadingfilter => "(LEVEL|INHIBIT|DIRECTION|WORKING)",
@ -508,6 +546,13 @@ use vars qw(%HMCCU_SCRIPTS);
statedatapoint => "1.TEMPERATURE", statedatapoint => "1.TEMPERATURE",
stripnumber => 1 stripnumber => 1
}, },
"HM-WDS100-C6-O-2" => {
_description => "Funk-Kombisensor",
ccureadingfilter => "(HUMIDITY|TEMPERATURE|WIND|RAIN|SUNSHINE|BRIGHTNESS)",
statedatapoint => "1.TEMPERATURE",
stripnumber => 1,
substitute => "RAINING!(1|true):yes,(0|false):no"
},
"HM-ES-TX-WM" => { "HM-ES-TX-WM" => {
_description => "Energiezaehler Sensor", _description => "Energiezaehler Sensor",
ccureadingfilter => "(ENERGY_COUNTER|POWER)" ccureadingfilter => "(ENERGY_COUNTER|POWER)"
@ -640,60 +685,259 @@ use vars qw(%HMCCU_SCRIPTS);
); );
###################################################################### ######################################################################
# Homematic scripts # Homematic scripts.
# Scripts can be executed via HMCCU set command 'hmscript'. Script
# name must be preceeded by a '!'.
# Example:
# set mydev hmscript !CreateStringVariable MyVar test "Test variable"
###################################################################### ######################################################################
%HMCCU_SCRIPTS = ( %HMCCU_SCRIPTS = (
"CreateVariable" => { "ActivateProgram" => {
_description => "Create CCU system variable of type STRING, NUMBER, BOOL or LIST", description => "Activate or deactivate a CCU program",
_pardesc => "Type, Name, Unit, Init, Desc [, { Min, Max | Val1, Val2 | ValList } ]", syntax => "name, mode",
parameters => 2,
code => qq(
object oPR = dom.GetObject("\$name");
if (oPR) {
oPR.Active(\$mode);
}
)
},
"CreateStringVariable" => {
description => "Create CCU system variable of type STRING",
syntax => "name, init, desc",
parameters => 3,
code => qq(
object oSV = dom.GetObject("\$name");
if (!oSV){
object oSysVars = dom.GetObject(ID_SYSTEM_VARIABLES);
oSV = dom.CreateObject(OT_VARDP);
oSysVars.Add(svObj.ID());
oSV.Name("\$name");
oSV.ValueType(ivtString);
oSV.ValueSubType(istChar8859);
oSV.DPInfo("\$desc");
oSV.ValueUnit("");
oSV.State("\$init");
oSV.Internal(false);
oSV.Visible(true);
dom.RTUpdate(false);
}
else {
oSV.State("\$init");
}
)
},
"CreateNumericVariable" => {
description => "Create CCU system variable of type FLOAT",
syntax => "name, unit, init, desc, min, max",
parameters => 6, parameters => 6,
code => qq( code => qq(
object oSV = dom.GetObject("\$name");
if (!oSV){
object oSysVars = dom.GetObject(ID_SYSTEM_VARIABLES);
oSV = dom.CreateObject(OT_VARDP);
oSysVars.Add(svObj.ID());
oSV.Name("\$name");
oSV.ValueType(ivtFloat);
oSV.ValueSubType(istGeneric);
oSV.ValueMin(\$min);
oSV.ValueMax(\$max);
oSV.DPInfo("\$desc");
oSV.ValueUnit("\$unit");
oSV.State("\$init");
oSV.Internal(false);
oSV.Visible(true);
dom.RTUpdate(false);
}
else {
oSV.State("\$init");
}
)
},
"CreateBoolVariable" => {
description => "Create CCU system variable of type BOOL",
syntax => "name, init, desc, value1, value2",
parameters => 5,
code => qq(
object oSV = dom.GetObject("\$name");
if (!oSV){
object oSysVars = dom.GetObject(ID_SYSTEM_VARIABLES);
oSV = dom.CreateObject(OT_VARDP);
oSysVars.Add(svObj.ID());
oSV.Name("\$name");
oSV.ValueType(ivtBinary);
oSV.ValueSubType(istBool);
oSV.ValueName0("\$value1");
oSV.ValueName1("\$value2");
oSV.DPInfo("\$desc");
oSV.State("\$init");
dom.RTUpdate(false);
}
else {
oSV.State("\$init");
}
)
},
"CreateListVariable" => {
description => "Create CCU system variable of type LIST",
syntax => "name, unit, init, desc, list",
parameters => 5,
code => qq(
object oSV = dom.GetObject("p2"); object oSV = dom.GetObject("p2");
if (!oSV){ if (!oSV){
object oSysVars = dom.GetObject(ID_SYSTEM_VARIABLES); object oSysVars = dom.GetObject(ID_SYSTEM_VARIABLES);
oSV = dom.CreateObject(OT_VARDP); oSV = dom.CreateObject(OT_VARDP);
oSysVars.Add(svObj.ID()); oSysVars.Add(svObj.ID());
oSV.Name("p2"); oSV.Name("\$name");
if ("p1" = "STRING") {
oSV.ValueType(ivtString);
oSV.ValueSubType(istChar8859);
}
if ("p1" = "NUMBER") {
oSV.ValueType(ivtFloat);
oSV.ValueSubType(istGeneric);
oSV.ValueMin(p6);
oSV.ValueMax(p7);
}
if ("p1" = "BOOL") {
oSV.ValueType(ivtBinary);
oSV.ValueSubType(istBool);
oSV.ValueName0("p6");
oSV.ValueName1("p7");
}
if ("p1" = "LIST") {
oSV.ValueType(ivtInteger); oSV.ValueType(ivtInteger);
oSV.ValueSubType(istEnum); oSV.ValueSubType(istEnum);
oSV.ValueList("p6"); oSV.ValueList("\$list");
} oSV.DPInfo("\$desc");
oSV.DPInfo("p5"); oSV.ValueUnit("\$unit");
oSV.ValueUnit("p3"); oSV.State("\$init");
oSV.State("p4");
oSV.Internal(false);
oSV.Visible(true);
dom.RTUpdate(false); dom.RTUpdate(false);
}
else {
oSV.State("\$init");
} }
) )
}, },
"DeleteVariable" => { "DeleteVariable" => {
_description => "Delete CCU system variable", description => "Delete CCU system variable",
syntax => "name",
parameters => 1, parameters => 1,
code => qq( code => qq(
object oSV = dom.GetObject("p1"); object oSV = dom.GetObject("\$name");
if (oSV) { if (oSV) {
dom.DeleteObject(oSV.ID()); dom.DeleteObject(oSV.ID());
} }
) )
},
"GetVariables" => {
description => "Query system variables",
syntax => "",
parameters => 0,
code => qq(
object osysvar;
string ssysvarid;
foreach (ssysvarid, dom.GetObject(ID_SYSTEM_VARIABLES).EnumUsedIDs())
{
osysvar = dom.GetObject(ssysvarid);
WriteLine (osysvar.Name() # "=" # osysvar.Variable() # "=" # osysvar.Value());
}
)
},
"GetDeviceInfo" => {
description => "Query device info",
syntax => "devname, ccuget",
parameters => 2,
code => qq(
string chnid;
string sDPId;
object odev = dom.GetObject ("\$devname");
if (odev) {
foreach (chnid, odev.Channels()) {
object ochn = dom.GetObject(chnid);
if (ochn) {
foreach(sDPId, ochn.DPs()) {
object oDP = dom.GetObject(sDPId);
if (oDP) {
integer op = oDP.Operations();
string flags = "";
if (OPERATION_READ & op) { flags = flags # "R"; }
if (OPERATION_WRITE & op) { flags = flags # "W"; }
if (OPERATION_EVENT & op) { flags = flags # "E"; }
WriteLine ("C;" # ochn.Address() # ";" # ochn.Name() # ";" # oDP.Name() # ";" # oDP.ValueType() # ";" # oDP.\$ccuget() # ";" # flags);
}
}
}
}
}
else {
WriteLine ("ERROR: Device not found");
}
)
},
"GetDeviceList" => {
description => "Query CCU devices and channels",
syntax => "",
parameters => 0,
code => qq(
string devid;
string chnid;
foreach(devid, root.Devices().EnumUsedIDs()) {
object odev=dom.GetObject(devid);
string intid=odev.Interface();
string intna=dom.GetObject(intid).Name();
integer cc=0;
foreach (chnid, odev.Channels()) {
object ochn=dom.GetObject(chnid);
WriteLine("C;" # ochn.Address() # ";" # ochn.Name());
cc=cc+1;
}
WriteLine("D;" # intna # ";" # odev.Address() # ";" # odev.Name() # ";" # odev.HssType() # ";" # cc);
}
)
},
"GetDatapointsByChannel" => {
description => "Query datapoints of channel list",
syntax => "list, ccuget",
parameters => 2,
code => qq(
string sDPId;
string sChnName;
string sChnList = "\$list";
integer c = 0;
foreach (sChnName, sChnList.Split(",")) {
object oChannel = dom.GetObject (sChnName);
if (oChannel) {
foreach(sDPId, oChannel.DPs()) {
object oDP = dom.GetObject(sDPId);
if (oDP) {
if (OPERATION_READ & oDP.Operations()) {
WriteLine (sChnName # "=" # oDP.Name() # "=" # oDP.\$ccuget());
c = c+1;
}
}
}
}
}
WriteLine (c);
)
},
"GetDatapointsByDevice" => {
description => "Query datapoints of device list",
syntax => "list, ccuget",
parameters => 2,
code => qq(
string chnid;
string sDPId;
string sDevName;
string sDevList = "\$list";
integer c = 0;
foreach (sDevName, sDevList.Split(",")) {
object odev = dom.GetObject (sDevName);
if (odev) {
foreach (chnid, odev.Channels()) {
object ochn = dom.GetObject(chnid);
if (ochn) {
foreach(sDPId, ochn.DPs()) {
object oDP = dom.GetObject(sDPId);
if (oDP) {
if (OPERATION_READ & oDP.Operations()) {
WriteLine (ochn.Name() # "=" # oDP.Name() # "=" # oDP.\$ccuget());
c = c+1;
}
}
}
}
}
}
}
WriteLine (c);
)
} }
); );