98_structure.pm 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. # $Id: 98_structure.pm 16865 2018-06-14 07:21:25Z rudolfkoenig $
  2. ##############################################################################
  3. #
  4. # 98_structure.pm
  5. # Copyright by
  6. # e-mail:
  7. #
  8. # This file is part of fhem.
  9. #
  10. # Fhem is free software: you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License as published by
  12. # the Free Software Foundation, either version 2 of the License, or
  13. # (at your option) any later version.
  14. #
  15. # Fhem is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License
  21. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  22. #
  23. ##############################################################################
  24. package main;
  25. use strict;
  26. use warnings;
  27. #use Data::Dumper;
  28. sub structure_getChangedDevice($);
  29. #####################################
  30. sub
  31. structure_Initialize($)
  32. {
  33. my ($hash) = @_;
  34. $hash->{DefFn} = "structure_Define";
  35. $hash->{UndefFn} = "structure_Undef";
  36. $hash->{NotifyFn} = "structure_Notify";
  37. $hash->{SetFn} = "structure_Set";
  38. $hash->{AttrFn} = "structure_Attr";
  39. no warnings 'qw';
  40. my @attrList = qw(
  41. async_delay
  42. clientstate_behavior:relative,relativeKnown,absolute,last
  43. clientstate_priority
  44. disable
  45. disabledForIntervals
  46. evaluateSetResult:1,0
  47. setStateIndirectly:1,0
  48. setStructType:0,1
  49. );
  50. use warnings 'qw';
  51. $hash->{AttrList} = join(" ", @attrList)." $readingFnAttributes";
  52. my %ahash = ( Fn=>"CommandAddStruct",
  53. Hlp=>"<structure> <devspec>,add <devspec> to <structure>" );
  54. $cmds{addstruct} = \%ahash;
  55. my %dhash = ( Fn=>"CommandDelStruct",
  56. Hlp=>"<structure> <devspec>,delete <devspec> from <structure>");
  57. $cmds{delstruct} = \%dhash;
  58. }
  59. sub structAdd($$);
  60. sub
  61. structAdd($$)
  62. {
  63. my ($d, $attrList) = @_;
  64. return if(!$defs{$d});
  65. $defs{$d}{INstructAdd} = 1;
  66. foreach my $c (@{$defs{$d}{".memberList"}}) {
  67. if($defs{$c} && $defs{$c}{INstructAdd}) {
  68. Log 1, "recursive structure definition"
  69. } else {
  70. addToDevAttrList($c, $attrList);
  71. structAdd($c, $attrList) if($defs{$c} && $defs{$c}{TYPE} eq "structure");
  72. }
  73. }
  74. delete $defs{$d}{INstructAdd} if($defs{$d});
  75. }
  76. sub structure_setDevs($;$);
  77. #############################
  78. sub
  79. structure_Define($$)
  80. {
  81. my ($hash, $def) = @_;
  82. my @a = split("[ \t][ \t]*", $def);
  83. my $u = "wrong syntax: define <name> structure <struct-type> [device ...]";
  84. return $u if(int(@a) < 4);
  85. my $devname = shift(@a);
  86. my $modname = shift(@a);
  87. my $stype = shift(@a);
  88. $hash->{ATTR} = $stype;
  89. $hash->{CHANGEDCNT} = 0;
  90. $hash->{".asyncQueue"} = [];
  91. structure_setDevs($hash, $def); # needed by set while init is running
  92. InternalTimer(1, sub { # repeat it for devices defined later
  93. structure_setDevs($hash, $def);
  94. structure_Attr("set", $devname, $stype, $devname)
  95. if(AttrVal($devname, "setStructType", $featurelevel <= 5.8));
  96. }, undef, 0);
  97. return undef;
  98. }
  99. sub
  100. structure_setDevs($;$)
  101. {
  102. my ($hash, $def) = @_;
  103. $def = "$hash->{NAME} structure $hash->{DEF}" if(!$def);
  104. my $c = $hash->{".memberHash"};
  105. my @a = split("[ \t][ \t]*", $def);
  106. my $devname = shift(@a);
  107. my $modname = shift(@a);
  108. my $stype = shift(@a);
  109. my (%list, @list);
  110. my $aList = "$stype ${stype}_map structexclude";
  111. foreach my $a (@a) {
  112. foreach my $d (devspec2array($a)) {
  113. next if(!$defs{$d} || $list{$d});
  114. $hash->{DEVSPECDEF} = 1 if($a ne $d);
  115. $list{$d} = 1;
  116. push(@list, $d);
  117. next if($c && $c->{$d});
  118. addToDevAttrList($d, $aList);
  119. structAdd($d, $aList) if($defs{$d} && $defs{$d}{TYPE} eq "structure");
  120. }
  121. }
  122. $hash->{".memberHash"} = \%list;
  123. $hash->{".memberList"} = \@list;
  124. delete $hash->{".cachedHelp"};
  125. }
  126. #############################
  127. sub
  128. structure_Undef($$)
  129. {
  130. my ($hash, $def) = @_;
  131. my @a = ( "del", $hash->{NAME}, $hash->{ATTR} );
  132. structure_Attr(@a);
  133. return undef;
  134. }
  135. #############################
  136. # returns the really changed Device
  137. #############################
  138. sub
  139. structure_getChangedDevice($)
  140. {
  141. my ($dev) = @_;
  142. my $lastDevice = ReadingsVal($dev, "LastDevice", undef);
  143. $dev = structure_getChangedDevice($lastDevice)
  144. if($lastDevice && $defs{$dev}->{TYPE} eq "structure");
  145. return $dev;
  146. }
  147. #############################
  148. sub
  149. structure_Notify($$)
  150. {
  151. my ($hash, $dev) = @_;
  152. my $me = $hash->{NAME};
  153. my $devmap = $hash->{ATTR}."_map";
  154. if($dev->{NAME} eq "global") {
  155. my $max = int(@{$dev->{CHANGED}});
  156. for (my $i = 0; $i < $max; $i++) {
  157. my $s = $dev->{CHANGED}[$i];
  158. $s = "" if(!defined($s));
  159. if($s =~ m/^RENAMED ([^ ]*) ([^ ]*)$/) {
  160. my ($old, $new) = ($1, $2);
  161. if($hash->{".memberHash"}{$old}) {
  162. $hash->{DEF} =~ s/\b$old\b/$new/;
  163. structure_setDevs($hash);
  164. }
  165. } elsif($s =~ m/^DELETED ([^ ]*)$/) {
  166. my $n = $1;
  167. if($hash->{".memberHash"}{$n}) {
  168. $hash->{DEF} =~ s/\b$n\b//;
  169. structure_setDevs($hash)
  170. }
  171. } elsif($s =~ m/^DEFINED ([^ ]*)$/) {
  172. structure_setDevs($hash) if($hash->{NAME} ne $1 && $hash->{DEVSPECDEF});
  173. }
  174. }
  175. return;
  176. }
  177. return "" if(IsDisabled($me));
  178. return "" if (! exists $hash->{".memberHash"}->{$dev->{NAME}});
  179. my $behavior = AttrVal($me, "clientstate_behavior", "absolute");
  180. my %clientstate;
  181. my @structPrio = attrSplit($attr{$me}{clientstate_priority})
  182. if($attr{$me} && $attr{$me}{clientstate_priority});
  183. return "" if($hash->{INSET} && !AttrVal($me, "evaluateSetResult", 0));
  184. return "" if(@{$hash->{".asyncQueue"}}); # Do not trigger during async set
  185. if($hash->{INNTFY}) {
  186. Log3 $me, 1, "ERROR: endless loop detected in structure_Notify $me";
  187. return "";
  188. }
  189. $hash->{INNTFY} = 1;
  190. my %priority;
  191. my (@priority, @foo);
  192. for (my $i=0; $i<@structPrio; $i++) {
  193. @foo = split(/\|/, $structPrio[$i]);
  194. for (my $j=0; $j<@foo;$j++) {
  195. $priority{$foo[$j]} = $i+1;
  196. $priority[$i+1]=$foo[0];
  197. }
  198. }
  199. my $minprio = 99999;
  200. my $devstate;
  201. foreach my $d (sort keys %{ $hash->{".memberHash"} }) {
  202. next if(!$defs{$d});
  203. if($attr{$d} && $attr{$d}{$devmap}) {
  204. my @gruppe = attrSplit($attr{$d}{$devmap});
  205. my @value;
  206. for (my $i=0; $i<@gruppe; $i++) {
  207. @value = split(":", $gruppe[$i]);
  208. if(@value == 1) { # value[0]:.* -> .*
  209. $devstate = ReadingsVal($d, $value[0], undef);
  210. } elsif(@value == 2) { # state:value[0] -> value[1]
  211. $devstate = ReadingsVal($d, "state", undef);
  212. $devstate = $defs{$d}{STATE} if(!defined($devstate));
  213. if(defined($devstate) && $devstate =~ m/^$value[0]/){
  214. $devstate = $value[1];
  215. $i=99999; # RKO: ??
  216. } else {
  217. $devstate = undef;
  218. }
  219. } elsif(@value == 3) { # value[0]:value[1] -> value[2]
  220. $devstate = ReadingsVal($d, $value[0], undef);
  221. if(defined($devstate) && $devstate =~ m/^$value[1]/){
  222. $devstate = $value[2];
  223. } else {
  224. $devstate = undef;
  225. }
  226. }
  227. if(defined($devstate)) {
  228. if(!$priority{$devstate} && $behavior eq "relativeKnown") {
  229. delete($hash->{INNTFY});
  230. return "";
  231. }
  232. $minprio = $priority{$devstate}
  233. if($priority{$devstate} && $priority{$devstate} < $minprio);
  234. $clientstate{$devstate} = 1;
  235. }
  236. }
  237. } else {
  238. $devstate = ReadingsVal($d, "state", undef);
  239. $devstate = $defs{$d}{STATE} if(!defined($devstate));
  240. if(defined($devstate)) {
  241. if(!$priority{$devstate} && $behavior eq "relativeKnown") {
  242. delete($hash->{INNTFY});
  243. return "";
  244. }
  245. $minprio = $priority{$devstate}
  246. if($priority{$devstate} && $priority{$devstate} < $minprio);
  247. $clientstate{$devstate} = 1;
  248. }
  249. }
  250. $hash->{".memberHash"}{$d} = $devstate;
  251. }
  252. my $newState = "undefined";
  253. if($behavior eq "absolute"){
  254. my @cKeys = keys %clientstate;
  255. $newState = (@cKeys == 1 ? $cKeys[0] : "undefined");
  256. } elsif($behavior =~ "^relative" && $minprio < 99999) {
  257. $newState = $priority[$minprio];
  258. } elsif($behavior eq "last"){
  259. my $readingName = AttrVal($dev->{NAME}, $devmap, "state");
  260. $newState = ReadingsVal($dev->{NAME}, $readingName, undef);
  261. $newState = "undefined" if(!defined($newState));
  262. }
  263. Log3 $me, 5, "Update structure '$me' to $newState" .
  264. " because device $dev->{NAME} has changed";
  265. readingsBeginUpdate($hash);
  266. readingsBulkUpdate($hash, "LastDevice", $dev->{NAME}, 0);
  267. readingsBulkUpdate($hash, "LastDevice_Abs",
  268. structure_getChangedDevice($dev->{NAME}), 0);
  269. readingsBulkUpdate($hash, "state", $newState);
  270. readingsEndUpdate($hash, 1);
  271. $hash->{CHANGEDCNT}++;
  272. delete($hash->{INNTFY});
  273. undef;
  274. }
  275. #####################################
  276. sub
  277. CommandAddStruct($)
  278. {
  279. my ($cl, $param) = @_;
  280. my @a = split(" ", $param);
  281. if(int(@a) != 2) {
  282. return "Usage: addstruct <structure_device> <devspec>";
  283. }
  284. my $name = shift(@a);
  285. my $hash = $defs{$name};
  286. if(!$hash || $hash->{TYPE} ne "structure") {
  287. return "$a is not a structure device";
  288. }
  289. foreach my $d (devspec2array($a[0])) {
  290. $hash->{".memberHash"}{$d} = 1;
  291. $hash->{DEF} .= " $d";
  292. }
  293. @a = ( "set", $hash->{NAME}, $hash->{ATTR}, $hash->{NAME} );
  294. structure_Attr(@a);
  295. delete $hash->{".cachedHelp"};
  296. return undef;
  297. }
  298. #####################################
  299. sub
  300. CommandDelStruct($)
  301. {
  302. my ($cl, $param) = @_;
  303. my @a = split(" ", $param);
  304. if(int(@a) != 2) {
  305. return "Usage: delstruct <structure_device> <devspec>";
  306. }
  307. my $name = shift(@a);
  308. my $hash = $defs{$name};
  309. if(!$hash || $hash->{TYPE} ne "structure") {
  310. return "$a is not a structure device";
  311. }
  312. foreach my $d (devspec2array($a[0])) {
  313. delete($hash->{".memberHash"}{$d});
  314. $hash->{DEF} =~ s/\b$d\b//g;
  315. }
  316. $hash->{DEF} =~ s/ / /g;
  317. @a = ( "del", $hash->{NAME}, $hash->{ATTR} );
  318. structure_Attr(@a);
  319. delete $hash->{".cachedHelp"};
  320. return undef;
  321. }
  322. ###################################
  323. sub
  324. structure_Set($@)
  325. {
  326. my ($hash, @list) = @_;
  327. my $me = $hash->{NAME};
  328. my $ret = "";
  329. my %pars;
  330. # see Forum # 28623 for .cachedHelp
  331. return $hash->{".cachedHelp"} if($list[1] eq "?" && $hash->{".cachedHelp"});
  332. my @devList = @{$hash->{".memberList"}};
  333. if(@list > 1 && $list[$#list] eq "reverse") {
  334. pop @list;
  335. @devList = reverse @devList;
  336. }
  337. if($list[1] =~ m/^(save|restore)StructState$/) {
  338. return "Usage: set $me $list[1] readingName" if(@list != 3);
  339. return "Bad reading name $list[2]" if(!goodReadingName($list[2]));
  340. if($1 eq "save") {
  341. readingsSingleUpdate($hash, $list[2],
  342. join(",", map { ReadingsVal($_,"state","on") } @devList), 1);
  343. return;
  344. }
  345. my @sl = split(",", ReadingsVal($me, $list[2], ""));
  346. for(my $i1=0; $i1<@devList && $i1<@sl; $i1++) {
  347. AnalyzeCommand($hash->{CL}, "set $devList[$i1] $sl[$i1]");
  348. }
  349. return;
  350. }
  351. $hash->{INSET} = 1;
  352. my $startAsyncProcessing;
  353. my $filter;
  354. if($list[1] ne "?") {
  355. my $state = join(" ", @list[1..@list-1]);
  356. readingsSingleUpdate($hash, "state", $state, 1)
  357. if(!AttrVal($me, "setStateIndirectly", undef));
  358. if($state =~ /^\[(FILTER=.*)]/) {
  359. delete($hash->{INSET}); # Forum #35382
  360. $filter = $1;
  361. @list = split(" ", $list[0] ." ". substr($state, length($filter)+2));
  362. }
  363. }
  364. foreach my $d (@devList) {
  365. next if(!$defs{$d});
  366. if($defs{$d}{INSET}) {
  367. Log3 $hash, 1, "ERROR: endless loop detected for $d in $me";
  368. next;
  369. }
  370. if($attr{$d} && $attr{$d}{structexclude}) {
  371. my $se = $attr{$d}{structexclude};
  372. next if($me =~ m/$se/);
  373. }
  374. my $dl0 = $defs{$d};
  375. my $is_structure = defined($dl0) && $dl0->{TYPE} eq "structure";
  376. my $async_delay = AttrVal($me, "async_delay", undef);
  377. my $cmd;
  378. if(!$filter) {
  379. $cmd = "set $d ". join(" ", @list[1..@list-1]);
  380. } elsif( $is_structure ) {
  381. $cmd = "set $d [$filter] ". join(" ", @list[1..@list-1]);
  382. } else {
  383. $cmd = "set $d:$filter ". join(" ", @list[1..@list-1]);
  384. }
  385. if(defined($async_delay) && $list[1] ne "?") {
  386. $startAsyncProcessing = $async_delay if(!@{$hash->{".asyncQueue"}});
  387. push @{$hash->{".asyncQueue"}}, $cmd;
  388. } else {
  389. my ($ostate,$ocnt) = ($dl0->{STATE}, $dl0->{CHANGEDCNT});
  390. my $sret = AnalyzeCommand(undef, $cmd);
  391. if($is_structure && $dl0->{CHANGEDCNT} == $ocnt) { # Forum #70488
  392. $dl0->{STATE} = $dl0->{READINGS}{state}{VAL} = $ostate;
  393. structure_Notify($hash, $dl0);
  394. }
  395. if($sret) {
  396. $ret .= "\n" if($ret);
  397. $ret .= $sret;
  398. }
  399. if($list[1] eq "?") {
  400. $sret =~ s/.*one of //;
  401. map { $pars{$_} = 1 } split(" ", $sret);
  402. }
  403. }
  404. }
  405. delete($hash->{INSET});
  406. Log3 $hash, 5, "SET: $ret" if($ret);
  407. if(defined($startAsyncProcessing)) {
  408. InternalTimer(gettimeofday(), "structure_asyncQueue", $hash, 0);
  409. }
  410. return $ret if($list[1] ne "?");
  411. $hash->{".cachedHelp"} = "Unknown argument ?, choose one of " .
  412. join(" ", sort keys(%pars)).
  413. " saveStructState restoreStructState";
  414. return $hash->{".cachedHelp"};
  415. }
  416. sub
  417. structure_asyncQueue(@)
  418. {
  419. my ($hash) = @_;
  420. my $next_cmd = shift @{$hash->{".asyncQueue"}};
  421. if(defined $next_cmd) {
  422. AnalyzeCommand(undef, $next_cmd);
  423. my $async_delay = AttrVal($hash->{NAME}, "async_delay", 0);
  424. InternalTimer(gettimeofday()+$async_delay,"structure_asyncQueue",$hash,0);
  425. }
  426. return undef;
  427. }
  428. ###################################
  429. sub
  430. structure_Attr($@)
  431. {
  432. my ($type, @list) = @_;
  433. my %ignore = (
  434. alias=>1,
  435. async_delay=>1,
  436. clientstate_behavior=>1,
  437. clientstate_priority=>1,
  438. devStateIcon=>1,
  439. disable=>1,
  440. disabledForIntervals=>1,
  441. group=>1,
  442. icon=>1,
  443. room=>1,
  444. setStateIndirectly=>1,
  445. stateFormat=>1,
  446. webCmd=>1,
  447. userattr=>1
  448. );
  449. return undef if($ignore{$list[1]} || !$init_done);
  450. my $me = $list[0];
  451. my $hash = $defs{$me};
  452. if($hash->{INATTR}) {
  453. Log3 $me, 1, "ERROR: endless loop detected in structure_Attr for $me";
  454. return;
  455. }
  456. $hash->{INATTR} = 1;
  457. my $ret = "";
  458. my @devList = @{$hash->{".memberList"}};
  459. foreach my $d (@devList) {
  460. next if(!$defs{$d});
  461. if($attr{$d} && $attr{$d}{structexclude}) {
  462. my $se = $attr{$d}{structexclude};
  463. next if("$me:$list[1]" =~ m/$se/);
  464. }
  465. $list[0] = $d;
  466. my $sret;
  467. if($type eq "del") {
  468. $sret .= CommandDeleteAttr(undef, join(" ", @list));
  469. } else {
  470. $sret .= CommandAttr(undef, join(" ", @list));
  471. }
  472. if($sret) {
  473. $ret .= "\n" if($ret);
  474. $ret .= $sret;
  475. }
  476. }
  477. delete($hash->{INATTR});
  478. Log3 $me, 4, "Stucture attr $type: $ret" if($ret);
  479. return undef;
  480. }
  481. 1;
  482. =pod
  483. =item helper
  484. =item summary organize/structure multiple devices
  485. =item summary_DE mehrere Ger&auml;te zu einem zusammenfassen
  486. =begin html
  487. <a name="structure"></a>
  488. <h3>structure</h3>
  489. <ul>
  490. <br>
  491. <a name="structuredefine"></a>
  492. <b>Define</b>
  493. <ul>
  494. <code>define &lt;name&gt; structure &lt;struct_type&gt; &lt;dev1&gt; &lt;dev2&gt; ...</code>
  495. <br><br>
  496. The structure device is used to organize/structure devices in order to
  497. set groups of them at once (e.g. switching everything off in a house).<br>
  498. The list of attached devices can be modified through the addstruct /
  499. delstruct commands. Each attached device will get the attribute
  500. &lt;struct_type&gt;=&lt;name&gt;<br> when it is added to the list, and the
  501. attribute will be deleted if the device is deleted from the structure.
  502. <br>
  503. The structure devices can also be added to a structure, e.g. you can have
  504. a building consisting of levels which consists of rooms of devices.
  505. <br>
  506. Example:<br>
  507. <ul>
  508. <li>define kitchen structure room lamp1 lamp2</li>
  509. <li>addstruct kitchen TYPE=FS20</li>
  510. <li>delstruct kitchen lamp1</li>
  511. <li>define house structure building kitchen living</li>
  512. <li>set house off</li>
  513. </ul>
  514. <br>
  515. </ul>
  516. <br>
  517. <a name="structureset"></a>
  518. <b>Set</b>
  519. <ul>
  520. <li>saveStructState &lt;readingName&gt;<br>
  521. The state reading of all members is stored comma separated in the
  522. specified readingName.
  523. </li><br>
  524. <li>restoreStructState &lt;readingName&gt;<br>
  525. The state of all members will be restored from readingName by calling
  526. "set memberName storedStateValue".
  527. </li><br>
  528. Every other set command is propagated to the attached devices. Exception:
  529. if an
  530. attached device has an attribute structexclude, and the attribute value
  531. matches (as a regexp) the name of the current structure.<br>
  532. If the set is of the form <code>set &lt;structure&gt;
  533. [FILTER=&lt;filter&gt;] &lt;type-specific&gt;</code> then
  534. :FILTER=&lt;filter&gt; will be appended to the device name in the
  535. propagated set for the attached devices like this: <code>set
  536. &lt;devN&gt;:FILTER=&lt;filter&gt; &lt;type-specific&gt;</code><br>
  537. If the last set parameter is "reverse", then execute the set commands in
  538. the reverse order.
  539. </ul>
  540. <br>
  541. <a name="structureget"></a>
  542. <b>Get</b>
  543. <ul>
  544. get is not supported through a structure device.
  545. </ul>
  546. <br>
  547. <a name="structureattr"></a>
  548. <b>Attributes</b>
  549. <ul>
  550. <a name="async_delay"></a>
  551. <li>async_delay<br>
  552. If this attribute is defined, unfiltered set commands will not be
  553. executed in the clients immediately. Instead, they are added to a queue
  554. to be executed later. The set command returns immediately, whereas the
  555. clients will be set timer-driven, one at a time. The delay between two
  556. timercalls is given by the value of async_delay (in seconds) and may be
  557. 0 for fastest possible execution. This way, excessive delays often
  558. known from large structures, can be broken down in smaller junks.
  559. </li>
  560. <li><a href="#disable">disable</a></li>
  561. <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
  562. <a name="clientstate_behavior"></a>
  563. <li>clientstate_behavior<br>
  564. The backward propagated status change from the devices to this structure
  565. works in two different ways.
  566. <ul>
  567. <li>absolute<br>
  568. The structure status will changed to the common device status of all
  569. defined devices to this structure if all devices are identical.
  570. Otherwise the structure status is "undefined".
  571. </li>
  572. <li>relative<br>
  573. See below for clientstate_priority.
  574. </li>
  575. <li>relativeKnown<br>
  576. Like relative, but do not trigger on events not described in
  577. clientstate_priority. Needed e.g. for HomeMatic devices.
  578. </li>
  579. <li>last<br>
  580. The structure state corresponds to the state of the device last changed.
  581. </li>
  582. </ul>
  583. </li>
  584. <a name="clientstate_priority"></a>
  585. <li>clientstate_priority<br>
  586. If clientstate_behavior is set to relative, then you have to set the
  587. attribute "clientstate_priority" with all states of the defined devices
  588. to this structure in descending order. Each group is delemited by
  589. space or /. Each entry of one group is delimited by "pipe". The status
  590. represented by the structure is the first entry of each group.
  591. Example:<br>
  592. <ul>
  593. <li>attr kitchen clientstate_behavior relative</li>
  594. <li>attr kitchen clientstate_priority An|On|on Aus|Off|off</li>
  595. <li>attr house clientstate_priority Any_On|An All_Off|Aus</li>
  596. </ul>
  597. In this example the status of kitchen is either on or off. The status
  598. of house is either Any_on or All_off.
  599. </li>
  600. <li>&lt;struct_type&gt;_map<br>
  601. With this attribute, which has to specified for the structure-
  602. <b>member</b>, you can redefine the value reported by a specific
  603. structure-member for the structure value. The attribute has three
  604. variants:
  605. <ul>
  606. <li>readingName<br>
  607. take the value from readingName instead of state.
  608. </li>
  609. <li>oldVal:newVal<br>
  610. if the state reading matches oldVal, then replace it with newVal
  611. </li>
  612. <li>readingName:oldVal:newVal<br>
  613. if readingName matches oldVal, then replace it with newVal
  614. </li>
  615. </ul>
  616. Example:
  617. <ul>
  618. <li>define door OWSWITCH &lt;ROMID&gt</li>
  619. <li>define lamp1 dummy</li>
  620. <li>attr lamp1 cmdlist on off</li>
  621. <li>define kitchen structure struct_kitchen lamp1 door</li>
  622. <li>attr kitchen clientstate_priority An|on OK|Aus|off</li>
  623. <li>attr lamp1 struct_kitchen_map on:An off:Aus</li>
  624. <li>attr door struct_kitchen_map A:open:on A:closed:off</li>
  625. <li>attr door2 struct_kitchen_map A</li>
  626. </ul>
  627. </li>
  628. <a name="evaluateSetResult"></a>
  629. <li>evaluateSetResult<br>
  630. if a set command sets the state of the structure members to something
  631. different from the set command (like set statusRequest), then you have to
  632. set this attribute to 1 in order to enable the structure instance to
  633. compute the new status.
  634. </li>
  635. <li>setStateIndirectly<br>
  636. If true (1), set the state only when member devices report a state
  637. change, else the state is first set to the set command argument. Default
  638. is 0.
  639. </li>
  640. <li>setStructType<br>
  641. If true (1), then the &lt;struct-type&gt; will be set as an attribute for
  642. each member device to the name of the structure. True is the default for
  643. featurelevel <= 5.8.
  644. </li>
  645. <a name="structexclude"></a>
  646. <li>structexclude<br>
  647. Note: this is an attribute for the member device, not for the struct
  648. itself.<br>
  649. Exclude the device from set/notify or attribute operations. For the set
  650. and notify the value of structexclude must match the structure name,
  651. for the attr/deleteattr commands ist must match the combination of
  652. structure_name:attribute_name. Examples:<br>
  653. <ul>
  654. <code>
  655. define kitchen structure room lamp1 lamp2<br>
  656. attr lamp1 structexclude kitchen<br>
  657. attr lamp1 structexclude kitchen:stateFormat<br>
  658. </code>
  659. </ul>
  660. </li>
  661. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  662. </ul>
  663. <br>
  664. </ul>
  665. =end html
  666. =begin html_DE
  667. <a name="structure"></a>
  668. <h3>structure</h3>
  669. <ul>
  670. <br>
  671. <a name="structuredefine"></a>
  672. <b>Define</b>
  673. <ul>
  674. <code>define &lt;name&gt; structure &lt;struct_type&gt; &lt;dev1&gt;
  675. &lt;dev2&gt; ...</code> <br><br>
  676. Mit dem Device "Structure" werden Strukturen/Zusammenstellungen von anderen
  677. Devices erstellt um sie zu Gruppen zusammenzufassen. (Beispiel: im Haus
  678. alles ausschalten) <br>
  679. Die Liste der Devices die einer Struktur zugeordnet sind kann duch das
  680. Kommando <code>addstruct / delstruct</code> im laufenden Betrieb
  681. ver&auml;ndert werden. Es k&ouml;nnen sowohl einzelne Devices als auch
  682. Gruppen von Devices (TYPE=FS20) zugef&uuml;gt werden. Jedes zugef&uuml;gt
  683. Device erh&auml;lt zwei neue Attribute &lt;struct_type&gt;=&lt;name&gt;
  684. sowie &lt;struct_type&gt;_map wenn es zu einer Struktur zugef&uuml;gt
  685. wurde. Diese Attribute werden wieder automatisch entfernt, sobald das
  686. Device von der Struktur entfernt wird.<br>
  687. Eine Struktur kann ebenfalls zu einer anderen Struktur zugef&uuml;gt
  688. werden. Somit k&ouml;nnen z b. kaskadierende Strukturen erstellt werden.
  689. (Z.b. KG,EG,OG, Haus)
  690. Beispiel:<br>
  691. <ul>
  692. <li>define Kueche structure room lampe1 lampe2</li>
  693. <li>addstruct Kueche TYPE=FS20</li>
  694. <li>delstruct Kueche lampe1</li>
  695. <li>define house structure building kitchen living</li>
  696. <li>set house off</li>
  697. </ul>
  698. <br>
  699. </ul>
  700. <br>
  701. <a name="structureset"></a>
  702. <b>Set</b>
  703. <ul>
  704. <li>saveStructState &lt;readingName&gt;<br>
  705. Der Status (genauer: state Reading) aller Mitglieder wird im angegebenen
  706. Reading Komma separiert gespeichert.
  707. </li><br>
  708. <li>restoreStructState &lt;readingName&gt;<br>
  709. Der Status der Mitglieder wird aus dem angegebenen Reading gelesen, und
  710. via "set Mitgliedsname StatusWert" gesetzt.
  711. </li><br>
  712. Jedes andere set Kommando wird an alle Devices dieser Struktur
  713. weitergegeben.<br>
  714. Aussnahme: das Attribut structexclude ist in einem Device definiert und
  715. dessen Attributwert matched als Regexp zum Namen der aktuellen
  716. Struktur.<br> Wenn das set Kommando diese Form hat <code>set
  717. &lt;structure&gt; [FILTER=&lt;filter&gt;] &lt;type-specific&gt;</code> wird
  718. :FILTER=&lt;filter&gt; bei der Weitergebe der set an jeden Devicenamen wie
  719. folgt angeh&auml;ngt: <code>set &lt;devN&gt;:FILTER=&lt;filter&gt;
  720. &lt;type-specific&gt;</code><br>
  721. Falls der letzte Parameter reverse ist, dann werden die Befehle in der
  722. umgekehrten Reihenfolge ausgef&uuml;hrt.
  723. </ul>
  724. <br>
  725. <a name="structureget"></a>
  726. <b>Get</b>
  727. <ul>
  728. Get wird im Structur-Device nicht unterst&uuml;tzt.
  729. </ul>
  730. <br>
  731. <a name="structureattr"></a>
  732. <b>Attribute</b>
  733. <ul>
  734. <a name="async_delay"></a>
  735. <li>async_delay<br>
  736. Wenn dieses Attribut gesetzt ist, werden ungefilterte set Kommandos nicht
  737. sofort an die Clients weitergereicht. Stattdessen werden sie einer
  738. Warteschlange hinzugef&uuml;gt, um sp&auml;ter ausgef&uuml;hrt zu werden.
  739. Das set Kommando kehrt sofort zur&uuml;ck, die Clients werden danach
  740. timer-gesteuert einzeln abgearbeitet. Die Zeit zwischen den
  741. Timer-Aufrufen ist dabei durch den Wert von async_delay (in Sekunden)
  742. gegeben, ein Wert von 0 entspricht der schnellstm&ouml;glichen Abfolge.
  743. So k&ouml;nnen besonders lange Verz&ouml;gerungen, die gerade bei
  744. gro&szlig;en structures vorkommen k&ouml;nnen, in unproblematischere
  745. H&auml;ppchen zerlegt werden.
  746. </li>
  747. <li><a href="#disable">disable</a></li>
  748. <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
  749. <a name="clientstate_behavior"></a>
  750. <li>clientstate_behavior<br>
  751. Der Status einer Struktur h&auml;ngt von den Status der zugef&uuml;gten
  752. Devices ab. Dabei wird das propagieren der Status der Devices in zwei
  753. Gruppen klassifiziert und mittels diesem Attribut definiert:
  754. <ul>
  755. <li>absolute<br>
  756. Die Struktur wird erst dann den Status der zugef&uuml;gten Devices
  757. annehmen, wenn alle Devices einen identischen Status vorweisen. Bei
  758. unterschiedlichen Devictypen kann dies per Attribut
  759. &lt;struct_type&gt;_map pro Device beinflusst werden. Andernfalls hat
  760. die Struktur den Status "undefined".
  761. </li>
  762. <li>relative<br>
  763. S.u. clientstate_priority.
  764. </li>
  765. <li>relativeKnown<br>
  766. wie relative, reagiert aber nicht auf unbekannte, in
  767. clientstate_priority nicht beschriebene Ereignisse. Wird f&uuml;r
  768. HomeMatic Ger&auml;te ben&ouml;tigt.
  769. </li>
  770. <li>last<br>
  771. Die Struktur &uuml;bernimmt den Status des zuletzt ge&auml;nderten
  772. Ger&auml;tes.
  773. </li>
  774. </ul>
  775. </li>
  776. <a name="clientstate_priority"></a>
  777. <li>clientstate_priority<br>
  778. Wird die Struktur auf ein relatives Verhalten eingestellt, so wird die
  779. Priorit&auml;t der Devicestatus &uuml;ber das Attribut
  780. <code>clientstate_priority</code> beinflusst. Die Priorit&auml;ten sind
  781. in absteigender Reihenfolge anzugeben. Dabei k&ouml;nnen Gruppen mit
  782. identischer Priorit&auml;t angegeben werden, um zb. unterschiedliche
  783. Devicetypen zusammenfassen zu k&ouml;nnen. Jede Gruppe wird durch
  784. Leerzeichen oder /, jeder Eintrag pro Gruppe durch Pipe getrennt. Der
  785. Status der Struktur ist der erste Eintrag in der entsprechenden Gruppe.
  786. <br>Beispiel:
  787. <ul>
  788. <li>attr kueche clientstate_behavior relative</li>
  789. <li>attr kueche clientstate_priority An|On|on Aus|Off|off</li>
  790. <li>attr haus clientstate_priority Any_On|An All_Off|Aus</li>
  791. </ul>
  792. In diesem Beipiel nimmt die Struktur <code>kueche</code>entweder den
  793. Status <code>An</code> oder <code>Aus</code> an. Die Struktur
  794. <code>haus</code> nimmt entweder den Status <code>Any_on</code> oder
  795. <code>All_off</code> an. Sobald ein Device der Struktur
  796. <code>haus</code> den Status <code>An</code> hat nimmt die Struktur den
  797. Status <code>Any_On</code> an. Um dagegen den Status <code>All_off</code>
  798. anzunehmen, m&uuml;ssen alle Devices dieser Struktur auf <code>off</code>
  799. stehen.
  800. </li>
  801. <li>&lt;struct_type&gt;_map<br>
  802. Mit diesem Attribut, das dem Struktur-<b>Mitglied</b> zugewiesen werden
  803. muss, koennen die Werte, die die einzelnen Struktur- Mitglieder melden,
  804. umdefiniert werden, damit man unterschiedliche Geraeteklassen
  805. zusammenfassen kann. Es existieren drei Varianten:
  806. <ul>
  807. <li>readingName<br>
  808. nehme den Wert von readingName anstatt von state
  809. </li>
  810. <li>oldVal:newVal<br>
  811. falls der Wert der state Reading oldVal (als regex) ist, dann ersetze
  812. diesen mit newVal.
  813. </li>
  814. <li>readingName:oldVal:newVal<br>
  815. falls der Wert der readingName oldVal (als regex) ist, dann ersetze
  816. diesen mit newVal.
  817. </li>
  818. </ul>
  819. Beispiel:<br>
  820. <ul>
  821. <li>define tuer OWSWITCH &lt;ROMID&gt</li>
  822. <li>define lampe1 dummy</li>
  823. <li>attr lampe1 cmdlist on off</li>
  824. <li>define kueche structure struct_kitchen lamp1 door</li>
  825. <li>attr kueche clientstate_priority An|on OK|Aus|off</li>
  826. <li>attr lampe1 struct_kitchen_map on:An off:Aus</li>
  827. <li>attr tuer struct_kitchen_map A:open:on A:closed:off</li>
  828. <li>attr tuer2 struct_kitchen_map A</li>
  829. </ul>
  830. </li>
  831. <a name="evaluateSetResult"></a>
  832. <li>evaluateSetResult<br>
  833. Falls ein set Befehl den Status der Struktur-Mitglieder auf was
  834. unterschiedliches setzt (wie z.Bsp. beim set statusRequest), dann muss
  835. dieses Attribut auf 1 gesetzt werden, wenn die Struktur Instanz diesen
  836. neuen Status auswerten soll.
  837. </li>
  838. <li>setStateIndirectly<br>
  839. Falls wahr (1), dann wird der Status der Struktur nur aus dem
  840. Statusmeldungen der Mitglied-Ger&auml;te bestimmt, sonst wird zuerst der
  841. Status auf dem set Argument gesetzt. Die Voreinstellung ist 0.
  842. </li>
  843. <li>setStructType<br>
  844. Falls wahr (1), &lt;struct-type&gt; wird als Attribute f&uuml;r jedes
  845. Mitglied-Ger&auml;t auf dem Namen der Struktur gesetzt.
  846. Wahr ist die Voreinstellung f&uuml;r featurelevel <= 5.8.
  847. </li>
  848. <li>structexclude<br>
  849. Bei gesetztem Attribut wird set, attr/deleteattr ignoriert. Dies
  850. trifft ebenfalls auf die Weitergabe des Devicestatus an die Struktur zu.
  851. Fuer set und fuer die Status-Weitergabe muss der Wert den Strukturnamen
  852. matchen, bei einem Attribut-Befehl die Kombination
  853. Strukturname:Attributname.
  854. Beispiel:
  855. <ul>
  856. <code>
  857. define kitchen structure room lamp1 lamp2<br>
  858. attr lamp1 structexclude kitchen<br>
  859. attr lamp1 structexclude kitchen:stateFormat<br>
  860. </code>
  861. </ul>
  862. </li>
  863. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  864. </ul>
  865. <br>
  866. </ul>
  867. =end html_DE
  868. =cut