From e2334d7910d96b00fb0927c4f789a0491165a522 Mon Sep 17 00:00:00 2001 From: zap <> Date: Tue, 27 Mar 2018 08:07:03 +0000 Subject: [PATCH] HMCCU: Non blocking set datapoint command git-svn-id: https://svn.fhem.de/fhem/trunk@16500 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/88_HMCCU.pm | 261 +++++++++++++++++++++++++++++---------- fhem/FHEM/88_HMCCUCHN.pm | 2 +- fhem/FHEM/88_HMCCUDEV.pm | 6 +- fhem/FHEM/HMCCUConf.pm | 6 +- 4 files changed, 204 insertions(+), 71 deletions(-) diff --git a/fhem/FHEM/88_HMCCU.pm b/fhem/FHEM/88_HMCCU.pm index b6579d158..8c510a748 100755 --- a/fhem/FHEM/88_HMCCU.pm +++ b/fhem/FHEM/88_HMCCU.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 4.2.003 +# Version 4.2.004 # # Module for communication between FHEM and Homematic CCU2. # @@ -37,7 +37,7 @@ # get dump {devtypes|datapoints} [] # get dutycycle # get exportdefaults {filename} -# get firmware +# get firmware [{type-expr}|full] # get parfile [] # get rpcevents # get rpcstate @@ -105,7 +105,7 @@ my %HMCCU_CUST_CHN_DEFAULTS; my %HMCCU_CUST_DEV_DEFAULTS; # HMCCU version -my $HMCCU_VERSION = '4.2.003'; +my $HMCCU_VERSION = '4.2.004'; # Default RPC port (BidCos-RF) my $HMCCU_RPC_PORT_DEFAULT = 2001; @@ -259,7 +259,7 @@ sub HMCCU_IsRPCStateBlocking ($); sub HMCCU_IsRPCServerRunning ($$$); sub HMCCU_GetDeviceInfo ($$$); sub HMCCU_FormatDeviceInfo ($); -sub HMCCU_GetFirmwareVersions ($); +sub HMCCU_GetFirmwareVersions ($$); sub HMCCU_GetDeviceList ($); sub HMCCU_GetDatapointList ($$$); sub HMCCU_FindDatapoint ($$$$$); @@ -294,6 +294,9 @@ sub HMCCU_GetDeviceInterface ($$$); sub HMCCU_ResetRPCQueue ($$); sub HMCCU_ReadRPCQueue ($); sub HMCCU_ProcessEvent ($$); +sub HMCCU_HMCommand ($$$); +sub HMCCU_HMCommandNB ($$$); +sub HMCCU_HMCommandCB ($$$); sub HMCCU_HMScriptExt ($$$); sub HMCCU_BulkUpdate ($$$$); sub HMCCU_GetDatapoint ($@); @@ -1362,7 +1365,7 @@ sub HMCCU_Get ($@) my $opt = shift @$a; my $options = "defaults:noArg exportdefaults devicelist dump dutycycle:noArg vars update". - " updateccu parfile configdesc firmware:noArg rpcevents:noArg rpcstate:noArg deviceinfo"; + " updateccu parfile configdesc firmware rpcevents:noArg rpcstate:noArg deviceinfo"; my $usage = "HMCCU: Unknown argument $opt, choose one of $options"; my $host = $hash->{host}; @@ -1644,25 +1647,40 @@ sub HMCCU_Get ($@) return HMCCU_SetState ($hash, "OK", "Read $dc duty cycle values"); } elsif ($opt eq 'firmware') { - my $dc = HMCCU_GetFirmwareVersions ($hash); + my $devtype = shift @$a; + $devtype = '.*' if (!defined ($devtype)); + my $dtexp = $devtype; + $dtexp = '.*' if ($devtype eq 'full'); + my $dc = HMCCU_GetFirmwareVersions ($hash, $dtexp); return "Found no firmware downloads" if ($dc == 0); - $result = "Found $dc firmware downloads."; - my @devlist = HMCCU_FindClientDevices ($hash, "(HMCCUDEV|HMCCUCHN)", undef, undef); - return $result if (scalar (@devlist) == 0); - - $result .= " Click on the new version number for download\n\n". - "Device Type Current Available Date\n". - "------------------------------------------------------------------------\n"; - foreach my $dev (@devlist) { - my $ch = $defs{$dev}; - my $ct = uc($ch->{ccutype}); - next if (!defined ($ch->{firmware})); - next if (!exists ($hash->{hmccu}{type}{$ct})); - $result .= sprintf "%-25s %-20s %-7s %-9s %-10s\n", - $ch->{NAME}, $ct, $ch->{firmware}, $hash->{hmccu}{type}{$ct}{download}, - $hash->{hmccu}{type}{$ct}{firmware}, $hash->{hmccu}{type}{$ct}{date}; + $result = "Found $dc firmware downloads. Click on the new version number for download\n\n"; + if ($devtype eq 'full') { + $result .= + "Type Available Date\n". + "-----------------------------------------\n"; + foreach my $ct (keys %{$hash->{hmccu}{type}}) { + $result .= sprintf "%-20s %-9s %-10s\n", + $ct, $hash->{hmccu}{type}{$ct}{download}, + $hash->{hmccu}{type}{$ct}{firmware}, $hash->{hmccu}{type}{$ct}{date}; + } } - + else { + my @devlist = HMCCU_FindClientDevices ($hash, "(HMCCUDEV|HMCCUCHN)", undef, undef); + return $result if (scalar (@devlist) == 0); + $result .= + "Device Type Current Available Date\n". + "---------------------------------------------------------------------------\n"; + foreach my $dev (@devlist) { + my $ch = $defs{$dev}; + my $ct = uc($ch->{ccutype}); + my $fw = defined ($ch->{firmware}) ? $ch->{firmware} : 'N/A'; + next if (!exists ($hash->{hmccu}{type}{$ct})); + $result .= sprintf "%-25s %-20s %-7s %-9s %-10s\n", + $ch->{NAME}, $ct, $fw, $hash->{hmccu}{type}{$ct}{download}, + $hash->{hmccu}{type}{$ct}{firmware}, $hash->{hmccu}{type}{$ct}{date}; + } + } + return HMCCU_SetState ($hash, "OK", $result); } elsif ($opt eq 'defaults') { @@ -3532,51 +3550,69 @@ sub HMCCU_FormatDeviceInfo ($) # Get available firmware versions from EQ-3 server. # Firmware version, date and download link are stored in hash # {hmccu}{type}{$type} in elements {firmware}, {date} and {download}. +# Parameter type can be a regular expression matching valid Homematic +# device types in upper case letters. Default is '.*'. # Return number of available firmware downloads. ###################################################################### -sub HMCCU_GetFirmwareVersions ($) +sub HMCCU_GetFirmwareVersions ($$) { - my ($hash) = @_; + my ($hash, $type) = @_; my $name = $hash->{NAME}; my $ccureqtimeout = AttrVal ($name, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); my $url = "http://www.eq-3.de/service/downloads.html"; my $response = GetFileFromURL ($url, $ccureqtimeout, "suchtext=&suche_in=&downloadart=11"); -# my @changebc = $response =~ m/href="(Downloads\/Software\/Firmware\/changelog_[^"]+)/g; -# my @changeip = $response =~ m/href="(Downloads\/Software\/Firmware\/Homematic IP\/changelog_[^"]+)/g; my @download = $response =~ m/{hmccu}{type}{$dt}{date})) { + my ($dd1, $mm1, $yy1) = split (/\./, $hash->{hmccu}{type}{$dt}{date}); + my $v1 = $yy1*10000+$mm1*100+$dd1; + my $v2 = $yy*10000+$mm*100+$dd; + next if ($v1 > $v2); + } + + $dc++; $hash->{hmccu}{type}{$dt}{firmware} = $fw; $hash->{hmccu}{type}{$dt}{date} = $date; $hash->{hmccu}{type}{$dt}{download} = $dl; @@ -4126,24 +4162,33 @@ sub HMCCU_IsValidDatapoint ($$$$$) my $ccuflags = AttrVal ($hmccu_hash->{NAME}, 'ccuflags', 'null'); return 1 if ($ccuflags =~ /dptnocheck/); - return 1 if (!exists ($hmccu_hash->{hmccu}{dp})); - my $chnno = $chn; - if (HMCCU_IsValidChannel ($hmccu_hash, $chn)) { - HMCCU_Trace ($hash, 2, $fnc, "$chn is a valid channel address"); - my ($a, $c) = split(":",$chn); - $chnno = $c; + my $chnno; + if (defined ($chn)) { + if ($chn =~ /^[0-9]{1,2}$/) { + HMCCU_Trace ($hash, 2, $fnc, "$chn is a channel number"); + $chnno = $chn; + } + elsif (HMCCU_IsValidChannel ($hmccu_hash, $chn)) { + HMCCU_Trace ($hash, 2, $fnc, "$chn is a valid channel address"); + my ($a, $c) = split(":",$chn); + $chnno = $c; + } + else { + HMCCU_Trace ($hash, 2, $fnc, "$chn is not a valid channel address or number"); + return 0; + } } - else { - HMCCU_Trace ($hash, 2, $fnc, "$chn is not a valid channel address"); - } - - if ($dpt =~ /^([0-9]{1,2})\.(.+)$/) { + elsif ($dpt =~ /^([0-9]{1,2})\.(.+)$/) { $chnno = $1; $dpt = $2; HMCCU_Trace ($hash, 2, $fnc, "$dpt contains channel number"); } + else { + HMCCU_Trace ($hash, 2, $fnc, "channel number missing in datapoint $dpt"); + return 0; + } HMCCU_Trace ($hash, 2, $fnc, "devtype=$devtype, chnno=$chnno, dpt=$dpt"); @@ -5097,6 +5142,79 @@ sub HMCCU_ReadRPCQueue ($) } } +###################################################################### +# Execute Homematic command on CCU. +# If parameter mode is 1 an empty string is a valid result. +###################################################################### + +sub HMCCU_HMCommand ($$$) +{ + my ($hash, $cmd, $mode) = @_; + my $name = $hash->{NAME}; + my $fnc = "HMCommand"; + + my $ccureqtimeout = AttrVal ($name, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); + my $url = "http://".$hash->{host}.":8181/do.exe?r1=$cmd"; + my $value; + + HMCCU_Trace ($hash, 2, $fnc, "URL=$url"); + + my $response = GetFileFromURL ($url, $ccureqtimeout); + $response =~ m/(.*)<\/r1>/; + $value = $1; + + HMCCU_Trace ($hash, 2, $fnc, "Response = $response"); + + if ($mode == 1) { + return (defined ($value) && $value ne 'null') ? $value : undef; + } + else { + return (defined ($value) && $value ne '' && $value ne 'null') ? $value : undef; + } +} + +###################################################################### +# Execute Homematic command on CCU without waiting for response. +###################################################################### + +sub HMCCU_HMCommandNB ($$$) +{ + my ($hash, $cmd, $cbfunc) = @_; + my $name = $hash->{NAME}; + my $fnc = "HMCommandNB"; + + my $hmccu_hash = HMCCU_GetHash ($hash); + my $ccureqtimeout = AttrVal ($hmccu_hash->{NAME}, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); + my $url = "http://".$hmccu_hash->{host}.":8181/do.exe?r1=$cmd"; + + HMCCU_Trace ($hash, 2, $fnc, "URL=$url"); + + if (defined ($cbfunc)) { + my $param = { url => $url, timeout => $ccureqtimeout, method => "GET", + callback => $cbfunc, devhash => $hash }; + HttpUtils_NonblockingGet ($param); + } + else { + my $param = { url => $url, timeout => $ccureqtimeout, method => "GET", + callback => \&HMCCU_HMCommandCB, devhash => $hash }; + HttpUtils_NonblockingGet ($param); + } +} + +###################################################################### +# Default callback function for non blocking CCU request. +###################################################################### + +sub HMCCU_HMCommandCB ($$$) +{ + my ($param, $err, $data) = @_; + my $hash = $param->{devhash}; + my $fnc = "HMCommandCB"; + + HMCCU_Log ($hash, 2, "Error during CCU request. $err", undef) if ($err ne ''); + HMCCU_Trace ($hash, 2, $fnc, "URL=".$param->{url}."
Response=$data"); +} + ###################################################################### # Execute Homematic script on CCU. # Parameters: device-hash, script-code or script-name, parameter-hash @@ -5270,6 +5388,7 @@ sub HMCCU_SetDatapoint ($$$) my $cdname = $hash->{NAME}; my $ccureqtimeout = AttrVal ($name, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); + my $ccuflags = AttrVal ($name, "ccuflags", 'null'); my $readingformat = HMCCU_GetAttrReadingFormat ($hash, $hmccu_hash); my $ccuverify = AttrVal ($cdname, 'ccuverify', 0); @@ -5288,7 +5407,7 @@ sub HMCCU_SetDatapoint ($$$) $param = $1; } - my $url = 'http://'.$hmccu_hash->{host}.':8181/do.exe?r1=dom.GetObject("'; + my $cmd = 'dom.GetObject("'; my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($hmccu_hash, $param, $HMCCU_FLAG_INTERFACE); return -1 if ($flags != $HMCCU_FLAGS_IACD && $flags != $HMCCU_FLAGS_NCD); @@ -5306,28 +5425,32 @@ sub HMCCU_SetDatapoint ($$$) } if ($flags == $HMCCU_FLAGS_IACD) { - $url .= $int.'.'.$add.':'.$chn.'.'.$dpt.'").State('.$value.')'; + $cmd .= $int.'.'.$add.':'.$chn.'.'.$dpt.'").State('.$value.')'; $nam = HMCCU_GetChannelName ($hmccu_hash, $add.":".$chn, ''); } elsif ($flags == $HMCCU_FLAGS_NCD) { - $url .= $nam.'").DPByHssDP("'.$dpt.'").State('.$value.')'; + $cmd .= $nam.'").DPByHssDP("'.$dpt.'").State('.$value.')'; ($add, $chn) = HMCCU_GetAddress ($hmccu_hash, $nam, '', ''); } my $addr = $add.":".$chn; - - my $response = GetFileFromURL ($url, $ccureqtimeout); + + if ($ccuflags =~ /nonBlocking/) { + HMCCU_HMCommandNB ($hash, $cmd, undef); + return 0; + } + + # Execute command (blocking) + my $response = HMCCU_HMCommand ($hmccu_hash, $cmd, 1); HMCCU_Trace ($hash, 2, $fnc, "Addr=$addr Name=$nam
". "Script response = \n".(defined ($response) ? $response: 'undef')."
". - "Script = \n".$url); - - return -2 if (!defined ($response) || $response =~ /null{ccutype}, $addr, $dpt, 1)) { if ($ccuverify == 1) { -# usleep (100000); my ($rc, $result) = HMCCU_GetDatapoint ($hash, $param); return $rc; } @@ -6900,9 +7023,12 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
  • get <name> exportdefaults <filename>
    Export default attributes into file.

  • -
  • get <name> firmware
    +
  • get <name> firmware [{<type-expr> | full}]
    Get available firmware downloads from eq-3.de. List FHEM devices with current and available - firmware version. Firmware versions are only displayed after RPC server has been started. + firmware version. By default only firmware version of defined HMCCUDEV or HMCCUCHN + devices are listet. With option 'full' all available firmware versions are listed. + With parameter type-expr one can filter displayed firmware versions by + Homematic device type.

  • get <name> parfile [<parfile>]
    Get values of all channels / datapoints specified in parfile. The parameter @@ -7009,20 +7135,27 @@ sub HMCCU_CCURPC_ListDevicesCB ($$) practice for creating a custom default attribute file is by exporting predefined default attributes from HMCCU with command 'get exportdefaults'.

  • -
  • ccuflags {extrpc, procrpc, intrpc}
    - Control behaviour of several HMCCU functions:
    +
  • ccuflags {<flags>}
    + Control behaviour of several HMCCU functions. Parameter flags is a comma + seperated list of the following strings:
    ackState - Acknowledge command execution by setting STATE to error or success.
    dptnocheck - Do not check within set or get commands if datapoint is valid
    intrpc - Use internal RPC server. This is the default.
    extrpc - Use external RPC server provided by module HMCCURPC. If no HMCCURPC device exists HMCCU will create one after command 'set rpcserver on'.
    logEvents - Write events from CCU into FHEM logfile
    - noReadings - Do not write readings
    + nonBlocking - Use non blocking (asynchronous) CCU requests
    + noReadings - Do not create or update readings
    + procrpc - Use external RPC server provided by module HMCCPRPCPROC. During first RPC + server start HMCCU will create a HMCCURPCPROC device for each interface confiugured + in attribute 'rpcinterface'
    + Flags intrpc, extrpc and procrpc cannot be combined.

  • ccuget {State | Value}
    Set read access method for CCU channel datapoints. Method 'State' is slower than 'Value' because each request is sent to the device. With method 'Value' only CCU - is queried. Default is 'Value'. + is queried. Default is 'Value'. Method for write access to datapoints is always + 'State'.

  • ccuReqTimeout <Seconds>
    Set timeout for CCU request. Default is 4 seconds. This timeout affects several diff --git a/fhem/FHEM/88_HMCCUCHN.pm b/fhem/FHEM/88_HMCCUCHN.pm index 73318027e..5b5869260 100644 --- a/fhem/FHEM/88_HMCCUCHN.pm +++ b/fhem/FHEM/88_HMCCUCHN.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 4.2.001 +# Version 4.2.002 # # (c) 2018 zap (zap01 t-online de) # diff --git a/fhem/FHEM/88_HMCCUDEV.pm b/fhem/FHEM/88_HMCCUDEV.pm index 86f8c5946..445e49b94 100644 --- a/fhem/FHEM/88_HMCCUDEV.pm +++ b/fhem/FHEM/88_HMCCUDEV.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 4.2 +# Version 4.2.001 # # (c) 2018 zap (zap01 t-online de) # @@ -310,7 +310,7 @@ sub HMCCUDEV_Set ($@) } return HMCCU_SetError ($hash, -8) - if (!HMCCU_IsValidDatapoint ($hash, $ccutype, 0, $objname, 2)); + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, undef, $objname, 2)); $objvalue =~ s/\\_/%20/g; $objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, undef, ''); @@ -599,7 +599,7 @@ sub HMCCUDEV_Get ($@) } return HMCCU_SetError ($hash, -8) - if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $0, $objname, 1)); + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, undef, $objname, 1)); $objname = $ccuif.'.'.$ccuaddr.':'.$objname; ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname); diff --git a/fhem/FHEM/HMCCUConf.pm b/fhem/FHEM/HMCCUConf.pm index 8faf04c0c..6d31e6eb8 100644 --- a/fhem/FHEM/HMCCUConf.pm +++ b/fhem/FHEM/HMCCUConf.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 4.2 +# Version 4.2.002 # # Configuration parameters for HomeMatic devices. # @@ -236,7 +236,7 @@ use vars qw(%HMCCU_SCRIPTS); statevals => "on:true,off:false", substitute => "STATE!(1|true):on,(0|false):off;WORKING!(1|true):yes,(0|false):no" }, - "HM-LC-Sw1-Pl|HM-LC-Sw1-Pl-2|HM-LC-Sw1-SM|HM-LC-Sw1-FM|HM-LC-Sw1-PB-FM" => { + "HM-LC-Sw1-Pl|HM-LC-Sw1-Pl-2|HM-LC-Sw1-SM|HM-LC-Sw1-FM|HM-LC-Sw1-PB-FM|HM-LC-Sw1-DR" => { _description => "1 Kanal Funk-Schaltaktor", _channels => "1", ccureadingfilter => "STATE", @@ -604,7 +604,7 @@ use vars qw(%HMCCU_SCRIPTS); statevals => "on:true,off:false", substitute => "STATE!(1|true):on,(0|false):off;WORKING!(1|true):yes,(0|false):no" }, - "HM-LC-Sw1-Pl|HM-LC-Sw1-Pl-2|HM-LC-Sw1-SM|HM-LC-Sw1-FM|HM-LC-Sw1-PB-FM" => { + "HM-LC-Sw1-Pl|HM-LC-Sw1-Pl-2|HM-LC-Sw1-SM|HM-LC-Sw1-FM|HM-LC-Sw1-PB-FM|HM-LC-Sw1-DR" => { _description => "1 Kanal Funk-Schaltaktor", ccureadingfilter => "STATE", statedatapoint => "1.STATE",