mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 06:39:11 +00:00
HMCCU: Optimized client device update
git-svn-id: https://svn.fhem.de/fhem/trunk@17633 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
e94ce46065
commit
4f2f7948f3
@ -1,5 +1,6 @@
|
||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||
# Do not insert empty lines here, update check depends on it.
|
||||
- feature: 88_HMCCU: client device update optimized
|
||||
- bugfix: 89_FULLY: fixed version check bug
|
||||
- feature: 74_AMADtaskerset: import with share link
|
||||
- change: 42_AptToDate: change to package methode
|
||||
|
@ -4,9 +4,9 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Version 4.3.004
|
||||
# Version 4.3.005
|
||||
#
|
||||
# Module for communication between FHEM and Homematic CCU2.
|
||||
# Module for communication between FHEM and Homematic CCU2/3.
|
||||
#
|
||||
# Supports BidCos-RF, BidCos-Wired, HmIP-RF, virtual CCU channels,
|
||||
# CCU group devices, HomeGear, CUxD, Osram Lightify, Homematic Virtual Layer
|
||||
@ -38,7 +38,7 @@
|
||||
# [defattr] [duplicates] [save] [<attr>=<val> [...]]}]
|
||||
# get <name> dump {devtypes|datapoints} [<filter>]
|
||||
# get <name> dutycycle
|
||||
# get <name> exportdefaults {filename} [csv]
|
||||
# get <name> exportdefaults {filename} [csv] [all]
|
||||
# get <name> firmware [{type-expr}|full]
|
||||
# get <name> parfile [<parfile>]
|
||||
# get <name> rpcevents
|
||||
@ -108,7 +108,7 @@ my %HMCCU_CUST_CHN_DEFAULTS;
|
||||
my %HMCCU_CUST_DEV_DEFAULTS;
|
||||
|
||||
# HMCCU version
|
||||
my $HMCCU_VERSION = '4.3.004';
|
||||
my $HMCCU_VERSION = '4.3.005';
|
||||
|
||||
# Default RPC port (BidCos-RF)
|
||||
my $HMCCU_RPC_PORT_DEFAULT = 2001;
|
||||
@ -245,8 +245,8 @@ sub HMCCU_AggregateReadings ($$);
|
||||
sub HMCCU_AggregationRules ($$);
|
||||
|
||||
# Handling of default attributes
|
||||
sub HMCCU_ExportDefaults ($);
|
||||
sub HMCCU_ExportDefaultsCSV ($);
|
||||
sub HMCCU_ExportDefaults ($$);
|
||||
sub HMCCU_ExportDefaultsCSV ($$);
|
||||
sub HMCCU_ImportDefaults ($);
|
||||
sub HMCCU_FindDefaults ($$);
|
||||
sub HMCCU_SetDefaults ($);
|
||||
@ -272,10 +272,11 @@ sub HMCCU_SubstVariables ($$$);
|
||||
sub HMCCU_BulkUpdate ($$$$);
|
||||
sub HMCCU_GetUpdate ($$$);
|
||||
sub HMCCU_UpdateClients ($$$$$);
|
||||
sub HMCCU_UpdateInternalValues ($$$$);
|
||||
sub HMCCU_UpdateMultipleDevices ($$);
|
||||
sub HMCCU_UpdatePeers ($$$$);
|
||||
sub HMCCU_UpdateSingleDatapoint ($$$$);
|
||||
sub HMCCU_UpdateSingleDevice ($$$);
|
||||
sub HMCCU_UpdateSingleDevice ($$$$);
|
||||
|
||||
# RPC functions
|
||||
sub HMCCU_GetRPCCallbackURL ($$$$$);
|
||||
@ -316,8 +317,10 @@ sub HMCCU_IsFlag ($$);
|
||||
|
||||
# Handle interfaces, devices and channels
|
||||
sub HMCCU_CreateDevice ($$$$$);
|
||||
sub HMCCU_DeleteDevice ($);
|
||||
sub HMCCU_FormatDeviceInfo ($);
|
||||
sub HMCCU_GetAddress ($$$$);
|
||||
sub HMCCU_GetAffectedAddresses ($);
|
||||
sub HMCCU_GetCCUDeviceParam ($$);
|
||||
sub HMCCU_GetChannelName ($$$);
|
||||
sub HMCCU_GetDeviceChannels ($$$);
|
||||
@ -369,7 +372,7 @@ sub HMCCU_QueueEnq ($$);
|
||||
sub HMCCU_QueueDeq ($);
|
||||
|
||||
# Helper functions
|
||||
sub HMCCU_CalculateReading ($$$);
|
||||
sub HMCCU_CalculateReading ($$);
|
||||
sub HMCCU_EncodeEPDisplay ($);
|
||||
sub HMCCU_ExprMatch ($$$);
|
||||
sub HMCCU_ExprNotMatch ($$$);
|
||||
@ -419,7 +422,7 @@ sub HMCCU_Initialize ($)
|
||||
" ccudef-hmstatevals:textField-long ccudef-substitute:textField-long".
|
||||
" ccudef-readingname:textField-long ccudef-readingfilter:textField-long".
|
||||
" ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc".
|
||||
" ccuflags:multiple-strict,extrpc,intrpc,procrpc,dptnocheck,noagg,nohmstate,logEvents,noReadings,nonBlocking".
|
||||
" ccuflags:multiple-strict,extrpc,intrpc,procrpc,dptnocheck,noagg,nohmstate,logEvents,noEvents,noReadings,nonBlocking".
|
||||
" ccuReqTimeout ccuGetVars rpcinterval:2,3,5,7,10 rpcqueue".
|
||||
" rpcport:multiple-strict,".join(',',sort keys %HMCCU_RPC_NUMPORT).
|
||||
" rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute".
|
||||
@ -831,9 +834,9 @@ sub HMCCU_AggregationRules ($$)
|
||||
# Export default attributes.
|
||||
######################################################################
|
||||
|
||||
sub HMCCU_ExportDefaults ($)
|
||||
sub HMCCU_ExportDefaults ($$)
|
||||
{
|
||||
my ($filename) = @_;
|
||||
my ($filename, $all) = @_;
|
||||
|
||||
return 0 if (!open (DEFFILE, ">$filename"));
|
||||
|
||||
@ -853,6 +856,24 @@ sub HMCCU_ExportDefaults ($)
|
||||
}
|
||||
}
|
||||
|
||||
if ($all) {
|
||||
print DEFFILE "# HMCCU custom default attributes for channels\n";
|
||||
foreach my $t (keys %HMCCU_CUST_CHN_DEFAULTS) {
|
||||
print DEFFILE "\nchannel:$t\n";
|
||||
foreach my $a (sort keys %{$HMCCU_CUST_CHN_DEFAULTS{$t}}) {
|
||||
print DEFFILE "$a=".$HMCCU_CUST_CHN_DEFAULTS{$t}{$a}."\n";
|
||||
}
|
||||
}
|
||||
|
||||
print DEFFILE "\n# HMCCU custom default attributes for devices\n";
|
||||
foreach my $t (keys %HMCCU_CUST_DEV_DEFAULTS) {
|
||||
print DEFFILE "\ndevice:$t\n";
|
||||
foreach my $a (sort keys %{$HMCCU_CUST_DEV_DEFAULTS{$t}}) {
|
||||
print DEFFILE "$a=".$HMCCU_CUST_DEV_DEFAULTS{$t}{$a}."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close (DEFFILE);
|
||||
|
||||
return 1;
|
||||
@ -862,9 +883,9 @@ sub HMCCU_ExportDefaults ($)
|
||||
# Export default attributes as CSV file.
|
||||
######################################################################
|
||||
|
||||
sub HMCCU_ExportDefaultsCSV ($)
|
||||
sub HMCCU_ExportDefaultsCSV ($$)
|
||||
{
|
||||
my ($filename) = @_;
|
||||
my ($filename, $all) = @_;
|
||||
|
||||
my %attrlist = (
|
||||
'_type' => '', '_description' => '', '_channels' => '',
|
||||
@ -903,6 +924,30 @@ sub HMCCU_ExportDefaultsCSV ($)
|
||||
print DEFFILE "\n";
|
||||
}
|
||||
|
||||
if ($all) {
|
||||
# Write channel configurations
|
||||
foreach my $t (keys %HMCCU_CUST_CHN_DEFAULTS) {
|
||||
print DEFFILE "C";
|
||||
$attrlist{'_type'} = $t;
|
||||
foreach $a (sort keys %attrlist) {
|
||||
my $v = exists ($HMCCU_CUST_CHN_DEFAULTS{$t}{$a}) ? $HMCCU_CUST_CHN_DEFAULTS{$t}{$a} : $attrlist{$a};
|
||||
print DEFFILE ",\"$v\"";
|
||||
}
|
||||
print DEFFILE "\n";
|
||||
}
|
||||
|
||||
# Write device configurations
|
||||
foreach my $t (keys %HMCCU_CUST_DEV_DEFAULTS) {
|
||||
print DEFFILE "D";
|
||||
$attrlist{'_type'} = $t;
|
||||
foreach $a (sort keys %attrlist) {
|
||||
my $v = exists ($HMCCU_CUST_DEV_DEFAULTS{$t}{$a}) ? $HMCCU_CUST_DEV_DEFAULTS{$t}{$a} : $attrlist{$a};
|
||||
print DEFFILE ",\"$v\"";
|
||||
}
|
||||
print DEFFILE "\n";
|
||||
}
|
||||
}
|
||||
|
||||
close (DEFFILE);
|
||||
|
||||
return 1;
|
||||
@ -1961,14 +2006,19 @@ sub HMCCU_Get ($@)
|
||||
}
|
||||
elsif ($opt eq 'exportdefaults') {
|
||||
my $filename = shift @$a;
|
||||
my $csv = shift @$a;
|
||||
$usage = "Usage: get $name $opt filename ['all'] ['csv']";
|
||||
my $csv = 0;
|
||||
my $all = 0;
|
||||
|
||||
foreach my $defopt (@$a) {
|
||||
if ($defopt eq 'csv') { $csv = 1; }
|
||||
elsif ($defopt eq 'all') { $all = 1; }
|
||||
else { return HMCCU_SetError ($hash, $usage); }
|
||||
}
|
||||
|
||||
$csv = 'default' if (!defined ($csv));
|
||||
$usage = "Usage: get $name $opt filename [{csv|default}]";
|
||||
return HMCCU_SetError ($hash, $usage) if (!defined ($filename));
|
||||
return HMCCU_SetError ($hash, $usage) if ($csv !~ /^(default|csv)$/);
|
||||
|
||||
my $rc = $csv ne 'csv' ? HMCCU_ExportDefaults ($filename) : HMCCU_ExportDefaultsCSV ($filename);
|
||||
my $rc = $csv ? HMCCU_ExportDefaultsCSV ($filename, $all) : HMCCU_ExportDefaults ($filename, $all);
|
||||
return HMCCU_SetError ($hash, -16) if ($rc == 0);
|
||||
return HMCCU_SetState ($hash, "OK", "Default attributes written to $filename");
|
||||
}
|
||||
@ -2671,11 +2721,14 @@ sub HMCCU_SubstRule ($$$)
|
||||
# %% = Previous original / raw value
|
||||
# $ = Converted / formatted value
|
||||
# $$ = Previous converted / formatted value
|
||||
# Parameter dplist is a comma separated list of value keys in format
|
||||
# [address:]Channel.Datapoint.
|
||||
######################################################################
|
||||
|
||||
sub HMCCU_SubstVariables ($$$)
|
||||
{
|
||||
my ($clhash, $text, $dplist) = @_;
|
||||
my $fnc = "HMCCU_SubstVariables";
|
||||
|
||||
my @varlist;
|
||||
if (defined ($dplist)) {
|
||||
@ -2685,9 +2738,14 @@ sub HMCCU_SubstVariables ($$$)
|
||||
@varlist = keys %{$clhash->{hmccu}{dp}};
|
||||
}
|
||||
|
||||
HMCCU_Trace ($clhash, 2, $fnc, "text=$text");
|
||||
|
||||
# Substitute datapoint variables by value
|
||||
foreach my $dp (@varlist) {
|
||||
my ($chn, $dpt) = split (/\./, $dp);
|
||||
|
||||
HMCCU_Trace ($clhash, 2, $fnc, "var=$dp");
|
||||
|
||||
if (defined ($clhash->{hmccu}{dp}{$dp}{OSVAL})) {
|
||||
$text =~ s/\$\$\{?$dp\}?/$clhash->{hmccu}{dp}{$dp}{OSVAL}/g;
|
||||
$text =~ s/\$\$\{?$dpt\}?/$clhash->{hmccu}{dp}{$dp}{OSVAL}/g;
|
||||
@ -2703,9 +2761,12 @@ sub HMCCU_SubstVariables ($$$)
|
||||
if (defined ($clhash->{hmccu}{dp}{$dp}{VAL})) {
|
||||
$text =~ s/\%\{?$dp\}?/$clhash->{hmccu}{dp}{$dp}{VAL}/g;
|
||||
$text =~ s/\%\{?$dpt\}?/$clhash->{hmccu}{dp}{$dp}{VAL}/g;
|
||||
$text =~ s/$dp/$clhash->{hmccu}{dp}{$dp}{VAL}/g;
|
||||
}
|
||||
}
|
||||
|
||||
HMCCU_Trace ($clhash, 2, $fnc, "text=$text");
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
@ -2837,6 +2898,44 @@ sub HMCCU_CreateDevice ($$$$$)
|
||||
return 0;
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
# Remove virtual device from internal device tables.
|
||||
##########################################################################
|
||||
|
||||
sub HMCCU_DeleteDevice ($)
|
||||
{
|
||||
my ($clthash) = @_;
|
||||
my $name = $clthash->{NAME};
|
||||
|
||||
return if (!exists ($clthash->{IODev}) || !exists ($clthash->{ccuif}) ||
|
||||
!exists ($clthash->{ccuaddr}));
|
||||
return if ($clthash->{ccuif} ne 'fhem');
|
||||
|
||||
my $hmccu_hash = $clthash->{IODev};
|
||||
my $devaddr = $clthash->{ccuaddr};
|
||||
my $channels = exists ($clthash->{channels}) ? $clthash->{channels} : 0;
|
||||
|
||||
# Delete device address entries
|
||||
if (exists ($hmccu_hash->{hmccu}{dev}{$devaddr})) {
|
||||
delete $hmccu_hash->{hmccu}{dev}{$devaddr};
|
||||
}
|
||||
|
||||
# Delete channel address and name entries
|
||||
for (my $chn=0; $chn<=$channels; $chn++) {
|
||||
if (exists ($hmccu_hash->{hmccu}{dev}{"$devaddr:$chn"})) {
|
||||
delete $hmccu_hash->{hmccu}{dev}{"$devaddr:$chn"};
|
||||
}
|
||||
if (exists ($hmccu_hash->{hmccu}{adr}{"$name:$chn"})) {
|
||||
delete $hmccu_hash->{hmccu}{adr}{"$name:$chn"};
|
||||
}
|
||||
}
|
||||
|
||||
# Delete device name entries
|
||||
if (exists ($hmccu_hash->{hmccu}{adr}{$name})) {
|
||||
delete $hmccu_hash->{hmccu}{adr}{$name};
|
||||
}
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
# Update parameters in internal device tables and client devices.
|
||||
# Parameter devices is a hash reference with following keys:
|
||||
@ -3034,14 +3133,14 @@ sub HMCCU_UpdateSingleDatapoint ($$$$)
|
||||
my ($devaddr, $chnnum) = HMCCU_SplitChnAddr ($ccuaddr);
|
||||
$objects{$devaddr}{$chn}{$dpt} = $value;
|
||||
|
||||
my $rc = HMCCU_UpdateSingleDevice ($hmccu_hash, $hash, \%objects);
|
||||
my $rc = HMCCU_UpdateMultipleDevices ($hmccu_hash, \%objects);
|
||||
return (ref ($rc)) ? $rc->{$devaddr}{$chn}{$dpt} : $value;
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Update readings of client device.
|
||||
# Parameter objects is a hash reference which contains updated data
|
||||
# for any device:
|
||||
# for devices:
|
||||
# {devaddr}{channelno}{datapoint} = value
|
||||
# If client device is virtual device group: check if group members are
|
||||
# affected by updates and update readings in virtual group device.
|
||||
@ -3049,9 +3148,9 @@ sub HMCCU_UpdateSingleDatapoint ($$$$)
|
||||
# {devaddr}{datapoint} = value
|
||||
######################################################################
|
||||
|
||||
sub HMCCU_UpdateSingleDevice ($$$)
|
||||
sub HMCCU_UpdateSingleDevice ($$$$)
|
||||
{
|
||||
my ($ccuhash, $clthash, $objects) = @_;
|
||||
my ($ccuhash, $clthash, $objects, $alref) = @_;
|
||||
my $ccuname = $ccuhash->{NAME};
|
||||
my $cltname = $clthash->{NAME};
|
||||
my $clttype = $clthash->{TYPE};
|
||||
@ -3060,143 +3159,140 @@ sub HMCCU_UpdateSingleDevice ($$$)
|
||||
return 0 if (!defined ($clthash->{IODev}) || !defined ($clthash->{ccuaddr}));
|
||||
return 0 if ($clthash->{IODev} != $ccuhash);
|
||||
|
||||
# Check for updated data
|
||||
# Build list of relevant addresses in object data hash
|
||||
my ($devaddr, $cnum) = HMCCU_SplitChnAddr ($clthash->{ccuaddr});
|
||||
return 0 if (!exists ($objects->{$devaddr}));
|
||||
return 0 if ($clttype eq 'HMCUCCHN' && !exists ($objects->{$devaddr}{$cnum}) &&
|
||||
!exists ($objects->{$devaddr}{0}));
|
||||
my @addlist = defined ($alref) ? @$alref : ($devaddr);
|
||||
|
||||
# Check if update of device allowed
|
||||
my $disable = AttrVal ($cltname, 'disable', 0);
|
||||
my $update = AttrVal ($cltname, 'ccureadings', 1);
|
||||
next if ($update == 0 || $disable == 1 || $clthash->{ccudevstate} ne 'active');
|
||||
|
||||
# Get device parameters and attributes
|
||||
my $ccuflags = HMCCU_GetFlags ($ccuname);
|
||||
my $cf = HMCCU_GetFlags ($cltname);
|
||||
my $peer = AttrVal ($cltname, 'peer', 'null');
|
||||
my $crf = HMCCU_GetAttrReadingFormat ($clthash, $ccuhash);
|
||||
my $substitute = HMCCU_GetAttrSubstitute ($clthash, $ccuhash);
|
||||
my ($sc, $st, $cc, $cd) = HMCCU_GetSpecialDatapoints ($clthash, '', 'STATE', '', '');
|
||||
|
||||
# Build device list including virtual devices
|
||||
my @grplist = ($cltname);
|
||||
my @virlist = HMCCU_FindClientDevices ($ccuhash, "HMCCUDEV", undef, "ccuif=(VirtualDevices|fhem)");
|
||||
foreach my $vd (@virlist) {
|
||||
my $vh = $defs{$vd};
|
||||
next if (!defined ($vh->{ccugroup}));
|
||||
foreach my $gadd (split (",", $vh->{ccugroup})) {
|
||||
if ("$gadd" eq "$devaddr") {
|
||||
push @grplist, $vd;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
# Virtual device flag
|
||||
my $vg = 0;
|
||||
$vg = 1 if (($clthash->{ccuif} eq 'VirtualDevices' || $clthash->{ccuif} eq 'fhem') &&
|
||||
exists ($clthash->{ccugroup}));
|
||||
|
||||
HMCCU_Trace ($clthash, 2, $fnc,
|
||||
"$cltname Virlist = ".join(',', @virlist)."<br>".
|
||||
"$cltname Grplist = ".join(',', @grplist)."<br>".
|
||||
"$cltname Objects = ".join(',', keys %{$objects}));
|
||||
|
||||
HMCCU_Trace ($clthash, 2, $fnc, "$cltname Objects = ".join(',', @addlist));
|
||||
|
||||
# Store the resulting readings
|
||||
my %results;
|
||||
|
||||
# Update device considering foreign device data assigned to group device.
|
||||
# Devices are not updated when:
|
||||
# - Device is disabled
|
||||
# - Attribute ccureadings is set to 0
|
||||
# - Device is not in state active
|
||||
foreach my $cn (@grplist) {
|
||||
my $ch = $defs{$cn};
|
||||
my $ct = $ch->{TYPE};
|
||||
my $disable = AttrVal ($cn, 'disable', 0);
|
||||
my $update = AttrVal ($cn, 'ccureadings', 1);
|
||||
next if ($update == 0 || $disable == 1 || $ch->{ccudevstate} ne 'active');
|
||||
my $cf = HMCCU_GetFlags ($cn);
|
||||
my $peer = AttrVal ($cn, 'peer', 'null');
|
||||
# Updated internal values
|
||||
my @chkeys = ();
|
||||
|
||||
HMCCU_Trace ($ch, 2, $fnc, "Processing device $cn");
|
||||
# Update readings of client device with data from address list
|
||||
readingsBeginUpdate ($clthash);
|
||||
|
||||
my $crf = HMCCU_GetAttrReadingFormat ($ch, $ccuhash);
|
||||
my $substitute = HMCCU_GetAttrSubstitute ($ch, $ccuhash);
|
||||
my ($sc, $st, $cc, $cd) = HMCCU_GetSpecialDatapoints ($ch, '', 'STATE', '', '');
|
||||
foreach my $addr (@addlist) {
|
||||
next if (!exists ($objects->{$addr}));
|
||||
|
||||
my @devlist = ($ch->{ccuaddr});
|
||||
push @devlist, split (",", $ch->{ccugroup})
|
||||
if (($ch->{ccuif} eq 'VirtualDevices' || $ch->{ccuif} eq 'fhem') && exists ($ch->{ccugroup}));
|
||||
|
||||
readingsBeginUpdate ($ch);
|
||||
|
||||
# Update devices
|
||||
foreach my $dev (@devlist) {
|
||||
my ($da, $cnum) = HMCCU_SplitChnAddr ($dev);
|
||||
next if (!exists ($objects->{$da}));
|
||||
next if ($clttype eq 'HMCUCCHN' && !exists ($objects->{$da}{$cnum}) &&
|
||||
!exists ($objects->{$da}{0}));
|
||||
HMCCU_Trace ($clthash, 2, $fnc, "Processing object $addr");
|
||||
|
||||
# Update channels of device
|
||||
foreach my $chnnum (keys (%{$objects->{$da}})) {
|
||||
next if ($ct eq 'HMCCUCHN' && "$chnnum" ne "$cnum" && "$chnnum" ne "0");
|
||||
foreach my $chnnum (keys (%{$objects->{$addr}})) {
|
||||
next if ($clttype eq 'HMCCUCHN' && "$chnnum" ne "$cnum" && "$chnnum" ne "0");
|
||||
next if ("$chnnum" eq "0" && $cf =~ /nochn0/);
|
||||
my $chnadd = "$da:$chnnum";
|
||||
my $chnadd = "$addr:$chnnum";
|
||||
|
||||
# Update datapoints of channel
|
||||
foreach my $dpt (keys (%{$objects->{$da}{$chnnum}})) {
|
||||
my $value = $objects->{$da}{$chnnum}{$dpt};
|
||||
foreach my $dpt (keys (%{$objects->{$addr}{$chnnum}})) {
|
||||
my $value = $objects->{$addr}{$chnnum}{$dpt};
|
||||
next if (!defined ($value));
|
||||
|
||||
# Key for storing values in client hash. Indirect updates of virtual devices
|
||||
# are stored with device address in key.
|
||||
my $chkey = $devaddr ne $addr ? "$chnadd.$dpt" : "$chnnum.$dpt";
|
||||
|
||||
# Store datapoint raw value in device hash
|
||||
if (exists ($clthash->{hmccu}{dp}{"$chnnum.$dpt"}{VAL})) {
|
||||
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{OVAL} = $clthash->{hmccu}{dp}{"$chnnum.$dpt"}{VAL};
|
||||
}
|
||||
else {
|
||||
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{OVAL} = $value;
|
||||
}
|
||||
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{VAL} = $value;
|
||||
HMCCU_UpdateInternalValues ($clthash, $chkey, 'VAL', $value);
|
||||
|
||||
HMCCU_Trace ($ch, 2, $fnc, "dev=$cn, chnadd=$chnadd, dpt=$dpt, value=$value");
|
||||
HMCCU_Trace ($clthash, 2, $fnc, "dev=$cltname, chnadd/object=$chnadd, dpt=$dpt, key=$chkey, value=$value");
|
||||
|
||||
if (HMCCU_FilterReading ($ch, $chnadd, $dpt)) {
|
||||
my @readings = HMCCU_GetReadingName ($ch, '', $da, $chnnum, $dpt, '', $crf);
|
||||
my $svalue = HMCCU_ScaleValue ($ch, $chnnum, $dpt, $value, 0);
|
||||
my $fvalue = HMCCU_FormatReadingValue ($ch, $svalue, $dpt);
|
||||
if (HMCCU_FilterReading ($clthash, $chnadd, $dpt)) {
|
||||
# Modify reading name and value
|
||||
my @readings = HMCCU_GetReadingName ($clthash, '', $addr, $chnnum, $dpt, '', $crf);
|
||||
my $svalue = HMCCU_ScaleValue ($clthash, $chnnum, $dpt, $value, 0);
|
||||
my $fvalue = HMCCU_FormatReadingValue ($clthash, $svalue, $dpt);
|
||||
my $cvalue = HMCCU_Substitute ($fvalue, $substitute, 0, $chnnum, $dpt);
|
||||
my %calcs = HMCCU_CalculateReading ($ch, $chnnum, $dpt);
|
||||
# my %calcs = HMCCU_CalculateReading ($clthash, $chkey);
|
||||
|
||||
# Store the resulting value after scaling, formatting and substitution
|
||||
if (exists ($clthash->{hmccu}{dp}{"$chnnum.$dpt"}{OSVAL})) {
|
||||
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{OSVAL} = $clthash->{hmccu}{dp}{"$chnnum.$dpt"}{SVAL};
|
||||
}
|
||||
else {
|
||||
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{OSVAL} = $cvalue;
|
||||
}
|
||||
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{SVAL} = $cvalue;
|
||||
$results{$da}{$chnnum}{$dpt} = $cvalue;
|
||||
HMCCU_UpdateInternalValues ($clthash, $chkey, 'SVAL', $cvalue);
|
||||
push @chkeys, $chkey;
|
||||
|
||||
HMCCU_Trace ($ch, 2, $fnc,
|
||||
# Store result, but not for indirect updates of virtual devices
|
||||
$results{$devaddr}{$chnnum}{$dpt} = $cvalue if ($devaddr eq $addr);
|
||||
|
||||
HMCCU_Trace ($clthash, 2, $fnc,
|
||||
"device=$cltname, readings=".join(',', @readings).
|
||||
", orgvalue=$value value=$cvalue peer=$peer");
|
||||
|
||||
# Update readings
|
||||
foreach my $rn (@readings) {
|
||||
HMCCU_BulkUpdate ($ch, $rn, $fvalue, $cvalue) if ($rn ne '');
|
||||
HMCCU_BulkUpdate ($clthash, $rn, $fvalue, $cvalue) if ($rn ne '');
|
||||
}
|
||||
foreach my $clcr (keys %calcs) {
|
||||
HMCCU_BulkUpdate ($ch, $clcr, $calcs{$clcr}, $calcs{$clcr});
|
||||
}
|
||||
HMCCU_BulkUpdate ($ch, 'control', $fvalue, $cvalue)
|
||||
# foreach my $clcr (keys %calcs) {
|
||||
# HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr});
|
||||
# }
|
||||
HMCCU_BulkUpdate ($clthash, 'control', $fvalue, $cvalue)
|
||||
if ($cd ne '' && $dpt eq $cd && $chnnum eq $cc);
|
||||
HMCCU_BulkUpdate ($ch, 'state', $fvalue, $cvalue)
|
||||
HMCCU_BulkUpdate ($clthash, 'state', $fvalue, $cvalue)
|
||||
if ($dpt eq $st && ($sc eq '' || $sc eq $chnnum));
|
||||
|
||||
# Update peers
|
||||
HMCCU_UpdatePeers ($ch, "$chnnum.$dpt", $cvalue, $peer) if ($peer ne 'null');
|
||||
HMCCU_UpdatePeers ($clthash, "$chnnum.$dpt", $cvalue, $peer) if (!$vg && $peer ne 'null');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar (@chkeys) > 0) {
|
||||
my %calcs = HMCCU_CalculateReading ($clthash, \@chkeys);
|
||||
foreach my $clcr (keys %calcs) {
|
||||
HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr});
|
||||
}
|
||||
}
|
||||
|
||||
# Calculate and update HomeMatic state
|
||||
if ($ccuflags !~ /nohmstate/) {
|
||||
my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cn, $ccuname, undef);
|
||||
HMCCU_BulkUpdate ($ch, $hms_read, $hms_val, $hms_val) if (defined ($hms_val));
|
||||
my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cltname, $ccuname, undef);
|
||||
HMCCU_BulkUpdate ($clthash, $hms_read, $hms_val, $hms_val) if (defined ($hms_val));
|
||||
}
|
||||
|
||||
readingsEndUpdate ($ch, 1);
|
||||
}
|
||||
readingsEndUpdate ($clthash, 1);
|
||||
|
||||
return \%results;
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Store datapoint values in device hash.
|
||||
# Parameter type is VAL or SVAL.
|
||||
######################################################################
|
||||
|
||||
sub HMCCU_UpdateInternalValues ($$$$)
|
||||
{
|
||||
my ($ch, $chkey, $type, $value) = @_;
|
||||
my $otype = "O".$type;
|
||||
|
||||
if (exists ($ch->{hmccu}{dp}{$chkey}{$type})) {
|
||||
$ch->{hmccu}{dp}{$chkey}{$otype} = $ch->{hmccu}{dp}{$chkey}{$type};
|
||||
}
|
||||
else {
|
||||
$ch->{hmccu}{dp}{$chkey}{$otype} = $value;
|
||||
}
|
||||
$ch->{hmccu}{dp}{$chkey}{$type} = $value;
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Update readings of multiple client devices.
|
||||
# Parameter objects is a hash reference:
|
||||
@ -3221,13 +3317,43 @@ sub HMCCU_UpdateMultipleDevices ($$)
|
||||
"ccudevstate=active");
|
||||
foreach my $d (@devlist) {
|
||||
my $ch = $defs{$d};
|
||||
my $rc = HMCCU_UpdateSingleDevice ($hash, $ch, $objects);
|
||||
my @addrlist = HMCCU_GetAffectedAddresses ($ch);
|
||||
next if (scalar (@addrlist) == 0);
|
||||
foreach my $addr (@addrlist) {
|
||||
if (exists ($objects->{$addr})) {
|
||||
my $rc = HMCCU_UpdateSingleDevice ($hash, $ch, $objects, \@addrlist);
|
||||
$c++ if (ref ($rc));
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Get list of device addresses including group device members.
|
||||
######################################################################
|
||||
|
||||
sub HMCCU_GetAffectedAddresses ($)
|
||||
{
|
||||
my ($clthash) = @_;
|
||||
my @addlist = ();
|
||||
|
||||
if ($clthash->{TYPE} eq 'HMCCUDEV' || $clthash->{TYPE} eq 'HMCCUCHN') {
|
||||
if (exists ($clthash->{ccuaddr})) {
|
||||
my ($devaddr, $cnum) = HMCCU_SplitChnAddr ($clthash->{ccuaddr});
|
||||
push @addlist, $devaddr;
|
||||
}
|
||||
if (($clthash->{ccuif} eq 'VirtualDevices' || $clthash->{ccuif} eq 'fhem') &&
|
||||
exists ($clthash->{ccugroup})) {
|
||||
push @addlist, split (',', $clthash->{ccugroup});
|
||||
}
|
||||
}
|
||||
|
||||
return @addlist;
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Update peer devices.
|
||||
# Syntax of peer definitions is:
|
||||
@ -5956,14 +6082,23 @@ sub HMCCU_SetDatapoint ($$$)
|
||||
}
|
||||
|
||||
if ($flags == $HMCCU_FLAGS_IACD) {
|
||||
$cmd = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$value.')';
|
||||
# $cmd = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$value.')';
|
||||
$nam = HMCCU_GetChannelName ($hmccu_hash, $add.":".$chn, '');
|
||||
}
|
||||
elsif ($flags == $HMCCU_FLAGS_NCD) {
|
||||
$cmd = '(dom.GetObject(ID_CHANNELS)).Get("'.$nam.'").DPByHssDP("'.$dpt.'").State('.$value.')';
|
||||
# $cmd = '(dom.GetObject(ID_CHANNELS)).Get("'.$nam.'").DPByHssDP("'.$dpt.'").State('.$value.')';
|
||||
($add, $chn) = HMCCU_GetAddress ($hmccu_hash, $nam, '', '');
|
||||
}
|
||||
|
||||
if ($type eq 'HMCCUDEV' && $hash->{ccuif} eq 'fhem' && $hash->{ccutype} ne 'n/a' && exists ($hash->{ccugroup})) {
|
||||
for my $gaddr (split (',', $hash->{ccugroup})) {
|
||||
$cmd .= '(datapoints.Get("'.$int.'.'.$gaddr.':'.$chn.'.'.$dpt.'")).State('.$value.");\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$cmd = '(datapoints.Get("'.$int.'.'.$add.':'.$chn.'.'.$dpt.'")).State('.$value.')';
|
||||
}
|
||||
|
||||
my $addr = $add.":".$chn;
|
||||
|
||||
if ($ccuflags =~ /nonBlocking/) {
|
||||
@ -6239,22 +6374,22 @@ sub HMCCU_GetUpdate ($$$)
|
||||
$events{$add}{$chn}{$dpt} = $value;
|
||||
}
|
||||
|
||||
if ($cl_hash->{ccuif} eq 'fhem') {
|
||||
# Calculate datapoints of virtual group device
|
||||
if ($cl_hash->{ccutype} ne 'n/a') {
|
||||
foreach my $da (split (",", $cl_hash->{ccugroup})) {
|
||||
foreach my $cn (keys %{$events{$da}}) {
|
||||
foreach my $dp (keys %{$events{$da}{$cn}}) {
|
||||
if (defined ($events{$da}{$cn}{$dp})) {
|
||||
$events{$cl_hash->{ccuaddr}}{$cn}{$dp} = $events{$da}{$cn}{$dp}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# if ($cl_hash->{ccuif} eq 'fhem') {
|
||||
# # Calculate datapoints of virtual group device
|
||||
# if ($cl_hash->{ccutype} ne 'n/a') {
|
||||
# foreach my $da (split (",", $cl_hash->{ccugroup})) {
|
||||
# foreach my $cn (keys %{$events{$da}}) {
|
||||
# foreach my $dp (keys %{$events{$da}{$cn}}) {
|
||||
# if (defined ($events{$da}{$cn}{$dp})) {
|
||||
# $events{$cl_hash->{ccuaddr}}{$cn}{$dp} = $events{$da}{$cn}{$dp}
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
HMCCU_UpdateSingleDevice ($hmccu_hash, $cl_hash, \%events);
|
||||
HMCCU_UpdateMultipleDevices ($hmccu_hash, \%events);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -6747,10 +6882,11 @@ sub HMCCU_GetTimeSpec ($)
|
||||
# Return readings array with reading/value pairs.
|
||||
######################################################################
|
||||
|
||||
sub HMCCU_CalculateReading ($$$)
|
||||
sub HMCCU_CalculateReading ($$)
|
||||
{
|
||||
my ($cl_hash, $chnno, $dpt) = @_;
|
||||
my ($cl_hash, $chkeys) = @_;
|
||||
my $name = $cl_hash->{NAME};
|
||||
my $fnc = "HMCCU_CalculateReading";
|
||||
|
||||
my @result = ();
|
||||
|
||||
@ -6759,22 +6895,39 @@ sub HMCCU_CalculateReading ($$$)
|
||||
|
||||
my @calclist = split (/[;\n]+/, $ccucalculate);
|
||||
foreach my $calculation (@calclist) {
|
||||
my ($vt, $rn, $dpts) = split (':', $calculation);
|
||||
next if (!defined ($rn));
|
||||
my ($vt, $rn, $dpts) = split (':', $calculation, 3);
|
||||
next if (!defined ($dpts));
|
||||
my $tmpdpts = ",$dpts,";
|
||||
$tmpdpts =~ s/[\$\%\{\}]+//g;
|
||||
HMCCU_Trace ($cl_hash, 2, $fnc, "vt=$vt, rn=$rn, dpts=$dpts, tmpdpts=$tmpdpts");
|
||||
my $f = 0;
|
||||
foreach my $chkey (@$chkeys) {
|
||||
if ($tmpdpts =~ /,$chkey,/) {
|
||||
$f = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
next if ($f == 0);
|
||||
my @dplist = split (',', $dpts);
|
||||
|
||||
# Get parameters values stored in device hash
|
||||
my @dplist = defined ($dpts) ? split (',', $dpts) : ();
|
||||
next if (@dplist > 0 && !(grep { $_ eq "$chnno.$dpt"} @dplist));
|
||||
my @pars = ();
|
||||
foreach my $dp (@dplist) {
|
||||
if (exists ($cl_hash->{hmccu}{dp}{$dp}{VAL})) {
|
||||
push @pars, $cl_hash->{hmccu}{dp}{$dp}{VAL};
|
||||
my $newdpts = HMCCU_SubstVariables ($cl_hash, $dpts, undef);
|
||||
my @pars = split (',', $newdpts);
|
||||
my $pc = scalar (@pars);
|
||||
next if ($pc != scalar(@dplist));
|
||||
$f = 0;
|
||||
for (my $i=0; $i<$pc; $i++) {
|
||||
$pars[$i] =~ s/^#//;
|
||||
if ($pars[$i] eq $dplist[$i]) {
|
||||
$f = 1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
next if ($f);
|
||||
|
||||
if ($vt eq 'dewpoint' || $vt eq 'abshumidity') {
|
||||
# Dewpoint and absolute humidity
|
||||
next if (scalar (@pars) < 2);
|
||||
next if ($pc < 2);
|
||||
my ($tmp, $hum) = @pars;
|
||||
if ($tmp >= 0.0) {
|
||||
$a = 7.5;
|
||||
@ -6799,18 +6952,29 @@ sub HMCCU_CalculateReading ($$$)
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($vt eq 'equ') {
|
||||
# Set reading to value if all variables have the same value
|
||||
next if ($pc < 1);
|
||||
my $curval = shift @pars;
|
||||
my $f = 1;
|
||||
foreach my $newval (@pars) {
|
||||
$f = 0 if ("$newval" ne "$curval");
|
||||
}
|
||||
push (@result, $rn, $f ? $curval : "n/a");
|
||||
}
|
||||
elsif ($vt eq 'min' || $vt eq 'max') {
|
||||
# Minimum or maximum values
|
||||
next if (scalar (@pars) < 1);
|
||||
my $newval = shift @pars;
|
||||
my $curval = ReadingsVal ($name, $rn, 0);
|
||||
next if ($pc < 1);
|
||||
my $curval = $pc > 1 ? shift @pars : ReadingsVal ($name, $rn, 0);
|
||||
foreach my $newval (@pars) {
|
||||
$curval = $newval if ($vt eq 'min' && $newval < $curval);
|
||||
$curval = $newval if ($vt eq 'max' && $newval > $curval);
|
||||
}
|
||||
push (@result, $rn, $curval);
|
||||
}
|
||||
elsif ($vt eq 'inc' || $vt eq 'dec') {
|
||||
# Increasing or decreasing values without reset
|
||||
next if (scalar (@pars) < 1);
|
||||
next if ($pc < 1);
|
||||
my $newval = shift @pars;
|
||||
my $oldval = ReadingsVal ($name, $rn."_old", 0);
|
||||
my $curval = ReadingsVal ($name, $rn, 0);
|
||||
@ -6823,7 +6987,7 @@ sub HMCCU_CalculateReading ($$$)
|
||||
}
|
||||
elsif ($vt eq 'avg') {
|
||||
# Average value
|
||||
next if (scalar (@pars) < 1);
|
||||
next if ($pc < 1);
|
||||
my $newval = shift @pars;
|
||||
my $cnt = ReadingsVal ($name, $rn."_cnt", 0);
|
||||
my $sum = ReadingsVal ($name, $rn."_sum", 0);
|
||||
@ -6834,10 +6998,29 @@ sub HMCCU_CalculateReading ($$$)
|
||||
}
|
||||
elsif ($vt eq 'sum') {
|
||||
# Sum of values
|
||||
next if (scalar (@pars) < 1);
|
||||
my $newval = shift @pars;
|
||||
my $curval = ReadingsVal ($name, $rn, 0);
|
||||
next if ($pc < 1);
|
||||
my $curval = $pc > 1 ? 0 : ReadingsVal ($name, $rn, 0);
|
||||
foreach my $newval (@pars) {
|
||||
$curval += $newval;
|
||||
}
|
||||
push (@result, $rn, $curval);
|
||||
}
|
||||
elsif ($vt eq 'or') {
|
||||
# Logical OR
|
||||
next if ($pc < 1);
|
||||
my $curval = $pc > 1 ? 0 : ReadingsVal ($name, $rn, 0);
|
||||
foreach my $newval (@pars) {
|
||||
$curval |= $newval;
|
||||
}
|
||||
push (@result, $rn, $curval);
|
||||
}
|
||||
elsif ($vt eq 'and') {
|
||||
# Logical AND
|
||||
next if ($pc < 1);
|
||||
my $curval = $pc > 1 ? 1 : ReadingsVal ($name, $rn, 1);
|
||||
foreach my $newval (@pars) {
|
||||
$curval &= $newval;
|
||||
}
|
||||
push (@result, $rn, $curval);
|
||||
}
|
||||
}
|
||||
@ -7620,8 +7803,9 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
iface_conn_n = interface connection state (1=connected, 0=disconnected)<br/>
|
||||
iface_ducy_n = duty cycle of interface (0-100)
|
||||
</li><br/>
|
||||
<li><b>get <name> exportdefaults <filename> [{<u>default</u>|csv}]</b><br/>
|
||||
Export default attributes into file.
|
||||
<li><b>get <name> exportdefaults <filename> [csv] [all]</b><br/>
|
||||
Export default attributes into file. If option <i>all</i> is specified, also defaults imported
|
||||
by customer will be exported.
|
||||
</li><br/>
|
||||
<li><b>get <name> firmware [{<type-expr> | full}]</b><br/>
|
||||
Get available firmware downloads from eq-3.de. List FHEM devices with current and available
|
||||
@ -7743,6 +7927,7 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
intrpc - Use internal RPC server. This is the default.<br/>
|
||||
extrpc - Same as procrpc (see below)<br/>
|
||||
logEvents - Write events from CCU into FHEM logfile<br/>
|
||||
noEvents - Ignore events / device updates sent by CCU. No readings will be updated!<br/>
|
||||
nonBlocking - Use non blocking (asynchronous) CCU requests<br/>
|
||||
noReadings - Do not create or update readings<br/>
|
||||
procrpc - Use external RPC server provided by module HMCCPRPCPROC. During first RPC
|
||||
@ -7763,8 +7948,9 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
<li><b>ccuReqTimeout <Seconds></b><br/>
|
||||
Set timeout for CCU request. Default is 4 seconds. This timeout affects several
|
||||
set and get commands, i.e. "set datapoint" or "set var". If a command runs into
|
||||
a timeout FHEM will block for <i>Seconds</i>.
|
||||
</li>
|
||||
a timeout FHEM will block for <i>Seconds</i>. To prevent blocking set flag 'nonBlocking'
|
||||
in attribute <i>ccuflags</i>.
|
||||
</li><br/>
|
||||
<li><b>ccureadings {0 | <u>1</u>}</b><br/>
|
||||
Deprecated. Readings are written by default. To deactivate readings set flag noReadings
|
||||
in attribute ccuflags.
|
||||
@ -7786,11 +7972,12 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
|
||||
</ul>
|
||||
</li><br/>
|
||||
<li><b>rpcinterval <Seconds></b><br/>
|
||||
Specifiy how often RPC queue is read. Default is 5 seconds.
|
||||
Specifiy how often RPC queue is read. Default is 5 seconds. Only relevant if internal
|
||||
RPC server is used (deprecated).
|
||||
</li><br/>
|
||||
<li><b>rpcport <value[,...]></b><br/>
|
||||
Deprecated, use 'rpcinterfaces' instead. Specify list of RPC ports on CCU. Default is
|
||||
2001. Valid RPC ports are:<br/><br/>
|
||||
Deprecated, use attribute 'rpcinterfaces' instead. Specify list of RPC ports on CCU.
|
||||
Default is 2001. Valid RPC ports are:<br/><br/>
|
||||
<ul>
|
||||
<li>2000 = Wired components</li>
|
||||
<li>2001 = BidCos-RF (wireless 868 MHz components with BidCos protocol)</li>
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Version 4.3.003
|
||||
# Version 4.3.004
|
||||
#
|
||||
# (c) 2018 zap (zap01 <at> t-online <dot> de)
|
||||
#
|
||||
@ -67,6 +67,7 @@ use SetExtensions;
|
||||
|
||||
sub HMCCUDEV_Initialize ($);
|
||||
sub HMCCUDEV_Define ($@);
|
||||
sub HMCCUDEV_Delete ($$);
|
||||
sub HMCCUDEV_InitDevice ($$);
|
||||
sub HMCCUDEV_Set ($@);
|
||||
sub HMCCUDEV_Get ($@);
|
||||
@ -81,18 +82,20 @@ 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 ccucalculate:textField-long ".
|
||||
$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 ccuverify:0,1,2 disable:0,1 ".
|
||||
"hmstatevals:textField-long statevals substexcl substitute:textField-long statechannel ".
|
||||
"statedatapoint controldatapoint stripnumber peer:textField-long ".$readingFnAttributes;
|
||||
"statedatapoint controldatapoint stripnumber peer:textField-long ".
|
||||
$readingFnAttributes;
|
||||
}
|
||||
|
||||
#####################################
|
||||
@ -105,7 +108,7 @@ sub HMCCUDEV_Define ($@)
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $usage = "Usage: define $name HMCCUDEV {device|'virtual'} [state-channel] ".
|
||||
"['readonly'] ['defaults'] [iodev={iodev-name}] ".
|
||||
"['readonly'] ['defaults'] [iodev={iodev-name}] [address={virtual-device-no}]".
|
||||
"[{groupexp=regexp|group={device|channel}[,...]]";
|
||||
return $usage if (scalar (@$a) < 3);
|
||||
|
||||
@ -116,7 +119,8 @@ sub HMCCUDEV_Define ($@)
|
||||
"No devices in group",
|
||||
"No matching CCU devices found",
|
||||
"Type of virtual device not defined",
|
||||
"Device type not found"
|
||||
"Device type not found",
|
||||
"Too many virtual devices"
|
||||
);
|
||||
|
||||
my $devname = shift @$a;
|
||||
@ -130,6 +134,18 @@ sub HMCCUDEV_Define ($@)
|
||||
$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';
|
||||
|
||||
@ -151,7 +167,7 @@ sub HMCCUDEV_Define ($@)
|
||||
$hmccu_hash = $defs{$h->{iodev}};
|
||||
}
|
||||
else {
|
||||
# The following call will fail during FHEM start if CCU is not ready
|
||||
# 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);
|
||||
}
|
||||
|
||||
@ -194,6 +210,7 @@ sub HMCCUDEV_Define ($@)
|
||||
# 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 ($$)
|
||||
@ -205,18 +222,26 @@ sub HMCCUDEV_InitDevice ($$)
|
||||
my $gdname = $devspec;
|
||||
|
||||
if ($devspec eq 'virtual') {
|
||||
# Virtual device FHEM only, search for free address
|
||||
my $no = 0;
|
||||
foreach my $d (sort keys %defs) {
|
||||
my $ch = $defs{$d};
|
||||
next if (!exists ($ch->{TYPE}));
|
||||
next if ($ch->{TYPE} ne 'HMCCUDEV' || $d eq $name);
|
||||
next if ($ch->{ccuif} ne 'fhem' || $ch->{ccuname} ne 'virtual');
|
||||
$no++;
|
||||
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+1);
|
||||
$dev_hash->{ccuname} = 'virtual';
|
||||
$dev_hash->{ccuaddr} = sprintf ("VIR%07d", $no);
|
||||
$dev_hash->{ccuname} = $name;
|
||||
}
|
||||
else {
|
||||
return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $devspec, 7));
|
||||
@ -303,6 +328,21 @@ sub HMCCUDEV_InitDevice ($$)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Delete device
|
||||
#####################################
|
||||
|
||||
sub HMCCUDEV_Delete ($$)
|
||||
{
|
||||
my ($hash, $name) = @_;
|
||||
|
||||
if ($hash->{ccuif} eq 'fhem') {
|
||||
HMCCU_DeleteDevice ($hash);
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
# Set attribute
|
||||
#####################################
|
||||
@ -677,7 +717,8 @@ sub HMCCUDEV_Get ($@)
|
||||
my $result = '';
|
||||
my $rc;
|
||||
|
||||
if ($ccuif eq "VirtualDevices" && $hash->{ccuname} eq "virtual" && $opt ne 'update') {
|
||||
# Virtual devices only support command get update
|
||||
if ($ccuif eq 'fhem' && $opt ne 'update') {
|
||||
return "HMCCUDEV: Unknown argument $opt, choose one of update:noArg";
|
||||
}
|
||||
|
||||
@ -723,19 +764,18 @@ sub HMCCUDEV_Get ($@)
|
||||
return HMCCU_SetError ($hash, "Usage: get $name update [{'State'|'Value'}]");
|
||||
}
|
||||
|
||||
if ($hash->{ccuname} ne 'virtual') {
|
||||
if ($hash->{ccuif} ne 'fhem') {
|
||||
$rc = HMCCU_GetUpdate ($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);
|
||||
# }
|
||||
# }
|
||||
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;
|
||||
}
|
||||
@ -856,8 +896,8 @@ sub HMCCUDEV_Get ($@)
|
||||
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 or channels without defining a real
|
||||
group in CCU by using option 'virtual' instead of a CCU device specification.
|
||||
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>
|
||||
|
Loading…
Reference in New Issue
Block a user