diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm index bfad73913..a82a9bc17 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm @@ -4,7 +4,7 @@ # # $Id: 88_HMCCU.pm 18745 2019-02-26 17:33:23Z zap $ # -# Version 4.4.014 +# Version 4.4.016 # # Module for communication between FHEM and Homematic CCU2/3. # @@ -56,7 +56,7 @@ my %HMCCU_CUST_CHN_DEFAULTS; my %HMCCU_CUST_DEV_DEFAULTS; # HMCCU version -my $HMCCU_VERSION = '4.4.014'; +my $HMCCU_VERSION = '4.4.016'; # Constants and default values my $HMCCU_MAX_IOERRORS = 100; @@ -210,7 +210,6 @@ sub HMCCU_AggregationRules ($$); # Handling of default attributes sub HMCCU_DetectDefaults ($$); sub HMCCU_ExportDefaults ($$); -sub HMCCU_ExportDefaultsCSV ($$); sub HMCCU_ImportDefaults ($); sub HMCCU_FindDefaults ($$); sub HMCCU_GetDefaults ($$); @@ -246,7 +245,7 @@ sub HMCCU_UpdateMultipleDevices ($$); sub HMCCU_UpdatePeers ($$$$); sub HMCCU_UpdateParamsetReadings ($$$;$); sub HMCCU_UpdateSingleDatapoint ($$$$); -sub HMCCU_UpdateSingleDevice ($$$$); +# sub HMCCU_UpdateSingleDevice ($$$$); # RPC functions sub HMCCU_EventsTimedOut ($); @@ -343,7 +342,7 @@ sub HMCCU_GetDatapointAttr ($$$$$); sub HMCCU_GetDatapointCount ($$$); sub HMCCU_GetDatapointList ($$$); sub HMCCU_GetSpecialCommands ($$); -sub HMCCU_GetSpecialDatapoints ($$$$$); +sub HMCCU_GetSpecialDatapoints ($); sub HMCCU_GetStateValues ($$;$); sub HMCCU_GetSwitchDatapoint ($$$); sub HMCCU_GetValidDatapoints ($$$$$); @@ -392,7 +391,9 @@ sub HMCCU_GetHMState ($$$); sub HMCCU_GetIdFromIP ($$); sub HMCCU_GetTimeSpec ($); sub HMCCU_FlagsToStr ($$$;$$); +sub HMCCU_Max ($$); sub HMCCU_MaxHashEntries ($$); +sub HMCCU_Min ($$); sub HMCCU_RefToString ($); sub HMCCU_ResolveName ($$); sub HMCCU_TCPConnect ($$); @@ -434,10 +435,10 @@ sub HMCCU_Initialize ($) $hash->{AttrList} = "stripchar stripnumber ccuaggregate:textField-long". " ccudefaults rpcinterfaces:multiple-strict,".join(',',sort keys %HMCCU_RPC_PORT). " ccudef-hmstatevals:textField-long ccudef-substitute:textField-long". - " ccudef-readingname:textField-long ccudef-readingfilter:textField-long". + " ccudef-readingfilter:textField-long". " ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc". " ccudef-stripnumber ccuReadingPrefix". - " ccuflags:multiple-strict,procrpc,dptnocheck,logCommand,noagg,nohmstate,". + " ccuflags:multiple-strict,procrpc,dptnocheck,logCommand,noagg,nohmstate,updGroupMembers,". "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). @@ -907,80 +908,6 @@ sub HMCCU_ExportDefaults ($$) return 1; } -###################################################################### -# Export default attributes as CSV file. -###################################################################### - -sub HMCCU_ExportDefaultsCSV ($$) -{ - my ($filename, $all) = @_; - - my %attrlist = ( - '_type' => '', '_description' => '', '_channels' => '', - 'ccureadingfilter' => '', 'ccureadingname' => '', 'ccuscaleval' => '', 'cmdIcon' => '', 'controldatapoint' => '', - 'eventMap' => '', 'event-on-change-reading' => '', 'event-on-update-reading' => '', - 'genericDeviceType' => '', - 'hmstatevals' => '', - 'statedatapoint' => '', 'statevals' => '', 'stripnumber' => '', 'substexcl' => '', 'substitute' => '', - 'webCmd' => '', 'widgetOverride' => '' - ); - - return 0 if (!open (DEFFILE, ">$filename")); - - # Write header - print DEFFILE "_flag,".join (',', sort keys %attrlist)."\n"; - - # Write channel configurations - foreach my $t (keys %{$HMCCU_CHN_DEFAULTS}) { - print DEFFILE "C"; - $attrlist{'_type'} = $t; - foreach $a (sort keys %attrlist) { - my $v = exists ($HMCCU_CHN_DEFAULTS->{$t}{$a}) ? $HMCCU_CHN_DEFAULTS->{$t}{$a} : $attrlist{$a}; - print DEFFILE ",\"$v\""; - } - print DEFFILE "\n"; - } - - # Write device configurations - foreach my $t (keys %{$HMCCU_DEV_DEFAULTS}) { - print DEFFILE "D"; - $attrlist{'_type'} = $t; - foreach $a (sort keys %attrlist) { - my $v = exists ($HMCCU_DEV_DEFAULTS->{$t}{$a}) ? $HMCCU_DEV_DEFAULTS->{$t}{$a} : $attrlist{$a}; - print DEFFILE ",\"$v\""; - } - print DEFFILE "\n"; - } - - if ($all) { - # Write channel configurations - foreach my $t (keys %HMCCU_CUST_CHN_DEFAULTS) { - print DEFFILE "C"; - $attrlist{'_type'} = $t; - foreach $a (sort keys %attrlist) { - my $v = exists ($HMCCU_CUST_CHN_DEFAULTS{$t}{$a}) ? $HMCCU_CUST_CHN_DEFAULTS{$t}{$a} : $attrlist{$a}; - print DEFFILE ",\"$v\""; - } - print DEFFILE "\n"; - } - - # Write device configurations - foreach my $t (keys %HMCCU_CUST_DEV_DEFAULTS) { - print DEFFILE "D"; - $attrlist{'_type'} = $t; - foreach $a (sort keys %attrlist) { - my $v = exists ($HMCCU_CUST_DEV_DEFAULTS{$t}{$a}) ? $HMCCU_CUST_DEV_DEFAULTS{$t}{$a} : $attrlist{$a}; - print DEFFILE ",\"$v\""; - } - print DEFFILE "\n"; - } - } - - close (DEFFILE); - - return 1; -} - ###################################################################### # Import customer default attributes # Returns 1 on success. Returns negative line number on syntax errors. @@ -1451,7 +1378,7 @@ sub HMCCU_DelayedShutdown ($) # HMCCU_Log ($hash, 3, "DelayedShutdown()"); - my $delay = max (AttrVal ("global", "maxShutdownDelay", 10)-2, 0); + my $delay = HMCCU_Max (AttrVal ("global", "maxShutdownDelay", 10)-2, 0); # Shutdown RPC server if (!exists ($hash->{hmccu}{delayedShutdown})) { @@ -1657,7 +1584,7 @@ sub HMCCU_Set ($@) foreach my $devName (@devList) { my $dh = $defs{$devName}; my $ccuif = $dh->{ccuif}; - my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($dh, '', '', '', ''); + my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($dh); my $stateVals = HMCCU_GetStateValues ($dh, $cd, $cc); if ($dh->{TYPE} eq 'HMCCUCHN') { @@ -2158,19 +2085,14 @@ sub HMCCU_Get ($@) } elsif ($opt eq 'exportdefaults') { my $filename = shift @$a; - $usage = "Usage: get $name $opt filename ['all'] ['csv']"; - my $csv = 0; + $usage = "Usage: get $name $opt filename ['all']"; my $all = 0; - - foreach my $defopt (@$a) { - if ($defopt eq 'csv') { $csv = 1; } - elsif ($defopt eq 'all') { $all = 1; } - else { return HMCCU_SetError ($hash, $usage); } - } + my $defopt = shift @$a; + $all = 1 if (defined($defopt) && $defopt eq 'all'); return HMCCU_SetError ($hash, $usage) if (!defined ($filename)); - my $rc = $csv ? HMCCU_ExportDefaultsCSV ($filename, $all) : HMCCU_ExportDefaults ($filename, $all); + my $rc = HMCCU_ExportDefaults ($filename, $all); return HMCCU_SetError ($hash, -16) if ($rc == 0); return HMCCU_SetState ($hash, "OK", "Default attributes written to $filename"); } @@ -2559,9 +2481,10 @@ sub HMCCU_GetReadingName ($$$$$$$;$) # Log3 $name, 1, "HMCCU: ChannelNo undefined: Addr=".$a if (!defined ($c)); $rf = HMCCU_GetAttrReadingFormat ($hash, $ioHash) if (!defined ($rf)); - my $gsr = AttrVal ($ioHash->{NAME}, 'ccudef-readingname', ''); - my $sr = AttrVal ($name, 'ccureadingname', $gsr); - $sr .= ";".$gsr if ($sr ne $gsr && $gsr ne ''); + my $sr = 'LEVEL$:pct;SET_TEMPERATURE$:desired-temp;ACTUAL_TEMPERATURE$measured-temp;'. + 'SET_POINT_TEMPERATURE$:desired-temp'; + my $asr = AttrVal ($name, 'ccureadingname', ''); + $sr .= ';'.$asr if ($asr ne ''); # Complete missing values if ($n eq '' && $a ne '') { @@ -3708,7 +3631,11 @@ sub HMCCU_GetChannelRole ($;$) return $clHash->{hmccu}{role}; } elsif ($clHash->{TYPE} eq 'HMCCUDEV') { - if (defined($chnNo)) { + if (!defined($chnNo)) { + my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($clHash); + $chnNo = $cc; + } + if ($chnNo ne '') { foreach my $role (split(',', $clHash->{hmccu}{role})) { my ($c, $r) = split(':', $role); return $r if (defined($r) && "$c" eq "$chnNo"); @@ -4479,7 +4406,7 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) my $clRF = HMCCU_GetAttrReadingFormat ($clHash, $ioHash); my $peer = AttrVal ($clName, 'peer', 'null'); my $clInt = $clHash->{ccuif}; - my ($sc, $sd, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clHash, '', 'STATE', '', ''); + my ($sc, $sd, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clHash); readingsBeginUpdate ($clHash); @@ -4620,141 +4547,141 @@ sub HMCCU_RefreshReadings ($) # {devaddr}{datapoint} = value ###################################################################### -sub HMCCU_UpdateSingleDevice ($$$$) -{ - my ($ccuhash, $clthash, $objects, $alref) = @_; - my $ccuname = $ccuhash->{NAME}; - my $cltname = $clthash->{NAME}; - my $clttype = $clthash->{TYPE}; - my $fnc = "UpdateSingleDevice"; - - return 0 if (!defined ($clthash->{IODev}) || !defined ($clthash->{ccuaddr})); - return 0 if ($clthash->{IODev} != $ccuhash); - - # Build list of relevant addresses in object data hash - my ($devaddr, $cnum) = HMCCU_SplitChnAddr ($clthash->{ccuaddr}); - my @addlist = defined ($alref) ? @$alref : ($devaddr); - - # Check if update of device allowed - my $disable = AttrVal ($cltname, 'disable', 0); - my $update = AttrVal ($cltname, 'ccureadings', HMCCU_IsFlag ($cltname, 'noReadings') ? 0 : 1); - return 0 if ($update == 0 || $disable == 1 || $clthash->{ccudevstate} ne 'active'); - - # Get device parameters and attributes - my $ccuflags = HMCCU_GetFlags ($ccuname); - my $cf = HMCCU_GetFlags ($cltname); - my $peer = AttrVal ($cltname, 'peer', 'null'); - my $crf = HMCCU_GetAttrReadingFormat ($clthash, $ccuhash); - my ($sc, $st, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clthash, '', 'STATE', '', ''); - - # Virtual device flag - my $vg = 0; - $vg = 1 if (($clthash->{ccuif} eq 'VirtualDevices' || $clthash->{ccuif} eq 'fhem') && - exists ($clthash->{ccugroup})); - - HMCCU_Trace ($clthash, 2, $fnc, "$cltname Objects = ".join(',', @addlist)); - - # Store the resulting readings - my %results; - - # Updated internal values - my @chkeys = (); - - # Update readings of client device with data from address list - readingsBeginUpdate ($clthash); - - foreach my $addr (@addlist) { - next if (!exists ($objects->{$addr})); - - HMCCU_Trace ($clthash, 2, $fnc, "Processing object $addr"); - - # Update channels of device - foreach my $chnnum (keys (%{$objects->{$addr}})) { - next if ($clttype eq 'HMCCUCHN' && "$chnnum" ne "$cnum" && "$chnnum" ne "0"); - next if ("$chnnum" eq "0" && $cf =~ /nochn0/); - my $chnadd = "$addr:$chnnum"; - - my $devDesc = HMCCU_GetDeviceDesc ($ccuhash, $chnadd, $clthash->{ccuif}); - my $chnType = defined($devDesc) ? $devDesc->{TYPE} : HMCCU_GetChannelRole ($clthash, $chnnum); - - # Update datapoints of channel - foreach my $dpt (keys (%{$objects->{$addr}{$chnnum}})) { - my $value = $objects->{$addr}{$chnnum}{$dpt}; - next if (!defined ($value)); - - # Key for storing values in client hash. Indirect updates of virtual devices - # are stored with device address in key. - my $chkey = $devaddr ne $addr ? "$chnadd.$dpt" : "$chnnum.$dpt"; - - # Store datapoint raw value in device hash - HMCCU_UpdateInternalValues ($clthash, $chkey, 'VALUES', 'VAL', $value); - - HMCCU_Trace ($clthash, 2, $fnc, "dev=$cltname, chnadd/object=$chnadd, dpt=$dpt, key=$chkey, value=$value"); - - if (HMCCU_FilterReading ($clthash, $chnadd, $dpt)) { - # Modify reading name and value - my @readings = HMCCU_GetReadingName ($clthash, '', $addr, $chnnum, $dpt, '', $crf); - my $svalue = HMCCU_ScaleValue ($clthash, $chnnum, $dpt, $value, 0); - my $fvalue = HMCCU_FormatReadingValue ($clthash, $svalue, $dpt); - my $cvalue = HMCCU_Substitute ($fvalue, $clthash, 0, $chnnum, $dpt, $chnType, $devDesc); - $cvalue = HMCCU_GetParamValue ($ccuhash, $devDesc, 'VALUES', $dpt, $fvalue) if ("$fvalue" eq "$cvalue"); -# my %calcs = HMCCU_CalculateReading ($clthash, $chkey); - - # Store the resulting value after scaling, formatting and substitution - HMCCU_UpdateInternalValues ($clthash, $chkey, 'VALUES', 'SVAL', $cvalue); - push @chkeys, $chkey; - - # Store result, but not for indirect updates of virtual devices - $results{$devaddr}{$chnnum}{$dpt} = $cvalue if ($devaddr eq $addr); - - HMCCU_Trace ($clthash, 2, $fnc, - "device=$cltname, readings=".join(',', @readings). - ", orgvalue=$value value=$cvalue peer=$peer"); - - # Update readings - if ("$chnnum" ne "0" || $cf =~ /devState/) { - foreach my $rn (@readings) { - HMCCU_BulkUpdate ($clthash, $rn, $fvalue, $cvalue) if ($rn ne ''); - } - } -# foreach my $clcr (keys %calcs) { -# HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr}); +# sub HMCCU_UpdateSingleDevice ($$$$) +# { +# my ($ccuhash, $clthash, $objects, $alref) = @_; +# my $ccuname = $ccuhash->{NAME}; +# my $cltname = $clthash->{NAME}; +# my $clttype = $clthash->{TYPE}; +# my $fnc = "UpdateSingleDevice"; +# +# return 0 if (!defined ($clthash->{IODev}) || !defined ($clthash->{ccuaddr})); +# return 0 if ($clthash->{IODev} != $ccuhash); +# +# # Build list of relevant addresses in object data hash +# my ($devaddr, $cnum) = HMCCU_SplitChnAddr ($clthash->{ccuaddr}); +# my @addlist = defined ($alref) ? @$alref : ($devaddr); +# +# # Check if update of device allowed +# my $disable = AttrVal ($cltname, 'disable', 0); +# my $update = AttrVal ($cltname, 'ccureadings', HMCCU_IsFlag ($cltname, 'noReadings') ? 0 : 1); +# return 0 if ($update == 0 || $disable == 1 || $clthash->{ccudevstate} ne 'active'); +# +# # Get device parameters and attributes +# my $ccuflags = HMCCU_GetFlags ($ccuname); +# my $cf = HMCCU_GetFlags ($cltname); +# my $peer = AttrVal ($cltname, 'peer', 'null'); +# my $crf = HMCCU_GetAttrReadingFormat ($clthash, $ccuhash); +# my ($sc, $st, $cc, $cd, $ss, $cs) = HMCCU_GetSpecialDatapoints ($clthash, '', 'STATE', '', ''); +# +# # Virtual device flag +# my $vg = 0; +# $vg = 1 if (($clthash->{ccuif} eq 'VirtualDevices' || $clthash->{ccuif} eq 'fhem') && +# exists ($clthash->{ccugroup})); +# +# HMCCU_Trace ($clthash, 2, $fnc, "$cltname Objects = ".join(',', @addlist)); +# +# # Store the resulting readings +# my %results; +# +# # Updated internal values +# my @chkeys = (); +# +# # Update readings of client device with data from address list +# readingsBeginUpdate ($clthash); +# +# foreach my $addr (@addlist) { +# next if (!exists ($objects->{$addr})); +# +# HMCCU_Trace ($clthash, 2, $fnc, "Processing object $addr"); +# +# # Update channels of device +# foreach my $chnnum (keys (%{$objects->{$addr}})) { +# next if ($clttype eq 'HMCCUCHN' && "$chnnum" ne "$cnum" && "$chnnum" ne "0"); +# next if ("$chnnum" eq "0" && $cf =~ /nochn0/); +# my $chnadd = "$addr:$chnnum"; +# +# my $devDesc = HMCCU_GetDeviceDesc ($ccuhash, $chnadd, $clthash->{ccuif}); +# my $chnType = defined($devDesc) ? $devDesc->{TYPE} : HMCCU_GetChannelRole ($clthash, $chnnum); +# +# # Update datapoints of channel +# foreach my $dpt (keys (%{$objects->{$addr}{$chnnum}})) { +# my $value = $objects->{$addr}{$chnnum}{$dpt}; +# next if (!defined ($value)); +# +# # Key for storing values in client hash. Indirect updates of virtual devices +# # are stored with device address in key. +# my $chkey = $devaddr ne $addr ? "$chnadd.$dpt" : "$chnnum.$dpt"; +# +# # Store datapoint raw value in device hash +# HMCCU_UpdateInternalValues ($clthash, $chkey, 'VALUES', 'VAL', $value); +# +# HMCCU_Trace ($clthash, 2, $fnc, "dev=$cltname, chnadd/object=$chnadd, dpt=$dpt, key=$chkey, value=$value"); +# +# if (HMCCU_FilterReading ($clthash, $chnadd, $dpt)) { +# # Modify reading name and value +# my @readings = HMCCU_GetReadingName ($clthash, '', $addr, $chnnum, $dpt, '', $crf); +# my $svalue = HMCCU_ScaleValue ($clthash, $chnnum, $dpt, $value, 0); +# my $fvalue = HMCCU_FormatReadingValue ($clthash, $svalue, $dpt); +# my $cvalue = HMCCU_Substitute ($fvalue, $clthash, 0, $chnnum, $dpt, $chnType, $devDesc); +# $cvalue = HMCCU_GetParamValue ($ccuhash, $devDesc, 'VALUES', $dpt, $fvalue) if ("$fvalue" eq "$cvalue"); +# # my %calcs = HMCCU_CalculateReading ($clthash, $chkey); +# +# # Store the resulting value after scaling, formatting and substitution +# HMCCU_UpdateInternalValues ($clthash, $chkey, 'VALUES', 'SVAL', $cvalue); +# push @chkeys, $chkey; +# +# # Store result, but not for indirect updates of virtual devices +# $results{$devaddr}{$chnnum}{$dpt} = $cvalue if ($devaddr eq $addr); +# +# HMCCU_Trace ($clthash, 2, $fnc, +# "device=$cltname, readings=".join(',', @readings). +# ", orgvalue=$value value=$cvalue peer=$peer"); +# +# # Update readings +# if ("$chnnum" ne "0" || $cf =~ /devState/) { +# foreach my $rn (@readings) { +# HMCCU_BulkUpdate ($clthash, $rn, $fvalue, $cvalue) if ($rn ne ''); +# } # } - HMCCU_BulkUpdate ($clthash, 'control', $fvalue, $cvalue) - 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'); - } - } - } - } - - if (scalar (@chkeys) > 0) { - my %calcs = HMCCU_CalculateReading ($clthash, \@chkeys); - foreach my $clcr (keys %calcs) { - HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr}); - } - } - - # Update device states - my ($devState, $battery, $activity) = HMCCU_GetDeviceStates ($clthash); - HMCCU_BulkUpdate ($clthash, 'battery', $battery, $battery); - HMCCU_BulkUpdate ($clthash, 'activity', $activity, $activity); - HMCCU_BulkUpdate ($clthash, 'devstate', $devState, $devState); - - # Calculate and update HomeMatic state - if ($ccuflags !~ /nohmstate/) { - my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cltname, $ccuname, undef); - HMCCU_BulkUpdate ($clthash, $hms_read, $hms_val, $hms_val) if (defined ($hms_val)); - } - - readingsEndUpdate ($clthash, 1); - - return \%results; -} +# # foreach my $clcr (keys %calcs) { +# # HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr}); +# # } +# HMCCU_BulkUpdate ($clthash, 'control', $fvalue, $cvalue) +# 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'); +# } +# } +# } +# } +# +# if (scalar (@chkeys) > 0) { +# my %calcs = HMCCU_CalculateReading ($clthash, \@chkeys); +# foreach my $clcr (keys %calcs) { +# HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr}); +# } +# } +# +# # Update device states +# my ($devState, $battery, $activity) = HMCCU_GetDeviceStates ($clthash); +# HMCCU_BulkUpdate ($clthash, 'battery', $battery, $battery); +# HMCCU_BulkUpdate ($clthash, 'activity', $activity, $activity); +# HMCCU_BulkUpdate ($clthash, 'devstate', $devState, $devState); +# +# # Calculate and update HomeMatic state +# if ($ccuflags !~ /nohmstate/) { +# my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cltname, $ccuname, undef); +# HMCCU_BulkUpdate ($clthash, $hms_read, $hms_val, $hms_val) if (defined ($hms_val)); +# } +# +# readingsEndUpdate ($clthash, 1); +# +# return \%results; +# } ###################################################################### # Store datapoint values in device hash. @@ -4822,21 +4749,25 @@ sub HMCCU_UpdateMultipleDevices ($$) ###################################################################### # Get list of device addresses including group device members. +# For virtual devices group members are only returned if ccuflag +# updGroupMembers is set. ###################################################################### sub HMCCU_GetAffectedAddresses ($) { - my ($clthash) = @_; + my ($clHash) = @_; my @addlist = (); - if ($clthash->{TYPE} eq 'HMCCUDEV' || $clthash->{TYPE} eq 'HMCCUCHN') { - if (exists ($clthash->{ccuaddr})) { - my ($devaddr, $cnum) = HMCCU_SplitChnAddr ($clthash->{ccuaddr}); + if ($clHash->{TYPE} eq 'HMCCUDEV' || $clHash->{TYPE} eq 'HMCCUCHN') { + my $ioHash = HMCCU_GetHash ($clHash); + my $ccuFlags = defined($ioHash) ? HMCCU_GetFlags ($ioHash) : 'null'; + if (exists($clHash->{ccuaddr})) { + my ($devaddr, $cnum) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); push @addlist, $devaddr; } - if (($clthash->{ccuif} eq 'VirtualDevices' || $clthash->{ccuif} eq 'fhem') && - exists ($clthash->{ccugroup})) { - push @addlist, split (',', $clthash->{ccugroup}); + if ((($clHash->{ccuif} eq 'VirtualDevices' && $ccuFlags =~ /updGroupMembers/) || + $clHash->{ccuif} eq 'fhem') && exists($clHash->{ccugroup})) { + push @addlist, split (',', $clHash->{ccugroup}); } } @@ -6666,6 +6597,7 @@ sub HMCCU_FindClientDevices ($$$$) ###################################################################### # Check if client device already exists +# Return name of existing device or empty string. ###################################################################### sub HMCCU_ExistsClientDevice ($$) @@ -6680,7 +6612,7 @@ sub HMCCU_ExistsClientDevice ($$) ); } - return undef; + return ''; } ###################################################################### @@ -6927,7 +6859,9 @@ sub HMCCU_SetDefaultAttributes ($;$) my $clName = $clHash->{NAME}; my $role = HMCCU_GetChannelRole ($clHash, $ctrlChn); + HMCCU_Log ($clHash, 2, "SetDefaultAttributes: role=$role"); if ($role ne '' && exists($HMCCU_ATTR->{$role})) { + HMCCU_Log ($clHash, 2, "SetDefaultAttributes: attributes found"); foreach my $a (keys %{$HMCCU_ATTR->{$role}}) { CommandAttr (undef, "$clName $a ".$HMCCU_ATTR->{$role}{$a}); } @@ -6984,80 +6918,108 @@ sub HMCCU_GetSpecialCommands ($$) # Return (sc, sd, cc, cd) ###################################################################### -sub HMCCU_GetSpecialDatapoints ($$$$$) +sub HMCCU_GetSpecialDatapoints ($) { - my ($hash, $sc, $sd, $cc, $cd) = @_; + my ($hash) = @_; my $name = $hash->{NAME}; my $type = $hash->{TYPE}; my $ccutype = $hash->{ccutype}; + my ($sc, $sd, $cc, $cd) = ('', '', '', ''); my $statedatapoint = AttrVal ($name, 'statedatapoint', ''); - my $statechannel = AttrVal ($name, 'statechannel', ''); - my $controldatapoint = AttrVal ($name, 'controldatapoint', $statedatapoint); + my $controldatapoint = AttrVal ($name, 'controldatapoint', ''); + my ($da, $dc) = HMCCU_SplitChnAddr ($hash->{ccuaddr}); - # If attribute statedatapoint is specified, use it + # Attributes controlchannel and statechannel are only valid for HMCCUDEV devices + if ($type eq 'HMCCUDEV') { + $sc = AttrVal ($name, 'statechannel', ''); + $cc = AttrVal ($name, 'controlchannel', ''); + } + else { + $sc = $dc; + $cc = $dc; + } + + # If attribute statedatapoint is specified, use it. Attribute statechannel overrides + # channel specification in statedatapoint if ($statedatapoint ne '') { if ($statedatapoint =~ /^([0-9]+)\.(.+)$/) { - ($sc, $sd) = ($1, $2); + ($sc, $sd) = $sc eq '' ? ($1, $2) : ($sc, $2); } else { $sd = $statedatapoint; - if ($statechannel eq '') { + if ($sc eq '') { # Try to find state channel my $c = HMCCU_FindDatapoint ($hash, $type, -1, $sd, 3); $sc = $c if ($c >= 0); } - else { - $sc = $statechannel; - } } } - # If attribute controldatapoint is specified, use it + # If attribute controldatapoint is specified, use it. Attribute controlchannel overrides + # channel specification in controldatapoint if ($controldatapoint ne '') { if ($controldatapoint =~ /^([0-9]+)\.(.+)$/) { ($cc, $cd) = ($1, $2); } else { $cd = $controldatapoint; - # Try to find control channel - my $c = HMCCU_FindDatapoint ($hash, $type, -1, $cd, 3); - $cc = $c if ($c >= 0); + if ($cc eq '') { + # Try to find control channel + my $c = HMCCU_FindDatapoint ($hash, $type, -1, $cd, 3); + $cc = $c if ($c >= 0); + } } } + # Detect by role, but do not override values defined as attributes if (exists($hash->{hmccu}{role})) { my $ccuRole = $hash->{hmccu}{role}; if ($type eq 'HMCCUCHN') { - my ($da, $dc) = HMCCU_SplitChnAddr ($hash->{ccuaddr}); - $sc = $dc; - $cc = $dc; if (exists($HMCCU_STATECONTROL->{$ccuRole}) && $HMCCU_STATECONTROL->{$ccuRole}{F} & 1) { - $sd = $HMCCU_STATECONTROL->{$ccuRole}{S} if ($HMCCU_STATECONTROL->{$ccuRole}{S} ne ''); - $cd = $HMCCU_STATECONTROL->{$ccuRole}{C} if ($HMCCU_STATECONTROL->{$ccuRole}{C} ne ''); + $sd = $HMCCU_STATECONTROL->{$ccuRole}{S} if ($HMCCU_STATECONTROL->{$ccuRole}{S} ne '' && $sd eq ''); + $cd = $HMCCU_STATECONTROL->{$ccuRole}{C} if ($HMCCU_STATECONTROL->{$ccuRole}{C} ne '' && $cd eq ''); } } elsif ($type eq 'HMCCUDEV') { + my ($rsdCnt, $rcdCnt) = (0, 0); + my ($rsc, $rsd, $rcc, $rcd) = ('', '', '', ''); foreach my $roleDef (split(',', $ccuRole)) { - my ($dc, $role) = split(':', $roleDef); - if (exists($HMCCU_STATECONTROL->{$role}) && $HMCCU_STATECONTROL->{$role}{F} & 2) { - if ($HMCCU_STATECONTROL->{$role}{S} ne '' && $sd eq '') { - $sd = $HMCCU_STATECONTROL->{$role}{S}; - $sc = $dc; + my ($rc, $role) = split(':', $roleDef); + + if (defined($role) && exists($HMCCU_STATECONTROL->{$role}) && $HMCCU_STATECONTROL->{$role}{F} & 2) { + if ($sd eq '' && $HMCCU_STATECONTROL->{$role}{S} ne '') { + if ($sc ne '' && $rc eq $sc) { + $sd = $HMCCU_STATECONTROL->{$role}{S}; + } + else { + $rsc = $rc; + $rsd = $HMCCU_STATECONTROL->{$role}{S}; + $rsdCnt++; + } } - if ($HMCCU_STATECONTROL->{$role}{C} ne '' && $cd eq '') { - $cd = $HMCCU_STATECONTROL->{$role}{C} ; - $cc = $dc; + if ($cd eq '' && $HMCCU_STATECONTROL->{$role}{C} ne '') { + if ($cc ne '' && $rc eq $cc) { + $cd = $HMCCU_STATECONTROL->{$role}{C}; + } + else { + $rcc = $rc; + $rcd = $HMCCU_STATECONTROL->{$role}{C}; + $rcdCnt++; + } } } - last if ($sd ne '' && $cd ne ''); } + + ($sc, $sd) = ($rsc, $rsd) if ($rsdCnt == 1 && $sd eq ''); + ($cc, $cd) = ($rcc, $rcd) if ($rcdCnt == 1 && $cd eq ''); } } - # By default set control channel and datapoint to state channel and datapoint - $cc = $sc if ($cc eq ''); - $cd = $sd if ($cd eq ''); + $cc = $sc if ($cc eq '' && $sc ne ''); + $sc = $cc if ($sc eq '' && $cc ne ''); + $cd = $sd if ($cd eq '' && $sd ne ''); + $sd = $cd if ($sd eq '' && $cd ne ''); $hash->{hmccu}{state}{dpt} = $sd; $hash->{hmccu}{state}{chn} = $sc; $hash->{hmccu}{control}{dpt} = $cd; @@ -7788,8 +7750,7 @@ sub HMCCU_GetDatapoint ($@) my $readingformat = HMCCU_GetAttrReadingFormat ($cl_hash, $io_hash); my $substitute = HMCCU_GetAttrSubstitute ($cl_hash, $io_hash); - my ($statechn, $statedpt, $controlchn, $controldpt) = HMCCU_GetSpecialDatapoints ( - $cl_hash, '', 'STATE', '', ''); + my ($statechn, $statedpt, $controlchn, $controldpt) = HMCCU_GetSpecialDatapoints ($cl_hash); my $ccuget = HMCCU_GetAttribute ($io_hash, $cl_hash, 'ccuget', 'Value'); my $ccureqtimeout = AttrVal ($io_hash->{NAME}, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST); @@ -7829,8 +7790,9 @@ 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. +# Parameter params is a hash reference. Keys are parameter names. +# Parameter address must be a device or a channel address. +# If no paramSet is specified, VALUES is used by default. ###################################################################### sub HMCCU_SetMultipleParameters ($$$;$) @@ -7839,14 +7801,14 @@ sub HMCCU_SetMultipleParameters ($$$;$) $paramSet = 'VALUES' if (!defined($paramSet)); my ($add, $chn) = HMCCU_SplitChnAddr ($address); - return -1 if (!defined ($chn)); + return -1 if ($paramSet eq 'VALUES' && !defined ($chn)); foreach my $p (sort keys %$params) { return -8 if ($paramSet eq 'VALUES' && !HMCCU_IsValidDatapoint ($clHash, $clHash->{ccutype}, $chn, $p, 2)); return -8 if ($paramSet eq 'MASTER' && !HMCCU_IsValidParameter ($clHash, $address, $paramSet, $p)); $params->{$p} = HMCCU_ScaleValue ($clHash, $chn, $p, $params->{$p}, 1); } - + return HMCCU_RPCRequest ($clHash, "putParamset", $address, $paramSet, $params); } @@ -8136,7 +8098,7 @@ sub HMCCU_ScaleValue ($$$$$) } if ($dpt eq 'LEVEL') { - return ($mode == 0) ? min($value,100.0)/100.0 : min($value,1.0)*100.0; + return ($mode == 0) ? HMCCU_Min($value,100.0)/100.0 : HMCCU_Min($value,1.0)*100.0; } elsif ($dpt =~ /^P[0-9]_ENDTIME/) { if ($mode == 0) { @@ -8271,44 +8233,44 @@ sub HMCCU_SetVariable ($$$$$) sub HMCCU_GetUpdate ($$$) { - my ($cl_hash, $addr, $ccuget) = @_; - my $name = $cl_hash->{NAME}; - my $type = $cl_hash->{TYPE}; + my ($clHash, $addr, $ccuget) = @_; + my $name = $clHash->{NAME}; + my $type = $clHash->{TYPE}; my $fnc = "GetUpdate"; my $disable = AttrVal ($name, 'disable', 0); return 1 if ($disable == 1); - my $hmccu_hash = HMCCU_GetHash ($cl_hash); - return -3 if (!defined ($hmccu_hash)); - return -4 if ($type ne 'HMCCU' && $cl_hash->{ccudevstate} eq 'deleted'); + my $ioHash = HMCCU_GetHash ($clHash); + return -3 if (!defined ($ioHash)); + return -4 if ($type ne 'HMCCU' && $clHash->{ccudevstate} eq 'deleted'); my $nam = ''; my $list = ''; my $script = ''; - $ccuget = HMCCU_GetAttribute ($hmccu_hash, $cl_hash, 'ccuget', 'Value') if ($ccuget eq 'Attr'); + $ccuget = HMCCU_GetAttribute ($ioHash, $clHash, 'ccuget', 'Value') if ($ccuget eq 'Attr'); - if (HMCCU_IsValidChannel ($hmccu_hash, $addr, $HMCCU_FL_ADDRESS)) { - $nam = HMCCU_GetChannelName ($hmccu_hash, $addr, ''); + if (HMCCU_IsValidChannel ($ioHash, $addr, $HMCCU_FL_ADDRESS)) { + $nam = HMCCU_GetChannelName ($ioHash, $addr, ''); return -1 if ($nam eq ''); my ($stadd, $stchn) = split (':', $addr); - my $stnam = HMCCU_GetChannelName ($hmccu_hash, "$stadd:0", ''); + my $stnam = HMCCU_GetChannelName ($ioHash, "$stadd:0", ''); $list = $stnam eq '' ? $nam : $stnam . "," . $nam; $script = "!GetDatapointsByChannel"; } - elsif (HMCCU_IsValidDevice ($hmccu_hash, $addr, $HMCCU_FL_ADDRESS)) { - $nam = HMCCU_GetDeviceName ($hmccu_hash, $addr, ''); + elsif (HMCCU_IsValidDevice ($ioHash, $addr, $HMCCU_FL_ADDRESS)) { + $nam = HMCCU_GetDeviceName ($ioHash, $addr, ''); return -1 if ($nam eq ''); - $list = $nam if ($cl_hash->{ccuif} ne 'fhem'); + $list = $nam if ($clHash->{ccuif} ne 'fhem'); $script = "!GetDatapointsByDevice"; # Consider members of group device if ($type eq 'HMCCUDEV' && - ($cl_hash->{ccuif} eq 'VirtualDevices' || $cl_hash->{ccuif} eq 'fhem') && - exists ($cl_hash->{ccugroup})) { - foreach my $gd (split (",", $cl_hash->{ccugroup})) { - $nam = HMCCU_GetDeviceName ($hmccu_hash, $gd, ''); + (($clHash->{ccuif} eq 'VirtualDevices' && HMCCU_IsFlag ($ioHash, 'updGroupMembers'))|| + $clHash->{ccuif} eq 'fhem') && exists($clHash->{ccugroup})) { + foreach my $gd (split (",", $clHash->{ccugroup})) { + $nam = HMCCU_GetDeviceName ($ioHash, $gd, ''); $list .= ','.$nam if ($nam ne ''); } } @@ -8317,59 +8279,21 @@ sub HMCCU_GetUpdate ($$$) return -1; } - if (HMCCU_IsFlag ($hmccu_hash->{NAME}, 'nonBlocking')) { - HMCCU_HMScriptExt ($hmccu_hash, $script, { list => $list, ccuget => $ccuget }, + if (HMCCU_IsFlag ($ioHash->{NAME}, 'nonBlocking')) { + HMCCU_HMScriptExt ($ioHash, $script, { list => $list, ccuget => $ccuget }, \&HMCCU_UpdateCB, undef); return 1; } else { - my $response = HMCCU_HMScriptExt ($hmccu_hash, $script, + my $response = HMCCU_HMScriptExt ($ioHash, $script, { list => $list, ccuget => $ccuget }, undef, undef); - HMCCU_Trace ($cl_hash, 2, $fnc, "Addr=$addr Name=$nam Script=$script
". + HMCCU_Trace ($clHash, 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); + HMCCU_UpdateCB ({ ioHash => $ioHash }, 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 -# if ($cl_hash->{ccutype} ne 'n/a') { -# foreach my $da (split (",", $cl_hash->{ccugroup})) { -# foreach my $cn (keys %{$events{$da}}) { -# foreach my $dp (keys %{$events{$da}{$cn}}) { -# if (defined ($events{$da}{$cn}{$dp})) { -# $events{$cl_hash->{ccuaddr}}{$cn}{$dp} = $events{$da}{$cn}{$dp} -# } -# } -# } -# } -# } -# } - -# HMCCU_UpdateMultipleDevices ($hmccu_hash, \%events); return 1; } @@ -8420,7 +8344,7 @@ sub HMCCU_UpdateCB ($$$) my $c_ok = HMCCU_UpdateMultipleDevices ($hash, \%events); my $c_err = 0; - $c_err = max($param->{devCount}-$c_ok, 0) if (exists ($param->{devCount})); + $c_err = HMCCU_Max($param->{devCount}-$c_ok, 0) if (exists ($param->{devCount})); HMCCU_Log ($hash, 2, "Update success=$c_ok failed=$c_err") if ($logcount); } @@ -8857,6 +8781,28 @@ sub HMCCU_GetTimeSpec ($) return ($s-$cs); } +###################################################################### +# Get minimum of 2 values +###################################################################### + +sub HMCCU_Min ($$) +{ + my ($a, $b) = @_; + + return $a < $b ? $a : $b; +} + +###################################################################### +# Get maximum of 2 values +###################################################################### + +sub HMCCU_Max ($$) +{ + my ($a, $b) = @_; + + return $a > $b ? $a : $b; +} + ###################################################################### # Build ReGa or RPC client URL # Parameter backend specifies type of URL, 'rega' or name or port of @@ -9991,7 +9937,7 @@ sub HMCCU_CCURPC_ListDevicesCB ($$) iface_conn_n = interface connection state (1=connected, 0=disconnected)
iface_ducy_n = duty cycle of interface (0-100)
-
  • get <name> exportdefaults <filename> [csv] [all]
    +
  • get <name> exportdefaults <filename> [all]
    Export default attributes into file. If option all is specified, also defaults imported by customer will be exported.

  • @@ -10084,11 +10030,6 @@ sub HMCCU_CCURPC_ListDevicesCB ($$) Set global reading format. This format is the default for all readings except readings of virtual device groups.
    -
  • ccudef-readingname <old-readingname-expr>:[+]<new-readingname> - [;...]
    - 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).
    @@ -10121,7 +10062,8 @@ sub HMCCU_CCURPC_ListDevicesCB ($$) procrpc - Use external RPC server provided by module HMCCPRPCPROC. During first RPC server start HMCCU will create a HMCCURPCPROC device for each interface confiugured in attribute 'rpcinterface'
    - reconnect - Automatically reconnect to CCU when events timeout occurred. + reconnect - Automatically reconnect to CCU when events timeout occurred.
    + updGroupMembers - Update readings of group members in virtual devices.

  • ccuget {State | Value}
    Set read access method for CCU channel datapoints. Method 'State' is slower than diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm index 5aaa24a62..6d720bfd3 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm @@ -4,7 +4,7 @@ # # $Id: 88_HMCCUCHN.pm 18552 2019-02-10 11:52:28Z zap $ # -# Version 4.4.014 +# Version 4.4.016 # # (c) 2020 zap (zap01 t-online de) # @@ -66,17 +66,16 @@ sub HMCCUCHN_Define ($@) my ($hash, $a, $h) = @_; my $name = $hash->{NAME}; - my $usage = "Usage: define $name HMCCUCHN {device} ['readonly'] ['noDefaults'] [iodev={iodevname}]"; + my $usage = "Usage: define $name HMCCUCHN {device} ['readonly'] ['noDefaults'|'defaults'] [iodev={iodevname}]"; return $usage if (@$a < 3); my $devname = shift @$a; my $devtype = shift @$a; my $devspec = shift @$a; - - my $ioHash = undef; + my $ioHash; my $existDev = HMCCU_ExistsClientDevice ($devspec, $devtype); - return "FHEM device $existDev for CCU device $devspec already exists" if (defined($existDev)); + return "FHEM device $existDev for CCU device $devspec already exists" if ($existDev ne ''); # Store some definitions for delayed initialization $hash->{hmccu}{devspec} = $devspec; @@ -87,19 +86,18 @@ sub HMCCUCHN_Define ($@) # Parse optional command line parameters my $n = 0; - my $arg = shift @$a; - while (defined ($arg)) { + while (my $arg = shift @$a) { return $usage if ($n == 3); if ($arg eq 'readonly') { $hash->{readonly} = "yes"; } - elsif ($arg ne 'noDefaults' && $init_done) { $hash->{hmccu}{nodefaults} = 1; } + elsif (lc($arg) eq 'nodefaults' && $init_done) { $hash->{hmccu}{nodefaults} = 1; } + elsif ($arg eq 'defaults' && $init_done) { $hash->{hmccu}{nodefaults} = 0; } else { return $usage; } $n++; - $arg = shift @$a; } # IO device can be set by command line parameter iodev, otherwise try to detect IO device if (exists ($h->{iodev})) { - return "Specified IO Device ".$h->{iodev}." does not exist" if (!exists ($defs{$h->{iodev}})); + return "Specified IO Device ".$h->{iodev}." does not exist" if (!exists($defs{$h->{iodev}})); return "Specified IO Device ".$h->{iodev}." is not a HMCCU device" if ($defs{$h->{iodev}}->{TYPE} ne 'HMCCU'); $ioHash = $defs{$h->{iodev}}; @@ -111,7 +109,7 @@ sub HMCCUCHN_Define ($@) if ($init_done) { # Interactive define command while CCU not ready or no IO device defined - if (!defined ($ioHash)) { + if (!defined($ioHash)) { my ($ccuactive, $ccuinactive) = HMCCU_IODeviceStates (); if ($ccuinactive > 0) { return "CCU and/or IO device not ready. Please try again later"; @@ -125,7 +123,6 @@ sub HMCCUCHN_Define ($@) # CCU not ready during FHEM start if (!defined ($ioHash) || $ioHash->{ccustate} ne 'active') { Log3 $name, 2, "HMCCUCHN: [$devname] Cannot detect IO device, maybe CCU not ready. Trying later ..."; -# readingsSingleUpdate ($hash, "state", "Pending", 1); $hash->{ccudevstate} = 'pending'; return undef; } @@ -171,7 +168,7 @@ sub HMCCUCHN_InitDevice ($$) HMCCU_AddDevice ($ioHash, $di, $da, $devHash->{NAME}); HMCCU_UpdateDevice ($ioHash, $devHash); HMCCU_UpdateDeviceRoles ($ioHash, $devHash); - if (!exists($devHash->{hmccu}{nodefaults})) { + if (!exists($devHash->{hmccu}{nodefaults}) || $devHash->{hmccu}{nodefaults} == 0) { if (!HMCCU_SetDefaultAttributes ($devHash)) { HMCCU_SetDefaults ($devHash); } @@ -193,6 +190,8 @@ sub HMCCUCHN_Undef ($$) if (defined($hash->{IODev})) { HMCCU_RemoveDevice ($hash->{IODev}, $hash->{ccuif}, $hash->{ccuaddr}, $hash->{NAME}); } + + return undef; } ###################################################################### @@ -270,7 +269,7 @@ sub HMCCUCHN_Set ($@) my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); # Get state and control datapoints - my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', '', '', ''); + my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash); # Get additional commands (including state commands) my $roleCmds = HMCCU_GetSpecialCommands ($hash, $cc); @@ -354,7 +353,7 @@ sub HMCCUCHN_Set ($@) return HMCCU_SetError ($hash, $usage) if (scalar (keys %dpval) < 1); $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval); - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } elsif ($opt eq 'toggle') { return HMCCU_SetError ($hash, -15) if ($stateVals eq ''); @@ -385,7 +384,7 @@ sub HMCCUCHN_Set ($@) $rc = HMCCU_SetMultipleDatapoints ($hash, { "001.$ccuif.$ccuaddr.$cd" => $stateCmds{$newState} } ); - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } elsif (defined($roleCmds) && exists($roleCmds->{$opt})) { my $value; @@ -472,11 +471,11 @@ sub HMCCUCHN_Set ($@) if (scalar(keys %dpval) > 0) { $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval); - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } if (scalar(keys %cfval) > 0) { ($rc, $result) = HMCCU_SetMultipleParameters ($hash, $ccuaddr, $h, 'MASTER'); - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } } elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') { @@ -502,7 +501,7 @@ sub HMCCUCHN_Set ($@) "001.$ccuif.$ccuaddr.ON_TIME" => $timespec, "002.$ccuif.$ccuaddr.$cd" => $stateCmds{"on"} }); - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } elsif ($opt eq 'clear') { my $rnexp = shift @$a; @@ -541,7 +540,7 @@ sub HMCCUCHN_Set ($@) } if ($paramset eq 'VALUES' || $paramset eq 'MASTER') { - ($rc, $result) = HMCCU_SetMultipleParameters ($hash, $ccuaddr, $h, $paramset); + ($rc, $result) = HMCCU_SetMultipleParameters ($hash, $ccuobj, $h, $paramset); } else { if (exists($defs{$receiver}) && defined($defs{$receiver}->{TYPE})) { @@ -571,7 +570,7 @@ sub HMCCUCHN_Set ($@) ($rc, $result) = HMCCU_RPCRequest ($hash, "putParamset", $ccuaddr, $receiver, $h); } - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } elsif ($opt eq 'defaults') { $rc = HMCCU_SetDefaultAttributes ($hash); @@ -581,12 +580,11 @@ sub HMCCUCHN_Set ($@) else { my $retmsg = "clear defaults:noArg"; if ($hash->{readonly} ne 'yes') { - $retmsg .= " config"; - $retmsg .= ':'.join(',', @states) if (scalar(@states) > 0); - $retmsg .= " datapoint".$cmdList; + $retmsg .= " config datapoint".$cmdList; +# $retmsg .= ':'.join(',', @states) if (scalar(@states) > 0); $retmsg .= ' toggle:noArg' if (scalar(@states) > 0); $retmsg .= " on-for-timer on-till" - if ($sc ne '' && HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "ON_TIME", 2)); + if ($cc ne '' && HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "ON_TIME", 2)); } return AttrTemplate_Set ($hash, $retmsg, $name, $opt, @$a); } @@ -656,8 +654,9 @@ sub HMCCUCHN_Get ($@) elsif ($opt eq 'deviceinfo') { my ($a, $c) = HMCCU_SplitChnAddr ($ccuaddr); $result = HMCCU_GetDeviceInfo ($hash, $a); - return HMCCU_SetError ($hash, -2) if ($result eq ''); - return HMCCU_FormatDeviceInfo ($result); + return HMCCU_SetError ($hash, -2) if ($result eq ''); + my $devInfo = HMCCU_FormatDeviceInfo ($result); + return $devInfo; } elsif ($opt =~ /^(config|values)$/) { my %parSets = ('config' => 'MASTER,LINK', 'values' => 'VALUES'); @@ -759,7 +758,7 @@ sub HMCCUCHN_Get ($@) Define

      define <name> HMCCUCHN {<channel-name> | <channel-address>} - [readonly] [noDefaults] [iodev=<iodev-name>] + [readonly] [defaults|noDefaults] [iodev=<iodev-name>]

      If option 'readonly' is specified no set command will be available. With option 'noDefaults' no default attributes will be set during interactive device definition.
      diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm index e73834226..06b36e4b4 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm @@ -4,7 +4,7 @@ # # $Id: 88_HMCCUDEV.pm 18552 2019-02-10 11:52:28Z zap $ # -# Version 4.4.008 +# Version 4.4.016 # # (c) 2020 zap (zap01 t-online de) # @@ -52,7 +52,7 @@ sub HMCCUDEV_Initialize ($) "ccureadingname:textField-long ". "ccuget:State,Value ccuscaleval ccuSetOnChange ccuverify:0,1,2 disable:0,1 ". "hmstatevals:textField-long statevals substexcl substitute:textField-long statechannel ". - "statedatapoint controldatapoint stripnumber peer:textField-long ". + "controlchannel statedatapoint controldatapoint stripnumber peer:textField-long ". $readingFnAttributes; } @@ -65,8 +65,8 @@ sub HMCCUDEV_Define ($@) my ($hash, $a, $h) = @_; my $name = $hash->{NAME}; - my $usage = "Usage: define $name HMCCUDEV {device|'virtual'} [state-channel] ". - "['readonly'] ['noDefaults'] [iodev={iodev-name}] [address={virtual-device-no}]". + my $usage = "Usage: define $name HMCCUDEV {device|'virtual'} [control-channel] ". + "['readonly'] ['noDefaults'|'defaults'] [iodev={iodev-name}] [address={virtual-device-no}]". "[{groupexp=regexp|group={device|channel}[,...]]"; return $usage if (scalar (@$a) < 3); @@ -111,8 +111,9 @@ sub HMCCUDEV_Define ($@) # Parse optional command line parameters foreach my $arg (@$a) { if ($arg eq 'readonly') { $hash->{readonly} = 'yes'; } - elsif ($arg ne 'noDefaults' && $init_done) { $hash->{hmccu}{nodefaults} = 1; } - elsif ($arg =~ /^[0-9]+$/) { $attr{$name}{statechannel} = $arg; } + elsif (lc($arg) eq 'nodefaults' && $init_done) { $hash->{hmccu}{nodefaults} = 1; } + elsif ($arg eq 'defaults' && $init_done) { $hash->{hmccu}{nodefaults} = 0; } + elsif ($arg =~ /^[0-9]+$/) { $attr{$name}{controlchannel} = $arg; } else { return $usage; } } @@ -177,7 +178,7 @@ sub HMCCUDEV_InitDevice ($$) my $devspec = $devHash->{hmccu}{devspec}; my $gdcount = 0; my $gdname = $devspec; - + if ($devspec eq 'virtual') { my $no = 0; if (exists ($devHash->{hmccu}{address})) { @@ -196,9 +197,14 @@ sub HMCCUDEV_InitDevice ($$) return 7 if ($no == 0); $devHash->{DEF} .= " address=$no"; } + + # Inform HMCCU device about client device + return 2 if (!HMCCU_AssignIODevice ($devHash, $ioHash->{NAME}, undef)); + $devHash->{ccuif} = 'fhem'; $devHash->{ccuaddr} = sprintf ("VIR%07d", $no); $devHash->{ccuname} = $name; + $devHash->{ccudevstate} = 'active'; } else { return 1 if (!HMCCU_IsValidDevice ($ioHash, $devspec, 7)); @@ -207,10 +213,14 @@ sub HMCCUDEV_InitDevice ($$) return 1 if (!defined ($da)); $gdname = $dn; + # Inform HMCCU device about client device + return 2 if (!HMCCU_AssignIODevice ($devHash, $ioHash->{NAME}, undef)); + $devHash->{ccuif} = $di; $devHash->{ccuaddr} = $da; $devHash->{ccuname} = $dn; $devHash->{ccutype} = $dt; + $devHash->{ccudevstate} = 'active'; $devHash->{hmccu}{channels} = $dc; if ($init_done) { @@ -218,7 +228,7 @@ sub HMCCUDEV_InitDevice ($$) HMCCU_AddDevice ($ioHash, $di, $da, $devHash->{NAME}); HMCCU_UpdateDevice ($ioHash, $devHash); HMCCU_UpdateDeviceRoles ($ioHash, $devHash); - if (!exists($devHash->{hmccu}{nodefaults})) { + if (!exists($devHash->{hmccu}{nodefaults}) || $devHash->{hmccu}{nodefaults} == 0) { if (!HMCCU_SetDefaultAttributes ($devHash)) { HMCCU_SetDefaults ($devHash); } @@ -288,11 +298,6 @@ sub HMCCUDEV_InitDevice ($$) } } - # Inform HMCCU device about client device - return 2 if (!HMCCU_AssignIODevice ($devHash, $ioHash->{NAME}, undef)); - - $devHash->{ccudevstate} = 'active'; - return 0; } @@ -310,6 +315,8 @@ sub HMCCUDEV_Undef ($$) HMCCU_DeleteDevice ($hash->{IODev}); } } + + return undef; } ###################################################################### @@ -389,28 +396,41 @@ sub HMCCUDEV_Set ($@) my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); # Get state and control datapoints - my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', '', '', ''); - + my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash); + # Get additional commands (including state commands) my $roleCmds = HMCCU_GetSpecialCommands ($hash, $cc); - + + my %pset = ('V' => 'VALUES', 'M' => 'MASTER', 'D' => 'MASTER'); my $cmdList = ''; + my %valLookup; foreach my $cmd (keys %$roleCmds) { $cmdList .= " $cmd"; my @setList = split (/\s+/, $roleCmds->{$cmd}); foreach my $set (@setList) { my ($ps, $dpt, $par) = split(/:/, $set); - if ($par !~ /^\?/) { - my @argList = split (',', $par); - $cmdList .= scalar(@argList) > 1 ? ":$par" : ":noArg"; + my @argList = (); + if ($par =~ /^#/) { + my $paramDef = HMCCU_GetParamDef ($ioHash, $ccuaddr, $pset{$ps}, $dpt); + if (defined($paramDef)) { + if ($paramDef->{TYPE} eq 'ENUM' && defined($paramDef->{VALUE_LIST})) { + $par = $paramDef->{VALUE_LIST}; + $par =~ s/[ ]+/-/g; + @argList = split (',', $par); + while (my ($i, $e) = each(@argList)) { $valLookup{$pset{$ps}}{$dpt}{$e} = $i; } + } + } } + elsif ($par !~ /^\?/) { + @argList = split (',', $par); + } + $cmdList .= scalar(@argList) > 1 ? ":$par" : ":noArg"; } } # Get state values related to control command and datapoint my $stateVals = HMCCU_GetStateValues ($hash, $cd, $cc); my @stateCmdList = split (/[:,]/, $stateVals); - my %stateCmds = @stateCmdList; my @states = keys %stateCmds; @@ -449,8 +469,8 @@ sub HMCCUDEV_Set ($@) return HMCCU_SetError ($hash, -7) if ($chn >= $hash->{hmccu}{channels}); } else { - return HMCCU_SetError ($hash, -11) if ($sc eq ''); - $objname = $sc.'.'.$objname; + return HMCCU_SetError ($hash, -11) if ($cc eq ''); + $objname = $cc.'.'.$objname; } my $no = sprintf ("%03d", $i); @@ -461,38 +481,12 @@ sub HMCCUDEV_Set ($@) return HMCCU_SetError ($hash, $usage) if (scalar (keys %dpval) < 1); $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval); - return HMCCU_SetError ($hash, min(0, $rc)); - } - elsif ($opt eq 'control') { - return HMCCU_SetError ($hash, -12) if ($cc eq ''); - return HMCCU_SetError ($hash, -14) if ($cd eq ''); - return HMCCU_SetError ($hash, -7) if ($cc >= $hash->{hmccu}{channels}); - - my $objvalue = shift @$a; - return HMCCU_SetError ($hash, "Usage: set $name control {value}") if (!defined ($objvalue)); - - $objvalue =~ s/\\_/%20/g; - - $rc = HMCCU_SetMultipleDatapoints ($hash, - { "001.$ccuif.$ccuaddr:$cc.$cd" => HMCCU_Substitute ($objvalue, $stateVals, 1, undef, '') } - ); - return HMCCU_SetError ($hash, min(0, $rc)); - } - elsif (exists($stateCmds{$opt})) { - return HMCCU_SetError ($hash, -12) if ($cc eq ''); - return HMCCU_SetError ($hash, -14) if ($cd eq ''); - return HMCCU_SetError ($hash, -8) - if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $cd, 2)); - - $rc = HMCCU_SetMultipleDatapoints ($hash, - { "001.$ccuif.$ccuaddr:$cc.$cd" => $stateCmds{$opt} } - ); - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } elsif ($opt eq 'toggle') { - return HMCCU_SetError ($hash, -15) if ($stateVals eq ''); return HMCCU_SetError ($hash, -12) if ($cc eq ''); return HMCCU_SetError ($hash, -14) if ($cd eq ''); + return HMCCU_SetError ($hash, -15) if ($stateVals eq ''); return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, $cd, 2)); @@ -518,76 +512,113 @@ sub HMCCUDEV_Set ($@) $rc = HMCCU_SetMultipleDatapoints ($hash, { "001.$ccuif.$ccuaddr:$cc.$cd" => $stateCmds{$newState} } ); - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } - elsif ($opt eq 'pct' || $opt eq 'up' || $opt eq 'down') { - return HMCCU_SetError ($hash, -11) if ($sc eq '' && $cc eq ''); - my $chn; - if (HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "LEVEL", 2)) { - $chn = $cc; - } - elsif (HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "LEVEL", 2)) { - $chn = $sc; - } - else { - return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype") - } + elsif (defined($roleCmds) && exists($roleCmds->{$opt})) { + return HMCCU_SetError ($hash, -12) if ($cc eq ''); + return HMCCU_SetError ($hash, -14) if ($cd eq ''); + + my $value; + my %dpval; + my %cfval; - if ($opt eq 'pct') { - my $objname = ''; - my $objvalue = shift @$a; - return HMCCU_SetError ($hash, "Usage: set $name pct {value} [{ontime} [{ramptime}]]") - if (!defined ($objvalue)); + my @setList = split (/\s+/, $roleCmds->{$opt}); + my $i = 0; + foreach my $set (@setList) { + my ($ps, $dpt, $par) = split(/:/, $set); - my $timespec = shift @$a; - my $ramptime = shift @$a; - my %dpval; - - # Set on time - if (defined ($timespec)) { - return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type $ccutype") - if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $chn, "ON_TIME", 2)); - - if ($timespec =~ /^[0-9]{2}:[0-9]{2}/) { - $timespec = HMCCU_GetTimeSpec ($timespec); - return HMCCU_SetError ($hash, "Wrong time format. Use HH:MM[:SS]") if ($timespec < 0); - } - $dpval{"001.$ccuif.$ccuaddr:$chn.ON_TIME"} = $timespec if ($timespec > 0); + return HMCCU_SetError ($hash, "Syntax error in definition of command $opt") + if (!defined($par)); + if (!HMCCU_IsValidParameter ($hash, $ccuaddr, $pset{$ps}, $dpt)) { + HMCCU_Trace ($hash, 2, "Set", "Invalid parameter $ps $dpt"); + return HMCCU_SetError ($hash, -8); } - # Set ramp time - $dpval{"002.$ccuif.$ccuaddr:$chn.RAMP_TIME"} = $ramptime if (defined ($ramptime)); - - # Set level - $dpval{"003.$ccuif.$ccuaddr:$chn.LEVEL"} = $objvalue; - $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval); - } - else { - my $delta = shift @$a; - $delta = 10 if (!defined ($delta)); - $delta = -$delta if ($opt eq 'down'); - my $objname = "$ccuif.$ccuaddr:$chn.LEVEL"; - - ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 1); - Log3 $name, 2, "HMCCU: set $opt: GetDatapoint returned $rc, $result" - if ($ccuflags =~ /trace/); - return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0); - - # Set level - my $objvalue = min(max($result+$delta,0),100); - $rc = HMCCU_SetMultipleDatapoints ($hash, { "001.$objname" => $objvalue }); - } + # Check if parameter is required + if ($par =~ /^\?(.+)$/) { + $par = $1; + # Consider default value for parameter + my ($parName, $parDef) = split ('=', $par); + $value = shift @$a; + if (!defined($value) && defined($parDef)) { + if ($parDef =~ /^[+-][0-9]+$/) { + return HMCCU_SetError ($hash, "Current value of $cc.$dpt not available") + if (!defined($hash->{hmccu}{dp}{"$cc.$dpt"}{$pset{$ps}}{SVAL})); + $value = $hash->{hmccu}{dp}{"$cc.$dpt"}{$pset{$ps}}{SVAL}+int($parDef); + } + else { + $value = $parDef; + } + } - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, "Missing parameter $parName") + if (!defined($value)); + } + else { + if (exists($valLookup{$ps}{$dpt})) { + return HMCCU_SetError ($hash, "Illegal value $par. Use one of ".join(',', keys %{$valLookup{$ps}{$dpt}})) + if (!exists($valLookup{$ps}{$dpt}{$par})); + $value = $valLookup{$ps}{$dpt}{$par}; + } + else { + $value = $par; + } + } + + if ($opt eq 'pct' || $opt eq 'level') { + my $timespec = shift @$a; + my $ramptime = shift @$a; + + # Set on time + if (defined ($timespec)) { + return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type $ccutype") + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "ON_TIME", 2)); + if ($timespec =~ /^[0-9]{2}:[0-9]{2}/) { + $timespec = HMCCU_GetTimeSpec ($timespec); + return HMCCU_SetError ($hash, "Wrong time format. Use HH:MM[:SS]") if ($timespec < 0); + } + $dpval{"001.$ccuif.$ccuaddr.$cc.ON_TIME"} = $timespec if ($timespec > 0); + } + + # Set ramp time + if (defined($ramptime)) { + return HMCCU_SetError ($hash, "Can't find RAMP_TIME datapoint for device type $ccutype") + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "RAMP_TIME", 2)); + $dpval{"002.$ccuif.$ccuaddr.$cc.RAMP_TIME"} = $ramptime if (defined ($ramptime)); + } + + $dpval{"003.$ccuif.$ccuaddr.$cc.$dpt"} = $value; + last; + } + else { + if ($ps eq 'V') { + my $no = sprintf ("%03d", $i); + $dpval{"$i.$ccuif.$ccuaddr.$cc.$dpt"} = $value; + $i++; + } + else { + $cfval{$dpt} = $value; + } + } + } + + if (scalar(keys %dpval) > 0) { + $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); + } + if (scalar(keys %cfval) > 0) { + ($rc, $result) = HMCCU_SetMultipleParameters ($hash, $ccuaddr, $h, 'MASTER'); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); + } } elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') { return HMCCU_SetError ($hash, -15) if ($stateVals eq '' || !exists($hash->{hmccu}{statevals})); return HMCCU_SetError ($hash, "No state value for 'on' defined") if ("on" !~ /($hash->{hmccu}{statevals})/); - return HMCCU_SetError ($hash, -11) if ($sc eq ''); - return HMCCU_SetError ($hash, -13) if ($sd eq ''); + return HMCCU_SetError ($hash, -12) if ($cc eq ''); + return HMCCU_SetError ($hash, -14) if ($cd eq ''); return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type") - if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "ON_TIME", 2)); + if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "ON_TIME", 2)); my $timespec = shift @$a; return HMCCU_SetError ($hash, "Usage: set $name $opt {ontime-spec}") @@ -599,66 +630,85 @@ sub HMCCUDEV_Set ($@) } $rc = HMCCU_SetMultipleDatapoints ($hash, { - "001.$ccuif.$ccuaddr:$sc.ON_TIME" => $timespec, - "002.$ccuif.$ccuaddr:$sc.$sd" => HMCCU_Substitute ("on", $stateVals, 1, undef, '') + "001.$ccuif.$ccuaddr:$cc.ON_TIME" => $timespec, + "002.$ccuif.$ccuaddr:$cc.$cd" => HMCCU_Substitute ("on", $stateVals, 1, undef, '') }); - return HMCCU_SetError ($hash, min(0, $rc)); + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } elsif ($opt eq 'clear') { my $rnexp = shift @$a; HMCCU_DeleteReadings ($hash, $rnexp); return HMCCU_SetState ($hash, "OK"); } - elsif ($opt eq 'config') { - return HMCCU_SetError ($hash, "Usage: set $name config [{channel-number}] {parameter}={value} [...]") - if ((scalar keys %{$h}) < 1); - my $objname = $ccuaddr; + elsif ($opt =~ /^(config|values)$/) { + my %parSets = ('config' => 'MASTER', 'values' => 'VALUES'); + my $paramset = $parSets{$opt}; + my $receiver = ''; + my $ccuobj = $ccuaddr; - # Channel number is optional because parameter can be related to device or channel - if ((scalar @$a) > 0 && $$a[0] =~ /^([0-9]{1,2})$/) { - return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{hmccu}{channels}); - $objname .= ':'.$1; - } - - my ($rc, $res) = HMCCU_RPCRequest ($hash, "putParamset", $objname, "MASTER", $h); - return HMCCU_SetError ($hash, min(0, $rc)); - } - elsif ($opt eq 'rpcparameter') { - return HMCCU_SetError ($hash, "Usage: set $name rpcparameter [channel] [MASTER|VALUES|LINK] {parameter}={value} [...]") + return HMCCU_SetError ($hash, "No parameter specified") if ((scalar keys %{$h}) < 1); - my $key; - my $chn; - - while (my $p = shift @$a) { - if (uc($p) =~ /^(MASTER|VALUES|LINK)$/ && !defined ($key)) { - $key = uc($p); + # Channel number is optional because parameter can be related to device or channel + my $p = shift @$a; + if (defined($p)) { + if ($p =~ /^([0-9]{1,2})$/) { + return HMCCU_SetError ($hash, -7) if ($p >= $hash->{hmccu}{channels}); + $ccuobj .= ':'.$p; } - elsif ($p =~ /^([0-9]+)$/ && !defined ($chn)) { - HMCCU_SetError ($hash, -7) if ($p >= $hash->{hmccu}{channels}); - $chn = $p; + else { + $receiver = $p; + $paramset = 'LINK'; } } - $key = 'VALUES' if (!defined ($key)); - return HMCCU_SetError ($hash, -7) if (!defined ($chn) && ($key eq 'VALUES' || $key eq 'LINK')); - - my $addr = defined ($chn) ? "$ccuaddr:$chn" : $ccuaddr; - - if ($key eq 'VALUES') { - ($rc, $result) = HMCCU_SetMultipleParameters ($hash, $addr, $h); + my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $ccuobj, $ccuif); + return HMCCU_SetError ($hash, "Can't get device description") + if (!defined($devDesc)); + return HMCCU_SetError ($hash, "Paramset $paramset not supported by device or channel") + if ($devDesc->{PARAMSETS} !~ /$paramset/); + if (!HMCCU_IsValidParameter ($ioHash, $devDesc, $paramset, $h)) { + my @parList = HMCCU_GetParamDef ($ioHash, $devDesc, $paramset); + return HMCCU_SetError ($hash, "Invalid parameter specified. Valid parameters are ". + join(',', @parList)); } - elsif ($key eq 'MASTER' || $key eq 'LINK') { - ($rc, $result) = HMCCU_RPCRequest ($hash, "putParamset", $addr, $key, $h); + + if ($paramset eq 'VALUES' || $paramset eq 'MASTER') { + ($rc, $result) = HMCCU_SetMultipleParameters ($hash, $ccuobj, $h, $paramset); } else { - return HMCCU_SetError ($hash, "Key must be MASTER, LINK or VALUES"); + if (exists($defs{$receiver}) && defined($defs{$receiver}->{TYPE})) { + my $clHash = $defs{$receiver}; + if ($clHash->{TYPE} eq 'HMCCUDEV') { + my $chnNo = shift @$a; + return HMCCU_SetError ($hash, "Channel number required for link receiver") + if (!defined($chnNo) || $chnNo !~ /^[0-9]{1,2}$/); + $receiver = $clHash->{ccuaddr}.":$chnNo"; + } + elsif ($clHash->{TYPE} eq 'HMCCUCHN') { + $receiver = $clHash->{ccuaddr}; + } + else { + return HMCCU_SetError ($hash, "Receiver $receiver is not a HMCCUCHN or HMCCUDEV device"); + } + } + elsif (!HMCCU_IsChnAddr ($receiver, 0)) { + my ($rcvAdd, $rcvChn) = HMCCU_GetAddress ($ioHash, $receiver, '', ''); + return HMCCU_SetError ($hash, "$receiver is not a valid CCU channel name") + if ($rcvAdd eq '' || $rcvChn eq ''); + $receiver = "$rcvAdd:$rcvChn"; + } + + return HMCCU_SetError ($hash, "$receiver is not a link receiver of $name") + if (!HMCCU_IsValidReceiver ($ioHash, $ccuaddr, $ccuif, $receiver)); + ($rc, $result) = HMCCU_RPCRequest ($hash, "putParamset", $ccuaddr, $receiver, $h); } - - return HMCCU_SetError ($hash, min(0, $rc)); + + return HMCCU_SetError ($hash, HMCCU_Min(0, $rc)); } elsif ($opt eq 'defaults') { - my $rc = HMCCU_SetDefaults ($hash); + $rc = HMCCU_SetDefaultAttributes ($hash, $cc); + $rc = HMCCU_SetDefaults ($hash) if (!$rc); return HMCCU_SetError ($hash, $rc == 0 ? "No default attributes found" : "OK"); } else { @@ -667,12 +717,10 @@ sub HMCCUDEV_Set ($@) if ($hash->{readonly} ne 'yes') { $retmsg .= " datapoint rpcparameter"; if ($sc ne '') { - $retmsg .= " config control"; - $retmsg .= ':'.join(',', @states) if (scalar(@states) > 0); - $retmsg .= $cmdList; + $retmsg .= " config datapoint".$cmdList; $retmsg .= " toggle:noArg" if (scalar(@states) > 0); $retmsg .= " on-for-timer on-till" - if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "ON_TIME", 2)); + if ($cc ne '' && HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $cc, "ON_TIME", 2)); } } return AttrTemplate_Set ($hash, $retmsg, $name, $opt, @$a); @@ -712,7 +760,7 @@ sub HMCCUDEV_Get ($@) my $ccuaddr = $hash->{ccuaddr}; my $ccuif = $hash->{ccuif}; my $ccuflags = AttrVal ($name, 'ccuflags', 'null'); - my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', ''); + my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash); my $result = ''; my $rc; @@ -791,7 +839,9 @@ sub HMCCUDEV_Get ($@) } $result = HMCCU_GetDeviceInfo ($hash, $ccuaddr, $ccuget); return HMCCU_SetError ($hash, -2) if ($result eq ''); - return HMCCU_FormatDeviceInfo ($result); + my $devInfo = HMCCU_FormatDeviceInfo ($result); + $devInfo .= "StateDatapoint = $sc.$sd\nControlDatapoint = $cc.$cd"; + return $devInfo; } elsif ($opt =~ /^(paramset|config|values)$/) { my $par = shift @$a; @@ -909,14 +959,14 @@ sub HMCCUDEV_Get ($@) Define

        - define <name> HMCCUDEV {<device> | 'virtual'} [<statechannel>] - [readonly] [defaults] [{group={device|channel}[,...]|groupexp=regexp] + define <name> HMCCUDEV {<device> | 'virtual'} [<controlchannel>] + [readonly] [defaults|noDefaults] [{group={device|channel}[,...]|groupexp=regexp] [iodev=<iodev-name>]

        If option 'readonly' is specified no set command will be available. With option 'defaults' some default attributes depending on CCU device type will be set. Default attributes are only available for some device types. The option is ignored during FHEM start. - Parameter statechannel corresponds to attribute 'statechannel'.
        + Parameter controlchannel corresponds to attribute 'controlchannel'.
        A HMCCUDEV device supports CCU group devices. The CCU devices or channels related to a group device are specified by using options 'group' or 'groupexp' followed by the names or addresses of the CCU devices or channels. By using 'groupexp' one can specify a regular @@ -1111,6 +1161,9 @@ sub HMCCUDEV_Get ($@)
      • ccuverify {0 | 1 | 2}
        see HMCCUCHN

      • +
      • controlchannel <channel-number>
        + Channel used for setting device states. +

      • controldatapoint <channel-number.datapoint>
        Set channel number and datapoint for device control. see HMCCUCHN @@ -1126,8 +1179,7 @@ sub HMCCUDEV_Get ($@) see HMCCUCHN

      • statechannel <channel-number>
        - Channel for setting device state by devstate command. Deprecated, use attribute - 'statedatapoint' instead. + Channel for getting device state. Deprecated, use attribute 'statedatapoint' instead.

      • statedatapoint [<channel-number>.]<datapoint>
        Set state channel and state datapoint for setting device state by devstate command. diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm index fcc4d214e..cf70f724a 100755 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm @@ -445,7 +445,7 @@ sub HMCCURPCPROC_DelayedShutdown ($) my $hmccu_hash = $hash->{IODev}; my $ifname = $hash->{rpcinterface}; - my $delay = max (AttrVal ("global", "maxShutdownDelay", 10)-2, 0); + my $delay = HMCCU_Max (AttrVal ("global", "maxShutdownDelay", 10)-2, 0); # Shutdown RPC server if (defined ($hmccu_hash) && exists ($hmccu_hash->{hmccu}{interfaces}{$ifname}{manager}) && diff --git a/fhem/contrib/HMCCU/FHEM/HMCCUConf.pm b/fhem/contrib/HMCCU/FHEM/HMCCUConf.pm index c68c84b43..4784c480c 100644 --- a/fhem/contrib/HMCCU/FHEM/HMCCUConf.pm +++ b/fhem/contrib/HMCCU/FHEM/HMCCUConf.pm @@ -205,17 +205,14 @@ use vars qw(%HMCCU_SCRIPTS); %HMCCU_ATTR = ( 'BLIND' => { - 'ccureadingname' => 'LEVEL$:pct', 'webCmd' => 'up:down:stop:pct', 'widgetOverride' => 'pct:slider,0,10,100' }, 'BLIND_VIRTUAL_RECEIVER' => { - 'ccureadingname' => 'LEVEL$:pct', 'webCmd' => 'up:down:stop:pct', 'widgetOverride' => 'pct:slider,0,10,100' }, 'SHUTTER_VIRTUAL_RECEIVER' => { - 'ccureadingname' => 'LEVEL$:pct', 'webCmd' => 'up:down:stop:pct', 'widgetOverride' => 'pct:slider,0,10,100' }, @@ -228,7 +225,6 @@ use vars qw(%HMCCU_SCRIPTS); 'widgetOverride' => 'toggle:uzsuToggle,off,on' }, 'DIMMER' => { - 'ccureadingname' => 'LEVEL$:pct', 'webCmd' => 'pct', 'widgetOverride' => 'pct:slider,0,10,100' }, @@ -238,19 +234,16 @@ use vars qw(%HMCCU_SCRIPTS); 'widgetOverride' => 'pct:slider,0,10,100' }, 'THERMALCONTROL_TRANSMIT' => { - 'ccureadingname' => 'SET_TEMPERATURE$:desired-temp;ACTUAL_TEMPERATURE$:measured-temp', 'cmdIcon' => 'auto:sani_heating_automatic manu:sani_heating_manual boost:sani_heating_boost on:general_an off:general_aus', 'webCmd' => 'desired-temp:auto:manu:boost:on:off', 'widgetOverride' => 'desired-temp:slider,4.5,0.5,30.5,1' }, 'CLIMATECONTROL_RT_TRANSCEIVER' => { - 'ccureadingname' => 'SET_TEMPERATURE$:desired-temp;ACTUAL_TEMPERATURE$:measured-temp', 'cmdIcon' => 'auto:sani_heating_automatic manu:sani_heating_manual boost:sani_heating_boost on:general_an off:general_aus', 'webCmd' => 'desired-temp', 'widgetOverride' => 'desired-temp:slider,4.5,0.5,30.5,1' }, 'HEATING_CLIMATECONTROL_TRANSCEIVER' => { - 'ccureadingname' => 'SET_POINT_TEMPERATURE$:desired-temp;ACTUAL_TEMPERATURE$:measured-temp', 'cmdIcon' => 'auto:sani_heating_automatic manu:sani_heating_manual boost:sani_heating_boost on:general_an off:general_aus', 'webCmd' => 'desired-temp:auto:manu:boost', 'widgetOverride' => 'desired-temp:slider,4.5,0.5,30.5,1'