77_SMASTP.pm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. ###############################################################
  2. # $Id: 77_SMASTP.pm 11815 2016-07-18 12:46:25Z vsauer $
  3. #
  4. # Copyright notice
  5. #
  6. # (c) 2016 Copyright: Volker Kettenbach (volker at kettenbach minus it dot de)
  7. #
  8. # Credits:
  9. # - based on an Idea by SpenZerX and HDO
  10. # - Waldmensch for various improvements
  11. # - sbfspot (https://sbfspot.codeplex.com/)
  12. #
  13. # Description:
  14. # This is an FHEM-Module for the SMA Sunny Tripower Inverter.
  15. # Tested on Sunny Tripower 6000TL-20, 10000-TL20 and 10000TL-10 with
  16. # Speedwire/Webconnect Piggyback
  17. #
  18. # Requirements:
  19. # This module requires:
  20. # - Perl Module: IO::Socket::INET
  21. # - Perl Module: Datime
  22. #
  23. # Origin:
  24. # https://github.com/kettenbach-it/FHEM-SMA-Speedwire
  25. #
  26. ###############################################################
  27. package main;
  28. use strict;
  29. use warnings;
  30. use IO::Socket::INET;
  31. use DateTime;
  32. # Global vars
  33. my $cmd_login = "534d4100000402a000000001003a001060650ea0ffffffffffff00017800C8E8033800010000000004800c04fdff07000000840300004c20cb5100000000encpw00000000";
  34. my $cmd_logout = "534d4100000402a00000000100220010606508a0ffffffffffff00037800C8E80338000300000000d7840e01fdffffffffff00000000";
  35. my $cmd_query_total_today = "534d4100000402a00000000100260010606509e0ffffffffffff00007800C8E80338000000000000f1b10002005400002600ffff260000000000";
  36. my $cmd_query_spot_ac_power = "534d4100000402a00000000100260010606509e0ffffffffffff00007800C8E8033800000000000081f00002005100002600ffff260000000000";
  37. my $cmd_query_spot_dc_power = "534d4100000402a00000000100260010606509e0ffffffffffff00007800C8E8033800000000000081f00002805300002500ffff260000000000";
  38. my $averagebuf = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
  39. my $code_login = "0d04fdff"; #0xfffd040d;
  40. my $code_total_today = "01020054"; #0x54000201;
  41. my $code_spot_ac_power = "01020051"; #0x51000201;
  42. my $code_spot_dc_power = "01028053"; #0x53800201;
  43. my $default_starthour = "05:00";
  44. my $starthour = 5;
  45. my $startminute = 0;
  46. my $default_endhour = "22:00";
  47. my $endhour = 22;
  48. my $endminute = 0;
  49. my $force_sleep = 0;
  50. my $sleep_forced = 0;
  51. my $suppress_night_mode = 0;
  52. my $suppress_inactivity_mode = 0;
  53. my $modulstate_enabled = 0;
  54. my ($alarm_value1,$alarm_value2,$alarm_value3);
  55. ###################################
  56. sub SMASTP_Initialize($)
  57. {
  58. my ($hash) = @_;
  59. my $name = $hash->{NAME};
  60. my $hval;
  61. my $mval;
  62. $hash->{DefFn} = "SMASTP_Define";
  63. $hash->{UndefFn} = "SMASTP_Undef";
  64. $hash->{AttrList} = "suppress-night-mode:0,1 " .
  65. "suppress-inactivity-mode:0,1 " .
  66. "starttime " .
  67. "endtime " .
  68. "force-sleepmode:0,1 " .
  69. "enable-modulstate:0,1 " .
  70. "alarm1-value " .
  71. "alarm2-value " .
  72. "alarm3-value " .
  73. "interval " .
  74. $readingFnAttributes;
  75. $hash->{AttrFn} = "SMASTP_Attr";
  76. if ($attr{$name}{"starttime"})
  77. {
  78. ($hval, $mval) = split(/:/,$attr{$name}{"starttime"});
  79. }
  80. else
  81. {
  82. ($hval, $mval) = split(/:/,$default_starthour);
  83. }
  84. $starthour = int($hval);
  85. $startminute = int($mval);
  86. if ($attr{$name}{"endtime"})
  87. {
  88. ($hval, $mval) = split(/:/,$attr{$name}{"endtime"});
  89. }
  90. else
  91. {
  92. ($hval, $mval) = split(/:/,$default_endhour);
  93. }
  94. $endhour = int($hval);
  95. $endminute = int($mval);
  96. $suppress_night_mode = ($attr{$name}{"suppress-night-mode"}) ? $attr{$name}{"suppress-night-mode"} : 0;
  97. $suppress_inactivity_mode = ($attr{$name}{"suppress-inactivity-mode"}) ? $attr{$name}{"suppress-inactivity-mode"} : 0;
  98. $force_sleep = ($attr{$name}{"force-sleepmode"}) ? $attr{$name}{"force-sleepmode"} : 0;
  99. $modulstate_enabled = ($attr{$name}{"enable-modulstate"}) ? $attr{$name}{"enable-modulstate"} : 0;
  100. $alarm_value1 = ($attr{$name}{"alarm1-value"}) ? $attr{$name}{"alarm1-value"} : 0;
  101. $alarm_value2 = ($attr{$name}{"alarm2-value"}) ? $attr{$name}{"alarm2-value"} : 0;
  102. $alarm_value3 = ($attr{$name}{"alarm3-value"}) ? $attr{$name}{"alarm3-value"} : 0;
  103. Log3 $name, 0, "$name: Started with sleepmode from $endhour:$endminute - $starthour:$startminute";
  104. }
  105. ###################################
  106. sub is_Sleepmode()
  107. {
  108. # Build 3 DateTime Objects to make the comparison more robust
  109. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
  110. my $dt_startdate = DateTime->new(year=>$year+1900,month=>$mon+1,day=>$mday,hour=>$starthour,minute=>$startminute,second=>0,time_zone=>'local');
  111. my $dt_enddate = DateTime->new(year=>$year+1900,month=>$mon+1,day=>$mday,hour=>$endhour,minute=>$endminute,second=>0,time_zone=>'local');
  112. my $dt_now = DateTime->now(time_zone=>'local');
  113. # Return of any value != 0 means "sleeping"
  114. if ($dt_now >= $dt_enddate || $dt_now <= $dt_startdate)
  115. {
  116. # switch forced sleepmode off because we have reached normal sleepmode now
  117. $sleep_forced = 0;
  118. return 1;
  119. }
  120. elsif ($sleep_forced == 1)
  121. {
  122. # 2 = forced sleep
  123. return 2;
  124. }
  125. else
  126. {
  127. return 0;
  128. }
  129. }
  130. ###################################
  131. sub SMASTP_Define($$)
  132. {
  133. my ($hash, $def) = @_;
  134. my @a = split("[ \t][ \t]*", $def);
  135. return "Wrong syntax: use define <name> SMASTP <inv-userpwd> <inv-hostname/inv-ip > " if ((int(@a) < 4) and (int(@a) > 5));
  136. my $name = $a[0];
  137. $hash->{NAME} = $name;
  138. $hash->{LASTUPDATE}=0;
  139. $hash->{INTERVAL} = 60;
  140. # SMASTP = $a[1];
  141. my ($IP,$Host,$Caps);
  142. my $Pass = $a[2]; # to do: check 1-12 Chars
  143. # extract IP or Hostname from $a[4]
  144. if ( $a[3] ~~ m/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/ )
  145. {
  146. if ( $1 <= 255 && $2 <= 255 && $3 <= 255 && $4 <= 255 )
  147. {
  148. $Host = int($1).".".int($2).".".int($3).".".int($4);
  149. }
  150. }
  151. if (!defined $Host)
  152. {
  153. if ( $a[3] =~ /^([A-Za-z0-9_.])/ )
  154. {
  155. $Host = $a[3];
  156. }
  157. }
  158. if (!defined $Host)
  159. {
  160. return "Argument:{$a[3]} not accepted as Host or IP. Read device specific help file.";
  161. }
  162. $hash->{Pass} = $Pass;
  163. $hash->{Host} = $Host;
  164. # Use C8E80338, but NOT the number of the Inverter!
  165. # my $src_serial = 939780296;
  166. my $encpw = "888888888888888888888888"; # unencoded pw
  167. for my $index (0..length $Pass ) # encode password
  168. {
  169. substr($encpw,($index*2),2) = substr(sprintf ("%lX", (hex(substr($encpw,($index*2),2)) + ord(substr($Pass,$index,1)))),0,2);
  170. }
  171. $cmd_login =~ s/encpw/$encpw/g; #replace the placeholder with password
  172. InternalTimer(gettimeofday()+5, "SMASTP_GetStatus", $hash, 0); # refresh timer start
  173. return undef;
  174. }
  175. #####################################
  176. sub SMASTP_Undef($$)
  177. {
  178. my ($hash, $name) = @_;
  179. RemoveInternalTimer($hash);
  180. Log3 $hash, 0, "$name: Undefined!";
  181. return undef;
  182. }
  183. ###################################
  184. sub SMASTP_Attr(@)
  185. {
  186. my ($cmd,$name,$aName,$aVal) = @_;
  187. # $cmd can be "del" or "set"
  188. # $name is device name
  189. # aName and aVal are Attribute name and value
  190. my $hash = $defs{$name};
  191. my $hval;
  192. my $mval;
  193. if (($aName eq "starttime" || $aName eq "endtime") && not ($aVal =~ /^([0-1]?[0-9]|[2][0-3]):([0-5][0-9])$/))
  194. {
  195. return "value $aVal invalid"; # no correct time format hh:mm
  196. }
  197. if ($aName eq "enable-modulstate")
  198. {
  199. $modulstate_enabled = ($cmd eq "set") ? int($aVal) : 0;
  200. Log3 $name, 3, "$name: Set $aName to $aVal";
  201. }
  202. if ($aName eq "alarm1-value")
  203. {
  204. $alarm_value1 = ($cmd eq "set") ? int($aVal) : 0;
  205. Log3 $name, 3, "$name: Set $aName to $aVal";
  206. }
  207. if ($aName eq "alarm2-value")
  208. {
  209. $alarm_value2 = ($cmd eq "set") ? int($aVal) : 0;
  210. Log3 $name, 3, "$name: Set $aName to $aVal";
  211. }
  212. if ($aName eq "alarm3-value")
  213. {
  214. $alarm_value3 = ($cmd eq "set") ? int($aVal) : 0;
  215. Log3 $name, 3, "$name: Set $aName to $aVal";
  216. }
  217. if ($aName eq "starttime")
  218. {
  219. if ($cmd eq "set")
  220. {
  221. ($hval, $mval) = split(/:/,$aVal);
  222. }
  223. else
  224. {
  225. ($hval, $mval) = split(/:/,$default_starthour);
  226. }
  227. if (int($hval) < 12)
  228. {
  229. $starthour = int($hval);
  230. $startminute = int($mval);
  231. }
  232. else
  233. {
  234. return "$name: Attr starttime must be set smaller than 12:00! Not set to $starthour:$startminute";
  235. }
  236. Log3 $name, 3, "$name: Attr starttime is set to " . sprintf("%02d:%02d",$starthour,$startminute);
  237. }
  238. if ($aName eq "endtime")
  239. {
  240. if ($cmd eq "set")
  241. {
  242. ($hval, $mval) = split(/:/,$aVal);
  243. }
  244. else
  245. {
  246. ($hval, $mval) = split(/:/,$default_endhour);
  247. }
  248. if (int($hval) > 12)
  249. {
  250. $endhour = int($hval);
  251. $endminute = int($mval);
  252. }
  253. else
  254. {
  255. return "$name: Attr endtime must be set larger than 12:00! Not set to $endhour:$endminute";
  256. }
  257. Log3 $name, 3, "$name: Attr endtime is set to " . sprintf("%02d:%02d",$endhour,$endminute);
  258. }
  259. if ($aName eq "suppress-night-mode")
  260. {
  261. $suppress_night_mode = ($cmd eq "set") ? $aVal : 0;
  262. Log3 $name, 3, "$name: Set $aName to $aVal";
  263. }
  264. if ($aName eq "suppress-inactivity-mode")
  265. {
  266. $suppress_inactivity_mode = ($cmd eq "set") ? $aVal : 0;
  267. Log3 $name, 3, "$name: Set $aName to $aVal";
  268. }
  269. if ($aName eq "force-sleepmode")
  270. {
  271. if ($cmd eq "set")
  272. {
  273. $force_sleep = $aVal;
  274. $sleep_forced = ($aVal == 0) ? 0 : $sleep_forced;
  275. Log3 $name, 3, "$name: Set $aName to $aVal";
  276. }
  277. else
  278. {
  279. $force_sleep = 0;
  280. $sleep_forced = 0;
  281. }
  282. }
  283. if ($aName eq "interval")
  284. {
  285. if ($cmd eq "set")
  286. {
  287. $hash->{INTERVAL} = $aVal;
  288. Log3 $name, 3, "$name: Set $aName to $aVal";
  289. }
  290. } else
  291. {
  292. $hash->{INTERVAL} = "60";
  293. Log3 $name, 3, "$name: Set $aName to $aVal";
  294. }
  295. return undef;
  296. }
  297. #####################################
  298. sub SMASTP_GetStatus($)
  299. {
  300. my ($hash) = @_;
  301. my $name = $hash->{NAME};
  302. if (defined($attr{$name}{"interval"})) {
  303. $hash->{INTERVAL} = $attr{$name}{"interval"};
  304. } else {
  305. $hash->{INTERVAL} = 60;
  306. }
  307. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
  308. if ($suppress_night_mode == 0)
  309. {
  310. if(is_Sleepmode() > 0)
  311. {
  312. Log3 $name, 5, "$name: " .
  313. sprintf("%02d:%02d",$hour,$min) .
  314. " is out of working hours " .
  315. sprintf("%02d:%02d",$starthour,$startminute) .
  316. " - " .
  317. sprintf("%02d:%02d",$endhour,$endminute) .
  318. " " .
  319. (($sleep_forced == 1) ? " FORCED" : "");
  320. my $modulstate = ($hash->{READINGS}{modulstate}{VAL}) ? $hash->{READINGS}{modulstate}{VAL} : "unknown";
  321. if($modulstate ne "sleeping" && $modulstate_enabled == 1)
  322. {
  323. readingsBeginUpdate($hash);
  324. readingsBulkUpdate($hash, "modulstate", "sleeping");
  325. readingsEndUpdate($hash, 1);
  326. }
  327. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "SMASTP_GetStatus", $hash, 1);
  328. return;
  329. }
  330. }
  331. use constant MAXBYTES => scalar 200; #1024 #80
  332. my $Host = $hash->{Host};
  333. my $interval = $hash->{INTERVAL};
  334. # my $averagebuf = $hash->{averagebuf};
  335. my ($AvP01,$AvP05,$AvP15,$TodayTotal,$SpotPower,$AlltimeTotal,$statusval,$PDC1,$PDC2);
  336. my ($socket,$data,$size,$code);
  337. my $error = 0;
  338. # flush after every write
  339. $| = 1;
  340. $socket = new IO::Socket::INET (PeerHost => $Host, PeerPort => 9522, Proto => 'udp',); # open Socket
  341. if (!$socket) {
  342. # in case of error
  343. Log3 $name, 1, "$name: ERROR. Can't open socket to inverter: $!";
  344. return undef;
  345. };
  346. # send login command
  347. Log3 $name, 2, "$name: Sending query to inverter $Host:9522";
  348. $data = pack "H*",$cmd_login;
  349. $socket->send($data);
  350. do
  351. {
  352. eval
  353. {
  354. local $SIG{ALRM} = sub { die "alarm time out" };
  355. alarm 5;
  356. # receive data
  357. $socket->recv($data, MAXBYTES) or die "recv: $!";
  358. $size = length($data);
  359. # too little data - exit loop
  360. if ((defined $size) && ($size > 60))
  361. {
  362. my $received = unpack("H*", $data);
  363. Log3 $name, 5, "$name: Received: ($received)";
  364. } else {
  365. if($size > 0)
  366. {
  367. my $received = unpack("H*", $data);
  368. Log3 $name, 5, "$name: Received Garbage: ($received)";
  369. }
  370. }
  371. alarm 0;
  372. 1;
  373. } or Log3 $name, 1, "$name query timed out";
  374. # too little data -> exit loop
  375. if ((not defined $size) || ($size < 60))
  376. {
  377. Log3 $name, 1, "$name: Too little data received (Len:".((not defined $size) ? "NaN/timeout" : $size).")";
  378. # send: cmd_logout
  379. $data = pack "H*",$cmd_logout;
  380. $size = $socket->send($data);
  381. $socket->close();
  382. $error = 1;
  383. }
  384. else
  385. {
  386. # unpack command
  387. $code = unpack("H*", substr $data, 42, 4);
  388. # answer to command login
  389. if ($code_login eq $code)
  390. {
  391. # send: Query total today
  392. $data = pack "H*",$cmd_query_total_today;
  393. $size = $socket->send($data);
  394. }
  395. # answer to command total today
  396. if ($code_total_today eq $code)
  397. {
  398. $TodayTotal = unpack("V*", substr $data, 78, 4);
  399. $AlltimeTotal = unpack("V*", substr $data, 62, 4);
  400. # send: Query spot power
  401. $data = pack "H*",$cmd_query_spot_ac_power;
  402. $size = $socket->send($data);
  403. }
  404. # answer to command AC Power
  405. if ($code_spot_ac_power eq $code)
  406. {
  407. $SpotPower = unpack("V*", substr $data, 62, 4);
  408. # special case at night ? Inverter off?
  409. if ($SpotPower eq 0x80000000) {$SpotPower = 0};
  410. # send: query spot DC power
  411. $data = pack "H*",$cmd_query_spot_dc_power;
  412. $size = $socket->send($data);
  413. }
  414. # answer to command DC Power
  415. if ($code_spot_dc_power eq $code)
  416. {
  417. $PDC1 = unpack("V*", substr $data, 62, 4);
  418. if ($PDC1 eq 0x80000000) {$PDC1 = 0};
  419. $PDC2 = unpack("V*", substr $data, 90, 4);
  420. if ($PDC2 eq 0x80000000) {$PDC2 = 0};
  421. # send: cmd_logout
  422. $data = pack "H*",$cmd_logout;
  423. $size = $socket->send($data);
  424. # close Socket
  425. $socket->close();
  426. }
  427. }
  428. } while (($code_spot_dc_power ne $code) && ($error eq 0)); # answer to command spot_ac_power
  429. if ($error ne 1)
  430. {
  431. if ( (int(hex(substr($averagebuf,0*8,8)))) eq 0)
  432. {
  433. for my $count (0..15)
  434. {
  435. # fill with new values
  436. substr($averagebuf,$count*8,1*8) = substr(sprintf ("%08X",$AlltimeTotal),0,8);
  437. }
  438. }
  439. # average buffer shiften und mit neuem Wert füllen
  440. substr($averagebuf,1*8,15*8) = substr($averagebuf,0*8,15*8);
  441. # und mit neuem Wert füllen
  442. substr($averagebuf,0*8,1*8) = substr(sprintf ("%08X",$AlltimeTotal),0,8);
  443. $AvP01 = int( ( (hex(substr($averagebuf,0*8,8))) - (hex(substr($averagebuf,1*8,8))) ) * ((3600 / 01) / $interval) );
  444. $AvP05 = int( ( (hex(substr($averagebuf,0*8,8))) - (hex(substr($averagebuf,5*8,8))) ) * ((3600 / 05) / $interval) );
  445. $AvP15 = int( ( (hex(substr($averagebuf,0*8,8))) - (hex(substr($averagebuf,15*8,8))) ) * ((3600 / 15) / $interval) );
  446. $statusval = "SP:$SpotPower W AvP1:$AvP01 W TTP:$TodayTotal Wh ATP:$AlltimeTotal Wh";
  447. Log3 $name, 4, "$name: from ($Host): ($statusval) ";
  448. Log3 $name, 5, "$name: AvP05 = $AvP05, SpotPower = $SpotPower, AvP15 = $AvP15";
  449. #Filter out error zero values and stop readingsupdate after 15 Mins on zero power
  450. if ( ((not ($AvP05 > 0 && $SpotPower == 0)) && $AvP15 > 0) || $suppress_inactivity_mode == 1 || $hash->{LASTUPDATE} eq 0)
  451. {
  452. readingsBeginUpdate($hash);
  453. readingsBulkUpdate($hash, "state", $statusval); # Status Overview
  454. readingsBulkUpdate($hash, "SpotP", $SpotPower); # Momentary Spot Power
  455. readingsBulkUpdate($hash, "SpotPDC1", $PDC1); # Momentary Spot DC Power String1
  456. readingsBulkUpdate($hash, "SpotPDC2", $PDC2); # Momentary Spot DC Power String2
  457. readingsBulkUpdate($hash, "TodayTotalP", $TodayTotal); # Today Total Power
  458. readingsBulkUpdate($hash, "AlltimeTotalP", $AlltimeTotal); # Alltime Total Power
  459. readingsBulkUpdate($hash, "AvP01", $AvP01); # Average Power (last) 1 Minute (if delay 60)
  460. readingsBulkUpdate($hash, "AvP05", $AvP05); # Average Power (last) 5 Minutes (if delay 60)
  461. readingsBulkUpdate($hash, "AvP15", $AvP15); # Average Power (last) 15 Minutes (if delay 60)
  462. readingsBulkUpdate($hash, "modulstate", "normal");
  463. if($alarm_value1 > 0 && $SpotPower > $alarm_value1) { readingsBulkUpdate($hash, "Alarm1", 1); }
  464. elsif ($alarm_value1 > 0 && $SpotPower < $alarm_value1) { readingsBulkUpdate($hash, "Alarm1", (-1)); }
  465. else { readingsBulkUpdate($hash, "Alarm1", 0); }
  466. if($alarm_value2 > 0 && $SpotPower > $alarm_value2) { readingsBulkUpdate($hash, "Alarm2", 1); }
  467. elsif ($alarm_value2 > 0 && $SpotPower < $alarm_value2) { readingsBulkUpdate($hash, "Alarm2", (-1)); }
  468. else { readingsBulkUpdate($hash, "Alarm2", 0); }
  469. if($alarm_value3 > 0 && $SpotPower > $alarm_value3) { readingsBulkUpdate($hash, "Alarm3", 1); }
  470. elsif ($alarm_value3 > 0 && $SpotPower < $alarm_value3) { readingsBulkUpdate($hash, "Alarm3", (-1)); }
  471. else { readingsBulkUpdate($hash, "Alarm3", 0); }
  472. readingsEndUpdate($hash, 1); # Notify is done by Dispatch
  473. $hash->{LASTUPDATE} = sprintf "%02d.%02d.%04d / %02d:%02d:%02d" , $mday , $mon+=1 ,$year+=1900 , $hour , $min , $sec ;
  474. Log3 $name, 5, "$name: Readings updated";
  475. }
  476. else
  477. {
  478. if ($AvP15 == 0 && $SpotPower == 0 && $force_sleep == 1 && $sleep_forced == 0 && $hour > 12)
  479. {
  480. $sleep_forced = 1;
  481. Log3 $name, 1, "$name: sleepmode forced after 15 minutes zero power";
  482. }
  483. my $modulstate = ($hash->{READINGS}{modulstate}{VAL}) ? $hash->{READINGS}{modulstate}{VAL} : "";
  484. if($modulstate ne "inactive" && $modulstate_enabled == 1)
  485. {
  486. readingsBeginUpdate($hash);
  487. readingsBulkUpdate($hash, "modulstate", "inactive");
  488. readingsEndUpdate($hash, 1);
  489. }
  490. Log3 $name, 5, "$name: Readings not updated";
  491. }
  492. }
  493. InternalTimer(gettimeofday()+$interval, "SMASTP_GetStatus", $hash, 1);
  494. #return undef;
  495. }
  496. 1;
  497. =pod
  498. =begin html
  499. <a name="SMASTP"></a>
  500. <h3>SMASTP</h3>
  501. Module for the integration of a Sunny Tripower Inverter build by SMA over it's Speedwire (=Ethernet) Interface.<br>
  502. Tested on Sunny Tripower 6000TL-20, 10000-TL20 and 10000TL-10 with Speedwire/Webconnect Piggyback.
  503. <p>
  504. <b>Define</b>
  505. <ul>
  506. <code>define &lt;name&gt; SMASTP &lt;pin&gt; &lt;hostname/ip&gt; [port]</code><br>
  507. <br>
  508. <li>pin: User-Password of the SMA STP Inverter. Default is 0000. Can be changed by "Sunny Explorer" Windows Software</li>
  509. <li>hostname/ip: Hostname or IP-Adress of the inverter (or it's speedwire piggyback module).</li>
  510. <li>port: Port of the inverter. 9522 by default.</li>
  511. </ul>
  512. <p>
  513. <b>Modus</b>
  514. <ul>
  515. The module automatically detects the inactvity of the inverter due to a lack of light (night). <br>
  516. This inactivity is therefore called "nightmode". During nightmode, the inverter is not queried over the network.<br>
  517. By default nightmode is between 9pm and 5am. This can be changed by "starttime" (start of inverter <br>
  518. operation, end of nightmode) and "endtime" (end of inverter operation, start of nightmode).<br>
  519. Further there is the inactivitymode: in inactivitymode, the inverter is queried but readings are not updated.
  520. </ul>
  521. <b>Parameter</b>
  522. <ul>
  523. <li>interval: Queryintreval in seconds </li>
  524. <li>suppress-night-mode: The nightmode is deactivated </li>
  525. <li>suppress-inactivity-mode: The inactivitymode is deactivated </li>
  526. <li>starttime: Starttime of inverter operation (default 5am) </li>
  527. <li>endtime: Endtime of inverter operation (default 9pm) </li>
  528. <li>force-sleepmode: The nightmode is activated on inactivity, even the endtime is not reached </li>
  529. <li>enable-modulstate: Turns the reading "modulstate" (normal / inactive / sleeping) on </li>
  530. <li>alarm1-value, alarm2-value, alarm3-value: Set an alarm on the reading SpotP in watt.<br>
  531. The readings Alarm1..Alarm3 are set accordingly: -1 for SpotP < alarmX-value and 1 for SpotP >= alarmX-value </li>
  532. </ul>
  533. <b>Readings</b>
  534. <ul>
  535. <li>SpotP: spotpower - Current power in watt delivered by the inverter </li>
  536. <li>AvP01: average power 1 minute: average power in watt of the last minute </li>
  537. <li>AvP05: average power 5 minutes: average power in watt of the five minutes </li>
  538. <li>AvP15: average power 15 minutes: average power in watt of the fifteen minutes </li>
  539. <li>SpotPDC1: current d.c. voltage delivered by string 1 </li>
  540. <li>SpotPDC2: current d.c. voltage delivered by string 2 </li>
  541. <li>TotalTodayP: generated power in Wh of the current day </li>
  542. <li>AlltimeTotalP: all time generated power in Wh </li>
  543. <li>Alarm1..3: alrm trigger 1..3. Set by parameter alarmN-value </li>
  544. </ul>
  545. =end html
  546. =begin html_DE
  547. <a name="SMASTP"></a>
  548. <h3>SMASTP</h3>
  549. Modul zur Einbindung eines Sunny Tripower Wechselrichters der Firma SMA über Speedwire (Ethernet).<br>
  550. Getestet mit Sunny Tripower 6000TL-20, 10000-TL20 sowie 10000TL-10 mit Speedwire/Webconnect Piggyback
  551. <p>
  552. <b>Define</b>
  553. <ul>
  554. <code>define &lt;name&gt; SMASTP &lt;pin&gt; &lt;hostname/ip&gt; [port]</code><br>
  555. <br>
  556. <li>pin: Benutzer-Passwort des SMA STP Wechselrichters. Default ist 0000. Kann über die Windows-Software "Sunny Explorer" geändert werden </li>
  557. <li>hostname/ip: Hostname oder IP-Adresse des Wechselrichters (bzw. dessen Speedwire Moduls mit Ethernetanschluss) </li>
  558. <li>port: Optional der Ports des Wechselrichters. Per default 9522. </li>
  559. </ul>
  560. <p>
  561. <b>Modus</b>
  562. <ul>
  563. Das Modul erkennt automatisch eine Inaktivität des Wechselrichters, wenn dieser aufgrund Dunkelheit seinen Betrieb einstellt. <br>
  564. Diese Betriebspause wird als "Nightmode" bezeichnet. Im Nightmode wird der Wechelrichter nicht mehr über das Netzwerk abgefragt.<br>
  565. Per default geht das Modul davon aus, dass vor 5:00 und nach 21:00 der Nightmode aktiv ist.<br>
  566. Diese Grenzen lassen sich mit den Parametern "starttime" (Start des Wechelrichterbetriebs, also Ende des Nightmode) <br>
  567. und "endtime" (Ende des Wechselrichterbetriebs, also Beginn des Nightmode) umdefinieren. <br>
  568. Darüber hinaus gibt es den "Inactivitymode": hier wird der Wechselrichter abgefragt, aber es werden keine Readings mehr aktualisiert. <br>
  569. </ul>
  570. <b>Parameter</b>
  571. <ul>
  572. <li>interval: Abfrageinterval in Sekunden </li>
  573. <li>suppress-night-mode: Der Nightmode wird deaktiviert </li>
  574. <li>suppress-inactivity-mode: Der Inactivitymode wird deaktiviert </li>
  575. <li>starttime: Startzeit des Betriebsmodus (Default 5:00 Uhr) </li>
  576. <li>endtime: Endezeit des Betriebsmodus (Default 21:00 Uhr) </li>
  577. <li>force-sleepmode: Der Nightmode wird bei entdeckter Inaktivität auch dann aktiviert, wenn endtime noch nicht erreicht ist </li>
  578. <li>enable-modulstate: Schaltet das reading "modulstate" (normal / inactive / sleeping) ein </li>
  579. <li>alarm1-value, alarm2-value, alarm3-value: Setzt einen Alarm in Watt auf das Reading SpotP.
  580. <br>Die Readings Alarm1..Alarm3 werden entsprechend gesetzt: -1 für SpotP < alarmX-value und 1 für Spot >= alarmX-value. </li>
  581. </ul>
  582. <b>Readings</b>
  583. <ul>
  584. <li>SpotP: SpotPower - Leistung in W zum Zeitpunkt der Abfrage</li>
  585. <li>AvP01: Average Power 1 Minute - Durchschnittliche Leistung in W der letzten Minute</li>
  586. <li>AvP05: Average Power 5 Minuten - Durchschnittliche Leistung in W der letzten 5 Minuten</li>
  587. <li>AvP15: Average Power 15 Minuten - Durchschnittliche Leistung in W der letzten 15 Minuten</li>
  588. <li>SpotPDC1: Spot Gleichspannung String 1 </li>
  589. <li>SpotPDC2: Spot Gleichspannung String 2 </li>
  590. <li>TotalTodayP: Erzeuge Leistung (in Wh) des heutigen Tages </li>
  591. <li>AlltimeTotalP: Erzeugte Leistung (in Wh) seit Inbetriebsnahme des Gerätes </li>
  592. <li>Alarm1..3: Alarm Trigger 1-3. Können über die Parameter "alarmN-value" gesetzt werden </li>
  593. </ul>
  594. =end html_DE