Integracja falownika Fronius Symo z systemami Smart Home
Systemy fotowoltaiczne odgrywają kluczową rolę w nowoczesnym zarządzaniu energią. Falownik Fronius Symo oferuje doskonałe możliwości integracji ze środowiskiem Smart Home. Dzięki otwartemu, lokalnemu API pobieranie danych jest niezwykle proste i nie wymaga stałego dostępu do chmury producenta. Takie lokalne podejście gwarantuje wysoką niezawodność i szybki czas reakcji dla automatyzacji.
Autorski skrypt PHP i widżet na smartfonie
Do monitorowania statusu instalacji PV można wykorzystać dedykowany skrypt PHP uruchomiony na lokalnym serwerze. Skrypt ten odpytuje endpoint API falownika pod adresem [http://192.168.XXX.XXX/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceId=1&DataCollection=CommonInverterData]. Otrzymana odpowiedź w formacie JSON jest dekodowana w celu wyodrębnienia kluczowych parametrów: aktualnie generowanej mocy (PAC) oraz dziennego uzysku energii (DAY_ENERGY).
Odświeżanie danych odbywa się cyklicznie, na przykład co 5 minut, co pozwala na bieżące śledzenie produkcji. W przypadku, gdy falownik przechodzi w stan uśpienia (np. w nocy, gdy produkcja spada do zera, a wartość DAY_ENERGY zwraca błąd lub wartość pustą), zastosowano mechanizm awaryjny. Skrypt odczytuje wówczas ostatnie znane wartości z lokalnego pliku 1.dat, zapobiegając błędom wyświetlania.
Od strony wizualnej skrypt posiada dynamiczne tło, które zmienia kolor w zależności od aktualnie generowanej mocy. Dzięki specjalnej funkcji matematycznej generującej gradient, tło płynnie przechodzi między kolorami (np. od białego, przez pomarańczowy, aż po czerwony) na podstawie wartości PAC. Tak przygotowana strona, wyposażona w mechanizm autoodświeżania, może zostać użyta w aplikacji „Widgetify”, tworząc wygodny i spersonalizowany widżet bezpośrednio na ekranie głównym smartfona.
<?php
// Adjust the IP Address
$x = json_decode(file_get_contents("http://192.168.XXX.XXX/"
."solar_api/v1/GetInverterRealtimeData.cgi?".
"Scope=Device&DeviceId=1&DataCollection=CommonInverterData"), false);
$fallback1 = "";
$fallback2 = "";
if(!isset($x->Body->Data->DAY_ENERGY->Value) ||
$x->Body->Data->DAY_ENERGY->Value === null ||
$x->Body->Data->DAY_ENERGY->Value === false) {
$y = explode("|", file_get_contents("./1.dat"));
$fallback1 = $y[0];
$fallback2 = $y[1];
}
else
{
file_put_contents("./1.dat",
$x->Body->Data->PAC->Value.$x->Body->Data->PAC->Unit.
"|".$x->Body->Data->DAY_ENERGY->Value.$x->Body->Data->DAY_ENERGY->Unit);
}
if(isset($_GET["x"])) {
exit($x->Body->Data->PAC->Value.$x->Body->Data->PAC->Unit.
$fallback1."|".$x->Body->Data->DAY_ENERGY->Value.
$x->Body->Data->DAY_ENERGY->Unit.$fallback2."|".
date("H:i:s").($fallback1 != "" ? " F" : ""));
}
function Gradient($HexFrom, $HexTo, $ColorSteps) {
// credit: Hailwood (stackoverflow)
$FromRGB['r'] = hexdec(substr($HexFrom, 0, 2));
$FromRGB['g'] = hexdec(substr($HexFrom, 2, 2));
$FromRGB['b'] = hexdec(substr($HexFrom, 4, 2));
$ToRGB['r'] = hexdec(substr($HexTo, 0, 2));
$ToRGB['g'] = hexdec(substr($HexTo, 2, 2));
$ToRGB['b'] = hexdec(substr($HexTo, 4, 2));
$StepRGB['r'] = ($FromRGB['r'] - $ToRGB['r']) / ($ColorSteps - 1);
$StepRGB['g'] = ($FromRGB['g'] - $ToRGB['g']) / ($ColorSteps - 1);
$StepRGB['b'] = ($FromRGB['b'] - $ToRGB['b']) / ($ColorSteps - 1);
$GradientColors = array();
for($i = 0; $i <= $ColorSteps; $i++) {
$RGB['r'] = floor($FromRGB['r'] - ($StepRGB['r'] * $i));
$RGB['g'] = floor($FromRGB['g'] - ($StepRGB['g'] * $i));
$RGB['b'] = floor($FromRGB['b'] - ($StepRGB['b'] * $i));
$HexRGB['r'] = sprintf('%02x', ($RGB['r']));
$HexRGB['g'] = sprintf('%02x', ($RGB['g']));
$HexRGB['b'] = sprintf('%02x', ($RGB['b']));
$GradientColors[] = implode(NULL, $HexRGB);
}
$GradientColors = array_filter($GradientColors, function($val){
return (strlen($val) == 6 ? true : false );
});
return $GradientColors;
}
function ColorFromGradient($n, $min, $max, $colors) {
$tablecolors = [];
$prevcolor = array_shift($colors);
foreach ($colors as $color) {
$tablecolors = array_merge($tablecolors, Gradient($prevcolor, $color, 10));
$prevcolor = $color;
}
$max = $max-$min;
$n-= $min;
if ($n > $max) $n = $max;
$ncolor = round(count($tablecolors)/$max * $n)-1;
return $tablecolors[$ncolor];
}
?><!DOCTYPE html><html>
<head><script>
function dR() {
setInterval(function(){
document.getElementById('f3').innerHTML += 'R...';
var xhr = new XMLHttpRequest();
xhr.open("GET", "./pvgirdcgi.php?x="+new Date(), true);
xhr.timeout = 5000;
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
document.getElementById('f1').innerHTML = xhr.responseText.split("|")[0];
document.getElementById('f2').innerHTML = xhr.responseText.split("|")[1];
document.getElementById('f3').innerHTML = xhr.responseText.split("|")[2];
}
}
xhr.onerror = function() {};
xhr.send();
}, 8000);
}
//setTimeout(function(){window.location.reload()},1000*20);
</script>
<style>
<?php
if(isset($gear) && $gear == true) {
?>
body{text-align:center;margin-top:50px;font-size:32px;}
<?php
}
?>
span {
text-shadow:
0 0 7px #fff,
0 0 10px #fff,
0 0 21px #fff,
0 0 42px #0fa,
0 0 82px #0fa,
0 0 92px #0fa,
0 0 102px #0fa,
0 0 151px #0fa;
font-family:Arial,Verdana;
}
</style>
</head><body <?php
if(isset($gear) && $gear == true)
{
?> onClick="document.querySelector('#pvx').style.display='block';"+
"document.querySelector('.loader').style.display='block';"+
"document.querySelector('#pvall').style.display='none';"+
"window.location.reload();"<?php
}
?>style="background:#<?php echo ColorFromGradient($x->Body->Data->PAC->Value,
0, 9000, ['FFFFFF', 'FFAA00', 'CC0000']); ?>">
<?php
if(isset($gear) && $gear == true) {
?>
<style>
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
}
/* Safari */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style><div style="display:none;text-align:center;width:100%;
margin-left:-16px;margin-top:100px;" id="pvx">
<span style="text-align: center;width: 120px;display: inline-block;">
<span class="loader" style="display:none;"></span>
</span>
</div>
<?php
}
?>
<div style="<?php
if(@$gear != true)
{?>width:80px;<?php } ?>text-align:center;" id="pvall">
<span style="text-shadow: 1px 1px #ffffff;
font-family:Arial,Verdana;font-weight:bold;">PV</span><br /><br />
<span style="font-weight:bold;" id="f1"><?php echo $x->Body->Data->PAC->Value.
$x->Body->Data->PAC->Unit.$fallback1; ?></span><br />
<span style="" id="f2"><?php echo $x->Body->Data->DAY_ENERGY->Value.
$x->Body->Data->DAY_ENERGY->Unit.$fallback2; ?></span>
<br /><br />
<span style="" id="f3"><?php echo date("H:i:s").($fallback1 != "" ? " F" : ""); ?></span>
</div>
</body>
</html>Możliwości w systemie Home Assistant
Ten sam endpoint API Froniusa otwiera ogromne możliwości dla rozbudowanych platform Smart Home, takich jak Home Assistant. Po skonfigurowaniu natywnej integracji Fronius, Home Assistant nieustannie odpytuje dokładnie te same dane.
Daje to następujące możliwości:
- Panel Energia (Energy Dashboard): Dane o wyprodukowanej energii zasilają wbudowany panel energetyczny, dostarczając długoterminowych statystyk, obliczeń opłacalności oraz poziomu niezależności od sieci energetycznej.
- Inteligentne automatyzacje: Dane o mocy w czasie rzeczywistym (
PAC) mogą służyć jako wyzwalacze (triggers). Na przykład bojler elektryczny, klimatyzator lub ładowarka do aut EV mogą włączać się automatycznie, gdy produkcja ze słońca przekroczy zadany próg, co maksymalizuje autokonsumpcję.
Podsumowanie
Falownik Fronius Symo udostępnia czytelne dane w czasie rzeczywistym za pośrednictwem lokalnego API. Zarówno w przypadku zastosowania autorskiego skryptu PHP połączonego z aplikacją Widgetify do szybkiego podglądu na telefonie, jak i przy wykorzystaniu potężnego systemu Home Assistant do zaawansowanych automatyzacji, integracja tego falownika czyni zarządzanie energią słoneczną procesem wysoce efektywnym.