diff --git a/fhem/CHANGED b/fhem/CHANGED index d8ed52345..841a055ca 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -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. + - change: 88_HMCCU: New features and bug fixes - feature: 36_WMBUS: initial support for mode 7 encryption (mostly untested) Digest::CMAC must be installed - feature: 72_XiaomiDevice: added S1 vacuum states diff --git a/fhem/FHEM/88_HMCCU.pm b/fhem/FHEM/88_HMCCU.pm index a51ac5eea..dcbc21104 100755 --- a/fhem/FHEM/88_HMCCU.pm +++ b/fhem/FHEM/88_HMCCU.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 4.3.016 +# Version 4.3.017 # # Module for communication between FHEM and Homematic CCU2/3. # @@ -52,7 +52,7 @@ my %HMCCU_CUST_CHN_DEFAULTS; my %HMCCU_CUST_DEV_DEFAULTS; # HMCCU version -my $HMCCU_VERSION = '4.3.015'; +my $HMCCU_VERSION = '4.3.017'; # Constants and default values my $HMCCU_MAX_IOERRORS = 100; @@ -202,12 +202,13 @@ sub HMCCU_AggregateReadings ($$); sub HMCCU_AggregationRules ($$); # Handling of default attributes +sub HMCCU_DetectDefaults ($$); sub HMCCU_ExportDefaults ($$); sub HMCCU_ExportDefaultsCSV ($$); sub HMCCU_ImportDefaults ($); sub HMCCU_FindDefaults ($$); -sub HMCCU_SetDefaults ($); sub HMCCU_GetDefaults ($$); +sub HMCCU_SetDefaults ($); # Status and logging functions sub HMCCU_Trace ($$$$); @@ -229,7 +230,8 @@ sub HMCCU_SubstVariables ($$$); # Update client device readings sub HMCCU_BulkUpdate ($$$$); sub HMCCU_GetUpdate ($$$); -sub HMCCU_UpdateClients ($$$$$); +sub HMCCU_UpdateCB ($$$); +sub HMCCU_UpdateClients ($$$$$$); sub HMCCU_UpdateInternalValues ($$$$); sub HMCCU_UpdateMultipleDevices ($$); sub HMCCU_UpdatePeers ($$$$); @@ -251,6 +253,7 @@ sub HMCCU_RPCDeRegisterCallback ($); sub HMCCU_RPCRegisterCallback ($); sub HMCCU_RPCGetConfig ($$$$); sub HMCCU_RPCSetConfig ($$$); +sub HMCCU_RPCRequest ($$$$$;$); sub HMCCU_StartExtRPCServer ($); sub HMCCU_StartIntRPCServer ($); sub HMCCU_StopExtRPCServer ($); @@ -261,6 +264,7 @@ sub HMCCU_ParseObject ($$$); sub HMCCU_IsDevAddr ($$); sub HMCCU_IsChnAddr ($$); sub HMCCU_SplitChnAddr ($); +sub HMCCU_SplitDatapoint ($;$); # FHEM device handling functions sub HMCCU_AssignIODevice ($$$); @@ -270,6 +274,7 @@ sub HMCCU_GetHash ($@); sub HMCCU_GetAttribute ($$$$); sub HMCCU_GetFlags ($); sub HMCCU_GetAttrReadingFormat ($$); +sub HMCCU_GetAttrStripNumber ($); sub HMCCU_GetAttrSubstitute ($$); sub HMCCU_IODeviceStates (); sub HMCCU_IsFlag ($$); @@ -308,8 +313,9 @@ sub HMCCU_GetSpecialDatapoints ($$$$$); sub HMCCU_GetSwitchDatapoint ($$$); sub HMCCU_GetValidDatapoints ($$$$$); sub HMCCU_IsValidDatapoint ($$$$$); -sub HMCCU_SetDatapoint ($$$); +# sub HMCCU_SetDatapoint ($$$); sub HMCCU_SetMultipleDatapoints ($$); +sub HMCCU_SetMultipleParameters ($$$); # Internal RPC server functions sub HMCCU_ResetRPCQueue ($$); @@ -321,7 +327,7 @@ sub HMCCU_GetVariables ($$); sub HMCCU_HMCommand ($$$); sub HMCCU_HMCommandCB ($$$); sub HMCCU_HMCommandNB ($$$); -sub HMCCU_HMScriptExt ($$$); +sub HMCCU_HMScriptExt ($$$$$); sub HMCCU_SetVariable ($$$$$); sub HMCCU_UpdateVariables ($); @@ -388,8 +394,9 @@ sub HMCCU_Initialize ($) " ccudef-hmstatevals:textField-long ccudef-substitute:textField-long". " ccudef-readingname:textField-long ccudef-readingfilter:textField-long". " ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc". + " ccudef-stripnumber". " ccuflags:multiple-strict,extrpc,intrpc,procrpc,dptnocheck,noagg,nohmstate,". - "logEvents,noEvents,noReadings,nonBlocking,reconnect,logPong,trace". + "logEvents,noEvents,noInitialUpdate,noReadings,nonBlocking,reconnect,logPong,trace". " ccuReqTimeout ccuGetVars rpcinterval:2,3,5,7,10 rpcqueue rpcPingCCU". " rpcport:multiple-strict,".join(',',sort keys %HMCCU_RPC_NUMPORT). " rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute". @@ -1090,6 +1097,17 @@ sub HMCCU_GetDefaults ($$) return $result; } +###################################################################### +# Try to detect default attributes +###################################################################### + +sub HMCCU_DetectDefaults ($$) +{ + my ($hash, $object) = @_; + + my $response = HMCCU_GetDeviceInfo ($hash, $object, 'Value'); +} + ###################################################################### # Handle FHEM events ###################################################################### @@ -1375,9 +1393,12 @@ sub HMCCU_Set ($@) my ($hash, $a, $h) = @_; my $name = shift @$a; my $opt = shift @$a; - my $options = "avar clear delete execute hmscript cleardefaults:noArg defaults:noArg ". + my $options = "avar clear delete execute hmscript cleardefaults:noArg datapoint defaults:noArg ". "importdefaults rpcregister:all rpcserver:on,off,restart ackmessages:noArg authentication ". "prgActivate prgDeactivate"; + + return "No set command specified" if (!defined ($opt)); + my @ifList = HMCCU_GetRPCInterfaceList ($hash); if (scalar (@ifList) > 0) { my $ifStr = join (',', @ifList); @@ -1487,7 +1508,56 @@ sub HMCCU_Set ($@) return HMCCU_SetState ($hash, "OK"); } elsif ($opt eq 'datapoint') { - return HMCCU_SetError ($hash, "Command set datapoint is no longer supported by I/O device"); + $usage = "set $name $opt FHEM-Device[.Channel].Datapoint=Value [...]"; + return HMCCU_SetError ($hash, $usage) if (scalar (keys %$h) < 1); + + my $cmd = 1; + my %dpValues; + + foreach my $dptSpec (keys %$h) { + my $adr; + my $chn; + my $dpt; + my ($devName, $t1, $t2) = split (/\./, $dptSpec); + + return HMCCU_SetError ($hash, "FHEM device $devName not defined") + if (!exists ($defs{$devName})); + + my $dh = $defs{$devName}; + my $ccuif = $dh->{ccuif}; + + if ($dh->{TYPE} eq 'HMCCUCHN') { + return HMCCU_SetError ($hash, "Channel number not allowed for FHEM device $devName") + if (defined ($t2)); + ($adr, $chn) = HMCCU_SplitChnAddr ($dh->{ccuaddr}); + $dpt = $t1; + } + elsif ($dh->{TYPE} eq 'HMCCUDEV') { + return HMCCU_SetError ($hash, "Missing channel number for device $devName") + if (!defined ($t2)); + return HMCCU_SetError ($hash, "Invalid channel number specified for device $devName") + if ($t1 !~ /^[0-9]+$/ || $t1 > $dh->{channels}); + $adr = $dh->{ccuaddr}; + $chn = $t1; + $dpt = $t2; + } + else { + return HMCCU_SetError ($hash, "FHEM device $devName has illegal type"); + } + + return HMCCU_SetError ($hash, "Invalid datapoint $dpt specified for device $devName") + if (!HMCCU_IsValidDatapoint ($dh, $dh->{ccutype}, $chn, $dpt, 2)); + + my $statevals = AttrVal ($dh->{NAME}, 'statevals', ''); + + my $no = sprintf ("%03d", $cmd); + $dpValues{"$no.$ccuif.$devName:$chn.$dpt"} = HMCCU_Substitute ($h->{$dptSpec}, $statevals, 1, undef, ''); + $cmd++; + } + + my $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpValues); + return HMCCU_SetError ($hash, $rc) if ($rc < 0); + return HMCCU_SetState ($hash, "OK"); } elsif ($opt eq 'delete') { my $objname = shift @$a; @@ -1498,7 +1568,8 @@ sub HMCCU_Set ($@) return HMCCU_SetError ($hash, $usage) if (!defined ($objname) || $objtype !~ /^(OT_VARDP|OT_DEVICE)$/); - $result = HMCCU_HMScriptExt ($hash, "!DeleteObject", { name => $objname, type => $objtype }); + $result = HMCCU_HMScriptExt ($hash, "!DeleteObject", { name => $objname, type => $objtype }, + undef, undef); return HMCCU_SetError ($hash, -2) if ($result =~ /^ERROR:.*/); return HMCCU_SetState ($hash, "OK"); @@ -1524,7 +1595,8 @@ sub HMCCU_Set ($@) return HMCCU_SetError ($hash, $usage) if (!defined ($program)); - $result = HMCCU_HMScriptExt ($hash, "!ActivateProgram", { name => $program, mode => $mode }); + $result = HMCCU_HMScriptExt ($hash, "!ActivateProgram", { name => $program, mode => $mode }, + undef, undef); return HMCCU_SetError ($hash, -2) if ($result =~ /^ERROR:.*/); return HMCCU_SetState ($hash, "OK"); @@ -1553,13 +1625,13 @@ sub HMCCU_Set ($@) return HMCCU_SetError ($hash, $usage) if (defined ($dump) && $dump ne 'dump'); # Execute script - $response = HMCCU_HMScriptExt ($hash, $script, $h); + $response = HMCCU_HMScriptExt ($hash, $script, $h, undef, undef); return HMCCU_SetError ($hash, -2, $response) if ($response =~ /^ERROR:/); HMCCU_SetState ($hash, "OK"); return $response if (! $ccureadings || defined ($dump)); - foreach my $line (split /\n/, $response) { + foreach my $line (split /[\n\r]+/, $response) { my @tokens = split /=/, $line; next if (@tokens != 2); my $reading; @@ -1646,7 +1718,7 @@ sub HMCCU_Set ($@) return HMCCU_SetState ($hash, "OK"); } elsif ($opt eq 'ackmessages') { - my $response = HMCCU_HMScriptExt ($hash, "!ClearUnreachable", undef); + my $response = HMCCU_HMScriptExt ($hash, "!ClearUnreachable", undef, undef, undef); return HMCCU_SetError ($hash, -2, $response) if ($response =~ /^ERROR:/); return HMCCU_SetState ($hash, "OK", "Unreach errors in CCU cleared"); } @@ -1690,7 +1762,9 @@ sub HMCCU_Get ($@) my ($hash, $a, $h) = @_; my $name = shift @$a; my $opt = shift @$a; - + + return "No get command specified" if (!defined ($opt)); + my $options = "defaults:noArg exportdefaults devicelist dump dutycycle:noArg vars update". " updateccu parfile configdesc firmware rpcevents:noArg rpcstate:noArg deviceinfo". " ccumsg:alarm,service"; @@ -1763,11 +1837,10 @@ sub HMCCU_Get ($@) my $ccuget = shift @$a; $ccuget = 'Attr' if (!defined ($ccuget)); return HMCCU_SetError ($hash, $usage) if ($ccuget !~ /^(Attr|State|Value)$/); + my $nonBlocking = HMCCU_IsFlag ($name, 'nonBlocking') ? 1 : 0; - my ($co, $ce) = HMCCU_UpdateClients ($hash, $devexp, $ccuget, ($opt eq 'updateccu') ? 1 : 0, undef); - - return HMCCU_SetState ($hash, "OK", - "$co client devices successfully updated. Update for $ce client devices failed"); + HMCCU_UpdateClients ($hash, $devexp, $ccuget, ($opt eq 'updateccu') ? 1 : 0, undef, 0); + return HMCCU_SetState ($hash, "OK"); } elsif ($opt eq 'parfile') { my $par_parfile = shift @$a; @@ -1973,7 +2046,7 @@ sub HMCCU_Get ($@) } elsif ($opt eq 'dutycycle') { my $dc = HMCCU_GetDutyCycle ($hash); - return HMCCU_SetState ($hash, "OK", "Read $dc duty cycle values"); + return HMCCU_SetState ($hash, "OK"); } elsif ($opt eq 'firmware') { my $devtype = shift @$a; @@ -2069,12 +2142,12 @@ sub HMCCU_Get ($@) return HMCCU_SetError ($hash, $usage) if (!defined ($msgtype)); my $script = ($msgtype eq 'service') ? "!GetServiceMessages" : "!GetAlarms"; - my $res = HMCCU_HMScriptExt ($hash, $script, undef); + my $res = HMCCU_HMScriptExt ($hash, $script, undef, undef, undef); return HMCCU_SetError ($hash, "Error") if ($res eq '' || $res =~ /^ERROR:.*/); # Generate event for each message - foreach my $msg (split /\n/, $res) { + foreach my $msg (split /[\n\r]+/, $res) { next if ($msg =~ /^[0-9]+$/); DoTrigger ($name, $msg); } @@ -2419,14 +2492,19 @@ sub HMCCU_GetReadingName ($$$$$$$) foreach my $rr (@rules) { my ($rold, $rnew) = split (':', $rr); next if (!defined ($rnew)); + my @rnewList = split (',', $rnew); + next if (scalar (@rnewList) < 1); if ($rnlist[0] =~ /$rold/) { - if ($rnew =~ /^\+(.+)$/) { - my $radd = $1; - $radd =~ s/$rold/$radd/; - push (@rnlist, $radd); - } - else { - $rnlist[0] =~ s/$rold/$rnew/; + foreach my $rnew (@rnewList) { + if ($rnew =~ /^\+(.+)$/) { + my $radd = $1; + $radd =~ s/$rold/$radd/; + push (@rnlist, $radd); + } + else { + $rnlist[0] =~ s/$rold/$rnew/; + last; + } } } } @@ -2454,26 +2532,35 @@ sub HMCCU_GetReadingName ($$$$$$$) sub HMCCU_FormatReadingValue ($$$) { my ($hash, $value, $dpt) = @_; + my $name = $hash->{NAME}; + my $fnc = "FormatReadingValue"; - my $stripnumber = AttrVal ($hash->{NAME}, 'stripnumber', 'null'); - return $value if ($stripnumber eq 'null' || $value !~ /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/); + my $stripnumber = HMCCU_GetAttrStripNumber ($hash); - my $isint = $value =~ /^[+-]?[0-9]+$/ ? 1 : 0; + if ($stripnumber ne 'null' && $value =~ /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/) { + my $isint = $value =~ /^[+-]?[0-9]+$/ ? 1 : 0; - foreach my $sr (split (';', $stripnumber)) { - my ($d, $s) = split ('!', $sr); - if (defined ($s)) { - next if ($d eq '' || $dpt !~ /$d/); - } - else { - $s = $sr; - } + foreach my $sr (split (';', $stripnumber)) { + my ($d, $s) = split ('!', $sr); + if (defined ($s)) { + next if ($d eq '' || $dpt !~ /$d/); + } + else { + $s = $sr; + } - if ($s eq '0' && !$isint) { return sprintf ("%d", $value); } - elsif ($s eq '1' && !$isint) { return sprintf ("%.1f", $value); } - elsif ($s eq '2' && !$isint) { return sprintf ("%g", $value); } - elsif ($s =~ /^-([0-9])$/ && !$isint) { my $f = '%.'.$1.'f'; return sprintf ($f, $value); } - elsif ($s =~ /^%.+$/) { return sprintf ($s, $value); } + if ($s eq '0' && !$isint) { return sprintf ("%d", $value); } + elsif ($s eq '1' && !$isint) { return sprintf ("%.1f", $value); } + elsif ($s eq '2' && !$isint) { return sprintf ("%g", $value); } + elsif ($s =~ /^-([0-9])$/ && !$isint) { my $f = '%.'.$1.'f'; return sprintf ($f, $value); } + elsif ($s =~ /^%.+$/) { return sprintf ($s, $value); } + } + + HMCCU_Trace ($hash, 2, $fnc, "sn = $stripnumber, dpt=$dpt, isint=$isint, value $value not changed"); + } + else { + my $h = unpack "H*", $value; + HMCCU_Trace ($hash, 2, $fnc, "sn = $stripnumber, Value $value $h not changed"); } return $value; @@ -2494,7 +2581,7 @@ sub HMCCU_Trace ($$$$) return if (!HMCCU_IsFlag ($name, "trace")); foreach my $m (split ("
", $msg)) { - $m = "$fnc: $m" if (defined ($fnc) && $fnc ne ''); + $m = "[$name] $fnc: $m" if (defined ($fnc) && $fnc ne ''); Log3 $name, $level, "$type: $m"; } } @@ -2562,7 +2649,8 @@ sub HMCCU_SetError ($@) -17 => 'Cannot detect or create external RPC device', -18 => 'Type of system variable not supported', -19 => 'Device not initialized', - -20 => 'Invalid or unknown device interface' + -20 => 'Invalid or unknown device interface', + -21 => 'Device disabled' ); $msg = exists ($errlist{$text}) ? $errlist{$text} : $text; @@ -2621,8 +2709,7 @@ sub HMCCU_SetRPCState ($@) HMCCU_Log ($hash, 1, "All RPC servers $state", undef); DoTrigger ($name, "RPC server $state"); if ($state eq 'running') { - my ($c_ok, $c_err) = HMCCU_UpdateClients ($hash, '.*', 'Attr', 0, $filter); - HMCCU_Log ($hash, 2, "Updated devices. Success=$c_ok Failed=$c_err", undef); + HMCCU_UpdateClients ($hash, '.*', 'Value', 0, $filter, 1); } } } @@ -2638,7 +2725,7 @@ sub HMCCU_SetRPCState ($@) foreach my $i (@iflist) { my $st = $hash->{hmccu}{interfaces}{$i}{state}; $stc{$st}++ if (exists ($stc{$st})); - if ($hash->{hmccu}{interfaces}{$i}{manager} eq 'HMCCU') { + if ($hash->{hmccu}{interfaces}{$i}{manager} eq 'HMCCU' && $ccuflags !~ /noInitialUpdate/) { my $rpcFlags = AttrVal ($hash->{hmccu}{interfaces}{$i}{device}, 'ccuflags', 'null'); if ($rpcFlags !~ /noInitialUpdate/) { $filter = defined ($filter) ? "$filter|$i" : $i; @@ -2661,8 +2748,7 @@ sub HMCCU_SetRPCState ($@) HMCCU_Log ($hash, 1, "All RPC servers $rpcstate", undef); DoTrigger ($name, "RPC server $rpcstate"); if ($rpcstate eq 'running' && defined ($filter)) { - my ($c_ok, $c_err) = HMCCU_UpdateClients ($hash, '.*', 'Attr', 0, $filter); - HMCCU_Log ($hash, 2, "Updated devices for interface filter $filter. Success=$c_ok Failed=$c_err", undef); + HMCCU_UpdateClients ($hash, '.*', 'Value', 0, $filter, 1); } } } @@ -2841,15 +2927,18 @@ sub HMCCU_SubstVariables ($$$) # only devices belonging to interface ifname are updated. ###################################################################### -sub HMCCU_UpdateClients ($$$$$) +sub HMCCU_UpdateClients ($$$$$$) { - my ($hash, $devexp, $ccuget, $fromccu, $ifname) = @_; + my ($hash, $devexp, $ccuget, $fromccu, $ifname, $nonBlock) = @_; my $fhname = $hash->{NAME}; - my $c_ok = 0; - my $c_err = 0; + my $c = 0; my $filter = "ccudevstate=active"; $filter .= ",ccuif=$ifname" if (defined ($ifname)); + $ccuget = AttrVal ($fhname, 'ccuget', 'Value') if ($ccuget eq 'Attr'); + my $list = ''; + HMCCU_Log ($hash, 2, "Updating devices for filter $filter", 0); + if ($fromccu) { foreach my $name (sort keys %{$hash->{hmccu}{adr}}) { next if ($name !~ /$devexp/ || !($hash->{hmccu}{adr}{$name}{valid})); @@ -2860,49 +2949,41 @@ sub HMCCU_UpdateClients ($$$$$) my $ch = $defs{$d}; next if (!defined ($ch->{IODev}) || !defined ($ch->{ccuaddr})); next if ($ch->{ccuaddr} ne $hash->{hmccu}{adr}{$name}{address}); - - my $rc = HMCCU_GetUpdate ($ch, $hash->{hmccu}{adr}{$name}{address}, $ccuget); - if ($rc <= 0) { - if ($rc == -10) { - Log3 $fhname, 3, "HMCCU: Device $name has no readable datapoints"; - } - else { - Log3 $fhname, 2, "HMCCU: Update of device $name failed"; - } - $c_err++; - } - else { - $c_ok++; - } + next if ($ch->{ccuif} eq 'fhem'); + next if (!HMCCU_IsValidDeviceOrChannel ($hash, $ch->{ccuaddr}, $HMCCU_FL_ADDRESS)); + $list .= ($list eq '') ? $name : ",$name"; + $c++; } } } else { my @devlist = HMCCU_FindClientDevices ($hash, "(HMCCUDEV|HMCCUCHN)", $devexp, $filter); - Log3 $fhname, 2, "HMCCU: No client devices matching $devexp" if (scalar (@devlist) == 0); + Log3 $fhname, 2, "HMCCU: Found ".scalar(@devlist)." client devices matching $devexp"; foreach my $d (@devlist) { my $ch = $defs{$d}; - next if (!defined ($ch->{IODev}) || !defined ($ch->{ccuaddr})); - - my $rc = HMCCU_GetUpdate ($ch, $ch->{ccuaddr}, $ccuget); - if ($rc <= 0) { - if ($rc == -10) { - Log3 $fhname, 3, "HMCCU: Device ".$ch->{ccuaddr}." has no readable datapoints"; - } - else { - Log3 $fhname, 2, "HMCCU: Update of device ".$ch->{ccuaddr}." failed" - if ($ch->{ccuif} ne 'VirtualDevices' && $ch->{ccuif} ne 'fhem'); - } - $c_err++; - } - else { - $c_ok++; - } + next if (!defined ($ch->{IODev}) || !defined ($ch->{ccuaddr}) || $ch->{ccuif} eq 'fhem'); + next if (!HMCCU_IsValidDeviceOrChannel ($hash, $ch->{ccuaddr}, $HMCCU_FL_ADDRESS)); + my $name = HMCCU_GetDeviceName ($hash, $ch->{ccuaddr}, ''); + next if ($name eq ''); + $list .= ($list eq '') ? $name : ",$name"; + $c++; } } + + if (HMCCU_IsFlag ($fhname, 'nonBlocking') || $nonBlock) { + HMCCU_HMScriptExt ($hash, '!GetDatapointsByDevice', { list => $list, ccuget => $ccuget }, + \&HMCCU_UpdateCB, { logCount => 1, devCount => $c }); + return 1; + } + else { + my $response = HMCCU_HMScriptExt ($hash, '!GetDatapointsByDevice', + { list => $list, ccuget => $ccuget }, undef, undef); + return -2 if ($response eq '' || $response =~ /^ERROR:.*/); - return ($c_ok, $c_err); + HMCCU_UpdateCB ({ ioHash => $hash, logCount => 1, devCount => $c }, undef, $response); + return 1; + } } ########################################################################## @@ -3237,7 +3318,7 @@ sub HMCCU_UpdateSingleDevice ($$$$) my $peer = AttrVal ($cltname, 'peer', 'null'); my $crf = HMCCU_GetAttrReadingFormat ($clthash, $ccuhash); my $substitute = HMCCU_GetAttrSubstitute ($clthash, $ccuhash); - my ($sc, $st, $cc, $cd) = HMCCU_GetSpecialDatapoints ($clthash, '', 'STATE', '', ''); + my ($sc, $st, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clthash, '', 'STATE', '', ''); # Virtual device flag my $vg = 0; @@ -3310,7 +3391,7 @@ sub HMCCU_UpdateSingleDevice ($$$$) if ($cd ne '' && $dpt eq $cd && $chnnum eq $cc); HMCCU_BulkUpdate ($clthash, 'state', $fvalue, $cvalue) if ($dpt eq $st && ($sc eq '' || $sc eq $chnnum)); - + # Update peers HMCCU_UpdatePeers ($clthash, "$chnnum.$dpt", $cvalue, $peer) if (!$vg && $peer ne 'null'); } @@ -3379,6 +3460,10 @@ sub HMCCU_UpdateMultipleDevices ($$) "ccudevstate=active"); foreach my $d (@devlist) { my $ch = $defs{$d}; + if (!defined ($ch)) { + HMCCU_Log ($name, 2, "Can't find hash for device $d", 0); + next; + } my @addrlist = HMCCU_GetAffectedAddresses ($ch); next if (scalar (@addrlist) == 0); foreach my $addr (@addrlist) { @@ -4152,7 +4237,7 @@ sub HMCCU_GetDeviceInfo ($$$) } $response .= HMCCU_HMScriptExt ($hmccu_hash, "!GetDeviceInfo", - { devname => $devname, ccuget => $ccuget }); + { devname => $devname, ccuget => $ccuget }, undef, undef); HMCCU_Trace ($hash, 2, undef, "Device=$devname Devname=$devname
". "Script response = \n".$response."
". @@ -4280,10 +4365,10 @@ sub HMCCU_GetDevice ($$) my $devtype; my %objects = (); - my $response = HMCCU_HMScriptExt ($hash, "!GetDevice", { name => $name }); + my $response = HMCCU_HMScriptExt ($hash, "!GetDevice", { name => $name }, undef, undef); return (-1, -1) if ($response eq '' || $response =~ /^ERROR:.*/); - my @scrlines = split /\n/,$response; + my @scrlines = split /[\n\r]+/,$response; foreach my $hmdef (@scrlines) { my @hmdata = split /;/,$hmdef; next if (scalar (@hmdata) == 0); @@ -4348,9 +4433,9 @@ sub HMCCU_GetDeviceList ($) my %objects = (); # Read devices, channels, interfaces and groups from CCU - my $response = HMCCU_HMScriptExt ($hash, "!GetDeviceList", undef); + my $response = HMCCU_HMScriptExt ($hash, "!GetDeviceList", undef, undef, undef); return (-1, -1, -1, -1, -1) if ($response eq '' || $response =~ /^ERROR:.*/); - my $groups = HMCCU_HMScriptExt ($hash, "!GetGroupDevices", undef); + my $groups = HMCCU_HMScriptExt ($hash, "!GetGroupDevices", undef, undef, undef); # CCU is reachable $hash->{ccustate} = 'active'; @@ -4377,7 +4462,7 @@ sub HMCCU_GetDeviceList ($) # {address}{rxmode} := Transmit mode # {address}{chndir} := Channel direction: 1=sensor 2=actor 0=none - my @scrlines = split /\n/,$response; + my @scrlines = split /[\n\r]+/,$response; foreach my $hmdef (@scrlines) { my @hmdata = split /;/,$hmdef; next if (scalar (@hmdata) == 0); @@ -4602,14 +4687,14 @@ sub HMCCU_GetDatapointList ($$$) my $devlist = join (',', @devunique); my $response = HMCCU_HMScriptExt ($hash, "!GetDatapointList", - { list => $devlist }); + { list => $devlist }, undef, undef); if ($response eq '' || $response =~ /^ERROR:.*/) { Log3 $name, 2, "HMCCU: Cannot get datapoint list"; return 0; } my $c = 0; - foreach my $dpspec (split /\n/,$response) { + foreach my $dpspec (split /[\n\r]+/,$response) { my ($iface, $chna, $devt, $devc, $dptn, $dptt, $dpto) = split (";", $dpspec); $devt = "CUX-".$devt if ($iface eq 'CUxD'); $devt = "HVL-".$devt if ($iface eq 'HVL'); @@ -4656,28 +4741,43 @@ sub HMCCU_IsValidDevice ($$$) # Address if ($mode & $HMCCU_FL_STADDRESS) { + my $i; + my $a = 'null'; + # Address with interface if (HMCCU_IsDevAddr ($param, 1)) { - my ($i, $a) = split (/\./, $param); - return 0 if (! exists ($hash->{hmccu}{dev}{$a})); - return $hash->{hmccu}{dev}{$a}{valid}; + ($i, $a) = split (/\./, $param); + } + elsif (HMCCU_IsDevAddr ($param, 0)) { + $a = $param; + } + else { + HMCCU_Log ($hash, 3, "$param is not a valid address", 0); } - # Address without interface - if (HMCCU_IsDevAddr ($param, 0)) { - return 0 if (! exists ($hash->{hmccu}{dev}{$param})); - return $hash->{hmccu}{dev}{$param}{valid}; + if (exists ($hash->{hmccu}{dev}{$a})) { + return $hash->{hmccu}{dev}{$a}{valid}; + } + else { + HMCCU_Log ($hash, 3, "Address $param not found", 0); } # Special address for Non-Homematic devices if (($mode & $HMCCU_FL_EXADDRESS) && exists ($hash->{hmccu}{dev}{$param})) { return $hash->{hmccu}{dev}{$param}{valid} && $hash->{hmccu}{dev}{$param}{addtype} eq 'dev' ? 1 : 0; } + + HMCCU_Log ($hash, 3, "Invalid address $param", 0); } # Name - if (($mode & 2) && exists ($hash->{hmccu}{adr}{$param})) { - return $hash->{hmccu}{adr}{$param}{valid} && $hash->{hmccu}{adr}{$param}{addtype} eq 'dev' ? 1 : 0; + if (($mode & $HMCCU_FL_NAME)) { + if (exists ($hash->{hmccu}{adr}{$param})) { + return $hash->{hmccu}{adr}{$param}{valid} && $hash->{hmccu}{adr}{$param}{addtype} eq 'dev' ? 1 : 0; + } + else { + HMCCU_Log ($hash, 3, "Device $param not found", 0); + } } return 0; @@ -4715,7 +4815,7 @@ sub HMCCU_IsValidChannel ($$$) } # Name - if (($mode & 2) && exists ($hash->{hmccu}{adr}{$param})) { + if (($mode & $HMCCU_FL_NAME) && exists ($hash->{hmccu}{adr}{$param})) { return $hash->{hmccu}{adr}{$param}{valid} && $hash->{hmccu}{adr}{$param}{addtype} eq 'chn' ? 1 : 0; } @@ -5204,6 +5304,15 @@ sub HMCCU_SplitChnAddr ($) return ($dev, $chn); } +sub HMCCU_SplitDatapoint ($;$) +{ + my ($dpt, $defchn) = @_; + + my @t = split ('.', $dpt); + + return (scalar (@t) > 1) ? @t : ($defchn, $t[0]); +} + ###################################################################### # Get list of client devices matching the specified criteria. # If no criteria is specified all device names will be returned. @@ -5621,6 +5730,35 @@ sub HMCCU_GetAttrReadingFormat ($$) return AttrVal ($clname, 'ccureadingformat', $rfdef); } +###################################################################### +# Get number format considering default attribute ccudef-stripnumber, +# Default is null +###################################################################### + +sub HMCCU_GetAttrStripNumber ($) +{ + my ($hash) = @_; + my $fnc = "GetAttrStripNumber"; + + my $snDef = 'null'; + + if ($hash->{TYPE} ne 'HMCCU') { + my $ioHash = HMCCU_GetHash ($hash); + if (defined ($ioHash)) { + $snDef = AttrVal ($ioHash->{NAME}, 'ccudef-stripnumber', 'null'); + } + } + else { + $snDef = AttrVal ($hash->{NAME}, 'ccudef-stripnumber', 'null'); + } + + my $stripnumber = AttrVal ($hash->{NAME}, 'stripnumber', $snDef); + + HMCCU_Trace ($hash, 2, $fnc, "stripnumber = $stripnumber"); + + return $stripnumber; +} + ###################################################################### # Get attributes substitute and substexcl considering default # attribute ccudef-substitute defined in I/O device. @@ -6102,9 +6240,9 @@ sub HMCCU_HMCommandCB ($$$) # Return script output or error message starting with "ERROR:". ###################################################################### -sub HMCCU_HMScriptExt ($$$) +sub HMCCU_HMScriptExt ($$$$$) { - my ($hash, $hmscript, $params) = @_; + my ($hash, $hmscript, $params, $cbFunc, $cbParam) = @_; my $name = $hash->{NAME}; my $code = $hmscript; my $scrname = ''; @@ -6113,6 +6251,9 @@ sub HMCCU_HMScriptExt ($$$) my $host = $hash->{host}; my $ccureqtimeout = AttrVal ($hash->{NAME}, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); +# if (!defined ($nonBlocking)) { +# $nonBlocking = HMCCU_IsFlag ($name, 'nonBlocking') ? 1 : 0; +# } if ($hmscript =~ /^!(.*)$/) { # Internal script @@ -6159,27 +6300,36 @@ sub HMCCU_HMScriptExt ($$$) $HMCCU_SCRIPTS->{$scrname}{syntax}; } } - + # Execute script on CCU 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>//; - $output =~ s/\r//g; - return $output; + if (defined ($cbFunc)) { + my $param = { url => $url, timeout => $ccureqtimeout, data => $code, method => "POST", + callback => $cbFunc, ioHash => $hash }; + if (defined ($cbParam)) { + foreach my $p (keys %{$cbParam}) { + $param->{$p} = $cbParam->{$p}; + } + } + $param->{sslargs} = { SSL_verify_mode => 0 }; + HttpUtils_NonblockingGet ($param); + return '' } else { -# my $msg = $response->status_line(); - HMCCU_Log ($hash, 2, "HMScript failed. $err", undef); - return "ERROR: HMScript failed. $err"; + my $param = { url => $url, timeout => $ccureqtimeout, data => $code, method => "POST" }; + $param->{sslargs} = { SSL_verify_mode => 0 }; + my ($err, $response) = HttpUtils_BlockingGet ($param); + + if ($err eq '') { + my $output = $response; + $output =~ s/.*<\/xml>//; + $output =~ s/\r//g; + return $output; + } + else { + HMCCU_Log ($hash, 2, "HMScript failed. $err", undef); + return "ERROR: HMScript failed. $err"; + } } } @@ -6272,67 +6422,111 @@ sub HMCCU_GetDatapoint ($@) } } +###################################################################### +# Set multiple values of parameter set. +# Parameter params is a hash reference. Keys are datapoint names. +# Parameter address must be a channel address. +###################################################################### + +sub HMCCU_SetMultipleParameters ($$$) +{ + my ($clHash, $address, $params) = @_; + + my ($add, $chn) = HMCCU_SplitChnAddr ($address); + return -1 if (!defined ($chn)); + + foreach my $p (sort keys %$params) { + return -8 if (!HMCCU_IsValidDatapoint ($clHash, $clHash->{ccutype}, $chn, $p, 2)); + $params->{$p} = HMCCU_ScaleValue ($clHash, $chn, $p, $params->{$p}, 1); + } + + return HMCCU_RPCRequest ($clHash, "putParamset", $address, 'VALUES', $params); +} + ###################################################################### # Set multiple datapoints on CCU in a single request. # Parameter params is a hash reference. Keys are full qualified CCU # datapoint specifications in format: -# no.interface.address:channelno.datapoint +# no.interface.{address|fhemdev}:channelno.datapoint # Parameter no defines the command order. ###################################################################### sub HMCCU_SetMultipleDatapoints ($$) { - my ($cl_hash, $params) = @_; + my ($clHash, $params) = @_; my $fnc = "SetMultipleDatapoints"; - my $type = $cl_hash->{TYPE}; + my $mdFlag = $clHash->{TYPE} eq 'HMCCU' ? 1 : 0; + my $ioHash; - my $io_hash = HMCCU_GetHash ($cl_hash); - return -3 if (!defined ($io_hash)); - return -4 if (exists ($cl_hash->{ccudevstate}) && $cl_hash->{ccudevstate} eq 'deleted'); - my $io_name = $io_hash->{NAME}; - my $cl_name = $cl_hash->{NAME}; + if ($mdFlag) { + $ioHash = $clHash; + } + else { + $ioHash = HMCCU_GetHash ($clHash); + return -3 if (!defined ($ioHash)); + } - my $ccureqtimeout = AttrVal ($io_name, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); - my $ccuflags = HMCCU_GetFlags ($io_name); - my $readingformat = HMCCU_GetAttrReadingFormat ($cl_hash, $io_hash); - my $ccuverify = AttrVal ($cl_name, 'ccuverify', 0); - my $ccuchange = AttrVal ($cl_name, 'ccuSetOnChange', 'null'); - my $ccutype = $cl_hash->{ccutype}; + my $ioName = $ioHash->{NAME}; + my $clName = $clHash->{NAME}; + my $ccuFlags = HMCCU_GetFlags ($ioName); # Build Homematic script - my @addrlist = $cl_hash->{ccuif} eq 'fhem' ? split (',', $cl_hash->{ccugroup}) : ($cl_hash->{ccuaddr}); my $cmd = ''; - foreach my $a (@addrlist) { - foreach my $p (sort keys %$params) { - my $v = $params->{$p}; + foreach my $p (sort keys %$params) { + my $v = $params->{$p}; - HMCCU_Trace ($cl_hash, 2, $fnc, "dpt=$p, value=$v"); + # Check address. dev is either a device address or a FHEM device name + my ($no, $int, $addchn, $dpt) = split (/\./, $p); + return -1 if (!defined ($dpt)); + my ($dev, $chn) = split (':', $addchn); + return -1 if (!defined ($chn)); + my $add = $dev; + + # Get hash of FHEM device + if ($mdFlag) { + return -1 if (!exists ($defs{$dev})); + $clHash = $defs{$dev}; + ($add, undef) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); + } - # Check address - my ($no, $int, $addchn, $dpt) = split (/\./, $p); - return -1 if (!defined ($dpt)); - my ($add, $chn) = split (/:/, $addchn); - return -1 if (!defined ($chn)); - return -8 if (!HMCCU_IsValidDatapoint ($cl_hash, $ccutype, $chn, $dpt, 2)); + # Device has been deleted or is disabled + return -4 if (exists ($clHash->{ccudevstate}) && $clHash->{ccudevstate} eq 'deleted'); + return -21 if (IsDisabled ($clHash->{NAME})); + + HMCCU_Trace ($clHash, 2, $fnc, "dpt=$p, value=$v"); - # Override device interface and address if device is virtual - if ($cl_hash->{ccuif} eq 'fhem') { - $add = $a; - $int = HMCCU_GetDeviceInterface ($io_hash, $add, ''); + # Check client device type and datapoint + my $clType = $clHash->{TYPE}; + my $ccuType = $clHash->{ccutype}; + return -1 if ($clType ne 'HMCCUCHN' && $clType ne 'HMCCUDEV'); + return -8 if (!HMCCU_IsValidDatapoint ($clHash, $ccuType, $chn, $dpt, 2)); + + my $ccuVerify = AttrVal ($clName, 'ccuverify', 0); + my $ccuChange = AttrVal ($clName, 'ccuSetOnChange', 'null'); + + # Build device address list considering group devices + my @addrList = $clHash->{ccuif} eq 'fhem' ? split (',', $clHash->{ccugroup}) : ($add); + return -1 if (scalar (@addrList) < 1); + + foreach my $a (@addrList) { + # Override address and interface of group device with address of group members + if ($clHash->{ccuif} eq 'fhem') { + ($add, undef) = HMCCU_SplitChnAddr ($a); + $int = HMCCU_GetDeviceInterface ($ioHash, $a, ''); return -20 if ($int eq ''); } - if ($ccutype eq 'HM-Dis-EP-WM55' && $dpt eq 'SUBMIT') { + if ($ccuType eq 'HM-Dis-EP-WM55' && $dpt eq 'SUBMIT') { $v = HMCCU_EncodeEPDisplay ($v); } else { - $v = HMCCU_ScaleValue ($cl_hash, $chn, $dpt, $v, 1); + $v = HMCCU_ScaleValue ($clHash, $chn, $dpt, $v, 1); } - - my $dpttype = HMCCU_GetDatapointAttr ($io_hash, $ccutype, $chn, $dpt, 'type'); - $v = "'".$v."'" if (defined ($dpttype) && $dpttype == $HMCCU_TYPE_STRING); + + my $dptType = HMCCU_GetDatapointAttr ($ioHash, $ccuType, $chn, $dpt, 'type'); + $v = "'".$v."'" if (defined ($dptType) && $dptType == $HMCCU_TYPE_STRING); my $c = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$v.");\n"; - if ($dpt =~ /$ccuchange/) { + if ($dpt =~ /$ccuChange/) { $cmd .= 'if((datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).Value() != '.$v.") {\n$c}\n"; } else { @@ -6342,13 +6536,13 @@ sub HMCCU_SetMultipleDatapoints ($$) { } # Execute command (non blocking) - if ($ccuflags =~ /nonBlocking/) { - HMCCU_HMCommandNB ($cl_hash, $cmd, undef); + if ($ccuFlags =~ /nonBlocking/) { + HMCCU_HMCommandNB ($clHash, $cmd, undef); return 0; } # Execute command (blocking) - my $response = HMCCU_HMCommand ($cl_hash, $cmd, 1); + my $response = HMCCU_HMCommand ($clHash, $cmd, 1); return -2 if (!defined ($response)); # Datapoint verification ??? @@ -6365,101 +6559,101 @@ sub HMCCU_SetMultipleDatapoints ($$) { # hmccu:hmccuchn_name.datapoint ###################################################################### -sub HMCCU_SetDatapoint ($$$) -{ - my ($hash, $param, $value) = @_; - my $fnc = "SetDatapoint"; - my $type = $hash->{TYPE}; - - my $hmccu_hash = HMCCU_GetHash ($hash); - return -3 if (!defined ($hmccu_hash)); - return -4 if (exists ($hash->{ccudevstate}) && $hash->{ccudevstate} eq 'deleted'); - my $name = $hmccu_hash->{NAME}; - my $cdname = $hash->{NAME}; - - my $ccureqtimeout = AttrVal ($name, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); - my $ccuflags = HMCCU_GetFlags ($name); - my $readingformat = HMCCU_GetAttrReadingFormat ($hash, $hmccu_hash); - my $ccuverify = AttrVal ($cdname, 'ccuverify', 0); - - HMCCU_Trace ($hash, 2, $fnc, "param=$param, value=$value"); - - if ($param =~ /^hmccu:.+$/) { - my @t = split (/\./, $param); - return -1 if (scalar (@t) < 2 || scalar (@t) > 3); - my $fhdpt = pop @t; - my ($fhadd, $fhchn) = HMCCU_GetAddress ($hmccu_hash, $t[0], '', ''); - $fhchn = $t[1] if (scalar (@t) == 2); - return -1 if ($fhadd eq '' || $fhchn eq ''); - $param = "$fhadd:$fhchn.$fhdpt"; - } - elsif ($param =~ /^ccu:(.+)$/) { - $param = $1; - } - - my $cmd = ''; - 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); - - if ($hash->{ccutype} eq 'HM-Dis-EP-WM55' && $dpt eq 'SUBMIT') { - $value = HMCCU_EncodeEPDisplay ($value); - } - else { - $value = HMCCU_ScaleValue ($hash, $chn, $dpt, $value, 1); - } - - my $dpttype = HMCCU_GetDatapointAttr ($hmccu_hash, $hash->{ccutype}, $chn, $dpt, 'type'); - if (defined ($dpttype) && $dpttype == $HMCCU_TYPE_STRING) { - $value = "'".$value."'"; - } - - if ($flags == $HMCCU_FLAGS_IACD) { -# $cmd = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$value.')'; - $nam = HMCCU_GetChannelName ($hmccu_hash, $add.":".$chn, ''); - } - elsif ($flags == $HMCCU_FLAGS_NCD) { -# $cmd = '(dom.GetObject(ID_CHANNELS)).Get("'.$nam.'").DPByHssDP("'.$dpt.'").State('.$value.')'; - ($add, $chn) = HMCCU_GetAddress ($hmccu_hash, $nam, '', ''); - } - - if ($type eq 'HMCCUDEV' && $hash->{ccuif} eq 'fhem' && $hash->{ccutype} ne 'n/a' && exists ($hash->{ccugroup})) { - foreach my $gaddr (split (',', $hash->{ccugroup})) { - $cmd .= '(datapoints.Get("'.$int.'.'.$gaddr.':'.$chn.'.'.$dpt.'")).State('.$value.");\n"; - } - } - else { - $cmd = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$value.')'; - } - - my $addr = $add.":".$chn; - - if ($ccuflags =~ /nonBlocking/) { - HMCCU_HMCommandNB ($hash, $cmd, undef); - return 0; - } - - # Execute command (blocking) - my $response = HMCCU_HMCommand ($hash, $cmd, 1); - HMCCU_Trace ($hash, 2, $fnc, - "Addr=$addr Name=$nam
". - "Script response = \n".(defined ($response) ? $response: 'undef')."
". - "Script = \n".$cmd); - return -2 if (!defined ($response)); - - # Verify setting of datapoint value or update reading with new datapoint value - if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $addr, $dpt, 1)) { - if ($ccuverify == 1) { - my ($rc, $result) = HMCCU_GetDatapoint ($hash, $param, 0); - return $rc; - } - elsif ($ccuverify == 2) { - HMCCU_UpdateSingleDatapoint ($hash, $chn, $dpt, $value); - } - } - - return 0; -} +# sub HMCCU_SetDatapoint ($$$) +# { +# my ($hash, $param, $value) = @_; +# my $fnc = "SetDatapoint"; +# my $type = $hash->{TYPE}; +# +# my $hmccu_hash = HMCCU_GetHash ($hash); +# return -3 if (!defined ($hmccu_hash)); +# return -4 if (exists ($hash->{ccudevstate}) && $hash->{ccudevstate} eq 'deleted'); +# my $name = $hmccu_hash->{NAME}; +# my $cdname = $hash->{NAME}; +# +# my $ccureqtimeout = AttrVal ($name, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); +# my $ccuflags = HMCCU_GetFlags ($name); +# my $readingformat = HMCCU_GetAttrReadingFormat ($hash, $hmccu_hash); +# my $ccuverify = AttrVal ($cdname, 'ccuverify', 0); +# +# HMCCU_Trace ($hash, 2, $fnc, "param=$param, value=$value"); +# +# if ($param =~ /^hmccu:.+$/) { +# my @t = split (/\./, $param); +# return -1 if (scalar (@t) < 2 || scalar (@t) > 3); +# my $fhdpt = pop @t; +# my ($fhadd, $fhchn) = HMCCU_GetAddress ($hmccu_hash, $t[0], '', ''); +# $fhchn = $t[1] if (scalar (@t) == 2); +# return -1 if ($fhadd eq '' || $fhchn eq ''); +# $param = "$fhadd:$fhchn.$fhdpt"; +# } +# elsif ($param =~ /^ccu:(.+)$/) { +# $param = $1; +# } +# +# my $cmd = ''; +# 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); +# +# if ($hash->{ccutype} eq 'HM-Dis-EP-WM55' && $dpt eq 'SUBMIT') { +# $value = HMCCU_EncodeEPDisplay ($value); +# } +# else { +# $value = HMCCU_ScaleValue ($hash, $chn, $dpt, $value, 1); +# } +# +# my $dpttype = HMCCU_GetDatapointAttr ($hmccu_hash, $hash->{ccutype}, $chn, $dpt, 'type'); +# if (defined ($dpttype) && $dpttype == $HMCCU_TYPE_STRING) { +# $value = "'".$value."'"; +# } +# +# if ($flags == $HMCCU_FLAGS_IACD) { +# # $cmd = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$value.')'; +# $nam = HMCCU_GetChannelName ($hmccu_hash, $add.":".$chn, ''); +# } +# elsif ($flags == $HMCCU_FLAGS_NCD) { +# # $cmd = '(dom.GetObject(ID_CHANNELS)).Get("'.$nam.'").DPByHssDP("'.$dpt.'").State('.$value.')'; +# ($add, $chn) = HMCCU_GetAddress ($hmccu_hash, $nam, '', ''); +# } +# +# if ($type eq 'HMCCUDEV' && $hash->{ccuif} eq 'fhem' && $hash->{ccutype} ne 'n/a' && exists ($hash->{ccugroup})) { +# foreach my $gaddr (split (',', $hash->{ccugroup})) { +# $cmd .= '(datapoints.Get("'.$int.'.'.$gaddr.':'.$chn.'.'.$dpt.'")).State('.$value.");\n"; +# } +# } +# else { +# $cmd = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$value.')'; +# } +# +# my $addr = $add.":".$chn; +# +# if ($ccuflags =~ /nonBlocking/) { +# HMCCU_HMCommandNB ($hash, $cmd, undef); +# return 0; +# } +# +# # Execute command (blocking) +# my $response = HMCCU_HMCommand ($hash, $cmd, 1); +# HMCCU_Trace ($hash, 2, $fnc, +# "Addr=$addr Name=$nam
". +# "Script response = \n".(defined ($response) ? $response: 'undef')."
". +# "Script = \n".$cmd); +# return -2 if (!defined ($response)); +# +# # Verify setting of datapoint value or update reading with new datapoint value +# if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $addr, $dpt, 1)) { +# if ($ccuverify == 1) { +# my ($rc, $result) = HMCCU_GetDatapoint ($hash, $param, 0); +# return $rc; +# } +# elsif ($ccuverify == 2) { +# HMCCU_UpdateSingleDatapoint ($hash, $chn, $dpt, $value); +# } +# } +# +# return 0; +# } ###################################################################### # Scale, spread and/or shift datapoint value. @@ -6510,7 +6704,6 @@ sub HMCCU_ScaleValue ($$$$$) # Do not scale if value out of range or interval wrong return $value if ($a[1] > $a[2] || $a[3] > $a[4]); return $value if ($mode == 0 && ($value < $a[1] || $value > $a[2])); -# return $value if ($mode == 1 && ($value >= $a[1] && $value <= $a[2])); return $value if ($mode == 1 && ($value < $a[3] || $value > $a[4])); # Reverse value @@ -6545,12 +6738,12 @@ sub HMCCU_GetVariables ($$) my $ccureadings = AttrVal ($name, 'ccureadings', HMCCU_IsFlag ($name, "noReadings") ? 0 : 1); - my $response = HMCCU_HMScriptExt ($hash, "!GetVariables", undef); + my $response = HMCCU_HMScriptExt ($hash, "!GetVariables", undef, undef, undef); return (-2, $response) if ($response eq '' || $response =~ /^ERROR:.*/); readingsBeginUpdate ($hash) if ($ccureadings); - foreach my $vardef (split /\n/, $response) { + foreach my $vardef (split /[\n\r]+/, $response) { my @vardata = split /=/, $vardef; next if (@vardata != 3); next if ($vardata[0] !~ /$pattern/); @@ -6619,7 +6812,7 @@ sub HMCCU_SetVariable ($$$$$) $params->{valtrue} = "ist wahr" if ($vartype eq 'bool' && !exists ($params->{valtrue})); $params->{valfalse} = "ist falsch" if ($vartype eq 'bool' && !exists ($params->{valfalse})); - my $rc = HMCCU_HMScriptExt ($hash, $varfnc{$vartype}, $params); + my $rc = HMCCU_HMScriptExt ($hash, $varfnc{$vartype}, $params, undef, undef); return HMCCU_Log ($hash, 1, $rc, -2) if ($rc =~ /^ERROR:.*/); } @@ -6630,6 +6823,7 @@ sub HMCCU_SetVariable ($$$$$) # Update all datapoints / readings of device or channel considering # attribute ccureadingfilter. # Parameter $ccuget can be 'State', 'Value' or 'Attr'. +# Return 1 on success, <= 0 on error ###################################################################### sub HMCCU_GetUpdate ($$$) @@ -6680,32 +6874,42 @@ sub HMCCU_GetUpdate ($$$) return -1; } - my $response = HMCCU_HMScriptExt ($hmccu_hash, $script, - { list => $list, ccuget => $ccuget }); - HMCCU_Trace ($cl_hash, 2, $fnc, "Addr=$addr Name=$nam Script=$script
". - "Script response = \n".$response); - return -2 if ($response eq '' || $response =~ /^ERROR:.*/); - - my @dpdef = split /\n/, $response; - my $count = pop (@dpdef); - return -10 if (!defined ($count) || $count == 0); - - my %events = (); - foreach my $dp (@dpdef) { - my ($chnname, $dpspec, $value) = split /=/, $dp; - next if (!defined ($value)); - my ($iface, $chnadd, $dpt) = split /\./, $dpspec; - next if (!defined ($dpt)); - my ($add, $chn) = ('', ''); - if ($iface eq 'sysvar' && $chnadd eq 'link') { - ($add, $chn) = HMCCU_GetAddress ($hmccu_hash, $chnname, '', ''); - } - else { - ($add, $chn) = HMCCU_SplitChnAddr ($chnadd); - } - next if ($chn eq ''); - $events{$add}{$chn}{$dpt} = $value; + if (HMCCU_IsFlag ($hmccu_hash->{NAME}, 'nonBlocking')) { + HMCCU_HMScriptExt ($hmccu_hash, $script, { list => $list, ccuget => $ccuget }, + \&HMCCU_UpdateCB, undef); + return 1; } + else { + my $response = HMCCU_HMScriptExt ($hmccu_hash, $script, + { list => $list, ccuget => $ccuget }, undef, undef); + HMCCU_Trace ($cl_hash, 2, $fnc, "Addr=$addr Name=$nam Script=$script
". + "Script response = \n".$response); + return -2 if ($response eq '' || $response =~ /^ERROR:.*/); + + HMCCU_UpdateCB ({ ioHash => $hmccu_hash }, undef, $response); + return 1; + } + +# my @dpdef = split /\n/, $response; +# my $count = pop (@dpdef); +# return -10 if (!defined ($count) || $count == 0); +# +# my %events = (); +# foreach my $dp (@dpdef) { +# my ($chnname, $dpspec, $value) = split /=/, $dp; +# next if (!defined ($value)); +# my ($iface, $chnadd, $dpt) = split /\./, $dpspec; +# next if (!defined ($dpt)); +# my ($add, $chn) = ('', ''); +# if ($iface eq 'sysvar' && $chnadd eq 'link') { +# ($add, $chn) = HMCCU_GetAddress ($hmccu_hash, $chnname, '', ''); +# } +# else { +# ($add, $chn) = HMCCU_SplitChnAddr ($chnadd); +# } +# next if ($chn eq ''); +# $events{$add}{$chn}{$dpt} = $value; +# } # if ($cl_hash->{ccuif} eq 'fhem') { # # Calculate datapoints of virtual group device @@ -6722,11 +6926,61 @@ sub HMCCU_GetUpdate ($$$) # } # } - HMCCU_UpdateMultipleDevices ($hmccu_hash, \%events); +# HMCCU_UpdateMultipleDevices ($hmccu_hash, \%events); return 1; } +###################################################################### +# Generic reading update callback function for non blocking HTTP +# requests. +# Format of $data: Newline separated list of datapoint values. +# ChannelName=Interface.ChannelAddress.Datapoint=Value +# Optionally last line can contain the number of datapoint lines. +###################################################################### + +sub HMCCU_UpdateCB ($$$) +{ + my ($param, $err, $data) = @_; + + if (!exists ($param->{ioHash})) { + Log3 1, undef, "HMCCU: Missing parameter ioHash in update callback"; + return; + } + + my $hash = $param->{ioHash}; + my $logcount = 0; + $logcount = 1 if (exists ($param->{logCount}) && $param->{logCount} == 1); + + my $count = 0; + my @dpdef = split /[\n\r]+/, $data; + my $lines = scalar (@dpdef); + $count = ($lines > 0 && $dpdef[$lines-1] =~ /^[0-9]+$/) ? pop (@dpdef) : $lines; + return if ($count == 0); + + my %events = (); + foreach my $dp (@dpdef) { + my ($chnname, $dpspec, $value) = split /=/, $dp; + next if (!defined ($value)); + my ($iface, $chnadd, $dpt) = split /\./, $dpspec; + next if (!defined ($dpt)); + my ($add, $chn) = ('', ''); + if ($iface eq 'sysvar' && $chnadd eq 'link') { + ($add, $chn) = HMCCU_GetAddress ($hash, $chnname, '', ''); + } + else { + ($add, $chn) = HMCCU_SplitChnAddr ($chnadd); + } + next if ($chn eq ''); + $events{$add}{$chn}{$dpt} = $value; + } + + my $c_ok = HMCCU_UpdateMultipleDevices ($hash, \%events); + my $c_err = 0; + $c_err = max($param->{devCount}-$c_ok, 0) if (exists ($param->{devCount})); + HMCCU_Log ($hash, 2, "Update success=$c_ok failed=$c_err", 0) if ($logcount); +} + ###################################################################### # Get multiple datapoints of channels and update readings of client # devices. @@ -6770,11 +7024,11 @@ sub HMCCU_GetChannel ($$) return (0, $result) if ($chnlist eq ''); my $response = HMCCU_HMScriptExt ($hash, "!GetChannel", - { list => $chnlist, ccuget => $ccuget }); + { list => $chnlist, ccuget => $ccuget }, undef, undef); return (-2, $result) if ($response eq '' || $response =~ /^ERROR:.*/); # Output format is Channelname=Interface.Channeladdress.Datapoint=Value - foreach my $dpdef (split /\n/, $response) { + foreach my $dpdef (split /[\n\r]+/, $response) { my ($chnname, $dptaddr, $value) = split /=/, $dpdef; next if (!defined ($value)); my ($iface, $chnaddr, $dpt) = split /\./, $dptaddr; @@ -6796,6 +7050,107 @@ sub HMCCU_GetChannel ($$) return ($count, $result); } +sub HMCCU_RPCRequest ($$$$$;$) +{ + my ($clHash, $method, $address, $key, $parref, $filter) = @_; + my $name = $clHash->{NAME}; + my $type = $clHash->{TYPE}; + my $fnc = "RPCRequest"; + + my $reqMethod = $method eq 'listParamset' ? 'getParamset' : $method; + $filter = '.*' if (!defined ($filter)); + my $addr = ''; + my $result = ''; + + my $ioHash = HMCCU_GetHash ($clHash); + return (-3, $result) if (!defined ($ioHash)); + return (-4, $result) if ($type ne 'HMCCU' && $clHash->{ccudevstate} eq 'deleted'); + + # Get flags and attributes + my $ioFlags = HMCCU_GetFlags ($ioHash->{NAME}); + my $clFlags = HMCCU_GetFlags ($name); + my $ccureadings = AttrVal ($name, 'ccureadings', $clFlags =~ /noReadings/ ? 0 : 1); + my $readingformat = HMCCU_GetAttrReadingFormat ($clHash, $ioHash); + my $substitute = HMCCU_GetAttrSubstitute ($clHash, $ioHash); + + # Parse address, complete address information + my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($ioHash, $address, + $HMCCU_FLAG_FULLADDR); + return (-1, $result) if (!($flags & $HMCCU_FLAG_ADDRESS)); + $addr = $add; + $addr .= ':'.$chn if ($flags & $HMCCU_FLAG_CHANNEL); + + # Get RPC type and port for interface of device address + my ($rpcType, $rpcPort) = HMCCU_GetRPCServerInfo ($ioHash, $int, 'type,port'); + return (-9, '') if (!defined ($rpcType) || !defined ($rpcPort)); + + # Search RPC device, do not create one + my ($rpcDevice, $save) = HMCCU_GetRPCDevice ($ioHash, 0, $int); + return (-17, $result) if ($rpcDevice eq ''); + my $rpcHash = $defs{$rpcDevice}; + + # Build parameter array + my @parArray = ($addr, $key); + if (defined ($parref)) { + foreach my $k (keys %{$parref}) { push @parArray, "$k=$parref->{$k}"; }; + } + + # Submit RPC request + my $reqResult = HMCCURPCPROC_SendRequest ($rpcHash, $reqMethod, @parArray); + return (-5, "Function not available") if (!defined ($reqResult)); + + HMCCU_Trace ($clHash, 2, $fnc, + "Dump of RPC request $method $addr. Result type=".ref($reqResult)."
". + HMCCU_RefToString ($reqResult)); + + my $parCount = 0; + + if (ref ($reqResult) eq 'HASH') { + if (exists ($reqResult->{faultString})) { + HMCCU_Log ($rpcHash, 1, $reqResult->{faultString}, 0); + return (-2, $reqResult->{faultString}); + } + else { + $parCount = keys %{$reqResult}; + } + } +# else { +# return (-2, defined ($RPC::XML::ERROR) ? $RPC::XML::ERROR : 'RPC request failed'); +# } + + if ($method eq 'listParamset') { + $result = join ("\n", map { $_ =~ /$filter/ ? $_.'='.$reqResult->{$_} : () } keys %$reqResult); + } + elsif ($method eq 'getParamsetDescription') { + my @operFlags = ('', 'R', 'W', 'RW', 'E', 'RE', 'WE', 'RWE'); + $result = join ("\n", + map { $_.': '.$reqResult->{$_}->{TYPE}." [".$operFlags[$reqResult->{$_}->{OPERATIONS}]."]" } keys %$reqResult); + } + elsif ($method eq 'getParamset') { + readingsBeginUpdate ($clHash) if ($ccureadings); + + foreach my $key (sort keys %$reqResult) { + next if ($key !~ /$filter/); + my $value = $reqResult->{$key}; + $result .= "$key=$value\n"; + if ($ccureadings) { + $value = HMCCU_FormatReadingValue ($clHash, $value, $key); + $value = HMCCU_Substitute ($value, $substitute, 0, $chn, $key); + my @readings = HMCCU_GetReadingName ($clHash, $int, $add, $chn, $key, $nam, $readingformat); + foreach my $rn (@readings) { + next if ($rn eq ''); + $rn = "R-".$rn; + readingsBulkUpdate ($clHash, $rn, $value); + } + } + } + + readingsEndUpdate ($clHash, 1) if ($ccureadings); + } + + return (0, $result); +} + ###################################################################### # Get RPC paramSet or paramSetDescription ###################################################################### @@ -6844,7 +7199,7 @@ sub HMCCU_RPCGetConfig ($$$$) } } else { - $res = HMCCURPCPROC_SendRequest ($rpchash, $method, $addr, "MASTER"); + $res = HMCCURPCPROC_SendRequest ($rpchash, $method, "$addr:STRING", "MASTER:STRING"); } return (-5, "Function not available") if (!defined ($res)); @@ -8165,6 +8520,10 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
  • set <name> cleardefaults
    Clear default attributes imported from file.

  • +
  • set <name> datapoint <FHEM-Device>[.<channel-number>].<datapoint>=<value>
    + Set datapoint values on multiple devices. If FHEM-Device is of type HMCCUDEV + a channel-number must be specified. +

  • set <name> defaults
    Set default attributes for I/O device.

  • @@ -8382,6 +8741,11 @@ sub HMCCU_CCURPC_ListDevicesCB ($$) Set global rules for reading name substitution. These rules are added to the rules specified by client device attribute 'ccureadingname'.
    +
  • ccudef-stripnumber [<datapoint-expr>!]{0|1|2|-n|%fmt}[;...]
    + Set global formatting rules for numeric datapoint or config parameter values. + Default value is 2 (strip trailing zeroes).
    + For details see description of attribute stripnumber in HMCCUCHN. +
  • ccudef-substitute <subst-rule>[;...]
    Set global substitution rules for datapoint value. These rules are added to the rules specified by client device attribute 'substitute'. @@ -8401,6 +8765,8 @@ sub HMCCU_CCURPC_ListDevicesCB ($$) logEvents - Write events from CCU into FHEM logfile
    logPong - Write log message when receiving pong event if verbose level is at least 3.
    noEvents - Ignore events / device updates sent by CCU. No readings will be updated!
    + noInitialUpdate - Do not update datapoints of devices after RPC server start. Overrides + settings in RPC devices. 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 diff --git a/fhem/FHEM/88_HMCCUCHN.pm b/fhem/FHEM/88_HMCCUCHN.pm index a7ee42942..84946e794 100644 --- a/fhem/FHEM/88_HMCCUCHN.pm +++ b/fhem/FHEM/88_HMCCUCHN.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 4.3.006 +# Version 4.3.007 # # (c) 2019 zap (zap01 t-online de) # @@ -205,7 +205,10 @@ sub HMCCUCHN_Set ($@) my $name = shift @$a; my $opt = shift @$a; - my $rocmds = "clear config defaults:noArg"; + return "No set command specified" if (!defined ($opt)); + + my $rocmds = "clear defaults:noArg"; + my $rwcmds = "clear config control datapoint defaults:noArg devstate rpcParameter"; # Get I/O device, check device state return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' || @@ -415,7 +418,20 @@ sub HMCCUCHN_Set ($@) if (defined ($par) && $par eq 'device') { ($ccuobj, undef) = HMCCU_SplitChnAddr ($ccuaddr); } - my $rc = HMCCU_RPCSetConfig ($hash, $ccuobj, $h); + my ($rc, $res) = HMCCU_RPCRequest ($hash, "putParamset", $ccuobj, "MASTER", $h); +# my $rc = HMCCU_RPCSetConfig ($hash, $ccuobj, $h); + return HMCCU_SetError ($hash, $rc) if ($rc < 0); + return HMCCU_SetState ($hash, "OK"); + } + elsif ($opt eq 'rpcParameter') { + return HMCCU_SetError ($hash, "Usage: set $name rpcParameter [MASTER|VALUES] {parameter}={value} [...]") + if ((scalar keys %{$h}) < 1); + my $key = shift @$a; + $key = 'VALUES' if (!defined ($key)); + return HMCCU_SetError ($hash, "Key must be MASTER or VALUES") + if ($key ne 'MASTER' && $key ne 'VALUES'); + + my ($rc, $res) = HMCCU_RPCRequest ($hash, "putParamset", $ccuaddr, $key, $h); return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetState ($hash, "OK"); } @@ -425,14 +441,13 @@ sub HMCCUCHN_Set ($@) return HMCCU_SetState ($hash, "OK"); } else { - return "HMCCUCHN: Unknown argument $opt, choose one of ".$rocmds + return "HMCCUCHN: Unknown argument $opt, choose one of $rocmds" if ($hash->{statevals} eq 'readonly'); - - my $retmsg = "HMCCUCHN: Unknown argument $opt, choose one of clear config control datapoint defaults:noArg devstate"; + my $retmsg = "HMCCUCHN: Unknown argument $opt, choose one of $rwcmds"; if ($hash->{statevals} ne '') { my @cmdlist = split /\|/,$hash->{statevals}; shift @cmdlist; - $retmsg .= ':'.join(',',@cmdlist) if (@cmdlist > 0); + $retmsg .= ':'.join(',',@cmdlist) if (scalar(@cmdlist) > 0); foreach my $sv (@cmdlist) { $retmsg .= ' '.$sv.':noArg'; } @@ -457,6 +472,8 @@ sub HMCCUCHN_Get ($@) my $name = shift @$a; my $opt = shift @$a; + return "No get command specified" if (!defined ($opt)); + return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' || !defined ($hash->{IODev})); @@ -533,7 +550,8 @@ sub HMCCUCHN_Get ($@) } $par = '.*' if (!defined ($par)); - my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par); + my ($rc, $res) = HMCCU_RPCRequest ($hash, "getParamset", $ccuobj, "MASTER", undef, $par); +# my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0); return $ccureadings ? undef : $res; } @@ -548,7 +566,8 @@ sub HMCCUCHN_Get ($@) } $par = '.*' if (!defined ($par)); - my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par); + my ($rc, $res) = HMCCU_RPCRequest ($hash, "listParamset", $ccuobj, "MASTER", undef, $par); +# my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0); return $res; } @@ -559,7 +578,8 @@ sub HMCCUCHN_Get ($@) ($ccuobj, undef) = HMCCU_SplitChnAddr ($ccuaddr); } - my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef); + my ($rc, $res) = HMCCU_RPCRequest ($hash, "getParamsetDescription", $ccuobj, "MASTER", undef); +# my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0); return $res; } @@ -827,7 +847,7 @@ sub HMCCUCHN_Get ($@) channel-name.datapoint. If set to 'datapoint' format is channel-number.datapoint. With suffix 'lc' reading names are converted to lowercase.

  • -
  • ccureadingname <old-readingname-expr>:[+]<new-readingname>[;...]
    +
  • ccureadingname <old-readingname-expr>:[+]<new-readingname>[,...];[;...]
    Set alternative or additional reading names or group readings. Only part of old reading name matching old-readingname-exptr is substituted by new-readingname. If new-readingname is preceded by '+' an additional reading is created. If diff --git a/fhem/FHEM/88_HMCCUDEV.pm b/fhem/FHEM/88_HMCCUDEV.pm index 7352d208c..b2798f93e 100644 --- a/fhem/FHEM/88_HMCCUDEV.pm +++ b/fhem/FHEM/88_HMCCUDEV.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 4.3.008 +# Version 4.3.009 # # (c) 2019 zap (zap01 t-online de) # @@ -340,6 +340,8 @@ sub HMCCUDEV_Set ($@) my $name = shift @$a; my $opt = shift @$a; + return "No set command specified" if (!defined ($opt)); + # Valid commands for read only devices my $rocmds = "clear config defaults:noArg"; @@ -593,11 +595,36 @@ sub HMCCUDEV_Set ($@) $objname .= ':'.$1; } - my $rc = HMCCU_RPCSetConfig ($hash, $objname, $h); + my ($rc, $res) = HMCCU_RPCRequest ($hash, "putParamset", $objname, "MASTER", $h); +# my $rc = HMCCU_RPCSetConfig ($hash, $objname, $h); return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetState ($hash, "OK"); } + elsif ($opt eq 'rpcParameter') { + return HMCCU_SetError ($hash, "Usage: set $name rpcParameter [channel] [MASTER|VALUES] {parameter}={value} [...]") + if ((scalar keys %{$h}) < 1); + + my $key; + my $addr; + + while (my $p = shift @$a) { + if ($p =~ /^(MASTER|VALUES)$/ && !defined ($key)) { + $key = $p; + } + elsif ($p =~ /^([0-9]+)$/ && !defined ($addr)) { + HMCCU_SetError ($hash, -7) if ($p >= $hash->{channels}); + $addr = $p; + } + } + + $key = 'VALUES' if (!defined ($key)); + $addr = defined ($addr) ? "$ccuaddr:$addr" : $ccuaddr; + + my ($rc, $res) = HMCCU_RPCRequest ($hash, "putParamset", $addr, $key, $h); + return HMCCU_SetError ($hash, $rc) if ($rc < 0); + return HMCCU_SetState ($hash, "OK"); + } elsif ($opt eq 'defaults') { my $rc = HMCCU_SetDefaults ($hash); return HMCCU_SetError ($hash, "HMCCU: No default attributes found") if ($rc == 0); @@ -640,6 +667,8 @@ sub HMCCUDEV_Get ($@) my $name = shift @$a; my $opt = shift @$a; + return "No get command specified" if (!defined ($opt)); + # Get I/O device return undef if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending' || !defined ($hash->{IODev})); @@ -739,7 +768,6 @@ sub HMCCUDEV_Get ($@) return HMCCU_FormatDeviceInfo ($result); } elsif ($opt eq 'config') { - my $channel = undef; my $ccuobj = $ccuaddr; my $par = shift @$a; if (defined ($par)) { @@ -751,13 +779,13 @@ sub HMCCUDEV_Get ($@) } $par = '.*' if (!defined ($par)); - my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par); + my ($rc, $res) = HMCCU_RPCRequest ($hash, "getParamset", $ccuobj, "MASTER", undef, $par); +# my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0); HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error"); return $ccureadings ? undef : $res; } elsif ($opt eq 'configlist') { - my $channel = undef; my $ccuobj = $ccuaddr; my $par = shift @$a; if (defined ($par)) { @@ -769,13 +797,13 @@ sub HMCCUDEV_Get ($@) } $par = '.*' if (!defined ($par)); - my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par); + my ($rc, $res) = HMCCU_RPCRequest ($hash, "listParamset", $ccuobj, "MASTER", undef, $par); +# my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0); HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error"); return $res; } elsif ($opt eq 'configdesc') { - my $channel = undef; my $ccuobj = $ccuaddr; my $par = shift @$a; if (defined ($par)) { @@ -788,7 +816,8 @@ sub HMCCUDEV_Get ($@) } } - my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef); + my ($rc, $res) = HMCCU_RPCRequest ($hash, "getParamsetDescription", $ccuobj, "MASTER", undef); +# my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef); return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0); HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error"); return $res; diff --git a/fhem/FHEM/88_HMCCURPCPROC.pm b/fhem/FHEM/88_HMCCURPCPROC.pm index e9b20a468..c68fae913 100755 --- a/fhem/FHEM/88_HMCCURPCPROC.pm +++ b/fhem/FHEM/88_HMCCURPCPROC.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 1.7.002 +# Version 1.8 # # Subprocess based RPC Server module for HMCCU. # @@ -35,7 +35,7 @@ use SetExtensions; ###################################################################### # HMCCURPC version -my $HMCCURPCPROC_VERSION = '1.7.001'; +my $HMCCURPCPROC_VERSION = '1.8'; # Maximum number of events processed per call of Read() my $HMCCURPCPROC_MAX_EVENTS = 100; @@ -97,6 +97,26 @@ my $BINRPC_RESPONSE = 0x42696E01; my $BINRPC_REQUEST_HEADER = 0x42696E40; my $BINRPC_ERROR = 0x42696EFF; +# BinRPC datatype mapping +my %BINRPC_TYPE_MAPPING = ( + "BOOL" => $BINRPC_BOOL, + "INTEGER" => $BINRPC_INTEGER, + "STRING" => $BINRPC_STRING, + "FLOAT" => $BINRPC_DOUBLE, + "DOUBLE" => $BINRPC_DOUBLE, + "BASE64" => $BINRPC_BASE64, + "ARRAY" => $BINRPC_ARRAY, + "STRUCT" => $BINRPC_STRUCT +); + +# Read/Write flags for RPC methods (0=Read, 1=Write) +my %RPC_METHODS = ( + 'putParamset' => 1, + 'getParamset' => 0, + 'getParamsetDescription' => 0, + 'setValue' => 1, + 'getValue' => 0 +); ###################################################################### # Functions @@ -157,8 +177,8 @@ sub HMCCURPCPROC_ReaddDevicesCB ($$$); sub HMCCURPCPROC_EventCB ($$$$$); sub HMCCURPCPROC_ListDevicesCB ($$); -# Binary RPC encoding functions -sub HMCCURPCPROC_RPCNewValue ($$); +# RPC encoding functions +sub HMCCURPCPROC_EncValue ($$); sub HMCCURPCPROC_EncInteger ($); sub HMCCURPCPROC_EncBool ($); sub HMCCURPCPROC_EncString ($); @@ -497,6 +517,8 @@ sub HMCCURPCPROC_Set ($@) my $name = shift @$a; my $opt = shift @$a; + return "No set command specified" if (!defined ($opt)); + my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); my $options = $ccuflags =~ /expert/ ? "cleanup:noArg deregister:noArg register:noArg rpcrequest rpcserver:on,off" : ""; @@ -576,6 +598,8 @@ sub HMCCURPCPROC_Get ($@) my $name = shift @$a; my $opt = shift @$a; + return "No get command specified" if (!defined ($opt)); + my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); my $options = "rpcevents:noArg rpcstate:noArg"; @@ -1080,13 +1104,7 @@ sub HMCCURPCPROC_RegisterCallback ($$) $hash->{hmccu}{rpc}{cburl} = $cburl; Log3 $name, 2, "HMCCURPCPROC: [$name] Registering callback $cburl of type $rpctype with ID $clkey at $clurl"; - my $rc; - if ($rpctype eq 'A') { - $rc = HMCCURPCPROC_SendRequest ($hash, "init", $cburl, $clkey); - } - else { - $rc = HMCCURPCPROC_SendRequest ($hash, "init", $BINRPC_STRING, $cburl, $BINRPC_STRING, $clkey); - } + my $rc = HMCCURPCPROC_SendRequest ($hash, "init", "$cburl:STRING", "$clkey:STRING"); if (defined ($rc)) { return (1, $ccuflags !~ /ccuInit/ ? 'running' : 'registered'); @@ -1126,13 +1144,7 @@ sub HMCCURPCPROC_DeRegisterCallback ($$) # Deregister up to 2 times for (my $i=0; $i<2; $i++) { - my $rc; - if (HMCCU_IsRPCType ($hmccu_hash, $port, 'A')) { - $rc = HMCCURPCPROC_SendRequest ($hash, "init", $cburl); - } - else { - $rc = HMCCURPCPROC_SendRequest ($hash, "init", $BINRPC_STRING, $cburl); - } + my $rc = HMCCURPCPROC_SendRequest ($hash, "init", "$cburl:STRING"); if (defined ($rc)) { HMCCURPCPROC_SetRPCState ($hash, $force == 0 ? 'deregistered' : $rpchash->{state}, @@ -1389,8 +1401,8 @@ sub HMCCURPCPROC_RPCServerStarted ($) # Update client devices if interface is managed by HMCCURPCPROC device. # Normally interfaces are managed by HMCCU device. if ($hmccu_hash->{hmccu}{interfaces}{$ifname}{manager} eq 'HMCCURPCPROC') { - my ($c_ok, $c_err) = HMCCU_UpdateClients ($hmccu_hash, '.*', 'Attr', 0, $ifname); - Log3 $name, 2, "HMCCURPCPROC: [$name] Updated devices. Success=$c_ok Failed=$c_err"; + HMCCU_UpdateClients ($hmccu_hash, '.*', 'Attr', 0, $ifname, 1); +# Log3 $name, 2, "HMCCURPCPROC: [$name] Updated devices. Success=$c_ok Failed=$c_err"; } RemoveInternalTimer ($hash, "HMCCURPCPROC_IsRPCServerRunning"); @@ -1633,6 +1645,25 @@ sub HMCCURPCPROC_StopRPCServer ($$) ###################################################################### # Send RPC request to CCU. # Supports XML and BINRPC requests. +# Parameter $request contains the RPC command (i.e. "init" or +# "putParamset"). If RPC command is a parameter set command, two +# additional parameters address and key (MASTER or VALUE) must be +# specified. +# If RPC command is putParamset or setValue, the remaining elements +# in array @param contains the request parameters in format: +# ParameterName=Value[:ParameterType] +# For other RPC command the array @param contains the parameters in +# format: +# Value[:ParameterType] +# For BINRPC interfaces ParameterType is mapped as follows: +# "INTEGER" = $BINRPC_INTEGER +# "BOOL" = $BINRPC_BOOL +# "STRING" = $BINRPC_STRING +# "FLOAT" = $BINRPC_DOUBLE +# "DOUBLE" = $BINRPC_DOUBLE +# "BASE64" = $BINRPC_BASE64 +# "ARRAY" = $BINRPC_ARRAY +# "STRUCT" = $BINRPC_STRUCT # Return response or undef on error. ###################################################################### @@ -1640,35 +1671,89 @@ sub HMCCURPCPROC_SendRequest ($@) { my ($hash, $request, @param) = @_; my $name = $hash->{NAME}; - my $hmccu_hash = $hash->{IODev}; + my $ioHash = $hash->{IODev}; my $port = $hash->{rpcport}; my $rc; - return HMCCU_Log ($hash, 2, "I/O device not found", undef) if (!defined ($hmccu_hash)); + return HMCCU_Log ($hash, 2, "I/O device not found", undef) if (!defined ($ioHash)); - if (HMCCU_IsRPCType ($hmccu_hash, $port, 'A')) { + my $re = ':('.join('|', keys(%BINRPC_TYPE_MAPPING)).')'; + + if (HMCCU_IsRPCType ($ioHash, $port, 'A')) { # Use XMLRPC - my $clurl = HMCCU_BuildURL ($hmccu_hash, $port); + my $clurl = HMCCU_BuildURL ($ioHash, $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"; + HMCCU_Log ($hash, 4, "Send ASCII RPC request $request to $clurl", undef); 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)); + + if (exists ($RPC_METHODS{$request})) { + # Read or write parameter sets + my $address = shift @param; + my $key = shift @param; + return HMCCU_Log ($hash, 2, "Missing address or key in RPC request $request", undef) + if (!defined ($key)); + + my %hparam; + + # Write requests have at least one parameters + if ($RPC_METHODS{$request} == 1) { + # Build a parameter hash + while (my $p = shift @param) { + my $pt = "STRING"; + if ($p =~ /${re}/) { + $pt = $1; + $p =~ s/${re}//; + } + my ($pn, $pv) = split ('=', $p, 2); + next if (!defined ($pv)); + $hparam{$pn} = HMCCURPCPROC_EncValue ($pv, $pt); + } + + return HMCCU_Log ($hash, 2, "Missing parameter in RPC request $request", undef) + if (!keys %hparam); + + # Submit write paramset request + $rc = $rpcclient->simple_request ($request, $address, $key, \%hparam); + } + else { + # Submit read paramset request + $rc = $rpcclient->simple_request ($request, $address, $key); + } + } + else { + # RPC commands + my @aparam = (); + + # Build a parameter array + while (my $p = shift @param) { + my $pt = "STRING"; + if ($p =~ /${re}/) { + $pt = $1; + $p =~ s/${re}//; + } + push (@aparam, HMCCURPCPROC_EncValue ($p, $pt)); + } + + # Submit RPC command + $rc = $rpcclient->simple_request ($request, @aparam); + } + + HMCCU_Log ($hash, 2, "RPC request error ".$RPC::XML::ERROR, undef) if (!defined ($rc)); } - elsif (HMCCU_IsRPCType ($hmccu_hash, $port, 'B')) { + elsif (HMCCU_IsRPCType ($ioHash, $port, 'B')) { # Use BINRPC - my ($serveraddr) = HMCCU_GetRPCServerInfo ($hmccu_hash, $port, 'host'); - return HMCCU_Log ($hash, 2, "Can't get server address for port $port", undef) + my ($serveraddr) = HMCCU_GetRPCServerInfo ($ioHash, $port, 'host'); + return HMCCU_Log ($ioHash, 2, "Can't get server address for port $port", undef) if (!defined ($serveraddr)); my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); my $verbose = GetVerbose ($name); - Log3 $name, 4, "HMCCURPCPROC: [$name] Send binary RPC request $request to $serveraddr:$port"; + HMCCU_Log ($hash, 4, "Send binary RPC request $request to $serveraddr:$port", undef); my $encreq = HMCCURPCPROC_EncodeRequest ($request, \@param); return HMCCU_Log ($hash, 2, "Error encoding binary request", undef) if ($encreq eq ''); @@ -1687,7 +1772,7 @@ sub HMCCURPCPROC_SendRequest ($@) if (defined ($encresp)) { if ($ccuflags =~ /logEvents/ && $verbose >= 4) { - Log3 $name, 4, "HMCCURPCPROC: [$name] Response"; + HMCCU_Log ($hash, 4, "Response", undef); HMCCURPCPROC_HexDump ($name, $encresp); } my ($response, $err) = HMCCURPCPROC_DecodeResponse ($encresp); @@ -1701,7 +1786,7 @@ sub HMCCURPCPROC_SendRequest ($@) $socket->close (); } else { - Log3 $name, 2, "HMCCURPCPROC: [$name] Unknown RPC server type"; + HMCCU_Log ($hash, 2, "Unknown RPC server type", undef); } return $rc; @@ -1723,7 +1808,7 @@ sub HMCCURPCPROC_RPCPing ($) if ($ping > 0) { if ($init_done && HMCCURPCPROC_CheckProcessState ($hash, 'running')) { my $clkey = 'CB'.$hash->{rpcport}.$hash->{rpcid}; - HMCCURPCPROC_SendRequest ($hash, "ping", $clkey); + HMCCURPCPROC_SendRequest ($hash, "ping", "$clkey:STRING"); } InternalTimer (gettimeofday()+$ping, "HMCCURPCPROC_RPCPing", $hash, 0); } @@ -2253,6 +2338,10 @@ sub HMCCURPCPROC_ListDevicesCB ($$) return RPC::XML::array->new (); } +###################################################################### +# RPC encoding functions +###################################################################### + ###################################################################### # Convert value to RPC data type # Valid types are bool, boolean, int, integer, float, double, string. @@ -2260,7 +2349,7 @@ sub HMCCURPCPROC_ListDevicesCB ($$) # value is returned as is. ###################################################################### -sub HMCCURPCPROC_RPCNewValue ($$) +sub HMCCURPCPROC_EncValue ($$) { my ($value, $type) = @_; @@ -2300,10 +2389,6 @@ sub HMCCURPCPROC_RPCNewValue ($$) return $value; } -###################################################################### -# Binary RPC encoding functions -###################################################################### - ###################################################################### # Encode integer (type = 1) ###################################################################### @@ -2448,6 +2533,8 @@ sub HMCCURPCPROC_EncType ($$) { my ($t, $v) = @_; + return '' if (!defined ($t)); + if ($t == $BINRPC_INTEGER) { return HMCCURPCPROC_EncInteger ($v); } @@ -2478,7 +2565,8 @@ sub HMCCURPCPROC_EncType ($$) # Encode RPC request with method and optional parameters. # Headers are not supported. # Input is method name and reference to parameter array. -# Array must contain (type, value) pairs +# Array must contain parameters in format value[:type]. Default for +# type is STRING. # Return encoded data or empty string on error ###################################################################### @@ -2490,14 +2578,19 @@ sub HMCCURPCPROC_EncodeRequest ($$) my $m = HMCCURPCPROC_EncName ($method); # Encode parameters + my $re = ':('.join('|', keys(%BINRPC_TYPE_MAPPING)).')'; my $r = ''; my $s = 0; - + if (defined ($args)) { - while (my $t = shift @$args) { - my $e = shift @$args; - last if (!defined ($e)); - $r .= HMCCURPCPROC_EncType ($t, $e); + while (my $p = shift @$args) { + my $pt = "STRING"; + if ($p =~ /${re}/) { + $pt = $1; + $p =~ s/${re}//; + } + my ($e, $t) = split (':', $p); + $r .= HMCCURPCPROC_EncType ($BINRPC_TYPE_MAPPING{uc($pt)}, $p); $s++; } } diff --git a/fhem/FHEM/HMCCUConf.pm b/fhem/FHEM/HMCCUConf.pm index fac305cfb..7a3b8dccd 100644 --- a/fhem/FHEM/HMCCUConf.pm +++ b/fhem/FHEM/HMCCUConf.pm @@ -4,7 +4,7 @@ # # $Id$ # -# Version 4.6 +# Version 4.6.002 # # Configuration parameters for HomeMatic devices. # @@ -168,6 +168,17 @@ use vars qw(%HMCCU_SCRIPTS); webCmd => "control:on:off", widgetOverride => "control:slider,0,10,100" }, + "HmIP-FCI6" => { + _description => "IP Kontaktschnittstelle Unterputz 6-fach", + _channels => "1,2,3,4,5,6", + ccureadingfilter => "STATE", + controldatapoint => "STATE", + statedatapoint => "STATE", + statevals => "on:true,off:false", + substitute => "STATE!(0|false):off,(1|true):on", + webCmd => "devstate", + widgetOverride => "devstate:uzsuToggle,off,on" + }, "HM-PB-2-FM" => { _description => "Funk-Wandtaster 2-fach", _channels => "1,2", @@ -297,7 +308,7 @@ use vars qw(%HMCCU_SCRIPTS); webCmd => "control:up:stop:down", widgetOverride => "control:slider,0,10,100" }, - "HmIP-BROLL" => { + "HmIP-BROLL|HmIP-FROLL" => { _description => "Rollladenaktor", _channels => "4", ccureadingfilter => "(ERROR_CODE|ERROR_OVERHEAT|ACTUAL_TEMPERATURE|LEVEL|ACTIVITY_STATE)", @@ -336,7 +347,7 @@ use vars qw(%HMCCU_SCRIPTS); stripnumber => 1, substitute => "RAINING,RAIN_COUNTER_OVERFLOW,SUNSHINEDURATION_OVERFLOW,SUNSHINE_THRESHOLD_OVERRUN,WIND_THRESHOLD_OVERRUN!(0|false):no,(1|true):yes" }, - "HM-Sec-MD|HM-Sec-MDIR|HM-Sec-MDIR-2|HM-Sec-MDIR-3" => { + "HM-Sec-MD|HM-Sec-MDIR|HM-Sec-MDIR-2|HM-Sec-MDIR-3|Hm-Sen-MDIR-O-3" => { _description => "Bewegungsmelder", _channels => "1", ccureadingfilter => "(BRIGHTNESS|MOTION)", @@ -348,9 +359,13 @@ use vars qw(%HMCCU_SCRIPTS); _description => "Bewegungsmelder", _channels => "1", ccureadingfilter => "(ILLUMINATION|MOTION)", - eventMap => "/datapoint MOTION_DETECTION_ACTIVE 1:detection-on/datapoint MOTION_DETECTION_ACTIVE 0:detection-off/", + controldatapoint => "MOTION_DETECTION_ACTIVE", + eventMap => "/datapoint RESET_MOTION 1:reset/datapoint MOTION_DETECTION_ACTIVE 1:detection-on/datapoint MOTION_DETECTION_ACTIVE 0:detection-off/", + hmstatevals => "SABOTAGE!(1|true):sabotage", statedatapoint => "MOTION", - substitute => "MOTION!(0|false):no,(1|true):yes" + substitute => "MOTION!(0|false):no,(1|true):yes;MOTION_DETECTION_ACTIVE!(0|false):off,(1|true):on", + webCmd => "control", + widgetOverride => "control:uzsuToggle,off,on" }, "HmIP-SPI" => { _description => "Anwesenheitssensor", @@ -712,16 +727,16 @@ use vars qw(%HMCCU_SCRIPTS); webCmd => "control:up:stop:down", widgetOverride => "control:slider,0,10,100" }, - "HmIP-BROLL" => { + "HmIP-BROLL|HmIP-FROLL" => { _description => "Rollladenaktor", - ccureadingfilter => "(ERROR_CODE|ERROR_OVERHEAT|ACTUAL_TEMPERATURE|LEVEL|ACTIVITY_STATE|SELF_CALIBRATION_RESULT)", - ccureadingname => "LEVEL:+pct", + ccureadingfilter => "3.LEVEL;(ERROR_CODE|ERROR_OVERHEAT|ACTUAL_TEMPERATURE|ACTIVITY_STATE|SELF_CALIBRATION_RESULT)", + ccureadingname => "3.LEVEL$:+control,+pct", ccuscaleval => "LEVEL:0:1:0:100", cmdIcon => "up:fts_shutter_up stop:fts_shutter_manual down:fts_shutter_down", controldatapoint => "4.LEVEL", hmstatevals => "ACTUAL_TEMPERATURE_STATUS!2:tempOverflow,3:tempUnderflow;ERROR_OVERHEAT!(1|true):overheat", eventMap => "/datapoint 4.STOP true:stop/datapoint 4.LEVEL 0:down/datapoint 4.LEVEL 100:up/datapoint 3.SELF_CALIBRATION 0:stopCalibration/datapoint 3.SELF_CALIBRATION 1:startCalibration/", - statedatapoint => "4.LEVEL", + statedatapoint => "3.LEVEL", stripnumber => 1, substexcl => "control|pct", substitute => "LEVEL!#0-0:closed,#100-100:open;ACTIVITY_STATE!0:unknown,1:up,2:down,3:stop;ERROR_OVERHEAT!(0|false):no,(1|true):yes;ACTUAL_TEMPERATURE_STATUS!0:normal,1:unknown,2:overflow,3:underflow;SELF_CALIBRATION_RESULT!(0|false):failed,(1|true):ok", @@ -763,7 +778,7 @@ use vars qw(%HMCCU_SCRIPTS); ccureadingname => "1.LEVEL:valve_position", ccuscaleval => "LEVEL:0:1:0:100", controldatapoint => "1.SET_POINT_TEMPERATURE", - eventMap => "/datapoint 1.BOOST_MODE true:Boost/datapoint 1.CONTROL_MODE 0:Auto/datapoint 1.CONTROL_MODE 1:Manual/datapoint 1.CONTROL_MODE 2:Holiday/datapoint 1.SET_POINT_TEMPERATURE 4.5:off/datapoint 1.SET_POINT_TEMPERATURE 30.5:on/", + eventMap => "/datapoint 1.BOOST_MODE true:Boost/datapoint 1.CONTROL_MODE 0:Auto/datapoint 1.CONTROL_MODE 1:Manual/datapoint 1.CONTROL_MODE 2:Holiday/datapoint 1.CONTROL_MODE 1 1.SET_POINT_TEMPERATURE 4.5:off/datapoint 1.CONTROL_MODE 0 1.SET_POINT_TEMPERATURE 30.5:on/", genericDeviceType => "thermostat", statedatapoint => "1.SET_POINT_TEMPERATURE", stripnumber => 1, @@ -833,7 +848,7 @@ use vars qw(%HMCCU_SCRIPTS); webCmd => "control:Auto:Manu:Boost:on:off", widgetOverride => "control:slider,4.5,0.5,30.5,1" }, - "HM-Sec-MD|HM-Sec-MDIR|HM-Sec-MDIR-2|HM-Sec-MDIR-3" => { + "HM-Sec-MD|HM-Sec-MDIR|HM-Sec-MDIR-2|HM-Sec-MDIR-3|Hm-Sen-MDIR-O-3" => { _description => "Bewegungsmelder", ccureadingfilter => "(BRIGHTNESS|MOTION)", hmstatevals => "ERROR!1:sabotage", @@ -843,9 +858,13 @@ use vars qw(%HMCCU_SCRIPTS); "HmIP-SMI" => { _description => "Bewegungsmelder", ccureadingfilter => "(ILLUMINATION|MOTION)", - eventMap => "/datapoint 1.MOTION_DETECTION_ACTIVE 1:detection-on/datapoint 1.MOTION_DETECTION_ACTIVE 0:detection-off/", + controldatapoint => "1.MOTION_DETECTION_ACTIVE", + eventMap => "/datapoint 1.RESET_MOTION 1:reset/datapoint 1.MOTION_DETECTION_ACTIVE 1:detection-on/datapoint 1.MOTION_DETECTION_ACTIVE 0:detection-off/", + hmstatevals => "SABOTAGE!(1|true):sabotage", statedatapoint => "1.MOTION", - substitute => "MOTION!(0|false):no,(1|true):yes" + substitute => "MOTION!(0|false):no,(1|true):yes;MOTION_DETECTION_ACTIVE!(0|false):off,(1|true):on", + webCmd => "control", + widgetOverride => "control:uzsuToggle,off,on" }, "HmIP-SMI55" => { _description => "Bewegungsmelder",