Dyn. Strompreis auslesen - Octopus Energy

Begonnen von maddinthebrain, 23 Juni 2026, 15:17:02

Vorheriges Thema - Nächstes Thema

maddinthebrain

Hallo zusammen,

ich möchte gerne den dyn. Strompreis von Octopus Energy über deren API in Fhem verfügbar machen. Ich haben Gemini um Unterstützung gefragt:

# --------- Octopus Strompreis auslesen -------

use HttpUtils;
use JSON::PP;
use Time::Local;

sub getOctopusPrices {
    # ------------------ KONFIGURATION HIER EINTRAGEN ------------------
    my $email    = 'tollemailadresse@mail.de';
    my $accountNo = 'A-xxxxxx';
    # -----------------------------------------------------------------

    # Passwort sicher aus dem FHEM-Speicher auslesen
    my ($pwdErr, $password) = getKeyValue("octopus_password");
   
    if ($pwdErr || !$password) {
        Log 2, "Octopus SMGW Fehler: Passwort konnte nicht aus FHEM-KeyValue-Speicher gelesen werden!";
        return;
    }

    my $gqlUrl = "https://api.oeg-kraken.energy/v1/graphql/";

    # Schritt 1: Login-Mutation mit dem dynamisch geladenen Passwort
    my $loginQuery = {
        query => 'mutation ($email: String!, $password: String!) { obtainKrakenToken(input: { email: $email, password: $password }) { token } }',
        variables => { email => $email, password => $password }
    };
   
    my $loginParam = {
        url        => $gqlUrl,
        timeout    => 10,
        header    => "Content-Type: application/json",
        data      => encode_json($loginQuery),
        callback  => sub {
            my ($param, $err, $data) = @_;
            if ($err ne "") { Log 3, "Octopus SMGW-Login-Fehler: $err"; return; }
           
            my $decodedLogin = eval { decode_json($data) };
            my $token = $decodedLogin->{data}{obtainKrakenToken}{token};
            if (!$token) { Log 3, "Octopus SMGW: Token-Generierung fehlgeschlagen."; return; }
           
            # Schritt 2: Abfrage der Marktpreise für iMSys (Smart Meter Gateway)
            my $dataQuery = {
                query => 'query ($accountNo: String!) {
                    account(accountNumber: $accountNo) {
                        properties {
                            electricityMeteringPoints {
                                agreements {
                                    tariff {
                                        ... on MarketIndexedTarif {
                                            unitRates {
                                                valueWithTax
                                                validFrom
                                                validTo
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }',
                variables => { accountNo => $accountNo }
            };

            my $dataParam = {
                url        => $gqlUrl,
                timeout    => 10,
                header    => "Authorization: Bearer $token\r\nContent-Type: application/json",
                data      => encode_json($dataQuery),
                callback  => sub {
                    my ($dParam, $dErr, $dData) = @_;
                    if ($dErr ne "") { Log 3, "Octopus SMGW-Datenabruf-Fehler: $dErr"; return; }
                   
                    my $decodedData = eval { decode_json($dData) };
                    if (!$decodedData || $decodedData->{errors}) {
                        Log 3, "Octopus API Fehler: " . ($decodedData->{errors}{message} // "Unbekannt");
                        return;
                    }
                   
                    my $points = eval { $decodedData->{data}{account}{properties}{electricityMeteringPoints}{agreements}{tariff}{unitRates} };
                    if (!$points || ref($points) ne 'ARRAY') {
                        Log 2, "Octopus SMGW Fehler: Konnte unitRates im JSON nicht finden.";
                        return;
                    }
                   
                    my $now = time();
                    my $price_current = undef;
                    my $price_next = undef;
                   
                    foreach my $rate (@$points) {
                        my $from = parseIsoToEpoch($rate->{validFrom});
                        my $to  = parseIsoToEpoch($rate->{validTo});
                       
                        if ($now >= $from && $now < $to) {
                            $price_current = $rate->{valueWithTax};
                        }
                        if (($now + 3600) >= $from && ($now + 3600) < $to) {
                            $price_next = $rate->{valueWithTax};
                        }
                    }
                   
                    if (defined($price_current)) {
                        fhem("setreading Octopus_Strompreis price_current " . sprintf("%.2f", $price_current));
                        fhem("setreading Octopus_Strompreis price_next_hour " . sprintf("%.2f", $price_next)) if defined($price_next);
                        fhem("setreading Octopus_Strompreis meter_type LandisGyr_iMSys");
                        fhem("setreading Octopus_Strompreis last_update " . localtime());
                        Log 3, "Octopus SMGW (Landis+Gyr) erfolgreich aktualisiert: $price_current Cent/kWh.";
                    } else {
                        Log 3, "Octopus SMGW: Kein Preis für die aktuelle Stunde im Datensatz vorhanden.";
                    }
                }
            };
            HttpUtils_NonblockingGet($dataParam);
        }
    };
    HttpUtils_NonblockingGet($loginParam);
}

sub parseIsoToEpoch {
    my ($iso) = @_;
    if ($iso =~ /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/) {
        return timegm($6, $5, $4, $3, $2 - 1, $1 - 1900);
    }
    return 0;
}

Nun ist es so, wenn ich die Funktion triggere stürzt FHEM komplett ab und startet neu. Die Werte sollen in ein Dummy geschrieben werden:

[code]define Octopus_Strompreis dummy
attr Octopus_Strompreis readingList price_current price_next_hour last_update
attr Octopus_Strompreis room Energie,MiniPV,Zähler
attr Octopus_Strompreis stateFormat Aktuell: price_current ct/kWh </br> Nächste Stunde: price_next_hour ct/kWh
#  FUUID      6a3a73f7-f33f-54a1-8d84-f2bfb88940d33850
#  FVERSION  98_dummy.pm:0.256060/2022-02-01
#  NAME      Octopus_Strompreis
#  NR        435
#  STATE      Aktuell: price_current ct/kWh </br> Nächste Stunde: price_next_hour ct/kWh
#  TYPE      dummy
#
setstate Octopus_Strompreis Aktuell: price_current ct/kWh </br> Nächste Stunde: price_next_hour ct/kWh

[/code]

Getriggert wird die Funktion alle 60min um 1min nach Ganz.

Hat jemand eine Idee warum das Skript auf die Bretter geht?

Vielen Dank

viele Grüße
Martin
Viele Grüße
Martin

Futro mit Proxmox und Debian: FHEM, Signalduino 433MHz & 868MHz, MAX!, WeeWX, FHEM2FHEM,
Raspi 4 mit ConBee mit deCONZ und Phoscon für ZigBee Aktoren und Sensoren

betateilchen

Meldung im Logfile zum Absturz?
FHEM schonmal in Debug Modus gestartet und dann getestet?
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

maddinthebrain

Hi betateilchen,

das ist ja das Problem, im Logfile steht nix dazu. Aber Evtl. ist das ein Hinweis:

Not a HASH reference at ./FHEM/99_myUtils.pm line 164.
2026.06.23 15:02:37 1: reload: Error:Modul 99_myUtils deactivated:

Leider steht dann nix nach dem Doppelpunkt. Achtung die Zeile 164 stimmt nicht mit dem Skript oben überein. Das steht in Realität noch Zeug davor.
Viele Grüße
Martin

Futro mit Proxmox und Debian: FHEM, Signalduino 433MHz & 868MHz, MAX!, WeeWX, FHEM2FHEM,
Raspi 4 mit ConBee mit deCONZ und Phoscon für ZigBee Aktoren und Sensoren