98_structure.pm 30 KB

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