2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-05-02 00:48:53 +00:00

HMCCU: Fixed some bugs

git-svn-id: https://svn.fhem.de/fhem/trunk@18134 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
zap 2019-01-04 15:48:08 +00:00
parent 78ef0f87f2
commit ba687a7d8e
6 changed files with 269 additions and 309 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.
- bugfix: 88_HMCCU: Bug fixes
- change: 10_MYSENSORS_DEVICE: make OTA feature available, - change: 10_MYSENSORS_DEVICE: make OTA feature available,
change battery name convention, change battery name convention,
support MySensors API 2.0 features like support MySensors API 2.0 features like

View File

@ -4,7 +4,7 @@
# #
# $Id$ # $Id$
# #
# Version 4.3.008 # Version 4.3.009
# #
# Module for communication between FHEM and Homematic CCU2/3. # Module for communication between FHEM and Homematic CCU2/3.
# #
@ -12,74 +12,17 @@
# CCU group devices, HomeGear, CUxD, Osram Lightify, Homematic Virtual Layer # CCU group devices, HomeGear, CUxD, Osram Lightify, Homematic Virtual Layer
# and Philips Hue (not tested) # and Philips Hue (not tested)
# #
# (c) 2018 by zap (zap01 <at> t-online <dot> de) # (c) 2019 by zap (zap01 <at> t-online <dot> de)
# #
############################################################################## ##############################################################################
# #
# define <name> HMCCU <hostname_or_ip_of_ccu> [ccunumber] [waitforccu=<seconds>]
#
# set <name> ackmessages
# set <name> cleardefaults
# set <name> defaults
# set <name> delete <name> [{ OT_VARDP | OT_DEVICE }]
# set <name> execute <ccu_program>
# set <name> importdefaults <filename>
# set <name> hmscript {<scriptfile>|!<function>|'['<code>']'} [dump] [<parname>=<value> [...]]
# set <name> rpcregister [{all|<interface>}]
# set <name> rpcserver {on|off|restart}
# set <name> var [<type>] <name> <value> [<parameter>=<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> [t={chn|dev|all}] [s=<suffix>] [p=<prefix>] [f=<format>]
# [defattr] [duplicates] [save] [<attr>=<val> [...]]}]
# get <name> dump {devtypes|datapoints} [<filter>]
# get <name> dutycycle
# get <name> exportdefaults {filename} [csv] [all]
# get <name> firmware [{type-expr}|full]
# get <name> parfile [<parfile>]
# get <name> rpcevents
# get <name> rpcstate
# get <name> update [<fhemdevexp> [{ State | Value }]]
# get <name> updateccu [<devexp> [{ State | Value }]]
# get <name> vars <regexp>
#
# attr <name> ccuaggregate <rules>
# attr <name> ccudef-hmstatevals <subst_rules>
# attr <name> ccudef-readingfilter <filter_rule>
# attr <name> ccudef-readingname <rules>
# attr <name> ccudef-substitute <subst_rule>
# attr <name> ccudefaults <filename>
# attr <name> ccuflags { intrpc,extrpc,procrpc,dptnocheck,noagg,logEvents,noReadings,nonBlocking }
# attr <name> ccuget { State | Value }
# attr <name> ccuReqTimeout <seconds>
# attr <name> ccuGetVars <seconds>[:<pattern>]
# attr <name> parfile <parfile>
# attr <name> rpcevtimeout <seconds>
# attr <name> rpcinterfaces { BidCos-Wired, BidCos-RF, HmIP-RF, VirtualDevices, Homegear, HVL }
# attr <name> rpcinterval <seconds>
# 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 [<datapoint-expr!]{-<digits>|0|1|2}[;...]
# attr <name> substitute <subst_rule>
#
# filter_rule := channel-regexp!datapoint-regexp[;...]
# subst_rule := [[channel.]datapoint[,...]!]<regexp>:<subtext>[,...][;...]
##############################################################################
# Verbose levels: # Verbose levels:
# #
# 0 = Log start/stop and initialization messages # 0 = Log start/stop and initialization messages
# 1 = Log errors # 1 = Log errors
# 2 = Log counters and warnings # 2 = Log counters and warnings
# 3 = Log events and runtime information # 3 = Log events and runtime information
#
############################################################################## ##############################################################################
package main; package main;
@ -108,7 +51,7 @@ my %HMCCU_CUST_CHN_DEFAULTS;
my %HMCCU_CUST_DEV_DEFAULTS; my %HMCCU_CUST_DEV_DEFAULTS;
# HMCCU version # HMCCU version
my $HMCCU_VERSION = '4.3.008'; my $HMCCU_VERSION = '4.3.009';
# Default RPC port (BidCos-RF) # Default RPC port (BidCos-RF)
my $HMCCU_RPC_PORT_DEFAULT = 2001; my $HMCCU_RPC_PORT_DEFAULT = 2001;
@ -119,6 +62,10 @@ my $HMCCU_MAX_IOERRORS = 100;
my $HMCCU_MAX_QUEUESIZE = 500; my $HMCCU_MAX_QUEUESIZE = 500;
my $HMCCU_TIME_WAIT = 100000; my $HMCCU_TIME_WAIT = 100000;
my $HMCCU_TIME_TRIGGER = 10; my $HMCCU_TIME_TRIGGER = 10;
# RPC ping interval for interface BidCos-RF, should be smaller than HMCCU_TIMEOUT_EVENT
my $HMCCU_TIME_PING = 300;
my $HMCCU_TIMEOUT_CONNECTION = 10; my $HMCCU_TIMEOUT_CONNECTION = 10;
my $HMCCU_TIMEOUT_WRITE = 0.001; my $HMCCU_TIMEOUT_WRITE = 0.001;
my $HMCCU_TIMEOUT_ACCEPT = 1; my $HMCCU_TIMEOUT_ACCEPT = 1;
@ -280,6 +227,7 @@ sub HMCCU_UpdateSingleDatapoint ($$$$);
sub HMCCU_UpdateSingleDevice ($$$$); sub HMCCU_UpdateSingleDevice ($$$$);
# RPC functions # RPC functions
sub HMCCU_EventsTimedOut ($);
sub HMCCU_GetRPCCallbackURL ($$$$$); sub HMCCU_GetRPCCallbackURL ($$$$$);
sub HMCCU_GetRPCDevice ($$$); sub HMCCU_GetRPCDevice ($$$);
sub HMCCU_GetRPCInterfaceList ($); sub HMCCU_GetRPCInterfaceList ($);
@ -424,8 +372,9 @@ sub HMCCU_Initialize ($)
" ccudef-hmstatevals:textField-long ccudef-substitute:textField-long". " ccudef-hmstatevals:textField-long ccudef-substitute:textField-long".
" ccudef-readingname:textField-long ccudef-readingfilter:textField-long". " ccudef-readingname:textField-long ccudef-readingfilter:textField-long".
" ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc". " ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc".
" ccuflags:multiple-strict,extrpc,intrpc,procrpc,dptnocheck,noagg,nohmstate,logEvents,noEvents,noReadings,nonBlocking". " ccuflags:multiple-strict,extrpc,intrpc,procrpc,dptnocheck,noagg,nohmstate,".
" ccuReqTimeout ccuGetVars rpcinterval:2,3,5,7,10 rpcqueue". "logEvents,noEvents,noReadings,nonBlocking,reconnect,logPong,trace".
" ccuReqTimeout ccuGetVars rpcinterval:2,3,5,7,10 rpcqueue rpcPingCCU".
" rpcport:multiple-strict,".join(',',sort keys %HMCCU_RPC_NUMPORT). " rpcport:multiple-strict,".join(',',sort keys %HMCCU_RPC_NUMPORT).
" rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute". " rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute".
" ccuget:Value,State ". " ccuget:Value,State ".
@ -1431,7 +1380,7 @@ sub HMCCU_Set ($@)
my @ifList = HMCCU_GetRPCInterfaceList ($hash); my @ifList = HMCCU_GetRPCInterfaceList ($hash);
if (scalar (@ifList) > 0) { if (scalar (@ifList) > 0) {
my $ifStr = join (',', @ifList); my $ifStr = join (',', @ifList);
$options =~ s/register:all/rpcregister:all,$ifStr/; $options =~ s/rpcregister:all/rpcregister:all,$ifStr/;
} }
my $usage = "HMCCU: Unknown argument $opt, choose one of $options"; my $usage = "HMCCU: Unknown argument $opt, choose one of $options";
my $host = $hash->{host}; my $host = $hash->{host};
@ -1498,13 +1447,14 @@ sub HMCCU_Set ($@)
} }
elsif ($opt eq 'execute') { elsif ($opt eq 'execute') {
my $program = shift @$a; my $program = shift @$a;
$program .= ' '.join(' ', @$a) if (scalar (@$a) > 0);
my $response; my $response;
$usage = "Usage: set $name $opt program-name"; $usage = "Usage: set $name $opt program-name";
return HMCCU_SetError ($hash, $usage) if (!defined ($program)); return HMCCU_SetError ($hash, $usage) if (!defined ($program));
my $cmd = qq(dom.GetObject("$program").ProgramExecute()); my $cmd = qq(dom.GetObject("$program").ProgramExecute());
my $value = HMCCU_HMCommand ($hash, $cmd, 0); my $value = HMCCU_HMCommand ($hash, $cmd, 1);
return HMCCU_SetState ($hash, "OK") if (defined ($value)); return HMCCU_SetState ($hash, "OK") if (defined ($value));
return HMCCU_SetError ($hash, "Program execution error"); return HMCCU_SetError ($hash, "Program execution error");
@ -3510,6 +3460,48 @@ sub HMCCU_GetRPCPortList ($)
return @ports; return @ports;
} }
######################################################################
# Called by HMCCURPCPROC device of interface BidCos-RF when no events
# from CCU were received for a specified time span.
# Return 1 if all RPC servers have been registered successfully.
# Return 0 if at least one RPC server failed to register or the
# corresponding HMCCURPCPROC device was not found.
######################################################################
sub HMCCU_EventsTimedOut ($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
return 1 if (!HMCCU_IsFlag ($name, 'reconnect'));
HMCCU_Log ($hash, 2, "Reconnecting to CCU", 0);
# Register callback for each interface
my $rc = 1;
my @iflist = HMCCU_GetRPCInterfaceList ($hash);
foreach my $ifname (@iflist) {
my ($rpcdev, $save) = HMCCU_GetRPCDevice ($hash, 0, $ifname);
if ($rpcdev eq '') {
HMCCU_Log ($hash, 0, "Can't find RPC device for interface $ifname", 0);
$rc = 0;
next;
}
my $cl_hash = $defs{$rpcdev};
# Check if CCU interface is reachable before registering callback
my ($nrc, $msg) = HMCCURPCPROC_RegisterCallback ($cl_hash, 2);
$rc &= $nrc;
if ($nrc) {
$cl_hash->{ccustate} = 'active';
}
else {
HMCCU_Log ($cl_hash, 1, $msg, 0);
}
}
return $rc;
}
###################################################################### ######################################################################
# Build RPC callback URL # Build RPC callback URL
# Parameter hash might be a HMCCU or a HMCCURPC hash. # Parameter hash might be a HMCCU or a HMCCURPC hash.
@ -4409,7 +4401,7 @@ sub HMCCU_GetDeviceList ($)
###################################################################### ######################################################################
# Read list of datapoints for all or one CCU device type(s). # Read list of datapoints for all or one CCU device type(s).
# Function must not be called before GetDeviceList. # Function must not be called before GetDeviceList.
# Return number of datapoints. # Return number of datapoints read.
###################################################################### ######################################################################
sub HMCCU_GetDatapointList ($$$) sub HMCCU_GetDatapointList ($$$)
@ -5170,10 +5162,6 @@ sub HMCCU_GetRPCDevice ($$$)
my %rpcdevattr = ('room' => 'copy', 'group' => 'copy', 'icon' => 'copy', my %rpcdevattr = ('room' => 'copy', 'group' => 'copy', 'icon' => 'copy',
'stateFormat' => 'rpcstate/state', 'eventMap' => '/rpcserver on:on/rpcserver off:off/', 'stateFormat' => 'rpcstate/state', 'eventMap' => '/rpcserver on:on/rpcserver off:off/',
'verbose' => 2, 'alias' => $alias ); 'verbose' => 2, 'alias' => $alias );
if ($ifname eq 'BidCos-RF') {
$rpcdevattr{'rpcPingCCU'} = 300;
$rpcdevattr{'ccuflags'} = 'reconnect';
}
foreach my $a (keys %rpcdevattr) { foreach my $a (keys %rpcdevattr) {
my $v = $rpcdevattr{$a} eq 'copy' ? AttrVal ($name, $a, '') : $rpcdevattr{$a}; my $v = $rpcdevattr{$a} eq 'copy' ? AttrVal ($name, $a, '') : $rpcdevattr{$a};
CommandAttr (undef, "$rpcdevname $a $v") if ($v ne ''); CommandAttr (undef, "$rpcdevname $a $v") if ($v ne '');
@ -6011,7 +5999,7 @@ sub HMCCU_BulkUpdate ($$$$)
} }
###################################################################### ######################################################################
# Get datapoint value from CCU and update reading. # Get datapoint value from CCU and optionally update reading.
# If parameter noupd is defined and > 0 no readings will be updated. # If parameter noupd is defined and > 0 no readings will be updated.
###################################################################### ######################################################################
@ -8049,12 +8037,14 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
intrpc - Use internal RPC server. This is the default.<br/> intrpc - Use internal RPC server. This is the default.<br/>
extrpc - Same as procrpc (see below)<br/> extrpc - Same as procrpc (see below)<br/>
logEvents - Write events from CCU into FHEM logfile<br/> logEvents - Write events from CCU into FHEM logfile<br/>
logPong - Write log message when receiving pong event if verbose level is at least 3.<br/>
noEvents - Ignore events / device updates sent by CCU. No readings will be updated!<br/> noEvents - Ignore events / device updates sent by CCU. No readings will be updated!<br/>
nonBlocking - Use non blocking (asynchronous) CCU requests<br/> nonBlocking - Use non blocking (asynchronous) CCU requests<br/>
noReadings - Do not create or update readings<br/> noReadings - Do not create or update readings<br/>
procrpc - Use external RPC server provided by module HMCCPRPCPROC. During first RPC procrpc - Use external RPC server provided by module HMCCPRPCPROC. During first RPC
server start HMCCU will create a HMCCURPCPROC device for each interface confiugured server start HMCCU will create a HMCCURPCPROC device for each interface confiugured
in attribute 'rpcinterface'<br/> in attribute 'rpcinterface'<br/>
reconnect - Automatically reconnect to CCU when events timeout occurred.
Flags intrpc, extrpc and procrpc cannot be combined. Flags intrpc, extrpc and procrpc cannot be combined.
</li><br/> </li><br/>
<li><b>ccuget {State | <u>Value</u>}</b><br/> <li><b>ccuget {State | <u>Value</u>}</b><br/>
@ -8097,6 +8087,11 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
Specifiy how often RPC queue is read. Default is 5 seconds. Only relevant if internal Specifiy how often RPC queue is read. Default is 5 seconds. Only relevant if internal
RPC server is used (deprecated). RPC server is used (deprecated).
</li><br/> </li><br/>
<li><b>rpcPingCCU &lt;interval&gt;</b><br/>
Send RPC ping request to CCU every <i>interval</i> seconds. If <i>interval</i> is 0
ping requests are disabled. Default value is 300 seconds. If attribut ccuflags is set
to logPong a log message with level 3 is created when receiving a pong event.
</li><br/>
<li><b>rpcport &lt;value[,...]&gt;</b><br/> <li><b>rpcport &lt;value[,...]&gt;</b><br/>
Deprecated, use attribute 'rpcinterfaces' instead. Specify list of RPC ports on CCU. Deprecated, use attribute 'rpcinterfaces' instead. Specify list of RPC ports on CCU.
Default is 2001. Valid RPC ports are:<br/><br/> Default is 2001. Valid RPC ports are:<br/><br/>

View File

@ -4,54 +4,12 @@
# #
# $Id$ # $Id$
# #
# Version 4.3.004 # Version 4.3.005
# #
# (c) 2018 zap (zap01 <at> t-online <dot> de) # (c) 2018 zap (zap01 <at> t-online <dot> de)
# #
###################################################################### ######################################################################
# # Client device for Homematic channels.
# define <name> HMCCUCHN <ccudev> [readonly] [defaults]
# [iodev=<iodevname>]
#
# set <name> config [device] <parameter>=<value> [...]
# set <name> control <value>
# set <name> datapoint <datapoint> <value> [...]
# set <name> defaults
# set <name> devstate <value>
# set <name> <stateval_cmds>
# set <name> on-till <timestamp>
# set <name> on-for-timer <ontime>
# set <name> pct <level> [{ <ontime> | 0 } [<ramptime>]]
# set <name> toggle
#
# get <name> config [device] [<filter-expr>]
# get <name> configdesc [device]
# get <name> configlist [device] [<filtet-expr>]
# get <name> datapoint <datapoint>
# get <name> defaults
# get <name> deviceinfo
# get <name> devstate
# get <name> update
#
# attr <name> ccucalculate <value>:<reading>[:<dp-list>][...]
# attr <name> ccuflags { ackState, nochn0, trace }
# attr <name> ccuget { State | Value }
# attr <name> ccureadings { 0 | 1 }
# attr <name> ccureadingfilter <filter-rule>[;...]
# attr <name> ccureadingformat { name[lc] | address[lc] | datapoint[lc] }
# attr <name> ccureadingname <oldname>:<newname>[;...]
# attr <name> ccuSetOnChange <expr>
# attr <name> ccuverify { 0 | 1 | 2 }
# attr <name> controldatapoint <datapoint>
# attr <name> disable { 0 | 1 }
# attr <name> peer datapoints:condition:{hmccu:object=value|ccu:object=value|fhem:command}
# attr <name> hmstatevals <subst-rule>[;...]
# attr <name> statedatapoint <datapoint>
# attr <name> statevals <text1>:<subtext1>[,...]
# attr <name> substexcl <reading-expr>
# attr <name> substitute <subst-rule>[;...]
#
######################################################################
# Requires modules 88_HMCCU.pm, HMCCUConf.pm # Requires modules 88_HMCCU.pm, HMCCUConf.pm
###################################################################### ######################################################################
@ -61,8 +19,6 @@ use strict;
use warnings; use warnings;
use SetExtensions; use SetExtensions;
# use Time::HiRes qw( gettimeofday usleep );
sub HMCCUCHN_Initialize ($); sub HMCCUCHN_Initialize ($);
sub HMCCUCHN_Define ($@); sub HMCCUCHN_Define ($@);
sub HMCCUCHN_InitDevice ($$); sub HMCCUCHN_InitDevice ($$);
@ -239,9 +195,9 @@ sub HMCCUCHN_Attr ($@)
return undef; return undef;
} }
##################################### ######################################################################
# Set commands # Set commands
##################################### ######################################################################
sub HMCCUCHN_Set ($@) sub HMCCUCHN_Set ($@)
{ {
@ -369,10 +325,11 @@ sub HMCCUCHN_Set ($@)
return HMCCU_SetState ($hash, "OK"); return HMCCU_SetState ($hash, "OK");
} }
elsif ($opt eq 'pct') { elsif ($opt eq 'pct' || $opt eq 'up' || $opt eq 'down') {
return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype") return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype")
if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "LEVEL", 2)); if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "LEVEL", 2));
if ($opt eq 'pct') {
my $objname = ''; my $objname = '';
my $objvalue = shift @$a; my $objvalue = shift @$a;
return HMCCU_SetError ($hash, "Usage: set $name pct {value} [{ontime} [{ramptime}]]") return HMCCU_SetError ($hash, "Usage: set $name pct {value} [{ontime} [{ramptime}]]")
@ -399,8 +356,22 @@ sub HMCCUCHN_Set ($@)
# Set level # Set level
$dpval{"003.$ccuif.$ccuaddr.LEVEL"} = $objvalue; $dpval{"003.$ccuif.$ccuaddr.LEVEL"} = $objvalue;
$rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval); $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); }
else {
my $delta = shift @$a;
$delta = 10 if (!defined ($delta));
$delta = -$delta if ($opt eq 'down');
my $objname = "$ccuif.$ccuaddr.LEVEL";
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 1);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
# Set level
my $objvalue = min(max($result+$delta,0),100);
$rc = HMCCU_SetMultipleDatapoints ($hash, { "001.$objname" => $objvalue });
}
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
return HMCCU_SetState ($hash, "OK"); return HMCCU_SetState ($hash, "OK");
} }
elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') { elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') {
@ -471,7 +442,7 @@ sub HMCCUCHN_Set ($@)
$retmsg .= " toggle:noArg"; $retmsg .= " toggle:noArg";
$retmsg .= " on-for-timer on-till" $retmsg .= " on-for-timer on-till"
if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $ccuaddr, "ON_TIME", 2)); if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $ccuaddr, "ON_TIME", 2));
$retmsg .= " pct" $retmsg .= " pct up down"
if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $ccuaddr, "LEVEL", 2)); if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $ccuaddr, "LEVEL", 2));
} }
@ -479,9 +450,9 @@ sub HMCCUCHN_Set ($@)
} }
} }
##################################### ######################################################################
# Get commands # Get commands
##################################### ######################################################################
sub HMCCUCHN_Get ($@) sub HMCCUCHN_Get ($@)
{ {
@ -679,6 +650,10 @@ sub HMCCUCHN_Get ($@)
Example:<br/> Example:<br/>
<code>set light_entrance devstate true</code> <code>set light_entrance devstate true</code>
</li><br/> </li><br/>
<li><b>set &lt;name&gt; down [&lt;value&gt;]</b><br/>
Decrement value of datapoint LEVEL. This command is only available if channel contains
a datapoint LEVEL. Default for <i>value</i> is 10.
</li><br/>
<li><b>set &lt;name&gt; &lt;statevalue&gt;</b><br/> <li><b>set &lt;name&gt; &lt;statevalue&gt;</b><br/>
Set state of a CCU device channel to <i>StateValue</i>. The state datapoint of a channel Set state of a CCU device channel to <i>StateValue</i>. The state datapoint of a channel
must be defined by setting attribute 'statedatapoint'. The available state values must must be defined by setting attribute 'statedatapoint'. The available state values must
@ -736,6 +711,10 @@ sub HMCCUCHN_Get ($@)
set myswitch pct 100 600 10 set myswitch pct 100 600 10
</code> </code>
</li><br/> </li><br/>
<li><b>set &lt;name&gt; up [&lt;value&gt;]</b><br/>
Increment value of datapoint LEVEL. This command is only available if channel contains
a datapoint LEVEL. Default for <i>value</i> is 10.
</li><br/>
</ul> </ul>
<br/> <br/>

View File

@ -4,55 +4,12 @@
# #
# $Id$ # $Id$
# #
# Version 4.3.005 # Version 4.3.006
# #
# (c) 2018 zap (zap01 <at> t-online <dot> de) # (c) 2018 zap (zap01 <at> t-online <dot> de)
# #
###################################################################### ######################################################################
# # Client device for Homematic devices.
# define <name> HMCCUDEV {<ccudev>|virtual} [<statechannel>] [readonly] [defaults]
# [{group={<device>|<channel>}[,...]|groupexp=<regexp>}] [iodev=<iodevname>]
#
# set <name> clear [<regexp>]
# set <name> config [<channel-number>] <parameter>=<value> [...]
# set <name> control <value>
# set <name> datapoint [<channel-number>.]<datapoint> <value> [...]
# set <name> defaults
# set <name> devstate <value>
# set <name> on-till <timestamp>
# set <name> on-for-timer <ontime>
# set <name> pct <level> [{<ontime>|0} [<ramptime>]]
# set <name> <stateval_cmds>
# set <name> toggle
#
# 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
# get <name> update
#
# attr <name> ccucalculate <value>:<reading>[:<dp-list>][...]
# attr <name> ccuflags { ackState, nochn0, trace }
# attr <name> ccuget { State | Value }
# attr <name> ccureadings { 0 | 1 }
# attr <name> ccureadingformat { address[lc] | name[lc] | datapoint[lc] }
# attr <name> ccureadingfilter <filter-rule>[,...]
# attr <name> ccureadingname <oldname>:<newname>[,...]
# attr <name> ccuscaleval <datapoint>:<factor>[:<min>:<max>][,...]
# attr <name> ccuverify { 0 | 1 | 2}
# attr <name> controldatapoint <channel-number>.<datapoint>
# attr <name> disable { 0 | 1 }
# attr <name> peer datapoints:condition:{hmccu:object=value|ccu:object=value|fhem:command}
# attr <name> hmstatevals <subst-rule>[;...]
# attr <name> statechannel <channel>
# attr <name> statedatapoint [<channel-number>.]<datapoint>
# attr <name> statevals <text1>:<subtext1>[,...]
# attr <name> substexcl <reading-expr>
# attr <name> substitute <subst-rule>[;...]
#
######################################################################
# Requires modules 88_HMCCU.pm, HMCCUConf.pm # Requires modules 88_HMCCU.pm, HMCCUConf.pm
###################################################################### ######################################################################
@ -61,9 +18,6 @@ package main;
use strict; use strict;
use warnings; use warnings;
use SetExtensions; use SetExtensions;
# use Data::Dumper;
# use Time::HiRes qw( gettimeofday usleep );
sub HMCCUDEV_Initialize ($); sub HMCCUDEV_Initialize ($);
sub HMCCUDEV_Define ($@); sub HMCCUDEV_Define ($@);
@ -73,9 +27,9 @@ sub HMCCUDEV_Set ($@);
sub HMCCUDEV_Get ($@); sub HMCCUDEV_Get ($@);
sub HMCCUDEV_Attr ($@); sub HMCCUDEV_Attr ($@);
##################################### ######################################################################
# Initialize module # Initialize module
##################################### ######################################################################
sub HMCCUDEV_Initialize ($) sub HMCCUDEV_Initialize ($)
{ {
@ -98,9 +52,9 @@ sub HMCCUDEV_Initialize ($)
$readingFnAttributes; $readingFnAttributes;
} }
##################################### ######################################################################
# Define device # Define device
##################################### ######################################################################
sub HMCCUDEV_Define ($@) sub HMCCUDEV_Define ($@)
{ {
@ -537,7 +491,7 @@ sub HMCCUDEV_Set ($@)
return HMCCU_SetState ($hash, "OK"); return HMCCU_SetState ($hash, "OK");
} }
elsif ($opt eq 'pct') { elsif ($opt eq 'pct' || $opt eq 'up' || $opt eq 'down') {
return HMCCU_SetError ($hash, -11) if ($sc eq '' && $cc eq ''); return HMCCU_SetError ($hash, -11) if ($sc eq '' && $cc eq '');
my $chn; my $chn;
if (HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "LEVEL", 2)) { if (HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "LEVEL", 2)) {
@ -550,6 +504,7 @@ sub HMCCUDEV_Set ($@)
return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype") return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype")
} }
if ($opt eq 'pct') {
my $objname = ''; my $objname = '';
my $objvalue = shift @$a; my $objvalue = shift @$a;
return HMCCU_SetError ($hash, "Usage: set $name pct {value} [{ontime} [{ramptime}]]") return HMCCU_SetError ($hash, "Usage: set $name pct {value} [{ontime} [{ramptime}]]")
@ -577,8 +532,24 @@ sub HMCCUDEV_Set ($@)
# Set level # Set level
$dpval{"003.$ccuif.$ccuaddr:$chn.LEVEL"} = $objvalue; $dpval{"003.$ccuif.$ccuaddr:$chn.LEVEL"} = $objvalue;
$rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval); $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); }
else {
my $delta = shift @$a;
$delta = 10 if (!defined ($delta));
$delta = -$delta if ($opt eq 'down');
my $objname = "$ccuif.$ccuaddr:$chn.LEVEL";
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 1);
Log3 $name, 2, "HMCCU: set $opt: GetDatapoint returned $rc, $result"
if ($ccuflags =~ /trace/);
return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
# Set level
my $objvalue = min(max($result+$delta,0),100);
$rc = HMCCU_SetMultipleDatapoints ($hash, { "001.$objname" => $objvalue });
}
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
return HMCCU_SetState ($hash, "OK"); return HMCCU_SetState ($hash, "OK");
} }
elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') { elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') {
@ -653,7 +624,7 @@ sub HMCCUDEV_Set ($@)
$retmsg .= " toggle:noArg"; $retmsg .= " toggle:noArg";
$retmsg .= " on-for-timer on-till" $retmsg .= " on-for-timer on-till"
if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "ON_TIME", 2)); if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "ON_TIME", 2));
$retmsg .= " pct" $retmsg .= " pct up down"
if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "LEVEL", 2) || if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "LEVEL", 2) ||
HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $cc, "LEVEL", 2)); HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $cc, "LEVEL", 2));
} }
@ -923,6 +894,9 @@ sub HMCCUDEV_Get ($@)
Set state of a CCU device channel. Channel and state datapoint must be defined as Set state of a CCU device channel. Channel and state datapoint must be defined as
attribute 'statedatapoint'. If <i>value</i> contains string \_ it is substituted by blank. attribute 'statedatapoint'. If <i>value</i> contains string \_ it is substituted by blank.
</li><br/> </li><br/>
<li><b>set &lt;name&gt; down [&lt;value&gt;]</b><br/>
<a href="#HMCCUCHNset">see HMCCUCHN</a>
</li><br/>
<li><b>set &lt;name&gt; on-for-timer &lt;ontime&gt;</b><br/> <li><b>set &lt;name&gt; on-for-timer &lt;ontime&gt;</b><br/>
<a href="#HMCCUCHNset">see HMCCUCHN</a> <a href="#HMCCUCHNset">see HMCCUCHN</a>
</li><br/> </li><br/>
@ -947,6 +921,9 @@ sub HMCCUDEV_Get ($@)
<li><b>set &lt;name&gt; toggle</b><br/> <li><b>set &lt;name&gt; toggle</b><br/>
<a href="#HMCCUCHNset">see HMCCUCHN</a> <a href="#HMCCUCHNset">see HMCCUCHN</a>
</li><br/> </li><br/>
<li><b>set &lt;name&gt; up [&lt;value&gt;]</b><br/>
<a href="#HMCCUCHNset">see HMCCUCHN</a>
</li><br/>
<li><b>ePaper Display</b><br/><br/> <li><b>ePaper Display</b><br/><br/>
This display has 5 text lines. The lines 1,2 and 4,5 are accessible via config parameters This display has 5 text lines. The lines 1,2 and 4,5 are accessible via config parameters
TEXTLINE_1 and TEXTLINE_2 in channels 1 and 2. Example:<br/><br/> TEXTLINE_1 and TEXTLINE_2 in channels 1 and 2. Example:<br/><br/>

View File

@ -4,11 +4,11 @@
# #
# $Id$ # $Id$
# #
# Version 1.3 # Version 1.4
# #
# Subprocess based RPC Server module for HMCCU. # Subprocess based RPC Server module for HMCCU.
# #
# (c) 2018 by zap (zap01 <at> t-online <dot> de) # (c) 2019 by zap (zap01 <at> t-online <dot> de)
# #
############################################################################## ##############################################################################
# #
@ -35,7 +35,7 @@ use SetExtensions;
###################################################################### ######################################################################
# HMCCURPC version # HMCCURPC version
my $HMCCURPCPROC_VERSION = '1.3'; my $HMCCURPCPROC_VERSION = '1.4';
# 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;
@ -52,6 +52,9 @@ my $HMCCURPCPROC_MAX_QUEUESEND = 70;
# Time to wait after data processing loop in microseconds # Time to wait after data processing loop in microseconds
my $HMCCURPCPROC_TIME_WAIT = 100000; my $HMCCURPCPROC_TIME_WAIT = 100000;
# RPC ping interval for interface BidCos-RF, should be smaller than HMCCURPCPROC_TIMEOUT_EVENT
my $HMCCURPCPROC_TIME_PING = 300;
# Timeout for established CCU connection in seconds # Timeout for established CCU connection in seconds
my $HMCCURPCPROC_TIMEOUT_CONNECTION = 1; my $HMCCURPCPROC_TIMEOUT_CONNECTION = 1;
@ -61,8 +64,8 @@ my $HMCCURPCPROC_TIMEOUT_WRITE = 0.001;
# Timeout for accepting incoming connections in seconds (0 = default) # Timeout for accepting incoming connections in seconds (0 = default)
my $HMCCURPCPROC_TIMEOUT_ACCEPT = 1; my $HMCCURPCPROC_TIMEOUT_ACCEPT = 1;
# Timeout for incoming CCU events in seconds # Timeout for incoming CCU events in seconds (0 = ignore timeout)
my $HMCCURPCPROC_TIMEOUT_EVENT = 600; my $HMCCURPCPROC_TIMEOUT_EVENT = 0;
# Send statistic information after specified amount of events # Send statistic information after specified amount of events
my $HMCCURPCPROC_STATISTICS = 500; my $HMCCURPCPROC_STATISTICS = 500;
@ -200,7 +203,7 @@ sub HMCCURPCPROC_Initialize ($)
$hash->{parseParams} = 1; $hash->{parseParams} = 1;
$hash->{AttrList} = "ccuflags:multiple-strict,expert,reconnect,logEvents,ccuInit,queueEvents,noEvents,logPong". $hash->{AttrList} = "ccuflags:multiple-strict,expert,logEvents,ccuInit,queueEvents,noEvents".
" rpcMaxEvents rpcQueueSend rpcQueueSize rpcMaxIOErrors". " rpcMaxEvents rpcQueueSend rpcQueueSize rpcMaxIOErrors".
" rpcServerAddr rpcServerPort rpcWriteTimeout rpcAcceptTimeout". " rpcServerAddr rpcServerPort rpcWriteTimeout rpcAcceptTimeout".
" rpcConnTimeout rpcStatistics rpcEventTimeout rpcPingCCU ". " rpcConnTimeout rpcStatistics rpcEventTimeout rpcPingCCU ".
@ -356,11 +359,10 @@ sub HMCCURPCPROC_InitDevice ($$) {
Log3 $name, 1, "HMCCURPCPROC: [$name] Initialized version $HMCCURPCPROC_VERSION for interface $ifname with I/O device $ioname"; Log3 $name, 1, "HMCCURPCPROC: [$name] Initialized version $HMCCURPCPROC_VERSION for interface $ifname with I/O device $ioname";
# Set some attributes and start CCU ping # Set some attributes
if ($init_done) { if ($init_done) {
$attr{$name}{stateFormat} = "rpcstate/state"; $attr{$name}{stateFormat} = "rpcstate/state";
$attr{$name}{verbose} = 2;+ $attr{$name}{verbose} = 2;
HMCCURPCPROC_RPCPing ($dev_hash);
} }
HMCCURPCPROC_ResetRPCState ($dev_hash); HMCCURPCPROC_ResetRPCState ($dev_hash);
@ -424,26 +426,19 @@ sub HMCCURPCPROC_Attr ($@)
$hash->{hmccu}{localaddr} = $attrval; $hash->{hmccu}{localaddr} = $attrval;
} }
elsif ($attrname eq 'rpcPingCCU') { elsif ($attrname eq 'rpcPingCCU') {
if ($attrval > 0) { Log3 $name, 1, "HMCCURPCPROC: [$name] Attribute rpcPingCCU ignored. Please set it in I/O device"
if ($hash->{rpcinterface} =~ /^(BidCos-RF|BidCos-Wired|HmIP-RF)$/) {
InternalTimer (gettimeofday()+$attrval, "HMCCURPCPROC_RPCPing", $hash, 0);
} }
else { elsif ($attrname eq 'ccuflags' && $attrval =~ /reconnect/) {
return "HMCCURPCPROC: [$name] RPC Ping not supported by interface ".$hash->{rpcinterface}; Log3 $name, 1, "HMCCURPCPROC: [$name] Flag reconnect ignored. Please set it in I/O device"
}
}
else {
RemoveInternalTimer ($hash, "HMCCURPCPROC_RPCPing");
} }
elsif ($attrname eq 'ccuflags' && $attrval =~ /logPong/) {
Log3 $name, 1, "HMCCURPCPROC: [$name] Flag logPong ignored. Please set it in I/O device"
} }
} }
elsif ($cmd eq 'del') { elsif ($cmd eq 'del') {
if ($attrname eq 'rpcServerAddr') { if ($attrname eq 'rpcServerAddr') {
$hash->{hmccu}{localaddr} = $hash->{hmccu}{defaultaddr}; $hash->{hmccu}{localaddr} = $hash->{hmccu}{defaultaddr};
} }
elsif ($attrname eq 'rpcPingCCU') {
RemoveInternalTimer ($hash, "HMCCURPCPROC_RPCPing");
}
} }
return undef; return undef;
@ -799,8 +794,9 @@ sub HMCCURPCPROC_ProcessEvent ($$)
); );
my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
my $evttimeout = HMCCURPCPROC_GetAttribute ($hash, 'rpcEventTimeout', 'rpcevtimeout', my $ping = AttrVal ($hmccu_hash->{NAME}, 'rpcPingCCU', $HMCCURPCPROC_TIME_PING);
$HMCCURPCPROC_TIMEOUT_EVENT); my $evttimeout = ($ping > 0 && $hash->{rpcinterface} eq 'BidCos-RF') ? $ping*2 :
HMCCURPCPROC_GetAttribute ($hash, 'rpcEventTimeout', 'rpcevtimeout', $HMCCURPCPROC_TIMEOUT_EVENT);
return undef if (!defined ($event) || $event eq ''); return undef if (!defined ($event) || $event eq '');
@ -853,7 +849,7 @@ sub HMCCURPCPROC_ProcessEvent ($$)
$rh->{avgdelay} = $rh->{sumdelay}/$rh->{rec}{$et}; $rh->{avgdelay} = $rh->{sumdelay}/$rh->{rec}{$et};
$hash->{ccustate} = 'active' if ($hash->{ccustate} ne 'active'); $hash->{ccustate} = 'active' if ($hash->{ccustate} ne 'active');
Log3 $name, 3, "HMCCURPCPROC: [$name] Received CENTRAL event from $clkey. ".$t[2]."=".$t[3] Log3 $name, 3, "HMCCURPCPROC: [$name] Received CENTRAL event from $clkey. ".$t[2]."=".$t[3]
if ($t[1] eq 'CENTRAL' && $t[3] eq $rpcname && $ccuflags =~ /logPong/); if ($t[1] eq 'CENTRAL' && $t[3] eq $rpcname && HMCCU_IsFlag ($hmccu_hash->{NAME}, 'logPong'));
my ($add, $chn) = split (/:/, $t[1]); my ($add, $chn) = split (/:/, $t[1]);
return defined ($chn) ? ($et, $clkey, $add, $chn, $t[2], $t[3]) : undef; return defined ($chn) ? ($et, $clkey, $add, $chn, $t[2], $t[3]) : undef;
} }
@ -953,21 +949,15 @@ sub HMCCURPCPROC_ProcessEvent ($$)
elsif ($et eq 'TO') { elsif ($et eq 'TO') {
# #
# Event timeout # Event timeout
# Input: TO|clkey|Time # Input: TO|clkey|DiffTime
# Output: TO, clkey, Port, Time # Output: TO, clkey, Port, DiffTime
# #
if ($evttimeout > 0 && $evttimeout >= $t[0]) { if ($evttimeout > 0 && $t[0] > $evttimeout) {
Log3 $name, 2, "HMCCURPCPROC: [$name] Received no events from interface $clkey for ".$t[0]." seconds"; Log3 $name, 2, "HMCCURPCPROC: [$name] Received no events from interface $clkey for ".$t[0]." seconds";
$hash->{ccustate} = 'timeout'; $hash->{ccustate} = 'timeout';
if ($hash->{RPCState} eq 'running' && $ccuflags =~ /reconnect/) { if ($hash->{RPCState} eq 'running' && $hash->{rpcport} == 2001) {
Log3 $name, 2, "HMCCURPCPROC: [$name] Reconnecting to CCU interface ".$hash->{rpcinterface}; # If interface is BidCos-RF inform IO device about timeout
my ($rc, $rcmsg) = HMCCURPCPROC_RegisterCallback ($hash, 2); HMCCU_EventsTimedOut ($hmccu_hash)
if ($rc) {
$hash->{ccustate} = 'active';
}
else {
Log3 $name, 1, "HMCCURPCPROC: [$name] $rcmsg";
}
} }
DoTrigger ($name, "No events from interface $clkey for ".$t[0]." seconds"); DoTrigger ($name, "No events from interface $clkey for ".$t[0]." seconds");
} }
@ -1227,10 +1217,12 @@ sub HMCCURPCPROC_StartRPCServer ($)
return (0, "RPC server already running") if (HMCCURPCPROC_CheckProcessState ($hash, 'running')); return (0, "RPC server already running") if (HMCCURPCPROC_CheckProcessState ($hash, 'running'));
# Get parameters and attributes # Get parameters and attributes
my %procpar; my $ping = AttrVal ($hmccu_hash->{NAME}, 'rpcPingCCU', $HMCCURPCPROC_TIME_PING);
my $localaddr = HMCCURPCPROC_GetAttribute ($hash, undef, 'rpcserveraddr', $hash->{hmccu}{localaddr}); my $localaddr = HMCCURPCPROC_GetAttribute ($hash, undef, 'rpcserveraddr', $hash->{hmccu}{localaddr});
my $rpcserverport = HMCCURPCPROC_GetAttribute ($hash, 'rpcServerPort', 'rpcserverport', $HMCCURPCPROC_SERVER_PORT); my $rpcserverport = HMCCURPCPROC_GetAttribute ($hash, 'rpcServerPort', 'rpcserverport', $HMCCURPCPROC_SERVER_PORT);
my $evttimeout = HMCCURPCPROC_GetAttribute ($hash, 'rpcEventTimeout', 'rpcevtimeout', $HMCCURPCPROC_TIMEOUT_EVENT); my $evttimeout = ($ping > 0 && $hash->{rpcinterface} eq 'BidCos-RF') ?
$ping*2 :
HMCCURPCPROC_GetAttribute ($hash, 'rpcEventTimeout', 'rpcevtimeout', $HMCCURPCPROC_TIMEOUT_EVENT);
my $ccunum = $hash->{CCUNum}; my $ccunum = $hash->{CCUNum};
my $rpcport = $hash->{rpcport}; my $rpcport = $hash->{rpcport};
my $serveraddr = HMCCU_GetRPCServerInfo ($hmccu_hash, $rpcport, 'host'); my $serveraddr = HMCCU_GetRPCServerInfo ($hmccu_hash, $rpcport, 'host');
@ -1239,16 +1231,16 @@ sub HMCCURPCPROC_StartRPCServer ($)
$hash->{hmccu}{localaddr} = $localaddr; $hash->{hmccu}{localaddr} = $localaddr;
# Store parameters for child process # Store parameters for child process
my %procpar;
$procpar{socktimeout} = AttrVal ($name, 'rpcWriteTimeout', $HMCCURPCPROC_TIMEOUT_WRITE); $procpar{socktimeout} = AttrVal ($name, 'rpcWriteTimeout', $HMCCURPCPROC_TIMEOUT_WRITE);
$procpar{conntimeout} = AttrVal ($name, 'rpcConnTimeout', $HMCCURPCPROC_TIMEOUT_CONNECTION); $procpar{conntimeout} = AttrVal ($name, 'rpcConnTimeout', $HMCCURPCPROC_TIMEOUT_CONNECTION);
$procpar{acctimeout} = AttrVal ($name, 'rpcAcceptTimeout', $HMCCURPCPROC_TIMEOUT_ACCEPT); $procpar{acctimeout} = AttrVal ($name, 'rpcAcceptTimeout', $HMCCURPCPROC_TIMEOUT_ACCEPT);
$procpar{evttimeout} = AttrVal ($name, 'rpcEventTimeout', $HMCCURPCPROC_TIMEOUT_EVENT);
$procpar{queuesize} = AttrVal ($name, 'rpcQueueSize', $HMCCURPCPROC_MAX_QUEUESIZE); $procpar{queuesize} = AttrVal ($name, 'rpcQueueSize', $HMCCURPCPROC_MAX_QUEUESIZE);
$procpar{queuesend} = AttrVal ($name, 'rpcQueueSend', $HMCCURPCPROC_MAX_QUEUESEND); $procpar{queuesend} = AttrVal ($name, 'rpcQueueSend', $HMCCURPCPROC_MAX_QUEUESEND);
$procpar{statistics} = AttrVal ($name, 'rpcStatistics', $HMCCURPCPROC_STATISTICS); $procpar{statistics} = AttrVal ($name, 'rpcStatistics', $HMCCURPCPROC_STATISTICS);
$procpar{maxioerrors} = AttrVal ($name, 'rpcMaxIOErrors', $HMCCURPCPROC_MAX_IOERRORS); $procpar{maxioerrors} = AttrVal ($name, 'rpcMaxIOErrors', $HMCCURPCPROC_MAX_IOERRORS);
$procpar{evttimeout} = AttrVal ($name, 'rpcEventTimeout', $HMCCURPCPROC_TIMEOUT_EVENT);
$procpar{ccuflags} = AttrVal ($name, 'ccuflags', 'null'); $procpar{ccuflags} = AttrVal ($name, 'ccuflags', 'null');
$procpar{evttimeout} = $evttimeout;
$procpar{interface} = $interface; $procpar{interface} = $interface;
$procpar{flags} = HMCCU_GetRPCServerInfo ($hmccu_hash, $rpcport, 'flags'); $procpar{flags} = HMCCU_GetRPCServerInfo ($hmccu_hash, $rpcport, 'flags');
$procpar{type} = HMCCU_GetRPCServerInfo ($hmccu_hash, $rpcport, 'type'); $procpar{type} = HMCCU_GetRPCServerInfo ($hmccu_hash, $rpcport, 'type');
@ -1290,7 +1282,8 @@ sub HMCCURPCPROC_StartRPCServer ($)
if (!$rpcpid) { if (!$rpcpid) {
# Child process, only needs parent socket # Child process, only needs parent socket
HMCCURPCPROC_HandleConnection ($rpcport, $callbackport, $sockparent, \%procpar); HMCCURPCPROC_HandleConnection ($rpcport, $callbackport, $sockparent, \%procpar);
# Exit child process
# Connection loop ended. Close sockets and exit child process
close ($sockparent); close ($sockparent);
close ($sockchild); close ($sockchild);
exit (0); exit (0);
@ -1338,18 +1331,28 @@ sub HMCCURPCPROC_RPCServerStarted ($)
my $hmccu_hash = $hash->{IODev}; my $hmccu_hash = $hash->{IODev};
my $clkey = 'CB'.$hash->{rpcport}.$hash->{rpcid}; my $clkey = 'CB'.$hash->{rpcport}.$hash->{rpcid};
my $ifname = $hash->{rpcinterface}; my $ifname = $hash->{rpcinterface};
my $ping = AttrVal ($hmccu_hash->{NAME}, 'rpcPingCCU', $HMCCURPCPROC_TIME_PING);
# Check if RPC servers are running. Set overall status # Check if RPC servers are running. Set overall status
if (HMCCURPCPROC_CheckProcessState ($hash, 'running')) { if (HMCCURPCPROC_CheckProcessState ($hash, 'running')) {
$hash->{hmccu}{rpcstarttime} = time (); $hash->{hmccu}{rpcstarttime} = time ();
HMCCURPCPROC_SetState ($hash, "OK"); HMCCURPCPROC_SetState ($hash, "OK");
# Update client devices if interface is managed by HMCCURPCPROC device.
# Normally interfaces are managed by HMCCU device.
if ($hmccu_hash->{hmccu}{interfaces}{$ifname}{manager} eq 'HMCCURPCPROC') { if ($hmccu_hash->{hmccu}{interfaces}{$ifname}{manager} eq 'HMCCURPCPROC') {
my ($c_ok, $c_err) = HMCCU_UpdateClients ($hmccu_hash, '.*', 'Attr', 0, $ifname); my ($c_ok, $c_err) = HMCCU_UpdateClients ($hmccu_hash, '.*', 'Attr', 0, $ifname);
Log3 $name, 2, "HMCCURPCPROC: [$name] Updated devices. Success=$c_ok Failed=$c_err"; Log3 $name, 2, "HMCCURPCPROC: [$name] Updated devices. Success=$c_ok Failed=$c_err";
} }
RemoveInternalTimer ($hash); RemoveInternalTimer ($hash, "HMCCURPCPROC_IsRPCServerRunning");
# Activate heartbeat if interface is BidCos-RF and rpcPingCCU > 0
if ($ping > 0 && $ifname eq "BidCos-RF") {
Log3 $name, 1, "HMCCURPCPROC: [$name] Scheduled CCU ping every $ping seconds";
InternalTimer (gettimeofday()+$ping, "HMCCURPCPROC_RPCPing", $hash, 0);
}
DoTrigger ($name, "RPC server $clkey running"); DoTrigger ($name, "RPC server $clkey running");
return 1; return 1;
} }
@ -1649,8 +1652,10 @@ sub HMCCURPCPROC_RPCPing ($)
{ {
my ($hash) = @_; my ($hash) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $hmccu_hash = $hash->{IODev};
my $ping = AttrVal ($hmccu_hash->{NAME}, 'rpcPingCCU', $HMCCURPCPROC_TIME_PING);
my $ping = AttrVal ($name, 'rpcPingCCU', 0); if ($hash->{rpcinterface} eq 'BidCos-RF') {
if ($ping > 0) { if ($ping > 0) {
if ($init_done && HMCCURPCPROC_CheckProcessState ($hash, 'running')) { if ($init_done && HMCCURPCPROC_CheckProcessState ($hash, 'running')) {
my $clkey = 'CB'.$hash->{rpcport}.$hash->{rpcid}; my $clkey = 'CB'.$hash->{rpcport}.$hash->{rpcid};
@ -1658,6 +1663,10 @@ sub HMCCURPCPROC_RPCPing ($)
} }
InternalTimer (gettimeofday()+$ping, "HMCCURPCPROC_RPCPing", $hash, 0); InternalTimer (gettimeofday()+$ping, "HMCCURPCPROC_RPCPing", $hash, 0);
} }
else {
Log3 $name, 1, "HMCCURPCPROC: [$name] CCU ping disabled";
}
}
} }
###################################################################### ######################################################################
@ -2732,11 +2741,9 @@ sub HMCCURPCPROC_DecodeResponse ($)
This flag is not supported by interfaces CUxD and HVL.<br/> This flag is not supported by interfaces CUxD and HVL.<br/>
expert - Activate expert mode<br/> expert - Activate expert mode<br/>
logEvents - Events are written into FHEM logfile if verbose is 4<br/> logEvents - Events are written into FHEM logfile if verbose is 4<br/>
logPong - Write log message when receiving pong event if verbose level is at least 3.<br/>
noEvents - Ignore events from CCU, do not update client device readings.<br/> noEvents - Ignore events from CCU, do not update client device readings.<br/>
queueEvents - Always write events into queue and send them asynchronously to FHEM. queueEvents - Always write events into queue and send them asynchronously to FHEM.
Frequency of event transmission to FHEM depends on attribute rpcConnTimeout.<br/> Frequency of event transmission to FHEM depends on attribute rpcConnTimeout.<br/>
reconnect - Try to re-register at CCU if no events received for rpcEventTimeout seconds<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
@ -2746,9 +2753,9 @@ sub HMCCURPCPROC_DecodeResponse ($)
Specify timeout of incoming CCU connections. Default is 1 second. Value must be greater than 0. Specify timeout of incoming CCU connections. Default is 1 second. Value must be greater than 0.
</li><br/> </li><br/>
<li><b>rpcEventTimeout &lt;seconds&gt;</b><br/> <li><b>rpcEventTimeout &lt;seconds&gt;</b><br/>
Specify timeout for CCU events. Default is 600 seconds. If timeout occurs an event Specify timeout for CCU events. Default is 0, timeout is ignored. If timeout occurs an event
is triggered. If set to 0 the timeout is ignored. If ccuflag reconnect is set the is triggered. If ccuflag reconnect is set in I/O device the RPC device tries to establish a new
RPC device tries to establish a new connection to the CCU. connection to the CCU.
</li><br/> </li><br/>
<li><b>rpcMaxEvents &lt;count&gt;</b><br/> <li><b>rpcMaxEvents &lt;count&gt;</b><br/>
Specify maximum number of events read by FHEM during one I/O loop. If FHEM performance Specify maximum number of events read by FHEM during one I/O loop. If FHEM performance
@ -2761,9 +2768,7 @@ sub HMCCURPCPROC_DecodeResponse ($)
to disable error counting. to disable error counting.
</li><br/> </li><br/>
<li><b>rpcPingCCU &lt;interval&gt;</b><br/> <li><b>rpcPingCCU &lt;interval&gt;</b><br/>
Send RPC ping request to CCU every <i>interval</i> seconds. If <i>interval</i> is 0 Ignored. Should be set in I/O device.
sending ping requests is disabled. If attribut ccuflags is set to logPong a log message
with level 3 is created when receiving a pong event.
</li><br/> </li><br/>
<li><b>rpcQueueSend &lt;events&gt;</b><br/> <li><b>rpcQueueSend &lt;events&gt;</b><br/>
Maximum number of events sent to FHEM per accept loop. Default is 70. If set to 0 Maximum number of events sent to FHEM per accept loop. Default is 70. If set to 0

View File

@ -4,7 +4,7 @@
# #
# $Id$ # $Id$
# #
# Version 4.4 # Version 4.5
# #
# Configuration parameters for HomeMatic devices. # Configuration parameters for HomeMatic devices.
# #
@ -765,7 +765,7 @@ use vars qw(%HMCCU_SCRIPTS);
webCmd => "control:Auto:Manu:Boost:on:off", webCmd => "control:Auto:Manu:Boost:on:off",
widgetOverride => "control:slider,4.5,0.5,30.5,1" widgetOverride => "control:slider,4.5,0.5,30.5,1"
}, },
"HmIP-eTRV|HmIP-eTRV-2" => { "HmIP-eTRV|HmIP-eTRV-2|HmIP-eTRV-B1" => {
_description => "Heizkoerperthermostat HM-IP", _description => "Heizkoerperthermostat HM-IP",
ccureadingfilter => "^ACTUAL_TEMPERATURE|^BOOST_MODE|^SET_POINT_MODE|^SET_POINT_TEMPERATURE|^LEVEL|^WINDOW_STATE", ccureadingfilter => "^ACTUAL_TEMPERATURE|^BOOST_MODE|^SET_POINT_MODE|^SET_POINT_TEMPERATURE|^LEVEL|^WINDOW_STATE",
ccureadingname => "1.LEVEL:valve_position", ccureadingname => "1.LEVEL:valve_position",
@ -782,6 +782,7 @@ use vars qw(%HMCCU_SCRIPTS);
}, },
"HmIP-WTH|HmIP-WTH-2|HmIP-BWTH" => { "HmIP-WTH|HmIP-WTH-2|HmIP-BWTH" => {
_description => "Wandthermostat HM-IP", _description => "Wandthermostat HM-IP",
ccureadingfilter => ".*",
controldatapoint => "1.SET_POINT_TEMPERATURE", controldatapoint => "1.SET_POINT_TEMPERATURE",
eventMap => "/datapoint 1.BOOST_MODE true:Boost/datapoint 1.CONTROL_MODE 0:Auto/datapoint 1.CONTROL_MODE 1:Manual/datapoint 1.CONTROL_MODE 2:Holiday/datapoint 1.SET_POINT_TEMPERATURE 4.5:off/datapoint 1.SET_POINT_TEMPERATURE 30.5:on/", eventMap => "/datapoint 1.BOOST_MODE true:Boost/datapoint 1.CONTROL_MODE 0:Auto/datapoint 1.CONTROL_MODE 1:Manual/datapoint 1.CONTROL_MODE 2:Holiday/datapoint 1.SET_POINT_TEMPERATURE 4.5:off/datapoint 1.SET_POINT_TEMPERATURE 30.5:on/",
genericDeviceType => "thermostat", genericDeviceType => "thermostat",
@ -1002,7 +1003,7 @@ use vars qw(%HMCCU_SCRIPTS);
syntax => "name, mode", syntax => "name, mode",
parameters => 2, parameters => 2,
code => qq( code => qq(
object oPR = dom.GetObject("\$name"); object oPR = (dom.GetObject(ID_PROGRAMS)).Get("\$name");
if (oPR) { if (oPR) {
oPR.Active(\$mode); oPR.Active(\$mode);
} }
@ -1026,7 +1027,7 @@ if (!oSV) {
oSV.State("\$init"); oSV.State("\$init");
oSV.Internal(false); oSV.Internal(false);
oSV.Visible(true); oSV.Visible(true);
dom.RTUpdate(false); dom.RTUpdate(0);
} }
else { else {
oSV.State("\$init"); oSV.State("\$init");
@ -1053,7 +1054,7 @@ if (!oSV) {
oSV.State("\$init"); oSV.State("\$init");
oSV.Internal(false); oSV.Internal(false);
oSV.Visible(true); oSV.Visible(true);
dom.RTUpdate(false); dom.RTUpdate(0);
} }
else { else {
oSV.State("\$init"); oSV.State("\$init");
@ -1078,7 +1079,7 @@ if (!oSV) {
oSV.DPInfo("\$desc"); oSV.DPInfo("\$desc");
oSV.ValueUnit("\$unit"); oSV.ValueUnit("\$unit");
oSV.State("\$init"); oSV.State("\$init");
dom.RTUpdate(false); dom.RTUpdate(0);
} }
else { else {
oSV.State("\$init"); oSV.State("\$init");
@ -1102,7 +1103,7 @@ if (!oSV){
oSV.DPInfo("\$desc"); oSV.DPInfo("\$desc");
oSV.ValueUnit("\$unit"); oSV.ValueUnit("\$unit");
oSV.State("\$init"); oSV.State("\$init");
dom.RTUpdate(false); dom.RTUpdate(0);
} }
else { else {
oSV.State("\$init"); oSV.State("\$init");
@ -1129,7 +1130,7 @@ if (oSV) {
code => qq( code => qq(
object osysvar; object osysvar;
string ssysvarid; string ssysvarid;
foreach (ssysvarid, dom.GetObject(ID_SYSTEM_VARIABLES).EnumUsedIDs()) { foreach (ssysvarid, (dom.GetObject(ID_SYSTEM_VARIABLES)).EnumUsedIDs()) {
osysvar = dom.GetObject(ssysvarid); osysvar = dom.GetObject(ssysvarid);
Write(osysvar.Name()); Write(osysvar.Name());
if(osysvar.ValueSubType() == 6) { if(osysvar.ValueSubType() == 6) {
@ -1148,7 +1149,7 @@ foreach (ssysvarid, dom.GetObject(ID_SYSTEM_VARIABLES).EnumUsedIDs()) {
parameters => 0, parameters => 0,
code => qq( code => qq(
string sSysVarId; string sSysVarId;
foreach (sSysVarId, dom.GetObject(ID_SYSTEM_VARIABLES).EnumUsedIDs()) { foreach (sSysVarId, (dom.GetObject(ID_SYSTEM_VARIABLES)).EnumUsedIDs()) {
object oSysVar = dom.GetObject(sSysVarId); object oSysVar = dom.GetObject(sSysVarId);
Write(oSysVar.Name()); Write(oSysVar.Name());
if (oSysVar.ValueSubType() == 6) { if (oSysVar.ValueSubType() == 6) {
@ -1181,7 +1182,7 @@ foreach (sSysVarId, dom.GetObject(ID_SYSTEM_VARIABLES).EnumUsedIDs()) {
code => qq( code => qq(
string chnid; string chnid;
string sDPId; string sDPId;
object odev = dom.GetObject ("\$devname"); object odev = (dom.GetObject(ID_DEVICES)).Get("\$devname");
if (odev) { if (odev) {
foreach (chnid, odev.Channels()) { foreach (chnid, odev.Channels()) {
object ochn = dom.GetObject(chnid); object ochn = dom.GetObject(chnid);
@ -1210,13 +1211,15 @@ else {
syntax => "name", syntax => "name",
parameters => 1, parameters => 1,
code => qq( code => qq(
object odev=dom.GetObject("\$name"); object odev = (dom.GetObject(ID_DEVICES)).Get("\$name");
if (odev) { if (!odev) {
if (odev.IsTypeOf (OT_CHANNEL)) { object ochn = (dom.GetObject(ID_CHANNELS)).Get("\$name");
string devid = odev.Device(); if(ochn) {
string devid = ochn.Device();
odev = dom.GetObject (devid); odev = dom.GetObject (devid);
} }
}
if(odev) {
string intid=odev.Interface(); string intid=odev.Interface();
string intna=dom.GetObject(intid).Name(); string intna=dom.GetObject(intid).Name();
string chnid; string chnid;
@ -1313,7 +1316,7 @@ string sDevName;
string sDevList = "\$list"; string sDevList = "\$list";
integer c = 0; integer c = 0;
foreach (sDevName, sDevList.Split(",")) { foreach (sDevName, sDevList.Split(",")) {
object odev = dom.GetObject (sDevName); object odev = (dom.GetObject(ID_DEVICES)).Get(sDevName);
if (odev) { if (odev) {
foreach (chnid, odev.Channels()) { foreach (chnid, odev.Channels()) {
object ochn = dom.GetObject(chnid); object ochn = dom.GetObject(chnid);
@ -1349,7 +1352,7 @@ string sDPId;
string sDevice; string sDevice;
string sDevList = "\$list"; string sDevList = "\$list";
foreach (sDevice, sDevList.Split(",")) { foreach (sDevice, sDevList.Split(",")) {
object odev = dom.GetObject (sDevice); object odev = (dom.GetObject(ID_DEVICES)).Get(sDevice);
if (odev) { if (odev) {
string intid = odev.Interface(); string intid = odev.Interface();
string intna = dom.GetObject(intid).Name(); string intna = dom.GetObject(intid).Name();
@ -1384,7 +1387,7 @@ string sDPId;
string sChannel; string sChannel;
string sChnList = "\$list"; string sChnList = "\$list";
foreach (sChannel, sChnList.Split(",")) { foreach (sChannel, sChnList.Split(",")) {
object oChannel = dom.GetObject (sChannel); object oChannel = (dom.GetObject(ID_CHANNELS)).Get(sChannel);
if (oChannel) { if (oChannel) {
foreach(sDPId, oChannel.DPs()) { foreach(sDPId, oChannel.DPs()) {
object oDP = dom.GetObject(sDPId); object oDP = dom.GetObject(sDPId);