89_FULLY.pm 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. ##############################################################################
  2. #
  3. # 89_FULLY.pm 0.9.001
  4. #
  5. # $Id: 89_FULLY.pm 17632 2018-10-28 09:49:59Z zap $
  6. #
  7. # Control Fully browser on Android tablets from FHEM.
  8. # Requires Fully Plus license!
  9. #
  10. ##############################################################################
  11. package main;
  12. use strict;
  13. use warnings;
  14. use Blocking;
  15. use SetExtensions;
  16. # Declare functions
  17. sub FULLY_Initialize ($);
  18. sub FULLY_Define ($$);
  19. sub FULLY_Undef ($$);
  20. sub FULLY_Shutdown ($);
  21. sub FULLY_Set ($@);
  22. sub FULLY_Get ($@);
  23. sub FULLY_Attr ($@);
  24. sub FULLY_Detail ($@);
  25. sub FULLY_UpdateDeviceInfo ($);
  26. sub FULLY_Execute ($$$$);
  27. sub FULLY_ScreenOff ($);
  28. sub FULLY_GetDeviceInfo ($);
  29. sub FULLY_ProcessDeviceInfo ($$);
  30. sub FULLY_GotDeviceInfo ($);
  31. sub FULLY_Abort ($);
  32. sub FULLY_UpdateReadings ($$);
  33. sub FULLY_Ping ($$);
  34. my $FULLY_VERSION = "0.9.001";
  35. my $FULLY_TIMEOUT = 4;
  36. my $FULLY_POLL_INTERVAL = 3600;
  37. my $FULLY_REQUIRED_VERSION = 1.27;
  38. my $FULLY_FHEM_COMMAND = qq(
  39. function SendRequest(FHEM_Address, Devicename, Command) {
  40. var Port = "8085"
  41. var url = "http://" + FHEM_Address + ":" + Port + "/fhem?XHR=1&cmd." + Devicename + "=" + Command;
  42. var req = new XMLHttpRequest();
  43. req.open("GET", url);
  44. req.send(null);
  45. req = null;
  46. }
  47. );
  48. ##################################################
  49. # Initialize module
  50. ##################################################
  51. sub FULLY_Initialize ($)
  52. {
  53. my ($hash) = @_;
  54. $hash->{DefFn} = "FULLY_Define";
  55. $hash->{UndefFn} = "FULLY_Undef";
  56. $hash->{SetFn} = "FULLY_Set";
  57. $hash->{GetFn} = "FULLY_Get";
  58. $hash->{AttrFn} = "FULLY_Attr";
  59. $hash->{ShutdownFn} = "FULLY_Shutdown";
  60. $hash->{FW_detailFn} = "FULLY_Detail";
  61. $hash->{parseParams} = 1;
  62. $hash->{AttrList} = "pingBeforeCmd:0,1,2 pollInterval requestTimeout repeatCommand:0,1,2 " .
  63. "disable:0,1 " .
  64. $readingFnAttributes;
  65. }
  66. ##################################################
  67. # Define device
  68. ##################################################
  69. sub FULLY_Define ($$)
  70. {
  71. my ($hash, $a, $h) = @_;
  72. my $name = $hash->{NAME};
  73. my $rc = 0;
  74. return "Usage: define devname FULLY IP_or_Hostname password [poll-interval]"
  75. if (@$a < 4);
  76. return "FULLY: polling interval must be in range 10 - 86400"
  77. if (@$a == 5 && ($$a[4] !~ /^[1-9][0-9]+$/ || $$a[4] < 10 || $$a[4] > 86400));
  78. $hash->{host} = $$a[2];
  79. $hash->{version} = $FULLY_VERSION;
  80. $hash->{onForTimer} = 'off';
  81. $hash->{fully}{password} = $$a[3];
  82. $hash->{fully}{schedule} = 0;
  83. Log3 $name, 1, "FULLY: [$name] Version $FULLY_VERSION Opening device ".$hash->{host};
  84. my $result = FULLY_GetDeviceInfo ($name);
  85. if (!FULLY_UpdateReadings ($hash, $result)) {
  86. Log3 $name, 2, "FULLY: [$name] Update of device info failed";
  87. }
  88. if (@$a == 5) {
  89. $attr{$name}{'pollInterval'} = $$a[4];
  90. $hash->{nextUpdate} = strftime "%d.%m.%Y %H:%M:%S", localtime (time+$$a[4]);
  91. InternalTimer (gettimeofday()+$$a[4], "FULLY_UpdateDeviceInfo", $hash, 0);
  92. }
  93. else {
  94. $hash->{nextUpdate} = 'off';
  95. }
  96. return undef;
  97. }
  98. #####################################
  99. # Set or delete attribute
  100. #####################################
  101. sub FULLY_Attr ($@)
  102. {
  103. my ($cmd, $name, $attrname, $attrval) = @_;
  104. my $hash = $defs{$name};
  105. if ($cmd eq 'set') {
  106. if ($attrname eq 'pollInterval') {
  107. if ($attrval >= 10 && $attrval <= 86400) {
  108. my $curval = AttrVal ($name, 'pollInterval', $FULLY_POLL_INTERVAL);
  109. if ($attrval != $curval) {
  110. Log3 $name, 2, "FULLY: [$name] Polling interval set to $attrval";
  111. RemoveInternalTimer ($hash);
  112. $hash->{nextUpdate} = strftime "%d.%m.%Y %H:%M:%S", localtime (time+$attrval);
  113. InternalTimer (gettimeofday()+$attrval, "FULLY_UpdateDeviceInfo", $hash, 0);
  114. }
  115. }
  116. elsif ($attrval == 0) {
  117. RemoveInternalTimer ($hash);
  118. $hash->{nextUpdate} = 'off';
  119. }
  120. else {
  121. return "FULLY: Polling interval must be in range 10-86400";
  122. }
  123. }
  124. elsif ($attrname eq 'requestTimeout') {
  125. return "FULLY: Timeout must be greater than 0" if ($attrval < 1);
  126. }
  127. }
  128. elsif ($cmd eq 'del') {
  129. if ($attrname eq 'pollInterval') {
  130. RemoveInternalTimer ($hash);
  131. $hash->{nextUpdate} = 'off';
  132. }
  133. }
  134. return undef;
  135. }
  136. #####################################
  137. # Delete device
  138. #####################################
  139. sub FULLY_Undef ($$)
  140. {
  141. my ($hash, $arg) = @_;
  142. RemoveInternalTimer ($hash);
  143. BlockingKill ($hash->{fully}{bc}) if (defined ($hash->{fully}{bc}));
  144. return undef;
  145. }
  146. #####################################
  147. # Shutdown FHEM
  148. #####################################
  149. sub FULLY_Shutdown ($)
  150. {
  151. my ($hash) = @_;
  152. RemoveInternalTimer ($hash);
  153. BlockingKill ($hash->{fully}{bc}) if (defined ($hash->{fully}{bc}));
  154. return undef;
  155. }
  156. #####################################
  157. # Enhance device detail view
  158. #####################################
  159. sub FULLY_Detail ($@)
  160. {
  161. my ($FW_wname, $name, $room, $pageHash) = @_;
  162. my $hash = $defs{$name};
  163. my $html = qq(
  164. <span class='mkTitle'>Device Administration</span>
  165. <table class="block wide">
  166. <tr class="odd">
  167. <td><div class="col1">
  168. <a target="_blank" href="http://$hash->{host}:2323">Remote Admin</a>
  169. </div></td>
  170. </tr>
  171. </table>
  172. );
  173. return $html;
  174. }
  175. #####################################
  176. # Set commands
  177. #####################################
  178. sub FULLY_Set ($@)
  179. {
  180. my ($hash, $a, $h) = @_;
  181. my $name = shift @$a;
  182. my $opt = shift @$a;
  183. my $options = "brightness photo:noArg clearCache:noArg exit:noArg lock:noArg motionDetection:on,off ".
  184. "off:noArg on:noArg on-for-timer playSound restart:noArg screenOffTimer screenSaver:start,stop ".
  185. "screenSaverTimer screenSaverURL speak startURL stopSound:noArg unlock:noArg url ".
  186. "volume";
  187. my $response;
  188. # Fully commands without argument
  189. my %cmds = (
  190. "clearCache" => "clearCache",
  191. "photo" => "getCamshot",
  192. "exit" => "exitApp",
  193. "restart" => "restartApp",
  194. "on" => "screenOn", "off" => "screenOff",
  195. "lock" => "enabledLockedMode", "unlock" => "disableLockedMode",
  196. "stopSound" => "stopSound"
  197. );
  198. my $disable = AttrVal ($name, 'disable', 0);
  199. return undef if ($disable);
  200. if (exists ($cmds{$opt})) {
  201. $response = FULLY_Execute ($hash, $cmds{$opt}, undef, 1);
  202. }
  203. elsif ($opt eq 'on-for-timer') {
  204. my $par = shift @$a;
  205. $par = "forever" if (!defined ($par));
  206. if ($par eq 'forever') {
  207. $response = FULLY_Execute ($hash, "setBooleanSetting",
  208. { "key" => "keepScreenOn", "value" => "true" }, 1);
  209. $response = FULLY_Execute ($hash, "screenOn", undef, 0)
  210. if (defined ($response) && $response ne '');
  211. }
  212. elsif ($par eq 'off') {
  213. $response = FULLY_Execute ($hash, "setBooleanSetting",
  214. { "key" => "keepScreenOn", "value" => "false" }, 1);
  215. $response = FULLY_Execute ($hash, "setStringSetting",
  216. { "key" => "timeToScreenOffV2", "value" => "0" }, 0)
  217. if (defined ($response) && $response ne '');
  218. }
  219. elsif ($par =~ /^[0-9]+$/) {
  220. $response = FULLY_Execute ($hash, "setBooleanSetting",
  221. { "key" => "keepScreenOn", "value" => "true" }, 1);
  222. $response = FULLY_Execute ($hash, "screenOn", undef, 0)
  223. if (defined ($response) && $response ne '');
  224. InternalTimer (gettimeofday()+$par, "FULLY_ScreenOff", $hash, 0);
  225. }
  226. else {
  227. return "Usage: set $name on-for-timer [{ Seconds | forever | off }]";
  228. }
  229. RemoveInternalTimer ($hash, "FULLY_ScreenOff") if ($par eq 'off' || $par eq 'forever');
  230. $hash->{onForTimer} = $par if (defined ($response) && $response ne '');
  231. }
  232. elsif ($opt eq 'screenOffTimer') {
  233. my $value = shift @$a;
  234. return "Usage: set $name $opt {seconds}" if (!defined ($value));
  235. $response = FULLY_Execute ($hash, "setStringSetting",
  236. { "key" => "timeToScreenOffV2", "value" => "$value" }, 1);
  237. }
  238. elsif ($opt eq 'screenSaver') {
  239. my $state = shift @$a;
  240. return "Usage: set $name $opt { start | stop }" if (!defined ($state));
  241. if ($state eq 'start') {
  242. $response = FULLY_Execute ($hash, "startScreensaver", undef, 1);
  243. }
  244. elsif ($state eq 'stop') {
  245. $response = FULLY_Execute ($hash, "stopScreensaver", undef, 1);
  246. }
  247. else {
  248. return "Usage: set $name $opt { start | stop }";
  249. }
  250. }
  251. elsif ($opt eq 'screenSaverTimer') {
  252. my $value = shift @$a;
  253. return "Usage: set $name $opt {seconds}" if (!defined ($value));
  254. $response = FULLY_Execute ($hash, "setStringSetting",
  255. { "key" => "timeToScreensaverV2", "value" => "$value" }, 1);
  256. }
  257. elsif ($opt eq 'screenSaverURL') {
  258. my $value = shift @$a;
  259. return "Usage: set $name $opt {URL}" if (!defined ($value));
  260. $response = FULLY_Execute ($hash, "setStringSetting",
  261. { "key" => "screensaverURL", "value" => "$value" }, 1);
  262. }
  263. elsif ($opt eq 'startURL') {
  264. my $value = shift @$a;
  265. return "Usage: set $name $opt {URL}" if (!defined ($value));
  266. $response = FULLY_Execute ($hash, "setStringSetting",
  267. { "key" => "startURL", "value" => "$value" }, 1);
  268. }
  269. elsif ($opt eq 'brightness') {
  270. my $value = shift @$a;
  271. return "Usage: set $name brightness 0-255" if (!defined ($value));
  272. $value = 255 if ($value > 255);
  273. $response = FULLY_Execute ($hash, "setStringSetting",
  274. { "key" => "screenBrightness", "value" => "$value" }, 1);
  275. }
  276. elsif ($opt eq 'motionDetection') {
  277. my $state = shift @$a;
  278. return "Usage: set $name motionDetection { on | off }" if (!defined ($state));
  279. my $value = $state eq 'on' ? 'true' : 'false';
  280. $response = FULLY_Execute ($hash, "setBooleanSetting",
  281. { "key" => "motionDetection", "value" => "$value" }, 1);
  282. }
  283. elsif ($opt eq 'speak') {
  284. my $text = shift @$a;
  285. return 'Usage: set $name speak "{Text}"' if (!defined ($text));
  286. while ($text =~ /\[(.+):(.+)\]/) {
  287. my ($device, $reading) = ($1, $2);
  288. my $value = ReadingsVal ($device, $reading, '');
  289. $text =~ s/\[$device:$reading\]/$value/g;
  290. }
  291. my $enctext = urlEncode ($text);
  292. $response = FULLY_Execute ($hash, "textToSpeech", { "text" => "$enctext" }, 1);
  293. }
  294. elsif ($opt eq 'playSound') {
  295. my $url = shift @$a;
  296. my $loop = shift @$a;
  297. $loop = defined ($loop) ? 'true' : 'false';
  298. return "Usage: set $name playSound {url} [loop]" if (!defined ($url));
  299. $response = FULLY_Execute ($hash, "playSound",
  300. { "url" => "$url", "loop" => "$loop"}, 1);
  301. }
  302. elsif ($opt eq 'volume') {
  303. my $level = shift @$a;
  304. my $stream = shift @$a;
  305. return "Usage: set $name volume {level} {stream}"
  306. if (!defined ($stream) || $level !~ /^[0-9]+$/ || $stream !~ /^[0-9]+$/);
  307. $response = FULLY_Execute ($hash, "setAudioVolume",
  308. { "level" => "$level", "stream" => "$stream"}, 1);
  309. }
  310. elsif ($opt eq 'url') {
  311. my $url = shift @$a;
  312. my $cmd = defined ($url) ? "loadURL" : "loadStartURL";
  313. $response = FULLY_Execute ($hash, $cmd, { "url" => "$url" }, 1);
  314. }
  315. else {
  316. return "FULLY: Unknown argument $opt, choose one of ".$options;
  317. }
  318. my $result = FULLY_ProcessDeviceInfo ($name, $response);
  319. if (!FULLY_UpdateReadings ($hash, $result)) {
  320. Log3 $name, 2, "FULLY: [$name] Command failed";
  321. return "FULLY: Command failed";
  322. }
  323. return undef;
  324. }
  325. #####################################
  326. # Get commands
  327. #####################################
  328. sub FULLY_Get ($@)
  329. {
  330. my ($hash, $a, $h) = @_;
  331. my $name = shift @$a;
  332. my $opt = shift @$a;
  333. my $options = "info:noArg stats:noArg update:noArg";
  334. my $response;
  335. my $disable = AttrVal ($name, 'disable', 0);
  336. return undef if ($disable);
  337. if ($opt eq 'info') {
  338. my $result = FULLY_Execute ($hash, 'deviceInfo', undef, 1);
  339. if (!defined ($result) || $result eq '') {
  340. Log3 $name, 2, "FULLY: [$name] Command failed";
  341. return "FULLY: Command failed";
  342. }
  343. elsif ($response =~ /Wrong password/) {
  344. Log3 $name, 2, "FULLY: [$name] Wrong password";
  345. return "FULLY: Wrong password";
  346. }
  347. $response = '';
  348. while ($result =~ /table-cell\">([^<]+)<\/td><td class="table-cell">([^<]+)</g) {
  349. $response .= "$1 = $2<br/>\n";
  350. }
  351. return $response;
  352. }
  353. elsif ($opt eq 'stats') {
  354. return "FULLY: Command not implemented";
  355. }
  356. elsif ($opt eq 'update') {
  357. my $result = FULLY_GetDeviceInfo ($name);
  358. if (!FULLY_UpdateReadings ($hash, $result)) {
  359. Log3 $name, 2, "FULLY: [$name] Command failed";
  360. return "FULLY: Command failed";
  361. }
  362. }
  363. else {
  364. return "FULLY: Unknown argument $opt, choose one of ".$options;
  365. }
  366. return undef;
  367. }
  368. #####################################
  369. # Execute Fully command
  370. #####################################
  371. sub FULLY_Execute ($$$$)
  372. {
  373. my ($hash, $command, $param, $doping) = @_;
  374. my $name = $hash->{NAME};
  375. # Get attributes
  376. my $timeout = AttrVal ($name, 'requestTimeout', $FULLY_TIMEOUT);
  377. my $repeatCommand = min (AttrVal ($name, 'repeatCommand', 0), 2);
  378. my $ping = min (AttrVal ($name, 'pingBeforeCmd', 0), 2);
  379. my $response = '';
  380. my $url = "http://".$hash->{host}.":2323/?cmd=$command";
  381. if (defined ($param)) {
  382. foreach my $parname (keys %$param) {
  383. if (defined ($param->{$parname})) {
  384. $url .= "&$parname=".$param->{$parname};
  385. }
  386. }
  387. }
  388. # Ping tablet device
  389. FULLY_Ping ($hash, $ping) if ($doping && $ping > 0);
  390. my $i = 0;
  391. while ($i <= $repeatCommand && (!defined ($response) || $response eq '')) {
  392. $response = GetFileFromURL ("$url&password=".$hash->{fully}{password}, $timeout);
  393. Log3 $name, 4, "FULLY: [$name] HTTP response empty" if (defined ($response) && $response eq '');
  394. $i++;
  395. }
  396. return $response;
  397. }
  398. #####################################
  399. # Timer function: Turn screen off
  400. #####################################
  401. sub FULLY_ScreenOff ($)
  402. {
  403. my ($hash) = @_;
  404. my $response = FULLY_Execute ($hash, "setBooleanSetting",
  405. { "key" => "keepScreenOn", "value" => "false" }, 1);
  406. $response = FULLY_Execute ($hash, "screenOff", undef, 1)
  407. if (defined ($response) && $response ne '');
  408. $hash->{onForTimer} = 'off' if (defined ($response) && $response ne '');
  409. }
  410. #####################################
  411. # Timer function: Read device info
  412. #####################################
  413. sub FULLY_UpdateDeviceInfo ($)
  414. {
  415. my ($hash) = @_;
  416. my $name = $hash->{NAME};
  417. my $disable = AttrVal ($name, 'disable', 0);
  418. if (!exists ($hash->{fully}{bc}) && $disable == 0) {
  419. $hash->{fully}{bc} = BlockingCall ("FULLY_GetDeviceInfo", $name, "FULLY_GotDeviceInfo",
  420. 120, "FULLY_Abort", $hash);
  421. }
  422. }
  423. #####################################
  424. # Get tablet device information
  425. #####################################
  426. sub FULLY_GetDeviceInfo ($)
  427. {
  428. my ($name) = @_;
  429. my $hash = $defs{$name};
  430. my $result = FULLY_Execute ($hash, 'deviceInfo', undef, 1);
  431. return FULLY_ProcessDeviceInfo ($name, $result);
  432. }
  433. #####################################
  434. # Extract parameters from HTML code
  435. #####################################
  436. sub FULLY_ProcessDeviceInfo ($$)
  437. {
  438. my ($name, $result) = @_;
  439. return "$name|0|state=failed" if (!defined ($result) || $result eq '');
  440. return "$name|0|state=wrong password" if ($result =~ /Wrong password/);
  441. my $parameters = "$name|1";
  442. while ($result =~ /table-cell\">([^<]+)<\/td><td class="table-cell">([^<]+)</g) {
  443. my $rn = lc($1);
  444. my $rv = $2;
  445. $rv =~ s/\s+$//;
  446. $rn =~ s/\:/\./g;
  447. $rn =~ s/[^A-Za-z\d_\.-]+/_/g;
  448. $rn =~ s/[_]+$//;
  449. if ($rn eq 'battery_level') {
  450. if ($rv =~ /^([0-9]+)% \(([^\)]+)\)$/) {
  451. $parameters .= "|$rn=$1|power=$2";
  452. next;
  453. }
  454. }
  455. elsif ($rn eq 'screen_brightness') {
  456. $rn = "brightness";
  457. }
  458. elsif ($rn eq 'screen_status') {
  459. $parameters .= "|state=$rv";
  460. }
  461. elsif ($rn eq 'fully_version') {
  462. if ($rv =~ /^([0-9]\.[0-9]+).*/) {
  463. my $cv = $1;
  464. Log3 $name, 1, "FULLY: [$name] Version of fully browser is $rv. Version $FULLY_REQUIRED_VERSION is required."
  465. if ($cv < $FULLY_REQUIRED_VERSION);
  466. }
  467. else {
  468. Log3 $name, 2, "FULLY: [$name] Cannot detect version of fully browser.";
  469. }
  470. }
  471. $parameters .= "|$rn=$rv";
  472. }
  473. return $parameters;
  474. }
  475. #####################################
  476. # Success function for blocking call
  477. #####################################
  478. sub FULLY_GotDeviceInfo ($)
  479. {
  480. my ($string) = @_;
  481. my ($name, $result) = split ('\|', $string, 2);
  482. my $hash = $defs{$name};
  483. my $pollInterval = AttrVal ($name, 'pollInterval', $FULLY_POLL_INTERVAL);
  484. my $timeout = AttrVal ($name, 'requestTimeout', $FULLY_TIMEOUT);
  485. delete $hash->{fully}{bc} if (exists ($hash->{fully}{bc}));
  486. my $rc = FULLY_UpdateReadings ($hash, $string);
  487. if (!$rc) {
  488. Log3 $name, 2, "FULLY: [$name] Request timed out";
  489. if ($hash->{fully}{schedule} == 0) {
  490. $hash->{fully}{schedule} += 1;
  491. Log3 $name, 2, "FULLY: [$name] Rescheduling in $timeout seconds.";
  492. $pollInterval = $timeout;
  493. }
  494. else {
  495. $hash->{fully}{schedule} = 0;
  496. }
  497. }
  498. $hash->{nextUpdate} = strftime "%d.%m.%Y %H:%M:%S", localtime (time+$pollInterval);
  499. InternalTimer (gettimeofday()+$pollInterval, "FULLY_UpdateDeviceInfo", $hash, 0)
  500. if ($pollInterval > 0);
  501. }
  502. #####################################
  503. # Abort function for blocking call
  504. #####################################
  505. sub FULLY_Abort ($)
  506. {
  507. my ($hash) = @_;
  508. my $name = $hash->{NAME};
  509. my $pollInterval = AttrVal ($name, 'pollInterval', $FULLY_POLL_INTERVAL);
  510. my $timeout = AttrVal ($name, 'requestTimeout', $FULLY_TIMEOUT);
  511. delete $hash->{fully}{bc} if (exists ($hash->{fully}{bc}));
  512. Log3 $name, 2, "FULLY: [$name] request timed out";
  513. if ($hash->{fully}{schedule} == 0) {
  514. $hash->{fully}{schedule} += 1;
  515. Log3 $name, 2, "FULLY: [$name] Rescheduling in $timeout seconds.";
  516. $pollInterval = $timeout;
  517. }
  518. else {
  519. $hash->{fully}{schedule} = 0;
  520. }
  521. $hash->{nextUpdate} = strftime "%d.%m.%Y %H:%M:%S", localtime (time+$pollInterval);
  522. InternalTimer (gettimeofday()+$pollInterval, "FULLY_UpdateDeviceInfo", $hash, 0)
  523. if ($pollInterval > 0);
  524. }
  525. #####################################
  526. # Update readings
  527. #####################################
  528. sub FULLY_UpdateReadings ($$)
  529. {
  530. my ($hash, $result) = @_;
  531. my $name = $hash->{NAME};
  532. my $rc = 1;
  533. if (!defined ($result) || $result eq '') {
  534. Log3 $name, 2, "FULLY: [$name] empty response";
  535. return 0;
  536. }
  537. my @parameters = split ('\|', $result);
  538. if (scalar (@parameters) == 0) {
  539. Log3 $name, 2, "FULLY: [$name] empty response";
  540. return 0;
  541. }
  542. if ($parameters[0] eq $name) {
  543. my $n = shift @parameters;
  544. $rc = shift @parameters;
  545. }
  546. readingsBeginUpdate ($hash);
  547. foreach my $parval (@parameters) {
  548. my ($rn, $rv) = split ('=', $parval);
  549. readingsBulkUpdate ($hash, $rn, $rv);
  550. }
  551. readingsEndUpdate ($hash, 1);
  552. return $rc;
  553. }
  554. #####################################
  555. # Send ICMP request to tablet device
  556. # Adapted from presence module.
  557. # Thx Markus.
  558. #####################################
  559. sub FULLY_Ping ($$)
  560. {
  561. my ($hash, $count) = @_;
  562. my $name = $hash->{NAME};
  563. my $host = $hash->{host};
  564. my $temp;
  565. if ($^O =~ m/(Win|cygwin)/)
  566. {
  567. $temp = qx(ping -n $count -4 $host);
  568. }
  569. elsif ($^O =~ m/solaris/)
  570. {
  571. $temp = qx(ping $host $count 2>&1);
  572. }
  573. else
  574. {
  575. $temp = qx(ping -c $count $host 2>&1);
  576. }
  577. Log3 $name, 4, "FULLY: [$name] Ping response = $temp" if (defined ($temp));
  578. sleep (1);
  579. return $temp;
  580. }
  581. 1;
  582. =pod
  583. =item device
  584. =item summary FULLY Browser Integration
  585. =begin html
  586. <a name="FULLY"></a>
  587. <h3>FULLY</h3>
  588. <ul>
  589. Module for controlling of Fully browser on Android tablets.
  590. </br></br>
  591. <a name="HMCCUdefine"></a>
  592. <b>Define</b><br/><br/>
  593. <ul>
  594. <code>define &lt;name&gt; FULLY &lt;HostOrIP&gt; &lt;password&gt; [&lt;poll-interval&gt;]</code>
  595. <br/><br/>
  596. The parameter <i>password</i> is the password set in Fully browser.
  597. </ul>
  598. <br/>
  599. <a name="FULLYset"></a>
  600. <b>Set</b><br/><br/>
  601. <ul>
  602. <li><b>set &lt;name&gt; brightness 0-255</b><br/>
  603. Adjust screen brightness.
  604. </li><br/>
  605. <li><b>set &lt;name&gt; clearCache</b><br/>
  606. Clear browser cache.
  607. </li><br/>
  608. <li><b>set &lt;name&gt; exit</b><br/>
  609. Terminate Fully.
  610. </li><br/>
  611. <li><b>set &lt;name&gt; motionDetection { on | off }</b><br/>
  612. Turn motion detection by camera on or off.
  613. </li><br/>
  614. <li><b>set &lt;name&gt; { lock | unlock }</b><br/>
  615. Lock or unlock display.
  616. </li><br/>
  617. <li><b>set &lt;name&gt; { on | off }</b><br/>
  618. Turn tablet display on or off.
  619. </li><br/>
  620. <li><b>set &lt;name&gt; on-for-timer [{ &lt;Seconds&gt; | <u>forever</u> | off }]</b><br/>
  621. Set timer for display. Default is forever.
  622. </li><br/>
  623. <li><b>set &lt;name&gt; photo</b><br/>
  624. Take a picture with device cam. Setting motion detection must be enabled. Picture
  625. can be viewed in remote admin interface under device info.
  626. </li><br/>
  627. <li><b>set &lt;name&gt; playSound &lt;url&gt; [loop]</b><br/>
  628. Play sound from URL.
  629. </li><br/>
  630. <li><b>set &lt;name&gt; restart</b><br/>
  631. Restart Fully.
  632. </li><br/>
  633. <li><b>set &lt;name&gt; screenOffTimer &lt;seconds&gt;</b><br/>
  634. Turn screen off after some idle seconds, set to 0 to disable timer.
  635. </li><br/>
  636. <li><b>set &lt;name&gt; screenSaver { start | stop }</b><br/>
  637. Start or stop screen saver. Screen saver URL can be set with command <b>set screenSaverURL</b>.
  638. </li><br/>
  639. <li><b>set &lt;name&gt; screenSaverTimer &lt;seconds&gt;</b><br/>
  640. Show screen saver URL after some idle seconds, set to 0 to disable timer.
  641. </li><br/>
  642. <li><b>set &lt;name&gt; screenSaverURL &lt;URL&gt;</b><br/>
  643. Show this URL when screensaver starts, set daydream: for Android daydream or dim: for black.<br/>
  644. </li><br/>
  645. <li><b>set &lt;name&gt; speak &lt;text&gt;</b><br/>
  646. Audio output of <i>text</i>. If <i>text</i> contains blanks it must be enclosed
  647. in double quotes. The text can contain device readings in format [device:reading].
  648. </li><br/>
  649. <li><b>set &lt;name&gt; startURL &lt;URL&gt;</b><br/>
  650. Show this URL when FULLY starts.<br/>
  651. </li><br/>
  652. <li><b>set &lt;name&gt; stopSound</b><br/>
  653. Stop playback of sound if playback has been started with option <i>loop</i>.
  654. </li><br/>
  655. <li><b>set &lt;name&gt; url [&lt;URL&gt;]</b><br/>
  656. Navigate to <i>URL</i>. If no URL is specified navigate to start URL.
  657. </li><br/>
  658. <li><b>set &lt;name&gt; volume &lt;level&gt; &lt;stream&gt;</b><br/>
  659. Set audio volume. Range of parameter <i>level</i> is 0-100, range of parameter
  660. <i>stream</i> is 1-10.
  661. </li><br/>
  662. </ul>
  663. <br/>
  664. <a name="FULLYget"></a>
  665. <b>Get</b><br/><br/>
  666. <ul>
  667. <li><b>get &lt;name&gt; info</b><br/>
  668. Display Fully information.
  669. </li><br/>
  670. <li><b>get &lt;name&gt; stats</b><br/>
  671. Show Fully statistics.
  672. </li><br/>
  673. <li><b>get &lt;name&gt; update</b><br/>
  674. Update readings.
  675. </li><br/>
  676. </ul>
  677. <br/>
  678. <a name="FULLYattr"></a>
  679. <b>Attributes</b><br/>
  680. <br/>
  681. <ul>
  682. <li><b>disable &lt;0 | 1&gt;</b><br/>
  683. Disable device and automatic polling.
  684. </li><br/>
  685. <li><b>pingBeforeCmd &lt;Count&gt;</b><br/>
  686. Send <i>Count</i> ping request to tablet before executing commands. Valid values
  687. for <i>Count</i> are 0,1,2. Default is 0 (do not send ping request).
  688. </li><br/>
  689. <li><b>pollInterval &lt;seconds&gt;</b><br/>
  690. Set polling interval for FULLY device information.
  691. If <i>seconds</i> is 0 polling is turned off. Valid values are from 10 to
  692. 86400 seconds.
  693. </li><br/>
  694. <li><b>requestTimeout &lt;seconds&gt;</b><br/>
  695. Set timeout for http requests. Default is 4 seconds.
  696. </li><br/>
  697. <li><b>repeatCommand &lt;Count&gt;</b><br/>
  698. Repeat fully command on failure. Valid values for <i>Count</i> are 0,1,2. Default
  699. is 0 (do not repeat commands).
  700. </li><br/>
  701. </ul>
  702. </ul>
  703. =end html
  704. =cut