31_Nello.pm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. ##############################################################################
  2. # $Id: 31_Nello.pm 15460 2017-11-19 19:05:42Z neumann $
  3. #
  4. # 31_Nello.pm
  5. #
  6. # 2017 Oskar Neumann
  7. # oskar.neumann@me.com
  8. #
  9. ##############################################################################
  10. # required packets
  11. # Net::MQTT
  12. # libcpan-meta-yaml-perl
  13. package main;
  14. use strict;
  15. use warnings;
  16. use JSON;
  17. use Date::Parse;
  18. sub Nello_Initialize($) {
  19. my ($hash) = @_;
  20. $hash->{DefFn} = 'Nello_Define';
  21. $hash->{NotifyFn} = 'Nello_Notify';
  22. $hash->{UndefFn} = 'Nello_Undefine';
  23. $hash->{SetFn} = 'Nello_Set';
  24. $hash->{GetFn} = 'Nello_Get';
  25. $hash->{AttrFn} = "Nello_Attr";
  26. $hash->{AttrList} = 'updateInterval disable:0,1 deviceID ';
  27. $hash->{AttrList} .= $readingFnAttributes;
  28. $hash->{NOTIFYDEV} = "global";
  29. }
  30. sub Nello_Define($) {
  31. my ($hash, $def) = @_;
  32. my $name = $hash->{NAME};
  33. my @a = split("[ \t][ \t]*", $def);
  34. Nello_loadInternals($hash) if($init_done);
  35. return undef;
  36. }
  37. sub Nello_Undefine($$) {
  38. my ($hash, $name) = @_;
  39. RemoveInternalTimer($hash);
  40. return undef;
  41. }
  42. sub Nello_Notify($$) {
  43. my ($own_hash, $dev_hash) = @_;
  44. my $ownName = $own_hash->{NAME}; # own name / hash
  45. return "" if(IsDisabled($ownName)); # Return without any further action if the module is disabled
  46. my $devName = $dev_hash->{NAME}; # Device that created the events
  47. my $events = deviceEvents($dev_hash, 1);
  48. if($devName eq "global" && grep(m/^INITIALIZED|REREADCFG$/, @{$events})) {
  49. Nello_loadInternals($own_hash);
  50. }
  51. my $deviceID = $attr{$ownName}{deviceID};
  52. if (defined $deviceID && $devName eq 'Nello_Events') {
  53. my $events = deviceEvents( $dev_hash, 1 );
  54. return "" unless ($events);
  55. foreach ( @{$events} ) {
  56. if(my ($action) = $_ =~ m/${deviceID}_(door|ring|tw):.*/) {
  57. if($action eq 'ring') {
  58. $own_hash->{helper}{last_ring} = time();
  59. Log3 "Nello", 3, $ownName . " ring";
  60. readingsSingleUpdate($own_hash, 'last_ring', time(), 1);
  61. }
  62. if($action eq 'door' && (!defined $own_hash->{helper}{last_opened} || time() - $own_hash->{helper}{last_opened} > 3)) {
  63. Log3 "Nello", 3, $ownName. " opened";
  64. readingsSingleUpdate($own_hash, 'last_user_open', time(), 1);
  65. readingsSingleUpdate($own_hash, 'last_open', time(), 1);
  66. }
  67. InternalTimer(gettimeofday()+1, "Nello_updateActivities", $own_hash);
  68. InternalTimer(gettimeofday()+2, "Nello_updateActivities", $own_hash);
  69. }
  70. }
  71. }
  72. }
  73. sub Nello_Set($$@) {
  74. my ($hash, $name, $cmd, @args) = @_;
  75. return "\"set $name\" needs at least one argument" unless(defined($cmd));
  76. my $list = '';
  77. if(!defined $hash->{helper}{session}) {
  78. $list .= ' login recoverPassword';
  79. } else {
  80. $list .= ' open:noArg update:noArg detectDeviceID:noArg';
  81. }
  82. return Nello_login($hash, $args[0], $args[1]) if($cmd eq 'login');
  83. return Nello_detectDeviceID($hash) if($cmd eq 'detectDeviceID');
  84. return Nello_recoverPassword($hash, $args[0], $args[1], $args[2]) if($cmd eq 'recoverPassword');
  85. if($cmd eq 'update') {
  86. Nello_updateLocations($hash);
  87. return Nello_updateActivities($hash);
  88. }
  89. return Nello_open($hash, $args[0]) if($cmd eq 'open');
  90. return "Unknown argument $cmd, choose one of $list";
  91. }
  92. sub Nello_Get($$@) {
  93. my ($hash, $name, $cmd, @args) = @_;
  94. my $list = "";
  95. return "Unknown argument $cmd, choose one of $list";
  96. }
  97. sub Nello_Attr(@) {
  98. my ($cmd, $name, $attrName, $attrValue) = @_;
  99. my $hash = $main::defs{$name};
  100. if($attrName eq 'updateInterval') {
  101. RemoveInternalTimer($hash);
  102. Nello_poll($hash);
  103. }
  104. if($attrName eq 'deviceID' && $init_done) {
  105. my $bridge = 'Nello_MQTT';
  106. if(!defined InternalVal($bridge, "TYPE", undef)) {
  107. CommandDefine(undef, $bridge . ' MQTT 18.194.251.238:1883');
  108. CommandAttr(undef, $bridge . ' room hidden');
  109. CommandSave(undef, undef);
  110. }
  111. my $eventdevice = 'Nello_Events';
  112. if(!defined InternalVal($eventdevice, 'TYPE', undef)) {
  113. CommandDefine(undef, $eventdevice . ' MQTT_DEVICE');
  114. CommandAttr(undef, $eventdevice . ' room hidden');
  115. CommandAttr(undef, $eventdevice . ' IODEV '. $bridge);
  116. }
  117. my $prefix = 'subscribeReading_'. $attrValue .'_';
  118. CommandAttr(undef, $eventdevice . ' '. $prefix . 'door /nello_one/'. $attrValue . '/door/') if(!defined $attr{$eventdevice}{$prefix . 'door'});
  119. CommandAttr(undef, $eventdevice . ' '. $prefix . 'ring /nello_one/'. $attrValue . '/ring/') if(!defined $attr{$eventdevice}{$prefix . 'ring'});;
  120. CommandAttr(undef, $eventdevice . ' '. $prefix . 'tw /nello_one/'. $attrValue . '/tw/') if(!defined $attr{$eventdevice}{$prefix . 'tw'});
  121. if(defined $hash->{helper}{deviceID} && $hash->{helper}{deviceID} ne $attrValue) {
  122. $prefix = 'subscribeReading_'. $hash->{helper}{deviceID} .'_';
  123. CommandDeleteAttr(undef, $eventdevice . ' '. $prefix . 'door');
  124. CommandDeleteAttr(undef, $eventdevice . ' '. $prefix . 'ring');
  125. CommandDeleteAttr(undef, $eventdevice . ' '. $prefix . 'tw');
  126. }
  127. CommandSave(undef, undef);
  128. $hash->{helper}{deviceID} = $attrValue;
  129. }
  130. return undef;
  131. }
  132. sub Nello_loadInternals($) {
  133. my ($hash) = @_;
  134. my $name = $hash->{NAME};
  135. $hash->{helper}{expires} = ReadingsVal($name, '.expires', undef);
  136. $hash->{helper}{session} = ReadingsVal($name, '.session', undef);
  137. $hash->{helper}{deviceID} = $attr{$name}{"deviceID"};
  138. if(!defined(ReadingsVal($name, '.session', undef))) {
  139. $hash->{STATE} = 'authorization pending';
  140. } else {
  141. $hash->{STATE} = 'connected';
  142. $attr{$name}{webCmd} = 'open' if(!defined $attr{$name}{webCmd});
  143. Nello_updateLocations($hash, 0);
  144. Nello_poll($hash);
  145. }
  146. InternalTimer(gettimeofday()+10, "Nello_updateMQTTIP", $hash);
  147. }
  148. sub Nello_login {
  149. my ($hash, $username, $password) = @_;
  150. my $name = $hash->{NAME};
  151. return 'wrong syntax: set <name> login <username> <password>' if(!defined $username || !defined $password);
  152. Nello_authenticate($hash, $username, $password);
  153. return undef;
  154. }
  155. sub Nello_authenticate {
  156. my ($hash, $username, $authhash) = @_;
  157. my $name = $hash->{NAME};
  158. $username = ReadingsVal($name, "username", undef) if(!defined $username);
  159. $authhash = ReadingsVal($name, ".authtoken", undef) if(!defined $authhash);
  160. Nello_apiRequest($hash, 'login', {username => $username, password => $authhash}, 'POST', 0);
  161. return undef;
  162. }
  163. sub Nello_updateLocations {
  164. my ($hash, $blocking) = @_;
  165. Nello_apiRequest($hash, 'locations/', undef, 'GET', $blocking);
  166. return undef;
  167. }
  168. sub Nello_updateActivities {
  169. my ($hash) = @_;
  170. Nello_apiRequest($hash, 'locations/'. Nello_defaultLocationID($hash) . '/activity', undef, 'GET', 0);
  171. return undef;
  172. }
  173. sub Nello_apiRequest {
  174. my ($hash, $path, $args, $method, $blocking) = @_;
  175. if(!defined $blocking || !$blocking) {
  176. HttpUtils_NonblockingGet({
  177. url => "https://api.nello.io/$path",
  178. method => $method,
  179. hash => $hash,
  180. apiPath => $path,
  181. timeout => 15,
  182. noshutdown => 1,
  183. data => $method eq 'POST' && defined $args ? encode_json $args : $args,
  184. header => "Cookie: session=". $hash->{helper}{session},
  185. callback => \&Nello_dispatch
  186. });
  187. } else {
  188. my ($err,$data) = HttpUtils_BlockingGet({
  189. url => "https://api.nello.io/$path",
  190. method => $method,
  191. hash => $hash,
  192. apiPath => $path,
  193. timeout => 15,
  194. noshutdown => 1,
  195. data => $method eq 'POST' && defined $args ? encode_json $args : $args,
  196. header => "Cookie: session=". $hash->{helper}{session}
  197. });
  198. return Nello_dispatch({hash => $hash, apiPath => $path, method => $method, data => $args}, $err, $data);
  199. }
  200. }
  201. sub Nello_dispatch($$$) {
  202. my ($param, $err, $data) = @_;
  203. my $hash = $param->{hash};
  204. my $name = $hash->{NAME};
  205. my ($path) = split('\?', $param->{apiPath}, 2);
  206. my ($pathpt0, $pathpt1, $pathpt2, $pathpt3, $pathpt4) = split('/', $path, 5);
  207. my $method = $param->{method};
  208. my $header = $param->{httpheader};
  209. my $args = $param->{data};
  210. delete $hash->{helper}{dispatch};
  211. if(!defined($param->{hash})){
  212. Log3 "Nello", 2, 'Nello: dispatch fail (hash missing)';
  213. return undef;
  214. }
  215. $args = eval { JSON->new->utf8(0)->decode($args) } if(defined $args);
  216. my $json = eval { JSON->new->utf8(0)->decode($data) };
  217. $hash->{helper}{dispatch}{json} = $json;
  218. my $status = $json->{result}{status};
  219. my $successful = $status && ($status eq "200" || lc $status eq "ok");
  220. #Log3 "Nello", 3, $header;
  221. #Log3 $name, 3, $name . ' : ' . $hash . $data;
  222. if($path eq 'login') {
  223. if(defined $json->{authentication} && $json->{authentication} && defined $header) {
  224. Log3 "Nello", 3, "$name: login successful";
  225. my ($session, $expires) = $header =~ m/:[^:]*session=([^;]*); Expires=([^;]*);/;
  226. readingsBeginUpdate($hash);
  227. readingsBulkUpdateIfChanged($hash, 'username', $args->{username});
  228. readingsBulkUpdateIfChanged($hash, '.authtoken', $args->{password});
  229. readingsBulkUpdateIfChanged($hash, '.session', $session);
  230. readingsBulkUpdate($hash, '.expires', $expires);
  231. readingsBulkUpdateIfChanged($hash, 'user_id', $json->{user}{user_id});
  232. Nello_saveLocations($hash, $json->{user}{roles}, 0);
  233. readingsEndUpdate($hash, 1);
  234. Nello_poll($hash) if($hash->{STATE} ne 'connected');
  235. $hash->{STATE} = 'connected';
  236. $hash->{helper}{session} = $session;
  237. my $failReq = $hash->{helper}{authfail};
  238. Nello_apiRequest($hash, $failReq->{path}, $failReq->{data}, $failReq->{method}, 0) if(defined $failReq); # repeat failed request
  239. } else {
  240. Log3 "Nello", 3, "$name: login failed";
  241. CommandDeleteReading(undef, "$name .*");
  242. $hash->{STATE} = 'authentication pending';
  243. delete $hash->{helper}{session};
  244. }
  245. delete $hash->{helper}{authfail};
  246. }
  247. if(defined $json->{result} && defined $json->{result}{status} && $json->{result}{status} eq "400") {
  248. Nello_authenticate($hash);
  249. $hash->{helper}{authfail} = {data => $args, path => $path, method => $method};
  250. }
  251. if($path eq 'locations/') {
  252. Nello_saveLocations($hash, $json->{user}{roles}, 1);
  253. Nello_open($hash) if(defined $hash->{helper}{retryopen});
  254. }
  255. if(defined $pathpt4 && $pathpt4 eq 'open') {
  256. Log3 $name, 3, $name . ': ' . ($successful ? 'opened' : 'open failed');
  257. if(!defined $attr{$name}{deviceID}) {
  258. Nello_updateActivities($hash);
  259. InternalTimer(gettimeofday()+2, "Nello_updateActivities", $hash);
  260. }
  261. }
  262. if(defined $pathpt2 && $pathpt2 eq 'activity') {
  263. my $last = ReadingsVal($name, '.last_activity', undef);
  264. if(defined $json->{activities} && @{$json->{activities}} > 0) {
  265. if(defined $last) {
  266. foreach my $activity (reverse @{$json->{activities}}) {
  267. my $time = str2time($activity->{date});
  268. $time = round($time, 0) if(defined $time);
  269. if($time > $last) {
  270. my $didring = ($activity->{type} eq 'door.open.one.tw' || $activity->{type} =~ m/bell.ring/) && (!defined $hash->{helper}{last_ring} || time() - $hash->{helper}{last_ring} > 5);
  271. delete $hash->{helper}{last_ring};
  272. readingsBeginUpdate($hash);
  273. readingsBulkUpdate($hash, 'activity', $activity->{type});
  274. readingsBulkUpdate($hash, 'activity_text', $activity->{description});
  275. readingsBulkUpdate($hash, 'activity_time', $time);
  276. readingsBulkUpdate($hash, 'last_user_open', $time) if($activity->{type} eq 'door.open.one.user');
  277. readingsBulkUpdate($hash, 'last_timewindow_open', $time) if($activity->{type} eq 'door.open.one.tw');
  278. readingsBulkUpdate($hash, 'last_open', $time) if($activity->{type} =~ m/open/);
  279. readingsBulkUpdate($hash, 'last_ring', $time) if($didring);
  280. readingsBulkUpdate($hash, 'last_ring_denied', $time) if($activity->{type} eq 'bell.ring.denied');
  281. readingsEndUpdate($hash, 1);
  282. Log3 $name, 3, $name. ' ring' if($didring);
  283. Log3 $name, 3, $name. ': '. $activity->{description};
  284. }
  285. }
  286. }
  287. my $next = $json->{activities}[0];
  288. $last = round(str2time($next->{date}), 0);
  289. } else {
  290. $last = time();
  291. }
  292. readingsSingleUpdate($hash, '.last_activity', $last, 1);
  293. }
  294. if($path eq 'detectMQTT') {
  295. if($successful) {
  296. CommandAttr(undef, $name . " deviceID ". $json->{id});
  297. CommandSave(undef, undef);
  298. Log3 $name, 3, $name . ": successfully detected device ID";
  299. } else {
  300. delete $hash->{helper}{detectMQTT};
  301. if($status eq 'busy') {
  302. Log3 $name, 3, $name . ": busy detecting device ID, trying again.";
  303. InternalTimer(gettimeofday()+5, "Nello_detectDeviceID", $hash);
  304. } else {
  305. Log3 $name, 3, $name . ": failed to detect deviceID";
  306. }
  307. }
  308. }
  309. if($path eq 'recover-password') {
  310. Log3 $name, 3, $name .": " . $json->{result}{message};
  311. }
  312. return undef;
  313. }
  314. sub Nello_saveLocations($$$) {
  315. my ($hash, $locations, $beginUpdate) = @_;
  316. my $name = $hash->{NAME};
  317. CommandDeleteReading(undef, "$name location_.*");
  318. readingsBeginUpdate($hash) if($beginUpdate);
  319. my $index = 0;
  320. foreach my $location (@{$locations}) {
  321. my $prefix = "location_". ($index+1);
  322. readingsBulkUpdate($hash, $prefix. "_id", $location->{location_id}, 1);
  323. readingsBulkUpdate($hash, $prefix. "_ssid", $location->{home_ssid}, 1);
  324. readingsBulkUpdate($hash, $prefix. "_role", $location->{role}, 1);
  325. readingsBulkUpdate($hash, $prefix. "_active", $location->{is_active} ? 1 : 0, 1);
  326. $index++;
  327. }
  328. readingsBulkUpdate($hash, "locations", $index, 1);
  329. readingsEndUpdate($hash, 1) if($beginUpdate);
  330. }
  331. sub Nello_open {
  332. my ($hash, $location_id) = @_;
  333. my $name = $hash->{NAME};
  334. $location_id = ReadingsVal($name, 'location_'. $location_id . '_id', undef) if(defined $location_id);
  335. $location_id = Nello_defaultLocationID($hash) if(!defined $location_id);
  336. if(!defined $location_id && !defined $hash->{helper}{retryopen}) {
  337. $hash->{helper}{retryopen} = 1;
  338. return Nello_updateLocations($hash);
  339. }
  340. delete $hash->{helper}{retryopen} if(defined $hash->{helper}{retryopen});
  341. return 'no location available' if(!defined $location_id);
  342. my $user_id = ReadingsVal($name, 'user_id', undef);
  343. Nello_apiRequest($hash, "locations/$location_id/users/$user_id/open", {type => "swipe"}, 'POST', 0);
  344. $hash->{helper}{last_opened} = time();
  345. return undef;
  346. }
  347. sub Nello_recoverPassword {
  348. my ($hash, $username) = @_;
  349. return 'wrong syntax: set <name> recoverPassword <username>' if(!defined $username);
  350. return Nello_apiRequest($hash, 'recover-password', {username => $username}, 'POST', 0);
  351. }
  352. sub Nello_poll {
  353. my ($hash) = @_;
  354. my $name = $hash->{NAME};
  355. return if(Nello_isDisabled($hash));
  356. my $pollInterval = $attr{$name}{updateInterval};
  357. InternalTimer(gettimeofday()+(defined $pollInterval ? $pollInterval : (!defined $attr{$name}{deviceID} ? 15 : 15*60)), "Nello_poll", $hash);
  358. Nello_updateLocations($hash) if(!defined Nello_defaultLocationID($hash));
  359. Nello_updateActivities($hash);
  360. }
  361. sub Nello_isDisabled($) {
  362. my ($hash) = @_;
  363. my $name = $hash->{NAME};
  364. return defined $attr{$name}{disable};
  365. }
  366. sub Nello_defaultLocationID {
  367. my ($hash) = @_;
  368. my $name = $hash->{NAME};
  369. return ReadingsVal($name, 'location_1_id', undef);
  370. }
  371. sub Nello_detectDeviceID {
  372. my ($hash) = @_;
  373. HttpUtils_NonblockingGet({
  374. url => "http://nello.oskar.pw/detectMQTT.php",
  375. method => 'GET',
  376. hash => $hash,
  377. apiPath => 'detectMQTT',
  378. timeout => 15,
  379. noshutdown => 1,
  380. callback => \&Nello_dispatch
  381. });
  382. $hash->{helper}{detectMQTT} = 1;
  383. InternalTimer(gettimeofday()+2, "Nello_detectExecute", $hash);
  384. return undef;
  385. }
  386. sub Nello_detectExecute {
  387. my ($hash) = @_;
  388. my $name = $hash->{NAME};
  389. return if(!defined $hash->{helper}{detectMQTT});
  390. Log3 $name, 3, $name . ": opening door to detect device ID now.";
  391. Nello_open($hash);
  392. Nello_open($hash);
  393. }
  394. sub Nello_updateMQTTIP {
  395. my $mqtt_ip = trim(InternalVal("Nello_MQTT", "DEF", undef));
  396. if(defined $mqtt_ip && $mqtt_ip ne "18.194.251.238:1883") {
  397. fhem("defmod Nello_MQTT MQTT 18.194.251.238:1883");
  398. CommandSave(undef, undef);
  399. }
  400. }
  401. 1;
  402. =pod
  403. =item device
  404. =item summary control your intercom with nello one
  405. =item summary_DE Steuerung der Gegensprechanlage mit nello one
  406. =begin html
  407. <a name="Nello"></a>
  408. <h3>Nello</h3>
  409. <ul>
  410. The <i>Nello</i> module enables you to control your intercom using the <a target="_blank" rel="nofollow" href="https://www.nello.io/en/">nello one</a> module.<br>
  411. To set it up, you need to <b>add a new user with admin rights</b> via the nello app just for use with fhem. You cannot use your main account since only one session at a time is possible.<br>
  412. After that, you can define the device and continue with login.<br>
  413. <b>ATTENTION:</b> If the login fails, try resetting your password using the recoverPassword function.<br>
  414. <b>Recommendation:</b> To receive instant events, call the detectDeviceID function after login.<br>
  415. <br>
  416. <p><b>Required Packages</b></p>
  417. <code>
  418. sudo apt-get install libcpan-meta-yaml-perl<br>
  419. sudo cpan -i Net::MQTT::Simple
  420. </code>
  421. <br>
  422. <br>
  423. <br>
  424. <a name="Nello_define"></a>
  425. <p><b>Define</b></p>
  426. <ul>
  427. <code>define &lt;name&gt; Nello</code><br>
  428. </ul>
  429. <br>
  430. <ul>
  431. Example: <code>define nello Nello</code><br>
  432. </ul>
  433. <br>
  434. <br>
  435. <a name="Nello_set"></a>
  436. <p><b>set &lt;required&gt; [ &lt;optional&gt; ]</b></p>
  437. <ul>
  438. <li>
  439. <i>login &lt;username&gt; &lt;password&gt;</i><br>
  440. login to your created account
  441. </li>
  442. <li>
  443. <i>recoverPassword &lt;username&gt;</i><br>
  444. recovers the password
  445. </li>
  446. <li>
  447. <i>detectDeviceID</i><br>
  448. detects your device ID by opening the door and creates MQTT helper (used for event hooks)
  449. </li>
  450. <li>
  451. <i>open [ &lt;location_id&gt; ]</i><br>
  452. opens the door for a given location (if the account has only access to one location the default one will be used automatically).
  453. </li>
  454. <li>
  455. <i>update</i><br>
  456. updates your locations and activities
  457. </li>
  458. </ul>
  459. <br>
  460. <a name="Nello_get"></a>
  461. <p><b>Get</b></p>
  462. <ul>
  463. N/A
  464. </ul>
  465. <br>
  466. <a name="Nello_attr"></a>
  467. <p><b>Attributes</b></p>
  468. <ul>
  469. <li>
  470. <i>updateInterval</i><br>
  471. the interval to fetch new activites in seconds<br>
  472. default: 900 (if deviceID is available), 15 otherwise
  473. </li>
  474. </ul>
  475. </ul>
  476. =end html
  477. =begin html_DE
  478. <a name="Nello"></a>
  479. <h3>Nello</h3>
  480. <ul>
  481. Das <i>Nello</i> Modul ermöglicht die Steuerung des <a target="_blank" rel="nofollow" href="https://www.nello.io/de/">nello one</a> Chips.<br>
  482. Um es aufzusetzen, muss zunächst ein <b>neuer Nutzer mit Admin-Rechten</b> in der Nello-App angelegt werden, der nur für FHEM verwendet wird - eine Nutzung per App ist mit diesem Account dann nicht mehr möglich.<br>
  483. Anschließend kann das Gerät angelegt werden. Sobald das Gerät erstellt wurde, kann der Login durchgeführt werden.<br>
  484. <b>ACHTUNG:</b> Sollte der Login fehlschlagen, versuche das Passwort über die recoverPassword Funktion zurückzusetzen.<br>
  485. <b>Dringend empfohlen:</b> Für verzögerungsfreie Events die detectDeviceID Funktion nach dem Login aufrufen.<br>
  486. <br>
  487. <p><b>Benötigte Pakete</b></p>
  488. <code>
  489. sudo apt-get install libcpan-meta-yaml-perl<br>
  490. sudo cpan -i Net::MQTT::Simple
  491. </code>
  492. <br>
  493. <br>
  494. <br>
  495. <a name="Nello_define"></a>
  496. <p><b>Define</b></p>
  497. <ul>
  498. <code>define &lt;name&gt; Nello</code><br>
  499. </ul>
  500. <br>
  501. <ul>
  502. Beispiel: <code>define nello Nello</code><br>
  503. </ul>
  504. <br>
  505. <a name="Nello_set"></a>
  506. <p><b>set &lt;required&gt; [ &lt;optional&gt; ]</b></p>
  507. <ul>
  508. <li>
  509. <i>login &lt;username&gt; &lt;password&gt;</i><br>
  510. Login
  511. </li>
  512. <li>
  513. <i>recoverPassword &lt;username&gt;</i><br>
  514. setzt das Passwort zurück
  515. </li>
  516. <li>
  517. <i>detectDeviceID</i><br>
  518. erkennt die Geräte-ID des Nellos durch einmaliges Öffnen der Tür und erstellt MQTT-Helper-Geräte für verzögerungsfreie Ereignisse
  519. </li>
  520. <li>
  521. <i>open [ &lt;location_id&gt; ]</i><br>
  522. öffnet die Tür
  523. </li>
  524. <li>
  525. <i>update</i><br>
  526. aktualisiert Aktionen und Ereignisse
  527. </li>
  528. </ul>
  529. <br>
  530. <a name="Nello_get"></a>
  531. <p><b>Get</b></p>
  532. <ul>
  533. N/A
  534. </ul>
  535. <br>
  536. <a name="Nello_attr"></a>
  537. <p><b>Attribute</b></p>
  538. <ul>
  539. <li>
  540. <i>updateInterval</i><br>
  541. das Intervall in Sekunden, in dem Ereignisse gepollt werden (nur relevant, wenn deviceID nicht erkannt wurde)<br>
  542. default: 900 (wenn Geräte-ID erkannt wurde), ansonten 15
  543. </li>
  544. </ul>
  545. </ul>
  546. =end html_DE
  547. =cut