86_Robonect.pm 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  1. ##############################################
  2. # $Id: 86_Robonect.pm 12685 2016-11-29 19:03:40Z andi291 $
  3. # ABU 20160307 First release
  4. # ABU 20160321 Added first parsing algos
  5. # ABU 20160311 Migrated to JSON
  6. # ABU 20160412 Integrated basic set-mechanism, added internals, changed callback-routine
  7. # ABU 20160413 Renamed to robonect, added status offline
  8. # ABU 20160414 Changed final two Automower to Robonect
  9. # ABU 20160414 Renamed private fn "decode"
  10. # ABU 20160415 Renamed private fn "decode" again, set eventonchangedreading, removed debug, increased interval to 90
  11. # ABU 20160416 Added logs, removed eventonchangedreading, added debug
  12. # ABU 20160416 Removed logs, added eventonchangedreading, removed debug, added 1=true for json data
  13. # ABU 20160426 Fixed decode warning, added error support
  14. # ABU 20160515 Added splitFn and doku
  15. # BJOERNAR 20160715 added duration and wlan-signal
  16. # ABU 20160831 Integrated API-changes for RC9, added attribute timeout for httpData
  17. # ABU 20160831 added calculations for duration and wlan - show in hours and percent
  18. # ABU 20160901 rounded duration and wlan
  19. # ABU 20161120 addaed encode_utf8 at json decode, tuned 0.5b repiar-stuff, added hibernate
  20. # ABU 20161126 added summary
  21. # ABU 20161129 fixed hash issues which prevents the module from loading
  22. package main;
  23. use strict;
  24. use warnings;
  25. use HttpUtils;
  26. use Encode;
  27. use JSON;
  28. my $EOD = "feierabend";
  29. my $HOME = "home";
  30. my $AUTO = "auto";
  31. my $MANUAL = "manuell";
  32. my $START = "start";
  33. my $STOP = "stop";
  34. my $OFFLINE = "offline";
  35. my $HYBERNATE = "winterschlaf";
  36. #available get cmds
  37. my %gets = (
  38. "status" => "noArg"
  39. );
  40. #available set cmds
  41. my %sets = (
  42. "feierabend" => "noArg",
  43. $HOME => "noArg",
  44. $AUTO => "noArg",
  45. $MANUAL => "noArg",
  46. $START => "noArg",
  47. $STOP => "noArg",
  48. $HYBERNATE => "on,off"
  49. );
  50. my %commands = (
  51. GET_STATUS => "cmd=status",
  52. SET_MODE => {$HOME=>"cmd=mode&mode=home", $MANUAL=>"cmd=mode&mode=man", $AUTO=>"cmd=mode&mode=auto", $EOD=>"cmd=mode&mode=eod", $STOP=>"cmd=stop", $START=>"cmd=start"}
  53. );
  54. #set to 1 for debug
  55. my $debug = 0;
  56. #elements within group next
  57. my %elements = (
  58. # "robonect" =>
  59. # {
  60. "successful" => {ALIAS=>"kommunikation", "true"=>"erfolgreich", "false"=>"fehlgeschlagen", 1=>"erfolgreich", 0=>"fehlgeschlagen"},
  61. "status" =>
  62. {
  63. ALIAS => "allgemein",
  64. "status" => {ALIAS=>"status", 0=>"schlafen", 1=>"parken", 2=>"maehen", 3=>"suche-base", 4=>"laden", 5=>"suche", 7=>"fehler", 8=>"schleife-fehlt", 16=>"abgeschaltet", 17=>"schlafen"},
  65. "mode" => {ALIAS=>"modus", 0=>"automatik", 1=>"manuell", 2=>"home", 3=>"demo"},
  66. "battery" => {ALIAS=>"batteriezustand"},
  67. "duration" => {ALIAS=>"dauer"},
  68. "hours" => {ALIAS=>"betriebsstunden"}
  69. },
  70. "timer" =>
  71. {
  72. ALIAS => "timer",
  73. "status" => {ALIAS=>"status", 0=>"deaktiviert", 1=>"aktiv", 2=>"standby"},
  74. "next" =>
  75. {
  76. ALIAS => "timer",
  77. "date" => {ALIAS=>"startdatum"},
  78. "time" => {ALIAS=>"startzeit"},
  79. #"date" => {ALIAS=>"start-unix"},
  80. }
  81. },
  82. "wlan" =>
  83. {
  84. ALIAS => "wlan",
  85. "signal" => {ALIAS=>"signal"}
  86. },
  87. "error" =>
  88. {
  89. ALIAS => "fehler",
  90. "error_code" => {ALIAS=>"code"},
  91. "error_message" => {ALIAS=>"nachricht"},
  92. "date" => {ALIAS=>"datum"},
  93. "time" => {ALIAS=>"zeit"}
  94. }
  95. # }
  96. );
  97. #Init this device
  98. #This declares the interface to fhem
  99. #############################
  100. sub Robonect_Initialize($) {
  101. my ($hash) = @_;
  102. $hash->{DefFn} = 'Robonect_Define';
  103. $hash->{UndefFn} = 'Robonect_Undef';
  104. $hash->{SetFn} = 'Robonect_Set';
  105. $hash->{GetFn} = 'Robonect_Get';
  106. $hash->{AttrFn} = 'Robonect_Attr';
  107. $hash->{ShutdownFn} = 'Robonect_Shutdown';
  108. $hash->{ReadyFn} = 'Robonect_Ready';
  109. $hash->{DbLog_splitFn} = 'Robonect_DbLog_split';
  110. $hash->{AttrList} = "do_not_notify:1,0 " . #supress any notification (including log)
  111. "showtime:1,0 " . #shows time instead of received value in state
  112. "credentials " . #user/password combination for authentication in mower, stored in a credentials file
  113. "basicAuth " . #user/password combination for authentication in mower
  114. "pollInterval " . #interval to poll in seconds
  115. "timeout " . #interval to poll in seconds
  116. "$readingFnAttributes "; #standard attributes
  117. }
  118. #Define this device
  119. #Is called at every define
  120. #############################
  121. sub Robonect_Define($$)
  122. {
  123. my ($hash, $def) = @_;
  124. my @a = split("[ \t][ \t]*", $def);
  125. #device name
  126. my $name = $a[0];
  127. #set verbose to 5, if debug enabled
  128. $attr{$name}{verbose} = 5 if ($debug eq 1);
  129. my $tempStr = join (", ", @a);
  130. Log3 ($name, 5, "define $name: enter $hash, attributes: $tempStr");
  131. #too less arguments
  132. return "wrong syntax - define <name> Robonect <ip-adress> [<user> <password>]" if (int(@a) < 3);
  133. #check IP
  134. my $ip = $a[2];
  135. #remove whitespaces
  136. $ip =~ s/^\s+|\s+$//g;
  137. #Syntax ok
  138. if ($ip =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)
  139. {
  140. my @octs = split (".", $ip);
  141. foreach my $octet (@octs)
  142. {
  143. return "wrong syntax - $octet has an invalid range. Allowed is 0..255" if (($octet >= 256) or ($octet <= -1));
  144. }
  145. }
  146. else
  147. {
  148. return "wrong syntax - IP must be supplied correctly <0..254>.<0..254>.<0..254>.<0..254>";
  149. }
  150. #wrong syntax for IP
  151. return "wrong syntax - IP must be supplied correctly <0..254>.<0..254>.<0..254>.<000..254>" if (int(@a) < 3);
  152. #assign name and port
  153. $hash->{NAME} = $name;
  154. $hash->{IP} = $ip;
  155. #backup name for a later rename
  156. $hash->{DEVNAME} = $name;
  157. #get first info and launch timer
  158. InternalTimer(gettimeofday(), "Robonect_GetUpdate", $hash, 0);
  159. #finally create decice
  160. #defptr is needed to supress FHEM input
  161. $modules{Robonect}{defptr}{$name} = $hash;
  162. #default event-on-changed-reading for all readings
  163. $attr{$name}{"event-on-change-reading"} = ".*";
  164. #defaul poll-interval
  165. $attr{$name}{"pollInterval"} = 90;
  166. Log3 ($name, 5, "exit define");
  167. return undef;
  168. }
  169. #Release this device
  170. #Is called at every delete / shutdown
  171. #############################
  172. sub Robonect_Undef($$)
  173. {
  174. my ($hash, $name) = @_;
  175. Log3 ($name, 5, "enter undef $name: hash: $hash name: $name");
  176. #kill interval timer
  177. RemoveInternalTimer($hash);
  178. #close port
  179. Robonect_Shutdown ($hash);
  180. #remove module. Refer to DevName, because module may be renamed
  181. delete $modules{KNX}{defptr}{$hash->{DEVNAME}};
  182. #remove name
  183. delete $hash->{NAME};
  184. #remove backuped name
  185. delete $hash->{DEVNAME};
  186. Log3 ($name, 5, "exit undef");
  187. return undef;
  188. }
  189. #Release this device
  190. #Is called at every delete / shutdown
  191. #############################
  192. sub Robonect_Shutdown($)
  193. {
  194. my ($hash) = @_;
  195. #hash may be de-referenced already
  196. my $name = "robonect-not_named_any_more";
  197. $name = $hash->{NAME} if (ref($hash) eq "HASH");
  198. Log3 ($name, 5, "enter shutdown $name: hash: $hash name: $name");
  199. Log3 ($name, 5, "exit shutdown");
  200. return undef;
  201. }
  202. #This function is called from fhem from rime to time
  203. #############################
  204. sub Robonect_Ready($)
  205. {
  206. my ($hash) = @_;
  207. my $name = $hash->{NAME};
  208. Log3 ($name, 5, "enter ready $name: hash: $hash name: $name");
  209. Log3 ($name, 5, "exit ready");
  210. return undef;
  211. }
  212. #Reads info from the mower
  213. #############################
  214. sub Robonect_Get($@)
  215. {
  216. my ($hash, @a) = @_;
  217. my $name = $hash->{NAME};
  218. my $tempStr = join (", ", @a);
  219. Log3 ($name, 5, "enter get $name: $name hash: $hash, attributes: $tempStr");
  220. #backup cmd
  221. my $cmd = $a[1];
  222. #lower cmd
  223. $cmd = lc($cmd);
  224. #create response, if cmd is wrong or gui asks
  225. my $cmdTemp = Robonect_getCmdList ($hash, $cmd, %gets);
  226. return $cmdTemp if (defined ($cmdTemp));
  227. my ($userName, $passWord) = Robonect_getCredentials ($hash);
  228. #basic url
  229. my $url = "http://" . $hash->{IP} . "/json?";
  230. #append userdata, if given
  231. $url = $url . "user=" . $userName . "&pass=" . $passWord . "&" if (defined ($userName) and defined ($passWord));
  232. #append command
  233. $url = $url . $commands{GET_STATUS};
  234. my $httpData;
  235. $httpData->{url} = $url;
  236. $httpData->{loglevel} = AttrVal ($name, "verbose", 2);
  237. $httpData->{loglevel} = 5;
  238. $httpData->{hideurl} = 0;
  239. $httpData->{callback} = \&Robonect_callback;
  240. $httpData->{hash} = $hash;
  241. $httpData->{cmd} = $commands{GET_STATUS};
  242. $httpData->{timeout} = AttrVal ($name, "timeout", 4);
  243. HttpUtils_NonblockingGet($httpData);
  244. Log3 ($name, 5, "exit get");
  245. my $err = $httpData->{err};
  246. if (defined ($err) and (length ($err) > 0))
  247. {
  248. return $err;
  249. }
  250. else
  251. {
  252. return "update requested";
  253. }
  254. }
  255. #Sends commands to the mower
  256. #############################
  257. sub Robonect_Set($@)
  258. {
  259. my ($hash, @a) = @_;
  260. my $name = $hash->{NAME};
  261. my $tempStr = join (", ", @a);
  262. Log3 ($name, 5, "enter set $name: $name hash: $hash, attributes: $tempStr");
  263. #backup cmd
  264. my $cmd = $a[1];
  265. #lower cmd
  266. $cmd = lc($cmd);
  267. #create response, if cmd is wrong or gui asks
  268. my $cmdTemp = Robonect_getCmdList ($hash, $cmd, %sets);
  269. return $cmdTemp if (defined ($cmdTemp));
  270. my ($userName, $passWord) = Robonect_getCredentials ($hash);
  271. my $decodedCmd = $commands{SET_MODE}{$cmd};
  272. #if command is hybernate, do this
  273. if ($cmd = lc($HYBERNATE))
  274. {
  275. Log3 ($name, 5, "got hybernate for set-command");
  276. my $val = lc($a[2]);
  277. $val = "off" if (!defined ($val));
  278. if ($val =~ m/on/)
  279. {
  280. readingsSingleUpdate($hash, $HYBERNATE, "on", 1);
  281. Log3 ($name, 5, "activated hybernate");
  282. }
  283. elsif ($val =~ m/off/)
  284. {
  285. readingsSingleUpdate($hash, $HYBERNATE, "off", 1);
  286. Log3 ($name, 5, "deactivated hybernate");
  287. }
  288. else
  289. {
  290. return "only on or off are supported for $HYBERNATE";
  291. }
  292. }
  293. #else proceed with communication to mower
  294. #execute it
  295. elsif (defined ($decodedCmd))
  296. {
  297. my $url = "http://" . $hash->{IP} . "/json?";
  298. #append userdata, if given
  299. $url = $url . "user=" . $userName . "&pass=" . $passWord . "&" if (defined ($userName) and defined ($passWord));
  300. #append command
  301. $url = $url . $decodedCmd;
  302. print "URL: $url\n";
  303. my $httpData;
  304. $httpData->{url} = $url;
  305. $httpData->{loglevel} = AttrVal ($name, "verbose", 2);
  306. $httpData->{loglevel} = 5;
  307. $httpData->{hideurl} = 0;
  308. $httpData->{callback} = \&Robonect_callback;
  309. $httpData->{hash} = $hash;
  310. $httpData->{cmd} = $decodedCmd;
  311. HttpUtils_NonblockingGet($httpData);
  312. return $httpData->{err};
  313. Robonect_GetUpdate($hash);
  314. }
  315. Log3 ($name, 5, "exit set");
  316. return;
  317. }
  318. #called on every mod of the attributes
  319. #############################
  320. sub Robonect_Attr(@)
  321. {
  322. my ($cmd,$name,$attr_name,$attr_value) = @_;
  323. Log3 ($name, 5, "enter attr $name: $name, attrName: $attr_name");
  324. #if($cmd eq "set")
  325. #{
  326. # if(($attr_name eq "debug") and (($attr_value eq "1") or ($attr_value eq "true")))
  327. # {
  328. # #todo
  329. # }
  330. #}
  331. Log3 ($name, 5, "exit attr");
  332. return undef;
  333. }
  334. #Split reading for DBLOG
  335. #############################
  336. sub Robonect_DbLog_split($) {
  337. my ($event) = @_;
  338. my ($reading, $value, $unit);
  339. my $tempStr = join (", ", @_);
  340. Log (5, "splitFn - enter, attributes: $tempStr");
  341. #detect reading - real reading or state?
  342. my $isReading = "false";
  343. $isReading = "true" if ($event =~ m/: /);
  344. #split input-string
  345. my @strings = split (" ", $event);
  346. my $startIndex = undef;
  347. $unit = "";
  348. return undef if (not defined ($strings[0]));
  349. #real reading?
  350. if ($isReading =~ m/true/)
  351. {
  352. #first one is always reading
  353. $reading = $strings[0];
  354. $reading =~ s/:?$//;
  355. $startIndex = 1;
  356. }
  357. #plain state
  358. else
  359. {
  360. #for reading state nothing is supplied
  361. $reading = "state";
  362. $startIndex = 0;
  363. }
  364. return undef if (not defined ($strings[$startIndex]));
  365. #per default join all single pieces
  366. $value = join(" ", @strings[$startIndex..(int(@strings) - 1)]);
  367. #numeric value?
  368. #if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+/)
  369. if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+$/)
  370. {
  371. $value = $strings[$startIndex];
  372. #single numeric value? Assume second par is unit...
  373. if ((defined ($strings[$startIndex + 1])) && !($strings[$startIndex+1] =~ /^[+-]?\d*[.,]?\d+/))
  374. {
  375. $unit = $strings[$startIndex + 1] if (defined ($strings[$startIndex + 1]));
  376. }
  377. }
  378. #numeric value?
  379. #if ($strings[$startIndex] =~ /^[+-]?\d*[.,]?\d+/)
  380. #{
  381. # $value = $strings[$startIndex];
  382. # $unit = $strings[$startIndex + 1] if (defined ($strings[$startIndex + 1]));
  383. #}
  384. #string or raw
  385. #else
  386. #{
  387. # $value = join(" ", @strings[$startIndex..(int(@strings) - 1)]);
  388. #}
  389. Log (5, "splitFn - READING: $reading, VALUE: $value, UNIT: $unit");
  390. return ($reading, $value, $unit);
  391. }
  392. #Called on the interval timer, if enabled
  393. #############################
  394. sub Robonect_GetUpdate($)
  395. {
  396. my ($hash) = @_;
  397. my $name = $hash->{NAME};
  398. Log3 ($name, 5, "enter update $name: $name");
  399. #evaluate reading hybernate
  400. my $hybernate = $hash->{READINGS}{$HYBERNATE}{VAL};
  401. #supress sending, if hybernate is set
  402. if (!defined ($hybernate) or ($hybernate =~ m/off/))
  403. {
  404. #get status
  405. my @callAttr;
  406. $callAttr[0] = $name;
  407. $callAttr[1] = "status";
  408. Robonect_Get ($hash, @callAttr);
  409. }
  410. my $interval = AttrVal($name,"pollInterval",90);
  411. #reset timer
  412. InternalTimer(gettimeofday() + $interval, "Robonect_GetUpdate", $hash, 1) if ($interval > 0);
  413. Log3 ($name, 5, "exit update");
  414. }
  415. #Private function which handles http-responses
  416. #############################
  417. sub Robonect_callback ($)
  418. {
  419. my ($param, $err, $data) = @_;
  420. my $hash = $param->{hash};
  421. my $name = $hash->{NAME};
  422. #wenn ein Fehler bei der HTTP Abfrage aufgetreten ist
  423. if($err ne "")
  424. {
  425. Log3 ($name, 3, "callback - error while requesting ".$param->{url}." - $err");
  426. $hash->{LAST_COMM_STATUS} = $err;
  427. #set reading with failure - notify only, when state has not changed
  428. readingsSingleUpdate($hash, "state", $OFFLINE, 1);
  429. }
  430. #wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)
  431. elsif($data ne "")
  432. {
  433. Log3 ($name, 3, "callback - url ".$param->{url}." returned: $data");
  434. #repair V5.0b
  435. $data =~ s/:"/,"/g;
  436. $data = "" if (!defined($data));
  437. my $answer = decode_json (encode_utf8($data));
  438. Log3 ($name, 3, "callback - url ".$param->{url}." repaired: $data");
  439. my ($key, $value) = Robonect_decodeContent ($hash, $answer, "successful", undef, undef);
  440. $hash->{LAST_CMD} = $param->{cmd};
  441. $hash->{LAST_COMM_STATUS} = "success: " . $value;
  442. Log3 ($name, 5, "callback - communication ok");
  443. #my %tmp = %$answer;
  444. #print "answer: ", %tmp, "\n";
  445. #status-readings
  446. if ($answer->{successful} =~ m/(true)|(1)/)
  447. {
  448. Log3 ($name, 5, "callback - update readings");
  449. readingsBeginUpdate($hash);
  450. #($key, $value) = Robonect_decodeContent ($hash, $answer, "successful", undef);
  451. #readingsBulkUpdate($hash, $key, $value);
  452. ($key, $value) = Robonect_decodeContent ($hash, $answer, "status", "status", undef);
  453. readingsBulkUpdate($hash, $key, $value);
  454. readingsBulkUpdate($hash, "state", $value);
  455. ($key, $value) = Robonect_decodeContent ($hash, $answer, "status", "mode", undef);
  456. readingsBulkUpdate($hash, $key, $value);
  457. ($key, $value) = Robonect_decodeContent ($hash, $answer, "status", "battery", undef);
  458. readingsBulkUpdate($hash, $key, $value);
  459. $value = 0;
  460. ($key, $value) = Robonect_decodeContent ($hash, $answer, "status", "duration", undef);
  461. readingsBulkUpdate($hash, $key, sprintf ("%d", $value/3600));
  462. ($key, $value) = Robonect_decodeContent ($hash, $answer, "status", "hours", undef);
  463. readingsBulkUpdate($hash, $key, $value);
  464. ($key, $value) = Robonect_decodeContent ($hash, $answer, "timer", "status", undef);
  465. readingsBulkUpdate($hash, $key, $value);
  466. ($key, $value) = Robonect_decodeContent ($hash, $answer, "timer", "next", "date");
  467. readingsBulkUpdate($hash, $key, $value);
  468. ($key, $value) = Robonect_decodeContent ($hash, $answer, "timer", "next", "time");
  469. readingsBulkUpdate($hash, $key, $value);
  470. $value = -95;
  471. ($key, $value) = Robonect_decodeContent ($hash, $answer, "wlan", "signal", undef);
  472. readingsBulkUpdate($hash, $key, $value);
  473. if (defined($value))
  474. {
  475. $value = sprintf ("%d", ($value + 95) / 0.6);
  476. readingsBulkUpdate($hash, $key . "-prozent", $value);
  477. }
  478. readingsEndUpdate($hash, 1);
  479. }
  480. #error?
  481. my $errorOccured = $answer->{status}->{status};
  482. if (defined ($errorOccured) and ($errorOccured =~ m/7/))
  483. {
  484. readingsSingleUpdate($hash, "fehler_aktuell", $answer->{error}->{error_message}, 1);
  485. }
  486. #no error
  487. elsif (defined ($errorOccured))
  488. {
  489. my $hashref = $hash->{READINGS};
  490. my %readings = %$hashref;
  491. #delete readings
  492. foreach my $key (keys %readings)
  493. {
  494. #delete $readings{$key} if ($key =~ m/^fehler.*/);
  495. delete $hash->{READINGS}{$key} if ($key =~ m/^fehler.*/);
  496. #delete $hash->{READINGS}{$key} if ($key =~ m/^fehler-aktuell.*/);
  497. }
  498. }
  499. }
  500. }
  501. #Private function to get json-content
  502. #############################
  503. sub Robonect_decodeContent ($$$$$)
  504. {
  505. my ($hash, $msg, $key1, $key2, $key3) = @_;
  506. my $name = $hash->{NAME};
  507. my $rdName = undef;
  508. my $rdValue = undef;
  509. my $template = undef;
  510. if (defined ($key2) && defined ($key3))
  511. {
  512. $template = $elements{$key1}{$key2}{$key3};
  513. $rdName = $elements{$key1}{$key2}{ALIAS} . "-" . $template->{ALIAS};
  514. $rdValue = $msg->{$key1}->{$key2}->{$key3};
  515. }
  516. elsif (defined ($key2))
  517. {
  518. $template = $elements{$key1}{$key2};
  519. $rdName = $elements{$key1}{ALIAS} . "-" . $template->{ALIAS};
  520. $rdValue = $msg->{$key1}->{$key2};
  521. }
  522. else
  523. {
  524. $template = $elements{$key1};
  525. $rdValue = $msg->{$key1};
  526. $rdName = $template->{ALIAS};
  527. }
  528. $rdValue = "undef" if (not defined ($rdValue));
  529. $rdValue = $template->{$rdValue} if (defined ($template->{$rdValue}));
  530. Log3 ($name, 5, "decodeContent - NAME: $rdName, VALUE: $rdValue");
  531. return $rdName, $rdValue;
  532. }
  533. #Private function to evaluate credentials
  534. #############################
  535. sub Robonect_decodeAnswer ($$$)
  536. {
  537. my ($hash, $getCmd, @readings) = @_;
  538. my $name = $hash->{NAME};
  539. my @list;
  540. foreach my $reading (@readings)
  541. {
  542. my $answer = undef;
  543. my $transval = undef;
  544. my ($header, $key, $value) = undef;
  545. $header = $reading->{header};
  546. $key = $reading->{key};
  547. $value = $reading->{value};
  548. if ($header =~ m/robonect/i)
  549. {
  550. $answer->{name} = $getCmd . "-" . $elements{$header}{$key}{ALIAS};
  551. $transval = $elements{$header}{$key}{$value};
  552. }
  553. else
  554. {
  555. $answer->{name} = $getCmd . "-" . $elements{"robonect"}{$header}{ALIAS} . "-" . $elements{"robonect"}{$header}{$key}{ALIAS};
  556. $transval = $elements{"robonect"}{$header}{$key}{$value};
  557. }
  558. if (defined($transval))
  559. {
  560. $answer->{value} = $transval;
  561. }
  562. else
  563. {
  564. $answer->{value} = $value;
  565. }
  566. #$answer->{name} = $getCmd . "-" . $answer->{name};
  567. Log3 ($name, 5, "decodeAnswer - NAME: $answer->{name}, VALUE: $answer->{value}");
  568. push (@list, $answer);
  569. }
  570. return @list;
  571. }
  572. #Private function to evaluate credentials
  573. #############################
  574. sub Robonect_getCredentials ($)
  575. {
  576. my ($hash) = @_;
  577. my $name = $hash->{NAME};
  578. my $userName = undef;
  579. my $passWord = undef;
  580. #parse basicAuth
  581. my $basicAuth = AttrVal ($name, "basicAuth", undef);
  582. if (defined ($basicAuth))
  583. {
  584. #if the string does NOT contain a ":", assume base64-encoded data
  585. if (not ($basicAuth =~ m/:/))
  586. {
  587. $basicAuth = decode_base64 ($basicAuth);
  588. Log3 ($name, 5, "credentials - found encrypted data");
  589. }
  590. #try to split
  591. my @plainAuth = split (":", $basicAuth);
  592. #found plain authentication
  593. if (int(@plainAuth) == 2)
  594. {
  595. $userName = $plainAuth[0];
  596. $passWord = $plainAuth[1];
  597. Log3 ($name, 5, "credentials - found plain data");
  598. }
  599. else
  600. {
  601. Log3 ($name, 0, "credentials - user/pw combination not correct");
  602. }
  603. }
  604. #parse credential-File - overrules basicAuth
  605. my $credentials = AttrVal ($name, "credentials", undef);
  606. if(defined($credentials))
  607. {
  608. #cannot open file
  609. if(!open(CFG, $credentials))
  610. {
  611. Log3 ($name, 0, "cannot open credentials file: $credentials") ;
  612. }
  613. #read it
  614. else
  615. {
  616. my @cfg = <CFG>;
  617. close(CFG);
  618. my %creds;
  619. eval join("", @cfg);
  620. #extract it
  621. $userName =~ $creds{$name}{username};
  622. $passWord =~ $creds{$name}{password};
  623. Log3 ($name, 5, "credentials - found in file");
  624. }
  625. }
  626. return $userName, $passWord;
  627. }
  628. #Private function to evaluate command-lists
  629. #############################
  630. sub Robonect_getCmdList ($$$)
  631. {
  632. my ($hash, $cmd, %cmdArray) = @_;
  633. my $name = $hash->{NAME};
  634. #return, if cmd is valid
  635. return undef if (defined ($cmd) and defined ($cmdArray{$cmd}));
  636. #response for gui or the user, if command is invalid
  637. my $retVal;
  638. foreach my $mySet (keys %cmdArray)
  639. {
  640. #append set-command
  641. $retVal = $retVal . " " if (defined ($retVal));
  642. $retVal = $retVal . $mySet;
  643. #get options
  644. my $myOpt = $cmdArray{$mySet};
  645. #append option, if valid
  646. $retVal = $retVal . ":" . $myOpt if (defined ($myOpt) and (length ($myOpt) > 0));
  647. $myOpt = "" if (!defined($myOpt));
  648. Log3 ($name, 5, "parse cmd-table - Set:$mySet, Option:$myOpt, RetVal:$retVal");
  649. }
  650. if (!defined ($retVal))
  651. {
  652. $retVal = "error while parsing set-table" ;
  653. }
  654. else
  655. {
  656. $retVal = "Unknown argument $cmd, choose one of " . $retVal;
  657. }
  658. return $retVal;
  659. }
  660. 1;
  661. =pod
  662. =begin html
  663. <a name="Robonect"></a>
  664. <h3>Robonect</h3>
  665. <ul>
  666. <p>Robonect is a after-market wifi-module for robomowers based on the husky G3-control. It was developed by Fabian H. and can be optained at www.robonect.de. This module gives you access to the basic commands. This module will not work without libjson-perl! Do not forget to install it first!</p>
  667. <p><a name="RobonectDefine"></a> <b>Define</b></p>
  668. <ul>
  669. <code>define &lt;name&gt; Robonect &lt;ip-adress&gt [&lt;user&gt; &lt;password&gt;]</code>
  670. <p>Setting Winterschlaf prevents communicating with the mower.</p>
  671. <p>The authentication can be supplied in the definition as plaine text or in a differen way - see the attributes. Per default the status is polled every 90s.</p>
  672. <p>Example:</p>
  673. <pre>
  674. define myMower Robonect 192.168.13.5 test tmySecret
  675. </pre>
  676. </ul>
  677. <p><a name="RobonectSet"></a> <b>Set</b></p>
  678. <ul>
  679. Switch the mower to automatic-timer:
  680. <code>set &lt;name&gt; auto</code>
  681. Send the mower home - prevents further runs triggered by timer (persistent):
  682. <code>set &lt;name&gt; home</code>
  683. Sends the mower home for the actual timer-slot. The next timer-slot starts the mower again:
  684. <code>set &lt;name&gt; feierabend</code>
  685. Start the mower (only needed after a manual stop:
  686. <code>set &lt;name&gt; start</code>
  687. Stop the mower immediately:
  688. <code>set &lt;name&gt; stop</code>
  689. </ul>
  690. <p><a name="RobonectGet"></a> <b>Get</b></p>
  691. <ul>
  692. <p>Gets the actual state of the mower - normally not needed, because the status is polled cyclic.</p>
  693. </ul>
  694. <p><a name="RobonectAttr"></a> <b>Attributes</b></p>
  695. <ul><br>
  696. Common attributes:<br>
  697. <a href="#DbLogInclude">DbLogInclude</a><br>
  698. <a href="#DbLogExclude">DbLogExclude</a><br>
  699. <a href="#IODev">IODev</a><br>
  700. <a href="#alias">alias</a><br>
  701. <a href="#comment">comment</a><br>
  702. <a href="#devStateIcon">devStateIcon</a><br>
  703. <a href="#devStateStyle">devStateStyle</a><br>
  704. <a href="#do_not_notify">do_not_notify</a><br>
  705. <a href="#readingFnAttributes">readingFnAttributes</a><br>
  706. <a href="#event-aggregator">event-aggregator</a><br>
  707. <a href="#event-min-interval">event-min-interval</a><br>
  708. <a href="#event-on-change-reading">event-on-change-reading</a><br>
  709. <a href="#event-on-update-reading">event-on-update-reading</a><br>
  710. <a href="#eventMap">eventMap</a><br>
  711. <a href="#group">group</a><br>
  712. <a href="#icon">icon</a><br>
  713. <a href="#room">room</a><br>
  714. <a href="#showtime">showtime</a><br>
  715. <a href="#sortby">sortby</a><br>
  716. <a href="#stateFormat">stateFormat</a><br>
  717. <a href="#userReadings">userReadings</a><br>
  718. <a href="#userattr">userattr</a><br>
  719. <a href="#verbose">verbose</a><br>
  720. <a href="#webCmd">webCmd</a><br>
  721. <a href="#widgetOverride">widgetOverride</a><br>
  722. <br>
  723. </ul>
  724. <p><a name="RobonectCredentials"></a> <b>credentials</b></p>
  725. <ul>
  726. If you supply a valid path to a credentials file, this combination is used to log in at robonect. This mechism overrules basicAuth.
  727. </ul>
  728. <p><a name="RobonectBasicAuth"></a> <b>basicAuth</b></p>
  729. <ul>
  730. You can supply username and password plain or base-64-encoded. For a base64-encoder, use google.
  731. <p>Example:</p>
  732. <pre>
  733. define myMower 192.168.5.1
  734. attr myMower basicAuth me:mySecret
  735. </pre>
  736. <pre>
  737. define myMower 192.168.5.1
  738. attr myMower basicAuth bWU6bXlTZWNyZXQ=
  739. </pre>
  740. </ul>
  741. <p><a name="RobonectPollInterval"></a> <b>pollInterval</b></p>
  742. <ul>
  743. Supplies the interval top poll the robonect in seconds. Per default 90s is set.
  744. </ul>
  745. <p><a name="RobonectTimeout"></a> <b>timeout</b></p>
  746. <ul>
  747. Timeout for httpData to recive data. Default is 4s.
  748. </ul>
  749. </ul>
  750. =end html
  751. =device
  752. =item summary Communicates to HW-module robonect
  753. =item summary_DE Kommuniziert mit dem HW-Modul Robonect
  754. =begin html_DE
  755. <a name="Robonect"></a>
  756. <h3>Robonect</h3>
  757. <ul>
  758. <p>Robonect ist ein Nachrüstmodul für automower, die auf der Husky-G3-Steuerung basieren. Es wurde von Fabian H. entwickelt und kann unter www.robonect.de bezogen werden. Dieses Modul gibt Euch Zugriff auf die nötigsten Kommandos. Dieses Modul benötigt libjson-perl. Bitte NICHT VERGESSEN zu installieren!</p>
  759. <p><a name="RobonectDefine"></a> <b>Define</b></p>
  760. <ul>
  761. <code>define &lt;name&gt; Robonect &lt;ip-adress&gt [&lt;user&gt; &lt;password&gt;]</code>
  762. <p>Mit gesetztem Winterschlaf wird die Kommunikation zum Mäher unterbunden.</p>
  763. <p>Die Zugangsinformationen können im Klartext bei der Definition angegeben werden. Wahlweise auch per Attribut. Standardmäßig wird der Status vom RObonect alle 90s aktualisiert.</p>
  764. <p>Beispiel:</p>
  765. <pre>
  766. define myMower Robonect 192.168.13.5 test tmySecret
  767. </pre>
  768. </ul>
  769. <p><a name="RobonectSet"></a> <b>Set</b></p>
  770. <ul>
  771. Versetzt den Mäher in den timerbasierten Automatikmodus:
  772. <code>set &lt;name&gt; auto</code>
  773. Schickt den Mäher nach hause. Ein erneutes Starten per Timer wird verhindert (persistent):
  774. <code>set &lt;name&gt; home</code>
  775. Schickt den Mäher nach Hause. Beim nächsten Timerstart fährt der Mäher wieder regulär:
  776. <code>set &lt;name&gt; feierabend</code>
  777. Startet den Mäher (wird nur nach einem manuellen Stop benötigt):
  778. <code>set &lt;name&gt; start</code>
  779. Stoppt den Mäher:
  780. <code>set &lt;name&gt; stop</code>
  781. </ul>
  782. <p><a name="RobonectGet"></a> <b>Get</b></p>
  783. <ul>
  784. <p>Holt den aktuellen Status des Mähers. Wird normalerweise nicht benötigt, da automatisch gepolled wird.</p>
  785. </ul>
  786. <p><a name="RobonectAttr"></a> <b>Attributes</b></p>
  787. <ul><br>
  788. Common attributes:<br>
  789. <a href="#DbLogInclude">DbLogInclude</a><br>
  790. <a href="#DbLogExclude">DbLogExclude</a><br>
  791. <a href="#IODev">IODev</a><br>
  792. <a href="#alias">alias</a><br>
  793. <a href="#comment">comment</a><br>
  794. <a href="#devStateIcon">devStateIcon</a><br>
  795. <a href="#devStateStyle">devStateStyle</a><br>
  796. <a href="#do_not_notify">do_not_notify</a><br>
  797. <a href="#readingFnAttributes">readingFnAttributes</a><br>
  798. <a href="#event-aggregator">event-aggregator</a><br>
  799. <a href="#event-min-interval">event-min-interval</a><br>
  800. <a href="#event-on-change-reading">event-on-change-reading</a><br>
  801. <a href="#event-on-update-reading">event-on-update-reading</a><br>
  802. <a href="#eventMap">eventMap</a><br>
  803. <a href="#group">group</a><br>
  804. <a href="#icon">icon</a><br>
  805. <a href="#room">room</a><br>
  806. <a href="#showtime">showtime</a><br>
  807. <a href="#sortby">sortby</a><br>
  808. <a href="#stateFormat">stateFormat</a><br>
  809. <a href="#userReadings">userReadings</a><br>
  810. <a href="#userattr">userattr</a><br>
  811. <a href="#verbose">verbose</a><br>
  812. <a href="#webCmd">webCmd</a><br>
  813. <a href="#widgetOverride">widgetOverride</a><br>
  814. <br>
  815. </ul>
  816. <p><a name="RobonectCredentials"></a> <b>credentials</b></p>
  817. <ul>
  818. Hier kann ein Link auf ein credentials-file angegeben werden. Die Zugansinformationen werden dann aus der Datei geholt. Dieser Mechanismus überschreibt basicAuth.
  819. </ul>
  820. <p><a name="RobonectBasicAuth"></a> <b>basicAuth</b></p>
  821. <ul>
  822. Hier werden die Zugangsinformationen entweder im Klartext oder base-64-codiert übergeben. Base64-encoder gibts bei google.
  823. <p>Example:</p>
  824. <pre>
  825. define myMower 192.168.5.1
  826. attr myMower basicAuth me:mySecret
  827. </pre>
  828. <pre>
  829. define myMower 192.168.5.1
  830. attr myMower basicAuth bWU6bXlTZWNyZXQ=
  831. </pre>
  832. </ul>
  833. <p><a name="RobonectPollInterval"></a> <b>pollInterval</b></p>
  834. <ul>
  835. Hier kann das polling-interval in Sekunden angegeben werden. Default sind 90s.
  836. </ul>
  837. <p><a name="RobonectTimeout"></a> <b>timeout</b></p>
  838. <ul>
  839. Für das holen der Daten per Wlan kann hier ein Timeout angegeben werden. Default sind 4s.
  840. </ul>
  841. </ul>
  842. =end html_DE
  843. =cut