95_holiday.pm 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. #######################################################################
  2. # $Id: 95_holiday.pm 16502 2018-03-27 20:59:14Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. use POSIX;
  7. sub holiday_refresh($;$$);
  8. #####################################
  9. sub
  10. holiday_Initialize($)
  11. {
  12. my ($hash) = @_;
  13. $hash->{DefFn} = "holiday_Define";
  14. $hash->{GetFn} = "holiday_Get";
  15. $hash->{SetFn} = "holiday_Set";
  16. $hash->{UndefFn} = "holiday_Undef";
  17. $hash->{AttrList} = $readingFnAttributes;
  18. $hash->{FW_detailFn} = "holiday_FW_detailFn";
  19. }
  20. #####################################
  21. sub
  22. holiday_Define($$)
  23. {
  24. my ($hash, $def) = @_;
  25. return holiday_refresh($hash->{NAME}, undef, 1) if($init_done);
  26. InternalTimer(gettimeofday()+1, "holiday_refresh", $hash->{NAME}, 0);
  27. return undef;
  28. }
  29. sub
  30. holiday_Undef($$)
  31. {
  32. my ($hash, $name) = @_;
  33. RemoveInternalTimer($name);
  34. return undef;
  35. }
  36. sub
  37. holiday_refresh($;$$)
  38. {
  39. my ($name, $fordate, $showAvailable) = (@_);
  40. my $hash = $defs{$name};
  41. my $fromTimer=0;
  42. return if(!$hash); # Just deleted
  43. my $nt = gettimeofday();
  44. my @lt = localtime($nt);
  45. my @fd;
  46. if(!$fordate) {
  47. $fromTimer = 1;
  48. $fordate = sprintf("%02d-%02d", $lt[4]+1, $lt[3]);
  49. @fd = @lt;
  50. } else {
  51. my ($m,$d) = split("-", $fordate);
  52. @fd = localtime(mktime(1,1,1,$d,$m-1,$lt[5],0,0,-1));
  53. }
  54. Log3 $name, 5, "holiday_refresh $name called for $fordate ($fromTimer)";
  55. my $dir = $attr{global}{modpath} . "/FHEM";
  56. my ($err, @holidayfile) = FileRead("$dir/$name.holiday");
  57. if($err) {
  58. $dir = $attr{global}{modpath}."/FHEM/holiday";
  59. ($err, @holidayfile) = FileRead("$dir/$name.holiday");
  60. $hash->{READONLY} = 1;
  61. } else {
  62. $hash->{READONLY} = 0;
  63. }
  64. if($err) {
  65. if($showAvailable) {
  66. my @ret;
  67. if(configDBUsed()) {
  68. @ret = cfgDB_FW_fileList($dir,".*.holiday",@ret);
  69. map { s/\.configDB$//;$_ } @ret;
  70. } else {
  71. if(opendir(DH, $dir)) {
  72. @ret = grep { m/\.holiday$/ } readdir(DH);
  73. closedir(DH);
  74. }
  75. }
  76. $err .= "\nAvailable holiday files: ".
  77. join(" ", map { s/.holiday//;$_ } @ret);
  78. } else {
  79. Log 1, "$name: $err";
  80. }
  81. return $err;
  82. }
  83. $hash->{HOLIDAYFILE} = "$dir/$name.holiday";
  84. my @foundList;
  85. foreach my $l (@holidayfile) {
  86. next if($l =~ m/^\s*#/);
  87. next if($l =~ m/^\s*$/);
  88. my $found;
  89. if($l =~ m/^1/) { # Exact date: 1 MM-DD Holiday
  90. my @args = split(" +", $l, 3);
  91. if($args[1] eq $fordate) {
  92. $found = $args[2];
  93. }
  94. } elsif($l =~ m/^2/) { # Easter date: 2 +1 Ostermontag
  95. ###mh new code for easter sunday calc w.o. requirement for
  96. # DateTime::Event::Easter
  97. # replace $a1 with $1 !!!
  98. # split line from file into args '2 <offset from E-sunday> <tagname>'
  99. my @a = split(" ", $l, 3);
  100. # get month & day for E-sunday
  101. my ($Om,$Od) = western_easter(($lt[5]+1900));
  102. my $timex = mktime(0,0,12,$Od,$Om-1, $lt[5],0,0,-1); # gen timevalue
  103. $timex = $timex + $a[1]*86400; # add offset days
  104. my ($msecond, $mminute, $mhour,
  105. $mday, $mmonth, $myear, $mrest) = localtime($timex);
  106. $myear = $myear+1900;
  107. $mmonth = $mmonth+1;
  108. #Log 1, "$name: Ostern:".sprintf("%04d-%02d-%02d", $lt[5]+1900, $Om, $Od).
  109. # " Target:".sprintf("%04d-%02d-%02d", $myear, $mmonth, $mday);
  110. next if($mday != $fd[3] || $mmonth != $fd[4]+1);
  111. $found = $a[2];
  112. Log 4, "$name: Match day: $a[2]\n";
  113. } elsif($l =~ m/^3/) { # Relative date: 3 -1 Mon 03 Holiday
  114. my @a = split(" +", $l, 5);
  115. my %wd = ("Sun"=>0, "Mon"=>1, "Tue"=>2, "Wed"=>3,
  116. "Thu"=>4, "Fri"=>5, "Sat"=>6);
  117. my @md = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  118. $md[1]=29 if(schaltjahr($fd[5]+1900) && $fd[4] == 1);
  119. my $wd = $wd{$a[2]};
  120. if(!defined($wd)) {
  121. Log 1, "Wrong timespec: $l";
  122. next;
  123. }
  124. next if($wd != $fd[6]); # Weekday
  125. next if($a[3] != ($fd[4]+1)); # Month
  126. if($a[1] > 0) { # N'th day from the start
  127. my $d = $fd[3] - ($a[1]-1)*7;
  128. next if($d < 1 || $d > 7);
  129. } elsif($a[1] < 0) { # N'th day from the end
  130. my $d = $fd[3] - ($a[1]+1)*7;
  131. my $md = $md[$fd[4]];
  132. next if($d > $md || $d < $md-6);
  133. }
  134. $found = $a[4];
  135. } elsif($l =~ m/^4/) { # Interval: 4 MM-DD MM-DD Holiday
  136. my @args = split(" +", $l, 4);
  137. if($args[1] le $fordate && $args[2] ge $fordate) {
  138. $found = $args[3];
  139. }
  140. } elsif($l =~ m/^5/) { # nth weekday since MM-DD / before MM-DD
  141. my @a = split(" +", $l, 6);
  142. # arguments: 5 <distance> <weekday> <month> <day> <name>
  143. my %wd = ("Sun"=>0, "Mon"=>1, "Tue"=>2, "Wed"=>3,
  144. "Thu"=>4, "Fri"=>5, "Sat"=>6);
  145. my $wd = $wd{$a[2]};
  146. if(!defined($wd)) {
  147. Log 1, "Wrong weekday spec: $l";
  148. next;
  149. }
  150. next if $wd != $fd[6]; # check wether weekday matches today
  151. my $yday=$fd[7];
  152. # create time object of target date - mktime counts months and their
  153. # days from 0 instead of 1, so subtract 1 from each
  154. my $tgt=mktime(0,0,1,$a[4],$a[3]-1,$fd[5],0,0,-1);
  155. my $tgtmin=$tgt;
  156. my $tgtmax=$tgt;
  157. my $weeksecs=7*24*60*60; # 7 days, 24 hours, 60 minutes, 60seconds each
  158. my $cd=mktime(0,0,1,$fd[3],$fd[4],$fd[5],0,0,-1);
  159. if ( $a[1] =~ /^-([0-9])*$/ ) {
  160. $tgtmin -= $1*$weeksecs; # Minimum: target date minus $1 weeks
  161. $tgtmax = $tgtmin+$weeksecs; # Maximum: one week after minimum
  162. # needs to be lower than max and greater than or equal to min
  163. if( ($cd ge $tgtmin) && ( $cd lt $tgtmax) ) {
  164. $found=$a[5];
  165. }
  166. } elsif ( $a[1] =~ /^\+?([0-9])*$/ ) {
  167. $tgtmin += ($1-1)*$weeksecs; # Minimum: target date plus $1-1 weeks
  168. $tgtmax = $tgtmin+$weeksecs; # Maximum: one week after minimum
  169. # needs to be lower than or equal to max and greater min
  170. if( ($cd gt $tgtmin) && ( $cd le $tgtmax) ) {
  171. $found=$a[5];
  172. }
  173. } else {
  174. Log 1, "Wrong distance spec: $l";
  175. next;
  176. }
  177. }
  178. push @foundList, $found if($found);
  179. }
  180. push @foundList, "none" if(!int(@foundList));
  181. my $found = join(", ", @foundList);
  182. if($fromTimer) {
  183. RemoveInternalTimer($name);
  184. $nt -= ($lt[2]*3600+$lt[1]*60+$lt[0]); # Midnight
  185. $nt += 86400 + 2; # Tomorrow
  186. $hash->{TRIGGERTIME} = $nt;
  187. InternalTimer($nt, "holiday_refresh", $name, 0);
  188. readingsBeginUpdate($hash);
  189. readingsBulkUpdate($hash, 'state', $found);
  190. readingsBulkUpdate($hash, 'yesterday', CommandGet(undef,"$name yesterday"));
  191. readingsBulkUpdate($hash, 'tomorrow', CommandGet(undef,"$name tomorrow"));
  192. readingsEndUpdate($hash,1);
  193. return undef;
  194. } else {
  195. return $found;
  196. }
  197. }
  198. sub
  199. holiday_Set($@)
  200. {
  201. my ($hash, @a) = @_;
  202. my %sets = (
  203. createPrivateCopy => $hash->{READONLY},
  204. deletePrivateCopy => !$hash->{READONLY},
  205. reload => 1
  206. );
  207. return "unknown argument $a[1], choose one of ".
  208. join(" ", map { "$_:noArg" }
  209. grep { $sets{$_} } keys %sets) if(!$sets{$a[1]});
  210. if($a[1] eq "createPrivateCopy") {
  211. return "Already a private version" if(!$hash->{READONLY});
  212. my $fname = $attr{global}{modpath}."/FHEM/holiday/$hash->{NAME}.holiday";
  213. my ($err, @holidayfile) = FileRead($fname);
  214. return $err if($err);
  215. $fname = $attr{global}{modpath}."/FHEM/$hash->{NAME}.holiday";
  216. $err = FileWrite($fname, @holidayfile);
  217. return $err if($err);
  218. holiday_refresh($hash->{NAME});
  219. } elsif($a[1] eq "deletePrivateCopy") {
  220. return "Not a private version" if($hash->{READONLY});
  221. my $err = FileDelete($attr{global}{modpath}."/FHEM/$hash->{NAME}.holiday");
  222. return $err if($err);
  223. holiday_refresh($hash->{NAME});
  224. } elsif($a[1] eq "reload") {
  225. holiday_refresh($hash->{NAME});
  226. }
  227. return undef;
  228. }
  229. sub
  230. holiday_Get($@)
  231. {
  232. my ($hash, @a) = @_;
  233. shift(@a) if($a[1] && $a[1] eq "MM-DD");
  234. return "argument is missing" if(int(@a) < 2);
  235. my $arg;
  236. if($a[1] =~ m/^[01]\d-[0-3]\d/) {
  237. $arg = $a[1];
  238. } elsif($a[1] =~ m/^(yesterday|today|tomorrow)$/) {
  239. my $t = time();
  240. $t += 86400 if($a[1] eq "tomorrow");
  241. $t -= 86400 if($a[1] eq "yesterday");
  242. my @a = localtime($t);
  243. $arg = sprintf("%02d-%02d", $a[4]+1, $a[3]);
  244. } elsif($a[1] eq "days") {
  245. my $t = time() + ($a[2] ? int($a[2]) : 0)*86400;
  246. my @a = localtime($t);
  247. $arg = sprintf("%02d-%02d", $a[4]+1, $a[3]);
  248. } else {
  249. return "unknown argument $a[1], choose one of ".
  250. "yesterday:noArg today:noArg tomorrow:noArg days:2,3,4,5,6,7 MM-DD";
  251. }
  252. return holiday_refresh($hash->{NAME}, $arg);
  253. }
  254. sub
  255. schaltjahr($)
  256. {
  257. my($jahr) = @_;
  258. return 0 if $jahr % 4; # 2009
  259. return 1 unless $jahr % 400; # 2000
  260. return 0 unless $jahr % 100; # 2100
  261. return 1; # 2012
  262. }
  263. ### mh sub western_easter copied from cpan Date::Time::Easter
  264. ### mh changes marked with # mh
  265. ### mh
  266. ### mh calling parameter is 4 digit year
  267. ### mh
  268. sub
  269. western_easter($)
  270. {
  271. my $year = shift;
  272. my $golden_number = $year % 19;
  273. #quasicentury is so named because its a century, only its
  274. # the number of full centuries rather than the current century
  275. my $quasicentury = int($year / 100);
  276. my $epact = ($quasicentury - int($quasicentury/4) -
  277. int(($quasicentury * 8 + 13)/25) + ($golden_number*19) + 15) % 30;
  278. my $interval = $epact - int($epact/28)*
  279. (1 - int(29/($epact+1)) * int((21 - $golden_number)/11) );
  280. my $weekday = ($year + int($year/4) + $interval +
  281. 2 - $quasicentury + int($quasicentury/4)) % 7;
  282. my $offset = $interval - $weekday;
  283. my $month = 3 + int(($offset+40)/44);
  284. my $day = $offset + 28 - 31* int($month/4);
  285. return $month, $day;
  286. }
  287. sub
  288. holiday_FW_detailFn($$$$)
  289. {
  290. my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
  291. return "" if($defs{$d}{READONLY});
  292. my $cfgDB = (configDBUsed() ? "configDB" : "");
  293. return FW_pH("cmd=style edit $d.holiday $cfgDB",
  294. "<div class=\"dval\">Edit $d.holiday</div>", 0, "dval", 1);
  295. }
  296. 1;
  297. =pod
  298. =item summary define holidays in a local file
  299. =item summary_DE Urlaubs-/Feiertagskalender aus einer lokalen Datei
  300. =begin html
  301. <a name="holiday"></a>
  302. <h3>holiday</h3>
  303. <ul>
  304. <a name="holidaydefine"></a>
  305. <b>Define</b>
  306. <ul>
  307. <code>define &lt;name&gt; holiday</code>
  308. <br><br>
  309. Define a set of holidays. The module will try to open the file
  310. &lt;name&gt;.holiday in the <a href="#modpath">modpath</a>/FHEM directory
  311. first, then in the modpath/FHEM/holiday directory, the latter containing a
  312. set of predefined files. This list of available holiday files will be shown
  313. if an error occurs at the time of the definition, e.g. if you type "define
  314. help holiday"<br>
  315. If entries in the holiday file match the current day, then the STATE of
  316. this holiday instance displayed in the <a href="#list">list</a> command
  317. will be set to the corresponding values, else the state is set to the text
  318. none. Most probably you'll want to query this value in some perl script:
  319. see Value() in the <a href="#perl">perl</a> section or the global attribute
  320. <a href="#holiday2we"> holiday2we</a>.<br> The file will be reread once
  321. every night, to compute the value for the current day, and by each get
  322. command (see below).<br>
  323. <br>
  324. Holiday file definition:<br>
  325. The file may contain comments (beginning with #) or empty lines.
  326. Significant lines begin with a number (type) and contain some space
  327. separated words, depending on the type. The different types are:<br>
  328. <ul>
  329. <li>1<br>
  330. Exact date. Arguments: &lt;MM-DD&gt; &lt;holiday-name&gt;<br>
  331. Exampe: 1 12-24 Christmas
  332. </li>
  333. <li>2<br>
  334. Easter-dependent date. Arguments: &lt;day-offset&gt;
  335. &lt;holiday-name&gt;.
  336. The offset is counted from Easter-Sunday.
  337. <br>
  338. Exampe: 2 1 Easter-Monday<br>
  339. Sidenote: You can check the easter date with:
  340. fhem> { join("-", western_easter(2011)) }
  341. </li>
  342. <li>3<br>
  343. Month dependent date. Arguments: &lt;nth&gt; &lt;weekday&gt;
  344. &lt;month &lt;holiday-name&gt;.<br>
  345. Examples:<br>
  346. <ul>
  347. 3 1 Mon 05 First Monday In May<br>
  348. 3 2 Mon 05 Second Monday In May<br>
  349. 3 -1 Mon 05 Last Monday In May<br>
  350. 3 0 Mon 05 Each Monday In May<br>
  351. </ul>
  352. </li>
  353. <li>4<br>
  354. Interval. Arguments: &lt;MM-DD&gt; &lt;MM-DD&gt; &lt;holiday-name&gt;
  355. .<br>
  356. Note: An interval cannot contain the year-end.
  357. Example:<br>
  358. <ul>
  359. 4 06-01 06-30 Summer holiday<br>
  360. 4 12-20 01-10 Winter holiday # DOES NOT WORK.
  361. Use the following 2 lines instead:<br>
  362. 4 12-20 12-31 Winter holiday<br>
  363. 4 01-01 01-10 Winter holiday<br>
  364. </ul>
  365. </li>
  366. <li>5<br>
  367. Date relative, weekday fixed holiday. Arguments: &lt;nth&gt;
  368. &lt;weekday&gt; &lt;month&gt; &lt;day&gt; &lt; holiday-name&gt;<br>
  369. Note that while +0 or -0 as offsets are not forbidden, their behaviour
  370. is undefined in the sense that it might change without notice.<br>
  371. Examples:<br>
  372. <ul>
  373. 5 -1 Wed 11 23 Buss und Bettag (first Wednesday before Nov, 23rd)<br>
  374. 5 1 Mon 01 31 First Monday after Jan, 31st (1st Monday in February)<br>
  375. </ul>
  376. </li>
  377. </ul>
  378. </ul>
  379. <br>
  380. <a name="holidayset"></a>
  381. <b>Set</b>
  382. <ul>
  383. <li>createPrivateCopy<br>
  384. <ul>
  385. if the holiday file is opened from the FHEM/holiday directory (which is
  386. refreshed by FHEM-update), then it is readonly, and should not be
  387. modified. With createPrivateCopy the file will be copied to the FHEM
  388. directory, where it can be modified.
  389. </ul></li>
  390. <li>deletePrivateCopy<br>
  391. <ul>
  392. delete the private copy, see createPrivateCopy above
  393. </ul></li>
  394. <li>reload<br>
  395. <ul>
  396. set the state, tomorrow and yesterday readings. Useful after manually
  397. editing the file.
  398. </ul></li>
  399. </ul><br>
  400. <a name="holidayget"></a>
  401. <b>Get</b>
  402. <ul>
  403. <code>get &lt;name&gt; &lt;MM-DD&gt;</code><br>
  404. <code>get &lt;name&gt; yesterday</code><br>
  405. <code>get &lt;name&gt; today</code><br>
  406. <code>get &lt;name&gt; tomorrow</code><br>
  407. <code>get &lt;name&gt; days &lt;offset&gt;</code><br>
  408. <br><br>
  409. Return the holiday name of the specified date or the text none.
  410. <br><br>
  411. </ul>
  412. <br>
  413. <a name="holidayattr"></a>
  414. <b>Attributes</b><ul>N/A</ul><br>
  415. </ul>
  416. =end html
  417. =begin html_DE
  418. <a name="holiday"></a>
  419. <h3>holiday</h3>
  420. <ul>
  421. <a name="holidaydefine"></a>
  422. <b>Define</b>
  423. <ul>
  424. <code>define &lt;name&gt; holiday</code>
  425. <br><br>
  426. Definiert einen Satz mit Urlaubsinformationen. Das Modul versucht die
  427. Datei &lt;name&gt;.holiday erst in <a href="#modpath">modpath</a>/FHEM zu
  428. &ouml;ffnen, und dann in modpath/FHEM/holiday, Letzteres enth&auml;lt eine
  429. Liste von per FHEM-update verteilten Dateien f&uuml;r diverse
  430. (Bundes-)L&auml;nder. Diese Liste wird bei einer Fehlermeldung angezeigt.
  431. Wenn Eintr&auml;ge im der Datei auf den aktuellen Tag passen wird der STATE
  432. der Holiday-Instanz die im <a href="#list">list</a> Befehl angezeigt wird
  433. auf die entsprechenden Werte gesetzt. Andernfalls ist der STATE auf den
  434. Text "none" gesetzt.
  435. Meistens wird dieser Wert mit einem Perl Script abgefragt: siehe Value() im
  436. <a href="#perl">perl</a> Abschnitt oder im globalen Attribut <a
  437. href="#holiday2we"> holiday2we</a>.<br> Die Datei wird jede Nacht neu
  438. eingelesen um den Wert des aktuellen Tages zu erzeugen. Auch jeder "get"
  439. Befehl liest die Datei neu ein.
  440. <br><br>
  441. Holiday file Definition:<br>
  442. Die Datei darf Kommentare, beginnend mit #, und Leerzeilen enthalten. Die
  443. entscheidenden Zeilen beginnen mit einer Zahl (Typ) und enthalten durch
  444. Leerzeichen getrennte W&ouml;rter, je nach Typ. Die verschiedenen Typen
  445. sind:<br>
  446. <ul>
  447. <li>1<br>
  448. Genaues Datum. Argument: &lt;MM-TT&gt; &lt;Feiertag-Name&gt;<br>
  449. Beispiel: 1 12-24 Weihnachten
  450. </li>
  451. <li>2<br>
  452. Oster-abh&auml;ngiges Datum. Argument: &lt;Tag-Offset&gt;
  453. &lt;Feiertag-Name&gt;.
  454. Der Offset wird vom Oster-Sonntag an gez&auml;hlt.
  455. <br>
  456. Beispiel: 2 1 Oster-Montag<br>
  457. Hinweis: Das Osterdatum kann vorher gepr&uuml;ft werden:
  458. fhem> { join("-", western_easter(2011)) }
  459. </li>
  460. <li>3<br>
  461. Monats-abh&auml;ngiges Datum. Argument: &lt;X&gt; &lt;Wochentag&gt;
  462. &lt;Monat&gt; &lt;Feiertag-Name&gt;.<br>
  463. Beispiel:<br>
  464. <ul>
  465. 3 1 Mon 05 Erster Montag In Mai<br>
  466. 3 2 Mon 05 Zweiter Montag In Mai<br>
  467. 3 -1 Mon 05 Letzter Montag In Mai<br>
  468. 3 0 Mon 05 Jeder Montag In Mai<br>
  469. </ul>
  470. </li>
  471. <li>4<br>
  472. Intervall. Argument: &lt;MM-TT&gt; &lt;MM-TT&gt; &lt;Feiertag-Name&gt;
  473. .<br>
  474. Achtung: Ein Intervall darf kein Jahresende enthalten.
  475. Beispiel:<br>
  476. <ul>
  477. 4 06-01 06-30 Sommerferien<br>
  478. 4 12-20 01-10 Winterferien # FUNKTIONIER NICHT,
  479. stattdessen folgendes verwenden:<br>
  480. 4 12-20 12-31 Winterferien<br>
  481. 4 01-01 01-10 Winterferien<br>
  482. </ul>
  483. </li>
  484. <li>5<br>
  485. Datum relativ, Wochentags ein fester Urlaubstag/Feiertag. Argument:
  486. &lt;X&gt; &lt;Wochentag&gt; &lt;Monat&gt; &lt;Tag&gt;
  487. &lt;Feiertag-Name&gt;<br> Hinweis: Da +0 oder -0 als Offset nicht
  488. verboten sind, ist das Verhalten hier nicht definiert, kann sich also
  489. ohne Info &auml;ndern;<br>
  490. Beispiel:<br>
  491. <ul>
  492. 5 -1 Wed 11 23 Buss und Bettag (erster Mittwoch vor dem 23. Nov)<br>
  493. 5 1 Mon 01 31 Erster Montag in Februar<br>
  494. </ul>
  495. </li>
  496. </ul>
  497. </ul>
  498. <br>
  499. <a name="holidayset"></a>
  500. <b>Set</b>
  501. <ul>
  502. <li>createPrivateCopy<br>
  503. <ul>
  504. Falls die Datei in der FHEM/holiday Verzeichnis ge&ouml;ffnet wurde,
  505. dann ist sie nicht beschreibbar, da dieses Verzeichnis mit FHEM
  506. update aktualisiert wird. Mit createPrivateCopy kann eine private Kopie
  507. im FHEM Verzeichnis erstellt werden.
  508. </ul></li>
  509. <li>deletePrivateCopy<br>
  510. <ul>
  511. Entfernt die private Kopie, siehe auch createPrivateCopy
  512. </ul></li>
  513. <li>reload<br>
  514. <ul>
  515. setzt die state, tomorrow und yesterday Readings. Wird nach einem
  516. manuellen Bearbeiten der .holiday Datei ben&ouml;tigt.
  517. </ul></li>
  518. </ul><br>
  519. <a name="holidayget"></a>
  520. <b>Get</b>
  521. <ul>
  522. <code>get &lt;name&gt; &lt;MM-DD&gt;</code><br>
  523. <code>get &lt;name&gt; yesterday</code><br>
  524. <code>get &lt;name&gt; today</code><br>
  525. <code>get &lt;name&gt; tomorrow</code><br>
  526. <code>get &lt;name&gt; days &lt;offset&gt;</code><br>
  527. <br><br>
  528. Gibt den Name des Feiertages zum angebenenen Datum zur&uuml;ck oder den
  529. Text none.
  530. <br><br>
  531. </ul>
  532. <br>
  533. <a name="holidayattr"></a>
  534. <b>Attributes</b><ul>
  535. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  536. </ul><br>
  537. </ul>
  538. =end html_DE
  539. =cut