[Script] solcast.com

Ich bekomme keine Daten:
Sind die Setting richtig?
Muss die API und rid mit <> geschrieben werden?
grafik

richimaint

Nein. Das waren nur die Markierungen für die Platzhalter

Habe es ohne <>.
Nach dem starten des Scripts kommt auch keine Fehlermeldung und die Variablen werden anglegt.

richimaint

Musst warten bis der Event „DailyUpdate“ ausgeführt wird… Script direkt Starten legt nur die Variablen usw. an.

1 „Gefällt mir“

Ich bekomme jetzt mit alten Scripeten folgende Fehlermeldungen:

26.05.2023, 10:12:13 | ScriptEngine         | Result for Event 30700

Fatal error: Cannot redeclare CreateVariableByName() (previously declared in C:\ProgramData\Symcon\scripts\System.Functions.ips.php:204) in C:\ProgramData\Symcon\scripts\59012.ips.php on line 43
Abort Processing during Fatal-Error: Cannot redeclare CreateVariableByName() (previously declared in C:\ProgramData\Symcon\scripts\System.Functions.ips.php:204)
   Error in Script C:\ProgramData\Symcon\scripts\59012.ips.php on Line 43

Das hängt glaub an der neuen „System.Functions.ips.php“ ab.
richimaint

Habe es glaube so gelöst:

richimaint

Also heute morgen war trotzdem noch alles leer.

Kommt auch zu Fehlermeldungen:

27.05.2023, 09:54:16 | ScriptEngine         | Result for Event 34652

Notice: Array to string conversion in C:\ProgramData\Symcon\scripts\40274.ips.php on line 236

Notice: Array to string conversion in C:\ProgramData\Symcon\scripts\40274.ips.php on line 255

Notice: Trying to access array offset on value of type null in C:\ProgramData\Symcon\scripts\40274.ips.php on line 192

Warning: Invalid argument supplied for foreach() in C:\ProgramData\Symcon\scripts\40274.ips.php on line 192

Notice: Undefined offset: 0 in C:\ProgramData\Symcon\scripts\40274.ips.php on line 170

Notice: Trying to access array offset on value of type null in C:\ProgramData\Symcon\scripts\40274.ips.php on line 170

Notice: Undefined offset: 0 in C:\ProgramData\Symcon\scripts\40274.ips.php on line 171

Notice: Trying to access array offset on value of type null in C:\ProgramData\Symcon\scripts\40274.ips.php on line 171

Notice: Undefined offset: 0 in C:\ProgramData\Symcon\scripts\40274.ips.php on line 172

Notice: Trying to access array offset on value of type null in C:\ProgramData\Symcon\scripts\40274.ips.php on line 172
Abort Processing during exceed of maximal ErrorCount: Undefined offset: 1
   Error in Script C:\ProgramData\Symcon\scripts\40274.ips.php on Line 174

Das ist mein Code, was mache ich falsch?

<?php

declare(strict_types=1);

################################################################################
# Script:   Amount.SolCast.ips.php
# Version:  1.3.20230515
# Author:   Heiko Wilknitz (@Pitti)
#           Idee von STELE99 (2022)
#
# Forecast von PV Analage(n) berechnen via solcast.com
#
# ------------------------------ Installation ----------------------------------
#
# Dieses Skript richtet automatisch alle nötigen Objekte bei manueller
# Ausführung ein. Eine weitere manuelle Ausführung setzt alle benötigten Objekte
# wieder auf den Ausgangszustand.
#
# - Neues Skript erstellen
# - Diesen PHP-Code hineinkopieren
# - Abschnitt 'Konfiguration' mit eigenen Werten anpassen (__WWX austauschen)
# - Skript Abspeichern
# - Skript Ausführen
# - Visualisierung per Link auf entstandene Variablen erstellen
#
# ------------------------------ Changelog -------------------------------------
#
# 22.02.2023 - Initalversion (v1.0)
# 12.03.2023 - BuildTable,CalcTotal & ArchiveValue hinzugefügt (v1.1)
# 17.03.2023 - Fix für Archive Control (v1.2)
# 15.05.2023 - Keine Zwischenwerte mehr im Archiv
#
# ------------------------------ Konfiguration ---------------------------------
#
# Global Debug Output Flag
$DEBUG = false;
#
# PV Anlagen definiert durch
#   token:       API Key
#   rid:         Ressource ID der Anlage in Solcast
#   graph:       Wenn true wird via quickchart.io ein Graph erstellt
#
# Erste Anlage (meine ist auf der Garage, bitte den Namen auch anpassen)
$PVA['Haus'] = [
    'token'     =>  ['############################'], // (string) erstezen durch => '<API-KEY>'
    'rid'       =>  ['####-####-####-#####'],   // (string) erstezen durch => '<GUID>'
    'graph'     => true,
];


#
# Weitere Anlage
# $PVA['Haus'] = [
#    'token'     => '<API-KEY>',
#    'rid'       => <GUID>,
#    'graph'      => true,
# ];
#
################################################################################
#
# Requires include of the global function script via autoload (__autoload.php)
# or direct in the script (uncomment next line)!
# require_once(IPS_GetKernelDir()."scripts".DIRECTORY_SEPARATOR.'System.Functions.ips.php');
# You can download it from here https://github.com/wilkware/ips-scripts
#


require_once IPS_GetKernelDir() . 'scripts' . DIRECTORY_SEPARATOR . 'System.QuickChart.ips.php';
require_once IPS_GetKernelDir() . 'scripts' . DIRECTORY_SEPARATOR . 'System.Functions.ips.php';
require_once IPS_GetKernelDir() . 'scripts' . DIRECTORY_SEPARATOR . 'System.Locals.ips.php';

defined('WWX_FUNCTIONS') || die('Global function library not available!');

// INSTALLATION
if ($_IPS['SENDER'] == 'Execute') {
    // Event 1 mal am Tag
    $midnight = mktime(0, 4, 16);
    CreateEventByName($_IPS['SELF'], 'UpdateMidnight', $midnight);
    // Event aller 6 Stunde von  03:00 bis 22:00 Uhr
    $from = mktime(2, 54, 16);
    $to = mktime(22, 0, 0);
    CreateEventByNameFromTo($_IPS['SELF'], 'UpdateDaily', 3, 1, $from, $to);
    foreach ($PVA as $name => $plant) {
        // Kategorie erzeugen
        $cid = CreateCategoryByName($_IPS['SELF'], $name);
        $vid = CreateVariableByName($cid, 'Daten', 3, 0);
        SetValueString($vid, '[]');
        $vid = CreateVariableByName($cid, 'Tabellarischer Verlauf', 3, 1, 'Database', '~HTMLBox');
        $vid = CreateVariableByName($cid, 'Graphischer Verlauf', 3, 2, 'Graph', '~HTMLBox');
        $vid = CreateVariableByName($cid, 'PV heute (normal)', 2, 10, '', '~Electricity');
        // Archivierung aktivieren (Typ: Zähler)
        RegisterArchive($vid, false);
        $vid = CreateVariableByName($cid, 'PV heute (besser)', 2, 11, '', '~Electricity');
        // Archivierung aktivieren (Typ: Zähler)
        RegisterArchive($vid, false);
        $vid = CreateVariableByName($cid, 'PV heute (schlechter)', 2, 12, '', '~Electricity');
        // Archivierung aktivieren (Typ: Zähler)
        RegisterArchive($vid, false);
        $vid = CreateVariableByName($cid, 'PV morgen (normal)', 2, 20, '', '~Electricity');
        $vid = CreateVariableByName($cid, 'PV morgen (besser)', 2, 21, '', '~Electricity');
        $vid = CreateVariableByName($cid, 'PV morgen (schlechter)', 2, 22, '', '~Electricity');
    }
}
// WEBFRONT
elseif ($_IPS['SENDER'] == 'WebFront') {
    // Benutzer hat etwas geändert!
}
// VARIABLENAENDERUNG
elseif ($_IPS['SENDER'] == 'Variable') {
    // ToDO?
}
// TIMER EVENT
elseif ($_IPS['SENDER'] == 'TimerEvent') {
    $event = IPS_GetName($_IPS['EVENT']);
    // pro Anlage werden die Daten auswerten
    foreach ($PVA as $name => $plant) {
        UpdateData($name, $plant, ($event == 'UpdateMidnight'));
    }
}

#----------------------------------- Functions ---------------------------------

// Für die Anlagen den Forecast ermitteln und tabellarisch und graphisch aufbereiten
function UpdateData($name, $plant, $reset = false)
{
    // Kategorie erzeugen oder lesen
    $cid = CreateCategoryByName($_IPS['SELF'], $name);
    // Daten holen
    $data = RequestData($plant);
    // Daten normalisieren
    $fore = ForecastData($data);
    // Gespeicherte Daten einlesen
    $did = CreateVariableByName($cid, 'Daten', 3);
    $data = json_decode(GetValue($did), true);
    // Variablen
    $hnid = CreateVariableByName($cid, 'PV heute (normal)', 2);
    $hbid = CreateVariableByName($cid, 'PV heute (besser)', 2);
    $hsid = CreateVariableByName($cid, 'PV heute (schlechter)', 2);
    $mnid = CreateVariableByName($cid, 'PV morgen (normal)', 2);
    $mbid = CreateVariableByName($cid, 'PV morgen (besser)', 2);
    $msid = CreateVariableByName($cid, 'PV morgen (schlechter)', 2);
    // Daten zurücksetzen oder zusammenführen
    if ($reset) {
        $data = $fore;
        // Heute Variablen
        SetValueFloat($hnid, 0);
        SetValueFloat($hbid, 0);
        SetValueFloat($hsid, 0);
        IPS_Sleep(1000);
        // Morgen Variablen
        SetValueFloat($mnid, 0);
        SetValueFloat($mbid, 0);
        SetValueFloat($msid, 0);
        IPS_Sleep(1000);
    } else {
        foreach ($fore as $day => $hours) {
            foreach ($hours as $hour => $values) {
                $data[$day][$hour] = $values;
            }
        }
    }

    
    // Data wieder speichern
    $str = json_encode($data);
    SetValueString($did, $str);
    // Summen ermitteln für Heute (Archive) und Morgen
    $values = CalcTotal($data);
    // Heute
    ArchiveValue($hnid, $values[0]['norm']);
    ArchiveValue($hbid, $values[0]['more']);
    ArchiveValue($hsid, $values[0]['poor']);
    // Morgen
    SetValueFloat($mnid, $values[1]['norm']);
    SetValueFloat($mbid, $values[1]['more']);
    SetValueFloat($msid, $values[1]['poor']);
    // HTML Table bauen
    $html = BuildTable($data);
    $vid = CreateVariableByName($cid, 'Tabellarischer Verlauf', 3);
    SetValueString($vid, $html);
    // SVG Chart plotten
    $svg = DrawChart($data);
    $svg = str_replace(['1024pt', '325pt'], '100%', $svg);
    $vid = CreateVariableByName($cid, 'Graphischer Verlauf', 3);
    SetValueString($vid, $svg);
}

// Von SolCast gelieferten Daten auf Tages- und stundenbasis Normalisieren
function ForecastData($data)
{
    $fd = [];
    foreach ($data['forecasts'] as $o) {
        $hour = date('H', strtotime($o['period_end']));
        $day = date('z', strtotime($o['period_end'])) - date('z');
        if (isset($fd[$day][$hour])) {
            // echo "Add " . $day . ' - ' . $hour . PHP_EOL;
            $fd[$day][$hour]['norm'] = ($fd[$day][$hour]['norm'] + $o['pv_estimate']) / 2;
            $fd[$day][$hour]['poor'] = ($fd[$day][$hour]['poor'] + $o['pv_estimate10']) / 2;
            $fd[$day][$hour]['more'] = ($fd[$day][$hour]['more'] + $o['pv_estimate90']) / 2;
        } else {
            // echo "New " . $day . ' - ' . $hour . PHP_EOL;
            $fd[$day][$hour] = [
                'norm'  => $o['pv_estimate'],
                'poor'  => $o['pv_estimate10'],
                'more'  => $o['pv_estimate90'],
            ];
        }
    }
    return $fd;
}

// Daten gezielt ins Archive schreiben (Zähler -> keine negativen Werte)
function ArchiveValue($vid, $value)
{
    $lv = GetValueFloat($vid);
    if (($lv > 0) && ($lv != $value)) {
        //Den letzten Wert, der in der Datenbank gespeichert wurde, holen
        $aid = IPS_GetInstanceListByModuleID(ExtractGuid('Archive Control'))[0];
        $last = AC_GetLoggedValues($aid, $vid, 0, 0, 1)[0];
        AC_DeleteVariableData($aid, $vid, $last['TimeStamp'], 0);
        IPS_Sleep(1000);
        SetValueFloat($vid, $value);
        AC_ReAggregateVariable($aid, $vid);
        IPS_Sleep(1000);
    } else {
        SetValueFloat($vid, $value);
    }
}

// Daten bei SolCast.com anfragen
function RequestData($plant, $forecast = true)

{
    
    // Which data
  
    $url = 'https://api.solcast.com.au/rooftop_sites/' . $plant['rid'];
    if ($forecast) {
        $url .= '/forecasts?format=json';
    } else {
        $url .= '/estimated_actuals?format=json';
    }

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 0);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
    curl_setopt($ch, CURLOPT_ENCODING, '');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $plant['token'],
    ]);
    $response = curl_exec($ch);
    if ($response === false) {
        throw new ErrorException(curl_error($ch));
    }
    curl_close($ch);
     //$response = $SO_JSON;
    $data = json_decode($response, true);
     //print_r($data);
    return $data;
}
 
// Tages-Summen bilden und zurückliefern
function CalcTotal($data)
{
    $total = [];
    foreach ($data as $day => $hours) {
        $poor = 0;
        $norm = 0;
        $more = 0;
        foreach ($hours as $hour => $values) {
            $poor += $values['poor'];
            $norm += $values['norm'];
            $more += $values['more'];
        }
        $total[$day]['poor'] = $poor;
        $total[$day]['norm'] = $norm;
        $total[$day]['more'] = $more;
    }
    return $total;
}

// HTML-Tabelle pro Tag bauen
function BuildTable($data)
{
    // cols
    $cols = 0;
    foreach ($data as $day => $hours) {
        $count = 0;
        foreach ($hours as $hour => $values) {
            if (($values['poor'] != 0) || ($values['norm'] != 0) || ($values['more'] != 0)) {
                $count++;
            }
        }
        $cols = max($cols, $count);
    }
    // html
    $html = '';
    $html .= '<table class="wwx">';
    foreach ($data as $day => $hours) {
        $count = 0;
        $th = '<thead class="orange"><th><center>' . date('d.m.Y', strtotime('today+' . $day . 'day')) . '</center></th>';
        $tr1 = '<tr><td>kW/h (bewölkt)</td>';
        $tr2 = '<tr><td>kW/h (normal)</td>';
        $tr3 = '<tr><td>kW/h (sonnig)</td>';
        foreach ($hours as $hour => $values) {
            if (($values['poor'] != 0) || ($values['norm'] != 0) || ($values['more'] != 0)) {
                $th .= '<th>' . $hour . ':00</th>';
                $tr1 .= '<td>' . round(floatval($values['poor']), 4) . '</td>';
                $tr2 .= '<td>' . round(floatval($values['norm']), 4) . '</td>';
                $tr3 .= '<td>' . round(floatval($values['more']), 4) . '</td>';
                $count++;
            }
        }
        for ($i = $count; $i < $cols; $i++) {
            $th .= '<th>-</th>';
            $tr1 .= '<td>-</td>';
            $tr2 .= '<td>-</td>';
            $tr3 .= '<td>-</td>';
        }
        $html .= $th . '</thead>';
        $html .= $tr1 . '</tr>';
        $html .= $tr2 . '</tr>';
        $html .= $tr3 . '</tr>';
    }
    $html .= '</table>';
    return $html;
}

// Daten als Chart plotten
function DrawChart($data)
{
    $axis_x = [];
    $axis_yn = [];
    $axis_yp = [];
    $axis_ym = [];

    foreach ($data as $day => $hours) {
        foreach ($hours as $hour => $value) {
            if (($value['norm'] != 0) && ($value['poor'] != 0) && ($value['more'] != 0)) {
                $axis_x[] = '\'' . $hour . ':00\'';
                $axis_yn[] = intval($value['norm'] * 1000);
                $axis_yp[] = intval($value['poor'] * 1000);
                $axis_ym[] = intval($value['more'] * 1000);
            }
        }
    }

    // new chart object
    $chart = new QuickChart(['width' => 1024, 'height' => 325, 'format' => 'svg']);
    // chart config
    $chart->setConfig("{
    type: 'line',
    data: {
        labels: [" . implode(',', $axis_x) . "],
        datasets: [
        {
            label: 'Sonniger',
            backgroundColor: 'rgba(255, 125, 0, 0.1)',
            borderColor: 'rgb(255, 125, 0)',
            borderWidth: 1,
            lineTension: 0.4,
            pointRadius: 1,
            fill: true,
            data: [" . implode(',', $axis_ym) . "]
        },
        {
            label: 'Normal',
            backgroundColor: 'rgba(255, 255, 0, 0.1)',
            borderColor: 'rgb(255, 255, 0)',
            borderWidth: 1,
            lineTension: 0.4,
            pointRadius: 1,
            fill: true,
            data: [" . implode(',', $axis_yn) . "]
        },
        {
            label: 'Bewölkter',
            backgroundColor: 'rgba(0, 255, 255, 0.1)',
            borderColor: 'rgb(0, 255, 255)',
            borderWidth: 1,
            lineTension: 0.4,
            pointRadius: 1,
            fill: true,
            data: [" . implode(',', $axis_yp) . "]
        }
        ]
    },
    options: {
        legend: {
            labels: {
                fontColor: 'rgb(255, 255, 255)'
            }
        },
        scales: {
        xAxes: [{
            ticks: {
                fontColor: '#fff',
            },
            gridLines: {
                color: 'rgba(211, 211, 211, 0.2)',
                borderDash: [5,5]
            },
            scaleLabel: {
            fontColor: '#fff',
            display: false,
            labelString: 'Uhrzeit'
            }
        }],
        yAxes: [{
            ticks: {
                fontColor: '#fff'
            },
            gridLines: {
                color: 'rgba(211, 211, 211, 0.2)',
                borderDash: [5,5]
            },
            scaleLabel: {
            fontColor: '#fff',
            display: true,
            labelString: 'Leistung (kW)'
            }
        }]
        }
    }
    }");
    return $chart->toBinary();
}

################################################################################

richimaint

Ich hatte anfangs auch probleme mit token/rid, hab es bei mir so eingebaut:

$PVA['Haus']['token'] = '###########';
$PVA['Haus']['rid'] = '#########';
$PVA['Haus']['graph'] = true;

Jetzt funktioniert es.

Dann ist das Script von @pitti doch fehlerhaft?

Danke
richimaint

Wenn man sich an die Anleitung hält, funktioniert es bestimmt…

Ich habe das bewusst nicht getan, daher vermute ich lag der Fehler eher bei mir.

Ich habe das Script von github importiert und nur die „rid“ ID , den Api Token und die " require_once" reinkopiert, dann sollte es eigentlich funktionieren.

richimaint

Auch das drumherum bedacht? Die Scripte in der Autoload.php, WWX Skin?

Die Autoload habe ich weggelassen, dafür das „require_once“ direkt ins Script geschrieben.

require_once IPS_GetKernelDir() . 'scripts' . DIRECTORY_SEPARATOR . 'System.QuickChart.ips.php';
require_once IPS_GetKernelDir() . 'scripts' . DIRECTORY_SEPARATOR . 'System.Functions.ips.php';
require_once IPS_GetKernelDir() . 'scripts' . DIRECTORY_SEPARATOR . 'System.Locals.ips.php';

Hat ja jetzt auch gleich funktioniert nach deiner Info zu den Token und „rid“.
Lag somit nur an dieser Stelle.

richimaint

Tolles skript, werde ich testen. Da ich mit meinen eigenem nicht so zufrieden bin.

Wenns funzt schaue ich mal auf deiner website vorbei.

Frage.
Welche skripte muss ich jetzt in meinen scripts ordner kopieren.?

System.QuickChart
System.Locals
System.Functions.
WwX_Functions

Diese müssen dann in der autoload eingetragen werden oder?

Hab des so noch nicht gemacht mit dem autoload.

Danke.

Puh, immer diese Fragen :rofl:

Systems.Local. und Systems.Functions. kommen in die __autoload, aber nur wenn man nicht schon über die Community die ganzen CreateVariableXyZ im System hat :slight_smile:

QuickChart einfach anlegen und wird dann per include reingezogen!

WWX_Functions kommt automatisch wenn vorher alles korrekt installiert wurde!

Gru0 Heiko

1 „Gefällt mir“

Hey @pitti unfassbar was Du alles raushaust, auf Deine Solardinger kam ich heute erst!
(Bist im Jan auch in Lübeck, Heiko? Da treffen sich dann die Münchner :wink: )

Ich kenne auch Deine Skriptbibliothek noch gar nicht, aber das wird sich nun ändern.

Meine eigentliche Frage:
Du schreibst Dein Skript nutzt Deinen WwxSkin, jetzt bin ich aber nur noch auf der Tile und IPSview unterwegs. Das ist vermutlich nur ein Hinweis für die Webfront User, oder?

Danke und Gruss Seppm

Weiß ich noch nicht - würde aber schon gern :slight_smile:

Ja, hauptsächlich wegen der Tabellen-Sachen! Sonst sieht es wohl etwas verschoben aus. Der Rest funzt auch ohne meinem Skin!

Danke & Ciao
Heiko

Danke Dir,
Werde es probieren, vermute mal dass es die Werte die in die Tabelle fließen auch als Variablen gibt und ich das ggf. mit Symcon eigenen Tabellen machen kann.
In jedem Fall eine sehr hilfreiche Sache.

Cheers Seppm

PS: Dann hoffe ich mal auf Lübeck, aber vlt. bekommt man im Münchner Osten auch so mal wieder was hin. Sind halt alle viel eingespannt.

1 „Gefällt mir“

Moin pitti, gestern nach deiner Anleitung den solcast-Script installiert, hat alles funktioniert . danke nochmal dafür.
Kleine Anmerkung im „Graphischen Verlauf“ ist die Leistung mit kW skaliert, sollte wohl eher W sein ?
Der „Tabellarische Verlauf“ wird auch mit den SolCast-Werten dargestellt, das Säulendiagramm hab ich bisher noch nicht gesehen, gibts dabei was besonderes zu beachten oder -braucht das halt ein paar Tage oder -muss ich das selbst erstellen?
Gruß Gerd

Danke, da hast Du wohl Recht :slight_smile:

Ja, der Tagesverlauf ist ein Diagramm welches man selber anlegen muss aus den entsprechenden Variablen. Hier wie ich es gemacht habe …

Gruß Heiko