10_UNIRoll.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. ###################LoTT Uniroll###################
  2. # UNIRoll:no synchronisation, the message protocoll begins directly with datas
  3. # group address 16 Bit like an housecode
  4. # channel address 4 Bit up to 16 devices
  5. # command 4 Bit up: E(1110),stop: D(1101), down: B(1011)
  6. # end off 1 Bit, zero or one it doesnot matter
  7. # whole length 25 Bit
  8. #time intervall:
  9. #Bit-digit 0 high ca. 1,6 ms equal 100(h64) 16us steps
  10. # low ca. 0,576 ms 36(h24)
  11. #Bit-digit 1 high ca. 0,576 ms 36(h24)
  12. # low ca. 1,6 ms 100(h64)
  13. #timespace ca. 100 ms
  14. #binary : 1010 1011 1100 1101 0110 1110 1
  15. #hexa: a b c d 6 e 8 (an additional one bit)
  16. #the message is sent with the general cul-command: G
  17. #G0031A364242464abcd6e8 : 00 synchbits 3 databytes, 1 databit, HHLLLLHH, data
  18. package main;
  19. use strict;
  20. use warnings;
  21. my %codes = (
  22. "e" => "up", #1110 e
  23. "d" => "stop", #1101 d
  24. "b" => "down", #1011 b
  25. );
  26. my %readonly = (
  27. "thermo-on" => 1,
  28. "thermo-off" => 1,
  29. );
  30. use vars qw(%UNIRoll_c2b); # Peter would like to access it from outside
  31. my $UNIRoll_simple ="off off-for-timer on on-for-timer on-till reset timer toggle";
  32. my %models = (
  33. R_23700 => 'simple',
  34. dummySimple => 'simple',
  35. );
  36. sub hex2fouru($);
  37. sub four2hexu($$);
  38. sub
  39. UNIRoll_Initialize($)
  40. {
  41. my ($hash) = @_;
  42. foreach my $k (keys %codes) {
  43. $UNIRoll_c2b{$codes{$k}} = $k; # c2b liest das allgmeine Array der Gerätegruppe
  44. }
  45. # print "UNIRoll_Initialize \n";
  46. $hash->{Match} = "^G.*";
  47. $hash->{SetFn} = "UNIRoll_Set";
  48. $hash->{StateFn} = "UNIRoll_SetState";
  49. $hash->{DefFn} = "UNIRoll_Define";
  50. $hash->{UndefFn} = "UNIRoll_Undef";
  51. $hash->{ParseFn} = "UNIRoll_Parse";
  52. $hash->{AttrList} = "IODev do_not_notify:1,0 ".
  53. "ignore:1,0 showtime:1,0 ".
  54. "loglevel:0,1,2,3,4,5,6 " .
  55. "model:".join(",", sort keys %models);
  56. }
  57. #####################################
  58. sub
  59. UNIRoll_SetState($$$$) # 4 Skalare Parameter
  60. {
  61. my ($hash, $tim, $vt, $val) = @_; #@_ Array
  62. # print "UNIRoll_SetState \n";
  63. $val = $1 if($val =~ m/^(.*) \d+$/); # m match Funktion
  64. my $name = $hash->{NAME};
  65. (undef, $val) = ReplaceEventMap($name, [$name, $val], 0)
  66. if($attr{$name}{eventMap});
  67. return "setstate $name: undefined value $val" if(!defined($UNIRoll_c2b{$val}));
  68. return undef;
  69. }
  70. ###################################
  71. sub
  72. UNIRoll_Set($@)
  73. {
  74. my ($hash, @a) = @_; # Eingabewerte nach define name typ devicecode channelcode
  75. my $ret = undef;
  76. my $na = int(@a); #na Anzahl Felder in a
  77. # print "UNIRoll_Set \n";
  78. return "no set value specified" if($na < 2 || $na > 3);
  79. return "Readonly value $a[1]" if(defined($readonly{$a[1]}));
  80. my $c = $UNIRoll_c2b{$a[1]}; # Wert des Kommandos: up stop down
  81. my $name = $a[0]; # Gerätename
  82. if(!defined($c)) {
  83. # Model specific set arguments
  84. if(defined($attr{$name}) && defined($attr{$name}{"model"})) {
  85. my $mt = $models{$attr{$name}{"model"}};
  86. return "Unknown argument $a[1], choose one of "
  87. if($mt && $mt eq "sender");
  88. return "Unknown argument $a[1], choose one of $UNIRoll_simple"
  89. if($mt && $mt eq "simple");
  90. }
  91. return "Unknown argument $a[1], choose one of " .
  92. join(" ", sort keys %UNIRoll_c2b);
  93. }
  94. my $v = join(" ", @a);
  95. Log GetLogLevel($name,2), "UNIRoll set $v";
  96. (undef, $v) = split(" ", $v, 2); # Not interested in the name...
  97. my $val;
  98. # G0030A364242464abcd6e8 : 00 Synchbits 3 Datenbytes, 1 Datenbit, HHLLLLHH, Daten
  99. # Damit kein Befehl einen zufälligen Betrieb stoppt
  100. # vorher ein gezielter Stopp Befehl
  101. my $stop = "d";
  102. IOWrite($hash, "","G0030A364242464".$hash->{XMIT}.$hash->{BTN}.$stop);
  103. # print "$hash XMIT:$hash->{XMIT} BTN: $hash->{BTN} c: $c \n";
  104. IOWrite($hash, "","G0030A364242464".$hash->{XMIT}.$hash->{BTN}.$c);
  105. # XMIT: Gerätegruppe, BTN: Kanalnummer, c: Commando
  106. ###########################################
  107. # Set the state of a device to off if on-for-timer is called
  108. if($modules{UNIRoll}{ldata}{$name}) {
  109. CommandDelete(undef, $name . "_timer");
  110. delete $modules{UNIRoll}{ldata}{$name};
  111. }
  112. if($a[1] =~ m/for-timer/ && $na == 3 &&
  113. defined($attr{$name}) && defined($attr{$name}{"follow-on-for-timer"})) {
  114. my $to = sprintf("%02d:%02d:%02d", $val/3600, ($val%3600)/60, $val%60);
  115. $modules{UNIRoll}{ldata}{$name} = $to;
  116. Log 4, "Follow: +$to setstate $name off";
  117. CommandDefine(undef,
  118. $name."_timer at +$to setstate $name off; trigger $name off");
  119. }
  120. ##########################
  121. # Look for all devices with the same code, and set state, timestamp
  122. my $code = "$hash->{XMIT} $hash->{BTN}";
  123. my $tn = TimeNow();
  124. my $defptr = $modules{UNIRoll}{defptr};
  125. foreach my $n (keys %{ $defptr->{$code} }) {
  126. my $lh = $defptr->{$code}{$n};
  127. $lh->{CHANGED}[0] = $v;
  128. $lh->{STATE} = $v;
  129. $lh->{READINGS}{state}{TIME} = $tn;
  130. $lh->{READINGS}{state}{VAL} = $v;
  131. my $lhname = $lh->{NAME};
  132. if($name ne $lhname) {
  133. DoTrigger($lhname, undef);
  134. }
  135. }
  136. return $ret;
  137. }
  138. #############################
  139. sub
  140. UNIRoll_Define($$)
  141. # Gerät anmelden hash: Hash-adresse, def: Eingabe bei define .....
  142. # Hauscode, Kanalnummer aufbereiten prüfen
  143. {
  144. my ($hash, $def) = @_;
  145. my @a = split("[ \t][ \t]*", $def);
  146. my $u = "wrong syntax: define <name> UNIRoll device adress " .
  147. "addr [fg addr] [lm addr] [gm FF]";
  148. # print "UNIRoll_Define \n";
  149. return $u if(int(@a) < 4);
  150. return "Define $a[0]: wrong device address format: specify a 4 digit hex value ".
  151. "or an 8 digit quad value"
  152. if( ($a[2] !~ m/^[a-f0-9]{4}$/i) && ($a[2] !~ m/^[1-4]{8}$/i) );
  153. return "Define $a[0]: wrong chanal format: specify a 1 digit hex value " .
  154. "or a 2 digit quad value"
  155. if( ($a[3] !~ m/^[a-f0-9]{1}$/i) && ($a[3] !~ m/^[1-4]{2}$/i) );
  156. my $devcode = $a[2];
  157. $devcode = four2hexu($devcode,4) if (length($devcode) == 8);
  158. my $chacode = $a[3];
  159. $chacode = four2hexu($chacode,2) if (length($chacode) == 4);
  160. $hash->{XMIT} = lc($devcode); # hex Kleinschreibung ?
  161. $hash->{BTN} = lc($chacode);
  162. # Gerätedaten aufbauen,
  163. # defptr: device pointer global
  164. my $code = lc("$devcode $chacode"); #lc lowercase Kleinschreibung
  165. my $ncode = 1; #?
  166. my $name = $a[0]; #Gerätename
  167. $hash->{CODE}{$ncode++} = $code;
  168. $modules{UNIRoll}{defptr}{$code}{$name} = $hash;
  169. # print "Test IoPort $hash def $def code $code.\n";
  170. AssignIoPort($hash); # Gerät anmelden
  171. }
  172. #############################
  173. sub
  174. UNIRoll_Undef($$)
  175. {
  176. my ($hash, $name) = @_;
  177. foreach my $c (keys %{ $hash->{CODE} } ) {
  178. $c = $hash->{CODE}{$c};
  179. # print "UNIRoll_Undef \n";
  180. # As after a rename the $name my be different from the $defptr{$c}{$n}
  181. # we look for the hash.
  182. foreach my $dname (keys %{ $modules{UNIRoll}{defptr}{$c} }) {
  183. delete($modules{UNIRoll}{defptr}{$c}{$dname})
  184. if($modules{UNIRoll}{defptr}{$c}{$dname} == $hash);
  185. }
  186. }
  187. return undef;
  188. }
  189. sub
  190. UNIRoll_Parse($$)
  191. {
  192. # print "UNIRoll_Parse \n";
  193. }
  194. #############################
  195. sub
  196. hex2fouru($)
  197. {
  198. my $v = shift;
  199. my $r = "";
  200. foreach my $x (split("", $v)) {
  201. $r .= sprintf("%d%d", (hex($x)/4)+1, (hex($x)%4)+1);
  202. }
  203. # print "UNIRoll_hex2fouru $r \n";
  204. return $r;
  205. }
  206. #############################
  207. sub
  208. four2hexu($$)
  209. {
  210. my ($v,$len) = @_;
  211. my $r = 0;
  212. foreach my $x (split("", $v)) {
  213. $r = $r*4+($x-1);
  214. }
  215. # print "UNIRoll_fourhex r:$r len: $len\n";
  216. return sprintf("%0*x", $len,$r);
  217. }
  218. 1;
  219. =pod
  220. =begin html
  221. <a name="UNIRoll"></a>
  222. <h3>UNIRoll</h3>
  223. <ul>
  224. The protocol is used by the Lott UNIROLL R-23700 reciever. The radio
  225. (868.35 MHz) messages are either received through an <a href="#FHZ">FHZ</a>
  226. or an <a href="#CUL">CUL</a> device, so this must be defined first.
  227. Recieving sender messages is not integrated.
  228. The CUL have to allow working with zero synchbits on the beginning of a message.
  229. <br><br>
  230. <a name="UNIRolldefine"></a>
  231. <b>Define</b>
  232. <ul>
  233. <code>define &lt;name&gt; UNIRoll &lt;devicegroup&gt; &lt;deviceaddress&gt; </code>
  234. <br><br>
  235. The values of devicegroup addres (similar to the housecode) and device address (button) can be either defined as
  236. hexadecimal value or as ELV-like "quad-decimal" value with digits 1-4. We
  237. will reference this ELV-like notation as ELV4 later in this document. You
  238. may even mix both hexadecimal and ELV4 notations, because FHEM can detect
  239. the used notation automatically by counting the digits.
  240. There is no master or group code integrated.
  241. <br>
  242. <ul>
  243. <li><code>&lt;devicecode&gt;</code> is a 4 digit hex or 8 digit ELV4 number,
  244. corresponding to the housecode address.</li>
  245. <li><code>&lt;channel&gt;</code> is a 1 digit hex or 2 digit ELV4 number,
  246. corresponding to a button of the transmitter.</li>
  247. </ul>
  248. <br>
  249. Examples:
  250. <ul>
  251. <code>define lamp UNIRoll 7777 0
  252. <code>define otherlamp UNIRoll 24242424 11
  253. </ul>
  254. </ul>
  255. <br>
  256. <a name="UNIRollset"></a>
  257. <b>Set </b>
  258. <ul>
  259. <code>set &lt;name&gt; &lt;value&gt; [&lt;time&gt]</code>
  260. <br><br>
  261. where <code>value</code> is one of:<br>
  262. <pre>
  263. up
  264. stop
  265. down
  266. </pre>
  267. Examples:
  268. <ul>
  269. <code>set roll up</code><br>
  270. <code>set roll1,roll2,roll3 up</code><br>
  271. <code>set roll1-roll3 up</code><br>
  272. </ul>
  273. <br>
  274. <b>Get</b> <ul>N/A</ul><br>
  275. <a name="UNIRollattr"></a>
  276. <b>Attributes</b>
  277. <ul>
  278. <a name="IODev"></a>
  279. <li>IODev<br>
  280. Set the IO or physical device which should be used for sending signals
  281. for this "logical" device. An example for the physical device is an FHZ
  282. or a CUL.</li><br>
  283. <a name="eventMap"></a>
  284. <li>eventMap<br>
  285. Replace event names and set arguments. The value of this attribute
  286. consists of a list of space separated values, each value is a colon
  287. separated pair. The first part specifies the "old" value, the second
  288. the new/desired value. If the first character is slash(/) or komma(,)
  289. then split not by space but by this character, enabling to embed spaces.
  290. Examples:<ul><code>
  291. attr device eventMap up:open down:closed<br>
  292. set device open
  293. </code></ul>
  294. </li><br>
  295. <li><a href="#loglevel">loglevel</a></li><br>
  296. <li><a href="#showtime">showtime</a></li><br>
  297. <a name="model"></a>
  298. <li>model<br>
  299. The model attribute denotes the model type of the device.
  300. The attributes will (currently) not be used by the fhem.pl directly.
  301. It can be used by e.g. external programs or web interfaces to
  302. distinguish classes of devices and send the appropriate commands.
  303. The spelling of the model names are as quoted on the printed
  304. documentation which comes which each device. This name is used
  305. without blanks in all lower-case letters. Valid characters should be
  306. <code>a-z 0-9</code> and <code>-</code> (dash),
  307. other characters should be ommited. Here is a list of "official"
  308. devices:<br><br>
  309. <b>Receiver/Actor</b>: there is only one reciever: R_23700
  310. </li><br>
  311. </ul>
  312. <br>
  313. </ul>