98_InfratecPM.pm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. ################################################################
  2. #
  3. # $Id: 98_InfratecPM.pm 8315 2015-03-28 18:46:25Z wzut $
  4. #
  5. # (c) 2015 Copyright: Wzut
  6. # forum : http://forum.fhem.de/index.php/topic,34131.0.html
  7. # All rights reserved
  8. #
  9. # This code is free software; you can redistribute it and/or modify
  10. # it under the terms of the GNU General Public License as published by
  11. # the Free Software Foundation; either version 2 of the License, or
  12. # (at your option) any later version.
  13. # The GNU General Public License can be found at
  14. # http://www.gnu.org/copyleft/gpl.html.
  15. # A copy is found in the textfile GPL.txt and important notices to the license
  16. # from the author is found in LICENSE.txt distributed with these scripts.
  17. # This script 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. ################################################################
  23. # Changelog:
  24. # 25.3.15 add force for set on and off
  25. # 28.3.15 fix delete Outs
  26. package main;
  27. use strict;
  28. use warnings;
  29. use Time::HiRes qw(gettimeofday);
  30. use HttpUtils;
  31. #my %sets = ( "Out1" => "on,off,toggle" , "Out2" => "on,off,toggle");
  32. my %sets = ();
  33. #########################################################################
  34. sub InfratecPM_Initialize($)
  35. {
  36. my ($hash) = @_;
  37. $hash->{DefFn} = "InfratecPM_Define";
  38. $hash->{UndefFn} = "InfratecPM_Undef";
  39. $hash->{SetFn} = "InfratecPM_Set";
  40. $hash->{GetFn} = "InfratecPM_Get";
  41. $hash->{AttrFn} = "InfratecPM_Attr";
  42. $hash->{FW_summaryFn} = "InfratecPM_summaryFn";
  43. $hash->{AttrList} = "interval timeout user password autocreate:0,1 ".$readingFnAttributes;
  44. }
  45. sub InfratecPM_updateConfig($)
  46. {
  47. # this routine is called 10 sec after the last define of a restart
  48. # this gives FHEM sufficient time to fill in attributes
  49. my ($hash) = @_;
  50. my $name = $hash->{NAME};
  51. $hash->{INTERVAL} = AttrVal($name, "interval", 30);
  52. readingsSingleUpdate($hash,"state","Initialized",1);
  53. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "InfratecPM_Status",$hash, 0) if ($hash->{INTERVAL});
  54. #InfratecPM_Get($hash,$name,"status");
  55. return undef;
  56. }
  57. ################################################################################
  58. sub InfratecPM_Define($$) {
  59. my ($hash, $def) = @_;
  60. my $name = $hash->{NAME};
  61. my @a = split("[ \t][ \t]*", $def);
  62. return "wrong syntax: define <name> InfratecPM <IP or FQDN> [<Port>]" if(int(@a) < 3);
  63. $hash->{host} = $a[2];
  64. $hash->{port} = (defined($a[3])) ? $a[3] : "80";
  65. if( !defined( $attr{$a[0]}{user} ) ) { $attr{$a[0]}{user} = "admin";}
  66. $hash->{user} = $attr{$a[0]}{user};
  67. if( !defined( $attr{$a[0]}{password} ) ) { $attr{$a[0]}{password} = "1234";}
  68. $hash->{pwd} = $attr{$a[0]}{password};
  69. if( !defined( $attr{$a[0]}{timeout} ) ) { $attr{$a[0]}{timeout} = "2"}
  70. $hash->{timeout} = (int($attr{$a[0]}{timeout}) > 1) ? $attr{$a[0]}{timeout} : "2";
  71. if( !defined( $attr{$a[0]}{autocreate} ) ) { $attr{$a[0]}{autocreate} = "1";}
  72. $hash->{Clients} = ":InfratecOut:";
  73. $hash->{PORTS} = 0;
  74. $hash->{force} = 0;
  75. $hash->{code} = "";
  76. $hash->{callback} = \&InfratecPM_Read;
  77. readingsSingleUpdate($hash, "state", "defined",0);
  78. InternalTimer(gettimeofday()+10, "InfratecPM_updateConfig",$hash,1);
  79. return undef;
  80. }
  81. ################################################################################
  82. sub InfratecPM_Undef($$)
  83. {
  84. my ($hash, $arg) = @_;
  85. RemoveInternalTimer($hash);
  86. return undef;
  87. }
  88. ################################################################################
  89. sub InfratecPM_force($)
  90. {
  91. my ($hash) = @_;
  92. my $name = $hash->{NAME};
  93. Log3 $name, 5, "Force : ".$hash->{lastcmd};
  94. if ($hash->{force})
  95. {
  96. CommandSet(undef,$name." Out".$hash->{lastcmd}. " force");
  97. }
  98. else
  99. {
  100. InfratecPM_unforce($hash);
  101. }
  102. return undef;
  103. }
  104. ################################################################################
  105. sub InfratecPM_unforce($)
  106. {
  107. my ($hash) = @_;
  108. my $name = $hash->{NAME};
  109. $hash->{force} = 0;
  110. Log3 $name, 5, "Unforce : ".$hash->{lastcmd};
  111. RemoveInternalTimer($hash);
  112. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "InfratecPM_Status",$hash, 0) if ($hash->{INTERVAL});
  113. return undef;
  114. }
  115. ################################################################################
  116. sub InfratecPM_Read($$$)
  117. {
  118. my ($hash, $err, $buffer) = @_;
  119. my $name = $hash->{NAME};
  120. my $state = "???";
  121. if ($err)
  122. {
  123. $hash->{ERROR} = $err;
  124. $hash->{ERRORTIME} = TimeNow();
  125. my $msg = "$name: Error ";
  126. $msg .= ($hash->{code}) ? "[".$hash->{code}."] -> $err" : "-> $err";
  127. Log3 $name, 3, $msg;
  128. $hash->{ERRORCOUNT} ++;
  129. readingsSingleUpdate($hash, "state", "error", 0);
  130. $hash->{INTERVAL} = 3600 if ($hash->{ERRORCOUNT} >9);
  131. InternalTimer(gettimeofday()+$hash->{timeout}, "InfratecPM_force",$hash,0) if ($hash->{force});
  132. return;
  133. }
  134. if (!$buffer)
  135. {
  136. # sollte eigentlich gar nicht vorkommen
  137. Log3 $name, 3, "$name: empty return buffer";
  138. $hash->{ERRORCOUNT} ++;
  139. $hash->{ERROR} = "empty return buffer";
  140. $hash->{ERRORTIME} = TimeNow();
  141. $hash->{INTERVAL} = 3600 if ($hash->{ERRORCOUNT} >9);
  142. InternalTimer(gettimeofday()+$hash->{timeout}, "InfratecPM_force",$hash,0) if ($hash->{force});
  143. return;
  144. }
  145. $hash->{RETURNED} = "";
  146. Log3 $name, 5, "$name, [".$hash->{code}."] Message1: $buffer\r";
  147. $buffer =~s/\n//g;
  148. $buffer =~s/ //g;
  149. $buffer =~s/<br>/-/g;
  150. $buffer =~s/--/-/g;
  151. $buffer =~s/<[^>]*>//gis;
  152. Log3 $name, 4, "$name, Message2: $buffer\r";
  153. $hash->{ERRORCOUNT} = 0;
  154. $hash->{INTERVAL} = AttrVal($name, "interval", 30);
  155. readingsBeginUpdate($hash);
  156. my @ret = split("-" , $buffer);
  157. my $i = 0;
  158. my $devstate;
  159. foreach (@ret)
  160. {
  161. Log3 $name, 5, "$name , ret -> $_";
  162. my @val = split(":" , $_);
  163. if (!defined($val[1])) { $val[1] = "" }
  164. if(($val[1] eq "0") || ($val[1] eq "1")) # hier wollen wir nur die on/ff haben
  165. {
  166. #$val[0] =~s/ //g;
  167. if (!$i) {$state="";} # erster Port
  168. $i++;
  169. $devstate = ($val[1] eq "0") ? "off" : "on";
  170. $state .= $devstate." ";
  171. $hash->{helper}{$i."state"} = $val[1];
  172. $hash->{helper}{$i."name"} = $val[0];
  173. #Log3 $name, 5, "$name , Status $i: ".$hash->{helper}{$i."state"}."\r";
  174. readingsBulkUpdate($hash, $val[0], $devstate);
  175. my $defptr = $modules{InfratecOut}{defptr}{$name.$i};
  176. if (defined($defptr)) { readingsSingleUpdate($defptr, "state", $devstate, 1); }
  177. elsif(AttrVal($name, "autocreate", 1))
  178. {
  179. Log3 $name, 3, "$name, autocreate InfratecOut for Out".$i;
  180. CommandDefine(undef, $val[0]." InfratecOut $name $i");
  181. }
  182. } elsif (defined($val[0]) ne "")
  183. {
  184. $hash->{RETURNED} =$val[0];
  185. InfratecPM_unforce($hash) if (($hash->{RETURNED} eq "Done.") && $hash->{force});
  186. }
  187. }
  188. if (($hash->{RETURNED} eq "Done.") || ($hash->{RETURNED} eq "Status"))
  189. {
  190. readingsBulkUpdate($hash, "state",$state);
  191. }
  192. else
  193. {
  194. Log3 $name, 2, "$name , Return : ".$hash->{RETURNED};
  195. readingsBulkUpdate($hash, "state", $hash->{RETURNED});
  196. }
  197. readingsEndUpdate($hash, 1 );
  198. # und wie viele Ports hat denn nun das Ding wirklich ?
  199. if($i && ($hash->{RETURNED} eq "Status") && !$hash->{PORTS})
  200. {
  201. $hash->{PORTS} = $i;
  202. for (my $j=1; $j<= $i; $j++) { $sets{"Out".$j} = "on,off,toggle"; }
  203. }
  204. return;
  205. }
  206. ################################################################################
  207. sub InfratecPM_Attr(@)
  208. {
  209. my ($cmd,$name, $attrName,$attrVal) = @_;
  210. my $hash = $defs{$name};
  211. if ($cmd eq "set")
  212. {
  213. if ($attrName eq "timeout")
  214. {
  215. if (int($attrVal)<"2") {$attrVal="2";}
  216. $hash->{timeout} = $attrVal;
  217. $attr{$name}{timeout} = $attrVal;
  218. }
  219. elsif ($attrName eq "user")
  220. {
  221. $hash->{user} = $attrVal;
  222. $attr{$name}{user} = $attrVal;
  223. }
  224. elsif ($attrName eq "password")
  225. {
  226. $hash->{pwd} = $attrVal;
  227. $attr{$name}{password} = $attrVal;
  228. }
  229. elsif ($attrName eq "interval")
  230. {
  231. $hash->{INTERVAL} = $attrVal;
  232. $attr{$name}{interval} = $attrVal;
  233. }
  234. }
  235. return undef;
  236. }
  237. ################################################################################
  238. sub InfratecPM_Get($@) {
  239. my ($hash, $name , @a) = @_;
  240. my $cmd = $a[0];
  241. Log3 $name, 5, "Get: ".join(" ", @a);
  242. return "get $name needs one argument" if (int(@a) != 1);
  243. return "Unknown argument $cmd, choose one of status:noArg " if ($cmd ne "status");
  244. InfratecPM_unforce($hash) if ($hash->{force});
  245. InfratecPM_Status($hash);
  246. return undef;
  247. }
  248. ################################################################################
  249. sub InfratecPM_Set($@) {
  250. my ($hash, @a) = @_;
  251. my $name = $hash->{NAME};
  252. my $port = (defined($a[1])) ? $a[1] : "?" ;
  253. my $cmd = (defined($a[2])) ? $a[2] : "";
  254. my $subcmd = (defined($a[3])) ? $a[3] : "";
  255. Log3 $name, 5, "Set: ".join(" ", @a);
  256. if(!defined($sets{Out1}) && $hash->{PORTS}) # neu aufbauen nach reload;
  257. {
  258. for (my $j=1; $j<= $hash->{PORTS}; $j++) { $sets{"Out".$j} = "on,off,toggle"; }
  259. }
  260. if(!defined($sets{$port}))
  261. {
  262. my @commands = ();
  263. foreach my $key (sort keys %sets)
  264. {
  265. push @commands, $sets{$key} ? $key.":".join(",",$sets{$key}) : $key;
  266. }
  267. return "Unknown argument for $port, choose one of " . join(" ", @commands);
  268. }
  269. return "wrong command, please use on, off or toggle" if($cmd !~ /^(on|off|toggle)$/);
  270. $port = substr($port,3,1);
  271. return "wrong port $port, please use 1 - ".$hash->{PORTS} if ((int($port)<1) || (int($port)>$hash->{PORTS})) ;
  272. $hash->{url} = "http://$hash->{host}:$hash->{port}/sw?u=$hash->{user}&p=$hash->{pwd}&o=";
  273. $hash->{url} .= $port."&f=".$cmd;
  274. $hash->{lastcmd} = $port." ".$cmd;
  275. $hash->{force} = ($subcmd eq "force") ? 1 : 0;
  276. RemoveInternalTimer($hash) if ($hash->{force});
  277. HttpUtils_NonblockingGet($hash);
  278. return undef;
  279. }
  280. ################################################################################
  281. sub InfratecPM_Status($)
  282. {
  283. my ($hash) = @_;
  284. my $name = $hash->{NAME};
  285. Log3 $name, 5, "GetUpdate, Interval : ".$hash->{INTERVAL};
  286. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "InfratecPM_Status",$hash, 0) if ($hash->{INTERVAL});
  287. $hash->{url} = "http://$hash->{host}:$hash->{port}/sw?s=0";
  288. $hash->{lastcmd} = "status";
  289. HttpUtils_NonblockingGet($hash);
  290. return undef;
  291. }
  292. ################################################################################
  293. sub InfratecPM_summaryFn($$$$) {
  294. my ($FW_wname, $hash, $room, $pageHash) = @_;
  295. $hash = $defs{$hash};
  296. my $state = $hash->{STATE};
  297. my $name = $hash->{NAME};
  298. return if(AttrVal($name, "stateFormat", ""));
  299. my ($icon,$html,$cmd,$i,$title,$txt,$a,$b);
  300. $html ="<nobr>";
  301. if (($state ne "defined") && ($state ne "error") && ($state ne "Initialized"))
  302. {
  303. for ($i=1; $i<= $hash->{PORTS}; $i++)
  304. {
  305. if (defined($hash->{helper}{$i."state"}))
  306. {
  307. if ($hash->{helper}{$i."state"})
  308. {
  309. $cmd = "Out".$i." off";
  310. $title = $hash->{helper}{$i."name"}. " on";
  311. ($icon, undef, undef) = FW_dev2image($name,"on");
  312. ($a,$b) = split('title=\"on\"' , FW_makeImage($icon, "on"));
  313. $txt = $a."title=\"".$title."\"".$b;
  314. }
  315. else
  316. {
  317. $cmd = "Out".$i." on";
  318. $title = $hash->{helper}{$i."name"}. " off";
  319. ($icon, undef, undef) = FW_dev2image($name,"off");
  320. ($a,$b) = split('title=\"off\"' , FW_makeImage($icon, "off"));
  321. $txt = $a."title=\"".$title."\"".$b;
  322. }
  323. $html .= "<a href=\"/fhem?cmd.$name=set $name ".$cmd."&room=$room&amp;room=$room\">$txt</a>&nbsp;&nbsp;";
  324. }
  325. }
  326. } else { $html .= $state };
  327. $html .= "</nobr>";
  328. return $html;
  329. }
  330. 1;
  331. =pod
  332. =begin html
  333. <a name="InfratecPM"></a>
  334. <h3>InfratecPM</h3>
  335. <ul>
  336. <table>
  337. <tr><td>
  338. Device for Infratec Power Modules , see <a href='http://www.infratec-plus.de/produktlinien/powerdistribution/switched-pdu/pm4-ip/'>
  339. http://www.infratec-plus.de/produktlinien/powerdistribution/switched-pdu/pm4-ip/</a> for details
  340. <br>
  341. FHEM Forum : <a href='http://forum.fhem.de/index.php/topic,34131.0.html'>http://forum.fhem.de/index.php/topic,34131.0.html</a>
  342. </td>
  343. </tr>
  344. </table>
  345. <a name="InfratecPMdefine"></a>
  346. <b>Define</b>
  347. <ul>
  348. <code>define &lt;name&gt; InfratecPM &lt;IP or FQDN&gt; [&lt;PORT&gt;] (Port 80 default)</code><br>
  349. example :<br>
  350. define myPM InfratecPM 192.168.0.100<br>
  351. define myPM InfratecPM myhost.dyndns.org 88<br>
  352. </ul>
  353. <br>
  354. <a name="InfratecPMset"></a>
  355. <b>Set </b>
  356. <ul>
  357. <li>Outx on (force)<br>
  358. turns Outx on</li><br>
  359. <li>Outx off (force)<br>
  360. turns Outx off</li><br>
  361. <li>Outx toggle<br>
  362. toggle Outx</li><br>
  363. </ul>
  364. <a name="InfratecPMget"></a>
  365. <b>Get</b>
  366. <ul>
  367. <li>status<br>
  368. returns the status of all Outs
  369. </li><br>
  370. </ul>
  371. <a name="InfratecPMattr"></a>
  372. <b>Attributes</b>
  373. <ul>
  374. <li>autocreate<br>
  375. autocreate sub devices for each reading (default 1)<br>
  376. requires 98_InfratecOut.pm</li><br>
  377. <li>interval<br>
  378. polling interval in seconds, set to 0 to disable polling (default 30)</li><br>
  379. <li>timeout<br>
  380. seconds to wait for a answer from the Power Module</li><br>
  381. <li>user<br>
  382. defined user on the Power Module</li><br>
  383. <li>password<br>
  384. password for user</li>
  385. </ul>
  386. <br>
  387. </ul>
  388. =end html