2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-09 07:44:19 +00:00

HMCCU: New commands and bug fixes

git-svn-id: https://svn.fhem.de/fhem/trunk@18552 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
zap 2019-02-10 11:52:28 +00:00
parent 32fa778897
commit 62e257e40c
6 changed files with 493 additions and 138 deletions

View File

@ -1,5 +1,6 @@
# 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.
- feature: 88_HMCCU: New commands and bug fixes
- changed: 93_DbLog: CommandRef revised
- feature: 98_HTTPMOD: starting with featurelevel >5.9 enableCookies,
enableControlSet, handleRedirects and

View File

@ -4,7 +4,7 @@
#
# $Id$
#
# Version 4.3.010
# Version 4.3.011
#
# Module for communication between FHEM and Homematic CCU2/3.
#
@ -51,7 +51,7 @@ my %HMCCU_CUST_CHN_DEFAULTS;
my %HMCCU_CUST_DEV_DEFAULTS;
# HMCCU version
my $HMCCU_VERSION = '4.3.010';
my $HMCCU_VERSION = '4.3.011';
# Default RPC interface and port (BidCos-RF)
my $HMCCU_RPC_PORT_DEFAULT = 2001;
@ -73,6 +73,11 @@ my $HMCCU_TIMEOUT_EVENT = 600;
my $HMCCU_STATISTICS = 500;
my $HMCCU_TIMEOUT_REQUEST = 4;
# ReGa Ports
my %HMCCU_REGA_PORT = (
'http' => 8181, 'https' => '48181'
);
# RPC port name by port number
my %HMCCU_RPC_NUMPORT = (
2000 => 'BidCos-Wired', 2001 => 'BidCos-RF', 2010 => 'HmIP-RF', 9292 => 'VirtualDevices',
@ -87,10 +92,15 @@ my %HMCCU_RPC_PORT = (
# RPC flags
my %HMCCU_RPC_FLAG = (
2000 => 'forceASCII', 2001 => 'forceASCII', 2003 => '_', 2010 => '_',
2000 => 'forceASCII', 2001 => 'forceASCII', 2003 => '_', 2010 => 'forceASCII',
7000 => 'forceInit', 8701 => 'forceInit', 9292 => '_'
);
my %HMCCU_RPC_SSL = (
2000 => 1, 2001 => 1, 2010 => 1, 9292 => 1,
'BidCos-Wired' => 1, 'BidCos-RF' => 1, 'HmIP-RF' => 1, 'VirtualDevices' => 1
);
# Initial intervals for registration of RPC callbacks and reading RPC queue
#
# X = Start RPC server
@ -323,14 +333,19 @@ sub HMCCU_QueueEnq ($$);
sub HMCCU_QueueDeq ($);
# Helper functions
sub HMCCU_BuildURL ($$);
sub HMCCU_CalculateReading ($$);
sub HMCCU_CorrectName ($);
sub HMCCU_Encrypt ($);
sub HMCCU_Decrypt ($);
sub HMCCU_DeleteReadings ($$);
sub HMCCU_EncodeEPDisplay ($);
sub HMCCU_ExprMatch ($$$);
sub HMCCU_ExprNotMatch ($$$);
sub HMCCU_GetDutyCycle ($);
sub HMCCU_GetHMState ($$$);
sub HMCCU_GetIdFromIP ($$);
sub HMCCU_GetTimeSpec ($);
sub HMCCU_CorrectName ($);
sub HMCCU_RefToString ($);
sub HMCCU_ResolveName ($$);
sub HMCCU_TCPConnect ($$);
@ -392,8 +407,17 @@ sub HMCCU_Define ($$)
my $name = $hash->{NAME};
return "Specify CCU hostname or IP address as a parameter" if (scalar (@$a) < 3);
$hash->{host} = $$a[2];
# Setup http or ssl connection
if ($$a[2] =~ /^(https?):\/\/(.+)/) {
$hash->{prot} = $1;
$hash->{host} = $2;
}
else {
$hash->{prot} = 'http';
$hash->{host} = $$a[2];
}
$hash->{Clients} = ':HMCCUDEV:HMCCUCHN:HMCCURPC:HMCCURPCPROC:';
$hash->{hmccu}{ccu}{delay} = exists ($h->{ccudelay}) ? $h->{ccudelay} : $HMCCU_CCU_BOOT_DELAY;
$hash->{hmccu}{ccu}{timeout} = exists ($h->{waitforccu}) ? $h->{waitforccu} : $HMCCU_CCU_PING_TIMEOUT;
@ -408,12 +432,12 @@ sub HMCCU_Define ($$)
HMCCU_Log ($hash, 1, "Forced delayed initialization", 0);
}
else {
if (HMCCU_TCPPing ($hash->{host}, 8181, $hash->{hmccu}{ccu}{timeout})) {
if (HMCCU_TCPPing ($hash->{host}, $HMCCU_REGA_PORT{$hash->{prot}}, $hash->{hmccu}{ccu}{timeout})) {
$hash->{ccustate} = 'active';
}
else {
$hash->{ccustate} = 'unreachable';
HMCCU_Log ($hash, 1, "CCU port 8181 is not reachable", 0);
HMCCU_Log ($hash, 1, "CCU port ".$HMCCU_REGA_PORT{$hash->{prot}}." is not reachable", 0);
}
}
@ -481,7 +505,7 @@ sub HMCCU_Define ($$)
# Initialization of FHEM device.
# Called during Define() or by HMCCU after CCU ready.
# Return 0 on successful initialization or >0 on error:
# 1 = CCU port 8181 is not reachable.
# 1 = CCU port 8181 or 48181 is not reachable.
# 2 = Error while reading device list from CCU.
######################################################################
@ -492,9 +516,9 @@ sub HMCCU_InitDevice ($)
if ($hash->{hmccu}{ccu}{delayed} == 1) {
HMCCU_Log ($hash, 1, "HMCCU: Initializing devices", 0);
if (!HMCCU_TCPPing ($hash->{host}, 8181, $hash->{hmccu}{ccu}{timeout})) {
if (!HMCCU_TCPPing ($hash->{host}, $HMCCU_REGA_PORT{$hash->{prot}}, $hash->{hmccu}{ccu}{timeout})) {
$hash->{ccustate} = 'unreachable';
HMCCU_Log ($hash, 1, "HMCCU: CCU port 8181 is not reachable", 0);
HMCCU_Log ($hash, 1, "HMCCU: CCU port ".$HMCCU_REGA_PORT{$hash->{prot}}." is not reachable", 0);
return 1;
}
}
@ -1169,12 +1193,12 @@ sub HMCCU_Detail ($$$$)
<table class="block wide">
<tr class="odd">
<td><div class="col1">
&gt; <a target="_blank" href="http://$hash->{host}">CCU WebUI</a>
&gt; <a target="_blank" href="$hash->{prot}://$hash->{host}">CCU WebUI</a>
</div></td>
</tr>
<tr class="odd">
<td><div class="col1">
&gt; <a target="_blank" href="http://$hash->{host}/addons/cuxd/index.ccc">CUxD Config</a>
&gt; <a target="_blank" href="$hash->{prot}://$hash->{host}/addons/cuxd/index.ccc">CUxD Config</a>
</div></td>
</tr>
</table>
@ -1379,8 +1403,8 @@ sub HMCCU_Set ($@)
my ($hash, $a, $h) = @_;
my $name = shift @$a;
my $opt = shift @$a;
my $options = "var delete execute hmscript cleardefaults:noArg defaults:noArg ".
"importdefaults rpcregister:all rpcserver:on,off,restart ackmessages:noArg";
my $options = "var clear delete execute hmscript cleardefaults:noArg defaults:noArg ".
"importdefaults rpcregister:all rpcserver:on,off,restart ackmessages:noArg authentication";
my @ifList = HMCCU_GetRPCInterfaceList ($hash);
if (scalar (@ifList) > 0) {
my $ifStr = join (',', @ifList);
@ -1389,7 +1413,9 @@ sub HMCCU_Set ($@)
my $usage = "HMCCU: Unknown argument $opt, choose one of $options";
my $host = $hash->{host};
return "HMCCU: I/O device not initialized. Try again later." if ($hash->{hmccu}{ccu}{delayed});
return undef if ($hash->{hmccu}{ccu}{delayed});
return "HMCCU: CCU is unreachable, choose one of initialize:noArg" if ($hash->{ccustate} eq 'unreachable');
return undef if ($hash->{ccustate} ne 'active');
return "HMCCU: CCU busy, choose one of rpcserver:off"
if ($opt ne 'rpcserver' && HMCCU_IsRPCStateBlocking ($hash));
@ -1419,7 +1445,7 @@ sub HMCCU_Set ($@)
$vartype = shift @$a if (scalar (@$a) == 3);
my $objname = shift @$a;
my $objvalue = shift @$a;
$usage = "set $name $opt [{'bool'|'list'|'number'|'test'}] variable value [param=value [...]]";
$usage = "set $name $opt [{'bool'|'list'|'number'|'text'}] variable value [param=value [...]]";
return HMCCU_SetError ($hash, $usage) if (!defined ($objvalue));
@ -1432,6 +1458,48 @@ sub HMCCU_Set ($@)
return HMCCU_SetError ($hash, $result) if ($result < 0);
return HMCCU_SetState ($hash, "OK");
}
# elsif ($opt eq 'test') {
# my $backend = shift @$a;
# my $url = defined ($backend) ? HMCCU_BuildURL ($hash, $backend) : '';
# return "URL=$url";
# }
elsif ($opt eq 'initialize') {
return HMCCU_SetError ($hash, "State of CCU must be unreachable")
if ($hash->{ccustate} ne 'unreachable');
my $err = HMCCU_InitDevice ($hash);
return HMCCU_SetError ($hash, "CCU not reachable") if ($err == 1);
return HMCCU_SetError ($hash, "Can't read device list from CCU") if ($err == 2);
return HMCCU_SetState ($hash, "OK");
}
elsif ($opt eq 'authentication') {
my $username = shift @$a;
my $password = shift @$a;
$usage = "set $name $opt username password";
if (!defined ($username)) {
setKeyValue ($name."_username", undef);
setKeyValue ($name."_password", undef);
return "Credentials for CCU authentication deleted";
}
return HMCCU_SetError ($hash, $usage) if (!defined ($password));
my $encuser = HMCCU_Encrypt ($username);
my $encpass = HMCCU_Encrypt ($password);
return HMCCU_SetError ($hash, "Encryption of credentials failed") if ($encuser eq '' || $encpass eq '');
my $err = setKeyValue ($name."_username", $encuser);
return HMCCU_SetError ($hash, "Can't store credentials. $err") if (defined ($err));
$err = setKeyValue ($name."_password", $encpass);
return HMCCU_SetError ($hash, "Can't store credentials. $err") if (defined ($err));
return "Credentials for CCU authentication stored";
}
elsif ($opt eq 'clear') {
my $rnexp = shift @$a;
HMCCU_DeleteReadings ($hash, $rnexp);
return HMCCU_SetState ($hash, "OK");
}
elsif ($opt eq 'datapoint') {
return HMCCU_SetError ($hash, "Command set datapoint is no longer supported by I/O device");
}
@ -1626,11 +1694,12 @@ sub HMCCU_Get ($@)
my $opt = shift @$a;
my $options = "defaults:noArg exportdefaults devicelist dump dutycycle:noArg vars update".
" updateccu parfile configdesc firmware rpcevents:noArg rpcstate:noArg deviceinfo";
" updateccu parfile configdesc firmware rpcevents:noArg rpcstate:noArg deviceinfo".
" ccumsg:alarm,service";
my $usage = "HMCCU: Unknown argument $opt, choose one of $options";
my $host = $hash->{host};
return "HMCCU: I/O device not initialized. Try again later." if ($hash->{hmccu}{ccu}{delayed});
return undef if ($hash->{hmccu}{ccu}{delayed} || $hash->{ccustate} ne 'active');
return "HMCCU: CCU busy, choose one of rpcstate:noArg"
if ($opt ne 'rpcstate' && HMCCU_IsRPCStateBlocking ($hash));
@ -1876,7 +1945,7 @@ sub HMCCU_Get ($@)
# Define new client device
my $ret = CommandDefine (undef, $devname." $defmod ".$add);
if ($ret) {
Log3 $name, 2, "HMCCU: Define command failed $devname $defmod $ccuname";
Log3 $name, 2, "HMCCU: [$name] Define command failed $devname $defmod $ccuname";
Log3 $name, 2, "$defmod: $ret";
$result .= "\nCan't create device $devname. $ret";
next;
@ -1996,6 +2065,24 @@ sub HMCCU_Get ($@)
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
return HMCCU_SetState ($hash, "OK", $res);
}
elsif ($opt eq 'ccumsg') {
my $msgtype = shift @$a;
$usage = "Usage: get $name $opt {service|alarm}";
return HMCCU_SetError ($hash, $usage) if (!defined ($msgtype));
my $script = ($msgtype eq 'service') ? "!GetServiceMessages" : "!GetAlarms";
my $res = HMCCU_HMScriptExt ($hash, $script, undef);
return HMCCU_SetError ($hash, "Error") if ($res eq '' || $res =~ /^ERROR:.*/);
# Generate event for each message
foreach my $msg (split /\n/, $res) {
next if ($msg =~ /^[0-9]+$/);
DoTrigger ($name, $msg);
}
return HMCCU_SetState ($hash, "OK", $res);
}
else {
if (exists ($hash->{hmccu}{agg})) {
my @rules = keys %{$hash->{hmccu}{agg}};
@ -2186,9 +2273,7 @@ sub HMCCU_FilterReading ($$$)
return 1 if (!defined ($hmccu_hash));
my $grf = AttrVal ($hmccu_hash->{NAME}, 'ccudef-readingfilter', '.*');
# $grf = '.*' if ($grf eq '');
my $rf = AttrVal ($name, 'ccureadingfilter', $grf);
# $rf = $grf.";".$rf if ($rf ne $grf && $grf ne '.*' && $grf ne '');
$rf = $grf.";".$rf if ($rf ne $grf && $grf ne '.*');
my $chnnam = '';
@ -3525,9 +3610,12 @@ sub HMCCU_GetRPCCallbackURL ($$$$$)
my $ifname = $iface =~ /^[0-9]+$/ ? $hmccu_hash->{hmccu}{ifports}{$iface} : $iface;
return undef if (!exists ($hmccu_hash->{hmccu}{interfaces}{$ifname}));
return $hmccu_hash->{hmccu}{interfaces}{$ifname}{prot}."://$localaddr:$cbport/fh".
my $url = $hmccu_hash->{hmccu}{interfaces}{$ifname}{prot}."://$localaddr:$cbport/fh".
$hmccu_hash->{hmccu}{interfaces}{$ifname}{port};
$url =~ s/^https/http/;
return $url;
}
######################################################################
@ -3535,6 +3623,7 @@ sub HMCCU_GetRPCCallbackURL ($$$$$)
# Parameter iface can be a port number or an interface name.
# Valid values for info are:
# url, port, prot, host, type, name, flags, device.
# Return undef for invalid interface or info token.
######################################################################
sub HMCCU_GetRPCServerInfo ($$$)
@ -3760,7 +3849,7 @@ sub HMCCU_StopExtRPCServer ($)
next;
}
$hash->{hmccu}{interfaces}{$ifname}{manager} = 'HMCCU';
$rc &= HMCCURPCPROC_StopRPCServer ($defs{$rpcdev});
$rc &= HMCCURPCPROC_StopRPCServer ($defs{$rpcdev}, undef);
}
return $rc;
@ -3867,7 +3956,7 @@ sub HMCCU_StartIntRPCServer ($)
# Initialize statistic counters
HMCCU_ResetCounters ($hash);
Log3 $name, 0, "RPC server(s) starting";
Log3 $name, 0, "HMCCU: [$name] RPC server(s) starting";
DoTrigger ($name, "RPC server starting");
InternalTimer (gettimeofday()+$rpcinterval, 'HMCCU_ReadRPCQueue', $hash, 0);
@ -4252,6 +4341,7 @@ sub HMCCU_GetDeviceList ($)
# Delete old entries
%{$hash->{hmccu}{dev}} = ();
%{$hash->{hmccu}{adr}} = ();
%{$hash->{hmccu}{interfaces}} = ();
%{$hash->{hmccu}{grp}} = ();
%{$hash->{hmccu}{prg}} = ();
$hash->{hmccu}{updatetime} = time ();
@ -4319,6 +4409,10 @@ sub HMCCU_GetDeviceList ($)
$port -= 30000;
$ifurl =~ s/:3$port/:$port/;
}
# if ($hash->{prot} eq 'https') {
# # For secure connections add 40000 to port
# $ifurl =~ s/:$port/:4$port/;
# }
if ($hash->{ccuip} ne 'N/A') {
$ifurl =~ s/127\.0\.0\.1/$hash->{ccuip}/;
$ipaddr =~ s/127\.0\.0\.1/$hash->{ccuip}/;
@ -5121,6 +5215,7 @@ sub HMCCU_GetRPCDevice ($$$)
my $rpcdevname;
my $rpcdevtype = 'HMCCURPCPROC';
my $rpchost = $hash->{host};
my $rpcprot = $hash->{prot};
my $ccuflags = HMCCU_GetFlags ($name);
@ -5156,8 +5251,12 @@ sub HMCCU_GetRPCDevice ($$$)
my $devhash = $defs{$dev};
next if ($devhash->{TYPE} ne $rpcdevtype);
my $ip = 'null';
my $addrnum = inet_aton ($devhash->{host});
$ip = inet_ntoa ($addrnum) if (defined ($addrnum));
if (!exists ($devhash->{rpcip})) {
$ip = HMCCU_Resolve ($devhash->{host}, 'null');
}
else {
$ip = $devhash->{rpcip};
}
next if ($devhash->{host} ne $rpchost && $ip ne $rpchost);
# next if ($rpcdevtype eq 'HMCCURPCPROC' && $devhash->{rpcinterface} ne $ifname);
next if ($devhash->{rpcinterface} ne $ifname);
@ -5181,15 +5280,20 @@ sub HMCCU_GetRPCDevice ($$$)
# Create RPC device
if ($create) {
my $alias = "CCU RPC";
my $rpccreate = "d_rpc $rpcdevtype ".$hash->{host};
my $alias = "CCU RPC $ifname";
my $rpccreate = '';
$rpcdevname = "d_rpc";
if (defined ($ifname)) {
$rpcdevname = makeDeviceName ("d_rpc".$ifname);
$alias .= " $ifname";
$rpccreate = "$rpcdevname $rpcdevtype $rpchost $ifname";
}
# Ensure unique device name by appending last 2 digits of CCU IP address
$rpcdevname .= HMCCU_GetIdFromIP ($hash->{ccuip}, '') if (exists ($hash->{ccuip}));
# Build device name and define command
$rpcdevname = makeDeviceName ($rpcdevname.$ifname);
$rpccreate = "$rpcdevname $rpcdevtype $rpcprot://$rpchost $ifname";
return (HMCCU_Log ($hash, 2, "Device $rpcdevname already exists. Please delete or rename it.", ''), 0)
if (exists ($defs{"$rpcdevname"}));
# Create RPC device
HMCCU_Log ($hash, 1, "Creating new RPC device $rpcdevname", undef);
my $ret = CommandDefine (undef, $rpccreate);
if (!defined ($ret)) {
@ -5237,7 +5341,7 @@ sub HMCCU_AssignIODevice ($$$)
return 0;
}
if ($type eq 'HMCCURPCPROC' && defined ($ifname)) {
if ($type eq 'HMCCURPCPROC' && defined ($ifname) && exists ($hmccu_hash->{hmccu}{interfaces}{$ifname})) {
# Register RPC device
$hmccu_hash->{hmccu}{interfaces}{$ifname}{device} = $name;
}
@ -5794,7 +5898,7 @@ sub HMCCU_ReadRPCQueue ($)
if ($hash->{hmccu}{evtime} > 0 && time()-$hash->{hmccu}{evtime} > $rpcevtimeout &&
$hash->{hmccu}{evtimeout} == 0) {
$hash->{hmccu}{evtimeout} = 1;
$hash->{ccustate} = HMCCU_TCPConnect ($hash->{host}, 8181) ne '' ? 'timeout' : 'unreachable';
$hash->{ccustate} = HMCCU_TCPConnect ($hash->{host}, $HMCCU_REGA_PORT{$hash->{prot}}) ne '' ? 'timeout' : 'unreachable';
Log3 $name, 2, "HMCCU: Received no events from CCU since $rpcevtimeout seconds";
DoTrigger ($name, "No events from CCU since $rpcevtimeout seconds");
}
@ -5858,6 +5962,7 @@ sub HMCCU_ReadRPCQueue ($)
######################################################################
# Execute Homematic command on CCU.
# If parameter mode is 1 an empty string is a valid result.
# Return undef on error.
######################################################################
sub HMCCU_HMCommand ($$$)
@ -5868,20 +5973,26 @@ sub HMCCU_HMCommand ($$$)
my $io_hash = HMCCU_GetHash ($cl_hash);
my $ccureqtimeout = AttrVal ($io_hash->{NAME}, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST);
my $url = "http://".$io_hash->{host}.":8181/tclrega.exe";
my $url = HMCCU_BuildURL ($io_hash, 'rega');
my $value;
HMCCU_Trace ($cl_hash, 2, $fnc, "URL=$url, cmd=$cmd");
my $response = GetFileFromURL ($url, $ccureqtimeout, $cmd);
if (defined ($response)) {
my $param = { url => $url, timeout => $ccureqtimeout, data => $cmd, method => "POST" };
$param->{sslargs} = { SSL_verify_mode => 0 };
my ($err, $response) = HttpUtils_BlockingGet ($param);
# my $response = GetFileFromURL ($url, $ccureqtimeout, $cmd);
if ($err eq '') {
$value = $response;
$value =~ s/<xml>(.*)<\/xml>//;
$value =~ s/\r//g;
HMCCU_Trace ($cl_hash, 2, $fnc, "Response=$response, Value=".(defined ($value) ? $value : "undef"));
}
else {
HMCCU_Trace ($cl_hash, 2, $fnc, "Response=undef");
HMCCU_Log ($io_hash, 2, "Error during HTTP request: $err", undef);
HMCCU_Trace ($cl_hash, 2, $fnc, "Response=$response");
return undef;
}
if ($mode == 1) {
@ -5904,18 +6015,20 @@ sub HMCCU_HMCommandNB ($$$)
my $io_hash = HMCCU_GetHash ($cl_hash);
my $ccureqtimeout = AttrVal ($io_hash->{NAME}, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST);
my $url = "http://".$io_hash->{host}.":8181/tclrega.exe";
my $url = HMCCU_BuildURL ($io_hash, 'rega');
HMCCU_Trace ($cl_hash, 2, $fnc, "URL=$url");
if (defined ($cbfunc)) {
my $param = { url => $url, timeout => $ccureqtimeout, data => $cmd, method => "POST",
callback => $cbfunc, devhash => $cl_hash };
$param->{sslargs} = { SSL_verify_mode => 0 };
HttpUtils_NonblockingGet ($param);
}
else {
my $param = { url => $url, timeout => $ccureqtimeout, data => $cmd, method => "POST",
callback => \&HMCCU_HMCommandCB, devhash => $cl_hash };
$param->{sslargs} = { SSL_verify_mode => 0 };
HttpUtils_NonblockingGet ($param);
}
}
@ -5957,6 +6070,8 @@ sub HMCCU_HMScriptExt ($$$)
return HMCCU_LogError ($hash, 2, "CCU host name not defined") if (!exists ($hash->{host}));
my $host = $hash->{host};
my $ccureqtimeout = AttrVal ($hash->{NAME}, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST);
if ($hmscript =~ /^!(.*)$/) {
# Internal script
$scrname = $1;
@ -6004,19 +6119,25 @@ sub HMCCU_HMScriptExt ($$$)
}
# Execute script on CCU
my $url = "http://".$host.":8181/tclrega.exe";
my $ua = new LWP::UserAgent ();
my $response = $ua->post($url, Content => $code);
if ($response->is_success ()) {
my $output = $response->content;
my $url = HMCCU_BuildURL ($hash, 'rega');
my $param = { url => $url, timeout => $ccureqtimeout, data => $code, method => "POST" };
$param->{sslargs} = { SSL_verify_mode => 0 };
my ($err, $response) = HttpUtils_BlockingGet ($param);
# my $ua = new LWP::UserAgent ();
# my $response = $ua->post($url, Content => $code);
# if ($response->is_success ()) {
# my $output = $response->content;
if ($err eq '') {
my $output = $response;
$output =~ s/<xml>.*<\/xml>//;
$output =~ s/\r//g;
return $output;
}
else {
my $msg = $response->status_line();
Log3 $name, 2, "HMCCU: HMScript failed. $msg";
return "ERROR: HMScript failed. $msg";
# my $msg = $response->status_line();
HMCCU_Log ($hash, 2, "HMScript failed. $err", undef);
return "ERROR: HMScript failed. $err";
}
}
@ -6027,9 +6148,28 @@ sub HMCCU_HMScriptExt ($$$)
sub HMCCU_BulkUpdate ($$$$)
{
my ($hash, $reading, $orgval, $subval) = @_;
my $excl = AttrVal ($hash->{NAME}, 'substexcl', '');
my $name = $hash->{NAME};
my $excl = AttrVal ($name, 'substexcl', '');
#
# For later use: Suppress reading update
#
# my $suppress = AttrVal ($name, 'ccusuppress', '');
#
# if ($suppress ne '') {
# my $ct = time();
# my @srules = split (";", $suppress);
#
# foreach my $sr (@srules) {
# my ($rnexp, $to) = split (":", $sr);
# next if (!defined ($to));
# if ($reading =~ /$rnexp/) {
# my $rt = ReadingsTimestamp ($name, $reading, '');
# return if ($rt ne '' && $ct-time_str2num($rt) < $to);
# }
# }
# }
readingsBulkUpdate ($hash, $reading, ($excl ne '' && $reading =~ /$excl/ ? $orgval : $subval));
}
@ -6664,7 +6804,8 @@ sub HMCCU_RPCGetConfig ($$$$)
}
}
else {
my $url = HMCCU_GetRPCServerInfo ($hmccu_hash, $int, 'url');
# my $url = HMCCU_GetRPCServerInfo ($hmccu_hash, $int, 'url');
my $url = HMCCU_BuildURL ($hmccu_hash, $int);
return (-9, '') if (!defined ($url));
HMCCU_Trace ($hash, 2, $fnc, "Method=$method Addr=$addr Port=$port");
my $client = RPC::XML::Client->new ($url);
@ -6793,7 +6934,8 @@ sub HMCCU_RPCSetConfig ($$$)
}
}
else {
my $url = HMCCU_GetRPCServerInfo ($hmccu_hash, $int, 'url');
# my $url = HMCCU_GetRPCServerInfo ($hmccu_hash, $int, 'url');
my $url = HMCCU_BuildURL ($hmccu_hash, $int);
return -9 if (!defined ($url));
my $client = RPC::XML::Client->new ($url);
$res = $client->simple_request ("putParamset", $addr, "MASTER", $parref);
@ -7023,6 +7165,51 @@ sub HMCCU_GetTimeSpec ($)
return ($s-$cs);
}
######################################################################
# Build ReGa or RPC client URL
# Parameter backend specifies type of URL, 'rega' or name or port of
# RPC interface.
# Return empty string on error.
######################################################################
sub HMCCU_BuildURL ($$)
{
my ($hash, $backend) = @_;
my $name = $hash->{NAME};
my $url = '';
my $username = '';
my $password = '';
my ($erruser, $encuser) = getKeyValue ($name."_username");
my ($errpass, $encpass) = getKeyValue ($name."_password");
if (!defined ($erruser) && !defined ($errpass) && defined ($encuser) && defined ($encpass)) {
$username = HMCCU_Decrypt ($encuser);
$password = HMCCU_Decrypt ($encpass);
}
my $auth = ($username ne '' && $password ne '') ? "$username:$password".'@' : '';
if ($backend eq 'rega') {
$url = $hash->{prot}."://$auth".$hash->{host}.":".
$HMCCU_REGA_PORT{$hash->{prot}}."/tclrega.exe";
}
else {
$url = HMCCU_GetRPCServerInfo ($hash, $backend, 'url');
if (defined ($url)) {
if (exists ($HMCCU_RPC_SSL{$backend})) {
my $p = $hash->{prot} eq 'https' ? '4' : '';
$url =~ s/^http:\/\//$hash->{prot}:\/\/$auth/;
$url =~ s/:([0-9]+)/:$p$1/;
}
}
else {
$url = '';
}
}
HMCCU_Log ($hash, 4, "Build URL = $url", undef);
return $url;
}
######################################################################
# Calculate special readings. Requires hash of client device, channel
# number and datapoint. Supported functions:
@ -7184,6 +7371,73 @@ sub HMCCU_CalculateReading ($$)
return @result;
}
######################################################################
# Encrypt string with FHEM unique ID
######################################################################
sub HMCCU_Encrypt ($)
{
my ($istr) = @_;
my $ostr = '';
my $id = getUniqueId();
return '' if (!defined ($id) || $id eq '');
my $key = $id;
foreach my $c (split //, $istr) {
my $k = chop($key);
if ($k eq '') {
$key = $id;
$k = chop($key);
}
$ostr .= sprintf ("%.2x",ord($c)^ord($k));
}
return $ostr;
}
######################################################################
# Decrypt string with FHEM unique ID
######################################################################
sub HMCCU_Decrypt ($)
{
my ($istr) = @_;
my $ostr = '';
my $id = getUniqueId();
return '' if (!defined ($id) || $id eq '');
my $key = $id;
for my $c (map { pack('C', hex($_)) } ($istr =~ /(..)/g)) {
my $k = chop($key);
if ($k eq '') {
$key = $id;
$k = chop($key);
}
$ostr .= chr(ord($c)^ord($k));
}
return $ostr;
}
######################################################################
# Delete readings matching regular expression.
# Default for rnexp is .*
# Readings 'state' and 'control' are ignored.
######################################################################
sub HMCCU_DeleteReadings ($$)
{
my ($hash, $rnexp) = @_;
$rnexp = '.*' if (!defined ($rnexp));
my @readlist = keys %{$hash->{READINGS}};
foreach my $rd (@readlist) {
delete ($hash->{READINGS}{$rd}) if ($rd ne 'state' && $rd ne 'control' && $rd =~ /$rnexp/);
}
}
######################################################################
# Encode command string for e-paper display
#
@ -7380,7 +7634,8 @@ sub HMCCU_GetDutyCycle ($)
foreach my $port (@rpcports) {
next if ($port != 2001 && $port != 2010);
my $url = HMCCU_GetRPCServerInfo ($hash, $port, 'url');
# my $url = HMCCU_GetRPCServerInfo ($hash, $port, 'url');
my $url = HMCCU_BuildURL ($hash, $port);
next if (!defined ($url));
my $rpcclient = RPC::XML::Client->new ($url);
my $response = $rpcclient->simple_request ("listBidcosInterfaces");
@ -7445,6 +7700,23 @@ sub HMCCU_TCPConnect ($$)
return '';
}
######################################################################
# Generate a 6 digit Id from last 2 segments of IP address
######################################################################
sub HMCCU_GetIdFromIP ($$)
{
my ($ip, $default) = @_;
my @ipseg = split (/\./, $ip);
if (scalar (@ipseg) == 4) {
return sprintf ("%03d%03d", $ipseg[2], $ipseg[3]);
}
else {
return $default;
}
}
######################################################################
# Resolve hostname.
# Return value defip if hostname can't be resolved.
@ -7806,15 +8078,16 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
<a name="HMCCUdefine"></a>
<b>Define</b><br/><br/>
<ul>
<code>define &lt;name&gt; HMCCU &lt;HostOrIP&gt; [&lt;ccu-number&gt;] [waitforccu=&lt;timeout&gt;]
<code>define &lt;name&gt; HMCCU [&lt;Protocol&gt;://]&lt;HostOrIP&gt; [&lt;ccu-number&gt;] [waitforccu=&lt;timeout&gt;]
[ccudelay=&lt;delay&gt;] [delayedinit=&lt;delay&gt;]</code>
<br/><br/>
Example:<br/>
<code>define myccu HMCCU 192.168.1.10 ccudelay=180</code>
<code>define myccu HMCCU https://192.168.1.10 ccudelay=180</code>
<br/><br/>
The parameter <i>HostOrIP</i> is the hostname or IP address of a Homematic CCU2 or CCU3. If you have
more than one CCU you can specifiy a unique CCU number with parameter <i>ccu-number</i>. With
option <i>waitforccu</i> HMCCU will wait for the specified time if CCU is not reachable.
The parameter <i>HostOrIP</i> is the hostname or IP address of a Homematic CCU2 or CCU3. Optionally
the <i>protocol</i> 'http' or 'https' can be specified. Default protocol is 'http'.<br/>
If you have more than one CCU you can specifiy a unique CCU number with parameter <i>ccu-number</i>.
With option <i>waitforccu</i> HMCCU will wait for the specified time if CCU is not reachable.
Parameter <i>timeout</i> should be a multiple of 20 in seconds. Warning: This option will
block the start of FHEM for <i>timeout</i> seconds.<br/>
The option <i>ccudelay</i> specifies the time for delayed initialization of CCU environment if
@ -7846,6 +8119,15 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
<ul>
<li><b>set &lt;name&gt; ackmessages</b><br/>
Acknowledge device was unreachable messages in CCU.
</li><br/>
<li><b>set &lt;name&gt; authentication [&lt;username&gt; &lt;password&gt;]</b><br/>
Set credentials for CCU authentication. Authentication must be activated by setting
attribute ccuflags to 'authenticate'.<br/>
When executing this command without arguments credentials are deleted.
</li><br/>
<li><b>set &lt;name&gt; clear [&lt;reading-exp&gt;]</b><br/>
Delete readings matching specified reading name expression. Default expression is '.*'.
Readings 'state' and 'control' are not deleted.
</li><br/>
<li><b>set &lt;name&gt; cleardefaults</b><br/>
Clear default attributes imported from file.
@ -7877,6 +8159,9 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
<li><b>set &lt;name&gt; importdefaults &lt;filename&gt;</b><br/>
Import default attributes from file.
</li><br/>
<li><b>set &lt;name&gt; initialize</b><br/>
Initialize I/O device if state of CCU is unreachable.
</li><br/>
<li><b>set &lt;name&gt; rpcregister [{all | &lt;interface&gt;}]</b><br/>
Register RPC servers at CCU.
</li><br/>
@ -7899,6 +8184,9 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
<li><b>get &lt;name&gt; aggregation {&lt;rule&gt;|all}</b><br/>
Process aggregation rule defined with attribute ccuaggregate.
</li><br/>
<li><b>get &lt;name&gt; ccumsg {service|alarm}</b><br/>
Query active service or alarm messages from CCU. Generate FHEM event for each message.
</li><br/>
<li><b>get &lt;name&gt; configdesc {&lt;device&gt;|&lt;channel&gt;}</b><br/>
Get configuration parameter description of CCU device or channel (similar
to device settings in CCU). Not every CCU device or channel provides a configuration

View File

@ -4,9 +4,9 @@
#
# $Id$
#
# Version 4.3.005
# Version 4.3.006
#
# (c) 2018 zap (zap01 <at> t-online <dot> de)
# (c) 2019 zap (zap01 <at> t-online <dot> de)
#
######################################################################
# Client device for Homematic channels.
@ -208,8 +208,8 @@ sub HMCCUCHN_Set ($@)
my $rocmds = "clear config defaults:noArg";
# Get I/O device, check device state
return HMCCU_SetError ($hash, -19) if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending');
return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' ||
!defined ($hash->{IODev}));
return undef if ($hash->{statevals} eq 'readonly' && $opt ne '?' &&
$opt !~ /^(clear|config|defaults)$/);
@ -403,11 +403,8 @@ sub HMCCUCHN_Set ($@)
}
elsif ($opt eq 'clear') {
my $rnexp = shift @$a;
$rnexp = '.*' if (!defined ($rnexp));
my @readlist = keys %{$hash->{READINGS}};
foreach my $rd (@readlist) {
delete ($hash->{READINGS}{$rd}) if ($rd ne 'state' && $rd ne 'control' && $rd =~ /$rnexp/);
}
HMCCU_DeleteReadings ($hash, $rnexp);
return HMCCU_SetState ($hash, "OK");
}
elsif ($opt eq 'config') {
return HMCCU_SetError ($hash, "Usage: set $name config [device] {parameter}={value} [...]")
@ -460,7 +457,8 @@ sub HMCCUCHN_Get ($@)
my $name = shift @$a;
my $opt = shift @$a;
return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' ||
!defined ($hash->{IODev}));
my $disable = AttrVal ($name, "disable", 0);
return undef if ($disable == 1);
@ -794,16 +792,33 @@ sub HMCCUCHN_Get ($@)
If set to 1 values read from CCU will be stored as readings. Default is 1.
</li><br/>
<li><b>ccureadingfilter &lt;filter-rule[;...]&gt;</b><br/>
Only datapoints matching specified expression are stored as readings.<br/>
Only datapoints matching specified expression <i>RegExp</i> are stored as readings.<br/>
Syntax for <i>filter-rule</i> is either:<br/>
[N:]{&lt;channel-name&gt;|&lt;channel-number&gt;}!&lt;RegExp&gt; or:<br/>
[N:]{&lt;channel-name&gt;}!RegExp&gt; or:<br/>
[N:][&lt;channel-number&gt;.]&lt;RegExp&gt;<br/>
If <i>channel-name</i> or <i>channel-number</i> is specified the following rule
applies only to this channel.
By default all datapoints will be stored as readings. Attribute ccudef-readingfilter
of I/O device will be checked before this attribute.<br/>
applies only to this channel.<br/>
If a rule starts with 'N:' the filter is negated which means that a reading is
stored if rule doesn't match.
stored if rule doesn't match.<br/>
The following table describes the dependencies between this attribute and attribute
ccudef-readingfilter in I/O device. The filtering of readings depends on which attribute
is set.<br/>
<table>
<tr><th>ccureadingfilter<br/>Device</th><th>ccudef-readingfilter<br/>I/O Device</th><th>Update Readings/Datapoints</th></tr>
<tr><td>not set</td><td>not set</td><td>all readings</td></tr>
<tr><td>not set</td><td>set</td><td>only readings from ccudef-readingfilter</td></tr>
<tr><td>set</td><td>not set</td><td>only readings from ccureadingfilter</td></tr>
<tr><td>set</td><td>set</td><td>both readings from ccureadingfilter and ccudef-readingfilter</td></tr>
</table>
So if ccudef-readingfilter is set in I/O device one must also set ccureadingfilter to
get updates for additional, device specific readings. Please keep in mind, that readings updates
are also affected by attributes event-on-change-reading and event-on-update-reading.<br/><br/>
Examples:<br/>
<code>
attr mydev ccureadingfilter .*<br/>
attr mydev ccureadingfilter 1.(^ACTUAL|CONTROL|^SET_TEMP);(^WINDOW_OPEN|^VALVE)<br/>
attr mydev ccureadingfilter MyBlindChannel!^LEVEL$<br/>
</code>
</li><br/>
<li><b>ccureadingformat {address[lc] | name[lc] | datapoint[lc]}</b><br/>
Set format of reading names. Default for virtual device groups is 'name'. The default for all

View File

@ -4,9 +4,9 @@
#
# $Id$
#
# Version 4.3.006
# Version 4.3.008
#
# (c) 2018 zap (zap01 <at> t-online <dot> de)
# (c) 2019 zap (zap01 <at> t-online <dot> de)
#
######################################################################
# Client device for Homematic devices.
@ -239,7 +239,6 @@ sub HMCCUDEV_InitDevice ($$)
# Group specified by CCU virtual group name
@devlist = HMCCU_GetGroupMembers ($hmccu_hash, $gdname);
$gdcount = scalar (@devlist);
return 5 if ($gdcount == 0);
}
return 3 if ($gdcount == 0);
@ -345,8 +344,8 @@ sub HMCCUDEV_Set ($@)
my $rocmds = "clear config defaults:noArg";
# Get I/O device, check device state
return HMCCU_SetError ($hash, -19) if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending');
return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' ||
!defined ($hash->{IODev}));
my $hmccu_hash = $hash->{IODev};
my $hmccu_name = $hmccu_hash->{NAME};
@ -580,11 +579,8 @@ sub HMCCUDEV_Set ($@)
}
elsif ($opt eq 'clear') {
my $rnexp = shift @$a;
$rnexp = '.*' if (!defined ($rnexp));
my @readlist = keys %{$hash->{READINGS}};
foreach my $rd (@readlist) {
delete ($hash->{READINGS}{$rd}) if ($rd ne 'state' && $rd ne 'control' && $rd =~ /$rnexp/);
}
HMCCU_DeleteReadings ($hash, $rnexp);
return HMCCU_SetState ($hash, "OK");
}
elsif ($opt eq 'config') {
return HMCCU_SetError ($hash, "Usage: set $name config [{channel-number}] {parameter}={value} [...]")
@ -645,7 +641,8 @@ sub HMCCUDEV_Get ($@)
my $opt = shift @$a;
# Get I/O device
return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' ||
!defined ($hash->{IODev}));
my $hmccu_hash = $hash->{IODev};
my $hmccu_name = $hmccu_hash->{NAME};

View File

@ -4,7 +4,7 @@
#
# $Id$
#
# Version 1.5
# Version 1.6
#
# Subprocess based RPC Server module for HMCCU.
#
@ -35,7 +35,7 @@ use SetExtensions;
######################################################################
# HMCCURPC version
my $HMCCURPCPROC_VERSION = '1.5';
my $HMCCURPCPROC_VERSION = '1.6';
# Maximum number of events processed per call of Read()
my $HMCCURPCPROC_MAX_EVENTS = 100;
@ -107,6 +107,7 @@ sub HMCCURPCPROC_Initialize ($);
sub HMCCURPCPROC_Define ($$);
sub HMCCURPCPROC_InitDevice ($$);
sub HMCCURPCPROC_Undef ($$);
sub HMCCURPCPROC_DelayedShutdown ($);
sub HMCCURPCPROC_Shutdown ($);
sub HMCCURPCPROC_Attr ($@);
sub HMCCURPCPROC_Set ($@);
@ -134,7 +135,7 @@ sub HMCCURPCPROC_RPCServerStopped ($);
sub HMCCURPCPROC_SendRequest ($@);
sub HMCCURPCPROC_SetRPCState ($$$$);
sub HMCCURPCPROC_StartRPCServer ($);
sub HMCCURPCPROC_StopRPCServer ($);
sub HMCCURPCPROC_StopRPCServer ($$);
sub HMCCURPCPROC_TerminateProcess ($);
# Helper functions
@ -197,6 +198,7 @@ sub HMCCURPCPROC_Initialize ($)
$hash->{ReadFn} = "HMCCURPCPROC_Read";
$hash->{AttrFn} = "HMCCURPCPROC_Attr";
$hash->{ShutdownFn} = "HMCCURPCPROC_Shutdown";
$hash->{DelayedShutdownFn} = "HMCCURPCPROC_DelayedShutdown";
$hash->{parseParams} = 1;
@ -231,17 +233,32 @@ sub HMCCURPCPROC_Define ($$)
$hmccu_hash = $defs{$ioname};
if (scalar (@$a) < 4) {
$hash->{host} = $hmccu_hash->{host};
$hash->{prot} = $hmccu_hash->{prot};
$iface = $$a[2];
}
else {
$hash->{host} = $$a[2];
if ($$a[2] =~ /^(https?):\/\/(.+)/) {
$hash->{prot} = $1;
$hash->{host} = $2;
}
else {
$hash->{prot} = 'http';
$hash->{host} = $$a[2];
}
$iface = $$a[3];
}
$rpcip = HMCCU_ResolveName ($hash->{host}, 'N/A');
}
else {
return $usage if (scalar (@$a) < 4);
$hash->{host} = $$a[2];
if ($$a[2] =~ /^(https?):\/\/(.+)/) {
$hash->{prot} = $1;
$hash->{host} = $2;
}
else {
$hash->{prot} = 'http';
$hash->{host} = $$a[2];
}
$iface = $$a[3];
$rpcip = HMCCU_ResolveName ($hash->{host}, 'N/A');
@ -250,12 +267,8 @@ sub HMCCURPCPROC_Define ($$)
my $dh = $defs{$d};
next if (!exists ($dh->{TYPE}) || !exists ($dh->{NAME}));
next if ($dh->{TYPE} ne 'HMCCU');
# The following call will fail during FHEM start if CCU is not ready
my $ifhost = HMCCU_GetRPCServerInfo ($dh, $iface, 'host');
next if (!defined ($ifhost));
if ($dh->{host} eq $hash->{host} || $ifhost eq $hash->{host} || $ifhost eq $rpcip) {
$hmccu_hash = $dh;
if ($dh->{ccuip} eq $rpcip) {
$hmccu_hash = $dh;
last;
}
}
@ -338,10 +351,11 @@ sub HMCCURPCPROC_InitDevice ($$) {
# Get unique ID for RPC server: last 2 segments of local IP address
# Do not append random digits because of https://forum.fhem.de/index.php/topic,83544.msg797146.html#msg797146
my @ipseg = split (/\./, $dev_hash->{hmccu}{localaddr});
return 3 if (scalar (@ipseg) != 4);
$dev_hash->{rpcid} = sprintf ("%03d%03d", $ipseg[2], $ipseg[3]);
my $id1 = HMCCU_GetIdFromIP ($dev_hash->{hmccu}{localaddr}, '');
my $id2 = HMCCU_GetIdFromIP ($hmccu_hash->{ccuip}, '');
return 3 if ($id1 eq '' || $id2 eq '');
$dev_hash->{rpcid} = $id1.$id2;
# Set I/O device and store reference for RPC device in I/O device
my $ioname = $hmccu_hash->{NAME};
return 2 if (!HMCCU_AssignIODevice ($dev_hash, $ioname, $ifname));
@ -380,10 +394,11 @@ sub HMCCURPCPROC_Undef ($$)
my $ifname = $hash->{rpcinterface};
# Shutdown RPC server
HMCCURPCPROC_Shutdown ($hash);
HMCCURPCPROC_StopRPCServer ($hash, $HMCCURPCPROC_INIT_INTERVAL2);
# Delete RPC device name in I/O device
if (exists ($hmccu_hash->{hmccu}{interfaces}{$ifname}{device}) &&
if (exists ($hmccu_hash->{hmccu}{interfaces}{$ifname}) &&
exists ($hmccu_hash->{hmccu}{interfaces}{$ifname}{device}) &&
$hmccu_hash->{hmccu}{interfaces}{$ifname}{device} eq $name) {
delete $hmccu_hash->{hmccu}{interfaces}{$ifname}{device};
}
@ -391,6 +406,30 @@ sub HMCCURPCPROC_Undef ($$)
return undef;
}
######################################################################
# Delayed shutdown FHEM
######################################################################
sub HMCCURPCPROC_DelayedShutdown ($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $delay = max (AttrVal ("global", "maxShutdownDelay", 10)-2, 0);
# Shutdown RPC server
if (!exists ($hash->{hmccu}{delayedShutdown})) {
$hash->{hmccu}{delayedShutdown} = 1;
Log3 $name, 1, "HMCCURPCPROC: [$name] Graceful shutdown";
HMCCURPCPROC_StopRPCServer ($hash, $delay);
}
else {
Log3 $name, 1, "HMCCURPCPROC: [$name] Shutdown already in progress";
}
return 1;
}
######################################################################
# Shutdown FHEM
######################################################################
@ -398,9 +437,15 @@ sub HMCCURPCPROC_Undef ($$)
sub HMCCURPCPROC_Shutdown ($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
# Shutdown RPC server
HMCCURPCPROC_StopRPCServer ($hash);
if (!exists ($hash->{hmccu}{delayedShutdown})) {
Log3 $name, 1, "HMCCURPCPROC: [$name] Immediate shutdown";
HMCCURPCPROC_StopRPCServer ($hash, 0);
}
# Remove all internal timers
RemoveInternalTimer ($hash);
return undef;
@ -511,7 +556,7 @@ sub HMCCURPCPROC_Set ($@)
}
elsif ($action eq 'off') {
$hmccu_hash->{hmccu}{interfaces}{$hash->{rpcinterface}}{manager} = 'HMCCURPCPROC';
HMCCURPCPROC_StopRPCServer ($hash);
HMCCURPCPROC_StopRPCServer ($hash, $HMCCURPCPROC_INIT_INTERVAL2);
}
return undef;
@ -565,7 +610,11 @@ sub HMCCURPCPROC_Get ($@)
$result .= "--------------------------\n";
my $sid = defined ($hash->{hmccu}{rpc}{pid}) ? sprintf ("%5d", $hash->{hmccu}{rpc}{pid}) : "N/A ";
my $sname = sprintf ("%-10s", $clkey);
$result .= $sid." ".$sname." ".$hash->{hmccu}{rpc}{state}."\n";
my $cbport = defined ($hash->{hmccu}{rpc}{cbport}) ? $hash->{hmccu}{rpc}{cbport} : "N/A";
my $addr = defined ($hash->{hmccu}{localaddr}) ? $hash->{hmccu}{localaddr} : "N/A";
$result .= $sid." ".$sname." ".$hash->{hmccu}{rpc}{state}."\n\n";
$result .= "Local address = $addr\n";
$result .= "Callback port = $cbport\n";
return $result;
}
else {
@ -760,7 +809,8 @@ sub HMCCURPCPROC_IsRPCStateBlocking ($)
{
my ($hash) = @_;
return ($hash->{RPCState} eq "running" || $hash->{RPCState} eq "inactive") ? 0 : 1;
return (exists ($hash->{RPCState}) &&
($hash->{RPCState} eq "running" || $hash->{RPCState} eq "inactive")) ? 0 : 1;
}
######################################################################
@ -950,7 +1000,7 @@ sub HMCCURPCPROC_ProcessEvent ($$)
# Input: TO|clkey|DiffTime
# Output: TO, clkey, Port, DiffTime
#
if ($evttimeout > 0 && $t[0] > $evttimeout) {
if ($evttimeout > 0) {
Log3 $name, 2, "HMCCURPCPROC: [$name] Received no events from interface $clkey for ".$t[0]." seconds";
$hash->{ccustate} = 'timeout';
if ($hash->{RPCState} eq 'running' && $hash->{rpcport} == $defPort) {
@ -1021,7 +1071,8 @@ sub HMCCURPCPROC_RegisterCallback ($$)
}
my $cburl = HMCCU_GetRPCCallbackURL ($hmccu_hash, $localaddr, $hash->{hmccu}{rpc}{cbport}, $clkey, $port);
my $clurl = HMCCU_GetRPCServerInfo ($hmccu_hash, $port, 'url');
# my $clurl = HMCCU_GetRPCServerInfo ($hmccu_hash, $port, 'url');
my $clurl = HMCCU_BuildURL ($hmccu_hash, $port);
my $rpctype = HMCCU_GetRPCServerInfo ($hmccu_hash, $port, 'type');
return (0, "Can't get RPC parameters for ID $clkey") if (!defined ($cburl) || !defined ($clurl) || !defined ($rpctype));
@ -1069,7 +1120,8 @@ sub HMCCURPCPROC_DeRegisterCallback ($$)
$cburl = $rpchash->{cburl} if (exists ($rpchash->{cburl}));
$clurl = $rpchash->{clurl} if (exists ($rpchash->{clurl}));
$cburl = HMCCU_GetRPCCallbackURL ($hmccu_hash, $localaddr, $rpchash->{cbport}, $clkey, $port) if ($cburl eq '');
$clurl = HMCCU_GetRPCServerInfo ($hmccu_hash, $port, 'url') if ($clurl eq '');
# $clurl = HMCCU_GetRPCServerInfo ($hmccu_hash, $port, 'url') if ($clurl eq '');
$clurl = HMCCU_BuildURL ($hmccu_hash, $port) if ($clurl eq '');
return (0, "Can't get RPC parameters for ID $clkey") if ($cburl eq '' || $clurl eq '');
Log3 $name, 1, "HMCCURPCPROC: [$name] Deregistering RPC server $cburl with ID $clkey at $clurl";
@ -1378,6 +1430,9 @@ sub HMCCURPCPROC_RPCServerStopped ($)
RemoveInternalTimer ($hash);
DoTrigger ($name, "RPC server $clkey stopped");
# Inform FHEM that instance can be shut down
CancelDelayedShutdown ($name) if (exists ($hash->{hmccu}{delayedShutdown}));
}
######################################################################
@ -1535,13 +1590,16 @@ sub HMCCURPCPROC_Housekeeping ($)
######################################################################
# Stop RPC server processes.
# If function is called by Shutdown, parameter wait must be 0
######################################################################
sub HMCCURPCPROC_StopRPCServer ($)
sub HMCCURPCPROC_StopRPCServer ($$)
{
my ($hash) = @_;
my ($hash, $wait) = @_;
my $name = $hash->{NAME};
my $clkey = 'CB'.$hash->{rpcport}.$hash->{rpcid};
$wait = $HMCCURPCPROC_INIT_INTERVAL2 if (!defined ($wait));
if (HMCCURPCPROC_CheckProcessState ($hash, 'running')) {
Log3 $name, 1, "HMCCURPCPROC: [$name] Stopping RPC server $clkey";
@ -1556,8 +1614,14 @@ sub HMCCURPCPROC_StopRPCServer ($)
# Trigger timer function for checking successful RPC stop
# Timer will be removed wenn receiving EX event from RPC server process
InternalTimer (gettimeofday()+$HMCCURPCPROC_INIT_INTERVAL2, "HMCCURPCPROC_Housekeeping",
$hash, 0);
if ($wait > 0) {
Log3 $name, 2, "HMCCURPCPROC: [$name] Scheduling cleanup in $wait seconds";
InternalTimer (gettimeofday()+$wait, "HMCCURPCPROC_Housekeeping", $hash, 0);
}
else {
Log3 $name, 2, "HMCCURPCPROC: [$name] Cleaning up immediately";
HMCCURPCPROC_Housekeeping ($hash);
}
# Give process the chance to terminate
sleep (1);
@ -1587,12 +1651,14 @@ sub HMCCURPCPROC_SendRequest ($@)
if (HMCCU_IsRPCType ($hmccu_hash, $port, 'A')) {
# Use XMLRPC
my $clurl = HMCCU_GetRPCServerInfo ($hmccu_hash, $port, 'url');
# my $clurl = HMCCU_GetRPCServerInfo ($hmccu_hash, $port, 'url');
my $clurl = HMCCU_BuildURL ($hmccu_hash, $port);
return HMCCU_Log ($hash, 2, "Can't get client URL for port $port", undef)
if (!defined ($clurl));
Log3 $name, 4, "HMCCURPCPROC: [$name] Send ASCII RPC request $request to $clurl";
my $rpcclient = RPC::XML::Client->new ($clurl);
my $rpcclient = RPC::XML::Client->new ($clurl, useragent => [
ssl_opts => { verify_hostname => 0, SSL_verify_mode => 0 } ]);
$rc = $rpcclient->simple_request ($request, @param);
Log3 $name, 2, "HMCCURPCPROC: [$name] RPC request error ".$RPC::XML::ERROR if (!defined ($rc));
}

View File

@ -4,19 +4,11 @@
#
# $Id$
#
# Version 4.5
# Version 4.6
#
# Configuration parameters for HomeMatic devices.
#
# (c) 2018 by zap (zap01 <at> t-online <dot> de)
#
# Datapoints LOWBAT, LOW_BAT, UNREACH, ERROR.*, SABOTAGE and FAULT.*
# must not be specified in attribute ccureadingfilter. They are always
# stored as readings.
# Datapoints LOWBAT, LOW_BAT and UNREACH must not be specified in
# attribute substitute because they are substituted by default.
# See also documentation of attributes ccudef-readingname and
# ccudef-substitute in module HMCCU.
# (c) 2019 by zap (zap01 <at> t-online <dot> de)
#
#########################################################################
@ -554,7 +546,7 @@ use vars qw(%HMCCU_SCRIPTS);
},
"HMIP-PSM" => {
_description => "Steckdose mit Energiemessung IP",
ccureadingfilter => "(STATE|CURRENT|^ENERGY_COUNTER\$|POWER)",
ccureadingfilter => "3.STATE;6.(CURRENT|^ENERGY_COUNTER\$|POWER)",
controldatapoint => "3.STATE",
statedatapoint => "3.STATE",
statevals => "on:true,off:false",
@ -1473,25 +1465,21 @@ if(oTmpArray) {
object oTmp = dom.GetObject(sTmp);
if (oTmp) {
if(oTmp.IsTypeOf(OT_ALARMDP) && (oTmp.AlState() == asOncoming)) {
boolean collect = true;
object trigDP = dom.GetObject(oTmp.AlTriggerDP());
object och = dom.GetObject((trigDP.Channel()));
object odev = dom.GetObject((och.Device()));
var ival = trigDP.Value();
time sftime = oTmp.AlOccurrenceTime();
time sltime = oTmp.LastTriggerTime();
var sdesc = trigDP.HSSID();
time sftime = oTmp.AlOccurrenceTime(); ! erste Meldezeit
time sltime = oTmp.LastTriggerTime();!letze Meldezeit
var sdesc = trigDP.HssType();
var sserial = odev.Address();
string sAlarmMessage = web.webKeyFromStringTable(sdesc.Name());
if(!sAlarmMessage.Length()) {
sAlarmMessage = sdesc;
}
var sname = odev.Name();
WriteLine(sftime.Format("%d.%m.%y %H:%M") # ";" # sltime.Format("%d.%m.%y %H:%M") # ";" # sserial # ";" # sname # ";" # sdesc);
c = c+1;
WriteLine(sftime # ";" # sltime # ";" # sAlarmMessage # ";" # sserial);
}
}
}
}
}
Write(c);
)
},