diff --git a/fhem/contrib/HMCCU/88_HMCCU.pm b/fhem/contrib/HMCCU/88_HMCCU.pm index aee842b8c..99de3315c 100644 --- a/fhem/contrib/HMCCU/88_HMCCU.pm +++ b/fhem/contrib/HMCCU/88_HMCCU.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 1.9 +# Version 2.0 # # (c) 2015 zap (zap01 t-online de) # @@ -16,11 +16,11 @@ # set datapoint :. [...] # set var [...] # set execute -# set clearmsg +# set hmscript # # get devicelist # get devstate [] -# get vars [,...] +# get vars # get channel :[.] # get datapoint . [] # get parfile [] @@ -30,14 +30,13 @@ # attr parfile # attr rpcinterval { 3 | 5 | 10 } # attr rpcport +# attr rpcqueue # attr rpcserver { on | off } +# attr statedatapoint # attr statevals :[,...] # attr stripchar # attr substitute :[,...] -# -# Notes: -# -# - Attribute 'unit' is no longer supported. +# attr updatemode { client | both | hmccu } ################################################################ package main; @@ -45,7 +44,8 @@ package main; use strict; use warnings; use SetExtensions; -use XML::Simple qw(:strict); +# use XML::Simple qw(:strict); +use RPC::XML::Client; use File::Queue; # use Data::Dumper; @@ -53,6 +53,8 @@ use File::Queue; my %HMCCU_Devices; # CCU Device addresses, key = CCU device name my %HMCCU_Addresses; +# Last update of device list +my $HMCCU_UpdateTime = 0; # Flags for CCU object specification my $HMCCU_FLAG_NAME = 1; @@ -60,6 +62,7 @@ my $HMCCU_FLAG_CHANNEL = 2; my $HMCCU_FLAG_DATAPOINT = 4; my $HMCCU_FLAG_ADDRESS = 8; my $HMCCU_FLAG_INTERFACE = 16; +my $HMCCU_FLAG_FULLADDR = 32; # Valid flag combinations my $HMCCU_FLAGS_IACD = $HMCCU_FLAG_INTERFACE | $HMCCU_FLAG_ADDRESS | @@ -85,17 +88,26 @@ sub HMCCU_GetReadingName ($$$$$$); sub HMCCU_SetError ($$); sub HMCCU_SetState ($$); sub HMCCU_Substitute ($$); -sub HMCCU_UpdateClientReading ($$$$); +sub HMCCU_UpdateClientReading ($@); sub HMCCU_StartRPCServer ($); sub HMCCU_StopRPCServer ($); sub HMCCU_IsRPCServerRunning ($); sub HMCCU_GetDeviceList ($); sub HMCCU_GetAddress ($$$); +sub HMCCU_GetCCUObjectAttribute ($$); +sub HMCCU_GetHash; sub HMCCU_GetDeviceName ($$); sub HMCCU_GetChannelName ($$); sub HMCCU_GetDeviceType ($$); sub HMCCU_GetDeviceInterface ($$); sub HMCCU_ReadRPCQueue ($); +sub HMCCU_HTTPRequest ($@); +sub HMCCU_HMScript ($$); +sub HMCCU_GetDatapoint ($@); +sub HMCCU_SetDatapoint ($$$); +sub HMCCU_GetVariables ($$); +sub HMCCU_SetVariable ($$$); +sub HMCCU_GetChannel ($$); ##################################### @@ -112,7 +124,7 @@ sub HMCCU_Initialize ($) $hash->{GetFn} = "HMCCU_Get"; $hash->{AttrFn} = "HMCCU_Attr"; - $hash->{AttrList} = "stripchar ccureadings:0,1 ccureadingformat:name,address rpcinterval:3,5,10 rpcport rpcserver:on,off parfile statevals substitute units:0,1 loglevel:0,1,2,3,4,5,6 ". $readingFnAttributes; + $hash->{AttrList} = "stripchar ccureadings:0,1 ccureadingformat:name,address rpcinterval:3,5,10 rpcqueue rpcport rpcserver:on,off parfile statedatapoint statevals substitute updatemode:client,both,hmccu loglevel:0,1,2,3,4,5,6 ". $readingFnAttributes; } ##################################### @@ -128,7 +140,7 @@ sub HMCCU_Define ($$) return "Define CCU hostname or IP address as a parameter" if(@a < 3); $hash->{host} = $a[2]; - $hash->{Clients} = ':HMCCUDEV:'; + $hash->{Clients} = ':HMCCUDEV:HMCCUCHN:'; $hash->{DevCount} = HMCCU_GetDeviceList ($hash); @@ -195,65 +207,42 @@ sub HMCCU_Set ($@) my ($hash, @a) = @_; my $name = shift @a; my $opt = shift @a; - my $options = "devstate datapoint var execute clearmsg:noArg"; + my $options = "devstate datapoint var execute hmscript"; my $host = $hash->{host}; my $stripchar = AttrVal ($name, "stripchar", ''); + my $statedatapoint = AttrVal ($name, "statedatapoint", 'STATE'); my $statevals = AttrVal ($name, "statevals", ''); + my $ccureadings = AttrVal ($name, "ccureadings", 'name'); + my $readingformat = AttrVal ($name, "ccureadingformat", 'name'); + my $substitute = AttrVal ($name, "substitute", ''); # process set command par1 par2 ... # if more than one parameter is specified parameters # are concatenated by blanks - if ($opt eq 'state' || $opt eq 'devstate' || $opt eq 'datapoint' || $opt eq 'var') { + if ($opt eq 'devstate' || $opt eq 'datapoint' || $opt eq 'var') { my $objname = shift @a; my $objvalue = join ('%20', @a); - my $url = 'http://'.$host.':8181/do.exe?r1=dom.GetObject("'; + my $result; if (!defined ($objname) || !defined ($objvalue)) { - return HMCCU_SetError ($hash, "Usage: set $opt "); + return HMCCU_SetError ($hash, "Usage: set $opt [...]"); } $objname =~ s/$stripchar$// if ($stripchar ne ''); $objvalue = HMCCU_Substitute ($objvalue, $statevals); - my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($objname, $HMCCU_FLAG_INTERFACE); if ($opt eq 'var') { - if ($flags & $HMCCU_FLAG_NAME) { - $url .= $objname.'").State("'.$objvalue.'")'; - } - else { - return HMCCU_SetError ($hash, "Invalid variable name"); - } + $result = HMCCU_SetVariable ($hash, $objname, $objvalue); } - elsif ($opt eq 'devstate' || $opt eq 'state') { - if ($flags == $HMCCU_FLAGS_NC) { - $url .= $objname.'").State("'.$objvalue.'")'; - } - elsif ($flags == $HMCCU_FLAGS_IAC) { - $url .= $int.'.'.$add.':'.$chn.'.STATE").State("'.$objvalue.'")'; - } - else { - return HMCCU_SetError ($hash, "Object := channelname | [interface.]address:channel"); - } + elsif ($opt eq 'devstate') { + $result = HMCCU_SetDatapoint ($hash, $objname.'.'.$statedatapoint, $objvalue); } else { - # Datapoint - if ($flags == $HMCCU_FLAGS_IACD) { - $url .= $int.'.'.$add.':'.$chn.'.'.$dpt.'").State("'.$objvalue.'")'; - } - elsif ($flags == $HMCCU_FLAGS_NCD) { - $url .= $nam.'").DPByHssDP("'.$dpt.'").State("'.$objvalue.'")'; - } - else { - return HMCCU_SetError ($hash, "Object := channelname.datapoint | interface.address:channel.datapoint"); - } + $result = HMCCU_SetDatapoint ($hash, $objname, $objvalue); } - my $response = GetFileFromURL ($url); - if ($response =~ /null execute "); - } + return HMCCU_SetError ($hash, "Usage: set execute ") if (!defined ($program)); - # Query program ID + my $url = qq(http://$host:8181/do.exe?r1=dom.GetObject("$program").ProgramExecute()); $response = GetFileFromURL ($url); - if ($response !~ /^<\?xml.*.*<\/programList>$/) { - return HMCCU_SetError ($hash, "XML-API request error (programlist.cgi)"); - } - - my $xmlin = XMLin ($response, ForceArray => 0, KeyAttr => ['name']); - if (!defined ($xmlin->{program}->{$program}->{id})) { - return HMCCU_SetError ($hash, "Program not found"); + $response =~ m/(.*)<\/r1>/; + my $value = $1; + if (defined ($value) && $value ne '' && $value ne 'null') { + HMCCU_SetState ($hash, "OK"); } else { - $programid = $xmlin->{program}->{$program}->{id}; + HMCCU_SetError ($hash, "Program execution error"); } - GetFileFromURL ($runurl . $programid); - - HMCCU_SetState ($hash, "OK"); - return undef; } - elsif ($opt eq 'clearmsg') { - my $url = 'http://'.$host.'/config/xmlapi/systemNotificationClear.cgi'; + elsif ($opt eq 'hmscript') { + my $scrfile = shift @a; + my $script; + my $response; - GetFileFromURL ($url); + return HMCCU_SetError ($hash, "Usage: set hmscript ") if (!defined ($scrfile)); + if (open (SCRFILE, "<$scrfile")) { + my @lines = ; + $script = join ("\n", @lines); + close (SCRFILE); + } + else { + return HMCCU_SetError ($hash, "Can't open file $scrfile"); + } + + $response = HMCCU_HMScript ($host, $script); + return HMCCU_SetError ($hash, -2) if ($response eq ''); HMCCU_SetState ($hash, "OK"); + return $response if (! $ccureadings); + + foreach my $line (split /\n/, $response) { + my @tokens = split /=/, $line; + next if (@tokens != 2); + my $reading; + my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($tokens[0], $HMCCU_FLAG_INTERFACE); + ($add, $chn) = HMCCU_GetAddress ($nam, '', '') if ($flags == $HMCCU_FLAGS_NCD); + if ($flags == $HMCCU_FLAGS_IACD || $flags == $HMCCU_FLAGS_NCD) { + $reading = HMCCU_GetReadingName ($int, $add, $chn, $dpt, $nam, $readingformat); + HMCCU_UpdateClientReading ($hash, $add, $chn, $reading, $tokens[1]); + } + else { + my $Value = HMCCU_Substitute ($tokens[1], $substitute); + readingsSingleUpdate ($hash, $tokens[0], $Value, 1); + } + } return undef; } @@ -320,220 +326,71 @@ sub HMCCU_Get ($@) my $ccureadingformat = AttrVal ($name, "ccureadingformat", 'name'); my $ccureadings = AttrVal ($name, "ccureadings", 1); my $parfile = AttrVal ($name, "parfile", ''); + my $statedatapoint = AttrVal ($name, "statedatapoint", 'STATE'); + my $substitute = AttrVal ($name, 'substitute', ''); my $readname; my $readaddr; + my $result = ''; + my $rc; - if ($opt eq 'state' || $opt eq 'devstate') { + if ($opt eq 'devstate') { my $ccuobj = shift @a; my $reading = shift @a; - my $response; - my $retcode; - my $url = 'http://'.$host.':8181/do.exe?r1=dom.GetObject("'; if (!defined ($ccuobj)) { return HMCCU_SetError ($hash, "Usage: get devstate { | [.]
: } []"); } + $reading = '' if (!defined ($reading)); - my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($ccuobj, $HMCCU_FLAG_INTERFACE); - if ($flags == $HMCCU_FLAGS_NC) { - $url .= $ccuobj.'").State()'; - $add = HMCCU_GetAddress ($nam, '', 1); - } - elsif ($flags == $HMCCU_FLAGS_IAC) { - $url .= $int.'.'.$add.':'.$chn.'.STATE").State()'; - } - else { - return HMCCU_SetError ($hash, "object := channelname | [interface.]address:channel"); - } + ($rc, $result) = HMCCU_GetDatapoint ($hash, $ccuobj.'.'.$statedatapoint, $reading); + return HMCCU_SetError ($hash, $rc) if ($rc < 0); - # Build reading name - if (!defined ($reading)) { - $reading = HMCCU_GetReadingName ($int, $add, $chn, 'STATE', $nam, - $ccureadingformat); - } - - # Send request to CCU - $response = GetFileFromURL ($url); - $response =~ m/(.*)<\/r1>/; - $retcode = $1; - - if (defined ($retcode) && $retcode ne '' && $retcode ne 'null') { - HMCCU_SetState ($hash, "OK"); - if ($reading ne '') { - $retcode = HMCCU_UpdateClientReading ($hash, $add, $reading, $retcode); - } - return $ccureadings ? undef : $retcode; - } - else { - Log 1,"HMCCU: Error URL = ".$url; - return HMCCU_SetError ($hash, "Error during CCU request"); - } + HMCCU_SetState ($hash, "OK"); + return $ccureadings ? undef : $result; } elsif ($opt eq 'datapoint') { my $ccuobj = shift @a; my $reading = shift @a; - my $response; - my $retcode; - my $url = 'http://'.$host.':8181/do.exe?r1=dom.GetObject("'; - if (!defined ($ccuobj)) { - return HMCCU_SetError ($hash, - "Usage: get datapoint { . | [.]
:. } []"); - } + return HMCCU_SetError ($hash, + "Usage: get datapoint {|}. []") if (!defined ($ccuobj)); + $reading = '' if (!defined ($reading)); - my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($ccuobj, $HMCCU_FLAG_INTERFACE); - if ($flags == $HMCCU_FLAGS_IACD) { - $url .= $int.'.'.$add.':'.$chn.'.'.$dpt.'").State()'; - } - elsif ($flags == $HMCCU_FLAGS_NCD) { - $url .= $nam.'").DPByHssDP("'.$dpt.'").State()'; - $add = HMCCU_GetAddress ($nam, '', 1); - } - else { - return HMCCU_SetError ($hash, "object := channelname.datapoint | [interface.]address:channel.datapoint"); - } + ($rc, $result) = HMCCU_GetDatapoint ($hash, $ccuobj, $reading); + return HMCCU_SetError ($hash, $rc) if ($rc < 0); - if (!defined ($reading)) { - $reading = HMCCU_GetReadingName ($int, $add, $chn, $dpt, $nam, $ccureadingformat); - } - - $response = GetFileFromURL ($url); - $response =~ m/(.*)<\/r1>/; - $retcode = $1; - - if (defined ($retcode) && $retcode ne '' && $retcode ne 'null') { - HMCCU_SetState ($hash, "OK"); - if ($reading ne '') { - $retcode = HMCCU_UpdateClientReading ($hash, $add, $reading, $retcode); - } - return $ccureadings ? undef : $retcode; - } - else { - Log 1,"HMCCU: Error URL = ".$url; - return HMCCU_SetError ($hash, "Error during CCU request"); - } + HMCCU_SetState ($hash, "OK"); + return $ccureadings ? undef : $result; } elsif ($opt eq 'vars') { my $varname = shift @a; - my $url = 'http://'.$host.'/config/xmlapi/sysvarlist.cgi'; - my $response; - my $result = ''; - if (!defined ($varname)) { - return HMCCU_SetError ($hash, "Usage: get vars [,...]"); - } + return HMCCU_SetError ($hash, "Usage: get vars [,...]") if (!defined ($varname)); - my @varlist = split /,/, $varname; + ($rc, $result) = HMCCU_GetVariables ($hash, $varname); + return HMCCU_SetError ($hash, $rc) if ($rc < 0); - $response = GetFileFromURL ($url); - if ($response !~ /^<\?xml.*.*<\/systemVariables>$/) { - return HMCCU_SetError ($hash, "XML-API request error (sysvarlist.cgi)"); - } - - my $xmlin = XMLin ($response, ForceArray => 0, KeyAttr => ['systemVariable']); - - if ($ccureadings) { - readingsBeginUpdate ($hash); - } - - foreach my $variable (@{$xmlin->{systemVariable}}) { - foreach my $varexp (@varlist) { - if ($variable->{name} =~ /$varexp/) { - $result .= $variable->{name}."=".$variable->{value}."\n"; - if ($ccureadings) { - readingsBulkUpdate ($hash, $variable->{name}, $variable->{value}); - } - } - } - } - - if ($ccureadings) { - readingsEndUpdate ($hash, 1); - HMCCU_SetState ($hash, "OK"); - return undef; - } - else { - HMCCU_SetState ($hash, "OK"); - return $result; - } + HMCCU_SetState ($hash, "OK"); + return $ccureadings ? undef : $result; } elsif ($opt eq 'channel') { my $param = shift @a; - my $url = 'http://'.$host.'/config/xmlapi/statelist.cgi'; - my $response; - my $result = ''; - my $rname; - my $devname = ''; - my $chnname = ''; - my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($param, - $HMCCU_FLAG_INTERFACE | $HMCCU_FLAG_DATAPOINT); + return HMCCU_SetError ($hash, "object := {channelname|channeladdress}.datapoint_expression") if (!defined ($param)); - if ($flags == $HMCCU_FLAGS_NCD || $flags == $HMCCU_FLAGS_IACD) { - $response = GetFileFromURL ($url); - if ($response !~ /^<\?xml.*.*<\/stateList>$/) { - return HMCCU_SetError ($hash, "XML-API request error (statelist.cgi)"); - } - - if ($flags == $HMCCU_FLAGS_IACD) { - $devname = HMCCU_GetDeviceName ($add, ''); - $chnname = HMCCU_GetChannelName ($add.':'.$chn, ''); - if ($devname eq '' || $chnname eq '') { - return HMCCU_SetError ($hash, "Device name or channel name not found for address $add"); - } - } - else { - $chnname = $nam; - $add = HMCCU_GetAddress ($nam, '', 1); - $devname = HMCCU_GetDeviceName ($add, ''); - } - - my $xmlin = XMLin ($response, - ForceArray => ['device', 'channel', 'datapoint'], - KeyAttr => { device => 'name', channel => 'name' } - ); - if (!defined (@{$xmlin->{device}->{$devname}->{channel}->{$chnname}->{datapoint}})) { - return HMCCU_SetError ($hash, "Device $devname or channel $chnname not defined in CCU"); - } - - my @dps = @{$xmlin->{device}->{$devname}->{channel}->{$chnname}->{datapoint}}; - - foreach my $dp (@dps) { - if ($dp->{name} =~ /$dpt/) { - my $dpname = $dp->{name}; - my $v = $dp->{value}; - - $dpname =~ /.*\.(.*)$/; - $rname = HMCCU_GetReadingName ($int, $add, $chn, $1, - $chnname, $ccureadingformat); - if ($rname ne '') { - $v = HMCCU_UpdateClientReading ($hash, $add, $rname, $v); - } - $result .= $rname."=".$v."\n"; - } - } - } - else { - HMCCU_SetError ($hash, "object := channelname.datapoint_expression | [interface.]address:channel.datapoint_expression"); - } + my @chnlist = ($param); + ($rc, $result) = HMCCU_GetChannel ($hash, \@chnlist); + return HMCCU_SetError ($hash, $rc) if ($rc < 0); HMCCU_SetState ($hash, "OK"); - return $ccureadings ? undef : $result; } elsif ($opt eq 'parfile') { my $par_parfile = shift @a; - my $url = 'http://'.$host.'/config/xmlapi/statelist.cgi'; my @parameters; my $parcount; - my $response; - my $result = ''; - my $rname; - my $text; - my $devname = ''; - my $chnname = ''; if (defined ($par_parfile)) { $parfile = $par_parfile; @@ -554,83 +411,31 @@ sub HMCCU_Get ($@) return HMCCU_SetError ($hash, "Empty parameter file") if ($parcount < 1); - $response = GetFileFromURL ($url); - if ($response !~ /^<\?xml.*.*<\/stateList>$/) { - return HMCCU_SetError ($hash, "XML-API request error (statelist.cgi)"); + ($rc, $result) = HMCCU_GetChannel ($hash, \@parameters); + return HMCCU_SetError ($hash, $rc) if ($rc < 0); + + HMCCU_SetState ($hash, "OK"); + return $ccureadings ? undef : $result; + } + elsif ($opt eq 'devicelist') { + my $dumplist = shift @a; + + $hash->{DevCount} = HMCCU_GetDeviceList ($hash); + + if ($hash->{DevCount} < 0) { + return HMCCU_SetError ($hash, "HM Script execution failed"); } - - my $xmlin = XMLin ( - $response, - ForceArray => ['device', 'channel', 'datapoint'], - KeyAttr => { device => 'name', channel => 'name' } - ); - - my $lineno = 0; - foreach my $param (@parameters) { - my @partoks = split /\s+/, $param; - $lineno++; - - # Ignore empty lines or comments - next if (scalar @partoks == 0); - next if ($partoks[0] =~ /^#/); - - my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($partoks[0], - $HMCCU_FLAG_INTERFACE | $HMCCU_FLAG_DATAPOINT); - - # Ignore wrong CCU object specifications - if ($flags != $HMCCU_FLAGS_IACD && $flags != $HMCCU_FLAGS_NCD) { - Log 1, "Invalid CCU object name or address ".$partoks[0]." in line $lineno"; - next; - } - - if ($flags == $HMCCU_FLAGS_IACD) { - $devname = HMCCU_GetDeviceName ($add, ''); - $chnname = HMCCU_GetChannelName ($add.':'.$chn, ''); - if ($devname eq '' || $chnname eq '') { - Log 1, "Device name or channel name not found for address $add in line $lineno"; - next; - } - } - else { - $chnname = $nam; - $add = HMCCU_GetAddress ($nam, '', 1); - $devname = HMCCU_GetDeviceName ($add, ''); - } - - # Ignore not existing devices/channels/datapoints - if (!defined (@{$xmlin->{device}->{$devname}->{channel}->{$chnname}->{datapoint}})) { - Log 1,"Device $devname or channel $chnname not defined in CCU in line $lineno"; - next; - } - - my @dps = @{$xmlin->{device}->{$devname}->{channel}->{$chnname}->{datapoint}}; - - foreach my $dp (@dps) { - if ($dp->{name} =~ /$dpt/) { - my $dpname = $dp->{name}; - my $v = $dp->{value}; - - $v = HMCCU_Substitute ($v, $partoks[1]) if (@partoks > 1); - $dpname =~ /.*\.(.*)$/; - $rname = HMCCU_GetReadingName ($int, $add, $chn, $1, - $chnname, $ccureadingformat); - if ($rname ne '') { - $v = HMCCU_UpdateClientReading ($hash, $add, $rname, $v); - } - $result .= $rname."=".$v.' '.$dp->{valueunit}."\n"; - } - } + elsif ($hash->{DevCount} == 0) { + return HMCCU_SetError ($hash, "No devices received from CCU"); } HMCCU_SetState ($hash, "OK"); - return $ccureadings ? undef : $result; - } - elsif ($opt eq 'devicelist') { - $hash->{DevCount} = HMCCU_GetDeviceList ($hash); - - if ($hash->{DevCount} > 0) { - HMCCU_SetState ($hash, "OK"); + if (defined ($dumplist) && $dumplist eq 'dump') { + foreach my $add (sort keys %HMCCU_Devices) { + $result .= $HMCCU_Devices{$add}{name}."\n"; + } + return $result; } return undef; @@ -714,58 +519,102 @@ sub HMCCU_ParseObject ($$) # Check if name is a valid channel name if ($f & $HMCCU_FLAG_NAME) { - my $add = HMCCU_GetAddress ($n, '', 0); - if ($add ne '' && $add =~ /:[0-9]$/) { + my ($add, $chn) = HMCCU_GetAddress ($n, '', ''); + if ($chn ne '') { $f = $f | $HMCCU_FLAG_CHANNEL; } + if ($flags & $HMCCU_FLAG_FULLADDR) { + ($i, $a, $c) = (HMCCU_GetDeviceInterface ($add, 'BidCos-RF'), $add, $chn); + $f |= $HMCCU_FLAG_INTERFACE; + $f |= $HMCCU_FLAG_ADDRESS if ($add ne ''); + $f |= $HMCCU_FLAG_CHANNEL if ($chn ne ''); + } } return ($i, $a, $c, $d, $n, $f); } +################################################################## +# Build reading name +# +# Parameters: +# +# Interface,Address,ChannelNo,Datapoint,ChannelNam,ReadingFormat +# +# ReadingFormat := { name | datapoint | address } +# +# Valid combinations: +# +# ChannelNam,Datapoint +# Address,Datapoint +# Address,ChannelNo,Datapoint +# +################################################################## + sub HMCCU_GetReadingName ($$$$$$) { my ($i, $a, $c, $d, $n, $rf) = @_; + # Datapoint is mandatory return '' if ($d eq ''); - if (($a eq '' || $c eq '') && $n ne '') { - $a = HMCCU_GetAddress ($n, '', 0); - if ($a =~ /:[0-9]+$/) { - $c = $a; - $a =~ s/:[0-9]+$//; - $c =~ s/^.+://; - } - $i = HMCCU_GetDeviceInterface ($n, ''); + if ($rf eq 'datapoint') { + return $d; } - elsif ($a ne '' && $n eq '') { - if ($c ne '') { - $n = HMCCU_GetChannelName ($a.":".$c, ''); + elsif ($rf eq 'name') { + if ($n eq '') { + if ($a ne '' && $c ne '') { + $n = HMCCU_GetChannelName ($a.':'.$c, ''); + } + elsif ($a ne '' && $c eq '') { + $n = HMCCU_GetDeviceName ($a, ''); + } + else { + return ''; + } } - else { - $n = HMCCU_GetDeviceName ($a, ''); + + return $n ne '' ? $n.'.'.$d : ''; + } + elsif ($rf eq 'address') { + if ($a eq '' && $n ne '') { + ($a, $c) = HMCCU_GetAddress ($n, '', ''); + } + + if ($a ne '') { + my $t = $a; + $i = HMCCU_GetDeviceInterface ($a, '') if ($i eq ''); + $t = $i.'.'.$t if ($i ne ''); + $t = $t.':'.$c if ($c ne ''); + return $t.'.'.$d; } } - $i = HMCCU_GetDeviceInterface ($a, '') if ($a ne ''); - - if ($rf eq 'name') { - return ($n ne '') ? $n.'.'.$d : ''; - } - else { - return ($i ne '' && $a ne '' && $c ne '') ? $i.'.'.$a.':'.$c.'.'.$d : ''; - } + return ''; } sub HMCCU_SetError ($$) { my ($hash, $text) = @_; my $name = $hash->{NAME}; + my $msg; + my %errlist = ( + -1 => 'Channel name or address invalid', + -2 => 'Execution of CCU script or command failed', + -3 => 'Cannot detect IO device' + ); - $text = "HMCCU: ".$name." ". $text; + if (exists ($errlist{$text})) { + $msg = $errlist{$text}; + } + else { + $msg = $text; + } + + $msg = "HMCCU: ".$name." ". $msg; HMCCU_SetState ($hash, "Error"); - Log 1, $text; - return $text; + Log 1, $msg; + return $msg; } sub HMCCU_SetState ($$) @@ -797,70 +646,87 @@ sub HMCCU_Substitute ($$) return $value; } -#################################################### +############################################################## # Update HMCCU readings and client readings. -# Reading values are substituted if attribute -# substitute is set in client device. -# Parameters: hash, clientdev, reading, value -#################################################### +# +# Parameters: +# hash, devadd, channelno, reading, value, [mode] +# +# Parameter devadd can be a device or a channel address. +# Parameter clientonly is ignored! +# Reading values are substituted if attribute substitute is +# is set in client device. +############################################################## -sub HMCCU_UpdateClientReading ($$$$) +sub HMCCU_UpdateClientReading ($@) { - my ($hash, $clientadd, $reading, $value) = @_; + my ($hash, $devadd, $channel, $reading, $value, $mode) = @_; my $name = $hash->{NAME}; my $hmccu_substitute = AttrVal ($name, 'substitute', ''); my $hmccu_updreadings = AttrVal ($name, 'ccureadings', 1); + my $updatemode = AttrVal ($name, 'updatemode', 'hmccu'); + $updatemode = $mode if (defined ($mode)); # Check syntax - return 0 if (!defined ($hash) || !defined ($clientadd) || - !defined ($reading) || !defined ($value)); + return 0 if (!defined ($hash) || !defined ($devadd) || + !defined ($channel) || !defined ($reading) || !defined ($value)); + my $chnadd = $channel ne '' ? $devadd.':'.$channel : $devadd; my $hmccu_value = ''; + my $dpt = ''; + if ($reading =~ /.*\.(.+)$/) { + $dpt = $1; + } + + if ($hmccu_updreadings && ($updatemode eq 'hmccu' || $updatemode eq 'both')) { + $hmccu_value = HMCCU_Substitute ($value, $hmccu_substitute); + readingsSingleUpdate ($hash, $reading, $hmccu_value, 1); + return $hmccu_value if ($updatemode eq 'hmccu'); + } # Update client readings foreach my $d (keys %defs) { - # Get hash of device + # Get hash and name of client device my $ch = $defs{$d}; + my $cn = $ch->{NAME}; - if (defined ($ch->{IODev}) && $ch->{IODev} == $hash && - defined ($ch->{ccuaddr}) && $ch->{ccuaddr} eq $clientadd) { - my $upd = AttrVal ($ch->{NAME}, 'ccureadings', 1); - my $crf = AttrVal ($ch->{NAME}, 'ccureadingformat', 'name'); - my $substitute = AttrVal ($ch->{NAME}, 'substitute', ''); + next if (!defined ($ch->{IODev}) || !defined ($ch->{IODev}) || !defined ($ch->{ccuaddr})); + next if ($ch->{ccuaddr} ne $devadd && $ch->{ccuaddr} ne $chnadd); - last if ($upd == 0); + # Get attributes of client device + my $upd = AttrVal ($cn, 'ccureadings', 1); + my $crf = AttrVal ($cn, 'ccureadingformat', 'name'); + my $substitute = AttrVal ($cn, 'substitute', ''); + last if ($upd == 0); - my $clreading = $reading; - if ($crf eq 'datapoint') { - $clreading =~ s/.*\.(.+)$/$1/; - } - - # Client substitute attribute has priority - if ($substitute ne '') { - $hmccu_value = HMCCU_Substitute ($value, $substitute); - $value = HMCCU_Substitute ($value, $substitute); - } - else { - $hmccu_value = HMCCU_Substitute ($value, $hmccu_substitute); - $value = $hmccu_value; - } - - readingsSingleUpdate ($ch, $clreading, $value, 1); - if ($reading =~ /\.STATE$/) { - HMCCU_SetState ($ch, $value); - } - - # There should be no duplicate devices - last; + my $clreading = $reading; + if ($crf eq 'datapoint') { + $clreading = $dpt ne '' ? $dpt : $reading; + } + elsif ($crf eq 'name') { + $clreading = HMCCU_GetChannelName ($chnadd, $reading); + $clreading .= '.'.$dpt if ($dpt ne ''); + } + elsif ($crf eq 'address') { + my $int = HMCCU_GetDeviceInterface ($devadd, 'BidCos-RF'); + $clreading = $int.'.'.$chnadd; + $clreading .= '.'.$dpt if ($dpt ne ''); } - } - if ($hmccu_substitute ne '') { - $hmccu_value = HMCCU_Substitute ($value, $hmccu_substitute); - } - if ($hmccu_updreadings) { - readingsSingleUpdate ($hash, $reading, $hmccu_value, 1); + # Client substitute attribute has priority + my $cl_value; + if ($substitute ne '') { + $cl_value = HMCCU_Substitute ($value, $substitute); + } + else { + $cl_value = HMCCU_Substitute ($value, $hmccu_substitute); + } + + readingsSingleUpdate ($ch, $clreading, $cl_value, 1); + if ($clreading =~ /\.STATE$/) { + HMCCU_SetState ($ch, $cl_value); + } } return $hmccu_value; @@ -877,7 +743,7 @@ sub HMCCU_StartRPCServer ($) my $modpath = AttrVal ('global', 'modpath', '/opt/fhem'); my $logfile = $modpath."/log/ccurpcd.log"; - my $queue = "/tmp/ccuqueue"; + my $rpcqueue = AttrVal ($name, 'rpcqueue', '/tmp/ccuqueue'); my $rpcport = AttrVal ($name, 'rpcport', '2001'); if (HMCCU_IsRPCServerRunning ($hash)) { @@ -901,7 +767,7 @@ sub HMCCU_StartRPCServer ($) if (!$pid) { # Child process, replace it by RPC server - exec ($rpcserver." ".$hash->{host}." ".$rpcport." ".$queue." ".$logfile); + exec ($rpcserver." ".$hash->{host}." ".$rpcport." ".$rpcqueue." ".$logfile); # When we reach this line start of RPC server failed die; @@ -922,7 +788,7 @@ sub HMCCU_StopRPCServer ($) my ($hash) = @_; if (HMCCU_IsRPCServerRunning ($hash)) { - Log 1, "HMCCU: Stoppinng RPC server"; + Log 1, "HMCCU: Stopping RPC server"; kill ('INT', $hash->{RPCPID}); $hash->{RPCPID} = 0; return 1; @@ -959,31 +825,49 @@ sub HMCCU_GetDeviceList ($) my ($hash) = @_; my $count = 0; - my $url = "http://".$hash->{host}."/config/xmlapi/devicelist.cgi?show_internal=1"; - my $response = GetFileFromURL ($url); - if ($response !~ /^<\?xml.*.*<\/deviceList>$/) { - HMCCU_SetError ($hash, "XML-API request error (devicelist.cgi)"); - return 0; - } + my $script = qq( +string devid; +string chnid; +foreach(devid, root.Devices().EnumUsedIDs()) +{ + object odev=dom.GetObject(devid); + string intid=odev.Interface(); + string intna=dom.GetObject(intid).Name(); + integer cc=0; + foreach (chnid, odev.Channels()) + { + object ochn=dom.GetObject(chnid); + WriteLine("C;" # ochn.Address() # ";" # ochn.Name()); + cc=cc+1; + } + WriteLine("D;" # intna # ";" # odev.Address() # ";" # odev.Name() # ";" # odev.HssType()) # ";" # cc; +} + ); + + my $response = HMCCU_HMScript ($hash->{host}, $script); + return -1 if ($response eq ''); - my $devlist = XMLin ($response, ForceArray => ['channel'], KeyAttr => ['address']); %HMCCU_Devices = (); %HMCCU_Addresses = (); + $HMCCU_UpdateTime = time (); - foreach my $da (keys %{$devlist->{'device'}}) { - # Device address in format CCCNNNNNNN - my $devname = $devlist->{'device'}->{$da}->{'name'}; - $HMCCU_Devices{$da}{name} = $devname; - $HMCCU_Devices{$da}{type} = $devlist->{'device'}->{$da}->{'device_type'}; - $HMCCU_Devices{$da}{interface} = $devlist->{'device'}->{$da}->{'interface'}; - $HMCCU_Addresses{$devname} = $da; - $count++; - - # Channels. Channel address in format CCCNNNNNNN:N - foreach my $ca (keys %{$devlist->{'device'}->{$da}->{'channel'}}) { - my $chnname = $devlist->{'device'}->{$da}->{'channel'}->{$ca}->{'name'}; - $HMCCU_Devices{$ca}{name} = $chnname; - $HMCCU_Addresses{$chnname} = $ca; + foreach my $hmdef (split /\n/,$response) { + my @hmdata = split /;/,$hmdef; + if ($hmdata[0] eq 'D') { + $HMCCU_Devices{$hmdata[2]}{name} = $hmdata[3]; + $HMCCU_Devices{$hmdata[2]}{type} = $hmdata[4]; + $HMCCU_Devices{$hmdata[2]}{interface} = $hmdata[1]; + $HMCCU_Devices{$hmdata[2]}{channels} = $hmdata[5]; + $HMCCU_Devices{$hmdata[2]}{addtype} = 'dev'; + $HMCCU_Addresses{$hmdata[3]}{address} = $hmdata[2]; + $HMCCU_Addresses{$hmdata[3]}{addtype} = 'dev'; + $count++; + } + elsif ($hmdata[0] eq 'C') { + $HMCCU_Devices{$hmdata[1]}{name} = $hmdata[2]; + $HMCCU_Devices{$hmdata[1]}{addtype} = 'chn'; + $HMCCU_Addresses{$hmdata[2]}{address} = $hmdata[1]; + $HMCCU_Addresses{$hmdata[2]}{addtype} = 'chn'; $count++; } } @@ -1002,7 +886,7 @@ sub HMCCU_GetDeviceName ($$) if ($addr =~ /^[A-Z]{3,3}[0-9]{7,7}$/ || $addr =~ /^[A-Z]{3,3}[0-9]{7,7}:[0-9]+$/) { $addr =~ s/:[0-9]+$//; - if (defined ($HMCCU_Devices{$addr})) { + if (exists ($HMCCU_Devices{$addr})) { return $HMCCU_Devices{$addr}{name}; } } @@ -1018,8 +902,10 @@ sub HMCCU_GetChannelName ($$) { my ($addr, $default) = @_; - if ($addr =~ /^[A-Z]{3,3}[0-9]{7,7}:[0-9]+$/ && defined ($HMCCU_Devices{$addr})) { - return $HMCCU_Devices{$addr}{name}; + if ($addr =~ /^[A-Z]{3,3}[0-9]{7,7}:[0-9]+$/) { + if (exists ($HMCCU_Devices{$addr})) { + return $HMCCU_Devices{$addr}{name}; + } } return $default; @@ -1036,7 +922,7 @@ sub HMCCU_GetDeviceType ($$) if ($addr =~ /^[A-Z]{3,3}[0-9]{7,7}$/ || $addr =~ /^[A-Z]{3,3}[0-9]{7,7}:[0-9]+$/) { $addr =~ s/:[0-9]+$//; - if (defined ($HMCCU_Devices{$addr})) { + if (exists ($HMCCU_Devices{$addr})) { return $HMCCU_Devices{$addr}{type}; } } @@ -1055,7 +941,7 @@ sub HMCCU_GetDeviceInterface ($$) if ($addr =~ /^[A-Z]{3,3}[0-9]{7,7}$/ || $addr =~ /^[A-Z]{3,3}[0-9]{7,7}:[0-9]+$/) { $addr =~ s/:[0-9]+$//; - if (defined ($HMCCU_Devices{$addr})) { + if (exists ($HMCCU_Devices{$addr})) { return $HMCCU_Devices{$addr}{interface}; } } @@ -1065,21 +951,97 @@ sub HMCCU_GetDeviceInterface ($$) #################################################### # Get address of a CCU device or channel by name. -# If parameter devaddress is set to 1, channel -# number will be removed from address. +# Return array with device address and channel no. #################################################### sub HMCCU_GetAddress ($$$) { - my ($name, $default, $devaddress) = @_; + my ($name, $defadd, $defchn) = @_; + my $add = $defadd; + my $chn = $defchn; - if (defined ($HMCCU_Addresses{$name})) { - my $addr = $HMCCU_Addresses{$name}; - $addr =~ s/:[0-9]+$// if ($devaddress == 1); - return $addr; + if (exists ($HMCCU_Addresses{$name})) { + my $addr = $HMCCU_Addresses{$name}{address}; + if ($addr =~ /^([A-Z]{3,3}[0-9]{7,7}):([0-9]+)$/) { + ($add, $chn) = ($1, $2); + } + elsif ($addr =~ /^[A-Z]{3,3}[0-9]{7,7}$/) { + $add = $addr; + } + } + else { + my $response = HMCCU_GetCCUObjectAttribute ($name, "Address()"); + if (defined ($response)) { + if ($response =~ /^([A-Z]{3,3}[0-9]{7,7}):([0-9]+)$/) { + ($add, $chn) = ($1, $2); + $HMCCU_Addresses{$name}{address} = $response; + $HMCCU_Addresses{$name}{addtype} = 'chn'; + } + elsif ($response =~ /^([A-Z]{3,3}[0-9]{7,7})$/) { + $add = $1; + $HMCCU_Addresses{$name}{address} = $response; + $HMCCU_Addresses{$name}{addtype} = 'dev'; + } + } } - return $default; + return ($add, $chn); +} + +sub HMCCU_GetCCUObjectAttribute ($$) +{ + my ($object, $attr) = @_; + + my $hash = HMCCU_GetHash (); + my $url = 'http://'.$hash->{host}.':8181/do.exe?r1=dom.GetObject("'.$object.'").'.$attr; + my $response = GetFileFromURL ($url); + if (defined ($response) && $response !~ /null(.+)<\/r1>/) { + return $1; + } + } + + return undef; +} + +#################################################### +# Get hash of HMCCU device +# Useful for client devices. +#################################################### + +sub HMCCU_GetHash +{ + foreach my $dn (sort keys %defs) { + return $defs{$dn} if ($defs{$dn}->{TYPE} eq 'HMCCU'); + } + + return undef; +} + +sub HMCCU_HTTPRequest ($@) +{ + my ($hash, $int, $add, $chn, $dpt, $val) = @_; + + my $host = $hash->{host}; + my $addr = $int.'.'.$add.':'.$chn.'.'.$dpt; + my $url = 'http://'.$host.':8181/do.exe?r1=dom.GetObject("'.$addr.'")'; + + if (defined ($val)) { + $url .= '.State("'.$val.'")'; + } + else { + $url .= '.State()'; + } + + my $response = GetFileFromURL ($url); + if (defined ($response) && $response =~ /(.+)<\/r1>/) { + my $retval = $1; + if ($retval ne 'null') { + return defined ($val) ? 1 : $retval; + } + } + + return 0; } #################################################### @@ -1092,35 +1054,346 @@ sub HMCCU_ReadRPCQueue ($) my $name = $hash->{NAME}; my $maxevents = 10; my $eventno = 0; + my $f = 0; - my $rpcinterval = AttrVal ($name, 'rpcinterval', 3); + my $rpcinterval = AttrVal ($name, 'rpcinterval', 5); + my $ccureadingformat = AttrVal ($name, 'ccureadingformat', 'name'); + my $rpcqueue = AttrVal ($name, 'rpcqueue', '/tmp/ccuqueue'); - my $queue = new File::Queue (File => '/tmp/ccuqueue'); + my $queue = new File::Queue (File => $rpcqueue, Mode => 0666); my $element = $queue->deq(); while ($element) { - my @Tokens = split ('|', $element); + my @Tokens = split (/\|/, $element); if ($Tokens[0] eq 'EV') { + my ($add, $chn) = split (/:/, $Tokens[1]); + my $reading = HMCCU_GetReadingName ('', $add, $chn, $Tokens[2], '', + $ccureadingformat); + HMCCU_UpdateClientReading ($hash, $add, $chn, $reading, $Tokens[3], 'client'); $eventno++; last if ($eventno == $maxevents); } elsif ($Tokens[0] eq 'ND') { - $HMCCU_Devices{$Tokens[1]}{name} = $Tokens[2]; } elsif ($Tokens[0] eq 'EX') { + Log 1, "HMCCU: Received EX event. RPC server terminated."; + $f = 1; + last; + } + else { +# Log 1,"HMCCU: Unknown RPC event type ".$Tokens[0]; } $element = $queue->deq(); } - if (HMCCU_IsRPCServerRunning ($hash)) { + if ($f == 0 && HMCCU_IsRPCServerRunning ($hash)) { InternalTimer (gettimeofday()+$rpcinterval, 'HMCCU_ReadRPCQueue', $hash, 0); } else { + Log 1, "HMCCU: RPC server has been shut down. f=$f"; $hash->{RPCPID} = 0; + $attr{$name}{rpcserver} = "off"; } } +#################################################### +# Execute Homematic script on CCU +#################################################### + +sub HMCCU_HMScript ($$) +{ + # Hostname, Script-Code + my ($host, $hmscript) = @_; + + my $url = "http://".$host.":8181/tclrega.exe"; + my $ua = new LWP::UserAgent (); + my $response = $ua->post($url, Content => $hmscript); + + if (! $response->is_success ()) { + Log 1, "HMCCU: ".$response->status_line(); + return ''; + } + else { + my $output = $response->content; + $output =~ s/.*<\/xml>//; + $output =~ s/\r//g; + return $output; + } +} + +#################################################### +# Get datapoint and update reading. +#################################################### + +sub HMCCU_GetDatapoint ($@) +{ + my ($hash, $param, $reading) = @_; + my $hmccu_hash; + my $value = ''; + + my $ccureadings = AttrVal ($hash->{NAME}, 'ccureadings', 1); + my $readingformat = AttrVal ($hash->{NAME}, 'ccureadingformat', 'name'); + my $substitute = AttrVal ($hash->{NAME}, 'substitute', ''); + + if ($hash->{TYPE} ne 'HMCCU') { + # Get hash of HMCCU IO device + return (-3, $value) if (!exists ($hash->{IODev})); + $hmccu_hash = $hash->{IODev}; + } + else { + # Hash of HMCCU IO device supplied as parameter + $hmccu_hash = $hash; + } + + my $url = 'http://'.$hmccu_hash->{host}.':8181/do.exe?r1=dom.GetObject("'; + my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($param, $HMCCU_FLAG_INTERFACE); + if ($flags == $HMCCU_FLAGS_IACD) { + $url .= $int.'.'.$add.':'.$chn.'.'.$dpt.'").State()'; + } + elsif ($flags == $HMCCU_FLAGS_NCD) { + $url .= $nam.'").DPByHssDP("'.$dpt.'").State()'; + ($add, $chn) = HMCCU_GetAddress ($nam, '', ''); + } + else { + return (-1, $value); + } + + my $response = GetFileFromURL ($url); + $response =~ m/(.*)<\/r1>/; + $value = $1; + + if (defined ($value) && $value ne '' && $value ne 'null') { + if (!defined ($reading) || $reading eq '') { + $reading = HMCCU_GetReadingName ($int, $add, $chn, $dpt, $nam, $readingformat); + } + return (0, $value) if ($reading eq ''); + + if ($hash->{TYPE} eq 'HMCCU') { + $value = HMCCU_UpdateClientReading ($hmccu_hash, $add, $chn, $reading, + $value); + } + else { + $value = HMCCU_Substitute ($value, $substitute); + readingsSingleUpdate ($hash, $reading, $value, 1) if ($ccureadings); + if (($reading =~ /\.STATE$/ || $reading eq 'STATE')&& $ccureadings) { + HMCCU_SetState ($hash, $value); + } + } + + return (1, $value); + } + else { + Log 1,"HMCCU: Error URL = ".$url; + return (-2, ''); + } +} + +#################################################### +# Set datapoint +#################################################### + +sub HMCCU_SetDatapoint ($$$) +{ + my ($hash, $param, $value) = @_; + my $hmccu_hash; + + if ($hash->{TYPE} ne 'HMCCU') { + # Get hash of HMCCU IO device + return -3 if (!exists ($hash->{IODev})); + $hmccu_hash = $hash->{IODev}; + } + else { + # Hash of HMCCU IO device supplied as parameter + $hmccu_hash = $hash; + } + + my $url = 'http://'.$hmccu_hash->{host}.':8181/do.exe?r1=dom.GetObject("'; + my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($param, $HMCCU_FLAG_INTERFACE); + if ($flags == $HMCCU_FLAGS_IACD) { + $url .= $int.'.'.$add.':'.$chn.'.'.$dpt.'").State('.$value.')'; + } + elsif ($flags == $HMCCU_FLAGS_NCD) { + $url .= $nam.'").DPByHssDP("'.$dpt.'").State('.$value.')'; + ($add, $chn) = HMCCU_GetAddress ($nam, '', ''); + } + else { + return -1; + } + + my $response = GetFileFromURL ($url); + return -2 if (!defined ($response) || $response =~ /null{NAME}, 'ccureadings', 1); + + my $script = qq( +object osysvar; +string ssysvarid; +foreach (ssysvarid, dom.GetObject(ID_SYSTEM_VARIABLES).EnumUsedIDs()) +{ + osysvar = dom.GetObject(ssysvarid); + WriteLine (osysvar.Name() # "=" # osysvar.Variable() # "=" # osysvar.Value()); +} + ); + + my $response = HMCCU_HMScript ($hash->{host}, $script); + return (-2, $result) if ($response eq ''); + + readingsBeginUpdate ($hash) if ($ccureadings); + + foreach my $vardef (split /\n/, $response) { + my @vardata = split /=/, $vardef; + next if (@vardata != 3); + next if ($vardata[0] !~ /$pattern/); + readingsBulkUpdate ($hash, $vardata[0], $vardata[2]) if ($ccureadings); + $result .= $vardata[0].'='.$vardata[2]."\n"; + $count++; + } + + readingsEndUpdate ($hash, 1) if ($hash->{TYPE} ne 'HMCCU' && $ccureadings); + + return ($count, $result); +} + +#################################################### +# Set CCU system variable +#################################################### + +sub HMCCU_SetVariable ($$$) +{ + my ($hash, $param, $value) = @_; + my $url = 'http://'.$hash->{host}.':8181/do.exe?r1=dom.GetObject("'.$param.'").State("'.$value.'")'; + + my $response = GetFileFromURL ($url); + if (!defined ($response) || $response =~ /null{NAME}, 'ccureadings', 1); + my $readingformat = AttrVal ($hash->{NAME}, 'ccureadingformat', 'name'); + + if ($hash->{TYPE} ne 'HMCCU') { + # Get hash of HMCCU IO device + return (-3, $result) if (!exists ($hash->{IODev})); + $hmccu_hash = $hash->{IODev}; + } + else { + # Hash of HMCCU IO device supplied as parameter + $hmccu_hash = $hash; + } + + # Build channel list + foreach my $chndef (@$chnref) { + my ($channel, $substitute) = split /\s+/, $chndef; + next if (!defined ($channel) || $channel =~ /^#/ || $channel eq ''); + $substitute = '' if (!defined ($substitute)); + my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($channel, + $HMCCU_FLAG_INTERFACE | $HMCCU_FLAG_DATAPOINT); + if ($flags == $HMCCU_FLAGS_IACD || $flags == $HMCCU_FLAGS_NCD) { + if ($flags == $HMCCU_FLAGS_IACD) { + $nam = HMCCU_GetChannelName ($add.':'.$chn, ''); + } + + $chnlist = $chnlist eq '' ? $nam : $chnlist.','.$nam; + $chnpars{$nam}{sub} = $substitute; + $chnpars{$nam}{dpt} = $dpt; + } + else { + return (-1, $result); + } + } + + return (0, $result) if ($chnlist eq ''); + + # CCU script to query datapoints + my $script = qq( +string sDPId; +string sChannel; +string sChnList = "$chnlist"; +foreach (sChannel, sChnList.Split(",")) +{ + object oChannel = dom.GetObject (sChannel); + foreach(sDPId, oChannel.DPs().EnumUsedIDs()) + { + object oDP = dom.GetObject(sDPId); + WriteLine (sChannel # "=" # oDP.Name() # "=" # oDP.Value()); + } +} + ); + + my $response = HMCCU_HMScript ($hmccu_hash->{host}, $script); + return (-2, $result) if ($response eq ''); + + readingsBeginUpdate ($hash) if ($hash->{TYPE} ne 'HMCCU' && $ccureadings); + + foreach my $dpdef (split /\n/, $response) { + my @dpdata = split /=/, $dpdef; + next if (@dpdata != 3); + my @adrtoks = split /\./, $dpdata[1]; + next if (@adrtoks != 3); + next if ($adrtoks[2] !~ /$chnpars{$dpdata[0]}{dpt}/); + + my ($add, $chn) = split /:/, $adrtoks[1]; + my $reading = HMCCU_GetReadingName ($adrtoks[0], $add, $chn, $adrtoks[2], + $dpdata[0], $readingformat); + next if ($reading eq ''); + + my $value = HMCCU_Substitute ($dpdata[2], $chnpars{$dpdata[0]}{sub}); + if ($hash->{TYPE} eq 'HMCCU') { + HMCCU_UpdateClientReading ($hmccu_hash, $add, $chn, $reading, $value); + } + else { + if ($ccureadings) { + readingsBulkUpdate ($hash, $reading, $value); + HMCCU_SetState ($hash, $value) if ($reading =~ /\.STATE$/); + } + } + + $result .= $reading.'='.$value."\n"; + $count++; + } + + readingsEndUpdate ($hash, 1) if ($hash->{TYPE} ne 'HMCCU' && $ccureadings); + + return ($count, $result); +} 1; @@ -1175,17 +1448,19 @@ sub HMCCU_ReadRPCQueue ($)
Set CCU variable value.
-
  • set <name> clearmsg -
    - Clear CCU messages. -

  • -
  • set <name> execute <program> +
  • set <name> execute <program>
    Execute CCU program.

    Example:
    set d_ccu execute PR-TEST

  • +
  • set <name> hmscript <script-file> +
    + Execute HM script on CCU. If output of script contains lines in format + Object=Value readings will be set. Object can be the name of a CCU system + variable or a valid datapoint specification. +

  • @@ -1198,11 +1473,11 @@ sub HMCCU_ReadRPCQueue ($) Get state of a CCU device. Specified channel must have a datapoint STATE. If <reading> is specified the value will be stored using this name.
    -
  • get <name> vars <regexp>[,...] +
  • get <name> vars <regexp>
    Get CCU system variables matching <regexp> and store them as readings.

  • -
  • get <name>> channel {[<interface>.]<channel-address>[.<datapoint-expr>]|<channel-name>[.<datapoint-expr>]} +
  • get <name> channel {[<interface>.]<channel-address>[.<datapoint-expr>]|<channel-name>[.<datapoint-expr>]}
    Get value of datapoint(s). If no datapoint is specified all datapoints of specified channel are read. <datapoint> can be specified as a regular expression. @@ -1245,6 +1520,27 @@ sub HMCCU_ReadRPCQueue ($)
    Define parameter file for command get parfile.

  • +
  • rpcinterval <3 | 5 | 10> +
    + Specifiy how often RPC queue is read. Default is 5 seconds. +

  • +
  • rpcport <value> +
    + Specify RPC port on CCU. Default is 2001. +

  • +
  • rpcqueue <queue-file> +
    + Specify name of RPC queue file. This parameter is only a prefix for the + queue files with extension .idx and .dat. Default is /tmp/ccuqueue. +

  • +
  • rpcserver <on | off> +
    + Start or stop RPC server. +

  • +
  • statedatapoint <datapoint> +
    + Set datapoint for devstate commands. Default is 'STATE'. +

  • statevals <text:substext[,...]>
    Define substitutions for values in set devstate/datapoint command. @@ -1259,6 +1555,13 @@ sub HMCCU_ReadRPCQueue ($) Strip the specified character from variable or device name in set commands. This is useful if a variable should be set in CCU using the reading with trailing colon.

  • +
  • updatemode { client | both | hmccu } +
    + Set update mode for readings.
    + 'client' = update only readings of client devices
    + 'both' = update readings of client devices and IO device
    + 'hmccu' = update readings of IO device +