mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-12 16:46:35 +00:00
HMCCU: Deleted old modules from contrib
git-svn-id: https://svn.fhem.de/fhem/trunk@20879 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
0025a416b1
commit
d32d7fb9d1
File diff suppressed because it is too large
Load Diff
@ -1,571 +0,0 @@
|
||||
################################################################
|
||||
#
|
||||
# 88_HMCCUCHN.pm
|
||||
#
|
||||
# $Id:$
|
||||
#
|
||||
# Version 3.3
|
||||
#
|
||||
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
||||
#
|
||||
################################################################
|
||||
#
|
||||
# define <name> HMCCUCHN <ccudev> [readonly]
|
||||
#
|
||||
# set <name> control <value>
|
||||
# set <name> datapoint <datapoint> <value>
|
||||
# set <name> devstate <value>
|
||||
# set <name> <stateval_cmds>
|
||||
# set <name> toggle
|
||||
# set <name> config <parameter>=<value> [...]
|
||||
#
|
||||
# get <name> devstate
|
||||
# get <name> datapoint <datapoint>
|
||||
# get <name> channel <datapoint-expr>
|
||||
# get <name> config
|
||||
# get <name> configdesc
|
||||
# get <name> update
|
||||
#
|
||||
# attr <name> ccureadings { 0 | 1 }
|
||||
# attr <name> ccureadingfilter <datapoint-expr>
|
||||
# attr <name> ccureadingformat { name | address | datapoint }
|
||||
# attr <name> ccuverify { 0 | 1 | 2 }
|
||||
# attr <name> controldatapoint <datapoint>
|
||||
# attr <name> disable { 0 | 1 }
|
||||
# attr <name> statedatapoint <datapoint>
|
||||
# attr <name> statevals <text1>:<subtext1>[,...]
|
||||
# attr <name> substitute <subst-rule>[;...]
|
||||
#
|
||||
################################################################
|
||||
# Requires module 88_HMCCU.pm
|
||||
################################################################
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use SetExtensions;
|
||||
|
||||
use Time::HiRes qw( gettimeofday usleep );
|
||||
|
||||
sub HMCCUCHN_Define ($@);
|
||||
sub HMCCUCHN_Set ($@);
|
||||
sub HMCCUCHN_Get ($@);
|
||||
sub HMCCUCHN_Attr ($@);
|
||||
sub HMCCUCHN_SetError ($$);
|
||||
|
||||
#####################################
|
||||
# Initialize module
|
||||
#####################################
|
||||
|
||||
sub HMCCUCHN_Initialize ($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = "HMCCUCHN_Define";
|
||||
$hash->{SetFn} = "HMCCUCHN_Set";
|
||||
$hash->{GetFn} = "HMCCUCHN_Get";
|
||||
$hash->{AttrFn} = "HMCCUCHN_Attr";
|
||||
|
||||
$hash->{AttrList} = "IODev ccureadingfilter ccureadingformat:name,address,datapoint ccureadings:0,1 ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint disable:0,1 statedatapoint statevals substitute stripnumber:0,1,2 ". $readingFnAttributes;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Define device
|
||||
#####################################
|
||||
|
||||
sub HMCCUCHN_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;
|
||||
|
||||
return "Invalid or unknown CCU channel name or address" if (! HMCCU_IsValidDevice ($devspec));
|
||||
|
||||
if (HMCCU_IsChnAddr ($devspec, 1)) {
|
||||
# CCU Channel address with interface
|
||||
$hash->{ccuif} = $1;
|
||||
$hash->{ccuaddr} = $2;
|
||||
$hash->{ccuname} = HMCCU_GetChannelName ($hash->{ccuaddr}, '');
|
||||
return "CCU device name not found for channel address $devspec" if ($hash->{ccuname} eq '');
|
||||
}
|
||||
elsif (HMCCU_IsChnAddr ($devspec, 0)) {
|
||||
# CCU Channel address
|
||||
$hash->{ccuaddr} = $devspec;
|
||||
$hash->{ccuif} = HMCCU_GetDeviceInterface ($hash->{ccuaddr}, 'BidCos-RF');
|
||||
$hash->{ccuname} = HMCCU_GetChannelName ($devspec, '');
|
||||
return "CCU device name not found for channel address $devspec" if ($hash->{ccuname} eq '');
|
||||
}
|
||||
else {
|
||||
# CCU Channel name
|
||||
$hash->{ccuname} = $devspec;
|
||||
my ($add, $chn) = HMCCU_GetAddress ($devspec, '', '');
|
||||
return "Channel address not found for channel name $devspec" if ($add eq '' || $chn eq '');
|
||||
$hash->{ccuaddr} = $add.':'.$chn;
|
||||
$hash->{ccuif} = HMCCU_GetDeviceInterface ($hash->{ccuaddr}, 'BidCos-RF');
|
||||
}
|
||||
|
||||
$hash->{ccutype} = HMCCU_GetDeviceType ($hash->{ccuaddr}, '');
|
||||
$hash->{channels} = 1;
|
||||
$hash->{statevals} = 'devstate';
|
||||
|
||||
my $arg = shift @a;
|
||||
if (defined ($arg) && $arg eq 'readonly') {
|
||||
$hash->{statevals} = $arg;
|
||||
}
|
||||
|
||||
# Inform HMCCU device about client device
|
||||
AssignIoPort ($hash);
|
||||
|
||||
readingsSingleUpdate ($hash, "state", "Initialized", 1);
|
||||
$hash->{ccudevstate} = 'Active';
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Set attribute
|
||||
#####################################
|
||||
|
||||
sub HMCCUCHN_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 undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Set commands
|
||||
#####################################
|
||||
|
||||
sub HMCCUCHN_Set ($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $name = shift @a;
|
||||
my $opt = shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
|
||||
return undef if ($hash->{statevals} eq 'readonly' && $opt ne 'config');
|
||||
|
||||
my $disable = AttrVal ($name, "disable", 0);
|
||||
return undef if ($disable == 1);
|
||||
|
||||
my $hmccu_hash = $hash->{IODev};
|
||||
if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) {
|
||||
return undef if ($opt eq '?');
|
||||
return "HMCCUCHN: CCU busy";
|
||||
}
|
||||
|
||||
my $statevals = AttrVal ($name, "statevals", '');
|
||||
# my $statedatapoint = AttrVal ($name, "statedatapoint", 'STATE');
|
||||
# my $controldatapoint = AttrVal ($name, "controldatapoint", '');
|
||||
my ($sc, $statedatapoint, $cc, $controldatapoint) = HMCCU_GetSpecialDatapoints (
|
||||
$hash, '', 'STATE', '', '');
|
||||
|
||||
my $result = '';
|
||||
my $rc;
|
||||
|
||||
if ($opt eq 'datapoint') {
|
||||
my $objname = shift @a;
|
||||
# my $objvalue = join ('%20', @a);
|
||||
my $objvalue = shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, "Usage: set $name datapoint {datapoint} {value} [...]")
|
||||
if (!defined ($objname) || !defined ($objvalue));
|
||||
return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $hash->{ccutype},
|
||||
$hash->{ccuaddr}, $objname, 2));
|
||||
|
||||
$objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, '');
|
||||
|
||||
# Build datapoint address
|
||||
$objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.'.'.$objname;
|
||||
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'control') {
|
||||
return HMCCU_SetError ($hash, "Attribute controldatapoint not set") if ($controldatapoint eq '');
|
||||
my $objvalue = shift @a;
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.'.'.$controldatapoint;
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt =~ /^($hash->{statevals})$/) {
|
||||
my $cmd = $1;
|
||||
my $objvalue = ($cmd ne 'devstate') ? $cmd : shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, "Usage: set $name devstate {value}") if (!defined ($objvalue));
|
||||
|
||||
$objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, '');
|
||||
|
||||
# Build datapoint address
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.'.'.$statedatapoint;
|
||||
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'toggle') {
|
||||
return HMCCU_SetError ($hash, "Attribute statevals not set")
|
||||
if ($statevals eq '' || !exists($hash->{statevals}));
|
||||
|
||||
my $tstates = $hash->{statevals};
|
||||
$tstates =~ s/devstate\|//;
|
||||
my @states = split /\|/, $tstates;
|
||||
my $sc = scalar (@states);
|
||||
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.'.'.$statedatapoint;
|
||||
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
my $objvalue = '';
|
||||
my $st = 0;
|
||||
while ($st < $sc) {
|
||||
if ($states[$st] eq $result) {
|
||||
$objvalue = ($st == $sc-1) ? $states[0] : $states[$st+1];
|
||||
last;
|
||||
}
|
||||
else {
|
||||
$st++;
|
||||
}
|
||||
}
|
||||
|
||||
return HMCCU_SetError ($hash, "Current device state doesn't match statevals")
|
||||
if ($objvalue eq '');
|
||||
|
||||
$objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, '');
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'config') {
|
||||
return HMCCU_SetError ($hash, "Usage: set $name config {parameter}={value} [...]") if (@a < 1);;
|
||||
|
||||
my $rc = HMCCU_RPCSetConfig ($hash, $hash->{ccuaddr}, \@a);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
else {
|
||||
my $retmsg = "HMCCUCHN: Unknown argument $opt, choose one of config datapoint devstate";
|
||||
return undef if ($hash->{statevals} eq 'readonly');
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
return $retmsg;
|
||||
}
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Get commands
|
||||
#####################################
|
||||
|
||||
sub HMCCUCHN_Get ($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $name = shift @a;
|
||||
my $opt = shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
|
||||
|
||||
my $disable = AttrVal ($name, "disable", 0);
|
||||
return undef if ($disable == 1);
|
||||
|
||||
my $hmccu_hash = $hash->{IODev};
|
||||
if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) {
|
||||
return undef if ($opt eq '?');
|
||||
return "HMCCUCHN: CCU busy";
|
||||
}
|
||||
|
||||
# my $statedatapoint = AttrVal ($name, "statedatapoint", 'STATE');
|
||||
my ($sc, $statedatapoint, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', '');
|
||||
my $ccureadings = AttrVal ($name, "ccureadings", 1);
|
||||
|
||||
my $result = '';
|
||||
my $rc;
|
||||
|
||||
if ($opt eq 'devstate') {
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.'.'.$statedatapoint;
|
||||
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return $ccureadings ? undef : $result;
|
||||
}
|
||||
elsif ($opt eq 'datapoint') {
|
||||
my $objname = shift @a;
|
||||
return HMCCU_SetError ($hash, "Usage: get $name datapoint {datapoint}") if (!defined ($objname));
|
||||
return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $hash->{ccutype},
|
||||
$hash->{ccuaddr}, $objname, 1));
|
||||
|
||||
$objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.'.'.$objname;
|
||||
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return $ccureadings ? undef : $result;
|
||||
}
|
||||
elsif ($opt eq 'channel') {
|
||||
my $dptexpr = shift @a;
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr};
|
||||
$objname .= '.'.$dptexpr if (defined ($dptexpr));
|
||||
my @chnlist = ($objname);
|
||||
($rc, $result) = HMCCU_GetChannel ($hash, \@chnlist);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
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'}]");
|
||||
}
|
||||
$rc = HMCCU_GetUpdate ($hash, $hash->{ccuaddr}, $ccuget);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'config') {
|
||||
my $ccuobj = $hash->{ccuaddr};
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset");
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return $ccureadings ? undef : $res;
|
||||
}
|
||||
elsif ($opt eq 'configdesc') {
|
||||
my $ccuobj = $hash->{ccuaddr};
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription");
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return $res;
|
||||
}
|
||||
else {
|
||||
my $retmsg = "HMCCUCHN: Unknown argument $opt, choose one of devstate:noArg datapoint";
|
||||
|
||||
my ($a, $c) = split(":", $hash->{ccuaddr});
|
||||
my @valuelist;
|
||||
my $valuecount = HMCCU_GetValidDatapoints ($hash, $hash->{ccutype}, $c, 1, \@valuelist);
|
||||
$retmsg .= ":".join(",",@valuelist) if ($valuecount > 0);
|
||||
$retmsg .= " channel update:noArg config:noArg configdesc:noArg";
|
||||
|
||||
return $retmsg;
|
||||
}
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Set error status
|
||||
#####################################
|
||||
|
||||
sub HMCCUCHN_SetError ($$)
|
||||
{
|
||||
my ($hash, $text) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $msg;
|
||||
my %errlist = (
|
||||
-1 => 'Channel name or address invalid',
|
||||
-2 => 'Execution of CCU script failed',
|
||||
-3 => 'Cannot detect IO device',
|
||||
-4 => 'Device deleted in CCU',
|
||||
-5 => 'No response from CCU',
|
||||
-6 => 'Update of readings disabled. Set attribute ccureadings first'
|
||||
);
|
||||
|
||||
if (exists ($errlist{$text})) {
|
||||
$msg = $errlist{$text};
|
||||
}
|
||||
else {
|
||||
$msg = $text;
|
||||
}
|
||||
|
||||
$msg = "HMCCUCHN: ".$name." ". $msg;
|
||||
readingsSingleUpdate ($hash, "state", "Error", 1);
|
||||
Log3 $name, 1, $msg;
|
||||
return $msg;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<a name="HMCCUCHN"></a>
|
||||
<h3>HMCCUCHN</h3>
|
||||
<ul>
|
||||
The module implements client devices for HMCCU. A HMCCU device must exist
|
||||
before a client device can be defined.
|
||||
</br></br>
|
||||
<a name="HMCCUCHNdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<br/>
|
||||
<code>define <name> HMCCUCHN {<channel-name>|<channel-address>} [readonly]</code>
|
||||
<br/><br/>
|
||||
If <i>readonly</i> parameter is specified no set command will be available.
|
||||
<br/><br/>
|
||||
Examples:<br/>
|
||||
<code>define window_living HMCCUCHN WIN-LIV-1 readonly</code><br/>
|
||||
<code>define temp_control HMCCUCHN BidCos-RF.LEQ1234567:1</code>
|
||||
<br/>
|
||||
</ul>
|
||||
<br/>
|
||||
|
||||
<a name="HMCCUCHNset"></a>
|
||||
<b>Set</b><br/>
|
||||
<ul>
|
||||
<br/>
|
||||
<li>set <name> devstate <value> [...]<br/>
|
||||
Set state of a CCU device channel. Channel datapoint must be defined
|
||||
by setting attribute 'statedatapoint'.
|
||||
<br/><br/>
|
||||
Example:<br/>
|
||||
<code>set light_entrance devstate on</code>
|
||||
</li><br/>
|
||||
<li>set <name> <statevalue><br/>
|
||||
State of a CCU device channel is set to <i>StateValue</i>. State datapoint
|
||||
must be defined as attribute statedatapoint. State values can be replaced
|
||||
by setting attribute statevals.
|
||||
<br/><br/>
|
||||
Example:<br/>
|
||||
<code>
|
||||
attr myswitch statedatapoint TEST<br/>
|
||||
attr myswitch statevals on:true,off:false<br/>
|
||||
set myswitch on
|
||||
</code>
|
||||
</li><br/>
|
||||
<li>set <name> toggle<br/>
|
||||
Toggles between values defined by attribute 'statevals'.
|
||||
</li><br/>
|
||||
<li>set <name> datapoint <datapoint> <value><br/>
|
||||
Set value of a datapoint of a CCU device channel.
|
||||
<br/><br/>
|
||||
Example:<br/>
|
||||
<code>set temp_control datapoint SET_TEMPERATURE 21</code>
|
||||
</li><br/>
|
||||
<li>set <name> config [<rpcport>] <parameter>=<value>] [...] <br/>
|
||||
Set config parameters of CCU channel.
|
||||
</li>
|
||||
</ul>
|
||||
<br/>
|
||||
|
||||
<a name="HMCCUCHNget"></a>
|
||||
<b>Get</b><br/>
|
||||
<ul>
|
||||
<br/>
|
||||
<li>get <name> devstate<br/>
|
||||
Get state of CCU device. Default datapoint STATE can be changed by setting
|
||||
attribute 'statedatapoint'.
|
||||
</li><br/>
|
||||
<li>get <name> datapoint <datapoint><br/>
|
||||
Get value of a CCU device datapoint.
|
||||
</li><br/>
|
||||
<li>get <name> config<br/>
|
||||
Get configuration parameters of CCU channel. If attribute ccureadings is 0 results will be
|
||||
displayed in browser window.
|
||||
</li><br/>
|
||||
<li>get <name> configdesc<br/>
|
||||
Get description of configuration parameters of CCU channel.
|
||||
</li><br/>
|
||||
<li>get <name> update [{'State'|'Value'}]<br/>
|
||||
Update all datapoints / readings of channel.
|
||||
</li>
|
||||
</ul>
|
||||
<br/>
|
||||
|
||||
<a name="HMCCUCHNattr"></a>
|
||||
<b>Attributes</b><br/>
|
||||
<br/>
|
||||
<ul>
|
||||
<li>ccuget <State | Value><br/>
|
||||
Set read access method for CCU channel datapoints. Method 'State' is slower than 'Value' because
|
||||
each request is sent to the device. With method 'Value' only CCU is queried. Default is 'Value'.
|
||||
</li><br/>
|
||||
<li>ccureadings <0 | 1><br/>
|
||||
If set to 1 values read from CCU will be stored as readings. Default is 1.
|
||||
</li><br/>
|
||||
<li>ccureadingfilter <filter-rule[,...]><br/>
|
||||
Only datapoints matching specified expression are stored as readings.<br/>
|
||||
Syntax for filter rule is: [channel-no:]RegExp<br/>
|
||||
If channel-no is specified the following rule applies only to this channel.
|
||||
</li><br/>
|
||||
<li>ccuscaleval <datapoint>:<factor>[,...] <br/>
|
||||
Scale datapoint values before executing set datapoint commands or after executing get
|
||||
datapoint commands. During get the value read from CCU is devided by factor. During set
|
||||
the value is multiplied by factor.
|
||||
</li><br/>
|
||||
<li>ccuverify <0 | 1 | 2><br/>
|
||||
If set to 1 a datapoint is read for verification after set operation. If set to 2 the
|
||||
corresponding reading will be set to the new value directly after setting a datapoint
|
||||
in CCU.
|
||||
</li><br/>
|
||||
<li>controldatapoint <datapoint><br/>
|
||||
Set datapoint for device control. Can be use to realize user defined control elements for
|
||||
setting control datapoint. For example if datapoint of thermostat control is
|
||||
SET_TEMPERATURE one can define a slider for setting the destination temperature with
|
||||
following attributes:<br/><br/>
|
||||
attr mydev controldatapoint SET_TEMPERATURE
|
||||
attr mydev webCmd control
|
||||
attr mydev widgetOverride control:slider,10,1,25
|
||||
</li><br/>
|
||||
<li>disable <0 | 1><br/>
|
||||
Disable client device.
|
||||
</li><br/>
|
||||
<li>statedatapoint <datapoint><br/>
|
||||
Set datapoint for devstate commands.
|
||||
</li><br/>
|
||||
<li>statevals <text>:<text>[,...]<br/>
|
||||
Define substitution for set commands values. The parameters <text>
|
||||
are available as set commands. Example:<br/>
|
||||
<code>attr my_switch statevals on:true,off:false</code><br/>
|
||||
<code>set my_switch on</code>
|
||||
</li><br/>
|
||||
<li>substitude <subst-rule>[;...]<br/>
|
||||
Define substitions for reading values. Substitutions for parfile values must
|
||||
be specified in parfiles. Syntax of subst-rule is<br/><br/>
|
||||
[datapoint!]<regexp1>:<text1>[,...]
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
=cut
|
||||
|
@ -1,830 +0,0 @@
|
||||
#####################################################################
|
||||
#
|
||||
# 88_HMCCUDEV.pm
|
||||
#
|
||||
# $Id:$
|
||||
#
|
||||
# Version 3.3
|
||||
#
|
||||
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
||||
#
|
||||
#####################################################################
|
||||
#
|
||||
# define <name> HMCCUDEV {<ccudev>|virtual} [statechannel] [readonly]
|
||||
# [{group={<device>|<channel>}[,...]|groupexp=<regexp>}]
|
||||
#
|
||||
# set <name> config [<channel>] <parameter>=<value> [...]
|
||||
# set <name> control <value>
|
||||
# set <name> datapoint <channel>.<datapoint> <value>
|
||||
# set <name> defaults
|
||||
# set <name> devstate <value>
|
||||
# set <name> on-for-timer <seconds>
|
||||
# set <name> <stateval_cmds>
|
||||
# set <name> toggle
|
||||
#
|
||||
# get <name> devstate
|
||||
# get <name> datapoint <channel>.<datapoint>
|
||||
# get <name> defaults
|
||||
# get <name> channel <channel>[.<datapoint-expr>]
|
||||
# get <name> config [<channel>]
|
||||
# get <name> configdesc [<channel>]
|
||||
# get <name> update
|
||||
#
|
||||
# attr <name> ccuget { State | Value }
|
||||
# attr <name> ccureadings { 0 | 1 }
|
||||
# attr <name> ccureadingformat { address | name }
|
||||
# attr <name> ccureadingfilter <filter-rule>[,...]
|
||||
# attr <name> ccuscaleval <datapoint>:<factor>[,...]
|
||||
# attr <name> ccuverify { 0 | 1 | 2}
|
||||
# attr <name> controldatapoint <channel-number>.<datapoint>
|
||||
# attr <name> disable { 0 | 1 }
|
||||
# attr <name> mapdatapoints <channel>.<datapoint>=<channel>.<datapoint>[,...]
|
||||
# attr <name> statechannel <channel>
|
||||
# attr <name> statedatapoint [<channel-number>.]<datapoint>
|
||||
# attr <name> statevals <text1>:<subtext1>[,...]
|
||||
# attr <name> substitute <regexp1>:<subtext1>[,...]
|
||||
#
|
||||
#####################################################################
|
||||
# Requires module 88_HMCCU
|
||||
#####################################################################
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use SetExtensions;
|
||||
# use Data::Dumper;
|
||||
|
||||
use Time::HiRes qw( gettimeofday usleep );
|
||||
|
||||
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 ccureadingfilter:textField-long ccureadingformat:name,address ccureadings:0,1 ccuget:State,Value ccuscaleval ccuverify:0,1,2 disable:0,1 mapdatapoints:textField-long statevals substitute statechannel statedatapoint controldatapoint stripnumber:0,1,2 ". $readingFnAttributes;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Define device
|
||||
#####################################
|
||||
|
||||
sub HMCCUDEV_Define ($@)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
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;
|
||||
|
||||
my $hmccu_hash = undef;
|
||||
|
||||
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');
|
||||
next if ($d eq $name);
|
||||
next if ($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 (HMCCU_IsDevAddr ($devspec, 0)) {
|
||||
# CCU Device address without interface
|
||||
$hash->{ccuaddr} = $devspec;
|
||||
$hash->{ccuname} = HMCCU_GetDeviceName ($devspec, '');
|
||||
$hash->{ccuif} = HMCCU_GetDeviceInterface ($hash->{ccuaddr}, 'BidCos-RF');
|
||||
}
|
||||
else {
|
||||
# CCU Device name
|
||||
$hash->{ccuname} = $devspec;
|
||||
my ($add, $chn) = HMCCU_GetAddress ($devspec, '', '');
|
||||
return "Name is a channel name" if ($chn ne '');
|
||||
$hash->{ccuaddr} = $add;
|
||||
$hash->{ccuif} = HMCCU_GetDeviceInterface ($hash->{ccuaddr}, 'BidCos-RF');
|
||||
}
|
||||
|
||||
return "CCU device address not found for $devspec" if ($hash->{ccuaddr} eq '');
|
||||
return "CCU device name not found for $devspec" if ($hash->{ccuname} eq '');
|
||||
|
||||
$hash->{ccutype} = HMCCU_GetDeviceType ($hash->{ccuaddr}, '');
|
||||
$hash->{channels} = HMCCU_GetDeviceChannels ($hash->{ccuaddr});
|
||||
|
||||
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 == 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++;
|
||||
}
|
||||
else {
|
||||
return $usage;
|
||||
}
|
||||
$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);
|
||||
|
||||
readingsSingleUpdate ($hash, "state", "Initialized", 1);
|
||||
$hash->{ccudevstate} = 'Active';
|
||||
|
||||
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 ($attrname eq "mapdatapoints") {
|
||||
return "Not a virtual device" if ($hash->{ccuif} ne "VirtualDevices");
|
||||
}
|
||||
}
|
||||
elsif ($cmd eq "del") {
|
||||
if ($attrname eq "statevals") {
|
||||
$hash->{statevals} = "devstate";
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Set commands
|
||||
#####################################
|
||||
|
||||
sub HMCCUDEV_Set ($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $name = shift @a;
|
||||
my $opt = shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, -3) if (!exists ($hash->{IODev}));
|
||||
return undef if ($hash->{statevals} eq 'readonly');
|
||||
|
||||
my $disable = AttrVal ($name, "disable", 0);
|
||||
return undef if ($disable == 1);
|
||||
|
||||
my $hmccu_hash = $hash->{IODev};
|
||||
my $hmccu_name = $hash->{IODev}->{NAME};
|
||||
if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) {
|
||||
return undef if ($opt eq '?');
|
||||
return "HMCCUDEV: CCU busy";
|
||||
}
|
||||
|
||||
# my $statechannel = AttrVal ($name, "statechannel", '');
|
||||
# my $statedatapoint = AttrVal ($name, "statedatapoint", 'STATE');
|
||||
my $statevals = AttrVal ($name, "statevals", '');
|
||||
# my $controldatapoint = AttrVal ($name, "controldatapoint", '');
|
||||
my ($statechannel, $statedatapoint, $controlchannel, $controldatapoint) =
|
||||
HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', '');
|
||||
|
||||
my $result = '';
|
||||
my $rc;
|
||||
|
||||
if ($opt eq 'datapoint') {
|
||||
my $objname = shift @a;
|
||||
# my $objvalue = join ('%20', @a);
|
||||
my $objvalue = shift @a;
|
||||
|
||||
if (!defined ($objname) || $objname !~ /^[0-9]+\..+$/ || !defined ($objvalue)) {
|
||||
return HMCCU_SetError ($hash, "Usage: set $name datapoint {channel-number}.{datapoint} {value}");
|
||||
}
|
||||
return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $hash->{ccutype},
|
||||
$hash->{ccuaddr}, $objname, 2));
|
||||
|
||||
$objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, '');
|
||||
|
||||
# Build datapoint address
|
||||
$objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$objname;
|
||||
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'control') {
|
||||
return HMCCU_SetError ($hash, "Attribute controldatapoint not set") if ($controldatapoint eq '');
|
||||
my $objvalue = shift @a;
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$controlchannel.'.'.$controldatapoint;
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt =~ /^($hash->{statevals})$/) {
|
||||
my $cmd = $1;
|
||||
my $objvalue = ($cmd ne 'devstate') ? $cmd : shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, "No state channel specified") if ($statechannel eq '');
|
||||
return HMCCU_SetError ($hash, "Usage: set $name devstate {value}") if (!defined ($objvalue));
|
||||
|
||||
$objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, '');
|
||||
|
||||
# Build datapoint address
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$statechannel.'.'.$statedatapoint;
|
||||
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'toggle') {
|
||||
return HMCCU_SetError ($hash, "Attribute statevals not set")
|
||||
if ($statevals eq '' || !exists($hash->{statevals}));
|
||||
return HMCCU_SetError ($hash, "No state channel specified") if ($statechannel eq '');
|
||||
|
||||
my $tstates = $hash->{statevals};
|
||||
$tstates =~ s/devstate\|//;
|
||||
my @states = split /\|/, $tstates;
|
||||
my $sc = scalar (@states);
|
||||
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$statechannel.'.'.$statedatapoint;
|
||||
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
my $objvalue = '';
|
||||
my $st = 0;
|
||||
while ($st < $sc) {
|
||||
if ($states[$st] eq $result) {
|
||||
$objvalue = ($st == $sc-1) ? $states[0] : $states[$st+1];
|
||||
last;
|
||||
}
|
||||
else {
|
||||
$st++;
|
||||
}
|
||||
}
|
||||
|
||||
return HMCCU_SetError ($hash, "Current device state doesn't match statevals")
|
||||
if ($objvalue eq '');
|
||||
|
||||
$objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, '');
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'on-for-timer') {
|
||||
return HMCCU_SetError ($hash, "Attribute statevals not set")
|
||||
if ($statevals eq '' || !exists($hash->{statevals}));
|
||||
return HMCCU_SetError ($hash, "No state channel specified") if ($statechannel eq '');
|
||||
return HMCCU_SetError ($hash, "No state value for 'on' defined")
|
||||
if ("on" !~ /($hash->{statevals})/);
|
||||
|
||||
my $timespec = shift @a;
|
||||
return HMCCU_SetError ($hash, "Usage: set $name on-for-timer {on-time} [{ramp-time}]")
|
||||
if (!defined ($timespec));
|
||||
|
||||
my $swrtdpt = '';
|
||||
my $ramptime = shift @a;
|
||||
if (defined ($ramptime)) {
|
||||
$swrtdpt = HMCCU_GetSwitchDatapoint ($hash, $hash->{ccutype}, 'ramptime');
|
||||
return HMCCU_SetError ($hash, "Can't find ramp-time datapoint for device type")
|
||||
if ($swrtdpt eq '');
|
||||
}
|
||||
|
||||
my $swotdpt = HMCCU_GetSwitchDatapoint ($hash, $hash->{ccutype}, 'ontime');
|
||||
return HMCCU_SetError ($hash, "Can't find on-time datapoint for device type")
|
||||
if ($swotdpt eq '');
|
||||
|
||||
# Set on time
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$swotdpt;
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $timespec);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
# Set ramp time
|
||||
if ($swrtdpt ne '') {
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$swrtdpt;
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $ramptime);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
}
|
||||
|
||||
# Set state
|
||||
$objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$statechannel.'.'.$statedatapoint;
|
||||
my $objvalue = HMCCU_Substitute ("on", $statevals, 1, '');
|
||||
$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'config') {
|
||||
return HMCCU_SetError ($hash, "Usage: set $name config [{channel-number}] {parameter}={value} [...]")
|
||||
if (@a < 1);
|
||||
my $objname = $hash->{ccuaddr};
|
||||
$objname .= ':'.shift @a if ($a[0] =~ /^[0-9]$/);
|
||||
|
||||
my $rc = HMCCU_RPCSetConfig ($hash, $objname, \@a);
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
elsif ($opt eq 'defaults') {
|
||||
HMCCU_SetDefaults ($hash);
|
||||
HMCCU_SetState ($hash, "OK");
|
||||
return undef;
|
||||
}
|
||||
else {
|
||||
my $retmsg = "HMCCUDEV: Unknown argument $opt, choose one of config control datapoint defaults:noArg";
|
||||
return undef if ($hash->{statevals} eq 'readonly');
|
||||
|
||||
if ($statechannel 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" if ($statechannel ne '' &&
|
||||
HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $statechannel, "ON_TIME", 2));
|
||||
}
|
||||
}
|
||||
|
||||
return $retmsg;
|
||||
}
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Get commands
|
||||
#####################################
|
||||
|
||||
sub HMCCUDEV_Get ($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $name = shift @a;
|
||||
my $opt = shift @a;
|
||||
|
||||
return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
|
||||
|
||||
my $disable = AttrVal ($name, "disable", 0);
|
||||
return undef if ($disable == 1);
|
||||
|
||||
my $hmccu_hash = $hash->{IODev};
|
||||
if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) {
|
||||
return undef if ($opt eq '?');
|
||||
return "HMCCUDEV: CCU busy";
|
||||
}
|
||||
|
||||
# my $statechannel = AttrVal ($name, 'statechannel', '');
|
||||
# my $statedatapoint = AttrVal ($name, 'statedatapoint', 'STATE');
|
||||
my $ccureadings = AttrVal ($name, 'ccureadings', 1);
|
||||
my ($statechannel, $statedatapoint, $cc, $cd) = HMCCU_GetSpecialDatapoints (
|
||||
$hash, '', 'STATE', '', '');
|
||||
|
||||
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') {
|
||||
return HMCCU_SetError ($hash, "No state channel specified") if ($statechannel eq '');
|
||||
|
||||
my $objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$statechannel.'.'.$statedatapoint;
|
||||
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
|
||||
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
return $ccureadings ? undef : $result;
|
||||
}
|
||||
elsif ($opt eq 'datapoint') {
|
||||
my $objname = shift @a;
|
||||
if (!defined ($objname) || $objname !~ /^[0-9]+\..*$/) {
|
||||
return HMCCU_SetError ($hash, "Usage: get $name datapoint {channel-number}.{datapoint}");
|
||||
}
|
||||
return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $hash->{ccutype},
|
||||
$hash->{ccuaddr}, $objname, 1));
|
||||
|
||||
$objname = $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$objname;
|
||||
($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
|
||||
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
|
||||
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
|
||||
return $ccureadings ? undef : $result;
|
||||
}
|
||||
elsif ($opt eq 'channel') {
|
||||
my @chnlist;
|
||||
foreach my $objname (@a) {
|
||||
last if (!defined ($objname));
|
||||
if ($objname =~ /^([0-9]+)/ && exists ($hash->{channels})) {
|
||||
return HMCCU_SetError ($hash, -7) if ($1 >= $hash->{channels});
|
||||
}
|
||||
else {
|
||||
return HMCCU_SetError ($hash, -7);
|
||||
}
|
||||
if ($objname =~ /^[0-9]{1,2}.*=/) {
|
||||
$objname =~ s/=/ /;
|
||||
}
|
||||
push (@chnlist, $hash->{ccuif}.'.'.$hash->{ccuaddr}.':'.$objname);
|
||||
}
|
||||
if (@chnlist == 0) {
|
||||
return HMCCU_SetError ($hash, "Usage: get $name channel {channel-number}[.{datapoint-expr}] [...]");
|
||||
}
|
||||
|
||||
($rc, $result) = HMCCU_GetChannel ($hash, \@chnlist);
|
||||
return HMCCU_SetError ($hash, $rc) 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->{ccuname} ne 'none') {
|
||||
$rc = HMCCU_GetUpdate ($hash, $hash->{ccuaddr}, $ccuget);
|
||||
return HMCCU_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 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, $hash->{ccuaddr}, $ccuget);
|
||||
return HMCCU_SetError ($hash, -2) if ($result eq '');
|
||||
return HMCCU_FormatDeviceInfo ($result);
|
||||
}
|
||||
elsif ($opt eq 'config') {
|
||||
my $channel = undef;
|
||||
my $par = shift @a;
|
||||
if (defined ($par)) {
|
||||
$channel = $par if ($par =~ /^[0-9]{1,2}$/);
|
||||
}
|
||||
|
||||
my $ccuobj = $hash->{ccuaddr};
|
||||
$ccuobj .= ':'.$channel if (defined ($channel));
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset");
|
||||
return HMCCU_SetError ($hash, $rc) if ($rc < 0);
|
||||
HMCCU_SetState ($hash, "OK") if (exists ($hash->{STATE}) && $hash->{STATE} eq "Error");
|
||||
return $ccureadings ? undef : $res;
|
||||
}
|
||||
elsif ($opt eq 'configdesc') {
|
||||
my $channel = undef;
|
||||
my $par = shift @a;
|
||||
if (defined ($par)) {
|
||||
$channel = $par if ($par =~ /^[0-9]{1,2}$/);
|
||||
}
|
||||
|
||||
my $ccuobj = $hash->{ccuaddr};
|
||||
$ccuobj .= ':'.$channel if (defined ($channel));
|
||||
|
||||
my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription");
|
||||
return HMCCU_SetError ($hash, $rc) 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);
|
||||
return $result;
|
||||
}
|
||||
else {
|
||||
my $retmsg = "HMCCUDEV: Unknown argument $opt, choose one of datapoint";
|
||||
|
||||
my @valuelist;
|
||||
my $valuecount = HMCCU_GetValidDatapoints ($hash, $hash->{ccutype}, -1, 1, \@valuelist);
|
||||
|
||||
$retmsg .= ":".join(",", @valuelist) if ($valuecount > 0);
|
||||
$retmsg .= " defaults:noArg channel update:noArg config configdesc deviceinfo:noArg";
|
||||
|
||||
if ($statechannel ne '') {
|
||||
$retmsg .= ' devstate:noArg';
|
||||
}
|
||||
return $retmsg;
|
||||
}
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Set error status
|
||||
#####################################
|
||||
|
||||
sub HMCCUDEV_SetError ($$)
|
||||
{
|
||||
my ($hash, $text) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $msg;
|
||||
my %errlist = (
|
||||
-1 => 'Channel name or address invalid',
|
||||
-2 => 'Execution of CCU script failed',
|
||||
-3 => 'Cannot detect IO device',
|
||||
-4 => 'Device deleted in CCU',
|
||||
-5 => 'No response from CCU',
|
||||
-6 => 'Update of readings disabled. Set attribute ccureadings first'
|
||||
);
|
||||
|
||||
if (exists ($errlist{$text})) {
|
||||
$msg = $errlist{$text};
|
||||
}
|
||||
else {
|
||||
$msg = $text;
|
||||
}
|
||||
|
||||
$msg = "HMCCUDEV: ".$name." ". $msg;
|
||||
readingsSingleUpdate ($hash, "state", "Error", 1);
|
||||
Log3 $name, 1, $msg;
|
||||
return $msg;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<a name="HMCCUDEV"></a>
|
||||
<h3>HMCCUDEV</h3>
|
||||
<ul>
|
||||
The module implements client devices for HMCCU. A HMCCU device must exist
|
||||
before a client device can be defined.
|
||||
</br></br>
|
||||
<a name="HMCCUDEVdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<br/>
|
||||
<code>define <name> HMCCUDEV {<device-name>|<device-address>} [<statechannel>] [readonly] [{group={device|channel}[,...]|groupexp=regexp]</code>
|
||||
<br/><br/>
|
||||
If <i>readonly</i> parameter is specified no set command will be available.
|
||||
<br/><br/>
|
||||
Examples:<br/>
|
||||
<code>define window_living HMCCUDEV WIN-LIV-1 readonly</code><br/>
|
||||
<code>define temp_control HMCCUDEV BidCos-RF.LEQ1234567 1</code>
|
||||
<br/>
|
||||
</ul>
|
||||
<br/>
|
||||
|
||||
<a name="HMCCUDEVset"></a>
|
||||
<b>Set</b><br/>
|
||||
<ul>
|
||||
<br/>
|
||||
<li>set <name> devstate <value> [...]<br/>
|
||||
Set state of a CCU device channel. Channel must be defined as attribute
|
||||
'statechannel'. Default datapoint can be modfied by setting attribute
|
||||
'statedatapoint'.
|
||||
<br/><br/>
|
||||
Example:<br/>
|
||||
<code>set light_entrance devstate on</code>
|
||||
</li><br/>
|
||||
<li>set <name> defaults<br/>
|
||||
Set default attributes for CCU device type.
|
||||
</li><br/>
|
||||
<li>set <name> on-for-timer <seconds> <seconds><br/>
|
||||
Switch device on for specified time. Requires that device contains a datapoint
|
||||
ON_TIME and optionally RAMP_TIME. Attribute 'statevals' must contain value 'on'.
|
||||
</li><br/>
|
||||
<li>set <name> <statevalue> <br/>
|
||||
State of a CCU device channel is set to 'statevalue'. Channel must
|
||||
be defined as attribute 'statechannel'. Default datapoint STATE can be
|
||||
modified by setting attribute 'statedatapoint'. Values for <i>statevalue</i>
|
||||
are defined by setting attribute 'statevals'.
|
||||
<br/><br/>
|
||||
Example:<br/>
|
||||
<code>
|
||||
attr myswitch statechannel 1<br/>
|
||||
attr myswitch statevals on:true,off:false<br/>
|
||||
set myswitch on
|
||||
</code>
|
||||
</li><br/>
|
||||
<li>set <name> toggle<br/>
|
||||
Toggles between values defined by attribute 'statevals'.
|
||||
</li><br/>
|
||||
<li>set <name> datapoint <channel-number>.<datapoint> <value> [...]<br/>
|
||||
Set value of a datapoint of a CCU device channel.
|
||||
<br/><br/>
|
||||
Example:<br/>
|
||||
<code>set temp_control datapoint 1.SET_TEMPERATURE 21</code>
|
||||
</li><br/>
|
||||
<li>set <name> config [<channel-number>] <parameter>=<value> [...]<br/>
|
||||
Set configuration parameter of CCU device or channel.
|
||||
</li>
|
||||
</ul>
|
||||
<br/>
|
||||
|
||||
<a name="HMCCUDEVget"></a>
|
||||
<b>Get</b><br/>
|
||||
<ul>
|
||||
<br/>
|
||||
<li>get <name> devstate<br/>
|
||||
Get state of CCU device. Attribute 'statechannel' must be set.
|
||||
</li><br/>
|
||||
<li>get <name> datapoint <channel-number>.<datapoint><br/>
|
||||
Get value of a CCU device datapoint.
|
||||
</li><br/>
|
||||
<li>get <name> config [<channel-number>]<br/>
|
||||
Get configuration parameters of CCU device. If attribute ccureadings is set to 0
|
||||
parameters are displayed in browser window (no readings set).
|
||||
</li><br/>
|
||||
<li>get <name> configdesc [<channel-number>] [<rpcport>]<br/>
|
||||
Get description of configuration parameters for CCU device.
|
||||
</li><br/>
|
||||
<li>get <name> defaults<br/>
|
||||
Display default attributes for CCU device type.
|
||||
</li><br/>
|
||||
<li>get <name> update [{'State'|'Value'}]<br/>
|
||||
Update datapoints / readings of device.
|
||||
</li><br/>
|
||||
<li>get <name> deviceinfo [{'State'|'Value'}]<br/>
|
||||
Display all channels and datapoints of device.
|
||||
</li>
|
||||
</ul>
|
||||
<br/>
|
||||
|
||||
<a name="HMCCUDEVattr"></a>
|
||||
<b>Attributes</b><br/>
|
||||
<br/>
|
||||
<ul>
|
||||
<li>ccuget <State | <u>Value</u>><br/>
|
||||
Set read access method for CCU channel datapoints. Method 'State' is slower than 'Value' because
|
||||
each request is sent to the device. With method 'Value' only CCU is queried. Default is 'Value'.
|
||||
</li><br/>
|
||||
<li>ccureadings <0 | <u>1</u>><br/>
|
||||
If set to 1 values read from CCU will be stored as readings. Default is 1.
|
||||
</li><br/>
|
||||
<li>ccureadingfilter <filter-rule[,...]><br/>
|
||||
Only datapoints matching specified expression are stored as readings.
|
||||
Syntax for filter rule is: [channel-no:]RegExp<br/>
|
||||
If channel-no is specified the following rule applies only to this channel.
|
||||
</li><br/>
|
||||
<li>ccureadingformat <address | name> <br/>
|
||||
Set format of readings. Default is 'name'.
|
||||
</li><br/>
|
||||
<li>ccuscaleval <datapoint>:<factor>[,...] <br/>
|
||||
Scale datapoint values before executing set datapoint commands or after executing get
|
||||
datapoint commands. During get the value read from CCU is devided by factor. During set
|
||||
the value is multiplied by factor.
|
||||
</li><br/>
|
||||
<li>ccuverify <0 | 1 | 2><br/>
|
||||
If set to 1 a datapoint is read for verification after set operation. If set to 2 the
|
||||
corresponding reading will be set to the new value directly after setting a datapoint
|
||||
in CCU.
|
||||
</li><br/>
|
||||
<li>controldatapoint <channel-number.datapoint><br/>
|
||||
Set datapoint for device control. Can be use to realize user defined control elements for
|
||||
setting control datapoint. For example if datapoint of thermostat control is
|
||||
2.SET_TEMPERATURE one can define a slider for setting the destination temperature with
|
||||
following attributes:<br/><br/>
|
||||
attr mydev controldatapoint 2.SET_TEMPERATURE
|
||||
attr mydev webCmd control
|
||||
attr mydev widgetOverride control:slider,10,1,25
|
||||
</li><br/>
|
||||
<li>disable <0 | 1><br/>
|
||||
Disable client device.
|
||||
</li><br/>
|
||||
<li>mapdatapoints <channel.datapoint>=<channel.datapoint>[,...]
|
||||
Map channel to other channel in virtual devices (groups). Readings will be duplicated.
|
||||
</li><br/>
|
||||
<li>statechannel <channel-number><br/>
|
||||
Channel for setting device state by devstate command.
|
||||
</li><br/>
|
||||
<li>statedatapoint <datapoint><br/>
|
||||
Datapoint for setting device state by devstate command.
|
||||
</li><br/>
|
||||
<li>statevals <text>:<text>[,...]<br/>
|
||||
Define substitution for set commands values. The parameters <text>
|
||||
are available as set commands. Example:<br/>
|
||||
<code>attr my_switch statevals on:true,off:false</code><br/>
|
||||
<code>set my_switch on</code>
|
||||
</li><br/>
|
||||
<li>substitute <subst-rule>[;...]<br/>
|
||||
Define substitutions for reading values. Substitutions for parfile values must
|
||||
be specified in parfiles. Syntax of subst-rule is<br/><br/>
|
||||
[datapoint!]<regexp>:<text>[,...]
|
||||
</li><br/>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
=cut
|
||||
|
@ -1,122 +0,0 @@
|
||||
#########################################################################
|
||||
#
|
||||
# HMCCUConf.pm
|
||||
#
|
||||
# $Id:$
|
||||
#
|
||||
# Version 3.2
|
||||
#
|
||||
# Configuration parameters for Homematic devices.
|
||||
#
|
||||
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
||||
#
|
||||
#########################################################################
|
||||
|
||||
package HMCCUConf;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use vars qw(%HMCCU_DEV_DEFAULTS);
|
||||
|
||||
# Default attributes for Homematic devices of type HMCCUDEV
|
||||
%HMCCU_DEV_DEFAULTS = (
|
||||
"HM-Sec-SCo" => { # Tuer/Fensterkontakt optisch
|
||||
ccureadingfilter => "(ERROR|UNREACH|LOWBAT|STATE)",
|
||||
statechannel => 1,
|
||||
substitute => "STATE!(0|false):closed,(1|true):open;LOWBAT!(0|false):no,(1|true):yes"
|
||||
},
|
||||
"HM-Sec-SC" => { # Tuer/Fensterkontakt magnetisch
|
||||
ccureadingfilter => "(ERROR|UNREACH|LOWBAT|STATE)",
|
||||
statechannel => 1,
|
||||
substitute => "STATE!(0|false):closed,(1|true):open;LOWBAT!(0|false):no,(1|true):yes"
|
||||
},
|
||||
"HM-LC-Sw1-Pl-2" => { # Steckdose
|
||||
ccureadingfilter => "(STATE|UNREACH)",
|
||||
controldatapoint => "1.STATE",
|
||||
statechannel => 1,
|
||||
statevals => "on:true,off:false",
|
||||
substitute => "STATE!(1|true):on,(0|false):off",
|
||||
webCmd => "control",
|
||||
widgetOverride => "control:uzsuToggle,off,on"
|
||||
},
|
||||
"HMIP-PS" => { # Steckdose (IP)
|
||||
ccureadingfilter => "(STATE|UNREACH)",
|
||||
statechannel => 3,
|
||||
statevals => "on:1,off:0",
|
||||
substitute => "STATE!(1|true):on,(0|false):off"
|
||||
},
|
||||
"HM-ES-PMSw1-Pl" => { # Steckdose mit Energiemessung
|
||||
ccureadingfilter => "(STATE|UNREACH|CURRENT|ENERGY_COUNTER|POWER)",
|
||||
statechannel => 1,
|
||||
statevals => "on:1,off:0",
|
||||
stripnumber => 1,
|
||||
substitute => "STATE!(1|true):on,(0|false):off"
|
||||
},
|
||||
"HMIP-PSM" => { # Steckdose mit Energiemessung (IP)
|
||||
ccureadingfilter => "(STATE|UNREACH|CURRENT|ENERGY_COUNTER|POWER)",
|
||||
statechannel => 3,
|
||||
statevals => "on:true,off:false",
|
||||
stripnumber => 1,
|
||||
substitute => "STATE!(1|true):on,(0|false):off"
|
||||
},
|
||||
"HM-LC-Bl1PBU-FM" => { # Rolladenaktor
|
||||
cmdIcon => "up:fts_shutter_up stop:fts_shutter_manual down:fts_shutter_down",
|
||||
controldatapoint => "1.LEVEL",
|
||||
eventMap => "/datapoint 1.STOP 1:stop/datapoint 1.LEVEL 1:down/datapoint 1.LEVEL 0:up/",
|
||||
statechannel => 1,
|
||||
statevals => "up:0.0,down:1.0",
|
||||
stripnumber => 1,
|
||||
webCmd => "control:up:stop:down",
|
||||
widgetOverride => "control:slider,0,0.05,1,1"
|
||||
},
|
||||
"HM-TC-IT-WM-W-EU" => { # Wandthermostat
|
||||
ccureadingfilter => "(UNREACH|^HUMIDITY|^TEMPERATURE|^SET_TEMPERATURE|^LOWBAT$|^WINDOW_OPEN)",
|
||||
cmdIcon => "Auto:sani_heating_automatic Manu:sani_heating_manual Boost:sani_heating_boost on:general_an off:general_aus",
|
||||
controldatapoint => "2.SET_TEMPERATURE",
|
||||
eventMap => "/datapoint 2.MANU_MODE 20.0:Manu/datapoint 2.AUTO_MODE 1:Auto/datapoint 2.BOOST_MODE 1:Boost/datapoint 2.MANU_MODE 4.5:off/datapoint 2.MANU_MODE 30.5:on/",
|
||||
statechannel => 2,
|
||||
statedatapoint => "SET_TEMPERATURE",
|
||||
stripnumber => 1,
|
||||
substitute => "LOWBAT!(0|false):no,(1|true):yes;CONTROL_MODE!0:AUTO,1:MANU,2:PARTY,3:BOOST;WINDOW_OPEN_REPORTING!(true|1):open,(false|0):closed",
|
||||
webCmd => "control:Auto:Manu:Boost:on:off",
|
||||
widgetOverride => "control:slider,10,1,25"
|
||||
},
|
||||
"HM-CC-RT-DN" => { # Heizkörperthermostat
|
||||
ccureadingfilter => "(UNREACH|LOWBAT|TEMPERATURE|VALVE_STATE|CONTROL)",
|
||||
cmdIcon => "Auto:sani_heating_automatic Manu:sani_heating_manual Boost:sani_heating_boost on:general_an off:general_aus",
|
||||
controldatapoint => "4.SET_TEMPERATURE",
|
||||
eventMap => "/datapoint 4.MANU_MODE 20.0:Manu/datapoint 4.AUTO_MODE 1:Auto/datapoint 4.BOOST_MODE 1:Boost/datapoint 4.MANU_MODE 4.5:off/datapoint 4.MANU_MODE 30.5:on/",
|
||||
statechannel => 4,
|
||||
statedatapoint => "SET_TEMPERATURE",
|
||||
stripnumber => 1,
|
||||
substitute => "LOWBAT!(0|false):no,(1|true):yes;CONTROL_MODE!0:AUTO,1:MANU,2:PARTY,3:BOOST",
|
||||
webCmd => "control:Auto:Manu:Boost:on:off",
|
||||
widgetOverride => "control:slider,10,1,25"
|
||||
},
|
||||
"HM-WDS40-TH-I" => { # Temperatur/Luftfeuchte Sensor
|
||||
ccureadingfilter => "(UNREACH|^HUMIDITY|^TEMPERATURE|^LOWBAT$)",
|
||||
statechannel => 1,
|
||||
statedatapoint => "TEMPERATURE",
|
||||
stripnumber => 1,
|
||||
substitute => "LOWBAT!(0|false):no,(1|true):yes"
|
||||
},
|
||||
"HM-ES-TX-WM" => { # Stromzähler Sensor
|
||||
ccureadingfilter => "(UNREACH|LOWBAT|^ENERGY_COUNTER|^POWER)",
|
||||
substitute => "LOWBAT!(true|1):yes,(false|0):no"
|
||||
},
|
||||
"HM-CC-VG-1" => { # Heizungsgruppe
|
||||
ccureadingfilter => "(^SET_TEMPERATURE|^TEMPERATURE|^HUMIDITY|LOWBAT$|^VALVE|^CONTROL|^WINDOW_OPEN)",
|
||||
cmdIcon => "Auto:sani_heating_automatic Manu:sani_heating_manual Boost:sani_heating_boost on:general_an off:general_aus",
|
||||
controldatapoint => "1.SET_TEMPERATURE",
|
||||
eventMap => "/datapoint 1.MANU_MODE 20.0:Manu/datapoint 1.AUTO_MODE 1:Auto/datapoint 1.BOOST_MODE 1:Boost/datapoint 1.MANU_MODE 4.5:off/datapoint 1.MANU_MODE 30.5:on/",
|
||||
statechannel => 1,
|
||||
statedatapoint => "SET_TEMPERATURE",
|
||||
stripnumber => 1,
|
||||
substitute => "LOWBAT!(0|false):no,(1|true):yes;CONTROL_MODE!0:AUTO,1:MANU,2:PARTY,3:BOOST;WINDOW_OPEN_REPORTING!(true|1):open,(false|0):closed",
|
||||
webCmd => "control:Auto:Manu:Boost:on:off",
|
||||
widgetOverride => "control:slider,10,1,25"
|
||||
}
|
||||
);
|
||||
|
||||
1;
|
@ -1,186 +0,0 @@
|
||||
package RPCQueue;
|
||||
|
||||
use strict;
|
||||
use IO::File;
|
||||
use Fcntl 'SEEK_END', 'SEEK_SET', 'O_CREAT', 'O_RDWR';
|
||||
use Carp qw(carp croak);
|
||||
|
||||
our $VERSION = '1.01';
|
||||
|
||||
sub new
|
||||
{
|
||||
my $class = shift;
|
||||
my $mi = $class . '->new()';
|
||||
|
||||
croak "$mi requires an even number of parameters" if (@_ & 1);
|
||||
my %params = @_;
|
||||
|
||||
# convert to lower case
|
||||
my @keylist = keys %params;
|
||||
foreach my $key (@keylist) {
|
||||
my $val = $params{$key};
|
||||
delete $params{$key};
|
||||
$params{ lc($key) } = $val;
|
||||
}
|
||||
|
||||
croak "$mi needs an File parameter" unless exists $params{file};
|
||||
my $queue_file = delete $params{file};
|
||||
my $idx_file = $queue_file . '.idx';
|
||||
$queue_file .= '.dat';
|
||||
|
||||
my $self;
|
||||
my $mode = delete $params{mode} || '0600';
|
||||
$self->{block_size} = delete $params{blocksize} || 64;
|
||||
$self->{seperator} = delete $params{seperator} || "\n";
|
||||
$self->{sep_length} = length $self->{seperator};
|
||||
|
||||
croak "Seperator length cannot be greater than BlockSize" if ($self->{sep_length} > $self->{block_size});
|
||||
|
||||
$self->{queue_file} = $queue_file;
|
||||
$self->{idx_file} = $idx_file;
|
||||
|
||||
$self->{queue} = new IO::File $queue_file, O_CREAT | O_RDWR, $mode or croak $!;
|
||||
$self->{idx} = new IO::File $idx_file, O_CREAT | O_RDWR, $mode or croak $!;
|
||||
|
||||
### Default ptr to 0, replace it with value in idx file if one exists
|
||||
$self->{idx}->sysseek(0, SEEK_SET);
|
||||
$self->{idx}->sysread($self->{ptr}, 1024);
|
||||
$self->{ptr} = '0' unless $self->{ptr};
|
||||
|
||||
if($self->{ptr} > -s $queue_file)
|
||||
{
|
||||
carp "Ptr is greater than queue file size, resetting ptr to '0'";
|
||||
|
||||
$self->{idx}->truncate(0) or croak "Could not truncate idx: $!";
|
||||
$self->{idx}->sysseek(0, SEEK_SET);
|
||||
$self->{idx}->syswrite('0') or croak "Could not syswrite to idx: $!";
|
||||
}
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub enq
|
||||
{
|
||||
my ($self, $element) = @_;
|
||||
|
||||
$self->{queue}->sysseek(0, SEEK_END);
|
||||
|
||||
if(ref $element)
|
||||
{
|
||||
croak 'Cannot handle references';
|
||||
}
|
||||
|
||||
if($element =~ s/$self->{seperator}//g)
|
||||
{
|
||||
carp "Removed illegal seperator(s) from $element";
|
||||
}
|
||||
|
||||
$self->{queue}->syswrite("$element$self->{seperator}") or croak "Could not syswrite to queue: $!";
|
||||
}
|
||||
|
||||
sub deq
|
||||
{
|
||||
my $self = shift;
|
||||
my $element;
|
||||
|
||||
$self->{queue}->sysseek($self->{ptr}, SEEK_SET);
|
||||
|
||||
my $i;
|
||||
while($self->{queue}->sysread($_, $self->{block_size}))
|
||||
{
|
||||
|
||||
$i = index($_, $self->{seperator});
|
||||
if($i != -1)
|
||||
{
|
||||
$element .= substr($_, 0, $i);
|
||||
$self->{ptr} += $i + $self->{sep_length};
|
||||
$self->{queue}->sysseek($self->{ptr}, SEEK_SET);
|
||||
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
## If seperator isn't found, go back 'sep_length' spaces to ensure we don't miss it between reads
|
||||
$element .= substr($_, 0, -$self->{sep_length}, '');
|
||||
$self->{ptr} += $self->{block_size} - $self->{sep_length};
|
||||
$self->{queue}->sysseek($self->{ptr}, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
## If queue seek pointer is at the EOF, truncate the queue file
|
||||
if($self->{queue}->sysread($_, 1) == 0)
|
||||
{
|
||||
$self->{queue}->truncate(0) or croak "Could not truncate queue: $!";
|
||||
$self->{queue}->sysseek($self->{ptr} = 0, SEEK_SET);
|
||||
}
|
||||
|
||||
## Set idx file contents to point to the current seek position in queue file
|
||||
$self->{idx}->truncate(0) or croak "Could not truncate idx: $!";
|
||||
$self->{idx}->sysseek(0, SEEK_SET);
|
||||
$self->{idx}->syswrite($self->{ptr}) or croak "Could not syswrite to idx: $!";
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
sub peek
|
||||
{
|
||||
my ($self, $count) = @_;
|
||||
croak "Invalid argument to peek ($count)" unless $count > 0;
|
||||
|
||||
my $elements;
|
||||
|
||||
$self->{queue}->sysseek($self->{ptr}, SEEK_SET);
|
||||
|
||||
my (@items, $remainder);
|
||||
GATHER:
|
||||
while($self->{queue}->sysread($_, $self->{block_size}))
|
||||
{
|
||||
if(defined $remainder)
|
||||
{
|
||||
$_ = $remainder . $_;
|
||||
}
|
||||
|
||||
@items = split /$self->{seperator}/, $_, -1;
|
||||
$remainder = pop @items;
|
||||
|
||||
foreach (@items)
|
||||
{
|
||||
push @$elements, $_;
|
||||
last GATHER if $count == @$elements;
|
||||
}
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
sub reset
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->{idx}->truncate(0) or croak "Could not truncate idx: $!";
|
||||
$self->{idx}->sysseek(0, SEEK_SET);
|
||||
$self->{idx}->syswrite('0') or croak "Could not syswrite to idx: $!";
|
||||
|
||||
$self->{queue}->sysseek($self->{ptr} = 0, SEEK_SET);
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->{idx}->close();
|
||||
$self->{queue}->close();
|
||||
}
|
||||
|
||||
sub delete
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->close();
|
||||
|
||||
unlink $self->{queue_file};
|
||||
unlink $self->{idx_file};
|
||||
}
|
||||
|
||||
1;
|
@ -1,443 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
#########################################################
|
||||
# ccurpcd.pl
|
||||
#
|
||||
# $Id:
|
||||
#
|
||||
# Version 2.1
|
||||
#
|
||||
# FHEM RPC server for Homematic CCU.
|
||||
#
|
||||
# (C) 2016 by zap
|
||||
#--------------------------------------------------------
|
||||
# Usage:
|
||||
#
|
||||
# ccurpcd.pl Hostname Port QueueFile LogFile
|
||||
# ccurpcd.pl shutdown Hostname Port PID
|
||||
#--------------------------------------------------------
|
||||
# Queue file entries:
|
||||
#
|
||||
# Code|Data[|...]
|
||||
#
|
||||
# Server Loop: SL|pid|Server
|
||||
# Initialized: IN|INIT|1|Server
|
||||
# New device: ND|Address|Type
|
||||
# Updated device: UD|Address|Hint
|
||||
# Deleted device: DD|Address
|
||||
# Replace device: RD|Address1|Address2
|
||||
# Readd device: RA|Address
|
||||
# Event: EV|Address|Attribute|Value
|
||||
# Shutdown: EX|SHUTDOWN|pid|Server
|
||||
#########################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
# use File::Queue;
|
||||
use RPC::XML::Server;
|
||||
use RPC::XML::Client;
|
||||
use IO::Socket::INET;
|
||||
use FindBin qw($Bin);
|
||||
use lib "$Bin";
|
||||
use RPCQueue;
|
||||
|
||||
# Global variables
|
||||
my $client;
|
||||
my $server;
|
||||
my $queue;
|
||||
my $logfile;
|
||||
my $shutdown = 0;
|
||||
my $eventcount = 0;
|
||||
my $totalcount = 0;
|
||||
my $loglevel = 0;
|
||||
my %ev = ('total', 0, 'EV', 0, 'ND', 0, 'DD', 0, 'UD', 0, 'RD', 0, 'RA', 0, 'SL', 0, 'IN', 0, 'EX', 0);
|
||||
|
||||
# Functions
|
||||
sub CheckProcess ($$);
|
||||
sub Log ($);
|
||||
sub WriteQueue ($);
|
||||
sub CCURPC_Shutdown ($$$);
|
||||
sub CCURPC_Initialize ($$);
|
||||
|
||||
|
||||
#####################################
|
||||
# Get PID of running RPC server or 0
|
||||
#####################################
|
||||
|
||||
sub CheckProcess ($$)
|
||||
{
|
||||
my ($prcname, $port) = @_;
|
||||
|
||||
my $filename = $prcname;
|
||||
my $pdump = `ps ax | grep $prcname | grep -v grep`;
|
||||
my @plist = split "\n", $pdump;
|
||||
foreach my $proc (@plist) {
|
||||
# Remove leading blanks, fix for MacOS. Thanks to mcdeck
|
||||
$proc =~ s/^\s+//;
|
||||
my @procattr = split /\s+/, $proc;
|
||||
if ($procattr[0] != $$ && $procattr[4] =~ /perl$/ &&
|
||||
($procattr[5] eq $prcname || $procattr[5] =~ /\/ccurpcd\.pl$/) &&
|
||||
$procattr[7] eq "$port") {
|
||||
Log "Process $proc is running connected to CCU port $port";
|
||||
return $procattr[0];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Write logfile entry
|
||||
#####################################
|
||||
|
||||
sub Log ($)
|
||||
{
|
||||
my @messages = @_;
|
||||
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime ();
|
||||
|
||||
if (open (LOGFILE, '>>', $logfile)) {
|
||||
printf LOGFILE "%02d.%02d.%04d %02d:%02d:%02d ",
|
||||
$mday,$mon+1,$year+1900,$hour,$min,$sec;
|
||||
foreach my $token (@messages) {
|
||||
print LOGFILE $token;
|
||||
}
|
||||
print LOGFILE "\n";
|
||||
close LOGFILE;
|
||||
}
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Write queue entry
|
||||
#####################################
|
||||
|
||||
sub WriteQueue ($)
|
||||
{
|
||||
my ($message) = @_;
|
||||
|
||||
$queue->enq ($message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Shutdown RPC connection
|
||||
#####################################
|
||||
|
||||
sub CCURPC_Shutdown ($$$)
|
||||
{
|
||||
my ($serveraddr, $serverport, $pid) = @_;
|
||||
|
||||
# Detect local IP
|
||||
my $socket = IO::Socket::INET->new (PeerAddr => $serveraddr, PeerPort => $serverport);
|
||||
if (!$socket) {
|
||||
print "Can't connect to CCU port $serverport";
|
||||
return undef;
|
||||
}
|
||||
my $localaddr = $socket->sockhost ();
|
||||
close ($socket);
|
||||
|
||||
my $ccurpcport = 5400+$serverport;
|
||||
my $callbackurl = "http://".$localaddr.":".$ccurpcport."/fh".$serverport;
|
||||
|
||||
$client = RPC::XML::Client->new ("http://$serveraddr:$serverport/");
|
||||
|
||||
print "Trying to deregister RPC server $callbackurl\n";
|
||||
$client->send_request("init", $callbackurl);
|
||||
sleep (3);
|
||||
|
||||
print "Sending SIGINT to PID $pid\n";
|
||||
kill ('INT', $pid);
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Initialize RPC connection
|
||||
#####################################
|
||||
|
||||
sub CCURPC_Initialize ($$)
|
||||
{
|
||||
my ($serveraddr, $serverport) = @_;
|
||||
my $callbackport = 5400+$serverport;
|
||||
|
||||
# Create RPC server
|
||||
$server = RPC::XML::Server->new (port=>$callbackport);
|
||||
if (!ref($server))
|
||||
{
|
||||
Log "Can't create RPC callback server on port $callbackport. Port in use?";
|
||||
return undef;
|
||||
}
|
||||
else {
|
||||
Log "Callback server created listening on port $callbackport";
|
||||
}
|
||||
|
||||
# Callback for events
|
||||
Log "Adding callback for events";
|
||||
$server->add_method (
|
||||
{ name=>"event",
|
||||
signature=> ["string string string string int","string string string string double","string string string string boolean","string string string string i4"],
|
||||
code=>\&CCURPC_EventCB
|
||||
}
|
||||
);
|
||||
|
||||
# Callback for new devices
|
||||
Log "Adding callback for new devices";
|
||||
$server->add_method (
|
||||
{ name=>"newDevices",
|
||||
signature=>["string string array"],
|
||||
code=>\&CCURPC_NewDevicesCB
|
||||
}
|
||||
);
|
||||
|
||||
# Callback for deleted devices
|
||||
Log "Adding callback for deleted devices";
|
||||
$server->add_method (
|
||||
{ name=>"deleteDevices",
|
||||
signature=>["string string array"],
|
||||
code=>\&CCURPC_DeleteDevicesCB
|
||||
}
|
||||
);
|
||||
|
||||
# Callback for modified devices
|
||||
Log "Adding callback for modified devices";
|
||||
$server->add_method (
|
||||
{ name=>"updateDevice",
|
||||
signature=>["string string string int"],
|
||||
code=>\&CCURPC_UpdateDeviceCB
|
||||
}
|
||||
);
|
||||
|
||||
# Callback for replaced devices
|
||||
Log "Adding callback for replaced devices";
|
||||
$server->add_method (
|
||||
{ name=>"replaceDevice",
|
||||
signature=>["string string string string"],
|
||||
code=>\&CCURPC_ReplaceDeviceCB
|
||||
}
|
||||
);
|
||||
|
||||
# Callback for readded devices
|
||||
Log "Adding callback for readded devices";
|
||||
$server->add_method (
|
||||
{ name=>"replaceDevice",
|
||||
signature=>["string string array"],
|
||||
code=>\&CCURPC_ReaddDeviceCB
|
||||
}
|
||||
);
|
||||
|
||||
# Dummy implementation, always return an empty array
|
||||
$server->add_method (
|
||||
{ name=>"listDevices",
|
||||
signature=>["array string"],
|
||||
code=>\&CCURPC_ListDevicesCB
|
||||
}
|
||||
);
|
||||
|
||||
return "OK";
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Callback for new devices
|
||||
#####################################
|
||||
|
||||
sub CCURPC_NewDevicesCB ($$$)
|
||||
{
|
||||
my ($server, $cb, $a) = @_;
|
||||
|
||||
Log "NewDevice: received ".scalar(@$a)." device specifications";
|
||||
|
||||
for my $dev (@$a) {
|
||||
$ev{total}++;
|
||||
$ev{ND}++;
|
||||
WriteQueue ("ND|".$dev->{ADDRESS}."|".$dev->{TYPE});
|
||||
}
|
||||
|
||||
# return RPC::XML::array->new();
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Callback for deleted devices
|
||||
#####################################
|
||||
|
||||
sub CCURPC_DeleteDevicesCB ($$$)
|
||||
{
|
||||
my ($server, $cb, $a) = @_;
|
||||
|
||||
Log "DeleteDevice: received ".scalar(@$a)." device addresses";
|
||||
|
||||
for my $dev (@$a) {
|
||||
$ev{total}++;
|
||||
$ev{DD}++;
|
||||
WriteQueue ("DD|".$dev);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Callback for modified devices
|
||||
#####################################
|
||||
|
||||
sub CCURPC_UpdateDeviceCB ($$$$)
|
||||
{
|
||||
my ($server, $cb, $devid, $hint) = @_;
|
||||
|
||||
$ev{total}++;
|
||||
$ev{UD}++;
|
||||
WriteQueue ("UD|".$devid."|".$hint);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Callback for replaced devices
|
||||
#####################################
|
||||
|
||||
sub CCURPC_ReplaceDeviceCB ($$$$)
|
||||
{
|
||||
my ($server, $cb, $devid1, $devid2) = @_;
|
||||
|
||||
$ev{total}++;
|
||||
$ev{RD}++;
|
||||
WriteQueue ("RD|".$devid1."|".$devid2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Callback for readded devices
|
||||
#####################################
|
||||
|
||||
sub CCURPC_ReaddDevicesCB ($$$)
|
||||
{
|
||||
my ($server, $cb, $a) = @_;
|
||||
|
||||
Log "ReaddDevice: received ".scalar(@$a)." device addresses";
|
||||
|
||||
for my $dev (@$a) {
|
||||
$ev{total}++;
|
||||
$ev{RA}++;
|
||||
WriteQueue ("RA|".$dev);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Callback for handling CCU events
|
||||
#####################################
|
||||
|
||||
sub CCURPC_EventCB ($$$$$)
|
||||
{
|
||||
my ($server,$cb,$devid,$attr,$val) = @_;
|
||||
|
||||
$ev{total}++;
|
||||
$ev{EV}++;
|
||||
WriteQueue ("EV|".$devid."|".$attr."|".$val);
|
||||
|
||||
$eventcount++;
|
||||
if (($eventcount % 500) == 0 && $loglevel == 2) {
|
||||
Log "Received $eventcount events from CCU since last check";
|
||||
my @stkeys = ('total', 'EV', 'ND', 'DD', 'RD', 'RA', 'UD', 'IN', 'SL', 'EX');
|
||||
my $msg = "ST";
|
||||
foreach my $stkey (@stkeys) {
|
||||
$msg .= "|".$ev{$stkey};
|
||||
}
|
||||
WriteQueue ($msg);
|
||||
}
|
||||
|
||||
# Never remove this statement!
|
||||
return;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Callback for list devices
|
||||
#####################################
|
||||
|
||||
sub CCURPC_ListDevicesCB ()
|
||||
{
|
||||
my ($server, $cb) = @_;
|
||||
|
||||
$ev{total}++;
|
||||
$ev{IN}++;
|
||||
$cb = "unknown" if (!defined ($cb));
|
||||
Log "ListDevices $cb. Sending init to HMCCU";
|
||||
WriteQueue ("IN|INIT|1|$cb");
|
||||
|
||||
return RPC::XML::array->new();
|
||||
}
|
||||
|
||||
#####################################
|
||||
# MAIN
|
||||
#####################################
|
||||
|
||||
my $name = $0;
|
||||
|
||||
# Process command line arguments
|
||||
if ($#ARGV+1 < 4) {
|
||||
print "Usage: $name CCU-Host Port QueueFile LogFile LogLevel\n";
|
||||
print " $name shutdown CCU-Host Port PID\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
#
|
||||
# Manually shutdown RPC server
|
||||
#
|
||||
if ($ARGV[0] eq 'shutdown') {
|
||||
CCURPC_Shutdown ($ARGV[1], $ARGV[2], $ARGV[3]);
|
||||
exit 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Start RPC server
|
||||
#
|
||||
my $ccuhost = $ARGV[0];
|
||||
my $ccuport = $ARGV[1];
|
||||
my $queuefile = $ARGV[2];
|
||||
$logfile = $ARGV[3];
|
||||
$loglevel = $ARGV[4] if ($#ARGV+1 == 5);
|
||||
|
||||
my $pid = CheckProcess ($name, $ccuport);
|
||||
if ($pid > 0) {
|
||||
Log "Error: ccurpcd.pl is already running (PID=$pid) for CCU port $ccuport";
|
||||
die "Error: ccurpcd.pl is already running (PID=$pid) for CCU port $ccuport\n";
|
||||
}
|
||||
|
||||
# Create or open queue
|
||||
Log "Creating file queue";
|
||||
$queue = new RPCQueue (File => $queuefile, Mode => 0666);
|
||||
if (!defined ($queue)) {
|
||||
Log "Error: Can't create queue";
|
||||
die "Error: Can't create queue\n";
|
||||
}
|
||||
else {
|
||||
$queue->reset ();
|
||||
while ($queue->deq ()) { }
|
||||
}
|
||||
|
||||
# Initialize RPC server
|
||||
Log "Initializing RPC server";
|
||||
my $callbackurl = CCURPC_Initialize ($ccuhost, $ccuport);
|
||||
if (!defined ($callbackurl)) {
|
||||
Log "Error: Can't initialize RPC server";
|
||||
die "Error: Can't initialize RPC server\n";
|
||||
}
|
||||
|
||||
# Server loop is interruptable bei signal SIGINT
|
||||
Log "Entering server loop. Use kill -SIGINT $$ to terminate program";
|
||||
WriteQueue ("SL|$$|CB".$ccuport);
|
||||
$server->server_loop;
|
||||
|
||||
$totalcount++;
|
||||
WriteQueue ("EX|SHUTDOWN|$$|CB".$ccuport);
|
||||
$ev{total}++;
|
||||
$ev{EX}++;
|
||||
Log "RPC server terminated";
|
||||
|
||||
if ($loglevel == 2) {
|
||||
foreach my $cnt (sort keys %ev) {
|
||||
Log "Events $cnt = ".$ev{$cnt};
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
UPD 2016-03-13_18:13:51 85357 FHEM/88_HMCCU.pm
|
||||
UPD 2016-03-11_18:56:09 17459 FHEM/88_HMCCUCHN.pm
|
||||
UPD 2016-03-11_18:56:09 24314 FHEM/88_HMCCUDEV.pm
|
||||
UPD 2016-03-11_18:56:21 7857 FHEM/ccurpcd.pl
|
||||
UPD 2016-03-02_17:28:28 4377 FHEM/RPCQueue.pm
|
@ -1,42 +0,0 @@
|
||||
#
|
||||
# Example configuration for modules HMCCU and HMCCUDEV
|
||||
#
|
||||
|
||||
# Define device (hostname of CCU is "homematic")
|
||||
define d_ccu HMCCU homematic
|
||||
|
||||
# Generate readings for values read from CCU
|
||||
attr d_ccu ccureadings 1
|
||||
|
||||
# Parameterfile with channel/datapoints to be read from CCU
|
||||
# by command get parfile
|
||||
attr d_ccu parfile /opt/fhem/scripts/hmvalues.txt
|
||||
|
||||
# If CCU systemvariable name ends with a ":" this character
|
||||
# will be removed. Applies to command set devstate only.
|
||||
# Helpful if CCU variable should be set as a reaction on an
|
||||
# event.
|
||||
attr d_ccu stripchar :
|
||||
|
||||
# Substitute values read from CCU before storing them in a
|
||||
# reading.
|
||||
attr d_ccu substitute false:closed,true:open
|
||||
|
||||
# Subsitute values before setting them
|
||||
attr d_ccu statevals on:true,off:false
|
||||
|
||||
# Define client device for door/window sensor
|
||||
define d_hm_dw_window HMCCUDEV TF-WZ-Window readonly
|
||||
attr d_hm_dw_window ccureadings 1
|
||||
attr d_hm_dw_window substitute false:closed,true:open
|
||||
|
||||
# Define client device for subwoofer with state channel 1
|
||||
define d_hm_st_sub HMCCUDEV ST-WZ-Sub 1
|
||||
attr d_hm_st_sub ccureadings 1
|
||||
attr d_hm_st_sub statevals on:true,off:false
|
||||
attr d_hm_st_sub substitute true:on,false:off
|
||||
|
||||
# Update CCU readings and client devices every 10 minutes
|
||||
define at_ccu at +*00:10:00 get d_ccu parfile
|
||||
attr at_ccu alignTime 00:05
|
||||
|
@ -1,35 +0,0 @@
|
||||
#
|
||||
# Beispiel fuer eine HMCCU Parameterdatei
|
||||
# Setzen mit attr <name> parfile <Datei>
|
||||
# oder Angabe bei get <name> parfile <Datei>
|
||||
#
|
||||
# Tueren und Fenster
|
||||
# Zustaende der CCU Geräte werden in den FHEM
|
||||
# Readings ersetzt (false=closed,true=open).
|
||||
#
|
||||
TF-AZ-Fenster1:1.STATE false:closed,true:open
|
||||
TF-AZ-Fenster2:1.STATE false:closed,true:open
|
||||
TF-BE-Fenster:1.STATE false:closed,true:open
|
||||
TF-BO-Fenster:1.STATE false:closed,true:open
|
||||
TF-FL-Haustuer:1.STATE false:closed,true:open
|
||||
TF-GA-Fenster:1.STATE false:closed,true:open
|
||||
TF-GZ-Fenster1:1.STATE false:closed,true:open
|
||||
TF-SZ-Fenster1:1.STATE false:closed,true:open
|
||||
TF-SZ-Fenster2:1.STATE false:closed,true:open
|
||||
TF-WZ-Balkon:1.STATE false:closed,true:open
|
||||
TF-WZ-Terrasse:1.STATE false:closed,true:open
|
||||
#
|
||||
# Schalter
|
||||
#
|
||||
ST-HR-Pumpe:1.STATE false:off,true:on
|
||||
ST-WZ-Bass:1.STATE false:off,true:on
|
||||
#
|
||||
# Temperatur und Luftfeuchte Sensoren
|
||||
# Angabe Datenpunkt fehlt => Es werden alle Datenpunkte
|
||||
# abgeholt (TEMPERATURE, HUMIDITY)
|
||||
#
|
||||
KL-SZ-THX:1
|
||||
KL-AZ-THX:1
|
||||
KL-GA-THX:1
|
||||
KL-BO-THX:1
|
||||
KL-WZ-THX-1:1
|
@ -1,35 +0,0 @@
|
||||
#
|
||||
# Beispiel fuer eine HMCCU Parameterdatei
|
||||
# Setzen mit attr <name> parfile <Datei>
|
||||
# oder Angabe bei get <name> parfile <Datei>
|
||||
#
|
||||
# Tueren und Fenster
|
||||
# Zustaende der CCU Geräte werden in den FHEM
|
||||
# Readings ersetzt (false=closed,true=open).
|
||||
#
|
||||
TF-AZ-Fenster1:1.STATE false:closed,true:open
|
||||
TF-AZ-Fenster2:1.STATE false:closed,true:open
|
||||
TF-BE-Fenster:1.STATE false:closed,true:open
|
||||
TF-BO-Fenster:1.STATE false:closed,true:open
|
||||
TF-FL-Haustuer:1.STATE false:closed,true:open
|
||||
TF-GA-Fenster:1.STATE false:closed,true:open
|
||||
TF-GZ-Fenster1:1.STATE false:closed,true:open
|
||||
TF-SZ-Fenster1:1.STATE false:closed,true:open
|
||||
TF-SZ-Fenster2:1.STATE false:closed,true:open
|
||||
TF-WZ-Balkon:1.STATE false:closed,true:open
|
||||
TF-WZ-Terrasse:1.STATE false:closed,true:open
|
||||
#
|
||||
# Schalter
|
||||
#
|
||||
ST-HR-Pumpe:1.STATE false:off,true:on
|
||||
ST-WZ-Bass:1.STATE false:off,true:on
|
||||
#
|
||||
# Temperatur und Luftfeuchte Sensoren
|
||||
# Angabe Datenpunkt fehlt => Es werden alle Datenpunkte
|
||||
# abgeholt (TEMPERATURE, HUMIDITY)
|
||||
#
|
||||
KL-SZ-THX:1
|
||||
KL-AZ-THX:1
|
||||
KL-GA-THX:1
|
||||
KL-BO-THX:1
|
||||
KL-WZ-THX-1:1
|
Loading…
x
Reference in New Issue
Block a user