diff --git a/custom_components/tibber_prices/custom_translations/de.json b/custom_components/tibber_prices/custom_translations/de.json index 1ca09b0..cad28b0 100644 --- a/custom_components/tibber_prices/custom_translations/de.json +++ b/custom_components/tibber_prices/custom_translations/de.json @@ -55,6 +55,36 @@ "long_description": "Zeigt den durchschnittlichen Preis pro kWh für den morgigen Tag von deinem Tibber-Abonnement an. Dieser Sensor wird nicht verfügbar, bis die Preise für morgen von Tibber veröffentlicht werden (typischerweise zwischen 13:00 und 14:00 Uhr MEZ).", "usage_tips": "Nutze dies als Grundlinie für den Vergleich mit den morgigen Preisen und zur Verbrauchsplanung. Vergleiche mit dem heutigen Durchschnitt, um zu sehen, ob morgen insgesamt teurer oder günstiger wird." }, + "yesterday_price_level": { + "description": "Aggregiertes Preisniveau für gestern", + "long_description": "Zeigt das aggregierte Preisniveau für alle Intervalle von gestern. Verwendet die gleiche Logik wie die Stundensensoren, um das Gesamtpreisniveau für den ganzen Tag zu ermitteln.", + "usage_tips": "Nutze dies, um die gestrige Preissituation zu verstehen. Vergleiche mit heute, um tägliche Trends zu sehen." + }, + "today_price_level": { + "description": "Aggregiertes Preisniveau für heute", + "long_description": "Zeigt das aggregierte Preisniveau für alle Intervalle von heute. Verwendet die gleiche Logik wie die Stundensensoren, um das Gesamtpreisniveau für den ganzen Tag zu ermitteln.", + "usage_tips": "Nutze dies, um die heutige Preissituation auf einen Blick zu verstehen. Hilfreich für schnelle Einschätzungen, ob heute generell günstig oder teuer ist." + }, + "tomorrow_price_level": { + "description": "Aggregiertes Preisniveau für morgen", + "long_description": "Zeigt das aggregierte Preisniveau für alle Intervalle von morgen. Verwendet die gleiche Logik wie die Stundensensoren, um das Gesamtpreisniveau für den ganzen Tag zu ermitteln. Dieser Sensor wird nicht verfügbar, bis die Preise für morgen von Tibber veröffentlicht werden (typischerweise zwischen 13:00 und 14:00 Uhr MEZ).", + "usage_tips": "Nutze dies, um die morgige Preissituation zu verstehen. Vergleiche mit heute, um zu sehen, ob morgen günstiger oder teurer für den Energieverbrauch wird." + }, + "yesterday_price_rating": { + "description": "Aggregierte Preisbewertung für gestern", + "long_description": "Zeigt die aggregierte Preisbewertung (niedrig/normal/hoch) für alle Intervalle von gestern, basierend auf deinen konfigurierten Schwellenwerten. Verwendet die gleiche Logik wie die Stundensensoren, um die Gesamtbewertung für den ganzen Tag zu ermitteln.", + "usage_tips": "Nutze dies, um die gestrige Preissituation relativ zu deinen persönlichen Schwellenwerten zu verstehen. Vergleiche mit heute für Trendanalysen." + }, + "today_price_rating": { + "description": "Aggregierte Preisbewertung für heute", + "long_description": "Zeigt die aggregierte Preisbewertung (niedrig/normal/hoch) für alle Intervalle von heute, basierend auf deinen konfigurierten Schwellenwerten. Verwendet die gleiche Logik wie die Stundensensoren, um die Gesamtbewertung für den ganzen Tag zu ermitteln.", + "usage_tips": "Nutze dies, um die heutige Preissituation relativ zu deinen persönlichen Schwellenwerten schnell einzuschätzen. Hilft bei Verbrauchsentscheidungen für den aktuellen Tag." + }, + "tomorrow_price_rating": { + "description": "Aggregierte Preisbewertung für morgen", + "long_description": "Zeigt die aggregierte Preisbewertung (niedrig/normal/hoch) für alle Intervalle von morgen, basierend auf deinen konfigurierten Schwellenwerten. Verwendet die gleiche Logik wie die Stundensensoren, um die Gesamtbewertung für den ganzen Tag zu ermitteln. Dieser Sensor wird nicht verfügbar, bis die Preise für morgen von Tibber veröffentlicht werden (typischerweise zwischen 13:00 und 14:00 Uhr MEZ).", + "usage_tips": "Nutze dies, um den morgigen Energieverbrauch basierend auf deinen persönlichen Preisschwellenwerten zu planen. Vergleiche mit heute, um zu entscheiden, ob du den Verbrauch auf morgen verschieben oder heute nutzen solltest." + }, "trailing_price_average": { "description": "Der durchschnittliche Strompreis für die letzten 24 Stunden pro kWh", "long_description": "Zeigt den durchschnittlichen Preis pro kWh berechnet aus den letzten 24 Stunden (nachlaufender Durchschnitt) von deinem Tibber-Abonnement an. Dies bietet einen gleitenden Durchschnitt, der alle 15 Minuten basierend auf historischen Daten aktualisiert wird.", diff --git a/custom_components/tibber_prices/custom_translations/en.json b/custom_components/tibber_prices/custom_translations/en.json index f7a46d3..6507669 100644 --- a/custom_components/tibber_prices/custom_translations/en.json +++ b/custom_components/tibber_prices/custom_translations/en.json @@ -55,6 +55,36 @@ "long_description": "Shows the average price per kWh for tomorrow from your Tibber subscription. This sensor becomes unavailable until tomorrow's data is published by Tibber (typically around 13:00-14:00 CET).", "usage_tips": "Use this as a baseline for comparing tomorrow's prices and planning consumption. Compare with today's average to see if tomorrow will be more or less expensive overall." }, + "yesterday_price_level": { + "description": "Aggregated price level for yesterday", + "long_description": "Shows the aggregated price level classification for all intervals in yesterday. Uses the same logic as hourly sensors to determine the overall price level for the entire day.", + "usage_tips": "Use this to understand yesterday's overall price situation. Compare with today to see daily trends." + }, + "today_price_level": { + "description": "Aggregated price level for today", + "long_description": "Shows the aggregated price level classification for all intervals in today. Uses the same logic as hourly sensors to determine the overall price level for the entire day.", + "usage_tips": "Use this to understand today's overall price situation at a glance. Helpful for quick assessments of whether today is generally cheap or expensive." + }, + "tomorrow_price_level": { + "description": "Aggregated price level for tomorrow", + "long_description": "Shows the aggregated price level classification for all intervals in tomorrow. Uses the same logic as hourly sensors to determine the overall price level for the entire day. This sensor becomes unavailable until tomorrow's data is published by Tibber (typically around 13:00-14:00 CET).", + "usage_tips": "Use this to understand tomorrow's overall price situation. Compare with today to see if tomorrow will be more or less favorable for energy consumption." + }, + "yesterday_price_rating": { + "description": "Aggregated price rating for yesterday", + "long_description": "Shows the aggregated price rating (low/normal/high) for all intervals in yesterday, based on your configured thresholds. Uses the same logic as hourly sensors to determine the overall rating for the entire day.", + "usage_tips": "Use this to understand yesterday's price situation relative to your personalized thresholds. Compare with today for trend analysis." + }, + "today_price_rating": { + "description": "Aggregated price rating for today", + "long_description": "Shows the aggregated price rating (low/normal/high) for all intervals in today, based on your configured thresholds. Uses the same logic as hourly sensors to determine the overall rating for the entire day.", + "usage_tips": "Use this to quickly assess today's price situation relative to your personalized thresholds. Helps make consumption decisions for the current day." + }, + "tomorrow_price_rating": { + "description": "Aggregated price rating for tomorrow", + "long_description": "Shows the aggregated price rating (low/normal/high) for all intervals in tomorrow, based on your configured thresholds. Uses the same logic as hourly sensors to determine the overall rating for the entire day. This sensor becomes unavailable until tomorrow's data is published by Tibber (typically around 13:00-14:00 CET).", + "usage_tips": "Use this to plan tomorrow's energy consumption based on your personalized price thresholds. Compare with today to decide if you should shift consumption to tomorrow or use energy today." + }, "trailing_price_average": { "description": "The average electricity price for the past 24 hours per kWh", "long_description": "Shows the average price per kWh calculated from the past 24 hours (trailing average) from your Tibber subscription. This provides a rolling average that updates every 15 minutes based on historical data.", diff --git a/custom_components/tibber_prices/custom_translations/nb.json b/custom_components/tibber_prices/custom_translations/nb.json index bbcb565..5671128 100644 --- a/custom_components/tibber_prices/custom_translations/nb.json +++ b/custom_components/tibber_prices/custom_translations/nb.json @@ -55,6 +55,36 @@ "long_description": "Viser gjennomsnittsprisen per kWh for morgendagen fra ditt Tibber-abonnement. Denne sensoren blir utilgjengelig inntil morgendagens data er publisert av Tibber (vanligvis rundt 13:00-14:00 CET).", "usage_tips": "Bruk dette som en baseline for å sammenligne morgendagens priser og planlegge forbruk. Sammenlign med dagens gjennomsnitt for å se om morgendagen vil være mer eller mindre dyr totalt sett." }, + "yesterday_price_level": { + "description": "Aggregert prisnivå for i går", + "long_description": "Viser det aggregerte prisnivået for alle intervaller i går. Bruker samme logikk som timesensorene for å bestemme det samlede prisnivået for hele dagen.", + "usage_tips": "Bruk dette for å forstå gårsdagens generelle prissituasjon. Sammenlign med i dag for å se daglige trender." + }, + "today_price_level": { + "description": "Aggregert prisnivå for i dag", + "long_description": "Viser det aggregerte prisnivået for alle intervaller i dag. Bruker samme logikk som timesensorene for å bestemme det samlede prisnivået for hele dagen.", + "usage_tips": "Bruk dette for å forstå dagens generelle prissituasjon på et øyeblikk. Nyttig for raske vurderinger av om i dag generelt er billig eller dyrt." + }, + "tomorrow_price_level": { + "description": "Aggregert prisnivå for i morgen", + "long_description": "Viser det aggregerte prisnivået for alle intervaller i morgen. Bruker samme logikk som timesensorene for å bestemme det samlede prisnivået for hele dagen. Denne sensoren blir utilgjengelig inntil morgendagens data er publisert av Tibber (vanligvis rundt 13:00-14:00 CET).", + "usage_tips": "Bruk dette for å forstå morgendagens generelle prissituasjon. Sammenlign med i dag for å se om morgendagen vil være mer eller mindre gunstig for energiforbruk." + }, + "yesterday_price_rating": { + "description": "Aggregert prisvurdering for i går", + "long_description": "Viser den aggregerte prisvurderingen (lav/normal/høy) for alle intervaller i går, basert på dine konfigurerte terskelverdier. Bruker samme logikk som timesensorene for å bestemme den samlede vurderingen for hele dagen.", + "usage_tips": "Bruk dette for å forstå gårsdagens prissituasjon i forhold til dine personlige terskelverdier. Sammenlign med i dag for trendanalyse." + }, + "today_price_rating": { + "description": "Aggregert prisvurdering for i dag", + "long_description": "Viser den aggregerte prisvurderingen (lav/normal/høy) for alle intervaller i dag, basert på dine konfigurerte terskelverdier. Bruker samme logikk som timesensorene for å bestemme den samlede vurderingen for hele dagen.", + "usage_tips": "Bruk dette for raskt å vurdere dagens prissituasjon i forhold til dine personlige terskelverdier. Hjelper med å ta forbruksbeslutninger for gjeldende dag." + }, + "tomorrow_price_rating": { + "description": "Aggregert prisvurdering for i morgen", + "long_description": "Viser den aggregerte prisvurderingen (lav/normal/høy) for alle intervaller i morgen, basert på dine konfigurerte terskelverdier. Bruker samme logikk som timesensorene for å bestemme den samlede vurderingen for hele dagen. Denne sensoren blir utilgjengelig inntil morgendagens data er publisert av Tibber (vanligvis rundt 13:00-14:00 CET).", + "usage_tips": "Bruk dette for å planlegge morgendagens energiforbruk basert på dine personlige pristerskelverdier. Sammenlign med i dag for å bestemme om du skal flytte forbruk til i morgen eller bruke energi i dag." + }, "trailing_price_average": { "description": "Den gjennomsnittlige elektrisitetsprisen for de siste 24 timene per kWh", "long_description": "Viser gjennomsnittsprisen per kWh beregnet fra de siste 24 timene (glidende gjennomsnitt) fra ditt Tibber-abonnement. Dette gir et rullende gjennomsnitt som oppdateres hvert 15. minutt basert på historiske data.", diff --git a/custom_components/tibber_prices/custom_translations/nl.json b/custom_components/tibber_prices/custom_translations/nl.json index 39f62a5..2744f51 100644 --- a/custom_components/tibber_prices/custom_translations/nl.json +++ b/custom_components/tibber_prices/custom_translations/nl.json @@ -55,6 +55,36 @@ "long_description": "Toont de gemiddelde prijs per kWh voor morgen van uw Tibber-abonnement. Deze sensor wordt niet beschikbaar totdat de gegevens van morgen door Tibber worden gepubliceerd (meestal rond 13:00-14:00 CET).", "usage_tips": "Gebruik dit als basislijn voor het vergelijken van prijzen van morgen en het plannen van verbruik. Vergelijk met het gemiddelde van vandaag om te zien of morgen over het algemeen duurder of goedkoper wordt." }, + "yesterday_price_level": { + "description": "Geaggregeerd prijsniveau voor gisteren", + "long_description": "Toont het geaggregeerde prijsniveau voor alle intervallen van gisteren. Gebruikt dezelfde logica als de uursensoren om het totale prijsniveau voor de hele dag te bepalen.", + "usage_tips": "Gebruik dit om de algemene prijssituatie van gisteren te begrijpen. Vergelijk met vandaag om dagelijkse trends te zien." + }, + "today_price_level": { + "description": "Geaggregeerd prijsniveau voor vandaag", + "long_description": "Toont het geaggregeerde prijsniveau voor alle intervallen van vandaag. Gebruikt dezelfde logica als de uursensoren om het totale prijsniveau voor de hele dag te bepalen.", + "usage_tips": "Gebruik dit om de prijssituatie van vandaag in één oogopslag te begrijpen. Handig voor snelle beoordelingen of vandaag over het algemeen goedkoop of duur is." + }, + "tomorrow_price_level": { + "description": "Geaggregeerd prijsniveau voor morgen", + "long_description": "Toont het geaggregeerde prijsniveau voor alle intervallen van morgen. Gebruikt dezelfde logica als de uursensoren om het totale prijsniveau voor de hele dag te bepalen. Deze sensor wordt niet beschikbaar totdat de gegevens van morgen door Tibber worden gepubliceerd (meestal rond 13:00-14:00 CET).", + "usage_tips": "Gebruik dit om de prijssituatie van morgen te begrijpen. Vergelijk met vandaag om te zien of morgen gunstiger of ongunstiger zal zijn voor energieverbruik." + }, + "yesterday_price_rating": { + "description": "Geaggregeerde prijsbeoordeling voor gisteren", + "long_description": "Toont de geaggregeerde prijsbeoordeling (laag/normaal/hoog) voor alle intervallen van gisteren, gebaseerd op uw geconfigureerde drempelwaarden. Gebruikt dezelfde logica als de uursensoren om de totale beoordeling voor de hele dag te bepalen.", + "usage_tips": "Gebruik dit om de prijssituatie van gisteren te begrijpen ten opzichte van uw persoonlijke drempelwaarden. Vergelijk met vandaag voor trendanalyse." + }, + "today_price_rating": { + "description": "Geaggregeerde prijsbeoordeling voor vandaag", + "long_description": "Toont de geaggregeerde prijsbeoordeling (laag/normaal/hoog) voor alle intervallen van vandaag, gebaseerd op uw geconfigureerde drempelwaarden. Gebruikt dezelfde logica als de uursensoren om de totale beoordeling voor de hele dag te bepalen.", + "usage_tips": "Gebruik dit om snel de prijssituatie van vandaag te beoordelen ten opzichte van uw persoonlijke drempelwaarden. Helpt bij het nemen van verbruiksbeslissingen voor de huidige dag." + }, + "tomorrow_price_rating": { + "description": "Geaggregeerde prijsbeoordeling voor morgen", + "long_description": "Toont de geaggregeerde prijsbeoordeling (laag/normaal/hoog) voor alle intervallen van morgen, gebaseerd op uw geconfigureerde drempelwaarden. Gebruikt dezelfde logica als de uursensoren om de totale beoordeling voor de hele dag te bepalen. Deze sensor wordt niet beschikbaar totdat de gegevens van morgen door Tibber worden gepubliceerd (meestal rond 13:00-14:00 CET).", + "usage_tips": "Gebruik dit om het energieverbruik van morgen te plannen op basis van uw persoonlijke prijsdrempelwaarden. Vergelijk met vandaag om te beslissen of u verbruik naar morgen moet verschuiven of vandaag energie moet gebruiken." + }, "trailing_price_average": { "description": "De gemiddelde elektriciteitsprijs voor de afgelopen 24 uur per kWh", "long_description": "Toont de gemiddelde prijs per kWh berekend uit de afgelopen 24 uur (voortschrijdend gemiddelde) van uw Tibber-abonnement. Dit biedt een voortschrijdend gemiddelde dat elke 15 minuten wordt bijgewerkt op basis van historische gegevens.", diff --git a/custom_components/tibber_prices/custom_translations/sv.json b/custom_components/tibber_prices/custom_translations/sv.json index 4cc31db..e05ccba 100644 --- a/custom_components/tibber_prices/custom_translations/sv.json +++ b/custom_components/tibber_prices/custom_translations/sv.json @@ -55,6 +55,36 @@ "long_description": "Visar genomsnittspriset per kWh för morgondagen från ditt Tibber-abonnemang. Denna sensor blir otillgänglig tills morgondagens data publiceras av Tibber (vanligtvis runt 13:00-14:00 CET).", "usage_tips": "Använd detta som baslinje för att jämföra morgondagens priser och planera konsumtion. Jämför med dagens genomsnitt för att se om morgondagen kommer att bli dyrare eller billigare totalt sett." }, + "yesterday_price_level": { + "description": "Aggregerad prisnivå för igår", + "long_description": "Visar den aggregerade prisnivån för alla intervall igår. Använder samma logik som timsensorerna för att bestämma den totala prisnivån för hela dagen.", + "usage_tips": "Använd detta för att förstå den övergripande prissituationen igår. Jämför med idag för att se dagliga trender." + }, + "today_price_level": { + "description": "Aggregerad prisnivå för idag", + "long_description": "Visar den aggregerade prisnivån för alla intervall idag. Använder samma logik som timsensorerna för att bestämma den totala prisnivån för hela dagen.", + "usage_tips": "Använd detta för att förstå dagens prissituation snabbt. Praktiskt för snabba bedömningar om dagen i allmänhet är billig eller dyr." + }, + "tomorrow_price_level": { + "description": "Aggregerad prisnivå för imorgon", + "long_description": "Visar den aggregerade prisnivån för alla intervall imorgon. Använder samma logik som timsensorerna för att bestämma den totala prisnivån för hela dagen. Denna sensor blir otillgänglig tills morgondagens data publiceras av Tibber (vanligtvis runt 13:00-14:00 CET).", + "usage_tips": "Använd detta för att förstå imorgonens prissituation. Jämför med idag för att se om imorgon blir mer eller mindre gynnsamt för energiförbrukning." + }, + "yesterday_price_rating": { + "description": "Aggregerad prisvärdering för igår", + "long_description": "Visar den aggregerade prisvärderingen (låg/normal/hög) för alla intervall igår, baserat på dina konfigurerade tröskelvärden. Använder samma logik som timsensorerna för att bestämma den totala värderingen för hela dagen.", + "usage_tips": "Använd detta för att förstå igårens prissituation i förhållande till dina personliga tröskelvärden. Jämför med idag för trendanalys." + }, + "today_price_rating": { + "description": "Aggregerad prisvärdering för idag", + "long_description": "Visar den aggregerade prisvärderingen (låg/normal/hög) för alla intervall idag, baserat på dina konfigurerade tröskelvärden. Använder samma logik som timsensorerna för att bestämme den totala värderingen för hela dagen.", + "usage_tips": "Använd detta för att snabbt bedöma dagens prissituation i förhållande till dina personliga tröskelvärden. Hjälper till att fatta förbrukningsbeslut för innevarande dag." + }, + "tomorrow_price_rating": { + "description": "Aggregerad prisvärdering för imorgon", + "long_description": "Visar den aggregerade prisvärderingen (låg/normal/hög) för alla intervall imorgon, baserat på dina konfigurerade tröskelvärden. Använder samma logik som timsensorerna för att bestämma den totala värderingen för hela dagen. Denna sensor blir otillgänglig tills morgondagens data publiceras av Tibber (vanligtvis runt 13:00-14:00 CET).", + "usage_tips": "Använd detta för att planera imorgonens energiförbrukning baserat på dina personliga priströskelvärden. Jämför med idag för att avgöra om du ska skjuta upp förbrukning till imorgon eller använda energi idag." + }, "trailing_price_average": { "description": "Det genomsnittliga elpriset för de senaste 24 timmarna per kWh", "long_description": "Visar genomsnittspriset per kWh beräknat från de senaste 24 timmarna (rullande genomsnitt) från ditt Tibber-abonnemang. Detta ger ett rullande genomsnitt som uppdateras var 15:e minut baserat på historiska data.", diff --git a/custom_components/tibber_prices/entity_utils/icons.py b/custom_components/tibber_prices/entity_utils/icons.py index f1bc306..456b92d 100644 --- a/custom_components/tibber_prices/entity_utils/icons.py +++ b/custom_components/tibber_prices/entity_utils/icons.py @@ -110,6 +110,9 @@ def get_level_sensor_icon(key: str, value: Any) -> str | None: "previous_interval_price_level", "current_hour_price_level", "next_hour_price_level", + "yesterday_price_level", + "today_price_level", + "tomorrow_price_level", ] or not isinstance(value, str): return None @@ -124,6 +127,9 @@ def get_rating_sensor_icon(key: str, value: Any) -> str | None: "previous_interval_price_rating", "current_hour_price_rating", "next_hour_price_rating", + "yesterday_price_rating", + "today_price_rating", + "tomorrow_price_rating", ] or not isinstance(value, str): return None diff --git a/custom_components/tibber_prices/sensor/attributes.py b/custom_components/tibber_prices/sensor/attributes.py index f2dbabb..7eb3708 100644 --- a/custom_components/tibber_prices/sensor/attributes.py +++ b/custom_components/tibber_prices/sensor/attributes.py @@ -97,7 +97,22 @@ def build_sensor_attributes( add_average_price_attributes(attributes=attributes, key=key, coordinator=coordinator) elif key.startswith("next_avg_"): add_next_avg_attributes(attributes=attributes, key=key, coordinator=coordinator) - elif any(pattern in key for pattern in ["_price_today", "_price_tomorrow", "rating", "data_timestamp"]): + elif any( + pattern in key + for pattern in [ + "_price_today", + "_price_tomorrow", + "_price_yesterday", + "yesterday_price_level", + "today_price_level", + "tomorrow_price_level", + "yesterday_price_rating", + "today_price_rating", + "tomorrow_price_rating", + "rating", + "data_timestamp", + ] + ): add_statistics_attributes( attributes=attributes, key=key, @@ -109,10 +124,21 @@ def build_sensor_attributes( elif key.endswith("_volatility"): add_volatility_attributes(attributes=attributes, cached_data=cached_data) - # For price_level, add the original level as attribute + # For current_interval_price_level, add the original level as attribute if key == "current_interval_price_level" and cached_data.get("last_price_level") is not None: attributes["level_id"] = cached_data["last_price_level"] + # Add icon_color for daily level and rating sensors (uses native_value) + if key in [ + "yesterday_price_level", + "today_price_level", + "tomorrow_price_level", + "yesterday_price_rating", + "today_price_rating", + "tomorrow_price_rating", + ]: + add_icon_color_attribute(attributes, key=key, state_value=native_value) + except (KeyError, ValueError, TypeError) as ex: coordinator.logger.exception( "Error getting sensor attributes", @@ -368,16 +394,58 @@ def add_statistics_attributes( attributes["timestamp"] = cached_data["last_extreme_interval"].get("startsAt") else: # Fallback: use the first timestamp of the appropriate day - day_key = "tomorrow" if "tomorrow" in key else "today" - day_data = price_info.get(day_key, []) - if day_data: - attributes["timestamp"] = day_data[0].get("startsAt") - else: - # Fallback: use the first timestamp of the appropriate day - day_key = "tomorrow" if "tomorrow" in key else "today" + _add_fallback_timestamp(attributes, key, price_info) + elif key in [ + "yesterday_price_level", + "today_price_level", + "tomorrow_price_level", + "yesterday_price_rating", + "today_price_rating", + "tomorrow_price_rating", + ]: + # Daily aggregated level/rating sensors - add timestamp + day_key = _get_day_key_from_sensor_key(key) day_data = price_info.get(day_key, []) if day_data: + # Use first timestamp of the day (00:00) attributes["timestamp"] = day_data[0].get("startsAt") + else: + # Fallback: use the first timestamp of the appropriate day + _add_fallback_timestamp(attributes, key, price_info) + + +def _get_day_key_from_sensor_key(key: str) -> str: + """ + Extract day key (yesterday/today/tomorrow) from sensor key. + + Args: + key: The sensor entity key + + Returns: + Day key: "yesterday", "today", or "tomorrow" + + """ + if "yesterday" in key: + return "yesterday" + if "tomorrow" in key: + return "tomorrow" + return "today" + + +def _add_fallback_timestamp(attributes: dict, key: str, price_info: dict) -> None: + """ + Add fallback timestamp to attributes based on the day in the sensor key. + + Args: + attributes: Dictionary to add timestamp to + key: The sensor entity key + price_info: Price info dictionary from coordinator data + + """ + day_key = _get_day_key_from_sensor_key(key) + day_data = price_info.get(day_key, []) + if day_data: + attributes["timestamp"] = day_data[0].get("startsAt") def add_average_price_attributes( diff --git a/custom_components/tibber_prices/sensor/core.py b/custom_components/tibber_prices/sensor/core.py index 83a6600..12ec13d 100644 --- a/custom_components/tibber_prices/sensor/core.py +++ b/custom_components/tibber_prices/sensor/core.py @@ -186,6 +186,14 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity): day="tomorrow", stat_func=lambda prices: sum(prices) / len(prices), ), + # Daily aggregated level sensors + "yesterday_price_level": lambda: self._get_daily_aggregated_value(day="yesterday", value_type="level"), + "today_price_level": lambda: self._get_daily_aggregated_value(day="today", value_type="level"), + "tomorrow_price_level": lambda: self._get_daily_aggregated_value(day="tomorrow", value_type="level"), + # Daily aggregated rating sensors + "yesterday_price_rating": lambda: self._get_daily_aggregated_value(day="yesterday", value_type="rating"), + "today_price_rating": lambda: self._get_daily_aggregated_value(day="today", value_type="rating"), + "tomorrow_price_rating": lambda: self._get_daily_aggregated_value(day="tomorrow", value_type="rating"), # ================================================================ # 24H WINDOW SENSORS (trailing/leading from current) # ================================================================ @@ -525,6 +533,79 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity): result = get_price_value(value, in_euro=False) return round(result, 2) + def _get_daily_aggregated_value( + self, + *, + day: str = "today", + value_type: str = "level", + ) -> str | None: + """ + Get aggregated price level or rating for a specific calendar day. + + Aggregates all intervals within a calendar day using the same logic + as rolling hour sensors, but for the entire day. + + Args: + day: "yesterday", "today", or "tomorrow" - which calendar day to calculate for + value_type: "level" or "rating" - type of aggregation to perform + + Returns: + Aggregated level/rating value (lowercase), or None if unavailable + + """ + if not self.coordinator.data: + return None + + price_info = self.coordinator.data.get("priceInfo", {}) + + # Get local midnight boundaries based on the requested day + local_midnight = dt_util.as_local(dt_util.start_of_local_day(dt_util.now())) + if day == "tomorrow": + local_midnight = local_midnight + timedelta(days=1) + elif day == "yesterday": + local_midnight = local_midnight - timedelta(days=1) + local_midnight_next_day = local_midnight + timedelta(days=1) + + # Collect all intervals from both today and tomorrow data + # that fall within the target day's local date boundaries + day_intervals = [] + for day_key in ["yesterday", "today", "tomorrow"]: + for price_data in price_info.get(day_key, []): + starts_at_str = price_data.get("startsAt") + if not starts_at_str: + continue + + starts_at = dt_util.parse_datetime(starts_at_str) + if starts_at is None: + continue + + # Convert to local timezone for comparison + starts_at = dt_util.as_local(starts_at) + + # Include interval if it starts within the target day's local date boundaries + if local_midnight <= starts_at < local_midnight_next_day: + day_intervals.append(price_data) + + if not day_intervals: + return None + + # Use the same aggregation logic as rolling hour sensors + if value_type == "level": + return aggregate_level_data(day_intervals) + if value_type == "rating": + # Get thresholds from config + threshold_low = self.coordinator.config_entry.options.get( + CONF_PRICE_RATING_THRESHOLD_LOW, + DEFAULT_PRICE_RATING_THRESHOLD_LOW, + ) + threshold_high = self.coordinator.config_entry.options.get( + CONF_PRICE_RATING_THRESHOLD_HIGH, + DEFAULT_PRICE_RATING_THRESHOLD_HIGH, + ) + return aggregate_rating_data(day_intervals, threshold_low, threshold_high) + + return None + def _get_24h_window_value( self, *, diff --git a/custom_components/tibber_prices/sensor/definitions.py b/custom_components/tibber_prices/sensor/definitions.py index b35d1dc..8179d9d 100644 --- a/custom_components/tibber_prices/sensor/definitions.py +++ b/custom_components/tibber_prices/sensor/definitions.py @@ -114,7 +114,7 @@ INTERVAL_RATING_SENSORS = ( key="current_interval_price_rating", translation_key="current_interval_price_rating", name="Current Price Rating", - icon="mdi:star-outline", + icon="mdi:thumbs-up-down", device_class=SensorDeviceClass.ENUM, options=["low", "normal", "high"], ), @@ -122,7 +122,7 @@ INTERVAL_RATING_SENSORS = ( key="next_interval_price_rating", translation_key="next_interval_price_rating", name="Next Price Rating", - icon="mdi:star-half-full", + icon="mdi:thumbs-up-down", device_class=SensorDeviceClass.ENUM, options=["low", "normal", "high"], ), @@ -130,7 +130,7 @@ INTERVAL_RATING_SENSORS = ( key="previous_interval_price_rating", translation_key="previous_interval_price_rating", name="Previous Price Rating", - icon="mdi:star-half-full", + icon="mdi:thumbs-up-down", entity_registry_enabled_default=False, device_class=SensorDeviceClass.ENUM, options=["low", "normal", "high"], @@ -192,7 +192,7 @@ ROLLING_HOUR_RATING_SENSORS = ( key="current_hour_price_rating", translation_key="current_hour_price_rating", name="Current Hour Price Rating", - icon="mdi:star-outline", + icon="mdi:thumbs-up-down", device_class=SensorDeviceClass.ENUM, options=["low", "normal", "high"], ), @@ -200,7 +200,7 @@ ROLLING_HOUR_RATING_SENSORS = ( key="next_hour_price_rating", translation_key="next_hour_price_rating", name="Next Hour Price Rating", - icon="mdi:star-half-full", + icon="mdi:thumbs-up-down", device_class=SensorDeviceClass.ENUM, options=["low", "normal", "high"], ), @@ -262,6 +262,68 @@ DAILY_STAT_SENSORS = ( ), ) +# NOTE: Enum options are defined inline (not imported from const.py) to avoid +# import timing issues with Home Assistant's entity platform initialization. +# Keep in sync with PRICE_LEVEL_OPTIONS in const.py! +DAILY_LEVEL_SENSORS = ( + SensorEntityDescription( + key="yesterday_price_level", + translation_key="yesterday_price_level", + name="Yesterday's Price Level", + icon="mdi:gauge", + device_class=SensorDeviceClass.ENUM, + options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"], + entity_registry_enabled_default=False, + ), + SensorEntityDescription( + key="today_price_level", + translation_key="today_price_level", + name="Today's Price Level", + icon="mdi:gauge", + device_class=SensorDeviceClass.ENUM, + options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"], + ), + SensorEntityDescription( + key="tomorrow_price_level", + translation_key="tomorrow_price_level", + name="Tomorrow's Price Level", + icon="mdi:gauge", + device_class=SensorDeviceClass.ENUM, + options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"], + ), +) + +# NOTE: Enum options are defined inline (not imported from const.py) to avoid +# import timing issues with Home Assistant's entity platform initialization. +# Keep in sync with PRICE_RATING_OPTIONS in const.py! +DAILY_RATING_SENSORS = ( + SensorEntityDescription( + key="yesterday_price_rating", + translation_key="yesterday_price_rating", + name="Yesterday's Price Rating", + icon="mdi:thumbs-up-down", + device_class=SensorDeviceClass.ENUM, + options=["low", "normal", "high"], + entity_registry_enabled_default=False, + ), + SensorEntityDescription( + key="today_price_rating", + translation_key="today_price_rating", + name="Today's Price Rating", + icon="mdi:thumbs-up-down", + device_class=SensorDeviceClass.ENUM, + options=["low", "normal", "high"], + ), + SensorEntityDescription( + key="tomorrow_price_rating", + translation_key="tomorrow_price_rating", + name="Tomorrow's Price Rating", + icon="mdi:thumbs-up-down", + device_class=SensorDeviceClass.ENUM, + options=["low", "normal", "high"], + ), +) + # ---------------------------------------------------------------------------- # 4. 24H WINDOW SENSORS (trailing/leading from current interval) # ---------------------------------------------------------------------------- @@ -556,6 +618,8 @@ ENTITY_DESCRIPTIONS = ( *ROLLING_HOUR_LEVEL_SENSORS, *ROLLING_HOUR_RATING_SENSORS, *DAILY_STAT_SENSORS, + *DAILY_LEVEL_SENSORS, + *DAILY_RATING_SENSORS, *WINDOW_24H_SENSORS, *FUTURE_AVG_SENSORS, *FUTURE_TREND_SENSORS, diff --git a/custom_components/tibber_prices/translations/de.json b/custom_components/tibber_prices/translations/de.json index b448f0e..f02af0c 100644 --- a/custom_components/tibber_prices/translations/de.json +++ b/custom_components/tibber_prices/translations/de.json @@ -266,6 +266,24 @@ "average_price_tomorrow": { "name": "Durchschnittspreis morgen" }, + "yesterday_price_level": { + "name": "Preisniveau gestern" + }, + "today_price_level": { + "name": "Preisniveau heute" + }, + "tomorrow_price_level": { + "name": "Preisniveau morgen" + }, + "yesterday_price_rating": { + "name": "Preisbewertung gestern" + }, + "today_price_rating": { + "name": "Preisbewertung heute" + }, + "tomorrow_price_rating": { + "name": "Preisbewertung morgen" + }, "trailing_price_average": { "name": "Nachlaufender 24h-Durchschnittspreis" }, diff --git a/custom_components/tibber_prices/translations/en.json b/custom_components/tibber_prices/translations/en.json index 53fb735..91801cc 100644 --- a/custom_components/tibber_prices/translations/en.json +++ b/custom_components/tibber_prices/translations/en.json @@ -262,6 +262,24 @@ "average_price_tomorrow": { "name": "Tomorrow's Average Price" }, + "yesterday_price_level": { + "name": "Yesterday's Price Level" + }, + "today_price_level": { + "name": "Today's Price Level" + }, + "tomorrow_price_level": { + "name": "Tomorrow's Price Level" + }, + "yesterday_price_rating": { + "name": "Yesterday's Price Rating" + }, + "today_price_rating": { + "name": "Today's Price Rating" + }, + "tomorrow_price_rating": { + "name": "Tomorrow's Price Rating" + }, "trailing_price_average": { "name": "Trailing 24h Average Price" },