37_SHCdev.pm 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. ##########################################################################
  2. # This file is part of the smarthomatic module for FHEM.
  3. #
  4. # Copyright (c) 2014 Stefan Baumann, Uwe Freese
  5. #
  6. # You can find smarthomatic at www.smarthomatic.org.
  7. # You can find FHEM at www.fhem.de.
  8. #
  9. # This file is free software: you can redistribute it and/or modify it
  10. # under the terms of the GNU General Public License as published by the
  11. # Free Software Foundation, either version 3 of the License, or (at your
  12. # option) any later version.
  13. #
  14. # This file is distributed in the hope that it will be useful, but
  15. # WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  17. # Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License along
  20. # with smarthomatic. If not, see <http://www.gnu.org/licenses/>.
  21. ###########################################################################
  22. # $Id: 37_SHCdev.pm 8190 2015-03-10 21:23:03Z rr2000 $
  23. package main;
  24. use strict;
  25. use feature qw(switch);
  26. use warnings;
  27. use SetExtensions;
  28. use SHC_parser;
  29. my $parser = new SHC_parser();
  30. my %dev_state_icons = (
  31. "PowerSwitch" => ".*1\\d{7}:on:off .*0\\d{7}:off:on set.*:light_question:off",
  32. "Dimmer" => "on:on off:off set.*:light_question:off",
  33. "EnvSensor" => undef,
  34. "RGBDimmer" => undef,
  35. "SoilMoistureMeter" => ".*H:\\s\\d\\..*:ampel_rot"
  36. );
  37. my %web_cmds = (
  38. "PowerSwitch" => "on:off:toggle:statusRequest",
  39. "Dimmer" => "on:off:statusRequest",
  40. "EnvSensor" => undef,
  41. "RGBDimmer" => undef,
  42. "SoilMoistureMeter" => undef
  43. );
  44. # Array format: [ reading1, str_format1, reading2, str_format2 ... ]
  45. # "on" reading translates 0 -> "off"
  46. # 1 -> "on"
  47. my %dev_state_format = (
  48. "PowerSwitch" => ["port", "Port: "],
  49. "Dimmer" => ["on", "", "brightness", "B: "],
  50. "EnvSensor" => [ # Results in "T: 23.4 H: 27.3 Baro: 978.34 B: 45"
  51. "temperature", "T: ",
  52. "humidity", "H: ",
  53. "barometric_pressure", "Baro: ",
  54. "brightness", "B: ",
  55. "distance", "D: ",
  56. "port", "Port: ",
  57. "ains", "Ain: "
  58. ],
  59. "RGBDimmer" => ["color", "Color: "],
  60. "SoilMoistureMeter" => ["humidity", "H: "]
  61. );
  62. # Supported set commands
  63. # use "" if no set commands are available for device type
  64. # use "cmd_name:cmd_additional_info"
  65. # cmd_additional_info: Description available at http://www.fhemwiki.de/wiki/DevelopmentModuleIntro#X_Set
  66. my %sets = (
  67. "PowerSwitch" => "on:noArg off:noArg toggle:noArg statusRequest:noArg " .
  68. # Used from SetExtensions.pm
  69. "blink on-for-timer on-till off-for-timer off-till intervals " .
  70. "DigitalPort " .
  71. "DigitalPortTimeout " .
  72. "DigitalPin " .
  73. "DigitalPinTimeout ",
  74. "Dimmer" => "on:noArg off:noArg toggle:noArg statusRequest:noArg pct:slider,0,1,100 ani " .
  75. # Used from SetExtensions.pm
  76. "blink on-for-timer on-till off-for-timer off-till intervals",
  77. "EnvSensor" => "",
  78. "RGBDimmer" => "Color " .
  79. "ColorAnimation",
  80. "SoilMoistureMeter" => "",
  81. "Custom" => "Dimmer.Brightness " .
  82. "Dimmer.Animation"
  83. );
  84. # Supported get commands
  85. # use syntax from set commands
  86. my %gets = (
  87. "PowerSwitch" => "",
  88. "Dimmer" => "",
  89. "EnvSensor" => "din:all,1,2,3,4,5,6,7,8 ain:all,1,2,3,4,5 ain_volt:1,2,3,4,5",
  90. "RGBDimmer" => "",
  91. "Custom" => ""
  92. );
  93. sub SHCdev_Parse($$);
  94. #####################################
  95. sub SHCdev_Initialize($)
  96. {
  97. my ($hash) = @_;
  98. $hash->{Match} = "^Packet Data: SenderID=[1-9]|0[1-9]|[1-9][0-9]|[0-9][0-9][0-9]|[0-3][0-9][0-9][0-9]|40[0-8][0-9]|409[0-6]";
  99. $hash->{SetFn} = "SHCdev_Set";
  100. $hash->{GetFn} = "SHCdev_Get";
  101. $hash->{DefFn} = "SHCdev_Define";
  102. $hash->{UndefFn} = "SHCdev_Undef";
  103. $hash->{ParseFn} = "SHCdev_Parse";
  104. $hash->{AttrList} = "IODev"
  105. ." readonly:1"
  106. ." forceOn:1"
  107. ." $readingFnAttributes"
  108. ." devtype:EnvSensor,Dimmer,PowerSwitch,RGBDimmer,SoilMoistureMeter";
  109. }
  110. #####################################
  111. sub SHCdev_Define($$)
  112. {
  113. my ($hash, $def) = @_;
  114. my @a = split("[ \t][ \t]*", $def);
  115. if (@a < 3 || @a > 4) {
  116. my $msg = "wrong syntax: define <name> SHCdev <SenderID> [<AesKey>] ";
  117. Log3 undef, 2, $msg;
  118. return $msg;
  119. }
  120. # Correct SenderID for SHC devices is from 1 - 4096 (leading zeros allowed)
  121. $a[2] =~ m/^([1-9]|0[1-9]|[1-9][0-9]|[0-9][0-9][0-9]|[0-3][0-9][0-9][0-9]|40[0-8][0-9]|409[0-6])$/i;
  122. return "$a[2] is not a valid SHCdev SenderID" if (!defined($1));
  123. my $aeskey;
  124. if (@a == 3) {
  125. $aeskey = 0;
  126. } else {
  127. return "$a[3] is not a valid SHCdev AesKey" if ($a[3] lt 0 || $a[3] gt 15);
  128. $aeskey = $a[3];
  129. }
  130. my $name = $a[0];
  131. my $addr = $a[2];
  132. return "SHCdev device $addr already used for $modules{SHCdev}{defptr}{$addr}->{NAME}." if ($modules{SHCdev}{defptr}{$addr}
  133. && $modules{SHCdev}{defptr}{$addr}->{NAME} ne $name);
  134. $hash->{addr} = $addr;
  135. $hash->{aeskey} = $aeskey;
  136. $modules{SHCdev}{defptr}{$addr} = $hash;
  137. AssignIoPort($hash);
  138. if (defined($hash->{IODev}->{NAME})) {
  139. Log3 $name, 3, "$name: I/O device is " . $hash->{IODev}->{NAME};
  140. } else {
  141. Log3 $name, 1, "$name: no I/O device";
  142. }
  143. return undef;
  144. }
  145. #####################################
  146. sub SHCdev_Undef($$)
  147. {
  148. my ($hash, $arg) = @_;
  149. my $name = $hash->{NAME};
  150. my $addr = $hash->{addr};
  151. delete($modules{SHCdev}{defptr}{$addr});
  152. return undef;
  153. }
  154. #####################################
  155. sub SHCdev_Parse($$)
  156. {
  157. my ($hash, $msg) = @_;
  158. my $name = $hash->{NAME};
  159. if (!$parser->parse($msg)) {
  160. Log3 $hash, 4, "SHC_TEMP: parser error: $msg";
  161. return "";
  162. }
  163. my $msgtypename = $parser->getMessageTypeName();
  164. my $msggroupname = $parser->getMessageGroupName();
  165. my $msgname = $parser->getMessageName();
  166. my $raddr = $parser->getSenderID();
  167. my $rhash = $modules{SHCdev}{defptr}{$raddr};
  168. my $rname = $rhash ? $rhash->{NAME} : $raddr;
  169. if (!$modules{SHCdev}{defptr}{$raddr}) {
  170. Log3 $name, 3, "SHC_TEMP: Unknown device $rname, please define it";
  171. return "UNDEFINED SHCdev_$rname SHCdev $raddr";
  172. }
  173. if (($msgtypename ne "Status") && ($msgtypename ne "AckStatus")) {
  174. Log3 $name, 3, "$rname: Ignoring MessageType $msgtypename";
  175. return "";
  176. }
  177. Log3 $name, 4, "$rname: Msg: $msg";
  178. Log3 $name, 4, "$rname: MsgType: $msgtypename, MsgGroupName: $msggroupname, MsgName: $msgname";
  179. my @list;
  180. push(@list, $rname);
  181. $rhash->{SHCdev_lastRcv} = TimeNow();
  182. $rhash->{SHCdev_msgtype} = "$msggroupname : $msgname : $msgtypename";
  183. my $readonly = AttrVal($rname, "readonly", "0");
  184. readingsBeginUpdate($rhash);
  185. given ($msggroupname) {
  186. when ('Generic') {
  187. given ($msgname) {
  188. when ('Version') {
  189. my $major = $parser->getField("Major");
  190. my $minor = $parser->getField("Minor");
  191. my $patch = $parser->getField("Patch");
  192. my $vhash = $parser->getField("Hash");
  193. readingsBulkUpdate($rhash, "version", "$major.$minor.$patch-$vhash");
  194. }
  195. when ('DeviceInfo') {
  196. my $devtype = $parser->getField("DeviceType");
  197. my $major = $parser->getField("VersionMajor");
  198. my $minor = $parser->getField("VersionMinor");
  199. my $patch = $parser->getField("VersionPatch");
  200. my $vhash = $parser->getField("VersionHash");
  201. # Assign device type
  202. my $devtypeOld = AttrVal( $rname, "devtype", undef );
  203. if (!defined($devtypeOld)) {
  204. $attr{$rname}{devtype} = $devtype;
  205. Log3 $name, 3, "$rname: Assign device type = " . $attr{$rname}{devtype};
  206. }
  207. readingsBulkUpdate($rhash, "version", "$major.$minor.$patch-$vhash");
  208. }
  209. when ('BatteryStatus') {
  210. readingsBulkUpdate($rhash, "battery", $parser->getField("Percentage"));
  211. }
  212. }
  213. }
  214. when ('GPIO') {
  215. given ($msgname) {
  216. when ('DigitalPortTimeout') {
  217. my $pins = "";
  218. for (my $i = 0 ; $i < 8 ; $i++) {
  219. my $pinx = $parser->getField("On", $i);
  220. my $timeoutx = $parser->getField("TimeoutSec", $i);
  221. my $channel = $i + 1;
  222. if ($channel == 1)
  223. {
  224. readingsBulkUpdate($rhash, "on", $pinx);
  225. }
  226. readingsBulkUpdate($rhash, "pin" . $channel, $pinx);
  227. readingsBulkUpdate($rhash, "timeout" . $channel, $timeoutx);
  228. $pins .= $pinx;
  229. }
  230. readingsBulkUpdate($rhash, "port", $pins);
  231. }
  232. when ('DigitalPort') {
  233. my $pins = "";
  234. for (my $i = 0 ; $i < 8 ; $i++) {
  235. my $pinx = $parser->getField("On", $i);
  236. my $channel = $i + 1;
  237. if ($channel == 1)
  238. {
  239. readingsBulkUpdate($rhash, "on", $pinx);
  240. }
  241. readingsBulkUpdate($rhash, "pin" . $channel, $pinx);
  242. $pins .= $pinx;
  243. }
  244. readingsBulkUpdate($rhash, "port", $pins);
  245. }
  246. when ('AnalogPort') {
  247. my $pins = "";
  248. for (my $i = 0 ; $i < 5 ; $i++) {
  249. my $pinx_on = $parser->getField("On", $i);
  250. my $pinx_volt = $parser->getField("Voltage", $i);
  251. my $channel = $i + 1;
  252. readingsBulkUpdate($rhash, "ain" . $channel, $pinx_on);
  253. readingsBulkUpdate($rhash, "ain_volt" . $channel, $pinx_volt);
  254. $pins .= $pinx_on;
  255. }
  256. readingsBulkUpdate($rhash, "ains", $pins);
  257. }
  258. }
  259. }
  260. when ('Weather') {
  261. given ($msgname) {
  262. when ('Temperature') {
  263. my $tmp = $parser->getField("Temperature") / 100; # parser returns centigrade
  264. readingsBulkUpdate($rhash, "temperature", $tmp);
  265. }
  266. when ('HumidityTemperature') {
  267. my $hum = $parser->getField("Humidity") / 10; # parser returns 1/10 percent
  268. my $tmp = $parser->getField("Temperature") / 100; # parser returns centigrade
  269. readingsBulkUpdate($rhash, "humidity", $hum);
  270. readingsBulkUpdate($rhash, "temperature", $tmp);
  271. }
  272. when ('BarometricPressureTemperature') {
  273. my $bar = $parser->getField("BarometricPressure") / 100; # parser returns pascal, use hPa
  274. my $tmp = $parser->getField("Temperature") / 100; # parser returns centigrade
  275. readingsBulkUpdate($rhash, "barometric_pressure", $bar);
  276. readingsBulkUpdate($rhash, "temperature", $tmp);
  277. }
  278. when ('Humidity') {
  279. my $hum = $parser->getField("Humidity") / 10; # parser returns 1/10 percent
  280. readingsBulkUpdate($rhash, "humidity", $hum);
  281. }
  282. }
  283. }
  284. when ('Environment') {
  285. given ($msgname) {
  286. when ('Brightness') {
  287. my $brt = $parser->getField("Brightness");
  288. readingsBulkUpdate($rhash, "brightness", $brt);
  289. }
  290. when ('Distance') {
  291. my $brt = $parser->getField("Distance");
  292. readingsBulkUpdate($rhash, "distance", $brt);
  293. }
  294. }
  295. }
  296. when ('Dimmer') {
  297. given ($msgname) {
  298. when ('Brightness') {
  299. my $brightness = $parser->getField("Brightness");
  300. my $on = $brightness == 0 ? 0 : 1;
  301. readingsBulkUpdate($rhash, "on", $on);
  302. readingsBulkUpdate($rhash, "brightness", $brightness);
  303. }
  304. when ('Color') {
  305. my $color = $parser->getField("Color");
  306. readingsBulkUpdate($rhash, "color", $color);
  307. }
  308. when ('ColorAnimation') {
  309. my $repeat = $parser->getField("Repeat");
  310. my $autoreverse = $parser->getField("AutoReverse");
  311. readingsBulkUpdate($rhash, "repeat", $repeat);
  312. readingsBulkUpdate($rhash, "autoreverse", $autoreverse);
  313. for (my $i = 0 ; $i < 10 ; $i = $i + 1) {
  314. my $time = $parser->getField("Time" , $i);
  315. my $color = $parser->getField("Color", $i);
  316. readingsBulkUpdate($rhash, "time$i", $time);
  317. readingsBulkUpdate($rhash, "color$i", $color);
  318. }
  319. }
  320. }
  321. }
  322. }
  323. # If the devtype is defined add, if not already done, the according webCmds and devStateIcons
  324. my $devtype2 = AttrVal( $rname, "devtype", undef );
  325. if (defined($devtype2)) {
  326. if (!defined($attr{$rname}{devStateIcon}) && defined($dev_state_icons{$devtype2})) {
  327. $attr{$rname}{devStateIcon} = $dev_state_icons{$devtype2};
  328. }
  329. if (!defined($attr{$rname}{webCmd}) && defined($web_cmds{$devtype2})) {
  330. $attr{$rname}{webCmd} = $web_cmds{$devtype2};
  331. }
  332. }
  333. # Assemble state string according to %dev_state_format
  334. my $devtype3 = AttrVal( $rname, "devtype", undef );
  335. if (defined($devtype3) && defined($dev_state_format{$devtype3})) {
  336. my $state_format_arr = $dev_state_format{$devtype3};
  337. # Iterate over state_format array, if readings are available append it to the state string
  338. my $state_str = "";
  339. for (my $i = 0 ; $i < @$state_format_arr ; $i = $i + 2) {
  340. if ( defined($rhash->{READINGS}{$state_format_arr->[$i]})
  341. && defined($rhash->{READINGS}{$state_format_arr->[$i]}{VAL}))
  342. {
  343. my $val = $rhash->{READINGS}{$state_format_arr->[$i]}{VAL};
  344. if ($state_str ne "") {
  345. $state_str .= " ";
  346. }
  347. # "on" reading requires a special treatment because 0 translates to off, 1 translates to on
  348. if ($state_format_arr->[$i] eq "on") {
  349. $state_str .= $val == 0 ? "off" : "on";
  350. } else {
  351. $state_str .= $state_format_arr->[$i + 1] . $val;
  352. }
  353. }
  354. }
  355. readingsBulkUpdate($rhash, "state", $state_str);
  356. }
  357. readingsEndUpdate($rhash, 1); # Do triggers to update log file
  358. return @list;
  359. }
  360. #####################################
  361. sub SHCdev_Set($@)
  362. {
  363. my ($hash, $name, @aa) = @_;
  364. my $cnt = @aa;
  365. my $cmd = $aa[0];
  366. my $arg = $aa[1];
  367. my $arg2 = $aa[2];
  368. my $arg3 = $aa[3];
  369. my $arg4 = $aa[4];
  370. return "\"set $name\" needs at least one parameter" if ($cnt < 1);
  371. # Return list of device-specific set-commands.
  372. # This list is used to provide the set commands in the web interface
  373. my $devtype = AttrVal( $name, "devtype", undef );
  374. if ($cmd eq "?") {
  375. if (!defined($devtype)) {
  376. return;
  377. } else {
  378. return $sets{$devtype};
  379. }
  380. }
  381. if (!defined($devtype)) {
  382. return "devtype not yet specifed. Currently supported device types are " . join(", ", sort keys %sets);
  383. }
  384. if (!defined($sets{$devtype})) {
  385. return "No set commands for " . $devtype . "device type supported ";
  386. }
  387. # TODO:
  388. # Currently the commands for every device type are defined in %sets but not used to verify the commands. Instead
  389. # the SetExtension.pm modulesis used for this purpose.
  390. # For more sophisticated device types this has to be revisited
  391. my $readonly = AttrVal($name, "readonly", "0");
  392. given ($devtype) {
  393. when ('PowerSwitch') {
  394. # Timeout functionality for SHCdev is not implemented, because FHEMs internal notification system
  395. # is able to do this as well. Even more it supports intervals, off-for-timer, off-till ...
  396. if ($cmd eq 'toggle') {
  397. $cmd = ReadingsVal($name, "on", "0") eq "0" ? "on" : "off";
  398. }
  399. if (!$readonly && $cmd eq 'off') {
  400. readingsSingleUpdate($hash, "state", "set-$cmd", 1);
  401. $parser->initPacket("GPIO", "DigitalPin", "SetGet");
  402. $parser->setField("GPIO", "DigitalPin", "Pos", 0);
  403. $parser->setField("GPIO", "DigitalPin", "On", 0);
  404. SHCdev_Send($hash);
  405. } elsif (!$readonly && $cmd eq 'on') {
  406. readingsSingleUpdate($hash, "state", "set-$cmd", 1);
  407. $parser->initPacket("GPIO", "DigitalPin", "SetGet");
  408. $parser->setField("GPIO", "DigitalPin", "Pos", 0);
  409. $parser->setField("GPIO", "DigitalPin", "On", 1);
  410. SHCdev_Send($hash);
  411. } elsif ($cmd eq 'statusRequest') {
  412. $parser->initPacket("GPIO", "DigitalPin", "Get");
  413. SHCdev_Send($hash);
  414. } elsif ($cmd eq 'DigitalPort') {
  415. $parser->initPacket("GPIO", "DigitalPort", "SetGet");
  416. # if not enough (less than 8) pinbits are available use zero as default
  417. my $pinbits = $arg . "00000000";
  418. for (my $i = 0 ; $i < 8 ; $i = $i + 1) {
  419. $parser->setField("GPIO", "DigitalPort", "On", substr($pinbits, $i , 1), $i);
  420. }
  421. SHCdev_Send($hash);
  422. } elsif ($cmd eq 'DigitalPortTimeout') { # TODO implement correctly
  423. $parser->initPacket("GPIO", "DigitalPortTimeout", "SetGet");
  424. # if not enough (less than 8) pinbits are available use zero as default
  425. my $pinbits = $arg . "00000000";
  426. for (my $i = 0 ; $i < 8 ; $i = $i + 1) {
  427. my $pintimeout = "0"; # default value for timeout
  428. if (exists $aa[$i + 2]) {
  429. $pintimeout = $aa[$i + 2];
  430. }
  431. Log3 $name, 3, "$name: $i: Pin: " . substr($pinbits, $i , 1) . " Timeout: $pintimeout";
  432. $parser->setField("GPIO", "DigitalPortTimeout", "On", substr($pinbits, $i , 1), $i);
  433. $parser->setField("GPIO", "DigitalPortTimeout", "TimeoutSec", $pintimeout, $i);
  434. }
  435. SHCdev_Send($hash);
  436. } elsif ($cmd eq 'DigitalPin') {
  437. $parser->initPacket("GPIO", "DigitalPin", "SetGet");
  438. $parser->setField("GPIO", "DigitalPin", "Pos", $arg);
  439. $parser->setField("GPIO", "DigitalPin", "On", $arg2);
  440. SHCdev_Send($hash);
  441. } elsif ($cmd eq 'DigitalPinTimeout') {
  442. $parser->initPacket("GPIO", "DigitalPinTimeout", "SetGet");
  443. $parser->setField("GPIO", "DigitalPinTimeout", "Pos", $arg);
  444. $parser->setField("GPIO", "DigitalPinTimeout", "On", $arg2);
  445. $parser->setField("GPIO", "DigitalPinTimeout", "TimeoutSec", $arg3);
  446. SHCdev_Send($hash);
  447. } else {
  448. return SetExtensions($hash, "", $name, @aa);
  449. }
  450. }
  451. when ('Dimmer') {
  452. # Timeout functionality for SHCdev is not implemented, because FHEMs internal notification system
  453. # is able to do this as well. Even more it supports intervals, off-for-timer, off-till ...
  454. if ($cmd eq 'toggle') {
  455. $cmd = ReadingsVal($name, "state", "on") eq "off" ? "on" : "off";
  456. }
  457. if (!$readonly && $cmd eq 'off') {
  458. readingsSingleUpdate($hash, "state", "set-$cmd", 1);
  459. $parser->initPacket("Dimmer", "Brightness", "SetGet");
  460. $parser->setField("Dimmer", "Brightness", "Brightness", 0);
  461. SHCdev_Send($hash);
  462. } elsif (!$readonly && $cmd eq 'on') {
  463. readingsSingleUpdate($hash, "state", "set-$cmd", 1);
  464. $parser->initPacket("Dimmer", "Brightness", "SetGet");
  465. $parser->setField("Dimmer", "Brightness", "Brightness", 100);
  466. SHCdev_Send($hash);
  467. } elsif (!$readonly && $cmd eq 'pct') {
  468. my $brightness = $arg;
  469. # DEBUG
  470. # Log3 $name, 3, "$name: Args: $arg, $arg2, $arg3, $brightness";
  471. readingsSingleUpdate($hash, "state", "set-pct:$brightness", 1);
  472. $parser->initPacket("Dimmer", "Brightness", "SetGet");
  473. $parser->setField("Dimmer", "Brightness", "Brightness", $brightness);
  474. SHCdev_Send($hash);
  475. } elsif (!$readonly && $cmd eq 'ani') {
  476. #TODO Verify argument values
  477. my $brightness = $arg;
  478. # DEBUG
  479. # Log3 $name, 3, "$name: ani args: $arg, $arg2, $arg3, $arg4, $brightness";
  480. readingsSingleUpdate($hash, "state", "set-ani", 1);
  481. $parser->initPacket("Dimmer", "Animation", "SetGet");
  482. $parser->setField("Dimmer", "Animation", "AnimationMode", $arg);
  483. $parser->setField("Dimmer", "Animation", "TimeoutSec", $arg2);
  484. $parser->setField("Dimmer", "Animation", "StartBrightness", $arg3);
  485. $parser->setField("Dimmer", "Animation", "EndBrightness", $arg4);
  486. SHCdev_Send($hash);
  487. } elsif ($cmd eq 'statusRequest') {
  488. $parser->initPacket("Dimmer", "Brightness", "Get");
  489. SHCdev_Send($hash);
  490. } else {
  491. return SetExtensions($hash, "", $name, @aa);
  492. }
  493. }
  494. when ('RGBDimmer') {
  495. if ($cmd eq 'Color') {
  496. #TODO Verify argument values
  497. my $color = $arg;
  498. # DEBUG
  499. # Log3 $name, 3, "$name: Color args: $arg, $arg2, $arg3, $arg4";
  500. readingsSingleUpdate($hash, "state", "set-color:$color", 1);
  501. $parser->initPacket("Dimmer", "Color", "SetGet");
  502. $parser->setField("Dimmer", "Color", "Color", $color);
  503. SHCdev_Send($hash);
  504. } elsif ($cmd eq 'ColorAnimation') {
  505. #TODO Verify argument values
  506. $parser->initPacket("Dimmer", "ColorAnimation", "SetGet");
  507. $parser->setField("Dimmer", "ColorAnimation", "Repeat", $arg);
  508. $parser->setField("Dimmer", "ColorAnimation", "AutoReverse", $arg2);
  509. my $curtime = 0;
  510. my $curcolor = 0;
  511. # Iterate over all given command line parameters and set Time and Color
  512. # accordingly. Fill the remaining values with zero.
  513. for (my $i = 0 ; $i < 10 ; $i = $i + 1) {
  514. if (!defined($aa[($i * 2) + 3])) {
  515. $curtime = 0;
  516. } else {
  517. $curtime = $aa[($i * 2) + 3];
  518. }
  519. if (!defined($aa[($i * 2) + 4])) {
  520. $curcolor = 0;
  521. } else {
  522. $curcolor = $aa[($i * 2) + 4];
  523. }
  524. # DEBUG
  525. # Log3 $name, 3, "$name: Nr: $i Time: $curtime Color: $curcolor";
  526. $parser->setField("Dimmer", "ColorAnimation", "Time" , $curtime, $i);
  527. $parser->setField("Dimmer", "ColorAnimation", "Color", $curcolor, $i);
  528. }
  529. readingsSingleUpdate($hash, "state", "set-coloranimation", 1);
  530. SHCdev_Send($hash);
  531. } else {
  532. return SetExtensions($hash, "", $name, @aa);
  533. }
  534. }
  535. }
  536. return undef;
  537. }
  538. #####################################
  539. sub SHCdev_Get($@)
  540. {
  541. my ($hash, $name, @aa) = @_;
  542. my $cnt = @aa;
  543. my $cmd = $aa[0];
  544. my $arg = $aa[1];
  545. return "\"get $name\" needs at least one parameter" if ($cnt < 1);
  546. my $devtype = AttrVal( $name, "devtype", undef );
  547. if (!defined($devtype)) {
  548. return "\"devtype\" not yet specifed. Currently supported device types are " . join(", ", sort keys %sets);
  549. }
  550. if (!defined($gets{$devtype})) {
  551. return "No get commands for " . $devtype . " device type supported ";
  552. }
  553. given ($devtype) {
  554. when ('EnvSensor') {
  555. if ($cmd eq 'din') {
  556. if ($arg =~ /[1-8]/) {
  557. my $channel = "din" . $arg;
  558. if ( defined($hash->{READINGS}{$channel})
  559. && defined($hash->{READINGS}{$channel}{VAL}))
  560. {
  561. return "$name.$channel => " . $hash->{READINGS}{$channel}{VAL};
  562. }
  563. return "Error: \"input " . $channel . "\" readings not yet available or not supported by device";
  564. }
  565. elsif ($arg eq "all")
  566. {
  567. if ( defined($hash->{READINGS}{port})
  568. && defined($hash->{READINGS}{port}{VAL}))
  569. {
  570. return "$name.port => " . $hash->{READINGS}{port}{VAL};
  571. }
  572. return "Error: \"input all\" readings not yet available or not supported by device";
  573. }
  574. }
  575. if ($cmd eq 'ain') {
  576. if ($arg =~ /[1-5]/) {
  577. my $channel = "ain" . $arg;
  578. if ( defined($hash->{READINGS}{$channel})
  579. && defined($hash->{READINGS}{$channel}{VAL}))
  580. {
  581. return "$name.$channel => " . $hash->{READINGS}{$channel}{VAL};
  582. }
  583. return "Error: \"input " . $channel . "\" readings not yet available or not supported by device";
  584. }
  585. elsif ($arg eq "all")
  586. {
  587. if ( defined($hash->{READINGS}{ains})
  588. && defined($hash->{READINGS}{ains}{VAL}))
  589. {
  590. return "$name.ains => " . $hash->{READINGS}{ains}{VAL};
  591. }
  592. return "Error: \"input all\" readings not yet available or not supported by device";
  593. }
  594. }
  595. if ($cmd eq 'ain_volt') {
  596. if ($arg =~ /[1-5]/) {
  597. my $channel = "ain_volt" . $arg;
  598. if ( defined($hash->{READINGS}{$channel})
  599. && defined($hash->{READINGS}{$channel}{VAL}))
  600. {
  601. return "$name.$channel => " . $hash->{READINGS}{$channel}{VAL};
  602. }
  603. return "Error: \"input " . $channel . "\" readings not yet available or not supported by device";
  604. }
  605. }
  606. # This return is required to provide the get commands in the web interface
  607. return "Unknown argument $cmd, choose one of " . $gets{$devtype};
  608. }
  609. }
  610. return undef;
  611. }
  612. #####################################
  613. sub SHCdev_Send($)
  614. {
  615. my ($hash) = @_;
  616. my $name = $hash->{NAME};
  617. $hash->{SHCdev_lastSend} = TimeNow();
  618. my $msg = $parser->getSendString($hash->{addr}, $hash->{aeskey});
  619. Log3 $name, 3, "$name: Sending $msg";
  620. IOWrite($hash, $msg);
  621. }
  622. 1;
  623. =pod
  624. =begin html
  625. <a name="SHCdev"></a>
  626. <h3>SHCdev</h3>
  627. <ul>
  628. SHC is the device module that supports several device types available
  629. at <a href="http://www.smarthomatic.org">www.smarthomatic.org</a>.<br><br>
  630. These device are connected to the FHEM server through the SHC base station (<a href="#SHC">SHC</a>).<br><br>
  631. Currently supported are:<br>
  632. <ul>
  633. <li>EnvSensor</li>
  634. <li>PowerSwitch</li>
  635. <li>Dimmer</li>
  636. <li>RGBDimmer</li>
  637. <li>SoilMoistureMeter</li>
  638. </ul><br>
  639. <a name="SHCdev_Define"></a>
  640. <b>Define</b>
  641. <ul>
  642. <code>define &lt;name&gt; SHCdev &lt;SenderID&gt; [&lt;AesKey&gt;]</code><br>
  643. <br>
  644. &lt;SenderID&gt;<br>
  645. is a number ranging from 0 .. 4095 to identify the SHCdev device.<br><br>
  646. &lt;AesKey&gt;<br>
  647. is a optional number ranging from 0 .. 15 to select an encryption key.
  648. It is required for the basestation to communicate with remote devides
  649. The default value is 0.<br><br>
  650. Note: devices are autocreated on reception of the first message.<br>
  651. </ul>
  652. <br>
  653. <a name="SHCdev_Set"></a>
  654. <b>Set</b>
  655. <ul>
  656. <li>on<br>
  657. Supported by Dimmer and PowerSwitch (on always refers to pin1).
  658. </li><br>
  659. <li>off<br>
  660. Supported by Dimmer and PowerSwitch (off always refers to pin1).
  661. </li><br>
  662. <li>pct &lt;0..100&gt;<br>
  663. Sets the brightness in percent. Supported by Dimmer.
  664. </li><br>
  665. <li>ani &lt;AnimationMode&gt; &lt;TimeoutSec&gt; &lt;StartBrightness&gt; &lt;EndBrightness&gt;<br>
  666. Description and details available at <a href="http://www.smarthomatic.org/basics/message_catalog.html#Dimmer_Animation">www.smarthomatic.org</a>
  667. Supported by Dimmer.
  668. </li><br>
  669. <li>statusRequest<br>
  670. Supported by Dimmer and PowerSwitch.
  671. </li><br>
  672. <li>Color &lt;ColorNumber&gt;<br>
  673. A detailed description is available at <a href="http://www.smarthomatic.org/basics/message_catalog.html#Dimmer_Color">www.smarthomatic.org</a>
  674. The color palette can be found <a href="http://www.smarthomatic.org/devices/rgb_dimmer.html">here</a>
  675. Supported by RGBDimmer.
  676. </li><br>
  677. <li>ColorAnimation &lt;Repeat&gt; &lt;AutoReverse&gt; &lt;Time0&gt; &lt;ColorNumber0&gt; &lt;Time1&gt; &lt;ColorNumber1&gt; ... up to 10 time/color pairs<br>
  678. A detailed description is available at <a href="http://www.smarthomatic.org/basics/message_catalog.html#Dimmer_ColorAnimation">www.smarthomatic.org</a>
  679. The color palette can be found <a href="http://www.smarthomatic.org/devices/rgb_dimmer.html">here</a>
  680. Supported by RGBDimmer.
  681. </li><br>
  682. <li>DigitalPin &lt;Pos&gt; &lt;On&gt;<br>
  683. A detailed description is available at <a href="http://www.smarthomatic.org/basics/message_catalog.html#GPIO_DigitalPin">www.smarthomatic.org</a>
  684. Supported by PowerSwitch.
  685. </li><br>
  686. <li>DigitalPinTimeout &lt;Pos&gt; &lt;On&gt; &lt;Timeout&gt;<br>
  687. A detailed description is available at <a href="http://www.smarthomatic.org/basics/message_catalog.html#GPIO_DigitalPinTimeout">www.smarthomatic.org</a>
  688. Supported by PowerSwitch.
  689. </li><br>
  690. <li>DigitalPort &lt;On&gt;<br>
  691. &lt;On&gt;<br>
  692. is a bit array (0 or 1) describing the port state. If less than eight bits were provided zero is assumed.
  693. Example: set SHC_device DigitalPort 10110000 will set pin0, pin2 and pin3 to 1.<br>
  694. A detailed description is available at <a href="http://www.smarthomatic.org/basics/message_catalog.html#GPIO_DigitalPort">www.smarthomatic.org</a>
  695. Supported by PowerSwitch.
  696. </li><br>
  697. <li>DigitalPortTimeout &lt;On&gt; &lt;Timeout0&gt; .. &lt;Timeout7&gt;<br>
  698. &lt;On&gt;<br>
  699. is a bit array (0 or 1) describing the port state. If less than eight bits were provided zero is assumed.
  700. Example: set SHC_device DigitalPort 10110000 will set pin0, pin2 and pin3 to 1.<br>
  701. &lt;Timeout0&gt; .. &lt;Timeout7&gt;<br>
  702. are the timeouts for each pin. If no timeout is provided zero is assumed.
  703. A detailed description is available at <a href="http://www.smarthomatic.org/basics/message_catalog.html#GPIO_DigitalPortTimeout">www.smarthomatic.org</a>
  704. Supported by PowerSwitch.
  705. </li><br>
  706. <li><a href="#setExtensions"> set extensions</a><br>
  707. Supported by Dimmer and PowerSwitch.</li>
  708. </ul><br>
  709. <a name="SHCdev_Get"></a>
  710. <b>Get</b>
  711. <ul>
  712. <li>din &lt;pin&gt;<br>
  713. Returns the state of the specified digital input pin for pin = 1..8. Or the state of all pins for pin = all.
  714. Supported by EnvSensor.
  715. </li><br>
  716. <li>ain &lt;pin&gt;<br>
  717. Returns the state of the specified analog input pin for pin = 1..5. Or the state of all pins for pin = all.
  718. If the voltage of the pin is over the specied trigger threshold) it return 1 otherwise 0.
  719. Supported by EnvSensor.
  720. </li><br>
  721. <li>ain &lt;pin&gt;<br>
  722. Returns the state of the specified analog input pin for pin = 1..5. Or the state of all pins for pin = all.
  723. If the voltage of the pin is over the specied trigger threshold) it return 1 otherwise 0.
  724. Supported by EnvSensor.
  725. </li><br>
  726. <li>ain_volt &lt;pin&gt;<br>
  727. Returns the voltage of the specified analog input pin for pin = 1..5 in millivolts, ranging from 0 .. 1100 mV.
  728. Supported by EnvSensor.
  729. </li><br>
  730. </ul><br>
  731. <a name="SHCdev_Attr"></a>
  732. <b>Attributes</b>
  733. <ul>
  734. <li>devtype<br>
  735. The device type determines the command set, default web commands and the
  736. default devStateicon. Currently supported are: EnvSensor, Dimmer, PowerSwitch, RGBDimmer, SoilMoistureMeter.<br><br>
  737. Note: If the device is not set manually, it will be determined automatically
  738. on reception of a device type specific message. For example: If a
  739. temperature message is received, the device type will be set to EnvSensor.
  740. </li><br>
  741. <li>readonly<br>
  742. if set to a value != 0 all switching commands (on, off, toggle, ...) will be disabled.
  743. </li><br>
  744. <li>forceOn<br>
  745. try to switch on the device whenever an off status is received.
  746. </li><br>
  747. </ul><br>
  748. </ul>
  749. =end html
  750. =cut