[Script] solcast.com

Moin, bin gerade auf der Adria unterwegs, kann ich mir erst nächste Woche anschauen :sailboat:

Keinen Stress, hab eh noch keine Anlage :smiley:
Schönen Urlaub!

1 „Gefällt mir“

Hab den Fehler gefunden, saß wie immer vorm PC :smiley:
Die abfrage um 0Uhr war genau die 11. Habe nun den Update Daily noch angepasst, nun sollte es dann hoffentlich klappen.

Moin @pitti

ich möchte gerne die Bibliothek installieren und bekomme die Meldung:
Wollte diese unter Module mit dieser URL installieren.

https://github.com/wilkware/ips-scripts/

grafik
richimaint

Moin, wie die Überschrift des Threads schon sagt, handelt es sich hier um ein Script und kein Modul!!!
Hast Du wirklich alles gelesen was dazu stand?

Gruß Heiko

Tja, wer lesen kann…

Sorry

richimaint

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.