70_NT5000.pm 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220
  1. ########################################################################################
  2. #
  3. # NT5000.pm
  4. #
  5. # FHEM module to read the data from a Sunways NT5000 solar inverter
  6. #
  7. # Prof. Dr. Peter A. Henning, 2008
  8. #
  9. # Version 2.2 - January 2014
  10. #
  11. # TODO: do not ask for serialport.pm when emulator is called
  12. #
  13. # Setup as:
  14. # define <name> NT5000 <device>
  15. #
  16. # where <name> may be replaced by any name string and <device>
  17. # is a serial (USB) device or the keyword "emulator".
  18. # In the latter case, a 4.5 kWP solar installation is simulated
  19. #
  20. # get <name> present => 1 if device present, 0 if not
  21. # get <name> serial => inverter serial number
  22. # get <name> proto => protocol
  23. # get <name> reading => measurement for all channels
  24. # get <name> month => monthly measurement
  25. # get <name> year => yearly measurement
  26. #
  27. # set <name> time => set inverter clock
  28. #
  29. # Additional attributes are defined in fhem.cfg as
  30. # attr nt5000 room Solaranlage
  31. # Area of solar installation
  32. # attr nt5000 Area 32.75
  33. # Peak Solar Power
  34. # attr nt5000 PSP 4.5
  35. # Monthly and yearly log file
  36. # attr nt5000 LogM SolarLogM
  37. # attr nt5000 LogY SolarLogY
  38. # Months with erroneous readings - see line 83 ff
  39. # attr nt5000 MERR <list>
  40. # Expected yields per month / year
  41. # attr nt5000 Wx_M1 150
  42. # attr nt5000 Wx_M2 250
  43. # attr nt5000 Wx_M3 350
  44. # attr nt5000 Wx_M4 450
  45. # attr nt5000 Wx_M5 600
  46. # attr nt5000 Wx_M6 600
  47. # attr nt5000 Wx_M7 600
  48. # attr nt5000 Wx_M8 600
  49. # attr nt5000 Wx_M9 450
  50. # attr nt5000 Wx_M10 350
  51. # attr nt5000 Wx_M11 250
  52. # attr nt5000 Wx_M12 150
  53. # attr nt5000 Wx_Y 4800
  54. #
  55. ########################################################################################
  56. #
  57. # This programm is free software; you can redistribute it and/or modify
  58. # it under the terms of the GNU General Public License as published by
  59. # the Free Software Foundation; either version 2 of the License, or
  60. # (at your option) any later version.
  61. #
  62. # The GNU General Public License can be found at
  63. # http://www.gnu.org/copyleft/gpl.html.
  64. # A copy is found in the textfile GPL.txt and important notices to the license
  65. # from the author is found in LICENSE.txt distributed with these scripts.
  66. #
  67. # This script is distributed in the hope that it will be useful,
  68. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  69. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  70. # GNU General Public License for more details.
  71. #
  72. ########################################################################################
  73. package main;
  74. use strict;
  75. use warnings;
  76. use Device::SerialPort;
  77. #-- Prototypes to make komodo happy
  78. use vars qw{%attr %defs};
  79. sub Log($$);
  80. #-- Line counter
  81. my $cline=0;
  82. #-- These we may get on request
  83. my %gets = (
  84. "present" => "",
  85. "serial" => "S",
  86. "proto" => "P",
  87. "reading" => "R",
  88. "month" => "M",
  89. "year" => "Y"
  90. );
  91. #-- These occur in a pulldown menu as settable values
  92. my %sets = (
  93. "time" => "T"
  94. );
  95. ########################################################################################
  96. #
  97. # NT5000_Initialize
  98. #
  99. # Parameter hash
  100. #
  101. ########################################################################################
  102. sub NT5000_Initialize ($) {
  103. my ($hash) = @_;
  104. $hash->{DefFn} = "NT5000_Define";
  105. $hash->{GetFn} = "NT5000_Get";
  106. $hash->{SetFn} = "NT5000_Set";
  107. # Area = Area of solar panels, to calculate expected output from solar irradiation
  108. # PSP = Peak Solar Power of installation
  109. # MERR = List of month entries that failed. Reason: Defective loggers in the NT5000 itself
  110. # sometimes "jump" ahead in the monthly setting. Maybe singular problem of author ?
  111. # Every pseudo-month-entry in this list means that its yield is ADDED to the FOLLOWING month
  112. # e.g. MERR = 4 => Month value for April is wrong and should be added to the value from May,
  113. # which is the following one.
  114. # WxM1 .. WxM12 = Expected yield from January .. December
  115. # WxY = Expected yield per year
  116. # LogM, LogY = name of the monthly and yearly log file
  117. $hash->{AttrList}= "Area PSP MERR ".
  118. "Wx_M1 Wx_M2 Wx_M3 Wx_M4 Wx_M5 Wx_M6 Wx_M7 Wx_M8 Wx_M9 Wx_M10 Wx_M11 Wx_M12 ".
  119. "Wx_Y LogM LogY ".
  120. "loglevel ".
  121. $readingFnAttributes;
  122. }
  123. ########################################################################################
  124. #
  125. # NT5000_Define - Implements DefFn function
  126. #
  127. # Parameter hash, definition string
  128. #
  129. ########################################################################################
  130. sub NT5000_Define($$) {
  131. my ($hash, $def) = @_;
  132. my @a = split("[ \t][ \t]*", $def);
  133. return "Define the serial device as a parameter, use none or emulator for a fake device"
  134. if(@a != 3);
  135. my $dev = $a[2];
  136. Log 1, "NT5000 device is none, commands will be echoed only"
  137. if($dev eq "none");
  138. Log 1, "NT5000 with emulator mode"
  139. if($dev eq "emulator");
  140. if( ($dev ne "none") && ($dev ne "emulator")) {
  141. Log 3, "NT5000 opening device $dev";
  142. my $nt5000_serport = new Device::SerialPort ($dev);
  143. return "NT5000 Can't open $dev: $!" if(!$nt5000_serport);
  144. Log 2, "NT5000 opened device $dev";
  145. $hash->{USBDev} = $nt5000_serport;
  146. sleep(1);
  147. $nt5000_serport->close();
  148. }
  149. $hash->{DeviceName} = $dev;
  150. $hash->{INTERVAL} = 60; # call every 60 seconds
  151. $hash->{Cmd} = "reading"; # get all data, min/max unchange
  152. $hash->{SerialNumber} = "";
  153. $hash->{Protocol} = "";
  154. $hash->{Firmware} = "";
  155. $modules{NT5000}{defptr}{$a[0]} = $hash;
  156. #-- InternalTimer blocks if init_done is not true
  157. my $oid = $init_done;
  158. $init_done = 1;
  159. readingsSingleUpdate($hash,"state","initialized",1);
  160. NT5000_GetStatus($hash);
  161. $init_done = $oid;
  162. return undef;
  163. }
  164. ########################################################################################
  165. #
  166. # NT5000_Get - Implements GetFn function
  167. #
  168. # Parameter hash, argument array
  169. #
  170. ########################################################################################
  171. sub NT5000_Get ($@) {
  172. my ($hash, @a) = @_;
  173. #-- check syntax
  174. return "NT5000_Get needs exactly one parameter" if(@a != 2);
  175. my $name = $hash->{NAME};
  176. my $v;
  177. #-- get present
  178. if($a[1] eq "present") {
  179. $v = ($hash->{READINGS}{"state"}{VAL} =~ m/.*kW/) ? 1 : 0;
  180. return "$a[0] present => $v";
  181. }
  182. #-- current reading
  183. if($a[1] eq "reading") {
  184. $v = NT5000_GetLine($hash,"reading");
  185. if(!defined($v)) {
  186. Log GetLogLevel($name,2), "NT5000_Get $a[1] error";
  187. return "$a[0] $a[1] => Error";
  188. }
  189. $v =~ s/[\r\n]//g; # Delete the NewLine
  190. readingsSingleUpdate($hash,"reading",$v,1);
  191. #-- monthly reading
  192. } elsif($a[1] eq "month") {
  193. $v = NT5000_GetMonth($hash);
  194. if(!defined($v)) {
  195. Log GetLogLevel($name,2), "NT5000_Get $a[1] error";
  196. return "$a[0] $a[1] => Error";
  197. }
  198. readingsSingleUpdate($hash,"month","Wm: ".$hash->{month}->[0]." kWh (".$hash->{month}->[2]."%)",1);
  199. #-- yearly reading
  200. } elsif($a[1] eq "year") {
  201. $v = NT5000_GetYear($hash);
  202. if(!defined($v)) {
  203. Log GetLogLevel($name,2), "NT5000_Get $a[1] error";
  204. return "$a[0] $a[1] => Error";
  205. }
  206. readingsSingleUpdate($hash,"year","Wy: ".$hash->{year}->[0]." kWh (".$hash->{year}->[4]."%)",1);
  207. } else {
  208. return "NT5000_Get with unknown argument $a[1], choose one of " . join(" ", sort keys %gets);
  209. }
  210. Log GetLogLevel($name,3), "NT5000_Get $a[1] $v";
  211. return "$a[0] $a[1] => $v";
  212. }
  213. ########################################################################################
  214. #
  215. # NT5000_Set - Implements SetFn function
  216. #
  217. # Parameter hash, a = argument array
  218. #
  219. ########################################################################################
  220. sub NT5000_Set ($@) {
  221. my ($hash, @a) = @_;
  222. my $name = shift @a;
  223. my $res;
  224. #-- for the selector: which values are possible
  225. return join(" ", sort keys %sets) if(@a != 2);
  226. return "NT5000_Set: With unknown argument $a[0], choose one of " . join(" ", sort keys %sets)
  227. if(!defined($sets{$a[0]}));
  228. #-- Set time value
  229. if( $a[0] eq "time" ){
  230. #-- only values >= 15 secs allowed
  231. if( $a[1] >= 15){
  232. $res = "not yet implemented";
  233. } else {
  234. $res = "not yet implemented";
  235. }
  236. Log GetLogLevel($name,3), "NT5000_Set $name ".join(" ",@a)." => $res";
  237. return "NT5000_Set => $name ".join(" ",@a)." => $res";
  238. }
  239. }
  240. ########################################################################################
  241. #
  242. # NT5000 - GetStatus - Called in regular intervals to obtain current reading
  243. #
  244. # Parameter hash
  245. #
  246. ########################################################################################
  247. sub NT5000_GetStatus ($) {
  248. my ($hash) = @_;
  249. my $name = $hash->{NAME};
  250. #-- restart timer for updates
  251. RemoveInternalTimer($hash);
  252. InternalTimer(gettimeofday()+ $hash->{INTERVAL}, "NT5000_GetStatus", $hash,1);
  253. # Obtain the current reading
  254. my $result = NT5000_GetLine($hash, "reading");
  255. # If one of these applies we must assume that the inverter is offline (no retry !)
  256. # Logging only if this is a change from the previous state
  257. if( !defined($result) ) {
  258. Log GetLogLevel($name,1), "NT5000 cannot be read, inverter offline" if( $hash->{READINGS}{"state"}{VAL} ne "offline" );
  259. #Log 3, "NT5000 cannot be read, inverter offline";
  260. readingsSingleUpdate($hash,"state","offline",1);
  261. return "offline";
  262. } elsif( length($result) < 13 ){
  263. Log GetLogLevel($name,1), "NT5000 returns incomplete line, inverter offline" if( $hash->{READINGS}{"state"}{VAL} ne "starting" );
  264. #Log 3, "NT5000 returns incomplete line";
  265. readingsSingleUpdate($hash,"state","incomplete",1);
  266. return "incomplete";
  267. }else {
  268. # we have obtained a reading: inverter is online
  269. readingsBeginUpdate($hash);
  270. my @names = ("Udc", "Idc", "Pdc", "Uac", "Iac", "Pac", "Temp", "S", "Wd", "Wtot", "Eta");
  271. my @units = ("Volt", "Ampere", "Kilowatt", "Volt", "Ampere", "Kilowatt", "Celsius", "Watt per m<sup>2</sup>", "Kilowatthours", "Kilowatthours", "percent");
  272. my @unitabbr = ("V", "A", "kW", "V", "A", "kW", "&deg;C", "W/m<sup>2</sup>", "kWh", "kWh", "%");
  273. my @type = ("voltage", "current", "power", "voltage", "current", "power", "temperature", "power density", "energy", "energy","efficiency");
  274. #-- we are in the first reading, have turned online recently
  275. if( $hash->{READINGS}{"state"}{VAL} !~ m/.*kW.*/ ) {
  276. Log GetLogLevel($name,2), "NT5000 inverter is online";
  277. readingsBulkUpdate($hash,"state","online");
  278. #-- Obtain the serial number and protocol
  279. my $serial = NT5000_GetLine($hash, "serial");
  280. $serial =~ s/^.*S://;
  281. $serial =~ s/[\r\n ]//g;
  282. $hash->{SerialNumber} = "$serial";
  283. my $proto = NT5000_GetLine($hash, "proto");
  284. $proto =~ s/^.*P://;
  285. $proto =~ s/[\r\n ]//g;
  286. $hash->{Firmware} = substr($proto,0,1).".".substr($proto,1,1);
  287. $hash->{Protocol} = substr($proto,2,1).".".substr($proto,4,2);
  288. # Obtain monthly readings in 20 seconds - only once
  289. InternalTimer(gettimeofday()+ 20, "NT5000_GetMonth", $hash,1);
  290. # Obtain yearly readings in 40 seconds - only once
  291. InternalTimer(gettimeofday()+ 40, "NT5000_GetYear", $hash,1);
  292. #-- Put header lines into the log file
  293. my $resmod ="";
  294. for(my $i = 0; $i < int(@names); $i++) {
  295. $resmod .= $names[$i]." ";
  296. }
  297. readingsBulkUpdate($hash,"header1",$resmod);
  298. $resmod ="";
  299. for(my $i = 0; $i < int(@unitabbr); $i++) {
  300. $resmod .= "[".$unitabbr[$i]."] ";
  301. }
  302. readingsBulkUpdate($hash,"header2",$resmod);
  303. #-- set units properly
  304. for(my $i = 0; $i < int(@names); $i++) {
  305. $hash->{READINGS}{$names[$i]}{UNIT} = $units[$i];
  306. $hash->{READINGS}{$names[$i]}{UNITABBR} = $unitabbr[$i];
  307. $hash->{READINGS}{$names[$i]}{TYPE} = $type[$i];
  308. }
  309. };
  310. #-- put into READINGS
  311. readingsBulkUpdate($hash,"reading",$result);
  312. Log GetLogLevel($name,5), "NT5000 online result = $result";
  313. #-- split result for writing into hash
  314. my @data = split(' ',$result);
  315. readingsBulkUpdate($hash,"state", sprintf("%5.3f %s",$data[5],$unitabbr[5]));
  316. for(my $i = 0; $i < int(@names); $i++) {
  317. # This puts individual pairs into the tabular view
  318. readingsBulkUpdate($hash,$names[$i],$data[$i]);
  319. }
  320. readingsEndUpdate($hash,1);
  321. $result =~ s/;/ /g;
  322. }
  323. return $hash->{READINGS}{"state"}{VAL};
  324. }
  325. ########################################################################################
  326. #
  327. # NT5000_GetMonth - Read monthly data from inverter
  328. #
  329. # Parameter hash
  330. #
  331. ########################################################################################
  332. sub NT5000_GetMonth ($) {
  333. my ($hash) = @_;
  334. my $name = $hash->{NAME};
  335. my ($ln,$lf,$ret,$daten);
  336. my ($sec,$min,$hour,$dayn,$month,$year,$wday,$yday,$isdst) = localtime(time);
  337. my $yearn = $year+1900;
  338. my $monn = $month+1;
  339. #-- Obtain the monthly reading
  340. my $result = NT5000_GetLine($hash, "month");
  341. $result =~ s/^.*M://;
  342. $result =~ s/[\r\n ]//g;
  343. Log GetLogLevel($name,3), "NT5000 monthly result = $result";
  344. $result=~ s/,/./g;
  345. my @data = split(";", $result);
  346. my $day = $data[0];
  347. #-- Expected yield for month
  348. my $mex = "Wx_M".($month+1);
  349. my $wex = $attr{$name}{$mex};
  350. my $wac = 0;
  351. my $wre;
  352. my $wav;
  353. my $value;
  354. my @names = ("W_D01","W_D02","W_D03","W_D04","W_D05","W_D06","W_D07","W_D08","W_D09","W_D10",
  355. "W_D11","W_D12","W_D13","W_D14","W_D15","W_D16","W_D17","W_D18","W_D19","W_D20",
  356. "W_D21","W_D22","W_D23","W_D24","W_D25","W_D26","W_D27","W_D28","W_D29","W_D30","W_D31");
  357. #-- Check current logfile
  358. $ln = $attr{$name}{"LogM"};
  359. if( !(defined($ln))){
  360. Log 1,"NT5000_GetMonth: Attribute LogM is missing";
  361. #-- here some other output of monthly data
  362. } else {
  363. $lf = $defs{$ln}{currentlogfile};
  364. $ret = open(NT5000FILE, "> $lf" );
  365. if( $ret) {
  366. print NT5000FILE "monthly data: Day Wd Wm Wex\n";
  367. for(my $i = 0; $i < $day; $i++) {
  368. $dayn = $i+1;
  369. #-- for current day actual time, otherwise dummy time
  370. if( $i == ($day-1) ){
  371. $daten = sprintf("%4d-%02d-%02d_%02d:%02d:%02d",$yearn,$monn,$dayn,$hour,$min,$sec);
  372. }else{
  373. $daten = sprintf("%4d-%02d-%02d_23:59:00",$yearn,$monn,$dayn);
  374. }
  375. $wac += $data[$day-$i];
  376. if( $wex ){
  377. $wre = int(1000*$wac/$wex)/10 if ($wex>0 );
  378. };
  379. # Put one item per line into the log file
  380. printf NT5000FILE "%s %s %5s %6.3f %5.1f %5.1f\n",$daten,$name,$names[$i],$data[$day-$i],$wac,$wre;
  381. };
  382. Log 1,"NT5000_GetMonth: File $lf overwritten";
  383. close(NT5000FILE);
  384. #-- daily average
  385. $wav = ($day > 1) ? int( 10*($wac-$data[1]) / ($day-1) )/10.0 : 0.0;
  386. #-- store value
  387. $hash->{month}->[0]=$wac;
  388. $hash->{month}->[1]=$wex;
  389. $hash->{month}->[2]=$wre;
  390. $hash->{month}->[2]=$wav;
  391. #-- return value
  392. $value = "\nWm ".$wac." kWh (monthly sum until now)\n";
  393. $value .= "Wa ".$wav." kWh/d (average until yesterday)\n";
  394. $value .= $wre."% of expected ".$wex." kWh";
  395. return $value;
  396. } else {
  397. Log 1,"NT5000_GetMonth: Cannot open $lf for writing!";
  398. return undef;
  399. }
  400. }
  401. }
  402. ########################################################################################
  403. #
  404. # NT5000_GetYear - Read yearly data from inverter
  405. #
  406. # Parameter hash
  407. #
  408. ########################################################################################
  409. sub NT5000_GetYear ($) {
  410. my ($hash) = @_;
  411. my $name = $hash->{NAME};
  412. my ($ln,$lf,$ret,$monn,$daten,$mex,$mmex,$value);
  413. my ($sec,$min,$hour,$dayn,$month,$year,$wday,$yday,$isdst) = localtime(time);
  414. my $yearn = $year+1900;
  415. #-- Obtain the yearly reading
  416. my $result = NT5000_GetLine($hash, "year");
  417. $result =~ s/^.*Y://;
  418. $result =~ s/[\r\n ]//g;
  419. Log GetLogLevel($name,3), "NT5000 yearly result = $result";
  420. $result=~ s/,/./g;
  421. my @data = split(";", $result);
  422. #-- Expected yield for year
  423. my $wex = $attr{$name}{Wx_Y};
  424. my $wac = 0;
  425. my $wre;
  426. my $wme = 0;
  427. my $wrm;
  428. my @names = ("W_M01","W_M02","W_M03","W_M04","W_M05","W_M06","W_M07","W_M08","W_M09","W_M10",
  429. "W_M11","W_M12");
  430. #-- Check current logfile
  431. $ln = $attr{$name}{"LogY"};
  432. if( !(defined($ln))){
  433. Log 1,"NT5000_GetYear: Attribute LogY is missing";
  434. #-- here some other output of yearly data
  435. } else {
  436. $lf = $defs{$ln}{currentlogfile};
  437. $ret = open(NT5000FILE, "> $lf" );
  438. if( $ret) {
  439. print NT5000FILE "yearly data: Month Wm Wex Wy Wy \n";
  440. for(my $i = 0; $i <= $month; $i++) {
  441. $monn = $i+1;
  442. #-- for current month actual time, otherwise dummy time
  443. if( $i == $month ){
  444. $daten = sprintf("%4d-%02d-28_%02d:%02d:%02d",$yearn,$monn,$hour,$min,$sec);
  445. }else{
  446. $daten = sprintf("%4d-%02d-28_23:59:00",$yearn,$monn);
  447. }
  448. $mex = "Wx_M".($monn);
  449. $mmex = $attr{$name}{$mex};
  450. $wme += $mmex;
  451. $wac += $data[$month+1-$i];
  452. #-- relative total
  453. if( $wex ){
  454. $wre = int(1000.0*$wac/$wex)/10 if ($wex > 0);
  455. };
  456. #-- relative expected
  457. if( $wme ){
  458. $wrm = int(1000.0*$wac/$wme)/10 if ($wme > 0);
  459. };
  460. # Put one item per line into the log file
  461. printf NT5000FILE "%s %s %5s %5.1f %3d %6.1f %5.1f %6.1f %5.1f\n",$daten,$name,$names[$i],$data[$month+1-$i],$mmex,$wac,$wre,$wme,$wrm;
  462. }
  463. Log 1,"NT5000_GetYear: File $lf overwritten";
  464. close(NT5000FILE);
  465. #-- store value
  466. $hash->{year}->[0]=$wac;
  467. $hash->{year}->[1]=$wex;
  468. $hash->{year}->[2]=$wre;
  469. $hash->{year}->[3]=$wme;
  470. $hash->{year}->[4]=$wrm;
  471. #-- return value
  472. $value = "\nWy ".$wac." kWh (yearly sum until now)\n";
  473. $value .= $wrm."% of expected ".$wme." kWh\n";
  474. $value .= $wre."% of total ".$wex." kWh";
  475. return $value;
  476. } else {
  477. Log 1,"NT5000_GetYear: Cannot open $lf for writing!";
  478. return undef;
  479. }
  480. }
  481. }
  482. ########################################################################################
  483. #
  484. # NT5000_GetLine - Read data from inverter
  485. #
  486. # Parameter hash, a = string parameter to define what will be asked
  487. #
  488. ########################################################################################
  489. sub NT5000_GetLine ($$) {
  490. my ($hash,$a) = @_;
  491. my $name = $hash->{NAME};
  492. return "NT5000_GetLine: Unknown argument $a, choose one of " . join(",", sort keys %gets)
  493. if(!defined($gets{$a}));
  494. my $dev = $hash->{DeviceName};
  495. #-- Inverter data
  496. my $rError = "\x00\x01\x01\x01";
  497. my $rOnline1 = "\x00\x01\x02\x01";
  498. my $rMon1 = "\x00\x01\x03\x01";
  499. my $rYear1 = "\x00\x01\x04\x01";
  500. my $rTime = "\x00\x01\x06\x01";
  501. my $rSerial = "\x00\x01\x08\x01";
  502. my $rProFW = "\x00\x01\x09\x01";
  503. my $sYY = "\x00\x01\x50";
  504. my $sLL = "\x00\x01\x51";
  505. my $sDD = "\x00\x01\x52";
  506. my $sHH = "\x00\x01\x53";
  507. my $sMM = "\x00\x01\x54";
  508. my @invBuffer;
  509. #------------------------ current readings -------------------------------------
  510. if( $a eq "reading" )
  511. {
  512. #Log 3, "Asking for online data";
  513. my $invReturn = NT5000_5to13($hash,$dev,$rOnline1);
  514. #-- test if this is an offline case
  515. if( !($invReturn) )
  516. {
  517. return undef;
  518. }
  519. @invBuffer=split(//,$invReturn);
  520. #-- test again if this is an offline case
  521. if( @invBuffer < 13 )
  522. {
  523. return undef;
  524. }
  525. #-- Process data
  526. my $udc = ord($invBuffer[0])*2.8+100;
  527. my $idc = ord($invBuffer[1])*0.08;
  528. my $uac = ord($invBuffer[2])+100.0;
  529. my $iac = ord($invBuffer[3])*0.120;
  530. my $t = ord($invBuffer[4])-40.0;
  531. my $s = ord($invBuffer[5])*6.0;
  532. my $pdc = int($udc*$idc)/1000;
  533. my $pac = int($uac*$iac)/1000;
  534. my $wd = (ord($invBuffer[6])* 256 + ord($invBuffer[7]))/1000;
  535. my $wtot= ord($invBuffer[8])* 256 + ord($invBuffer[9]);
  536. #-- Calculate eta
  537. my $name= $hash->{NAME};
  538. my $a = $attr{$name}{Area};
  539. my $eta;
  540. if ( $s && $a ) {
  541. if( ($a>0)&&($s>0) ) {
  542. $eta = int(100000*$pac/($s*$a))/100;
  543. } else {
  544. $eta = 0;
  545. };
  546. } else {
  547. $eta=0;
  548. }
  549. return sprintf "%3.1f %2.2f %1.3f %3.1f %2.2f %1.3f %2.0f %4.0f %2.3f %5.1f %1.2f",
  550. $udc,$idc,$pdc,$uac,$iac,$pac,$t,$s,$wd,$wtot,$eta;
  551. #------------------------ montly readings -------------------------------------
  552. } elsif( $a eq "month" ) {
  553. my $i=1;
  554. #-- Get the first block anyhow
  555. my $ica=$i%256;
  556. my $cmd2=sprintf("%s%c",substr($rMon1,0,3),$ica);
  557. my $invReturn = NT5000_5to13($hash,$dev,$cmd2);
  558. #-- test if this is an offline case
  559. if( !($invReturn) ){
  560. return undef;
  561. }
  562. @invBuffer=split(//,$invReturn);
  563. #-- test again if this is an offline case
  564. if( @invBuffer < 13 ) {
  565. return undef;
  566. }
  567. #-- Process data
  568. my $day = ord($invBuffer[1]);
  569. my $result="M:$day;";
  570. for( my $j=0; $j<3; $j++ ) {
  571. $result .= ((ord($invBuffer[2+4*$j])* 256 + ord($invBuffer[3+4*$j]))/1000).";" if( ($day-$j) > 0);
  572. }
  573. #-- Get further blocks if necessary
  574. for( $i=2; $i<=($day+2)/3; $i++) {
  575. $ica=$i%256;
  576. $cmd2=sprintf("%s%c",substr($rMon1,0,3),$ica);
  577. $invReturn = NT5000_5to13($hash,$dev,$cmd2);
  578. #-- test if this is an offline case
  579. if( !($invReturn) ) {
  580. return undef;
  581. }
  582. @invBuffer=split(//,$invReturn);
  583. #-- test again if this is an offline case
  584. if( @invBuffer < 13 ) {
  585. return undef;
  586. }
  587. for( my $j=0; $j<3; $j++ ) {
  588. $result .= ((ord($invBuffer[2+4*$j])* 256 + ord($invBuffer[3+4*$j]))/1000).";" if( ($day-($i-1)*3-$j) > 0);
  589. }
  590. };
  591. return "$result\n";
  592. #------------------------ yearly readings -------------------------------------
  593. } elsif( $a eq "year" ) {
  594. my $i=1;
  595. #-- We read the full data, e.g. current month (cm) .. cm-12
  596. my @pmval;
  597. #-- Get the first block
  598. my $ica=$i%256;
  599. my $cmd2=sprintf("%s%c",substr($rYear1,0,3),$ica);
  600. my $invReturn = NT5000_5to13($hash,$dev,$cmd2);
  601. #-- test if this is an offline case
  602. if( !($invReturn) ) {
  603. return undef;
  604. }
  605. @invBuffer=split(//,$invReturn);
  606. #-- test again if this is an offline case
  607. if( @invBuffer < 13 ) {
  608. return undef;
  609. }
  610. #-- Process data for current month (cm) .. cm-4
  611. my $month = ord($invBuffer[1]);
  612. for( my $j=0; $j<5; $j++ ) {
  613. #-- value for pseudo-month
  614. push(@pmval,(ord($invBuffer[2+2*$j])* 256 + ord($invBuffer[3+2*$j]))/10);
  615. }
  616. #-- Get the second block
  617. $i++;
  618. $ica=$i%256;
  619. $cmd2=sprintf("%s%c",substr($rYear1,0,3),$ica);
  620. $invReturn = NT5000_5to13($hash,$dev,$cmd2);
  621. #-- test if this is an offline case
  622. if( !($invReturn) ) {
  623. return undef;
  624. }
  625. @invBuffer=split(//,$invReturn);
  626. #-- test again if this is an offline case
  627. if( @invBuffer < 13 ) {
  628. return undef;
  629. }
  630. #-- Process data for cm-5 .. cm-10
  631. for( my $j=0; $j<6; $j++ ) {
  632. #-- value for pseudo-month
  633. push(@pmval,(ord($invBuffer[2*$j])* 256 + ord($invBuffer[1+2*$j]))/10);
  634. };
  635. #-- Get the third block
  636. $i++;
  637. $ica=$i%256;
  638. $cmd2=sprintf("%s%c",substr($rYear1,0,3),$ica);
  639. $invReturn = NT5000_5to13($hash,$dev,$cmd2);
  640. #-- test if this is an offline case
  641. if( !($invReturn) ) {
  642. return undef;
  643. }
  644. @invBuffer=split(//,$invReturn);
  645. #-- test again if this is an offline case
  646. if( @invBuffer < 13 ) {
  647. return undef;
  648. }
  649. #-- Process data for cm-11 .. cm-12
  650. for( my $j=0; $j<2; $j++ ) {
  651. #-- value for pseudo-month
  652. push(@pmval,(ord($invBuffer[2*$j])* 256 + ord($invBuffer[1+2*$j]))/10);
  653. };
  654. #-- Now we have to correct for those erroneous jumps of the internal data logger
  655. # of the NT5000
  656. # The first one is never wrong, belongs to the current month
  657. my @val = ($pmval[0]);
  658. my $result;
  659. # which and how many pseudo-month-entries do we have ?
  660. my $merr = $attr{$name}{MERR};
  661. # none, we my return HERE
  662. if( !defined($merr) ) {
  663. @val= ($pmval[0]);
  664. for( my $j=1; $j<=$month; $j++ ){
  665. push(@val,$pmval[$j]);
  666. }
  667. $result = "Y:$month;".join(';',@val);
  668. return $result;
  669. };
  670. # oops, correction has to be done
  671. my @merrs = split(',',$merr);
  672. my $merrno = @merrs;
  673. #-- For the year we therefore need the first $month+$merrno entries
  674. my $pm;
  675. my $mlim = $month+$merrno;
  676. for( my $j=1; $j<$mlim; $j++ ) {
  677. $pm = $month-$j;
  678. my $listed = 0;
  679. #-- check if this is in the list
  680. for( my $k=0; $k<$merrno; $k++ ) {
  681. # yes, it is
  682. if( $merrs[$k]==$pm ) {
  683. $listed = 1;
  684. }
  685. }
  686. # yes, it is indeed
  687. if( $listed==1) {
  688. # add data to the last entry in @val
  689. $val[@val-1]+=$pmval[$j];
  690. # no, it is not
  691. } else {
  692. # append value to array
  693. push(@val,$pmval[$j]);
  694. }
  695. };
  696. #-- Compare the results
  697. #Log 3, "YEAR PSEUDO ".join(';',@pmval);
  698. #Log 3, "YEAR CORR ".join(';',@val);
  699. $result = "Y:$month;".join(';',@val);
  700. return $result;
  701. }elsif( $a eq "serial" ) {
  702. my $r1 = NT5000_5to13($hash,$dev,$rSerial);
  703. return "S:".substr($r1,0,12)."\n"
  704. }elsif( $a eq "proto" ){
  705. my $r1 = NT5000_5to13($hash,$dev,$rProFW);
  706. return "P:".substr($r1,0,6)."\n"
  707. }else {
  708. print "OHOH => NT5000_GetLine mit Argument $a\n";
  709. }
  710. }
  711. ########################################################################################
  712. #
  713. # NT5000_5to13 - Read 13 bytes from the inverter or the emulator
  714. #
  715. # Parameter: hash,dev = none,emulator or a serial port definition
  716. # cmd = 5 byte parameter to query the device properly
  717. #
  718. ########################################################################################
  719. sub NT5000_5to13($$$) {
  720. my $retry = 0;
  721. my ($hash,$dev,$cmd) = @_;
  722. my $result;
  723. my ($i,$j,$k);
  724. if( $dev eq "none" ) #no inverter attached
  725. {
  726. return "\x00\x01\x02\x03\x04\x05\x06\x07\x07\x09\x0a\x0b\x0c";
  727. #-- read from emulator
  728. } elsif ( $dev eq "emulator" ) {
  729. my $CS = unpack("%32C*", $cmd);
  730. $CS=$CS%256;
  731. my $cmd2=sprintf("%s%c",$cmd,$CS);
  732. my $result = NT5000_emu(5,$cmd);
  733. return($result);
  734. #-- here we do the real thing
  735. } else {
  736. my $serport = new Device::SerialPort ($dev);
  737. if(!$serport) {
  738. Log 1, "NT5000: Can't open $dev: $!";
  739. return undef;
  740. }
  741. $serport->reset_error();
  742. $serport->baudrate(9600);
  743. $serport->databits(8);
  744. $serport->parity('none');
  745. $serport->stopbits(1);
  746. $serport->handshake('none');
  747. $serport->write_settings;
  748. #-- calculate checksum and send
  749. my $CS = unpack("%32C*", $cmd);
  750. $CS=$CS%256;
  751. my $cmd2=sprintf("%s%c",$cmd,$CS);
  752. my $count_out = $serport->write($cmd2);
  753. Log 3, "NT5000 write failed\n" unless ($count_out);
  754. Log 3, "NT5000 write incomplete $count_out ne ".(length($cmd2))."\n" if ( $count_out != 5 );;
  755. #-- sleeping 0.05 seconds
  756. select(undef,undef,undef,0.05);
  757. my ($count_in, $string_in) = $serport->read(13);
  758. #-- sleeping 0.05 seconds
  759. select(undef,undef,undef,0.05);
  760. $serport->close();
  761. return($string_in);
  762. }
  763. }
  764. ########################################################################################
  765. #
  766. # NT5000_emu - Emulator section - to be used, if the real solar inverter is not attached.
  767. #
  768. ########################################################################################
  769. sub NT5000_emu {
  770. #-- For configuration purpose: when does the sun come up, and when does it go down
  771. my $start = 6 + 55.0/60;
  772. my $stop = 21 + 31.0/60;
  773. #-- Inverter data
  774. my $rError = "\x00\x01\x01\x01";
  775. my $rOnline1 = "\x00\x01\x02\x01";
  776. my $rMon1 = "\x00\x01\x03\x01";
  777. my $rYear1 = "\x00\x01\x04\x01";
  778. my $rTime = "\x00\x01\x06\x01";
  779. my $rSerial = "\x00\x01\x08\x01";
  780. my $rProFW = "\x00\x01\x09\x01";
  781. my $sYY = "\x00\x01\x50";
  782. my $sLL = "\x00\x01\x51";
  783. my $sDD = "\x00\x01\x52";
  784. my $sHH = "\x00\x01\x53";
  785. my $sMM = "\x00\x01\x54";
  786. #-- Timer data
  787. my ($sec,$min,$hour,$day,$month,$year,$wday,$yday,$isdst) = localtime(time);
  788. #-- parse incoming parameters
  789. my ($count,$buf1)=@_;
  790. #-- default: do not send data
  791. my $senddata=0;
  792. my ($i,$j,$k);
  793. my (@buf3,@buf3a);
  794. #-- No bytes received
  795. if( $count == 0)
  796. {
  797. Log 3, "[NT5000 emulator] Zero bytes received, count=0";
  798. return undef;
  799. }
  800. #-- no sun yet
  801. if( (($hour+$min/60.0-$start)<0) || (($stop-$hour-$min/60.0)<0) )
  802. {
  803. Log 3, "[NT5000 emulator] No Sun !";
  804. return undef;
  805. }
  806. #-- 5 bytes received
  807. elsif( $count == 5)
  808. {
  809. my $buf2 = substr($buf1,0,4);
  810. my $buf4 = substr($buf1,0,3);
  811. #---- Error
  812. if( $buf2 eq $rError )
  813. {
  814. Log 3, "[NT5000 emulator] Request for error list received";
  815. my @buf3=($year,$month,$day,$hour,
  816. $min,0,0,0,0,0,0,0);
  817. $senddata=1;
  818. }
  819. #---- Online block 1
  820. elsif( $buf2 eq $rOnline1 )
  821. {
  822. #Log 3 "[NT5000 mulator] Request for online data block 1 received";
  823. my ($wd,$wdl,$wdh,$wtot,$wtotl,$wtoth,$uac,$udc,$iac,$idc,$pac,$t,$s)=(0,0,0,0,0,0,0,0,0,0,0,0,0);
  824. #-- shift into full day
  825. my $q = ($hour+$min/60.0-$start)/($stop-$start);
  826. if( ($q>0) && ($q<1.0) )
  827. {
  828. #-- produce fake data
  829. $wd= int( 4500/3.14*($stop-$start)*(1-cos($q*3.14)) );
  830. $wdl=$wd%256;
  831. $wdh=int(($wd-$wdl)/256);
  832. $wtot=1000+int($wd/1000);
  833. $wtotl=$wtot%256;
  834. $wtoth=int(($wtot-$wtotl)/256);
  835. $uac=int( 230-100 + 0.5 );
  836. $udc=int( (600-100)/2.8 + 0.5 );
  837. $iac=int( 4500/230*sin($q * 3.14)/0.12 + 0.5 );
  838. $idc=int( 4550/600*sin($q * 3.14)/0.08 + 0.5 );
  839. $pac=int(4500*sin($q*3.14))/1000.0;
  840. $t=10+40;
  841. $s=int(100/6);
  842. }
  843. @buf3=($udc,$idc,$uac,$iac,$t,$s,$wdh,$wdl,$wtoth,$wtotl,0,0);
  844. $senddata=1;
  845. }
  846. #---- Monthly data block
  847. elsif( substr($buf2,0,3) eq substr($rMon1,0,3) )
  848. {
  849. my $ica = substr($buf2,3,1);
  850. my $blocknr = ord($ica)%16;
  851. # Log 3, "[NT5000 emulator] Request for monthly data block $blocknr received";
  852. my ($mon1,$day1,$wdh1,$wdl1,$mon2,$day2,$wdh2,$wdl2,$mon3,$day3,$wdh3,$wdl3)=(0,0,0,0,0,0,0,0,0,0,0,0,0);
  853. #-- produce fake data
  854. $mon1 = $month;
  855. $day1 = $day+3-$blocknr*3;
  856. my $wd = (19.001 + 2*($day1 % 2))*1000.0;
  857. $wdl1 = $wd%256;
  858. $wdh1 = int(($wd-$wdl1)/256);
  859. $mon2 = $mon1;
  860. $day2 = $day1-1;
  861. if( $day2 < 1 )
  862. {
  863. $mon2-- if($mon2 > 0);
  864. $day2 = 31;
  865. }
  866. $wd = (19.001 + 2*($day2 % 2))*1000.0;
  867. $wdl2 = ($wd)%256;
  868. $wdh2 = int(($wd-$wdl2)/256);
  869. $mon3 = $mon2;
  870. $day3 = $day2-1;
  871. if( $day3 < 1 )
  872. {
  873. $mon3-- if($mon3 > 0);
  874. $day3 = 31;
  875. }
  876. $wd = (19.001 + 2*($day3 % 2))*1000.0;
  877. $wdl3=$wd%256;
  878. $wdh3=int(($wd-$wdl3)/256);
  879. @buf3=($mon1,$day1,$wdh1,$wdl1,$mon2,$day2,$wdh2,$wdl2,$mon3,$day3,$wdh3,$wdl3);
  880. $senddata=1;
  881. }
  882. #---- Yearly data block
  883. elsif( substr($buf2,0,3) eq substr($rYear1,0,3) )
  884. {
  885. my $ica = substr($buf2,3,1);
  886. my $blocknr = ord($ica)%16;
  887. Log 3, "[NT5000 emulator] Request for yearly data block $blocknr received";
  888. my ($wmh1,$wml1,$wmh2,$wml2,$wmh3,$wml3,$wmh4,$wml4,$wmh5,$wml5)=(0,0,0,0,0,0,0,0,0,0);
  889. my @pwm=(1500,2500,3500,4500,6000,6000,6000,6000,4500,3500,2500,1500);
  890. #-- produce fake data
  891. my $ip;
  892. @buf3=(0,0,0,0,0,0,0,0,0,0,0,0);
  893. if( $blocknr==1){
  894. $buf3[1]=$month;
  895. for( my $i=0;$i<5;$i++)
  896. {
  897. my $ip=$month-$i;
  898. if( $ip<0 )
  899. {
  900. $ip += 12;
  901. };
  902. my $wd=$pwm[$ip];
  903. my $wdl = ($wd)%256;
  904. my $wdh = int(($wd-$wdl)/256);
  905. $buf3[2*$i+2]=$wdh;
  906. $buf3[2*$i+3]=$wdl;
  907. }
  908. }elsif( $blocknr==2){
  909. for( my $i=0;$i<6;$i++)
  910. {
  911. my $ip=$month-5-$i;
  912. if( $ip<0 )
  913. {
  914. $ip += 12;
  915. };
  916. my $wd=$pwm[$ip];
  917. my $wdl = ($wd)%256;
  918. my $wdh = int(($wd-$wdl)/256);
  919. $buf3[2*$i]=$wdh;
  920. $buf3[2*$i+1]=$wdl;
  921. }
  922. }else{
  923. for( my $i=0;$i<2;$i++)
  924. {
  925. my $ip=$month-11-$i;
  926. if( $ip<0 )
  927. {
  928. $ip += 12;
  929. };
  930. my $wd=$pwm[$ip];
  931. my $wdl = ($wd)%256;
  932. my $wdh = int(($wd-$wdl)/256);
  933. $buf3[2*$i]=$wdh;
  934. $buf3[2*$i+1]=$wdl;
  935. }
  936. }
  937. $senddata=1;
  938. }
  939. #---- Time data
  940. elsif( $buf2 eq $rTime )
  941. {
  942. Log 3, "[NT5000 emulator] Request for time data received";
  943. @buf3=($year,$month,$day,$hour,
  944. $min,0,0,0,0,0,0,0,0);
  945. $senddata=1;
  946. }
  947. #---- Serial number
  948. elsif( $buf2 eq $rSerial )
  949. {
  950. Log 3, "[NT5000 emulator] Request for serial number received";
  951. @buf3 = (ord('1'),ord('5'),ord('3'),ord('3'),ord('A'),ord('5'),ord('0'),ord('1'),ord('2'),ord('3'),ord('4'),ord('5'));
  952. $senddata=1;
  953. }
  954. #---- Protocol and Firmware
  955. elsif( $buf2 eq $rProFW )
  956. {
  957. Log 3, "[NT5000 emulator] Request for protocol version received";
  958. @buf3=(ord('1'),ord('1'),ord('1'),ord('-'),ord('2'),ord('3'),0,0,0,0,0,0);
  959. $senddata=1;
  960. }
  961. #---- Set year
  962. elsif( $buf4 eq $sYY )
  963. {
  964. $year=ord(substr($buf2,3,1));
  965. Log 3, "[NT5000 eulator] Setting year to $year";
  966. }
  967. #---- Set month
  968. elsif( $buf4 eq $sLL )
  969. {
  970. $month=ord(substr($buf2,3,1));
  971. Log 3, "[NT5000 emulator] Setting month to $month";
  972. }
  973. #---- Set day
  974. elsif( $buf4 eq $sDD )
  975. {
  976. $day=ord(substr($buf2,3,1));
  977. Log 3, "[NT5000 emulator] Setting day to $day";
  978. }
  979. #---- Set hour
  980. elsif( $buf4 eq $sHH )
  981. {
  982. $hour=ord(substr($buf2,3,1))-1;
  983. Log 3, "[NT5000 emulator] Setting hour to $hour";
  984. }
  985. #---- Set minute
  986. elsif( $buf4 eq $sMM )
  987. {
  988. $min=ord(substr($buf2,3,1))-1;
  989. Log 3, "[NT5000 emulator] Setting minute to $min";
  990. }
  991. #---- show content
  992. else
  993. {
  994. Log 3, "[NT5000 emulator] Unknown request of 5 bytes received";
  995. for($i=0;$i<5;$i++)
  996. { $j=int(ord(substr($buf2,$i,1))/16);
  997. $k=ord(substr($buf2,$i,1))%16;
  998. print "byte $i = 0x$j$k\n";
  999. }
  1000. }
  1001. #---- Other number of bytes received
  1002. } else
  1003. {
  1004. Log 3, "[NT5000 emulator] $count bytes received";
  1005. for($i=0;$i<$count;$i++)
  1006. { $j=int(ord(substr($buf1,$i,1))/16);
  1007. $k=ord(substr($buf1,$i,1))%16;
  1008. print "byte $i = 0x$j$k\n";
  1009. }
  1010. }
  1011. #-- Here we are really sending data back to the main program
  1012. if( $senddata==1 ) {
  1013. #-- calculate checksum
  1014. my $CS=0;
  1015. for($i=0,$i<=11,$i++)
  1016. {
  1017. $CS+=$buf3[$i];
  1018. }
  1019. $CS=$CS%256;
  1020. my $data = sprintf("%c%c%c%c%c%c%c%c%c%c%c%c%c",
  1021. $buf3[0],$buf3[1],$buf3[2],$buf3[3],
  1022. $buf3[4],$buf3[5],$buf3[6],$buf3[7],
  1023. $buf3[8],$buf3[9],$buf3[10],$buf3[11],$CS);
  1024. #-- control
  1025. #print "Sending out:";
  1026. #for($i=0;$i<13;$i++)
  1027. # { $j=int(ord(substr($data,$i,1))/16);
  1028. # $k=ord(substr($data,$i,1))%16;
  1029. # print "byte $i = 0x$j$k\n";
  1030. # }
  1031. $senddata=0;
  1032. return($data);
  1033. }
  1034. else
  1035. {
  1036. return undef;
  1037. }
  1038. }
  1039. 1;
  1040. =pod
  1041. =begin html
  1042. <a name="NT5000"></a>
  1043. <h3>NT5000</h3>
  1044. <p>FHEM module to commmunicate with a Sunways NT5000 solar inverter<br />
  1045. </p>
  1046. <h4>Example</h4>
  1047. <p>
  1048. <code>define nt5000 NT5000 /dev/ttyUSB0 </code>
  1049. </p><br />
  1050. <a name="NT5000define"></a>
  1051. <h4>Define</h4>
  1052. <p>
  1053. <code>define &lt;name&gt; NT5000 &lt;device&gt; </code>
  1054. <br /><br /> Define a NT5000 solar inverter</p>
  1055. <ul>
  1056. <li>
  1057. <code>&lt;name&gt;</code>
  1058. Serial device port or the keyword <code>emulator</code>. In the latter case, a 4.5 kWP solar installation is simulated
  1059. </li>
  1060. </ul>
  1061. <a name="NT5000set"></a>
  1062. <h4>Set</h4>
  1063. <ul>
  1064. <li><a name="nt5000_time">
  1065. Not yet implemented</a></li>
  1066. </ul>
  1067. <br />
  1068. <a name="NT5000get"></a>
  1069. <h4>Get</h4>
  1070. <ul>
  1071. <li><a name="nt5000_reading">
  1072. <code>get &lt;name&gt; reading</code></a>
  1073. <br /> read all current data </li>
  1074. <li><a name="nt5000_month">
  1075. <code>get &lt;name&gt; month</code></a>
  1076. <br /> read all data from current month </li>
  1077. <li><a name="nt5000_year">
  1078. <code>get &lt;name&gt; year</code></a>
  1079. <br /> read all data from current year </li>
  1080. <li><a name="nt5000_present">
  1081. <code>get &lt;name&gt; present</code></a>
  1082. <br /> 1 if device present, 0 if not </li>
  1083. <li><a name="nt5000_serial">
  1084. <code>get &lt;name&gt; serial</code></a>
  1085. <br /> inverter serial number </li>
  1086. <li><a name="nt5000_proto">
  1087. <code>get &lt;name&gt; proto</code></a>
  1088. <br /> inverter protocol </li>
  1089. </ul>
  1090. <br />
  1091. <a name="NT5000attr"></a>
  1092. <h4>Attributes</h4>
  1093. <ul>
  1094. <li><a name="nt5000_Area"><code>attr &lt;name&gt; Area &lt;float&gt;</code>
  1095. </a>
  1096. <br />Effective area [m<sup>2</sup>] of the installation</li>
  1097. <li><a name="nt5000_PSP"><code>attr &lt;name&gt; PSP &lt;float&gt;</code>
  1098. </a>
  1099. <br />Peak Solar Power [kW] of the installation</li>
  1100. <li><a name="nt5000_Wx_M"><code>attr &lt;name&gt; Wx_M&lt;n&gt; &lt;float&gt;</code>
  1101. </a>
  1102. <br />Expected yield [kWh] for month &lt;n&gt;=1...12</li>
  1103. <li><a name="nt5000_Wx_Y"><code>attr &lt;name&gt; Wx_Y &lt;float&gt;</code>
  1104. </a>
  1105. <br />Expected yield [kWh] for a full year</li>
  1106. <li><a name="nt5000_MERR"><code>attr &lt;name&gt; MERR &lt;list&gt;</code>
  1107. </a>
  1108. <br />List of months with erroneous logging, see lines 83ff</li>
  1109. <li>Standard attributes <a href="#alias">alias</a>, <a href="#comment">comment</a>, <a
  1110. href="#event-on-update-reading">event-on-update-reading</a>, <a
  1111. href="#event-on-change-reading">event-on-change-reading</a>, <a
  1112. href="#stateFormat">stateFormat</a>, <a href="#room"
  1113. >room</a>, <a href="#eventMap">eventMap</a>, <a href="#loglevel">loglevel</a>,
  1114. <a href="#webCmd">webCmd</a></li>
  1115. </ul>
  1116. =end html
  1117. =cut