HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen

Begonnen von ch.eick, 30 Juli 2025, 15:26:34

Vorheriges Thema - Nächstes Thema

ch.eick

Nächste Schritte wären jetzt die Kostenberechnung, die bei den Tibber Kunden über deren Portal kommen, jedoch nicht bei den eventuell interessierten, die noch keine Kunden sind.

Für die early birds hätte ich hier mal ein MySQL, das die Kosten innerhalb der Datenbank berechnet und dort auch direkt einträgt, es erfolgt somit keine Ausgabe als Tabelle, was jedoch auch möglich wäre. Momentan habe ich jedoch Probleme mit dem DBI vom FHEM zur Datenbank :-(
Die Ausgabe soll gleich der von Tibber sein, damit ein Wechsel nicht direkt mit Anpassungen innerhalb der Datenbank einhergeht.

- Für den INSERT sind die Kommentare zu entfernen

- Der eigene Zähler müsste an dieser Stelle eingetragen werden
    WHERE DEVICE  = 'WR_0_KSEM'
      AND READING = 'Active_energy+'

- Man kann natürlich die Zeiträume ändern, das Muster ist nur für den aktuellen Tag ausgelegt

-- INSERT INTO history (TIMESTAMP, DEVICE, TYPE, READING, VALUE)


WITH price AS (
  SELECT
    TIMESTAMP AS interval_start,
    VALUE AS price_ct_per_kWh   -- z.B. Cent/kWh
  FROM history
  WHERE DEVICE = 'Stromboerse_connect'
    AND READING = 'fc0_total'
    AND TIMESTAMP >= CURDATE()
    AND TIMESTAMP < CURDATE() + INTERVAL 1 DAY
),

consumption AS (
  SELECT
    interval_start,
    CASE
      WHEN interval_start = CURDATE() THEN 0
      WHEN VALUE < LAG(VALUE) OVER (ORDER BY interval_start) THEN NULL
      ELSE VALUE - LAG(VALUE) OVER (ORDER BY interval_start)
    END AS kWh_15min
  FROM (
    SELECT
      FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(TIMESTAMP) / 900) * 900) AS interval_start,
      VALUE,
      ROW_NUMBER() OVER (
        PARTITION BY FLOOR(UNIX_TIMESTAMP(TIMESTAMP) / 900)
        ORDER BY TIMESTAMP DESC
      ) AS rn
    FROM history
    WHERE DEVICE  = 'WR_0_KSEM'
      AND READING = 'Active_energy+'
      AND TIMESTAMP >= CURDATE() - INTERVAL 15 MINUTE
  ) t
  WHERE rn = 1
    AND interval_start >= CURDATE()
    AND interval_start < CURDATE() + INTERVAL 1 DAY
)

SELECT *
FROM (
  SELECT
    c.interval_start                   AS TIMESTAMP,
    'Stromboerse_connect'              AS DEVICE,
    'cost'                             AS TYPE,
    'nodes_cost'                       AS READING,
ROUND(COALESCE(c.kWh_15min,0) * COALESCE(p.price_ct_per_kWh,0) / 100, 6 ) AS VALUE
  FROM consumption c
LEFT JOIN price p
       ON p.interval_start = c.interval_start
) AS new_values

-- ON DUPLICATE KEY UPDATE
--   VALUE = new_values.VALUE;

VG   Christian
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

ch.eick

#16
Hallo zusammen,
die nächste Version ist da und liegt im contrib.
Bitte denkt daran für die Kostenberechnung Euren letzten Jahresverbrauch einzutragen, da darüber die Grundkosten auf Preis/kWh umgerechnet werden.

- Fehler Korrekturen

- Code Optimierungen

- SQL Optimierungen
  Es wurde versucht einzelne INSERT jeweils zu massen INSERT umzustellen, was performanter ist

- Kostenberechnung bei Tibber ohne Vertrag
  Hier wird ein lokales SmartMeter oder ein Lesekopf auf dem Zähler benötigt
  Das SmartMeter wird in den Attributen definiert
  Die Berechnung erfolgt in der Datenbank und wird im Anschluss auch als readings im Device angezeigt

- Änderung beim fc_max zu fc_max1 und fc_max2
  In den letzten Tagen gab es Abends jeweils einen extrem hohen Maximalpreis, der dem fc_trigger_max_price
  unverhältnismäßeg nach eben gerissen hat. Mit der Umstellung gibt es nun auch noch den zweit höchsten Preis,
  der bei einer Abweichung von 20% zum fc_max1 genommen wird. Hierdurch wird das fc0_trigger_max Fenster größer.

- Beim fc0_trigger_*_stop wurde leider eine viertel Stunde zu spät gestoppt, was jetzt korrigiert ist.

Beim Device Stromboerse muss man auch etwas korrigieren und ich habe mal ganz unten das lokale SmartMeter eingeblendet.
Du darfst diesen Dateianhang nicht ansehen.

Generell war ich auch die wichtigen letzten Wintermonate ziemlich enttäuscht von den Preisen. Natürlich waren sie hoch, wie schon lange.
Wichtiger jedoch waren die Schwankungen im Winter nicht so stark, dass sich ein Laden des Speichers gelohnt hätte, was ich in der Simulation auf 20% unter dem Maximalpreis eingestellt hatte. Auch ein Verschieben meiner Starkverbraucher hätte in Summe nicht soviel gesparrt, wie mich Tibber gekostet hätte. Ich scheine wohl ein sehr gutes EVU gepaart mit einer größeren PV zu haben.
Gedanklich werde ich mich mal mit dem Modul 1+3 des § 14a EnWG beschäftigen und testen, ob man über die Netzgebühren etwas sparen kann.

VG  Christian
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

rabehd

#17
Ich habe es auch mal ausprobiert.

Zitatnodes_consumption_day  Please define LogDBRep_Stromboerse_connect_SQL first
nodes_consumption_month  Please define LogDBRep_Stromboerse_connect_SQL first
nodes_consumption_year  Please define LogDBRep_Stromboerse_connect_SQL first
nodes_cost_avg  Please define LogDBRep_Stromboerse_connect_SQL first
nodes_cost_max  Please define LogDBRep_Stromboerse_connect_SQL first
nodes_cost_min  Please define LogDBRep_Stromboerse_connect_SQL first
total_cost_day  Please define LogDBRep_Stromboerse_connect_SQL first
total_cost_month  Please define LogDBRep_Stromboerse_connect_SQL first
total_cost_year  Please define LogDBRep_Stromboerse_connect_SQL first

Was habe ich in der hier übersehen?
Auch funktionierende Lösungen kann man hinterfragen.

ch.eick

In meinem contrib findest Du noch ein DbRep Device, das Du entsprechend als LogDBRep_Stromboerse_connect_SQL benennen musst. Damit werden aus der DbLog Werte geholt und geschrieben.

VG Christian
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

ch.eick

Besonders wichtig für die Berechnung sind Eure Preise des EVU und Netzbetreiber, die sollten in den Abrechnungen oder im Internet zufinden sein.
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

rabehd

Zitat von: ch.eick am 19 März 2026, 20:41:18In meinem contrib findest Du noch ein DbRep Device, das Du entsprechend als LogDBRep_Stromboerse_connect_SQL benennen musst. Damit werden aus der DbLog Werte geholt und geschrieben.

VG Christian

Danke, hatte ich nicht für notwendig erachtet.

Ist das auch ein Versäumnis von mir?
ZitatDBD::MariaDB::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'AS new ON DUPLICATE KEY UPDATE VALUE = new.VALUE' at line 1 at ./FHEM/93_DbRep.pm line 7073.
Auch funktionierende Lösungen kann man hinterfragen.

ch.eick

#21
Zitat von: rabehd am 20 März 2026, 09:22:13Ist das auch ein Versäumnis von mir?
ZitatDBD::MariaDB::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'AS new ON DUPLICATE KEY UPDATE VALUE = new.VALUE' at line 1 at ./FHEM/93_DbRep.pm line 7073.
Sorry, ich verwende die original Oracle MySQL Datenbank.
Du könntest gerne den MySQL Syntax auf MariaDB anpassen, eventuell hiltft ChatGBT auch einwenig.
MariaDB ist wohl etwas schlanker und hat nicht alle aktuellen Änderungen von MySQL. Im original MySQL bekomme ich die aktuellen Sicherheitsänderungen, was mir wichtig ist.

Ich schau mal was ChatGBT für MariaDB als Kompatibel vorschlägt.

Beispiel vom userReadings

nodes_TIMESTAMP:current_price.* {
...
MySQL Version >8 Syntax zu MariaDB übersetzen

MYSQL V8

INSERT INTO history (TIMESTAMP, DEVICE, TYPE, READING, VALUE)
      WITH price AS (
        SELECT
          TIMESTAMP AS interval_start,
          VALUE AS price_ct_per_kWh
        FROM history
        WHERE DEVICE = '$name'
          AND READING = 'fc0_total'
          AND TIMESTAMP >= CURDATE()
          AND TIMESTAMP < CURDATE() + INTERVAL 1 DAY
      ),
     
      consumption AS (
        SELECT
          interval_start,
          CASE
            WHEN interval_start = CURDATE() THEN 0
            WHEN VALUE < LAG(VALUE) OVER (ORDER BY interval_start) THEN NULL
            ELSE VALUE - LAG(VALUE) OVER (ORDER BY interval_start)
          END AS kWh_15min
        FROM (
          SELECT
            FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(TIMESTAMP) / 900) * 900) AS interval_start,
            VALUE,
            ROW_NUMBER() OVER (
              PARTITION BY FLOOR(UNIX_TIMESTAMP(TIMESTAMP) / 900)
              ORDER BY TIMESTAMP DESC
            ) AS rn
          FROM history
          WHERE DEVICE  = '$conf_Netz_Zaehler'
            AND READING = '$conf_Netz_Zaehler_Reading'
            AND TIMESTAMP >= CURDATE() - INTERVAL 2 DAY
        ) t
        WHERE rn = 1
          AND interval_start >= CURDATE()
          AND interval_start < CURDATE() + INTERVAL 1 DAY
      )
     
      SELECT *
      FROM (
        SELECT
          c.interval_start                   AS TIMESTAMP,
          '$name'              AS DEVICE,
          'cost'                             AS TYPE,
          'nodes_cost'                       AS READING,
      ROUND(COALESCE(c.kWh_15min,0) * COALESCE(p.price_ct_per_kWh,0) / 100, 6 ) AS VALUE
        FROM consumption c
      LEFT JOIN price p
             ON p.interval_start = c.interval_start
      ) AS new
      WHERE TIMESTAMP >= CURDATE()
      ON DUPLICATE KEY UPDATE
        VALUE = new.VALUE;



Dein MySQL-8 Statement ist syntaktisch völlig korrekt – aber genau diese Kombination ist in MariaDB oft problematisch:

❗ INSERT ... WITH ... SELECT ... ON DUPLICATE KEY UPDATE
→ funktioniert je nach MariaDB-Version nicht zuverlässig



🔧 Saubere MariaDB-kompatible Lösung

Die stabilste Variante ist:
👉 CTE in ein Derived Table (Subquery) kapseln

INSERT INTO history (TIMESTAMP, DEVICE, TYPE, READING, VALUE)
SELECT *
FROM (
    WITH price AS (
        SELECT
            TIMESTAMP AS interval_start,
            VALUE AS price_ct_per_kWh
        FROM history
        WHERE DEVICE = '$name'
          AND READING = 'fc0_total'
          AND TIMESTAMP >= CURDATE()
          AND TIMESTAMP < CURDATE() + INTERVAL 1 DAY
    ),

    consumption AS (
        SELECT
            interval_start,
            CASE
                WHEN interval_start = CURDATE() THEN 0
                WHEN VALUE < LAG(VALUE) OVER (ORDER BY interval_start) THEN NULL
                ELSE VALUE - LAG(VALUE) OVER (ORDER BY interval_start)
            END AS kWh_15min
        FROM (
            SELECT
                FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(TIMESTAMP) / 900) * 900) AS interval_start,
                VALUE,
                ROW_NUMBER() OVER (
                    PARTITION BY FLOOR(UNIX_TIMESTAMP(TIMESTAMP) / 900)
                    ORDER BY TIMESTAMP DESC
                ) AS rn
            FROM history
            WHERE DEVICE  = '$conf_Netz_Zaehler'
              AND READING = '$conf_Netz_Zaehler_Reading'
              AND TIMESTAMP >= CURDATE() - INTERVAL 2 DAY
        ) t
        WHERE rn = 1
          AND interval_start >= CURDATE()
          AND interval_start < CURDATE() + INTERVAL 1 DAY
    )

    SELECT
        c.interval_start AS TIMESTAMP,
        '$name'          AS DEVICE,
        'cost'           AS TYPE,
        'nodes_cost'     AS READING,
        ROUND(
            COALESCE(c.kWh_15min,0) * COALESCE(p.price_ct_per_kWh,0) / 100,
            6
        ) AS VALUE
    FROM consumption c
    LEFT JOIN price p
           ON p.interval_start = c.interval_start
) AS new
WHERE TIMESTAMP >= CURDATE()

ON DUPLICATE KEY UPDATE
    VALUE = new.VALUE;



⚠️ Warum das nötig ist

MariaDB hat hier mehrere Eigenheiten:

CTE + INSERT + UPDATE Kombination buggy

ON DUPLICATE KEY UPDATE kann Probleme mit CTE-Aliasen machen

Window Functions (LAG, ROW_NUMBER) sind ok – aber nur sauber in Subqueries



💡 Alternative (noch robuster)

Falls du auf Nummer sicher gehen willst (z. B. ältere MariaDB):

👉 CTE komplett vermeiden → nur Subqueries verwenden

Das wird länger, aber 100 % kompatibel.



🧠 Kurzfazit

Dein MySQL-8 Code = ✔️ korrekt

Für MariaDB:

❌ Direkt übernehmen → oft Fehler

✅ In SELECT FROM (WITH ...) AS new kapseln → stabil


⚠️ Wichtige Unterschiede MySQL vs. MariaDB

MySQL 8: INSERT ... WITH ... SELECT direkt möglich

MariaDB:

CTE (WITH) erst ab 10.2

Stabil mit INSERT erst ab 10.3+

Reihenfolge manchmal empfindlicher
In solch einer Session sollte die Umsetzung recht gut funktionieren. Es würde mich sehr freuen, wenn Du mir das dann getestet zusendest, dann würde ich im contrib zusätzlich das userReadings für MariaDB ablegen.


VG  Christian
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

ch.eick

By the way...

Wenn Ihr kein Tibber-Live verwenden möchtet, könntet Ihr einfach die userReadings davon entfernen und bei Bedarf später wieder mit der aktuellsten Version einsteigen.
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick

rabehd

#23
Zitat von: ch.eick am 20 März 2026, 10:02:50Es würde mich sehr freuen, wenn Du mir das dann getestet zusendest, dann würde ich im contrib zusätzlich das userReadings für MariaDB ablegen.

Ich habe alle Vorkommen ersetzt und keine Fehlermeldung mehr.

Aus:
AS new
WHERE TIMESTAMP >= CURDATE()
ON DUPLICATE KEY UPDATE
VALUE = new.VALUE;
wird:

WHERE TIMESTAMP >= CURDATE()
ON DUPLICATE KEY UPDATE
VALUE = VALUES(VALUE);
--AS new muss auch weg
Auch funktionierende Lösungen kann man hinterfragen.

ch.eick

Hallo rabehd,
in MySQL > V8 ist dieser Syntax bereits abgekündigt :-) , aber gut wenn es dann in MariaDB läuft.

Läuft der Rest mit dem "with" dann auch?
Welche MariaDb Version müsste dann verwendet werden?
RPI4; Docker; CUNX; Eltako FSB61NP; SamsungTV H-Serie; Sonos; Vallox; Luxtronik; 3x FB7490; Stromzähler mit DvLIR; wunderground; Plenticore 10 mit BYD; EM410; SMAEM; Modbus TCP
Contrib: https://svn.fhem.de/trac/browser/trunk/fhem/contrib/ch.eick