2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 12:49:34 +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:
zap 2018-10-28 11:31:20 +00:00
parent e94ce46065
commit 4f2f7948f3
3 changed files with 450 additions and 222 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # 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. # 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 - bugfix: 89_FULLY: fixed version check bug
- feature: 74_AMADtaskerset: import with share link - feature: 74_AMADtaskerset: import with share link
- change: 42_AptToDate: change to package methode - change: 42_AptToDate: change to package methode

View File

@ -4,9 +4,9 @@
# #
# $Id$ # $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, # Supports BidCos-RF, BidCos-Wired, HmIP-RF, virtual CCU channels,
# CCU group devices, HomeGear, CUxD, Osram Lightify, Homematic Virtual Layer # CCU group devices, HomeGear, CUxD, Osram Lightify, Homematic Virtual Layer
@ -38,7 +38,7 @@
# [defattr] [duplicates] [save] [<attr>=<val> [...]]}] # [defattr] [duplicates] [save] [<attr>=<val> [...]]}]
# get <name> dump {devtypes|datapoints} [<filter>] # get <name> dump {devtypes|datapoints} [<filter>]
# get <name> dutycycle # get <name> dutycycle
# get <name> exportdefaults {filename} [csv] # get <name> exportdefaults {filename} [csv] [all]
# get <name> firmware [{type-expr}|full] # get <name> firmware [{type-expr}|full]
# get <name> parfile [<parfile>] # get <name> parfile [<parfile>]
# get <name> rpcevents # get <name> rpcevents
@ -108,7 +108,7 @@ my %HMCCU_CUST_CHN_DEFAULTS;
my %HMCCU_CUST_DEV_DEFAULTS; my %HMCCU_CUST_DEV_DEFAULTS;
# HMCCU version # HMCCU version
my $HMCCU_VERSION = '4.3.004'; my $HMCCU_VERSION = '4.3.005';
# Default RPC port (BidCos-RF) # Default RPC port (BidCos-RF)
my $HMCCU_RPC_PORT_DEFAULT = 2001; my $HMCCU_RPC_PORT_DEFAULT = 2001;
@ -245,8 +245,8 @@ sub HMCCU_AggregateReadings ($$);
sub HMCCU_AggregationRules ($$); sub HMCCU_AggregationRules ($$);
# Handling of default attributes # Handling of default attributes
sub HMCCU_ExportDefaults ($); sub HMCCU_ExportDefaults ($$);
sub HMCCU_ExportDefaultsCSV ($); sub HMCCU_ExportDefaultsCSV ($$);
sub HMCCU_ImportDefaults ($); sub HMCCU_ImportDefaults ($);
sub HMCCU_FindDefaults ($$); sub HMCCU_FindDefaults ($$);
sub HMCCU_SetDefaults ($); sub HMCCU_SetDefaults ($);
@ -272,10 +272,11 @@ sub HMCCU_SubstVariables ($$$);
sub HMCCU_BulkUpdate ($$$$); sub HMCCU_BulkUpdate ($$$$);
sub HMCCU_GetUpdate ($$$); sub HMCCU_GetUpdate ($$$);
sub HMCCU_UpdateClients ($$$$$); sub HMCCU_UpdateClients ($$$$$);
sub HMCCU_UpdateInternalValues ($$$$);
sub HMCCU_UpdateMultipleDevices ($$); sub HMCCU_UpdateMultipleDevices ($$);
sub HMCCU_UpdatePeers ($$$$); sub HMCCU_UpdatePeers ($$$$);
sub HMCCU_UpdateSingleDatapoint ($$$$); sub HMCCU_UpdateSingleDatapoint ($$$$);
sub HMCCU_UpdateSingleDevice ($$$); sub HMCCU_UpdateSingleDevice ($$$$);
# RPC functions # RPC functions
sub HMCCU_GetRPCCallbackURL ($$$$$); sub HMCCU_GetRPCCallbackURL ($$$$$);
@ -316,8 +317,10 @@ sub HMCCU_IsFlag ($$);
# Handle interfaces, devices and channels # Handle interfaces, devices and channels
sub HMCCU_CreateDevice ($$$$$); sub HMCCU_CreateDevice ($$$$$);
sub HMCCU_DeleteDevice ($);
sub HMCCU_FormatDeviceInfo ($); sub HMCCU_FormatDeviceInfo ($);
sub HMCCU_GetAddress ($$$$); sub HMCCU_GetAddress ($$$$);
sub HMCCU_GetAffectedAddresses ($);
sub HMCCU_GetCCUDeviceParam ($$); sub HMCCU_GetCCUDeviceParam ($$);
sub HMCCU_GetChannelName ($$$); sub HMCCU_GetChannelName ($$$);
sub HMCCU_GetDeviceChannels ($$$); sub HMCCU_GetDeviceChannels ($$$);
@ -369,7 +372,7 @@ sub HMCCU_QueueEnq ($$);
sub HMCCU_QueueDeq ($); sub HMCCU_QueueDeq ($);
# Helper functions # Helper functions
sub HMCCU_CalculateReading ($$$); sub HMCCU_CalculateReading ($$);
sub HMCCU_EncodeEPDisplay ($); sub HMCCU_EncodeEPDisplay ($);
sub HMCCU_ExprMatch ($$$); sub HMCCU_ExprMatch ($$$);
sub HMCCU_ExprNotMatch ($$$); sub HMCCU_ExprNotMatch ($$$);
@ -419,7 +422,7 @@ sub HMCCU_Initialize ($)
" ccudef-hmstatevals:textField-long ccudef-substitute:textField-long". " ccudef-hmstatevals:textField-long ccudef-substitute:textField-long".
" ccudef-readingname:textField-long ccudef-readingfilter:textField-long". " ccudef-readingname:textField-long ccudef-readingfilter:textField-long".
" ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc". " 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". " ccuReqTimeout ccuGetVars rpcinterval:2,3,5,7,10 rpcqueue".
" rpcport:multiple-strict,".join(',',sort keys %HMCCU_RPC_NUMPORT). " rpcport:multiple-strict,".join(',',sort keys %HMCCU_RPC_NUMPORT).
" rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute". " rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute".
@ -831,9 +834,9 @@ sub HMCCU_AggregationRules ($$)
# Export default attributes. # Export default attributes.
###################################################################### ######################################################################
sub HMCCU_ExportDefaults ($) sub HMCCU_ExportDefaults ($$)
{ {
my ($filename) = @_; my ($filename, $all) = @_;
return 0 if (!open (DEFFILE, ">$filename")); 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); close (DEFFILE);
return 1; return 1;
@ -862,9 +883,9 @@ sub HMCCU_ExportDefaults ($)
# Export default attributes as CSV file. # Export default attributes as CSV file.
###################################################################### ######################################################################
sub HMCCU_ExportDefaultsCSV ($) sub HMCCU_ExportDefaultsCSV ($$)
{ {
my ($filename) = @_; my ($filename, $all) = @_;
my %attrlist = ( my %attrlist = (
'_type' => '', '_description' => '', '_channels' => '', '_type' => '', '_description' => '', '_channels' => '',
@ -903,6 +924,30 @@ sub HMCCU_ExportDefaultsCSV ($)
print DEFFILE "\n"; 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); close (DEFFILE);
return 1; return 1;
@ -1961,14 +2006,19 @@ sub HMCCU_Get ($@)
} }
elsif ($opt eq 'exportdefaults') { elsif ($opt eq 'exportdefaults') {
my $filename = shift @$a; 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 (!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_SetError ($hash, -16) if ($rc == 0);
return HMCCU_SetState ($hash, "OK", "Default attributes written to $filename"); return HMCCU_SetState ($hash, "OK", "Default attributes written to $filename");
} }
@ -2671,11 +2721,14 @@ sub HMCCU_SubstRule ($$$)
# %% = Previous original / raw value # %% = Previous original / raw value
# $ = Converted / formatted value # $ = Converted / formatted value
# $$ = Previous 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 ($$$) sub HMCCU_SubstVariables ($$$)
{ {
my ($clhash, $text, $dplist) = @_; my ($clhash, $text, $dplist) = @_;
my $fnc = "HMCCU_SubstVariables";
my @varlist; my @varlist;
if (defined ($dplist)) { if (defined ($dplist)) {
@ -2685,9 +2738,14 @@ sub HMCCU_SubstVariables ($$$)
@varlist = keys %{$clhash->{hmccu}{dp}}; @varlist = keys %{$clhash->{hmccu}{dp}};
} }
HMCCU_Trace ($clhash, 2, $fnc, "text=$text");
# Substitute datapoint variables by value # Substitute datapoint variables by value
foreach my $dp (@varlist) { foreach my $dp (@varlist) {
my ($chn, $dpt) = split (/\./, $dp); my ($chn, $dpt) = split (/\./, $dp);
HMCCU_Trace ($clhash, 2, $fnc, "var=$dp");
if (defined ($clhash->{hmccu}{dp}{$dp}{OSVAL})) { if (defined ($clhash->{hmccu}{dp}{$dp}{OSVAL})) {
$text =~ s/\$\$\{?$dp\}?/$clhash->{hmccu}{dp}{$dp}{OSVAL}/g; $text =~ s/\$\$\{?$dp\}?/$clhash->{hmccu}{dp}{$dp}{OSVAL}/g;
$text =~ s/\$\$\{?$dpt\}?/$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})) { if (defined ($clhash->{hmccu}{dp}{$dp}{VAL})) {
$text =~ s/\%\{?$dp\}?/$clhash->{hmccu}{dp}{$dp}{VAL}/g; $text =~ s/\%\{?$dp\}?/$clhash->{hmccu}{dp}{$dp}{VAL}/g;
$text =~ s/\%\{?$dpt\}?/$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; return $text;
} }
@ -2837,6 +2898,44 @@ sub HMCCU_CreateDevice ($$$$$)
return 0; 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. # Update parameters in internal device tables and client devices.
# Parameter devices is a hash reference with following keys: # Parameter devices is a hash reference with following keys:
@ -3034,14 +3133,14 @@ sub HMCCU_UpdateSingleDatapoint ($$$$)
my ($devaddr, $chnnum) = HMCCU_SplitChnAddr ($ccuaddr); my ($devaddr, $chnnum) = HMCCU_SplitChnAddr ($ccuaddr);
$objects{$devaddr}{$chn}{$dpt} = $value; $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; return (ref ($rc)) ? $rc->{$devaddr}{$chn}{$dpt} : $value;
} }
###################################################################### ######################################################################
# Update readings of client device. # Update readings of client device.
# Parameter objects is a hash reference which contains updated data # Parameter objects is a hash reference which contains updated data
# for any device: # for devices:
# {devaddr}{channelno}{datapoint} = value # {devaddr}{channelno}{datapoint} = value
# If client device is virtual device group: check if group members are # If client device is virtual device group: check if group members are
# affected by updates and update readings in virtual group device. # affected by updates and update readings in virtual group device.
@ -3049,9 +3148,9 @@ sub HMCCU_UpdateSingleDatapoint ($$$$)
# {devaddr}{datapoint} = value # {devaddr}{datapoint} = value
###################################################################### ######################################################################
sub HMCCU_UpdateSingleDevice ($$$) sub HMCCU_UpdateSingleDevice ($$$$)
{ {
my ($ccuhash, $clthash, $objects) = @_; my ($ccuhash, $clthash, $objects, $alref) = @_;
my $ccuname = $ccuhash->{NAME}; my $ccuname = $ccuhash->{NAME};
my $cltname = $clthash->{NAME}; my $cltname = $clthash->{NAME};
my $clttype = $clthash->{TYPE}; my $clttype = $clthash->{TYPE};
@ -3060,143 +3159,140 @@ sub HMCCU_UpdateSingleDevice ($$$)
return 0 if (!defined ($clthash->{IODev}) || !defined ($clthash->{ccuaddr})); return 0 if (!defined ($clthash->{IODev}) || !defined ($clthash->{ccuaddr}));
return 0 if ($clthash->{IODev} != $ccuhash); 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}); my ($devaddr, $cnum) = HMCCU_SplitChnAddr ($clthash->{ccuaddr});
return 0 if (!exists ($objects->{$devaddr})); my @addlist = defined ($alref) ? @$alref : ($devaddr);
return 0 if ($clttype eq 'HMCUCCHN' && !exists ($objects->{$devaddr}{$cnum}) &&
!exists ($objects->{$devaddr}{0}));
# 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 $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 # Virtual device flag
my @grplist = ($cltname); my $vg = 0;
my @virlist = HMCCU_FindClientDevices ($ccuhash, "HMCCUDEV", undef, "ccuif=(VirtualDevices|fhem)"); $vg = 1 if (($clthash->{ccuif} eq 'VirtualDevices' || $clthash->{ccuif} eq 'fhem') &&
foreach my $vd (@virlist) { exists ($clthash->{ccugroup}));
my $vh = $defs{$vd};
next if (!defined ($vh->{ccugroup}));
foreach my $gadd (split (",", $vh->{ccugroup})) {
if ("$gadd" eq "$devaddr") {
push @grplist, $vd;
last;
}
}
}
HMCCU_Trace ($clthash, 2, $fnc,
"$cltname Virlist = ".join(',', @virlist)."<br>". HMCCU_Trace ($clthash, 2, $fnc, "$cltname Objects = ".join(',', @addlist));
"$cltname Grplist = ".join(',', @grplist)."<br>".
"$cltname Objects = ".join(',', keys %{$objects}));
# Store the resulting readings # Store the resulting readings
my %results; my %results;
# Update device considering foreign device data assigned to group device. # Updated internal values
# Devices are not updated when: my @chkeys = ();
# - 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');
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); foreach my $addr (@addlist) {
my $substitute = HMCCU_GetAttrSubstitute ($ch, $ccuhash); next if (!exists ($objects->{$addr}));
my ($sc, $st, $cc, $cd) = HMCCU_GetSpecialDatapoints ($ch, '', 'STATE', '', '');
my @devlist = ($ch->{ccuaddr}); HMCCU_Trace ($clthash, 2, $fnc, "Processing object $addr");
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}));
# Update channels of device # Update channels of device
foreach my $chnnum (keys (%{$objects->{$da}})) { foreach my $chnnum (keys (%{$objects->{$addr}})) {
next if ($ct eq 'HMCCUCHN' && "$chnnum" ne "$cnum" && "$chnnum" ne "0"); next if ($clttype eq 'HMCCUCHN' && "$chnnum" ne "$cnum" && "$chnnum" ne "0");
next if ("$chnnum" eq "0" && $cf =~ /nochn0/); next if ("$chnnum" eq "0" && $cf =~ /nochn0/);
my $chnadd = "$da:$chnnum"; my $chnadd = "$addr:$chnnum";
# Update datapoints of channel # Update datapoints of channel
foreach my $dpt (keys (%{$objects->{$da}{$chnnum}})) { foreach my $dpt (keys (%{$objects->{$addr}{$chnnum}})) {
my $value = $objects->{$da}{$chnnum}{$dpt}; my $value = $objects->{$addr}{$chnnum}{$dpt};
next if (!defined ($value)); 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 # Store datapoint raw value in device hash
if (exists ($clthash->{hmccu}{dp}{"$chnnum.$dpt"}{VAL})) { HMCCU_UpdateInternalValues ($clthash, $chkey, 'VAL', $value);
$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_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)) { if (HMCCU_FilterReading ($clthash, $chnadd, $dpt)) {
my @readings = HMCCU_GetReadingName ($ch, '', $da, $chnnum, $dpt, '', $crf); # Modify reading name and value
my $svalue = HMCCU_ScaleValue ($ch, $chnnum, $dpt, $value, 0); my @readings = HMCCU_GetReadingName ($clthash, '', $addr, $chnnum, $dpt, '', $crf);
my $fvalue = HMCCU_FormatReadingValue ($ch, $svalue, $dpt); 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 $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 # Store the resulting value after scaling, formatting and substitution
if (exists ($clthash->{hmccu}{dp}{"$chnnum.$dpt"}{OSVAL})) { HMCCU_UpdateInternalValues ($clthash, $chkey, 'SVAL', $cvalue);
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{OSVAL} = $clthash->{hmccu}{dp}{"$chnnum.$dpt"}{SVAL}; push @chkeys, $chkey;
}
else {
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{OSVAL} = $cvalue;
}
$clthash->{hmccu}{dp}{"$chnnum.$dpt"}{SVAL} = $cvalue;
$results{$da}{$chnnum}{$dpt} = $cvalue;
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). "device=$cltname, readings=".join(',', @readings).
", orgvalue=$value value=$cvalue peer=$peer"); ", orgvalue=$value value=$cvalue peer=$peer");
# Update readings # Update readings
foreach my $rn (@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) { # foreach my $clcr (keys %calcs) {
HMCCU_BulkUpdate ($ch, $clcr, $calcs{$clcr}, $calcs{$clcr}); # HMCCU_BulkUpdate ($clthash, $clcr, $calcs{$clcr}, $calcs{$clcr});
} # }
HMCCU_BulkUpdate ($ch, 'control', $fvalue, $cvalue) HMCCU_BulkUpdate ($clthash, 'control', $fvalue, $cvalue)
if ($cd ne '' && $dpt eq $cd && $chnnum eq $cc); 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)); if ($dpt eq $st && ($sc eq '' || $sc eq $chnnum));
# Update peers # 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 # Calculate and update HomeMatic state
if ($ccuflags !~ /nohmstate/) { if ($ccuflags !~ /nohmstate/) {
my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cn, $ccuname, undef); my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cltname, $ccuname, undef);
HMCCU_BulkUpdate ($ch, $hms_read, $hms_val, $hms_val) if (defined ($hms_val)); HMCCU_BulkUpdate ($clthash, $hms_read, $hms_val, $hms_val) if (defined ($hms_val));
} }
readingsEndUpdate ($ch, 1); readingsEndUpdate ($clthash, 1);
}
return \%results; 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. # Update readings of multiple client devices.
# Parameter objects is a hash reference: # Parameter objects is a hash reference:
@ -3221,13 +3317,43 @@ sub HMCCU_UpdateMultipleDevices ($$)
"ccudevstate=active"); "ccudevstate=active");
foreach my $d (@devlist) { foreach my $d (@devlist) {
my $ch = $defs{$d}; 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)); $c++ if (ref ($rc));
last;
}
}
} }
return $c; 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. # Update peer devices.
# Syntax of peer definitions is: # Syntax of peer definitions is:
@ -5956,14 +6082,23 @@ sub HMCCU_SetDatapoint ($$$)
} }
if ($flags == $HMCCU_FLAGS_IACD) { 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, ''); $nam = HMCCU_GetChannelName ($hmccu_hash, $add.":".$chn, '');
} }
elsif ($flags == $HMCCU_FLAGS_NCD) { 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, '', ''); ($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; my $addr = $add.":".$chn;
if ($ccuflags =~ /nonBlocking/) { if ($ccuflags =~ /nonBlocking/) {
@ -6239,22 +6374,22 @@ sub HMCCU_GetUpdate ($$$)
$events{$add}{$chn}{$dpt} = $value; $events{$add}{$chn}{$dpt} = $value;
} }
if ($cl_hash->{ccuif} eq 'fhem') { # if ($cl_hash->{ccuif} eq 'fhem') {
# Calculate datapoints of virtual group device # # Calculate datapoints of virtual group device
if ($cl_hash->{ccutype} ne 'n/a') { # if ($cl_hash->{ccutype} ne 'n/a') {
foreach my $da (split (",", $cl_hash->{ccugroup})) { # foreach my $da (split (",", $cl_hash->{ccugroup})) {
foreach my $cn (keys %{$events{$da}}) { # foreach my $cn (keys %{$events{$da}}) {
foreach my $dp (keys %{$events{$da}{$cn}}) { # foreach my $dp (keys %{$events{$da}{$cn}}) {
if (defined ($events{$da}{$cn}{$dp})) { # if (defined ($events{$da}{$cn}{$dp})) {
$events{$cl_hash->{ccuaddr}}{$cn}{$dp} = $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; return 1;
} }
@ -6747,10 +6882,11 @@ sub HMCCU_GetTimeSpec ($)
# Return readings array with reading/value pairs. # 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 $name = $cl_hash->{NAME};
my $fnc = "HMCCU_CalculateReading";
my @result = (); my @result = ();
@ -6759,22 +6895,39 @@ sub HMCCU_CalculateReading ($$$)
my @calclist = split (/[;\n]+/, $ccucalculate); my @calclist = split (/[;\n]+/, $ccucalculate);
foreach my $calculation (@calclist) { foreach my $calculation (@calclist) {
my ($vt, $rn, $dpts) = split (':', $calculation); my ($vt, $rn, $dpts) = split (':', $calculation, 3);
next if (!defined ($rn)); 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 # Get parameters values stored in device hash
my @dplist = defined ($dpts) ? split (',', $dpts) : (); my $newdpts = HMCCU_SubstVariables ($cl_hash, $dpts, undef);
next if (@dplist > 0 && !(grep { $_ eq "$chnno.$dpt"} @dplist)); my @pars = split (',', $newdpts);
my @pars = (); my $pc = scalar (@pars);
foreach my $dp (@dplist) { next if ($pc != scalar(@dplist));
if (exists ($cl_hash->{hmccu}{dp}{$dp}{VAL})) { $f = 0;
push @pars, $cl_hash->{hmccu}{dp}{$dp}{VAL}; 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') { if ($vt eq 'dewpoint' || $vt eq 'abshumidity') {
# Dewpoint and absolute humidity # Dewpoint and absolute humidity
next if (scalar (@pars) < 2); next if ($pc < 2);
my ($tmp, $hum) = @pars; my ($tmp, $hum) = @pars;
if ($tmp >= 0.0) { if ($tmp >= 0.0) {
$a = 7.5; $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') { elsif ($vt eq 'min' || $vt eq 'max') {
# Minimum or maximum values # Minimum or maximum values
next if (scalar (@pars) < 1); next if ($pc < 1);
my $newval = shift @pars; my $curval = $pc > 1 ? shift @pars : ReadingsVal ($name, $rn, 0);
my $curval = ReadingsVal ($name, $rn, 0); foreach my $newval (@pars) {
$curval = $newval if ($vt eq 'min' && $newval < $curval); $curval = $newval if ($vt eq 'min' && $newval < $curval);
$curval = $newval if ($vt eq 'max' && $newval > $curval); $curval = $newval if ($vt eq 'max' && $newval > $curval);
}
push (@result, $rn, $curval); push (@result, $rn, $curval);
} }
elsif ($vt eq 'inc' || $vt eq 'dec') { elsif ($vt eq 'inc' || $vt eq 'dec') {
# Increasing or decreasing values without reset # Increasing or decreasing values without reset
next if (scalar (@pars) < 1); next if ($pc < 1);
my $newval = shift @pars; my $newval = shift @pars;
my $oldval = ReadingsVal ($name, $rn."_old", 0); my $oldval = ReadingsVal ($name, $rn."_old", 0);
my $curval = ReadingsVal ($name, $rn, 0); my $curval = ReadingsVal ($name, $rn, 0);
@ -6823,7 +6987,7 @@ sub HMCCU_CalculateReading ($$$)
} }
elsif ($vt eq 'avg') { elsif ($vt eq 'avg') {
# Average value # Average value
next if (scalar (@pars) < 1); next if ($pc < 1);
my $newval = shift @pars; my $newval = shift @pars;
my $cnt = ReadingsVal ($name, $rn."_cnt", 0); my $cnt = ReadingsVal ($name, $rn."_cnt", 0);
my $sum = ReadingsVal ($name, $rn."_sum", 0); my $sum = ReadingsVal ($name, $rn."_sum", 0);
@ -6834,10 +6998,29 @@ sub HMCCU_CalculateReading ($$$)
} }
elsif ($vt eq 'sum') { elsif ($vt eq 'sum') {
# Sum of values # Sum of values
next if (scalar (@pars) < 1); next if ($pc < 1);
my $newval = shift @pars; my $curval = $pc > 1 ? 0 : ReadingsVal ($name, $rn, 0);
my $curval = ReadingsVal ($name, $rn, 0); foreach my $newval (@pars) {
$curval += $newval; $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); push (@result, $rn, $curval);
} }
} }
@ -7620,8 +7803,9 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
iface_conn_n = interface connection state (1=connected, 0=disconnected)<br/> iface_conn_n = interface connection state (1=connected, 0=disconnected)<br/>
iface_ducy_n = duty cycle of interface (0-100) iface_ducy_n = duty cycle of interface (0-100)
</li><br/> </li><br/>
<li><b>get &lt;name&gt; exportdefaults &lt;filename&gt; [{<u>default</u>|csv}]</b><br/> <li><b>get &lt;name&gt; exportdefaults &lt;filename&gt; [csv] [all]</b><br/>
Export default attributes into file. Export default attributes into file. If option <i>all</i> is specified, also defaults imported
by customer will be exported.
</li><br/> </li><br/>
<li><b>get &lt;name&gt; firmware [{&lt;type-expr&gt; | full}]</b><br/> <li><b>get &lt;name&gt; firmware [{&lt;type-expr&gt; | full}]</b><br/>
Get available firmware downloads from eq-3.de. List FHEM devices with current and available 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/> intrpc - Use internal RPC server. This is the default.<br/>
extrpc - Same as procrpc (see below)<br/> extrpc - Same as procrpc (see below)<br/>
logEvents - Write events from CCU into FHEM logfile<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/> nonBlocking - Use non blocking (asynchronous) CCU requests<br/>
noReadings - Do not create or update readings<br/> noReadings - Do not create or update readings<br/>
procrpc - Use external RPC server provided by module HMCCPRPCPROC. During first RPC procrpc - Use external RPC server provided by module HMCCPRPCPROC. During first RPC
@ -7763,8 +7948,9 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
<li><b>ccuReqTimeout &lt;Seconds&gt;</b><br/> <li><b>ccuReqTimeout &lt;Seconds&gt;</b><br/>
Set timeout for CCU request. Default is 4 seconds. This timeout affects several 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 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>. a timeout FHEM will block for <i>Seconds</i>. To prevent blocking set flag 'nonBlocking'
</li> in attribute <i>ccuflags</i>.
</li><br/>
<li><b>ccureadings {0 | <u>1</u>}</b><br/> <li><b>ccureadings {0 | <u>1</u>}</b><br/>
Deprecated. Readings are written by default. To deactivate readings set flag noReadings Deprecated. Readings are written by default. To deactivate readings set flag noReadings
in attribute ccuflags. in attribute ccuflags.
@ -7786,11 +7972,12 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
</ul> </ul>
</li><br/> </li><br/>
<li><b>rpcinterval &lt;Seconds&gt;</b><br/> <li><b>rpcinterval &lt;Seconds&gt;</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><br/>
<li><b>rpcport &lt;value[,...]&gt;</b><br/> <li><b>rpcport &lt;value[,...]&gt;</b><br/>
Deprecated, use 'rpcinterfaces' instead. Specify list of RPC ports on CCU. Default is Deprecated, use attribute 'rpcinterfaces' instead. Specify list of RPC ports on CCU.
2001. Valid RPC ports are:<br/><br/> Default is 2001. Valid RPC ports are:<br/><br/>
<ul> <ul>
<li>2000 = Wired components</li> <li>2000 = Wired components</li>
<li>2001 = BidCos-RF (wireless 868 MHz components with BidCos protocol)</li> <li>2001 = BidCos-RF (wireless 868 MHz components with BidCos protocol)</li>

View File

@ -4,7 +4,7 @@
# #
# $Id$ # $Id$
# #
# Version 4.3.003 # Version 4.3.004
# #
# (c) 2018 zap (zap01 <at> t-online <dot> de) # (c) 2018 zap (zap01 <at> t-online <dot> de)
# #
@ -67,6 +67,7 @@ use SetExtensions;
sub HMCCUDEV_Initialize ($); sub HMCCUDEV_Initialize ($);
sub HMCCUDEV_Define ($@); sub HMCCUDEV_Define ($@);
sub HMCCUDEV_Delete ($$);
sub HMCCUDEV_InitDevice ($$); sub HMCCUDEV_InitDevice ($$);
sub HMCCUDEV_Set ($@); sub HMCCUDEV_Set ($@);
sub HMCCUDEV_Get ($@); sub HMCCUDEV_Get ($@);
@ -81,18 +82,20 @@ sub HMCCUDEV_Initialize ($)
my ($hash) = @_; my ($hash) = @_;
$hash->{DefFn} = "HMCCUDEV_Define"; $hash->{DefFn} = "HMCCUDEV_Define";
$hash->{DeleteFn} = "HMCCUDEV_Delete";
$hash->{SetFn} = "HMCCUDEV_Set"; $hash->{SetFn} = "HMCCUDEV_Set";
$hash->{GetFn} = "HMCCUDEV_Get"; $hash->{GetFn} = "HMCCUDEV_Get";
$hash->{AttrFn} = "HMCCUDEV_Attr"; $hash->{AttrFn} = "HMCCUDEV_Attr";
$hash->{parseParams} = 1; $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 ". "ccuflags:multiple-strict,ackState,nochn0,trace ccureadingfilter:textField-long ".
"ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ". "ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ".
"ccureadingname:textField-long ". "ccureadingname:textField-long ".
"ccureadings:0,1 ccuget:State,Value ccuscaleval ccuverify:0,1,2 disable:0,1 ". "ccureadings:0,1 ccuget:State,Value ccuscaleval ccuverify:0,1,2 disable:0,1 ".
"hmstatevals:textField-long statevals substexcl substitute:textField-long statechannel ". "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 $name = $hash->{NAME};
my $usage = "Usage: define $name HMCCUDEV {device|'virtual'} [state-channel] ". 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}[,...]]"; "[{groupexp=regexp|group={device|channel}[,...]]";
return $usage if (scalar (@$a) < 3); return $usage if (scalar (@$a) < 3);
@ -116,7 +119,8 @@ sub HMCCUDEV_Define ($@)
"No devices in group", "No devices in group",
"No matching CCU devices found", "No matching CCU devices found",
"Type of virtual device not defined", "Type of virtual device not defined",
"Device type not found" "Device type not found",
"Too many virtual devices"
); );
my $devname = shift @$a; my $devname = shift @$a;
@ -130,6 +134,18 @@ sub HMCCUDEV_Define ($@)
$hash->{hmccu}{groupexp} = $h->{groupexp} if (exists ($h->{groupexp})); $hash->{hmccu}{groupexp} = $h->{groupexp} if (exists ($h->{groupexp}));
$hash->{hmccu}{group} = $h->{group} if (exists ($h->{group})); $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 # Defaults
$hash->{statevals} = 'devstate'; $hash->{statevals} = 'devstate';
@ -151,7 +167,7 @@ sub HMCCUDEV_Define ($@)
$hmccu_hash = $defs{$h->{iodev}}; $hmccu_hash = $defs{$h->{iodev}};
} }
else { 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); $hmccu_hash = $devspec eq 'virtual' ? HMCCU_GetHash (0) : HMCCU_FindIODevice ($devspec);
} }
@ -194,6 +210,7 @@ sub HMCCUDEV_Define ($@)
# 4 = No matching CCU devices found # 4 = No matching CCU devices found
# 5 = Type of virtual device not defined # 5 = Type of virtual device not defined
# 6 = Device type not found # 6 = Device type not found
# 7 = Too many virtual devices
###################################################################### ######################################################################
sub HMCCUDEV_InitDevice ($$) sub HMCCUDEV_InitDevice ($$)
@ -205,18 +222,26 @@ sub HMCCUDEV_InitDevice ($$)
my $gdname = $devspec; my $gdname = $devspec;
if ($devspec eq 'virtual') { if ($devspec eq 'virtual') {
# Virtual device FHEM only, search for free address
my $no = 0; my $no = 0;
foreach my $d (sort keys %defs) { if (exists ($dev_hash->{hmccu}{address})) {
my $ch = $defs{$d}; # Only true during FHEM start
next if (!exists ($ch->{TYPE})); $no = $dev_hash->{hmccu}{address};
next if ($ch->{TYPE} ne 'HMCCUDEV' || $d eq $name); }
next if ($ch->{ccuif} ne 'fhem' || $ch->{ccuname} ne 'virtual'); else {
$no++; # 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->{ccuif} = 'fhem';
$dev_hash->{ccuaddr} = sprintf ("VIR%07d", $no+1); $dev_hash->{ccuaddr} = sprintf ("VIR%07d", $no);
$dev_hash->{ccuname} = 'virtual'; $dev_hash->{ccuname} = $name;
} }
else { else {
return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $devspec, 7)); return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $devspec, 7));
@ -303,6 +328,21 @@ sub HMCCUDEV_InitDevice ($$)
return 0; return 0;
} }
#####################################
# Delete device
#####################################
sub HMCCUDEV_Delete ($$)
{
my ($hash, $name) = @_;
if ($hash->{ccuif} eq 'fhem') {
HMCCU_DeleteDevice ($hash);
}
return undef;
}
##################################### #####################################
# Set attribute # Set attribute
##################################### #####################################
@ -677,7 +717,8 @@ sub HMCCUDEV_Get ($@)
my $result = ''; my $result = '';
my $rc; 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"; 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'}]"); 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); $rc = HMCCU_GetUpdate ($hash, $ccuaddr, $ccuget);
return HMCCU_SetError ($hash, $rc) if ($rc < 0); return HMCCU_SetError ($hash, $rc) if ($rc < 0);
} }
else {
# Update other devices belonging to group # Update all devices belonging to group
# if ($hash->{ccuif} eq "VirtualDevices" && exists ($hash->{ccugroup})) { my @vdevs = split (",", $hash->{ccugroup});
# my @vdevs = split (",", $hash->{ccugroup}); foreach my $vd (@vdevs) {
# foreach my $vd (@vdevs) { $rc = HMCCU_GetUpdate ($hash, $vd, $ccuget);
# $rc = HMCCU_GetUpdate ($hash, $vd, $ccuget); return HMCCU_SetError ($hash, $rc) if ($rc < 0);
# return HMCCU_SetError ($hash, $rc) if ($rc < 0); }
# } }
# }
return undef; return undef;
} }
@ -856,8 +896,8 @@ sub HMCCUDEV_Get ($@)
expression for CCU device or channel names. Since version 4.2.009 of HMCCU HMCCUDEV 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 is able to detect members of group devices automatically. So options 'group' or
'groupexp' are no longer necessary to define a group device.<br/> '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 It's also possible to group any kind of CCU devices without defining a real group
group in CCU by using option 'virtual' instead of a CCU device specification. in CCU by using option 'virtual' instead of a CCU device specification.
<br/><br/> <br/><br/>
Examples:<br/> Examples:<br/>
<code> <code>