98_notice.pm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. # $Id: 98_notice.pm 11984 2016-08-19 12:47:50Z rudolfkoenig $
  2. # vim: ts=2:et
  3. ################################################################
  4. #
  5. # Copyright notice
  6. #
  7. # (c) 2013 Copyright: Martin Fischer (m_fischer at gmx dot de)
  8. # All rights reserved
  9. #
  10. # This file is part of fhem.
  11. #
  12. # Fhem is free software: you can redistribute it and/or modify
  13. # it under the terms of the GNU General Public License as published by
  14. # the Free Software Foundation, either version 2 of the License, or
  15. # (at your option) any later version.
  16. #
  17. # Fhem is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. # You should have received a copy of the GNU General Public License
  23. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  24. #
  25. ################################################################
  26. package main;
  27. use strict;
  28. use warnings;
  29. use Time::Local;
  30. sub CommandNotice($$);
  31. sub notice_Confirmation($$$);
  32. sub notice_Get($$$);
  33. sub notice_List($$);
  34. sub notice_Read($$$);
  35. use vars qw(@locale);
  36. @locale = qw(de en);
  37. my $confirmationFile = ".notice-confirmation";
  38. ########################################
  39. sub
  40. notice_Initialize($$)
  41. {
  42. my %hash = (
  43. Fn => "CommandNotice",
  44. Hlp => "[confirm|list|reset|view] <id>,view and confirmation of system messages",
  45. );
  46. $cmds{notice} = \%hash;
  47. }
  48. ########################################
  49. sub
  50. CommandNotice($$)
  51. {
  52. my ($cl,$param) = @_;
  53. my $modPath = (-d "updatefhem.dir" ? "updatefhem.dir":$attr{global}{modpath});
  54. my $modDir = "$modPath/FHEM";
  55. my $noticeDir = "$modDir/FhemUtils";
  56. my $name = "notice";
  57. my @commands = qw(confirm condition get list position reset view);
  58. my $ret;
  59. # split arguments
  60. my @args = split(/ +/,$param);
  61. $args[0] = "list" if(!defined($args[0]));
  62. if(!@args ||grep (m/^$args[0]$/, @commands)) {
  63. my $cmd = $args[0];
  64. if($cmd eq "list") {
  65. # view all a list of notes
  66. my $type = "all";
  67. if(defined($args[1])) {
  68. $type = $args[1];
  69. }
  70. $ret = notice_List($noticeDir,$type);
  71. } elsif($cmd eq "view") {
  72. # view a single note
  73. return "notice view needs an argument"
  74. if(!defined($args[1]));
  75. my $id = $args[1];
  76. my $notice_ref = {};
  77. $notice_ref = notice_Read($notice_ref,$noticeDir,$args[1]);
  78. return "Nothing to view. Maybe wrong ID?"
  79. if(!keys %$notice_ref);
  80. my $locale = "en";
  81. my $header = 1;
  82. if(@args == 3) {
  83. $locale = (grep (m/^$args[2]$/, @locale)) ? $args[2] : "en";
  84. $locale = (grep (m/^$args[2]$/, @locale)) ? $args[2] : "en";
  85. $header = ($args[2] eq "noheader") ? 0 : 1;
  86. } elsif(@args == 4) {
  87. if(grep (m/^$args[2]$/, @locale)) {
  88. $locale = (grep (m/^$args[2]$/, @locale)) ? $args[2] : "en";
  89. $header = ($args[3] eq "noheader") ? 0 : 1;
  90. } elsif($args[2] eq "noheader") {
  91. $header = ($args[2] eq "noheader") ? 0 : 1;
  92. $locale = (grep (m/^$args[3]$/, @locale)) ? $args[3] : "en";
  93. }
  94. }
  95. if($header) {
  96. $ret = sprintf("%-10s: %s\n","ID",$id);
  97. $ret .= sprintf("%-10s: %s\n","From",$notice_ref->{$id}{from})
  98. if(exists $notice_ref->{$id}{from});
  99. $ret .= sprintf("%-10s: %s\n","Date",$notice_ref->{$id}{date})
  100. if(exists $notice_ref->{$id}{from});
  101. $ret .= sprintf("%-10s: %s\n","Expire",$notice_ref->{$id}{expire})
  102. if(exists $notice_ref->{$id}{expire});
  103. $ret .= sprintf("%-10s: %s\n","Title",$notice_ref->{$id}{locale}{$locale}{title})
  104. if(exists $notice_ref->{$id}{locale}{$locale}{title});
  105. $ret .= "### Start of Text\n";
  106. }
  107. foreach my $line (@{$notice_ref->{$id}{locale}{$locale}{text}}) {
  108. $ret .= $line."\n";
  109. }
  110. $ret .= "### End of Text\n" if($header);
  111. } elsif($cmd eq "confirm") {
  112. # confirm a note
  113. return "notice view needs an argument"
  114. if(!defined($args[1]));
  115. my $id = $args[1];
  116. my $notice_ref = {};
  117. $notice_ref = notice_Read($notice_ref,$noticeDir,$id);
  118. return "Nothing to view. Maybe wrong ID?"
  119. if(!keys %$notice_ref);
  120. if(!defined($notice_ref->{$id}{confirm}) ||
  121. (defined($notice_ref->{$id}{confirm}) && $notice_ref->{$id}{confirm} == 0) ) {
  122. return "$id needs no confirmation.";
  123. } else {
  124. my $confirmation = 1;
  125. if(@args > 2) {
  126. shift @args;
  127. shift @args;
  128. $confirmation = "@args";
  129. }
  130. $ret = notice_Confirmation($noticeDir,$id,$confirmation);
  131. }
  132. } elsif($cmd eq "get") {
  133. # get list of notes
  134. my $type = (defined($args[1])) ? $args[1] : "all";
  135. my $value = (defined($args[2]) && $args[2] =~ /[0-8]/) ? $args[2] : 0;
  136. return notice_Get($noticeDir,$type,$value);
  137. } elsif($cmd eq "position") {
  138. # returns position of notice
  139. return "notice position needs an argument"
  140. if(!defined($args[1]));
  141. my $id = $args[1];
  142. my $notice_ref = {};
  143. $notice_ref = notice_Read($notice_ref,$noticeDir,$id);
  144. return (defined($notice_ref->{$id}{position})) ? $notice_ref->{$id}{position} : undef;
  145. } elsif($cmd eq "reset") {
  146. # reset all confirmations
  147. if(-e "$noticeDir/$confirmationFile") {
  148. if(defined($args[1] && lc($args[1]) eq "yes")) {
  149. my $cmdret = unlink "$noticeDir/$confirmationFile";
  150. if(!$cmdret) {
  151. $ret = "an error occured while deleting file '$noticeDir/$confirmationFile': $!";
  152. } else {
  153. $ret = "all confirmations deleted successfully.";
  154. }
  155. } else {
  156. $ret = "This command delete all confirmations.\n";
  157. $ret .= "If you really want to do this, call 'notice reset yes'";
  158. return $ret;
  159. }
  160. } else {
  161. $ret = "nothing to do. no confirmation exists.";
  162. }
  163. } elsif($cmd eq "condition") {
  164. # supplies a value of an embedded test
  165. return "condition view needs an argument"
  166. if(!defined($args[1]));
  167. my $id = $args[1];
  168. my $notice_ref = {};
  169. $notice_ref = notice_Read($notice_ref,$noticeDir,$id);
  170. return "Nothing to view. Maybe wrong ID?"
  171. if(!keys %$notice_ref);
  172. my %conditions;
  173. foreach my $key (sort %{$notice_ref->{$id}}) {
  174. my $order;
  175. if(lc($key) =~ /^key_/) {
  176. (undef,$order) = split("_",$key);
  177. if(defined($notice_ref->{$id}{"val_$order"})) {
  178. $conditions{$notice_ref->{$id}{$key}}{value} = ($notice_ref->{$id}{"val_$order"}) ?
  179. eval $notice_ref->{$id}{"val_$order"} : undef;
  180. $conditions{$notice_ref->{$id}{$key}}{condition} = (defined($notice_ref->{$id}{"con_$order"})) ?
  181. $notice_ref->{$id}{"con_$order"} : "";
  182. Log 5, "notice id:$id condition key:".$notice_ref->{$id}{$key} . " " .
  183. "value:" .$conditions{$notice_ref->{$id}{$key}}{value} . " " .
  184. "condition:".$notice_ref->{$id}{"val_$order"};
  185. }
  186. }
  187. }
  188. if(keys %conditions) {
  189. foreach my $key (sort keys %conditions) {
  190. Log 5, "notice id:$id condition key:$key value:$conditions{$key}{value} condition:$conditions{$key}{condition}";
  191. $ret .= "$key:$conditions{$key}{value}:$conditions{$key}{condition}";
  192. $ret .= "|";
  193. }
  194. chop $ret;
  195. return $ret;
  196. } else {
  197. return undef;
  198. }
  199. }
  200. } else {
  201. return "Unknown argument $args[0]; choose one of " . join(" ", sort @commands);
  202. }
  203. return $ret;
  204. }
  205. ########################################
  206. sub
  207. notice_List($$)
  208. {
  209. my ($noticeDir,$type) = @_;
  210. $type = ($type eq "all") ? ".*" : $type;
  211. my @dir;
  212. my $ret;
  213. if(opendir(my $DH, "$noticeDir")) {
  214. @dir = grep { /^$type-.*\d+$/ && -f "$noticeDir/$_" } readdir($DH);
  215. closedir $DH;
  216. my $notice_ref = {};
  217. foreach my $file (@dir) {
  218. $notice_ref = notice_Read($notice_ref,$noticeDir,$file);
  219. }
  220. my @col1 = sort keys %{$notice_ref};
  221. my $col1 = (reverse sort { $a <=> $b } map { length($_) } @col1)[0];
  222. if(!keys %$notice_ref) {
  223. $ret = "==> nothing found";
  224. } else {
  225. my @confirmationFile;
  226. if(open(my $FH, "<$noticeDir/$confirmationFile")) {
  227. Log 5, "notice read file: $noticeDir/$confirmationFile";
  228. while(my $line = <$FH>) {
  229. chomp $line;
  230. push(@confirmationFile,$line);
  231. }
  232. close $FH;
  233. }
  234. foreach my $lang (sort @locale) {
  235. $ret .= "==> Language: $lang\n";
  236. $ret .= sprintf(" %-*s %-10s %-10s %-10s %s\n",$col1,"ID","Published","Expired","Confirmed","Description");
  237. foreach my $notice (sort keys %{$notice_ref}) {
  238. my ($dateTime,$oldConfirmation);
  239. next if(!exists $notice_ref->{$notice}{locale}{$lang});
  240. foreach my $line (@confirmationFile) {
  241. if($line =~ /^$notice\s*/) {
  242. ($dateTime,$oldConfirmation) = $line =~ /^.*\s*(\d{4}-\d{2}-\d{2})\s\d{2}:\d{2}:\d{2}\s*(.*)$/;
  243. $dateTime = substr($dateTime,8,2).".".substr($dateTime,5,2).".".substr($dateTime,0,4);
  244. }
  245. }
  246. $ret .= sprintf(" %-*s %-10s %-10s %-10s %s\n",
  247. $col1,
  248. $notice,
  249. (defined($notice_ref->{$notice}{publish}) && $notice_ref->{$notice}{publish} ne "0") ?
  250. $notice_ref->{$notice}{publish} : "actually",
  251. (defined($notice_ref->{$notice}{expire}) && $notice_ref->{$notice}{expire} ne "0") ?
  252. $notice_ref->{$notice}{expire} : "never",
  253. ($dateTime) ? $dateTime :
  254. (defined($notice_ref->{$notice}{confirm}) && $notice_ref->{$notice}{confirm} ne "0") ?
  255. "no" : "not needed",
  256. $notice_ref->{$notice}{locale}{$lang}{title});
  257. }
  258. $ret .= "\n";
  259. }
  260. chomp $ret;
  261. }
  262. } else {
  263. $ret = "update could not open directory '$noticeDir': $!";
  264. Log 1, $ret;
  265. }
  266. return $ret;
  267. }
  268. ########################################
  269. # value: 0 = all
  270. # 1 = not confirmed
  271. # 2 = not expired
  272. # 3 = not confirmed, not expired
  273. # 4 = published
  274. # 5 = not confirmed, published
  275. # 6 = not expired, published
  276. # 7 = not confirmed, not expired, published
  277. # 8 = confirmed
  278. sub
  279. notice_Get($$$)
  280. {
  281. my ($noticeDir,$type,$value) = @_;
  282. $value = ($value) ? $value : 0;
  283. my @now = localtime();
  284. my @dir;
  285. if(opendir(my $DH, "$noticeDir")) {
  286. my $search = ($type eq "all") ? ".*" : $type;
  287. @dir = grep { /^$search-.*\d+$/ && -f "$noticeDir/$_" } readdir($DH);
  288. closedir $DH;
  289. } else {
  290. Log 1, "notice could not open directory '$noticeDir': $!";
  291. }
  292. my @confirmed;
  293. if($value == 1 || $value == 3 || $value == 5 || $value == 7 || $value == 8) {
  294. if(open(my $FH, "<$noticeDir/$confirmationFile")) {
  295. Log 5, "notice read file: $noticeDir/$confirmationFile";
  296. while(my $line = <$FH>) {
  297. my ($id,undef) = split(" ",$line);
  298. if($type eq "all") {
  299. push(@confirmed,$id);
  300. } elsif($id =~ /^$type/) {
  301. push(@confirmed,$id);
  302. }
  303. }
  304. close $FH;
  305. }
  306. }
  307. if(@dir) {
  308. my $notice_ref = {};
  309. foreach my $file (sort @dir) {
  310. $notice_ref = notice_Read($notice_ref,$noticeDir,$file);
  311. }
  312. if(!keys %$notice_ref) {
  313. return undef;
  314. } else {
  315. my $ret;
  316. if($value == 0) {
  317. # all
  318. $ret = join(",",sort @dir);
  319. } elsif($value == 1) {
  320. # not confirmed
  321. $ret = _notConfirmed($notice_ref,@confirmed);
  322. Log 5, "notice notConfirmed:$ret";
  323. } elsif($value == 2) {
  324. # not expired
  325. $ret = _notExpired($notice_ref,@now);
  326. Log 5, "notice notExpired:$ret";
  327. } elsif($value == 3) {
  328. # not confirmed, not expired
  329. my $notConfirmed = _notConfirmed($notice_ref,@confirmed);
  330. my $notExpired = _notExpired($notice_ref,@now);
  331. Log 5, "notice notConfirmed:$notConfirmed notExpired:$notExpired";
  332. my @merged;
  333. foreach my $id (@dir) {
  334. push (@merged, $id) if($notConfirmed =~ /$id/ && $notExpired =~ /$id/);
  335. }
  336. $ret = join(",",sort @merged);
  337. } elsif($value == 4) {
  338. # published
  339. $ret = _published($notice_ref,@now);
  340. Log 5, "notice published:$ret";
  341. } elsif($value == 5) {
  342. # not confirmed, published
  343. my $notConfirmed = _notConfirmed($notice_ref,@confirmed);
  344. my $published = _published($notice_ref,@now);
  345. Log 5, "notice notConfirmed:$notConfirmed published:$published";
  346. my @merged;
  347. foreach my $id (sort @dir) {
  348. push (@merged, $id) if($notConfirmed =~ /$id/ && $published =~ /$id/);
  349. }
  350. $ret = join(",",sort @merged);
  351. } elsif($value == 6) {
  352. # not expired, published
  353. my $notExpired = _notExpired($notice_ref,@now);
  354. my $published = _published($notice_ref,@now);
  355. Log 5, "notice notExpired:$notExpired published:$published";
  356. my @merged;
  357. foreach my $id (sort @dir) {
  358. push (@merged, $id) if($notExpired =~ /$id/ && $published =~ /$id/);
  359. }
  360. $ret = join(",",sort @merged);
  361. } elsif($value == 7) {
  362. # not confirmed, not expired, published
  363. my $notConfirmed = _notConfirmed($notice_ref,@confirmed);
  364. my $notExpired = _notExpired($notice_ref,@now);
  365. my $published = _published($notice_ref,@now);
  366. Log 5, "notice notConfirmed:$notConfirmed notExpired:$notExpired published:$published";
  367. my @merged;
  368. foreach my $id (sort @dir) {
  369. push (@merged, $id) if($notConfirmed =~ /$id/ && $notExpired =~ /$id/ && $published =~ /$id/);
  370. }
  371. $ret = join(",",sort @merged);
  372. } elsif($value == 8) {
  373. # confirmed
  374. $ret = join(",",sort @confirmed);
  375. }
  376. return $ret;
  377. }
  378. } else {
  379. return undef;
  380. }
  381. }
  382. ########################################
  383. sub
  384. _notConfirmed($@)
  385. {
  386. my ($notice_ref,@confirmed) = @_;
  387. my @ret;
  388. foreach my $id (sort keys %{$notice_ref}) {
  389. push(@ret,$id)
  390. if(defined($notice_ref->{$id}{confirm}) &&
  391. $notice_ref->{$id}{confirm} != 0 && !grep (m/^$id$/,@confirmed));
  392. }
  393. return join(",",@ret);
  394. }
  395. ########################################
  396. sub
  397. _notExpired($@)
  398. {
  399. my ($notice_ref,@now) = @_;
  400. my @ret;
  401. foreach my $id (sort keys %{$notice_ref}) {
  402. my ($d,$m,$y);
  403. if(defined($notice_ref->{$id}{expire}) && $notice_ref->{$id}{expire} =~ /\d{2}.\d{2}.\d{4}/) {
  404. $d = substr($notice_ref->{$id}{expire},0,2);
  405. $m = substr($notice_ref->{$id}{expire},3,2)-1;
  406. $y = substr($notice_ref->{$id}{expire},6,4)-1900;
  407. }
  408. push(@ret,$id)
  409. if(!defined($notice_ref->{$id}{expire}) ||
  410. (defined($notice_ref->{$id}{expire}) && $notice_ref->{$id}{expire} !~ /\d{2}.\d{2}.\d{4}/) ||
  411. (defined($notice_ref->{$id}{expire}) && $notice_ref->{$id}{expire} =~ /\d{2}.\d{2}.\d{4}/ &&
  412. notice_epochDate($now[3],$now[4],$now[5]) <= notice_epochDate($d,$m,$y)));
  413. }
  414. return join(",",@ret);
  415. }
  416. ########################################
  417. sub
  418. _published($@)
  419. {
  420. my ($notice_ref,@now) = @_;
  421. my @ret;
  422. foreach my $id (sort keys %{$notice_ref}) {
  423. my ($d,$m,$y);
  424. if(defined($notice_ref->{$id}{publish}) && $notice_ref->{$id}{publish} =~ /\d{2}.\d{2}.\d{4}/) {
  425. $d = substr($notice_ref->{$id}{publish},0,2);
  426. $m = substr($notice_ref->{$id}{publish},3,2)-1;
  427. $y = substr($notice_ref->{$id}{publish},6,4)-1900;
  428. }
  429. push(@ret,$id)
  430. if(!defined($notice_ref->{$id}{publish}) ||
  431. (defined($notice_ref->{$id}{publish}) && $notice_ref->{$id}{publish} !~ /\d{2}.\d{2}.\d{4}/) ||
  432. (defined($notice_ref->{$id}{publish}) && $notice_ref->{$id}{publish} =~ /\d{2}.\d{2}.\d{4}/ &&
  433. notice_epochDate($now[3],$now[4],$now[5]) >= notice_epochDate($d,$m,$y)));
  434. }
  435. return join(",",@ret);
  436. }
  437. ########################################
  438. sub
  439. notice_epochDate($$$)
  440. {
  441. my ($day,$month,$year) = @_;
  442. return timelocal("0","0","0",$day,$month,$year);
  443. }
  444. ########################################
  445. sub
  446. notice_Confirmation($$$)
  447. {
  448. my ($noticeDir,$id,$confirmation) = @_;
  449. my @file;
  450. my $confirmed = 0;
  451. my $oldConfirmation = "";
  452. my $dateTime;
  453. my $now = TimeNow();
  454. my $ret;
  455. if(open(my $FH, "<$noticeDir/$confirmationFile")) {
  456. Log 5, "notice read file: $noticeDir/$confirmationFile";
  457. while(my $line = <$FH>) {
  458. chomp $line;
  459. if($line =~ /^$id\s*/) {
  460. ($dateTime,$oldConfirmation) = $line =~ /^.*\s*(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s*(.*)$/;
  461. $confirmed = 1;
  462. }
  463. push(@file,$line);
  464. }
  465. close $FH;
  466. }
  467. if($confirmed == 0) {
  468. push(@file,"$id $now $confirmation\n");
  469. }
  470. if($oldConfirmation eq $confirmation) {
  471. $ret = "$id already confirmed on $dateTime: $oldConfirmation";
  472. } else {
  473. if(open(my $FH, ">$noticeDir/$confirmationFile")) {
  474. Log 5, "notice write file: $noticeDir/$confirmationFile";
  475. foreach my $line (sort @file) {
  476. if($line =~ /^$id\s*/) {
  477. print $FH "$id $now $confirmation\n";
  478. if(!$oldConfirmation) {
  479. $ret = "$id confirmed on $now: $confirmation";
  480. } else {
  481. $ret = "$id changed on $now: $confirmation";
  482. }
  483. Log 1, "notice $ret";
  484. } else {
  485. print $FH "$line\n";
  486. }
  487. }
  488. } else {
  489. $ret = "error while writing file: $noticeDir/$confirmationFile: $!";
  490. Log 1, "notice $ret";
  491. }
  492. }
  493. return $ret;
  494. }
  495. ########################################
  496. sub
  497. notice_Read($$$)
  498. {
  499. my ($notice_ref,$noticeDir,$noticeFile) = @_;
  500. my %notice = %$notice_ref if($notice_ref && ref($notice_ref) eq "HASH");
  501. if(open(my $FH, "<$noticeDir/$noticeFile")) {
  502. Log 5, "notice read file: $noticeDir/$noticeFile";
  503. my $key;
  504. my $value;
  505. my $locale;
  506. while(my $line = <$FH>) {
  507. chomp $line;
  508. if(uc($line) =~ /^#\s.*:.*$/ && uc($line) !~ /^#\s*NOTICE_\S{2}$/ && uc($line) !~ /^#\s*TITLE_\S{2}\s*:.*$/) {
  509. ($key,$value) = $line =~ /^#\s*(.*)\s*:\s*(.*)$/;
  510. $notice{$noticeFile}{lc($key)} = $value;
  511. } elsif (uc($line) =~ /^#\s*TITLE_\S{2}\s*:.*$/) {
  512. ($locale,$value) = $line =~ /^#\s*TITLE_(\S{2})\s*:\s*(.*)$/;
  513. $notice{$noticeFile}{locale}{lc($locale)}{title} = $value;
  514. } elsif (uc($line) =~ /^#\s*NOTICE_\S{2}$/) {
  515. ($locale) = $line =~ /^#\s*NOTICE_(\S{2})$/;
  516. } else {
  517. $locale = "EN" if(!$locale);
  518. push @{ $notice{$noticeFile}{locale}{lc($locale)}{text} }, $line;
  519. }
  520. }
  521. close $FH;
  522. } else {
  523. Log 1, "update could not open notice '$noticeDir/$noticeFile': $!";
  524. return undef;
  525. }
  526. return \%notice;
  527. }
  528. =pod
  529. =item command
  530. =item summary read and confirm system messages
  531. =item summary_DE Systemnachrichten anzeigen und best&auml;tigen
  532. =begin html
  533. <a name="notice"></a>
  534. <h3>notice</h3>
  535. <ul>
  536. <code>notice [confirm [value]|list [&lt;keyword&gt;]|reset [yes]|view &lt;id&gt; [noheader|[de|en]]]</code><br>
  537. <br>
  538. View and confirmation of system messages.
  539. <br>
  540. <br>
  541. During an update or a system start from FHEM sometimes it is necessary to
  542. inform the user about important changes or additions. It may be necessary
  543. to confirm a system message by the user.
  544. <br>
  545. <br>
  546. By entering the command '<code>notice</code>' a list of all messages is displayed.
  547. Are messages available in different languages, they are ordered by language.
  548. <br>
  549. Example:
  550. <blockquote><code><pre>
  551. fhem&gt; notice
  552. ==&gt; Language: de
  553. ID Published Expired Confirmed Description
  554. advice-20130128-002 actually never not needed kurze beschreibung
  555. update-20130128-002 31.01.2013 01.02.2013 no kurze beschreibung
  556. ==&gt; Language: en
  557. ID Published Expired Confirmed Description
  558. advice-20130128-001 actually never no short description
  559. advice-20130128-002 actually never not needed short description
  560. update-20130128-001 actually never no short description
  561. update-20130128-002 31.01.2013 01.02.2013 no short description
  562. </pre></code></blockquote>
  563. By entering '<code>notice list &lt;keyword&gt;</code>' the output of the list contains only
  564. available messages that starts with '<code>&lt;keyword&gt;</code>'.
  565. <br>
  566. Example:
  567. <blockquote><code><pre>
  568. fhem&gt; notice list update
  569. ==&gt; Language: de
  570. ID Published Expired Confirmed Description
  571. update-20130128-002 31.01.2013 01.02.2013 no kurze beschreibung
  572. ==&gt; Language: en
  573. ID Published Expired Confirmed Description
  574. update-20130128-001 actually never no short description
  575. update-20130128-002 31.01.2013 01.02.2013 no short description
  576. </pre></code></blockquote>
  577. To display a single message, enter the command '<code>notice view &lt;id&gt;</code>' where <code>id</code>
  578. is the Identifier of the message. You can use the optional parameter <code>noheader</code>
  579. or the language codes <code>de</code> or <code>en</code> to display the message
  580. without the header informations or in your prefered language if available.
  581. <br>
  582. Example:
  583. <blockquote><code><pre>
  584. fhem&gt; notice view advice-20130128-002 de
  585. ID : advice-20130128-002
  586. From : M. Fischer
  587. Date : 28.01.2013
  588. Expire : 0
  589. Title : kurze beschreibung
  590. ### Start of Text
  591. test-advice
  592. dies ist ein test
  593. 001
  594. ### End of Text
  595. </pre></code></blockquote>
  596. If it is necessary to confirm a message, this is be done by entering '<code>notice confirm &lt;id&gt; [value]</code>'.
  597. The optional argument <code>value</code> will also be stored with the confirmation.
  598. <br>
  599. Example:
  600. <blockquote><code><pre>
  601. fhem&gt; notice confirm update-20130128-001 foo:bar
  602. update-20130128-001 confirmed on 2013-01-29 20:58:57: foo:bar
  603. </pre></code></blockquote>
  604. Sometimes it is necessary to reset all confirmations. This is be done by entering
  605. '<code>notice reset</code>'.
  606. <br>
  607. Example:
  608. <blockquote><code><pre>
  609. fhem&gt; notice reset
  610. This command delete all confirmations.
  611. If you really want to do this, call 'notice reset yes'
  612. </pre></code></blockquote>
  613. <br>
  614. <strong>For developers only:</strong>
  615. <br>
  616. <br>
  617. <code>notice [condition &lt;id&gt;|get &lt;keyword&gt; &lt;value&gt;|position &lt;id&gt;]</code><br>
  618. <br>
  619. <br>
  620. These arguments are normally not needed by any user.
  621. <br>
  622. <br>
  623. A message may optionally contains one or more code snippets. The argument <code>condition</code> supplies the determined
  624. value(s) of the embedded test(s) as a key:value pair. If more than one pair returned, they they are seperated by <code>|</code>.
  625. It is possible to define your own rules for a condition, like <code>!empty</code> or <code>&gt;>5</code> and so on. An example
  626. of a condition is shown in the below example message file.
  627. Example:
  628. <blockquote><code><pre>
  629. fhem&gt; notice condition update-20130127-001
  630. configfile:./fhem.cfg|sendStatistics:never:!empty
  631. </pre></code></blockquote>
  632. The argument <code>get</code>, followed by a <code>keyword</code> and a number from 0 to 8, returns a
  633. comma seperated list of message ids.
  634. The possible outputs are:
  635. <ul>
  636. <li><code>0 returns a list of all messages.</code></li>
  637. <li><code>1 returns a list of unconfirmed messages.</code></li>
  638. <li><code>2 returns a list of messages that are not expired.</code></li>
  639. <li><code>3 returns a list of messages that are not expired and unconfirmed.</code></li>
  640. <li><code>4 returns a list of published messages.</code></li>
  641. <li><code>5 returns a list of unconfirmed and published messages.</code></li>
  642. <li><code>6 returns a list of published messages that are not expired.</code></li>
  643. <li><code>7 returns a list of published, unconfirmed and not expired messages.</code></li>
  644. <li><code>8 returns a list of confirmed messages.</code></li>
  645. </ul>
  646. Example:
  647. <blockquote><code><pre>
  648. fhem&gt; notice get all 2
  649. advice-20130128-001,advice-20130128-002,update-20130128-001,update-20130128-002
  650. </pre></code></blockquote>
  651. The argument <code>position</code> followed by an <code>&lt;id&gt;</code> returns the view position of a message if defined.
  652. <br>
  653. Example:
  654. <blockquote><code><pre>
  655. fhem&gt; notice position update-20130128-001
  656. before
  657. </pre></code></blockquote>
  658. Example of a message file:
  659. <blockquote><code><pre>
  660. # FROM: M. Fischer
  661. # DATE: 28.01.2013
  662. # CONFIRM: 1
  663. # PUBLISH: 31.01.2013
  664. # EXPIRE: 01.02.2013
  665. # KEY_1: sendStatistics
  666. # VAL_1: AttrVal("global","sendStatistics",undef);
  667. # CON_1: !empty
  668. # KEY_2: configfile
  669. # VAL_2: AttrVal("global","configfile",undef);
  670. # POSITION: top
  671. # TITLE_DE: kurze beschreibung
  672. # NOTICE_DE
  673. Hinweis:
  674. dies ist ein test
  675. # TITLE_EN: short description
  676. # NOTICE_EN
  677. Advice:
  678. this is a test
  679. </pre></code></blockquote>
  680. The keywords '<code>FROM, DATE, CONFIRM, PUBLISH, EXPIRE, TITLE_DE, TITLE_EN, NOTICE_DE, NOTICE_EN</code>' are fixed.
  681. It is possible to add any key:value string to these files. Also it is possible to set only one or both keywords of
  682. '<code>TITLE_DE, TITLE_EN</code>' and '<code>NOTICE_DE, NOTICE_EN</code>'.
  683. </ul>
  684. =end html
  685. 1;