21_N4HMODULE.pm 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451
  1. #############################################################################
  2. #
  3. # 21_N4HMODULE.pm
  4. #
  5. # net4home Busconnector Device
  6. #
  7. # (c) 2014-2018 Oliver Koerber <koerber@net4home.de>
  8. #
  9. # $Id: 21_N4HMODULE.pm 15730 2017-12-30 20:40:07Z oliverk $
  10. #
  11. ##############################################################################
  12. package main;
  13. use strict;
  14. use warnings;
  15. use POSIX;
  16. use SetExtensions;
  17. sub N4HMODULE_Set($@);
  18. sub N4HMODULE_Get ($$@);
  19. sub N4HMODULE_Update($@);
  20. sub N4HMODULE_DbLog_splitFn($$);
  21. my $n4hmodule_Version = "1.0.2.0 - 30.12.2017";
  22. my %OT_devices = (
  23. "1" => {"name" => "leer", "OTcanSet" => "false", "OTcanReq" => "false", "fields" => [] },
  24. "2" => {"name" => "Eingang, Binär, Sx", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  25. "3" => {"name" => "Ausgang, Binär, Relais", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
  26. { "cmd" => "350000", "ID" => "0", "text" => "UM", "Type" => "Button" , "set" => "toggle:noArg" },
  27. { "cmd" => "326400", "ID" => "1", "text" => "EIN", "Type" => "Button" , "set" => "on:noArg" },
  28. { "cmd" => "320000", "ID" => "2", "text" => "AUS", "Type" => "Button" , "set" => "off:noArg" },
  29. { "cmd" => "428100", "ID" => "3", "text" => "Zwangsgeführt EIN", "Type" => "Button" , "set" => "lockon:noArg" },
  30. { "cmd" => "428000", "ID" => "4", "text" => "Zwangsgeführt AUS", "Type" => "Button" , "set" => "lockoff:noArg" },
  31. { "cmd" => "420000", "ID" => "5", "text" => "Zwangsführung deaktiv", "Type" => "Button" , "set" => "unlock:noArg" },
  32. ]},
  33. "4" => {"name" => "Ausgang, Binär, Timer, Relais", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
  34. { "cmd" => "350000", "ID" => "0", "text" => "EIN für Dauer: Zeit 1", "Type" => "Button" , "set" => "toggle:noArg" },
  35. { "cmd" => "320000", "ID" => "2", "text" => "AUS", "Type" => "Button" , "set" => "off:noArg" },
  36. { "cmd" => "428100", "ID" => "3", "text" => "Zwangsgeführt EIN", "Type" => "Button" , "set" => "lockon:noArg" },
  37. { "cmd" => "428000", "ID" => "4", "text" => "Zwangsgeführt AUS", "Type" => "Button" , "set" => "lockoff:noArg" },
  38. { "cmd" => "420000", "ID" => "5", "text" => "Zwangsführung deaktiv", "Type" => "Button" , "set" => "unlock:noArg" },
  39. ]},
  40. "5" => {"name" => "Ausgang, Dimmer", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
  41. { "cmd" => "320000", "ID" => "3", "text" => "AUS", "Type" => "Button" , "set" => "off:noArg" },
  42. { "cmd" => "326500", "ID" => "4", "text" => "EIN mit letztem Wert", "Type" => "Button" , "set" => "on:noArg" },
  43. { "cmd" => "350000", "ID" => "5", "text" => "UM", "Type" => "Button" , "set" => "toggle:noArg" },
  44. { "cmd" => "330000", "ID" => "7", "text" => "Heller", "Type" => "Button" , "set" => "dimup:noArg" },
  45. { "cmd" => "340000", "ID" => "8", "text" => "Dunkler", "Type" => "Button" , "set" => "dimdown:noArg" },
  46. { "cmd" => "428100", "ID" => "9", "text" => "Zwangsgeführt EIN", "Type" => "Button" , "set" => "lockon:noArg" },
  47. { "cmd" => "428000", "ID" => "10","text" => "Zwangsgeführt AUS", "Type" => "Button" , "set" => "lockoff:noArg" },
  48. { "cmd" => "32" , "ID" => "10","text" => "Wert setzen auf", "Type" => "Button" , "set" => "pct:slider,0,1,100" },
  49. { "cmd" => "420000", "ID" => "11", "Text" => "Zwangsführung deaktiv", "Type" => "Button" , "set" => "unlock:noArg" },
  50. ]},
  51. "24" => {"name" => "Messwert,Temperatur", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  52. "25" => {"name" => "Messwert,Helligkeit", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  53. "26" => {"name" => "Messwert,Feuchte", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  54. "34" => {"name" => "TLH-Regler H,Sollwert,Temperatur", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
  55. { "cmd" => "3B0000", "ID" => "0", "Text" => "AUS", "Type" => "Button" , "set" => "off:noArg" },
  56. { "cmd" => "3B0100", "ID" => "1", "Text" => "Heizen", "Type" => "Button" , "set" => "heat:noArg" },
  57. { "cmd" => "3B0200", "ID" => "2", "Text" => "Kühlen", "Type" => "Button" , "set" => "cool:noArg" },
  58. { "cmd" => "3B0300", "ID" => "3", "Text" => "Auto", "Type" => "Button" , "set" => "auto:noArg" },
  59. { "cmd" => "3200", "ID" => "4", "Text" => "Sollwert", "Type" => "Button" , "set" => "desired-temperature:slider,4,0.5,30,1" },
  60. { "cmd" => "3B1400", "ID" => "5", "Text" => "Tagwert aktivieren", "Type" => "Button" , "set" => "comfort:noArg" },
  61. { "cmd" => "3B1E00", "ID" => "6", "Text" => "Nachtwert aktivieren", "Type" => "Button" , "set" => "eco:noArg" },
  62. { "cmd" => "3B1500", "ID" => "7", "Text" => "Sollwert -&gt; Tagwert", "Type" => "Button" , "set" => "setdesired2comfort:noArg" },
  63. { "cmd" => "3B1F00", "ID" => "8", "Text" => "Sollwert -&gt; Nachtwert","Type" => "Button" , "set" => "setdesired2eco:noArg" },
  64. ]},
  65. "95" => {"name" => "Ausgang, Jal, Motor AJ3", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
  66. { "cmd" => "350000", "ID" => "0", "text" => "Weiterschalten", "Type" => "Button" , "set" => "toggle:noArg" },
  67. { "cmd" => "320000", "ID" => "1", "text" => "STOP", "Type" => "Button" , "set" => "stop:noArg" },
  68. { "cmd" => "320300", "ID" => "2", "text" => "AUF", "Type" => "Button" , "set" => "up:noArg" },
  69. { "cmd" => "320100", "ID" => "3", "text" => "AB", "Type" => "Button" , "set" => "down:noArg" },
  70. { "cmd" => "428300", "ID" => "4", "text" => "Sperre OFFEN", "Type" => "Button" , "set" => "lockup:noArg" },
  71. { "cmd" => "428100", "ID" => "5", "text" => "Sperre GESCHLOSSEN", "Type" => "Button" , "set" => "lockdown:noArg" },
  72. { "cmd" => "420000", "ID" => "6", "text" => "Sperre freigeben", "Type" => "Button" , "set" => "unlock:noArg" },
  73. ]},
  74. "100" => {"name" => "Safety Basis", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
  75. { "cmd" => "320000", "ID" => "0", "text" => "AUS", "Type" => "Button" , "set" => "off:noArg" },
  76. { "cmd" => "320100", "ID" => "1", "text" => "Intern scharf 1", "Type" => "Button" , "set" => "intern1:noArg" },
  77. { "cmd" => "320200", "ID" => "2", "text" => "Intern scharf 1", "Type" => "Button" , "set" => "intern2:noArg" },
  78. { "cmd" => "320300", "ID" => "3", "text" => "Extern scharf", "Type" => "Button" , "set" => "extern:noArg" },
  79. ]},
  80. "210" => {"name" => "UP-RF Absender", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  81. "240" => {"name" => "Messwert,Wind", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  82. "242" => {"name" => "Messwert,Luftdruck", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  83. "245" => {"name" => "Messwert,Regenmenge", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  84. "310" => {"name" => "Stromzähler", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [
  85. { "cmd" => "330000", "ID" => "0", "Text" => "Inc", "Type" => "Button" , "set" => "inc" },
  86. ]},
  87. "348" => {"name" => "Zähler", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [
  88. { "cmd" => "330000", "ID" => "0", "Text" => "+1", "Type" => "Button" , "set" => "inc"},
  89. { "cmd" => "340000", "ID" => "1", "Text" => "-1", "Type" => "Button" , "set" => "dec" },
  90. { "cmd" => "320000", "ID" => "3", "Text" => "Zählerwert reset", "Type" => "Button" , "set" => "reset" },
  91. { "cmd" => "360000", "ID" => "4", "Text" => "Zählerwert lesen (nur LCD)", "Type" => "Button" },
  92. ] },
  93. "999" => {"name" => "Messwert,N56", "OTcanSet" => "false", "OTcanReq" => "true", "fields" => [] },
  94. );
  95. ##################################################################################
  96. sub N4HMODULE_Initialize($) {
  97. ##################################################################################
  98. my ($hash) = @_;
  99. my @otlist;
  100. foreach my $model (keys %OT_devices){
  101. push @otlist,$OT_devices{$model}->{name};
  102. }
  103. $hash->{DefFn} = "N4HMODULE_Define";
  104. $hash->{UndefFn} = "N4HMODULE_Undefine";
  105. $hash->{ParseFn} = "N4HMODULE_Parse";
  106. $hash->{SetFn} = "N4HMODULE_Set";
  107. $hash->{GetFn} = "N4HMODULE_Get";
  108. $hash->{AttrFn} = "N4HMODULE_Attr";
  109. $hash->{AttrList} = "IoDev dummy:1,0 Interval sendack:on,off setList ".
  110. "$readingFnAttributes ";
  111. # "OT:" .join(",", sort @otlist);
  112. $hash->{DbLog_splitFn} = "N4HMODULE_DbLog_splitFn";
  113. }
  114. ##################################################################################
  115. sub N4HMODULE_Define($$) {
  116. ##################################################################################
  117. my ($hash, $def) = @_;
  118. my @args = split("[ \t]+", $def);
  119. my ($name, $type, $n4hbus, $ot, $objadr) = @args;
  120. my ($readings) = "";
  121. if(@args < 4) {
  122. my $msg = "Usage: define <name> N4HMODULE <N4HBUS> <OBJECTTYPE> <OBJADDR>";
  123. Log3 $hash, 2, $msg;
  124. return $msg;
  125. }
  126. $hash->{VERSION} = $n4hmodule_Version;
  127. $hash->{Hardware} = "undefined";
  128. $hash->{Software} = "undefined";
  129. $hash->{STATE} = "Initializing";
  130. $hash->{NOTIFYDEV} = "global";
  131. $hash->{IODev} = $n4hbus;
  132. $hash->{OBJADR} = $objadr;
  133. $hash->{OT} = $ot;
  134. $hash->{DESC} = $OT_devices{$ot}{name};
  135. $hash->{OTcanSet} = $OT_devices{$ot}{OTcanSet};
  136. $hash->{OTcanReq} = $OT_devices{$ot}{OTcanReq};
  137. $hash->{Interval} = 0;
  138. $modules{N4HMODULE}{defptr}{$objadr} = $hash;
  139. AssignIoPort($hash, $n4hbus);
  140. Log3 $hash, 3, "N4HMODULE_Define -> $name ($ot) at device $n4hbus with objectadr $objadr";
  141. $hash->{helper}{from} = '';
  142. $hash->{helper}{value} = '';
  143. $hash->{helper}{cmd} = '';
  144. $hash->{helper}{ddata} = '';
  145. $hash->{helper}{pct} = 0;
  146. if ($hash->{OTcanSet}eq"true") {
  147. $hash->{helper}{state} = 'undefined';
  148. }
  149. $attr{$name}{room} = "net4home" if( !defined($attr{$name}{room}) );
  150. # Timer zum regelmäßigem aktualisieren auf dem Bus starten
  151. if (($ot == 24) or #Temperatur
  152. ($ot == 25) or #Licht
  153. ($ot == 26) or #Luftfeuchte
  154. ($ot == 240) or #Wind
  155. ($ot == 241) or #Sonne
  156. ($ot == 242) or #Luftdruck
  157. ($ot == 246) or #Luftdruck-Tendenz
  158. ($ot == 245)) { #Regenmenge l/h
  159. my $state_format;
  160. if( $ot == 24 ) {
  161. $state_format .= " " if( $state_format );
  162. $state_format .= "T: temperature";
  163. } elsif( $ot == 25 ) {
  164. $state_format .= " " if( $state_format );
  165. $state_format .= "L: brightness";
  166. } elsif( $ot == 26 ) {
  167. $state_format .= " " if( $state_format );
  168. $state_format .= "H: humidity";
  169. } elsif( $ot == 240 ) {
  170. $state_format .= " " if( $state_format );
  171. $state_format .= "W: wind";
  172. } elsif( $ot == 241 ) {
  173. $state_format .= " " if( $state_format );
  174. $state_format .= "L: brightness";
  175. } elsif( $ot == 242 ) {
  176. $state_format .= " " if( $state_format );
  177. $state_format .= "P: pressure";
  178. } elsif( $ot == 245 ) {
  179. $state_format .= " " if( $state_format );
  180. $state_format .= "R: rain";
  181. }
  182. $attr{$name}{stateFormat} = $state_format if( !defined($attr{$name}{stateFormat}) && defined($state_format) );
  183. RemoveInternalTimer($hash);
  184. $hash->{Interval} = 30;
  185. # Timer Zeitversetzt starten, damit nicht alles auf den Bus gleichzeit kommt 30 Sekunden + x
  186. Log3 $hash, 3, "N4HMODULE_Define (set timer) -> $name ($ot)";
  187. InternalTimer( gettimeofday() + 30 + int(rand(15)) , "N4HMODULE_Start", $hash, 0 );
  188. } elsif (( $ot == 34 ) or # TLH
  189. ( $ot == 3 ) or # Aktor, Relais
  190. ( $ot == 5)) { # Aktor, Dimmer
  191. # get initial value from bus after first start
  192. Log3 $hash, 3, "N4HMODULE_Define (get) -> $name ($ot)";
  193. InternalTimer( gettimeofday() + int(rand(10)) , "N4HMODULE_Start", $hash, 0 );
  194. }
  195. return undef;
  196. }
  197. ##################################################################################
  198. sub N4HMODULE_Start($)
  199. ##################################################################################
  200. {
  201. my ($hash) = @_;
  202. my $name = $hash->{NAME};
  203. my $interval = $hash->{Interval};
  204. my $ot = $hash->{OT};
  205. my $webCmd;
  206. $webCmd = AttrVal($name,"webCmd",undef);
  207. $interval = $attr{$name}{Interval} if( defined($attr{$name}{Interval}) );
  208. Log3 $hash, 5, "N4HMODULE (start): ($name)-> ".$interval." Sekunden";
  209. # reset timer if interval is defined
  210. if (($interval >= 30) and ($interval <= 86400)) {
  211. Log3 $hash, 5, "N4HMODULE (restart timer): ($name)-> ".$interval." Sekunden";
  212. RemoveInternalTimer( $hash );
  213. InternalTimer(gettimeofday() + $interval, "N4HMODULE_Start", $hash, 1 );
  214. N4HMODULE_Update( $hash );
  215. } else {
  216. # get initial values/state after start
  217. if ( $ot == 3 ) {
  218. # ot 3 = "Ausgang, Binär, Relais"
  219. if(!defined $webCmd){ $webCmd="toggle:on:off";}
  220. Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> status";
  221. N4HMODULE_Get($hash, $hash->{NAME}, "status");
  222. } elsif ( $ot == 5 ) {
  223. # ot 5 = "Ausgang, Dimmer"
  224. if(!defined $webCmd){ $webCmd="pct:toggle:on:off";}
  225. Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> status";
  226. N4HMODULE_Get($hash, $hash->{NAME}, "status");
  227. } elsif ( $ot == 34 ) {
  228. # ot 34 = "TLH-Regler H,Sollwert,Temperatur"
  229. if(!defined $webCmd){ $webCmd="off:heat:cool:auto";}
  230. Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> desired-temperature";
  231. N4HMODULE_Get($hash, $hash->{NAME}, "desired-temperature");
  232. }
  233. }
  234. }
  235. ##################################################################################
  236. sub N4HMODULE_Undefine($$) {
  237. ##################################################################################
  238. my ($hash,$arg) = @_;
  239. my $name = $hash->{NAME};
  240. Log3 $hash, 3, "N4HMODULE_Undefine -> $name";
  241. RemoveInternalTimer( $hash );
  242. delete($modules{N4HMODULE}{defptr}{$hash->{OBJADR}});
  243. return undef;
  244. }
  245. ##################################################################################
  246. sub N4HMODULE_DbLog_splitFn($$) {
  247. ##################################################################################
  248. my ($event, $device) = @_;
  249. my ($reading, $value, $unit) = "";
  250. my $hash = $defs{$device};
  251. my @parts = split(/ /,$event);
  252. $value = $parts[1];
  253. if ($event =~ m/temperature/) {
  254. $reading = 'temperature';
  255. $unit = '°C';
  256. } elsif ($event =~ m/humidity/) {
  257. $reading = 'humidity';
  258. $unit = '%';
  259. } elsif ($event =~ m/pressure/) {
  260. $reading = 'pressure';
  261. $unit = 'hPas';
  262. } elsif ($event =~ m/co2/) {
  263. $reading = 'co2';
  264. $unit = 'ppm';
  265. } elsif ($event =~ m/rain/) {
  266. $reading = 'rain';
  267. $unit = 'l/h';
  268. } elsif ($event =~ m/brightness/) {
  269. $reading = 'brightness';
  270. $unit = '';
  271. }
  272. return ($reading, $value, $unit);
  273. }
  274. ##################################################################################
  275. sub N4HMODULE_Parse($$) {
  276. ##################################################################################
  277. my ($iodev, $msg, $local) = @_;
  278. my $ioName = $iodev->{NAME};
  279. my $object = "";
  280. # Modul suchen
  281. my $type8 = hex(substr($msg,0,2));
  282. my $ipsrc = substr($msg,4,2).substr($msg,2,2);
  283. my $ipdst = hex(substr($msg,8,2).substr($msg,6,2));
  284. my $objsrc = hex(substr($msg,12,2).substr($msg,10,2));
  285. my $datalen = int(hex(substr($msg,14,2)));
  286. my $ddata = substr($msg,16, ($datalen*2));
  287. my $pos = $datalen*2+16;
  288. # Log3 "xx", 1, "(DECOMP2): T8($type8) - MI$ipsrc DST-> $ipdst SRC<-$objsrc";
  289. if ( length($msg) <= $pos ) {
  290. return undef;
  291. }
  292. my $csRX = substr($msg,$pos,2);
  293. my $csCalc = substr($msg,$pos+2,2);
  294. my $len = substr($msg,$pos+4,2);
  295. my $posb = substr($msg,$pos+6,2);
  296. if ($ipdst == 32767) {
  297. $object = $objsrc;
  298. } else {
  299. $object = $ipdst;
  300. }
  301. my $hash = $modules{N4HMODULE}{defptr}{$object};
  302. Log3 $hash, 5, "N4HMODULE (parse): $msg";
  303. if (!$hash) {
  304. $object = $objsrc;
  305. $hash = $modules{N4HMODULE}{defptr}{$objsrc};
  306. }
  307. if(!$hash) {
  308. my $ret = "Undefined ObjectAddress ($object)";
  309. return "";
  310. }
  311. my $devtype = $hash->{OT};
  312. N4HMODULE_ParsePayload($hash, $devtype, $ipsrc, $objsrc, $ddata);
  313. return $hash->{NAME};
  314. }
  315. ##################################################################################
  316. sub N4HMODULE_ParsePayload($@) {
  317. ##################################################################################
  318. my ($hash, $devtype, $ipsrc, $objsrc, $ddata) = @_;
  319. my $name = $hash->{NAME};
  320. my $ot = $hash->{OT};
  321. my $dev_funcion = hex(substr($ddata,0,2));
  322. my $newState = "";
  323. my $myval = "";
  324. my $objadr = $hash->{OBJADR};
  325. readingsBeginUpdate($hash);
  326. # +++++++ D0_GET_TYP
  327. if ($dev_funcion == hex("03")) {
  328. if (hex(substr($ddata,2,2)) == "1F") {
  329. $hash->{Hardware} = "UP-S4";
  330. } elsif (hex(substr($ddata,2,2)) == "29") {
  331. $hash->{Hardware} = "HS-AD3";
  332. }
  333. }
  334. # +++++++ D0_SET
  335. if ($dev_funcion == hex("32")) {
  336. Log3 $hash, 5, "N4HMODULE -> D0_SET ($name) (".$hash->{OT}." OBJ->$objsrc - IP->$ipsrc - ".$hash->{OBJADR}.")";
  337. if ( $ot == 34 && $objsrc != $objadr) {
  338. # ot 34 = "TLH-Regler H,Sollwert,Temperatur"
  339. my ($lastval) = sprintf "%.1f", hex(substr($ddata,4,2))/10;
  340. readingsBulkUpdate($hash, "desired-temperature", $lastval);
  341. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  342. readingsBulkUpdate($hash,"ddata", $ddata);
  343. } elsif ( $ot == 5 ) {
  344. # ot 5 = "Ausgang, Dimmer"
  345. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  346. readingsBulkUpdate($hash,"ddata", $ddata);
  347. readingsBulkUpdate($hash,"cmd", "D0_SET");
  348. my ($lastval) = sprintf "%.0f", hex(substr($ddata,2,2));
  349. if ( $lastval == 0 ) {
  350. readingsBulkUpdate($hash, "state", "off");
  351. $hash->{helper}{pct} = $hash->{READINGS}{pct}{VAL};
  352. readingsBulkUpdate($hash, "pct", 0);
  353. } elsif ( $lastval == 101 ) {
  354. readingsBulkUpdate($hash, "state", "on");
  355. readingsBulkUpdate($hash, "pct", $hash->{helper}{pct});
  356. } else {
  357. readingsBulkUpdate($hash, "state", "on");
  358. $hash->{helper}{pct} = $lastval;
  359. readingsBulkUpdate($hash, "pct", $lastval);
  360. }
  361. } elsif ( $ot == 34 ) {
  362. # do nothing - cmd only for ATe
  363. } elsif ( $ot == 95 ) {
  364. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  365. readingsBulkUpdate($hash,"ddata", $ddata);
  366. readingsBulkUpdate($hash,"cmd", "D0_SET");
  367. if (hex(substr($ddata,2,2))== hex("00")) {
  368. readingsBulkUpdate($hash,"state", "stop");
  369. $hash->{STATE} = "stop";
  370. readingsBulkUpdate($hash, "position", 50);
  371. } elsif (hex(substr($ddata,2,2))== hex("01")) {
  372. readingsBulkUpdate($hash,"state", "down");
  373. readingsBulkUpdate($hash, "position", 0);
  374. $hash->{STATE} = "down";
  375. } elsif (hex(substr($ddata,2,2))== hex("03")) {
  376. readingsBulkUpdate($hash,"state", "up");
  377. readingsBulkUpdate($hash, "position", 100);
  378. $hash->{STATE} = "up";
  379. }
  380. } else {
  381. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  382. readingsBulkUpdate($hash,"ddata", $ddata);
  383. readingsBulkUpdate($hash,"cmd", "D0_SET");
  384. if (hex(substr($ddata,2,2))== hex("00")) {
  385. readingsBulkUpdate($hash,"state", "off");
  386. $hash->{STATE} = "off";
  387. } else {
  388. readingsBulkUpdate($hash,"state", "on");
  389. $hash->{STATE} = "on";
  390. }
  391. }
  392. }
  393. # +++++++ D0_INC
  394. if ($dev_funcion == hex("33")) {
  395. if ( $ot == 5 ) {
  396. # ot 5 = "Ausgang, Dimmer"
  397. readingsBulkUpdate($hash, "state", "on");
  398. $hash->{helper}{pct} = $hash->{helper}{pct}+2;
  399. readingsBulkUpdate($hash, "pct", $hash->{helper}{pct});
  400. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  401. readingsBulkUpdate($hash,"ddata", $ddata);
  402. readingsBulkUpdate($hash,"cmd", "D0_INC");
  403. } else {
  404. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  405. readingsBulkUpdate($hash,"ddata", $ddata);
  406. readingsBulkUpdate($hash,"cmd", "D0_INC");
  407. }
  408. }
  409. # +++++++ D0_DEC
  410. if ($dev_funcion == hex("34")) {
  411. if ( $ot == 5 ) {
  412. # ot 5 = "Ausgang, Dimmer"
  413. readingsBulkUpdate($hash, "state", "on");
  414. $hash->{helper}{pct} = $hash->{helper}{pct}-2;
  415. readingsBulkUpdate($hash, "pct", $hash->{helper}{pct});
  416. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  417. readingsBulkUpdate($hash,"ddata", $ddata);
  418. readingsBulkUpdate($hash,"cmd", "D0_DEC");
  419. } else {
  420. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  421. readingsBulkUpdate($hash,"ddata", $ddata);
  422. readingsBulkUpdate($hash,"cmd", "D0_DEC");
  423. }
  424. }
  425. # +++++++ D0_TOGGLE
  426. if ($dev_funcion == hex("35")) {
  427. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  428. readingsBulkUpdate($hash,"ddata", $ddata);
  429. readingsBulkUpdate($hash,"cmd", "D0_TOGGLE");
  430. if ( $ot == 5 ) {
  431. # ot 5 = "Ausgang, Dimmer"
  432. if (ReadingsVal($name, "state", "") eq "on") {
  433. readingsBulkUpdate($hash,"state", "off");
  434. $hash->{helper}{pct} = $hash->{READINGS}{pct}{VAL};
  435. readingsBulkUpdate($hash, "pct", 0);
  436. $hash->{STATE} = "off";
  437. } elsif (ReadingsVal($name, "state", "") eq "off") {
  438. readingsBulkUpdate($hash,"state", "on");
  439. readingsBulkUpdate($hash, "pct", $hash->{helper}{pct});
  440. $hash->{STATE} = "on";
  441. }
  442. } else {
  443. if (ReadingsVal($name, "state", "") eq "on") {
  444. readingsBulkUpdate($hash,"state", "off");
  445. $hash->{STATE} = "off";
  446. } elsif (ReadingsVal($name, "state", "") eq "off") {
  447. readingsBulkUpdate($hash,"state", "on");
  448. $hash->{STATE} = "on";
  449. }
  450. }
  451. }
  452. # +++++++ D0_REQ
  453. if ($dev_funcion == hex("36")) {
  454. if ( $ot == 3 ) {
  455. # ot 3 = "Ausgang, Binär, Relais"
  456. } elsif ( $ot == 34 ) {
  457. # ot 34 = "TLH-Regler H,Sollwert,Temperatur"
  458. } else {
  459. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  460. readingsBulkUpdate($hash,"ddata", $ddata);
  461. readingsBulkUpdate($hash,"cmd", "D0_REQ");
  462. Log3 $hash, 5, "N4HMODULE -> D0_REQ ($name) (".$hash->{OT}.")";
  463. N4HMODULE_Update( $hash );
  464. }
  465. }
  466. # +++++++ D0_ACTOR_ACK
  467. if ($dev_funcion == hex("37")) {
  468. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  469. readingsBulkUpdate($hash,"ddata", $ddata);
  470. readingsBulkUpdate($hash,"cmd", "D0_ACTOR_ACK");
  471. Log3 $hash, 5, "N4HMODULE -> D0_ACTOR_ACK ($name) (".$hash->{OT}.")";
  472. if ( $ot == 3 ) {
  473. # ot 3 = "Ausgang, Binär, Relais"
  474. if ( (hex(substr($ddata,4,2))) == 0) {
  475. readingsBulkUpdate($hash, "state", "off");
  476. $hash->{STATE} = "off";
  477. } elsif ( (hex(substr($ddata,4,2))) == 1) {
  478. readingsBulkUpdate($hash, "state", "on");
  479. $hash->{STATE} = "on";
  480. }
  481. } elsif ( $ot == 5 ) {
  482. # ot 5 = "Ausgang, Dimmer"
  483. if ( hex(substr($ddata,4,2)) > 128) {
  484. my ($lastval) = sprintf "%.0f", hex(substr($ddata,4,2));
  485. readingsBulkUpdate($hash, "pct", $lastval-128);
  486. $hash->{helper}{pct} = $lastval-128;
  487. readingsBulkUpdate($hash, "state", "on");
  488. $hash->{STATE} = "on";
  489. } else {
  490. my ($lastval) = sprintf "%.0f", hex(substr($ddata,4,2));
  491. $hash->{helper}{pct} = $lastval;
  492. readingsBulkUpdate($hash, "state", "off");
  493. $hash->{STATE} = "off";
  494. }
  495. } elsif ( $ot == 34 ) {
  496. # ot 34 = "TLH-Regler H,Sollwert,Temperatur"
  497. my ($lastval) = sprintf "%.1f", hex(substr($ddata,4,2))/10;
  498. readingsBulkUpdate($hash, "desired-temperature", $lastval);
  499. if ( (hex(substr($ddata,6,2))) == 0) {
  500. readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "0");
  501. readingsBulkUpdate($hash, "state", "off");
  502. $hash->{STATE} = "off";
  503. } elsif ( (hex(substr($ddata,6,2))) == 1) {
  504. readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "1");
  505. readingsBulkUpdate($hash, "state", "heat");
  506. $hash->{STATE} = "heat";
  507. } elsif ( (hex(substr($ddata,6,2))) == 2) {
  508. readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "2");
  509. readingsBulkUpdate($hash, "state", "cool");
  510. $hash->{STATE} = "cool";
  511. } elsif ( (hex(substr($ddata,6,2))) == 3) {
  512. readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "3");
  513. readingsBulkUpdate($hash, "state", "auto");
  514. $hash->{STATE} = "auto";
  515. };
  516. };
  517. }
  518. # +++++++ D0_SENSOR_ACK
  519. if ($dev_funcion == hex("41")) {
  520. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  521. readingsBulkUpdate($hash,"ddata", $ddata);
  522. readingsBulkUpdate($hash,"cmd", "D0_SENSOR_ACK");
  523. Log3 $hash, 5, "N4HMODULE -> D0_SENSOR_ACK ($name) (".$hash->{OT}.")";
  524. N4HMODULE_Update( $hash );
  525. }
  526. # +++++++ D0_LOCK_STATE_ACK
  527. if ($dev_funcion == hex("44")) {
  528. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  529. readingsBulkUpdate($hash,"ddata", $ddata);
  530. readingsBulkUpdate($hash,"cmd", "D0_LOCK_STATE_ACK");
  531. Log3 $hash, 5, "N4HMODULE -> D0_LOCK_STATE_ACK ($name) (".$hash->{OT}.")";
  532. }
  533. # +++++++ D0_VALUE_ACK (101)
  534. if ($dev_funcion == hex("65")) {
  535. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  536. readingsBulkUpdate($hash,"ddata", $ddata);
  537. readingsBulkUpdate($hash,"cmd", "D0_VALUE_ACK");
  538. if ( (hex(substr($ddata,4,2))) == 0) {
  539. readingsBulkUpdate($hash, "state", "off");
  540. $hash->{STATE} = "off";
  541. } elsif ( (hex(substr($ddata,4,2))) == 1) {
  542. readingsBulkUpdate($hash, "state", "on");
  543. $hash->{STATE} = "on";
  544. }
  545. my ($valtype, $lastval) = N4HMODULE_paramToText($hash, $ddata);
  546. readingsBulkUpdate($hash, $valtype, $lastval);
  547. readingsBulkUpdate($hash, "state", $lastval);
  548. }
  549. # +++++++ D0_VALUE_REQ
  550. if ($dev_funcion == hex("66")) {
  551. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  552. readingsBulkUpdate($hash,"ddata", $ddata);
  553. readingsBulkUpdate($hash,"cmd", "D0_VALUE_REQ");
  554. Log3 $hash, 5, "N4HMODULE -> D0_VALUE_REQ ($name) (".$hash->{OT}.")";
  555. N4HMODULE_Update( $hash );
  556. }
  557. # +++++++ SetN
  558. if ($dev_funcion == hex("3b")) {
  559. if( defined($objsrc)) {readingsBulkUpdate($hash,"from", $objsrc);}
  560. readingsBulkUpdate($hash,"ddata", $ddata);
  561. # ot 34 = "TLH-Regler H,Sollwert,Temperatur"
  562. if ($ot == 34) {
  563. readingsBulkUpdate($hash,"cmd", "SetN");
  564. Log3 $hash, 5, "N4HMODULE -> SetN ($name) (".$hash->{OT}.")";
  565. if ( (hex(substr($ddata,2,2))) == 0) {
  566. readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "0");
  567. readingsBulkUpdate($hash, "state", "off");
  568. $hash->{STATE} = "off";
  569. } elsif ( (hex(substr($ddata,2,2))) == 1) {
  570. readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "1");
  571. readingsBulkUpdate($hash, "state", "heat");
  572. $hash->{STATE} = "heat";
  573. } elsif ( (hex(substr($ddata,2,2))) == 2) {
  574. readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "2");
  575. readingsBulkUpdate($hash, "state", "cool");
  576. $hash->{STATE} = "cool";
  577. } elsif ( (hex(substr($ddata,2,2))) == 3) {
  578. readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "3");
  579. readingsBulkUpdate($hash, "state", "auto");
  580. $hash->{STATE} = "auto";
  581. };
  582. } else {
  583. readingsBulkUpdate($hash,"cmd", "SetN");
  584. }
  585. }
  586. readingsEndUpdate( $hash , 1);
  587. return undef;
  588. }
  589. ##################################################################################
  590. sub N4HMODULE_paramToText($@) {
  591. ##################################################################################
  592. my ($hash, $ddata) = @_;
  593. my $name = $hash->{NAME};
  594. my $rettype = "unbekannte Formel / Parameter";
  595. my $w = hex(substr($ddata,6,2))*256+hex(substr($ddata,8,2));
  596. my $ret = $w;
  597. my $t;
  598. # +++++++++++++++++++ Licht analog - IN_HW_NR_IS_LICHT_ANALOG
  599. if (hex(substr($ddata,2,2)) == 5 ){
  600. $rettype = "Brightness";
  601. }
  602. # +++++++++++++++++++ Uhrzeit - IN_HW_NR_IS_CLOCK
  603. elsif (hex(substr($ddata,2,2)) == 6 ){
  604. $rettype = "Uhrzeit";
  605. }
  606. # +++++++++++++++++++ RF-Tag Reader - IN_HW_NR_IS_RF_TAG_READER
  607. elsif (hex(substr($ddata,2,2)) == 7 ){
  608. $ret = uc substr($ddata,6,10);
  609. if ( (hex(substr($ddata,18,2)) & 6) == 0) {
  610. $ret = $ret." vorgehalten";
  611. } elsif ( (hex(substr($ddata,18,2)) & 6) == 2) {
  612. $ret = $ret." lang vorgehalten";
  613. } elsif ( (hex(substr($ddata,18,2)) & 6) == 4) {
  614. $ret = $ret." weggezogen nach kurz";
  615. }
  616. else { $ret = $ret." ".hex(substr($ddata,18,2)) }
  617. $rettype = "RF-Tag";
  618. }
  619. # +++++++++++++++++++ Temperatur - IN_HW_NR_IS_TEMP
  620. elsif (hex(substr($ddata,2,2)) == 9 ){
  621. if (hex(substr($ddata,4,2)) == 5 ){ # USE_FROMEL_TEMP_UI16
  622. if (hex(substr($ddata,6,2)) == 0xff ) {
  623. $t = (hex(substr($ddata,8,2))-0xff)/16;
  624. }
  625. else {
  626. $t = $w/16;
  627. }
  628. $ret = sprintf "%.1f °C", $t;
  629. $rettype = "temperature";
  630. }
  631. }
  632. # +++++++++++++++++++ Feuchte - IN_HW_NR_IS_HUMIDITY
  633. elsif (hex(substr($ddata,2,2)) == 11 ){
  634. $ret = $w." %";
  635. $rettype = "humidity";
  636. }
  637. # +++++++++++++++++++ Wind - IN_HW_NR_IS_KMH
  638. elsif (hex(substr($ddata,2,2)) == 41 ){
  639. if (hex(substr($ddata,4,2)) == 6 ){ # USE_FROMEL_RAW_16BIT
  640. $ret = $w." km/h";
  641. }
  642. elsif (hex(substr($ddata,4,2)) == 7 ){ # USE_FROMEL_16BIT_X8
  643. $t = $w/8;
  644. $ret = $t." km/h";
  645. }
  646. $rettype = "wind";
  647. }
  648. # +++++++++++++++++++ Luftdruck - IN_HW_NR_IS_PRESS_MBAR
  649. elsif (hex(substr($ddata,2,2)) == 48 ){
  650. $t = $w/10;
  651. $ret = $t." hPas";
  652. $rettype = "pressure";
  653. }
  654. # +++++++++++++++++++ pressure Tendenz - IN_HW_NR_IS_PRESS_TENDENZ
  655. elsif (hex(substr($ddata,2,2)) == 49 ){
  656. $rettype = "pressure (Tendenz)";
  657. }
  658. # +++++++++++++++++++ Uhrzeit - Sonnenaufgang heute
  659. elsif (hex(substr($ddata,2,2)) == 50 ){
  660. $rettype = "Sonnenaufgang";
  661. }
  662. # +++++++++++++++++++ Uhrzeit - Sonnenuntergang heute
  663. elsif (hex(substr($ddata,2,2)) == 51 ){
  664. $rettype = "Sonnenuntergang";
  665. }
  666. # +++++++++++++++++++ Regenmenge (Liter/Stunde) - VAL_IS_MENGE_LITER
  667. elsif (hex(substr($ddata,2,2)) == 53 ){
  668. if (hex(substr($ddata,4,2)) == 8 ){ # USE_FROMEL_16BIT_X10
  669. $t = $w/10;
  670. $ret = $t." l/h";
  671. }
  672. else {
  673. $ret = $w." l/h";
  674. }
  675. $rettype = "rain";
  676. }
  677. return ($rettype, $ret);
  678. }
  679. ##################################################################################
  680. sub N4HMODULE_Set($@) {
  681. ##################################################################################
  682. my ($hash, @a) = @_;
  683. return "\"set $a[0]\" needs at least two parameters" if(@a < 2);
  684. my $name = shift(@a);
  685. my $cmd = shift(@a);
  686. my $ext = shift(@a);
  687. # Log3 $hash, 5, "N4HMODULE (set): (Name: $name) (CMD: $cmd) (ext: $ext)";
  688. my $ot = $hash->{OT};
  689. my $ipdst = $hash->{OBJADR};
  690. my $ddata = "";
  691. my @sets;
  692. my $fieldcmd = "";
  693. my $fieldname;
  694. my $fieldset;
  695. my $setfield;
  696. my $devtype = $OT_devices{$ot};
  697. if ($ot == 3 || $ot == 4 || $ot == 5 || $ot == 95 || $ot == 100) {
  698. for my $field (@{$devtype->{fields}}) {
  699. $setfield = $field->{set};
  700. if (defined($setfield)) {
  701. push(@sets,$field->{set});
  702. $setfield = ( split /:/, $setfield, 2 )[0];
  703. if ($setfield eq $cmd) {
  704. $fieldname = $field->{text};
  705. $fieldcmd = $field->{cmd};
  706. $fieldset = $field->{set};
  707. }
  708. }
  709. }
  710. if ($fieldcmd ne "" && $cmd ne "?") {
  711. if (defined($ext)) {
  712. if ($cmd eq "pct") {
  713. $fieldcmd = "$fieldcmd".sprintf ("%02x", ($ext))."00";
  714. $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
  715. readingsSingleUpdate($hash, "pct", "$ext", 1);
  716. Log3 $hash, 5, "N4HMODULE (set): $name to $cmd ($ext%) ($ddata)";
  717. } else {
  718. $ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd);
  719. Log3 $hash, 5, "N4HMODULE (set): $name to $cmd/$ext (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)";
  720. }
  721. } else {
  722. if ($cmd eq "toggle") {
  723. $ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd);
  724. Log3 $hash, 5, "N4HMODULE (set): $name $cmd ($ddata)";
  725. } else {
  726. Log3 $hash, 5, "N4HMODULE (set): $name to $cmd (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)";
  727. $ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd);
  728. readingsSingleUpdate($hash, "state", "$cmd", 1);
  729. }
  730. }
  731. IOWrite($hash, $ipdst, $ddata, 0);
  732. return undef;
  733. }
  734. else {
  735. return SetExtensions($hash, join(" ", @sets), $name, $cmd, @a);
  736. }
  737. } elsif ($ot == 34) {
  738. for my $field (@{$devtype->{fields}}) {
  739. $setfield = $field->{set};
  740. if (defined($setfield)) {
  741. push(@sets,$field->{set});
  742. $setfield = ( split /:/, $setfield, 2 )[0];
  743. if ($setfield eq $cmd) {
  744. $fieldname = $field->{text};
  745. $fieldcmd = $field->{cmd};
  746. $fieldset = $field->{set};
  747. }
  748. }
  749. }
  750. if ($fieldcmd ne "" && $cmd ne "?") {
  751. Log3 $hash, 5, "N4HMODULE (set): $name to $cmd (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)";
  752. if ($cmd eq "desired-temperature") {
  753. $fieldcmd = "$fieldcmd".sprintf ("%02x", ($ext*10))."00";
  754. $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
  755. readingsSingleUpdate($hash, "desired-temperature", "$ext", 1);
  756. } else {
  757. $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
  758. }
  759. IOWrite($hash, $ipdst, $ddata, 0);
  760. return undef;
  761. }
  762. else {
  763. return SetExtensions($hash, join(" ", @sets), $name, $cmd, @a);
  764. }
  765. }
  766. elsif ($ot == 24 || $ot == 25 || $ot == 26 || $ot == 240 || $ot == 242 || $ot == 245) {
  767. if ($cmd ne "?") {
  768. Log3 $hash, 5, "N4HMODULE (set): $name to $cmd";
  769. N4HMODULE_Update($hash, $cmd);
  770. }
  771. return undef;
  772. }
  773. elsif ($ot == 999) {
  774. if ($cmd ne "?") {
  775. Log3 $hash, 5, "N4HMODULE (set n56): $name to $cmd";
  776. N4HMODULE_Update($hash, $cmd);
  777. }
  778. return undef;
  779. }
  780. return undef;
  781. }
  782. ##################################################################################
  783. sub N4HMODULE_Get($$@) {
  784. ##################################################################################
  785. my ( $hash, $name, $opt, @args ) = @_;
  786. my $ot = $hash->{OT};
  787. my $ipdst = $hash->{OBJADR};
  788. my $ddata = "";
  789. my $fieldcmd = "";
  790. my $list = "";
  791. my $list = "desired-temperature:noArg";
  792. return "\"get $name\" needs at least one argument" unless(defined($opt));
  793. if (( $ot == 3 ) or
  794. ( $ot == 5)) {
  795. if ($opt eq "status") {
  796. $fieldcmd = "360000";
  797. $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
  798. Log3 $hash, 5, "N4HMODULE (get): $opt from $name ($ddata)";
  799. IOWrite($hash, $ipdst, $ddata, 0);
  800. } else {
  801. return "unknown argument $opt, choose one of gettype:noArg status:noArg";
  802. }
  803. } elsif ( $ot == 34 ) {
  804. if ($opt eq "desired-temperature") {
  805. $fieldcmd = "360000";
  806. $ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
  807. Log3 $hash, 5, "N4HMODULE (get): $opt from $name ($ddata)";
  808. IOWrite($hash, $ipdst, $ddata, 0);
  809. } else {
  810. return "unknown argument $opt, choose one of gettype:noArg desired-temperature:noArg";
  811. }
  812. } else {
  813. return "unknown argument, choose one of gettype:noArg";
  814. }
  815. return undef;
  816. }
  817. ##################################################################################
  818. sub N4HMODULE_Update($@) {
  819. ##################################################################################
  820. my ($hash, @a) = @_;
  821. my $value = shift(@a);
  822. my $name = $hash->{NAME};
  823. return unless (defined($hash->{NAME}) );
  824. if (defined $value) {
  825. Log3 $hash, 5, "N4HMODULE (update): ($name) ($value)";
  826. } else {
  827. Log3 $hash, 5, "N4HMODULE (update): ($name)";
  828. }
  829. my $ot = $hash->{OT};
  830. my $ipdst = $hash->{OBJADR};
  831. my $ddata = "";
  832. my $cs = "";
  833. my $cmd = "";
  834. if ($ot == 3) {
  835. # +++++++ 69 D0_STATUS_INFO (105)
  836. if ( (hex(substr($ddata,6,2))) == 0) {
  837. readingsBulkUpdate($hash, "state", "off");
  838. $hash->{STATE} = "off";
  839. } elsif ( (hex(substr($ddata,6,2))) == 1) {
  840. readingsBulkUpdate($hash, "state", "on");
  841. $hash->{STATE} = "on";
  842. }
  843. } elsif ($ot == 24) {
  844. # +++++++ 65 D0_VALUE_ACK (101)
  845. # +++++++ 09 Temperatur
  846. # +++++++ 05 USE_FROMEL_TEMP_UI16
  847. my $ddata1 = "650905";
  848. if (defined $value) {
  849. $cmd = $value;
  850. readingsSingleUpdate($hash, "temperature", "$cmd °C", 1);
  851. }
  852. else {
  853. ($cmd, undef) = split(/ /, ReadingsVal($name , "temperature", "")); }
  854. if (defined $cmd) {
  855. if ($cmd >= 0) {
  856. $cs = $cmd*16;
  857. $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) );
  858. }
  859. elsif ($cmd < 0) {
  860. $cs = 0xff+($cmd*16);
  861. $ddata1 = $ddata1.sprintf ("%02X", 0xff );
  862. }
  863. $ddata1 = $ddata1.sprintf ("%02X", ( ($cs>>0) & 255 ) );
  864. $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
  865. Log3 $hash, 5, "N4HMODULE (set temperature): $name to $cmd - $ddata, $ddata1, $ipdst";
  866. IOWrite($hash, 32767, $ddata, $ipdst);
  867. }
  868. return undef;
  869. }
  870. elsif ($ot == 25) { # Licht
  871. # +++++++ 65 D0_VALUE_ACK
  872. # +++++++ 05 Licht
  873. # +++++++ 01 USE_FROMEL
  874. my $ddata1 = "650506";
  875. if (defined $value) {
  876. $cmd = $value;
  877. readingsSingleUpdate($hash, "Brightness", "$cmd", 1);
  878. }
  879. else {
  880. ($cmd, undef) = split(/ /, ReadingsVal($name , "Brightness","")); }
  881. if (defined $cmd) {
  882. my $cs = $cmd;
  883. $ddata1 = $ddata1.sprintf ("%02X", (0x00) );
  884. $ddata1 = $ddata1.sprintf ("%02X", ( ($cs) ) );
  885. $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
  886. Log3 $hash, 5, "N4HMODULE (set brightness): $name to $cmd - $ddata, $ddata1, $ipdst";
  887. IOWrite($hash, 32767, $ddata, $ipdst);
  888. }
  889. return undef;
  890. }
  891. elsif ($ot == 26) { # Luftfeuchte
  892. # +++++++ 65 D0_VALUE_ACK
  893. # +++++++ 0B Luftfeuchte
  894. # +++++++ 01 USE_FROMEL
  895. my $ddata1 = "650B01";
  896. if (defined $value) {
  897. $cmd = $value;
  898. readingsSingleUpdate($hash, "humidity", "$cmd %", 1);
  899. }
  900. else {
  901. ($cmd, undef) = split(/ /, ReadingsVal($name , "humidity","")); }
  902. if (defined $cmd) {
  903. my $cs = $cmd;
  904. $ddata1 = $ddata1.sprintf ("%02X", (0x00) );
  905. $ddata1 = $ddata1.sprintf ("%02X", ( ($cs) ) );
  906. $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
  907. Log3 $hash, 5, "N4HMODULE (set humidity): $name to $cmd - $ddata, $ddata1, $ipdst";
  908. IOWrite($hash, 32767, $ddata, $ipdst);
  909. }
  910. return undef;
  911. }
  912. elsif ($ot == 34) { # TLH-Regler H,Sollwert,Temperatur
  913. # +++++++ 37 D0_ACTOR_ACK (101)
  914. Log3 $hash, 5, "N4HMODULE (xxx): $name to $cmd - $ddata, $ipdst";
  915. my $ddata1 = "3700";
  916. if (defined $value) {
  917. $cmd = $value;
  918. readingsSingleUpdate($hash, "desired-temp", "$cmd", 1);
  919. }
  920. else {
  921. ($cmd, undef) = split(/ /, ReadingsVal($name , "desired-temp", "")); }
  922. if (defined $cmd) {
  923. if ($cmd >= 0) {
  924. $cs = $cmd*16;
  925. $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) );
  926. }
  927. elsif ($cmd < 0) {
  928. $cs = 0xff+($cmd*16);
  929. $ddata1 = $ddata1.sprintf ("%02X", 0xff );
  930. }
  931. $ddata1 = $ddata1.sprintf ("%02X", ( ($cs>>0) & 255 ) );
  932. $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
  933. Log3 $hash, 5, "N4HMODULE (set desired-temp): $name to $cmd - $ddata, $ddata1, $ipdst";
  934. IOWrite($hash, 32767, $ddata, $ipdst);
  935. }
  936. return undef;
  937. }
  938. elsif ($ot == 240) { # Wind
  939. # +++++++ 65 D0_VALUE_ACK
  940. # +++++++ 29 Windgeschwindigkeit
  941. # +++++++ 01 USE_FROMEL
  942. my $ddata1 = "652907";
  943. if (defined $value) {
  944. $cmd = $value;
  945. readingsSingleUpdate($hash, "wind", "$cmd km/h", 1);
  946. }
  947. else {
  948. ($cmd, undef) = split(/ /, ReadingsVal($name , "wind","")); }
  949. if (defined $cmd) {
  950. my $cs = $cmd*8;
  951. $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) );
  952. $ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) );
  953. $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
  954. Log3 $hash, 5, "N4HMODULE (set wind): $name to $cmd - $ddata, $ddata1, $ipdst";
  955. IOWrite($hash, 32767, $ddata, $ipdst);
  956. }
  957. return undef;
  958. }
  959. elsif ($ot == 242) { # Luftdruck
  960. # +++++++ 65 D0_VALUE_ACK
  961. # +++++++ 30 Luftdruck
  962. # +++++++ 01 USE_FROMEL
  963. my $ddata1 = "653001";
  964. if (defined $value) {
  965. $cmd = $value;
  966. readingsSingleUpdate($hash, "pressure", "$cmd hPas", 1);
  967. }
  968. else {
  969. ($cmd, undef) = split(/ /, ReadingsVal($name , "pressure","")); }
  970. if (defined $cmd) {
  971. my $cs = $cmd*10;
  972. $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) );
  973. $ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) );
  974. $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
  975. Log3 $hash, 5, "N4HMODULE (set pressure): $name to $cmd - $ddata, $ddata1, $ipdst";
  976. IOWrite($hash, 32767, $ddata, $ipdst);
  977. }
  978. return undef;
  979. }
  980. elsif ($ot == 245) { # Regenmenge
  981. # +++++++ 65 D0_VALUE_ACK
  982. # +++++++ 35 Regenmenge
  983. # +++++++ 01 USE_FROMEL
  984. my $ddata1 = "653508";
  985. if (defined $value) {
  986. $cmd = $value;
  987. readingsSingleUpdate($hash, "rain", "$cmd l/h", 1);
  988. }
  989. else {
  990. ($cmd, undef) = split(/ /, ReadingsVal($name , "rain","")); }
  991. if (defined $cmd) {
  992. my $cs = $cmd*10;
  993. $ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) );
  994. $ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) );
  995. $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
  996. Log3 $hash, 5, "N4HMODULE (set rain): $name to $cmd - $ddata, $ddata1, $ipdst";
  997. IOWrite($hash, 32767, $ddata, $ipdst);
  998. }
  999. return undef;
  1000. }
  1001. elsif ($ot == 999) { # N56
  1002. # +++++++ 65 D0_SENSOR_ACK (41)
  1003. my $ddata1 = "41";
  1004. if (defined $value) {
  1005. $cmd = $value;
  1006. # $cmd = N4HMODULE_ParseN56(substr($hash->{READINGS}{ddata},2,18));
  1007. readingsSingleUpdate($hash, "value", "$cmd", 1);
  1008. } else{
  1009. return undef;
  1010. }
  1011. my $n56 = N4HMODULE_CreateN56($value,0,0,0,2);
  1012. $ddata1 = $ddata1.$n56;
  1013. $ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
  1014. $ipdst = 1;
  1015. Log3 $hash, 5, "N4HMODULE (set N56): $name to $cmd - $ddata, $ddata1, $ipdst, $n56";
  1016. IOWrite($hash, 1, $ddata, $ipdst);
  1017. return undef;
  1018. }
  1019. return undef;
  1020. }
  1021. ##################################################################################
  1022. sub N4HMODULE_Attr(@) {
  1023. ##################################################################################
  1024. my ($cmd,$name,$attr_name,$attr_value) = @_;
  1025. if($cmd eq "set") {
  1026. if($attr_name eq "Interval") {
  1027. if (($attr_value < 30) or ($attr_value > 86400)) {
  1028. my $err = "Invalid time $attr_value to $attr_name. Must be > 30 and < 86400.";
  1029. return $err;
  1030. }
  1031. }
  1032. }
  1033. return undef;
  1034. }
  1035. ##################################################################################
  1036. sub N4HMODULE_CreateN56(@) {
  1037. ##################################################################################
  1038. my ($val32,$exp8,$expb,$findex,$decimal) = @_;
  1039. my $ddata = "";
  1040. my $myformel = "";
  1041. # print "\n".$val32*($expb**$exp8)."\n";
  1042. my $unsigned = $val32 < 0 ? 2 ** 32 + $val32 : $val32;
  1043. my $myddata = sprintf("%08X", $unsigned);
  1044. my $ps = $exp8 < 0 ? 2 ** 8 + $exp8 : $exp8;
  1045. $myformel = sprintf("%04X",$findex);
  1046. $ddata = substr($myddata,6,2).substr($myddata,4,2).substr($myddata,2,2).substr($myddata,0,2).sprintf("%02X",$ps).substr($myformel,2,2).substr($myformel,0,2).sprintf("%02X",$decimal).sprintf("%02X",$expb);
  1047. return ($ddata);
  1048. }
  1049. ##################################################################################
  1050. sub N4HMODULE_ParseN56($) {
  1051. ##################################################################################
  1052. my ($n56) = @_;
  1053. my $exp8 = hex(substr($n56,8,2));
  1054. my $val32 = hex(substr($n56,6,2).substr($n56,4,2).substr($n56,2,2).substr($n56,0,2));
  1055. my $expb = hex(substr($n56,16,2));
  1056. my $formel = hex(substr($n56,10,2)).hex(substr($n56,12,2));;
  1057. my $deci = hex(substr($n56,14,2));
  1058. $exp8 = $exp8 >> 7 ? $exp8 - 2 ** 8 : $exp8;
  1059. $val32 = $val32 >> 31 ? $val32 - 2 ** 32 : $val32;
  1060. if ($expb eq 0) {
  1061. $expb = 10;
  1062. } elsif ($expb eq 1) {
  1063. $expb = 2;
  1064. }
  1065. my $ddata = sprintf("Wert: %.".$deci."f", ($val32*($expb**$exp8)))." (Formel: $formel)";
  1066. return ($ddata);
  1067. }
  1068. ##################################################################################
  1069. ##################################################################################
  1070. 1;
  1071. =pod
  1072. =item device
  1073. =item summary Module to emulate net4home Actors and Sensors via N4HBUS
  1074. =item summary_DE Modul zum emulieren von net4home Aktoren und Sensoren ueber N4HBUS
  1075. =begin html
  1076. <a name="N4HMODULE"></a>
  1077. <h3>N4HMODULE</h3>
  1078. fhem-Module to communicate with net4home modules via IP
  1079. <br /><br />
  1080. <ul>
  1081. <br />
  1082. <a name="N4HMODULE_Define"></a>
  1083. <b>Define</b>
  1084. <ul>
  1085. <code>define &lt;name&gt; N4HMODULE &lt;device&gt; &lt;type&gt; &lt;objectaddress&gt;</code><br />
  1086. <br />
  1087. Defines a net4home device connected to a <a href="#N4HBUS">N4HBUS</a> device <br /><br />
  1088. Examples:
  1089. <ul>
  1090. <code>define n4h_28204 N4HMODULE n4h 24 28204</code><br />
  1091. </ul>
  1092. Currently the following values are supported:<br />
  1093. <b>Measurement</b><br />
  1094. <ul>
  1095. <li> 24 - Measurement,Temperature</li>
  1096. <li> 25 - Measurement,Brightness</li>
  1097. <li> 26 - Measurement,Humidity</li>
  1098. <li> 34 - TLH-Regler H,Sollwert,Temperatur</li>
  1099. <li> 95 - Ausgang, Jal, Motor AJ3</li>
  1100. <li>210 - RF Reader</li>
  1101. <li>240 - Measurement,Wind</li>
  1102. <li>242 - Measurement,Pressure</li>
  1103. <li>245 - Measurement,Rain</li>
  1104. </ul>
  1105. </ul><br />
  1106. <a name="N4HMODULE_Readings"></a>
  1107. <b>Readings</b>
  1108. <ul>
  1109. <li>The readings are dependent of the object of the net4home bus module.<br /></li>
  1110. </ul><br />
  1111. <a name="N4HMODULE_Attr"></a>
  1112. <b>Attributes</b>
  1113. <ul>
  1114. <li>interval<br>
  1115. the interval in seconds used to send values to bus.</li>
  1116. </ul>
  1117. </ul>
  1118. =end html
  1119. =begin html_DE
  1120. <a name="N4HMODULE"></a>
  1121. <h3>N4HMODULE</h3>
  1122. fhem-Modul zur Kommunikation mit dem net4home Bus über IP
  1123. <br /><br />
  1124. <ul>
  1125. <br />
  1126. <a name="N4HMODULE_Define"></a>
  1127. <b>Define</b>
  1128. <ul>
  1129. <code>define &lt;name&gt; N4HMODULE &lt;device&gt; &lt;type&gt; &lt;objectaddress&gt;</code><br />
  1130. <br />
  1131. Erstellt ein net4home Modul-Device welches mit dem <a href="#N4HBUS">N4HBUS</a> Device kommuniziert.
  1132. Beispiel:
  1133. <ul>
  1134. <code>define MyN4HMODULEice N4HMODULE 24 26004</code><br />
  1135. </ul>
  1136. Derzeit werden folgende Typen unterst&uuml;tzt:<br />
  1137. <b>Messwerte</b><br />
  1138. <ul>
  1139. <li> 24 - Messwert,Temperatur</li>
  1140. <li> 25 - Messwert,Helligkeit</li>
  1141. <li> 26 - Messwert,Feuchte</li>
  1142. <li> 34 - TLH-Regler H,Sollwert,Temperatur</li>
  1143. <li> 95 - Ausgang, Jal, Motor AJ3</li>
  1144. <li>210 - RF Reader</li>
  1145. <li>240 - Messwert,Wind</li>
  1146. <li>242 - Messwert,Luftdruck</li>
  1147. <li>245 - Messwert,Regenmenge</li>
  1148. </ul>
  1149. </ul><br />
  1150. <a name="N4HMODULE_Readings"></a>
  1151. <b>Readings</b>
  1152. <ul>
  1153. <li>Die Readings werden Abhängig vom Modultyp angegeben.<br /></li>
  1154. </ul><br />
  1155. <a name="N4HMODULE_Attr"></a>
  1156. <b>Attributes</b>
  1157. <ul>
  1158. <li>Interval<br>
  1159. Das Interval bestimmt bei Messwerten die Zeit zwischen dem Senden der Daten auf den Bus. Ist kein Attribut definiert, so wird der Standardwert genutzt.</li>
  1160. </ul>
  1161. </ul>
  1162. =end html_DE
  1163. =cut