# $Id$
##############################################################################
#
# 98_structure.pm
# Copyright by
# e-mail:
#
# This file is part of fhem.
#
# Fhem is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Fhem is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with fhem. If not, see .
#
##############################################################################
package main;
use strict;
use warnings;
#use Data::Dumper;
sub structure_getChangedDevice($);
#####################################
sub
structure_Initialize($)
{
my ($hash) = @_;
$hash->{DefFn} = "structure_Define";
$hash->{UndefFn} = "structure_Undef";
$hash->{NotifyFn} = "structure_Notify";
$hash->{SetFn} = "structure_Set";
$hash->{AttrFn} = "structure_Attr";
no warnings 'qw';
my @attrList = qw(
async_delay
clientstate_behavior:relative,relativeKnown,absolute,last
clientstate_priority
considerDisabledMembers
disable
disabledForIntervals
evaluateSetResult:1,0
filterEvents:1,0
propagateAttr
setStateIndirectly:1,0
setStructType:0,1
);
use warnings 'qw';
$hash->{AttrList} = join(" ", @attrList)." $readingFnAttributes";
my %ahash = ( Fn=>"CommandAddStruct",
Hlp=>" ,add to " );
$cmds{addstruct} = \%ahash;
my %dhash = ( Fn=>"CommandDelStruct",
Hlp=>" ,delete from ");
$cmds{delstruct} = \%dhash;
}
sub structAdd($$);
sub
structAdd($$)
{
my ($d, $attrList) = @_;
return if(!$defs{$d});
$defs{$d}{INstructAdd} = 1;
foreach my $c (@{$defs{$d}{".memberList"}}) {
if($defs{$c} && $defs{$c}{INstructAdd}) {
Log 1, "recursive structure definition"
} else {
addToDevAttrList($c, $attrList, 'structure');
structAdd($c, $attrList) if($defs{$c} && $defs{$c}{TYPE} eq "structure");
}
}
delete $defs{$d}{INstructAdd} if($defs{$d});
}
sub structure_setDevs($;$);
#############################
sub
structure_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
my $u = "wrong syntax: define structure [device ...]";
return $u if(int(@a) < 4);
my $devname = shift(@a);
my $modname = shift(@a);
my $stype = shift(@a);
$hash->{ATTR} = $stype;
$hash->{CHANGEDCNT} = 0;
$hash->{".asyncQueue"} = [];
structure_setDevs($hash, $def); # needed by set while init is running
InternalTimer(1, sub { # repeat it for devices defined later
structure_setDevs($hash, $def);
structure_Attr("set", $devname, $stype, $devname)
if(AttrVal($devname, "setStructType", $featurelevel <= 5.8));
}, undef, 0);
return undef;
}
sub
structure_setDevs($;$)
{
my ($hash, $def) = @_;
$def = "$hash->{NAME} structure $hash->{DEF}" if(!$def);
my $c = $hash->{".memberHash"};
my @a = split(/\s+/, $def);
my $devname = shift(@a);
my $modname = shift(@a);
my $stype = shift(@a);
my (%list, @list);
my $aList = "$stype ${stype}_map structexclude";
foreach my $a (@a) {
foreach my $d (devspec2array($a)) {
next if(!$defs{$d} || $list{$d});
$hash->{DEVSPECDEF} = 1 if($a ne $d);
$list{$d} = 1;
push(@list, $d);
next if($c && $c->{$d});
addToDevAttrList($d, $aList, 'structure');
structAdd($d, $aList) if($defs{$d} && $defs{$d}{TYPE} eq "structure");
}
}
$hash->{".memberHash"} = \%list;
$hash->{".memberList"} = \@list;
delete $hash->{".cachedHelp"};
notifyRegexpChanged($hash, 'global|'.join('|', @list));
}
#############################
sub
structure_Undef($$)
{
my ($hash, $def) = @_;
my @a = ( "del", $hash->{NAME}, $hash->{ATTR} );
structure_Attr(@a);
return undef;
}
#############################
# returns the really changed Device
#############################
sub
structure_getChangedDevice($)
{
my ($dev) = @_;
my $lastDevice = ReadingsVal($dev, "LastDevice", undef);
$dev = structure_getChangedDevice($lastDevice)
if($lastDevice && $defs{$dev}->{TYPE} eq "structure");
return $dev;
}
#############################
sub
structure_Notify($$)
{
my ($hash, $dev) = @_;
my $me = $hash->{NAME};
my $devmap = $hash->{ATTR}."_map";
my $devName = $dev->{NAME};
if($devName eq "global") {
my $max = int(@{$dev->{CHANGED}});
for (my $i = 0; $i < $max; $i++) {
my $s = $dev->{CHANGED}[$i];
$s = "" if(!defined($s));
if($s =~ m/^RENAMED ([^ ]*) ([^ ]*)$/) {
my ($old, $new) = ($1, $2);
if($hash->{".memberHash"}{$old}) {
$hash->{DEF} =~ s/\b$old\b/$new/;
structure_setDevs($hash);
}
} elsif($s =~ m/^DELETED ([^ ]*)$/) {
my $n = $1;
if($hash->{".memberHash"}{$n}) {
$hash->{DEF} =~ s/\b$n\b//;
structure_setDevs($hash)
}
} elsif($s =~ m/^DEFINED ([^ ]*)$/) {
structure_setDevs($hash) if($hash->{NAME} ne $1 && $hash->{DEVSPECDEF});
}
}
return;
}
return "" if(IsDisabled($me));
return "" if (! exists $hash->{".memberHash"}->{$devName});
if(AttrVal($me, "filterEvents", 0) &&
AttrVal($devName, $devmap, 0)) { #131841
my %re;
map {
my @val = split(":",$_);
$re{$val[0]} = 1 if(@val==1 || @val==3);
} attrSplit($attr{$devName}{$devmap});
my @re = keys %re;
my $fnd;
map {
my $e = $_; map { $fnd = 1 if($e =~ m/^$_:/) } @re;
} @{deviceEvents($dev, 0)};
return "" if(!$fnd);
}
my $behavior = AttrVal($me, "clientstate_behavior", "absolute");
my %clientstate;
my @structPrio = attrSplit($attr{$me}{clientstate_priority})
if($attr{$me} && $attr{$me}{clientstate_priority});
return "" if($hash->{INSET} && !AttrVal($me, "evaluateSetResult", 0));
return "" if(@{$hash->{".asyncQueue"}}); # Do not trigger during async set
if($hash->{INNTFY}) {
Log3 $me, 1, "ERROR: endless loop detected in structure_Notify $me";
return "";
}
$hash->{INNTFY} = 1;
my %priority;
my (@priority, @foo);
for (my $i=0; $i<@structPrio; $i++) {
@foo = split(/\|/, $structPrio[$i]);
for (my $j=0; $j<@foo;$j++) {
$priority{$foo[$j]} = $i+1;
$priority[$i+1]=$foo[0];
}
}
my $minprio = 99999;
my $devstate;
my $stateCause="";
my $cdm = AttrVal($me, "considerDisabledMembers", undef);
foreach my $d (sort keys %{ $hash->{".memberHash"} }) {
next if(!$defs{$d} || (!$cdm && IsDisabled($d)));
if($attr{$d} && $attr{$d}{$devmap}) {
my @gruppe = attrSplit($attr{$d}{$devmap});
my @value;
for (my $i=0; $i<@gruppe; $i++) {
@value = split(":", $gruppe[$i]);
if(@value == 1) { # value[0]:.* -> .*
$devstate = ReadingsVal($d, $value[0], undef);
} elsif(@value == 2) { # state:value[0] -> value[1]
$devstate = ReadingsVal($d, "state", undef);
$devstate = $defs{$d}{STATE} if(!defined($devstate));
if(defined($devstate) && $devstate =~ m/^$value[0]/){
$devstate = $value[1];
$i=99999; # RKO: ??
} else {
$devstate = undef;
}
} elsif(@value == 3) { # value[0]:value[1] -> value[2]
$devstate = ReadingsVal($d, $value[0], undef);
if(defined($devstate) && $devstate =~ m/^$value[1]/){
$devstate = $value[2];
} else {
$devstate = undef;
}
}
if(defined($devstate)) {
if(!$priority{$devstate} && $behavior eq "relativeKnown") {
delete($hash->{INNTFY});
return "";
}
if($priority{$devstate} && $priority{$devstate} < $minprio) {
$minprio = $priority{$devstate};
$stateCause = $d;
}
$clientstate{$devstate} = 1;
}
}
} else {
$devstate = ReadingsVal($d, "state", undef);
$devstate = $defs{$d}{STATE} if(!defined($devstate));
if(defined($devstate)) {
if(!$priority{$devstate} && $behavior eq "relativeKnown") {
delete($hash->{INNTFY});
return "";
}
if($priority{$devstate} && $priority{$devstate} < $minprio) {
$minprio = $priority{$devstate};
$stateCause = $d;
}
$clientstate{$devstate} = 1;
}
}
$hash->{".memberHash"}{$d} = $devstate;
}
$devstate = ReadingsVal($devName, AttrVal($devName,$devmap,"state"),undef);
$devstate = $defs{$devName}{STATE} if(!defined($devstate));
$devstate = "undefined" if(!defined($devstate));
my $newState = "undefined";
if($behavior eq "absolute"){
my @cKeys = keys %clientstate;
$newState = (@cKeys == 1 ? $cKeys[0] : "undefined");
$stateCause = "different states: ".join(",", @cKeys);
} elsif($behavior =~ "^relative" && $minprio < 99999) {
$newState = $priority[$minprio];
} elsif($behavior eq "last"){
$newState = $devstate;
}
my $dStr = "structure $me: event from $devName: setting state to $newState";
$dStr .= ", cause $stateCause" if($newState ne $devstate);
Log3 $me, 5, $dStr;
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "LastDevice", $devName, 0);
readingsBulkUpdate($hash, "LastDevice_Abs",
structure_getChangedDevice($devName), 0);
readingsBulkUpdate($hash, "state", $newState);
readingsEndUpdate($hash, 1);
$hash->{CHANGEDCNT}++;
delete($hash->{INNTFY});
undef;
}
#####################################
sub
CommandAddStruct($)
{
my ($cl, $param) = @_;
my @a = split(" ", $param);
if(int(@a) != 2) {
return "Usage: addstruct ";
}
my $name = shift(@a);
my $hash = $defs{$name};
if(!$hash || $hash->{TYPE} ne "structure") {
return "$a is not a structure device";
}
foreach my $d (devspec2array($a[0])) {
$hash->{DEF} .= " $d";
CommandAttr($cl, "$d $hash->{ATTR} $hash->{NAME}");
}
addStructChange("addstruct", $name, $param);
structure_setDevs($hash);
return undef;
}
#####################################
sub
CommandDelStruct($)
{
my ($cl, $param) = @_;
my @a = split(" ", $param);
if(int(@a) != 2) {
return "Usage: delstruct ";
}
my $name = shift(@a);
my $hash = $defs{$name};
if(!$hash || $hash->{TYPE} ne "structure") {
return "$a is not a structure device";
}
foreach my $d (devspec2array($a[0])) {
$hash->{DEF} =~ s/\b$d\b//g;
CommandDeleteAttr($cl, "$d $hash->{ATTR}");
}
$hash->{DEF} =~ s/ / /g;
addStructChange("delstruct", $name, $param);
structure_setDevs($hash);
return undef;
}
###################################
sub
structure_Set($@)
{
my ($hash, @list) = @_;
my $me = $hash->{NAME};
my $ret = "";
my %pars;
# see Forum # 28623 for .cachedHelp
if(@list > 1 && $list[1] eq "?") {
return $hash->{".cachedHelp"} if($hash->{".cachedHelp"});
} elsif(IsDisabled($me) =~ m/1|2/) {
return undef;
}
my @devList = @{$hash->{".memberList"}};
if(@list > 1 && $list[$#list] eq "reverse") {
pop @list;
@devList = reverse @devList;
}
if($list[1] =~ m/^(save|restore)StructState$/) {
return "Usage: set $me $list[1] readingName" if(@list != 3);
return "Bad reading name $list[2]" if(!goodReadingName($list[2]));
if($1 eq "save") {
readingsSingleUpdate($hash, $list[2],
join(",", map { ReadingsVal($_,"state","on") } @devList), 1);
return;
}
my @sl = split(",", ReadingsVal($me, $list[2], ""));
for(my $i1=0; $i1<@devList && $i1<@sl; $i1++) {
AnalyzeCommand($hash->{CL}, "set $devList[$i1] $sl[$i1]");
}
return;
}
$hash->{INSET} = 1;
my $startAsyncProcessing;
my $filter;
if($list[1] ne "?") {
my $state = join(" ", @list[1..@list-1]);
readingsSingleUpdate($hash, "state", $state, 1)
if(!AttrVal($me, "setStateIndirectly", undef));
if($state =~ /^\[(FILTER=.*)]/) {
delete($hash->{INSET}); # Forum #35382
$filter = $1;
@list = split(" ", $list[0] ." ". substr($state, length($filter)+2));
}
}
foreach my $d (@devList) {
next if(!$defs{$d});
if($defs{$d}{INSET}) {
Log3 $hash, 1, "ERROR: endless loop detected for $d in $me";
next;
}
if($attr{$d} && $attr{$d}{structexclude}) {
my $se = $attr{$d}{structexclude};
next if($me =~ m/$se/);
}
my $dl0 = $defs{$d};
my $is_structure = defined($dl0) && $dl0->{TYPE} eq "structure";
my $async_delay = AttrVal($me, "async_delay", undef);
my $cmd;
if(!$filter) {
$cmd = "set $d ". join(" ", @list[1..@list-1]);
} elsif( $is_structure ) {
$cmd = "set $d [$filter] ". join(" ", @list[1..@list-1]);
} else {
$cmd = "set $d:$filter ". join(" ", @list[1..@list-1]);
}
if(defined($async_delay) && $list[1] ne "?") {
$startAsyncProcessing = $async_delay if(!@{$hash->{".asyncQueue"}});
push @{$hash->{".asyncQueue"}}, $cmd;
} else {
my ($ostate,$ocnt) = ($dl0->{STATE}, $dl0->{CHANGEDCNT});
my $sret = AnalyzeCommand(undef, $cmd);
if($is_structure && $dl0->{CHANGEDCNT} == $ocnt) { # Forum #70488
$dl0->{STATE} = $dl0->{READINGS}{state}{VAL} = $ostate;
structure_Notify($hash, $dl0);
}
if($sret) {
$ret .= "\n" if($ret);
$ret .= $sret;
}
if($list[1] eq "?") {
if(!defined($sret)) {
Log 1, "$me: 'set $d ?' returned undef";
} else {
$sret =~ s/.*one of //;
map { $pars{$_} = 1 } split(" ", $sret);
}
}
}
}
delete($hash->{INSET});
Log3 $hash, 5, "SET: $ret" if($ret);
if(defined($startAsyncProcessing)) {
InternalTimer(gettimeofday(), "structure_asyncQueue", $hash, 0);
}
return $ret if($list[1] ne "?");
$hash->{".cachedHelp"} = "Unknown argument ?, choose one of " .
join(" ", sort keys(%pars)).
" saveStructState restoreStructState";
return $hash->{".cachedHelp"};
}
sub
structure_asyncQueue(@)
{
my ($hash) = @_;
my $next_cmd = shift @{$hash->{".asyncQueue"}};
if(defined $next_cmd) {
AnalyzeCommand(undef, $next_cmd);
my $async_delay = AttrVal($hash->{NAME}, "async_delay", 0);
InternalTimer(gettimeofday()+$async_delay,"structure_asyncQueue",$hash,0);
}
return undef;
}
###################################
sub
structure_Attr($@)
{
my ($type, @list) = @_;
my %ignore = (
alias=>1,
async_delay=>1,
clientstate_behavior=>1,
clientstate_priority=>1,
devStateIcon=>1,
disable=>1,
disabledForIntervals=>1,
group=>1,
icon=>1,
room=>1,
propagateAttr=>1,
setStateIndirectly=>1,
stateFormat=>1,
webCmd=>1,
userattr=>1
);
return undef if(($ignore{$list[1]} && $featurelevel <= 5.9) || !$init_done);
my $me = $list[0];
my $hash = $defs{$me};
my $pa = AttrVal($me, "propagateAttr", $featurelevel <= 5.9 ? '.*' : '^$');
return undef if($list[1] !~ m/$pa/);
if($hash->{INATTR}) {
Log3 $me, 1, "ERROR: endless loop detected in structure_Attr for $me";
return;
}
$hash->{INATTR} = 1;
my $ret = "";
my @devList = @{$hash->{".memberList"}};
foreach my $d (@devList) {
next if(!$defs{$d});
if($attr{$d} && $attr{$d}{structexclude}) {
my $se = $attr{$d}{structexclude};
next if("$me:$list[1]" =~ m/$se/);
}
$list[0] = $d;
my $sret;
if($type eq "del") {
$sret .= CommandDeleteAttr(undef, join(" ", @list));
} else {
$sret .= CommandAttr(undef, join(" ", @list));
}
if($sret) {
$ret .= "\n" if($ret);
$ret .= $sret;
}
}
delete($hash->{INATTR});
Log3 $me, 4, "Stucture attr $type: $ret" if($ret);
return undef;
}
1;
=pod
=item helper
=item summary organize/structure multiple devices
=item summary_DE mehrere Geräte zu einem zusammenfassen
=begin html
structure
Define
define <name> structure <struct_type> <dev1> <dev2> ...
The structure device is used to organize/structure devices in order to
set groups of them at once (e.g. switching everything off in a house).
The list of attached devices can be modified through the addstruct /
delstruct commands. Each attached device will get the attribute
<struct_type>=<name>
when it is added to the list, and the
attribute will be deleted if the device is deleted from the structure.
The structure devices can also be added to a structure, e.g. you can have
a building consisting of levels which consists of rooms of devices.
Example:
- define kitchen structure room lamp1 lamp2
- addstruct kitchen TYPE=FS20
- delstruct kitchen lamp1
- define house structure building kitchen living
- set house off
Set
- saveStructState <readingName>
The state reading of all members is stored comma separated in the
specified readingName.
- restoreStructState <readingName>
The state of all members will be restored from readingName by calling
"set memberName storedStateValue".
Every other set command is propagated to the attached devices. Exception:
if an
attached device has an attribute structexclude, and the attribute value
matches (as a regexp) the name of the current structure.
If the set is of the form set <structure>
[FILTER=<filter>] <type-specific>
then
:FILTER=<filter> will be appended to the device name in the
propagated set for the attached devices like this: set
<devN>:FILTER=<filter> <type-specific>
If the last set parameter is "reverse", then execute the set commands in
the reverse order.
Get
get is not supported through a structure device.
Attributes
- async_delay
If this attribute is defined, unfiltered set commands will not be
executed in the clients immediately. Instead, they are added to a queue
to be executed later. The set command returns immediately, whereas the
clients will be set timer-driven, one at a time. The delay between two
timercalls is given by the value of async_delay (in seconds) and may be
0 for fastest possible execution. This way, excessive delays often
known from large structures, can be broken down in smaller junks.
- disable
- disabledForIntervals
- considerDisabledMembers
if set, consider disabled members when computing the overall state of
the structure. If not set or set to 0, disabled members are ignored.
- clientstate_behavior
The backward propagated status change from the devices to this structure
works in two different ways.
- absolute
The structure status will changed to the common device status of all
defined devices to this structure if all devices are identical.
Otherwise the structure status is "undefined".
- relative
See below for clientstate_priority.
- relativeKnown
Like relative, but do not trigger on events not described in
clientstate_priority. Needed e.g. for HomeMatic devices.
- last
The structure state corresponds to the state of the device last
changed.
- clientstate_priority
If clientstate_behavior is set to relative, then you have to set the
attribute "clientstate_priority" with all states of the defined devices
to this structure in descending order. Each group is delemited by
space or /. Each entry of one group is delimited by "pipe". The status
represented by the structure is the first entry of each group.
Example:
- attr kitchen clientstate_behavior relative
- attr kitchen clientstate_priority An|On|on Aus|Off|off
- attr house clientstate_priority Any_On|An All_Off|Aus
In this example the status of kitchen is either on or off. The status
of house is either Any_on or All_off.
- <struct_type>_map
With this attribute, which has to specified for the structure-
member, you can redefine the value reported by a specific
structure-member for the structure value. The attribute has three
variants:
- readingName
take the value from readingName instead of state.
- oldVal:newVal
if the state reading matches oldVal, then replace it with newVal
- readingName:oldVal:newVal
if readingName matches oldVal, then replace it with newVal
Example:
- define door OWSWITCH <ROMID>
- define lamp1 dummy
- attr lamp1 cmdlist on off
- define kitchen structure struct_kitchen lamp1 door
- attr kitchen clientstate_priority An|on OK|Aus|off
- attr lamp1 struct_kitchen_map on:An off:Aus
- attr door struct_kitchen_map A:open:on A:closed:off
- attr door2 struct_kitchen_map A
If this attribute is not set, the member devices state reading is taken,
or, if also absent, the STATE internal.
- evaluateSetResult
if a set command sets the state of the structure members to something
different from the set command (like set statusRequest), then you have to
set this attribute to 1 in order to enable the structure instance to
compute the new status.
- filterEvents
if set, and the device triggering the event has a struct_type map, the
only events (i.e. reading names) contained in the structure map will
trigger the structure. Note: only the readingName and
readingName:oldVal:newVal entries of the struct_type map are considered.
- propagateAttr <regexp>
if the regexp matches the name of the attribute, then this attribute will
be propagated to all the members. The default is .* (each attribute) for
featurelevel <= 5.9, else ^$ (no attribute).
Note: the following attibutes were never propagated for featurelevel<=5.9
alias async_delay clientstate_behavior clientstate_priority
devStateIcon disable disabledForIntervals group icon room propagateAttr
setStateIndirectly stateFormat webCmd userattr
- setStateIndirectly
If true (1), set the state only when member devices report a state
change, else the state is first set to the set command argument. Default
is 0.
- setStructType
If true (1), then the <struct-type> will be set as an attribute for
each member device to the name of the structure. True is the default for
featurelevel <= 5.8.
- structexclude
Note: this is an attribute for the member device, not for the struct
itself.
Exclude the device from set/notify or attribute operations. For the set
and notify the value of structexclude must match the structure name,
for the attr/deleteattr commands ist must match the combination of
structure_name:attribute_name. Examples:
define kitchen structure room lamp1 lamp2
attr lamp1 structexclude kitchen
attr lamp1 structexclude kitchen:stateFormat
- readingFnAttributes
=end html
=begin html_DE
structure
Define
define <name> structure <struct_type> <dev1>
<dev2> ...
Mit dem Device "Structure" werden Strukturen/Zusammenstellungen von anderen
Devices erstellt um sie zu Gruppen zusammenzufassen. (Beispiel: im Haus
alles ausschalten)
Die Liste der Devices die einer Struktur zugeordnet sind kann duch das
Kommando addstruct / delstruct
im laufenden Betrieb
verändert werden. Es können sowohl einzelne Devices als auch
Gruppen von Devices (TYPE=FS20) zugefügt werden. Jedes zugefügt
Device erhält zwei neue Attribute <struct_type>=<name>
sowie <struct_type>_map wenn es zu einer Struktur zugefügt
wurde. Diese Attribute werden wieder automatisch entfernt, sobald das
Device von der Struktur entfernt wird.
Eine Struktur kann ebenfalls zu einer anderen Struktur zugefügt
werden. Somit können z b. kaskadierende Strukturen erstellt werden.
(Z.b. KG,EG,OG, Haus)
Beispiel:
- define Kueche structure room lampe1 lampe2
- addstruct Kueche TYPE=FS20
- delstruct Kueche lampe1
- define house structure building kitchen living
- set house off
Set
- saveStructState <readingName>
Der Status (genauer: state Reading) aller Mitglieder wird im angegebenen
Reading Komma separiert gespeichert.
- restoreStructState <readingName>
Der Status der Mitglieder wird aus dem angegebenen Reading gelesen, und
via "set Mitgliedsname StatusWert" gesetzt.
Jedes andere set Kommando wird an alle Devices dieser Struktur
weitergegeben.
Aussnahme: das Attribut structexclude ist in einem Device definiert und
dessen Attributwert matched als Regexp zum Namen der aktuellen
Struktur.
Wenn das set Kommando diese Form hat set
<structure> [FILTER=<filter>] <type-specific>
wird
:FILTER=<filter> bei der Weitergebe der set an jeden Devicenamen wie
folgt angehängt: set <devN>:FILTER=<filter>
<type-specific>
Falls der letzte Parameter reverse ist, dann werden die Befehle in der
umgekehrten Reihenfolge ausgeführt.
Get
Get wird im Structur-Device nicht unterstützt.
Attribute
- async_delay
Wenn dieses Attribut gesetzt ist, werden ungefilterte set Kommandos nicht
sofort an die Clients weitergereicht. Stattdessen werden sie einer
Warteschlange hinzugefügt, um später ausgeführt zu werden.
Das set Kommando kehrt sofort zurück, die Clients werden danach
timer-gesteuert einzeln abgearbeitet. Die Zeit zwischen den
Timer-Aufrufen ist dabei durch den Wert von async_delay (in Sekunden)
gegeben, ein Wert von 0 entspricht der schnellstmöglichen Abfolge.
So können besonders lange Verzögerungen, die gerade bei
großen structures vorkommen können, in unproblematischere
Häppchen zerlegt werden.
- disable
- disabledForIntervals
- considerDisabledMembers
wenn gesetzt (auf 1), werden "disabled" Mitglieder bei der Berechnung
der Struktur-Status berücksichtigt, sonst werden diese ignoriert.
- clientstate_behavior
Der Status einer Struktur hängt von den Status der zugefügten
Devices ab. Dabei wird das propagieren der Status der Devices in zwei
Gruppen klassifiziert und mittels diesem Attribut definiert:
- absolute
Die Struktur wird erst dann den Status der zugefügten Devices
annehmen, wenn alle Devices einen identischen Status vorweisen. Bei
unterschiedlichen Devictypen kann dies per Attribut
<struct_type>_map pro Device beinflusst werden. Andernfalls hat
die Struktur den Status "undefined".
- relative
S.u. clientstate_priority.
- relativeKnown
wie relative, reagiert aber nicht auf unbekannte, in
clientstate_priority nicht beschriebene Ereignisse. Wird für
HomeMatic Geräte benötigt.
- last
Die Struktur übernimmt den Status des zuletzt geänderten
Gerätes.
- clientstate_priority
Wird die Struktur auf ein relatives Verhalten eingestellt, so wird die
Priorität der Devicestatus über das Attribut
clientstate_priority
beinflusst. Die Prioritäten sind
in absteigender Reihenfolge anzugeben. Dabei können Gruppen mit
identischer Priorität angegeben werden, um zb. unterschiedliche
Devicetypen zusammenfassen zu können. Jede Gruppe wird durch
Leerzeichen oder /, jeder Eintrag pro Gruppe durch Pipe getrennt. Der
Status der Struktur ist der erste Eintrag in der entsprechenden Gruppe.
Beispiel:
- attr kueche clientstate_behavior relative
- attr kueche clientstate_priority An|On|on Aus|Off|off
- attr haus clientstate_priority Any_On|An All_Off|Aus
In diesem Beipiel nimmt die Struktur kueche
entweder den
Status An
oder Aus
an. Die Struktur
haus
nimmt entweder den Status Any_on
oder
All_off
an. Sobald ein Device der Struktur
haus
den Status An
hat nimmt die Struktur den
Status Any_On
an. Um dagegen den Status All_off
anzunehmen, müssen alle Devices dieser Struktur auf off
stehen.
- <struct_type>_map
Mit diesem Attribut, das dem Struktur-Mitglied zugewiesen werden
muss, koennen die Werte, die die einzelnen Struktur- Mitglieder melden,
umdefiniert werden, damit man unterschiedliche Geraeteklassen
zusammenfassen kann. Es existieren drei Varianten:
- readingName
nehme den Wert von readingName anstatt von state
- oldVal:newVal
falls der Wert der state Reading oldVal (als regex) ist, dann ersetze
diesen mit newVal.
- readingName:oldVal:newVal
falls der Wert der readingName oldVal (als regex) ist, dann ersetze
diesen mit newVal.
Beispiel:
- define tuer OWSWITCH <ROMID>
- define lampe1 dummy
- attr lampe1 cmdlist on off
- define kueche structure struct_kitchen lamp1 door
- attr kueche clientstate_priority An|on OK|Aus|off
- attr lampe1 struct_kitchen_map on:An off:Aus
- attr tuer struct_kitchen_map A:open:on A:closed:off
- attr tuer2 struct_kitchen_map A
Ist das Attribut nicht gesetzt, wertet structure den state des Devices
aus, bzw. falls dieser nicht existiert, dessen STATE.
- evaluateSetResult
Falls ein set Befehl den Status der Struktur-Mitglieder auf was
unterschiedliches setzt (wie z.Bsp. beim set statusRequest), dann muss
dieses Attribut auf 1 gesetzt werden, wenn die Struktur Instanz diesen
neuen Status auswerten soll.
- filterEvents
falls gesetzt, und das Event auslösende Gerät über ein
struct_type map verfügt, dann werden nur solche Events die
Strukturberechnung auslösen, die in diesem map enthalten sind.
Achtung: in struct_type map werden nur die readingName und
readingName:oldVal:newVal Einträge berücksichtigt.
- propagateAttr <regexp>
Falls der Regexp auf den Namen des Attributes zutrifft, dann wird dieses
Attribut an allen Mitglieder weitergegeben. Für featurelevel <= 5.9
ist die Voreinstellung .* (d.h. alle Attribute), sonst ^$ (d.h. keine
Attribute).
Achtung: folgende Attribute wurden fuer featurelevel<=5.9 nicht
weitervererbt:
alias async_delay clientstate_behavior clientstate_priority
devStateIcon disable disabledForIntervals group icon room propagateAttr
setStateIndirectly stateFormat webCmd userattr
- setStateIndirectly
Falls wahr (1), dann wird der Status der Struktur nur aus dem
Statusmeldungen der Mitglied-Geräte bestimmt, sonst wird zuerst der
Status auf dem set Argument gesetzt. Die Voreinstellung ist 0.
- setStructType
Falls wahr (1), <struct-type> wird als Attribute für jedes
Mitglied-Gerät auf dem Namen der Struktur gesetzt.
Wahr ist die Voreinstellung für featurelevel <= 5.8.
- structexclude
Bei gesetztem Attribut wird set, attr/deleteattr ignoriert. Dies
trifft ebenfalls auf die Weitergabe des Devicestatus an die Struktur zu.
Fuer set und fuer die Status-Weitergabe muss der Wert den Strukturnamen
matchen, bei einem Attribut-Befehl die Kombination
Strukturname:Attributname.
Beispiel:
define kitchen structure room lamp1 lamp2
attr lamp1 structexclude kitchen
attr lamp1 structexclude kitchen:stateFormat
- readingFnAttributes
=end html_DE
=cut