23_LUXTRONIK2.pm 96 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352
  1. ###############################################################
  2. # $Id: 23_LUXTRONIK2.pm 13384 2017-02-11 10:04:02Z tupol $Date: $
  3. #
  4. # 23_LUXTRONIK2.pm
  5. #
  6. # (c) 2012,2014 Torsten Poitzsch (torsten poitzsch at gmx . de)
  7. # (c) 2012-2013 Jan-Hinrich Fessel (oskar at fessel . org)
  8. #
  9. # Copyright notice
  10. #
  11. # The modul reads and writes parameters of the heat pump controller
  12. # Luxtronik 2.0 used in Alpha Innotec and Siemens Novelan (WPR NET) heat pumps.
  13. #
  14. # This script is free software; you can redistribute it and/or modify
  15. # it under the terms of the GNU General Public License as published by
  16. # the Free Software Foundation; either version 2 of the License, or
  17. # (at your option) any later version.
  18. #
  19. # The GNU General Public License can be found at
  20. # http://www.gnu.org/copyleft/gpl.html.
  21. # A copy is found in the textfile GPL.txt and important notices to the license
  22. # from the author is found in LICENSE.txt distributed with these scripts.
  23. #
  24. # This script is distributed in the hope that it will be useful,
  25. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. # GNU General Public License for more details.
  28. #
  29. # This copyright notice MUST APPEAR in all copies of the script!
  30. #
  31. ################################################################
  32. ##############################################
  33. package main;
  34. use strict;
  35. use warnings;
  36. use Blocking;
  37. use IO::Socket;
  38. use Time::HiRes qw/ time /;
  39. use Net::Telnet;
  40. sub LUXTRONIK2_doStatisticThermalPower ($$$$$$$$$);
  41. sub LUXTRONIK2_doStatisticMinMax ($$$);
  42. sub LUXTRONIK2_doStatisticMinMaxSingle ($$$$);
  43. sub LUXTRONIK2_storeReadings ($$$$$$);
  44. sub LUXTRONIK2_doStatisticDelta ($$$$$) ;
  45. sub LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$);
  46. #List of firmware versions that are known to be compatible with this modul
  47. my $testedFirmware = "#V1.51#V1.54C#V1.60#V1.61#V1.64#V1.69#V1.70#V1.73#V1.77#V1.80#";
  48. my $compatibleFirmware = "#V1.51#V1.54C#V1.60#V1.61#V1.64#V1.69#V1.70#V1.73#V1.77#V1.80#";
  49. sub ##########################################
  50. LUXTRONIK2_Log($$$)
  51. {
  52. my ( $hash, $loglevel, $text ) = @_;
  53. my $xline = ( caller(0) )[2];
  54. my $xsubroutine = ( caller(1) )[3];
  55. my $sub = ( split( ':', $xsubroutine ) )[2];
  56. $sub =~ s/LUXTRONIK2_//;
  57. my $instName = ( ref($hash) eq "HASH" ) ? $hash->{NAME} : $hash;
  58. Log3 $instName, $loglevel, "LUXTRONIK2 $instName: $sub.$xline " . $text;
  59. }
  60. sub ########################################
  61. LUXTRONIK2_Initialize($)
  62. {
  63. my ($hash) = @_;
  64. $hash->{DefFn} = "LUXTRONIK2_Define";
  65. $hash->{UndefFn} = "LUXTRONIK2_Undefine";
  66. $hash->{GetFn} = "LUXTRONIK2_Get";
  67. $hash->{SetFn} = "LUXTRONIK2_Set";
  68. $hash->{AttrFn} = "LUXTRONIK2_Attr";
  69. $hash->{AttrList} = "disable:0,1 ".
  70. "allowSetParameter:0,1 ".
  71. "autoSynchClock:slider,10,5,300 ".
  72. "boilerVolumn ".
  73. "heatPumpElectricalPowerFactor ".
  74. "heatPumpElectricalPowerWatt ".
  75. "heatRodElectricalPowerWatt ".
  76. "compressor2ElectricalPowerWatt ".
  77. "doStatistics:0,1 ".
  78. "ignoreFirmwareCheck:0,1 ".
  79. "statusHTML ".
  80. $readingFnAttributes;
  81. }
  82. sub ########################################
  83. LUXTRONIK2_Define($$)
  84. {
  85. my ($hash, $def) = @_;
  86. my @a = split("[ \t][ \t]*", $def);
  87. return "Usage: define <name> LUXTRONIK2 <ip-address> [poll-interval]"
  88. if(@a <3 || @a >4);
  89. my $name = $a[0];
  90. my $host = $a[2];
  91. my $interval = 5*60;
  92. $interval = $a[3] if(int(@a) == 4);
  93. $interval = 30 if( $interval < 30 );
  94. $hash->{NAME} = $name;
  95. $hash->{STATE} = "Initializing";
  96. $hash->{HOST} = $host;
  97. if ( $host =~ /(.*):(.*)/ ) {
  98. $hash->{HOST} = $1;
  99. $hash->{PORT} = $2;
  100. $hash->{fhem}{portDefined} = 1;
  101. }
  102. else {
  103. $hash->{HOST} = $host;
  104. $hash->{PORT} = 8888;
  105. $hash->{fhem}{portDefined} = 0;
  106. }
  107. $hash->{INTERVAL} = $interval;
  108. $hash->{NOTIFYDEV} = "global";
  109. RemoveInternalTimer($hash);
  110. #Get first data after 10 seconds
  111. InternalTimer(gettimeofday() + 10, "LUXTRONIK2_GetUpdate", $hash, 0);
  112. #Reset temporary values
  113. $hash->{fhem}{durationFetchReadingsMin} = 0;
  114. $hash->{fhem}{durationFetchReadingsMax} = 0;
  115. $hash->{fhem}{alertFirmware} = 0;
  116. $hash->{fhem}{statBoilerHeatUpStep} = 0;
  117. $hash->{fhem}{statBoilerCoolDownStep} = 0;
  118. $hash->{fhem}{defrost}{mode}="none";
  119. $hash->{fhem}{modulVersion} = '$Date: 2017-02-11 11:04:02 +0100 (Sat, 11 Feb 2017) $';
  120. return undef;
  121. }
  122. sub ########################################
  123. LUXTRONIK2_Undefine($$)
  124. {
  125. my ($hash, $arg) = @_;
  126. RemoveInternalTimer($hash);
  127. BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
  128. return undef;
  129. }
  130. sub ########################################
  131. LUXTRONIK2_Attr(@)
  132. {
  133. my ($cmd,$name,$aName,$aVal) = @_;
  134. # $cmd can be "del" or "set"
  135. # $name is device name
  136. # aName and aVal are Attribute name and value
  137. if ($cmd eq "set") {
  138. if ($aName eq "1allowSetParameter") {
  139. eval { qr/$aVal/ };
  140. if ($@) {
  141. LUXTRONIK2_Log $name, 3, "Invalid allowSetParameter in attr $name $aName $aVal: $@";
  142. return "Invalid allowSetParameter $aVal";
  143. }
  144. }
  145. }
  146. return undef;
  147. }
  148. sub ########################################
  149. LUXTRONIK2_Set($$@)
  150. {
  151. my ($hash, $name, $cmd, $val) = @_;
  152. my $resultStr = "";
  153. if($cmd eq 'statusRequest') {
  154. $hash->{LOCAL} = 1;
  155. Log3 $name, 3, "set $name $cmd";
  156. LUXTRONIK2_GetUpdate($hash);
  157. $hash->{LOCAL} = 0;
  158. return undef;
  159. }
  160. elsif ($cmd eq 'resetStatistics') {
  161. Log3 $name, 3, "set $name $cmd $val";
  162. if ( $val eq "statBoilerGradientCoolDownMin"
  163. && exists($hash->{READINGS}{statBoilerGradientCoolDownMin})) {
  164. delete $hash->{READINGS}{statBoilerGradientCoolDownMin};
  165. $resultStr .= " statBoilerGradientCoolDownMin";
  166. }
  167. elsif ($val =~ /all|statAmbientTemp\.\.\.|statElectricity\.\.\.|statHours\.\.\.|statHeatQ\.\.\./) {
  168. my $regExp;
  169. if ($val eq "all") { $regExp = "stat"; }
  170. else { $regExp = substr $val, 0, -3; }
  171. foreach (sort keys %{ $hash->{READINGS} }) {
  172. if ($_ =~ /^\.?$regExp/ && $_ ne "state") {
  173. delete $hash->{READINGS}{$_};
  174. $resultStr .= " " . $_;
  175. }
  176. }
  177. }
  178. if ( $resultStr eq "" ) {
  179. $resultStr = "$name: No statistics to reset";
  180. } else {
  181. $resultStr = "$name: Statistic value(s) deleted:" . $resultStr;
  182. WriteStatefile();
  183. }
  184. # LUXTRONIK2_Log $hash, 3, $resultStr;
  185. return $resultStr;
  186. }
  187. elsif($cmd eq 'INTERVAL' && int(@_)==4 ) {
  188. Log3 $name, 3, "set $name $cmd $val";
  189. $val = 30 if( $val < 30 );
  190. $hash->{INTERVAL}=$val;
  191. return "Polling interval set to $val seconds.";
  192. }
  193. elsif($cmd eq 'activeTariff' && int(@_)==4 ) {
  194. $val = 0 if( $val < 1 || $val > 9 );
  195. readingsSingleUpdate($hash,"activeTariff",$val, 1);
  196. $hash->{LOCAL} = 1;
  197. LUXTRONIK2_GetUpdate($hash);
  198. $hash->{LOCAL} = 0;
  199. Log3 $name, 3, "set $name $cmd $val";
  200. return undef;
  201. }
  202. #Check Firmware and Set-Parameter-lock
  203. if ( $cmd =~ /^(synchronizeClockHeatPump|hotWaterTemperatureTarget|opModeHotWater)$/i )
  204. {
  205. my $firmware = ReadingsVal($name,"firmware","");
  206. my $firmwareCheck = LUXTRONIK2_checkFirmware($firmware);
  207. # stop in case of incompatible firmware
  208. if ($firmwareCheck eq "fwNotCompatible") {
  209. LUXTRONIK2_Log $name, 3, " Error: Host firmware '$firmware' not compatible for parameter setting.";
  210. return "Firmware '$firmware' not compatible for parameter setting. ";
  211. # stop in case of untested firmware and firmware check enabled
  212. } elsif (AttrVal($name, "ignoreFirmwareCheck", 0)!= 1 &&
  213. $firmwareCheck eq "fwNotTested") {
  214. LUXTRONIK2_Log $name, 3, " Error: Host firmware '$firmware' not tested for parameter setting. To test set attribute 'ignoreFirmwareCheck' to 1";
  215. return "Firmware '$firmware' not compatible for parameter setting. To test set attribute 'ignoreFirmwareCheck' to 1.";
  216. # stop in case setting of parameters is not enabled
  217. } elsif ( AttrVal($name, "allowSetParameter", 0) != 1) {
  218. LUXTRONIK2_Log $name, 3, " Error: Setting of parameters not allowed. Please set attribut 'allowSetParameter' to 1";
  219. return "Setting of parameters not allowed. To unlock, please set attribut 'allowSetParameter' to 1.";
  220. }
  221. }
  222. if ($cmd eq 'synchronizeClockHeatPump') {
  223. $hash->{LOCAL} = 1;
  224. $resultStr = LUXTRONIK2_synchronizeClock($hash);
  225. $hash->{LOCAL} = 0;
  226. LUXTRONIK2_Log $name, 3, $resultStr;
  227. return $resultStr;
  228. }
  229. elsif ($cmd eq 'boostHotWater' && int(@_)<=4) {
  230. Log3 $name, 3, "set $name $cmd" unless $val;
  231. Log3 $name, 3, "set $name $cmd $val" if $val;
  232. return LUXTRONIK2_boostHotWater_Start( $hash, $val );
  233. }
  234. elsif(int(@_)==4 &&
  235. ($cmd eq 'hotWaterTemperatureTarget'
  236. || $cmd eq 'opModeHotWater'
  237. || $cmd eq 'returnTemperatureHyst'
  238. || $cmd eq 'returnTemperatureSetBack'
  239. || $cmd eq 'heatingCurveEndPoint'
  240. || $cmd eq 'heatingCurveOffset'
  241. || $cmd eq 'heatSourceDefrostAirEnd'
  242. || $cmd eq 'heatSourceDefrostAirThreshold')) {
  243. Log3 $name, 3, "set $name $cmd $val";
  244. $hash->{LOCAL} = 1;
  245. $resultStr = LUXTRONIK2_SetParameter ($hash, $cmd, $val);
  246. $hash->{LOCAL} = 0;
  247. return $resultStr;
  248. }
  249. elsif( int(@_)==4 && $cmd eq 'hotWaterCircPumpDeaerate' ) { # Einstellung->Entlüftung
  250. Log3 $name, 3, "set $name $cmd $val";
  251. return "$name Error: Wrong parameter given for opModeHotWater, use Automatik,Party,Off"
  252. if $val !~ /on|off/;
  253. $hash->{LOCAL} = 1;
  254. $resultStr = LUXTRONIK2_SetParameter ($hash, $cmd, $val);
  255. if ($val eq "on" ) { $resultStr .= LUXTRONIK2_SetParameter ($hash, "runDeaerate", 1); }
  256. else { $resultStr .= LUXTRONIK2_SetParameter ($hash, "runDeaerate", 0); } # only send if no Deaerate checkbox is selected at all.
  257. $hash->{LOCAL} = 0;
  258. return $resultStr;
  259. }
  260. my $list = "statusRequest:noArg"
  261. ." activeTariff:0,1,2,3,4,5,6,7,8,9"
  262. ." boostHotWater"
  263. ." heatingCurveEndPoint"
  264. ." heatingCurveOffset"
  265. ." hotWaterCircPumpDeaerate:on,off"
  266. ." hotWaterTemperatureTarget "
  267. ." resetStatistics:all,statBoilerGradientCoolDownMin,statAmbientTemp...,statElectricity...,statHours...,statHeatQ..."
  268. ." returnTemperatureHyst "
  269. ." returnTemperatureSetBack "
  270. ." opModeHotWater:Auto,Party,Off"
  271. ." synchronizeClockHeatPump:noArg"
  272. ." INTERVAL ";
  273. return "Unknown argument $cmd, choose one of $list";
  274. }
  275. sub ########################################
  276. LUXTRONIK2_Get($$@)
  277. {
  278. my ($hash, $name, $cmd, @val ) = @_;
  279. my $resultStr = "";
  280. if($cmd eq 'heatingCurveParameter') {
  281. # Log3 $name, 3, "get $name $cmd";
  282. if (int @val !=4 ) {
  283. my $msg = "Wrong number of parameter (".int @val.")in get $name $cmd";
  284. Log3 $name, 3, $msg;
  285. return $msg;
  286. }
  287. else {
  288. return LUXTRONIK2_calcHeatingCurveParameter ( $hash, $val[0], $val[1], $val[2], $val[3]);
  289. }
  290. }
  291. elsif($cmd eq 'heatingCurveReturnTemperature') {
  292. # Log3 $name, 3, "get $name $cmd";
  293. if (int @val !=1) {
  294. my $msg = "Wrong number of parameter (".int @val.")in get $name $cmd";
  295. Log3 $name, 3, $msg;
  296. return $msg;
  297. }
  298. else {
  299. my $heatingCurveEndPoint = $hash->{READINGS}{heatingCurveEndPoint}{VAL};
  300. my $heatingCurveOffset = $hash->{READINGS}{heatingCurveOffset}{VAL};
  301. return LUXTRONIK2_getHeatingCurveReturnTemperature ( $hash, $val[0], $heatingCurveEndPoint, $heatingCurveOffset);
  302. }
  303. }
  304. my $list = "heatingCurveParameter "
  305. . "heatingCurveReturnTemperature ";
  306. return "Unknown argument $cmd, choose one of $list";
  307. }
  308. sub ########################################
  309. LUXTRONIK2_GetUpdate($)
  310. {
  311. my ($hash) = @_;
  312. my $name = $hash->{NAME};
  313. if(!$hash->{LOCAL}) {
  314. RemoveInternalTimer($hash);
  315. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "LUXTRONIK2_GetUpdate", $hash, 1);
  316. return undef if( AttrVal($name, "disable", 0 ) == 1 );
  317. }
  318. $hash->{helper}{RUNNING_PID} = BlockingCall("LUXTRONIK2_DoUpdate", $name, "LUXTRONIK2_UpdateDone", 25, "LUXTRONIK2_UpdateAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID}));
  319. }
  320. sub ########################################
  321. LUXTRONIK2_DoUpdate($)
  322. {
  323. my ($name) = @_;
  324. my $hash = $defs{$name};
  325. my $host = $hash->{HOST};
  326. my $port = $hash->{PORT};
  327. my @heatpump_values;
  328. my @heatpump_parameters;
  329. my @heatpump_visibility;
  330. my $count=0;
  331. my $result="";
  332. my $readingStartTime = time();
  333. LUXTRONIK2_Log $name, 5, "Opening connection to $host:$port";
  334. my $socket = new IO::Socket::INET (
  335. PeerAddr => $host,
  336. PeerPort => $port,
  337. # Type = SOCK_STREAM, # probably needed on some systems
  338. Proto => 'tcp'
  339. );
  340. if (!$socket) {
  341. LUXTRONIK2_Log $name, 1, "Could not open connection to host $host:$port";
  342. return "$name|0|Can't connect to $host:$port";
  343. }
  344. $socket->autoflush(1);
  345. ############################
  346. #Fetch operational values (FOV)
  347. ############################
  348. LUXTRONIK2_Log $name, 5, "Ask host for operational values";
  349. $socket->send(pack("N", 3004));
  350. $socket->send(pack("N", 0));
  351. LUXTRONIK2_Log $name, 5, "Start to receive operational values";
  352. #(FOV) read first 4 bytes of response -> should be request_echo = 3004
  353. $socket->recv($result,4);
  354. $count = unpack("N", $result);
  355. if($count != 3004) {
  356. LUXTRONIK2_Log $name, 2, "Fetching operational values - wrong echo of request 3004: ".length($result)." -> ".$count;
  357. $socket->close();
  358. return "$name|0|3004 != $count";
  359. }
  360. #(FOV) read next 4 bytes of response -> should be status = 0
  361. $socket->recv($result,4);
  362. $count = unpack("N", $result);
  363. if($count > 0) {
  364. LUXTRONIK2_Log $name, 4, "Parameter on target changed, restart parameter reading after 5 seconds";
  365. $socket->close();
  366. return "$name|2|Status = $count - parameter on target changed, restart device reading after 5 seconds";
  367. }
  368. #(FOV) read next 4 bytes of response -> should be count_calc_values > 0
  369. $socket->recv($result,4);
  370. my $count_calc_values = unpack("N", $result);
  371. if($count_calc_values == 0) {
  372. LUXTRONIK2_Log $name, 2, "Fetching operational values - 0 values announced: ".length($result)." -> ".$count_calc_values;
  373. $socket->close();
  374. return "$name|0|0 values read";
  375. }
  376. #(FOV) read remaining response -> should be previous number of parameters
  377. my $i=1;
  378. $result="";
  379. my $buf="";
  380. while($i<=$count_calc_values) {
  381. $socket->recv($buf,4);
  382. $result.=$buf;
  383. $i++;
  384. }
  385. if(length($result) != $count_calc_values*4) {
  386. LUXTRONIK2_Log $name, 1, "Operational values length check: ".length($result)." should have been ". $count_calc_values * 4;
  387. $socket->close();
  388. return "$name|0|Number of values read mismatch ( $!)\n";
  389. }
  390. #(FOV) unpack response in array
  391. @heatpump_values = unpack("N$count_calc_values", $result);
  392. if(scalar(@heatpump_values) != $count_calc_values) {
  393. LUXTRONIK2_Log $name, 2, "Unpacking problem by operation values: ".scalar(@heatpump_values)." instead of ".$count_calc_values;
  394. $socket->close();
  395. return "$name|0|Unpacking problem of operational values";
  396. }
  397. LUXTRONIK2_Log $name, 5, "$count_calc_values operational values received";
  398. ############################
  399. #Fetch set parameters (FSP)
  400. ############################
  401. LUXTRONIK2_Log $name, 5, "Ask host for set parameters";
  402. $socket->send(pack("N", 3003));
  403. $socket->send(pack("N", 0));
  404. LUXTRONIK2_Log $name, 5, "Start to receive set parameters";
  405. #(FSP) read first 4 bytes of response -> should be request_echo=3003
  406. $socket->recv($result,4);
  407. $count = unpack("N", $result);
  408. if($count != 3003) {
  409. LUXTRONIK2_Log $name, 2, "Wrong echo of request 3003: ".length($result)." -> ".$count;
  410. $socket->close();
  411. return "$name|0|3003 != 3003";
  412. }
  413. #(FSP) read next 4 bytes of response -> should be number_of_parameters > 0
  414. $socket->recv($result,4);
  415. my $count_set_parameter = unpack("N", $result);
  416. if($count_set_parameter == 0) {
  417. LUXTRONIK2_Log $name, 2, "0 parameter read: ".length($result)." -> ".$count_set_parameter;
  418. $socket->close();
  419. return "$name|0|0 parameter read";
  420. }
  421. #(FSP) read remaining response -> should be previous number of parameters
  422. $i=1;
  423. $result="";
  424. $buf="";
  425. while($i<=$count_set_parameter) {
  426. $socket->recv($buf,4);
  427. $result.=$buf;
  428. $i++;
  429. }
  430. if(length($result) != $count_set_parameter*4) {
  431. LUXTRONIK2_Log $name, 1, "Parameter length check: ".length($result)." should have been ". $count_set_parameter * 4;
  432. $socket->close();
  433. return "$name|0|Number of parameters read mismatch ( $!)\n";
  434. }
  435. @heatpump_parameters = unpack("N$count_set_parameter", $result);
  436. if(scalar(@heatpump_parameters) != $count_set_parameter) {
  437. LUXTRONIK2_Log $name, 2, "Unpacking problem by set parameter: ".scalar(@heatpump_parameters)." instead of ".$count_set_parameter;
  438. $socket->close();
  439. return "$name|0|Unpacking problem of set parameters";
  440. }
  441. LUXTRONIK2_Log $name, 5, "$count_set_parameter set values received";
  442. ############################
  443. #Fetch Visibility Attributes (FVA)
  444. ############################
  445. LUXTRONIK2_Log $name, 5, "Ask host for visibility attributes";
  446. $socket->send(pack("N", 3005));
  447. $socket->send(pack("N", 0));
  448. LUXTRONIK2_Log $name, 5, "Start to receive visibility attributes";
  449. #(FVA) read first 4 bytes of response -> should be request_echo=3005
  450. $socket->recv($result,4);
  451. $count = unpack("N", $result);
  452. if($count != 3005) {
  453. LUXTRONIK2_Log $name, 2, "Wrong echo of request 3005: ".length($result)." -> ".$count;
  454. $socket->close();
  455. return "$name|0|3005 != $count";
  456. }
  457. #(FVA) read next 4 bytes of response -> should be number_of_Visibility_Attributes > 0
  458. $socket->recv($result,4);
  459. my $countVisibAttr = unpack("N", $result);
  460. if($countVisibAttr == 0) {
  461. LUXTRONIK2_Log $name, 2, "0 visibility attributes announced: ".length($result)." -> ".$countVisibAttr;
  462. $socket->close();
  463. return "$name|0|0 visibility attributes announced";
  464. }
  465. #(FVA) read remaining response bytewise -> should be previous number of parameters
  466. $i=1;
  467. $result="";
  468. $buf="";
  469. while($i<=$countVisibAttr) {
  470. $socket->recv($buf,1);
  471. $result.=$buf;
  472. $i++;
  473. }
  474. if(length($result) != $countVisibAttr) {
  475. LUXTRONIK2_Log $name, 1, "Visibility attributes length check: ".length($result)." should have been ". $countVisibAttr;
  476. $socket->close();
  477. return "$name|0|Number of Visibility attributes read mismatch ( $!)\n";
  478. }
  479. @heatpump_visibility = unpack("C$countVisibAttr", $result);
  480. if(scalar(@heatpump_visibility) != $countVisibAttr) {
  481. LUXTRONIK2_Log $name, 2, "Unpacking problem by visibility attributes: ".scalar(@heatpump_visibility)." instead of ".$countVisibAttr;
  482. $socket->close();
  483. return "$name|0|Unpacking problem of visibility attributes";
  484. }
  485. LUXTRONIK2_Log $name, 5, "$countVisibAttr visibility attributs received";
  486. ####################################
  487. LUXTRONIK2_Log $name, 5, "Closing connection to host $host";
  488. $socket->close();
  489. my $readingEndTime = time();
  490. #return certain readings for further processing
  491. # 0 - name
  492. my $return_str="$name";
  493. # 1 - no error = 1
  494. $return_str .= "|1";
  495. # 2 - opStateHeatPump1
  496. $return_str .= "|".$heatpump_values[117];
  497. # 3 - opStateHeatPump3
  498. $return_str .= "|".$heatpump_values[119];
  499. # 4 - Stufe - ID_WEB_HauptMenuAHP_Stufe
  500. $return_str .= "|".$heatpump_values[121];
  501. # 5 - Temperature Value - ID_WEB_HauptMenuAHP_Temp
  502. $return_str .= "|".$heatpump_values[122];
  503. # 6 - Compressor1
  504. $return_str .= "|".$heatpump_values[44];
  505. # 7 - opModeHotWater
  506. $return_str .= "|".$heatpump_parameters[4];
  507. # 8 - hotWaterMonitoring
  508. $return_str .= "|".$heatpump_values[124];
  509. # 9 - hotWaterBoilerValve
  510. $return_str .= "|".$heatpump_values[38];
  511. # 10 - opModeHeating
  512. $return_str .= "|".$heatpump_parameters[3];
  513. # 11 - heatingLimit
  514. $return_str .= "|".$heatpump_parameters[699];
  515. # 12 - ambientTemperature
  516. $return_str .= "|".$heatpump_values[15];
  517. # 13 - averageAmbientTemperature
  518. $return_str .= "|".$heatpump_values[16];
  519. # 14 - hotWaterTemperature
  520. $return_str .= "|".$heatpump_values[17];
  521. # 15 - flowTemperature
  522. $return_str .= "|".$heatpump_values[10];
  523. # 16 - returnTemperature
  524. $return_str .= "|".$heatpump_values[11];
  525. # 17 - returnTemperatureTarget
  526. $return_str .= "|".$heatpump_values[12];
  527. # 18 - returnTemperatureExtern
  528. $return_str .= "|".($heatpump_visibility[24]==1 ? $heatpump_values[13] : "no");
  529. # 19 - flowRate
  530. $return_str .= "|".($heatpump_parameters[870]!=0 ? $heatpump_values[155] : "no");
  531. # 20 - firmware
  532. my $fwvalue = "";
  533. for(my $fi=81; $fi<91; $fi++) {
  534. $fwvalue .= chr($heatpump_values[$fi]) if $heatpump_values[$fi];
  535. }
  536. $return_str .= "|".$fwvalue;
  537. # 21 - thresholdHeatingLimit
  538. $return_str .= "|".$heatpump_parameters[700];
  539. # 22 - rawDeviceTimeCalc
  540. $return_str .= "|".$heatpump_values[134];
  541. # 23 - heatSourceIN
  542. $return_str .= "|".$heatpump_values[19];
  543. # 24 - heatSourceOUT
  544. $return_str .= "|".$heatpump_values[20];
  545. # 25 - hotWaterTemperatureTarget
  546. $return_str .= "|".$heatpump_values[18];
  547. # 26 - hotGasTemperature
  548. $return_str .= "|".$heatpump_values[14];
  549. # 27 - heatingSystemCircPump
  550. $return_str .= "|".$heatpump_values[39];
  551. # 28 - hotWaterCircPumpExtern
  552. $return_str .= "|". ($heatpump_visibility[57]==1 ? $heatpump_values[46] : "no");
  553. # 29 - readingFhemStartTime
  554. $return_str .= "|".$readingStartTime;
  555. # 30 - readingFhemEndTime
  556. $return_str .= "|".$readingEndTime;
  557. # 31 - typeHeatpump
  558. $return_str .= "|".$heatpump_values[78];
  559. # 32 - counterHours2ndHeatSource1
  560. $return_str .= "|". ($heatpump_visibility[84]==1 ? $heatpump_values[60] : "no");
  561. # 33 - counterHoursHeatpump
  562. $return_str .= "|". ($heatpump_visibility[87]==1 ? $heatpump_values[63] : "no");
  563. # 34 - counterHoursHeating
  564. $return_str .= "|". ($heatpump_visibility[195]==1 ? $heatpump_values[64] : "no");
  565. # 35 - counterHoursHotWater
  566. $return_str .= "|". ($heatpump_visibility[196]==1 ? $heatpump_values[65] : "no");
  567. # 36 - counterHeatQHeating
  568. $return_str .= "|". ($heatpump_visibility[0]==1 ? $heatpump_values[151] : "no");
  569. # 37 - counterHeatQHotWater
  570. $return_str .= "|". ($heatpump_visibility[1]==1 ? $heatpump_values[152] : "no");
  571. # 38 - counterHours2ndHeatSource2
  572. $return_str .= "|". ($heatpump_visibility[85]==1 ? $heatpump_values[61] : "no");
  573. # 39 - counterHours2ndHeatSource3
  574. $return_str .= "|". ($heatpump_visibility[86]==1 ? $heatpump_values[62] : "no");
  575. # 40 - opStateHeatPump2
  576. $return_str .= "|".$heatpump_values[118];
  577. # 41 - opStateHeatPump2Duration
  578. $return_str .= "|".$heatpump_values[120];
  579. # 42 - timeError0
  580. $return_str .= "|".$heatpump_values[95];
  581. # 43 - bivalentLevel
  582. $return_str .= "|".$heatpump_values[79];
  583. # 44 - Number of calculated values
  584. $return_str .= "|".$count_calc_values;
  585. # 45 - Number of set parameters
  586. $return_str .= "|".$count_set_parameter;
  587. # 46 - opStateHeating
  588. $return_str .= "|".$heatpump_values[125];
  589. # 47 - deltaHeatingReduction
  590. $return_str .= "|".$heatpump_parameters[13];
  591. # 48 - thresholdTemperatureSetBack
  592. $return_str .= "|".$heatpump_parameters[111];
  593. # 49 - hotWaterTemperatureHysterese
  594. $return_str .= "|".$heatpump_parameters[74];
  595. # 50 - solarCollectorTemperature
  596. $return_str .= "|". ($heatpump_visibility[36]==1 ? $heatpump_values[26] : "no");
  597. # 51 - solarBufferTemperature
  598. $return_str .= "|". ($heatpump_visibility[37]==1 ? $heatpump_values[27] : "no");
  599. # 52 - counterHoursSolar
  600. $return_str .= "|". ($heatpump_visibility[248]==1 ? $heatpump_values[161] : "no");
  601. # 53 - Number of visibility attributes
  602. $return_str .= "|".$countVisibAttr;
  603. # 54 - returnTemperatureSetBack
  604. $return_str .= "|".$heatpump_parameters[1];
  605. # 55 - mixer1FlowTemperature
  606. $return_str .= "|". ($heatpump_visibility[31]==1 ? $heatpump_values[21] : "no");
  607. # 56 - mixer1TargetTemperature
  608. $return_str .= "|". ($heatpump_visibility[32]==1 ? $heatpump_values[22] : "no");
  609. # 57 - mixer2FlowTemperature
  610. $return_str .= "|". ($heatpump_visibility[34]==1 ? $heatpump_values[24] : "no");
  611. # 58 - mixer2TargetTemperature
  612. $return_str .= "|". ($heatpump_visibility[35]==1 ? $heatpump_values[25] : "no");
  613. # 59 - mixer3FlowTemperature
  614. $return_str .= "|". ($heatpump_visibility[210]==1 ? $heatpump_values[137] : "no");
  615. # 60 - mixer3TargetTemperature
  616. $return_str .= "|". ($heatpump_visibility[211]==1 ? $heatpump_values[136] : "no");
  617. # 61 - hotWaterCircPumpDeaerate
  618. $return_str .= "|". ($heatpump_visibility[167]==1 ? $heatpump_parameters[684] : "no");
  619. # 62 - counterHeatQPool
  620. $return_str .= "|". ($heatpump_visibility[2]==1 ? $heatpump_values[153] : "no");
  621. # 63 - returnTemperatureTargetMin
  622. $heatpump_visibility[295]=1 unless defined($heatpump_visibility[295]);
  623. $return_str .= "|". ($heatpump_visibility[295]==1 ? $heatpump_parameters[979] : "no");
  624. # 64 - heatSourceMotor
  625. $return_str .= "|". ($heatpump_visibility[54]==1 ? $heatpump_values[43] : "no");
  626. # 65 - typeSerial
  627. $return_str .= "|".substr($heatpump_parameters[874],0,4)."/".substr($heatpump_parameters[874],4)."-".sprintf("%03X",$heatpump_parameters[875]);
  628. # 66 - heatSourceDefrostTimer
  629. $return_str .= "|". ($heatpump_visibility[219]==1 ? $heatpump_values[141] : "no");
  630. # 67 - defrostValve
  631. $return_str .= "|". ($heatpump_visibility[47]==1 ? $heatpump_values[37] : "no");
  632. # 68 - returnTempHyst
  633. $return_str .= "|". ($heatpump_visibility[93]==1 ? $heatpump_parameters[88] : "no");
  634. # 69 - Heating curve end point
  635. $return_str .= "|". ($heatpump_visibility[207]==1 ? $heatpump_parameters[11] : "no");
  636. # 70 - Heating curve parallel offset
  637. $return_str .= "|". ($heatpump_visibility[207]==1 ? $heatpump_parameters[12] : "no");
  638. # 71 - heatSourcedefrostAirThreshold
  639. $return_str .= "|". ($heatpump_visibility[97]==1 ? $heatpump_parameters[44] : "no");
  640. # 72 - heatSourcedefrostAirEnd
  641. $return_str .= "|". ($heatpump_visibility[105]==1 ? $heatpump_parameters[98] : "no");
  642. return $return_str;
  643. }
  644. sub ########################################
  645. LUXTRONIK2_UpdateDone($)
  646. {
  647. my ($string) = @_;
  648. return unless(defined($string));
  649. my $value = "";
  650. my $state = "";
  651. my @a = split("\\|",$string);
  652. my $hash = $defs{$a[0]};
  653. my $name = $a[0];
  654. delete($hash->{helper}{RUNNING_PID});
  655. return if($hash->{helper}{DISABLED});
  656. my $cop = 0;
  657. LUXTRONIK2_Log $hash, 5, $string;
  658. #Define Status Messages
  659. my %wpOpStat1 = ( 0 => "Waermepumpe laeuft",
  660. 1 => "Waermepumpe steht",
  661. 2 => "Waermepumpe kommt",
  662. 4 => "Fehler",
  663. 5 => "Abtauen",
  664. 6 => "Warte auf LIN-Verbindung",
  665. 7 => "Verdichter heizt auf",
  666. 8 => "Pumpenvorlauf" );
  667. my %wpOpStat2 = ( 0 => "Heizbetrieb",
  668. 1 => "Keine Anforderung",
  669. 2 => "Netz Einschaltverzoegerung",
  670. 3 => "Schaltspielzeit",
  671. 4 => "EVU Sperrzeit",
  672. 5 => "Brauchwasser",
  673. 6 => "Stufe",
  674. 7 => "Abtauen",
  675. 8 => "Pumpenvorlauf",
  676. 9 => "Thermische Desinfektion",
  677. 10 => "Kuehlbetrieb",
  678. 12 => "Schwimmbad/Photovoltaik",
  679. 13 => "Heizen_Ext_En",
  680. 14 => "Brauchw_Ext_En",
  681. 16 => "Durchflussueberwachung",
  682. 17 => "Elektrische Zusatzheizung" );
  683. my %wpMode = ( 0 => "Automatik",
  684. 1 => "Zusatzheizung",
  685. 2 => "Party",
  686. 3 => "Ferien",
  687. 4 => "Aus" );
  688. my %heatingState = ( 0 => "Abgesenkt",
  689. 1 => "Normal",
  690. 3 => "Aus");
  691. my %wpType = ( 0 => "ERC", 1 => "SW1",
  692. 2 => "SW2", 3 => "WW1",
  693. 4 => "WW2", 5 => "L1I",
  694. 6 => "L2I", 7 => "L1A",
  695. 8 => "L2A", 9 => "KSW",
  696. 10 => "KLW", 11 => "SWC",
  697. 12 => "LWC", 13 => "L2G",
  698. 14 => "WZS", 15 => "L1I407",
  699. 16 => "L2I407", 17 => "L1A407",
  700. 18 => "L2A407", 19 => "L2G407",
  701. 20 => "LWC407", 21 => "L1AREV", 22 => "L2AREV", 23 => "WWC1",
  702. 24 => "WWC2", 25 => "L2G404", 26 => "WZW", 27 => "L1S",
  703. 28 => "L1H", 29 => "L2H", 30 => "WZWD", 31 => "ERC",
  704. 40 => "WWB_20", 41 => "LD5", 42 => "LD7", 43 => "SW 37_45",
  705. 44 => "SW 58_69", 45 => "SW 29_56", 46 => "LD5 (230V)",
  706. 47 => "LD7 (230 V)", 48 => "LD9", 49 => "LD5 REV",
  707. 50 => "LD7 REV", 51 => "LD5 REV 230V",
  708. 52 => "LD7 REV 230V", 53 => "LD9 REV 230V",
  709. 54 => "SW 291", 55 => "LW SEC", 56 => "HMD 2", 57 => "MSW 4",
  710. 58 => "MSW 6", 59 => "MSW 8", 60 => "MSW 10", 61 => "MSW 12",
  711. 62 => "MSW 14", 63 => "MSW 17", 64 => "MSW 19", 65 => "MSW 23",
  712. 66 => "MSW 26", 67 => "MSW 30", 68 => "MSW 4S", 69 => "MSW 6S",
  713. 70 => "MSW 8S", 71 => "MSW 10S",72 => "MSW 13S", 73 => "MSW 16S",
  714. 74 => "MSW2-6S", 75 => "MSW4-16",76 => "LD2AG", 77 => "LWD90V",
  715. 78 => "MSW3-12", 79 => "MSW3-12S");
  716. my $counterRetry = $hash->{fhem}{counterRetry};
  717. $counterRetry++;
  718. my $doStatistic = AttrVal($name,"doStatistics",0);
  719. # Error
  720. if ($a[1]==0 ) {
  721. readingsSingleUpdate($hash,"state","Error: ".$a[2],1);
  722. $counterRetry = 0;
  723. if ( $hash->{fhem}{portDefined} == 0 ) {
  724. if ($hash->{PORT} == 8888 ) {
  725. $hash->{PORT} = 8889;
  726. LUXTRONIK2_Log $name, 3, "Error when using port 8888. Changed port to 8889";
  727. }
  728. elsif ($hash->{PORT} == 8889 ) {
  729. $hash->{PORT} = 8888;
  730. LUXTRONIK2_Log $name, 3, "Error when using port 8889. Changed port to 8888";
  731. }
  732. }
  733. }
  734. # Busy, restart update
  735. elsif ($a[1]==2 ) {
  736. if ($counterRetry <=3) {
  737. InternalTimer(gettimeofday() + 5, "LUXTRONIK2_GetUpdate", $hash, 0);
  738. }
  739. else {
  740. readingsSingleUpdate($hash,"state","Error: Reading skipped after $counterRetry tries",1);
  741. LUXTRONIK2_Log $hash, 2, "Device reading skipped after $counterRetry tries with parameter change on target";
  742. }
  743. }
  744. # Update readings
  745. elsif ($a[1]==1 ) {
  746. $counterRetry = 0;
  747. readingsBeginUpdate($hash);
  748. # Temporary storage of values because needed several times
  749. my $ambientTemperature = LUXTRONIK2_CalcTemp($a[12]);
  750. my $averageAmbientTemperature = LUXTRONIK2_CalcTemp($a[13]);
  751. my $hotWaterTemperature = LUXTRONIK2_CalcTemp($a[14]);
  752. my $hotWaterTemperatureTarget = LUXTRONIK2_CalcTemp($a[25]);
  753. my $hotWaterTemperatureThreshold = LUXTRONIK2_CalcTemp($a[25] - $a[49]);
  754. my $heatSourceIN = LUXTRONIK2_CalcTemp($a[23]);
  755. my $heatSourceOUT = LUXTRONIK2_CalcTemp($a[24]);
  756. my $thresholdHeatingLimit = LUXTRONIK2_CalcTemp($a[21]);
  757. my $thresholdTemperatureSetBack = LUXTRONIK2_CalcTemp($a[48]);
  758. my $flowTemperature = LUXTRONIK2_CalcTemp($a[15]);
  759. my $returnTemperature = LUXTRONIK2_CalcTemp($a[16]);
  760. my $returnTemperatureTarget = LUXTRONIK2_CalcTemp($a[17]);
  761. my $returnTempHyst = LUXTRONIK2_CalcTemp($a[68]);
  762. my $returnTemperatureTargetMin = LUXTRONIK2_CalcTemp($a[63]);
  763. my $compressor1 = $a[6]; #Ausgang Verdichter 1
  764. my $heatSourceMotor = $a[64]; #Ausgang Ventilator_BOSUP
  765. my $defrostValve = $a[67]; #AVout
  766. my $hotWaterBoilerValve = $a[9]; #BUP
  767. my $heatingSystemCircPump = $a[27]; #HUP
  768. my $opStateHeatPump3 = $a[3];
  769. my $heatPumpPower = 0;
  770. my $heatRodPower = AttrVal($name, "heatRodElectricalPowerWatt", 0);
  771. #WM[kW] = delta_Temp [K] * Durchfluss [l/h] / ( 3.600 [kJ/kWh] / ( 4,179 [kJ/(kg*K)] (H2O Wärmekapazität bei 30 & 40°C) * 0,994 [kg/l] (H2O Dichte bei 35°C) )
  772. my $thermalPower = 0;
  773. # 0=Heizen, 5=Brauchwasser, 7=Abtauen, 16=Durchflussüberwachung
  774. if ($opStateHeatPump3 =~ /^(0|5|16)$/) {
  775. if ($a[19] !~ /no/) { $thermalPower = abs($flowTemperature - $returnTemperature) * $a[19] / 866.65; } #Nur bei Wärmezählern
  776. $heatPumpPower = AttrVal($name, "heatPumpElectricalPowerWatt", -1);
  777. $heatPumpPower *= (1 + ($flowTemperature-35) * AttrVal($name, "heatPumpElectricalPowerFactor", 0));
  778. }
  779. if ($a[19] !~ /no/) { readingsBulkUpdate( $hash, "thermalPower", sprintf "%.1f", $thermalPower); } #Nur bei Wärmezählern
  780. if ($heatPumpPower >-1 ) { readingsBulkUpdate( $hash, "heatPumpElectricalPowerEstimated", sprintf "%.0f", $heatPumpPower); }
  781. if ($heatPumpPower > 0 && $a[19] !~ /no/) { #Nur bei Wärmezählern
  782. $cop = $thermalPower * 1000 / $heatPumpPower;
  783. readingsBulkUpdate( $hash, "COP", sprintf "%.2f", $cop);
  784. }
  785. # if selected, do all the statistic calculations
  786. if ( $doStatistic == 1) {
  787. #LUXTRONIK2_doStatisticBoilerHeatUp $hash, $currOpHours, $currHQ, $currTemp, $opState, $target
  788. $value = LUXTRONIK2_doStatisticBoilerHeatUp ($hash, $a[35], $a[37]/10, $hotWaterTemperature, $opStateHeatPump3,$hotWaterTemperatureTarget);
  789. if ($value ne "") {
  790. readingsBulkUpdate($hash,"statBoilerGradientHeatUp",$value);
  791. LUXTRONIK2_Log $name, 3, "statBoilerGradientHeatUp set to $value";
  792. }
  793. #LUXTRONIK2_doStatisticBoilerCoolDown $hash, $time, $currTemp, $opState, $target, $threshold
  794. $value = LUXTRONIK2_doStatisticBoilerCoolDown ($hash, $a[22], $hotWaterTemperature, $opStateHeatPump3, $hotWaterTemperatureTarget, $hotWaterTemperatureThreshold);
  795. if ($value ne "") {
  796. readingsBulkUpdate($hash,"statBoilerGradientCoolDown",$value);
  797. LUXTRONIK2_Log $name, 3, "statBoilerGradientCoolDown set to $value";
  798. my @new = split / /, $value;
  799. if ( exists( $hash->{READINGS}{statBoilerGradientCoolDownMin} ) ) {
  800. my @old = split / /, $hash->{READINGS}{statBoilerGradientCoolDownMin}{VAL};
  801. if ($new[5]>6 && $new[1]>$old[1] && $new[1] < 0) {
  802. readingsBulkUpdate($hash,"statBoilerGradientCoolDownMin",$value,1);
  803. LUXTRONIK2_Log $name, 3, "statBoilerGradientCoolDownMin set to '$value'";
  804. }
  805. } elsif ($new[5]>6 && $new[1] < 0) {
  806. readingsBulkUpdate($hash,"statBoilerGradientCoolDownMin",$value,1);
  807. LUXTRONIK2_Log $name, 3, "statBoilerGradientCoolDownMin set to '$value'";
  808. }
  809. }
  810. # LUXTRONIK2_doStatisticThermalPower: $hash, $MonitoredOpState, $currOpState, $currHeatQuantity, $currOpHours, $currAmbTemp, $currHeatSourceIn, $TargetTemp, $electricalPower
  811. $value = LUXTRONIK2_doStatisticThermalPower ($hash, 5, $opStateHeatPump3, $a[37]/10, $a[35], $ambientTemperature, $heatSourceIN,$hotWaterTemperatureTarget, $heatPumpPower);
  812. if ($value ne "") { readingsBulkUpdate($hash,"statThermalPowerBoiler",$value); }
  813. $value = LUXTRONIK2_doStatisticThermalPower ($hash, 0, $opStateHeatPump3, $a[36]/10, $a[34], $ambientTemperature, $heatSourceIN, $returnTemperatureTarget, $heatPumpPower);
  814. if ($value ne "") { readingsBulkUpdate($hash,"statThermalPowerHeating",$value); }
  815. # LUXTRONIK2_doStatisticMinMax $hash, $readingName, $value
  816. LUXTRONIK2_doStatisticMinMax ( $hash, "statAmbientTemp", $ambientTemperature);
  817. }
  818. #Operating status of heat pump
  819. my $opStateHeatPump1 = $wpOpStat1{$a[2]}; ##############
  820. $opStateHeatPump1 = "unbekannt (".$a[2].")" unless $opStateHeatPump1;
  821. readingsBulkUpdate($hash,"opStateHeatPump1",$opStateHeatPump1);
  822. my $opStateHeatPump2 = "unknown ($a[40])"; ##############
  823. my $prefix = "";
  824. if ($a[40] == 0 || $a[40] == 2) { $prefix = "seit ";}
  825. elsif ($a[40] == 1) { $prefix = "in ";}
  826. if ($a[40] == 2) { #Sonderbehandlung bei WP-Fehlern
  827. $opStateHeatPump2 = $prefix . strftime "%d.%m.%Y %H:%M:%S", localtime($a[42]);
  828. }
  829. else {
  830. $opStateHeatPump2 = $prefix . LUXTRONIK2_FormatDuration($a[41]);
  831. }
  832. readingsBulkUpdate($hash,"opStateHeatPump2",$opStateHeatPump2);
  833. my $opStateHeatPump3Txt = $wpOpStat2{$opStateHeatPump3}; ##############
  834. # refine text of third state
  835. if ($opStateHeatPump3==6) {
  836. $opStateHeatPump3Txt = "Stufe ".$a[4]." ".LUXTRONIK2_CalcTemp($a[5])." C ";
  837. }
  838. elsif ($opStateHeatPump3==7) {
  839. if ( $defrostValve==1 ) {$opStateHeatPump3Txt = "Abtauen (Kreisumkehr)";}
  840. elsif ( $compressor1==0 && $heatSourceMotor==1 ) {$opStateHeatPump3Txt = "Luftabtauen";}
  841. else {$opStateHeatPump3Txt = "Abtauen";}
  842. }
  843. $opStateHeatPump3Txt = "unbekannt (".$opStateHeatPump3.")" unless $opStateHeatPump3Txt;
  844. readingsBulkUpdate($hash,"opStateHeatPump3",$opStateHeatPump3Txt);
  845. # Hot water operating mode
  846. $value = $wpMode{$a[7]};
  847. $value = "unbekannt (".$a[7].")" unless $value;
  848. readingsBulkUpdate($hash,"opModeHotWater",$value);
  849. # opStateHotWater
  850. if ($a[8]==0) {$value="Sperrzeit";}
  851. elsif ($a[8]==1 && $hotWaterBoilerValve==1) {$value="Aufheizen";}
  852. elsif ($a[8]==1 && $hotWaterBoilerValve==0) {$value="Temp. OK";}
  853. elsif ($a[8]==3) {$value="Aus";}
  854. else {$value = "unbekannt (".$a[8]."/".$hotWaterBoilerValve.")";}
  855. readingsBulkUpdate($hash,"opStateHotWater",$value);
  856. # Heating operating mode
  857. $value = $wpMode{$a[10]};
  858. $value = "unbekannt (".$a[10].")" unless $value;
  859. readingsBulkUpdate($hash,"opModeHeating",$value);
  860. # Heating operating state
  861. # Consider also heating limit
  862. if ($a[10] == 0 && $a[11] == 1
  863. && $averageAmbientTemperature >= $thresholdHeatingLimit
  864. && ($returnTemperatureTarget eq $returnTemperatureTargetMin || $returnTemperatureTarget == 20 && $ambientTemperature<10)
  865. ) {
  866. if ($ambientTemperature>=10 ) {
  867. $value = "Heizgrenze (Soll ".$returnTemperatureTargetMin." C)";
  868. }
  869. else {
  870. $value = "Frostschutz (Soll 20 C)";
  871. }
  872. } else {
  873. $value = $heatingState{$a[46]};
  874. $value = "unbekannt (".$a[46].")" unless $value;
  875. # Consider heating reduction limit
  876. if ($a[46] == 0) {
  877. if ($thresholdTemperatureSetBack <= $ambientTemperature) {
  878. $value .= " ".LUXTRONIK2_CalcTemp($a[47])." C"; #° &deg; &#176; &#x00B0;
  879. } else {
  880. $value = "Normal da < ".$thresholdTemperatureSetBack." C";
  881. }
  882. }
  883. }
  884. readingsBulkUpdate($hash,"opStateHeating",$value);
  885. # Defrost times
  886. if ($compressor1 != $heatSourceMotor) {
  887. if ($hash->{fhem}{defrost}{mode} eq "none") {
  888. $hash->{fhem}{defrost}{startTime} = time();
  889. $hash->{fhem}{defrost}{mode} = "air" if $heatSourceMotor;
  890. $hash->{fhem}{defrost}{mode} = "reverse" unless $heatSourceMotor;
  891. $hash->{fhem}{defrost}{ambStart} = $ambientTemperature;
  892. $hash->{fhem}{defrost}{hsInStart} = $heatSourceIN;
  893. $hash->{fhem}{defrost}{hsOutStart} = $heatSourceOUT;
  894. }
  895. $hash->{fhem}{defrost}{amb} = $ambientTemperature;
  896. $hash->{fhem}{defrost}{hsIn} = $heatSourceIN;
  897. $hash->{fhem}{defrost}{hsOut} = $heatSourceOUT;
  898. }
  899. elsif ( $hash->{fhem}{defrost}{mode} ne "none" ) {
  900. my $value = "Mode: " . $hash->{fhem}{defrost}{mode} . " Time: ";
  901. $value .= strftime ( "%M:%S", localtime( time() - $hash->{fhem}{defrost}{startTime} ) );
  902. $value .= " Amb: ".$hash->{fhem}{defrost}{ambStart} . " - ". $hash->{fhem}{defrost}{amb};
  903. $value .= " hsIN: ".$hash->{fhem}{defrost}{hsInStart} . " - ". $hash->{fhem}{defrost}{hsIn};
  904. #$value .= " hsOUT: ".$hash->{fhem}{defrost}{hsOutStart} . " - ". $heatSourceOUT;
  905. readingsBulkUpdate( $hash, "heatSourceDefrostLast", $value);
  906. $hash->{fhem}{defrost}{mode} = "none";
  907. # 16 => "Durchflussueberwachung"
  908. if ($opStateHeatPump3 == 16) {
  909. readingsBulkUpdate( $hash, "heatSourceDefrostLastTimeout", "Amb: ".$hash->{fhem}{defrost}{amb}." hsIN: ".$hash->{fhem}{defrost}{hsIn}." hsOUT: ".$hash->{fhem}{defrost}{hsOut});
  910. }
  911. }
  912. # Device and reading times, delays and durations
  913. $value = strftime "%Y-%m-%d %H:%M:%S", localtime($a[22]);
  914. readingsBulkUpdate($hash, "deviceTimeCalc", $value);
  915. my $delayDeviceTimeCalc=sprintf("%.0f",$a[29]-$a[22]);
  916. readingsBulkUpdate($hash, "delayDeviceTimeCalc", $delayDeviceTimeCalc);
  917. my $durationFetchReadings = sprintf("%.2f",$a[30]-$a[29]);
  918. readingsBulkUpdate($hash, "durationFetchReadings", $durationFetchReadings);
  919. #Remember min and max reading durations, will be reset when initializing the device
  920. if ($hash->{fhem}{durationFetchReadingsMin} == 0 || $hash->{fhem}{durationFetchReadingsMin} > $durationFetchReadings) {
  921. $hash->{fhem}{durationFetchReadingsMin} = $durationFetchReadings;
  922. }
  923. if ($hash->{fhem}{durationFetchReadingsMax} < $durationFetchReadings) {
  924. $hash->{fhem}{durationFetchReadingsMax} = $durationFetchReadings;
  925. }
  926. # Temperatures and flow rate
  927. readingsBulkUpdate( $hash, "ambientTemperature", $ambientTemperature);
  928. readingsBulkUpdate( $hash, "averageAmbientTemperature", $averageAmbientTemperature);
  929. readingsBulkUpdate( $hash, "heatingLimit",$a[11]?"on":"off");
  930. readingsBulkUpdate( $hash, "thresholdHeatingLimit", $thresholdHeatingLimit);
  931. readingsBulkUpdate( $hash, "thresholdTemperatureSetBack", $thresholdTemperatureSetBack);
  932. if ($a[69] !~ /no/) {readingsBulkUpdate( $hash, "heatingCurveEndPoint",LUXTRONIK2_CalcTemp($a[69]));}
  933. if ($a[70] !~ /no/) {readingsBulkUpdate( $hash, "heatingCurveOffset",LUXTRONIK2_CalcTemp($a[70]));}
  934. readingsBulkUpdate( $hash, "hotWaterTemperature", $hotWaterTemperature);
  935. readingsBulkUpdate( $hash, "hotWaterTemperatureTarget",$hotWaterTemperatureTarget);
  936. readingsBulkUpdate( $hash, "flowTemperature", $flowTemperature);
  937. readingsBulkUpdate( $hash, "returnTemperature", $returnTemperature);
  938. readingsBulkUpdate( $hash, "returnTemperatureTarget", $returnTemperatureTarget);
  939. readingsBulkUpdate( $hash, "returnTemperatureHyst", $returnTempHyst);
  940. readingsBulkUpdate( $hash, "returnTemperatureSetBack",LUXTRONIK2_CalcTemp($a[54]));
  941. if ($a[18] !~ /no/) {readingsBulkUpdate( $hash, "returnTemperatureExtern",LUXTRONIK2_CalcTemp($a[18]));}
  942. if ($a[19] !~ /no/) {readingsBulkUpdate( $hash, "flowRate",$a[19]);}
  943. readingsBulkUpdate( $hash, "heatSourceIN", $heatSourceIN );
  944. readingsBulkUpdate( $hash, "heatSourceOUT", $heatSourceOUT );
  945. readingsBulkUpdate( $hash, "heatSourceMotor", $heatSourceMotor?"on":"off");
  946. if ($a[71] !~ /no/) {readingsBulkUpdate( $hash, "heatSourceDefrostAirThreshold",LUXTRONIK2_CalcTemp($a[71]));}
  947. if ($a[72] !~ /no/) {readingsBulkUpdate( $hash, "heatSourceDefrostAirEnd",LUXTRONIK2_CalcTemp($a[72]));}
  948. if ($a[66] !~ /no/) {readingsBulkUpdate( $hash, "heatSourceDefrostTimer",$a[66]);}
  949. readingsBulkUpdate( $hash, "compressor1",$compressor1?"on":"off");
  950. readingsBulkUpdate( $hash, "hotGasTemperature",LUXTRONIK2_CalcTemp($a[26]));
  951. if ($a[55] !~ /no/) {readingsBulkUpdate( $hash, "mixer1FlowTemperature",LUXTRONIK2_CalcTemp($a[55]));}
  952. if ($a[56] !~ /no/) {readingsBulkUpdate( $hash, "mixer1TargetTemperature",LUXTRONIK2_CalcTemp($a[56]));}
  953. if ($a[57] !~ /no/) {readingsBulkUpdate( $hash, "mixer2FlowTemperature",LUXTRONIK2_CalcTemp($a[57]));}
  954. if ($a[58] !~ /no/) {readingsBulkUpdate( $hash, "mixer2TargetTemperature",LUXTRONIK2_CalcTemp($a[58]));}
  955. if ($a[59] !~ /no/) {readingsBulkUpdate( $hash, "mixer3FlowTemperature",LUXTRONIK2_CalcTemp($a[59]));}
  956. if ($a[60] !~ /no/) {readingsBulkUpdate( $hash, "mixer3TargetTemperature",LUXTRONIK2_CalcTemp($a[60]));}
  957. # Operating hours (seconds->hours) and heat quantities
  958. # LUXTRONIK2_storeReadings: $hash, $readingName, $value, $factor, $doStatistic, $electricalPower
  959. LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource1", $a[32], 3600, $doStatistic, $heatRodPower;
  960. LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource2", $a[38], 3600, $doStatistic, $heatRodPower;
  961. LUXTRONIK2_storeReadings $hash, "counterHours2ndHeatSource3", $a[39], 3600, $doStatistic, $heatRodPower;
  962. LUXTRONIK2_storeReadings $hash, "counterHoursHeatPump", $a[33], 3600, $doStatistic, $heatPumpPower;
  963. LUXTRONIK2_storeReadings $hash, "counterHoursHeating", $a[34], 3600, $doStatistic, $heatPumpPower;
  964. LUXTRONIK2_storeReadings $hash, "counterHoursHotWater", $a[35], 3600, $doStatistic, $heatPumpPower;
  965. my $heatQTotal = 0 ;
  966. if ($a[36] !~ /no/) {
  967. LUXTRONIK2_storeReadings $hash, "counterHeatQHeating", $a[36], 10, ($a[19] !~ /no/ ? $doStatistic : 0), -1;
  968. $heatQTotal += $a[36];
  969. }
  970. if ($a[37] !~ /no/) {
  971. LUXTRONIK2_storeReadings $hash, "counterHeatQHotWater", $a[37], 10, ($a[19] !~ /no/ ? $doStatistic : 0), -1;
  972. $heatQTotal += $a[37];
  973. }
  974. if ($a[62] !~ /no/) {
  975. LUXTRONIK2_storeReadings $hash, "counterHeatQPool", $a[62], 10, ($a[19] !~ /no/ ? $doStatistic : 0), -1;
  976. $heatQTotal += $a[62];
  977. }
  978. LUXTRONIK2_storeReadings $hash, "counterHeatQTotal", $heatQTotal, 10, ($a[19] !~ /no/ ? $doStatistic : 0), -1;
  979. # Input / Output status
  980. readingsBulkUpdate($hash,"heatingSystemCircPump",$heatingSystemCircPump?"on":"off");
  981. readingsBulkUpdate($hash,"hotWaterCircPumpExtern",$a[28]?"on":"off");
  982. readingsBulkUpdate($hash,"hotWaterSwitchingValve",$hotWaterBoilerValve?"on":"off");
  983. # Deaerate Function
  984. readingsBulkUpdate( $hash, "hotWaterCircPumpDeaerate",$a[61]?"on":"off") unless $a[61] eq "no";
  985. # bivalentLevel
  986. readingsBulkUpdate($hash,"bivalentLevel",$a[43]);
  987. # Firmware
  988. my $firmware = $a[20];
  989. readingsBulkUpdate($hash,"firmware",$firmware);
  990. my $firmwareCheck = LUXTRONIK2_checkFirmware($firmware);
  991. # if unknown firmware, ask at each startup to inform comunity
  992. if ($hash->{fhem}{alertFirmware} != 1 && $firmwareCheck eq "fwNotTested") {
  993. $hash->{fhem}{alertFirmware} = 1;
  994. LUXTRONIK2_Log $hash, 2, "Alert: Host uses untested Firmware '$a[20]'. Please inform FHEM comunity about compatibility.";
  995. }
  996. # Type of Heatpump
  997. $value = $wpType{$a[31]};
  998. $value = "unbekannt (".$a[31].")" unless $value;
  999. readingsBulkUpdate($hash,"typeHeatpump",$value);
  1000. readingsBulkUpdate($hash,"typeSerial",$a[65]);
  1001. # Solar
  1002. if ($a[50] !~ /no/) {readingsBulkUpdate($hash, "solarCollectorTemperature", LUXTRONIK2_CalcTemp($a[50]));}
  1003. if ($a[51] !~ /no/) {readingsBulkUpdate($hash, "solarBufferTemperature", LUXTRONIK2_CalcTemp($a[51]));}
  1004. if ($a[52] !~ /no/) {readingsBulkUpdate($hash, "counterHoursSolar", sprintf("%.1f", $a[52]/3600));}
  1005. # HTML for floorplan
  1006. if(AttrVal($name, "statusHTML", "none") ne "none") {
  1007. $value = ""; #"<div class=fp_" . $a[0] . "_title>" . $a[0] . "</div> \n";
  1008. $value .= "$opStateHeatPump1<br>\n";
  1009. $value .= "$opStateHeatPump2<br>\n";
  1010. $value .= "$opStateHeatPump3Txt<br>\n";
  1011. $value .= "Brauchwasser: ".$hotWaterTemperature."&deg;C";
  1012. readingsBulkUpdate($hash,"floorplanHTML",$value);
  1013. }
  1014. # State update
  1015. $value = "$opStateHeatPump1 $opStateHeatPump2 - $opStateHeatPump3Txt";
  1016. if ($thermalPower != 0) {
  1017. $value .= sprintf (" (%.1f kW", $thermalPower);
  1018. if ($heatPumpPower>0) {$value .= sprintf (", COP: %.2f", $cop);}
  1019. $value .= ")"; }
  1020. readingsBulkUpdate($hash, "state", $value);
  1021. # Special readings
  1022. # $compressor1 = $a[6]; #Ausgang Verdichter 1
  1023. # $heatSourceMotor = $a[64]; #Ausgang Ventilator_BOSUP
  1024. # $defrostValve = $a[67]; #AVout
  1025. # $hotWaterBoilerValve = $a[9]; #BUP
  1026. # $heatingSystemCircPump = $a[27]; #HUP
  1027. # 0=Heizen, 1=keine Anforderung, 3=Schaltspielzeit, 5=Brauchwasser, 7=Abtauen, 16=Durchflussüberwachung
  1028. my $lastHeatingCycle = ReadingsVal($name, "heatingCycle", "");
  1029. if ( $opStateHeatPump3 == 0 ) {
  1030. readingsBulkUpdate($hash, "heatingCycle", "running");
  1031. }
  1032. elsif ( $opStateHeatPump3 > 1 && $lastHeatingCycle eq "running") {
  1033. readingsBulkUpdate($hash, "heatingCycle", "paused");
  1034. }
  1035. elsif ( $opStateHeatPump3 == 1 && $lastHeatingCycle eq "running") {
  1036. readingsBulkUpdate($hash, "heatingCycle", "finished");
  1037. }
  1038. elsif ( $opStateHeatPump3 =~ /1|3/ && $lastHeatingCycle ne "finished" && $returnTemperature-$returnTemperatureTarget >= $returnTempHyst ) {
  1039. readingsBulkUpdate($hash, "heatingCycle", "finished");
  1040. }
  1041. elsif ( $opStateHeatPump3 == 1 && $lastHeatingCycle eq "paused") {
  1042. readingsBulkUpdate($hash, "heatingCycle", "discontinued");
  1043. }
  1044. readingsEndUpdate($hash,1);
  1045. $hash->{helper}{fetched_calc_values} = $a[44];
  1046. $hash->{helper}{fetched_parameters} = $a[45];
  1047. $hash->{helper}{fetched_visib_attr} = $a[53];
  1048. ############################
  1049. #Auto Synchronize Device Clock
  1050. my $autoSynchClock = AttrVal($name, "autoSynchClock", 0);
  1051. $autoSynchClock = 10 unless ($autoSynchClock >= 10 || $autoSynchClock == 0);
  1052. $autoSynchClock = 600 unless $autoSynchClock <= 600;
  1053. if ($autoSynchClock != 0 and abs($delayDeviceTimeCalc) > $autoSynchClock ) {
  1054. LUXTRONIK2_Log $name, 3, "autoSynchClock triggered (delayDeviceTimeCalc ".abs($delayDeviceTimeCalc)." > $autoSynchClock).";
  1055. # Firmware not tested and Firmware Check not ignored
  1056. if ($firmwareCheck eq "fwNotTested" && AttrVal($name, "ignoreFirmwareCheck", 0)!= 1) {
  1057. LUXTRONIK2_Log $name, 1, "Host firmware '$firmware' not tested for clock synchronization. To test set 'ignoreFirmwareCheck' to 1.";
  1058. $attr{$name}{autoSynchClock} = 0;
  1059. LUXTRONIK2_Log $name, 3, "Attribute 'autoSynchClock' set to 0.";
  1060. #Firmware not compatible
  1061. } elsif ($firmwareCheck eq "fwNotCompatible") {
  1062. LUXTRONIK2_Log $name, 1, "Host firmware '$firmware' not compatible for host clock synchronization.";
  1063. $attr{$name}{autoSynchClock} = 0;
  1064. LUXTRONIK2_Log $name, 3, "Attribute 'autoSynchClock' set to 0.";
  1065. #Firmware OK -> Synchronize Clock
  1066. } else {
  1067. $value = LUXTRONIK2_synchronizeClock($hash, 600);
  1068. LUXTRONIK2_Log $hash, 3, $value;
  1069. }
  1070. }
  1071. #End of Auto Synchronize Device Clock
  1072. ############################
  1073. }
  1074. else {
  1075. LUXTRONIK2_Log $hash, 5, "Status = $a[1]";
  1076. }
  1077. $hash->{fhem}{counterRetry} = $counterRetry;
  1078. }
  1079. sub ########################################
  1080. LUXTRONIK2_UpdateAborted($)
  1081. {
  1082. my ($hash) = @_;
  1083. delete($hash->{helper}{RUNNING_PID});
  1084. my $name = $hash->{NAME};
  1085. my $host = $hash->{HOST};
  1086. LUXTRONIK2_Log $hash, 1, "Timeout when connecting to host $host";
  1087. }
  1088. sub ########################################
  1089. LUXTRONIK2_CalcTemp($)
  1090. {
  1091. my ($temp) = @_;
  1092. #change unsigned into signed
  1093. if ($temp > 2147483648) {$temp = $temp-4294967296;}
  1094. $temp /= 10;
  1095. return sprintf ("%.1f", $temp);
  1096. }
  1097. ########################################
  1098. sub LUXTRONIK2_FormatDuration($)
  1099. {
  1100. my ($value) = @_;
  1101. my $returnstr;
  1102. $returnstr = sprintf "%01dd ", int($value/86400)
  1103. if $value >= 86400;
  1104. $value %= 86400;
  1105. $returnstr .= sprintf "%02d:", int($value/3600);
  1106. $value %= 3600;
  1107. $returnstr .= sprintf "%02d:", int($value/60);
  1108. $value %= 60;
  1109. $returnstr .= sprintf "%02d", $value;
  1110. return $returnstr;
  1111. }
  1112. ########################################
  1113. sub LUXTRONIK2_SetParameter ($$$)
  1114. {
  1115. my ($hash, $parameterName, $realValue) = @_;
  1116. my $setParameter = 0;
  1117. my $setValue = 0;
  1118. my $result;
  1119. my $buffer;
  1120. my $host = $hash->{HOST};
  1121. my $name = $hash->{NAME};
  1122. my %opMode = ( "Auto" => 0,
  1123. "Party" => 2,
  1124. "Off" => 4);
  1125. if(AttrVal($name, "allowSetParameter", 0) != 1) {
  1126. return $name." Error: Setting of parameters not allowed. Please set attribut 'allowSetParameter' to 1";
  1127. }
  1128. if ($parameterName eq "hotWaterTemperatureTarget") {
  1129. #parameter number
  1130. $setParameter = 2;
  1131. #limit temperature range
  1132. $realValue = 30 if( $realValue < 30 );
  1133. $realValue = 65 if( $realValue > 65 );
  1134. #Allow only integer temperatures with decimal .1
  1135. $setValue = int($realValue * 10);
  1136. $realValue = $setValue / 10;
  1137. }
  1138. elsif ($parameterName eq "heatingCurveEndPoint") {
  1139. #parameter number
  1140. $setParameter = 11;
  1141. #limit temperature range
  1142. $realValue = 20 if( $realValue < 20.0 );
  1143. $realValue = 70 if( $realValue > 70.0 );
  1144. #Allow only integer temperatures
  1145. $setValue = int($realValue * 10);
  1146. $realValue = $setValue / 10;
  1147. }
  1148. elsif ($parameterName eq "heatingCurveOffset") {
  1149. #parameter number
  1150. $setParameter = 12;
  1151. #limit temperature range
  1152. $realValue = 5 if( $realValue < 5.0 );
  1153. $realValue = 35 if( $realValue > 35.0 );
  1154. #Allow only integer temperatures
  1155. $setValue = int($realValue * 10);
  1156. $realValue = $setValue / 10;
  1157. }
  1158. elsif ($parameterName eq "heatSourceDefrostAirEnd") {
  1159. #parameter number
  1160. $setParameter = 98;
  1161. #limit temperature range
  1162. $realValue = 1 if( $realValue < 1.0 );
  1163. $realValue = 24 if( $realValue > 24.0 );
  1164. #Allow only integer temperatures
  1165. $setValue = int($realValue * 10);
  1166. $realValue = $setValue / 10;
  1167. }
  1168. elsif ($parameterName eq "heatSourceDefrostAirThreshold") {
  1169. #parameter number
  1170. $setParameter = 44;
  1171. #limit temperature range
  1172. $realValue = 1.5 if( $realValue < 1.5 );
  1173. $realValue = 20 if( $realValue > 20.0 );
  1174. #Allow only integer temperatures
  1175. $setValue = int($realValue * 10);
  1176. $realValue = $setValue / 10;
  1177. }
  1178. elsif ($parameterName eq "opModeHotWater") {
  1179. if (! exists($opMode{$realValue})) {
  1180. return "$name Error: Wrong parameter given for opModeHotWater, use Automatik,Party,Off"
  1181. }
  1182. $setParameter = 4;
  1183. $setValue = $opMode{$realValue};
  1184. }
  1185. elsif ($parameterName eq "returnTemperatureHyst") {
  1186. #parameter number
  1187. $setParameter = 88;
  1188. #limit temperature range
  1189. $realValue = 0.5 if( $realValue < 0.5 );
  1190. $realValue = 3.0 if( $realValue > 3.0 );
  1191. #Allow only integer temperatures
  1192. $setValue = int($realValue * 10);
  1193. $realValue = $setValue / 10;
  1194. }
  1195. elsif ($parameterName eq "returnTemperatureSetBack") {
  1196. #parameter number
  1197. $setParameter = 1;
  1198. #limit temperature range
  1199. $realValue = -5 if( $realValue < -5 );
  1200. $realValue = 5 if( $realValue > 5 );
  1201. #Allow only integer temperature or with decimal .5
  1202. $setValue = int($realValue * 2) * 5;
  1203. $realValue = $setValue / 10;
  1204. }
  1205. elsif ($parameterName eq "hotWaterCircPumpDeaerate") { #isVisible(167)
  1206. $setParameter = 684;
  1207. $setValue = $realValue eq "on" ? 1 : 0;
  1208. }
  1209. elsif ($parameterName eq "runDeaerate") {
  1210. $setParameter = 158;
  1211. $setValue = $realValue;
  1212. }
  1213. else {
  1214. return "$name LUXTRONIK2_SetParameter-Error: unknown parameter $parameterName";
  1215. }
  1216. ############################
  1217. # Send new parameter to host
  1218. ############################
  1219. if ($setParameter != 0) {
  1220. LUXTRONIK2_Log $name, 5, "Opening connection to host ".$host;
  1221. my $socket = new IO::Socket::INET ( PeerAddr => $host,
  1222. PeerPort => 8888,
  1223. Proto => 'tcp'
  1224. );
  1225. # Socket error
  1226. if (!$socket) {
  1227. LUXTRONIK2_Log $name, 1, "Could not open connection to host ".$host;
  1228. return "$name Error: Could not open connection to host ".$host;
  1229. }
  1230. $socket->autoflush(1);
  1231. LUXTRONIK2_Log $name, 5, "Set parameter $parameterName ($setParameter) = $realValue ($setValue)";
  1232. $socket->send(pack("N", 3002));
  1233. $socket->send(pack("N", $setParameter));
  1234. $socket->send(pack("N", $setValue));
  1235. LUXTRONIK2_Log $name, 5, "Receive confirmation";
  1236. #read first 4 bytes of response -> should be request_echo = 3002
  1237. $socket->recv($buffer,4);
  1238. $result = unpack("N", $buffer);
  1239. if($result != 3002) {
  1240. LUXTRONIK2_Log $name, 2, "Set parameter $parameterName - wrong echo of request: $result instead of 3002";
  1241. $socket->close();
  1242. return "$name Error: Host did not confirm parameter setting";
  1243. }
  1244. #Read next 4 bytes of response -> should be setParameter
  1245. $socket->recv($buffer,4);
  1246. $result = unpack("N", $buffer);
  1247. if($result !=$setParameter) {
  1248. LUXTRONIK2_Log $name, 2, "Set parameter $parameterName - missing confirmation: $result instead of $setParameter";
  1249. $socket->close();
  1250. return "$name Error: Host did not confirm parameter setting";
  1251. }
  1252. LUXTRONIK2_Log $name, 5, "Parameter setting confirmed";
  1253. $socket->close();
  1254. readingsSingleUpdate($hash,$parameterName,$realValue,0) unless $parameterName eq "runDeaerate";
  1255. return undef;
  1256. }
  1257. }
  1258. ########################################
  1259. sub LUXTRONIK2_synchronizeClock (@)
  1260. {
  1261. my ($hash,$maxDelta) = @_;
  1262. my $host = $hash->{HOST};
  1263. my $name = $hash->{NAME};
  1264. my $delay = 0;
  1265. my $returnStr = "";
  1266. $maxDelta = 60 unless $maxDelta >= 0;
  1267. $maxDelta = 600 unless $maxDelta <= 600;
  1268. LUXTRONIK2_Log $name, 5, "Open telnet connection to $host";
  1269. my $telnet = new Net::Telnet ( Host=>$host, Port => 23, Timeout=>10, Errmode=>'return');
  1270. if (!$telnet) {
  1271. LUXTRONIK2_Log $name, 1, $telnet->errmsg;
  1272. return "$name synchronizeDeviceClock-Error: ".$telnet->errmsg;
  1273. }
  1274. LUXTRONIK2_Log $name, 5, "Log into $host";
  1275. if (!$telnet->login('root', '')) {
  1276. LUXTRONIK2_Log $name, 1, $telnet->errmsg;
  1277. return "$name synchronizeDeviceClock-Error: ".$telnet->errmsg;
  1278. }
  1279. LUXTRONIK2_Log $name, 5, "Read current time of host";
  1280. my @output = $telnet->cmd('date +%s');
  1281. $delay = sprintf("%.1f",time() - $output[0]);
  1282. LUXTRONIK2_Log $name, 5, "Current time is ".localtime($output[0])." Delay is $delay seconds.";
  1283. if (abs($delay)>$maxDelta && $maxDelta!=0) {
  1284. $returnStr = "Do not dare to synchronize. Device clock of host $host differs by $delay seconds (max. is $maxDelta).";
  1285. } elsif ($delay == 0) {
  1286. $returnStr = "Internal clock of host $host has no delay. -> not synchronized";
  1287. } else {
  1288. my $newTime = strftime "%m%d%H%M%Y.%S", localtime();
  1289. LUXTRONIK2_Log $name, 5, "Run command 'date ".$newTime."'";
  1290. @output=$telnet->cmd('date '.$newTime);
  1291. $returnStr = "Internal clock of host $host corrected by $delay seconds. -> ".$output[0];
  1292. readingsSingleUpdate($hash,"deviceTimeLastSync",TimeNow,1);
  1293. }
  1294. LUXTRONIK2_Log $name, 5, "Close telnet connection.";
  1295. $telnet->close;
  1296. return $returnStr;
  1297. }
  1298. ########################################
  1299. sub LUXTRONIK2_boostHotWater_Start ($$)
  1300. { my ($hash, $temperature) = @_;
  1301. my $name = $hash->{NAME};
  1302. return "boostHotWater not implemented yet";
  1303. return "$temperature is not a number." if defined $temperature && $temperature !~ /^\d*\.?\d*$/;
  1304. my $currTarget = $hash->{READINGS}{hotWaterTemperatureTarget}{VAL};
  1305. return "Could not determine current hotWaterTemperatureTarget." unless defined $currTarget;
  1306. readingsSingleUpdate($hash,"bhwLastTarget",$currTarget, 0) unless $hash->{READINGS}{bhwLastTarget}{VAL};
  1307. my $currMode = $hash->{READINGS}{opModeHotWater}{VAL};
  1308. return "Could not determine current opModeHotWater." unless defined $currMode;
  1309. readingsSingleUpdate($hash,"bhwLastMode",$currMode, 0) unless $hash->{READINGS}{bhwLastMode}{VAL};
  1310. my $currState = $hash->{READINGS}{opStateHotWater}{VAL};
  1311. return "Could not determine current opStateHotWater." unless defined $currState;
  1312. $hash->{boostHotWater} = 1;
  1313. if ( defined $temperature ) {
  1314. LUXTRONIK2_Log $name, 4, "set 'hotWaterTemperatureTarget' temporarly to ".$temperature;
  1315. LUXTRONIK2_SetParameter($hash, "hotWaterTemperatureTarget", $temperature);
  1316. }
  1317. if ( $currState !~ /Aufheizen|Temp. OK/) {
  1318. LUXTRONIK2_Log $name, 4, "set 'opModeHotWater' temporarly to 'Party'";
  1319. LUXTRONIK2_SetParameter($hash, "opModeHotWater", "Party");
  1320. }
  1321. }
  1322. ########################################
  1323. sub LUXTRONIK2_calcHeatingCurveParameter ($$$$$)
  1324. { my ($hash, $aussen_1, $rtSoll_1, $aussen_2, $rtSoll_2) = @_;
  1325. if ($aussen_1 > $aussen_2) {
  1326. my $temp= $aussen_1;
  1327. $aussen_1=$aussen_2;
  1328. $aussen_2=$temp;
  1329. $temp= $rtSoll_1;
  1330. $rtSoll_1=$rtSoll_2;
  1331. $rtSoll_2=$temp;
  1332. }
  1333. my $endPoint_Ist = $hash->{READINGS}{heatingCurveEndPoint}{VAL};
  1334. my $endPoint = $endPoint_Ist;
  1335. my $offset_Ist = $hash->{READINGS}{heatingCurveOffset}{VAL};
  1336. my $offset = $offset_Ist;
  1337. my $rtIst_1 = LUXTRONIK2_getHeatingCurveReturnTemperature ( $hash, $aussen_1, $endPoint, $offset);
  1338. my $rtIst_2 = LUXTRONIK2_getHeatingCurveReturnTemperature ( $hash, $aussen_2, $endPoint, $offset);
  1339. my $delta_1; my $delta_2;
  1340. my $msg; my $i;
  1341. #get Heizung heatingCurveParameter 0 27 10 25
  1342. for ( $i=0; $i<1000; $i++ ) {
  1343. $delta_1 = LUXTRONIK2_getHeatingCurveReturnTemperature ( $hash, $aussen_1, $endPoint, $offset) - $rtSoll_1;
  1344. $delta_1 = int(10.0 * $delta_1 + 0.5) / 10.0;
  1345. $delta_2 = LUXTRONIK2_getHeatingCurveReturnTemperature ( $hash, $aussen_2, $endPoint, $offset) - $rtSoll_2;
  1346. $delta_2 = int(10.0 * $delta_2 + 0.5) / 10.0;
  1347. $msg = "Calculate loop $i: hcEndPoint=$endPoint, hcOffset=$offset, delta($aussen_1)=$delta_1, delta($aussen_2)=$delta_2)\n";
  1348. LUXTRONIK2_Log $hash, 4, $msg;
  1349. last if $delta_1 == 0 && $delta_2 == 0;
  1350. if ($delta_2 > 0) {
  1351. $offset -= 0.1;
  1352. }
  1353. elsif ($delta_2 < 0) {
  1354. $offset += 0.1;
  1355. }
  1356. elsif ($delta_1 > 0) {
  1357. $endPoint -= 0.1;
  1358. }
  1359. elsif ($delta_1 < 0) {
  1360. $endPoint += 0.1;
  1361. }
  1362. $endPoint = int(10.0 * $endPoint + 0.5) / 10.0;
  1363. $offset = int(10.0 * $offset + 0.5) / 10.0;
  1364. }
  1365. LUXTRONIK2_Log $hash, 3, "Heating-Curve-Parameter calculated in $i loops.";
  1366. $msg = "New Values: heatingCurveEndPoint=$endPoint heatingCurveOffset=$offset\n";
  1367. $msg .= "Old Values: heatingCurveEndPoint=$endPoint_Ist heatingCurveOffset=$offset_Ist\n\n";
  1368. $msg .= "New Heating-Curve: returnTemp($aussen_1)=".($delta_1+$rtSoll_1)." and returnTemp($aussen_2)=".($delta_2+$rtSoll_2)."\n";
  1369. $msg .= "Old Heating-Curve: returnTemp($aussen_1)=".($rtIst_1)." and returnTemp($aussen_2)=".($rtIst_2)."\n";
  1370. $msg .= "calculated in $i loops\n";
  1371. return $msg;
  1372. }
  1373. ########################################
  1374. sub LUXTRONIK2_getHeatingCurveReturnTemperature ($$$$)
  1375. { my ($hash, $aussen, $endPoint, $offset) = @_;
  1376. LUXTRONIK2_Log $hash, 5, "Calculate return-temperature at $aussen with heating curve ($endPoint, $offset)";
  1377. my $result = $offset + ($endPoint - 20.0) * ($offset - $aussen) / (20.0 - ($aussen - $offset) / 2);
  1378. $result = int(10.0 * $result + 0.5) / 10.0;
  1379. return $result;
  1380. }
  1381. ########################################
  1382. sub LUXTRONIK2_checkFirmware ($)
  1383. {
  1384. my ($myFirmware) = @_;
  1385. #Firmware not tested
  1386. if (index($testedFirmware,"#".$myFirmware."#") == -1) {
  1387. return "fwNotTested";
  1388. #Firmware tested but not compatible
  1389. } elsif (index($compatibleFirmware,"#".$myFirmware."#") == -1) {
  1390. return "fwNotCompatible";
  1391. #Firmware compatible
  1392. } else {
  1393. return "fwCompatible";
  1394. }
  1395. }
  1396. # Calculate heat-up gradients of boiler based on hotWaterTemperature and counterHeatQHeating
  1397. sub ########################################
  1398. LUXTRONIK2_doStatisticThermalPower ($$$$$$$$$)
  1399. {
  1400. my ($hash, $MonitoredOpState, $currOpState, $currHeatQuantity, $currOpHours, $currAmbTemp, $currHeatSourceIn, $targetTemp, $electricalPower) = @_;
  1401. my @last = split / /, $hash->{fhem}{"statThermalPowerOpState_".$MonitoredOpState} || "1";
  1402. my $returnStr = "";
  1403. my $value1;
  1404. my $value2;
  1405. my $value3;
  1406. my $save = 0;
  1407. if ( $last[0] != $MonitoredOpState && $currOpState == $MonitoredOpState ) {
  1408. # Save start values at the beginning of the monitored operation (5=Hot Water, 0=Heating)
  1409. $save = 1;
  1410. $last[0] = $currOpState;
  1411. $last[1] = $currHeatQuantity;
  1412. $last[2] = $currOpHours;
  1413. $last[3] = $currAmbTemp;
  1414. $last[4] = $currHeatSourceIn;
  1415. $last[5] = 1;
  1416. $last[6] = $targetTemp;
  1417. $last[7] = $electricalPower;
  1418. } elsif ($last[0] == $MonitoredOpState && ($currOpState == $MonitoredOpState || $currOpState == 16) ) { #16=Durchflussüberwachung
  1419. # Store intermediate values as long as the correct opMode runs
  1420. $save = 1;
  1421. $last[3] += $currAmbTemp;
  1422. $last[4] += $currHeatSourceIn;
  1423. $last[5]++;
  1424. $last[7] += $electricalPower;
  1425. } elsif ($last[0] == $MonitoredOpState && $currOpState != $MonitoredOpState && $currOpState != 16 ) { #16=Durchflussüberwachung
  1426. # Do statistics at the end of the monitored operation if it run at least 9.5 minutes
  1427. $save = 1;
  1428. $last[0] = $currOpState;
  1429. $value2 = ($currOpHours - $last[2])/60;
  1430. if ($value2 >= 6) {
  1431. $returnStr = sprintf "aT: %.1f iT: %.1f tT: %.1f", $last[3]/$last[5], $last[4]/$last[5], $targetTemp;
  1432. $value1 = $currHeatQuantity - $last[1];
  1433. $value3 = $value1 * 60 / $value2;
  1434. $returnStr .= sprintf " thP: %.1f DQ: %.1f t: %.0f", $value3, $value1, $value2;
  1435. if ($last[7]>0) {
  1436. $value1 = $value3 *1000 / $last[7] * $last[5];;
  1437. $returnStr .= sprintf " COP: %.2f", $value1;
  1438. }
  1439. if ($last[6] > $targetTemp) { $returnStr .= " tTStart: " . $last[6]; }
  1440. }
  1441. }
  1442. if ($save == 1) { $hash->{fhem}{"statThermalPowerOpState_".$MonitoredOpState} = join( " ", @last);}
  1443. return $returnStr;
  1444. }
  1445. # Calculate heat-up gradients of boiler based on hotWaterTemperature and counterHeatQHeating
  1446. sub ########################################
  1447. LUXTRONIK2_doStatisticBoilerHeatUp ($$$$$$)
  1448. {
  1449. my ($hash, $currOpHours, $currHQ, $currTemp, $opState, $target) = @_;
  1450. my $name = $hash->{NAME};
  1451. my $step = $hash->{fhem}{statBoilerHeatUpStep};
  1452. my $minTemp = $hash->{fhem}{statBoilerHeatUpMin};
  1453. my $maxTemp = $hash->{fhem}{statBoilerHeatUpMax};
  1454. my $lastHQ = $hash->{fhem}{statBoilerHeatUpHQ};
  1455. my $lastOpHours = $hash->{fhem}{statBoilerHeatUpOpHours};
  1456. my $value1 = 0;
  1457. my $value2 = 0;
  1458. my $value3 = 0;
  1459. my $returnStr = "";
  1460. # step 0 = Initialize - if hot water preparation is off
  1461. if ($step == 0) {
  1462. if ($opState != 5) { # wait till hot water preparation stopped
  1463. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 0->1: Initializing Measurment";
  1464. $step = 1;
  1465. $lastOpHours = $currOpHours;
  1466. $lastHQ = $currHQ;
  1467. $minTemp = $currTemp;
  1468. }
  1469. # step 1 = wait till hot water preparation starts -> monitor Tmin, take previous HQ and previous operating hours
  1470. } elsif ($step == 1) {
  1471. if ($currTemp < $minTemp) { # monitor minimum temperature
  1472. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 1: Monitor minimum temperature ($minTemp -> $currTemp)";
  1473. $minTemp = $currTemp;
  1474. }
  1475. if ($opState != 5) { # wait -> update operating hours and HQ to be used as start value in calculations
  1476. $lastOpHours = $currOpHours;
  1477. $lastHQ = $currHQ;
  1478. } else { # go to step 2 - if hot water preparation running
  1479. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 1->2: Hot water preparation started ".($currOpHours-$lastOpHours)." s ago";
  1480. $step = 2;
  1481. $maxTemp = $currTemp;
  1482. }
  1483. # step 2 = wait till hot water preparation done and target reached
  1484. } elsif ($step == 2) {
  1485. if ($currTemp < $minTemp) { # monitor minimal temperature
  1486. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 2: Boiler temperature still decreasing ($minTemp -> $currTemp)";
  1487. $minTemp = $currTemp;
  1488. }
  1489. if ($currTemp > $maxTemp) { # monitor maximal temperature
  1490. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 2: Boiler temperature increasing ($maxTemp -> $currTemp)";
  1491. $maxTemp = $currTemp;
  1492. }
  1493. if ($opState != 5) { # wait till hot water preparation stopped
  1494. if ($currTemp >= $target) {
  1495. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 2->3: Hot water preparation stopped";
  1496. $step = 3;
  1497. } else {
  1498. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 2->1: Measurement cancelled (hot water preparation stopped but target not reached, $currTemp < $target)";
  1499. $step = 1;
  1500. $lastOpHours = $currOpHours;
  1501. $lastHQ = $currHQ;
  1502. $minTemp = $currTemp;
  1503. }
  1504. }
  1505. # step 3 = wait with calculation till temperature maximum reached once
  1506. } elsif ($step == 3) {
  1507. # cancel measurement - if hot water preparation has restarted
  1508. if ($opState == 5) {
  1509. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 3->0: Measurement cancelled (hot water preparation restarted before maximum reached)";
  1510. $step = 0;
  1511. # monitor maximal temperature
  1512. } elsif ($currTemp > $maxTemp) {
  1513. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 3: Temperature still increasing ($maxTemp -> $currTemp)";
  1514. $maxTemp = $currTemp;
  1515. # else calculate temperature gradient
  1516. } else {
  1517. LUXTRONIK2_Log $name, 4, "Statistic Boiler Heat-Up step 3->1: Boiler heat-up measurement finished";
  1518. $value1 = ( int(10 * $maxTemp) - int(10 * $minTemp) ) / 10; # delta hot water temperature
  1519. $value2 = ( $currOpHours - $lastOpHours ) / 60; # delta time (minutes)
  1520. $value3 = $currHQ - $lastHQ; # delta heat quantity, average thermal power
  1521. $returnStr = sprintf "DT/min: %.2f DT: %.2f Dmin: %.0f DQ: %.1f thP: %.1f", $value1/$value2, $value1, $value2, $value3, $value3*60/$value2;
  1522. #real (mixed) Temperature-Difference
  1523. my $boilerVolumn = AttrVal($name, "boilerVolumn", 0);
  1524. if ($boilerVolumn >0 ) {
  1525. # (delta T) [K] = Wärmemenge [kWh] / #Volumen [l] * ( 3.600 [kJ/kWh] / ( 4,179 [kJ/(kg*K)] (H2O Wärmekapazität bei 40°C) * 0,992 [kg/l] (H2O Dichte bei 40°C) ) [K/(kWh*l)] )
  1526. $value2 = 868.4 * $value3 / $boilerVolumn ;
  1527. $returnStr .= sprintf " realDT: %.0f", $value2;
  1528. }
  1529. $step = 1;
  1530. $lastOpHours = $currOpHours;
  1531. $lastHQ = $currHQ;
  1532. $minTemp = $currTemp;
  1533. }
  1534. }
  1535. $hash->{fhem}{statBoilerHeatUpStep} = $step;
  1536. $hash->{fhem}{statBoilerHeatUpMin} = $minTemp;
  1537. $hash->{fhem}{statBoilerHeatUpMax} = $maxTemp;
  1538. $hash->{fhem}{statBoilerHeatUpHQ} = $lastHQ;
  1539. $hash->{fhem}{statBoilerHeatUpOpHours} = $lastOpHours;
  1540. return $returnStr;
  1541. }
  1542. # Calculate heat loss gradients of boiler based on hotWaterTemperature and counterHeatQHeating
  1543. sub ########################################
  1544. LUXTRONIK2_doStatisticBoilerCoolDown ($$$$$$)
  1545. {
  1546. my ($hash, $time, $currTemp, $opState, $target, $threshold) = @_;
  1547. my $name = $hash->{NAME};
  1548. my $step = $hash->{fhem}{statBoilerCoolDownStep};
  1549. my $maxTemp = $hash->{fhem}{statBoilerCoolDownMax};
  1550. my $startTime = $hash->{fhem}{statBoilerCoolDownStartTime};
  1551. my $lastTime = $hash->{fhem}{statBoilerCoolDownLastTime};
  1552. my $lastTemp = $hash->{fhem}{statBoilerCoolDownLastTemp};
  1553. my $value1 = 0;
  1554. my $value2 = 0;
  1555. my $value3 = 0;
  1556. my $returnStr = "";
  1557. # step 0 = Initialize - if hot water preparation is off and target reached,
  1558. if ($step == 0) {
  1559. if ($opState == 5 || $currTemp < $target) { # -> stay step 0
  1560. # LUXTRONIK2_Log $name, 4, "Statistic Boiler Cool-Down step 0: Wait till hot water preparation stops and target is reached ($currTemp < $target)";
  1561. } else {
  1562. LUXTRONIK2_Log $name, 4, "Statistic Boiler Cool-Down step 0->1: Initializing, target reached ($currTemp >= $target)";
  1563. $step = 1;
  1564. $startTime = $time;
  1565. $maxTemp = $currTemp;
  1566. }
  1567. # step 1 = wait till threshold is reached -> do calculation, monitor maximal temperature
  1568. } elsif ($step == 1) {
  1569. if ($currTemp > $maxTemp) { # monitor maximal temperature
  1570. LUXTRONIK2_Log $name, 4, "Statistic Boiler Cool-Down step 1: Temperature still increasing ($currTemp > $maxTemp)";
  1571. $maxTemp = $currTemp;
  1572. $startTime = $time;
  1573. }
  1574. if ($opState == 5 || $currTemp <= $threshold) {
  1575. if ($opState == 5) {
  1576. LUXTRONIK2_Log $name, 4, "Statistic Boiler Cool-Down step 1->0: Heat-up started, measurement finished";
  1577. $value1 = $lastTemp - $maxTemp; # delta hot water temperature
  1578. $value2 = ( $lastTime - $startTime ) / 3600; # delta time (hours)
  1579. } elsif ($currTemp <= $threshold) {
  1580. LUXTRONIK2_Log $name, 4, "Statistic Boiler Cool-Down step 1->0: Measurement finished, threshold reached ($currTemp <= $threshold)";
  1581. $value1 = $currTemp - $maxTemp; # delta hot water temperature
  1582. $value2 = ( $time - $startTime ) / 3600; # delta time (hours)
  1583. }
  1584. $returnStr = sprintf "DT/h: %.2f DT: %.1f Dh: %.2f", $value1/$value2, $value1, $value2;
  1585. $step = 0;
  1586. }
  1587. }
  1588. $hash->{fhem}{statBoilerCoolDownStep} = $step;
  1589. $hash->{fhem}{statBoilerCoolDownMax} = $maxTemp;
  1590. $hash->{fhem}{statBoilerCoolDownStartTime} = $startTime;
  1591. $hash->{fhem}{statBoilerCoolDownLastTime} = $time;
  1592. $hash->{fhem}{statBoilerCoolDownLastTemp} = $currTemp;
  1593. return $returnStr;
  1594. }
  1595. # Calculates single MaxMin Values and informs about end of day and month
  1596. sub ########################################
  1597. LUXTRONIK2_doStatisticMinMax ($$$)
  1598. {
  1599. my ($hash, $readingName, $value) = @_;
  1600. my $dummy;
  1601. my $saveLast;
  1602. my $statReadingName;
  1603. my $lastReading;
  1604. my $lastSums;
  1605. my @newReading;
  1606. my $yearLast;
  1607. my $monthLast;
  1608. my $dayLast;
  1609. my $dayNow;
  1610. my $monthNow;
  1611. my $yearNow;
  1612. # Determine date of last and current reading
  1613. if (exists($hash->{READINGS}{$readingName."Day"}{TIME})) {
  1614. ($yearLast, $monthLast, $dayLast) = $hash->{READINGS}{$readingName."Day"}{TIME} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)/;
  1615. } else {
  1616. ($dummy, $dummy, $dummy, $dayLast, $monthLast, $yearLast) = localtime;
  1617. $yearLast += 1900;
  1618. $monthLast ++;
  1619. }
  1620. ($dummy, $dummy, $dummy, $dayNow, $monthNow, $yearNow) = localtime;
  1621. $yearNow += 1900;
  1622. $monthNow ++;
  1623. # Daily Statistic
  1624. $saveLast = ($dayNow != $dayLast);
  1625. $statReadingName = $readingName."Day";
  1626. LUXTRONIK2_doStatisticMinMaxSingle $hash, $statReadingName, $value, $saveLast;
  1627. # Monthly Statistic
  1628. $saveLast = ($monthNow != $monthLast);
  1629. $statReadingName = $readingName."Month";
  1630. LUXTRONIK2_doStatisticMinMaxSingle $hash, $statReadingName, $value, $saveLast;
  1631. # Yearly Statistic
  1632. $saveLast = ($yearNow != $yearLast);
  1633. $statReadingName = $readingName."Year";
  1634. LUXTRONIK2_doStatisticMinMaxSingle $hash, $statReadingName, $value, $saveLast;
  1635. return ;
  1636. }
  1637. # Calculates single MaxMin Values and informs about end of day and month
  1638. sub ########################################
  1639. LUXTRONIK2_doStatisticMinMaxSingle ($$$$)
  1640. {
  1641. my ($hash, $readingName, $value, $saveLast) = @_;
  1642. my $result;
  1643. my $lastReading = $hash->{READINGS}{$readingName}{VAL} || "";
  1644. # Initializing
  1645. if ( $lastReading eq "" ) {
  1646. my $since = strftime "%Y-%m-%d_%H:%M:%S", localtime();
  1647. $result = "Count: 1 Sum: $value ShowDate: 1";
  1648. readingsBulkUpdate($hash, ".".$readingName, $result);
  1649. $result = "Min: $value Avg: $value Max: $value (since: $since )";
  1650. readingsBulkUpdate($hash, $readingName, $result);
  1651. # Calculations
  1652. } else {
  1653. my @a = split / /, $hash->{READINGS}{"." . $readingName}{VAL}; # Internal values
  1654. my @b = split / /, $lastReading;
  1655. # Do calculations
  1656. $a[1]++; # Count
  1657. $a[3] += $value; # Sum
  1658. if ($value < $b[1]) { $b[1]=$value; } # Min
  1659. if ($a[1]>0) {$b[3] = sprintf "%.1f" , $a[3] / $a[1] ;} # Avg
  1660. if ($value > $b[5]) { $b[5]=$value; } # Max
  1661. # in case of period change, save "last" values and reset counters
  1662. if ($saveLast) {
  1663. $result = "Min: $b[1] Avg: $b[3] Max: $b[5]";
  1664. if ($a[5] == 1) { $result .= " (since: $b[7] )"; }
  1665. readingsBulkUpdate($hash, $readingName . "Last", $lastReading);
  1666. $a[1] = 1; $a[3] = $value; $a[5] = 0;
  1667. $b[1] = $value; $b[3] = $value; $b[5] = $value;
  1668. }
  1669. # Store internal calculation values
  1670. $result = "Count: $a[1] Sum: $a[3] ShowDate: $a[5]";
  1671. readingsBulkUpdate($hash, ".".$readingName, $result);
  1672. # Store visible Reading
  1673. if ($a[5] == 1) {
  1674. $result = "Min: $b[1] Avg: $b[3] Max: $b[5] (since: $b[7] )";
  1675. } else {
  1676. $result = "Min: $b[1] Avg: $b[3] Max: $b[5]";
  1677. }
  1678. readingsBulkUpdate($hash, $readingName, $result);
  1679. }
  1680. return;
  1681. }
  1682. sub ########################################
  1683. LUXTRONIK2_storeReadings($$$$$$)
  1684. {
  1685. my ($hash, $readingName, $value, $factor, $doStatistics, $electricalPower) = @_;
  1686. if ($value eq "no" || $value == 0 ) { return; }
  1687. readingsBulkUpdate($hash, $readingName, sprintf("%.1f", $value / $factor));
  1688. $readingName =~ s/counter//;
  1689. # LUXTRONIK2_doStatisticDelta: $hash, $readingName, $value, $factor, $electricalPower
  1690. if ( $doStatistics == 1) { LUXTRONIK2_doStatisticDelta $hash, "stat".$readingName, $value, $factor, $electricalPower; }
  1691. }
  1692. # Calculates deltas for day, month and year
  1693. sub ########################################
  1694. LUXTRONIK2_doStatisticDelta ($$$$$)
  1695. {
  1696. my ($hash, $readingName, $value, $factor, $electricalPower) = @_;
  1697. my $name = $hash->{NAME};
  1698. my $dummy;
  1699. my $result;
  1700. my $deltaValue;
  1701. my $previousTariff;
  1702. my $showDate;
  1703. # Determine if time period switched (day, month, year)
  1704. # Get deltaValue and Tariff of previous call
  1705. my $periodSwitch = 0;
  1706. my $yearLast; my $monthLast; my $dayLast; my $dayNow; my $monthNow; my $yearNow;
  1707. if (exists($hash->{READINGS}{"." . $readingName . "Before"})) {
  1708. ($yearLast, $monthLast, $dayLast) = ($hash->{READINGS}{"." . $readingName . "Before"}{TIME} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)/);
  1709. $yearLast -= 1900;
  1710. $monthLast --;
  1711. ($dummy, $deltaValue, $dummy, $previousTariff, $dummy, $showDate) = split / /, $hash->{READINGS}{"." . $readingName . "Before"}{VAL} || "";
  1712. $deltaValue = $value - $deltaValue;
  1713. } else {
  1714. ($dummy, $dummy, $dummy, $dayLast, $monthLast, $yearLast) = localtime;
  1715. $deltaValue = 0;
  1716. $previousTariff = 0;
  1717. $showDate = 6;
  1718. }
  1719. ($dummy, $dummy, $dummy, $dayNow, $monthNow, $yearNow) = localtime;
  1720. if ($yearNow != $yearLast) { $periodSwitch = 3; }
  1721. elsif ($monthNow != $monthLast) { $periodSwitch = 2; }
  1722. elsif ($dayNow != $dayLast) { $periodSwitch = 1; }
  1723. # Determine if "since" value has to be shown in current and last reading
  1724. if ($periodSwitch == 3) {
  1725. if ($showDate == 1) { $showDate = 0; } # Do not show the "since:" value for year changes anymore
  1726. if ($showDate >= 2) { $showDate = 1; } # Shows the "since:" value for the first year change
  1727. }
  1728. if ($periodSwitch >= 2){
  1729. if ($showDate == 3) { $showDate = 2; } # Do not show the "since:" value for month changes anymore
  1730. if ($showDate >= 4) { $showDate = 3; } # Shows the "since:" value for the first month change
  1731. }
  1732. if ($periodSwitch >= 1){
  1733. if ($showDate == 5) { $showDate = 4; } # Do not show the "since:" value for day changes anymore
  1734. if ($showDate >= 6) { $showDate = 5; } # Shows the "since:" value for the first day change
  1735. }
  1736. # LUXTRONIK2_doStatisticDeltaSingle; $hash, $readingName, $deltaValue, $periodSwitch, $showDate, $firstCall
  1737. LUXTRONIK2_doStatisticDeltaSingle ($hash, $readingName, $deltaValue, $factor, $periodSwitch, $showDate, 0);
  1738. my $activeTariff = ReadingsVal($name,"activeTariff",0);
  1739. if ( $electricalPower >=0 ) {
  1740. my $readingNamePower = $readingName;
  1741. $readingNamePower =~ s/Hours/Electricity/ ;
  1742. if ($activeTariff > 0) {
  1743. foreach (1,2,3,4,5,6,7,8,9) {
  1744. if ( $previousTariff == $_ ) {
  1745. LUXTRONIK2_doStatisticDeltaSingle ($hash, $readingNamePower."Tariff".$_, $deltaValue * $electricalPower, $factor, $periodSwitch, $showDate, 1);
  1746. } elsif ($activeTariff == $_ || ($periodSwitch > 0 && exists($hash->{READINGS}{$readingNamePower . "Tariff".$_}))) {
  1747. LUXTRONIK2_doStatisticDeltaSingle ($hash, $readingNamePower."Tariff".$_, 0, $factor, $periodSwitch, $showDate, 1);
  1748. }
  1749. }
  1750. } else {
  1751. LUXTRONIK2_doStatisticDeltaSingle ($hash, $readingNamePower, $deltaValue * $electricalPower, $factor, $periodSwitch, $showDate, 1);
  1752. }
  1753. }
  1754. # Hidden storage of current values for next call(before values)
  1755. $result = "Value: $value Tariff: $activeTariff ShowDate: $showDate";
  1756. readingsBulkUpdate($hash, ".".$readingName."Before", $result);
  1757. return ;
  1758. }
  1759. sub ########################################
  1760. LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
  1761. {
  1762. my ($hash, $readingName, $deltaValue, $factor, $periodSwitch, $showDate, $specMonth) = @_;
  1763. my $dummy;
  1764. my $result;
  1765. # get existing statistic reading
  1766. my @curr;
  1767. if (exists($hash->{READINGS}{".".$readingName}{VAL})) {
  1768. @curr = split / /, $hash->{READINGS}{".".$readingName}{VAL} || "";
  1769. } else {
  1770. $curr[1] = 0; $curr[3] = 0; $curr[5] = 0;
  1771. if ($showDate>5) {$curr[7] = strftime "%Y-%m-%d_%H:%M:%S", localtime();} # start
  1772. else {$curr[7] = strftime "%Y-%m-%d", localtime();} # start
  1773. }
  1774. # get statistic values of previous period
  1775. my @last;
  1776. if ($periodSwitch >= 1) {
  1777. if (exists ($hash->{READINGS}{$readingName."Last"})) {
  1778. @last = split / /, $hash->{READINGS}{$readingName."Last"}{VAL};
  1779. } else {
  1780. @last = split / /, "Day: - Month: - Year: -";
  1781. }
  1782. }
  1783. # Do statistic
  1784. $curr[1] += $deltaValue;
  1785. $curr[3] += $deltaValue;
  1786. $curr[5] += $deltaValue;
  1787. # If change of year, change yearly statistic
  1788. if ($periodSwitch == 3){
  1789. if ($specMonth) { $last[5] = sprintf("%.3f",$curr[5] / $factor/ 1000); }
  1790. else {$last[5] = sprintf("%.0f",$curr[5] / $factor);}
  1791. $curr[5] = 0;
  1792. if ($showDate == 1) { $last[7] = $curr[7]; }
  1793. }
  1794. # If change of month, change monthly statistic
  1795. if ($periodSwitch >= 2){
  1796. if ($specMonth) { $last[3] = sprintf("%.3f",$curr[3] / $factor/ 1000); }
  1797. else {$last[3] = sprintf("%.0f",$curr[3] / $factor);}
  1798. $curr[3] = 0;
  1799. if ($showDate == 3) { $last[7] = $curr[7];}
  1800. }
  1801. # If change of day, change daily statistic
  1802. if ($periodSwitch >= 1){
  1803. $last[1] = sprintf("%.1f",$curr[1] / $factor);
  1804. $curr[1] = 0;
  1805. if ($showDate == 5) {
  1806. $last[7] = $curr[7];
  1807. # Next monthly and yearly values start at 00:00 and show only date (no time)
  1808. $curr[3] = 0;
  1809. $curr[5] = 0;
  1810. $curr[7] = strftime "%Y-%m-%d", localtime(); # start
  1811. }
  1812. }
  1813. # Store hidden statistic readings (delta values)
  1814. $result = "Day: $curr[1] Month: $curr[3] Year: $curr[5]";
  1815. if ( $showDate >=2 ) { $result .= " (since: $curr[7] )"; }
  1816. readingsBulkUpdate($hash,".".$readingName,$result);
  1817. # Store visible statistic readings (delta values)
  1818. if ($specMonth) { $result = sprintf "Day: %.1f Month: %.3f Year: %.3f", $curr[1]/$factor, $curr[3]/$factor/1000, $curr[5]/$factor/1000; }
  1819. else { $result = sprintf "Day: %.1f Month: %.0f Year: %.0f", $curr[1]/$factor, $curr[3]/$factor, $curr[5]/$factor; }
  1820. if ( $showDate >=2 ) { $result .= " (since: $curr[7] )"; }
  1821. readingsBulkUpdate($hash,$readingName,$result);
  1822. # if changed, store previous visible statistic (delta) values
  1823. if ($periodSwitch >= 1) {
  1824. $result = "Day: $last[1] Month: $last[3] Year: $last[5]";
  1825. if ( $showDate =~ /1|3|5/ ) { $result .= " (since: $last[7] )";}
  1826. readingsBulkUpdate($hash,$readingName."Last",$result);
  1827. }
  1828. }
  1829. 1;
  1830. =pod
  1831. =item device
  1832. =item summary Controls a Luxtronik 2.0 controller for heat pumps
  1833. =item summary_DE Steuert eine Luxtronik 2.0 Heizungssteuerung für W&auml;rmepumpen.
  1834. =begin html
  1835. <a name="LUXTRONIK2"></a>
  1836. <h3>LUXTRONIK2</h3>
  1837. <div>
  1838. <ul>
  1839. Luxtronik 2.0 is a heating controller used in <a href="http://www.alpha-innotec.de">Alpha Innotec</a>, Siemens Novelan (WPR NET) and Wolf Heiztechnik (BWL/BWS) heat pumps.
  1840. <br>
  1841. It has a built-in ethernet port, so it can be directly integrated into a local area network (LAN).
  1842. <br>
  1843. <i>The modul is reported to work with firmware: V1.51, V1.54C, V1.60, V1.64, V1.69, V1.70, V1.73, V1.77.</i>
  1844. <br>
  1845. More Info on the particular <a href="http://www.fhemwiki.de/wiki/Luxtronik_2.0">page of FHEM-Wiki</a> (in German).
  1846. <br>
  1847. &nbsp;
  1848. <br>
  1849. <a name="LUXTRONIK2define"></a>
  1850. <b>Define</b>
  1851. <ul>
  1852. <code>define &lt;name&gt; LUXTRONIK2 &lt;IP-address[:Port]&gt; [poll-interval]</code><br>
  1853. If the pool interval is omitted, it is set to 300 (seconds). Smallest possible value is 30.
  1854. <br>
  1855. Usually, the port needs not to be defined.
  1856. <br>
  1857. Example: <code>define Heizung LUXTRONIK2 192.168.0.12 600</code>
  1858. </ul>
  1859. <br>
  1860. <a name="LUXTRONIK2set"></a>
  1861. <b>Set</b>
  1862. <ul>A firmware check assures before each set operation that a heat pump with untested firmware is not damaged accidently.
  1863. <li><code>activeTariff &lt; 0 - 9 &gt;</code>
  1864. <br>
  1865. Allows the separate measurement of the consumption (doStatistics = 1) within different tariffs.<br>
  1866. This value must be set at the correct point of time in accordance to the existing or planned tariff <b>by the FHEM command "at"</b>.<br>
  1867. 0 = without separate tariffs
  1868. </li><br>
  1869. <li><code>INTERVAL &lt;polling interval&gt;</code><br>
  1870. Polling interval in seconds
  1871. </li><br>
  1872. <li><code>hotWaterTemperatureTarget &lt;temperature&gt;</code><br>
  1873. Target temperature of domestic hot water boiler in &deg;C
  1874. </li><br>
  1875. <li><code>hotWaterCircPumpDeaerate &lt;on | off&gt;</code><br>
  1876. Switches the external circulation pump for the hot water on or off. The circulation prevents a cool down of the hot water in the pipes but increases the heat consumption drastically.
  1877. <br>
  1878. NOTE! It uses the deaerate function of the controller. So, the pump alternates always 5 minutes on and 5 minutes off.
  1879. </li><br>
  1880. <li><code>opModeHotWater &lt;Mode&gt;</code><br>
  1881. Operating Mode of domestic hot water boiler (Auto | Party | Off)
  1882. </li><br>
  1883. <li><code>resetStatistics &lt;statReadings&gt;</code>
  1884. <br>
  1885. Deletes the selected statistic values <i>all, statBoilerGradientCoolDownMin, statAmbientTemp..., statElectricity..., statHours..., statHeatQ...</i>
  1886. </li><br>
  1887. <li><code>returnTemperatureHyst &lt;Temperature&gt;</code>
  1888. <br>
  1889. Hysteresis of the returnTemperatureTarget of the heating controller . 0.5 K till 3 K. Adjustable in 0.1 steps.
  1890. </li><br>
  1891. <li><code>returnTemperatureSetBack &lt;Temperature&gt;</code>
  1892. <br>
  1893. Decreasing or increasing of the returnTemperatureTarget by -5 K till + 5 K
  1894. </li><br>
  1895. <li><code>statusRequest</code><br>
  1896. Update device information
  1897. </li><br>
  1898. <li><code>synchClockHeatPump</code><br>
  1899. Synchronizes controller clock with FHEM time. <b>!! This change is lost in case of controller power off!!</b></li>
  1900. </ul>
  1901. <br>
  1902. <a name="LUXTRONIK2get"></a>
  1903. <b>Get</b>
  1904. <ul>
  1905. No get implemented yet ...
  1906. </ul>
  1907. <br>
  1908. <a name="LUXTRONIK2attr"></a>
  1909. <b>Attributes</b>
  1910. <ul>
  1911. <li><code>allowSetParameter &lt; 0 | 1 &gt;</code>
  1912. <br>
  1913. The <a href="#LUXTRONIK2set">parameters</a> of the heat pump controller can only be changed if this attribut is set to 1.
  1914. </li><br>
  1915. <li><code>autoSynchClock &lt;delay&gt;</code>
  1916. <br>
  1917. Corrects the clock of the heatpump automatically if a certain <i>delay</i> (10 s - 600 s) against the FHEM time is exeeded. Does a firmware check before.
  1918. <br>
  1919. <i>(A 'delayDeviceTimeCalc' &lt;= 2 s can be caused by the internal calculation interval of the heat pump controller.)</i>
  1920. </li><br>
  1921. <li><code>compressor2ElectricalPowerWatt</code><br>
  1922. Electrical power of the 2nd compressor to calculated the COP and estimate electrical consumption (calculations not implemented yet)
  1923. </li><br>
  1924. <li><code>doStatistics &lt; 0 | 1 &gt;</code>
  1925. <br>
  1926. Calculates statistic values: <i>statBoilerGradientHeatUp, statBoilerGradientCoolDown, statBoilerGradientCoolDownMin (boiler heat loss)</i>
  1927. <br>
  1928. Builds daily, monthly and yearly statistics for certain readings (average/min/max or cumulated values).
  1929. <br>
  1930. Logging and visualisation of the statistic should be done with readings of type 'stat<i>ReadingName</i><b>Last</b>'.
  1931. </li><br>
  1932. <li><code>heatPumpElectricalPowerWatt</code><br>
  1933. Electrical power of the heat pump by a flow temperature of 35&deg;C to calculated coefficency factor and estimate electrical consumption
  1934. </li><br>
  1935. <li><code>heatPumpElectricalPowerFactor</code><br>
  1936. Change of electrical power consumption per 1 K flow temperature differenz to 35&deg;C (e.g. 2% per 1 K = 0,02)
  1937. </li><br>
  1938. <li><code>heatRodElectricalPowerWatt</code><br>
  1939. Electrical power of the heat rods (2nd heat source) to estimate electrical consumption
  1940. </li><br>
  1941. <li><code>ignoreFirmwareCheck &lt; 0 | 1 &gt;</code>
  1942. <br>
  1943. A firmware check assures before each set operation that a heatpump controller with untested firmware is not damaged accidently.
  1944. <br>
  1945. If this attribute is set to 1, the firmware check is ignored and new firmware can be tested for compatibility.
  1946. </li><br>
  1947. <li><code>statusHTML</code>
  1948. <br>
  1949. If set, a HTML-formatted reading named "floorplanHTML" is created. It can be used with the <a href="#FLOORPLAN">FLOORPLAN</a> module.
  1950. <br>
  1951. Currently, if the value of this attribute is not NULL, the corresponding reading consists of the current status of the heat pump and the temperature of the water.
  1952. </li><br>
  1953. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1954. </ul>
  1955. </ul>
  1956. </div>
  1957. =end html
  1958. =begin html_DE
  1959. <a name="LUXTRONIK2"></a>
  1960. <h3>LUXTRONIK2</h3>
  1961. <div>
  1962. <ul>
  1963. Die Luxtronik 2.0 ist eine Heizungssteuerung der Firma <a href="http://www.alpha-innotec.de">Alpha Innotec</a>, welche in W&auml;rmepumpen von Alpha Innotec,
  1964. Siemens Novelan (WPR NET), Roth (ThermoAura®, ThermoTerra), Elco und Wolf Heiztechnik (BWL/BWS) verbaut ist.
  1965. Sie besitzt einen Ethernet Anschluss, so dass sie direkt in lokale Netzwerke (LAN) integriert werden kann.
  1966. <br>
  1967. <i>Das Modul wurde bisher mit folgender Steuerungs-Firmware getestet: V1.51, V1.54C, V1.60, V1.64, V1.69, V1.70, V1.73, V1.77.</i>
  1968. <br>
  1969. Mehr Infos im entsprechenden <u><a href="http://www.fhemwiki.de/wiki/Luxtronik_2.0">Artikel der FHEM-Wiki</a></u>.
  1970. <br>&nbsp;
  1971. <br>
  1972. <a name="LUXTRONIK2define"></a>
  1973. <b>Define</b>
  1974. <ul>
  1975. <code>define &lt;name&gt; LUXTRONIK2 &lt;IP-Adresse[:Port]&gt; [Abfrageinterval]</code>
  1976. <br>
  1977. Wenn das Abfrage-Interval nicht angegeben ist, wird es auf 300 (Sekunden) gesetzt. Der kleinste m&ouml;gliche Wert ist 30.
  1978. <br>
  1979. Die Angabe des Portes kann gew&ouml;hnlich entfallen.
  1980. <br>
  1981. Beispiel: <code>define Heizung LUXTRONIK2 192.168.0.12 600</code>
  1982. </ul>
  1983. <br>
  1984. <a name="LUXTRONIK2set"></a>
  1985. <b>Set</b><br>
  1986. <ul>
  1987. Durch einen Firmware-Test wird vor jeder Set-Operation sichergestellt, dass W&auml;rmepumpen mit ungetester Firmware nicht unabsichtlich besch&auml;digt werden.
  1988. <br>&nbsp;
  1989. <li><code>activeTariff &lt; 0 - 9 &gt;</code>
  1990. <br>
  1991. Erlaubt die gezielte, separate Erfassung der statistischen Verbrauchswerte (doStatistics = 1) f&uuml;r verschiedene Tarife (Doppelstromz&auml;hler)<br>
  1992. Dieser Wert muss entsprechend des vorhandenen oder geplanten Tarifes zum jeweiligen Zeitpunkt z.B. durch den FHEM-Befehl "at" gesetzt werden.<br>
  1993. 0 = tariflos
  1994. </li><br>
  1995. <li><code>hotWaterCircPumpDeaerate &lt;on | off&gt;</code><br>
  1996. Schaltet die externe Warmwasser-Zirkulationspumpe an oder aus. Durch die Zirkulation wird das Abk&uuml;hlen des Warmwassers in den Hausleitungen verhindert. Der W&auml;rmeverbrauch steigt jedoch drastisch.
  1997. <br>
  1998. Achtung! Es wird die Entl&uuml;ftungsfunktion der Steuerung genutzt. Dadurch taktet die Pumpe jeweils 5 Minuten ein und 5 Minuten aus.
  1999. </li><br>
  2000. <li><code>hotWaterTemperatureTarget &lt;Temperatur&gt;</code>
  2001. <br>
  2002. Soll-Temperatur des Hei&szlig;wasserboilers in &deg;C
  2003. </li><br>
  2004. <li><code>opModeHotWater &lt;Betriebsmodus&gt;</code>
  2005. <br>
  2006. Betriebsmodus des Hei&szlig;wasserboilers ( Auto | Party | Off )
  2007. </li><br>
  2008. <li><code>resetStatistics &lt;statWerte&gt;</code>
  2009. <br>
  2010. L&ouml;scht die ausgew&auml;hlten statisischen Werte: <i>all, statBoilerGradientCoolDownMin, statAmbientTemp..., statElectricity..., statHours..., statHeatQ...</i>
  2011. </li><br>
  2012. <li><code>returnTemperatureHyst &lt;Temperatur&gt;</code>
  2013. <br>
  2014. Sollwert-Hysterese der Heizungsregelung. 0.5 K bis 3 K. In 0.1er Schritten einstellbar.
  2015. </li><br>
  2016. <li><code>returnTemperatureSetBack &lt;Temperatur&gt;</code>
  2017. <br>
  2018. Absenkung oder Anhebung der R&uuml;cklauftemperatur von -5&deg;C bis + 5&deg;C
  2019. </li><br>
  2020. <li><code>INTERVAL &lt;Sekunden&gt;</code>
  2021. <br>
  2022. Abfrageinterval in Sekunden
  2023. </li><br>
  2024. <li><code>statusRequest</code>
  2025. <br>
  2026. Aktualisieren der Ger&auml;tewerte
  2027. </li><br>
  2028. <li><code>synchClockHeatPump</code>
  2029. <br>
  2030. Abgleich der Uhr der Steuerung mit der FHEM Zeit. <b>Diese &Auml;nderung geht verloren, sobald die Steuerung ausgeschaltet wird!!</b></li>
  2031. </ul>
  2032. <br>
  2033. <a name="LUXTRONIK2get"></a>
  2034. <b>Get</b>
  2035. <ul>
  2036. Es wurde noch kein "get" implementiert ...
  2037. </ul>
  2038. <br>
  2039. <a name="LUXTRONIK2attr"></a>
  2040. <b>Attribute</b>
  2041. <ul>
  2042. <li><code>allowSetParameter &lt; 0 | 1 &gt;</code>
  2043. <br>
  2044. Die internen <a href="#LUXTRONIK2set">Parameter</a> der W&auml;rmepumpensteuerung k&ouml;nnen
  2045. nur ge&auml;ndert werden, wenn dieses Attribut auf 1 gesetzt ist.
  2046. </li><br>
  2047. <li><code>autoSynchClock &lt;Zeitunterschied&gt;</code>
  2048. <br>
  2049. Die Uhr der W&auml;rmepumpe wird automatisch korrigiert, wenn ein gewisser <i>Zeitunterschied</i> (10 s - 600 s)
  2050. gegen&uuml;ber der FHEM Zeit erreicht ist. Zuvor wird die Kompatibilit&auml;t der Firmware &uuml;berpr&uuml;ft.<br>
  2051. <i>(Ein Ger&auml;tewert 'delayDeviceTimeCalc' &lt;= 2 s ist auf die internen Berechnungsintervale der
  2052. W&auml;rmepumpensteuerung zur&uuml;ckzuf&uuml;hren.)</i>
  2053. </li><br>
  2054. <li><code>compressor2ElectricalPowerWatt</code><br>
  2055. Betriebsleistung des zweiten Kompressors zur Berechung der Arbeitszahl (erzeugte W&auml;rme pro elektrische Energieeinheit)
  2056. und Absch&auml;tzung des elektrischen Verbrauches (Auswertungen noch nicht implementiert)
  2057. </li><br>
  2058. <li><code>doStatistics &lt; 0 | 1 &gt;</code>
  2059. <br>
  2060. Berechnet statistische Werte: <i>statBoilerGradientHeatUp, statBoilerGradientCoolDown,
  2061. statBoilerGradientCoolDownMin (W&auml;rmeverlust des Boilers)</i>
  2062. <br>
  2063. Bildet t&auml;gliche, monatliche und j&auml;hrliche Statistiken bestimmter Ger&auml;tewerte.<br>
  2064. F&uuml;r grafische Auswertungen k&ouml;nnen die Werte der Form 'stat<i>ReadingName</i><b>Last</b>' genutzt werden.
  2065. </li><br>
  2066. <li><code>heatPumpElectricalPowerWatt &lt;E-Leistung in Watt&gt;</code><br>
  2067. Elektrische Leistungsaufnahme der W&auml;rmepumpe in Watt bei einer Vorlauftemperatur von 35 &deg;C zur Berechung der Arbeitszahl (erzeugte W&auml;rme pro elektrische Energieeinheit)
  2068. und Absch&auml;tzung des elektrischen Verbrauches
  2069. </li><br>
  2070. <li><code>heatPumpElectricalPowerFactor</code><br>
  2071. &Auml;nderung der elektrischen Leistungsaufnahme pro 1 K Vorlauftemperaturdifferenz zu 35 &deg;C
  2072. <br>
  2073. (z.B. 2% pro 1 K = 0,02)
  2074. </li><br>
  2075. <li><code>heatRodElectricalPowerWatt &lt;E-Leistung in Watt&gt;</code><br>
  2076. Elektrische Leistungsaufnahme der Heizst&auml;be in Watt zur Absch&auml;tzung des elektrischen Verbrauches
  2077. </li><br>
  2078. <li><code>ignoreFirmwareCheck &lt; 0 | 1 &gt;</code>
  2079. <br>
  2080. Durch einen Firmware-Test wird vor jeder Set-Operation sichergestellt, dass W&auml;rmepumpen
  2081. mit ungetester Firmware nicht unabsichtlich besch&auml;digt werden. Wenn dieses Attribute auf 1
  2082. gesetzt ist, dann wird der Firmware-Test ignoriert und neue Firmware kann getestet werden.
  2083. Dieses Attribut wird jedoch ignoriert, wenn die Steuerungs-Firmware bereits als nicht kompatibel berichtet wurde.
  2084. </li><br>
  2085. <li><code>statusHTML</code>
  2086. <br>
  2087. wenn gesetzt, dann wird ein HTML-formatierter Wert "floorplanHTML" erzeugt,
  2088. welcher vom Modul <a href="#FLOORPLAN">FLOORPLAN</a> genutzt werden kann.<br>
  2089. Momentan wird nur gepr&uuml;ft, ob der Wert dieses Attributes ungleich NULL ist,
  2090. der entsprechende Ger&auml;tewerte besteht aus dem aktuellen W&auml;rmepumpenstatus und der Heizwassertemperatur.
  2091. </li><br>
  2092. <li><a href="#readingFnAttributes">readingFnAttributes</a>
  2093. </li><br>
  2094. </ul>
  2095. </ul>
  2096. </div>
  2097. =end html_DE
  2098. =cut