2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-05-02 19:15:31 +00:00

HMCCU: Features and bugfixes

git-svn-id: https://svn.fhem.de/fhem/trunk@28502 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
zap 2024-02-11 16:56:41 +00:00
parent 9e2cef7b10
commit 9f82e0ec40
5 changed files with 570 additions and 578 deletions

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
# #
# Version 5.0 # Version 5.0
# #
# (c) 2022 zap (zap01 <at> t-online <dot> de) # (c) 2024 zap (zap01 <at> t-online <dot> de)
# #
###################################################################### ######################################################################
# Client device for Homematic channels. # Client device for Homematic channels.
@ -30,7 +30,7 @@ sub HMCCUCHN_Set ($@);
sub HMCCUCHN_Get ($@); sub HMCCUCHN_Get ($@);
sub HMCCUCHN_Attr ($@); sub HMCCUCHN_Attr ($@);
my $HMCCUCHN_VERSION = '5.0 240121821'; my $HMCCUCHN_VERSION = '5.0 2024-02';
###################################################################### ######################################################################
# Initialize module # Initialize module
@ -51,7 +51,7 @@ sub HMCCUCHN_Initialize ($)
$hash->{parseParams} = 1; $hash->{parseParams} = 1;
$hash->{AttrList} = 'IODev ccucalculate '. $hash->{AttrList} = 'IODev ccucalculate '.
'ccuflags:multiple-strict,hideStdReadings,replaceStdReadings,noBoundsChecking,ackState,logCommand,noAutoSubstitute,noReadings,trace,simulate,showMasterReadings,showLinkReadings,showDeviceReadings,showServiceReadings '. 'ccuflags:multiple-strict,hideStdReadings,replaceStdReadings,noBoundsChecking,ackState,logCommand,noAutoSubstitute,noReadings,trace,simulate,showMasterReadings,showLinkReadings,showDeviceReadings '.
'ccureadingfilter:textField-long statedatapoint controldatapoint '. 'ccureadingfilter:textField-long statedatapoint controldatapoint '.
'ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc '. 'ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc '.
'ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix '. 'ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix '.
@ -212,7 +212,7 @@ sub HMCCUCHN_InitDevice ($$)
$rc = -2; $rc = -2;
} }
HMCCU_GetUpdate ($devHash, $da); HMCCU_ExecuteGetExtValuesCommand ($devHash, $da);
} }
return $rc; return $rc;
@ -352,9 +352,6 @@ sub HMCCUCHN_Set ($@)
elsif ($lcopt eq 'datapoint') { elsif ($lcopt eq 'datapoint') {
return HMCCU_ExecuteSetDatapointCommand ($hash, $a, $h); return HMCCU_ExecuteSetDatapointCommand ($hash, $a, $h);
} }
# elsif ($lcopt eq 'toggle') {
# return HMCCU_ExecuteToggleCommand ($hash);
# }
elsif (exists($hash->{hmccu}{roleCmds}{set}{$opt})) { elsif (exists($hash->{hmccu}{roleCmds}{set}{$opt})) {
return HMCCU_ExecuteRoleCommand ($ioHash, $hash, 'set', $opt, $a, $h); return HMCCU_ExecuteRoleCommand ($ioHash, $hash, 'set', $opt, $a, $h);
} }
@ -455,7 +452,7 @@ sub HMCCUCHN_Get ($@)
} }
elsif ($lcopt eq 'extvalues') { elsif ($lcopt eq 'extvalues') {
my $filter = shift @$a; my $filter = shift @$a;
my $rc = HMCCU_GetUpdate ($hash, $ccuaddr, $filter); my $rc = HMCCU_ExecuteGetExtValuesCommand ($hash, $ccuaddr, $filter);
return $rc < 0 ? HMCCU_SetError ($hash, $rc) : 'OK'; return $rc < 0 ? HMCCU_SetError ($hash, $rc) : 'OK';
} }
elsif ($lcopt eq 'paramsetdesc') { elsif ($lcopt eq 'paramsetdesc') {
@ -526,6 +523,9 @@ sub HMCCUCHN_Get ($@)
<li><b>set &lt;name&gt; armState {DISARMED|EXTSENS_ARMED|ALLSENS_ARMED|ALARM_BLOCKED}</b><br/> <li><b>set &lt;name&gt; armState {DISARMED|EXTSENS_ARMED|ALLSENS_ARMED|ALARM_BLOCKED}</b><br/>
[alarm siren] Set arm state. [alarm siren] Set arm state.
</li><br/> </li><br/>
<li><b>set &lt;name&gt; calibrate {START|STOP}</b><br/>
[blind] Run calibration.
</li><br/>
<li><b>set &lt;name&gt; clear [&lt;reading-exp&gt;|reset]</b><br/> <li><b>set &lt;name&gt; clear [&lt;reading-exp&gt;|reset]</b><br/>
Delete readings matching specified reading name expression. Default expression is '.*'. Delete readings matching specified reading name expression. Default expression is '.*'.
Readings 'state' and 'control' are not deleted. With option 'reset' all readings Readings 'state' and 'control' are not deleted. With option 'reset' all readings
@ -687,7 +687,6 @@ sub HMCCUCHN_Get ($@)
<li>showMasterReadings: Store configuration readings of parameter set 'MASTER' of current channel.</li> <li>showMasterReadings: Store configuration readings of parameter set 'MASTER' of current channel.</li>
<li>showDeviceReadings: Store configuration readings of device and value readings of channel 0.</li> <li>showDeviceReadings: Store configuration readings of device and value readings of channel 0.</li>
<li>showLinkReadings: Store readings of links.</li> <li>showLinkReadings: Store readings of links.</li>
<li>showServiceReadings: Store readings of parameter set 'SERVICE'</li>
</ul> </ul>
If non of the flags is set, only readings belonging to parameter set VALUES (datapoints) If non of the flags is set, only readings belonging to parameter set VALUES (datapoints)
are stored. are stored.
@ -776,7 +775,6 @@ sub HMCCUCHN_Get ($@)
showDeviceReadings: Show readings of device and channel 0.<br/> showDeviceReadings: Show readings of device and channel 0.<br/>
showLinkReadings: Show link readings.<br/> showLinkReadings: Show link readings.<br/>
showMasterReadings: Show configuration readings.<br/> showMasterReadings: Show configuration readings.<br/>
showServiceReadings: Show service readings (HmIP only)<br/>
trace: Write log file information for operations related to this device. trace: Write log file information for operations related to this device.
</li><br/> </li><br/>
<a name="ccuget"></a> <a name="ccuget"></a>
@ -855,7 +853,6 @@ sub HMCCUCHN_Get ($@)
MASTER (configuration parameters): 'R-'<br/> MASTER (configuration parameters): 'R-'<br/>
LINK (links parameters): 'L-'<br/> LINK (links parameters): 'L-'<br/>
PEER (peering parameters): 'P-'<br/> PEER (peering parameters): 'P-'<br/>
SERVICE (service parameters): S-<br/>
To hide prefix do not specify <i>prefix</i>. To hide prefix do not specify <i>prefix</i>.
</li><br/> </li><br/>
<a name="ccuscaleval"></a> <a name="ccuscaleval"></a>

View File

@ -6,7 +6,7 @@
# #
# Version 5.0 # Version 5.0
# #
# (c) 2022 zap (zap01 <at> t-online <dot> de) # (c) 2024 zap (zap01 <at> t-online <dot> de)
# #
###################################################################### ######################################################################
# Client device for Homematic devices. # Client device for Homematic devices.
@ -31,7 +31,7 @@ sub HMCCUDEV_Set ($@);
sub HMCCUDEV_Get ($@); sub HMCCUDEV_Get ($@);
sub HMCCUDEV_Attr ($@); sub HMCCUDEV_Attr ($@);
my $HMCCUDEV_VERSION = '5.0 240121821'; my $HMCCUDEV_VERSION = '5.0 2024-02';
###################################################################### ######################################################################
# Initialize module # Initialize module
@ -52,7 +52,7 @@ sub HMCCUDEV_Initialize ($)
$hash->{parseParams} = 1; $hash->{parseParams} = 1;
$hash->{AttrList} = 'IODev ccuaggregate:textField-long ccucalculate:textField-long '. $hash->{AttrList} = 'IODev ccuaggregate:textField-long ccucalculate:textField-long '.
'ccuflags:multiple-strict,ackState,hideStdReadings,replaceStdReadings,noAutoSubstitute,noBoundsChecking,logCommand,noReadings,trace,simulate,showMasterReadings,showLinkReadings,showDeviceReadings,showServiceReadings '. 'ccuflags:multiple-strict,ackState,hideStdReadings,replaceStdReadings,noAutoSubstitute,noBoundsChecking,logCommand,noReadings,trace,simulate,showMasterReadings,showLinkReadings,showDeviceReadings '.
'ccureadingfilter:textField-long '. 'ccureadingfilter:textField-long '.
'ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc '. 'ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc '.
'ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix devStateFlags '. 'ccureadingname:textField-long ccuSetOnChange ccuReadingPrefix devStateFlags '.
@ -259,7 +259,7 @@ sub HMCCUDEV_InitDevice ($$)
} }
# Update readings # Update readings
HMCCU_GetUpdate ($devHash, $da); HMCCU_ExecuteGetExtValuesCommand ($devHash, $da);
} }
# Parse group options # Parse group options
@ -437,9 +437,6 @@ sub HMCCUDEV_Set ($@)
elsif ($lcopt eq 'datapoint') { elsif ($lcopt eq 'datapoint') {
return HMCCU_ExecuteSetDatapointCommand ($hash, $a, $h); return HMCCU_ExecuteSetDatapointCommand ($hash, $a, $h);
} }
# elsif ($lcopt eq 'toggle') {
# return HMCCU_ExecuteToggleCommand ($hash);
# }
elsif (exists($hash->{hmccu}{roleCmds}{set}{$opt})) { elsif (exists($hash->{hmccu}{roleCmds}{set}{$opt})) {
return HMCCU_ExecuteRoleCommand ($ioHash, $hash, 'set', $opt, $a, $h); return HMCCU_ExecuteRoleCommand ($ioHash, $hash, 'set', $opt, $a, $h);
} }
@ -541,7 +538,7 @@ sub HMCCUDEV_Get ($@)
} }
elsif ($lcopt eq 'extvalues') { elsif ($lcopt eq 'extvalues') {
my $filter = shift @$a; my $filter = shift @$a;
my $rc = HMCCU_GetUpdate ($hash, $ccuaddr, $filter); my $rc = HMCCU_ExecuteGetExtValuesCommand ($hash, $ccuaddr, $filter);
return $rc < 0 ? HMCCU_SetError ($hash, $rc) : 'OK'; return $rc < 0 ? HMCCU_SetError ($hash, $rc) : 'OK';
} }
elsif ($lcopt eq 'paramsetdesc') { elsif ($lcopt eq 'paramsetdesc') {

View File

@ -8,7 +8,7 @@
# #
# Subprocess based RPC Server module for HMCCU. # Subprocess based RPC Server module for HMCCU.
# #
# (c) 2023 by zap (zap01 <at> t-online <dot> de) # (c) 2024 by zap (zap01 <at> t-online <dot> de)
# #
############################################################################## ##############################################################################
# #
@ -31,15 +31,12 @@ use RPC::XML::Client;
use RPC::XML::Server; use RPC::XML::Server;
use SetExtensions; use SetExtensions;
# require "$attr{global}{modpath}/FHEM/88_HMCCU.pm";
###################################################################### ######################################################################
# Constants # Constants
###################################################################### ######################################################################
# HMCCURPC version # HMCCURPC version
my $HMCCURPCPROC_VERSION = '5.0 240121821'; my $HMCCURPCPROC_VERSION = '5.0 2024-02';
# Maximum number of events processed per call of Read() # Maximum number of events processed per call of Read()
my $HMCCURPCPROC_MAX_EVENTS = 100; my $HMCCURPCPROC_MAX_EVENTS = 100;
@ -89,6 +86,9 @@ my $HMCCURPCPROC_INIT_INTERVAL2 = 30;
# Delay for RPC server functionality check after start in seconds # Delay for RPC server functionality check after start in seconds
my $HMCCURPCPROC_INIT_INTERVAL3 = 25; my $HMCCURPCPROC_INIT_INTERVAL3 = 25;
# Interval for checking status of parent (FHEM) process in seconds
my $HMCCURPCPROC_PARENT_CHECK_INTERVAL = 5;
my %HMCCURPCPROC_RPC_FLAGS = ( my %HMCCURPCPROC_RPC_FLAGS = (
'BidCos-Wired' => '_', 'BidCos-RF' => 'multicalls', 'HmIP-RF' => '_', 'BidCos-Wired' => '_', 'BidCos-RF' => 'multicalls', 'HmIP-RF' => '_',
'VirtualDevices' => '_', 'Homegear' => '_', 'CUxD' => '_', 'VirtualDevices' => '_', 'Homegear' => '_', 'CUxD' => '_',
@ -133,7 +133,8 @@ my %RPC_METHODS = (
'getValue' => [ 'STRING', 'STRING' ] 'getValue' => [ 'STRING', 'STRING' ]
); );
# RPC event types # RPC event types:
#
# EV = Event # EV = Event
# ND = New device # ND = New device
# DD = Delete device # DD = Delete device
@ -142,7 +143,7 @@ my %RPC_METHODS = (
# UD = Update device # UD = Update device
# IN = Init RPC connection # IN = Init RPC connection
# EX = Exit RPC process # EX = Exit RPC process
# SL = Server loop # SL = Server loop (server is accepting connections)
# ST = Statistics (not in list of event types) # ST = Statistics (not in list of event types)
# TO = Timeout # TO = Timeout
my @RPC_EVENT_TYPES = ('EV', 'ND', 'DD', 'RD', 'RA', 'UD', 'IN', 'EX', 'SL', 'TO'); my @RPC_EVENT_TYPES = ('EV', 'ND', 'DD', 'RD', 'RA', 'UD', 'IN', 'EX', 'SL', 'TO');
@ -211,7 +212,7 @@ sub HMCCURPCPROC_HandleConnection ($$$$);
sub HMCCURPCPROC_SendQueue ($$$$); sub HMCCURPCPROC_SendQueue ($$$$);
sub HMCCURPCPROC_SendData ($$); sub HMCCURPCPROC_SendData ($$);
sub HMCCURPCPROC_ReceiveData ($$); sub HMCCURPCPROC_ReceiveData ($$);
sub HMCCURPCPROC_ReadFromSocket ($$$); sub HMCCURPCPROC_ReadFromSocket ($$);
sub HMCCURPCPROC_DataAvailableOnSocket ($$); sub HMCCURPCPROC_DataAvailableOnSocket ($$);
sub HMCCURPCPROC_WriteToSocket ($$$); sub HMCCURPCPROC_WriteToSocket ($$$);
sub HMCCURPCPROC_Write ($$$$); sub HMCCURPCPROC_Write ($$$$);
@ -569,7 +570,7 @@ sub HMCCURPCPROC_Shutdown ($)
} }
###################################################################### ######################################################################
# Set attribute # Set/delete attribute
###################################################################### ######################################################################
sub HMCCURPCPROC_Attr ($@) sub HMCCURPCPROC_Attr ($@)
@ -577,12 +578,15 @@ sub HMCCURPCPROC_Attr ($@)
my ($cmd, $name, $attrname, $attrval) = @_; my ($cmd, $name, $attrname, $attrval) = @_;
my $hash = $defs{$name}; my $hash = $defs{$name};
my $ioHash = $hash->{IODev}; my $ioHash = $hash->{IODev};
my $restartRPC = 0;
if ($cmd eq 'set') { if ($cmd eq 'set') {
if ($attrname =~ /^(rpcAcceptTimeout|rpcReadTimeout|rpcWriteTimeout)$/ && $attrval == 0) { if ($attrname =~ /^(rpcAcceptTimeout|rpcReadTimeout|rpcWriteTimeout)$/ && $attrval == 0) {
$restartRPC = 1;
return "HMCCURPCPROC: [$name] Value for attribute $attrname must be greater than 0"; return "HMCCURPCPROC: [$name] Value for attribute $attrname must be greater than 0";
} }
elsif ($attrname eq 'rpcServerAddr') { elsif ($attrname eq 'rpcServerAddr') {
$restartRPC = 1;
$hash->{hmccu}{localaddr} = $attrval; $hash->{hmccu}{localaddr} = $attrval;
} }
elsif ($attrname eq 'rpcPingCCU') { elsif ($attrname eq 'rpcPingCCU') {
@ -594,12 +598,13 @@ sub HMCCURPCPROC_Attr ($@)
} }
elsif ($cmd eq 'del') { elsif ($cmd eq 'del') {
if ($attrname eq 'rpcServerAddr') { if ($attrname eq 'rpcServerAddr') {
$restartRPC = 1;
$hash->{hmccu}{localaddr} = $hash->{hmccu}{defaultaddr}; $hash->{hmccu}{localaddr} = $hash->{hmccu}{defaultaddr};
} }
} }
HMCCU_LogDisplay ($hash, 2, 'Please restart RPC server to apply attribute changes') HMCCU_LogDisplay ($hash, 2, 'Please restart RPC server to apply attribute changes')
if ($init_done && (!defined($ioHash) || $ioHash->{hmccu}{postInit} == 0) && if ($restartRPC && $init_done && (!defined($ioHash) || $ioHash->{hmccu}{postInit} == 0) &&
HMCCURPCPROC_CheckProcessState ($hash, 'running')); HMCCURPCPROC_CheckProcessState ($hash, 'running'));
return undef; return undef;
@ -764,7 +769,6 @@ sub HMCCURPCPROC_Get ($@)
$result .= "$eh->{$i}{k} / $dn : $eh->{$i}{v}\n"; $result .= "$eh->{$i}{k} / $dn : $eh->{$i}{v}\n";
} }
} }
$result .= ('=' x 40)."\nRPC requests: ".$hash->{hmccu}{rpc}{requests};
return $result eq '' ? 'No event statistics found' : $result; return $result eq '' ? 'No event statistics found' : $result;
} }
elsif ($opt eq 'rpcstate') { elsif ($opt eq 'rpcstate') {
@ -1406,6 +1410,9 @@ sub HMCCURPCPROC_RegisterCallback ($$)
###################################################################### ######################################################################
# Deregister RPC callbacks at CCU # Deregister RPC callbacks at CCU
# force:
# >0 - Ignore state of RPC server. Deregister in any case.
# >1 - Do not update RPC server state.
###################################################################### ######################################################################
sub HMCCURPCPROC_DeRegisterCallback ($$) sub HMCCURPCPROC_DeRegisterCallback ($$)
@ -1417,18 +1424,14 @@ sub HMCCURPCPROC_DeRegisterCallback ($$)
my $port = $hash->{rpcport}; my $port = $hash->{rpcport};
my $clkey = HMCCURPCPROC_GetKey ($hash); my $clkey = HMCCURPCPROC_GetKey ($hash);
my $localaddr = $hash->{hmccu}{localaddr}; my $localaddr = $hash->{hmccu}{localaddr};
my $cburl = '';
my $clurl = '';
my $auth = '';
my $rpchash = \%{$hash->{hmccu}{rpc}}; my $rpchash = \%{$hash->{hmccu}{rpc}};
return (0, "RPC server $clkey not in state registered or running") return (0, "RPC server $clkey not in state registered or running")
if ($rpchash->{state} ne 'registered' && $rpchash->{state} ne 'running' && $force == 0); if ($rpchash->{state} ne 'registered' && $rpchash->{state} ne 'running' && $force == 0);
$cburl = $rpchash->{cburl} if (exists($rpchash->{cburl})); my $cburl = $rpchash->{cburl} // HMCCU_GetRPCCallbackURL ($ioHash, $localaddr, $rpchash->{cbport}, $clkey, $port);
$clurl = $rpchash->{clurl} if (exists($rpchash->{clurl})); my $clurl = $rpchash->{clurl} // '';
$auth = $rpchash->{auth} if (exists($rpchash->{auth})); my $auth = $rpchash->{auth} // '';
$cburl = HMCCU_GetRPCCallbackURL ($ioHash, $localaddr, $rpchash->{cbport}, $clkey, $port) if ($cburl eq '');
($clurl, $auth) = HMCCU_BuildURL ($ioHash, $port) if ($clurl eq ''); ($clurl, $auth) = HMCCU_BuildURL ($ioHash, $port) if ($clurl eq '');
return (0, "Can't get RPC parameters for ID $clkey") if ($cburl eq '' || $clurl eq ''); return (0, "Can't get RPC parameters for ID $clkey") if ($cburl eq '' || $clurl eq '');
@ -1439,7 +1442,7 @@ sub HMCCURPCPROC_DeRegisterCallback ($$)
my $err; my $err;
for (my $i=0; $i<2; $i++) { for (my $i=0; $i<2; $i++) {
($resp, $err) = HMCCURPCPROC_SendRequest ($hash, "init", "$cburl:STRING", ''); ($resp, $err) = HMCCURPCPROC_SendRequest ($hash, "init", "$cburl:STRING", '');
if (defined ($resp)) { if (defined ($resp) && $force < 2) {
HMCCURPCPROC_SetRPCState ($hash, $force == 0 ? 'deregistered' : $rpchash->{state}, HMCCURPCPROC_SetRPCState ($hash, $force == 0 ? 'deregistered' : $rpchash->{state},
"Callback for RPC server $clkey deregistered", 1); "Callback for RPC server $clkey deregistered", 1);
@ -1576,23 +1579,31 @@ sub HMCCURPCPROC_StartRPCServer ($)
my $rpcport = $hash->{rpcport}; my $rpcport = $hash->{rpcport};
my ($serveraddr, $interface) = HMCCU_GetRPCServerInfo ($ioHash, $rpcport, 'host,name'); my ($serveraddr, $interface) = HMCCU_GetRPCServerInfo ($ioHash, $rpcport, 'host,name');
my $clkey = 'CB'.$rpcport.$hash->{rpcid}; my $clkey = 'CB'.$rpcport.$hash->{rpcid};
my $callbackport = $rpcserverport+$rpcport+($ccunum*10);
$hash->{hmccu}{localaddr} = $localaddr; $hash->{hmccu}{localaddr} = $localaddr;
my ($clurl, $auth) = HMCCU_BuildURL ($ioHash, $hash->{rpcport});
my ($flags, $type) = HMCCU_GetRPCServerInfo ($ioHash, $rpcport, 'flags,type');
# Store parameters for child process # Store parameters for child process
my %procpar; my %procpar = (
$procpar{socktimeout} = AttrVal ($name, 'rpcWriteTimeout', $HMCCURPCPROC_TIMEOUT_WRITE); socktimeout => AttrVal ($name, 'rpcWriteTimeout', $HMCCURPCPROC_TIMEOUT_WRITE),
$procpar{conntimeout} = AttrVal ($name, 'rpcConnTimeout', $HMCCURPCPROC_TIMEOUT_CONNECTION); conntimeout => AttrVal ($name, 'rpcConnTimeout', $HMCCURPCPROC_TIMEOUT_CONNECTION),
$procpar{acctimeout} = AttrVal ($name, 'rpcAcceptTimeout', $HMCCURPCPROC_TIMEOUT_ACCEPT); acctimeout => AttrVal ($name, 'rpcAcceptTimeout', $HMCCURPCPROC_TIMEOUT_ACCEPT),
$procpar{queuesize} = AttrVal ($name, 'rpcQueueSize', $HMCCURPCPROC_MAX_QUEUESIZE); queuesize => AttrVal ($name, 'rpcQueueSize', $HMCCURPCPROC_MAX_QUEUESIZE),
$procpar{queuesend} = AttrVal ($name, 'rpcQueueSend', $HMCCURPCPROC_MAX_QUEUESEND); queuesend => AttrVal ($name, 'rpcQueueSend', $HMCCURPCPROC_MAX_QUEUESEND),
$procpar{statistics} = AttrVal ($name, 'rpcStatistics', $HMCCURPCPROC_STATISTICS); statistics => AttrVal ($name, 'rpcStatistics', $HMCCURPCPROC_STATISTICS),
$procpar{maxioerrors} = AttrVal ($name, 'rpcMaxIOErrors', $HMCCURPCPROC_MAX_IOERRORS); maxioerrors => AttrVal ($name, 'rpcMaxIOErrors', $HMCCURPCPROC_MAX_IOERRORS),
$procpar{ccuflags} = AttrVal ($name, 'ccuflags', 'null'); ccuflags => AttrVal ($name, 'ccuflags', 'null'),
$procpar{evttimeout} = $evttimeout; name => $name,
$procpar{interface} = $interface; evttimeout => $evttimeout,
($procpar{flags}, $procpar{type}) = HMCCU_GetRPCServerInfo ($ioHash, $rpcport, 'flags,type'); serveraddr => $serveraddr,
$procpar{name} = $name; interface => $interface,
$procpar{clkey} = $clkey; clkey => $clkey,
flags => $flags,
type => $type,
parentPID => $$
);
# Reset state of server processes # Reset state of server processes
$hash->{hmccu}{rpc}{state} = 'inactive'; $hash->{hmccu}{rpc}{state} = 'inactive';
@ -1610,11 +1621,9 @@ sub HMCCURPCPROC_StartRPCServer ($)
my $pid = $$; my $pid = $$;
$hash->{FD} = fileno $sockchild; $hash->{FD} = fileno $sockchild;
$selectlist{"RPC.$name.$pid"} = $hash; $selectlist{"RPC.$name.$pid"} = $hash;
my $callbackport = $rpcserverport+$rpcport+($ccunum*10);
# Initialize RPC server $hash->{hmccu}{rpc}{clkey} = $clkey;
# my $err = ''; $hash->{hmccu}{rpc}{cbport} = $callbackport;
# my %srvprocpar;
# Start RPC server process # Start RPC server process
my $rpcpid = fhemFork (); my $rpcpid = fhemFork ();
@ -1638,8 +1647,6 @@ sub HMCCURPCPROC_StartRPCServer ($)
HMCCU_Log ($hash, 2, "RPC server process started for interface $interface with PID=$rpcpid"); HMCCU_Log ($hash, 2, "RPC server process started for interface $interface with PID=$rpcpid");
# Store process parameters # Store process parameters
$hash->{hmccu}{rpc}{clkey} = $clkey;
$hash->{hmccu}{rpc}{cbport} = $callbackport;
$hash->{hmccu}{rpc}{pid} = $rpcpid; $hash->{hmccu}{rpc}{pid} = $rpcpid;
$hash->{hmccu}{rpc}{state} = 'initialized'; $hash->{hmccu}{rpc}{state} = 'initialized';
@ -1687,7 +1694,7 @@ sub HMCCURPCPROC_RPCServerStarted ($)
# Update client devices if interface is managed by HMCCURPCPROC device. # Update client devices if interface is managed by HMCCURPCPROC device.
# Normally interfaces are managed by HMCCU device. # Normally interfaces are managed by HMCCU device.
if ($ioHash->{hmccu}{interfaces}{$ifname}{manager} eq 'HMCCURPCPROC') { if ($ioHash->{hmccu}{interfaces}{$ifname}{manager} eq 'HMCCURPCPROC') {
HMCCU_UpdateClients ($ioHash, '.*', 'Attr', 0, $ifname, 1); HMCCU_UpdateClients ($ioHash, '.*', 'Attr', $ifname, 1);
} }
RemoveInternalTimer ($hash, "HMCCURPCPROC_IsRPCServerRunning"); RemoveInternalTimer ($hash, "HMCCURPCPROC_IsRPCServerRunning");
@ -2081,6 +2088,12 @@ sub HMCCURPCPROC_ProcessMulticallResponse ($$$$$)
###################################################################### ######################################################################
# Send RPC request to CCU. # Send RPC request to CCU.
# Supports XML and BINRPC requests. # Supports XML and BINRPC requests.
# Parameters:
# hash - FHEM hash reference or parameter hash reference
# Parameter hash used by sub processes:
# NAME - HMCCURPCPROC device name
# rpcport - CCU RPC port
# methods - list of RPC methods
# Return value: # Return value:
# (response, undef) - Request successful # (response, undef) - Request successful
# (undef, error) - Request failed with error # (undef, error) - Request failed with error
@ -2089,22 +2102,29 @@ sub HMCCURPCPROC_ProcessMulticallResponse ($$$$$)
sub HMCCURPCPROC_SendRequest ($@) sub HMCCURPCPROC_SendRequest ($@)
{ {
my ($hash, $request, @param) = @_; my ($hash, $request, @param) = @_;
my $port = $hash->{rpcport};
my $ph = exists($hash->{TYPE}) ? 0 : 1;
my $ioHash = $hash->{IODev}; my $ioHash = $hash->{IODev};
if (!defined($ioHash)) { if (!$ph && !defined($ioHash)) {
HMCCU_Log ($hash, 2, 'I/O device not found'); HMCCU_Log ($hash, 2, 'I/O device not found');
return (undef, 'I/O device not found'); return (undef, 'I/O device not found');
} }
my $port = $hash->{rpcport};
my $multicalls = 0;
if (!$ph) {
$multicalls = 1 if (
!HMCCU_IsFlag ($hash, 'noMulticalls') && defined($hash->{hmccu}{rpc}{multicall}) &&
HMCCU_IsRPCType ($ioHash, $port, 'A')
);
}
my $retry = AttrVal ($hash->{NAME}, 'rpcRetryRequest', 1); my $retry = AttrVal ($hash->{NAME}, 'rpcRetryRequest', 1);
$retry = 2 if ($retry > 2); $retry = 2 if ($retry > 2);
$hash->{hmccu}{rpc}{requests} //= 0; # Count RPC requests
# Multicall request # Multicall request
if ($request eq 'system.multicall' && ( if ($request eq 'system.multicall' && !$multicalls) {
HMCCU_IsFlag ($hash, 'noMulticalls') || !defined($hash->{hmccu}{rpc}{multicall}) || HMCCU_IsRPCType ($ioHash, $port, 'B')
)) {
# If multicalls are not supported or disabled, execute multiple requests # If multicalls are not supported or disabled, execute multiple requests
my @respList = (); my @respList = ();
my $reqList = shift @param; # Reference to request array my $reqList = shift @param; # Reference to request array
@ -2143,13 +2163,11 @@ sub HMCCURPCPROC_SendRequest ($@)
for (my $reqNo=0; $reqNo<=$retry; $reqNo++) { for (my $reqNo=0; $reqNo<=$retry; $reqNo++) {
if (HMCCU_IsRPCType ($ioHash, $port, 'A')) { if (HMCCU_IsRPCType ($ioHash, $port, 'A')) {
# XML RPC request # XML RPC request
$hash->{hmccu}{rpc}{requests}++;
($resp, $err) = HMCCURPCPROC_SendXMLRequest ($hash, $ioHash, $request, @param); ($resp, $err) = HMCCURPCPROC_SendXMLRequest ($hash, $ioHash, $request, @param);
last if (defined($resp)); last if (defined($resp));
} }
elsif (HMCCU_IsRPCType ($ioHash, $port, 'B')) { elsif (HMCCU_IsRPCType ($ioHash, $port, 'B')) {
# Binary RPC request # Binary RPC request
$hash->{hmccu}{rpc}{requests}++;
($resp, $err) = HMCCURPCPROC_SendBINRequest ($hash, $ioHash, $request, @param); ($resp, $err) = HMCCURPCPROC_SendBINRequest ($hash, $ioHash, $request, @param);
last if (defined($resp)); last if (defined($resp));
} }
@ -2258,7 +2276,7 @@ sub HMCCURPCPROC_SendBINRequest ($@)
my ($bytesWritten, $errmsg) = HMCCURPCPROC_WriteToSocket ($hash->{hmccu}{rpc}{connection}, $encreq, $timeoutWrite); my ($bytesWritten, $errmsg) = HMCCURPCPROC_WriteToSocket ($hash->{hmccu}{rpc}{connection}, $encreq, $timeoutWrite);
if ($bytesWritten > 0) { if ($bytesWritten > 0) {
my ($bytesRead, $encresp) = HMCCURPCPROC_ReadFromSocket ($hash, $hash->{hmccu}{rpc}{connection}, $timeoutRead); my ($bytesRead, $encresp) = HMCCURPCPROC_ReadFromSocket ($hash->{hmccu}{rpc}{connection}, $timeoutRead);
# $socket->close (); # $socket->close ();
if ($bytesRead > 0) { if ($bytesRead > 0) {
@ -2374,6 +2392,7 @@ sub HMCCURPCPROC_HandleConnection ($$$$)
my $maxsnd = $procpar->{queuesend}; my $maxsnd = $procpar->{queuesend};
my $maxioerrors = $procpar->{maxioerrors}; my $maxioerrors = $procpar->{maxioerrors};
my $clkey = $procpar->{clkey}; my $clkey = $procpar->{clkey};
my $parentPID = $procpar->{parentPID};
my $ioerrors = 0; my $ioerrors = 0;
my $sioerrors = 0; my $sioerrors = 0;
@ -2408,20 +2427,34 @@ sub HMCCURPCPROC_HandleConnection ($$$$)
$rpcsrv->{hmccu}{snd}{$et} = 0; $rpcsrv->{hmccu}{snd}{$et} = 0;
} }
# Recover device hash
my $rpcDeviceHash = $defs{$name};
# Signal handler # Signal handler
$SIG{INT} = sub { $run = 0; HMCCU_Log ($name, 2, "$clkey received signal INT"); }; $SIG{INT} = sub { $run = 0; HMCCU_Log ($name, 2, "$clkey received signal INT"); };
my $checkTime = time(); # At this point in time we checked the state of the parent process
HMCCURPCPROC_Write ($rpcsrv, 'SL', $clkey, $pid); HMCCURPCPROC_Write ($rpcsrv, 'SL', $clkey, $pid);
HMCCU_Log ($name, 2, "$clkey accepting connections. PID=$pid"); HMCCU_Log ($name, 2, "$clkey accepting connections. PID=$pid");
$rpcsrv->{__daemon}->timeout ($acctimeout) if ($acctimeout > 0.0); $rpcsrv->{__daemon}->timeout ($acctimeout) if ($acctimeout > 0.0);
while ($run) { while ($run > 0) {
my $currentTime = time();
# Check for event timeout
if ($evttimeout > 0) { if ($evttimeout > 0) {
my $difftime = time()-$rpcsrv->{hmccu}{evttime}; my $difftime = $currentTime-$rpcsrv->{hmccu}{evttime};
HMCCURPCPROC_Write ($rpcsrv, 'TO', $clkey, $difftime) if ($difftime >= $evttimeout); HMCCURPCPROC_Write ($rpcsrv, 'TO', $clkey, $difftime) if ($difftime >= $evttimeout);
} }
# Check if parent process is still running
if ($currentTime-$checkTime > $HMCCURPCPROC_PARENT_CHECK_INTERVAL) {
$run = kill(0, $parentPID) ? 1 : -1;
$checkTime = $currentTime;
}
# Send queue entries to parent process # Send queue entries to parent process
if (scalar (@queue) > 0) { if (scalar (@queue) > 0) {
HMCCU_Log ($name, 4, "RPC server $clkey sending data to FHEM"); HMCCU_Log ($name, 4, "RPC server $clkey sending data to FHEM");
@ -2440,7 +2473,7 @@ sub HMCCURPCPROC_HandleConnection ($$$$)
HMCCU_Log ($name, 4, "RPC server $clkey accepting connections"); HMCCU_Log ($name, 4, "RPC server $clkey accepting connections");
my $connection = $rpcsrv->{__daemon}->accept (); my $connection = $rpcsrv->{__daemon}->accept ();
next if (! $connection); next if (! $connection);
last if (! $run); last if ($run < 1);
$connection->timeout ($conntimeout) if ($conntimeout > 0.0); $connection->timeout ($conntimeout) if ($conntimeout > 0.0);
HMCCU_Log ($name, 4, "RPC server $clkey processing request"); HMCCU_Log ($name, 4, "RPC server $clkey processing request");
@ -2456,10 +2489,18 @@ sub HMCCURPCPROC_HandleConnection ($$$$)
undef $connection; undef $connection;
} }
HMCCU_Log ($name, 1, "RPC server $clkey stopped handling connections. PID=$pid"); HMCCU_Log ($name, 1, "RPC server $clkey stopped handling connections. PID=$pid run=$run");
close ($rpcsrv->{__daemon}) if ($prot eq 'B'); close ($rpcsrv->{__daemon}) if ($prot eq 'B');
if ($run < 0) {
# Parent process not running: try to deregister callback URL and terminate RPC server process
HMCCU_Log ($name, 1, "Parent process (FHEM,PID=$parentPID) not running. Shutting down RPC server process $clkey.");
HMCCURPCPROC_DeRegisterCallback ($rpcDeviceHash, 1);
HMCCU_Log ($name, 1, "FHEM will be restarted automatically if restart is enabled in system.d configuration.");
return;
}
# Send statistic info # Send statistic info
HMCCURPCPROC_WriteStats ($rpcsrv, $clkey); HMCCURPCPROC_WriteStats ($rpcsrv, $clkey);
@ -2593,9 +2634,9 @@ sub HMCCURPCPROC_ReceiveData ($$)
# Return (BytesRead, Data) on success. # Return (BytesRead, Data) on success.
###################################################################### ######################################################################
sub HMCCURPCPROC_ReadFromSocket ($$$) sub HMCCURPCPROC_ReadFromSocket ($$)
{ {
my ($hash, $socket, $timeout) = @_; my ($socket, $timeout) = @_;
my $data = ''; my $data = '';
my $totalBytes = 0; my $totalBytes = 0;

View File

@ -46,7 +46,8 @@ $HMCCU_CONFIG_VERSION = '5.0';
###################################################################### ######################################################################
# Channel roles with state and control datapoints # Channel roles with state and control datapoints
# F: 1=Channel/HMCCUCHN, 2=Device/HMCCUDEV, 3=Both # F: 1=Channel/HMCCUCHN, 2=Device/HMCCUDEV, 3=Both
# S: State datapoint, C: Control datapoint, V: Control values # S: State datapoint, C: Control datapoint,
# V: Control values, #=Enum or const:value[,...]
# P: Priority (used by HMCCUDEV if more than 1 channel role fits) # P: Priority (used by HMCCUDEV if more than 1 channel role fits)
# 1=lowest priority # 1=lowest priority
###################################################################### ######################################################################
@ -327,7 +328,7 @@ $HMCCU_CONFIG_VERSION = '5.0';
# Set/Get commands related to channel role # Set/Get commands related to channel role
# Role => { Command-Definition, ... } # Role => { Command-Definition, ... }
# Command-Defintion: # Command-Defintion:
# [Mode ]Command[:InterfaceExpr] => [No:]Datapoint-Def[:Function] [...]' # '[Mode ]Command[:InterfaceExpr]' => '[CombDatapoint ][No:]Datapoint-Def[:Function] [...]'
# Mode: # Mode:
# Either 'set' or 'get'. Default is 'set'. # Either 'set' or 'get'. Default is 'set'.
# Command: # Command:
@ -335,6 +336,9 @@ $HMCCU_CONFIG_VERSION = '5.0';
# InterfaceExpr: # InterfaceExpr:
# Command is only available, if interface of device is matching the regular # Command is only available, if interface of device is matching the regular
# expression. # expression.
# CombDatapoint:
# Either 'COMBINED_PARAMETER' or 'SUBMIT'
# Datapoint names are combined datapoint shortcuts.
# No: # No:
# Execution order of subcommands. By default subcommands are executed from left to # Execution order of subcommands. By default subcommands are executed from left to
# right. # right.
@ -375,7 +379,7 @@ $HMCCU_CONFIG_VERSION = '5.0';
%HMCCU_ROLECMDS = ( %HMCCU_ROLECMDS = (
'ACOUSTIC_SIGNAL_TRANSMITTER' => { 'ACOUSTIC_SIGNAL_TRANSMITTER' => {
'level' => 'V:LEVEL:?level', 'level' => 'V:LEVEL:?level',
'on' => 'V:LEVEL:1', 'on' => 'V:LEVEL:100',
'off' => 'V:LEVEL:0' 'off' => 'V:LEVEL:0'
}, },
'ALARM_SWITCH_VIRTUAL_RECEIVER' => { 'ALARM_SWITCH_VIRTUAL_RECEIVER' => {
@ -388,7 +392,7 @@ $HMCCU_CONFIG_VERSION = '5.0';
}, },
'BLIND' => { 'BLIND' => {
'pct' => 'V:LEVEL:?level', 'pct' => 'V:LEVEL:?level',
'open' => 'V:LEVEL:1', 'open' => 'V:LEVEL:100',
'close' => 'V:LEVEL:0', 'close' => 'V:LEVEL:0',
'up' => 'V:LEVEL:?delta=+20', 'up' => 'V:LEVEL:?delta=+20',
'down' => 'V:LEVEL:?delta=-20', 'down' => 'V:LEVEL:?delta=-20',
@ -397,7 +401,7 @@ $HMCCU_CONFIG_VERSION = '5.0';
}, },
'BLIND_VIRTUAL_RECEIVER' => { 'BLIND_VIRTUAL_RECEIVER' => {
'pct' => 'V:LEVEL:?level', 'pct' => 'V:LEVEL:?level',
'open' => 'V:LEVEL:1', 'open' => 'V:LEVEL:100',
'close' => 'V:LEVEL:0', 'close' => 'V:LEVEL:0',
'oldLevel' => 'V:LEVEL:1.005', 'oldLevel' => 'V:LEVEL:1.005',
'up' => 'V:LEVEL:?delta=+20', 'up' => 'V:LEVEL:?delta=+20',
@ -425,27 +429,27 @@ $HMCCU_CONFIG_VERSION = '5.0';
'DIMMER' => { 'DIMMER' => {
'pct' => '3:V:LEVEL:?level 1:V:ON_TIME:?time=0.0 2:V:RAMP_TIME:?ramp=0.5', 'pct' => '3:V:LEVEL:?level 1:V:ON_TIME:?time=0.0 2:V:RAMP_TIME:?ramp=0.5',
'level' => 'V:LEVEL:?level', 'level' => 'V:LEVEL:?level',
'on' => 'V:LEVEL:1', 'on' => 'V:LEVEL:100',
'off' => 'V:LEVEL:0', 'off' => 'V:LEVEL:0',
'on-for-timer' => 'V:ON_TIME:?duration V:LEVEL:1', 'on-for-timer' => 'V:ON_TIME:?duration V:LEVEL:100',
'on-till' => 'V:ON_TIME:?time V:LEVEL:1', 'on-till' => 'V:ON_TIME:?time V:LEVEL:100',
'up' => 'V:LEVEL:?delta=+10', 'up' => 'V:LEVEL:?delta=+10',
'down' => 'V:LEVEL:?delta=-10', 'down' => 'V:LEVEL:?delta=-10',
'stop' => 'V:RAMP_STOP:1', 'stop' => 'V:RAMP_STOP:1',
'toggle' => 'V:LEVEL:0,1' 'toggle' => 'V:LEVEL:0,100'
}, },
'DIMMER_VIRTUAL_RECEIVER' => { 'DIMMER_VIRTUAL_RECEIVER' => {
'pct' => '5:V:LEVEL:?level 1:V:DURATION_UNIT:0 2:V:ON_TIME,DURATION_VALUE:?time=0.0 3:V:RAMP_TIME_UNIT:0 4:V:RAMP_TIME,RAMP_TIME_VALUE:?ramp=0.5', 'pct' => '5:V:LEVEL:?level 1:V:DURATION_UNIT:0 2:V:ON_TIME,DURATION_VALUE:?time=0.0 3:V:RAMP_TIME_UNIT:0 4:V:RAMP_TIME,RAMP_TIME_VALUE:?ramp=0.5',
'level' => 'V:LEVEL:?level', 'level' => 'V:LEVEL:?level',
'on' => 'V:LEVEL:1', 'on' => 'V:LEVEL:100',
'off' => 'V:LEVEL:0', 'off' => 'V:LEVEL:0',
'oldLevel' => 'V:LEVEL:1.005', 'oldLevel' => 'V:LEVEL:1.005',
'on-for-timer' => '1:V:DURATION_UNIT:0 2:V:ON_TIME,DURATION_VALUE:?duration 3:V:LEVEL:1', 'on-for-timer' => '1:V:DURATION_UNIT:0 2:V:ON_TIME,DURATION_VALUE:?duration 3:V:LEVEL:100',
'on-till' => '1:V:DURATION_UNIT:0 2:V:ON_TIME,DURATION_VALUE:?time 3:V:LEVEL:1', 'on-till' => '1:V:DURATION_UNIT:0 2:V:ON_TIME,DURATION_VALUE:?time 3:V:LEVEL:100',
'up' => 'V:LEVEL:?delta=+10', 'up' => 'V:LEVEL:?delta=+10',
'down' => 'V:LEVEL:?delta=-10', 'down' => 'V:LEVEL:?delta=-10',
'color' => 'V:COLOR:#color', 'color' => 'V:COLOR:#color',
'toggle' => 'V:LEVEL:0,1' 'toggle' => 'V:LEVEL:0,100'
}, },
'DIMMER_WEEK_PROFILE' => { 'DIMMER_WEEK_PROFILE' => {
'progMode' => 'V:WEEK_PROGRAM_TARGET_CHANNEL_LOCK:#progMode' 'progMode' => 'V:WEEK_PROGRAM_TARGET_CHANNEL_LOCK:#progMode'
@ -472,13 +476,13 @@ $HMCCU_CONFIG_VERSION = '5.0';
}, },
'JALOUSIE' => { 'JALOUSIE' => {
'pct' => 'V:LEVEL:?level', 'pct' => 'V:LEVEL:?level',
'open' => 'V:LEVEL:1', 'open' => 'V:LEVEL:100',
'close' => 'V:LEVEL:0', 'close' => 'V:LEVEL:0',
'up' => 'V:LEVEL:?delta=+20', 'up' => 'V:LEVEL:?delta=+20',
'down' => 'V:LEVEL:?delta=-20', 'down' => 'V:LEVEL:?delta=-20',
'stop' => 'V:STOP:1', 'stop' => 'V:STOP:1',
'pctSlats' => 'V:LEVEL_SLATS:?level', 'pctSlats' => 'V:LEVEL_SLATS:?level',
'openSlats' => 'V:LEVEL_SLATS:1', 'openSlats' => 'V:LEVEL_SLATS:100',
'closeSlats' => 'V:LEVEL_SLATS:0', 'closeSlats' => 'V:LEVEL_SLATS:0',
}, },
'KEY' => { 'KEY' => {
@ -513,9 +517,12 @@ $HMCCU_CONFIG_VERSION = '5.0';
'color' => 'V:COLOR:?color V:ACT_HSV_COLOR_VALUE:?hsvColor', 'color' => 'V:COLOR:?color V:ACT_HSV_COLOR_VALUE:?hsvColor',
'brightness' => 'V:ACT_BRIGHTNESS:?brightness' 'brightness' => 'V:ACT_BRIGHTNESS:?brightness'
}, },
'SHUTTER_TRANSMITTER' => {
'calibrate' => 'V:SELF_CALIBRATION:#Mode'
},
'SHUTTER_VIRTUAL_RECEIVER' => { 'SHUTTER_VIRTUAL_RECEIVER' => {
'pct' => 'V:LEVEL:?level', 'pct' => 'V:LEVEL:?level',
'open' => 'V:LEVEL:1', 'open' => 'V:LEVEL:100',
'oldLevel' => 'V:LEVEL:1.005', 'oldLevel' => 'V:LEVEL:1.005',
'close' => 'V:LEVEL:0', 'close' => 'V:LEVEL:0',
'up' => 'V:LEVEL:?delta=+20', 'up' => 'V:LEVEL:?delta=+20',
@ -543,9 +550,13 @@ $HMCCU_CONFIG_VERSION = '5.0';
'sensor-on-till' => 'V:ON_TIME:?time V:STATE:1' 'sensor-on-till' => 'V:ON_TIME:?time V:STATE:1'
}, },
'SWITCH_VIRTUAL_RECEIVER' => { 'SWITCH_VIRTUAL_RECEIVER' => {
'COMBINED_PARAMETER' => {
'OT' => 'ON_TIME',
'S' => 'STATE'
},
'on' => 'V:STATE:1', 'on' => 'V:STATE:1',
'off' => 'V:STATE:0', 'off' => 'V:STATE:0',
'on-for-timer' => 'V:ON_TIME:?duration V:STATE:1', 'on-for-timer' => 'COMBINED_PARAMETER V:OT:?duration V:S:1',
'on-till' => 'V:ON_TIME:?time V:STATE:1', 'on-till' => 'V:ON_TIME:?time V:STATE:1',
'toggle' => 'V:STATE:0,1' 'toggle' => 'V:STATE:0,1'
}, },
@ -560,15 +571,22 @@ $HMCCU_CONFIG_VERSION = '5.0';
'get week-program' => 'D:WEEK_PROGRAM_POINTER:#program:HMCCU_DisplayWeekProgram' 'get week-program' => 'D:WEEK_PROGRAM_POINTER:#program:HMCCU_DisplayWeekProgram'
}, },
'UNIVERSAL_LIGHT_RECEIVER' => { 'UNIVERSAL_LIGHT_RECEIVER' => {
'COMBINED_PARAMETER' => {
'L' => 'LEVEL',
'OT' => 'ON_TIME',
'H' => 'HUE',
'SAT' => 'SATURATION'
},
'pct' => '5:V:LEVEL:?level 1:V:DURATION_UNIT:0 2:V:DURATION_VALUE:?time=0.0 3:V:RAMP_TIME_UNIT:0 4:V:RAMP_TIME_VALUE:?ramp=0.5', 'pct' => '5:V:LEVEL:?level 1:V:DURATION_UNIT:0 2:V:DURATION_VALUE:?time=0.0 3:V:RAMP_TIME_UNIT:0 4:V:RAMP_TIME_VALUE:?ramp=0.5',
'level' => 'V:LEVEL:?level', 'level' => 'V:LEVEL:?level',
'on' => 'V:LEVEL:1', 'on' => 'V:LEVEL:100',
'off' => 'V:LEVEL:0', 'off' => 'V:LEVEL:0',
'on-for-timer' => '1:V:DURATION_UNIT:0 2:V:DURATION_VALUE:?duration 3:V:LEVEL:1', 'on-for-timer' => '1:V:DURATION_UNIT:0 2:V:DURATION_VALUE:?duration 3:V:LEVEL:100',
'on-till' => '1:V:DURATION_UNIT:0 2:V:DURATION_VALUE:?time 3:V:LEVEL:1', 'on-till' => '1:V:DURATION_UNIT:0 2:V:DURATION_VALUE:?time 3:V:LEVEL:100',
'up' => 'V:LEVEL:?delta=+10', 'up' => 'V:LEVEL:?delta=+10',
'down' => 'V:LEVEL:?delta=-10', 'down' => 'V:LEVEL:?delta=-10',
'toggle' => 'V:LEVEL:0,1' 'toggle' => 'V:LEVEL:0,100',
'color' => 'COMBINED_PARAMETER V:L:?level V:H:?hue V:SAT:?saturation'
}, },
'VIRTUAL_KEY' => { 'VIRTUAL_KEY' => {
'on' => 'V:PRESS_SHORT:1', 'on' => 'V:PRESS_SHORT:1',