88_HMCCUCHN.pm 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. ################################################################
  2. #
  3. # 88_HMCCUCHN.pm
  4. #
  5. # $Id: 88_HMCCUCHN.pm 13427 2017-02-17 16:30:12Z zap $
  6. #
  7. # Version 3.9.003
  8. #
  9. # (c) 2016 zap (zap01 <at> t-online <dot> de)
  10. #
  11. ################################################################
  12. #
  13. # define <name> HMCCUCHN <ccudev> [readonly] [defaults]
  14. # [iodev=<iodevname>]
  15. #
  16. # set <name> config [device] <parameter>=<value> [...]
  17. # set <name> control <value>
  18. # set <name> datapoint <datapoint> <value>
  19. # set <name> defaults
  20. # set <name> devstate <value>
  21. # set <name> <stateval_cmds>
  22. # set <name> on-till <timestamp>
  23. # set <name> on-for-timer <ontime>
  24. # set <name> pct <level> [{ <ontime> | 0 } [<ramptime>]]
  25. # set <name> toggle
  26. #
  27. # get <name> config [device] [<filter-expr>]
  28. # get <name> configdesc [device]
  29. # get <name> configlist [device] [<filtet-expr>]
  30. # get <name> datapoint <datapoint>
  31. # get <name> defaults
  32. # get <name> deviceinfo
  33. # get <name> devstate
  34. # get <name> update
  35. #
  36. # attr <name> ccuackstate { 0 | 1 }
  37. # attr <name> ccucalculate <value>:<reading>[:<dp-list>][...]
  38. # attr <name> ccuflags { altread, nochn0, trace }
  39. # attr <name> ccuget { State | Value }
  40. # attr <name> ccureadings { 0 | 1 }
  41. # attr <name> ccureadingfilter <datapoint-expr>
  42. # attr <name> ccureadingformat { name[lc] | address[lc] | datapoint[lc] }
  43. # attr <name> ccureadingname <oldname>:<newname>[;...]
  44. # attr <name> ccuverify { 0 | 1 | 2 }
  45. # attr <name> controldatapoint <datapoint>
  46. # attr <name> disable { 0 | 1 }
  47. # attr <name> hmstatevals <subst-rule>[;...]
  48. # attr <name> statedatapoint <datapoint>
  49. # attr <name> statevals <text1>:<subtext1>[,...]
  50. # attr <name> substexcl <reading-expr>
  51. # attr <name> substitute <subst-rule>[;...]
  52. #
  53. ################################################################
  54. # Requires module 88_HMCCU.pm
  55. ################################################################
  56. package main;
  57. use strict;
  58. use warnings;
  59. use SetExtensions;
  60. # use Time::HiRes qw( gettimeofday usleep );
  61. sub HMCCUCHN_Define ($@);
  62. sub HMCCUCHN_Set ($@);
  63. sub HMCCUCHN_Get ($@);
  64. sub HMCCUCHN_Attr ($@);
  65. sub HMCCUCHN_SetError ($$);
  66. ##################################################
  67. # Initialize module
  68. ##################################################
  69. sub HMCCUCHN_Initialize ($)
  70. {
  71. my ($hash) = @_;
  72. $hash->{DefFn} = "HMCCUCHN_Define";
  73. $hash->{SetFn} = "HMCCUCHN_Set";
  74. $hash->{GetFn} = "HMCCUCHN_Get";
  75. $hash->{AttrFn} = "HMCCUCHN_Attr";
  76. $hash->{parseParams} = 1;
  77. $hash->{AttrList} = "IODev ccuackstate:0,1 ccucalculate ccuflags:multiple-strict,altread,nochn0,trace ccureadingfilter ccureadingformat:name,namelc,address,addresslc,datapoint,datapointlc ccureadingname ccureadings:0,1 ccuscaleval ccuverify:0,1,2 ccuget:State,Value controldatapoint disable:0,1 hmstatevals:textField-long statedatapoint statevals substitute:textField-long substexcl stripnumber ". $readingFnAttributes;
  78. }
  79. ##################################################
  80. # Define device
  81. ##################################################
  82. sub HMCCUCHN_Define ($@)
  83. {
  84. my ($hash, $a, $h) = @_;
  85. my $name = $hash->{NAME};
  86. my $usage = "Usage: define $name HMCCUCHN {device} ['readonly'] ['defaults'] [iodev={iodevname}]";
  87. return $usage if (@$a < 3);
  88. my $devname = shift @$a;
  89. my $devtype = shift @$a;
  90. my $devspec = shift @$a;
  91. my $hmccu_hash = undef;
  92. # IO device can be set by command line parameter iodev
  93. if (exists ($h->{iodev})) {
  94. $hmccu_hash = $defs{$h->{iodev}} if (exists ($defs{$h->{iodev}}));
  95. }
  96. $hmccu_hash = HMCCU_FindIODevice ($devspec) if (!defined ($hmccu_hash));
  97. return "Cannot detect IO device" if (!defined ($hmccu_hash));
  98. return "Invalid or unknown CCU channel name or address"
  99. if (! HMCCU_IsValidChannel ($hmccu_hash, $devspec));
  100. my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec);
  101. return "Invalid or unknown CCU device name or address" if (!defined ($da));
  102. $hash->{ccuif} = $di;
  103. $hash->{ccuaddr} = $da;
  104. $hash->{ccuname} = $dn;
  105. $hash->{ccutype} = $dt;
  106. $hash->{channels} = 1;
  107. $hash->{statevals} = 'devstate';
  108. # Parse optional command line parameters
  109. my $n = 0;
  110. my $arg = shift @$a;
  111. while (defined ($arg)) {
  112. return $usage if ($n == 3);
  113. if ($arg eq 'readonly') {
  114. $hash->{statevals} = $arg;
  115. }
  116. elsif ($arg eq 'defaults') {
  117. HMCCU_SetDefaults ($hash);
  118. }
  119. else {
  120. return $usage;
  121. }
  122. $n++;
  123. $arg = shift @$a;
  124. }
  125. # Inform HMCCU device about client device
  126. AssignIoPort ($hash, $hmccu_hash->{NAME});
  127. readingsSingleUpdate ($hash, "state", "Initialized", 1);
  128. $hash->{ccudevstate} = 'Active';
  129. return undef;
  130. }
  131. #####################################
  132. # Set attribute
  133. #####################################
  134. sub HMCCUCHN_Attr ($@)
  135. {
  136. my ($cmd, $name, $attrname, $attrval) = @_;
  137. my $hash = $defs{$name};
  138. if ($cmd eq "set") {
  139. return "Missing attribute value" if (!defined ($attrval));
  140. if ($attrname eq 'IODev') {
  141. $hash->{IODev} = $defs{$attrval};
  142. }
  143. elsif ($attrname eq 'statevals') {
  144. return "Device is read only" if ($hash->{statevals} eq 'readonly');
  145. $hash->{statevals} = "devstate";
  146. my @states = split /,/,$attrval;
  147. foreach my $st (@states) {
  148. my @statesubs = split /:/,$st;
  149. return "value := text:substext[,...]" if (@statesubs != 2);
  150. $hash->{statevals} .= '|'.$statesubs[0];
  151. }
  152. }
  153. }
  154. elsif ($cmd eq "del") {
  155. if ($attrname eq 'statevals') {
  156. $hash->{statevals} = "devstate";
  157. }
  158. }
  159. return undef;
  160. }
  161. #####################################
  162. # Set commands
  163. #####################################
  164. sub HMCCUCHN_Set ($@)
  165. {
  166. my ($hash, $a, $h) = @_;
  167. my $name = shift @$a;
  168. my $opt = shift @$a;
  169. my $rocmds = "clear config defaults:noArg";
  170. return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
  171. return undef if ($hash->{statevals} eq 'readonly' && $opt ne '?' &&
  172. $opt !~ /^(clear|config|defaults)$/);
  173. my $disable = AttrVal ($name, "disable", 0);
  174. return undef if ($disable == 1);
  175. my $hmccu_hash = $hash->{IODev};
  176. if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) {
  177. return undef if ($opt eq '?');
  178. return "HMCCUCHN: CCU busy";
  179. }
  180. my $ccutype = $hash->{ccutype};
  181. my $ccuaddr = $hash->{ccuaddr};
  182. my $ccuif = $hash->{ccuif};
  183. my $statevals = AttrVal ($name, "statevals", '');
  184. my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', '');
  185. my $result = '';
  186. my $rc;
  187. if ($opt eq 'datapoint') {
  188. my $objname = shift @$a;
  189. my $objvalue = shift @$a;
  190. return HMCCU_SetError ($hash, "Usage: set $name datapoint {datapoint} {value}")
  191. if (!defined ($objname) || !defined ($objvalue));
  192. return HMCCU_SetError ($hash, -8)
  193. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $objname, 2));
  194. $objvalue =~ s/\\_/%20/g;
  195. $objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, undef, '');
  196. $objname = $ccuif.'.'.$ccuaddr.'.'.$objname;
  197. $rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
  198. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  199. HMCCU_SetState ($hash, "OK");
  200. return undef;
  201. }
  202. elsif ($opt eq 'control') {
  203. return HMCCU_SetError ($hash, -14) if ($cd eq '');
  204. return HMCCU_SetError ($hash, -8) if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, $cd, 2));
  205. my $objvalue = shift @$a;
  206. return HMCCU_SetError ($hash, "Usage: set $name control {value}") if (!defined ($objvalue));
  207. $objvalue =~ s/\\_/%20/g;
  208. $objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, undef, '');
  209. my $objname = $ccuif.'.'.$ccuaddr.'.'.$cd;
  210. $rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
  211. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  212. HMCCU_SetState ($hash, "OK");
  213. return undef;
  214. }
  215. elsif ($opt =~ /^($hash->{statevals})$/) {
  216. my $cmd = $1;
  217. my $objvalue = ($cmd ne 'devstate') ? $cmd : shift @$a;
  218. return HMCCU_SetError ($hash, -13) if ($sd eq '');
  219. return HMCCU_SetError ($hash, -8)
  220. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $sd, 2));
  221. return HMCCU_SetError ($hash, "Usage: set $name devstate {value}") if (!defined ($objvalue));
  222. $objvalue =~ s/\\_/%20/g;
  223. $objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, undef, '');
  224. my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd;
  225. $rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
  226. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  227. HMCCU_SetState ($hash, "OK");
  228. return undef;
  229. }
  230. elsif ($opt eq 'toggle') {
  231. return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals}));
  232. return HMCCU_SetError ($hash, -13) if ($sd eq '');
  233. return HMCCU_SetError ($hash, -8)
  234. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $sd, 2));
  235. my $tstates = $hash->{statevals};
  236. $tstates =~ s/devstate\|//;
  237. my @states = split /\|/, $tstates;
  238. my $stc = scalar (@states);
  239. my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd;
  240. ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
  241. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  242. my $objvalue = '';
  243. my $st = 0;
  244. while ($st < $stc) {
  245. if ($states[$st] eq $result) {
  246. $objvalue = ($st == $stc-1) ? $states[0] : $states[$st+1];
  247. last;
  248. }
  249. else {
  250. $st++;
  251. }
  252. }
  253. return HMCCU_SetError ($hash, "Current device state doesn't match statevals")
  254. if ($objvalue eq '');
  255. $objvalue = HMCCU_Substitute ($objvalue, $statevals, 1, undef, '');
  256. $rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
  257. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  258. HMCCU_SetState ($hash, "OK");
  259. return undef;
  260. }
  261. elsif ($opt eq 'pct') {
  262. return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype")
  263. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "LEVEL", 2));
  264. my $objname = '';
  265. my $objvalue = shift @$a;
  266. return HMCCU_SetError ($hash, "Usage: set $name pct {value} [{ontime} [{ramptime}]]")
  267. if (!defined ($objvalue));
  268. my $timespec = shift @$a;
  269. my $ramptime = shift @$a;
  270. # Set on time
  271. if (defined ($timespec)) {
  272. return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type $ccutype")
  273. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "ON_TIME", 2));
  274. if ($timespec =~ /^[0-9]{2}:[0-9]{2}/) {
  275. my (undef, $h, $m, $s) = GetTimeSpec ($timespec);
  276. return HMCCU_SetError ($hash, "Wrong time format. Use HH:MM or HH:MM:SS")
  277. if (!defined ($h));
  278. $s += $h*3600+$m*60;
  279. my @lt = localtime;
  280. my $cs = $lt[2]*3600+$lt[1]*60+$lt[0];
  281. $s += 86400 if ($cs > $s);
  282. $timespec = $s-$cs;
  283. }
  284. if ($timespec > 0) {
  285. $objname = $ccuif.'.'.$ccuaddr.'.ON_TIME';
  286. $rc = HMCCU_SetDatapoint ($hash, $objname, $timespec);
  287. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  288. }
  289. }
  290. # Set ramp time
  291. if (defined ($ramptime)) {
  292. return HMCCU_SetError ($hash, "Can't find RAMP_TIME datapoint for device type $ccutype")
  293. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "RAMP_TIME", 2));
  294. $objname = $ccuif.'.'.$ccuaddr.'.RAMP_TIME';
  295. $rc = HMCCU_SetDatapoint ($hash, $objname, $ramptime);
  296. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  297. }
  298. # Set level
  299. $objname = $ccuif.'.'.$ccuaddr.'.LEVEL';
  300. $rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
  301. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  302. HMCCU_SetState ($hash, "OK");
  303. return undef;
  304. }
  305. elsif ($opt eq 'on-for-timer' || $opt eq 'on-till') {
  306. return HMCCU_SetError ($hash, -15) if ($statevals eq '' || !exists($hash->{statevals}));
  307. return HMCCU_SetError ($hash, "No state value for 'on' defined")
  308. if ("on" !~ /($hash->{statevals})/);
  309. return HMCCU_SetError ($hash, -13) if ($sd eq '');
  310. return HMCCU_SetError ($hash, -8)
  311. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $sd, 2));
  312. return HMCCU_SetError ($hash, "Can't find ON_TIME datapoint for device type")
  313. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, "ON_TIME", 2));
  314. my $timespec = shift @$a;
  315. return HMCCU_SetError ($hash, "Usage: set $name $opt {ontime-spec}")
  316. if (!defined ($timespec));
  317. if ($opt eq 'on-till') {
  318. my (undef, $h, $m, $s) = GetTimeSpec ($timespec);
  319. return HMCCU_SetError ($hash, "Wrong time format. Use HH:MM or HH:MM:SS")
  320. if (!defined ($h));
  321. $s += $h*3600+$m*60;
  322. my @lt = localtime;
  323. my $cs = $lt[2]*3600+$lt[1]*60+$lt[0];
  324. $s += 86400 if ($cs > $s);
  325. $timespec = $s-$cs;
  326. }
  327. # Set time
  328. my $objname = $ccuif.'.'.$ccuaddr.'.ON_TIME';
  329. $rc = HMCCU_SetDatapoint ($hash, $objname, $timespec);
  330. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  331. # Set state
  332. $objname = $ccuif.'.'.$ccuaddr.'.'.$sd;
  333. my $objvalue = HMCCU_Substitute ("on", $statevals, 1, undef, '');
  334. $rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
  335. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  336. HMCCU_SetState ($hash, "OK");
  337. return undef;
  338. }
  339. elsif ($opt eq 'clear') {
  340. my $rnexp = shift @$a;
  341. $rnexp = '.*' if (!defined ($rnexp));
  342. my @readlist = keys %{$hash->{READINGS}};
  343. foreach my $rd (@readlist) {
  344. delete ($hash->{READINGS}{$rd}) if ($rd ne 'state' && $rd ne 'control' && $rd =~ /$rnexp/);
  345. }
  346. }
  347. elsif ($opt eq 'config') {
  348. return HMCCU_SetError ($hash, "Usage: set $name config [device] {parameter}={value} [...]")
  349. if ((scalar keys %{$h}) < 1);
  350. my $ccuobj = $ccuaddr;
  351. my $par = shift @$a;
  352. if (defined ($par) && $par eq 'device') {
  353. ($ccuobj, undef) = HMCCU_SplitChnAddr ($ccuaddr);
  354. }
  355. my $rc = HMCCU_RPCSetConfig ($hash, $ccuobj, $h);
  356. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  357. HMCCU_SetState ($hash, "OK");
  358. return undef;
  359. }
  360. elsif ($opt eq 'defaults') {
  361. my $rc = HMCCU_SetDefaults ($hash);
  362. return HMCCU_SetError ($hash, "HMCCU: No default attributes found") if ($rc == 0);
  363. HMCCU_SetState ($hash, "OK");
  364. return undef;
  365. }
  366. else {
  367. return "HMCCUCHN: Unknown argument $opt, choose one of ".$rocmds
  368. if ($hash->{statevals} eq 'readonly');
  369. my $retmsg = "HMCCUCHN: Unknown argument $opt, choose one of clear config control datapoint defaults:noArg devstate";
  370. if ($hash->{statevals} ne '') {
  371. my @cmdlist = split /\|/,$hash->{statevals};
  372. shift @cmdlist;
  373. $retmsg .= ':'.join(',',@cmdlist) if (@cmdlist > 0);
  374. foreach my $sv (@cmdlist) {
  375. $retmsg .= ' '.$sv.':noArg';
  376. }
  377. $retmsg .= " toggle:noArg";
  378. $retmsg .= " on-for-timer on-till"
  379. if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $ccuaddr, "ON_TIME", 2));
  380. $retmsg .= " pct"
  381. if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $ccuaddr, "LEVEL", 2));
  382. }
  383. return $retmsg;
  384. }
  385. }
  386. #####################################
  387. # Get commands
  388. #####################################
  389. sub HMCCUCHN_Get ($@)
  390. {
  391. my ($hash, $a, $h) = @_;
  392. my $name = shift @$a;
  393. my $opt = shift @$a;
  394. return HMCCU_SetError ($hash, -3) if (!defined ($hash->{IODev}));
  395. my $disable = AttrVal ($name, "disable", 0);
  396. return undef if ($disable == 1);
  397. my $hmccu_hash = $hash->{IODev};
  398. if (HMCCU_IsRPCStateBlocking ($hmccu_hash)) {
  399. return undef if ($opt eq '?');
  400. return "HMCCUCHN: CCU busy";
  401. }
  402. my $ccutype = $hash->{ccutype};
  403. my $ccuaddr = $hash->{ccuaddr};
  404. my $ccuif = $hash->{ccuif};
  405. my ($sc, $sd, $cc, $cd) = HMCCU_GetSpecialDatapoints ($hash, '', 'STATE', '', '');
  406. my $ccureadings = AttrVal ($name, "ccureadings", 1);
  407. my $result = '';
  408. my $rc;
  409. if ($opt eq 'devstate') {
  410. return HMCCU_SetError ($hash, -13) if ($sd eq '');
  411. return HMCCU_SetError ($hash, -8)
  412. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $sd, 1));
  413. my $objname = $ccuif.'.'.$ccuaddr.'.'.$sd;
  414. ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
  415. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  416. return $ccureadings ? undef : $result;
  417. }
  418. elsif ($opt eq 'datapoint') {
  419. my $objname = shift @$a;
  420. return HMCCU_SetError ($hash, "Usage: get $name datapoint {datapoint}")
  421. if (!defined ($objname));
  422. return HMCCU_SetError ($hash, -8)
  423. if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $ccuaddr, $objname, 1));
  424. $objname = $ccuif.'.'.$ccuaddr.'.'.$objname;
  425. ($rc, $result) = HMCCU_GetDatapoint ($hash, $objname);
  426. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  427. return $ccureadings ? undef : $result;
  428. }
  429. elsif ($opt eq 'update') {
  430. my $ccuget = shift @$a;
  431. $ccuget = 'Attr' if (!defined ($ccuget));
  432. if ($ccuget !~ /^(Attr|State|Value)$/) {
  433. return HMCCU_SetError ($hash, "Usage: get $name update [{'State'|'Value'}]");
  434. }
  435. $rc = HMCCU_GetUpdate ($hash, $ccuaddr, $ccuget);
  436. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  437. return undef;
  438. }
  439. elsif ($opt eq 'deviceinfo') {
  440. my $ccuget = shift @$a;
  441. $ccuget = 'Attr' if (!defined ($ccuget));
  442. if ($ccuget !~ /^(Attr|State|Value)$/) {
  443. return HMCCU_SetError ($hash, "Usage: get $name deviceinfo [{'State'|'Value'}]");
  444. }
  445. my ($a, $c) = split(":", $hash->{ccuaddr});
  446. $result = HMCCU_GetDeviceInfo ($hash, $a, $ccuget);
  447. return HMCCU_SetError ($hash, -2) if ($result eq '');
  448. return HMCCU_FormatDeviceInfo ($result);
  449. }
  450. elsif ($opt eq 'config') {
  451. my $ccuobj = $ccuaddr;
  452. my $par = shift @$a;
  453. if (defined ($par)) {
  454. if ($par eq 'device') {
  455. ($ccuobj, undef) = HMCCU_SplitChnAddr ($ccuaddr);
  456. $par = shift @$a;
  457. }
  458. }
  459. $par = '.*' if (!defined ($par));
  460. my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamset", $par);
  461. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  462. return $ccureadings ? undef : $res;
  463. }
  464. elsif ($opt eq 'configlist') {
  465. my $ccuobj = $ccuaddr;
  466. my $par = shift @$a;
  467. if (defined ($par)) {
  468. if ($par eq 'device') {
  469. ($ccuobj, undef) = HMCCU_SplitChnAddr ($ccuaddr);
  470. $par = shift @$a;
  471. }
  472. }
  473. $par = '.*' if (!defined ($par));
  474. my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "listParamset", $par);
  475. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  476. return $res;
  477. }
  478. elsif ($opt eq 'configdesc') {
  479. my $ccuobj = $ccuaddr;
  480. my $par = shift @$a;
  481. if (defined ($par) && $par eq 'device') {
  482. ($ccuobj, undef) = HMCCU_SplitChnAddr ($ccuaddr);
  483. }
  484. my ($rc, $res) = HMCCU_RPCGetConfig ($hash, $ccuobj, "getParamsetDescription", undef);
  485. return HMCCU_SetError ($hash, $rc) if ($rc < 0);
  486. return $res;
  487. }
  488. elsif ($opt eq 'defaults') {
  489. $result = HMCCU_GetDefaults ($hash, 0);
  490. return $result;
  491. }
  492. else {
  493. my $retmsg = "HMCCUCHN: Unknown argument $opt, choose one of devstate:noArg defaults:noArg datapoint";
  494. my ($a, $c) = split(":", $hash->{ccuaddr});
  495. my @valuelist;
  496. my $valuecount = HMCCU_GetValidDatapoints ($hash, $hash->{ccutype}, $c, 1, \@valuelist);
  497. $retmsg .= ":".join(",",@valuelist) if ($valuecount > 0);
  498. $retmsg .= " update:noArg deviceinfo config configlist configdesc:noArg";
  499. return $retmsg;
  500. }
  501. }
  502. #####################################
  503. # Set error status
  504. #####################################
  505. sub HMCCUCHN_SetError ($$)
  506. {
  507. my ($hash, $text) = @_;
  508. my $name = $hash->{NAME};
  509. my $msg;
  510. my %errlist = (
  511. -1 => 'Channel name or address invalid',
  512. -2 => 'Execution of CCU script failed',
  513. -3 => 'Cannot detect IO device',
  514. -4 => 'Device deleted in CCU',
  515. -5 => 'No response from CCU',
  516. -6 => 'Update of readings disabled. Set attribute ccureadings first'
  517. );
  518. if (exists ($errlist{$text})) {
  519. $msg = $errlist{$text};
  520. }
  521. else {
  522. $msg = $text;
  523. }
  524. $msg = "HMCCUCHN: ".$name." ". $msg;
  525. readingsSingleUpdate ($hash, "state", "Error", 1);
  526. Log3 $name, 1, $msg;
  527. return $msg;
  528. }
  529. 1;
  530. =pod
  531. =item device
  532. =item summary controls HMCCU client devices for Homematic CCU2 - FHEM integration
  533. =begin html
  534. <a name="HMCCUCHN"></a>
  535. <h3>HMCCUCHN</h3>
  536. <ul>
  537. The module implements Homematic CCU channels as client devices for HMCCU. A HMCCU I/O device must
  538. exist before a client device can be defined. If a CCU channel is not found execute command
  539. 'get devicelist' in I/O device.
  540. </br></br>
  541. <a name="HMCCUCHNdefine"></a>
  542. <b>Define</b><br/><br/>
  543. <ul>
  544. <code>define &lt;name&gt; HMCCUCHN {&lt;channel-name&gt; | &lt;channel-address&gt;}
  545. [readonly] [defaults] [iodev=&lt;iodev-name&gt;]</code>
  546. <br/><br/>
  547. If option 'readonly' is specified no set command will be available. With option 'defaults'
  548. some default attributes depending on CCU device type will be set. Default attributes are only
  549. available for some device types.<br/>
  550. The define command accepts a CCU2 channel name or channel address as parameter.
  551. <br/><br/>
  552. Examples:<br/>
  553. <code>define window_living HMCCUCHN WIN-LIV-1 readonly</code><br/>
  554. <code>define temp_control HMCCUCHN BidCos-RF.LEQ1234567:1</code>
  555. <br/><br/>
  556. The interface part of a channel address must not be specified. The default is 'BidCos-RF'.
  557. Channel addresses can be found with command 'get deviceinfo &lt;devicename&gt;' executed
  558. in I/O device.
  559. </ul>
  560. <br/>
  561. <a name="HMCCUCHNset"></a>
  562. <b>Set</b><br/><br/>
  563. <ul>
  564. <li><b>set &lt;name&gt; clear [&lt;reading-exp&gt;]</b><br/>
  565. Delete readings matching specified reading name expression. Default expression is '.*'.
  566. Readings 'state' and 'control' are not deleted.
  567. </li><br/>
  568. <li><b>set &lt;name&gt; config [device] [&lt;rpcport&gt;] &lt;parameter&gt;=&lt;value&gt;]
  569. [...]</b><br/>
  570. Set config parameters of CCU channel. This is equal to setting device parameters in CCU.
  571. Valid parameters can be listed by using commands 'get configdesc' or 'get configlist'.
  572. With option 'device' specified parameters are set in device instead of channel.
  573. </li><br/>
  574. <li><b>set &lt;name&gt; datapoint &lt;datapoint&gt; &lt;value&gt;</b><br/>
  575. Set value of a datapoint of a CCU channel. If parameter <i>value</i> contains special
  576. character \_ it's substituted by blank.
  577. <br/><br/>
  578. Examples:<br/>
  579. <code>set temp_control datapoint SET_TEMPERATURE 21</code>
  580. </li><br/>
  581. <li><b>set &lt;name&gt; defaults</b><br/>
  582. Set default attributes for CCU device type. Default attributes are only available for
  583. some device types and for some channels of a device type.
  584. </li><br/>
  585. <li><b>set &lt;name&gt; devstate &lt;value&gt;</b><br/>
  586. Set state of a CCU device channel. The state datapoint of a channel must be defined
  587. by setting attribute 'statedatapoint' to a valid datapoint name.
  588. <br/><br/>
  589. Example:<br/>
  590. <code>set light_entrance devstate true</code>
  591. </li><br/>
  592. <li><b>set &lt;name&gt; &lt;statevalue&gt;</b><br/>
  593. Set state of a CCU device channel to <i>StateValue</i>. The state datapoint of a channel
  594. must be defined by setting attribute 'statedatapoint'. The available state values must
  595. be defined by setting attribute 'statevals'.
  596. <br/><br/>
  597. Example: Turn switch on<br/>
  598. <code>
  599. attr myswitch statedatapoint STATE<br/>
  600. attr myswitch statevals on:true,off:false<br/>
  601. set myswitch on
  602. </code>
  603. </li><br/>
  604. <li><b>set &lt;name&gt; toggle</b><br/>
  605. Toggle state datapoint between values defined by attribute 'statevals'. This command is
  606. only available if attribute 'statevals' is set. Toggling supports more than two state
  607. values.
  608. <br/><br/>
  609. Example: Toggle blind actor<br/>
  610. <code>
  611. attr myswitch statedatapoint LEVEL<br/>
  612. attr myswitch statevals up:100,down:0<br/>
  613. set myswitch toggle
  614. </code>
  615. </li><br/>
  616. <li><b>set &lt;name&gt; on-for-timer &lt;ontime&gt;</b><br/>
  617. Switch device on for specified number of seconds. This command is only available if
  618. channel contains a datapoint ON_TIME. The attribute 'statevals' must contain at least a
  619. value for 'on'. The attribute 'statedatapoint' must be set to a writeable datapoint.
  620. <br/><br/>
  621. Example: Turn switch on for 300 seconds<br/>
  622. <code>
  623. attr myswitch statedatapoint STATE<br/>
  624. attr myswitch statevals on:true,off:false<br/>
  625. set myswitch on-for-timer 300
  626. </code>
  627. </li><br/>
  628. <li><b>set &lt;name&gt; on-till &lt;timestamp&gt;</b><br/>
  629. Switch device on until <i>timestamp</i>. Parameter <i>timestamp</i> can be a time in
  630. format HH:MM or HH:MM:SS. This command is only available if channel contains a datapoint
  631. ON_TIME. The attribute 'statevals' must contain at least a value for 'on'. The Attribute
  632. 'statedatapoint' must be set to a writeable datapoint.
  633. </li><br/>
  634. <li><b>set &lt;name&gt; pct &lt;value&gt; [&lt;ontime&gt; [&lt;ramptime&gt;]]</b><br/>
  635. Set datapoint LEVEL of a channel to the specified <i>value</i>. Optionally a <i>ontime</i>
  636. and a <i>ramptime</i> (both in seconds) can be specified. This command is only available
  637. if channel contains at least a datapoint LEVEL and optionally datapoints ON_TIME and
  638. RAMP_TIME. The parameter <i>ontime</i> can be specified in seconds or as timestamp in
  639. format HH:MM or HH:MM:SS. If <i>ontime</i> is 0 it's ignored. This syntax can be used to
  640. modify the ramp time only.
  641. <br/><br/>
  642. Example: Turn dimmer on for 600 second. Increase light to 100% over 10 seconds<br>
  643. <code>
  644. attr myswitch statedatapoint LEVEL<br/>
  645. attr myswitch statevals on:100,off:0<br/>
  646. set myswitch pct 100 600 10
  647. </code>
  648. </li><br/>
  649. </ul>
  650. <br/>
  651. <a name="HMCCUCHNget"></a>
  652. <b>Get</b><br/><br/>
  653. <ul>
  654. <li><b>get &lt;name&gt; config [device] [&lt;filter-expr&gt;]</b><br/>
  655. Get configuration parameters of CCU channel. If attribute 'ccureadings' is 0 results
  656. are displayed in browser window. Parameters can be filtered by <i>filter-expr</i>.
  657. Parameters to be stored as readings must be part of 'ccureadingfilter'. If option
  658. 'device' is specified parameters of device are read.
  659. </li><br/>
  660. <li><b>get &lt;name&gt; configdesc [device]</b><br/>
  661. Get description of configuration parameters of CCU channel or device if option 'device'
  662. is specified.
  663. </li><br/>
  664. <li><b>get &lt;name&gt; configlist [device] [&lt;filter-expr&gt;]</b><br/>
  665. Get configuration parameters of CCU channel. Parameters can be filtered by
  666. <i>filter-expr</i>. With option 'device' device parameters are listed.
  667. </li><br/>
  668. <li><b>get &lt;name&gt; datapoint &lt;datapoint&gt;</b><br/>
  669. Get value of a CCU channel datapoint.
  670. </li><br/>
  671. <li><b>get &lt;name&gt; defaults</b><br/>
  672. Display default attributes for CCU device type.
  673. </li><br/>
  674. <li><b>get &lt;name&gt; deviceinfo [{State | <u>Value</u>}]</b><br/>
  675. Display all channels and datapoints of device with datapoint values and types.
  676. </li><br/>
  677. <li><b>get &lt;name&gt; devstate</b><br/>
  678. Get state of CCU device. Default datapoint STATE can be changed by setting
  679. attribute 'statedatapoint'. Command will fail if state datapoint does not exist in
  680. channel.
  681. </li><br/>
  682. <li><b>get &lt;name&gt; update [{State | <u>Value</u>}]</b><br/>
  683. Update all datapoints / readings of channel. With option 'State' the device is queried.
  684. This request method is more accurate but slower then 'Value'.
  685. </li>
  686. </ul>
  687. <br/>
  688. <a name="HMCCUCHNattr"></a>
  689. <b>Attributes</b><br/><br/>
  690. <ul>
  691. To reduce the amount of events it's recommended to set attribute 'event-on-change-reading'
  692. to '.*'.
  693. <br/><br/>
  694. <li><b>ccuackstate {<u>0</u> | 1}</b><br/>
  695. If set to 1 state will be set to result of command (i.e. 'OK'). Otherwise state is only
  696. updated if value of state datapoint has changed.
  697. </li><br/>
  698. <li><b>ccucalculate &lt;value&gt;:&lt;reading&gt;[:&lt;dp-list&gt;[;...]</b><br/>
  699. Calculate special values like dewpoint based on datapoints specified in
  700. <i>dp-list</i>. The result is stored in <i>reading</i>. The following <i>values</i>
  701. are supported:<br/>
  702. dewpoint = calculate dewpoint, <i>dp-list</i> = &lt;temperature&gt;,&lt;humidity&gt;
  703. </li><br/>
  704. <li><b>ccuflags {nochn0, trace}</b><br/>
  705. Control behaviour of device:<br/>
  706. nochn0: Prevent update of status channel 0 datapoints / readings.<br/>
  707. trace: Write log file information for operations related to this device.
  708. </li><br/>
  709. <li><b>ccuget {State | <u>Value</u>}</b><br/>
  710. Set read access method for CCU channel datapoints. Method 'State' is slower than 'Value'
  711. because each request is sent to the device. With method 'Value' only CCU is queried.
  712. Default is 'Value'.
  713. </li><br/>
  714. <li><b>ccureadings {0 | <u>1</u>}</b><br/>
  715. If set to 1 values read from CCU will be stored as readings. Default is 1.
  716. </li><br/>
  717. <li><b>ccureadingfilter &lt;filter-rule[;...]&gt;</b><br/>
  718. Only datapoints matching specified expression are stored as readings.<br/>
  719. Syntax for <i>filter-rule</i> is: [N:][&lt;channel-name&gt;!]&lt;RegExp&gt;<br/>
  720. If <i>channel-name</i> is specified the following rule applies only to this channel.
  721. By default all datapoints will be stored as readings. Attribute ccudef-readingfilter
  722. of I/O device will be checked before this attribute.<br/>
  723. If a rule starts with 'N:' the filter is negated which means that a reading is
  724. stored if rule doesn't match.
  725. </li><br/>
  726. <li><b>ccureadingformat {address[lc] | name[lc] | datapoint[lc]}</b><br/>
  727. Set format of reading names. Default is 'name'. If set to 'address' format of reading names
  728. is channel-address.datapoint. If set to 'name' format of reading names is
  729. channel-name.datapoint. If set to 'datapoint' format is channel-number.datapoint. With
  730. suffix 'lc' reading names are converted to lowercase.
  731. </li><br/>
  732. <li><b>ccureadingname &lt;old-readingname-expr&gt;:[+]&lt;new-readingname&gt;[;...]</b><br/>
  733. Set alternative or additional reading names or group readings. Only part of old reading
  734. name matching <i>old-readingname-exptr</i> is substituted by <i>new-readingname</i>.
  735. If <i>new-readingname</i> is preceded by '+' an additional reading is created. If
  736. <i>old-readingname-expr</i> matches more than one reading the values of these readings
  737. are stored in one reading. This makes sense only in some cases, i.e. if a device has
  738. several pressed_short datapoints and a reading should contain a value if any button
  739. is pressed.<br/><br/>
  740. Examples:<br/>
  741. <code>
  742. # Rename readings 0.LOWBAT and 0.LOW_BAT as battery<br/>
  743. attr mydev ccureadingname 0.(LOWBAT|LOW_BAT):battery<br/>
  744. # Add reading battery as a copy of readings LOWBAT and LOW_BAT.<br/>
  745. # Rename reading 4.SET_TEMPERATURE as desired-temp<br/>
  746. attr mydev ccureadingname 0.(LOWBAT|LOW_BAT):+battery,1.SET_TEMPERATURE:desired-temp<br/>
  747. # Store values of readings n.PRESS_SHORT in new reading pressed.<br/>
  748. # Value of pressed is 1/true if any button is pressed<br/>
  749. attr mydev ccureadingname [1-4].PRESSED_SHORT:+pressed
  750. </code>
  751. </li><br/>
  752. <li><b>ccuscaleval &lt;datapoint&gt;:&lt;factor&gt;[,...]</b><br/>
  753. <b>ccuscaleval &lt;[!]datapoint&gt;:&lt;min&gt;:&lt;max&gt;:&lt;minn&gt;:&lt;maxn&gt;[,...]
  754. </b><br/>
  755. Scale, spread, shift and optionally reverse values before executing set datapoint commands
  756. or after executing get datapoint commands / before storing values in readings.<br/>
  757. If first syntax is used during get the value read from CCU is devided by <i>factor</i>.
  758. During set the value is multiplied by factor.<br/>
  759. With second syntax one must specify the interval in CCU (<i>min,max</i>) and the interval
  760. in FHEM (<i>minn, maxn</i>). The scaling factor is calculated automatically. If parameter
  761. <i>datapoint</i> starts with a '!' the resulting value is reversed.
  762. <br/><br/>
  763. Example: Scale values of datapoint LEVEL for blind actor and reverse values<br/>
  764. <code>
  765. attr myblind ccuscale !LEVEL:0:1:0:100
  766. </code>
  767. </li><br/>
  768. <li><b>ccuverify {<u>0</u> | 1 | 2}</b><br/>
  769. If set to 1 a datapoint is read for verification after set operation. If set to 2 the
  770. corresponding reading will be set to the new value directly after setting a datapoint
  771. in CCU without any verification.
  772. </li><br/>
  773. <li><b>controldatapoint &lt;datapoint&gt;</b><br/>
  774. Set datapoint for device control. Can be use to realize user defined control elements for
  775. setting control datapoint. For example if datapoint of thermostat control is
  776. SET_TEMPERATURE one can define a slider for setting the destination temperature with
  777. following attributes:<br/><br/>
  778. attr mydev controldatapoint SET_TEMPERATURE<br/>
  779. attr mydev webCmd control<br/>
  780. attr mydev widgetOverride control:slider,10,1,25
  781. </li><br/>
  782. <li><b>disable {<u>0</u> | 1}</b><br/>
  783. Disable client device.
  784. </li><br/>
  785. <li><b>hmstatevals &lt;subst-rule&gt;[;...]</b><br/>
  786. Define building rules and substitutions for reading hmstate. Syntax of <i>subst-rule</i>
  787. is<br/>
  788. [=&lt;reading&gt;;]&lt;datapoint-expr&gt;!&lt;{#n1-m1|regexp}&gt;:&lt;text&gt;[,...]
  789. <br/><br/>
  790. The syntax is almost the same as of attribute 'substitute', except there's no channel
  791. specification possible for datapoint and parameter <i>datapoint-expr</i> is a regular
  792. expression.<br/>
  793. The value of the I/O device attribute 'ccudef-hmstatevals' is appended to the value of
  794. this attribute. The default value of 'ccudef-hmstatevals' is
  795. '^UNREACH!(1|true):unreachable;LOW_?BAT!(1|true):warn_battery'.
  796. Normally one should not specify a substitution rule for the "good" value of an error
  797. datapoint (i.e. 0 for UNREACH). If none of the rules is matching, reading 'hmstate' is set
  798. to value of reading 'state'.<br/>
  799. Parameter <i>text</i> can contain variables in format ${<i>varname</i>}. The variable
  800. $value is substituted by the original datapoint value. All other variables must match
  801. with a valid datapoint name or a combination of channel number and datapoint name
  802. seperated by a '.'.<br/>
  803. Optionally the name of the HomeMatic state reading can be specified at the beginning of
  804. the attribute in format =&lt;reading&gt;;. The default reading name is 'hmstate'.
  805. </li><br/>
  806. <li><b>statedatapoint &lt;datapoint&gt;</b><br/>
  807. Set state datapoint used by some commands like 'set devstate'.
  808. </li><br/>
  809. <li><b>statevals &lt;text&gt;:&lt;text&gt;[,...]</b><br/>
  810. Define substitution for values of set commands. The parameters <i>text</i> are available
  811. as set commands.
  812. <br/><br/>
  813. Example:<br/>
  814. <code>
  815. attr my_switch statevals on:true,off:false<br/>
  816. set my_switch on
  817. </code>
  818. </li><br/>
  819. <li><b>stripnumber {<u>0</u> | 1 | 2 | -n}</b><br/>
  820. Remove trailing digits or zeroes from floating point numbers and/or round floating
  821. point numbers. If attribute is negative (-0 is valid) floating point values are rounded
  822. to the specified number of digits before they are stored in readings. The meaning of
  823. values 0-2 is:<br/>
  824. 0 = Floating point numbers are stored as read from CCU (i.e. with trailing zeros)<br/>
  825. 1 = Trailing zeros are stripped from floating point numbers except one digit.<br/>
  826. 2 = All trailing zeros are stripped from floating point numbers.
  827. </li><br/>
  828. <li><b>substexcl &lt;reading-expr&gt;</b><br/>
  829. Exclude values of readings matching <i>reading-expr</i> from substitution. This is helpful
  830. for reading 'control' if the reading is used for a slider widget and the corresponding
  831. datapoint is assigned to attribute statedatapoint and controldatapoint.
  832. </li><br/>
  833. <li><b>substitute &lt;subst-rule&gt;[;...]</b><br/>
  834. Define substitutions for datapoint/reading values. Syntax of <i>subst-rule</i> is<br/><br/>
  835. [[&lt;channelno.&gt;]&lt;datapoint&gt;[,...]!]&lt;{#n1-m1|regexp}&gt;:&lt;text&gt;[,...]
  836. <br/><br/>
  837. Parameter <i>text</i> can contain variables in format ${<i>varname</i>}. The variable
  838. ${value} is
  839. substituted by the original datapoint value. All other variables must match with a valid
  840. datapoint name or a combination of channel number and datapoint name seperated by a '.'.
  841. <br/><br/>
  842. Example: Substitute the value of datapoint TEMPERATURE by the string
  843. 'T=<i>val</i> deg' and append current value of datapoint 1.HUMIDITY<br/>
  844. <code>
  845. attr my_weather substitute TEMPERATURE!.+:T=${value} deg H=${1.HUMIDITY}%
  846. </code>
  847. If rule expression starts with a hash sign a numeric datapoint value is substituted if
  848. it fits in the number range n &lt;= value &lt;= m.
  849. <br/><br/>
  850. Example: Interpret LEVEL values of dimmer as "on" and "off"<br/>
  851. <code>
  852. attr my_dim substitute LEVEL!#0-0:off,#1-100:on
  853. </code>
  854. </li>
  855. </ul>
  856. </ul>
  857. =end html
  858. =cut