LW IT Solutions
« Blog Overview /Smart Home / Integrating the Fronius Symo Inverter with Smart...
This post in other languages:

Integrating the Fronius Symo Inverter with Smart Home Systems

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>
Flow of the five-minute query from the Fronius Symo local API through the PHP script, with the branch for a present DAY_ENERGY value and the fallback to the 1.dat file, plus the gradient scale for the background colour
The API delivers, 1.dat remembers — which is why the display keeps its shape at night instead of showing an error.

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.

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in Touch

Briefly describe your project or inquiry for a tailored response. This site is protected by reCAPTCHA.

2 comments

  1. Krystyna Wilk

    The fallback file is a neat solution to the sleeping inverter — the whole widget falls apart at dusk without something like it.

    One thing about that file worries me: with auto-refresh on a phone widget and a browser tab open, two requests can land at the same moment. Is there a risk of reading a half-written file?

    1. Lukas Wojcik Author

      There is, and it is the kind of race that shows up as a single implausible reading rather than as an error — a value that is half of yesterday’s number, once a week, with nothing in the logs.

      Two small changes remove it. Writing with an exclusive lock keeps a reader from seeing a partial file, and writing to a temporary file followed by a rename makes the replacement atomic on the same filesystem — the reader sees either the old content or the new one, never something in between.

      The third habit matters more than either: the reader should tolerate nonsense. The file is a fallback, so an empty or malformed read should degrade to "no current reading" rather than to a number. That also covers the first run, when the file does not exist yet — which is the other moment this code path gets exercised and the one where a stray value looks most convincing.

Write a comment

The email address is not published. Required fields are marked with an asterisk.

ALL ARTICLES & CATEGORIES

CCTV

Follow this category by RSS

Cloud & AI

Follow this category by RSS

Data Privacy

All 12 articles in this category Follow this category by RSS

Digital Analytics

All 47 articles in this category Follow this category by RSS

Digital Marketing

All 29 articles in this category Follow this category by RSS

IT & Networks

All 16 articles in this category Follow this category by RSS

Music Production

Follow this category by RSS

Raspberry PI

Follow this category by RSS

Smart Home

All 18 articles in this category Follow this category by RSS

Web Development

Follow this category by RSS

WordPress Plugins & Tricks

Follow this category by RSS