Integrating the Fronius Symo Inverter with Smart Home Systems
Photovoltaic systems play a crucial role in modern energy management. The Fronius Symo inverter offers excellent connectivity for Smart Home environments. Thanks to its open, local API, data extraction is straightforward and does not require constant cloud connectivity. This local approach ensures high reliability and fast response times for various automations.
Custom PHP Script and Smartphone Widget Integration
To monitor the status of the PV installation, a custom PHP script running on a local server can be utilized. This script queries the inverter’s API endpoint at [http://192.168.XXX.XXX/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DeviceId=1&DataCollection=CommonInverterData]. The JSON response is decoded to extract key metrics, specifically the current power output (PAC) and the total energy generated for the day (DAY_ENERGY).
The data is fetched periodically, such as every 5 minutes, keeping the information up to date. To handle periods when the inverter is offline—like during the night when solar production drops to zero and the DAY_ENERGY value returns as null or false—a fallback mechanism is implemented. In such cases, the script reads the last known values from a local 1.dat file, ensuring the display remains intact without errors.
Visually, the script features a dynamic background color that changes based on the current power generation. By using a custom gradient function, the background transitions smoothly through different colors (e.g., from white to orange to red) depending on the PAC value. This lightweight, self-refreshing webpage can then be displayed directly on a smartphone home screen using an application like “Widgetify”, creating a highly convenient and personalized monitoring widget:
<?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>Home Assistant Capabilities
The exact same Fronius API endpoint is incredibly valuable for more complex Smart Home platforms like Home Assistant. By configuring the native Fronius integration, Home Assistant continuously queries the inverter’s JSON data.
This enables several powerful features:
- Energy Dashboard: The generated energy data feeds directly into the Home Assistant Energy Dashboard, providing long-term statistics, cost calculations, and grid dependency metrics.
- Smart Automations: Real-time power data (
PAC) can be used as a trigger for automations. For instance, a heat pump, air conditioning unit, or electric vehicle charger can be automatically turned on when the solar production exceeds a specific threshold, maximizing self-consumption.
Summary
The Fronius Symo inverter provides easily accessible, real-time data through its local API. Whether utilizing a custom PHP script linked to the Widgetify app for quick smartphone monitoring or deploying a comprehensive Home Assistant setup for advanced automations, integrating this inverter into a Smart Home ecosystem makes tracking and optimizing solar energy usage highly efficient.