mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-04 22:19:38 +00:00
HMCCU: Version 3.9
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@13241 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
132dcb214c
commit
63498f7bbf
1
CHANGED
1
CHANGED
@ -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.
|
||||||
|
- update: 88_HMCCU.pm: version 3.9
|
||||||
- update: 98_DOIFtools.pm: marking an eventline in DOIFs event monitor
|
- update: 98_DOIFtools.pm: marking an eventline in DOIFs event monitor
|
||||||
shows different representations of the event as operand
|
shows different representations of the event as operand
|
||||||
for DOIF definitions
|
for DOIF definitions
|
||||||
|
330
FHEM/88_HMCCU.pm
330
FHEM/88_HMCCU.pm
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# $Id$
|
# $Id$
|
||||||
#
|
#
|
||||||
# Version 3.8
|
# Version 3.9
|
||||||
#
|
#
|
||||||
# Module for communication between FHEM and Homematic CCU2.
|
# Module for communication between FHEM and Homematic CCU2.
|
||||||
# Supports BidCos-RF, BidCos-Wired, HmIP-RF, virtual CCU channels,
|
# Supports BidCos-RF, BidCos-Wired, HmIP-RF, virtual CCU channels,
|
||||||
@ -43,6 +43,7 @@
|
|||||||
#
|
#
|
||||||
# attr <name> ccuackstate { 0 | 1 }
|
# attr <name> ccuackstate { 0 | 1 }
|
||||||
# attr <name> ccuaggregate <rules>
|
# attr <name> ccuaggregate <rules>
|
||||||
|
# attr <name> ccudef-hmstatevals <subst_rules>
|
||||||
# attr <name> ccudef-readingfilter <filter_rule>
|
# attr <name> ccudef-readingfilter <filter_rule>
|
||||||
# attr <name> ccudef-readingname <rules>
|
# attr <name> ccudef-readingname <rules>
|
||||||
# attr <name> ccudef-substitute <subst_rule>
|
# attr <name> ccudef-substitute <subst_rule>
|
||||||
@ -102,7 +103,7 @@ my %HMCCU_CUST_CHN_DEFAULTS;
|
|||||||
my %HMCCU_CUST_DEV_DEFAULTS;
|
my %HMCCU_CUST_DEV_DEFAULTS;
|
||||||
|
|
||||||
# HMCCU version
|
# HMCCU version
|
||||||
my $HMCCU_VERSION = '3.8';
|
my $HMCCU_VERSION = '3.9';
|
||||||
|
|
||||||
# RPC Ports and URL extensions
|
# RPC Ports and URL extensions
|
||||||
my %HMCCU_RPC_PORT = (
|
my %HMCCU_RPC_PORT = (
|
||||||
@ -193,6 +194,7 @@ sub HMCCU_SetError ($$);
|
|||||||
sub HMCCU_SetState ($$);
|
sub HMCCU_SetState ($$);
|
||||||
sub HMCCU_Substitute ($$$$$);
|
sub HMCCU_Substitute ($$$$$);
|
||||||
sub HMCCU_SubstRule ($$$);
|
sub HMCCU_SubstRule ($$$);
|
||||||
|
sub HMCCU_SubstVariables ($$);
|
||||||
sub HMCCU_UpdateClients ($$$$);
|
sub HMCCU_UpdateClients ($$$$);
|
||||||
sub HMCCU_UpdateClientReading ($@);
|
sub HMCCU_UpdateClientReading ($@);
|
||||||
sub HMCCU_AddDevices ($$);
|
sub HMCCU_AddDevices ($$);
|
||||||
@ -223,7 +225,9 @@ sub HMCCU_GetDatapointCount ($$$);
|
|||||||
sub HMCCU_GetSpecialDatapoints ($$$$$);
|
sub HMCCU_GetSpecialDatapoints ($$$$$);
|
||||||
sub HMCCU_GetAttrReadingFormat ($$);
|
sub HMCCU_GetAttrReadingFormat ($$);
|
||||||
sub HMCCU_GetAttrSubstitute ($$);
|
sub HMCCU_GetAttrSubstitute ($$);
|
||||||
|
sub HMCCU_IsValidDeviceOrChannel ($$);
|
||||||
sub HMCCU_IsValidDevice ($$);
|
sub HMCCU_IsValidDevice ($$);
|
||||||
|
sub HMCCU_IsValidChannel ($$);
|
||||||
sub HMCCU_GetCCUDeviceParam ($$);
|
sub HMCCU_GetCCUDeviceParam ($$);
|
||||||
sub HMCCU_GetValidDatapoints ($$$$$);
|
sub HMCCU_GetValidDatapoints ($$$$$);
|
||||||
sub HMCCU_GetSwitchDatapoint ($$$);
|
sub HMCCU_GetSwitchDatapoint ($$$);
|
||||||
@ -259,10 +263,11 @@ sub HMCCU_QueueEnq ($$);
|
|||||||
sub HMCCU_QueueDeq ($);
|
sub HMCCU_QueueDeq ($);
|
||||||
|
|
||||||
# Helper functions
|
# Helper functions
|
||||||
sub HMCCU_GetHMState ($$);
|
sub HMCCU_GetHMState ($$$);
|
||||||
sub HMCCU_AggReadings ($$$$$);
|
sub HMCCU_AggReadings ($$$$$);
|
||||||
sub HMCCU_GetTimeSpec ($);
|
sub HMCCU_GetTimeSpec ($);
|
||||||
sub HMCCU_Dewpoint ($$$$);
|
sub HMCCU_Dewpoint ($$$$);
|
||||||
|
sub HMCCU_CalculateReading ($$$);
|
||||||
sub HMCCU_EncodeEPDisplay ($);
|
sub HMCCU_EncodeEPDisplay ($);
|
||||||
|
|
||||||
# Subprocess functions
|
# Subprocess functions
|
||||||
@ -297,7 +302,7 @@ sub HMCCU_Initialize ($)
|
|||||||
$hash->{ShutdownFn} = "HMCCU_Shutdown";
|
$hash->{ShutdownFn} = "HMCCU_Shutdown";
|
||||||
$hash->{parseParams} = 1;
|
$hash->{parseParams} = 1;
|
||||||
|
|
||||||
$hash->{AttrList} = "stripchar stripnumber ccuackstate:0,1 ccuaggregate:textField-long ccudefaults ccudef-substitute:textField-long ccudef-readingname:textField-long ccudef-readingfilter:textField-long ccudef-readingformat:name,namelc,address,addresslc,datapoint,datapointlc ccuflags:multiple-strict,intrpc,dptnocheck,noagg,nohmstate ccureadings:0,1 ccureadingfilter ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc rpcinterval:2,3,5,7,10 rpcqueue rpcport:multiple-strict,2000,2001,2003,2010,9292 rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute ccutrace ccuget:Value,State ". $readingFnAttributes;
|
$hash->{AttrList} = "stripchar stripnumber ccuackstate:0,1 ccuaggregate:textField-long ccudefaults 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,intrpc,dptnocheck,noagg,nohmstate ccureadings:0,1 ccureadingfilter ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc rpcinterval:2,3,5,7,10 rpcqueue rpcport:multiple-strict,2000,2001,2003,2010,9292 rpcserver:on,off rpcserveraddr rpcserverport rpctimeout rpcevtimeout parfile substitute ccutrace ccuget:Value,State ". $readingFnAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
@ -572,13 +577,13 @@ sub HMCCU_FindDefaults ($$)
|
|||||||
foreach my $deftype (keys %HMCCU_CUST_CHN_DEFAULTS) {
|
foreach my $deftype (keys %HMCCU_CUST_CHN_DEFAULTS) {
|
||||||
my @chnlst = split (',', $HMCCU_CUST_CHN_DEFAULTS{$deftype}{_channels});
|
my @chnlst = split (',', $HMCCU_CUST_CHN_DEFAULTS{$deftype}{_channels});
|
||||||
return \%{$HMCCU_CUST_CHN_DEFAULTS{$deftype}}
|
return \%{$HMCCU_CUST_CHN_DEFAULTS{$deftype}}
|
||||||
if ($ccutype =~ /^($deftype)$/ && grep { $_ eq $chn} @chnlst);
|
if ($ccutype =~ /^($deftype)$/i && grep { $_ eq $chn} @chnlst);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $deftype (keys %{$HMCCU_CHN_DEFAULTS}) {
|
foreach my $deftype (keys %{$HMCCU_CHN_DEFAULTS}) {
|
||||||
my @chnlst = split (',', $HMCCU_CHN_DEFAULTS->{$deftype}{_channels});
|
my @chnlst = split (',', $HMCCU_CHN_DEFAULTS->{$deftype}{_channels});
|
||||||
return \%{$HMCCU_CHN_DEFAULTS->{$deftype}}
|
return \%{$HMCCU_CHN_DEFAULTS->{$deftype}}
|
||||||
if ($ccutype =~ /^($deftype)$/ && grep { $_ eq $chn} @chnlst);
|
if ($ccutype =~ /^($deftype)$/i && grep { $_ eq $chn} @chnlst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ($type eq 'HMCCUDEV' || $type eq 'HMCCU') {
|
elsif ($type eq 'HMCCUDEV' || $type eq 'HMCCU') {
|
||||||
@ -588,11 +593,11 @@ sub HMCCU_FindDefaults ($$)
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach my $deftype (keys %HMCCU_CUST_DEV_DEFAULTS) {
|
foreach my $deftype (keys %HMCCU_CUST_DEV_DEFAULTS) {
|
||||||
return \%{$HMCCU_CUST_DEV_DEFAULTS{$deftype}} if ($ccutype =~ /^($deftype)$/);
|
return \%{$HMCCU_CUST_DEV_DEFAULTS{$deftype}} if ($ccutype =~ /^($deftype)$/i);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $deftype (keys %{$HMCCU_DEV_DEFAULTS}) {
|
foreach my $deftype (keys %{$HMCCU_DEV_DEFAULTS}) {
|
||||||
return \%{$HMCCU_DEV_DEFAULTS->{$deftype}} if ($ccutype =~ /^($deftype)$/);
|
return \%{$HMCCU_DEV_DEFAULTS->{$deftype}} if ($ccutype =~ /^($deftype)$/i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1221,7 +1226,7 @@ sub HMCCU_Get ($@)
|
|||||||
return HMCCU_SetError ($hash, "Usage: get $name deviceinfo {device} [{'State'|'Value'}]")
|
return HMCCU_SetError ($hash, "Usage: get $name deviceinfo {device} [{'State'|'Value'}]")
|
||||||
if ($ccuget !~ /^(Attr|State|Value)$/);
|
if ($ccuget !~ /^(Attr|State|Value)$/);
|
||||||
|
|
||||||
return HMCCU_SetError ($hash, -1) if (!HMCCU_IsValidDevice ($hash, $device));
|
return HMCCU_SetError ($hash, -1) if (!HMCCU_IsValidDeviceOrChannel ($hash, $device));
|
||||||
$result = HMCCU_GetDeviceInfo ($hash, $device, $ccuget);
|
$result = HMCCU_GetDeviceInfo ($hash, $device, $ccuget);
|
||||||
return HMCCU_SetError ($hash, -2) if ($result eq '');
|
return HMCCU_SetError ($hash, -2) if ($result eq '');
|
||||||
return HMCCU_FormatDeviceInfo ($result);
|
return HMCCU_FormatDeviceInfo ($result);
|
||||||
@ -1792,8 +1797,14 @@ sub HMCCU_Substitute ($$$$$)
|
|||||||
|
|
||||||
##################################################################
|
##################################################################
|
||||||
# Execute substitution list.
|
# Execute substitution list.
|
||||||
# Syntax for single substitution: {#n-n|regexp|text}:text
|
# Syntax for single substitution: {#n-n|regexp|text}:newtext
|
||||||
# mode: 0=Substitute regular expression, 1=Substitute text
|
# mode=0: Substitute regular expression
|
||||||
|
# mode=1: Substitute text (for setting statevals)
|
||||||
|
# newtext can contain ':'. Parameter ${value} in newtext is
|
||||||
|
# substituted by original value.
|
||||||
|
# Return (status, value)
|
||||||
|
# status=1: value = substituted value
|
||||||
|
# status=0: value = original value
|
||||||
##################################################################
|
##################################################################
|
||||||
|
|
||||||
sub HMCCU_SubstRule ($$$)
|
sub HMCCU_SubstRule ($$$)
|
||||||
@ -1801,24 +1812,30 @@ sub HMCCU_SubstRule ($$$)
|
|||||||
my ($value, $substitutes, $mode ) = @_;
|
my ($value, $substitutes, $mode ) = @_;
|
||||||
my $rc = 0;
|
my $rc = 0;
|
||||||
|
|
||||||
|
$substitutes =~ s/\$\{value\}/$value/g;
|
||||||
|
|
||||||
my @sub_list = split /,/,$substitutes;
|
my @sub_list = split /,/,$substitutes;
|
||||||
foreach my $s (@sub_list) {
|
foreach my $s (@sub_list) {
|
||||||
my ($regexp, $text) = split /:/,$s;
|
# my ($regexp, $text) = split /:/,$s;
|
||||||
|
my ($regexp, $text) = split /:/,$s,2;
|
||||||
next if (!defined ($regexp) || !defined($text));
|
next if (!defined ($regexp) || !defined($text));
|
||||||
if ($regexp =~ /^#([+-]?\d*\.?\d+?)\-([+-]?\d*\.?\d+?)$/) {
|
if ($regexp =~ /^#([+-]?\d*\.?\d+?)\-([+-]?\d*\.?\d+?)$/) {
|
||||||
my ($mi, $ma) = ($1, $2);
|
my ($mi, $ma) = ($1, $2);
|
||||||
if ($value =~ /^\d*\.?\d+?/ && $value >= $mi && $value <= $ma) {
|
if ($value =~ /^\d*\.?\d+?/ && $value >= $mi && $value <= $ma) {
|
||||||
$value = $text;
|
# $value = $text;
|
||||||
$rc = 1;
|
# $rc = 1;
|
||||||
last;
|
my $x = eval { $value =~ s/^\d*\.?\d+?/$text/ };
|
||||||
|
$rc = 1 if (defined ($x)); last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($mode == 0 && $value =~ /$regexp/ && $value !~ /^[+-]?\d+$/) {
|
if ($mode == 0 && $value =~ /$regexp/ && $value !~ /^[+-]?\d+$/) {
|
||||||
|
# my $x = eval { $value =~ s/$regexp/$text/ };
|
||||||
my $x = eval { $value =~ s/$regexp/$text/ };
|
my $x = eval { $value =~ s/$regexp/$text/ };
|
||||||
$rc = 1 if (defined ($x));
|
$rc = 1 if (defined ($x));
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
elsif (($mode == 1 || $value =~/^[+-]?\d+$/) && $value =~ /^$regexp$/) {
|
elsif (($mode == 1 || $value =~/^[+-]?\d+$/) && $value =~ /^$regexp$/) {
|
||||||
|
# my $x = eval { $value =~ s/^$regexp$/$text/ };
|
||||||
my $x = eval { $value =~ s/^$regexp$/$text/ };
|
my $x = eval { $value =~ s/^$regexp$/$text/ };
|
||||||
$rc = 1 if (defined ($x));
|
$rc = 1 if (defined ($x));
|
||||||
last;
|
last;
|
||||||
@ -1828,6 +1845,26 @@ sub HMCCU_SubstRule ($$$)
|
|||||||
return ($rc, $value);
|
return ($rc, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##################################################################
|
||||||
|
# Substitute datapoint variables in string by datapoint value.
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
sub HMCCU_SubstVariables ($$)
|
||||||
|
{
|
||||||
|
my ($clhash, $text) = @_;
|
||||||
|
|
||||||
|
# Substitute datapoint variables by value
|
||||||
|
foreach my $dp (keys %{$clhash->{hmccu}{dp}}) {
|
||||||
|
my ($chn,$dpt) = split (/\./, $dp);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $text;
|
||||||
|
}
|
||||||
|
|
||||||
##################################################################
|
##################################################################
|
||||||
# Update all datapoint/readings of all client devices matching
|
# Update all datapoint/readings of all client devices matching
|
||||||
# specified regular expression. Update will fail if device is
|
# specified regular expression. Update will fail if device is
|
||||||
@ -1997,6 +2034,7 @@ sub HMCCU_UpdateClientReading ($@)
|
|||||||
my $svalue = HMCCU_ScaleValue ($ch, $dpt, $value, 0);
|
my $svalue = HMCCU_ScaleValue ($ch, $dpt, $value, 0);
|
||||||
my $fvalue = HMCCU_FormatReadingValue ($ch, $svalue);
|
my $fvalue = HMCCU_FormatReadingValue ($ch, $svalue);
|
||||||
my $cvalue = HMCCU_Substitute ($fvalue, $substitute, 0, $chn, $dpt);
|
my $cvalue = HMCCU_Substitute ($fvalue, $substitute, 0, $chn, $dpt);
|
||||||
|
my %calcs = HMCCU_CalculateReading ($ch, $chn, $dpt);
|
||||||
|
|
||||||
Log3 $name, 2, "HMCCU: $fnc device=$cn, readings=".join(',', @readings).
|
Log3 $name, 2, "HMCCU: $fnc device=$cn, readings=".join(',', @readings).
|
||||||
", orgvalue=$value value=$cvalue" if ($cf =~ /trace/);
|
", orgvalue=$value value=$cvalue" if ($cf =~ /trace/);
|
||||||
@ -2004,6 +2042,9 @@ sub HMCCU_UpdateClientReading ($@)
|
|||||||
foreach my $rn (@readings) {
|
foreach my $rn (@readings) {
|
||||||
HMCCU_BulkUpdate ($ch, $rn, $value, $cvalue) if ($rn ne '');
|
HMCCU_BulkUpdate ($ch, $rn, $value, $cvalue) if ($rn ne '');
|
||||||
}
|
}
|
||||||
|
foreach my $clcr (keys %calcs) {
|
||||||
|
HMCCU_BulkUpdate ($ch, $clcr, $calcs{$clcr}, $calcs{$clcr});
|
||||||
|
}
|
||||||
HMCCU_BulkUpdate ($ch, 'control', $fvalue, $cvalue)
|
HMCCU_BulkUpdate ($ch, 'control', $fvalue, $cvalue)
|
||||||
if ($cd ne '' && $dpt eq $cd && $chn eq $cc);
|
if ($cd ne '' && $dpt eq $cd && $chn eq $cc);
|
||||||
HMCCU_BulkUpdate ($ch, 'state', $fvalue, $cvalue)
|
HMCCU_BulkUpdate ($ch, 'state', $fvalue, $cvalue)
|
||||||
@ -2011,8 +2052,8 @@ sub HMCCU_UpdateClientReading ($@)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($ccuflags !~ /nohmstate/) {
|
if ($ccuflags !~ /nohmstate/) {
|
||||||
my ($hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cn, undef);
|
my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cn, $name, undef);
|
||||||
HMCCU_BulkUpdate ($ch, 'hmstate', $hms_val, $hms_val) if (defined ($hms_val));
|
HMCCU_BulkUpdate ($ch, $hms_read, $hms_val, $hms_val) if (defined ($hms_val));
|
||||||
}
|
}
|
||||||
|
|
||||||
readingsEndUpdate ($ch, 1);
|
readingsEndUpdate ($ch, 1);
|
||||||
@ -2841,7 +2882,7 @@ foreach (sDevice, sDevList.Split(",")) {
|
|||||||
# and refers to an existing device or channel.
|
# and refers to an existing device or channel.
|
||||||
####################################################
|
####################################################
|
||||||
|
|
||||||
sub HMCCU_IsValidDevice ($$)
|
sub HMCCU_IsValidDeviceOrChannel ($$)
|
||||||
{
|
{
|
||||||
my ($hash, $param) = @_;
|
my ($hash, $param) = @_;
|
||||||
|
|
||||||
@ -2861,6 +2902,56 @@ sub HMCCU_IsValidDevice ($$)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# Check if device name or address is valid
|
||||||
|
# and refers to an existing device.
|
||||||
|
####################################################
|
||||||
|
|
||||||
|
sub HMCCU_IsValidDevice ($$)
|
||||||
|
{
|
||||||
|
my ($hash, $param) = @_;
|
||||||
|
|
||||||
|
if (HMCCU_IsDevAddr ($param, 1)) {
|
||||||
|
my ($i, $a) = split (/\./, $param);
|
||||||
|
return 0 if (! exists ($hash->{hmccu}{dev}{$a}));
|
||||||
|
return $hash->{hmccu}{dev}{$a}{valid};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HMCCU_IsDevAddr ($param, 0)) {
|
||||||
|
return 0 if (! exists ($hash->{hmccu}{dev}{$param}));
|
||||||
|
return $hash->{hmccu}{dev}{$param}{valid};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0 if (! exists ($hash->{hmccu}{adr}{$param}));
|
||||||
|
return $hash->{hmccu}{adr}{$param}{valid} && $hash->{hmccu}{adr}{$param}{addtype} eq 'dev';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# Check if channel name or address is valid
|
||||||
|
# and refers to an existing channel.
|
||||||
|
####################################################
|
||||||
|
|
||||||
|
sub HMCCU_IsValidChannel ($$)
|
||||||
|
{
|
||||||
|
my ($hash, $param) = @_;
|
||||||
|
|
||||||
|
if (HMCCU_IsChnAddr ($param, 1)) {
|
||||||
|
my ($i, $a) = split (/\./, $param);
|
||||||
|
return 0 if (! exists ($hash->{hmccu}{dev}{$a}));
|
||||||
|
return $hash->{hmccu}{dev}{$a}{valid};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HMCCU_IsChnAddr ($param, 0)) {
|
||||||
|
return 0 if (! exists ($hash->{hmccu}{dev}{$param}));
|
||||||
|
return $hash->{hmccu}{dev}{$param}{valid};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0 if (! exists ($hash->{hmccu}{adr}{$param}));
|
||||||
|
return $hash->{hmccu}{adr}{$param}{valid} && $hash->{hmccu}{adr}{$param}{addtype} eq 'chn';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
####################################################
|
####################################################
|
||||||
# Get CCU parameters of device or channel. Returns
|
# Get CCU parameters of device or channel. Returns
|
||||||
# list containing interface, deviceaddress, name
|
# list containing interface, deviceaddress, name
|
||||||
@ -3318,7 +3409,7 @@ sub HMCCU_FindIODevice ($)
|
|||||||
next if (!exists ($ch->{TYPE}));
|
next if (!exists ($ch->{TYPE}));
|
||||||
next if ($ch->{TYPE} ne 'HMCCU');
|
next if ($ch->{TYPE} ne 'HMCCU');
|
||||||
|
|
||||||
return $ch if (HMCCU_IsValidDevice ($ch, $param));
|
return $ch if (HMCCU_IsValidDeviceOrChannel ($ch, $param));
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
@ -3476,6 +3567,7 @@ sub HMCCU_GetAttrReadingFormat ($$)
|
|||||||
######################################################################
|
######################################################################
|
||||||
# Get attributes substitute and substexcl considering default
|
# Get attributes substitute and substexcl considering default
|
||||||
# attribute ccudef-substitute defined in I/O device.
|
# attribute ccudef-substitute defined in I/O device.
|
||||||
|
# Substitute ${xxx} by datapoint value.
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
sub HMCCU_GetAttrSubstitute ($$)
|
sub HMCCU_GetAttrSubstitute ($$)
|
||||||
@ -3485,9 +3577,17 @@ sub HMCCU_GetAttrSubstitute ($$)
|
|||||||
my $clname = $clhash->{NAME};
|
my $clname = $clhash->{NAME};
|
||||||
my $ioname = $iohash->{NAME};
|
my $ioname = $iohash->{NAME};
|
||||||
|
|
||||||
|
my $ccuflags = AttrVal ($clname, 'ccuflags', 'null');
|
||||||
my $substdef = AttrVal ($ioname, 'ccudef-substitute', '');
|
my $substdef = AttrVal ($ioname, 'ccudef-substitute', '');
|
||||||
my $subst = AttrVal ($clname, 'substitute', $substdef);
|
my $subst = AttrVal ($clname, 'substitute', $substdef);
|
||||||
$subst .= ";$substdef" if ($subst ne $substdef && $substdef ne '');
|
$subst .= ";$substdef" if ($subst ne $substdef && $substdef ne '');
|
||||||
|
Log3 $clname, 2, "HMCCU: GetAttrSubstitute: subst = $subst" if ($ccuflags =~ /trace/);
|
||||||
|
|
||||||
|
return $subst if ($subst !~ /\$\{.+\}/);
|
||||||
|
|
||||||
|
$subst = HMCCU_SubstVariables ($clhash, $subst);
|
||||||
|
|
||||||
|
Log3 $clname, 2, "HMCCU: GetAttrSubstitute: subst = $subst" if ($ccuflags =~ /trace/);
|
||||||
|
|
||||||
return $subst;
|
return $subst;
|
||||||
}
|
}
|
||||||
@ -3882,12 +3982,13 @@ sub HMCCU_UpdateSingleReading ($$$$$)
|
|||||||
|
|
||||||
my $hmccu_hash = HMCCU_GetHash ($hash);
|
my $hmccu_hash = HMCCU_GetHash ($hash);
|
||||||
return $value if (!defined ($hmccu_hash));
|
return $value if (!defined ($hmccu_hash));
|
||||||
|
my $ioname = $hmccu_hash->{NAME};
|
||||||
|
|
||||||
my $ccureadings = AttrVal ($name, 'ccureadings', 1);
|
my $ccureadings = AttrVal ($name, 'ccureadings', 1);
|
||||||
my $disable = AttrVal ($name, 'disable', 0);
|
my $disable = AttrVal ($name, 'disable', 0);
|
||||||
return $value if ($ccureadings == 0 || $disable == 1);
|
return $value if ($ccureadings == 0 || $disable == 1);
|
||||||
|
|
||||||
my $hmccuflags = AttrVal ($hmccu_hash->{NAME}, 'ccuflags', 'null');
|
my $hmccuflags = AttrVal ($ioname, 'ccuflags', 'null');
|
||||||
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
|
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
|
||||||
my $readingformat = HMCCU_GetAttrReadingFormat ($hash, $hmccu_hash);
|
my $readingformat = HMCCU_GetAttrReadingFormat ($hash, $hmccu_hash);
|
||||||
my $substitute = HMCCU_GetAttrSubstitute ($hash, $hmccu_hash);
|
my $substitute = HMCCU_GetAttrSubstitute ($hash, $hmccu_hash);
|
||||||
@ -3898,20 +3999,24 @@ sub HMCCU_UpdateSingleReading ($$$$$)
|
|||||||
my $svalue = HMCCU_ScaleValue ($hash, $dpt, $value, 0);
|
my $svalue = HMCCU_ScaleValue ($hash, $dpt, $value, 0);
|
||||||
my $fvalue = HMCCU_FormatReadingValue ($hash, $svalue);
|
my $fvalue = HMCCU_FormatReadingValue ($hash, $svalue);
|
||||||
my $cvalue = HMCCU_Substitute ($fvalue, $substitute, 0, $chn, $dpt);
|
my $cvalue = HMCCU_Substitute ($fvalue, $substitute, 0, $chn, $dpt);
|
||||||
|
my %calcs = HMCCU_CalculateReading ($hash, $chn, $dpt);
|
||||||
|
|
||||||
readingsBeginUpdate ($hash);
|
readingsBeginUpdate ($hash);
|
||||||
|
|
||||||
foreach my $rn (@$readings) {
|
foreach my $rn (@$readings) {
|
||||||
HMCCU_BulkUpdate ($hash, $rn, $value, $cvalue) if ($rn ne '');
|
HMCCU_BulkUpdate ($hash, $rn, $value, $cvalue) if ($rn ne '');
|
||||||
}
|
}
|
||||||
|
foreach my $clcr (keys %calcs) {
|
||||||
|
HMCCU_BulkUpdate ($hash, $clcr, $calcs{$clcr}, $calcs{$clcr});
|
||||||
|
}
|
||||||
HMCCU_BulkUpdate ($hash, 'control', $fvalue, $cvalue)
|
HMCCU_BulkUpdate ($hash, 'control', $fvalue, $cvalue)
|
||||||
if ($cd ne '' && $dpt eq $cd && $chn eq $cc);
|
if ($cd ne '' && $dpt eq $cd && $chn eq $cc);
|
||||||
HMCCU_BulkUpdate ($hash, 'state', $fvalue, $cvalue)
|
HMCCU_BulkUpdate ($hash, 'state', $fvalue, $cvalue)
|
||||||
if ($dpt eq $sd && ($sc eq '' || $sc eq $chn));
|
if ($dpt eq $sd && ($sc eq '' || $sc eq $chn));
|
||||||
|
|
||||||
if ($hmccuflags !~ /nohmstate/) {
|
if ($hmccuflags !~ /nohmstate/) {
|
||||||
my ($hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($name, undef);
|
my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($name, $ioname, undef);
|
||||||
HMCCU_BulkUpdate ($hash, 'hmstate', $hms_val, $hms_val) if (defined ($hms_val));
|
HMCCU_BulkUpdate ($hash, $hms_read, $hms_val, $hms_val) if (defined ($hms_val));
|
||||||
}
|
}
|
||||||
|
|
||||||
readingsEndUpdate ($hash, 1);
|
readingsEndUpdate ($hash, 1);
|
||||||
@ -4322,13 +4427,14 @@ sub HMCCU_UpdateDeviceReadings ($$)
|
|||||||
|
|
||||||
my $hmccu_hash = HMCCU_GetHash ($cl_hash);
|
my $hmccu_hash = HMCCU_GetHash ($cl_hash);
|
||||||
return 0 if (!defined ($hmccu_hash));
|
return 0 if (!defined ($hmccu_hash));
|
||||||
|
my $ioname = $hmccu_hash->{NAME};
|
||||||
|
|
||||||
my $disable = AttrVal ($cn, 'disable', 0);
|
my $disable = AttrVal ($cn, 'disable', 0);
|
||||||
return 0 if ($disable == 1);
|
return 0 if ($disable == 1);
|
||||||
my $ccureadings = AttrVal ($cn, 'ccureadings', 1);
|
my $ccureadings = AttrVal ($cn, 'ccureadings', 1);
|
||||||
return -6 if ($ccureadings == 0);
|
return -6 if ($ccureadings == 0);
|
||||||
|
|
||||||
my $hmccuflags = AttrVal ($hmccu_hash->{NAME}, 'ccuflags', 'null');
|
my $hmccuflags = AttrVal ($ioname, 'ccuflags', 'null');
|
||||||
my $ccuflags = AttrVal ($cn, 'ccuflags', 'null');
|
my $ccuflags = AttrVal ($cn, 'ccuflags', 'null');
|
||||||
my $readingformat = HMCCU_GetAttrReadingFormat ($cl_hash, $hmccu_hash);
|
my $readingformat = HMCCU_GetAttrReadingFormat ($cl_hash, $hmccu_hash);
|
||||||
my $substitute = HMCCU_GetAttrSubstitute ($cl_hash, $hmccu_hash);
|
my $substitute = HMCCU_GetAttrSubstitute ($cl_hash, $hmccu_hash);
|
||||||
@ -4356,10 +4462,14 @@ sub HMCCU_UpdateDeviceReadings ($$)
|
|||||||
my $svalue = HMCCU_ScaleValue ($cl_hash, $dpt, $value, 0);
|
my $svalue = HMCCU_ScaleValue ($cl_hash, $dpt, $value, 0);
|
||||||
my $fvalue = HMCCU_FormatReadingValue ($cl_hash, $svalue);
|
my $fvalue = HMCCU_FormatReadingValue ($cl_hash, $svalue);
|
||||||
my $cvalue = HMCCU_Substitute ($fvalue, $substitute, 0, $chn, $dpt);
|
my $cvalue = HMCCU_Substitute ($fvalue, $substitute, 0, $chn, $dpt);
|
||||||
|
my %calcs = HMCCU_CalculateReading ($cl_hash, $chn, $dpt);
|
||||||
|
|
||||||
foreach my $rn (@readings) {
|
foreach my $rn (@readings) {
|
||||||
HMCCU_BulkUpdate ($cl_hash, $rn, $value, $cvalue) if ($rn ne '');
|
HMCCU_BulkUpdate ($cl_hash, $rn, $value, $cvalue) if ($rn ne '');
|
||||||
}
|
}
|
||||||
|
foreach my $clcr (keys %calcs) {
|
||||||
|
HMCCU_BulkUpdate ($cl_hash, $clcr, $calcs{$clcr}, $calcs{$clcr});
|
||||||
|
}
|
||||||
HMCCU_BulkUpdate ($cl_hash, 'control', $fvalue, $cvalue)
|
HMCCU_BulkUpdate ($cl_hash, 'control', $fvalue, $cvalue)
|
||||||
if ($cd ne '' && $adrtoks[2] eq $cd && $chn eq $cc);
|
if ($cd ne '' && $adrtoks[2] eq $cd && $chn eq $cc);
|
||||||
HMCCU_BulkUpdate ($cl_hash, "state", $fvalue, $cvalue)
|
HMCCU_BulkUpdate ($cl_hash, "state", $fvalue, $cvalue)
|
||||||
@ -4370,8 +4480,8 @@ sub HMCCU_UpdateDeviceReadings ($$)
|
|||||||
|
|
||||||
# Get HomeMatic state
|
# Get HomeMatic state
|
||||||
if ($hmccuflags !~ /nohmstate/) {
|
if ($hmccuflags !~ /nohmstate/) {
|
||||||
my ($hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cn, undef);
|
my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($cn, $ioname, undef);
|
||||||
HMCCU_BulkUpdate ($cl_hash, 'hmstate', $hms_val, $hms_val) if (defined ($hms_val));
|
HMCCU_BulkUpdate ($cl_hash, $hms_read, $hms_val, $hms_val) if (defined ($hms_val));
|
||||||
}
|
}
|
||||||
|
|
||||||
readingsEndUpdate ($cl_hash, 1);
|
readingsEndUpdate ($cl_hash, 1);
|
||||||
@ -4404,8 +4514,9 @@ sub HMCCU_GetChannel ($$)
|
|||||||
return (-3, $result) if (!defined ($hmccu_hash));;
|
return (-3, $result) if (!defined ($hmccu_hash));;
|
||||||
return (-4, $result) if ($type ne 'HMCCU' && $hash->{ccudevstate} eq 'Deleted');
|
return (-4, $result) if ($type ne 'HMCCU' && $hash->{ccudevstate} eq 'Deleted');
|
||||||
my $type_hash = $type eq 'HMCCU' ? $hmccu_hash : $hash;
|
my $type_hash = $type eq 'HMCCU' ? $hmccu_hash : $hash;
|
||||||
|
my $ioname = $hmccu_hash->{NAME};
|
||||||
|
|
||||||
my $hmccuflags = AttrVal ($hmccu_hash->{NAME}, 'ccuflags', 'null');
|
my $hmccuflags = AttrVal ($ioname, 'ccuflags', 'null');
|
||||||
my $ccuget = HMCCU_GetAttribute ($hmccu_hash, $hash, 'ccuget', 'Value');
|
my $ccuget = HMCCU_GetAttribute ($hmccu_hash, $hash, 'ccuget', 'Value');
|
||||||
my $ccureadings = AttrVal ($name, 'ccureadings', 1);
|
my $ccureadings = AttrVal ($name, 'ccureadings', 1);
|
||||||
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
|
my $ccuflags = AttrVal ($name, 'ccuflags', 'null');
|
||||||
@ -4480,6 +4591,7 @@ foreach (sChannel, sChnList.Split(",")) {
|
|||||||
$value = HMCCU_ScaleValue ($hash, $dpt, $dpdata[2], 0);
|
$value = HMCCU_ScaleValue ($hash, $dpt, $dpdata[2], 0);
|
||||||
my $fvalue = HMCCU_FormatReadingValue ($hash, $value);
|
my $fvalue = HMCCU_FormatReadingValue ($hash, $value);
|
||||||
my $cvalue = HMCCU_Substitute ($fvalue, $chnpars{$dpdata[0]}{sub}, 0, $chn, $dpt);
|
my $cvalue = HMCCU_Substitute ($fvalue, $chnpars{$dpdata[0]}{sub}, 0, $chn, $dpt);
|
||||||
|
my %calcs = HMCCU_CalculateReading ($hash, $chn, $dpt);
|
||||||
|
|
||||||
my @readings = HMCCU_GetReadingName ($type_hash, $adrtoks[0], $add, $chn, $dpt,
|
my @readings = HMCCU_GetReadingName ($type_hash, $adrtoks[0], $add, $chn, $dpt,
|
||||||
$dpdata[0], $readingformat);
|
$dpdata[0], $readingformat);
|
||||||
@ -4490,6 +4602,10 @@ foreach (sChannel, sChnList.Split(",")) {
|
|||||||
HMCCU_BulkUpdate ($hash, $rn, $value, $cvalue);
|
HMCCU_BulkUpdate ($hash, $rn, $value, $cvalue);
|
||||||
$result .= $rn.'='.$cvalue."\n";
|
$result .= $rn.'='.$cvalue."\n";
|
||||||
}
|
}
|
||||||
|
foreach my $clcr (keys %calcs) {
|
||||||
|
HMCCU_BulkUpdate ($hash, $clcr, $calcs{$clcr}, $calcs{$clcr});
|
||||||
|
$result .= $clcr.'='.$calcs{$clcr}."\n";
|
||||||
|
}
|
||||||
HMCCU_BulkUpdate ($hash, 'control', $fvalue, $cvalue)
|
HMCCU_BulkUpdate ($hash, 'control', $fvalue, $cvalue)
|
||||||
if ($cd ne '' && $adrtoks[2] eq $cd && $chn eq $cc);
|
if ($cd ne '' && $adrtoks[2] eq $cd && $chn eq $cc);
|
||||||
HMCCU_BulkUpdate ($hash, 'state', $fvalue, $cvalue)
|
HMCCU_BulkUpdate ($hash, 'state', $fvalue, $cvalue)
|
||||||
@ -4502,8 +4618,8 @@ foreach (sChannel, sChnList.Split(",")) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($hmccuflags !~ /nohmstate/) {
|
if ($hmccuflags !~ /nohmstate/) {
|
||||||
my ($hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($name, undef);
|
my ($hms_read, $hms_chn, $hms_dpt, $hms_val) = HMCCU_GetHMState ($name, $ioname, undef);
|
||||||
HMCCU_BulkUpdate ($hash, 'hmstate', $hms_val, $hms_val) if (defined ($hms_val));
|
HMCCU_BulkUpdate ($hash, $hms_read, $hms_val, $hms_val) if (defined ($hms_val));
|
||||||
}
|
}
|
||||||
|
|
||||||
readingsEndUpdate ($hash, 1) if ($type ne 'HMCCU' && $ccureadings);
|
readingsEndUpdate ($hash, 1) if ($type ne 'HMCCU' && $ccureadings);
|
||||||
@ -4776,86 +4892,55 @@ sub HMCCU_QueueDeq ($)
|
|||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Determine Homematic state of device.
|
# Determine HomeMatic state considering datapoint values specified
|
||||||
#
|
# in attributes ccudef-hmstatevals and hmstatevals.
|
||||||
# Considered datapoints (sorted by priority):
|
# Return (reading, channel, datapoint, value)
|
||||||
# UNREACH, ERROR.*, FAULT_REPORTING, SABOTAGE, LOWBAT/LOW_BAT
|
|
||||||
#
|
|
||||||
# Return (channelno, datapoint, value)
|
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
sub HMCCU_GetHMState ($$)
|
sub HMCCU_GetHMState ($$$)
|
||||||
{
|
{
|
||||||
my ($name, $defval) = @_;
|
my ($name, $ioname, $defval) = @_;
|
||||||
my @hmstate = (undef, undef, $defval);
|
my @hmstate = ('hmstate', undef, undef, $defval);
|
||||||
|
|
||||||
return @hmstate if (!exists ($defs{$name}) || !exists ($defs{$name}->{TYPE}));
|
|
||||||
|
|
||||||
my $clhash = $defs{$name};
|
my $clhash = $defs{$name};
|
||||||
my $cltype = $clhash->{TYPE};
|
my $cltype = $clhash->{TYPE};
|
||||||
return @hmstate if ($cltype ne 'HMCCUDEV' && $cltype ne 'HMCCUCHN');
|
return @hmstate if ($cltype ne 'HMCCUDEV' && $cltype ne 'HMCCUCHN');
|
||||||
|
|
||||||
|
my $ghmstatevals = AttrVal ($ioname, 'ccudef-hmstatevals',
|
||||||
|
'^UNREACH!(1|true):unreachable;^LOW_?BAT!(1|true):warn_battery');
|
||||||
|
my $hmstatevals = AttrVal ($name, 'hmstatevals', $ghmstatevals);
|
||||||
|
$hmstatevals .= ";".$ghmstatevals if ($hmstatevals ne $ghmstatevals);
|
||||||
|
if ($hmstatevals =~ /^=([^;]*);/) {
|
||||||
|
$hmstate[0] = $1;
|
||||||
|
$hmstatevals =~ s/^=[^;]*;//;
|
||||||
|
}
|
||||||
|
|
||||||
# Default hmstate is equal to state
|
# Default hmstate is equal to state
|
||||||
$hmstate[2] = ReadingsVal ($name, 'state', undef) if (!defined ($defval));
|
$hmstate[3] = ReadingsVal ($name, 'state', undef) if (!defined ($defval));
|
||||||
|
|
||||||
# Check if any datapoint values are stored in device hash
|
# Substitute variables
|
||||||
return @hmstate if (!exists ($clhash->{hmccu}{dp}));
|
$hmstatevals = HMCCU_SubstVariables ($clhash, $hmstatevals);
|
||||||
|
|
||||||
# Priority of datapoints
|
my @rulelist = split (";", $hmstatevals);
|
||||||
my @dpprio = ('UNREACH', 'ERROR', 'FAULT', 'SABOTAGE', 'LOWBAT');
|
|
||||||
|
|
||||||
# Combine datapoints: LOW_BAT => LOWBAT, ERROR.* => ERROR, FAULT.* => FAULT
|
|
||||||
my $dpsub = "^LOW_BAT:LOWBAT,^ERROR_.*:ERROR,^FAULT_REPORTING:FAULT";
|
|
||||||
my %est;
|
|
||||||
my @srules = split (",", $dpsub);
|
|
||||||
|
|
||||||
# Get substitution rules for hmstate values
|
|
||||||
my $defsub = 'LOWBAT,LOW_BAT!1:battery_low;UNREACH!1:unreachable';
|
|
||||||
my $hmstatevals = AttrVal ($name, 'hmstatevals', '');
|
|
||||||
if ($hmstatevals eq '') {
|
|
||||||
my $t = HMCCU_FindDefaults ($clhash, 0);
|
|
||||||
$hmstatevals = $t->{"hmstatevals"} if (defined ($t) && exists ($t->{"hmstatevals"}));
|
|
||||||
}
|
|
||||||
my $vlsub = $hmstatevals eq '' ? $defsub : "$hmstatevals;$defsub";
|
|
||||||
|
|
||||||
# Normalize datapoints and values
|
|
||||||
foreach my $dp (keys %{$clhash->{hmccu}{dp}}) {
|
foreach my $dp (keys %{$clhash->{hmccu}{dp}}) {
|
||||||
my ($c, $d) = split (/\./, $dp);
|
next if (!defined ($clhash->{hmccu}{dp}{$dp}{VAL}));
|
||||||
my $v = $clhash->{hmccu}{dp}{$dp}{VAL};
|
my ($chn, $dpt) = split (/\./, $dp);
|
||||||
|
my $value = $clhash->{hmccu}{dp}{$dp}{VAL};
|
||||||
next if (!defined ($d));
|
|
||||||
if ($d =~ /^(UNREACH|LOWBAT|LOW_BAT|ERROR.*|FAULT_REPORTING|SABOTAGE)$/) {
|
foreach my $rule (@rulelist) {
|
||||||
my $edp = $1;
|
my ($dptexpr, $subst) = split ('!', $rule, 2);
|
||||||
|
next if (!defined ($dptexpr) || !defined ($subst));
|
||||||
# Combine datapoints
|
next if ($dpt !~ /$dptexpr/);
|
||||||
foreach my $r (@srules) {
|
$subst =~ s/\$\{value\}/$value/g;
|
||||||
my ($org, $new) = split (":", $r);
|
my ($rc, $newvalue) = HMCCU_SubstRule ($value, $subst, 0);
|
||||||
if ($edp =~ /$org/) {
|
return ($hmstate[0], $chn, $dpt, $newvalue) if ($rc);
|
||||||
$edp =~ s/$org/$new/;
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# Normalize values
|
|
||||||
$v = 1 if ($v eq 'true');
|
|
||||||
$v = 0 if ($v eq 'false');
|
|
||||||
my $n = $v;
|
|
||||||
$n = HMCCU_Substitute ($v, $vlsub, 0, undef, $d) if ($v > 0);
|
|
||||||
$est{$edp}{chn} = $c;
|
|
||||||
$est{$edp}{dpt} = $d;
|
|
||||||
$est{$edp}{val} = $v;
|
|
||||||
$est{$edp}{nvl} = $n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Return datapoints by priority
|
|
||||||
foreach my $e (@dpprio) {
|
|
||||||
if (exists ($est{$e}) && $est{$e}{val} > 0) {
|
|
||||||
return ($est{$e}{chn}, $est{$e}{dpt}, $est{$e}{nvl});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return @hmstate;
|
return @hmstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
####################################################
|
####################################################
|
||||||
# Aggregate readings. Valid operations are 'and',
|
# Aggregate readings. Valid operations are 'and',
|
||||||
@ -4949,6 +5034,60 @@ sub HMCCU_Dewpoint ($$$$)
|
|||||||
return sprintf "%.1f", $td;
|
return sprintf "%.1f", $td;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# Calculate special readings. Requires hash of
|
||||||
|
# client device and channel number and datapoint.
|
||||||
|
# Return readings.
|
||||||
|
####################################################
|
||||||
|
|
||||||
|
sub HMCCU_CalculateReading ($$$)
|
||||||
|
{
|
||||||
|
my ($cl_hash, $chnno, $dpt) = @_;
|
||||||
|
my $name = $cl_hash->{NAME};
|
||||||
|
|
||||||
|
my @result = ();
|
||||||
|
|
||||||
|
my $ccucalculate = AttrVal ($name, 'ccucalculate', '');
|
||||||
|
return @result if ($ccucalculate eq '');
|
||||||
|
|
||||||
|
my @calclist = split (';', $ccucalculate);
|
||||||
|
foreach my $calculation (@calclist) {
|
||||||
|
my ($valuetype, $reading, $datapoints) = split (':', $calculation);
|
||||||
|
next if (!defined ($reading));
|
||||||
|
|
||||||
|
my @dplist = defined ($datapoints) ? split (',', $datapoints) : ();
|
||||||
|
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};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valuetype eq 'dewpoint') {
|
||||||
|
next if (@pars < 2);
|
||||||
|
my ($tmp, $hum) = @pars;
|
||||||
|
if ($tmp >= 0.0) {
|
||||||
|
$a = 7.5;
|
||||||
|
$b = 237.3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$a = 7.6;
|
||||||
|
$b = 240.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $sdd = 6.1078*(10.0**(($a*$tmp)/($b+$tmp)));
|
||||||
|
my $dd = $hum/100.0*$sdd;
|
||||||
|
my $v = log($dd/6.1078)/log(10.0);
|
||||||
|
my $td = $b*$v/($a-$v);
|
||||||
|
|
||||||
|
push (@result, $reading, (sprintf "%.1f", $td));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return @result;
|
||||||
|
}
|
||||||
|
|
||||||
####################################################
|
####################################################
|
||||||
# Encode command string for e-paper display
|
# Encode command string for e-paper display
|
||||||
#
|
#
|
||||||
@ -5605,6 +5744,9 @@ sub HMCCU_CCURPC_GetEventsCB ($$)
|
|||||||
Example: Find devices with low batteries<br/>
|
Example: Find devices with low batteries<br/>
|
||||||
name=battery,filter:name=.*,read:(LOWBAT|LOW_BAT),if:any=yes,else:no,prefix:batt_,coll:NAME<br/>
|
name=battery,filter:name=.*,read:(LOWBAT|LOW_BAT),if:any=yes,else:no,prefix:batt_,coll:NAME<br/>
|
||||||
</li><br/>
|
</li><br/>
|
||||||
|
<li><b>ccudef-hmstatevals <subst-rule[;...]></b><br/>
|
||||||
|
Set global rules for calculation of reading hmstate.
|
||||||
|
</li><br/>
|
||||||
<li><b>ccudef-readingfilter <filter-rule[;...]></b><br/>
|
<li><b>ccudef-readingfilter <filter-rule[;...]></b><br/>
|
||||||
Set global reading/datapoint filter. This filter is added to the filter specified by
|
Set global reading/datapoint filter. This filter is added to the filter specified by
|
||||||
client device attribute 'ccureadingfilter'.
|
client device attribute 'ccureadingfilter'.
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# $Id$
|
# $Id$
|
||||||
#
|
#
|
||||||
# Version 3.8
|
# Version 3.9
|
||||||
#
|
#
|
||||||
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
||||||
#
|
#
|
||||||
@ -33,6 +33,7 @@
|
|||||||
# get <name> update
|
# get <name> update
|
||||||
#
|
#
|
||||||
# attr <name> ccuackstate { 0 | 1 }
|
# attr <name> ccuackstate { 0 | 1 }
|
||||||
|
# attr <name> ccucalculate <value>:<reading>[:<dp-list>][...]
|
||||||
# attr <name> ccuflags { altread, nochn0, trace }
|
# attr <name> ccuflags { altread, nochn0, trace }
|
||||||
# attr <name> ccuget { State | Value }
|
# attr <name> ccuget { State | Value }
|
||||||
# attr <name> ccureadings { 0 | 1 }
|
# attr <name> ccureadings { 0 | 1 }
|
||||||
@ -80,7 +81,7 @@ sub HMCCUCHN_Initialize ($)
|
|||||||
$hash->{AttrFn} = "HMCCUCHN_Attr";
|
$hash->{AttrFn} = "HMCCUCHN_Attr";
|
||||||
$hash->{parseParams} = 1;
|
$hash->{parseParams} = 1;
|
||||||
|
|
||||||
$hash->{AttrList} = "IODev ccuackstate:0,1 ccuflags:multiple-strict,altread,nochn0,trace ccureadingfilter ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ccureadingname ccureadings:0,1 ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint disable:0,1 hmstatevals statedatapoint statevals substitute:textField-long substexcl stripnumber ". $readingFnAttributes;
|
$hash->{AttrList} = "IODev ccuackstate:0,1 ccucalculate ccuflags:multiple-strict,altread,nochn0,trace ccureadingfilter ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ccureadingname ccureadings:0,1 ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint disable:0,1 hmstatevals:textField-long statedatapoint statevals substitute:textField-long substexcl stripnumber ". $readingFnAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
@ -109,7 +110,7 @@ sub HMCCUCHN_Define ($@)
|
|||||||
return "Cannot detect IO device" if (!defined ($hmccu_hash));
|
return "Cannot detect IO device" if (!defined ($hmccu_hash));
|
||||||
|
|
||||||
return "Invalid or unknown CCU channel name or address"
|
return "Invalid or unknown CCU channel name or address"
|
||||||
if (! HMCCU_IsValidDevice ($hmccu_hash, $devspec));
|
if (! HMCCU_IsValidChannel ($hmccu_hash, $devspec));
|
||||||
|
|
||||||
my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec);
|
my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec);
|
||||||
return "Invalid or unknown CCU device name or address" if (!defined ($da));
|
return "Invalid or unknown CCU device name or address" if (!defined ($da));
|
||||||
@ -760,9 +761,14 @@ sub HMCCUCHN_SetError ($$)
|
|||||||
If set to 1 state will be set to result of command (i.e. 'OK'). Otherwise state is only
|
If set to 1 state will be set to result of command (i.e. 'OK'). Otherwise state is only
|
||||||
updated if value of state datapoint has changed.
|
updated if value of state datapoint has changed.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
<li><b>ccuflags {altread, nochn0, trace}</b><br/>
|
<li><b>ccucalculate <value>:<reading>[:<dp-list>[;...]</b><br/>
|
||||||
|
Calculate special values like dewpoint based on datapoints specified in
|
||||||
|
<i>dp-list</i>. The result is stored in <i>reading</i>. The following <i>values</i>
|
||||||
|
are supported:<br/>
|
||||||
|
dewpoint = calculate dewpoint, <i>dp-list</i> = <temperature>,<humidity>
|
||||||
|
</li><br/>
|
||||||
|
<li><b>ccuflags {nochn0, trace}</b><br/>
|
||||||
Control behaviour of device:<br/>
|
Control behaviour of device:<br/>
|
||||||
altread: Create additional CUL_HM style readings.<br/>
|
|
||||||
nochn0: Prevent update of status channel 0 datapoints / readings.<br/>
|
nochn0: Prevent update of status channel 0 datapoints / readings.<br/>
|
||||||
trace: Write log file information for operations related to this device.
|
trace: Write log file information for operations related to this device.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
@ -842,11 +848,25 @@ sub HMCCUCHN_SetError ($$)
|
|||||||
Disable client device.
|
Disable client device.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
<li><b>hmstatevals <subst-rule>[;...]</b><br/>
|
<li><b>hmstatevals <subst-rule>[;...]</b><br/>
|
||||||
Define substitution rules for datapoint values stored in reading 'hmstate'. Parameter
|
Define building rules and substitutions for reading hmstate. Syntax of <i>subst-rule</i>
|
||||||
<i>subst-rule</i> is equal to attribute 'substitute'. Default substitution rule is
|
is<br/>
|
||||||
LOWBAT,LOW_BAT!1:battery_low;UNREACH!1:unreachable. The substitution rule must
|
[=<reading>;]<datapoint-expr>!<{#n1-m1|regexp}>:<text>[,...]
|
||||||
contain only error conditions because 'hmstate' contains only error conditions or
|
<br/><br/>
|
||||||
the 'state' value.
|
The syntax is almost the same as of attribute 'substitute', except there's no channel
|
||||||
|
specification possible for datapoint and parameter <i>datapoint-expr</i> is a regular
|
||||||
|
expression.<br/>
|
||||||
|
The value of the I/O device attribute 'ccudef-hmstatevals' is appended to the value of
|
||||||
|
this attribute. The default value of 'ccudef-hmstatevals' is
|
||||||
|
'^UNREACH!(1|true):unreachable;LOW_?BAT!(1|true):warn_battery'.
|
||||||
|
Normally one should not specify a substitution rule for the "good" value of an error
|
||||||
|
datapoint (i.e. 0 for UNREACH). If none of the rules is matching, reading 'hmstate' is set
|
||||||
|
to value of reading 'state'.<br/>
|
||||||
|
Parameter <i>text</i> can contain variables in format ${<i>varname</i>}. The variable
|
||||||
|
$value is substituted by the original datapoint value. All other variables must match
|
||||||
|
with a valid datapoint name or a combination of channel number and datapoint name
|
||||||
|
seperated by a '.'.<br/>
|
||||||
|
Optionally the name of the HomeMatic state reading can be specified at the beginning of
|
||||||
|
the attribute in format =<reading>;. The default reading name is 'hmstate'.
|
||||||
</li><br/>
|
</li><br/>
|
||||||
<li><b>statedatapoint <datapoint></b><br/>
|
<li><b>statedatapoint <datapoint></b><br/>
|
||||||
Set state datapoint used by some commands like 'set devstate'.
|
Set state datapoint used by some commands like 'set devstate'.
|
||||||
@ -877,8 +897,17 @@ sub HMCCUCHN_SetError ($$)
|
|||||||
</li><br/>
|
</li><br/>
|
||||||
<li><b>substitute <subst-rule>[;...]</b><br/>
|
<li><b>substitute <subst-rule>[;...]</b><br/>
|
||||||
Define substitutions for datapoint/reading values. Syntax of <i>subst-rule</i> is<br/><br/>
|
Define substitutions for datapoint/reading values. Syntax of <i>subst-rule</i> is<br/><br/>
|
||||||
[[<channelno.>]<datapoint>[,...]!]<{#n1-m1|regexp1}>:<text1>[,...]
|
[[<channelno.>]<datapoint>[,...]!]<{#n1-m1|regexp}>:<text>[,...]
|
||||||
<br/>
|
<br/>
|
||||||
|
Parameter text can contain variables in format ${<i>varname</i>}. The variable $value is
|
||||||
|
substituted by the original datapoint value. All other variables must match with a valid
|
||||||
|
datapoint name or a combination of channel number and datapoint name seperated by a '.'.
|
||||||
|
<br/><br/>
|
||||||
|
Example: Substitute the value of datapoint TEMPERATURE by the string
|
||||||
|
'T=<i>val</i> deg' and append current value of datapoint 1.HUMIDITY<br/>
|
||||||
|
<code>
|
||||||
|
attr my_weather substitute TEMPERATURE!.+:T=${value} deg H=${1.HUMIDITY}%
|
||||||
|
</code>
|
||||||
If rule expression starts with a hash sign a numeric datapoint value is substituted if
|
If rule expression starts with a hash sign a numeric datapoint value is substituted if
|
||||||
it fits in the number range n <= value <= m.
|
it fits in the number range n <= value <= m.
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# $Id$
|
# $Id$
|
||||||
#
|
#
|
||||||
# Version 3.8
|
# Version 3.9
|
||||||
#
|
#
|
||||||
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
# (c) 2016 zap (zap01 <at> t-online <dot> de)
|
||||||
#
|
#
|
||||||
@ -34,6 +34,7 @@
|
|||||||
# get <name> update
|
# get <name> update
|
||||||
#
|
#
|
||||||
# attr <name> ccuackstate { 0 | 1 }
|
# attr <name> ccuackstate { 0 | 1 }
|
||||||
|
# attr <name> ccucalculate <value>:<reading>[:<dp-list>][...]
|
||||||
# attr <name> ccuflags { altread, nochn0, trace }
|
# attr <name> ccuflags { altread, nochn0, trace }
|
||||||
# attr <name> ccuget { State | Value }
|
# attr <name> ccuget { State | Value }
|
||||||
# attr <name> ccureadings { 0 | 1 }
|
# attr <name> ccureadings { 0 | 1 }
|
||||||
@ -83,7 +84,7 @@ sub HMCCUDEV_Initialize ($)
|
|||||||
$hash->{AttrFn} = "HMCCUDEV_Attr";
|
$hash->{AttrFn} = "HMCCUDEV_Attr";
|
||||||
$hash->{parseParams} = 1;
|
$hash->{parseParams} = 1;
|
||||||
|
|
||||||
$hash->{AttrList} = "IODev ccuackstate:0,1 ccuflags:multiple-strict,altread,nochn0,trace ccureadingfilter:textField-long ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ccureadingname ccureadings:0,1 ccuget:State,Value ccuscaleval ccuverify:0,1,2 disable:0,1 hmstatevals statevals substexcl substitute:textField-long statechannel statedatapoint controldatapoint stripnumber ". $readingFnAttributes;
|
$hash->{AttrList} = "IODev ccuackstate:0,1 ccucalculate ccuflags:multiple-strict,altread,nochn0,trace ccureadingfilter:textField-long ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ccureadingname 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 ". $readingFnAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
@ -134,6 +135,9 @@ sub HMCCUDEV_Define ($@)
|
|||||||
$hmccu_hash = HMCCU_FindIODevice ($devspec) if (!defined ($hmccu_hash));
|
$hmccu_hash = HMCCU_FindIODevice ($devspec) if (!defined ($hmccu_hash));
|
||||||
return "Cannot detect IO device" if (!defined ($hmccu_hash));
|
return "Cannot detect IO device" if (!defined ($hmccu_hash));
|
||||||
|
|
||||||
|
return "Invalid or unknown CCU device name or address"
|
||||||
|
if (! HMCCU_IsValidDevice ($hmccu_hash, $devspec));
|
||||||
|
|
||||||
my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec);
|
my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec);
|
||||||
return "Invalid or unknown CCU device name or address: $devspec" if (!defined ($da));
|
return "Invalid or unknown CCU device name or address: $devspec" if (!defined ($da));
|
||||||
|
|
||||||
@ -886,7 +890,14 @@ sub HMCCUDEV_Get ($@)
|
|||||||
<li><b>ccuackstate {<u>0</u> | 1}</b><br/>
|
<li><b>ccuackstate {<u>0</u> | 1}</b><br/>
|
||||||
<a href="#HMCCUCHNattr">see HMCCUCHN</a>
|
<a href="#HMCCUCHNattr">see HMCCUCHN</a>
|
||||||
</li><br/>
|
</li><br/>
|
||||||
<li><b>ccuflags {altread, nochn0, trace}</b><br/>
|
<li><b>ccucalculate <value>:<reading>[:<dp-list>[;...]</b><br/>
|
||||||
|
Calculate special values like dewpoint based on datapoints specified in
|
||||||
|
<i>dp-list</i>. Datapoints in <i>dp-list</i> must be specified in format
|
||||||
|
<channelno>.<datapoint>. The result is stored in <i>reading</i>.
|
||||||
|
The following <i>values</i> are supported:<br/>
|
||||||
|
dewpoint = calculate dewpoint, <i>dp-list</i> = <temperature>,<humidity>
|
||||||
|
</li><br/>
|
||||||
|
<li><b>ccuflags {nochn0, trace}</b><br/>
|
||||||
<a href="#HMCCUCHNattr">see HMCCUCHN</a>
|
<a href="#HMCCUCHNattr">see HMCCUCHN</a>
|
||||||
</li><br/>
|
</li><br/>
|
||||||
<li><b>ccuget {State | <u>Value</u>}</b><br/>
|
<li><b>ccuget {State | <u>Value</u>}</b><br/>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# $Id$
|
# $Id$
|
||||||
#
|
#
|
||||||
# Version 3.8
|
# Version 3.9
|
||||||
#
|
#
|
||||||
# Configuration parameters for Homematic devices.
|
# Configuration parameters for Homematic devices.
|
||||||
#
|
#
|
||||||
@ -181,6 +181,13 @@ use vars qw(%HMCCU_DEV_DEFAULTS);
|
|||||||
statedatapoint => "MOTION",
|
statedatapoint => "MOTION",
|
||||||
substitute => "MOTION!(0|false):no,(1|true):yes;ERROR!0:no,1:sabotage"
|
substitute => "MOTION!(0|false):no,(1|true):yes;ERROR!0:no,1:sabotage"
|
||||||
},
|
},
|
||||||
|
"HmIP-SMI" => {
|
||||||
|
_description => "Bewegungsmelder",
|
||||||
|
_channels => "1",
|
||||||
|
ccureadingfilter => "(ILLUMINATION|MOTION)",
|
||||||
|
statedatapoint => "MOTION",
|
||||||
|
substitute => "MOTION!(0|false):no,(1|true):yes"
|
||||||
|
},
|
||||||
"HM-Sen-LI-O" => {
|
"HM-Sen-LI-O" => {
|
||||||
_description => "Lichtsensor",
|
_description => "Lichtsensor",
|
||||||
_channels => "1",
|
_channels => "1",
|
||||||
@ -248,7 +255,7 @@ use vars qw(%HMCCU_DEV_DEFAULTS);
|
|||||||
%HMCCU_DEV_DEFAULTS = (
|
%HMCCU_DEV_DEFAULTS = (
|
||||||
"CCU2" => {
|
"CCU2" => {
|
||||||
_description => "HomeMatic CCU2",
|
_description => "HomeMatic CCU2",
|
||||||
"ccudef-readingfilter" => '.*',
|
"ccudef-readingfilter" => '^(LOW_?BAT|UNREACH)$',
|
||||||
"ccudef-readingformat" => 'datapoint',
|
"ccudef-readingformat" => 'datapoint',
|
||||||
"ccudef-readingname" => '^(.+\.)?AES_KEY$:sign;^(.+\.)?LOW_?BAT$:battery;^(.+\.)?BATTERY_STATE$:batteryLevel;^(.+\.)?UNREACH$:Activity;^(.+\.)?TEMPERATURE$:+temperature;^(.+\.)?SET_TEMPERATURE$:+desired-temp;^(.+\.)?HUMIDITY$:+humidity;^(.+\.)?LEVEL$:+pct;^(.+\.)?CONTROL_MODE$:+controlMode',
|
"ccudef-readingname" => '^(.+\.)?AES_KEY$:sign;^(.+\.)?LOW_?BAT$:battery;^(.+\.)?BATTERY_STATE$:batteryLevel;^(.+\.)?UNREACH$:Activity;^(.+\.)?TEMPERATURE$:+temperature;^(.+\.)?SET_TEMPERATURE$:+desired-temp;^(.+\.)?HUMIDITY$:+humidity;^(.+\.)?LEVEL$:+pct;^(.+\.)?CONTROL_MODE$:+controlMode',
|
||||||
"ccudef-substitute" => 'AES_KEY!(0|false):off,(1|true):on;LOWBAT,LOW_BAT!(0|false):ok,(1|true):low;UNREACH!(0|false):alive,(1|true):dead;MOTION!(0|false):noMotion,(1|true):motion;DIRECTION!0:stop,1:up,2:down,3:undefined;WORKING!0:false,1:true;INHIBIT!(0|false):unlocked,(1|true):locked'
|
"ccudef-substitute" => 'AES_KEY!(0|false):off,(1|true):on;LOWBAT,LOW_BAT!(0|false):ok,(1|true):low;UNREACH!(0|false):alive,(1|true):dead;MOTION!(0|false):noMotion,(1|true):motion;DIRECTION!0:stop,1:up,2:down,3:undefined;WORKING!0:false,1:true;INHIBIT!(0|false):unlocked,(1|true):locked'
|
||||||
@ -487,6 +494,12 @@ use vars qw(%HMCCU_DEV_DEFAULTS);
|
|||||||
statedatapoint => "1.MOTION",
|
statedatapoint => "1.MOTION",
|
||||||
substitute => "MOTION!(0|false):no,(1|true):yes;ERROR!0:no,1:sabotage"
|
substitute => "MOTION!(0|false):no,(1|true):yes;ERROR!0:no,1:sabotage"
|
||||||
},
|
},
|
||||||
|
"HmIP-SMI" => {
|
||||||
|
_description => "Bewegungsmelder",
|
||||||
|
ccureadingfilter => "(ILLUMINATION|MOTION)",
|
||||||
|
statedatapoint => "1.MOTION",
|
||||||
|
substitute => "MOTION!(0|false):no,(1|true):yes"
|
||||||
|
},
|
||||||
"HM-Sen-LI-O" => {
|
"HM-Sen-LI-O" => {
|
||||||
_description => "Lichtsensor",
|
_description => "Lichtsensor",
|
||||||
ccureadingfilter => "LUX",
|
ccureadingfilter => "LUX",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user