Tibber-1/4-Stunden-Integration

Fragen zur Nutzung, Features, usw..
Antworten
gvz
Beiträge: 73
Registriert: So Sep 12, 2021 8:28 am
Wohnort: Grevenbroich

Tibber-1/4-Stunden-Integration

Beitrag von gvz »

Moin,
Wer auch immer die GUI-Integration von Tibber (und Co.) gemacht hat: Was für eine saubere Arbeit!
Ich habe immer noch nicht den Hintern zur Umstellung auf 2.1 hochgekriegt (wenngleich heute die SD-Karte bestellt).
Um von der heutigen Umstellung der dynamischen Stromtarife in Deutschland auf 15-Minuten-Takt zu profitieren, musste ich sehr wenig anpassen.
Unten der diff für diejenigen, die Shell-Access zu ihrer Box haben:

Code: Alles auswählen

diff --git a/modules/et_tibber/stromtarifinfo/infopage.php b/modules/et_tibber/stromtarifinfo/infopage.php
index ff344573..cba90029 100755
--- a/modules/et_tibber/stromtarifinfo/infopage.php
+++ b/modules/et_tibber/stromtarifinfo/infopage.php
@@ -230,13 +230,13 @@
                                        initialDataRead = false;
                                        var tibberQueryHead = '{ "query": "{viewer {name home(id:\\"' + tibberHomeID + '\\") {';
                                        var tibberQueryGetAdress = 'address {address1 postalCode city} ';
-                                       var tibberQueryGetPriceInfo = 'currentSubscription {priceInfo {current{total energy tax startsAt} today {total startsAt} tomorrow {total startsAt}}} ';
+                                       var tibberQueryGetPriceInfo = 'currentSubscription {priceInfo(resolution: QUARTER_HOURLY) {current{total energy tax startsAt} today {total startsAt} tomorrow {total startsAt}}} ';
                                } else {
                                        var tibberQueryHead = '{ "query": "{viewer {home(id:\\"' + tibberHomeID + '\\") {';
                                        var tibberQueryGetAdress = '';
                                        var tibberQueryGetPriceInfo = '';
                                }
-                               var tibberQueryGetHourlyConsumption = 'cons_hourly: consumption(resolution: HOURLY, after:\\"' + timeStringBase64 + '\\", first: 25) {nodes {from to cost unitPrice unitPriceVAT consumption}}';
+                               var tibberQueryGetHourlyConsumption = 'cons_hourly: consumption(resolution: HOURLY, after:\\"' + timeStringBase64 + '\\", first: 100) {nodes {from to cost unitPrice unitPriceVAT consumption}}';
                                var tibberQueryTail = '}}}" }';
                                var tibberQuery = tibberQueryHead + tibberQueryGetAdress + tibberQueryGetPriceInfo + tibberQueryGetHourlyConsumption + tibberQueryTail;
 
diff --git a/modules/et_tibber/tibbergetprices.py b/modules/et_tibber/tibbergetprices.py
index dd247139..3450d55f 100755
--- a/modules/et_tibber/tibbergetprices.py
+++ b/modules/et_tibber/tibbergetprices.py
@@ -108,7 +108,8 @@ def _exit_on_invalid_price_data(error, current_module_name):
         file_current_price.write(str(_failure_price) + '\n')
         file_pricelist.write('%s\n' % current_module_name)  # erster Eintrag ist für Preisliste verantwortliches Modul
         now = datetime.now(timezone.utc)  # timezone-aware datetime-object in UTC
-        timestamp = now.replace(minute=0, second=0, microsecond=0)  # volle Stunde
+        quarter_hour = (now.minute // 15) * 15
+        timestamp = now.replace(minute=quarter_hour, second=0, microsecond=0)  # volle Stunde
         for i in range(9):
             file_pricelist.write('%d, %f\n' % (timestamp.timestamp(), _failure_price))
             timestamp = timestamp + timedelta(hours=1)
@@ -178,7 +179,7 @@ def _try_api_call(max_tries=3, delay=5, backoff=2, exceptions=(Exception,), hook
 @_try_api_call()
 def _readAPI(token, id):
     headers = {'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json'}
-    data = '{ "query": "{viewer {home(id:\\"' + id + '\\") {currentSubscription {priceInfo {today {total startsAt} tomorrow {total startsAt}}}}}}" }'
+    data = '{ "query": "{viewer {home(id:\\"' + id + '\\") {currentSubscription {priceInfo(resolution: QUARTER_HOURLY) {today {total startsAt} tomorrow {total startsAt}}}}}}" }'
     # Timeout für Verbindung = 2 sek und Antwort = 6 sek
     response = requests.post('https://api.tibber.com/v1-beta/gql', headers=headers, data=data, timeout=(2, 6))
     return response
@@ -191,34 +192,36 @@ def _get_utcfromtimestamp(timestamp):
     return datetime_obj
 
 def _cleanup_pricelist(pricelist):
-    # bereinigt sortierte Preisliste, löscht Einträge die älter als aktuelle Stunde sind
+    # bereinigt sortierte Preisliste, löscht Einträge die älter als aktuelle Viertelstunde sind
     # und über morgen hinausgehen
-    # wenn der erste Preis nicht für die aktuelle Stunde ist, wird leere Liste zurückgegeben
+    # wenn der erste Preis nicht für die aktuelle Viertelstunde ist, wird leere Liste zurückgegeben
     # prüft auf Abstand der Preise: ist dieser >1h, wird Liste ab diesem Punkt abgeschnitten
     if len(pricelist) > 0:
         now = datetime.now(timezone.utc)  # timezone-aware datetime-object in UTC
-        now_full_hour = now.replace(minute=0, second=0, microsecond=0)  # volle Stunde
-        now_full_hour_timestamp = datetime.timestamp(now_full_hour)
+        # Runde Minuten auf die nächste Viertelstunde (0, 15, 30, 45)
+        quarter_hour = (now.minute // 15) * 15
+        now_quarter_hour = now.replace(minute=quarter_hour, second=0, microsecond=0)
+        now_quarter_hour_timestamp = datetime.timestamp(now_quarter_hour)
         # zuerst filtern auf "ab diese Stunde" bis "längstens morgen"
         for index, price in enumerate(pricelist[:]):  # über Kopie der Liste iterieren, um das Original zu manipulieren
             try:
                 starttime_utc = _get_utcfromtimestamp(float(price[0]))  # Start-Zeitstempel aus Preisliste umwandeln
             except:
                 raise TypeError('Zeitstempel-Umwandlung fehlgeschlagen') from None
-            # ältere als aktuelle Stunde und weiter als morgen löschen
-            if (float(price[0]) < now_full_hour_timestamp) or (starttime_utc.date() > now.date() + timedelta(days=1)):
+            # ältere als aktuelle Viertelstunde und weiter als morgen löschen
+            if (float(price[0]) < now_quarter_hour_timestamp) or (starttime_utc.date() > now.date() + timedelta(days=1)):
                 pricelist.remove(price)
-        # jetzt prüfen auf Start mit aktueller Stunde und Stundenabstände
+        # jetzt prüfen auf Start mit aktueller Viertelstunde und Viertelstundenabstände
         if len(pricelist) > 0:
             timestamp_prev = float(pricelist[0][0])  # erster Listeneintrag
             starttime_utc = _get_utcfromtimestamp(float(pricelist[0][0]))
-            if _get_utcfromtimestamp(timestamp_prev) == now_full_hour:  # erster Preis ist der von aktueller Stunde
+            if _get_utcfromtimestamp(timestamp_prev) == now_quarter_hour:  # erster Preis ist der von aktueller Stunde
                 for index, price in enumerate(pricelist[:]):  # über Kopie der Liste iterieren, um das Original zu manipulieren
                     if index > 0:
                         timestamp = float(price[0])
                         secondsdiff = timestamp - timestamp_prev
                         timestamp_prev = float(price[0])
-                        if secondsdiff != 3600.0:  # ist Abstand <> 1h dann ab hier Liste löschen
+                        if secondsdiff != 900.0:  # ist Abstand <> 1h dann ab hier Liste löschen
                             del pricelist[index:]
                             break
             else:
@@ -262,7 +265,8 @@ def _get_updated_pricelist():
         _write_log_entry("Tibber-Preisliste extrahiert", 1)
         # alle Zeiten in UTC verarbeiten
         now = datetime.now(timezone.utc)  # timezone-aware datetime-object in UTC
-        now_full_hour = now.replace(minute=0, second=0, microsecond=0)  # volle Stunde
+        quarter_hour = (now.minute // 15) * 15
+        now_quarter_hour = now.replace(minute=quarter_hour, second=0, microsecond=0)  # volle Stunde
         _write_log_entry('Formatiere und analysiere Preisliste', 1)
         pricelist = []
         for price_data in sorted_marketprices:
Nach dem Einpflegen einmal /var/www/html/openWB/ramdisk/etprovidergraphlist löschen, damit die Preise neu gezogen werden - that's it!
OpenWB S2 (Touchscreen, RFID, Zähler, 11kW), 10 kWp PV ohne Speicher, ID.3
Benutzeravatar
Thomas aus W
Beiträge: 1031
Registriert: Mi Apr 01, 2020 4:00 pm
Has thanked: 87 times
Been thanked: 32 times

Re: Tibber-1/4-Stunden-Integration

Beitrag von Thomas aus W »

gvz hat geschrieben: Mi Okt 01, 2025 5:33 pm Um von der heutigen Umstellung der dynamischen Stromtarife in Deutschland auf 15-Minuten-Takt zu profitieren, musste ich sehr wenig anpassen.
Danke.

Aber erwarte nicht, dass Zielladen damit weiterhin korrekt funktioniert...

bye
TT
Antworten