10_MYSENSORS_DEVICE.pm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. ##############################################
  2. #
  3. # fhem bridge to MySensors (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: 10_MYSENSORS_DEVICE.pm 10967 2016-02-29 20:37:40Z ntruchsess $
  23. #
  24. ##############################################
  25. use strict;
  26. use warnings;
  27. my %gets = (
  28. "version" => "",
  29. );
  30. sub MYSENSORS_DEVICE_Initialize($) {
  31. my $hash = shift @_;
  32. # Consumer
  33. $hash->{DefFn} = "MYSENSORS::DEVICE::Define";
  34. $hash->{UndefFn} = "MYSENSORS::DEVICE::UnDefine";
  35. $hash->{SetFn} = "MYSENSORS::DEVICE::Set";
  36. $hash->{AttrFn} = "MYSENSORS::DEVICE::Attr";
  37. $hash->{AttrList} =
  38. "config:M,I ".
  39. "mode:node,repeater ".
  40. "version:1.4 ".
  41. "setCommands ".
  42. "setReading_.+ ".
  43. "mapReadingType_.+ ".
  44. "mapReading_.+ ".
  45. "requestAck:1 ".
  46. "IODev ".
  47. "showtime:0,1 ".
  48. $main::readingFnAttributes;
  49. main::LoadModule("MYSENSORS");
  50. }
  51. package MYSENSORS::DEVICE;
  52. use strict;
  53. use warnings;
  54. use GPUtils qw(:all);
  55. use Device::MySensors::Constants qw(:all);
  56. use Device::MySensors::Message qw(:all);
  57. use SetExtensions qw/ :all /;
  58. BEGIN {
  59. MYSENSORS->import(qw(:all));
  60. GP_Import(qw(
  61. AttrVal
  62. readingsSingleUpdate
  63. CommandAttr
  64. CommandDeleteAttr
  65. CommandDeleteReading
  66. AssignIoPort
  67. Log3
  68. SetExtensions
  69. ReadingsVal
  70. ))
  71. };
  72. my %static_types = (
  73. S_DOOR => { receives => [], sends => [V_TRIPPED,V_ARMED] }, # Door and window sensors
  74. S_MOTION => { receives => [], sends => [V_TRIPPED] }, # MotionSensor
  75. S_SMOKE => { receives => [], sends => [V_TRIPPED,V_ARMED] }, # Smoke sensor
  76. S_LIGHT => { receives => [V_STATUS,V_WATT], sends => [V_STATUS,V_WATT] }, # Binary device (on/off), Alias for S_BINARY
  77. S_DIMMER => { receives => [V_STATUS,V_PERCENTAGE,V_WATT], sends => [V_STATUS,V_PERCENTAGE,V_WATT] }, # Dimmable device of some kind
  78. S_COVER => { receives => [V_UP,V_DOWN,V_STOP,V_PERCENTAGE], sends => [V_PERCENTAGE] }, # Window covers or shades
  79. S_TEMP => { receives => [], sends => [V_TEMP] }, # Temperature sensor
  80. S_HUM => { receives => [], sends => [V_HUM] }, # Humidity sensor
  81. S_BARO => { receives => [], sends => [V_PRESSURE,V_FORECAST] }, # Barometer sensor (Pressure)
  82. S_WIND => { receives => [], sends => [V_WIND,V_GUST] }, # Wind sensor
  83. S_RAIN => { receives => [], sends => [V_RAIN,V_RAINRATE] }, # Rain sensor
  84. S_UV => { receives => [], sends => [V_UV] }, # UV Sensor
  85. S_WEIGHT => { receives => [], sends => [V_WEIGHT,V_IMPEDANCE] }, # Weight sensor for scales etc.
  86. S_POWER => { receives => [V_VAR1], sends => [V_WATT,V_KWH,V_VAR1] }, # Power measuring device, like power meters
  87. S_HEATER => { receives => [], sends => [V_HVAC_SETPOINT_HEAT,V_HVAC_FLOW_STATE,V_TEMP] }, # Heater device
  88. S_DISTANCE => { receives => [], sends => [V_DISTANCE] }, # Distance sensor
  89. S_LIGHT_LEVEL => { receives => [], sends => [V_LIGHT_LEVEL] }, # Light sensor
  90. S_ARDUINO_NODE => { receives => [], sends => [] }, # Arduino node device
  91. S_ARDUINO_REPEATER_NODE => { receives => [], sends => [] }, # Arduino repeating node device
  92. S_LOCK => { receives => [V_LOCK_STATUS], sends => [V_LOCK_STATUS] }, # Lock device
  93. S_IR => { receives => [V_IR_SEND], sends => [V_IR_RECEIVE] }, # Ir sender/receiver device
  94. S_WATER => { receives => [V_VAR1], sends => [V_FLOW,V_VOLUME,V_VAR1] }, # Water meter
  95. S_AIR_QUALITY => { receives => [], sends => [V_LEVEL,V_UNIT_PREFIX] }, # Air quality sensor e.g. MQ-2
  96. S_CUSTOM => { receives => [V_VAR1,V_VAR2,V_VAR3,V_VAR4,V_VAR5], sends => [V_VAR1,V_VAR2,V_VAR3,V_VAR4,V_VAR5] }, # Use this for custom sensors where no other fits.
  97. S_DUST => { receives => [], sends => [V_LEVEL,V_UNIT_PREFIX] }, # Dust level sensor
  98. S_SCENE_CONTROLLER => { receives => [], sends => [V_SCENE_ON,V_SCENE_OFF] }, # Scene controller device
  99. S_RGB_LIGHT => { receives => [V_RGB,V_WATT], sends => [V_RGB,V_WATT] }, # RGB light
  100. S_RGBW_LIGHT => { receives => [V_RGBW,V_WATT], sends => [V_RGBW,V_WATT] }, # RGBW light (with separate white component)
  101. S_COLOR_SENSOR => { receives => [V_RGB], sends => [V_RGB] }, # Color sensor
  102. S_HVAC => { receives => [], sends => [V_HVAC_SETPOINT_HEAT,V_HVAC_SETPOINT_COOL,V_HVAC_FLOW_STATE,V_HVAC_FLOW_MODE,V_HVAC_SPEED] }, # Thermostat/HVAC device
  103. S_MULTIMETER => { receives => [], sends => [V_VOLTAGE,V_CURRENT,V_IMPEDANCE] }, # Multimeter device
  104. S_SPRINKLER => { receives => [], sends => [V_STATUS,V_TRIPPED] }, # Sprinkler device
  105. S_WATER_LEAK => { receives => [], sends => [V_TRIPPED,V_ARMED] }, # Water leak sensor
  106. S_SOUND => { receives => [], sends => [V_LEVEL,V_TRIPPED,V_ARMED] }, # Sound sensor
  107. S_VIBRATION => { receives => [], sends => [V_LEVEL,V_TRIPPED,V_ARMED] }, # Vibration sensor
  108. S_MOISTURE => { receives => [], sends => [V_LEVEL,V_TRIPPED,V_ARMED] }, # Moisture sensor
  109. );
  110. my %static_mappings = (
  111. V_TEMP => { type => "temperature" },
  112. V_HUM => { type => "humidity" },
  113. V_STATUS => { type => "status", val => { 0 => 'off', 1 => 'on' }},
  114. V_PERCENTAGE => { type => "percentage", range => { min => 0, step => 1, max => 100 }},
  115. V_PRESSURE => { type => "pressure" },
  116. V_FORECAST => { type => "forecast", val => { # PressureSensor, DP/Dt explanation
  117. 0 => 'stable', # 0 = "Stable Weather Pattern"
  118. 1 => 'sunny', # 1 = "Slowly rising Good Weather", "Clear/Sunny"
  119. 2 => 'cloudy', # 2 = "Slowly falling L-Pressure ", "Cloudy/Rain"
  120. 3 => 'unstable', # 3 = "Quickly rising H-Press", "Not Stable"
  121. 4 => 'thunderstorm',# 4 = "Quickly falling L-Press", "Thunderstorm"
  122. 5 => 'unknown' }}, # 5 = "Unknown (More Time needed)
  123. V_RAIN => { type => "rain" },
  124. V_RAINRATE => { type => "rainrate" },
  125. V_WIND => { type => "wind" },
  126. V_GUST => { type => "gust" },
  127. V_DIRECTION => { type => "direction" },
  128. V_UV => { type => "uv" },
  129. V_WEIGHT => { type => "weight" },
  130. V_DISTANCE => { type => "distance" },
  131. V_IMPEDANCE => { type => "impedance" },
  132. V_ARMED => { type => "armed", val => { 0 => 'off', 1 => 'on' }},
  133. V_TRIPPED => { type => "tripped", val => { 0 => 'off', 1 => 'on' }},
  134. V_WATT => { type => "power" },
  135. V_KWH => { type => "energy" },
  136. V_SCENE_ON => { type => "button_on" },
  137. V_SCENE_OFF => { type => "button_off" },
  138. V_HVAC_FLOW_STATE => { type => "hvacflowstate" },
  139. V_HVAC_SPEED => { type => "hvacspeed" },
  140. V_LIGHT_LEVEL => { type => "brightness", range => { min => 0, step => 1, max => 100 }},
  141. V_VAR1 => { type => "value1" },
  142. V_VAR2 => { type => "value2" },
  143. V_VAR3 => { type => "value3" },
  144. V_VAR4 => { type => "value4" },
  145. V_VAR5 => { type => "value5" },
  146. V_UP => { type => "up" },
  147. V_DOWN => { type => "down" },
  148. V_STOP => { type => "stop" },
  149. V_IR_SEND => { type => "ir_send" },
  150. V_IR_RECEIVE => { type => "ir_receive" },
  151. V_FLOW => { type => "flow" },
  152. V_VOLUME => { type => "volume" },
  153. V_LOCK_STATUS => { type => "lockstatus", val => { 0 => 'off', 1 => 'on' }},
  154. V_LEVEL => { type => "level" },
  155. V_VOLTAGE => { type => "voltage" },
  156. V_CURRENT => { type => "current" },
  157. V_RGB => { type => "rgb" },
  158. V_RGBW => { type => "rgbw" },
  159. V_ID => { type => "id" },
  160. V_UNIT_PREFIX => { type => "unitprefix" },
  161. V_HVAC_SETPOINT_COOL => { type => "hvacsetpointcool" },
  162. V_HVAC_SETPOINT_HEAT => { type => "hvacsetpointheat" },
  163. V_HVAC_FLOW_MODE => { type => "hvacflowmode" },
  164. );
  165. sub Define($$) {
  166. my ( $hash, $def ) = @_;
  167. my ($name, $type, $radioId) = split("[ \t]+", $def);
  168. return "requires 1 parameters" unless (defined $radioId and $radioId ne "");
  169. $hash->{radioId} = $radioId;
  170. $hash->{sets} = {
  171. 'time' => "",
  172. reboot => "",
  173. };
  174. $hash->{ack} = 0;
  175. $hash->{typeMappings} = {map {variableTypeToIdx($_) => $static_mappings{$_}} keys %static_mappings};
  176. $hash->{sensorMappings} = {map {sensorTypeToIdx($_) => $static_types{$_}} keys %static_types};
  177. $hash->{readingMappings} = {};
  178. AssignIoPort($hash);
  179. };
  180. sub UnDefine($) {
  181. my ($hash) = @_;
  182. return undef;
  183. }
  184. sub Set($@) {
  185. my ($hash,$name,$command,@values) = @_;
  186. return "Need at least one parameters" unless defined $command;
  187. if(!defined($hash->{sets}->{$command})) {
  188. my $list = join(" ", map {$hash->{sets}->{$_} ne "" ? "$_:$hash->{sets}->{$_}" : $_} sort keys %{$hash->{sets}});
  189. return grep (/(^on$)|(^off$)/,keys %{$hash->{sets}}) == 2 ? SetExtensions($hash, $list, $name, $command, @values) : "Unknown argument $command, choose one of $list";
  190. }
  191. COMMAND_HANDLER: {
  192. $command eq "clear" and do {
  193. sendClientMessage($hash, childId => 255, cmd => C_INTERNAL, subType => I_CHILDREN, payload => "C");
  194. last;
  195. };
  196. $command eq "time" and do {
  197. sendClientMessage($hash, childId => 255, cmd => C_INTERNAL, subType => I_TIME, payload => time);
  198. last;
  199. };
  200. $command eq "reboot" and do {
  201. sendClientMessage($hash, childId => 255, cmd => C_INTERNAL, subType => I_REBOOT);
  202. last;
  203. };
  204. (defined ($hash->{setcommands}->{$command})) and do {
  205. my $setcommand = $hash->{setcommands}->{$command};
  206. eval {
  207. my ($type,$childId,$mappedValue) = mappedReadingToRaw($hash,$setcommand->{var},$setcommand->{val});
  208. sendClientMessage($hash,
  209. childId => $childId,
  210. cmd => C_SET,
  211. subType => $type,
  212. payload => $mappedValue,
  213. );
  214. readingsSingleUpdate($hash,$setcommand->{var},$setcommand->{val},1) unless ($hash->{ack} or $hash->{IODev}->{ack});
  215. };
  216. return "$command not defined: ".GP_Catch($@) if $@;
  217. last;
  218. };
  219. my $value = @values ? join " ",@values : "";
  220. eval {
  221. my ($type,$childId,$mappedValue) = mappedReadingToRaw($hash,$command,$value);
  222. sendClientMessage($hash, childId => $childId, cmd => C_SET, subType => $type, payload => $mappedValue);
  223. readingsSingleUpdate($hash,$command,$value,1) unless ($hash->{ack} or $hash->{IODev}->{ack});
  224. };
  225. return "$command not defined: ".GP_Catch($@) if $@;
  226. }
  227. }
  228. sub Attr($$$$) {
  229. my ($command,$name,$attribute,$value) = @_;
  230. my $hash = $main::defs{$name};
  231. ATTRIBUTE_HANDLER: {
  232. $attribute eq "config" and do {
  233. if ($main::init_done) {
  234. sendClientMessage($hash, cmd => C_INTERNAL, childId => 255, subType => I_CONFIG, payload => $command eq 'set' ? $value : "M");
  235. }
  236. last;
  237. };
  238. $attribute eq "mode" and do {
  239. if ($command eq "set" and $value eq "repeater") {
  240. $hash->{repeater} = 1;
  241. $hash->{sets}->{clear} = "";
  242. } else {
  243. $hash->{repeater} = 0;
  244. delete $hash->{sets}->{clear};
  245. }
  246. last;
  247. };
  248. $attribute eq "version" and do {
  249. if ($command eq "set") {
  250. $hash->{protocol} = $value;
  251. } else {
  252. delete $hash->{protocol};
  253. }
  254. last;
  255. };
  256. $attribute eq "setCommands" and do {
  257. foreach my $set (keys %{$hash->{setcommands}}) {
  258. delete $hash->{sets}->{$set};
  259. }
  260. $hash->{setcommands} = {};
  261. if ($command eq "set" and $value) {
  262. foreach my $setCmd (split ("[, \t]+",$value)) {
  263. if ($setCmd =~ /^(.+):(.+):(.+)$/) {
  264. $hash->{sets}->{$1}="";
  265. $hash->{setcommands}->{$1} = {
  266. var => $2,
  267. val => $3,
  268. };
  269. } else {
  270. return "unparsable value in setCommands for $name: $setCmd";
  271. }
  272. }
  273. }
  274. last;
  275. };
  276. $attribute =~ /^setReading_(.+)$/ and do {
  277. if ($command eq "set") {
  278. $hash->{sets}->{$1}= (defined $value) ? join(",",split ("[, \t]+",$value)) : "";
  279. } else {
  280. CommandDeleteReading(undef,"$hash->{NAME} $1");
  281. delete $hash->{sets}->{$1};
  282. }
  283. last;
  284. };
  285. $attribute =~ /^mapReadingType_(.+)/ and do {
  286. my $type = variableTypeToIdx("V_$1");
  287. if ($command eq "set") {
  288. my @values = split ("[, \t]",$value);
  289. $hash->{typeMappings}->{$type}={
  290. type => shift @values,
  291. val => {map {$_ =~ /^(.+):(.+)$/; $1 => $2} @values},
  292. }
  293. } else {
  294. if ($static_mappings{"V_$1"}) {
  295. $hash->{typeMappings}->{$type}=$static_mappings{"V_$1"};
  296. } else {
  297. delete $hash->{typeMappings}->{$type};
  298. }
  299. my $readings = $hash->{READINGS};
  300. my $readingMappings = $hash->{readingMappings};
  301. foreach my $todelete (map {$readingMappings->{$_}->{name}} grep {$readingMappings->{$_}->{type} == $type} keys %$readingMappings) {
  302. CommandDeleteReading(undef,"$hash->{NAME} $todelete"); #TODO do propper remap of existing readings
  303. }
  304. }
  305. last;
  306. };
  307. $attribute =~ /^mapReading_(.+)/ and do {
  308. my $readingMappings = $hash->{readingMappings};
  309. FIND: foreach my $id (keys %$readingMappings) {
  310. my $readingsForId = $readingMappings->{$id};
  311. foreach my $type (keys %$readingsForId) {
  312. if (($readingsForId->{$type}->{name} // "") eq $1) {
  313. delete $readingsForId->{$type};
  314. unless (keys %$readingsForId) {
  315. delete $readingMappings->{$id};
  316. }
  317. last FIND;
  318. }
  319. }
  320. }
  321. if ($command eq "set") {
  322. my ($id,$typeStr,@values) = split ("[, \t]",$value);
  323. my $typeMappings = $hash->{typeMappings};
  324. if (my @match = grep {$typeMappings->{$_}->{type} eq $typeStr} keys %$typeMappings) {
  325. my $type = shift @match;
  326. $readingMappings->{$id}->{$type}->{name} = $1;
  327. if (@values) {
  328. $readingMappings->{$id}->{$type}->{val} = {map {$_ =~ /^(.+):(.+)$/; $1 => $2} @values}; #TODO range?
  329. }
  330. } else {
  331. return "unknown reading type $typeStr";
  332. }
  333. } else {
  334. CommandDeleteReading(undef,"$hash->{NAME} $1");
  335. }
  336. last;
  337. };
  338. $attribute eq "requestAck" and do {
  339. if ($command eq "set") {
  340. $hash->{ack} = 1;
  341. } else {
  342. $hash->{ack} = 0;
  343. }
  344. last;
  345. };
  346. }
  347. }
  348. sub onGatewayStarted($) {
  349. my ($hash) = @_;
  350. }
  351. sub onPresentationMessage($$) {
  352. my ($hash,$msg) = @_;
  353. my $name = $hash->{NAME};
  354. my $nodeType = $msg->{subType};
  355. my $id = $msg->{childId};
  356. if ($id == 255) { #special id
  357. NODETYPE: {
  358. $nodeType == S_ARDUINO_NODE and do {
  359. CommandAttr(undef, "$name mode node");
  360. last;
  361. };
  362. $nodeType == S_ARDUINO_REPEATER_NODE and do {
  363. CommandAttr(undef, "$name mode repeater");
  364. last;
  365. };
  366. };
  367. CommandAttr(undef, "$name version $msg->{payload}");
  368. };
  369. my $readingMappings = $hash->{readingMappings};
  370. my $typeMappings = $hash->{typeMappings};
  371. if (my $sensorMappings = $hash->{sensorMappings}->{$nodeType}) {
  372. my $idStr = ($id > 0 ? $id : "");
  373. my @ret = ();
  374. foreach my $type (@{$sensorMappings->{sends}}) {
  375. next if (defined $readingMappings->{$id}->{$type});
  376. my $typeStr = $typeMappings->{$type}->{type};
  377. if ($hash->{IODev}->{'inclusion-mode'}) {
  378. if (my $ret = CommandAttr(undef,"$name mapReading_$typeStr$idStr $id $typeStr")) {
  379. push @ret,$ret;
  380. }
  381. } else {
  382. push @ret,"no mapReading for $id, $typeStr";
  383. }
  384. }
  385. foreach my $type (@{$sensorMappings->{receives}}) {
  386. my $typeMapping = $typeMappings->{$type};
  387. my $typeStr = $typeMapping->{type};
  388. next if (defined $hash->{sets}->{"$typeStr$idStr"});
  389. if ($hash->{IODev}->{'inclusion-mode'}) {
  390. my @values = ();
  391. if ($typeMapping->{range}) {
  392. @values = ('slider',$typeMapping->{range}->{min},$typeMapping->{range}->{step},$typeMapping->{range}->{max});
  393. } elsif ($typeMapping->{val}) {
  394. @values = values %{$typeMapping->{val}};
  395. }
  396. if (my $ret = CommandAttr(undef,"$name setReading_$typeStr$idStr".(@values ? " ".join (",",@values) : ""))) {
  397. push @ret,$ret;
  398. }
  399. } else {
  400. push @ret,"no setReading for $id, $typeStr";
  401. }
  402. }
  403. Log3 ($hash->{NAME},4,"MYSENSORS_DEVICE $hash->{NAME}: errors on C_PRESENTATION-message for childId $id, subType ".sensorTypeToStr($nodeType)." ".join (", ",@ret)) if @ret;
  404. }
  405. }
  406. sub onSetMessage($$) {
  407. my ($hash,$msg) = @_;
  408. if (defined $msg->{payload}) {
  409. eval {
  410. my ($reading,$value) = rawToMappedReading($hash,$msg->{subType},$msg->{childId},$msg->{payload});
  411. readingsSingleUpdate($hash,$reading,$value,1);
  412. };
  413. Log3 ($hash->{NAME},4,"MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message ".GP_Catch($@)) if $@;
  414. } else {
  415. Log3 ($hash->{NAME},5,"MYSENSORS_DEVICE $hash->{NAME}: ignoring C_SET-message without payload");
  416. }
  417. }
  418. sub onRequestMessage($$) {
  419. my ($hash,$msg) = @_;
  420. eval {
  421. my ($readingname,$val) = rawToMappedReading($hash, $msg->{subType}, $msg->{childId}, $msg->{payload});
  422. sendClientMessage($hash,
  423. childId => $msg->{childId},
  424. cmd => C_SET,
  425. subType => $msg->{subType},
  426. payload => ReadingsVal($hash->{NAME},$readingname,$val)
  427. );
  428. };
  429. Log3 ($hash->{NAME},4,"MYSENSORS_DEVICE $hash->{NAME}: ignoring C_REQ-message ".GP_Catch($@)) if $@;
  430. }
  431. sub onInternalMessage($$) {
  432. my ($hash,$msg) = @_;
  433. my $name = $hash->{NAME};
  434. my $type = $msg->{subType};
  435. my $typeStr = internalMessageTypeToStr($type);
  436. INTERNALMESSAGE: {
  437. $type == I_BATTERY_LEVEL and do {
  438. readingsSingleUpdate($hash,"batterylevel",$msg->{payload},1);
  439. Log3 ($name,4,"MYSENSORS_DEVICE $name: batterylevel $msg->{payload}");
  440. last;
  441. };
  442. $type == I_TIME and do {
  443. if ($msg->{ack}) {
  444. Log3 ($name,4,"MYSENSORS_DEVICE $name: response to time-request acknowledged");
  445. } else {
  446. sendClientMessage($hash,cmd => C_INTERNAL, childId => 255, subType => I_TIME, payload => time);
  447. Log3 ($name,4,"MYSENSORS_DEVICE $name: update of time requested");
  448. }
  449. last;
  450. };
  451. $type == I_VERSION and do {
  452. $hash->{$typeStr} = $msg->{payload};
  453. last;
  454. };
  455. $type == I_ID_REQUEST and do {
  456. $hash->{$typeStr} = $msg->{payload};
  457. last;
  458. };
  459. $type == I_ID_RESPONSE and do {
  460. $hash->{$typeStr} = $msg->{payload};
  461. last;
  462. };
  463. $type == I_INCLUSION_MODE and do {
  464. $hash->{$typeStr} = $msg->{payload};
  465. last;
  466. };
  467. $type == I_CONFIG and do {
  468. if ($msg->{ack}) {
  469. Log3 ($name,4,"MYSENSORS_DEVICE $name: response to config-request acknowledged");
  470. } else {
  471. readingsSingleUpdate($hash,"parentId",$msg->{payload},1);
  472. sendClientMessage($hash,cmd => C_INTERNAL, childId => 255, subType => I_CONFIG, payload => AttrVal($name,"config","M"));
  473. Log3 ($name,4,"MYSENSORS_DEVICE $name: respond to config-request, node parentId = " . $msg->{payload});
  474. }
  475. last;
  476. };
  477. $type == I_FIND_PARENT and do {
  478. $hash->{$typeStr} = $msg->{payload};
  479. last;
  480. };
  481. $type == I_FIND_PARENT_RESPONSE and do {
  482. $hash->{$typeStr} = $msg->{payload};
  483. last;
  484. };
  485. $type == I_LOG_MESSAGE and do {
  486. $hash->{$typeStr} = $msg->{payload};
  487. last;
  488. };
  489. $type == I_CHILDREN and do {
  490. readingsSingleUpdate($hash,"state","routingtable cleared",1);
  491. Log3 ($name,4,"MYSENSORS_DEVICE $name: routingtable cleared");
  492. last;
  493. };
  494. $type == I_SKETCH_NAME and do {
  495. $hash->{$typeStr} = $msg->{payload};
  496. readingsSingleUpdate($hash,"SKETCH_NAME",$msg->{payload},1);
  497. last;
  498. };
  499. $type == I_SKETCH_VERSION and do {
  500. $hash->{$typeStr} = $msg->{payload};
  501. readingsSingleUpdate($hash,"SKETCH_VERSION",$msg->{payload},1);
  502. last;
  503. };
  504. $type == I_REBOOT and do {
  505. $hash->{$typeStr} = $msg->{payload};
  506. last;
  507. };
  508. $type == I_GATEWAY_READY and do {
  509. $hash->{$typeStr} = $msg->{payload};
  510. last;
  511. };
  512. $type == I_REQUEST_SIGNING and do {
  513. $hash->{$typeStr} = $msg->{payload};
  514. last;
  515. };
  516. $type == I_GET_NONCE and do {
  517. $hash->{$typeStr} = $msg->{payload};
  518. last;
  519. };
  520. $type == I_GET_NONCE_RESPONSE and do {
  521. $hash->{$typeStr} = $msg->{payload};
  522. last;
  523. };
  524. }
  525. }
  526. sub sendClientMessage($%) {
  527. my ($hash,%msg) = @_;
  528. $msg{radioId} = $hash->{radioId};
  529. $msg{ack} = $hash->{ack} unless defined $msg{ack};
  530. sendMessage($hash->{IODev},%msg);
  531. }
  532. sub rawToMappedReading($$$$) {
  533. my($hash, $type, $childId, $value) = @_;
  534. my $name;
  535. if (defined (my $mapping = $hash->{readingMappings}->{$childId}->{$type})) {
  536. my $val = $mapping->{val} // $hash->{typeMappings}->{$type}->{val};
  537. return ($mapping->{name},defined $val ? ($val->{$value} // $value) : $value);
  538. }
  539. die "no reading-mapping for childId $childId, type ".($hash->{typeMappings}->{$type}->{type} ? $hash->{typeMappings}->{$type}->{type} : variableTypeToStr($type));
  540. }
  541. sub mappedReadingToRaw($$$) {
  542. my ($hash,$reading,$value) = @_;
  543. my $readingsMapping = $hash->{readingMappings};
  544. foreach my $id (keys %$readingsMapping) {
  545. my $readingTypesForId = $readingsMapping->{$id};
  546. foreach my $type (keys %$readingTypesForId) {
  547. if (($readingTypesForId->{$type}->{name} // "") eq $reading) {
  548. if (my $valueMappings = $readingTypesForId->{$type}->{val} // $hash->{typeMappings}->{$type}->{val}) {
  549. if (my @mappedValues = grep {$valueMappings->{$_} eq $value} keys %$valueMappings) {
  550. return ($type,$id,shift @mappedValues);
  551. }
  552. }
  553. return ($type,$id,$value);
  554. }
  555. }
  556. }
  557. die "no mapping for reading $reading";
  558. }
  559. 1;
  560. =pod
  561. =begin html
  562. <a name="MYSENSORS_DEVICE"></a>
  563. <h3>MYSENSORS_DEVICE</h3>
  564. <ul>
  565. <p>represents a mysensors sensor attached to a mysensor-node</p>
  566. <p>requires a <a href="#MYSENSOR">MYSENSOR</a>-device as IODev</p>
  567. <a name="MYSENSORS_DEVICEdefine"></a>
  568. <p><b>Define</b></p>
  569. <ul>
  570. <p><code>define &lt;name&gt; MYSENSORS_DEVICE &lt;Sensor-type&gt; &lt;node-id&gt;</code><br/>
  571. Specifies the MYSENSOR_DEVICE device.</p>
  572. </ul>
  573. <a name="MYSENSORS_DEVICEset"></a>
  574. <p><b>Set</b></p>
  575. <ul>
  576. <li>
  577. <p><code>set &lt;name&gt; clear</code><br/>
  578. clears routing-table of a repeater-node</p>
  579. </li>
  580. <li>
  581. <p><code>set &lt;name&gt; time</code><br/>
  582. sets time for nodes (that support it)</p>
  583. </li>
  584. <li>
  585. <p><code>set &lt;name&gt; reboot</code><br/>
  586. reboots a node (requires a bootloader that supports it).<br/>
  587. Attention: Nodes that run the standard arduino-bootloader will enter a bootloop!<br/>
  588. Dis- and reconnect the nodes power to restart in this case.</p>
  589. </li>
  590. </ul>
  591. <a name="MYSENSORS_DEVICEattr"></a>
  592. <p><b>Attributes</b></p>
  593. <ul>
  594. <li>
  595. <p><code>attr &lt;name&gt; config [&lt;M|I&gt;]</code><br/>
  596. configures metric (M) or inch (I). Defaults to 'M'</p>
  597. </li>
  598. <li>
  599. <p><code>attr &lt;name&gt; setCommands [&lt;command:reading:value&gt;]*</code><br/>
  600. configures one or more commands that can be executed by set.<br/>
  601. e.g.: <code>attr &lt;name&gt; setCommands on:switch_1:on off:switch_1:off</code><br/>
  602. if list of commands contains both 'on' and 'off' <a href="#setExtensions">set extensions</a> are supported</p>
  603. </li>
  604. <li>
  605. <p><code>attr &lt;name&gt; setReading_&lt;reading&gt; [&lt;value&gt;]*</code><br/>
  606. configures a reading that can be modified by set-command<br/>
  607. e.g.: <code>attr &lt;name&gt; setReading_switch_1 on,off</code></p>
  608. </li>
  609. <li>
  610. <p><code>attr &lt;name&gt; mapReading_&lt;reading&gt; &lt;childId&gt; &lt;readingtype&gt; [&lt;value&gt;:&lt;mappedvalue&gt;]*</code><br/>
  611. configures the reading-name for a given childId and sensortype<br/>
  612. E.g.: <code>attr xxx mapReading_aussentemperatur 123 temperature</code></p>
  613. </li>
  614. <li>
  615. <p><code>att &lt;name&gt; requestAck</code><br/>
  616. request acknowledge from nodes.<br/>
  617. if set the Readings of nodes are updated not before requested acknowledge is received<br/>
  618. if not set the Readings of nodes are updated immediatly (not awaiting the acknowledge).<br/>
  619. May also be configured on the gateway for all nodes at once</p>
  620. </li>
  621. <li>
  622. <p><code>attr &lt;name&gt; mapReadingType_&lt;reading&gt; &lt;new reading name&gt; [&lt;value&gt;:&lt;mappedvalue&gt;]*</code><br/>
  623. configures reading type names that should be used instead of technical names<br/>
  624. E.g.: <code>attr xxx mapReadingType_LIGHT switch 0:on 1:off</code>
  625. to be used for mysensor Variabletypes that have no predefined defaults (yet)</p>
  626. </li>
  627. </ul>
  628. </ul>
  629. =end html
  630. =cut