diff --git a/fhem/contrib/HMCCU/CHANGED b/fhem/contrib/HMCCU/CHANGED index 5b426a013..4ee221978 100644 --- a/fhem/contrib/HMCCU/CHANGED +++ b/fhem/contrib/HMCCU/CHANGED @@ -1,9 +1,10 @@ + - bugfix: 88_HMCCU.pm: Fixed some bux in version 5.0 + - bugfix: 88_HMCCU.pm: Version 5.0 + - bugfix: 88_HMCCU.pm: Release candidate 7 - bugfix: 88_HMCCU.pm: Release candidate 6 - bugfix: 88_HMCCU.pm: Release candidate 5 - bugfix: 88_HMCCU.pm: Release candidate 4 - bugfix: 88_HMCCU.pm: Release candidate 3 - bugfix: 88_HMCCU.pm: Release candidate 2 - bugfix: 88_HMCCU.pm: Fixed bug in set defaults command - - bugfix: 88_HMCCU.pm: Fixed state-/controldatapoint bug during FHEM start - - bugfix: 88_HMCCU.pm: Fixed some bugs. New command set readingFilter - bugfix: 88_HMCCU.pm: Fixed device detection bugs diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCU.pm index 71229f044..372ca1237 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.069 +# Version 5.0 # # Module for communication between FHEM and Homematic CCU2/3. # @@ -32,7 +32,6 @@ no if $] >= 5.017011, warnings => 'experimental::smartmatch'; use strict; use warnings; # use Data::Dumper; -use IO::File; use Encode qw(decode encode); use RPC::XML::Client; use RPC::XML::Server; @@ -58,7 +57,7 @@ my %HMCCU_CUST_CHN_DEFAULTS; my %HMCCU_CUST_DEV_DEFAULTS; # HMCCU version -my $HMCCU_VERSION = '4.4.069'; +my $HMCCU_VERSION = '5.0'; # Timeout for CCU requests (seconds) my $HMCCU_TIMEOUT_REQUEST = 4; @@ -152,6 +151,7 @@ sub HMCCU_Initialize ($); sub HMCCU_Define ($$$); sub HMCCU_InitDevice ($); sub HMCCU_Undef ($$); +sub HMCCU_Renane ($$); sub HMCCU_DelayedShutdown ($); sub HMCCU_Shutdown ($); sub HMCCU_Set ($@); @@ -354,7 +354,7 @@ sub HMCCU_Max ($$); sub HMCCU_MaxHashEntries ($$); sub HMCCU_Min ($$); sub HMCCU_MinMax ($$$); -sub HMCCU_RefToString ($); +sub HMCCU_RefToString ($;$); sub HMCCU_ResolveName ($$); sub HMCCU_TCPConnect ($$;$); sub HMCCU_TCPPing ($$$); @@ -370,6 +370,7 @@ sub HMCCU_Initialize ($) $hash->{DefFn} = 'HMCCU_Define'; $hash->{UndefFn} = 'HMCCU_Undef'; + $hash->{RenameFn} = 'HMCCU_Rename'; $hash->{SetFn} = 'HMCCU_Set'; $hash->{GetFn} = 'HMCCU_Get'; $hash->{ReadFn} = 'HMCCU_Read'; @@ -381,7 +382,7 @@ sub HMCCU_Initialize ($) $hash->{parseParams} = 1; $hash->{AttrList} = 'stripchar stripnumber ccuaggregate:textField-long'. - ' ccudefaults'. + ' ccudefaults createDeviceGroup'. ' ccudef-hmstatevals:textField-long ccudef-substitute:textField-long'. ' ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc'. ' ccudef-stripnumber ccudef-attributes ccuReadingPrefix'. @@ -490,7 +491,7 @@ sub HMCCU_Define ($$$) } if (($hash->{ccustate} ne 'active' || $rc > 0) && !$init_done) { - # Schedule update of CCU assets if CCU is not active during FHEM startup + # Schedule later update of CCU assets if CCU is not active during FHEM startup $hash->{hmccu}{ccu}{delayed} = 1; HMCCU_Log ($hash, 1, 'Scheduling delayed initialization in '.$hash->{hmccu}{ccu}{delay}.' seconds'); InternalTimer (gettimeofday()+$hash->{hmccu}{ccu}{delay}, "HMCCU_InitDevice", $hash); @@ -501,7 +502,9 @@ sub HMCCU_Define ($$$) HMCCU_UpdateReadings ($hash, { 'state' => 'Initialized', 'rpcstate' => 'inactive' }); if ($init_done) { + # Set default attributes $attr{$name}{stateFormat} = 'rpcstate/state'; + $attr{$name}{room} = 'Homematic'; } return undef; @@ -543,7 +546,7 @@ sub HMCCU_InitDevice ($) if ($init_done && $hash->{hmccu}{ccu}{delayed} == 0) { # Force sync with CCU during interactive device definition if ($hash->{hmccu}{ccu}{sync} == 1) { - HMCCU_Log ($hash, 1, 'Reading device config from CCU. This may take a couple of seconds ...'); + HMCCU_LogDisplay ($hash, 1, 'Reading device config from CCU. This may take a couple of seconds ...'); my ($cDev, $cPar, $cLnk) = HMCCU_GetDeviceConfig ($hash); HMCCU_Log ($hash, 2, "Read RPC device configuration: devices/channels=$cDev parametersets=$cPar links=$cLnk"); } @@ -1041,20 +1044,21 @@ sub HMCCU_Notify ($$) # Attribute of client device set or deleted my $refreshAttrList = 'ccucalculate|ccuflags|ccureadingfilter|ccureadingformat|'. 'ccureadingname|ccuReadingPrefix|ccuscaleval|controldatapoint|hmstatevals|'. - 'statedatapoint|statevals|substitute:textField-long|substexcl|stripnumber'; - my $cmdAttrList = 'statechannel|statedatapoint|controlchannel|controldatapoint'; + 'statedatapoint|statevals|substitute|substexcl|stripnumber'; + my $cmdAttrList = 'statechannel|statedatapoint|controlchannel|controldatapoint|statevals'; my ($aCmd, $aDev, $aAtt, $aVal) = split (/\s+/, $event); $aAtt = $aVal if ($aCmd eq 'DELETEATTR'); if (defined($aAtt)) { my $clHash = $defs{$aDev}; - if (defined($clHash->{TYPE}) && ($clHash->{TYPE} eq 'HMCCUCHN' || $clHash->{TYPE} eq 'HMCCUDEV')) { + if (defined($clHash->{TYPE}) && ($clHash->{TYPE} eq 'HMCCUCHN' || $clHash->{TYPE} eq 'HMCCUDEV') && + (!defined($clHash->{hmccu}{semDefaults}) || $clHash->{hmccu}{semDefaults} == 0)) { if ($aAtt =~ /^($cmdAttrList)$/) { - my ($sc, $sd, $cc, $cd, $sdCnt, $cdCnt) = HMCCU_GetSCDatapoints ($hash); - if ($cdCnt < 2) { + my ($cc, $cd) = HMCCU_ControlDatapoint ($clHash); + # if ($cc ne '' && $cd ne '') { HMCCU_UpdateRoleCommands ($hash, $clHash, $cc); HMCCU_UpdateAdditionalCommands ($hash, $clHash, $cc, $cd); - } + # } } if ($aAtt =~ /^($refreshAttrList)$/i) { HMCCU_RefreshReadings ($clHash); @@ -1222,23 +1226,55 @@ sub HMCCU_AggregateReadings ($$) sub HMCCU_Undef ($$) { my ($hash, $arg) = @_; + my $name = $hash->{NAME}; # Shutdown RPC server HMCCU_Shutdown ($hash); - # Delete reference to IO module in client devices my @keylist = keys %defs; foreach my $d (@keylist) { my $ch = $defs{$d} // next; - if (exists ($ch->{TYPE}) && $ch->{TYPE} =~ /^(HMCCUDEV|HMCCUCHN|HMCCURPCPROC)$/ && - exists($ch->{IODev}) && $ch->{IODev} == $hash) { + next if (!exists($ch->{TYPE}) || !exists($ch->{IODev}) || $ch->{IODev} != $hash); + if ($ch->{TYPE} eq 'HMCCUDEV' || $ch->{TYPE} eq 'HMCCUCHN') { + # Delete reference to IO module in client devices delete $defs{$d}{IODev}; } + elsif ($ch->{TYPE} eq 'HMCCURPCPROC') { + # Delete RPC server devices associated with deleted I/O device + HMCCU_Log ($hash, 1, "Deleting RPC server device $ch->{NAME}"); + CommandDelete (undef, $ch->{NAME}); + } } + # Delete CCU credentials + my ($erruser, $encuser) = getKeyValue ($name.'_username'); + my ($errpass, $encpass) = getKeyValue ($name.'_password'); + setKeyValue ($name."_username", undef) if (!defined($erruser) && defined($encuser)); + setKeyValue ($name."_password", undef) if (!defined($errpass) && defined($encpass)); + return undef; } +###################################################################### +# Rename device +###################################################################### + +sub HMCCU_Rename ($$) +{ + my ($oldName, $newName); + + my ($erruser, $encuser) = getKeyValue ($oldName.'_username'); + my ($errpass, $encpass) = getKeyValue ($oldName.'_password'); + if (!defined($erruser) && defined($encuser)) { + setKeyValue ($newName."_username", $encuser); + setKeyValue ($oldName."_username", undef); + } + if (!defined($errpass) && defined($encpass)) { + setKeyValue ($newName."_password", $encpass); + setKeyValue ($oldName."_password", undef); + } +} + ###################################################################### # Delayed shutdown FHEM ###################################################################### @@ -1300,7 +1336,7 @@ sub HMCCU_Set ($@) "prgActivate prgDeactivate on:noArg off:noArg"; $opt = lc($opt); - my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 1); + my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 0); my @ifList = keys %$interfaces; if (scalar(@ifList) > 0) { my $ifStr = join(',', @ifList); @@ -1646,7 +1682,7 @@ sub HMCCU_Get ($@) my $opt = shift @$a // return 'No get command specified'; $opt = lc($opt); - my $options = "create createDev defaults:noArg exportDefaults dutycycle:noArg vars update". + my $options = "create createDev detectDev defaults:noArg exportDefaults dutycycle:noArg vars update". " updateCCU paramsetDesc firmware rpcEvents:noArg rpcState:noArg deviceInfo". " ccuMsg:alarm,service ccuConfig:noArg ccuDevices:noArg". " internal:groups,interfaces"; @@ -1654,6 +1690,7 @@ sub HMCCU_Get ($@) $options =~ s/createDev/createDev:$hash->{hmccu}{ccuSuppDevList}/; } if (defined($hash->{hmccu}{ccuDevList}) && $hash->{hmccu}{ccuDevList} ne '') { + $options =~ s/detectDev/detectDev:$hash->{hmccu}{ccuDevList}/; $options =~ s/deviceInfo/deviceInfo:$hash->{hmccu}{ccuDevList}/; $options =~ s/paramsetDesc/paramsetDesc:$hash->{hmccu}{ccuDevList}/; } @@ -1666,7 +1703,7 @@ sub HMCCU_Get ($@) my $ccuflags = HMCCU_GetFlags ($name); my $ccureadings = AttrVal ($name, "ccureadings", $ccuflags =~ /noReadings/ ? 0 : 1); - my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 1); + my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 0); my @ifList = keys %$interfaces; my $readname; @@ -1752,6 +1789,26 @@ sub HMCCU_Get ($@) "Paramset descriptions: $cPar\nLinks/Peerings: $cLnk\n". "Interfaces: $ifcount\nPrograms: $prgcount\nVirtual groups: $gcount"; } + elsif ($opt eq 'detectdev') { + $usage = "Usage: get $name detectDev device-name"; + my $devSpec = shift @$a // return HMCCU_SetError ($hash, $usage); + + foreach my $iface (keys %{$hash->{hmccu}{device}}) { + foreach my $address (keys %{$hash->{hmccu}{device}{$iface}}) { + if ($hash->{hmccu}{device}{$iface}{$address}{_name} eq $devSpec) { + my $detect = HMCCU_DetectDevice ($hash, $address, $iface); + if (defined($detect)) { + return HMCCU_RefToString ($detect); + } + else { + return "Automatic detection of $devSpec not possible"; + } + } + } + } + + return "Device $devSpec not found"; + } elsif ($opt eq 'create' || $opt eq 'createdev') { $usage = $opt eq 'createdev' ? "Usage: get $name createDev device-name" : @@ -2449,10 +2506,11 @@ sub HMCCU_LogDisplay ($$$;$) { my ($hash, $level, $msg, $rc) = @_; - if (exists($hash->{CL}) && $init_done) { + if ($init_done && exists($hash->{CL})) { my $devType = $hash->{TYPE} // ''; my $devName = defined($hash->{NAME}) ? " [$hash->{NAME}]" : ''; - asyncOutput ($hash->{CL}, "$devType $devName $msg"); + my $cl = $hash->{CL}; + InternalTimer (gettimeofday()+1, sub { asyncOutput ($cl, "$devType $devName $msg") }, undef, 1); } return HMCCU_Log ($hash, $level, $msg, $rc); @@ -2605,7 +2663,7 @@ sub HMCCU_SetRPCState ($@) # Count number of processes in state running, error or inactive # Prepare filter for updating client devices my %stc = ('running' => 0, 'error' => 0, 'inactive' => 0); - my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 1); + my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 0); my @iflist = keys %$interfaces; my $ifCount = scalar(@iflist); foreach my $i (@iflist) { @@ -2868,12 +2926,11 @@ sub HMCCU_UpdateClients ($$$$;$$) my ($hash, $devexp, $ccuget, $fromccu, $ifname, $nonBlock) = @_; my $fhname = $hash->{NAME}; $nonBlock //= HMCCU_IsFlag ($fhname, 'nonBlocking'); - my $c = 0; my $dc = 0; my $filter = 'ccudevstate=active'; $filter .= ",ccuif=$ifname" if (defined($ifname)); $ccuget = AttrVal ($fhname, 'ccuget', 'Value') if ($ccuget eq 'Attr'); - my $list = ''; + my @list = (); if ($fromccu) { foreach my $name (sort keys %{$hash->{hmccu}{adr}}) { @@ -2886,8 +2943,7 @@ sub HMCCU_UpdateClients ($$$$;$$) next if (!defined($ch->{IODev}) || !defined($ch->{ccuaddr}) || $ch->{ccuaddr} ne $hash->{hmccu}{adr}{$name}{address} || !HMCCU_IsValidDeviceOrChannel ($hash, $ch->{ccuaddr}, $HMCCU_FL_ADDRESS)); - $list .= ($list eq '') ? $name : ",$name"; - $c++; + push @list, $name; } } } @@ -2896,26 +2952,40 @@ sub HMCCU_UpdateClients ($$$$;$$) $dc = scalar(@devlist); foreach my $d (@devlist) { my $ch = $defs{$d}; - next if (!defined($ch->{IODev}) || !defined($ch->{ccuaddr}) || - !HMCCU_IsValidDeviceOrChannel ($hash, $ch->{ccuaddr}, $HMCCU_FL_ADDRESS)); + my $cn = $ch->{NAME}; + if (!defined($ch->{IODev})) { + HMCCU_Log ($hash, 2, "Device $cn not updated. I/O device not specified"); + next; + } + if (!defined($ch->{ccuaddr})) { + HMCCU_Log ($hash, 2, "Device $cn not updated. CCU address not specified"); + next; + } + if (!HMCCU_IsValidDeviceOrChannel ($hash, $ch->{ccuaddr}, $HMCCU_FL_ADDRESS)) { + HMCCU_Log ($hash, 2, "Device $cn not updated. Address $ch->{ccuaddr} is not valid"); + next; + } my $name = HMCCU_GetDeviceName ($hash, $ch->{ccuaddr}); - next if ($name eq ''); - $list .= ($list eq '') ? $name : ",$name"; - $c++; + if ($name eq '') { + HMCCU_Log ($hash, 2, "Device $cn not updated. Can't get device name for address $ch->{ccuaddr}"); + next; + } + push @list, $name; } } + my $c = scalar(@list); return HMCCU_Log ($hash, 2, 'Found no devices to update') if ($c == 0); HMCCU_Log ($hash, 2, "Updating $c of $dc client devices matching devexp=$devexp filter=$filter"); if ($nonBlock) { - HMCCU_HMScriptExt ($hash, '!GetDatapointsByDevice', { list => $list, ccuget => $ccuget }, + HMCCU_HMScriptExt ($hash, '!GetDatapointsByDevice', { list => join(',', @list), ccuget => $ccuget }, \&HMCCU_UpdateCB, { logCount => 1, devCount => $c }); return 1; } else { my $response = HMCCU_HMScriptExt ($hash, '!GetDatapointsByDevice', - { list => $list, ccuget => $ccuget }); + { list => join(',', @list), ccuget => $ccuget }); return -2 if ($response eq '' || $response =~ /^ERROR:.*/); HMCCU_UpdateCB ({ ioHash => $hash, logCount => 1, devCount => $c }, undef, $response); @@ -3137,7 +3207,8 @@ sub HMCCU_CreateFHEMDevices ($@) next if ($hash->{hmccu}{device}{$iface}{$address}{_addtype} ne 'dev' || HMCCU_ExprNotMatch ($ccuName, $devSpec, 1)); - if ($hash->{hmccu}{device}{$iface}{$address}{_model} =~ /^(HM-RCV-50|HmIP-RCV-50)$/) { + my $ccuType = $hash->{hmccu}{device}{$iface}{$address}{_model}; + if ($ccuType =~ /^(HM-RCV-50|HmIP-RCV-50)$/) { $cs{notSupported}{$ccuName} = "$address [$ccuName]"; next; } @@ -3168,6 +3239,8 @@ sub HMCCU_CreateFHEMDevices ($@) HMCCU_CreateDevice ($hash, $ccuName, $devName, 'HMCCUDEV', $defAdd, $defOpts, $ah, \%cs); } else { + HMCCU_BuildGroupAttr ($hash, $address, $ccuType, $ah) if ($detect->{controlRoleCount}+$detect->{stateRoleCount} > 1); + # Create a HMCCUCHN for each channel if ($detect->{controlRoleCount} > 0) { # First create a HMCCUCHN for each control channel @@ -3193,6 +3266,23 @@ sub HMCCU_CreateFHEMDevices ($@) $ah->{controldatapoint} = $detect->{defCDP} if ($detect->{defCCh} != -1 && $detect->{controlRoleCount} > 1); HMCCU_CreateDevice ($hash, $ccuName, $devName, $defMod, $defAdd, $defOpts, $ah, \%cs); } + elsif ($detect->{level} == 5) { + # 4-channel role patterns, create a HMCCUDEV for each occurrence + my $rpCount = scalar(keys %{$detect->{rolePattern}}); + HMCCU_BuildGroupAttr ($hash, $address, $ccuType, $ah) if ($rpCount > 1); + HMCCU_Log ($hash, 2, "4-channel role patterns found $rpCount"); + foreach my $firstChannel (keys %{$detect->{rolePattern}}) { + my $ccuChnName = $hash->{hmccu}{device}{$iface}{"$address:$firstChannel"}{_name}; + my $devChnName = HMCCU_MakeDeviceName ($defAdd, $devPrefix, $devFormat, $devSuffix, $ccuChnName); + delete $ah->{statedatapoint} if (exists($ah->{statedatapoint})); + delete $ah->{controldatapoint} if (exists($ah->{controldatapoint})); + $ah->{statedatapoint} = $detect->{rolePattern}{$firstChannel}{stateChannel}.'.'.$detect->{rolePattern}{$firstChannel}{stateDatapoint} + if (exists($detect->{rolePattern}{$firstChannel}{stateChannel})); + $ah->{controldatapoint} = $detect->{rolePattern}{$firstChannel}{controlChannel}.'.'.$detect->{rolePattern}{$firstChannel}{controlDatapoint} + if (exists($detect->{rolePattern}{$firstChannel}{controlChannel})); + HMCCU_CreateDevice ($hash, $ccuChnName, $devChnName, $defMod, $defAdd, $defOpts, $ah, \%cs); + } + } } } @@ -3262,6 +3352,27 @@ sub HMCCU_CreateDevice ($@) return 1; } +###################################################################### +# Build group attribute +###################################################################### + +sub HMCCU_BuildGroupAttr ($$$$) +{ + my ($ioHash, $address, $ccuType, $ah) = @_; + + my $groupName = AttrVal ($ioHash->{NAME}, 'createDeviceGroup', ''); + if ($groupName ne '') { + my $devName = HMCCU_GetDeviceName ($ioHash, $address); + $groupName =~ s/%n/$devName/g; + $groupName =~ s/%a/$address/g; + $groupName =~ s/%t/$ccuType/g; + $ah->{group} = $groupName; + return 1; + } + + return 0; +} + ###################################################################### # Create device name ###################################################################### @@ -3273,7 +3384,7 @@ sub HMCCU_MakeDeviceName ($$$$$) my $devName = $devPrefix.$devFormat.$devSuffix; $devName =~ s/%n/$ccuName/g; $devName =~ s/%a/$defAdd/g; - $devName =~ s/[^A-Za-z\d_\. ]+/_/g; + $devName =~ s/[^A-Za-z\d_\.]+/_/g; return $devName; } @@ -3397,40 +3508,6 @@ sub HMCCU_UpdateDevice ($$) $clHash->{sender} = join (',', HMCCU_Unique (@sndList)) if (scalar(@sndList) > 0); } - - # if (exists($ioHash->{hmccu}{snd}{$iface}{$da})) { - # delete $clHash->{sender} if (exists($clHash->{sender})); - # delete $clHash->{receiver} if (exists($clHash->{receiver})); - # my @rcvList = (); - # my @sndList = (); - - # foreach my $c (sort keys %{$ioHash->{hmccu}{snd}{$iface}{$da}}) { - # next if ($clType eq 'HMCCUCHN' && "$c" ne "$dc"); - # foreach my $r (keys %{$ioHash->{hmccu}{snd}{$iface}{$da}{$c}}) { - # my ($la, $lc) = HMCCU_SplitChnAddr ($r); - # next if ($la eq $da); # Ignore link if receiver = current device - # my @rcvNames = HMCCU_GetDeviceIdentifier ($ioHash, $r, $iface); - # my $rcvFlags = HMCCU_FlagsToStr ('peer', 'FLAGS', - # $ioHash->{hmccu}{snd}{$iface}{$da}{$c}{$r}{FLAGS}, ','); - # push @rcvList, map { $_.($rcvFlags ne 'OK' ? " [".$rcvFlags."]" : '') } @rcvNames; - # } - # } - - # foreach my $c (sort keys %{$ioHash->{hmccu}{rcv}{$iface}{$da}}) { - # next if ($clType eq 'HMCCUCHN' && "$c" ne "$dc"); - # foreach my $s (keys %{$ioHash->{hmccu}{rcv}{$iface}{$da}{$c}}) { - # my ($la, $lc) = HMCCU_SplitChnAddr ($s); - # next if ($la eq $da); # Ignore link if sender = current device - # my @sndNames = HMCCU_GetDeviceIdentifier ($ioHash, $s, $iface); - # my $sndFlags = HMCCU_FlagsToStr ('peer', 'FLAGS', - # $ioHash->{hmccu}{snd}{$iface}{$da}{$c}{$s}{FLAGS}, ','); - # push @sndList, map { $_.($sndFlags ne 'OK' ? " [".$sndFlags."]" : '') } @sndNames; - # } - # } - - # $clHash->{sender} = join (',', @sndList) if (scalar(@sndList) > 0); - # $clHash->{receiver} = join (',', @rcvList) if (scalar(@rcvList) > 0); - # } } ###################################################################### @@ -3494,8 +3571,7 @@ sub HMCCU_RenameDevice ($$$) return 0 if (!exists($ioHash->{hmccu}{device}{$iface}{$address})); if (exists($ioHash->{hmccu}{device}{$iface}{$address}{_fhem})) { - my @devList = grep { $_ ne $oldName } split(',', $ioHash->{hmccu}{device}{$iface}{$address}{_fhem}); - push @devList, $name; + my @devList = map { $_ eq $oldName ? $name : $_ } split(',', $ioHash->{hmccu}{device}{$iface}{$address}{_fhem}); $ioHash->{hmccu}{device}{$iface}{$address}{_fhem} = join(',', @devList); } else { @@ -3533,6 +3609,7 @@ sub HMCCU_SetSCAttributes ($$;$) # Detect device and initialize attribute lists for statedatapoint and controldatapoint my @userattr = grep (!/statedatapoint|controldatapoint/, split(' ', $modules{$clHash->{TYPE}}{AttrList})); if (defined($detect) && $detect->{level} > 0) { + $clHash->{hmccu}{detect} = $detect->{level}; if ($type eq 'HMCCUDEV') { push @userattr, 'statedatapoint:select,'. join(',', sort map { $_.'.'.$detect->{stateRole}{$_}{datapoint} } keys %{$detect->{stateRole}}) @@ -3553,6 +3630,7 @@ sub HMCCU_SetSCAttributes ($$;$) else { push @userattr, 'statedatapoint:select,'.join(',', sort @dpRead) if ($dpReadCnt > 0); push @userattr, 'controldatapoint:select,'.join(',', sort @dpWrite) if ($dpWriteCnt > 0); + $clHash->{hmccu}{detect} = 0; } # Make sure that generic attributes are available, if no role attributes found @@ -3632,7 +3710,7 @@ sub HMCCU_GetDeviceConfig ($) my ($cDev, $cPar, $cLnk) = (0, 0, 0); my $c = 0; - my $interfaces = HMCCU_GetRPCInterfaceList ($ioHash, 1); + my $interfaces = HMCCU_GetRPCInterfaceList ($ioHash, 0); foreach my $iface (keys %$interfaces) { my ($rpcdev, $save) = HMCCU_GetRPCDevice ($ioHash, 1, $iface); if ($rpcdev ne '') { @@ -3703,7 +3781,13 @@ sub HMCCU_GetDeviceConfig ($) # Update FHEM devices foreach my $d (@devList) { my $clHash = $defs{$d}; + my $name = $clHash->{NAME}; + if (!exists($clHash->{ccuaddr})) { + HMCCU_Log ($ioHash, 2, "Disabling client device $name because CCU address is missing. Does the device exist on CCU?"); + CommandAttr (undef, "$name disable 1"); + next; + } HMCCU_SetSCAttributes ($ioHash, $clHash); HMCCU_UpdateDevice ($ioHash, $clHash); HMCCU_UpdateDeviceRoles ($ioHash, $clHash); @@ -4200,6 +4284,10 @@ sub HMCCU_FindParamDef ($$$) # reference. # $ps - Parameter set name. # $parameter - Parameter name. +# $oper - Access mode: +# 1 = parameter readable +# 2 = parameter writeable +# 4 = parameter events # Returns 0 or 1 ###################################################################### @@ -4447,9 +4535,6 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) my $clInt = $clHash->{ccuif}; my ($sc, $sd, $cc, $cd) = HMCCU_GetSCDatapoints ($clHash); -# HMCCU_Trace ($clHash, 2, 'AddList='.join(',', @addList)); -# HMCCU_Trace ($clHash, 2, 'Objects='.Dumper($objects)); - readingsBeginUpdate ($clHash); # Loop over all addresses @@ -4538,9 +4623,6 @@ sub HMCCU_UpdateParamsetReadings ($$$;$) # Update device states HMCCU_UpdateDeviceStates ($clHash); -# HMCCU_BulkUpdate ($clHash, 'battery', $battery) if ($battery ne 'unknown'); -# HMCCU_BulkUpdate ($clHash, 'activity', $activity); -# HMCCU_BulkUpdate ($clHash, 'devstate', $devState); # Calculate and update HomeMatic state if ($ccuflags !~ /nohmstate/) { @@ -4816,7 +4898,7 @@ sub HMCCU_EventsTimedOut ($) # Register callback for each interface my $rc = 1; - my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 1); + my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 0); foreach my $ifname (keys %$interfaces) { my ($rpcdev, $save) = HMCCU_GetRPCDevice ($hash, 0, $ifname); if ($rpcdev eq '') { @@ -4901,7 +4983,7 @@ sub HMCCU_GetRPCServerInfo ($$$) ###################################################################### # Check if RPC interface is of specified type. -# Parameter type is A for XML or B for binary. +# Parameter $type is A for XML or B for binary. ###################################################################### sub HMCCU_IsRPCType ($$$) @@ -4944,7 +5026,7 @@ sub HMCCU_StartExtRPCServer ($) my $c = 0; my $d = 0; my $s = 0; - my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 1); + my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 0); my @iflist = keys %$interfaces; foreach my $ifname1 (@iflist) { my ($rpcdev, $save) = HMCCU_GetRPCDevice ($hash, 1, $ifname1); @@ -4997,7 +5079,7 @@ sub HMCCU_StopExtRPCServer ($;$) HMCCU_SetRPCState ($hash, 'stopping'); my $rc = 1; - my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 1); + my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 0); foreach my $ifname (keys %$interfaces) { my ($rpcdev, $save) = HMCCU_GetRPCDevice ($hash, 0, $ifname); if ($rpcdev eq '') { @@ -5044,7 +5126,7 @@ sub HMCCU_IsRPCServerRunning ($;$) my $ccuflags = HMCCU_GetFlags ($name); @$pids = () if (defined($pids)); - my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 1); + my $interfaces = HMCCU_GetRPCInterfaceList ($hash, 0); foreach my $ifname (keys %$interfaces) { my ($rpcdev, $save) = HMCCU_GetRPCDevice ($hash, 0, $ifname); next if ($rpcdev eq ''); @@ -5144,7 +5226,7 @@ sub HMCCU_FormatHashTable ($) foreach my $row (sort keys %$hash) { if ($t == 0) { # Begin of table with header - $result .= '\n'; + $result .= '
'; $result .= ''; foreach my $col (sort keys %{$hash->{$row}}) { $result .= ""; @@ -6305,11 +6387,13 @@ sub HMCCU_CreateRPCDevice ($$$$) { my ($hash, $ifname, $rpcprot, $rpchost) = @_; - my $alias = "CCU RPC $ifname"; + my $ccuNum = $hash->{CCUNum} // '1'; my $rpcdevname = 'd_rpc'; # Ensure unique device name by appending last 2 digits of CCU IP address - $rpcdevname .= HMCCU_GetIdFromIP ($hash->{ccuip}, '') if (exists($hash->{ccuip})); + my $uID = HMCCU_GetIdFromIP ($hash->{ccuip}, ''); + $rpcdevname .= $uID; + my $alias = "CCU ".($uID eq '' ? $ccuNum : $uID)." RPC $ifname"; # Build device name and define command $rpcdevname = makeDeviceName ($rpcdevname.$ifname); @@ -6321,7 +6405,7 @@ sub HMCCU_CreateRPCDevice ($$$$) HMCCU_Log ($hash, 1, "Creating new RPC device $rpcdevname for interface $ifname"); my $ret = CommandDefine (undef, $rpccreate); if (!defined($ret)) { - # RPC device created. Set/copy some attributes from HMCCU device + # RPC device created. Set/copy some attributes from I/O device my %rpcdevattr = ('room' => 'copy', 'group' => 'copy', 'icon' => 'copy', 'stateFormat' => 'rpcstate/state', 'eventMap' => '/rpcserver on:on/rpcserver off:off/', 'verbose' => 2, 'alias' => $alias ); @@ -6484,26 +6568,49 @@ sub HMCCU_SetDefaultAttributes ($;$) my $ioHash = HMCCU_GetHash ($clHash); my $clName = $clHash->{NAME}; - my ($sc, $sd, $cc, $cd) = HMCCU_GetSCDatapoints ($clHash); $parRef //= { mode => 'update', role => undef, roleChn => undef }; - my $role = $parRef->{role} // HMCCU_GetChannelRole ($clHash, $parRef->{roleChn} // $cc); + my $role; - if ($role ne '') { - $clHash->{hmccu}{semDefaults} = 1; - - # Delete obsolete attributes - if ($parRef->{mode} eq 'reset') { - my @removeAttr = ('ccureadingname', 'ccuscaleval', 'eventMap', 'cmdIcon', - 'substitute', 'webCmd', 'widgetOverride' - ); - my $detect = HMCCU_DetectDevice ($ioHash, $clHash->{ccuaddr}, $clHash->{ccuif}); - if (defined($detect) && ($detect->{level} == 1 || ($detect->{level} == 2 && $clHash->{TYPE} eq 'HMCCUCHN'))) { - push @removeAttr, 'statechannel', 'statedatapoint', 'controlchannel', 'controldatapoint', 'statevals' - } - foreach my $a (@removeAttr) { - CommandDeleteAttr (undef, "$clName $a") if (exists($attr{$clName}{$a})); - } + if ($parRef->{mode} eq 'reset') { + # List of attributes to be removed + my @removeAttr = ('ccureadingname', 'ccuscaleval', 'eventMap', 'cmdIcon', + 'substitute', 'webCmd', 'widgetOverride' + ); + + my $detect = HMCCU_DetectDevice ($ioHash, $clHash->{ccuaddr}, $clHash->{ccuif}); + if (defined($detect) && $detect->{level} > 0) { + my ($sc, $sd, $cc, $cd, $rsd, $rcd) = HMCCU_SetDefaultSCDatapoints ($ioHash, $clHash, $detect, 1); + HMCCU_Log ($clHash, 2, "Cannot set default state- and/or control datapoints") + if ($rsd == 0 && $rcd == 0); + + $role = HMCCU_GetChannelRole ($clHash, $detect->{defCCh}); + + HMCCU_SetInitialAttributes ($ioHash, $clName); + + # Remove additional attributes if device type is supported by HMCCU + push @removeAttr, 'statechannel', 'statedatapoint' if ($detect->{defSCh} != -1); + push @removeAttr, 'controlchannel', 'controldatapoint', 'statevals' if ($detect->{defCCh} != -1); + + # Remove attributes + HMCCU_DeleteAttributes ($clHash, \@removeAttr, 1); + + # Update command tables + HMCCU_UpdateRoleCommands ($ioHash, $clHash, $cc); + HMCCU_UpdateAdditionalCommands ($ioHash, $clHash, $cc, $cd); } + else { + HMCCU_LogDisplay ($clHash, 2, "Device type $clHash->{ccutype} not known by HMCCU"); + # Remove attributes + HMCCU_DeleteAttributes ($clHash, \@removeAttr, 1); + } + } + else { + my ($sc, $sd, $cc, $cd) = HMCCU_GetSCDatapoints ($clHash); + $role = $parRef->{role} // HMCCU_GetChannelRole ($clHash, $parRef->{roleChn} // $cc); + } + + if (defined($role) && $role ne '') { + $clHash->{hmccu}{semDefaults} = 1; # Set additional attributes if (exists($HMCCU_ATTR->{$role}) && !exists($HMCCU_ATTR->{$role}{_none_})) { @@ -6521,6 +6628,23 @@ sub HMCCU_SetDefaultAttributes ($;$) } } +###################################################################### +# Delete list of attributes +###################################################################### + +sub HMCCU_DeleteAttributes ($$;$) +{ + my ($clHash, $attrList, $sem) = @_; + $sem //= 0; + my $clName = $clHash->{NAME}; + + $clHash->{hmccu}{semDefaults} = $sem; + foreach my $a (@$attrList) { + CommandDeleteAttr (undef, "$clName $a") if (exists($attr{$clName}{$a})); + } + $clHash->{hmccu}{semDefaults} = 0; +} + ###################################################################### # Get state values of client device # Return '' if no state values available @@ -6532,7 +6656,6 @@ sub HMCCU_GetStateValues ($;$$) $dpt //= ''; $ctrlChn //= ''; - HMCCU_Trace ($clHash, 2, "dpt=$dpt, ctrlChn=$ctrlChn"); my $sv = AttrVal ($clHash->{NAME}, 'statevals', ''); if ($sv eq '' && $dpt ne '' && $ctrlChn ne '') { my $role = HMCCU_GetChannelRole ($clHash, $ctrlChn); @@ -6553,12 +6676,14 @@ sub HMCCU_GetStateValues ($;$$) # Command-Defintion: # 'Datapoint-Definition [...]' # Datapoint-Definition: -# Paramset:Datapoint:[+|-]Value -# Paramset:Datapoint:?Parameter -# Paramset:Datapoint:?Parameter=Default-Value -# Paramset:Datapoint:#Parameter=[Value1[,...]] +# Paramset:Datapoints:[+|-]Value +# Paramset:Datapoints:?Parameter +# Paramset:Datapoints:?Parameter=Default-Value +# Paramset:Datapoints:#Parameter[=Value1[,...]] # Paramset: # V=VALUES, M=MASTER (channel), D=MASTER (device) +# Datapoints: +# List of parameter names separated by , # If Parameter is preceded by ? any value is accepted. # If Parameter is preceded by #, Datapoint must have type ENUM or # a list of values must be specified. @@ -6624,6 +6749,7 @@ sub HMCCU_UpdateRoleCommands ($$;$) $cmdType = $1; $cmd = $2; } + my $parAccess = $cmdType eq 'set' ? 2 : 5; $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{syntax} = $cmdSyntax; $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{role} = $role; @@ -6635,17 +6761,36 @@ sub HMCCU_UpdateRoleCommands ($$;$) my @parTypes = (0, 0, 0, 0, 0); URCSUB: foreach my $subCmd (split(/\s+/, $cmdSyntax)) { - my $pt = 0; # Default = no parameter - my $scn = sprintf ("%03d", $cnt); - my ($ps, $dpt, $par, $fnc) = split(/:/, $subCmd); + my $pt = 0; # Default = no parameter + my $scn = sprintf ("%03d", $cnt); # Subcommand number in command definition + my $subCmdNo = $cnt; # Subcommand number in command execution + my @subCmdList = split(/:/, $subCmd); + if ($subCmdList[0] =~ /^([0-9]+)$/) { + $subCmdNo = $1; + shift @subCmdList; + } + my ($ps, $dpt, $par, $fnc) = @subCmdList; + my $psName = $ps eq 'I' ? 'VALUES' : $pset{$ps}; my ($addr, undef) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); $cmdChn = 'd' if ($ps eq 'D'); + + # Allow different parameter names for same command (depend on firmware revision of device) + my @dptList = split /,/, $dpt; + if (scalar(@dptList) > 1) { + foreach my $d (@dptList) { + if (HMCCU_IsValidParameter ($clHash, "$addr:$cmdChn", $psName, $d, $parAccess)) { + $dpt = $d; + last; + } + } + } - my $paramDef = HMCCU_GetParamDef ($ioHash, "$addr:$cmdChn", $ps eq 'I' ? 'VALUES' : $pset{$ps}, $dpt); + my $paramDef = HMCCU_GetParamDef ($ioHash, "$addr:$cmdChn", $psName, $dpt); if (!defined($paramDef)) { - HMCCU_Log ($ioHash, 2, "Can't get definition of $addr:$cmdChn.$dpt. Ignoring command $cmd for device $clHash->{NAME}"); + HMCCU_Log ($ioHash, 3, "Can't get definition of datapoint $addr:$cmdChn.$dpt. Ignoring command $cmd for device $clHash->{NAME}"); next URCCMD; } + $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{scn} = sprintf("%03d", $subCmdNo); $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{min} = $paramDef->{MIN}; $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{max} = $paramDef->{MAX}; $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{unit} = $paramDef->{UNIT} // ''; @@ -6669,23 +6814,49 @@ sub HMCCU_UpdateRoleCommands ($$;$) } if (defined($par) && $par ne '') { - if ($par =~ /^#([^=]+)/) { + if ($par =~ /^#(.+)$/) { + my ($pn, $pv) = split('=', $1); + # Parameter list my $argList = ''; - $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{parname} = $1; + $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{parname} = $pn; $pt = 1; # Enum / List of fixed values if ($paramDef->{TYPE} eq 'ENUM' && defined($paramDef->{VALUE_LIST})) { + # Parameter type ENUM $argList = $paramDef->{VALUE_LIST}; } else { - my ($pn, $pv) = split('=', $par); + # Parameter with list of values +# my ($pn, $pv) = split('=', $par); $argList = $pv // ''; my %valList; - foreach my $cv (split(',', $HMCCU_STATECONTROL->{$role}{V})) { - my ($vn, $vv) = split(':', $cv); - $valList{$vn} = $vv // $vn; + if ($dpt eq $HMCCU_STATECONTROL->{$role}{C}) { + # If parameter is control datapoint, use values/conversions from HMCCU_STATECONTROL + foreach my $cv (split(',', $HMCCU_STATECONTROL->{$role}{V})) { + my ($vn, $vv) = split(':', $cv); + $valList{$vn} = $vv // $vn; + } } + elsif (exists($HMCCU_CONVERSIONS->{$role}{$dpt})) { + # If a list of conversions exists, use values/conversions from HMCCU_CONVERSIONS + foreach my $cv (split(',', $argList)) { + if (exists($HMCCU_CONVERSIONS->{$role}{$dpt}{$cv})) { + $valList{$HMCCU_CONVERSIONS->{$role}{$dpt}{$cv}} = $cv; + } + else { + $valList{$cv} = $cv; + } + } + } + else { + # As fallback use values as specified + foreach my $cv (split(',', $argList)) { + $valList{$cv} = $cv; + } + } + + # Build the lookup table my @el = split(',', $argList); while (my ($i, $e) = each @el) { $clHash->{hmccu}{roleCmds}{$cmdType}{$cmd}{subcmd}{$scn}{look}{$e} = $valList{$e} // $i; @@ -6782,12 +6953,57 @@ sub HMCCU_UpdateAdditionalCommands ($$;$$) $cc //= ''; $cd //= ''; - # Toggle command - my $stateVals = HMCCU_GetStateValues ($clHash, $cd, $cc); - HMCCU_Trace ($clHash, 2, "stateVals=$stateVals, cd=$cd, cc=$cc"); - my %stateCmds = split (/[:,]/, $stateVals); - my @states = keys %stateCmds; - $clHash->{hmccu}{cmdlist}{set} .= ' toggle:noArg' if (scalar(@states) > 1); + # No controldatapoint available (read only device) + if ($cd eq '' || $cc eq '') { + HMCCU_Log ($clHash, 4, "No control datapoint. Maybe device is read only or attribute will be set later"); + return; + } + + my $s = exists($clHash->{hmccu}{cmdlist}{set}) && $clHash->{hmccu}{cmdlist}{set} ne '' ? ' ' : ''; + my ($addr, $chn) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); + + # Check if role of control channel is supported by HMCCU + my $role = HMCCU_GetChannelRole ($clHash, $cc); + if ($role ne '' && exists($HMCCU_STATECONTROL->{$role}) && + HMCCU_DetectSCDatapoint ($HMCCU_STATECONTROL->{$role}{C}, $clHash->{ccuif}) eq $cd) { + # Only add toggle command, ignore attribute statevals + my %stateCmds = split (/[:,]/, $HMCCU_STATECONTROL->{$role}{V}); + my @states = keys %stateCmds; + $clHash->{hmccu}{cmdlist}{set} .= $s.'toggle:noArg' if (scalar(@states) > 1); + return; + } + + my $sv = AttrVal ($clHash->{NAME}, 'statevals', ''); + if ($sv ne '') { + my %stateCmds = split (/[:,]/, $sv); + my @states = keys %stateCmds; + + my $paramDef = HMCCU_GetParamDef ($ioHash, "$addr:$cc", 'VALUES', $cd); + if (defined($paramDef)) { + foreach my $cmd (@states) { + $clHash->{hmccu}{roleCmds}{set}{$cmd}{channel} = $cc; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{role} = $role; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcount} = 1; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{syntax} = "V:$cd:".$stateCmds{$cmd}; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{usage} = $cmd; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{partype} = 3; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{args} = $stateCmds{$cmd}; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{min} = $paramDef->{MIN}; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{max} = $paramDef->{MAX}; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{unit} = $paramDef->{UNIT} // ''; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{ps} = 'VALUES'; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{dpt} = $cd; + $clHash->{hmccu}{roleCmds}{set}{$cmd}{subcmd}{'000'}{fnc} = ''; + } + $clHash->{hmccu}{cmdlist}{set} .= $s.join(' ', map { $_ . ':noArg' } @states) + if (scalar(@states) > 0); + $clHash->{hmccu}{cmdlist}{set} .= ' toggle:noArg' + if (scalar(@states) > 1); + } + else { + HMCCU_Log ($clHash, 3, "Can't get definition of datapoint $addr:$cc.$cd. Ignoring commands ".join(',',@states)." for device $clHash->{NAME}"); + } + } } ###################################################################### @@ -6874,15 +7090,18 @@ sub HMCCU_ExecuteRoleCommand ($@) # Align new value with min/max boundaries if (exists($cmd->{min}) && exists($cmd->{max})) { - $value = HMCCU_MinMax ($value, - HMCCU_ScaleValue ($clHash, $channel, $cmd->{dpt}, $cmd->{min}, 0), - HMCCU_ScaleValue ($clHash, $channel, $cmd->{dpt}, $cmd->{max}, 0) - ); + # Use mode = 0 in HMCCU_ScaleValue to get the min and max value allowed + HMCCU_Trace ($clHash, 2, "MinMax: value=$value, min=$cmd->{min}, max=$cmd->{max}"); + my $scMin = HMCCU_ScaleValue ($clHash, $channel, $cmd->{dpt}, $cmd->{min}, 0); + my $scMax = HMCCU_ScaleValue ($clHash, $channel, $cmd->{dpt}, $cmd->{max}, 0); + $value = HMCCU_MinMax ($value, $scMin, $scMax); + HMCCU_Trace ($clHash, 2, "scMin=$scMin, scMax=$scMax, scVal=$value"); } if ($cmd->{ps} eq 'VALUES') { - my $dno = sprintf ("%03d", $c); - $dpval{"$dno.$clHash->{ccuif}.$chnAddr.$cmd->{dpt}"} = $value; + # my $dno = sprintf ("%03d", $c); + # $dpval{"$dno.$clHash->{ccuif}.$chnAddr.$cmd->{dpt}"} = $value; + $dpval{"$cmd->{scn}.$clHash->{ccuif}.$chnAddr.$cmd->{dpt}"} = $value; $c++; } elsif ($cmd->{ps} eq 'INTERNAL') { @@ -7216,10 +7435,10 @@ sub HMCCU_ExecuteGetDeviceInfoCommand ($@) "
Failed to detect device settings. Device must be configured manually.
"; if ($extended) { $devInfo .= "
Detection level: $detect->{level}
". - "
Detected default state datapoint: $detect->{defSCh}.$detect->{defSDP}
". - "
Detected default control datapoint: $detect->{defCCh}.$detect->{defCDP}
". + "
Detected default state datapoint: $detect->{defSDP}
". + "
Detected default control datapoint: $detect->{defCDP}
". "
Unique state roles: $detect->{uniqueStateRoleCount}
". - "
Unique state roles: $detect->{uniqueControlRoleCount}
"; + "
Unique control roles: $detect->{uniqueControlRoleCount}
"; } } $devInfo .= "
Current state datapoint = $sc.$sd
"; @@ -7242,7 +7461,8 @@ sub HMCCU_ExecuteGetDeviceInfoCommand ($@) sub HMCCU_ExecuteGetParameterCommand ($@) { - my ($ioHash, $clHash, $command, $addList) = @_; + my ($ioHash, $clHash, $command, $addList, $filter) = @_; + $filter //= '.*'; my %parSets = ('config' => 'MASTER,LINK,SERVICE', 'values' => 'VALUES', 'update' => 'VALUES,MASTER,LINK,SERVICE'); my $defParamset = $parSets{$command}; @@ -7263,7 +7483,9 @@ sub HMCCU_ExecuteGetParameterCommand ($@) foreach my $rcv (HMCCU_GetReceivers ($ioHash, $a, $clHash->{ccuif})) { my ($rc, $result) = HMCCU_RPCRequest ($clHash, 'getRawParamset', $a, $rcv); next if ($rc < 0); - foreach my $p (keys %$result) { $objects{$da}{$dc}{"LINK.$rcv"}{$p} = $result->{$p}; } + foreach my $p (keys %$result) { + $objects{$da}{$dc}{"LINK.$rcv"}{$p} = $result->{$p} if ($p =~ /$filter/); + } } } else { @@ -7272,7 +7494,9 @@ sub HMCCU_ExecuteGetParameterCommand ($@) HMCCU_Log ($clHash, 2, "Can't get parameterset $ps for address $a"); next; } - foreach my $p (keys %$result) { $objects{$da}{$dc}{$ps}{$p} = $result->{$p}; } + foreach my $p (keys %$result) { + $objects{$da}{$dc}{$ps}{$p} = $result->{$p} if ($p =~ /$filter/); + } } } } @@ -7289,6 +7513,9 @@ sub HMCCU_DisplayGetParameterResult ($$$) my ($ioHash, $clHash, $objects) = @_; my $res = ''; + my $flags = HMCCU_GetFlags ($clHash->{NAME}); + $res = "Readings for config parameters are not updated until you set showXXX flags in attribute ccuflags\n\n" + if ($flags !~ /show(Master|Device)/); if (scalar(keys %$objects) > 0) { my $convRes = HMCCU_UpdateParamsetReadings ($ioHash, $clHash, $objects); if (defined($convRes)) { @@ -7407,6 +7634,10 @@ sub HMCCU_CheckParameter ($$;$$$) # Parameter d specifies the value to be set: # state, control, statechannel, statedatapoint, controlchannel, # controldatapoint +# If parameter v is missing, the attribute is deleted +# Parameter r contains the role. +# Return: +# 0=Error, 1=Success ###################################################################### sub HMCCU_SetSCDatapoints ($$;$$) @@ -7426,7 +7657,10 @@ sub HMCCU_SetSCDatapoints ($$;$$) my $chn; my $dpt; my $f = $flags{$d} // return 0; + + # d becomes the hash key: state or control $d =~ s/^(state|control)(channel|datapoint)$/$1/; + return 0 if ($d ne 'state' && $d ne 'control'); if (defined($v)) { # Set value @@ -7457,30 +7691,79 @@ sub HMCCU_SetSCDatapoints ($$;$$) $clHash->{hmccu}{$d}{chn} = $chn if (defined($chn)); $clHash->{hmccu}{$d}{dpt} = $dpt if (defined($dpt)); + # Try to set missing state/control datapoint to the same datapoint + if (defined($chn) && defined($dpt) && defined($clHash->{ccuaddr})) { + my ($da, undef) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); + if ($d eq 'control' && !HMCCU_IsValidStateDatapoint ($clHash, 1) && + HMCCU_IsValidParameter ($clHash, "$da:$chn", 'VALUES', $dpt, 5) + ) { + $clHash->{hmccu}{state}{chn} = $chn; + $clHash->{hmccu}{state}{dpt} = $dpt; + } + elsif ($d eq 'state' && !HMCCU_IsValidControlDatapoint ($clHash, 1) && + HMCCU_IsValidParameter ($clHash, "$da:$chn", 'VALUES', $dpt, 2) + ) { + $clHash->{hmccu}{control}{chn} = $chn; + $clHash->{hmccu}{control}{dpt} = $dpt; + } + } + return 1; } -sub HMCCU_SetDefaultSCDatapoints ($$;$) +###################################################################### +# Set default state and control datapoint +###################################################################### + +sub HMCCU_SetDefaultSCDatapoints ($$;$$) { - my ($ioHash, $clHash, $detect) = @_; + my ($ioHash, $clHash, $detect, $cmd) = @_; $detect //= HMCCU_DetectDevice ($ioHash, $clHash->{ccuaddr}, $clHash->{ccuif}); - return 0 if (!defined($detect)); + return ('', '', '', '', 0, 0) if (!defined($detect)); + $cmd //= 0; - my $si = HMCCU_GetSCInfo ($detect, 0); # State info - my $ci = HMCCU_GetSCInfo ($detect, 1); # Control info - return 0 if (!defined($si) && !defined($ci)); - - HMCCU_SetSCDatapoints ($clHash, 'statedatapoint', $detect->{defSDP}, $si->{role}); - HMCCU_SetSCDatapoints ($clHash, 'controldatapoint', $detect->{defCDP}, $ci->{role}); + my $si = HMCCU_GetSCInfo ($detect, 0); # State info of default state datapoint + my $ci = HMCCU_GetSCInfo ($detect, 1); # Control info of default control datapoint + return ('', '', '', '', 0, 0) if (!defined($si) && !defined($ci)); - my $chn = $detect->{defCCh} != -1 ? $detect->{defCCh} : $detect->{defSCh}; - my $dpt = defined($ci) ? $ci->{datapoint} : $si->{datapoint}; + my $sc = AttrVal ($clHash->{NAME}, 'statechannel', ''); + my $cc = AttrVal ($clHash->{NAME}, 'controlchannel', ''); + my $sd = ''; + my $cd = ''; - HMCCU_UpdateRoleCommands ($ioHash, $clHash, $chn); - HMCCU_UpdateAdditionalCommands ($ioHash, $clHash, $chn, $dpt); + # Consider attributes statechannel and controlchannel + if ($sc ne '' && exists($detect->{stateRole}{$sc})) { + HMCCU_SetSCDatapoints ($clHash, 'statedatapoint', $sc.'.'.$detect->{stateRole}{$sc}{datapoint}, $detect->{stateRole}{$sc}{role}); + } + else { + $sc = $detect->{defSCh}; + HMCCU_SetSCDatapoints ($clHash, 'statedatapoint', $detect->{defSDP}, $si->{role}); + } + if ($cc ne '' && exists($detect->{controlRole}{$cc})) { + HMCCU_SetSCDatapoints ($clHash, 'controldatapoint', $cc.'.'.$detect->{controlRole}{$cc}{datapoint}, $detect->{controlRole}{$cc}{role}); + } + else { + $cc = $detect->{defCCh}; + HMCCU_SetSCDatapoints ($clHash, 'controldatapoint', $detect->{defCDP}, $ci->{role}); + } - return 1; + if ($cmd) { + my $chn = $cc != -1 ? $cc : $sc; + my $dpt = defined($ci) ? $ci->{datapoint} : $si->{datapoint}; + + HMCCU_UpdateRoleCommands ($ioHash, $clHash, $chn); + HMCCU_UpdateAdditionalCommands ($ioHash, $clHash, $chn, $dpt); + } + + $sc = $clHash->{hmccu}{state}{chn} // ''; + $sd = $clHash->{hmccu}{state}{dpt} // ''; + $cc = $clHash->{hmccu}{control}{chn} // ''; + $cd = $clHash->{hmccu}{control}{dpt} // ''; + my $rsd = $sc ne '' && $sd ne '' ? 1 : 0; + my $rcd = $cc ne '' && $cd ne '' ? 1 : 0; + + return ($sc, $sd, $cc, $cd, $rsd, $rcd); } ###################################################################### @@ -7511,65 +7794,74 @@ sub HMCCU_GetSCDatapoints ($) my $ioHash = HMCCU_GetHash ($clHash); my $type = $clHash->{TYPE}; - my $sc = exists($clHash->{hmccu}{state}{chn}) ? $clHash->{hmccu}{state}{chn} : ''; - my $sd = exists($clHash->{hmccu}{state}{dpt}) ? $clHash->{hmccu}{state}{dpt} : ''; - my $cc = exists($clHash->{hmccu}{control}{chn}) ? $clHash->{hmccu}{control}{chn} : ''; - my $cd = exists($clHash->{hmccu}{control}{dpt}) ? $clHash->{hmccu}{control}{dpt} : ''; + my ($sc, $sd) = HMCCU_StateDatapoint ($clHash); + my ($cc, $cd) = HMCCU_ControlDatapoint ($clHash); my $rsdCnt; my $rcdCnt; # Detect by attributes ($sc, $sd, $cc, $cd, $rsdCnt, $rcdCnt) = HMCCU_DetectSCAttr ($clHash, $sc, $sd, $cc, $cd); - return ($sc, $sd, $cc, $cd, 1, 1) if ($rsdCnt == 1 && $rcdCnt == 1); + return ($sc, $sd, $cc, $cd, $rsdCnt, $rcdCnt) if ($rsdCnt); - HMCCU_SetDefaultSCDatapoints ($ioHash, $clHash); - - return ( - exists($clHash->{hmccu}{state}{chn}) ? $clHash->{hmccu}{state}{chn} : '', - exists($clHash->{hmccu}{state}{dpt}) ? $clHash->{hmccu}{state}{dpt} : '', - exists($clHash->{hmccu}{control}{chn}) ? $clHash->{hmccu}{control}{chn} : '', - exists($clHash->{hmccu}{control}{dpt}) ? $clHash->{hmccu}{control}{dpt} : '', - 1, 1 - ) - - # Detect by role, but do not override values defined as attributes -# if (defined($clHash->{hmccu}{role}) && $clHash->{hmccu}{role} ne '') { -# HMCCU_Trace ($clHash, 2, "hmccurole=$clHash->{hmccu}{role}"); - # if ($type eq 'HMCCUCHN') { - # ($sd, $cd, $rsdCnt, $rcdCnt) = HMCCU_DetectSCChn ($clHash, $sd, $cd); - # } - # elsif ($type eq 'HMCCUDEV') { - # ($sc, $sd, $cc, $cd, $rsdCnt, $rcdCnt) = HMCCU_DetectSCDev ($clHash, $sc, $sd, $cc, $cd); - # } - # } - - # if ($rsdCnt == 0 && $rcdCnt == 1 && HMCCU_IsValidDatapoint ($clHash, $clHash->{ccutype}, $cc, $cd, 5)) { - # Use control datapoint as state datapoint if control datapoint is readable or updated by events - # ($sc, $sd) = ($cc, $cd); - # } - # elsif ($rsdCnt == 1 && $rcdCnt == 0 && HMCCU_IsValidDatapoint ($clHash, $clHash->{ccutype}, $sc, $sd, 2)) { - # # Use state datapoint as control datapoint if state datapoint is writeable - # ($cc, $cd) = ($sc, $sd); - # } - - # Store channels and datapoints in device hash - # $clHash->{hmccu}{state}{dpt} = $sd; - # $clHash->{hmccu}{state}{chn} = $sc; - # $clHash->{hmccu}{control}{dpt} = $cd; - # $clHash->{hmccu}{control}{chn} = $cc; - - # return ($sc, $sd, $cc, $cd, $rsdCnt, $rcdCnt); + return HMCCU_SetDefaultSCDatapoints ($ioHash, $clHash); } +sub HMCCU_ControlDatapoint ($) +{ + my ($clHash) = @_; + + return ($clHash->{hmccu}{control}{chn} // '', $clHash->{hmccu}{control}{dpt} // ''); +} + +sub HMCCU_IsValidControlDatapoint ($;$) +{ + my ($clHash, $checkHashOnly) = @_; + + return 0 if (!defined($clHash->{ccuaddr})); + + $checkHashOnly //= 0; + my ($cc, $cd) = HMCCU_ControlDatapoint ($clHash); + my ($da, $chnNo) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); + + return $cc ne '' && $cd ne '' && ($checkHashOnly || HMCCU_IsValidParameter ($clHash, "$da:$cc", 'VALUES', $cd, 2)) ? 1 : 0; +} + +sub HMCCU_StateDatapoint ($) +{ + my ($clHash) = @_; + + return ($clHash->{hmccu}{state}{chn} // '', $clHash->{hmccu}{state}{dpt} // ''); +} + +sub HMCCU_IsValidStateDatapoint ($;$) +{ + my ($clHash, $checkHashOnly) = @_; + + return 0 if (!defined($clHash->{ccuaddr})); + + $checkHashOnly //= 0; + my ($sc, $sd) = HMCCU_StateDatapoint ($clHash); + my ($da, $chnNo) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); + + return $sc ne '' && $sd ne '' && ($checkHashOnly || HMCCU_IsValidParameter ($clHash, "$da:$sc", 'VALUES', $sd, 5)) ? 1 : 0; +} + +###################################################################### +# Get state and control datapoints from attributes +# Return defaults passed as parameters if attribute(s) not defined +###################################################################### + sub HMCCU_DetectSCAttr ($$$$$) { my ($clHash, $sc, $sd, $cc, $cd) = @_; my $name = $clHash->{NAME}; my $type = $clHash->{TYPE}; + $sc //= ''; + $cc //= ''; my $da; my $dc; - if (exists($clHash->{ccuaddr})) { + if (defined($clHash->{ccuaddr})) { ($da, $dc) = HMCCU_SplitChnAddr ($clHash->{ccuaddr}); } @@ -7761,6 +8053,7 @@ sub HMCCU_DetectSCDev ($;$$$$) # int controlRoleCount: Number of controlRole entries # hash stateRole: Hash with state roles, key is channel number # hash controlRole: Hash with control roles, key is channel number +# hash rolePattern: Hash with 4-channel role patterns # string defMod: Default module 'HMCCUDEV', 'HMCCUCHN' or '' # string defAdd: Device address (append channel number fpr HMCCUCHN) # int defSCh: Default state channel or -1 @@ -7776,12 +8069,20 @@ sub HMCCU_DetectSCDev ($;$$$$) # roles (i.e. roles KEY and THERMALCONTROL) => HMCCUDEV # 4 = device type detected with different state and control role # (>=2 different channels) => HMCCUDEV +# 5 = device type detected with one or more 4-channel-groups (1xState,3xControl) # # Structure of stateRole / controlRole hashes: -# int : Channel number +# int : Channel number (key) # string {}{role}: Channel role # string {}{datapoint}: State or control datapoint # int {}{priority}: Priority of role/datapoint +# +# Structure of rolePattern hash (detection level = 5) +# int : Number of first channel of a group +# string {}{stateRole}: Role of state channel +# string {}{controlRole}: Role of control channel +# string {}{stateDatapoint}: The state datapoint +# string {}{controlDatapoint}: The control datapoint ###################################################################### sub HMCCU_DetectDevice ($$$) @@ -7792,6 +8093,12 @@ sub HMCCU_DetectDevice ($$$) my @stateRoles = (); my @controlRoles = (); my ($prioState, $prioControl) = (-1, -1); + + if (!defined($address)) { + HMCCU_Log ($ioHash, 2, "Parameter address not defined ".stacktraceAsString(undef)); + return undef; + } + my ($devAdd, $devChn) = HMCCU_SplitChnAddr ($address); my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $address, $iface); @@ -7826,7 +8133,7 @@ sub HMCCU_DetectDevice ($$$) my %di = ( stateRoleCount => $stateRoleCnt, controlRoleCount => $ctrlRoleCnt, uniqueStateRoleCount => $cntUniqStateRoles, uniqueControlRoleCount => $cntUniqCtrlRoles, - defSCh => -1, defCCh => -1, defSDP => '', defCDP => '', + defMod => '', defSCh => -1, defCCh => -1, defSDP => '', defCDP => '', level => 0 ); my $p = -1; @@ -7908,25 +8215,39 @@ sub HMCCU_DetectDevice ($$$) '^(?!([A-Z]+_VIRTUAL))([A-Z]+)[A-Z_]+(,\g2_VIRTUAL_[A-Z_]+){3}$', 4, 4); if (defined($rolePatterns)) { ROLEPATTERN: foreach my $rp (keys %$rolePatterns) { + # A role pattern is a comma separated list of channel roles my @patternRoles = split(',', $rp); + + # The number of the first channel of the first channel group my $firstChannel = (split(',', $rolePatterns->{$rp}{i}))[0]; + + # Check if all roles of a pattern role are supported (TODO: move this check to HMCCU_DetectRolePattern) PATTERNROLE: foreach my $pr (@patternRoles) { next ROLEPATTERN if (!exists($HMCCU_STATECONTROL->{$pr})); } + # state/control channel is the first channel with a state/control datapoint while (my ($i, $pr) = each @patternRoles) { if ($HMCCU_STATECONTROL->{$pr}{S} ne '') { + $di{rolePattern}{$firstChannel}{stateRole} = $pr; + $di{rolePattern}{$firstChannel}{stateChannel} = $firstChannel+$i; + $di{rolePattern}{$firstChannel}{stateDatapoint} = $HMCCU_STATECONTROL->{$pr}{S}; $di{defSCh} = $firstChannel+$i; last; } } while (my ($i, $pr) = each @patternRoles) { if ($HMCCU_STATECONTROL->{$pr}{C} ne '') { + $di{rolePattern}{$firstChannel}{controlRole} = $pr; + $di{rolePattern}{$firstChannel}{controlChannel} = $firstChannel+$i; + $di{rolePattern}{$firstChannel}{controlDatapoint} = $HMCCU_STATECONTROL->{$pr}{C}; $di{defCCh} = $firstChannel+$i; last; } } } + + $di{level} = 5 if (exists($di{rolePattern}) && scalar(keys %{$di{rolePattern}}) > 0); } } } @@ -8563,14 +8884,19 @@ sub HMCCU_ScaleValue ($$$$$;$) $paramSet //= 'VALUES'; my $name = $hash->{NAME}; my $ioHash = HMCCU_GetHash ($hash); + + HMCCU_Trace ($hash, 2, "Called by ".stacktraceAsString(undef)); + # Only numeric values allowed + return $value if (!HMCCU_IsFltNum ($value)); + # Get parameter definition and min/max values my $min; my $max; my $unit; my $ccuaddr = $hash->{ccuaddr}; $ccuaddr .= ':'.$chnno if ($hash->{TYPE} eq 'HMCCUDEV' && $chnno ne ''); - my $paramDef = HMCCU_GetParamDef ($ioHash, $ccuaddr, 'VALUES', $dpt); + my $paramDef = HMCCU_GetParamDef ($ioHash, $ccuaddr, $paramSet, $dpt); if (defined($paramDef)) { $min = $paramDef->{MIN} if (defined($paramDef->{MIN}) && $paramDef->{MIN} ne ''); $max = $paramDef->{MAX} if (defined($paramDef->{MAX}) && $paramDef->{MAX} ne ''); @@ -8642,12 +8968,6 @@ sub HMCCU_ScaleValue ($$$$$;$) return $value; } -# if ($dpt eq 'LEVEL') { -# my $rv = ($mode == 0) ? HMCCU_Min($value,1.0)*100.0 : HMCCU_Min($value,100.0)/100.0; -# HMCCU_Trace ($hash, 2, "LEVEL: $rv"); -# return $rv; -## return ($mode == 0) ? HMCCU_Min($value,1.0)*100.0 : HMCCU_Min($value,100.0)/100.0; -# } if ($dpt =~ /^RSSI_/) { # Subtract 256 from Rega value (Rega bug) $value = abs ($value) == 65535 || $value == 0 ? 'N/A' : ($value > 0 ? $value-256 : $value); @@ -9634,25 +9954,22 @@ sub HMCCU_EncodeEPDisplay ($) # Supports reference to ARRAY, HASH and SCALAR and scalar values. ###################################################################### -sub HMCCU_RefToString ($) +sub HMCCU_RefToString ($;$) { - my ($r) = @_; + my ($r, $l) = @_; + $l //= 0; + my $s1 = ' ' x ($l*2); + my $s2 = ' ' x (($l+1)*2); if (ref($r) eq 'ARRAY') { my $result = "[\n"; - foreach my $e (@$r) { - $result .= ',' if ($result ne '['); - $result .= HMCCU_RefToString ($e); - } - return "$result\n]"; + $result .= join (",\n", map { $s2.HMCCU_RefToString($_, $l+1) } @$r); + return "$result\n$s1]"; } elsif (ref($r) eq 'HASH') { my $result .= "{\n"; - foreach my $k (sort keys %$r) { - $result .= ',' if ($result ne '{'); - $result .= "$k=".HMCCU_RefToString ($r->{$k}); - } - return "$result\n}"; + $result .= join (",\n", map { $s2."$_=".HMCCU_RefToString($r->{$_}, $l+1) } keys %$r); + return "$result\n$s1}"; } elsif (ref($r) eq 'SCALAR') { return $$r; @@ -9795,9 +10112,19 @@ sub HMCCU_TCPConnect ($$;$) sub HMCCU_GetIdFromIP ($$) { my ($ip, $default) = @_; + return $default if (!defined($ip)); - my @ipseg = split (/\./, $ip); - return scalar(@ipseg) == 4 ? sprintf ("%03d%03d", $ipseg[2], $ipseg[3]) : $default; + if ($ip =~ /:[0-9]{1,4}$/) { + # Looks like an IPv6 address + $ip =~ s/://g; + my $ip1 = int(hex('0x'.substr($ip,-4))/256) // 0; + my $ip2 = hex('0x'.substr($ip,-4))%256 // 0; + return $ip1 > 0 || $ip2 > 0 ? sprintf("%03d%03d", $ip1, $ip2) : $default; + } + else { + my @ipseg = split (/\./, $ip); + return scalar(@ipseg) == 4 ? sprintf ("%03d%03d", $ipseg[2], $ipseg[3]) : $default; + } } ###################################################################### @@ -10020,9 +10347,13 @@ sub HMCCU_MaxHashEntries ($$) If option 'noDefaults' is specified, HMCCU does not set default attributes for a device. Option 'save' will save FHEM config after device definition.
-
  • get <name> createDev <devname>
    - Simplified version of 'get create'. Doesn't accept a regular expression for device name. -

  • +
  • get <name> createDev <devname>
    + Simplified version of 'get create'. Doesn't accept a regular expression for device name. +

  • +
  • get <name> detectDev <devname>
    + Diagnostics command. Try to auto-detect device and display the result. Add this information + to your post in FHEM forum, if a device is not created as expected. +

  • get <name> defaults
    List device types and channels with default attributes available.

  • @@ -10188,6 +10519,23 @@ sub HMCCU_MaxHashEntries ($$) Deprecated. Readings are written by default. To deactivate readings set flag noReadings in attribute ccuflags.
    +
  • createDeviceGroup <pattern>
    + The commands "get create" and "get createDev" will automatically set the group + attribute for newly created devices to the specified pattern if multiple FHEM + devices were created for a single CCU device. This will happen i.e. for remote controls + with mutliple keys or HmIP-Wired multi-switches.
    + The parameter pattern supports the following placeholders:
    + %n - replaced by CCU device name
    + %a - replaced by CCU device address
    + %t - replaced by CCU device type
    + Example: A remote with 4 channels named 'Light_Control' should be created in FHEM. Using + command "get createDev" will define one HMCCUCHN device per channel. Our naming scheme + for automatically assigned groups should be "ccuDeviceType ccuDeviceName".
    +
    +		attr myIODev createDeviceGroup "%t %n" 
    +		get myIODev createDev Light_Control
    +		
    +

  • rpcinterfaces <interface>[,...]
    Specify list of CCU RPC interfaces. HMCCU will register a RPC server for each interface. Either interface BidCos-RF or HmIP-RF (HmIP only) is default. Valid interfaces are:

    diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUCHN.pm index d88764516..023ba0f6c 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.039 +# Version 5.0 # # (c) 2021 zap (zap01 t-online de) # @@ -71,6 +71,18 @@ sub HMCCUCHN_Define ($@) my ($devname, $devtype, $devspec) = splice (@$a, 0, 3); my $ioHash; + my @errmsg = ( + "OK", + "Invalid or unknown CCU device name or address", + "Can't assign I/O device" + ); + + my @warnmsg = ( + "OK", + "Unknown warning message", + "Device type not known by HMCCU. Please set control and/or state channel with attributes controldatapoint and statedatapoint" + ); + my $existDev = HMCCU_ExistsClientDevice ($devspec, $devtype); return "FHEM device $existDev for CCU device $devspec already exists" if ($existDev ne ''); @@ -82,6 +94,7 @@ sub HMCCUCHN_Define ($@) $hash->{hmccu}{channels} = 1; $hash->{hmccu}{nodefaults} = $init_done ? 0 : 1; $hash->{hmccu}{semDefaults} = 0; + $hash->{hmccu}{detect} = 0; # Parse optional command line parameters my $n = 0; @@ -117,7 +130,7 @@ sub HMCCUCHN_Define ($@) else { # CCU not ready during FHEM start if (!defined($ioHash) || $ioHash->{ccustate} ne 'active') { - HMCCU_Log ($hash, 3, 'Cannot detect IO device, maybe CCU not ready. Trying later ...'); + HMCCU_Log ($hash, 3, "Cannot detect IO device, maybe CCU not ready or device doesn't exist on CCU"); $hash->{ccudevstate} = 'pending'; return undef; } @@ -125,10 +138,14 @@ sub HMCCUCHN_Define ($@) # Initialize FHEM device, set IO device my $rc = HMCCUCHN_InitDevice ($ioHash, $hash); - return 'Invalid or unknown CCU channel name or address' if ($rc == 1); - return "Can't assign I/O device $ioHash->{NAME}" if ($rc == 2); - - return undef; + if (HMCCU_IsIntNum ($rc)) { + return $errmsg[$rc] if ($rc > 0 && $rc < scalar(@errmsg)); + HMCCU_LogDisplay ($hash, 2, $warnmsg[-$rc]) if ($rc < 0 && -$rc < scalar(@warnmsg)); + return undef; + } + else { + return $rc; + } } ###################################################################### @@ -137,6 +154,7 @@ sub HMCCUCHN_Define ($@) # Return 0 on successful initialization or >0 on error: # 1 = Invalid channel name or address # 2 = Cannot assign IO device +# -2 = Device type not known by HMCCU ###################################################################### sub HMCCUCHN_InitDevice ($$) @@ -168,11 +186,11 @@ sub HMCCUCHN_InitDevice ($$) HMCCU_UpdateDevice ($ioHash, $devHash); HMCCU_UpdateDeviceRoles ($ioHash, $devHash); - return -2 if (!defined($detect) || $detect->{level} == 0); # Device not detected + return -2 if (!defined($detect) || $detect->{level} == 0); - if (!HMCCU_SetDefaultSCDatapoints ($ioHash, $devHash, $detect)) { - HMCCU_Log ($devHash, 2, "Cannot set default state- and control datapoints"); - } + my ($sc, $sd, $cc, $cd, $rsd, $rcd) = HMCCU_SetDefaultSCDatapoints ($ioHash, $devHash, $detect, 1); + HMCCU_Log ($devHash, 2, "Cannot set default state- and/or control datapoints. Maybe device type not known by HMCCU") + if ($rsd == 0 && $rcd == 0); HMCCU_SetInitialAttributes ($ioHash, $name); @@ -233,7 +251,11 @@ sub HMCCUCHN_Attr ($@) $clHash->{IODev} = $defs{$attrval}; } elsif ($attrname eq 'statevals') { - return 'Device is read only' if ($clHash->{readonly} eq 'yes'); + return 'Attribute statevals ignored. Device is read only' if ($clHash->{readonly} eq 'yes'); + return 'Attribute statevals ignored. Device type is known by HMCCU' if ($clHash->{hmccu}{detect} > 0); + if ($init_done && !HMCCU_IsValidControlDatapoint ($clHash)) { + HMCCU_LogDisplay ($clHash, 2, 'Warning: Attribute controldatapoint not set or set to invalid datapoint'); + } } elsif ($attrname =~ /^(state|control)datapoint$/) { my $role = HMCCU_GetChannelRole ($clHash); @@ -248,9 +270,9 @@ sub HMCCUCHN_Attr ($@) if (exists($clHash->{hmccu}{roleCmds}) && (!exists($clHash->{hmccu}{control}{chn}) || $clHash->{hmccu}{control}{chn} eq '')); if ($init_done) { - if (!HMCCU_SetDefaultSCDatapoints ($ioHash, $clHash)) { - HMCCU_Log ($clHash, 2, "Cannot set default state- and control datapoints"); - } + my ($sc, $sd, $cc, $cd, $rsd, $rcd) = HMCCU_SetDefaultSCDatapoints ($ioHash, $clHash, undef, 1); + HMCCU_Log ($clHash, 2, "Cannot set default state- and/or control datapoints") + if ($rsd == 0 && $rcd == 0); } } } @@ -282,7 +304,7 @@ sub HMCCUCHN_Set ($@) if (HMCCU_IsRPCStateBlocking ($ioHash)); # Build set command syntax - my $syntax = 'clear defaults:reset,update'; + my $syntax = 'clear defaults:reset,update,old'; # Command readingFilter depends on readable datapoints my ($add, $chn) = split(":", $hash->{ccuaddr}); @@ -329,7 +351,10 @@ sub HMCCUCHN_Set ($@) } elsif ($lcopt eq 'defaults') { my $mode = shift @$a // 'update'; - my $rc = HMCCU_SetDefaultAttributes ($hash, { mode => $mode, role => undef, roleChn => undef }); + my $rc = 0; + if ($mode ne 'old') { + $rc = HMCCU_SetDefaultAttributes ($hash, { mode => $mode, role => undef, roleChn => undef }); + } $rc = HMCCU_SetDefaults ($hash) if (!$rc); HMCCU_RefreshReadings ($hash) if ($rc); return HMCCU_SetError ($hash, $rc == 0 ? "No default attributes found" : "OK"); @@ -396,9 +421,10 @@ sub HMCCUCHN_Get ($@) return HMCCU_ExecuteGetDeviceInfoCommand ($ioHash, $hash, $devAddr, defined($extended) ? 1 : 0); } elsif ($lcopt =~ /^(config|values|update)$/) { + my $filter = shift @$a; my ($devAddr, undef) = HMCCU_SplitChnAddr ($ccuaddr); my @addList = ($devAddr, "$devAddr:0", $ccuaddr); - my $result = HMCCU_ExecuteGetParameterCommand ($ioHash, $hash, $lcopt, \@addList); + my $result = HMCCU_ExecuteGetParameterCommand ($ioHash, $hash, $lcopt, \@addList, $filter); return HMCCU_SetError ($hash, "Can't get device description") if (!defined($result)); return HMCCU_DisplayGetParameterResult ($ioHash, $hash, $result); } @@ -503,12 +529,13 @@ sub HMCCUCHN_Get ($@) set temp_control datapoint SET_TEMPERATURE 21
    set temp_control datapoint AUTO_MODE 1 SET_TEMPERATURE=21

  • -
  • set <name> defaults ['reset'|'update']
    +
  • set <name> defaults ['reset'|'old'|'update']
    Set default attributes for CCU device type. Default attributes are only available for some device types and for some channels of a device type. If option 'reset' is specified, the following attributes are deleted before the new attributes are set: 'ccureadingname', 'ccuscaleval', 'eventMap', 'substexcl', 'webCmd', 'widgetOverride'. - During update to version 4.4 it's recommended to use option 'reset'. + During update to version 4.4 it's recommended to use option 'reset'. With option 'old' + the attributes are set according to HMCCU 4.3 defaults mechanism.

  • set <name> down [<value>]
    [dimmer, blind] Decrement value of datapoint LEVEL. This command is only available @@ -522,7 +549,7 @@ sub HMCCUCHN_Get ($@) set myswitch on-for-timer 300

  • set <name> on-till <timestamp>
    - [switch] Switch device on until timestamp. Parameter timestamp can be a time in + [switch,dimmer] Switch device on until timestamp. Parameter timestamp can be a time in format HH:MM or HH:MM:SS. This command is only available if channel contains a datapoint ON_TIME.

  • @@ -545,7 +572,7 @@ sub HMCCUCHN_Get ($@) channel contains a datapoint STOP.
  • set <name> toggle
    - Toggle state datapoint between values defined by attribute 'statevals'. This command is + Toggle state datapoint between values defined by attribute 'statevals' or by channel role. This command is only available if state values can be detected or are defined by using attribute 'statevals'. Toggling supports more than two state values.

    Example: Toggle blind actor between states 'open', 'half' and 'close'
    @@ -570,8 +597,9 @@ sub HMCCUCHN_Get ($@) Get

      -
    • get <name> config
      - Get configuration parameters of device and channel. +
    • get <name> config [<filter-expr>]
      + Get configuration parameters of device and channel. If filter-expr is specified, + only parameters matching the expression are stored as readings.
      Values related to configuration or link parameters are stored as readings beginning with "R-" for MASTER parameter set and "L-" for LINK parameter set. Prefixes "R-" and "L-" can be modified with attribute 'ccuReadingPrefix'. Whether parameters are @@ -614,11 +642,13 @@ sub HMCCUCHN_Get ($@) information to your post in the FHEM forum, if you have a question about the integration of a new device. See also command 'get deviceInfo'.

    • -
    • get <name> update
      +
    • get <name> update [<filter-expr>]
      Update all readings for all parameters of all parameter sets (MASTER, LINK, VALUES). + If filter-expr is specified, only parameters matching the expression are stored as readings.

    • -
    • get <name> values
      +
    • get <name> values [<filter-expr>]
      Update all readings for all parameters of parameter set VALUES (datapoints). + If filter-expr is specified, only parameters matching the expression are stored as readings.

    • get <name> weekProgram [<program-number>|all]
      Display week programs. This command is only available if a device supports week programs. @@ -827,14 +857,18 @@ sub HMCCUCHN_Get ($@) state datapoint cannot be detected automatically.

    • -
    • statevals <text>:<text>[,...]
      - Define substitution for values of set commands. The parameters text are available - as set commands. +
    • statevals <new-command>:<control-datapoint-value>[,...]
      + Define set commands for control datapoint. This attribute should only be used if the device + type is not recognized by HMCCU. Using this attribute for automatically detected devices + could lead to problems!

      - Example:
      + Example: controldatapoint of a device is STATE. Device is not recognized by HMCCU:
      + # Define 2 new commands on and off representing the possible states of STATE:
      attr my_switch statevals on:true,off:false
      - set my_switch on + # After attr the commands on and off are available:
      + set my_switch on
      + set my_switch off

    • @@ -862,7 +896,9 @@ sub HMCCUCHN_Get ($@)
    • substitute <subst-rule>[;...]
      - Define substitutions for datapoint/reading values. Syntax of subst-rule is

      + Define substitutions for datapoint/reading values. This attribute is helpful / necessary if + a device is not automatically detected by HMCCU.
      + Syntax of subst-rule is

      [[<type>:][<channelno>.]<datapoint>[,...]!]<{#n1-m1|regexp}>:<text>[,...]

      Parameter type is a valid channel type/role, i.e. "SHUTTER_CONTACT". diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm index b0fc37393..e63b40ec2 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.048 +# Version 5.0 # # (c) 2021 zap (zap01 t-online de) # @@ -95,7 +95,7 @@ sub HMCCUDEV_Define ($@) # Handle some legacy options return 'Virtual devices are no longer supported. Use FHEM built in features like readingsgroup or structure' if ($devspec eq 'virtual'); - HMCCU_Log ($hash, 2, "Found old device definition syntax using group or groupexp. Group options will be ignored in furture versions.") + HMCCU_Log ($hash, 2, "Found old device definition syntax using group or groupexp. Group options will be ignored in future versions.") if (exists($h->{group}) || exists($h->{groupexp})); # Store some definitions for delayed initialization @@ -106,6 +106,7 @@ sub HMCCUDEV_Define ($@) $hash->{hmccu}{nodefaults} = $init_done ? 0 : 1; $hash->{hmccu}{semDefaults} = 0; $hash->{hmccu}{forcedev} = 0; + $hash->{hmccu}{detect} = 0; # Parse optional command line parameters foreach my $arg (@$a) { @@ -139,7 +140,7 @@ sub HMCCUDEV_Define ($@) else { # CCU not ready during FHEM start if (!defined($ioHash) || $ioHash->{ccustate} ne 'active') { - HMCCU_Log ($hash, 3, 'Cannot detect IO device, maybe CCU not ready. Trying later ...'); + HMCCU_Log ($hash, 3, "Cannot detect IO device, maybe CCU not ready or device doesn't exist on CCU"); $hash->{ccudevstate} = 'pending'; return undef; } @@ -149,7 +150,7 @@ sub HMCCUDEV_Define ($@) my $rc = HMCCUDEV_InitDevice ($ioHash, $hash); if (HMCCU_IsIntNum ($rc)) { return $errmsg[$rc] if ($rc > 0 && $rc < scalar(@errmsg)); - HMCCU_LogDisplay ($hash, 2, $warnmsg[-$rc]) if ($init_done && $rc < 0 && -$rc < scalar(@warnmsg)); + HMCCU_LogDisplay ($hash, 2, $warnmsg[-$rc]) if ($rc < 0 && -$rc < scalar(@warnmsg)); return undef; } else { @@ -168,7 +169,8 @@ sub HMCCUDEV_Define ($@) # 5 = Type of virtual device not defined # 6 = Device type not found # 7 = Too many virtual devices -# -1 = Control channel must be specified +# -1 = Control channel ambiguous +# -2 = Device type not known by HMCCU ###################################################################### sub HMCCUDEV_InitDevice ($$) @@ -209,11 +211,11 @@ sub HMCCUDEV_InitDevice ($$) HMCCU_UpdateDevice ($ioHash, $devHash); HMCCU_UpdateDeviceRoles ($ioHash, $devHash); - return -2 if (!defined($detect) || $detect->{level} == 0); # Device not detected + return -2 if (!defined($detect) || $detect->{level} == 0); - if (!HMCCU_SetDefaultSCDatapoints ($ioHash, $devHash, $detect)) { - HMCCU_Log ($devHash, 2, "Cannot set default state- and control datapoints"); - } + my ($sc, $sd, $cc, $cd, $rsd, $rcd) = HMCCU_SetDefaultSCDatapoints ($ioHash, $devHash, $detect, 1); + HMCCU_Log ($devHash, 2, "Cannot set default state- and/or control datapoints. Maybe device type not known by HMCCU") + if ($rsd == 0 && $rcd == 0); HMCCU_SetInitialAttributes ($ioHash, $name); @@ -310,7 +312,11 @@ sub HMCCUDEV_Attr ($@) $clHash->{IODev} = $defs{$attrval}; } elsif ($attrname eq 'statevals') { - return "Device is read only" if ($clHash->{readonly} eq 'yes'); + return 'Attribute statevals ignored. Device is read only' if ($clHash->{readonly} eq 'yes'); + return 'Attribute statevals ignored. Device type is known by HMCCU' if ($clHash->{hmccu}{detect} > 0); + if ($init_done && !HMCCU_IsValidControlDatapoint ($clHash)) { + HMCCU_LogDisplay ($clHash, 2, 'Warning: Attribute controldatapoint not set or set to invalid datapoint'); + } } elsif ($attrname =~ /^(state|control)(channel|datapoint)$/) { my $chn = $attrval; @@ -329,11 +335,6 @@ sub HMCCUDEV_Attr ($@) my $role = HMCCU_GetChannelRole ($clHash, $chn); return "Invalid value $attrval" if (!HMCCU_SetSCDatapoints ($clHash, $attrname, $attrval, $role)); - if ($init_done && exists($clHash->{hmccu}{control}{chn}) && $clHash->{hmccu}{control}{chn} ne '') { - HMCCU_UpdateRoleCommands ($ioHash, $clHash, $clHash->{hmccu}{control}{chn}); - HMCCU_UpdateAdditionalCommands ($ioHash, $clHash, $clHash->{hmccu}{control}{chn}, $clHash->{hmccu}{control}{dpt}) - if (exists($clHash->{hmccu}{control}{dpt}) && $clHash->{hmccu}{control}{dpt} ne ''); - } } } elsif ($cmd eq 'del') { @@ -344,9 +345,10 @@ sub HMCCUDEV_Attr ($@) if (exists($clHash->{hmccu}{roleCmds}) && (!exists($clHash->{hmccu}{control}{chn}) || $clHash->{hmccu}{control}{chn} eq '')); if ($init_done) { - if (!HMCCU_SetDefaultSCDatapoints ($ioHash, $clHash)) { - HMCCU_Log ($clHash, 2, "Cannot set default state- and control datapoints"); - } + # Try to set default state and control datapoint and update command list + my ($sc, $sd, $cc, $cd, $rsd, $rcd) = HMCCU_SetDefaultSCDatapoints ($ioHash, $clHash, undef, 1); + HMCCU_Log ($clHash, 2, "Cannot set default state- and/or control datapoints") + if ($rsd == 0 && $rcd == 0); } } } @@ -377,7 +379,7 @@ sub HMCCUDEV_Set ($@) if (HMCCU_IsRPCStateBlocking ($ioHash)); # Build set command syntax - my $syntax = 'clear defaults:reset,update'; + my $syntax = 'clear defaults:reset,update,old'; # Command readingFilter depends on readable datapoints my @dpRList = (); @@ -422,7 +424,10 @@ sub HMCCUDEV_Set ($@) } elsif ($lcopt eq 'defaults') { my $mode = shift @$a // 'update'; - my $rc = HMCCU_SetDefaultAttributes ($hash, { mode => $mode, role => undef, roleChn => undef }); + my $rc = 0; + if ($mode ne 'old') { + $rc = HMCCU_SetDefaultAttributes ($hash, { mode => $mode, role => undef, roleChn => undef }); + } $rc = HMCCU_SetDefaults ($hash) if (!$rc); HMCCU_RefreshReadings ($hash) if ($rc); return HMCCU_SetError ($hash, $rc == 0 ? 'No default attributes found' : 'OK'); @@ -503,13 +508,14 @@ sub HMCCUDEV_Get ($@) return HMCCU_ExecuteGetDeviceInfoCommand ($ioHash, $hash, $ccuaddr, defined($extended) ? 1 : 0); } elsif ($lcopt =~ /^(config|values|update)$/) { + my $filter = shift @$a; my @addList = ($ccuaddr); my $devDesc = HMCCU_GetDeviceDesc ($ioHash, $ccuaddr, $ccuif); return HMCCU_SetError ($hash, "Can't get device description") if (!defined($devDesc)); push @addList, split (',', $devDesc->{CHILDREN}); - my $result = HMCCU_ExecuteGetParameterCommand ($ioHash, $hash, $lcopt, \@addList); + my $result = HMCCU_ExecuteGetParameterCommand ($ioHash, $hash, $lcopt, \@addList, $filter); return HMCCU_SetError ($hash, "Can't get device description") if (!defined($result)); return HMCCU_DisplayGetParameterResult ($ioHash, $hash, $result); } @@ -608,12 +614,13 @@ sub HMCCUDEV_Get ($@) set temp_control datapoint 2.SET_TEMPERATURE 21
      set temp_control datapoint 2.AUTO_MODE 1 2.SET_TEMPERATURE 21

    • -
    • set <name> defaults ['reset'|'update']
      +
    • set <name> defaults ['reset'|'old'|'update']
      Set default attributes for CCU device type. Default attributes are only available for some device types and for some channels of a device type. If option 'reset' is specified, the following attributes are deleted before the new attributes are set: 'ccureadingname', 'ccuscaleval', 'eventMap', 'substexcl', 'webCmd', 'widgetOverride'. - During update to version 4.4 it's recommended to use option 'reset'. + During update to version 4.4 it's recommended to use option 'reset'. With option 'old' + the attributes are set according to HMCCU 4.3 defaults mechanism.

    • set <name> down [<value>]
      see HMCCUCHN diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm index a169392b5..8747e9b42 100755 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCURPCPROC.pm @@ -4,7 +4,7 @@ # # $Id: 88_HMCCURPCPROC.pm 18745 2019-02-26 17:33:23Z zap $ # -# Version 4.4.014 +# Version 5.0 # # Subprocess based RPC Server module for HMCCU. # @@ -39,7 +39,7 @@ require "$attr{global}{modpath}/FHEM/88_HMCCU.pm"; ###################################################################### # HMCCURPC version -my $HMCCURPCPROC_VERSION = '4.4.014'; +my $HMCCURPCPROC_VERSION = '5.0'; # Maximum number of events processed per call of Read() my $HMCCURPCPROC_MAX_EVENTS = 100; @@ -126,6 +126,17 @@ my %RPC_METHODS = ( ); # RPC event types +# EV = Event +# ND = New device +# DD = Delete device +# RD = Replace device +# RA = Readded device +# UD = Update device +# IN = Init RPC connection +# EX = Exit RPC process +# SL = Server loop +# ST = Statistics (not in list of event types) +# TO = Timeout my @RPC_EVENT_TYPES = ('EV', 'ND', 'DD', 'RD', 'RA', 'UD', 'IN', 'EX', 'SL', 'TO'); @@ -138,6 +149,7 @@ sub HMCCURPCPROC_Initialize ($); sub HMCCURPCPROC_Define ($$); sub HMCCURPCPROC_InitDevice ($$); sub HMCCURPCPROC_Undef ($$); +sub HMCCURPCPROC_Rename ($$); sub HMCCURPCPROC_DelayedShutdown ($); sub HMCCURPCPROC_Shutdown ($); sub HMCCURPCPROC_Attr ($@); @@ -237,6 +249,7 @@ sub HMCCURPCPROC_Initialize ($) $hash->{DefFn} = 'HMCCURPCPROC_Define'; $hash->{UndefFn} = 'HMCCURPCPROC_Undef'; + $hash->{RenameFn} = 'HMCCURPCPROC_Rename'; $hash->{SetFn} = 'HMCCURPCPROC_Set'; $hash->{GetFn} = 'HMCCURPCPROC_Get'; $hash->{ReadFn} = 'HMCCURPCPROC_Read'; @@ -447,6 +460,21 @@ sub HMCCURPCPROC_Undef ($$) return undef; } +###################################################################### +# Rename device +###################################################################### + +sub HMCCURPCPROC_Rename ($$) +{ + my ($newName, $oldName) = @_; + my $hash = $defs{$newName}; + + my $ioHash = $hash->{IODev}; + my $ifName = $hash->{rpcinterface}; + + $ioHash->{hmccu}{interfaces}{$ifName}{device} = $newName; +} + ###################################################################### # Delayed shutdown FHEM ###################################################################### @@ -531,7 +559,9 @@ sub HMCCURPCPROC_Attr ($@) $hash->{hmccu}{localaddr} = $hash->{hmccu}{defaultaddr}; } } - + + HMCCU_LogDisplay ($hash, 2, 'Please restart RPC server to apply attribute changes') if ($init_done); + return undef; } @@ -2403,6 +2433,11 @@ sub HMCCURPCPROC_HexDump ($$) { my ($name, $data) = @_; + if (!defined($data)) { + HMCCU_Log ($name, 4, 'HexDump called without data'); + return; + } + my $offset = 0; foreach my $chunk (unpack "(a16)*", $data) { diff --git a/fhem/contrib/HMCCU/FHEM/HMCCUConf.pm b/fhem/contrib/HMCCU/FHEM/HMCCUConf.pm index f1e8c1e4c..dd79f04ea 100644 --- a/fhem/contrib/HMCCU/FHEM/HMCCUConf.pm +++ b/fhem/contrib/HMCCU/FHEM/HMCCUConf.pm @@ -4,7 +4,7 @@ # # $Id: HMCCUConf.pm 18552 2019-02-10 11:52:28Z zap $ # -# Version 4.8.030 +# Version 5.0 # # Configuration parameters for HomeMatic devices. # @@ -28,7 +28,7 @@ use vars qw(%HMCCU_CHN_DEFAULTS); use vars qw(%HMCCU_DEV_DEFAULTS); use vars qw(%HMCCU_SCRIPTS); -$HMCCU_CONFIG_VERSION = '4.8.030'; +$HMCCU_CONFIG_VERSION = '5.0'; ###################################################################### # Map subtype to default role. Subtype is only available for HMIP @@ -64,9 +64,18 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; 'ROTARY_HANDLE_TRANSCEIVER' => { F => 3, S => 'STATE', C => '', V => '', P => 2 }, + 'MULTI_MODE_INPUT_TRANSMITTER' => { + F => 3, S => 'STATE', C => '', V => '', P => 1 + }, 'ALARM_SWITCH_VIRTUAL_RECEIVER' => { F => 3, S => 'ACOUSTIC_ALARM_ACTIVE', C => 'ACOUSTIC_ALARM_SELECTION', V => '', P => 2 }, + 'DOOR_LOCK_STATE_TRANSMITTER' => { + F => 3, S => 'LOCK_STATE', C => 'LOCK_TARGET_LEVEL', V => 'open:2,unlocked:1,locked:0' + }, + 'ACCELERATION_TRANSCEIVER' => { + F => 3, S => 'MOTION', C => '', V => '', P => 1 + }, 'MOTION_DETECTOR' => { F => 3, S => 'MOTION', C => '', V => '', P => 1 }, @@ -76,6 +85,9 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; 'PRESENCEDETECTOR_TRANSCEIVER' => { F => 3, S => 'PRESENCE_DETECTION_STATE', C => 'PRESENCE_DETECTION_ACTIVE', V => 'active:1,inactive:0', P => 2 }, + 'PASSAGE_DETECTOR_DIRECTION_TRANSMITTER' => { + F => 3, S => 'CURRENT_PASSAGE_DIRECTION', C => '', V => '', P => 1 + }, 'SMOKE_DETECTOR' => { F => 3, S => 'BidCos-RF:STATE,SMOKE_DETECTOR_ALARM_STATUS', C => 'HmIP-RF:SMOKE_DETECTOR_COMMAND', V => '', P => 2 }, @@ -137,7 +149,7 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; F => 3, S => 'TEMPERATURE', C => '', V => '', P => 1 }, 'WEATHER_TRANSMIT' => { - F => 3, S => 'TEMPERATURE', C => '', V => '', P => 1 + F => 3, S => 'ACTUAL_TEMPERATURE', C => '', V => '', P => 1 }, 'CLIMATE_TRANSCEIVER' => { F => 3, S => 'ACTUAL_TEMPERATURE', C => '', V => '', P => 1 @@ -156,6 +168,9 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; }, 'CLIMATECONTROL_VENT_DRIVE' => { F => 3, S => 'VALVE_STATE', C => '', V => '', P => 2 + }, + 'WATER_DETECTION_TRANSMITTER' => { + F => 3, S => 'ALARMSTATE', C => '', V => '', P => 1 } ); @@ -183,6 +198,14 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; '(C#\.)?LEVEL$:+pct', 'DIMMER_VIRTUAL_RECEIVER' => '(C#\.)?LEVEL$:+pct', + 'KEY' => + '(C#\.)?PRESS_(SHORT|LONG)$:+pressed', + 'KEY_TRANSCEIVER' => + '(C#\.)?PRESS_(SHORT|LONG)$:+pressed', + 'VIRTUAL_KEY' => + '(C#\.)?PRESS_(SHORT|LONG)$:+pressed', + 'ACCELERATION_TRANSCEIVER' => + '(C#\.)?MOTION:motion', 'MOTION_DETECTOR' => '^(C#\.)?BRIGHTNESS$:brightness;(C#\.)?MOTION:motion', 'MOTIONDETECTOR_TRANSCEIVER' => @@ -224,23 +247,37 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; # Set commands related to channel role # Role => { Command-Definition, ... } # Command-Defintion: -# Command[:InterfaceExpr] => 'Datapoint-Definition[:Function] [...]' +# Command[:InterfaceExpr] => [No:]Datapoint-Def[:Function] [...]' +# No: +# Execution order of subcommands. By default subcommands are +# executed from left to right. # Function: # A Perl function name -# Datapoint-Definition: -# Paramset:Datapoint:[Parameter=]FixedValue -# Paramset:Datapoint:?Parameter -# Paramset:Datapoint:?Parameter=Default-Value -# Paramset:Datapoint:#Parameter[=FixedValue,[...]] -# Paramset:Datapoint:*Parameter=Default-Value +# Datapoint-Def: +# Paramset:Datapoints:[Parameter=]FixedValue +# Paramset:Datapoints:?Parameter +# Paramset:Datapoints:?Parameter=Default-Value +# Paramset:Datapoints:#Parameter[=FixedValue,[...]] +# Paramset:Datapoints:*Parameter=Default-Value # Paramset: # V=VALUES, M=MASTER (channel), D=MASTER (device), I=INTERNAL +# Datapoints: +# List of parameter names separated by ',' # Parameter characters: # ? = any value is accepted # # = If datapoint is of type ENUM, values are taken from # parameter set description. Otherwise a list of values must -# be specified. +# be specified after '='. # * = internal value $hash->{hmccu}{values}{parameterName} +# FixedValue: Parameter values are detected in the following order: +# 1. If command parameter name is identical with controldatapoint, +# option values are taken from controldatapoint definition {V}. The +# FixedValues are used as lookup key into HMCCU_STATECCONTROL. +# The command options are identical to the FixedValues. +# 2. FixedValues are treated as option values. The option +# names are taken from HMCCU_CONVERSIONS by using FixedValues as +# lookup key. +# 3. As a fallback command options and option values are identical. # If Default-Value is preceeded by + or -, value is added to or # subtracted from current datapoint value ###################################################################### @@ -248,11 +285,14 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; %HMCCU_ROLECMDS = ( 'MOTIONDETECTOR_TRANSCEIVER' => { 'detection' => 'V:MOTION_DETECTION_ACTIVE:#detection=inactive,active', - 'reset' => 'V:RESET_MOTION:1' + 'reset' => 'V:RESET_MOTION:true' }, 'PRESENCEDETECTOR_TRANSCEIVER' => { 'detection' => 'V:PRESENCE_DETECTION_ACTIVE:#detection=inactive,active', - 'reset' => 'V:RESET_PRESENCE:1' + 'reset' => 'V:RESET_PRESENCE:true' + }, + 'PASSAGE_DETECTOR_DIRECTION_TRANSMITTER' => { + 'detection' => 'M:PASSAGE_DETECTION,CHANNEL_OPERATION_MODE:#inactive,active' }, 'SMOKE_DETECTOR' => { 'command' => 'V:SMOKE_DETECTOR_COMMAND:#command' @@ -262,6 +302,11 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; 'acousticAlarm' => 'V:ACOUSTIC_ALARM_SELECTION:#alarmMode V:OPTICAL_ALARM_SELECTION:0 V:DURATION_UNIT:0 V:DURATION_VALUE:10', 'duration' => 'I:DURATION_VALUE:?duration I:DURATION_UNIT:#unit' }, + 'DOOR_LOCK_STATE_TRANSMITTER' => { + 'open' => 'V:LOCK_TARGET_LEVEL:2', + 'unlock' => 'V:LOCK_TARGET_LEVEL:1', + 'lock' => 'V:LOCK_TARGET_LEVEL:0' + }, 'KEY' => { 'on' => 'V:PRESS_SHORT:1', 'off' => 'V:PRESS_SHORT:1', @@ -309,19 +354,23 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; 'on-till' => 'V:ON_TIME:?time V:STATE:1' }, 'DIMMER' => { - 'pct' => 'V:LEVEL:?level V:ON_TIME:?time=0.0 V:RAMP_TIME:?ramp=0.5', + 'pct' => '3:V:LEVEL:?level 1:V:ON_TIME:?time=0.0 2:V:RAMP_TIME:?ramp=0.5', 'on' => 'V:LEVEL:100', 'off' => 'V:LEVEL:0', - 'on-for-timer' => 'V:ON_TIME:?duration V:STATE:1', - 'on-till' => 'V:ON_TIME:?time V:STATE:1', + 'on-for-timer' => 'V:ON_TIME:?duration V:LEVEL:100', + 'on-till' => 'V:ON_TIME:?time V:LEVEL:100', + 'up' => 'V:LEVEL:?delta=+10', + 'down' => 'V:LEVEL:?delta=-10', 'stop' => 'V:RAMP_STOP:1' }, 'DIMMER_VIRTUAL_RECEIVER' => { - 'pct' => 'V:LEVEL:?level V:ON_TIME:?time V:RAMP_TIME:?ramp', + 'pct' => '3:V:LEVEL:?level 1:V:ON_TIME:?time=0.0 2:V:RAMP_TIME:?ramp=0.5', 'on' => 'V:LEVEL:100', 'off' => 'V:LEVEL:0', - 'on-for-timer' => 'V:ON_TIME:?duration V:STATE:1', - 'on-till' => 'V:ON_TIME:?time V:STATE:1' + 'on-for-timer' => 'V:ON_TIME:?duration V:LEVEL:100', + 'on-till' => 'V:ON_TIME:?time V:LEVEL:100', + 'up' => 'V:LEVEL:?delta=+10', + 'down' => 'V:LEVEL:?delta=-10', }, 'THERMALCONTROL_TRANSMIT' => { 'desired-temp' => 'V:SET_TEMPERATURE:?temperature', @@ -371,9 +420,19 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; 'SHUTTER_CONTACT_TRANSCEIVER' => { '_none_' => '' }, + 'MULTI_MODE_INPUT_TRANSMITTER' => { + '_none_' => '' + }, + 'ACCELERATION_TRANSCEIVER' => { + '_none_' => '' + }, 'MOTION_DETECTOR' => { '_none_' => '' }, + 'DOOR_LOCK_STATE_TRANSMITTER' => { + 'cmdIcon' => 'open:fts_door_open unlock:secur_open lock:secur_lock', + 'webCmd' => 'lock:unlock:open' + }, 'MOTIONDETECTOR_TRANSCEIVER' => { 'cmdIcon' => 'reset:rc_BACK', 'webCmd' => 'detection:reset' @@ -382,6 +441,9 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; 'cmdIcon' => 'reset:rc_BACK', 'webCmd' => 'detection:reset' }, + 'PASSAGE_DETECTOR_DIRECTION_TRANSMITTER' => { + '_none_' => '' + }, 'KEY' => { 'event-on-update-reading' => 'PRESS.*', 'cmdIcon' => 'press:taster', @@ -461,6 +523,9 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; }, 'CLIMATECONTROL_VENT_DRIVE' => { '_none_' => '' + }, + 'WATER_DETECTION_TRANSMITTER' => { + '_none_' => '' } ); @@ -473,16 +538,23 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; ###################################################################### %HMCCU_CONVERSIONS = ( + 'ACCELERATION_TRANSCEIVER' => { + 'MOTION' => { '0' => 'noMotion', 'false' => 'noMotion', '1' => 'motion', 'true' => 'motion' } + }, 'MOTION_DETECTOR' => { 'MOTION' => { '0' => 'noMotion', 'false' => 'noMotion', '1' => 'motion', 'true' => 'motion' } }, 'MOTIONDETECTOR_TRANSCEIVER' => { 'MOTION' => { '0' => 'noMotion', 'false' => 'noMotion', '1' => 'motion', 'true' => 'motion' }, - 'MOTION_DETECTION_ACTIVE' => { '0' => 'inactive', 'false' => 'inactive', '1' => 'active', 'true', 'active' } + 'MOTION_DETECTION_ACTIVE' => { '0' => 'inactive', 'false' => 'inactive', '1' => 'active', 'true' => 'active' } }, 'PRESENCEDETECTOR_TRANSCEIVER' => { 'PRESENCE_DETECTION_STATE' => { '0' => 'noPresence', 'false' => 'noPresence', '1' => 'presence', 'true' => 'presence' }, - 'PRESENCE_DETECTION_ACTIVE' => { '0' => 'inactive', 'false' => 'inactive', '1' => 'active', 'true', 'active' } + 'PRESENCE_DETECTION_ACTIVE' => { '0' => 'inactive', 'false' => 'inactive', '1' => 'active', 'true' => 'active' } + }, + 'PASSAGE_DETECTOR_DIRECTION_TRANSMITTER' => { + 'PASSAGE_DETECTION' => { '0' => 'inactive', 1 => 'active' }, + 'CHANNEL_OPERATION_MODE' => { '0' => 'inactive', 1 => 'active'} }, 'KEY' => { 'PRESS_SHORT' => { '1' => 'pressed', 'true' => 'pressed' }, @@ -564,6 +636,9 @@ $HMCCU_CONFIG_VERSION = '4.8.030'; 'CLIMATECONTROL_REGULATOR' => { 'SETPOINT' => { '4.5' => 'off', '30.5' => 'on' } }, + 'WATER_DETECTION_TRANSMITTER' => { + 'ALARMSTATE' => { '0' => 'noAlarm', '1' => 'Alarm', 'false' => 'noAlarm', 'true' => 'alarm' } + }, 'DEFAULT' => { 'AES_KEY' => { '0' => 'off', 'false' => 'off', '1' => 'on', 'true' => 'on' }, 'LOW_BAT' => { '0' => 'ok', 'false' => 'ok', '1' => 'low', 'true' => 'low' }, diff --git a/fhem/contrib/HMCCU/controls_HMCCU.txt b/fhem/contrib/HMCCU/controls_HMCCU.txt index ced64f86e..e69df7c87 100644 --- a/fhem/contrib/HMCCU/controls_HMCCU.txt +++ b/fhem/contrib/HMCCU/controls_HMCCU.txt @@ -1,5 +1,5 @@ -UPD 2021-06-07_18:52:09 102813 FHEM/88_HMCCURPCPROC.pm -UPD 2021-06-07_18:52:09 82168 FHEM/HMCCUConf.pm -UPD 2021-06-07_18:52:09 42050 FHEM/88_HMCCUCHN.pm -UPD 2021-06-07_18:52:09 344833 FHEM/88_HMCCU.pm -UPD 2021-06-07_18:52:09 30896 FHEM/88_HMCCUDEV.pm +UPD 2021-09-09_19:12:29 103713 FHEM/88_HMCCURPCPROC.pm +UPD 2021-09-09_18:45:27 84909 FHEM/HMCCUConf.pm +UPD 2021-09-02_19:33:39 43994 FHEM/88_HMCCUCHN.pm +UPD 2021-09-08_20:42:05 358142 FHEM/88_HMCCU.pm +UPD 2021-09-02_19:33:39 31267 FHEM/88_HMCCUDEV.pm
  • Key$col