| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101 |
- ######################################################################
- #
- # 88_HMCCUDEV.pm
- #
- # $Id: 88_HMCCUDEV.pm 17672 2018-11-04 12:40:18Z zap $
- #
- # Version 4.3.005
- #
- # (c) 2018 zap (zap01 <at> t-online <dot> de)
- #
- ######################################################################
- #
- # define <name> HMCCUDEV {<ccudev>|virtual} [<statechannel>] [readonly] [defaults]
- # [{group={<device>|<channel>}[,...]|groupexp=<regexp>}] [iodev=<iodevname>]
- #
- # set <name> clear [<regexp>]
- # set <name> config [<channel-number>] <parameter>=<value> [...]
- # set <name> control <value>
- # set <name> datapoint [<channel-number>.]<datapoint> <value> [...]
- # set <name> defaults
- # set <name> devstate <value>
- # set <name> on-till <timestamp>
- # set <name> on-for-timer <ontime>
- # set <name> pct <level> [{<ontime>|0} [<ramptime>]]
- # set <name> <stateval_cmds>
- # set <name> toggle
- #
- # get <name> config [<channel-number>] [<filter-expr>]
- # get <name> configdesc [<channel-number>]
- # get <name> configlist [<channel-number>]
- # get <name> datapoint [<channel-number>.]<datapoint>
- # get <name> defaults
- # get <name> devstate
- # get <name> update
- #
- # attr <name> ccucalculate <value>:<reading>[:<dp-list>][...]
- # attr <name> ccuflags { ackState, nochn0, trace }
- # attr <name> ccuget { State | Value }
- # attr <name> ccureadings { 0 | 1 }
- # attr <name> ccureadingformat { address[lc] | name[lc] | datapoint[lc] }
- # attr <name> ccureadingfilter <filter-rule>[,...]
- # attr <name> ccureadingname <oldname>:<newname>[,...]
- # attr <name> ccuscaleval <datapoint>:<factor>[:<min>:<max>][,...]
- # attr <name> ccuverify { 0 | 1 | 2}
- # attr <name> controldatapoint <channel-number>.<datapoint>
- # attr <name> disable { 0 | 1 }
- # attr <name> peer datapoints:condition:{hmccu:object=value|ccu:object=value|fhem:command}
- # attr <name> hmstatevals <subst-rule>[;...]
- # attr <name> statechannel <channel>
- # attr <name> statedatapoint [<channel-number>.]<datapoint>
- # attr <name> statevals <text1>:<subtext1>[,...]
- # attr <name> substexcl <reading-expr>
- # attr <name> substitute <subst-rule>[;...]
- #
- ######################################################################
- # Requires modules 88_HMCCU.pm, HMCCUConf.pm
- ######################################################################
- package main;
- use strict;
- use warnings;
- use SetExtensions;
- # use Data::Dumper;
- # use Time::HiRes qw( gettimeofday usleep );
- sub HMCCUDEV_Initialize ($);
- sub HMCCUDEV_Define ($@);
- sub HMCCUDEV_Delete ($$);
- sub HMCCUDEV_InitDevice ($$);
- sub HMCCUDEV_Set ($@);
- sub HMCCUDEV_Get ($@);
- sub HMCCUDEV_Attr ($@);
- #####################################
- # Initialize module
- #####################################
- sub HMCCUDEV_Initialize ($)
- {
- my ($hash) = @_;
- $hash->{DefFn} = "HMCCUDEV_Define";
- $hash->{DeleteFn} = "HMCCUDEV_Delete";
- $hash->{SetFn} = "HMCCUDEV_Set";
- $hash->{GetFn} = "HMCCUDEV_Get";
- $hash->{AttrFn} = "HMCCUDEV_Attr";
- $hash->{parseParams} = 1;
- $hash->{AttrList} = "IODev ccuaggregate:textField-long ccucalculate:textField-long ".
- "ccuflags:multiple-strict,ackState,nochn0,trace ccureadingfilter:textField-long ".
- "ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ".
- "ccureadingname:textField-long ".
- "ccureadings:0,1 ccuget:State,Value ccuscaleval ccuSetOnChange ccuverify:0,1,2 disable:0,1 ".
- "hmstatevals:textField-long statevals substexcl substitute:textField-long statechannel ".
- "statedatapoint controldatapoint stripnumber peer:textField-long ".
- $readingFnAttributes;
- }
- #####################################
- # Define device
- #####################################
- sub HMCCUDEV_Define ($@)
- {
- my ($hash, $a, $h) = @_;
- my $name = $hash->{NAME};
-
- my $usage = "Usage: define $name HMCCUDEV {device|'virtual'} [state-channel] ".
- "['readonly'] ['defaults'] [iodev={iodev-name}] [address={virtual-device-no}]".
- "[{groupexp=regexp|group={device|channel}[,...]]";
- return $usage if (scalar (@$a) < 3);
-
- my @errmsg = (
- "OK",
- "Invalid or unknown CCU device name or address",
- "Can't assign I/O device",
- "No devices in group",
- "No matching CCU devices found",
- "Type of virtual device not defined",
- "Device type not found",
- "Too many virtual devices"
- );
- my $devname = shift @$a;
- my $devtype = shift @$a;
- my $devspec = shift @$a;
- my $hmccu_hash = undef;
-
- # Store some definitions for delayed initialization
- $hash->{hmccu}{devspec} = $devspec;
- $hash->{hmccu}{groupexp} = $h->{groupexp} if (exists ($h->{groupexp}));
- $hash->{hmccu}{group} = $h->{group} if (exists ($h->{group}));
- if (exists ($h->{address})) {
- if ($init_done || $devspec ne 'virtual') {
- return "Option address not allowed";
- }
- else {
- $hash->{hmccu}{address} = $h->{address};
- }
- }
- else {
- return "Option address not specified" if (!$init_done && $devspec eq 'virtual');
- }
- # Defaults
- $hash->{statevals} = 'devstate';
-
- # Parse optional command line parameters
- foreach my $arg (@$a) {
- if ($arg eq 'readonly') { $hash->{statevals} = $arg; }
- elsif ($arg eq 'defaults') {
- HMCCU_SetDefaults ($hash) if ($init_done);
- }
- elsif ($arg =~ /^[0-9]+$/) { $attr{$name}{statechannel} = $arg; }
- else { return $usage; }
- }
-
- # IO device can be set by command line parameter iodev, otherwise try to detect IO device
- if (exists ($h->{iodev})) {
- return "Specified IO Device ".$h->{iodev}." does not exist" if (!exists ($defs{$h->{iodev}}));
- return "Specified IO Device ".$h->{iodev}." is not a HMCCU device"
- if ($defs{$h->{iodev}}->{TYPE} ne 'HMCCU');
- $hmccu_hash = $defs{$h->{iodev}};
- }
- else {
- # The following call will fail for non virtual devices during FHEM start if CCU is not ready
- $hmccu_hash = $devspec eq 'virtual' ? HMCCU_GetHash (0) : HMCCU_FindIODevice ($devspec);
- }
- if ($init_done) {
- # Interactive define command while CCU not ready
- if (!defined ($hmccu_hash)) {
- my ($ccuactive, $ccuinactive) = HMCCU_IODeviceStates ();
- if ($ccuinactive > 0) {
- return "CCU and/or IO device not ready. Please try again later";
- }
- else {
- return "Cannot detect IO device";
- }
- }
- }
- else {
- # CCU not ready during FHEM start
- if (!defined ($hmccu_hash) || $hmccu_hash->{ccustate} ne 'active') {
- Log3 $name, 2, "HMCCUDEV: [$devname] Cannot detect IO device, maybe CCU not ready. Trying later ...";
- readingsSingleUpdate ($hash, "state", "Pending", 1);
- $hash->{ccudevstate} = 'pending';
- return undef;
- }
- }
- # Initialize FHEM device, set IO device
- my $rc = HMCCUDEV_InitDevice ($hmccu_hash, $hash);
- return $errmsg[$rc] if ($rc > 0);
- return undef;
- }
- ######################################################################
- # Initialization of FHEM device.
- # Called during Define() or by HMCCU after CCU ready.
- # Return 0 on successful initialization or >0 on error:
- # 1 = Invalid channel name or address
- # 2 = Cannot assign IO device
- # 3 = No devices in group
- # 4 = No matching CCU devices found
- # 5 = Type of virtual device not defined
- # 6 = Device type not found
- # 7 = Too many virtual devices
- ######################################################################
- sub HMCCUDEV_InitDevice ($$)
- {
- my ($hmccu_hash, $dev_hash) = @_;
- my $name = $dev_hash->{NAME};
- my $devspec = $dev_hash->{hmccu}{devspec};
- my $gdcount = 0;
- my $gdname = $devspec;
-
- if ($devspec eq 'virtual') {
- my $no = 0;
- if (exists ($dev_hash->{hmccu}{address})) {
- # Only true during FHEM start
- $no = $dev_hash->{hmccu}{address};
- }
- else {
- # Search for free address. Maximum of 10000 virtual devices allowed.
- for (my $i=1; $i<=10000; $i++) {
- my $va = sprintf ("VIR%07d", $i);
- if (!HMCCU_IsValidDevice ($hmccu_hash, $va, 1)) {
- $no = $i;
- last;
- }
- }
- return 7 if ($no == 0);
- $dev_hash->{DEF} .= " address=$no";
- }
- $dev_hash->{ccuif} = 'fhem';
- $dev_hash->{ccuaddr} = sprintf ("VIR%07d", $no);
- $dev_hash->{ccuname} = $name;
- }
- else {
- return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $devspec, 7));
- my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec);
- return 1 if (!defined ($da));
- $gdname = $dn;
- $dev_hash->{ccuif} = $di;
- $dev_hash->{ccuaddr} = $da;
- $dev_hash->{ccuname} = $dn;
- $dev_hash->{ccutype} = $dt;
- $dev_hash->{channels} = $dc;
- }
-
- # Parse group options
- if ($dev_hash->{ccuif} eq 'VirtualDevices' || $dev_hash->{ccuif} eq 'fhem') {
- my @devlist = ();
- if (exists ($dev_hash->{hmccu}{groupexp})) {
- # Group devices specified by name expression
- $gdcount = HMCCU_GetMatchingDevices ($hmccu_hash, $dev_hash->{hmccu}{groupexp}, 'dev', \@devlist);
- return 4 if ($gdcount == 0);
- }
- elsif (exists ($dev_hash->{hmccu}{group})) {
- # Group devices specified by comma separated name list
- my @gdevlist = split (",", $dev_hash->{hmccu}{group});
- $dev_hash->{ccugroup} = '' if (@gdevlist > 0);
- foreach my $gd (@gdevlist) {
- my ($gda, $gdc, $gdo) = ('', '', '', '');
- return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $gd, 7));
- ($gda, $gdc) = HMCCU_GetAddress ($hmccu_hash, $gd, '', '');
- $gdo = $gda;
- $gdo .= ':'.$gdc if ($gdc ne '');
- push @devlist, $gdo;
- $gdcount++;
- }
- }
- else {
- # Group specified by CCU virtual group name
- @devlist = HMCCU_GetGroupMembers ($hmccu_hash, $gdname);
- $gdcount = scalar (@devlist);
- return 5 if ($gdcount == 0);
- }
- return 3 if ($gdcount == 0);
-
- $dev_hash->{ccugroup} = join (',', @devlist);
- if ($devspec eq 'virtual') {
- my $dev = shift @devlist;
- my $devtype = HMCCU_GetDeviceType ($hmccu_hash, $dev, 'n/a');
- my $devna = $devtype eq 'n/a' ? 1 : 0;
- for my $d (@devlist) {
- if (HMCCU_GetDeviceType ($hmccu_hash, $d, 'n/a') ne $devtype) {
- $devna = 1;
- last;
- }
- }
-
- my $rc = 0;
- if ($devna) {
- $dev_hash->{ccutype} = 'n/a';
- $dev_hash->{statevals} = 'readonly';
- $rc = HMCCU_CreateDevice ($hmccu_hash, $dev_hash->{ccuaddr}, $name, undef, $dev);
- }
- else {
- $dev_hash->{ccutype} = $devtype;
- $rc = HMCCU_CreateDevice ($hmccu_hash, $dev_hash->{ccuaddr}, $name, $devtype, $dev);
- }
- return $rc+4 if ($rc > 0);
-
- # Set default attributes
- $attr{$name}{ccureadingformat} = 'name';
- }
- }
- # Inform HMCCU device about client device
- return 2 if (!HMCCU_AssignIODevice ($dev_hash, $hmccu_hash->{NAME}, undef));
-
- readingsSingleUpdate ($dev_hash, "state", "Initialized", 1);
- $dev_hash->{ccudevstate} = 'active';
-
- return 0;
- }
- #####################################
- # Delete device
- #####################################
- sub HMCCUDEV_Delete ($$)
- {
- my ($hash, $name) = @_;
-
- if ($hash->{ccuif} eq 'fhem') {
- HMCCU_DeleteDevice ($hash);
- }
- return undef;
- }
- #####################################
- # Set attribute
- #####################################
- sub HMCCUDEV_Attr ($@)
- {
- my ($cmd, $name, $attrname, $attrval) = @_;
- my $hash = $defs{$name};
- if ($cmd eq "set") {
- return "Missing attribute value" if (!defined ($attrval));
- if ($attrname eq 'IODev') {
- $hash->{IODev} = $defs{$attrval};
- }
- elsif ($attrname eq "statevals") {
- return "Device is read only" if ($hash->{statevals} eq 'readonly');
- $hash->{statevals} = 'devstate';
- my @states = split /,/,$attrval;
- foreach my $st (@states) {
- my @statesubs = split /:/,$st;
- return "value := text:substext[,...]" if (@statesubs != 2);
- $hash->{statevals} .= '|'.$statesubs[0];
- }
- }
- }
- elsif ($cmd eq "del") {
- if ($attrname eq "statevals") {
- $hash->{statevals} = "devstate";
- }
- }
- return;
- }
- #####################################
- # Set commands
- #####################################
- sub HMCCUDEV_Set ($@)
- {
- my ($hash, $a, $h) = @_;
- my $name = shift @$a;
- my $opt = shift @$a;
- # Valid commands for read only devices
- my $rocmds = "clear config defaults:noArg";
- # Get I/O device, check device state
- return HMCCU_SetError ($hash, -19) if (!defined ($hash->{ccudevstate}) || $hash->{ccudevstate} eq 'pending');
- return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
- my $hmccu_hash = $hash->{IODev};
- my $hmccu_name = $hmccu_hash->{NAME};
- # Handle read only and disabled devices
- return undef if ($hash->{statevals} eq 'readonly' && $opt ne '?'
- && $opt !~ /^(clear|config|defaults)$/);
- my $disable = AttrVal ($name, "disable", 0);
- return undef if ($disable == 1);
- # Check if CCU is busy
- if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) {
- return undef if ($opt eq '?');
- return "HMCCUDEV: CCU busy";
- }
- # Get parameters of current device
- my $ccutype = $hash->{ccutype};
- my $ccuaddr = $hash->{ccuaddr};
- my $ccuif = $hash->{ccuif};
- my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
- my $statevals = AttrVal ($name, 'statevals', '');
- my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', '');
- my $result = '';
- my $rc;
- if ($opt eq 'datapoint') {
- my $usage = "Usage: set $name datapoint [{channel-number}.]{datapoint} {value} [...]";
- my %dpval;
- my $i = 0;
- while (my $objname = shift @$a) {
- my $objvalue = shift @$a;
- $i += 1;
- if ($ccutype eq 'HM-Dis-EP-WM55' && !defined ($objvalue)) {
- $objvalue = '';
- foreach my $t (keys %{$h}) {
- if ($objvalue eq '') {
- $objvalue = $t.'='.$h->{$t};
- }
- else {
- $objvalue .= ','.$t.'='.$h->{$t};
- }
- }
- }
- return HMCCU_SetError ($hash, $usage) if (!defined ($objvalue) || $objvalue eq '');
- if ($objname =~ /^([0-9]+)\..+$/) {
- my $chn = $1;
- return HMCCU_SetError ($hash, -7) if ($chn >= $hash->{channels});
- }
- else {
- return HMCCU_SetError ($hash, -11) if ($sc eq '');
- $objname = $sc.'.'.$objname;
- }
-
- my $no = sprintf ("%03d", $i);
- $objvalue =~ s/\\_/%20/g;
- $dpval{"$no.$ccuif.$ccuaddr:$objname"} = HMCCU_Substitute ($objvalue, $statevals, 1, undef, '');
- }
- return HMCCU_SetError ($hash, $usage) if (scalar (keys %dpval) < 1);
-
- $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval);
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
- return HMCCU_SetState ($hash, "OK");
- }
- elsif ($opt eq 'control') {
- return HMCCU_SetError ($hash, -12) if ($cc eq '');
- return HMCCU_SetError ($hash, -14) if ($cd eq '');
- return HMCCU_SetError ($hash, -7) if ($cc >= $hash->{channels});
- my $objvalue = shift @$a;
- return HMCCU_SetError ($hash, "Usage: set $name control {value}") if (!defined ($objvalue));
- $objvalue =~ s/\\_/%20/g;
- $rc = HMCCU_SetMultipleDatapoints ($hash,
- { "001.$ccuif.$ccuaddr:$cc.$cd" => HMCCU_Substitute ($objvalue, $statevals, 1, undef, '') }
- );
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
- return HMCCU_SetState ($hash, "OK");
- }
- elsif ($opt =~ /^($hash->{statevals})$/) {
- my $cmd = $1;
- my $objvalue = ($cmd ne 'devstate') ? $cmd : shift @$a;
- return HMCCU_SetError ($hash, -11) if ($sc eq '');
- return HMCCU_SetError ($hash, -13) if ($sd eq '');
- return HMCCU_SetError ($hash, "Usage: set $name devstate {value}") if (!defined ($objvalue));
- $objvalue =~ s/\\_/%20/g;
- $rc = HMCCU_SetMultipleDatapoints ($hash,
- { "001.$ccuif.$ccuaddr:$sc.$sd" => HMCCU_Substitute ($objvalue, $statevals, 1, undef, '') }
- );
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
- return HMCCU_SetState ($hash, "OK");
- }
- elsif ($opt eq 'toggle') {
- return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals}));
- return HMCCU_SetError ($hash, -11) if ($sc eq '');
- return HMCCU_SetError ($hash, -13) if ($sd eq '');
- return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, $sd, 2));
- my $tstates = $hash->{statevals};
- $tstates =~ s/devstate\|//;
- my @states = split /\|/, $tstates;
- my $stc = scalar (@states);
- my $objname = $ccuif.'.'.$ccuaddr.':'.$sc.'.'.$sd;
-
- # Read current value of datapoint without updating reading
- ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 1);
- Log3 $name, 2, "HMCCU: set toggle: GetDatapoint returned $rc, $result"
- if ($ccuflags =~ /trace/);
- return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
- my $objvalue = '';
- my $st = 0;
- while ($st < $stc) {
- if ($states[$st] eq $result) {
- $objvalue = ($st == $stc-1) ? $states[0] : $states[$st+1];
- last;
- }
- else {
- $st++;
- }
- }
- return HMCCU_SetError ($hash, "Current device state doesn't match statevals")
- if ($objvalue eq '');
- $rc = HMCCU_SetMultipleDatapoints ($hash,
- { "001.$objname" => HMCCU_Substitute ($objvalue, $statevals, 1, undef, '') }
- );
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
- return HMCCU_SetState ($hash, "OK");
- }
- elsif ($opt eq 'pct') {
- return HMCCU_SetError ($hash, -11) if ($sc eq '' && $cc eq '');
- my $chn;
- if (HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "LEVEL", 2)) {
- $chn = $cc;
- }
- elsif (HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "LEVEL", 2)) {
- $chn = $sc;
- }
- else {
- return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype")
- }
-
- my $objname = '';
- my $objvalue = shift @$a;
- return HMCCU_SetError ($hash, "Usage: set $name pct {value} [{ontime} [{ramptime}]]")
- if (!defined ($objvalue));
-
- my $timespec = shift @$a;
- my $ramptime = shift @$a;
- my %dpval;
-
- # Set on time
- if (defined ($timespec)) {
- return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type $ccutype")
- if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $chn, "ON_TIME", 2));
-
- if ($timespec =~ /^[0-9]{2}:[0-9]{2}/) {
- $timespec = HMCCU_GetTimeSpec ($timespec);
- return HMCCU_SetError ($hash, "Wrong time format. Use HH:MM[:SS]") if ($timespec < 0);
- }
- $dpval{"001.$ccuif.$ccuaddr:$chn.ON_TIME"} = $timespec if ($timespec > 0);
- }
-
- # Set ramp time
- $dpval{"002.$ccuif.$ccuaddr:$chn.RAMP_TIME"} = $ramptime if (defined ($ramptime));
- # Set level
- $dpval{"003.$ccuif.$ccuaddr:$chn.LEVEL"} = $objvalue;
- $rc = HMCCU_SetMultipleDatapoints ($hash, \%dpval);
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
-
- return HMCCU_SetState ($hash, "OK");
- }
- elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') {
- return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals}));
- return HMCCU_SetError ($hash, "No state value for 'on' defined")
- if ("on" !~ /($hash->{statevals})/);
- return HMCCU_SetError ($hash, -11) if ($sc eq '');
- return HMCCU_SetError ($hash, -13) if ($sd eq '');
- return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type")
- if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "ON_TIME", 2));
- my $timespec = shift @$a;
- return HMCCU_SetError ($hash, "Usage: set $name $opt {ontime-spec}")
- if (!defined ($timespec));
-
- if ($opt eq 'on-till') {
- $timespec = HMCCU_GetTimeSpec ($timespec);
- return HMCCU_SetError ($hash, "Wrong time format. Use HH:MM[:SS]") if ($timespec < 0);
- }
-
- $rc = HMCCU_SetMultipleDatapoints ($hash, {
- "001.$ccuif.$ccuaddr:$sc.ON_TIME" => $timespec,
- "002.$ccuif.$ccuaddr:$sc.$sd" => HMCCU_Substitute ("on", $statevals, 1, undef, '')
- });
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
-
- return HMCCU_SetState ($hash, "OK");
- }
- elsif ($opt eq 'clear') {
- my $rnexp = shift @$a;
- $rnexp = '.*' if (!defined ($rnexp));
- my @readlist = keys %{$hash->{READINGS}};
- foreach my $rd (@readlist) {
- delete ($hash->{READINGS}{$rd}) if ($rd ne 'state' && $rd ne 'control' && $rd =~ /$rnexp/);
- }
- }
- elsif ($opt eq 'config') {
- return HMCCU_SetError ($hash, "Usage: set $name config [{channel-number}] {parameter}={value} [...]")
- if ((scalar keys %{$h}) < 1);
- my $objname = $ccuaddr;
-
- # Channel number is optional because parameter can be related to device or channel
- if ((scalar @$a) > 0 && $$a[0] =~ /^([0-9]{1,2})$/) {
- return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
- $objname .= ':'.$1;
- }
- my $rc = HMCCU_RPCSetConfig ($hash, $objname, $h);
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
- return HMCCU_SetState ($hash, "OK");
- }
- elsif ($opt eq 'defaults') {
- my $rc = HMCCU_SetDefaults ($hash);
- return HMCCU_SetError ($hash, "HMCCU: No default attributes found") if ($rc == 0);
- return HMCCU_SetState ($hash, "OK");
- }
- else {
- return "HMCCUCHN: Unknown argument $opt, choose one of ".$rocmds
- if ($hash->{statevals} eq 'readonly');
- my $retmsg = "HMCCUDEV: Unknown argument $opt, choose one of clear config control datapoint defaults:noArg";
- if ($sc ne '') {
- $retmsg .= " devstate";
- if ($hash->{statevals} ne '') {
- my @cmdlist = split /\|/,$hash->{statevals};
- shift @cmdlist;
- $retmsg .= ':'.join(',',@cmdlist) if (@cmdlist > 0);
- foreach my $sv (@cmdlist) {
- $retmsg .= ' '.$sv.':noArg';
- }
- $retmsg .= " toggle:noArg";
- $retmsg .= " on-for-timer on-till"
- if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "ON_TIME", 2));
- $retmsg .= " pct"
- if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "LEVEL", 2) ||
- HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $cc, "LEVEL", 2));
- }
- }
- return $retmsg;
- }
- }
- #####################################
- # Get commands
- #####################################
- sub HMCCUDEV_Get ($@)
- {
- my ($hash, $a, $h) = @_;
- my $name = shift @$a;
- my $opt = shift @$a;
- # Get I/O device
- return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
- my $hmccu_hash = $hash->{IODev};
- my $hmccu_name = $hmccu_hash->{NAME};
- # Handle disabled devices
- my $disable = AttrVal ($name, "disable", 0);
- return undef if ($disable == 1);
- # Check if CCU is busy
- if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) {
- return undef if ($opt eq '?');
- return "HMCCUDEV: CCU busy";
- }
- # Get parameters of current device
- my $ccutype = $hash->{ccutype};
- my $ccuaddr = $hash->{ccuaddr};
- my $ccuif = $hash->{ccuif};
- my $ccureadings = AttrVal ($name, 'ccureadings', 1);
- my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', '');
- my $result = '';
- my $rc;
- # Virtual devices only support command get update
- if ($ccuif eq 'fhem' && $opt ne 'update') {
- return "HMCCUDEV: Unknown argument $opt, choose one of update:noArg";
- }
- if ($opt eq 'devstate') {
- return HMCCU_SetError ($hash, -11) if ($sc eq '');
- return HMCCU_SetError ($hash, -13) if ($sd eq '');
- return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, $sd, 1));
-
- my $objname = $ccuif.'.'.$ccuaddr.':'.$sc.'.'.$sd;
- ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 0);
- return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
- return $ccureadings ? undef : $result;
- }
- elsif ($opt eq 'datapoint') {
- my $objname = shift @$a;
- return HMCCU_SetError ($hash, "Usage: get $name datapoint [{channel-number}.]{datapoint}")
- if (!defined ($objname));
- if ($objname =~ /^([0-9]+)\..+$/) {
- my $chn = $1;
- return HMCCU_SetError ($hash, -7) if ($chn >= $hash->{channels});
- }
- else {
- return HMCCU_SetError ($hash, -11) if ($sc eq '');
- $objname = $sc.'.'.$objname;
- }
- return HMCCU_SetError ($hash, -8)
- if (!HMCCU_IsValidDatapoint ($hash, $ccutype, undef, $objname, 1));
- $objname = $ccuif.'.'.$ccuaddr.':'.$objname;
- ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname, 0);
- return HMCCU_SetError ($hash, $rc, $result) if ($rc < 0);
- HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
- return $ccureadings ? undef : $result;
- }
- elsif ($opt eq 'update') {
- my $ccuget = shift @$a;
- $ccuget = 'Attr' if (!defined ($ccuget));
- if ($ccuget !~ /^(Attr|State|Value)$/) {
- return HMCCU_SetError ($hash, "Usage: get $name update [{'State'|'Value'}]");
- }
- if ($hash->{ccuif} ne 'fhem') {
- $rc = HMCCU_GetUpdate ($hash, $ccuaddr, $ccuget);
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
- }
- else {
- # Update all devices belonging to group
- my @vdevs = split (",", $hash->{ccugroup});
- foreach my $vd (@vdevs) {
- $rc = HMCCU_GetUpdate ($hash, $vd, $ccuget);
- return HMCCU_SetError ($hash, $rc) if ($rc < 0);
- }
- }
- return undef;
- }
- elsif ($opt eq 'deviceinfo') {
- my $ccuget = shift @$a;
- $ccuget = 'Attr' if (!defined ($ccuget));
- if ($ccuget !~ /^(Attr|State|Value)$/) {
- return HMCCU_SetError ($hash, "Usage: get $name deviceinfo [{'State'|'Value'}]");
- }
- $result = HMCCU_GetDeviceInfo ($hash, $ccuaddr, $ccuget);
- return HMCCU_SetError ($hash, -2) if ($result eq '');
- return HMCCU_FormatDeviceInfo ($result);
- }
- elsif ($opt eq 'config') {
- my $channel = undef;
- my $ccuobj = $ccuaddr;
- my $par = shift @$a;
- if (defined ($par)) {
- if ($par =~ /^([0-9]{1,2})$/) {
- return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
- $ccuobj .= ':'.$1;
- $par = shift @$a;
- }
- }
- $par = '.*' if (!defined ($par));
- my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par);
- return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
- HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
- return $ccureadings ? undef : $res;
- }
- elsif ($opt eq 'configlist') {
- my $channel = undef;
- my $ccuobj = $ccuaddr;
- my $par = shift @$a;
- if (defined ($par)) {
- if ($par =~ /^([0-9]{1,2})$/) {
- return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
- $ccuobj .= ':'.$1;
- $par = shift @$a;
- }
- }
- $par = '.*' if (!defined ($par));
- my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par);
- return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
- HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
- return $res;
- }
- elsif ($opt eq 'configdesc') {
- my $channel = undef;
- my $ccuobj = $ccuaddr;
- my $par = shift @$a;
- if (defined ($par)) {
- if ($par =~ /^([0-9]{1,2})$/) {
- return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
- $ccuobj .= ':'.$1;
- }
- else {
- return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
- }
- }
- my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef);
- return HMCCU_SetError ($hash, $rc, $res) if ($rc < 0);
- HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
- return $res;
- }
- elsif ($opt eq 'defaults') {
- $result = HMCCU_GetDefaults ($hash, 0);
- return $result;
- }
- else {
- my $retmsg = "HMCCUDEV: Unknown argument $opt, choose one of datapoint";
-
- my @valuelist;
- my $valuecount = HMCCU_GetValidDatapoints ($hash, $ccutype, -1, 1, \@valuelist);
-
- $retmsg .= ":".join(",", @valuelist) if ($valuecount > 0);
- $retmsg .= " defaults:noArg update:noArg config configlist configdesc deviceinfo:noArg";
- $retmsg .= ' devstate:noArg' if ($sc ne '');
-
- return $retmsg;
- }
- }
- 1;
- =pod
- =item device
- =item summary controls HMCCU client devices for Homematic CCU2 - FHEM integration
- =begin html
- <a name="HMCCUDEV"></a>
- <h3>HMCCUDEV</h3>
- <ul>
- The module implements Homematic CCU devices as client devices for HMCCU. A HMCCU I/O device must
- exist before a client device can be defined. If a CCU channel is not found execute command
- 'get devicelist' in I/O device.<br/>
- This reference contains only commands and attributes which differ from module
- <a href="#HMCCUCHN">HMCCUCHN</a>.
- </br></br>
- <a name="HMCCUDEVdefine"></a>
- <b>Define</b><br/><br/>
- <ul>
- <code>define <name> HMCCUDEV {<device> | 'virtual'} [<statechannel>]
- [readonly] [defaults] [{group={device|channel}[,...]|groupexp=regexp]
- [iodev=<iodev-name>]</code>
- <br/><br/>
- If option 'readonly' is specified no set command will be available. With option 'defaults'
- some default attributes depending on CCU device type will be set. Default attributes are only
- available for some device types. The option is ignored during FHEM start.
- Parameter <i>statechannel</i> corresponds to attribute 'statechannel'.<br/>
- A HMCCUDEV device supports CCU group devices. The CCU devices or channels related to a group
- device are specified by using options 'group' or 'groupexp' followed by the names or
- addresses of the CCU devices or channels. By using 'groupexp' one can specify a regular
- expression for CCU device or channel names. Since version 4.2.009 of HMCCU HMCCUDEV
- is able to detect members of group devices automatically. So options 'group' or
- 'groupexp' are no longer necessary to define a group device.<br/>
- It's also possible to group any kind of CCU devices without defining a real group
- in CCU by using option 'virtual' instead of a CCU device specification.
- <br/><br/>
- Examples:<br/>
- <code>
- # Simple device by using CCU device name<br/>
- define window_living HMCCUDEV WIN-LIV-1<br/>
- # Simple device by using CCU device address and with state channel<br/>
- define temp_control HMCCUDEV BidCos-RF.LEQ1234567 1<br/>
- # Simple read only device by using CCU device address and with default attributes<br/>
- define temp_sensor HMCCUDEV BidCos-RF.LEQ2345678 1 readonly defaults
- # Group device by using CCU group device and 3 group members<br/>
- define heating_living HMCCUDEV GRP-LIV group=WIN-LIV,HEAT-LIV,THERM-LIV
- </code>
- <br/>
- </ul>
- <br/>
-
- <a name="HMCCUDEVset"></a>
- <b>Set</b><br/><br/>
- <ul>
- <li><b>set <name> clear [<reading-exp>]</b><br/>
- <a href="#HMCCUCHNset">see HMCCUCHN</a>
- </li><br/>
- <li><b>set <name> config [<channel-number>] <parameter>=<value>
- [...]</b><br/>
- Set configuration parameter of CCU device or channel. Valid parameters can be listed by
- using command 'get configdesc'.
- </li><br/>
- <li><b>set <name> datapoint [<channel-number>.]<datapoint>
- <value> [...]</b><br/>
- Set datapoint values of a CCU device channel. If channel number is not specified
- state channel is used. String \_ is substituted by blank.
- <br/><br/>
- Example:<br/>
- <code>set temp_control datapoint 2.SET_TEMPERATURE 21</code><br/>
- <code>set temp_control datapoint 2.AUTO_MODE 1 2.SET_TEMPERATURE 21</code>
- </li><br/>
- <li><b>set <name> defaults</b><br/>
- Set default attributes for CCU device type. Default attributes are only available for
- some device types.
- </li><br/>
- <li><b>set <name> devstate <value></b><br/>
- Set state of a CCU device channel. Channel and state datapoint must be defined as
- attribute 'statedatapoint'. If <i>value</i> contains string \_ it is substituted by blank.
- </li><br/>
- <li><b>set <name> on-for-timer <ontime></b><br/>
- <a href="#HMCCUCHNset">see HMCCUCHN</a>
- </li><br/>
- <li><b>set <name> on-till <timestamp></b><br/>
- <a href="#HMCCUCHNset">see HMCCUCHN</a>
- </li><br/>
- <li><b>set <name> pct <value;> [<ontime> [<ramptime>]]</b><br/>
- <a href="#HMCCUCHNset">see HMCCUCHN</a>
- </li><br/>
- <li><b>set <name> <statevalue></b><br/>
- State datapoint of a CCU device channel is set to 'statevalue'. State channel and state
- datapoint must be defined as attribute 'statedatapoint'. Values for <i>statevalue</i>
- are defined by setting attribute 'statevals'.
- <br/><br/>
- Example:<br/>
- <code>
- attr myswitch statedatapoint 1.STATE<br/>
- attr myswitch statevals on:true,off:false<br/>
- set myswitch on
- </code>
- </li><br/>
- <li><b>set <name> toggle</b><br/>
- <a href="#HMCCUCHNset">see HMCCUCHN</a>
- </li><br/>
- <li><b>ePaper Display</b><br/><br/>
- This display has 5 text lines. The lines 1,2 and 4,5 are accessible via config parameters
- TEXTLINE_1 and TEXTLINE_2 in channels 1 and 2. Example:<br/><br/>
- <code>
- define HM_EPDISP HMCCUDEV CCU_EPDISP<br/>
- set HM_EPDISP config 2 TEXTLINE_1=Line1<br/>
- set HM_EPDISP config 2 TEXTLINE_2=Line2<br/>
- set HM_EPDISP config 1 TEXTLINE_1=Line4<br/>
- set HM_EPDISP config 1 TEXTLINE_2=Line5<br/>
- </code>
- <br/>
- The lines 2,3 and 4 of the display can be accessed by setting the datapoint SUBMIT of the
- display to a string containing command tokens in format 'parameter=value'. The following
- commands are allowed:
- <br/><br/>
- <ul>
- <li>text1-3=Text - Content of display line 2-4</li>
- <li>icon1-3=IconCode - Icons of display line 2-4</li>
- <li>sound=SoundCode - Sound</li>
- <li>signal=SignalCode - Optical signal</li>
- <li>pause=Seconds - Pause between signals (1-160)</li>
- <li>repeat=Count - Repeat count for sound (0-15)</li>
- </ul>
- <br/>
- IconCode := ico_off, ico_on, ico_open, ico_closed, ico_error, ico_ok, ico_info,
- ico_newmsg, ico_svcmsg<br/>
- SignalCode := sig_off, sig_red, sig_green, sig_orange<br/>
- SoundCode := snd_off, snd_longlong, snd_longshort, snd_long2short, snd_short, snd_shortshort,
- snd_long<br/><br/>
- Example:<br/>
- <code>
- set HM_EPDISP datapoint 3.SUBMIT text1=Line2,text2=Line3,text3=Line4,sound=snd_short,
- signal=sig_red
- </code>
- </li>
- </ul>
- <br/>
-
- <a name="HMCCUDEVget"></a>
- <b>Get</b><br/><br/>
- <ul>
- <li><b>get <name> config [<channel-number>] [<filter-expr>]</b><br/>
- Get configuration parameters of CCU device. If attribute 'ccureadings' is set to 0
- parameters are displayed in browser window (no readings set). Parameters can be filtered
- by <i>filter-expr</i>.
- </li><br/>
- <li><b>get <name> configdesc [<channel-number>] [<rpcport>]</b><br/>
- Get description of configuration parameters for CCU device. Default value for Parameter
- <i>rpcport</i> is 2001 (BidCos-RF). Other valid values are 2000 (wired) and 2010 (HMIP).
- </li><br/>
- <li><b>get <name> configlist [<channel-number>] [<filter-expr>]</b><br/>
- Display configuration parameters of CCU device. Parameters can be filtered by
- <i>filter-expr</i>.
- </li><br/>
- <li><b>get <name> datapoint [<channel-number>.]<datapoint></b><br/>
- Get value of a CCU device datapoint. If <i>channel-number</i> is not specified state
- channel is used.
- </li><br/>
- <li><b>get <name> defaults</b><br/>
- <a href="#HMCCUCHNget">see HMCCUCHN</a>
- </li><br/>
- <li><b>get <name> deviceinfo [{State | <u>Value</u>}]</b><br/>
- Display all channels and datapoints of device with datapoint values and types.
- </li><br/>
- <li><b>get <name> devstate</b><br/>
- Get state of CCU device. Attribute 'statechannel' must be set. Default state datapoint
- STATE can be modified by attribute 'statedatapoint'.
- </li><br/>
- <li><b>get <name> update [{State | <u>Value</u>}]</b><br/>
- <a href="#HMCCUCHNget">see HMCCUCHN</a>
- </li><br/>
- </ul>
- <br/>
-
- <a name="HMCCUDEVattr"></a>
- <b>Attributes</b><br/><br/>
- <ul>
- To reduce the amount of events it's recommended to set attribute 'event-on-change-reading'
- to '.*'.<br/><br/>
- <li><b>ccucalculate <value-type>:<reading>[:<dp-list>[;...]</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccuflags {nochn0, trace}</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccuget {State | <u>Value</u>}</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccureadings {0 | <u>1</u>}</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccureadingfilter <filter-rule[,...]></b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccureadingformat {address[lc] | name[lc] | datapoint[lc]}</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccureadingname <old-readingname-expr>:<new-readingname>[,...]</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccuscaleval <datapoint>:<factor>[,...]</b><br/>
- ccuscaleval <[!]datapoint>:<min>:<max>:<minn>:<maxn>[,...]<br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccuSetOnChange <expression></b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>ccuverify {0 | 1 | 2}</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>controldatapoint <channel-number.datapoint></b><br/>
- Set channel number and datapoint for device control.
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>disable {<u>0</u> | 1}</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>hmstatevals <subst-rule>[;...]</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>peer [<datapoints>:<condition>:
- {ccu:<object>=<value>|hmccu:<object>=<value>|fhem:<command>}</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>statechannel <channel-number></b><br/>
- Channel for setting device state by devstate command. Deprecated, use attribute
- 'statedatapoint' instead.
- </li><br/>
- <li><b>statedatapoint [<channel-number>.]<datapoint></b><br/>
- Set state channel and state datapoint for setting device state by devstate command.
- Default is STATE. If 'statedatapoint' is not defined at least attribute 'statechannel'
- must be set.
- </li><br/>
- <li><b>statevals <text>:<text>[,...]</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>stripnumber {0 | 1 | 2 | -n}</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>substexcl <reading-expr></b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- <li><b>substitute <subst-rule>[;...]</b><br/>
- <a href="#HMCCUCHNattr">see HMCCUCHN</a>
- </li><br/>
- </ul>
- </ul>
- =end html
- =cut
|