[Script] solarprognose.de (Full version)

Hallo,

das Warten hat ein Ende - die Vorgeschichte zum Script kann man hier nachlesen => Abfrage Solarprognose!
Ich mache hier nur ein neuen Thread auf um das Script besser supporten zu können.

Wie im anderen Thread schon gesagt, das Script ist nicht im Stil „fire & forget“, sondern man muss noch paar Sachen anpassen bzw. konfigurieren.

Grob sind das folgende Dinge:

  • API Key auf solarprognose.de beantragen und bekommen
  • PV Anlage dort entsprechend konfigurieren
  • API-Daten im Script entsprechend eintragen
  • Script einmal in der Konsole ausführen
  • Archivierung (siehe Script) für den Tageswert einschalten und auf Zähler setzen
  • Timer für stündliches und tägliches Ereignis einrichten
  • Webfront den eigenen Bedürfnissen zusammenbauen

HINWEIS/VORRAUSSETZUNG:

  1. Meine globale Script-Bibliothek wurde installiert
  2. Das Quickchart-Skript wurde installiert
  3. Das Script nutzt meinen WwxSkin
  4. Das Script nutzt nur die „freie Version“ von solarprognose.de, also 1 Tag in die Zukunft

Von der Objektstruktur sollte es dann so aussehen:

Das UpdateHourly Ereignis sieht so aus:

image

Und das Script kommt jetzt:

<?php

declare(strict_types=1);

################################################################################
# Script:   Ammount.SolarForcast.ips.php
# Version:  1.1.20230223
# Author:   Heiko Wilknitz (@Pitti)
#
# Script zur Abholung und Aufbereitung der prognostizierten Solorproduktion
# von solarprognose.de.
#
# API
#   https://www.solarprognose.de/web/solarprediction/api/v1
#       ?access-token=ACCESS-TOKEN
#       &project=Hier Ihre Projekt-Website oder Ihre Kontakt-E-Mail
#       &item=ITEM
#       &id=ID
#       &type=hourly|daily
#       &_format=json|xml
#       &algorithm=mosmix|own-v1|clearsky
#       &day=DAY
#       &start_epoch_time=START_EPOCH_TIME&end_epoch_time=END_EPOCH_TIME
#       &start_day=START_DAY&end_day=END_DAY
#       &snomminixml=true # für snom VoIP Telefone
#
# ------------------------------ 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' den eigenen Gegebenheiten anpassen
# - Skript Abspeichern
# - Skript Ausführen
# - Visualisierung per Link auf entstandene Variablen erstellen
#
# ------------------------------ Changelog -------------------------------------
#
# 08.02.2023 - Initalversion (v1.0)
# 23.02.2023 - API Doc, Type fixes (v1.1)
#
# ------------------------------ Konfiguration ---------------------------------
#
# Global Debug Output Flag
$DEBUG = false;
#
# Solar Prognose API Parameter
$SP_TOKEN = '<API-KEY hier zwischen den Hochkommas reinkopieren>';
$SP_ITEM = 'location';
$SP_PROJECT = '<API-MAILADRESSE hier zwischen den Hochkommas reinkopieren>';
$SP_FORMAT = 'json';
$SP_TYPE = 'hourly'; // 'daily';
$SP_ID = <<LOCATION-ID hier reinkopieren>;
$SP_START = 0;
$SP_END = 1;
#
################################################################################
#
# 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
#
defined('WWX_FUNCTIONS') || die('Global function library not available!');

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

// INSTALLATION
if ($_IPS['SENDER'] == 'Execute') {
    $vid = CreateVariableByName($_IPS['SELF'], 'Vorhersage', 3, 0, 'Graph', '~HTMLBox');
    $vid = CreateVariableByName($_IPS['SELF'], 'Tagesprognose', 2, 1, '', '~Electricity'); // TODO: Archivierung aktivieren (Typ: Zähler)
    $vid = CreateVariableByName($_IPS['SELF'], 'Stundenprognose', 2, 2, '', '~Electricity');
    $vid = CreateVariableByName($_IPS['SELF'], 'Aktuelle Leistung', 3, 3, 'EnergyProduction', '~HTMLBox');
}
// WEBFRONT
elseif ($_IPS['SENDER'] == 'WebFront') {
    // Benutzer hat etwas geändert!
}
// VARIABLENAENDERUNG
elseif ($_IPS['SENDER'] == 'Variable') {
    // Varaible hat sich geändert!
}
// TIMER EVENT
elseif ($_IPS['SENDER'] == 'TimerEvent') {
    $event = IPS_GetName($_IPS['EVENT']);
    if ($event == 'UpdateDaily') {
        // Mitternacht - Reset auf 0
        $vid = CreateVariableByName($_IPS['SELF'], 'Tagesprognose', 2);
        SetValue($vid, 0);
        $vid = CreateVariableByName($_IPS['SELF'], 'Stundenprognose', 2);
        SetValue($vid, 0);
    } elseif ($event == 'UpdateHourly') {
        // von 05:00 bis 21:00 - TODO: Timer im Sommer vielleicht anpassen
        $data = UpdateForecast($SP_TOKEN, $SP_PROJECT, $SP_ITEM, $SP_ID, $SP_TYPE, $SP_FORMAT, $SP_START, $SP_END);
        // aktuellen Werte abgleichen wenn notwendig
        $vid = CreateVariableByName($_IPS['SELF'], 'Tagesprognose', 2);
        $lv = GetValue($vid);
        if ($lv < $data['Tageswert']) {
            SetValue($vid, $data['Tageswert']);
        } elseif ($lv > $data['Tageswert']) {
            //Den letzten Wert, der in der Datenbank gespeichert wurde, holen
            $last = AC_GetLoggedValues(38732, $vid, 0, 0, 1)[0];
            AC_DeleteVariableData(38732, $vid, $last['TimeStamp'], 0);
            SetValue($vid, $data['Tageswert']);
        }
        $vid = CreateVariableByName($_IPS['SELF'], 'Stundenprognose', 2);
        SetValue($vid, $data['Stundenwert']);
        // SVG Chart
        $vid = CreateVariableByName($_IPS['SELF'], 'Aktuelle Leistung', 3);
        $svg = DrawChart($data['Stundenwert']);
        $svg = str_replace(['220pt', '120pt'], '100%', $svg);
        SetValue($vid, $svg);
        // HTML Table
        $html = BuildHtml($data);
        $vid = CreateVariableByName($_IPS['SELF'], 'Vorhersage', 3);
        SetValue($vid, $html);
    }
}

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

function UpdateForecast($token, $project, $item, $id, $type, $format, $start, $end, $day = null)
{
    $url = "https://www.solarprognose.de/web/solarprediction/api/v1?access-token=$token&project=$project&item=$item&id=$id&type=$type&_format=$format&algorithm=own-v1&start_day=$start&end_day=$end";
    //EchoDebug('URL: ', $url);
    $json = file_get_contents($url);
    //EchoDebug('Daten :', $json);
    $result = [];
    if ($json !== false) {
        $data = json_decode($json, true);
        // aktueller Tag und aktuelle Stunde
        $ad = date('d.m.Y');
        $at = date('H') . ':00';
        $result['Tageswert'] = 0;
        $result['Stundenwert'] = 0;
        // Stundenwerte
        foreach ($data['data'] as $key => $values) {
            // Tag
            $dd = date('d.m.Y', $key);
            // Uhrzeit
            $dt = date('H:i', $key);
            // Tageswert hochzählen
            if ($ad == $dd) {
                $result['Tageswert'] += $values[0];
            }
            if (($ad == $dd) && ($at == $dt)) {
                $result['Stundenwert'] = $values[0];
            }
            // Pro Tag ein Feld
            if (!isset($result[$ad])) {
                $result[$ad] = [];
            }
            // Stundenwerte und akkumulierten Werte
            $result[$dd][$dt] = $values;
        }
    }
    return $result;
}

function BuildHtml($data)
{
    unset($data['Tageswert']);
    unset($data['Stundenwert']);
    // Rows
    $rows = array_keys($data);
    // cols
    $cols = max(count($data[$rows[0]]), count($data[$rows[1]]));
    // html
    $html = '';
    $html .= '<table class="wwx">';
    foreach ($data as $day => $hours) {
        $count = 0;
        $th = '<thead class="orange"><th><center>' . $day . '</center></th>';
        $tr1 = '<tr><td>kW/h</td>';
        $tr2 = '<tr><td>&sum; kWh</td>';
        foreach ($hours as $hour => $values) {
            $th .= '<th>' . $hour . '</th>';
            $tr1 .= '<td>' . $values[0] . '</td>';
            $tr2 .= '<td>' . $values[1] . '</td>';
            $count++;
        }
        for ($i = $count; $i < $cols; $i++) {
            $th .= '<th>-</th>';
            $tr1 .= '<td>-</td>';
            $tr2 .= '<td>-</td>';
        }
        $html .= $th . '</thead>';
        $html .= $tr1 . '</tr>';
        $html .= $tr2 . '</tr>';
    }
    $html .= '</table>';
    return $html;
}

function DrawChart($value)
{
    $int = intval($value * 1000);
    // new chart object
    $chart = new QuickChart(['width' => 220, 'height' => 120, 'format' => 'svg']);
    // chart config
    $chart->setConfig("{
        type: 'gauge',
        data: {
            datasets: [
                {
                    data: [2500, 5000, 7500],
                    value: $int,
                    minValue: 0,
                    backgroundColor: ['red', 'orange', 'green'],
                    borderWidth: 1,
                },
            ],
        },
        options: {
            legend: {
                display: false,
            },
            title: {
                display: false,
                text: 'Aktuelle Leistung',
                position: 'bottom',
            },
            needle: {
                radiusPercentage: 0,
                widthPercentage: 2,
                lengthPercentage: 40,
                color: '#c0c0c0',
            },
            valueLabel: {
                fontSize: 8,
                backgroundColor: 'transparent',
                color: '#fff',
                formatter: function (value, context) {
                    return  value.toLocaleString('de-DE') + ' kW';
                },
                bottomMarginPercentage: 10,
            },
            plugins: {
                datalabels: {
                    display: 'auto',
                    formatter: function (value, context) {
                        return context.chart.data.labels[context.dataIndex/100];
                    },
                    color: '#fff',
                    font: {
                        weight: 'bold',
                        size: 6,
                    }
                },
            },
        },
    }");
    return $chart->toBinary();
}
################################################################################

Viel Erfolg
Heiko

1 „Gefällt mir“

Hallo Heiko,

vielen Dank für dein Skript inkl. Zubehör. Ich habe jetzt denke ich alles soweit „installiert“.
Die Variablen wurde angelegt, es gab keine Fehlermeldung.

Die Prognose ist bisher leer. Woran kann das liegen?

Vielen Dank schon mal.

Grüße
Stephan

Hi Stephan,

naja, wenn alles fehlerfrei verlief und ich das richtig sehe, läuft morgen früh der erste Timer und dann sollten auch Daten fließen!

Also warten :joy: jetzt scheint sowieso keine Sonne :sun_with_face:

Gruß Heiko

Zeig doch zur Sicherheit mal deinen Objektbaum?!

Hallo Heiko,

du hast natürlich recht, heute scheint keine Sonne mehr. Hatte nur gehofft gleich zu wissen das alles passt, da ich heute Abend ein wenig Zeit habe. Morgen sieht das wieder anders aus…

Mein Objektbaum sieht so aus:

Diese beiden Skripte liegen bei mir im Skripts Verzeichnis.

Die Änderungen in der _autoload.php habe ich vorgenommen.

Passt das dann so?

Grüße
Stephan

Muss hier etwas geändert werden?

Ist damit die ID in IP Symcon der Location Instanz gemeint?

Wenn ich das Skript manuell ausführe, werden die Variablen die vom Skript angelegt wurden nicht aktualisiert, warum?

$SP_ITEM kannste so belassen und bei $SP_ID kommt die Standort ID rein von Solarprognose.

Bei mir sieht das im Moment noch so aus (Ist noch nichts einsortiert):

Danke für deine Antwort. Dann habe ich das richtig, hatte nur überlegt weil nichts kam ob das dann evtl. nicht passt.

Genau wie Stefan schreibt :+1:

Stell doch einfach spaßeshalber das Ergebnis auf 22 Uhr :rofl:

Welches Ereignis meinst du? Das stündliche? oder das einmal um null Uhr läuft?
Hatte das stündliche vorhin für eine Ausführung auf minütlich, dabei hat sich nichts getan :disappointed_relieved:.

Ja das Stündliche!
Wenn dann nix passiert wird’s blöd :rofl:

Dann Bau doch an paar stellen ein Logging ein!

Habt ihr die Events auch richtig benannt???

Wie genau meinst du das mit „die Events richtig benannt?“.

Ich habe nichts gemacht außer diese zu erstellen :scream:.

1 „Gefällt mir“

Da liegt der Fehler. Darum hatte ich ja den Screen in die Erklärung eingebunden.

UpdateDaily und UpdateHourly glaube müssen die heissen!

Habe mal den Timer umgestellt auf 21:53:15, bei mir sieht es jetzt so aus:

D.h. es geht? Oder wie sieht’s aus?

Meine Variablen wurden aktualisiert, außer die Vorhersage nicht, aber nicht befüllt mit der Prognose von Morgen oder kommt das dann erst morgen früh von dem anderen timer.

Eigentlich nicht. Packt doch mal ein Ips_Logmessage in die UpdateData Funktion um zu sehen ob Daten überhaupt geholt werden.

Hatte ja gesagt, Script ist keine fertige Lösung.

Wahninn… dazu hat es bei mir natürlich wieder nicht gereicht.
Jetzt geht es. Sieht jetzt so aus.


Gerade morgen schein mir sehr positiv zu sein muss ich sagen. Aber ich bin gespannt.

Heute hatte ich 48 kWh, die Prognose mit 52 ist da ja wirklich sehr in Ordnung.

Danke Heiko, viele Grüße und noch einen schönen Abend.
Stephan

1 „Gefällt mir“

Sag mal wo wohnst Du, das ist ja unverschämt so eine gute Prognose :rofl::rofl::rofl: