################################################################ # # 88_HMCCUDEV.pm # # $Id:$ # # Version 1.9 # # (c) 2015 zap (zap01 t-online de) # ################################################################ # # define HMCCUDEV [readonly] # # set datapoint . # set devstate # set # # get devstate # get datapoint . # get update # # attr ccureadings { 0 | 1 } # attr statechannel # attr statevals :[,...] # attr substitute :[,...] # ################################################################ # Requires module 88_HMCCU ################################################################ package main; use strict; use warnings; use SetExtensions; sub HMCCUDEV_Define ($$); sub HMCCUDEV_Set ($@); sub HMCCUDEV_Get ($@); sub HMCCUDEV_Attr ($@); sub HMCCUDEV_SetError ($$); ##################################### # Initialize module ##################################### sub HMCCUDEV_Initialize ($) { my ($hash) = @_; $hash->{DefFn} = "HMCCUDEV_Define"; $hash->{SetFn} = "HMCCUDEV_Set"; $hash->{GetFn} = "HMCCUDEV_Get"; $hash->{AttrFn} = "HMCCUDEV_Attr"; $hash->{AttrList} = "IODev ccureadingformat:name,address,datapoint ccureadings:0,1 statevals substitute statechannel loglevel:0,1,2,3,4,5,6 ". $readingFnAttributes; } ##################################### # Define device ##################################### sub HMCCUDEV_Define ($$) { my ($hash, $def) = @_; my $name = $hash->{NAME}; my @a = split("[ \t][ \t]*", $def); return "Specifiy the CCU device name or address as parameters" if (@a < 3); my $devname = shift @a; my $devtype = shift @a; my $devspec = shift @a; if ($devspec =~ /^[A-Z]{3,3}[0-9]{7,7}$/) { # CCU Device address $hash->{ccuaddr} = $devspec; $hash->{ccuname} = HMCCU_GetDeviceName ($devspec, ''); } elsif ($devspec =~ /^(.*):/ || $devspec =~ /\..+$/) { # Channel and/or datapoint specified return "Channel or datapoint not allowed in CCU device name"; } else { # CCU Device name $hash->{ccuname} = $devspec; $hash->{ccuaddr} = HMCCU_GetAddress ($devspec, '', 1); } return "CCU device address not found" if ($hash->{ccuaddr} eq ''); return "CCU device name not found" if ($hash->{ccuname} eq ''); $hash->{ccutype} = HMCCU_GetDeviceType ($hash->{ccuaddr}, ''); $hash->{ccuif} = HMCCU_GetDeviceInterface ($hash->{ccuaddr}, ''); $hash->{statevals} = 'devstate'; $hash->{statechannel} = ''; my $arg = shift @a; while (defined ($arg)) { if ($arg eq 'readonly') { $hash->{statevals} = $arg; } else { return "State channel must be numeric" if ($arg !~ /^[0-9]+$/); $hash->{statechannel} = $arg; } $arg = shift @a; } # Inform HMCCU device about client device AssignIoPort ($hash); readingsSingleUpdate ($hash, "state", "Initialized", 1); return undef; } ##################################### # Set attribute ##################################### sub HMCCUDEV_Attr ($@) { my ($cmd, $name, $attrname, $attrval) = @_; if (defined ($attrval) && $cmd eq "set") { if ($attrname eq "IODev") { $defs{$name}{IODev} = $defs{$attrval}; } elsif ($attrname eq "statevals") { $defs{$name}{statevals} = "devstate"; my @states = split /,/,$attrval; foreach my $st (@states) { my @statesubs = split /:/,$st; next if (@statesubs != 2); $defs{$name}{statevals} .= '|'.$statesubs[0]; } } elsif ($attrname eq "statechannel") { $defs{$name}{statechannel} = $attrval; } } return undef; } ##################################### # Set commands ##################################### sub HMCCUDEV_Set ($@) { my ($hash, @a) = @_; my $name = shift @a; my $opt = shift @a; if (!defined ($hash->{IODev})) { return HMCCUDEV_SetError ($hash, "No IO device defined"); } if ($hash->{statevals} eq 'readonly') { return undef; } # my $statechannel = AttrVal ($name, "statechannel", ''); my $statechannel = $hash->{statechannel}; my $statevals = AttrVal ($name, "statevals", ''); my $substitute = AttrVal ($name, "substitute", ''); my $hmccu_hash = $hash->{IODev}; my $hmccu_name = $hash->{IODev}->{NAME}; # process set command par1 ... if ($opt eq 'datapoint') { my $objname = shift @a; my $objvalue = join ('%20', @a); if (!defined ($objname) || $objname !~ /^[0-9]+\..+$/ || !defined ($objvalue)) { return HMCCUDEV_SetError ($hash, "Usage: set datapoint . [...]"); } $objvalue = HMCCU_Substitute ($objvalue, $substitute); # Build datapoint address $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$objname; HMCCU_Set ($hmccu_hash, $hmccu_name, 'datapoint', $objname, $objvalue); usleep (100000); HMCCU_Get ($hmccu_hash, $hmccu_name, 'datapoint', $objname); return undef; } elsif ($opt =~ /^($hash->{statevals})$/) { my $cmd = $1; my $objvalue = ($cmd ne 'devstate') ? $cmd : join ('%20', @a); if ($statechannel eq '') { return HMCCUDEV_SetError ($hash, "No STATE channel specified"); } if (!defined ($objvalue)) { return HMCCUDEV_SetError ($hash, "Usage: set devstate "); } $objvalue = HMCCU_Substitute ($objvalue, $statevals); # Build datapoint address my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$statechannel.'.STATE'; HMCCU_Set ($hmccu_hash, $hmccu_name, 'datapoint', $objname, $objvalue); usleep (100000); HMCCU_Get ($hmccu_hash, $hmccu_name, 'datapoint', $objname); return undef; } else { my $retmsg = "HMCCUDEV: Unknown argument $opt, choose one of datapoint devstate"; return undef if ($hash->{statevals} eq 'readonly'); if ($statevals ne '') { my @cmdlist = split /\|/,$hash->{statevals}; shift @cmdlist; $retmsg .= ':'.join(',',@cmdlist); foreach my $sv (@cmdlist) { $retmsg .= ' '.$sv.':noArg'; } } return $retmsg; } } ##################################### # Get commands ##################################### sub HMCCUDEV_Get ($@) { my ($hash, @a) = @_; my $name = shift @a; my $opt = shift @a; if (!defined ($hash->{IODev})) { return HMCCUDEV_SetError ($hash, "No IO device defined"); } my $hmccu_hash = $hash->{IODev}; my $hmccu_name = $hash->{IODev}->{NAME}; if ($opt eq 'devstate') { # my $statechannel = AttrVal ($name, 'statechannel', ''); my $statechannel = $hash->{statechannel}; if ($statechannel eq '') { return HMCCUDEV_SetError ($hash, "No STATE channel specified"); } # Build datapoint address my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$statechannel; HMCCU_Get ($hmccu_hash, $hmccu_name, 'devstate', $objname); return undef; } elsif ($opt eq 'datapoint') { my $objname = shift @a; if (!defined ($objname) || $objname !~ /^[0-9]+\..*$/) { return HMCCUDEV_SetError ($hash, "Usage: get datapoint ."); } # Build datapoint address $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$objname; HMCCU_Get ($hmccu_hash, $hmccu_name, 'datapoint', $objname); return undef; } elsif ($opt eq 'update') { foreach my $r (keys %{$hash->{READINGS}}) { if ($r =~ /^.+:[0-9]+\..+/) { HMCCU_Get ($hmccu_hash, $hmccu_name, 'datapoint', $r); } } } else { return "HMCCUDEV: Unknown argument $opt, choose one of devstate:noArg datapoint update:noArg"; } } ##################################### # Set error status ##################################### sub HMCCUDEV_SetError ($$) { my ($hash, $text) = @_; my $name = $hash->{NAME}; $text = "HMCCUDEV: ".$name." ". $text; readingsSingleUpdate ($hash, "state", "Error", 1); Log 1, $text; return $text; } 1; =pod =begin html

HMCCUDEV

    The module implements client devices for HMCCU. A HMCCU device must exist before a client device can be defined.

    Define

      define <name> HMCCUDEV <CCU_Device> [readonly]

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

      Examples:
      define window_living HMCCUDEV WIN-LIV-1 readonly
      define temp_control HMCCUDEV TEMP-CONTROL

      CCU_Device - Name of device in CCU without channel or datapoint.

    Set

    • set <Name> devstate <Value>
      Set state of a CCU device channel. Channel must be defined as attribute statechannel.

      Example:
      set light_entrance devstate on

    • set <Name> <StateValue>
      State of a CCU device channel is set to StateValue. Channel must be defined as attribute statechannel. State values can be replaced by setting attribute statevals.

      Example:
      attr myswitch statechannel 1
      attr myswitch statevals on:true,off:false
      set myswitch on

    • set <Name> datapoint <channel.datapoint> <Value>
      Set value of a datapoint of a CCU device channel.

      Example:
      set temp_control datapoint TEMP_CONTROL:2.SET_TEMPERATURE 21


    Get

    • get <Name> devstate
      Get state of CCU device. Attribute 'statechannel' must be set.

    • get <Name> datapoint <Device:Channel.datapoint>
      Get value of a CCU device datapoint.

    • get <Name> update
      Update current readings matching CCU device name.


    Attributes

    • ccureadings <0 | 1>
      If set to 1 values read from CCU will be stored as readings.

    • 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

    • substitude <expression>:<subststr>[,...]
      Define substitions for reading values. Substitutions for parfile values must be specified in parfiles.

=end html =cut