97_GROUP.pm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. ################################################################################
  2. # 97 GROUP
  3. # Feedback: http://groups.google.com/group/fhem-users
  4. # Autor: Axel Rieger fhem[bei]anax.info
  5. # Stand: 05.07.2012
  6. # Version: 1.2
  7. ################################################################################
  8. # Usage:
  9. # define <New-Group-Name> GROUP <CATEGORY>
  10. # set <New-Group-Name> ADD/DEL <NAME>:<DEVICENAME>:<READING>
  11. # READING-VALUES are first searched there $hash{<DEVICENAME>}{READINGS}{<READING>}
  12. # and for the second there $hash{<DEVICENAME>}{<READING>}
  13. #
  14. # Special READINGs
  15. # FHT-Device and READING = DNW
  16. # Displays: Day-Temp Night-Temp WindowOpen-Temp: D:22.00 N:18.00 W:5.50
  17. #
  18. # Special Categories:
  19. # SHOWLEFT -> DisplayName & Value appear on the Left-Side (DIV-Left)
  20. #
  21. # Unkown READINGS appear as "???"
  22. # Unkown TimeStamps appear as "****-**-** **:**:**"
  23. ################################################################################
  24. package main;
  25. use strict;
  26. use warnings;
  27. use Data::Dumper;
  28. use vars qw(%data);
  29. #-------------------------------------------------------------------------------
  30. sub GROUP_Initialize($)
  31. {
  32. my ($hash) = @_;
  33. $hash->{DefFn} = "GRP_Define";
  34. $hash->{SetFn} = "GRP_Set";
  35. $hash->{UndefFn} = "GRP_Undef";
  36. $hash->{AttrList} = "loglevel:0,5";
  37. # CGI
  38. my $name = "GROUPS";
  39. my $fhem_url = "/" . $name ;
  40. $data{FWEXT}{$fhem_url}{FUNC} = "GRP_CGI";
  41. $data{FWEXT}{$fhem_url}{LINK} = $name;
  42. $data{FWEXT}{$fhem_url}{NAME} = $name;
  43. # Special READINGs
  44. $data{GROUP}{READINGS}{DNW} = "GRP_GET_READING_DNW";
  45. return undef;
  46. }
  47. #-------------------------------------------------------------------------------
  48. sub GRP_Define(){
  49. # define <GROUP-NMAE> GROUP <CATEGORY-NAME>
  50. # If no Cat is defined:<GROUP-NMAE> = <CATEGORY-NAME>
  51. my ($self, $defs) = @_;
  52. my $name = $self->{NAME};
  53. # defs = $a[0] <GROUP-DEVICE-NAME> $a[1] GROUP $a[2]<CATEGORY-NAME>;
  54. my @a = split(/ /, $defs);
  55. # CATEGORY
  56. my $cat = $name;
  57. if(int(@a) gt 2){$cat = $a[2];}
  58. my $ret = &GRP_HANDLE_CAT($name,$cat);
  59. # Save cat to State
  60. $self->{STATE} = $cat;
  61. #Default ROOM DEF.GROUP
  62. $attr{$self->{NAME}}{room} = "DEF.GROUP";
  63. return undef;
  64. }
  65. #-------------------------------------------------------------------------------
  66. sub GRP_Undef(){
  67. my ($self, $name) = @_;
  68. # $dc = Device-Count in categorie
  69. my $dc = 1;
  70. if(defined($modules{GROUP}{defptr})) {
  71. foreach my $d (sort keys %{$modules{GROUP}{defptr}}){
  72. if(defined($modules{GROUP}{defptr}{$d}{$name})){
  73. delete $modules{GROUP}{defptr}{$d}{$name};
  74. $dc = keys(%{$modules{GROUP}{defptr}{$d}});
  75. }
  76. # Delete empty Categories
  77. if($dc eq 0) {
  78. Log 0, "GROUP UNDEF DELETE CAT: $d";
  79. delete $modules{GROUP}{defptr}{$d};
  80. };
  81. }
  82. }
  83. $dc = 1;
  84. if(defined($modules{GROUP}{conf})) {
  85. foreach my $c (sort keys %{$modules{GROUP}{conf}}){
  86. if(defined($modules{GROUP}{conf}{$c}{$name})){
  87. delete $modules{GROUP}{conf}{$c}{$name};
  88. $dc = keys(%{$modules{GROUP}{conf}{$c}});
  89. }
  90. # Delete empty Categories
  91. if($dc eq 0) {
  92. Log 0, "GROUP UNDEF DELETE CAT: $c";
  93. delete $modules{GROUP}{defptr}{$c};
  94. };
  95. }
  96. }
  97. # ??? empty CAT is left ???
  98. # Check for empty categories
  99. return undef;
  100. }
  101. #-------------------------------------------------------------------------------
  102. sub GRP_Set()
  103. {
  104. # set <NAME> ADD/DEL <NAME>:<DEVICE-NAME>:<READING>
  105. # @a => a[0]:<NAME>; a[1]=ADD; a[2]= <DEVICE-NAME>:<READING>
  106. my ($self, @a) = @_;
  107. # FHEMWEB Question....select
  108. return "GROUP Unknown argument $a[1], choose one of ". join(" ",sort keys %{$self->{READINGS}}) if($a[1] eq "?");
  109. # ADD
  110. if($a[1] eq "ADD") {
  111. my ($name,$dev,$reading) = split(/:/,$a[2]);
  112. if(!defined($defs{$dev})){return "Device unkwon";}
  113. $self->{READINGS}{$name}{VAL} = $dev . ":" . $reading;
  114. $self->{READINGS}{$name}{TIME} = TimeNow();
  115. }
  116. if($a[1] eq "DEL") {
  117. delete $self->{READINGS}{$a[2]};
  118. }
  119. # Set GROUP-CAT
  120. # set <NAME> CAT <CATEGORY-NAME>
  121. if($a[1] eq "CAT") {
  122. $self->{STATE} = $a[2];
  123. }
  124. return undef;
  125. }
  126. #-------------------------------------------------------------------------------
  127. sub GRP_CGI()
  128. {
  129. my ($htmlarg) = @_;
  130. # htmlarg = /GROUPS/<CAT-NAME>
  131. my $Cat = GRP_CGI_DISPTACH_URL($htmlarg);
  132. Log 0,"GROUPS-FW-FEHM: $FW_ME";
  133. if(!defined($Cat)){$Cat = ""};
  134. my ($ret_html);
  135. $ret_html = "<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.01\/\/EN\" \"http:\/\/www.w3.org\/TR\/html4\/strict.dtd\">\n";
  136. $ret_html .= "<html>\n";
  137. $ret_html .= "<head>\n";
  138. $ret_html .= &GRP_CGI_CSS();
  139. $ret_html .= "<title>FHEM GROUPS<\/title>\n";
  140. $ret_html .= "<link href=\"$FW_ME/darkstyle.css\" rel=\"stylesheet\"/>\n";
  141. $ret_html .= "<\/head>\n";
  142. $ret_html .= "<body>\n";
  143. # DIV HDR
  144. $ret_html .= &GRP_CGI_TOP($Cat);
  145. # DIV LEFT
  146. $ret_html .= &GRP_CGI_LEFT($Cat);
  147. # DIV RIGHT
  148. $ret_html .= &GRP_CGI_RIGHT($Cat);
  149. # HTML
  150. $ret_html .= "</body>\n";
  151. $ret_html .= "</html>\n";
  152. return ("text/html; charset=ISO-8859-1", $ret_html);
  153. }
  154. #-------------------------------------------------------------------------------
  155. sub GRP_CGI_CSS() {
  156. my $css;
  157. $css = "<style type=\"text/css\"><!--\n";
  158. $css .= "\#left {float: left; width: 15%; height:100%;}\n";
  159. # $css .= "table.GROUP { border:thin solid; background: #E0E0E0; text-align:left;}\n";
  160. $css .= "table.GROUP { border:thin solid; text-align:left;}\n";
  161. # $css .= "table.GROUP tr.odd { background: #F0F0F0;}\n";
  162. $css .= "table.GROUP td {nowrap;}";
  163. $css .= "\/\/--><\/style>";
  164. # TEST
  165. #$css = "<link href=\"$FW_ME/group.css\" rel=\"stylesheet\"/>\n";
  166. return $css;
  167. }
  168. #-------------------------------------------------------------------------------
  169. sub GRP_CGI_TOP($) {
  170. my $CAT = shift(@_);
  171. # rh = return-Html
  172. my $rh;
  173. $rh = "<div id=\"hdr\">\n";
  174. $rh .= "<form method=\"get\" action=\"" . $FW_ME . "\">\n";
  175. $rh .= "<table WIDTH=\"100%\">\n";
  176. $rh .= "<tr>";
  177. $rh .= "<td><a href=\"" . $FW_ME . "\">FHEM:</a>$CAT</td>";
  178. $rh .= "<td><input type=\"text\" name=\"cmd\" size=\"30\"/></td>";
  179. $rh .= "</tr>\n";
  180. $rh .= "</table>\n";
  181. $rh .= "</form>\n";
  182. $rh .= "<br>\n";
  183. $rh .= "</div>\n";
  184. return $rh;
  185. }
  186. #-------------------------------------------------------------------------------
  187. sub GRP_CGI_LEFT(){
  188. # rh = return-Html
  189. my $rh;
  190. # $rh = "<div id=\"left\">\n";
  191. $rh = "<div id=\"logo\"><img src=\"" . $FW_ME . "/fhem.png\"></div>";
  192. $rh .= "<div id=\"menu\">\n";
  193. # Print Groups
  194. $rh .= "<table class=\"room\">\n";
  195. foreach my $g (sort keys %{$modules{GROUP}{defptr}}){
  196. $rh .= "<tr><td>";
  197. $rh .= "<a href=\"" . $FW_ME . "/GROUPS/$g\">$g</a></h3>";
  198. $rh .= "</td></tr>\n";
  199. }
  200. $rh .= "</table><br>\n";
  201. #SHOWLEFT
  202. if(defined($modules{GROUP}{conf}{SHOWLEFT})){
  203. # $rh .= "<table class=\"room\">\n";
  204. $rh .= "<table id=\"room\">\n";
  205. foreach my $g (sort keys %{$modules{GROUP}{conf}{SHOWLEFT}}){
  206. #Tabelle
  207. $rh .= "<tr><th>$g</th><th></th></tr>\n";
  208. foreach my $r (sort keys %{$defs{$g}{READINGS}}){
  209. # $dn = DeviceName + $rn = Readingname to get ReadingValue
  210. my ($dn,$rn) = split(/:/,$defs{$g}{READINGS}{$r}{VAL});
  211. # $rv = ReadingValue; $rt = ReadingTime; $ru = ReadingUnit
  212. my ($rv,undef,undef) = &GRP_GET_READING_VAL($dn,$rn);
  213. $rh .= "<tr><td>$r</td><td>$rv</td></tr>\n";
  214. }
  215. }
  216. $rh .= "</table>\n";
  217. }
  218. $rh .= "</div>\n";
  219. return $rh;
  220. }
  221. #-------------------------------------------------------------------------------
  222. sub GRP_CGI_RIGHT(){
  223. my ($CAT) = @_;
  224. my ($name,$device,$reading,$value,$vtime,$rh,$tr_class,$comment);
  225. # rh = return-Html
  226. my $row = 1;
  227. # Table GROUP
  228. # Name | Value | Time | Device-Type
  229. # $rh = "<div id=\"right\">\n";
  230. $rh = "<div id=\"content\">\n";
  231. # Category -> DEVICE
  232. foreach my $c (sort keys %{$modules{GROUP}{defptr}{$CAT}}){
  233. # Log 0,"GROUP CGI-RIGHT DEV: $c";
  234. $rh .= "<table class=\"GROUP\" WIDTH=\"85%\">\n";
  235. $rh .= "<tr>";
  236. $rh .= "<th align=\"left\" WIDTH=\"10%\"><a href=\"$FW_ME?detail=$c\">$c</a></th>";
  237. $rh .= "<th align=\"left\" WIDTH=\"8%\"></th>";
  238. if(defined($attr{$c}{comment})){
  239. $comment = $attr{$c}{comment};
  240. $rh .= "<th align=\"left\" WIDTH=\"20%\" colspan=\"2\">$comment</th>";}
  241. else {
  242. $rh .= "<th align=\"left\" WIDTH=\"10%\"></th>";
  243. $rh .= "<th align=\"left\" WIDTH=\"10%\"></th>";}
  244. $rh .= "</tr>\n";
  245. # GROUP -> READING
  246. foreach my $r (sort keys %{$defs{$c}{READINGS}}){
  247. # $dn = DeviceName + $rn = Readingname to get ReadingValue
  248. my ($dn,$rn) = split(/:/,$defs{$c}{READINGS}{$r}{VAL});
  249. # $rv = ReadingValue; $rt = ReadingTime; $ru = ReadingUnit
  250. my ($rv,$rt,$ru) = &GRP_GET_READING_VAL($dn,$rn);
  251. $tr_class = $row?"odd":"even";
  252. $rh .= "<tr class=\"" . $tr_class . "\"><td>$r</td><td>$rv&nbsp;$ru</td><td>$rt</td>";
  253. $rh .= "<td><a href=\"$FW_ME?detail=$dn\">$dn</a></td></tr>\n";
  254. $row = ($row+1)%2;
  255. }
  256. $rh .= "</table><br>\n";
  257. }
  258. $rh .= "</div>\n";
  259. return $rh;
  260. }
  261. #-------------------------------------------------------------------------------
  262. sub GRP_CGI_DISPTACH_URL($){
  263. my ($htmlarg) = @_;
  264. my @params = split(/\//,$htmlarg);
  265. my $CAT = undef;
  266. if($params[2]) {
  267. $CAT = $params[2];
  268. # Log 0,"GRP URL-DISP-CAT: " . $CAT;
  269. }
  270. return $CAT;
  271. }
  272. #-------------------------------------------------------------------------------
  273. sub GRP_HANDLE_CAT($$){
  274. my($device,$cat) = @_;
  275. # Normal Categories -> %modules{GROUP}{defptr}{<CAT-NAME>}{<GROUP-DEVICE-NAME>}
  276. # Spezial Categories -> %modules{GROUP}{conf}{<CAT-NAME>}{<GROUP-DEVICE-NAME>}
  277. if($cat eq "SHOWLEFT") {
  278. # Log 0,"GRP CAT-DISP-> SHOWLEFT -> $cat -> $device";
  279. $modules{GROUP}{conf}{$cat}{$device} = 1;
  280. return undef;
  281. }
  282. $modules{GROUP}{defptr}{$cat}{$device} = 1;
  283. return undef;
  284. }
  285. #-------------------------------------------------------------------------------
  286. sub GRP_GET_READING_VAL($$){
  287. # IN $dn = DeviceName; $rn = ReadingName
  288. my($dn,$rn) = @_;
  289. # OUT $rv = ReadingValue; $rt = ReadingTime; $ru = ReadingUnit
  290. # Default Values
  291. my $rv = "???";
  292. my $rt = "****-**-** **:**:**";
  293. my $ru = "";
  294. # First $hash->{READINGS}
  295. if(defined($defs{$dn}{READINGS}{$rn}{VAL})) {
  296. $rv = $defs{$dn}{READINGS}{$rn}{VAL};
  297. $rt = $defs{$dn}{READINGS}{$rn}{TIME};
  298. }
  299. if($rv =~ m/ /){
  300. my @a = split(/ /, $rv);
  301. $rv = $a[0];
  302. $ru = $a[1];
  303. }
  304. if($rv =~ /\d{1}/) {
  305. $rv = sprintf("%.2f", $rv);
  306. }
  307. # Log 0,"GROUP GET-READING: $rv,$rt,$ru";
  308. # Second $hash
  309. # First Wins
  310. if(defined($defs{$dn}{$rn}) && $rv eq "???"){
  311. $rv = $defs{$dn}{$rn};
  312. $rt = " ";
  313. }
  314. # third Special READINGs
  315. if(defined($data{GROUP}{READINGS}{$rn}) && $rv eq "???" ){
  316. my $rv_function = $data{GROUP}{READINGS}{$rn};
  317. Log 0, "GROUP SP-READINGS Func: " . $rv_function;
  318. no strict "refs";
  319. if(defined(&$rv_function)){
  320. my ($rv_return,$rt_return) = &$rv_function($dn,$rn);
  321. # On ERROR return undef
  322. if($rv_return) {
  323. $rv = $rv_return;
  324. $rt = $rt_return;
  325. }
  326. }
  327. use strict "refs";
  328. }
  329. # Log 0,"GROUP GET-READING: $rv,$rt,$ru";
  330. return ($rv,$rt,$ru);
  331. }
  332. #-------------------------------------------------------------------------------
  333. sub GRP_GET_READING_DNW($$){
  334. # FHT-Device and READING = DNW
  335. # Displays: Day-Temp Night-Temp WindowOpen-Temp: D:22.00 N:18.00 W:5.50
  336. # IN $dn = DeviceName; $rn = ReadingName
  337. my($dn,$rn) = @_;
  338. # Type = FHT ???
  339. if($defs{$dn}{TYPE} ne "FHT"){return undef;}
  340. my($day,$night,$window,$rv_time);
  341. $day = "??";
  342. $night = "??";
  343. $window = "??";
  344. $rv_time = "--";
  345. if(defined($defs{$dn}{READINGS}{'day-temp'}{VAL})) {
  346. $day = $defs{$dn}{READINGS}{'day-temp'}{VAL};
  347. }
  348. if(defined($defs{$dn}{READINGS}{'day-temp'}{TIME})) {
  349. $rv_time = $defs{$dn}{READINGS}{'day-temp'}{TIME};
  350. }
  351. if(defined($defs{$dn}{READINGS}{'night-temp'}{VAL})) {
  352. $night = $defs{$dn}{READINGS}{'night-temp'}{VAL};
  353. }
  354. if(defined($defs{$dn}{READINGS}{'windowopen-temp'}{VAL})) {
  355. $window = $defs{$dn}{READINGS}{'windowopen-temp'}{VAL};
  356. }
  357. # Retunr Value
  358. my $rv = "D:$day N:$night W:$window";
  359. return ($rv,$rv_time);
  360. }
  361. 1;