Skip to content

Conversation

@tpd-opitz
Copy link
Contributor

@tpd-opitz tpd-opitz commented Sep 30, 2025

  • Tibber

    • Afrage für Tibber auf 15-Mituten Auflösung umgestellt
  • alle ET Provider

    • Abfragehäufigkeit für alle Provider auf ein mal am Tag begrenzt
    • zufälliger Abfragezeitpunkt zwischen 13:30 und 14:00 zur Entlastung der ET-Provider-Resourcen
    • Wiederholung der Abfrage wenn Daten noch nicht verfügbar
    • Wiederholung der Abfrage bei Abfragefehler
    • Reduzierung der Preisliste so dass der erste Eintrag die aktuelle Zeit beinhaltet
    • sofortiger Abruf bei Anbieterwechsel
  • Backend

    • minimal 5Min Auflösung (Intervall für Reduzierung der Preisliste)
    • Ladezeitenberechnung für Eco-Modus
      • unterstützt beliebige Auflösungen bei den Preisdaten, solange alle Einträge in der Liste die selbe Gültigkeitsdauer haben.
      • Ladeziel wird spätest möglich erreicht
      • Laden beginnt ggf. im aktuellen Zeitfenster
  • Frontend

    • Zeitpunkt der nächsten Abfrage wird im Status angezeigt
    • Meldung über zu wenige Einträge berücksichtigt (beliebige) Auflösung der Preisdaten

@benderl
Copy link
Contributor

benderl commented Sep 30, 2025

Den Diagrammen ist es ziemlich egal, in welchem Abstand die Werte sind. Es wird eine Zeitachse verwendet und darauf die Timestamps abgebildet.

Eine Anpassung im Status oder den offiziellen Themes ist daher zu 99% nicht erforderlich.

@tpd-opitz
Copy link
Contributor Author

tpd-opitz commented Sep 30, 2025

Den Diagrammen ist es ziemlich egal, in welchem Abstand die Werte sind. Es wird eine Zeitachse verwendet und darauf die Timestamps abgebildet.

Eine Anpassung im Status oder den offiziellen Themes ist daher zu 99% nicht erforderlich.

stimmt...
image

aber dafür habe ich das hier auskommentiert:
./web/et_tibber/tibbergetprices.py"

210                 pricelist.remove(price)$
211 #        # jetzt prüfen auf Start mit aktueller Stunde und Stundenabstände$
212 #        if len(pricelist) > 0:$
213 #            timestamp_prev = float(pricelist[0][0])  # erster Listeneintrag$
214 #            starttime_utc = _get_utcfromtimestamp(float(pricelist[0][0]))$
215 #            if _get_utcfromtimestamp(timestamp_prev) == now_full_hour:  # erster Preis ist der von aktueller Stunde    $
216 #                for index, price in enumerate(pricelist[:]):  # über Kopie der Liste iterieren, um das Original zu     manipulieren$
217 #                    if index > 0:$
218 #                        timestamp = float(price[0])$
219 #                        secondsdiff = timestamp - timestamp_prev$
220 #                        timestamp_prev = float(price[0])$
221 #                        if secondsdiff != 3600.0:  # ist Abstand <> 1h dann ab hier Liste löschen$
222 #                            del pricelist[index:]$
223 #                            break$
224 #            else:$
225 #                return []$
226         return pricelist$

@tpd-opitz
Copy link
Contributor Author

@benderl : muss ich mich wirklich um den timeout-Fehler kümmern?

@benderl
Copy link
Contributor

benderl commented Sep 30, 2025

Welchen Timeout Fehler? Ich sehe aktuell nur einen fehlgeschlagenen Test im Tibber Modul. Da passt das erwartete Ergebnis nicht.

@tpd-opitz tpd-opitz marked this pull request as ready for review September 30, 2025 18:21
@tpd-opitz

This comment was marked as resolved.

@benderl
Copy link
Contributor

benderl commented Oct 1, 2025

mit primitiven Datentypen rechnen ist keine so tolle Idee, wenn's um Geld geht...

Hast Du einen besseren Vorschlag?

@benderl
Copy link
Contributor

benderl commented Oct 1, 2025

aber dafür habe ich das hier auskommentiert: ./web/et_tibber/tibbergetprices.py"

210                 pricelist.remove(price)$
211 #        # jetzt prüfen auf Start mit aktueller Stunde und Stundenabstände$
212 #        if len(pricelist) > 0:$
213 #            timestamp_prev = float(pricelist[0][0])  # erster Listeneintrag$
214 #            starttime_utc = _get_utcfromtimestamp(float(pricelist[0][0]))$
215 #            if _get_utcfromtimestamp(timestamp_prev) == now_full_hour:  # erster Preis ist der von aktueller Stunde    $
216 #                for index, price in enumerate(pricelist[:]):  # über Kopie der Liste iterieren, um das Original zu     manipulieren$
217 #                    if index > 0:$
218 #                        timestamp = float(price[0])$
219 #                        secondsdiff = timestamp - timestamp_prev$
220 #                        timestamp_prev = float(price[0])$
221 #                        if secondsdiff != 3600.0:  # ist Abstand <> 1h dann ab hier Liste löschen$
222 #                            del pricelist[index:]$
223 #                            break$
224 #            else:$
225 #                return []$
226         return pricelist$

Wo soll die Datei liegen? Ich finde die aktuell nicht.

@tpd-opitz

This comment was marked as outdated.

@tpd-opitz
Copy link
Contributor Author

mit primitiven Datentypen rechnen ist keine so tolle Idee, wenn's um Geld geht...

Hast Du einen besseren Vorschlag?

Decimal wäre das Mittel der Wahl, aber das nachträglich einzuziehen dürfte aufwendig sein...

@tpd-opitz
Copy link
Contributor Author

Puhhh.. Geschafft...
Zielladen funktioniert mit viertelstündlichen Preisen von Tibber.

TODO: viertelstündliches Update des MQTT, damit der aktuelle Preis ins Frontend kommt...

@LKuemmel
Copy link
Contributor

LKuemmel commented Oct 8, 2025

Warte bitte erstmal mit weiteren Implmentierungen an deiner Lösung. Mit dieser legst Du dich auf 60 und 15 Min fest. Die Info, ob die Preise alle 15 oder 60 Min übermittelt werden, sollte nur im Stromtarif-Modul relevant sein und keine Abhängigkeit von den Stromtarifmodulen ins Ziel- und Ecoladen ziehen. Die Preise werden mit Timestamps übermittelt und sind bis zum folgenden Timestamp gültig, das sollte als Info ausreichen.

@tpd-opitz
Copy link
Contributor Author

tpd-opitz commented Oct 8, 2025

Die Info, ob die Preise alle 15 oder 60 Min übermittelt werden, sollte nur im Stromtarif-Modul relevant sein und keine Abhängigkeit von den Stromtarifmodulen ins Ziel- und Ecoladen ziehen.

Im Zielladen (Option.get_loading_hours()) wird die Länge der übermittelten Zeit-Intervalle berechnet und nicht übergeben. da wurde keine neue Abhängigkeit eingefügt.
Beim Eco-Laden habe ich nichts geändert aber es funktioniert out of the box...

Das neue Property in TariffState ist in erster Linie für die Info-Meldung im Status um dort die Anzahl der Einträge für einen Tag zu berechnen. Das könnte man auch so wie beim Zielladen machen, aber die Abhängigkeit zu TariffState existiert hier sowieso schon...

Die einzige Stelle im Backed, die tatsächlich auf 60/14 Minuten festgelegt ist ist timecheck mit den create_unix_timestamp_current_*_hour() Funktionen. Die müssten dahin gehend verallgemeinert werden, dass es nur eine Funktion gibt, die die Länge eines Preis-Zeit-Slots übergeben bekommt...
(+ Aufruf in Optional anpassen...)

Comment on lines 234 to 245
def create_unix_timestamp_current_full_hour() -> int:
full_hour = datetime.datetime.fromtimestamp(create_timestamp()).strftime("%m/%d/%Y, %H")
return int(datetime.datetime.strptime(full_hour, "%m/%d/%Y, %H").timestamp())


def create_unix_timestamp_current_quarter_hour() -> int:
def round_to_quarter_hour(current_time: float, quarter_hour: int = 900) -> float:
log.debug(f"current time: {current_time} => modified: {current_time - (current_time % quarter_hour)}")
return current_time - (current_time % quarter_hour)
return int(round_to_quarter_hour(datetime.datetime.today().timestamp()))


Copy link
Contributor Author

@tpd-opitz tpd-opitz Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LKuemmel was hältst Du davon?

Suggested change
def __calculate_price_timeslot_length(self, prices: dict[str, int]) -> int:
first_timestamps = sorted(list(prices.keys()))[:2]
return int(first_timestamps[1]) - int(first_timestamps[0])
def select_unix_timestamp_current_price_slot(prices: dict[str, float]) -> int:
now = int(create_timestamp())
price_timeslot_seconds = self.__calculate_price_timeslot_length(prices)
return next(iter( [int(timestamp) for timestamp in list(prices.keys()) if (
int(timestamp) <= now and int(timestamp) >= now - price_timeslot_seconds
)]))

@tpd-opitz tpd-opitz force-pushed the feature/tibber/quarter-hourly-prices branch from 8ebfdfa to 939a371 Compare October 8, 2025 19:25
@tpd-opitz
Copy link
Contributor Author

Erledigt ist:

  • Abfrage gegen Tibber angepasst
  • Abfrage der API (aller ET-Provider) nur ein mal am Tag 14:00 +- 7Minuten
  • Aktualisierung der Strompreise im Backend alle 5 Minuten -> Frontendaktualisierung implizit auch
  • Zielladen errechnet benötigte Anzahl der Zeitslots aus den Zeitstempeln der Strompreisdaten
    • know issue: Strompreislisten mit variablen Gültigkeitsdauern (octopus intelligent go?)
  • fix Zielladen
    • wenn der erste Zeitslot ausgewählt, aber schon weit abgelaufen muss ggf. ein weiterer Zeitslot ausgewählt werden, dammit genügend lange geladen wird

@tpd-opitz tpd-opitz force-pushed the feature/tibber/quarter-hourly-prices branch 13 times, most recently from a60fcde to 3ef43ec Compare October 12, 2025 18:45
@tpd-opitz
Copy link
Contributor Author

Den "Wechsel" zur Uhrzeit 0:00 finde ich sehr überflüssig, m.E. erwarten fast alle Nutzer, dass man etwas einstellt und es dann - ohne Reboot - sofort live ist. Die Leute würden doch auch "verrückt", käme jemand auf die Idee, eine Änderung des Lademodus auf einen Zeitpunkt X zu legen.

Dabei ging es mir nicht um den Lademodus, sondern wann der neue Traif, also die Preisliste des neuen Tarifs aktiv wird.
Wenn jemand zum Beispiel vom Tibber zu octopus wechselt bekommt er ja ganz andere Preis vorhersagen.

@tpd-opitz tpd-opitz force-pushed the feature/tibber/quarter-hourly-prices branch from 59b23ac to 81fe394 Compare November 1, 2025 22:15
Copy link
Contributor

@LKuemmel LKuemmel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ein paar kleiner Anmerkungen und eine Lösung für die globalen Variablen, die in einem der letzten Commits reingekommen sind.

Comment on lines +17 to +22
'''
next_query_time and internal_tariff_state are defined outside of class ConfigurableElectricityTariff because
for an unknown reason defining them as a class variable does not keep their values.
'''
next_query_time: datetime = datetime.fromtimestamp(1)
internal_tariff_state: TariffState = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Die globalen Variablen sind ein architektonischer Fallstrick und können da nicht bleiben.
Vorschlag: Die Verantwortlichkeit wird aufgeteilt: ConfigurableElectricityTariff ist nur für das Abfragen und Publishen der Preisliste zuständig, et_get_prices prüft, ob optional eine neue Preisliste braucht und ruft dann die update-Methode auf. et_get_prices hat ohnehin die Information, bis wann die letzte Preisliste geht.
Wenn im Fall einer Warnung die Preisliste nicht aktualisiert werden soll, wird die self.store.update() einfach nicht aufgerufen und die letzte, abgerufene Preisliste bleibt im Broker stehen.
Dann sind die beiden globalen Variablen obsolet und die Entscheidung, wird dort getroffen, wo die Preisliste vorhanden ist und die Daten nicht über den Code verstreut.

Copy link
Contributor Author

@tpd-opitz tpd-opitz Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dann sind die beiden globalen Variablen obsolet und die Entscheidung, wird dort getroffen, wo die Preisliste vorhanden ist und die Daten nicht über den Code verstreut.

Das sehe ich genau anders herum, denn "wo die Preisliste vorhanden ist" ist ja in den einzelnen ET-Provider-Modulen also muss da jedes ET-Provider Modul selbst das Caching implementieren, und dass bedeutet für mich "über den Code verstreut". Ja, ich bin auch kein Freund von globalen Variablen, aber weil der in anderen Programmiersprachen übliche Weg über Objektvariablen in Python offenbar nicht funktioniert ist das für mich das kleiner Übel.

Wenn das OWB Team aber darauf besteht werde ich das noch ändern, damit dieser PR aber bald möglichst life gehen kann würde ich das in einem neuen PR machen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reden wir von der gleichen Stelle? Ich würde hier prüfen, ob die Preisliste aktualisiert werden muss: https://github.com/tpd-opitz/openwb-core/blob/d9e3006a947d2cf9a64255b5738bb3b5054ba7bb/packages/control/optional.py#L204
Das ist unabhängig vom Provider-Modul und in self.data.et.get.prices ist die letzte Preisliste vorhanden.

Copy link
Contributor Author

@tpd-opitz tpd-opitz Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reden wir von der gleichen Stelle? Ich würde hier prüfen, ob die Preisliste aktualisiert werden muss: https://github.com/tpd-opitz/openwb-core/blob/d9e3006a947d2cf9a64255b5738bb3b5054ba7bb/packages/control/optional.py#L204 Das ist unabhängig vom Provider-Modul und in self.data.et.get.prices ist die letzte Preisliste vorhanden.

Diese Stelle wird "nur" der Thread zum Update gestartet, wenn der schon existiert passiert nichts. Zudem hätte ich hier das selbe Problem mit den nicht persistenten Objektvariablen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ich habe hier mal einen Entwurf gemacht: #2897

@tpd-opitz tpd-opitz force-pushed the feature/tibber/quarter-hourly-prices branch from 81fe394 to 0e6f195 Compare November 3, 2025 16:21
@tpd-opitz tpd-opitz force-pushed the feature/tibber/quarter-hourly-prices branch from 0e6f195 to d9e3006 Compare November 3, 2025 16:37
@tpd-opitz tpd-opitz requested review from LKuemmel and benderl November 3, 2025 16:52
@LKuemmel LKuemmel merged commit 17f5cbf into openWB:master Nov 4, 2025
1 check passed
@tpd-opitz tpd-opitz deleted the feature/tibber/quarter-hourly-prices branch November 4, 2025 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants