00_MYSENSORS.pm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. ##############################################
  2. #
  3. # fhem driver for MySensors serial or network gateway (see http://mysensors.org)
  4. #
  5. # Copyright (C) 2014 Norbert Truchsess
  6. #
  7. # This file is part of fhem.
  8. #
  9. # Fhem is free software: you can redistribute it and/or modify
  10. # it under the terms of the GNU General Public License as published by
  11. # the Free Software Foundation, either version 2 of the License, or
  12. # (at your option) any later version.
  13. #
  14. # Fhem is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License
  20. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  21. #
  22. # $Id: 00_MYSENSORS.pm 17290 2018-09-06 08:29:45Z Hauswart $
  23. #
  24. ##############################################
  25. my %sets = (
  26. "connect" => [],
  27. "disconnect" => [],
  28. "inclusion-mode" => [qw(on off)],
  29. );
  30. my %gets = (
  31. "version" => ""
  32. );
  33. my @clients = qw(
  34. MYSENSORS_DEVICE
  35. );
  36. sub MYSENSORS_Initialize($) {
  37. my $hash = shift @_;
  38. require "$main::attr{global}{modpath}/FHEM/DevIo.pm";
  39. # Provider
  40. $hash->{Clients} = join (':',@clients);
  41. $hash->{ReadyFn} = "MYSENSORS::Ready";
  42. $hash->{ReadFn} = "MYSENSORS::Read";
  43. # Consumer
  44. $hash->{DefFn} = "MYSENSORS::Define";
  45. $hash->{UndefFn} = "MYSENSORS::Undef";
  46. $hash->{SetFn} = "MYSENSORS::Set";
  47. $hash->{AttrFn} = "MYSENSORS::Attr";
  48. $hash->{NotifyFn} = "MYSENSORS::Notify";
  49. $hash->{AttrList} =
  50. "autocreate:1 ".
  51. "requestAck:1 ".
  52. "first-sensorid ".
  53. "last-sensorid ".
  54. "stateFormat ".
  55. "OTA_firmwareConfig";
  56. }
  57. package MYSENSORS;
  58. use Exporter ('import');
  59. @EXPORT = ();
  60. @EXPORT_OK = qw(
  61. sendMessage
  62. getFirmwareTypes
  63. getLatestFirmware
  64. );
  65. %EXPORT_TAGS = (all => [@EXPORT_OK]);
  66. use strict;
  67. use warnings;
  68. use GPUtils qw(:all);
  69. use Device::MySensors::Constants qw(:all);
  70. use Device::MySensors::Message qw(:all);
  71. BEGIN {GP_Import(qw(
  72. CommandDefine
  73. CommandModify
  74. CommandAttr
  75. gettimeofday
  76. readingsSingleUpdate
  77. DevIo_OpenDev
  78. DevIo_SimpleWrite
  79. DevIo_SimpleRead
  80. DevIo_CloseDev
  81. RemoveInternalTimer
  82. InternalTimer
  83. AttrVal
  84. Log3
  85. FileRead
  86. ))};
  87. my %sensorAttr = (
  88. LIGHT => ['setCommands on:V_LIGHT:1 off:V_LIGHT:0' ],
  89. ARDUINO_NODE => [ 'config M' ],
  90. ARDUINO_REPEATER_NODE => [ 'config M' ],
  91. );
  92. sub Define($$) {
  93. my ( $hash, $def ) = @_;
  94. $hash->{NOTIFYDEV} = "global";
  95. if ($main::init_done) {
  96. return Start($hash);
  97. } else {
  98. return undef;
  99. }
  100. }
  101. sub Undef($) {
  102. Stop(shift);
  103. return undef;
  104. }
  105. sub Set($@) {
  106. my ($hash, @a) = @_;
  107. return "Need at least one parameters" if(@a < 2);
  108. return "Unknown argument $a[1], choose one of " . join(" ", map {@{$sets{$_}} ? $_.':'.join ',', @{$sets{$_}} : $_} sort keys %sets)
  109. if(!defined($sets{$a[1]}));
  110. my $command = $a[1];
  111. my $value = $a[2];
  112. COMMAND_HANDLER: {
  113. $command eq "connect" and do {
  114. Start($hash);
  115. last;
  116. };
  117. $command eq "disconnect" and do {
  118. Stop($hash);
  119. last;
  120. };
  121. $command eq "inclusion-mode" and do {
  122. sendMessage($hash,radioId => 0, childId => 0, cmd => C_INTERNAL, ack => 0, subType => I_INCLUSION_MODE, payload => $value eq 'on' ? 1 : 0);
  123. $hash->{'inclusion-mode'} = $value eq 'on' ? 1 : 0;
  124. last;
  125. };
  126. };
  127. }
  128. sub Attr($$$$) {
  129. my ($command,$name,$attribute,$value) = @_;
  130. my $hash = $main::defs{$name};
  131. ATTRIBUTE_HANDLER: {
  132. $attribute eq "autocreate" and do {
  133. if ($main::init_done) {
  134. my $mode = $command eq "set" ? 1 : 0;
  135. sendMessage($hash,radioId => $hash->{radioId}, childId => $hash->{childId}, ack => 0, subType => I_INCLUSION_MODE, payload => $mode);
  136. $hash->{'inclusion-mode'} = $mode;
  137. }
  138. last;
  139. };
  140. $attribute eq "requestAck" and do {
  141. if ($command eq "set") {
  142. $hash->{ack} = 1;
  143. } else {
  144. $hash->{ack} = 0;
  145. $hash->{messages} = {};
  146. $hash->{outstandingAck} = 0;
  147. }
  148. last;
  149. };
  150. $attribute eq "OTA_firmwareConfig" and do {
  151. last;
  152. };
  153. }
  154. }
  155. sub Notify($$) {
  156. my ($hash,$dev) = @_;
  157. if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
  158. Start($hash);
  159. } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
  160. }
  161. }
  162. sub Start($) {
  163. my $hash = shift;
  164. my ($dev) = split("[ \t]+", $hash->{DEF});
  165. $hash->{DeviceName} = $dev;
  166. CommandAttr(undef, "$hash->{NAME} stateFormat connection") unless AttrVal($hash->{NAME},"stateFormat",undef);
  167. DevIo_CloseDev($hash);
  168. return DevIo_OpenDev($hash, 0, "MYSENSORS::Init");
  169. }
  170. sub Stop($) {
  171. my $hash = shift;
  172. DevIo_CloseDev($hash);
  173. RemoveInternalTimer($hash);
  174. readingsSingleUpdate($hash,"connection","disconnected",1);
  175. }
  176. sub Ready($) {
  177. my $hash = shift;
  178. return DevIo_OpenDev($hash, 1, "MYSENSORS::Init") if($hash->{STATE} eq "disconnected");
  179. if(defined($hash->{USBDev})) {
  180. my $po = $hash->{USBDev};
  181. my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status;
  182. return ( $InBytes > 0 );
  183. }
  184. }
  185. sub Init($) {
  186. my $hash = shift;
  187. my $name = $hash->{NAME};
  188. $hash->{'inclusion-mode'} = AttrVal($name,"autocreate",0);
  189. $hash->{ack} = AttrVal($name,"requestAck",0);
  190. $hash->{outstandingAck} = 0;
  191. if ($hash->{ack}) {
  192. GP_ForallClients($hash,sub {
  193. my $client = shift;
  194. $hash->{messagesForRadioId}->{$client->{radioId}} = {
  195. lastseen => -1,
  196. nexttry => -1,
  197. numtries => 1,
  198. messages => [],
  199. };
  200. });
  201. }
  202. readingsSingleUpdate($hash,"connection","connected",1);
  203. sendMessage($hash, radioId => 0, childId => 0, cmd => C_INTERNAL, ack => 0, subType => I_VERSION, payload => '');
  204. return undef;
  205. }
  206. # GetConnectStatus
  207. sub GetConnectStatus($){
  208. my ($hash) = @_;
  209. my $name = $hash->{NAME};
  210. Log3 $name, 4, "MySensors: GetConnectStatus called ...";
  211. #query heartbeat from gateway
  212. sendMessage($hash, radioId => 0, childId => 0, cmd => C_INTERNAL, ack => 0, subType => I_HEARTBEAT_REQUEST, payload => '');
  213. # neuen Timer starten in einem konfigurierten Interval.
  214. InternalTimer(gettimeofday()+300, "MYSENSORS::GetConnectStatus", $hash);# Restart check in 5 mins again
  215. InternalTimer(gettimeofday()+5, "MYSENSORS::Start", $hash); #Start timer for reset if after 5 seconds RESPONSE is not received
  216. }
  217. sub Timer($) {
  218. my $hash = shift;
  219. my $now = time;
  220. foreach my $radioid (keys %{$hash->{messagesForRadioId}}) {
  221. my $msgsForId = $hash->{messagesForRadioId}->{$radioid};
  222. if ($now > $msgsForId->{nexttry}) {
  223. foreach my $msg (@{$msgsForId->{messages}}) {
  224. my $txt = createMsg(%$msg);
  225. Log3 ($hash->{NAME},5,"MYSENSORS outstanding ack, re-send: ".dumpMsg($msg));
  226. DevIo_SimpleWrite($hash,"$txt\n",undef);
  227. }
  228. $msgsForId->{numtries}++;
  229. $msgsForId->{nexttry} = gettimeofday()+$msgsForId->{numtries};
  230. }
  231. }
  232. _scheduleTimer($hash);
  233. }
  234. sub Read {
  235. my ($hash) = @_;
  236. my $name = $hash->{NAME};
  237. my $buf = DevIo_SimpleRead($hash);
  238. return "" if(!defined($buf));
  239. my $data = $hash->{PARTIAL};
  240. Log3 ($name, 4, "MYSENSORS/RAW: $data/$buf");
  241. $data .= $buf;
  242. while ($data =~ m/\n/) {
  243. my $txt;
  244. ($txt,$data) = split("\n", $data, 2);
  245. $txt =~ s/\r//;
  246. if (my $msg = parseMsg($txt)) {
  247. Log3 ($name,4,"MYSENSORS Read: ".dumpMsg($msg));
  248. if ($msg->{ack}) {
  249. onAcknowledge($hash,$msg);
  250. }
  251. RemoveInternalTimer($hash,"MYSENSORS::GetConnectStatus");
  252. InternalTimer(gettimeofday()+300, "MYSENSORS::GetConnectStatus", $hash);# Restart check in 5 mins again
  253. my $type = $msg->{cmd};
  254. MESSAGE_TYPE: {
  255. $type == C_PRESENTATION and do {
  256. onPresentationMsg($hash,$msg);
  257. last;
  258. };
  259. $type == C_SET and do {
  260. onSetMsg($hash,$msg);
  261. last;
  262. };
  263. $type == C_REQ and do {
  264. onRequestMsg($hash,$msg);
  265. last;
  266. };
  267. $type == C_INTERNAL and do {
  268. onInternalMsg($hash,$msg);
  269. last;
  270. };
  271. $type == C_STREAM and do {
  272. onStreamMsg($hash,$msg);
  273. last;
  274. };
  275. }
  276. } else {
  277. Log3 ($name,5,"MYSENSORS Read: ".$txt."is no parsable mysensors message");
  278. }
  279. }
  280. $hash->{PARTIAL} = $data;
  281. return undef;
  282. };
  283. sub onPresentationMsg($$) {
  284. my ($hash,$msg) = @_;
  285. my $client = matchClient($hash,$msg);
  286. my $clientname;
  287. my $sensorType = $msg->{subType};
  288. unless ($client) {
  289. if ($hash->{'inclusion-mode'}) {
  290. $clientname = "MYSENSOR_$msg->{radioId}";
  291. CommandDefine(undef,"$clientname MYSENSORS_DEVICE $msg->{radioId}");
  292. $client = $main::defs{$clientname};
  293. return unless ($client);
  294. } else {
  295. Log3($hash->{NAME},3,"MYSENSORS: ignoring presentation-msg from unknown radioId $msg->{radioId}, childId $msg->{childId}, sensorType $sensorType");
  296. return;
  297. }
  298. }
  299. MYSENSORS::DEVICE::onPresentationMessage($client,$msg);
  300. };
  301. sub onSetMsg($$) {
  302. my ($hash,$msg) = @_;
  303. if (my $client = matchClient($hash,$msg)) {
  304. MYSENSORS::DEVICE::onSetMessage($client,$msg);
  305. } else {
  306. Log3($hash->{NAME},3,"MYSENSORS: ignoring set-msg from unknown radioId $msg->{radioId}, childId $msg->{childId} for ".variableTypeToStr($msg->{subType}));
  307. }
  308. };
  309. sub onRequestMsg($$) {
  310. my ($hash,$msg) = @_;
  311. if (my $client = matchClient($hash,$msg)) {
  312. MYSENSORS::DEVICE::onRequestMessage($client,$msg);
  313. } else {
  314. Log3($hash->{NAME},3,"MYSENSORS: ignoring req-msg from unknown radioId $msg->{radioId}, childId $msg->{childId} for ".variableTypeToStr($msg->{subType}));
  315. }
  316. };
  317. sub onInternalMsg($$) {
  318. my ($hash,$msg) = @_;
  319. my $address = $msg->{radioId};
  320. my $type = $msg->{subType};
  321. if ($address == 0 or $address == 255) { #msg to or from gateway
  322. TYPE: {
  323. $type == I_INCLUSION_MODE and do {
  324. if (AttrVal($hash->{NAME},"autocreate",0)) { #if autocreate is switched on, keep gateways inclusion-mode active
  325. if ($msg->{payload} == 0) {
  326. sendMessage($hash,radioId => $msg->{radioId}, childId => $msg->{childId}, ack => 0, subType => I_INCLUSION_MODE, payload => 1);
  327. }
  328. } else {
  329. $hash->{'inclusion-mode'} = $msg->{payload};
  330. }
  331. last;
  332. };
  333. $type == I_GATEWAY_READY and do {
  334. readingsSingleUpdate($hash,'connection','startup complete',1);
  335. GP_ForallClients($hash,sub {
  336. my $client = shift;
  337. MYSENSORS::DEVICE::onGatewayStarted($client);
  338. });
  339. InternalTimer(gettimeofday()+300, "MYSENSORS::GetConnectStatus", $hash);
  340. last;
  341. };
  342. $type == I_HEARTBEAT_RESPONSE and do {
  343. RemoveInternalTimer($hash,"MYSENSORS::Start"); ## Reset reconnect because timeout was not reached
  344. readingsSingleUpdate($hash, "heartbeat", "last", 0);
  345. };
  346. $type == I_VERSION and do {
  347. $hash->{version} = $msg->{payload};
  348. last;
  349. };
  350. $type == I_LOG_MESSAGE and do {
  351. Log3($hash->{NAME},5,"MYSENSORS gateway $hash->{NAME}: $msg->{payload}");
  352. last;
  353. };
  354. $type == I_ID_REQUEST and do {
  355. if ($hash->{'inclusion-mode'}) {
  356. my %nodes = map {$_ => 1} (AttrVal($hash->{NAME},"first-sensorid",20) ... AttrVal($hash->{NAME},"last-sensorid",254));
  357. GP_ForallClients($hash,sub {
  358. my $client = shift;
  359. delete $nodes{$client->{radioId}};
  360. });
  361. if (keys %nodes) {
  362. my $newid = (sort keys %nodes)[0];
  363. sendMessage($hash,radioId => 255, childId => 255, cmd => C_INTERNAL, ack => 0, subType => I_ID_RESPONSE, payload => $newid);
  364. Log3($hash->{NAME},4,"MYSENSORS $hash->{NAME} assigned new nodeid $newid");
  365. } else {
  366. Log3($hash->{NAME},4,"MYSENSORS $hash->{NAME} cannot assign new nodeid");
  367. }
  368. } else {
  369. Log3($hash->{NAME},4,"MYSENSORS: ignoring id-request-msg from unknown radioId $msg->{radioId}");
  370. }
  371. last;
  372. };
  373. }
  374. } elsif (my $client = matchClient($hash,$msg)) {
  375. MYSENSORS::DEVICE::onInternalMessage($client,$msg);
  376. } else {
  377. Log3($hash->{NAME},3,"MYSENSORS: ignoring internal-msg from unknown radioId $msg->{radioId}, childId $msg->{childId} for ".internalMessageTypeToStr($msg->{subType}));
  378. }
  379. };
  380. sub onStreamMsg($$) {
  381. my ($hash,$msg) = @_;
  382. if (my $client = matchClient($hash, $msg)) {
  383. MYSENSORS::DEVICE::onStreamMessage($client, $msg);
  384. } else {
  385. Log3($hash->{NAME},3,"MYSENSORS: ignoring stream-msg from unknown radioId $msg->{radioId}, childId $msg->{childId} for ".datastreamTypeToStr($msg->{subType}));
  386. }
  387. };
  388. sub onAcknowledge($$) {
  389. my ($hash,$msg) = @_;
  390. my $ack;
  391. if (defined (my $outstanding = $hash->{messagesForRadioId}->{$msg->{radioId}}->{messages})) {
  392. my @remainMsg = grep {
  393. $_->{childId} != $msg->{childId}
  394. or $_->{cmd} != $msg->{cmd}
  395. or $_->{subType} != $msg->{subType}
  396. or $_->{payload} ne $msg->{payload}
  397. } @$outstanding;
  398. if ($ack = @remainMsg < @$outstanding) {
  399. $hash->{outstandingAck} -= 1;
  400. @$outstanding = @remainMsg;
  401. }
  402. $hash->{messagesForRadioId}->{$msg->{radioId}}->{numtries} = 1;
  403. }
  404. Log3 ($hash->{NAME},4,"MYSENSORS Read: unexpected ack ".dumpMsg($msg)) unless $ack;
  405. }
  406. sub getFirmwareTypes($) {
  407. my ($hash) = @_;
  408. my $name = $hash->{NAME};
  409. my @fwTypes = ();
  410. my $filename = AttrVal($name, "OTA_firmwareConfig", undef);
  411. if (defined($filename)) {
  412. my ($err, @lines) = FileRead({FileName => "./FHEM/firmware/" . $filename,
  413. ForceType => "file"});
  414. if (defined($err) && $err) {
  415. Log3($name, 2, "$name: could not read MySensor firmware configuration file - $err");
  416. } else {
  417. for (my $i = 0; $i < @lines ; $i++) {
  418. chomp(my $row = $lines[$i]);
  419. if (index($row, "#") != 0) {
  420. my @tokens = split(",", $row);
  421. push(@fwTypes, $tokens[0]);
  422. }
  423. }
  424. }
  425. }
  426. Log3($name, 5, "$name: getFirmwareTypes - list contains: @fwTypes");
  427. return @fwTypes;
  428. }
  429. sub getLatestFirmware($$) {
  430. my ($hash, $type) = @_;
  431. my $name = $hash->{NAME};
  432. my $cfgfilename = AttrVal($name, "OTA_firmwareConfig", undef);
  433. my $version = undef;
  434. $name = undef;
  435. my $filename = undef;
  436. if (defined($cfgfilename)) {
  437. my ($err, @lines) = FileRead({FileName => "./FHEM/firmware/" . $cfgfilename,
  438. ForceType => "file"});
  439. if (defined($err) && $err) {
  440. Log3($name, 2, "$name: could not read MySensor firmware configuration file - $err");
  441. } else {
  442. for (my $i = 0; $i < @lines ; $i++) {
  443. chomp(my $row = $lines[$i]);
  444. if (index($row, "#") != 0) {
  445. my @tokens = split(",", $row);
  446. if ($tokens[0] eq $type) {
  447. if ((not defined $version) || ($tokens[2] > $version)) {
  448. $name = $tokens[1];
  449. $version = $tokens[2];
  450. $filename = $tokens[3];
  451. }
  452. }
  453. }
  454. }
  455. }
  456. }
  457. return ($version, $filename, $name);
  458. }
  459. sub sendMessage($%) {
  460. my ($hash,%msg) = @_;
  461. $msg{ack} = $hash->{ack} unless defined $msg{ack};
  462. my $txt = createMsg(%msg);
  463. Log3 ($hash->{NAME},5,"MYSENSORS send: ".dumpMsg(\%msg));
  464. DevIo_SimpleWrite($hash,"$txt\n",undef);
  465. if ($msg{ack}) {
  466. my $messagesForRadioId = $hash->{messagesForRadioId}->{$msg{radioId}};
  467. unless (defined $messagesForRadioId) {
  468. $messagesForRadioId = {
  469. lastseen => -1,
  470. numtries => 1,
  471. messages => [],
  472. };
  473. $hash->{messagesForRadioId}->{$msg{radioId}} = $messagesForRadioId;
  474. }
  475. my $messages = $messagesForRadioId->{messages};
  476. @$messages = grep {
  477. $_->{childId} != $msg{childId}
  478. or $_->{cmd} != $msg{cmd}
  479. or $_->{subType} != $msg{subType}
  480. } @$messages;
  481. push @$messages,\%msg;
  482. $messagesForRadioId->{nexttry} = gettimeofday()+$messagesForRadioId->{numtries};
  483. _scheduleTimer($hash);
  484. }
  485. };
  486. sub _scheduleTimer($) {
  487. my ($hash) = @_;
  488. $hash->{outstandingAck} = 0;
  489. RemoveInternalTimer($hash);
  490. my $next;
  491. foreach my $radioid (keys %{$hash->{messagesForRadioId}}) {
  492. my $msgsForId = $hash->{messagesForRadioId}->{$radioid};
  493. $hash->{outstandingAck} += @{$msgsForId->{messages}};
  494. $next = $msgsForId->{nexttry} unless (defined $next and $next < $msgsForId->{nexttry});
  495. };
  496. InternalTimer($next, "MYSENSORS::Timer", $hash, 0) if (defined $next);
  497. }
  498. sub matchClient($$) {
  499. my ($hash,$msg) = @_;
  500. my $radioId = $msg->{radioId};
  501. my $found;
  502. GP_ForallClients($hash,sub {
  503. return if $found;
  504. my $client = shift;
  505. if ($client->{radioId} == $radioId) {
  506. $found = $client;
  507. }
  508. });
  509. return $found;
  510. }
  511. 1;
  512. =pod
  513. =item device
  514. =item summary includes a MYSENSORS gateway
  515. =item summary_DE integriert ein MYSENSORS Gateway
  516. =begin html
  517. <a name="MYSENSORS"></a>
  518. <h3>MYSENSORS</h3>
  519. <ul>
  520. <p>connects fhem to <a href="http://MYSENSORS.org">MYSENSORS</a>.</p>
  521. <p>A single MYSENSORS device can serve multiple <a href="#MYSENSORS_DEVICE">MYSENSORS_DEVICE</a> clients.<br/>
  522. Each <a href="#MYSENSORS_DEVICE">MYSENSORS_DEVICE</a> represents a mysensors node.<br/>
  523. <a name="MYSENSORSdefine"></a>
  524. <p><b>Define</b></p>
  525. <ul>
  526. <p><code>define &lt;name&gt; MYSENSORS &lt;serial device&gt|&lt;ip:port&gt;</code></p>
  527. <p>Specifies the MYSENSORS device.</p>
  528. </ul>
  529. <a name="MYSENSORSset"></a>
  530. <p><b>Set</b></p>
  531. <ul>
  532. <li>
  533. <p><code>set &lt;name&gt; connect</code><br/>
  534. (re-)connects the MYSENSORS-device to the MYSENSORS-gateway</p>
  535. </li>
  536. <li>
  537. <p><code>set &lt;name&gt; disconnect</code><br/>
  538. disconnects the MYSENSORS-device from the MYSENSORS-gateway</p>
  539. </li>
  540. <li>
  541. <p><code>set &lt;name&gt; inclusion-mode on|off</code><br/>
  542. turns the gateways inclusion-mode on or off</p>
  543. </li>
  544. </ul>
  545. <a name="MYSENSORSattr"></a>
  546. <p><b>Attributes</b></p>
  547. <ul>
  548. <li>
  549. <p><code>attr &lt;name&gt; autocreate</code><br/>
  550. enables auto-creation of MYSENSOR_DEVICE-devices on receival of presentation-messages</p>
  551. </li>
  552. <li>
  553. <p><code>attr &lt;name&gt; requestAck</code><br/>
  554. request acknowledge from nodes.<br/>
  555. if set the Readings of nodes are updated not before requested acknowledge is received<br/>
  556. if not set the Readings of nodes are updated immediatly (not awaiting the acknowledge).
  557. May also be configured for individual nodes if not set for gateway.</p>
  558. </li>
  559. <li>
  560. <p><code>attr &lt;name&gt; first-sensorid <&lt;number &lth; 255&gt;></code><br/>
  561. configures the lowest node-id assigned to a mysensor-node on request (defaults to 20)</p>
  562. </li>
  563. <li>
  564. <p><code>attr &lt;name&gt; OTA_firmwareConfig &lt;filename&gt;</code><br/>
  565. specifies a configuration file for the <a href="https://www.mysensors.org/about/fota">FOTA</a>
  566. (firmware over the air - wireless programming of the nodes) configuration. It must be stored
  567. in the folder FHEM/firmware. The format of the configuration file is the following (csv):</p>
  568. <p><code>#Type,Name,Version,File,Comments</code><br/>
  569. <code>10,Blink,1,Blink.hex,blinking example</code><br/></p>
  570. <p>The meaning of the columns is the following:</br>
  571. <dl>
  572. <dt><code>Type</code></dt>
  573. <dd>a numeric value (range 0 .. 65536) - each node will be assigned a firmware type</dd>
  574. <dt><code>Name</code></dt>
  575. <dd>a short name for this type</dd>
  576. <dt><code>Version</code></dt>
  577. <dd>a numeric value (range 0 .. 65536) - the version of the firmware (may be different
  578. to the value that is send during the node presentation)</dd>
  579. <dt><code>File</code></dt>
  580. <dd>the filename containing the firmware - must also be stored in the folder FHEM/firmware</dd>
  581. <dt><code>Comments</code></dt>
  582. <dd>a description / comment for the firmware</dd>
  583. </dl></p>
  584. </li>
  585. </ul>
  586. </ul>
  587. =end html
  588. =cut