From 4164f34ce6d3b76efb1611e50515ba1e880632c7 Mon Sep 17 00:00:00 2001 From: fhemzap <> Date: Wed, 2 Mar 2016 16:31:59 +0000 Subject: [PATCH] HMCCU: new version git-svn-id: https://svn.fhem.de/fhem/trunk@10979 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm | 164 +++++++++++++++++++------ 1 file changed, 126 insertions(+), 38 deletions(-) diff --git a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm index c99183b1a..acdb074b0 100644 --- a/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm +++ b/fhem/contrib/HMCCU/FHEM/88_HMCCUDEV.pm @@ -1,16 +1,17 @@ -################################################################ +##################################################################### # # 88_HMCCUDEV.pm # # $Id:$ # -# Version 2.7 +# Version 2.8 # # (c) 2016 zap (zap01 t-online de) # -################################################################ +##################################################################### # -# define HMCCUDEV [readonly] +# define HMCCUDEV {|virtual} [statechannel] [readonly] +# [{group={|}[,...]|groupexp=}] # # set control # set datapoint . @@ -26,19 +27,21 @@ # get configdesc [] [] # get update # +# attr ccuget { State | Value } # attr ccureadings { 0 | 1 } # attr ccureadingformat { address | name } -# attr ccureadingfilter +# attr ccureadingfilter [,...] # attr ccuverify { 0 | 1 } # attr controldatapoint . +# attr mapdatapoints .=.[,...] # attr statechannel # attr statedatapoint # attr statevals :[,...] # attr substitute :[,...] # -################################################################ +##################################################################### # Requires module 88_HMCCU -################################################################ +##################################################################### package main; @@ -68,7 +71,7 @@ sub HMCCUDEV_Initialize ($) $hash->{GetFn} = "HMCCUDEV_Get"; $hash->{AttrFn} = "HMCCUDEV_Attr"; - $hash->{AttrList} = "IODev ccureadingfilter ccureadingformat:name,address ccureadings:0,1 ccustate ccuget:State,Value ccuverify:0,1 statevals substitute statechannel statedatapoint controldatapoint stripnumber:0,1,2 ". $readingFnAttributes; + $hash->{AttrList} = "IODev ccureadingfilter:textField-long ccureadingformat:name,address ccureadings:0,1 ccuget:State,Value ccuverify:0,1 mapdatapoints:textField-long statevals substitute statechannel statedatapoint controldatapoint stripnumber:0,1,2 ". $readingFnAttributes; } ##################################### @@ -81,22 +84,41 @@ sub HMCCUDEV_Define ($@) my $name = $hash->{NAME}; my @a = split("[ \t][ \t]*", $def); - my $usage = "Usage: define HMCCUDEV {|} [] [readonly]"; + my $usage = "Usage: define $name HMCCUDEV {device|'virtual'} [state-channel] ['readonly'] [{groupexp=regexp|group={device|channel}[,...]]"; return $usage if (@a < 3); my $devname = shift @a; my $devtype = shift @a; my $devspec = shift @a; - return "Invalid or unknown CCU device name or address" if (! HMCCU_IsValidDevice ($devspec)); + my $hmccu_hash = undef; - if ($devspec =~ /^(.+)\.([A-Z]{3,3}[0-9]{7,7})$/) { + if ($devspec ne 'virtual') { + return "Invalid or unknown CCU device name or address" if (!HMCCU_IsValidDevice ($devspec)); + } + + if ($devspec eq 'virtual') { + # Virtual device FHEM only + my $no = 0; + foreach my $d (sort keys %defs) { + my $ch = $defs{$d}; + $hmccu_hash = $ch if ($ch->{TYPE} eq 'HMCCU' && !defined ($hmccu_hash)); + next if ($ch->{TYPE} ne 'HMCCUDEV' || $ch->{ccuif} ne 'VirtualDevices' || + $ch->{ccuname} ne 'none'); + $no++; + } + return "No IO device found" if (!defined ($hmccu_hash)); + $hash->{ccuif} = "VirtualDevices"; + $hash->{ccuaddr} = sprintf ("VIR%07d", $no+1); + $hash->{ccuname} = "none"; + } + elsif (HMCCU_IsDevAddr ($devspec, 1)) { # CCU Device address with interface $hash->{ccuif} = $1; $hash->{ccuaddr} = $2; $hash->{ccuname} = HMCCU_GetDeviceName ($hash->{ccuaddr}, ''); } - elsif ($devspec =~ /^[A-Z]{3,3}[0-9]{7,7}$/) { + elsif (HMCCU_IsDevAddr ($devspec, 0)) { # CCU Device address without interface $hash->{ccuaddr} = $devspec; $hash->{ccuname} = HMCCU_GetDeviceName ($devspec, ''); @@ -116,16 +138,61 @@ sub HMCCUDEV_Define ($@) $hash->{ccutype} = HMCCU_GetDeviceType ($hash->{ccuaddr}, ''); $hash->{channels} = HMCCU_GetDeviceChannels ($hash->{ccuaddr}); - $hash->{statevals} = 'devstate'; + + if ($hash->{ccuif} eq "VirtualDevices" && $hash->{ccuname} eq 'none') { + $hash->{statevals} = 'readonly'; + } + else { + $hash->{statevals} = 'devstate'; + } my $n = 0; my $arg = shift @a; while (defined ($arg)) { - return $usage if ($n == 2); + return $usage if ($n == 3); if ($arg eq 'readonly') { $hash->{statevals} = $arg; $n++; } + elsif ($arg =~ /^groupexp=/ && $hash->{ccuif} eq "VirtualDevices") { + my ($g, $gdev) = split ("=", $arg); + return $usage if (!defined ($gdev)); + my @devlist; + my $cnt = HMCCU_GetMatchingDevices ($hmccu_hash, $gdev, 'dev', \@devlist); + return "No matching CCU devices found" if ($cnt == 0); + $hash->{ccugroup} = shift @devlist; + foreach my $gd (@devlist) { + $hash->{ccugroup} .= ",".$gd; + } + } + elsif ($arg =~ /^group=/ && $hash->{ccuif} eq "VirtualDevices") { + my ($g, $gdev) = split ("=", $arg); + return $usage if (!defined ($gdev)); + my @gdevlist = split (",", $gdev); + $hash->{ccugroup} = '' if (@gdevlist > 0); + foreach my $gd (@gdevlist) { + my ($gda, $gdc, $gdo) = ('', '', '', ''); + + return "Invalid device or channel $gd" + if (!HMCCU_IsValidDevice ($gd)); + + if (HMCCU_IsDevAddr ($gd, 0) || HMCCU_IsChnAddr ($gd, 1)) { + $gdo = $gd; + } + else { + ($gda, $gdc) = HMCCU_GetAddress ($gd, '', ''); + $gdo = $gda; + $gdo .= ':'.$gdc if ($gdc ne ''); + } + + if (exists ($hash->{ccugroup}) && $hash->{ccugroup} ne '') { + $hash->{ccugroup} .= ",".$gdo; + } + else { + $hash->{ccugroup} = $gdo; + } + } + } elsif ($arg =~ /^[0-9]+$/) { $attr{$name}{statechannel} = $arg; $n++; @@ -136,6 +203,9 @@ sub HMCCUDEV_Define ($@) $arg = shift @a; } + return "No devices in group" if ($hash->{ccuif} eq "VirtualDevices" && ( + !exists ($hash->{ccugroup}) || $hash->{ccugroup} eq '')); + # Inform HMCCU device about client device AssignIoPort ($hash); @@ -169,6 +239,9 @@ sub HMCCUDEV_Attr ($@) $hash->{statevals} .= '|'.$statesubs[0]; } } + elsif ($attrname eq "mapdatapoints") { + return "Not a virtual device" if ($hash->{ccuif} ne "VirtualDevices"); + } } elsif ($cmd eq "del") { if ($attrname eq "statevals") { @@ -364,6 +437,10 @@ sub HMCCUDEV_Get ($@) my $result = ''; my $rc; + if ($hash->{ccuif} eq "VirtualDevices" && $hash->{ccuname} eq "none" && $opt ne 'update') { + return "HMCCUDEV: Unknown argument $opt, choose one of update:noArg"; + } + if ($opt eq 'devstate') { if ($statechannel eq '') { return HMCCUDEV_SetError ($hash, "No state channel specified"); @@ -420,8 +497,20 @@ sub HMCCUDEV_Get ($@) if ($ccuget !~ /^(Attr|State|Value)$/) { return HMCCUDEV_SetError ($hash, "Usage: get $name update [{'State'|'Value'}]"); } - $rc = HMCCU_GetUpdate ($hash, $hash->{ccuaddr}, $ccuget); - return HMCCUDEV_SetError ($hash, $rc) if ($rc < 0); + + if ($hash->{ccuname} ne 'none') { + $rc = HMCCU_GetUpdate ($hash, $hash->{ccuaddr}, $ccuget); + return HMCCUDEV_SetError ($hash, $rc) if ($rc < 0); + } + + # Update other devices belonging to group + if ($hash->{ccuif} eq "VirtualDevices" && exists ($hash->{ccugroup})) { + my @vdevs = split (",", $hash->{ccugroup}); + foreach my $vd (@vdevs) { + $rc = HMCCU_GetUpdate ($hash, $vd, $ccuget); + return HMCCUDEV_SetError ($hash, $rc) if ($rc < 0); + } + } return undef; } @@ -532,7 +621,7 @@ sub HMCCUDEV_SetError ($$) Define

    - define <name> HMCCUDEV {<device-name>|<device-address>} [<statechannel>] [readonly] + define <name> HMCCUDEV {<device-name>|<device-address>} [<statechannel>] [readonly] [{group={device|channel}[,...]|groupexp=regexp]

    If readonly parameter is specified no set command will be available.

    @@ -624,10 +713,10 @@ sub HMCCUDEV_SetError ($$)
  • ccureadings <0 | 1>
    If set to 1 values read from CCU will be stored as readings. Default is 1.

  • -
  • ccureadingfilter <datapoint-expr> -
    - Only datapoints matching specified expression are stored as - readings. +
  • ccureadingfilter <filter-rule[,...]>
    + Only datapoints matching specified expression are stored as readings. + Syntax for filter rule is: [channel-no:]RegExp
    + If channel-no is specified the following rule applies only to this channel.

  • ccureadingformat <address | name>
    Set format of readings. Default is 'name'. @@ -644,26 +733,25 @@ sub HMCCUDEV_SetError ($$) attr mydev webCmd control attr mydev widgetOverride control:slider,10,1,25

  • -
  • statechannel <channel-number> -
    - Channel for setting device state by devstate command. +
  • mapdatapoints <channel.datapoint>=<channel.datapoint>[,...] + Map channel to other channel in virtual devices (groups). Readings will be duplicated.

  • -
  • statedatapoint <datapoint> -
    - Datapoint for setting device state by devstate command. +
  • statechannel <channel-number>
    + Channel for setting device state by devstate command.

  • -
  • statevals <text>:<text>[,...] -
    - Define substitution for set commands values. The parameters <text> - are available as set commands. Example:
    - attr my_switch statevals on:true,off:false
    - set my_switch on +
  • statedatapoint <datapoint>
    + Datapoint for setting device state by devstate command.

  • -
  • substitude <subst-rule>[;...] -
    - Define substitions for reading values. Substitutions for parfile values must - be specified in parfiles. Syntax of subst-rule is

    - [datapoint!]<regexp1>:<text1>[,...] +
  • statevals <text>:<text>[,...]
    + Define substitution for set commands values. The parameters <text> + are available as set commands. Example:
    + attr my_switch statevals on:true,off:false
    + set my_switch on +

  • +
  • substitude <subst-rule>[;...]
    + Define substitions for reading values. Substitutions for parfile values must + be specified in parfiles. Syntax of subst-rule is

    + [datapoint!]<regexp>:<text>[,...]