74_HusqvarnaAutomower.pm 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  1. ###############################################################################
  2. #
  3. # (c) 2018 Copyright: Dr. Dennis Krannich (blog at krannich dot de)
  4. # All rights reserved
  5. #
  6. # This script is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # any later version.
  10. #
  11. # The GNU General Public License can be found at
  12. # http://www.gnu.org/copyleft/gpl.html.
  13. # A copy is found in the textfile GPL.txt and important notices to the license
  14. # from the author is found in LICENSE.txt distributed with these scripts.
  15. #
  16. # This script is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU General Public License for more details.
  20. #
  21. #
  22. # $Id: 74_HusqvarnaAutomower.pm 16888 2018-06-19 21:07:42Z krannich $
  23. #
  24. ################################################################################
  25. package main;
  26. my $missingModul = "";
  27. use strict;
  28. use warnings;
  29. use Time::Local;
  30. use JSON;
  31. use HttpUtils;
  32. use Blocking;
  33. eval "use JSON;1" or $missingModul .= "JSON ";
  34. my $version = "0.1";
  35. use constant AUTHURL => "https://iam-api.dss.husqvarnagroup.net/api/v3/";
  36. use constant APIURL => "https://amc-api.dss.husqvarnagroup.net/app/v1/";
  37. ##############################################################
  38. #
  39. # Declare functions
  40. #
  41. ##############################################################
  42. sub HusqvarnaAutomower_Initialize($);
  43. sub HusqvarnaAutomower_Define($$);
  44. sub HusqvarnaAutomower_Notify($$);
  45. sub HusqvarnaAutomower_Attr(@);
  46. sub HusqvarnaAutomower_Set($@);
  47. sub HusqvarnaAutomower_Undef($$);
  48. sub HusqvarnaAutomower_CONNECTED($@);
  49. sub HusqvarnaAutomower_CMD($$);
  50. ##############################################################
  51. sub HusqvarnaAutomower_Initialize($) {
  52. my ($hash) = @_;
  53. $hash->{SetFn} = "HusqvarnaAutomower_Set";
  54. $hash->{DefFn} = "HusqvarnaAutomower_Define";
  55. $hash->{UndefFn} = "HusqvarnaAutomower_Undef";
  56. $hash->{NotifyFn} = "HusqvarnaAutomower_Notify";
  57. $hash->{AttrFn} = "HusqvarnaAutomower_Attr";
  58. $hash->{AttrList} = "username " .
  59. "password " .
  60. "mower " .
  61. "language " .
  62. "interval " .
  63. $readingFnAttributes;
  64. foreach my $d(sort keys %{$modules{HusqvarnaAutomower}{defptr}}) {
  65. my $hash = $modules{HusqvarnaAutomower}{defptr}{$d};
  66. $hash->{HusqvarnaAutomower}{version} = $version;
  67. }
  68. }
  69. sub HusqvarnaAutomower_Define($$){
  70. my ( $hash, $def ) = @_;
  71. my @a = split( "[ \t]+", $def );
  72. my $name = $a[0];
  73. return "too few parameters: define <NAME> HusqvarnaAutomower" if( @a < 1 ) ;
  74. return "Cannot define HusqvarnaAutomower device. Perl modul $missingModul is missing." if ( $missingModul );
  75. %$hash = (%$hash,
  76. NOTIFYDEV => "global,$name",
  77. HusqvarnaAutomower => {
  78. CONNECTED => 0,
  79. version => $version,
  80. token => '',
  81. provider => '',
  82. user_id => '',
  83. mower_id => '',
  84. mower_name => '',
  85. mower_model => '',
  86. mower_battery => 0,
  87. mower_status => '',
  88. mower_mode => '',
  89. mower_cuttingMode => '',
  90. mower_lastLatitude => 0,
  91. mower_lastLongitude => 0,
  92. mower_nextStart => 0,
  93. mower_nextStartSource => '',
  94. mower_restrictedReason => '',
  95. mower => 0,
  96. username => '',
  97. language => 'DE',
  98. password => '',
  99. interval => 300,
  100. expires => time(),
  101. },
  102. );
  103. $attr{$name}{room} = "HusqvarnaAutomower" if( !defined( $attr{$name}{room} ) );
  104. HusqvarnaAutomower_CONNECTED($hash,'initialized');
  105. return undef;
  106. }
  107. sub HusqvarnaAutomower_Notify($$) {
  108. my ($hash,$dev) = @_;
  109. my ($name) = ($hash->{NAME});
  110. if (AttrVal($name, "disable", 0)) {
  111. Log3 $name, 5, "Device '$name' is disabled, do nothing...";
  112. HusqvarnaAutomower_CONNECTED($hash,'disabled');
  113. return undef;
  114. }
  115. my $devname = $dev->{NAME};
  116. my $devtype = $dev->{TYPE};
  117. my $events = deviceEvents($dev,1);
  118. return if (!$events);
  119. $hash->{HusqvarnaAutomower}->{updateStartTime} = time();
  120. if ( $devtype eq 'Global') {
  121. if (
  122. grep /^INITIALIZED$/,@{$events}
  123. or grep /^REREADCFG$/,@{$events}
  124. or grep /^DEFINED.$name$/,@{$events}
  125. or grep /^MODIFIED.$name$/,@{$events}
  126. ) {
  127. HusqvarnaAutomower_APIAuth($hash);
  128. }
  129. }
  130. if ( $devtype eq 'HusqvarnaAutomower') {
  131. if ( grep(/^state:.authenticated$/, @{$events}) ) {
  132. HusqvarnaAutomower_getMower($hash);
  133. }
  134. if ( grep(/^state:.connected$/, @{$events}) ) {
  135. HusqvarnaAutomower_DoUpdate($hash);
  136. }
  137. if ( grep(/^state:.disconnected$/, @{$events}) ) {
  138. Log3 $name, 3, "Reconnecting...";
  139. HusqvarnaAutomower_APIAuth($hash);
  140. }
  141. }
  142. return undef;
  143. }
  144. sub HusqvarnaAutomower_Attr(@) {
  145. my ( $cmd, $name, $attrName, $attrVal ) = @_;
  146. my $hash = $defs{$name};
  147. if( $attrName eq "disable" ) {
  148. if( $cmd eq "set" and $attrVal eq "1" ) {
  149. RemoveInternalTimer($hash);
  150. readingsSingleUpdate ( $hash, "state", "disable", 1 );
  151. Log3 $name, 5, "$name - disabled";
  152. }
  153. elsif( $cmd eq "del" ) {
  154. readingsSingleUpdate ( $hash, "state", "active", 1 );
  155. Log3 $name, 5, "$name - enabled";
  156. }
  157. }
  158. elsif( $attrName eq "username" ) {
  159. if( $cmd eq "set" ) {
  160. $hash->{HusqvarnaAutomower}->{username} = $attrVal;
  161. Log3 $name, 5, "$name - username set to " . $hash->{HusqvarnaAutomower}->{username};
  162. }
  163. }
  164. elsif( $attrName eq "password" ) {
  165. if( $cmd eq "set" ) {
  166. $hash->{HusqvarnaAutomower}->{password} = $attrVal;
  167. Log3 $name, 5, "$name - password set to " . $hash->{HusqvarnaAutomower}->{password};
  168. }
  169. }
  170. elsif( $attrName eq "language" ) {
  171. if( $cmd eq "set" ) {
  172. $hash->{HusqvarnaAutomower}->{language} = $attrVal;
  173. Log3 $name, 5, "$name - language set to " . $hash->{HusqvarnaAutomower}->{language};
  174. }
  175. }
  176. elsif( $attrName eq "mower" ) {
  177. if( $cmd eq "set" ) {
  178. $hash->{HusqvarnaAutomower}->{mower} = $attrVal;
  179. Log3 $name, 5, "$name - mower set to " . $hash->{HusqvarnaAutomower}->{mower};
  180. }
  181. elsif( $cmd eq "del" ) {
  182. $hash->{HusqvarnaAutomower}->{mower} = 0;
  183. Log3 $name, 5, "$name - deleted mower and set to default: 0";
  184. }
  185. }
  186. elsif( $attrName eq "interval" ) {
  187. if( $cmd eq "set" ) {
  188. return "Interval must be greater than 0"
  189. unless($attrVal > 0);
  190. $hash->{HusqvarnaAutomower}->{interval} = $attrVal;
  191. RemoveInternalTimer($hash);
  192. InternalTimer( time() + $hash->{HusqvarnaAutomower}->{interval}, "HusqvarnaAutomower_DoUpdate", $hash, 0 );
  193. Log3 $name, 5, "$name - set interval: $attrVal";
  194. }
  195. elsif( $cmd eq "del" ) {
  196. $hash->{HusqvarnaAutomower}->{interval} = 300;
  197. RemoveInternalTimer($hash);
  198. InternalTimer( time() + $hash->{HusqvarnaAutomower}->{interval}, "HusqvarnaAutomower_DoUpdate", $hash, 0 );
  199. Log3 $name, 5, "$name - deleted interval and set to default: 300";
  200. }
  201. }
  202. return undef;
  203. }
  204. sub HusqvarnaAutomower_Undef($$){
  205. my ( $hash, $arg ) = @_;
  206. my $name = $hash->{NAME};
  207. my $deviceId = $hash->{DEVICEID};
  208. delete $modules{HusqvarnaAutomower}{defptr}{$deviceId};
  209. RemoveInternalTimer($hash);
  210. return undef;
  211. }
  212. sub HusqvarnaAutomower_Set($@){
  213. my ($hash,@a) = @_;
  214. return "\"set $hash->{NAME}\" needs at least an argument" if ( @a < 2 );
  215. my ($name,$setName,$setVal,$setVal2,$setVal3) = @a;
  216. Log3 $name, 3, "$name: set called with $setName " . ($setVal ? $setVal : "") if ($setName ne "?");
  217. if (HusqvarnaAutomower_CONNECTED($hash) eq 'disabled' && $setName !~ /clear/) {
  218. return "Unknown argument $setName, choose one of clear:all,readings";
  219. Log3 $name, 3, "$name: set called with $setName but device is disabled!" if ($setName ne "?");
  220. return undef;
  221. }
  222. if ($setName !~ /start|stop|park|update/) {
  223. return "Unknown argument $setName, choose one of start stop park update";
  224. }
  225. if ($setName eq 'update') {
  226. RemoveInternalTimer($hash);
  227. HusqvarnaAutomower_DoUpdate($hash);
  228. }
  229. if (HusqvarnaAutomower_CONNECTED($hash)) {
  230. if ($setName eq 'start') {
  231. HusqvarnaAutomower_CMD($hash,'START');
  232. } elsif ($setName eq 'stop') {
  233. HusqvarnaAutomower_CMD($hash,'STOP');
  234. } elsif ($setName eq 'park') {
  235. HusqvarnaAutomower_CMD($hash,'PARK');
  236. }
  237. }
  238. return undef;
  239. }
  240. ##############################################################
  241. #
  242. # API AUTHENTICATION
  243. #
  244. ##############################################################
  245. sub HusqvarnaAutomower_APIAuth($) {
  246. my ($hash, $def) = @_;
  247. my $name = $hash->{NAME};
  248. my $username = $hash->{HusqvarnaAutomower}->{username};
  249. my $password = $hash->{HusqvarnaAutomower}->{password};
  250. my $header = "Content-Type: application/json\r\nAccept: application/json";
  251. my $json = ' {
  252. "data" : {
  253. "type" : "token",
  254. "attributes" : {
  255. "username" : "' . $username. '",
  256. "password" : "' . $password. '"
  257. }
  258. }
  259. }';
  260. HttpUtils_NonblockingGet({
  261. url => AUTHURL . "token",
  262. timeout => 5,
  263. hash => $hash,
  264. method => "POST",
  265. header => $header,
  266. data => $json,
  267. callback => \&HusqvarnaAutomower_APIAuthResponse,
  268. });
  269. }
  270. sub HusqvarnaAutomower_APIAuthResponse($) {
  271. my ($param, $err, $data) = @_;
  272. my $hash = $param->{hash};
  273. my $name = $hash->{NAME};
  274. if($err ne "") {
  275. HusqvarnaAutomower_CONNECTED($hash,'error');
  276. Log3 $name, 5, "error while requesting ".$param->{url}." - $err";
  277. } elsif($data ne "") {
  278. my $result = decode_json($data);
  279. if ($result->{errors}) {
  280. HusqvarnaAutomower_CONNECTED($hash,'error');
  281. Log3 $name, 5, "Error: " . $result->{errors}[0]->{detail};
  282. } else {
  283. Log3 $name, 5, "$data";
  284. $hash->{HusqvarnaAutomower}->{token} = $result->{data}{id};
  285. $hash->{HusqvarnaAutomower}->{provider} = $result->{data}{attributes}{provider};
  286. $hash->{HusqvarnaAutomower}->{user_id} = $result->{data}{attributes}{user_id};
  287. $hash->{HusqvarnaAutomower}->{expires} = time() + $result->{data}{attributes}{expires_in};
  288. # set Readings
  289. readingsBeginUpdate($hash);
  290. readingsBulkUpdate($hash,'token',$hash->{HusqvarnaAutomower}->{token} );
  291. readingsBulkUpdate($hash,'provider',$hash->{HusqvarnaAutomower}->{provider} );
  292. readingsBulkUpdate($hash,'user_id',$hash->{HusqvarnaAutomower}->{user_id} );
  293. my $expire_date = strftime("%Y-%m-%d %H:%M:%S", localtime($hash->{HusqvarnaAutomower}->{expires}));
  294. readingsBulkUpdate($hash,'expires',$expire_date );
  295. readingsEndUpdate($hash, 1);
  296. HusqvarnaAutomower_CONNECTED($hash,'authenticated');
  297. }
  298. }
  299. }
  300. sub HusqvarnaAutomower_CONNECTED($@) {
  301. my ($hash,$set) = @_;
  302. if ($set) {
  303. $hash->{HusqvarnaAutomower}->{CONNECTED} = $set;
  304. RemoveInternalTimer($hash);
  305. %{$hash->{updateDispatch}} = ();
  306. if (!defined($hash->{READINGS}->{state}->{VAL}) || $hash->{READINGS}->{state}->{VAL} ne $set) {
  307. readingsSingleUpdate($hash,"state",$set,1);
  308. }
  309. return undef;
  310. } else {
  311. if ($hash->{HusqvarnaAutomower}->{CONNECTED} eq 'disabled') {
  312. return 'disabled';
  313. }
  314. elsif ($hash->{HusqvarnaAutomower}->{CONNECTED} eq 'connected') {
  315. return 1;
  316. } else {
  317. return 0;
  318. }
  319. }
  320. }
  321. ##############################################################
  322. #
  323. # UPDATE FUNCTIONS
  324. #
  325. ##############################################################
  326. sub HusqvarnaAutomower_DoUpdate($) {
  327. my ($hash) = @_;
  328. my ($name,$self) = ($hash->{NAME},HusqvarnaAutomower_Whoami());
  329. Log3 $name, 3, "doUpdate() called.";
  330. if (HusqvarnaAutomower_CONNECTED($hash) eq "disabled") {
  331. Log3 $name, 3, "$name - Device is disabled.";
  332. return undef;
  333. }
  334. if (time() >= $hash->{HusqvarnaAutomower}->{expires} ) {
  335. Log3 $name, 3, "LOGIN TOKEN MISSING OR EXPIRED";
  336. HusqvarnaAutomower_CONNECTED($hash,'disconnected');
  337. } elsif ($hash->{HusqvarnaAutomower}->{CONNECTED} eq 'connected') {
  338. Log3 $name, 3, "Update with device: " . $hash->{HusqvarnaAutomower}->{mower_id};
  339. HusqvarnaAutomower_getMowerStatus($hash);
  340. InternalTimer( time() + $hash->{HusqvarnaAutomower}->{interval}, $self, $hash, 0 );
  341. }
  342. }
  343. ##############################################################
  344. #
  345. # GET MOWERS
  346. #
  347. ##############################################################
  348. sub HusqvarnaAutomower_getMower($) {
  349. my ($hash) = @_;
  350. my ($name) = $hash->{NAME};
  351. my $token = $hash->{HusqvarnaAutomower}->{token};
  352. my $provider = $hash->{HusqvarnaAutomower}->{provider};
  353. my $header = "Content-Type: application/json\r\nAccept: application/json\r\nAuthorization: Bearer " . $token . "\r\nAuthorization-Provider: " . $provider;
  354. HttpUtils_NonblockingGet({
  355. url => APIURL . "mowers",
  356. timeout => 5,
  357. hash => $hash,
  358. method => "GET",
  359. header => $header,
  360. callback => \&HusqvarnaAutomower_getMowerResponse,
  361. });
  362. return undef;
  363. }
  364. sub HusqvarnaAutomower_getMowerResponse($) {
  365. my ($param, $err, $data) = @_;
  366. my $hash = $param->{hash};
  367. my $name = $hash->{NAME};
  368. if($err ne "") {
  369. Log3 $name, 5, "error while requesting ".$param->{url}." - $err";
  370. } elsif($data ne "") {
  371. if ($data eq "[]") {
  372. Log3 $name, 3, "Please register an automower first";
  373. $hash->{HusqvarnaAutomower}->{mower_id} = "none";
  374. # STATUS LOGGEDIN MUST BE REMOVED
  375. HusqvarnaAutomower_CONNECTED($hash,'connected');
  376. } else {
  377. Log3 $name, 5, "Automower(s) found";
  378. Log3 $name, 5, $data;
  379. my $result = decode_json($data);
  380. my $mower = $hash->{HusqvarnaAutomower}->{mower};
  381. Log3 $name, 5, $result->[$mower]->{'name'};
  382. # MOWER DATA
  383. my $mymower = $result->[$mower];
  384. $hash->{HusqvarnaAutomower}->{mower_id} = $mymower->{'id'};
  385. $hash->{HusqvarnaAutomower}->{mower_name} = $mymower->{'name'};
  386. $hash->{HusqvarnaAutomower}->{mower_model} = $mymower->{'model'};
  387. # MOWER STATUS
  388. my $mymowerStatus = $mymower->{'status'};
  389. $hash->{HusqvarnaAutomower}->{mower_battery} = $mymowerStatus->{'batteryPercent'};
  390. $hash->{HusqvarnaAutomower}->{mower_status} = $mymowerStatus->{'mowerStatus'}->{'activity'};
  391. $hash->{HusqvarnaAutomower}->{mower_mode} = $mymowerStatus->{'operatingMode'};
  392. $hash->{HusqvarnaAutomower}->{mower_nextStart} = HusqvarnaAutomower_Correct_Localtime( $mymowerStatus->{'nextStartTimestamp'} );
  393. HusqvarnaAutomower_CONNECTED($hash,'connected');
  394. }
  395. readingsBeginUpdate($hash);
  396. #readingsBulkUpdate($hash,$reading,$value);
  397. readingsBulkUpdate($hash, "mower_id", $hash->{HusqvarnaAutomower}->{mower_id} );
  398. readingsBulkUpdate($hash, "mower_name", $hash->{HusqvarnaAutomower}->{mower_name} );
  399. readingsBulkUpdate($hash, "mower_battery", chop($hash->{HusqvarnaAutomower}->{mower_battery}) );
  400. readingsBulkUpdate($hash, "mower_status", $hash->{HusqvarnaAutomower}->{mower_status} );
  401. readingsBulkUpdate($hash, "mower_mode", HusqvarnaAutomower_ToGerman($hash, $hash->{HusqvarnaAutomower}->{mower_mode} ));
  402. my $nextStartTimestamp = strftime("%Y-%m-%d %H:%M:%S", localtime($hash->{HusqvarnaAutomower}->{mower_nextStart}) );
  403. readingsBulkUpdate($hash, "mower_nextStart", $nextStartTimestamp );
  404. readingsEndUpdate($hash, 1);
  405. }
  406. return undef;
  407. }
  408. sub HusqvarnaAutomower_getMowerStatus($) {
  409. my ($hash) = @_;
  410. my ($name) = $hash->{NAME};
  411. my $token = $hash->{HusqvarnaAutomower}->{token};
  412. my $provider = $hash->{HusqvarnaAutomower}->{provider};
  413. my $header = "Content-Type: application/json\r\nAccept: application/json\r\nAuthorization: Bearer " . $token . "\r\nAuthorization-Provider: " . $provider;
  414. my $mymower_id = $hash->{HusqvarnaAutomower}->{mower_id};
  415. HttpUtils_NonblockingGet({
  416. url => APIURL . "mowers/" . $mymower_id . "/status",
  417. timeout => 5,
  418. hash => $hash,
  419. method => "GET",
  420. header => $header,
  421. callback => \&HusqvarnaAutomower_getMowerStatusResponse,
  422. });
  423. return undef;
  424. }
  425. sub HusqvarnaAutomower_getMowerStatusResponse($) {
  426. my ($param, $err, $data) = @_;
  427. my $hash = $param->{hash};
  428. my $name = $hash->{NAME};
  429. if($err ne "") {
  430. Log3 $name, 5, "error while requesting ".$param->{url}." - $err";
  431. } elsif($data ne "") {
  432. Log3 $name, 5, $data;
  433. my $result = decode_json($data);
  434. $hash->{HusqvarnaAutomower}->{mower_battery} = $result->{'batteryPercent'};
  435. $hash->{HusqvarnaAutomower}->{mower_status} = HusqvarnaAutomower_ToGerman($hash, $result->{'mowerStatus'}->{'activity'});
  436. $hash->{HusqvarnaAutomower}->{mower_mode} = HusqvarnaAutomower_ToGerman($hash, $result->{'operatingMode'});
  437. $hash->{HusqvarnaAutomower}->{mower_nextStart} = HusqvarnaAutomower_Correct_Localtime( $result->{'nextStartTimestamp'} );
  438. $hash->{HusqvarnaAutomower}->{mower_nextStartSource} = HusqvarnaAutomower_ToGerman($hash, $result->{'nextStartSource'});
  439. $hash->{HusqvarnaAutomower}->{mower_restrictedReason} = HusqvarnaAutomower_ToGerman($hash, $result->{'mowerStatus'}->{'restrictedReason'});
  440. $hash->{HusqvarnaAutomower}->{mower_cuttingMode} = HusqvarnaAutomower_ToGerman($hash, $result->{'mowerStatus'}->{'mode'});
  441. $hash->{HusqvarnaAutomower}->{mower_lastLatitude} = $result->{'lastLocations'}->[0]->{'latitude'};
  442. $hash->{HusqvarnaAutomower}->{mower_lastLongitude} = $result->{'lastLocations'}->[0]->{'longitude'};
  443. readingsBeginUpdate($hash);
  444. readingsBulkUpdate($hash, "mower_battery", $hash->{HusqvarnaAutomower}->{mower_battery} );
  445. readingsBulkUpdate($hash, "mower_status", $hash->{HusqvarnaAutomower}->{mower_status} );
  446. readingsBulkUpdate($hash, "mower_mode", $hash->{HusqvarnaAutomower}->{mower_mode} );
  447. my $nextStartTimestamp = strftime("%Y-%m-%d %H:%M", localtime($hash->{HusqvarnaAutomower}->{mower_nextStart}));
  448. if ($nextStartTimestamp eq "1969-12-31 23:00") { $nextStartTimestamp = "-"; }
  449. if (
  450. strftime("%Y-%m-%d", localtime($hash->{HusqvarnaAutomower}->{mower_nextStart}) )
  451. eq
  452. strftime("%Y-%m-%d", localtime() )
  453. ) {
  454. $nextStartTimestamp = HusqvarnaAutomower_ToGerman($hash, "Today at") . " " . strftime("%H:%M", localtime($hash->{HusqvarnaAutomower}->{mower_nextStart}));
  455. } elsif (
  456. strftime("%Y-%m-%d", localtime($hash->{HusqvarnaAutomower}->{mower_nextStart}) )
  457. eq
  458. strftime("%Y-%m-%d", localtime(time + 86400) )
  459. ) {
  460. $nextStartTimestamp = HusqvarnaAutomower_ToGerman($hash, "Tomorrow at") . " " . strftime("%H:%M", localtime($hash->{HusqvarnaAutomower}->{mower_nextStart}));
  461. } elsif ($nextStartTimestamp ne "-") {
  462. my @c_time = split(" ", $nextStartTimestamp);
  463. my $c_date = join("." => reverse split('-', (split(' ',$nextStartTimestamp))[0]));
  464. $nextStartTimestamp = $c_date . " " . HusqvarnaAutomower_ToGerman($hash, "at") . " " . $c_time[1];
  465. }
  466. readingsBulkUpdate($hash, "mower_nextStart", $nextStartTimestamp );
  467. readingsBulkUpdate($hash, "mower_nextStartSource", $hash->{HusqvarnaAutomower}->{mower_nextStartSource} );
  468. readingsBulkUpdate($hash, "mower_restrictedReason", $hash->{HusqvarnaAutomower}->{mower_restrictedReason} );
  469. readingsBulkUpdate($hash, "mower_cuttingMode", $hash->{HusqvarnaAutomower}->{mower_cuttingMode} );
  470. readingsBulkUpdate($hash, "mower_lastLatitude", $hash->{HusqvarnaAutomower}->{mower_lastLatitude} );
  471. readingsBulkUpdate($hash, "mower_lastLongitude", $hash->{HusqvarnaAutomower}->{mower_lastLongitude} );
  472. readingsEndUpdate($hash, 1);
  473. }
  474. return undef;
  475. }
  476. ##############################################################
  477. #
  478. # SEND COMMAND
  479. #
  480. ##############################################################
  481. sub HusqvarnaAutomower_CMD($$) {
  482. my ($hash,$cmd) = @_;
  483. my $name = $hash->{NAME};
  484. # valid commands ['PARK', 'STOP', 'START']
  485. my $token = $hash->{HusqvarnaAutomower}->{token};
  486. my $provider = $hash->{HusqvarnaAutomower}->{provider};
  487. my $mower_id = $hash->{HusqvarnaAutomower}->{mower_id};
  488. my $header = "Content-Type: application/json\r\nAccept: application/json\r\nAuthorization: Bearer " . $token . "\r\nAuthorization-Provider: " . $provider;
  489. Log3 $name, 5, "cmd: " . $cmd;
  490. my $json = '{"action": "' . $cmd . '"}';
  491. HttpUtils_NonblockingGet({
  492. url => APIURL . "mowers/". $mower_id . "/control",
  493. timeout => 5,
  494. hash => $hash,
  495. method => "POST",
  496. header => $header,
  497. data => $json,
  498. callback => \&HusqvarnaAutomower_CMDResponse,
  499. });
  500. }
  501. sub HusqvarnaAutomower_CMDResponse($) {
  502. my ($param, $err, $data) = @_;
  503. my $hash = $param->{hash};
  504. my $name = $hash->{NAME};
  505. if($err ne "") {
  506. HusqvarnaAutomower_CONNECTED($hash,'error');
  507. Log3 $name, 5, "error while requesting ".$param->{url}." - $err";
  508. } elsif($data ne "") {
  509. my $result = decode_json($data);
  510. if ($result->{errors}) {
  511. HusqvarnaAutomower_CONNECTED($hash,'error');
  512. Log3 $name, 5, "Error: " . $result->{errors}[0]->{detail};
  513. } else {
  514. Log3 $name, 5, $data;
  515. }
  516. }
  517. }
  518. ###############################################################################
  519. sub HusqvarnaAutomower_Correct_Localtime($) {
  520. my ($time) = @_;
  521. my ($dst) = (localtime)[8]; # fetch daylight savings time flag
  522. if ($dst) {
  523. return $time - ( 2 * 3600 );
  524. } else {
  525. return $time - ( 1 * 3600 );
  526. }
  527. }
  528. sub HusqvarnaAutomower_ToGerman($$) {
  529. my ($hash,$readingValue) = @_;
  530. my $name = $hash->{NAME};
  531. my %langGermanMapping = (
  532. #'initialized' => 'initialisiert',
  533. #'authenticated' => 'authentifiziert',
  534. #'disabled' => 'deaktiviert',
  535. #'connected' => 'verbunden',
  536. 'Today at' => 'Heute um',
  537. 'Tomorrow at' => 'Morgen um',
  538. 'at' => 'um',
  539. 'NO_SOURCE' => 'keine Quelle',
  540. 'NOT_APPLICABLE' => 'nicht zutreffend',
  541. 'AUTO' => 'Automatisch',
  542. 'MAIN_AREA' => 'Hauptbereich',
  543. 'MOWING' => 'mäht',
  544. 'CHARGING' => 'lädt',
  545. 'LEAVING' => 'verlässt Ladestation',
  546. 'GOING_HOME' => 'auf dem Weg zur Ladestation',
  547. 'WEEK_TIMER' => 'Wochen-Zeitplan',
  548. 'WEEK_SCHEDULE' => 'Wochen-Zeitplan',
  549. 'PARKED_IN_CS' => 'In der Ladestation geparkt',
  550. 'COMPLETED_CUTTING_TODAY_AUTO' => 'Fertig für heute',
  551. 'PAUSED' => 'pausiert',
  552. 'SENSOR' => 'Sensor',
  553. 'OFF_DISABLED' => 'ausgeschaltet',
  554. 'OFF_HATCH_OPEN' => 'Abdeckung ist offen',
  555. 'OFF_HATCH_CLOSED' => 'Ausgeschaltet, manueller Start erforderlich',
  556. 'PARKED_TIMER' => 'geparkt nach Zeitplan',
  557. 'PARKED_PARK_SELECTED' => 'geparkt',
  558. 'MOWER_CHARGING' => 'Automower lädt',
  559. 'OK_SEARCHING' => 'sucht Ladestation',
  560. 'OK_LEAVING' => 'verlässt Ladestation',
  561. 'OK_CHARGING' => 'lädt',
  562. 'OK_CUTTING' => 'mäht',
  563. 'OK_CUTTING_TIMER_OVERRIDDEN' => 'manuelles Mähen',
  564. 'OK' => 'OK'
  565. );
  566. if( defined($langGermanMapping{$readingValue}) and HusqvarnaAutomower_isSetGerman($hash) ) {
  567. return $langGermanMapping{$readingValue};
  568. } else {
  569. return $readingValue;
  570. }
  571. }
  572. sub HusqvarnaAutomower_isSetGerman($) {
  573. my ($hash) = @_;
  574. my $name = $hash->{NAME};
  575. if ( AttrVal('global','language','EN') eq 'DE' or $hash->{HusqvarnaAutomower}->{language} eq 'DE') {
  576. return 1;
  577. } else {
  578. return 0;
  579. }
  580. }
  581. sub HusqvarnaAutomower_Whoami() { return (split('::',(caller(1))[3]))[1] || ''; }
  582. sub HusqvarnaAutomower_Whowasi() { return (split('::',(caller(2))[3]))[1] || ''; }
  583. ##############################################################
  584. 1;
  585. =pod
  586. =item device
  587. =item summary Modul to control Husqvarna Automower with Connect Module (SIM)
  588. =item summary_DE Modul zur Steuerung von Husqvarna Automower mit Connect Modul (SIM)
  589. =begin html
  590. <a name="HusqvarnaAutomower"></a>
  591. <h3>Husqvarna Automower with Connect Module (SIM)</h3>
  592. <ul>
  593. <u><b>Requirements</b></u>
  594. <br><br>
  595. <ul>
  596. <li>This module allows the communication between the Husqvarna Cloud and FHEM.</li>
  597. <li>You can control any Automower that is equipped with the original Husqvarna Connect Module (SIM).</li>
  598. <li>The Automower must be registered in the Husqvarna App beforehand.</li>
  599. </ul>
  600. <br>
  601. <a name="HusqvarnaAutomowerdefine"></a>
  602. <b>Define</b>
  603. <ul>
  604. <code>define &lt;name&gt; HusqvarnaAutomower</code>
  605. <br><br>
  606. Beispiel:
  607. <ul><br>
  608. <code>define myMower HusqvarnaAutomower<br>
  609. attr myMower username YOUR_USERNAME<br>
  610. attr myMower password YOUR_PASSWORD
  611. </code><br>
  612. </ul>
  613. <br><br>
  614. You must set both attributes <b>username</b> and <b>password</b>. These are the same that you use to login via the Husqvarna App.
  615. </ul>
  616. <br>
  617. <a name="HusqvarnaAutomowerattributes"></a>
  618. <b>Attributes</b>
  619. <ul>
  620. <li>username - Email that is used in Husqvarna App</li>
  621. <li>password - Password that is used in Husqvarna App</li>
  622. </ul>
  623. <br>
  624. <b>Optional attributes</b>
  625. <ul>
  626. <li>mower - ID of Automower, if more that one is registered. Default: 0</li>
  627. <li>interval - Time in seconds that is used to get new data from Husqvarna Cloud. Default: 300</li>
  628. <li>language - language setting, EN = original messages, DE = german translation. Default: DE</li>
  629. </ul>
  630. <br>
  631. <a name="HusqvarnaAutomowerreadings"></a>
  632. <b>Readings</b>
  633. <ul>
  634. <li>expires - date when session of Husqvarna Cloud expires</li>
  635. <li>mower_id - ID of the mower</li>
  636. <li>mower_lastLatitude - last known position (latitude)</li>
  637. <li>mower_lastLongitude - last known position (longitude)</li>
  638. <li>mower_mode - current working mode (e. g. AUTO)</li>
  639. <li>mower_name - name of the mower</li>
  640. <li>mower_nextStart - next start time</li>
  641. <li>mower_status - current status (e. g. OFF_HATCH_CLOSED_DISABLED, PARKED_IN_CS)</li>
  642. <li>mower_cuttingMode - mode of cutting area (e. g. MAIN_AREA)</li>
  643. <li>mower_nextStartSource - detailed status (e. g. COMPLETED_CUTTING_TODAY_AUTO)</li>
  644. <li>mower_restrictedReason - reason for parking (e. g. SENSOR)</li>
  645. <li>provider - should be Husqvarna</li>
  646. <li>state - status of connection to Husqvarna Cloud (e. g. connected)</li>
  647. <li>token - current session token of Husqvarna Cloud</li>
  648. <li>user_id - your user ID in Husqvarna Cloud</li>
  649. </ul>
  650. </ul>
  651. =end html
  652. =begin html_DE
  653. <a name="HusqvarnaAutomower"></a>
  654. <h3>Husqvarna Automower mit Connect Modul</h3>
  655. <ul>
  656. <u><b>Voraussetzungen</b></u>
  657. <br><br>
  658. <ul>
  659. <li>Dieses Modul ermöglicht die Kommunikation zwischen der Husqvarna Cloud und FHEM.</li>
  660. <li>Es kann damit jeder Automower, der über ein original Husqvarna Connect Modul (SIM) verfügt, überwacht und gesteuert werden.</li>
  661. <li>Der Automower muss vorab in der Husqvarna App eingerichtet sein.</li>
  662. </ul>
  663. <br>
  664. <a name="HusqvarnaAutomowerdefine"></a>
  665. <b>Define</b>
  666. <ul>
  667. <br>
  668. <code>define &lt;name&gt; HusqvarnaAutomower</code>
  669. <br><br>
  670. Beispiel:
  671. <ul><br>
  672. <code>define myMower HusqvarnaAutomower<br>
  673. attr myMower username YOUR_USERNAME<br>
  674. attr myMower password YOUR_PASSWORD
  675. </code><br>
  676. </ul>
  677. <br><br>
  678. Es müssen die beiden Attribute <b>username</b> und <b>password</b> gesetzt werden. Diese sind identisch mit den Logindaten der Husqvarna App.
  679. </ul>
  680. <br>
  681. <a name="HusqvarnaAutomowerattributes"></a>
  682. <b>Attributes</b>
  683. <ul>
  684. <li>username - Email, die in der Husqvarna App verwendet wird</li>
  685. <li>password - Passwort, das in der Husqvarna App verwendet wird</li>
  686. </ul>
  687. <br>
  688. <b>Optionale Attribute</b>
  689. <ul>
  690. <li>mower - ID des Automowers, sofern mehrere registriert sind. Standard: 0</li>
  691. <li>interval - Zeit in Sekunden nach denen neue Daten aus der Husqvarna Cloud abgerufen werden. Standard: 300</li>
  692. <li>language - Spracheinstellungen, EN = original Meldungen, DE = deutsche Übersetzung. Standard: DE</li>
  693. </ul>
  694. <br>
  695. <a name="HusqvarnaAutomowerreadings"></a>
  696. <b>Readings</b>
  697. <ul>
  698. <li>expires - Datum wann die Session der Husqvarna Cloud abläuft</li>
  699. <li>mower_id - ID des Automowers</li>
  700. <li>mower_lastLatitude - letzte bekannte Position (Breitengrad)</li>
  701. <li>mower_lastLongitude - letzte bekannte Position (Längengrad)</li>
  702. <li>mower_mode - aktueller Arbeitsmodus (e. g. AUTO)</li>
  703. <li>mower_name - Name des Automowers</li>
  704. <li>mower_nextStart - nächste Startzeit</li>
  705. <li>mower_status - aktueller Status (e. g. OFF_HATCH_CLOSED_DISABLED, PARKED_IN_CS)</li>
  706. <li>mower_cuttingMode - Angabe welcher Bereich gemäht wird (e. g. MAIN_AREA)</li>
  707. <li>mower_nextStartSource - detaillierter Status (e. g. COMPLETED_CUTTING_TODAY_AUTO)</li>
  708. <li>mower_restrictedReason - Grund für Parken (e. g. SENSOR)</li>
  709. <li>provider - Sollte immer Husqvarna sein</li>
  710. <li>state - Status der Verbindung zur Husqvarna Cloud (e. g. connected)</li>
  711. <li>token - aktueller Sitzungstoken für die Husqvarna Cloud</li>
  712. <li>user_id - Nutzer-ID in der Husqvarna Cloud</li>
  713. </ul>
  714. </ul>
  715. =end html_DE