Compare commits

..

No commits in common. "e1da4cfa895d7e06af2d67b479ab88d3208c54e0" and "d0b6ea0e1a848c2f5817c9fe7c690f56023fbda6" have entirely different histories.

31 changed files with 353 additions and 1214 deletions

View file

@ -313,7 +313,6 @@ After successful refactoring:
- Platform modules: `__init__.py`, `sensor.py` (deprecated, now `sensor/`), `binary_sensor.py` (deprecated, now `binary_sensor/`), future platforms - Platform modules: `__init__.py`, `sensor.py` (deprecated, now `sensor/`), `binary_sensor.py` (deprecated, now `binary_sensor/`), future platforms
- Core integration files: `const.py`, `manifest.json`, `services.yaml`, `diagnostics.py`, `data.py` - Core integration files: `const.py`, `manifest.json`, `services.yaml`, `diagnostics.py`, `data.py`
- Translation directories: `translations/`, `custom_translations/` - Translation directories: `translations/`, `custom_translations/`
- Brand images: `brand/` (icon.png, dark_icon.png, logo.png, dark_logo.png + `@2x` variants) — served via HA brands proxy API (HA ≥ 2026.4), silently ignored on older versions
**❌ PROHIBITED in root:** **❌ PROHIBITED in root:**
- Utility modules (use `/utils/` package instead) - Utility modules (use `/utils/` package instead)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View file

@ -69,21 +69,14 @@ STEP_TO_SENSOR_KEYS: dict[str, list[str]] = {
# Also affects trend sensors (adaptive thresholds) # Also affects trend sensors (adaptive thresholds)
"current_price_trend", "current_price_trend",
"next_price_trend_change", "next_price_trend_change",
"price_outlook_1h", "price_trend_1h",
"price_outlook_2h", "price_trend_2h",
"price_outlook_3h", "price_trend_3h",
"price_outlook_4h", "price_trend_4h",
"price_outlook_5h", "price_trend_5h",
"price_outlook_6h", "price_trend_6h",
"price_outlook_8h", "price_trend_8h",
"price_outlook_12h", "price_trend_12h",
"price_trajectory_2h",
"price_trajectory_3h",
"price_trajectory_4h",
"price_trajectory_5h",
"price_trajectory_6h",
"price_trajectory_8h",
"price_trajectory_12h",
], ],
# Best Price settings affect best price binary sensor and timing sensors # Best Price settings affect best price binary sensor and timing sensors
"best_price": [ "best_price": [
@ -113,21 +106,14 @@ STEP_TO_SENSOR_KEYS: dict[str, list[str]] = {
"price_trend": [ "price_trend": [
"current_price_trend", "current_price_trend",
"next_price_trend_change", "next_price_trend_change",
"price_outlook_1h", "price_trend_1h",
"price_outlook_2h", "price_trend_2h",
"price_outlook_3h", "price_trend_3h",
"price_outlook_4h", "price_trend_4h",
"price_outlook_5h", "price_trend_5h",
"price_outlook_6h", "price_trend_6h",
"price_outlook_8h", "price_trend_8h",
"price_outlook_12h", "price_trend_12h",
"price_trajectory_2h",
"price_trajectory_3h",
"price_trajectory_4h",
"price_trajectory_5h",
"price_trajectory_6h",
"price_trajectory_8h",
"price_trajectory_12h",
], ],
} }

View file

@ -62,22 +62,14 @@ TIME_SENSITIVE_ENTITY_KEYS = frozenset(
"current_price_trend", "current_price_trend",
"next_price_trend_change", "next_price_trend_change",
# Price trend sensors # Price trend sensors
"price_outlook_1h", "price_trend_1h",
"price_outlook_2h", "price_trend_2h",
"price_outlook_3h", "price_trend_3h",
"price_outlook_4h", "price_trend_4h",
"price_outlook_5h", "price_trend_5h",
"price_outlook_6h", "price_trend_6h",
"price_outlook_8h", "price_trend_8h",
"price_outlook_12h", "price_trend_12h",
# Price trajectory sensors (first-half vs second-half window comparison)
"price_trajectory_2h",
"price_trajectory_3h",
"price_trajectory_4h",
"price_trajectory_5h",
"price_trajectory_6h",
"price_trajectory_8h",
"price_trajectory_12h",
# Trailing/leading 24h calculations (based on current interval) # Trailing/leading 24h calculations (based on current interval)
"trailing_price_average", "trailing_price_average",
"leading_price_average", "leading_price_average",

View file

@ -227,80 +227,45 @@
"long_description": "Zeigt den Durchschnittspreis für die nächsten 48 Intervalle (12 Stunden) beginnend ab dem nächsten 15-Minuten-Intervall.", "long_description": "Zeigt den Durchschnittspreis für die nächsten 48 Intervalle (12 Stunden) beginnend ab dem nächsten 15-Minuten-Intervall.",
"usage_tips": "Absolute Preisschwelle: Strategische Entscheidungen mit Preisobergrenzen. Fahre nur fort, wenn der 12h-Durchschnitt unter deinem maximal akzeptablen Preis liegt. Gut für verschiebbare Großlasten." "usage_tips": "Absolute Preisschwelle: Strategische Entscheidungen mit Preisobergrenzen. Fahre nur fort, wenn der 12h-Durchschnitt unter deinem maximal akzeptablen Preis liegt. Gut für verschiebbare Großlasten."
}, },
"price_outlook_1h": { "price_trend_1h": {
"description": "Preisausblick für die nächste Stunde", "description": "Preistrend für die nächste Stunde",
"long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten Stunde (4 Intervalle). Alle Ausblick-Sensoren (1h12h) haben dieselbe Basis: dein aktueller Preis — sie unterscheiden sich nur im Zeitfenster. Größere Fenster umfassen mehr Zukunftsstunden und glätten kurzfristige Spitzen. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).", "long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten Stunde (4 Intervalle). Alle Trend-Sensoren (1h12h) haben dieselbe Basis: dein aktueller Preis — sie unterscheiden sich nur im Zeitfenster. Größere Fenster umfassen mehr Zukunftsstunden und glätten kurzfristige Spitzen. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).",
"usage_tips": "Entscheidungshilfe: 'steigend' = JETZT HANDELN, dein aktueller Preis ist günstiger als der Stunden-Durchschnitt. 'fallend' = WARTEN, der Fenster-Durchschnitt ist günstiger als jetzt. 'stabil' = Timing egal. Häufiges Missverständnis: 'steigend' bedeutet NICHT 'zu spät' — es heißt, jetzt ist gerade ein guter Preis! Funktioniert unabhängig vom absoluten Preisniveau." "usage_tips": "Entscheidungshilfe: 'steigend' = JETZT HANDELN, dein aktueller Preis ist günstiger als die nächste Stunde. 'fallend' = WARTEN, günstigere Preise kommen. 'stabil' = Timing egal. Häufiges Missverständnis: 'steigend' bedeutet NICHT 'zu spät' — es heißt, jetzt ist gerade ein guter Preis! Funktioniert unabhängig vom absoluten Preisniveau."
}, },
"price_outlook_2h": { "price_trend_2h": {
"description": "Preisausblick für die nächsten 2 Stunden", "description": "Preistrend für die nächsten 2 Stunden",
"long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 2 Stunden (8 Intervalle). 'steigend' = aktueller Preis liegt unter dem 2h-Fenster-Durchschnitt; 'fallend' = Fenster-Durchschnitt ist günstiger als jetzt. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).", "long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 2 Stunden (8 Intervalle) — das gesamte Zeitfenster ab jetzt, nicht nur der spätere Teil. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).",
"usage_tips": "Haushaltsgeräte: 'steigend' = jetzt starten, du hast gerade einen guten Preis. 'fallend' = bessere Preise kommen im 2h-Fenster, verschiebe wenn möglich. 'stabil' = egal, starte nach Bedarf. Kombiniere mit price_trajectory_2h um zu unterscheiden ob Preise noch fallen oder schon steigen." "usage_tips": "Haushaltsgeräte: 'steigend' = jetzt starten, du hast gerade einen guten Preis. 'fallend' = bessere Preise kommen in 2h, verschiebe wenn möglich. 'stabil' = egal, starte nach Bedarf. Nicht auf 'stabil' warten — bei 'steigend' ist JETZT der beste Zeitpunkt."
}, },
"price_outlook_3h": { "price_trend_3h": {
"description": "Preisausblick für die nächsten 3 Stunden", "description": "Preistrend für die nächsten 3 Stunden",
"long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 3 Stunden (12 Intervalle). 'steigend' = aktueller Preis liegt unter dem 3h-Fenster-Durchschnitt; 'fallend' = Fenster-Durchschnitt ist günstiger als jetzt. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).", "long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 3 Stunden (12 Intervalle) — das gesamte Zeitfenster ab jetzt, nicht nur der spätere Teil. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).",
"usage_tips": "Eco-Programme: 'steigend' = Eco-Zyklus jetzt starten, Preise steigen im Fenster-Durchschnitt. 'fallend' = warten, günstigeres Fenster kommt. Kombiniere mit avg-Sensor: starte wenn Ausblick 'steigend' oder 'stabil' UND avg < dein Limit." "usage_tips": "Eco-Programme: 'steigend' = Eco-Zyklus jetzt starten, Preise steigen ab hier. 'fallend' = warten, günstigeres Fenster kommt. Kombiniere mit avg-Sensor: starte wenn Trend 'steigend' oder 'stabil' UND avg < dein Limit. Funktioniert in jeder Saison."
}, },
"price_outlook_4h": { "price_trend_4h": {
"description": "Preisausblick für die nächsten 4 Stunden", "description": "Preistrend für die nächsten 4 Stunden",
"long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 4 Stunden (16 Intervalle). 'steigend' = aktueller Preis liegt unter dem 4h-Fenster-Durchschnitt; 'fallend' = Fenster-Durchschnitt ist günstiger als jetzt. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).", "long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 4 Stunden (16 Intervalle) — das gesamte Zeitfenster ab jetzt, nicht nur der spätere Teil. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).",
"usage_tips": "Wärmepumpe/Batterie: 'steigend' = jetzt laden, du bist an einem relativen Tiefpunkt. 'fallend' = warten auf besseres Fenster im Durchschnitt. Wichtig: 'stark fallend' an einem Preistief bedeutet das Fenster ist im Schnitt günstiger — kombiniere mit price_trajectory_4h um zu sehen ob die Preise noch fallen oder schon aufsteigen." "usage_tips": "Wärmepumpe/Batterie: 'steigend' = jetzt laden, du bist an einem relativen Tiefpunkt. 'fallend' = warten auf besseres Ladefenster. 'stabil' = laden nach Bedarf. Funktioniert unabhängig vom Preisniveau — findet relative beste Zeit ob 10 oder 50 Cent."
}, },
"price_outlook_5h": { "price_trend_5h": {
"description": "Preisausblick für die nächsten 5 Stunden", "description": "Preistrend für die nächsten 5 Stunden",
"long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 5 Stunden (20 Intervalle). 'steigend' = aktueller Preis liegt unter dem 5h-Fenster-Durchschnitt; 'fallend' = Fenster-Durchschnitt ist günstiger als jetzt. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).", "long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 5 Stunden (20 Intervalle) — das gesamte Zeitfenster ab jetzt, nicht nur der spätere Teil. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).",
"usage_tips": "Erweiterte Zyklen: 'steigend' oder 'stabil' = guter Zeitpunkt zum Starten, Preise werden im Fenster-Schnitt nicht günstiger. 'fallend' = warten wenn dein Zeitplan es erlaubt." "usage_tips": "Erweiterte Zyklen: 'steigend' oder 'stabil' = guter Zeitpunkt zum Starten, Preise werden nicht günstiger. 'fallend' = warten wenn dein Zeitplan es erlaubt. Passt sich dem Markt an — findet bestes relatives Timing in jedem Preisumfeld."
}, },
"price_outlook_6h": { "price_trend_6h": {
"description": "Preisausblick für die nächsten 6 Stunden", "description": "Preistrend für die nächsten 6 Stunden",
"long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 6 Stunden (24 Intervalle). 'steigend' = aktueller Preis liegt unter dem 6h-Fenster-Durchschnitt; 'fallend' = Fenster-Durchschnitt ist günstiger als jetzt. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).", "long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 6 Stunden (24 Intervalle) — das gesamte Zeitfenster ab jetzt, nicht nur der spätere Teil. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).",
"usage_tips": "Abendentscheidungen: 'steigend' = Strom jetzt nutzen, solange er relativ günstig ist. 'fallend' = Abend-/Nachtpreise werden im Durchschnitt besser, warte wenn möglich." "usage_tips": "Abendentscheidungen: 'steigend' = Strom jetzt nutzen, solange er relativ günstig ist. 'fallend' = Abend-/Nachtpreise werden besser, warte wenn möglich. Passt sich automatisch an Winter/Sommer-Preisniveaus an — keine festen Schwellenwerte nötig."
}, },
"price_outlook_8h": { "price_trend_8h": {
"description": "Preisausblick für die nächsten 8 Stunden", "description": "Preistrend für die nächsten 8 Stunden",
"long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 8 Stunden (32 Intervalle). 'steigend' = aktueller Preis liegt unter dem 8h-Fenster-Durchschnitt; 'fallend' = Fenster-Durchschnitt ist günstiger als jetzt. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).", "long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 8 Stunden (32 Intervalle) — das gesamte Zeitfenster ab jetzt, nicht nur der spätere Teil. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).",
"usage_tips": "Nachtplanung: 'steigend' = heute Nacht/morgen wird im Schnitt teurer, Strom jetzt nutzen. 'fallend' = Nachtpreise werden im Schnitt günstiger, Warten lohnt sich." "usage_tips": "Nachtplanung: 'steigend' = heute Nacht/morgen wird teurer, Strom jetzt nutzen. 'fallend' = Nachtpreise werden günstiger, Warten lohnt sich. 'stabil' = starte nach Bedarf. Funktioniert ganzjährig ohne manuelle Schwellenwert-Anpassungen."
}, },
"price_outlook_12h": { "price_trend_12h": {
"description": "Preisausblick für die nächsten 12 Stunden", "description": "Preistrend für die nächsten 12 Stunden",
"long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 12 Stunden (48 Intervalle). 'steigend' = aktueller Preis liegt unter dem 12h-Fenster-Durchschnitt; 'fallend' = Fenster-Durchschnitt ist günstiger als jetzt. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).", "long_description": "Vergleicht deinen aktuellen Preis mit dem Durchschnitt aller Intervalle der nächsten 12 Stunden (48 Intervalle) — das gesamte Zeitfenster ab jetzt, nicht nur der spätere Teil. Steigend/fallend ab ±3%, stark ab ±9% (konfigurierbar, volatilitätsadaptiv).",
"usage_tips": "Strategische Entscheidungen: 'steigend' = du bist an einem Tiefpunkt relativ zu den nächsten 12h, guter Zeitpunkt für stromintensive Aufgaben. 'fallend' = deutlich bessere Preise kommen im Schnitt, warte wenn möglich." "usage_tips": "Strategische Entscheidungen: 'steigend' = du bist an einem Tiefpunkt, guter Zeitpunkt für stromintensive Aufgaben. 'fallend' = deutlich bessere Preise kommen, warte wenn möglich. Am besten kombiniert mit avg-Sensor für absolute Preisobergrenzen."
},
"price_trajectory_2h": {
"description": "Preisverlauf innerhalb des nächsten 2-Stunden-Fensters",
"long_description": "Compares the average of the first hour (4 intervals) with the average of the second hour (4 intervals) within the next 2-hour window. 'rising' = second half more expensive than first half — prices are climbing within the window. 'falling' = second half cheaper — prices are dropping within the window. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "At a price minimum: price_outlook_2h may show 'falling' (window average is below current), but price_trajectory_2h shows 'rising' (second half more expensive than first) — revealing the upcoming reversal. Power combination: 'outlook: falling + trajectory: rising' = you're AT the minimum, act now."
},
"price_trajectory_3h": {
"description": "Preisverlauf innerhalb des nächsten 3-Stunden-Fensters",
"long_description": "Compares the average of the first 1.5 hours with the average of the second 1.5 hours within the next 3-hour window. 'rising' = prices are climbing over the 3h window; 'falling' = prices are dropping. Reveals the direction of price movement independent of the current price level. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Appliance timing: 'outlook: strongly_falling + trajectory: rising' = you're at or past the minimum, the window average is low but prices are already recovering — start now."
},
"price_trajectory_4h": {
"description": "Preisverlauf innerhalb des nächsten 4-Stunden-Fensters",
"long_description": "Compares the average of the first 2 hours with the average of the second 2 hours within the next 4-hour window. 'rising' = prices are climbing over the 4h window; 'falling' = prices are dropping. Complementary to price_outlook_4h: outlook answers 'is NOW cheap vs the window average?', trajectory answers 'are prices rising or falling within the window?'. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Heat pump charging: 'rising' = first half window is cheaper, charge now before prices climb. 'falling' = second half window is cheaper, wait if you can. Combine with outlook: if both rising, very strong signal to act now."
},
"price_trajectory_5h": {
"description": "Preisverlauf innerhalb des nächsten 5-Stunden-Fensters",
"long_description": "Compares the average of the first 2.5 hours with the average of the second 2.5 hours within the next 5-hour window. 'rising' = prices are climbing over the 5h window; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Eco/long cycles: 'outlook: rising + trajectory: rising' = clear signal to start now. 'outlook: falling + trajectory: rising' = you're near the bottom, good time to start before costs climb."
},
"price_trajectory_6h": {
"description": "Preisverlauf innerhalb des nächsten 6-Stunden-Fensters",
"long_description": "Compares the average of the first 3 hours with the average of the second 3 hours within the next 6-hour window. 'rising' = prices are climbing over the 6h window; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Evening/overnight planning: 'falling' at evening peak = overnight will be cheaper, postpone. 'rising' in the morning = current morning prices are the day's low — good time for high consumption."
},
"price_trajectory_8h": {
"description": "Preisverlauf innerhalb des nächsten 8-Stunden-Fensters",
"long_description": "Compares the average of the first 4 hours with the average of the second 4 hours within the next 8-hour window. 'rising' = prices are climbing over the 8h window; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Overnight charging: 'rising' during evening = first half of night is cheapest, start charging earlier. 'falling' = second half of night will be cheapest, delay start."
},
"price_trajectory_12h": {
"description": "Preisverlauf innerhalb des nächsten 12-Stunden-Fensters",
"long_description": "Compares the average of the first 6 hours with the average of the second 6 hours within the next 12-hour window. 'rising' = prices are climbing over the 12h window; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Day-ahead planning: 'rising' at midnight = first half of day is cheaper, schedule morning loads. 'falling' = prices drop in the second part of the day, afternoon/evening scheduling is better."
}, },
"current_price_trend": { "current_price_trend": {
"description": "Aktuelle Preistrend-Richtung und wie lange sie anhält", "description": "Aktuelle Preistrend-Richtung und wie lange sie anhält",

View file

@ -227,80 +227,45 @@
"long_description": "Shows the average price for the next 48 intervals (12 hours) starting from the next 15-minute interval.", "long_description": "Shows the average price for the next 48 intervals (12 hours) starting from the next 15-minute interval.",
"usage_tips": "Absolute price threshold: Strategic decisions with price caps. Only proceed if 12h average is below your maximum acceptable price. Good for postponable large loads." "usage_tips": "Absolute price threshold: Strategic decisions with price caps. Only proceed if 12h average is below your maximum acceptable price. Good for postponable large loads."
}, },
"price_outlook_1h": { "price_trend_1h": {
"description": "Price outlook for the next hour", "description": "Price trend for the next hour",
"long_description": "Compares your current price with the average of all intervals in the next hour (4 intervals). All outlook sensors (1h12h) share the same base: your current price — they differ only in window size. Larger windows include more future hours and smooth out short-term spikes. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).", "long_description": "Compares your current price with the average of all intervals in the next hour (4 intervals). All trend sensors (1h12h) share the same base: your current price — they differ only in window size. Larger windows include more future hours and smooth out short-term spikes. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Decision guide: 'rising' = ACT NOW, your current price is cheaper than the next hour average. 'falling' = WAIT, the next hour average is cheaper than now. 'stable' = timing doesn't matter. Common misconception: 'rising' does NOT mean 'too late' — it means right now is a good price! Works regardless of absolute price level." "usage_tips": "Decision guide: 'rising' = ACT NOW, your current price is cheaper than the next hour. 'falling' = WAIT, cheaper prices coming. 'stable' = timing doesn't matter. Common misconception: 'rising' does NOT mean 'too late' — it means right now is a good price! Works regardless of absolute price level."
}, },
"price_outlook_2h": { "price_trend_2h": {
"description": "Price outlook for the next 2 hours", "description": "Price trend for the next 2 hours",
"long_description": "Compares your current price with the average of all intervals in the next 2 hours (8 intervals). 'rising' = current price is below the 2h window average; 'falling' = window average is cheaper than now. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).", "long_description": "Compares your current price with the average of all intervals in the next 2 hours (8 intervals) — the entire window from now, not just the later part. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Appliances: 'rising' = start now, you're at a good price relative to the next 2h. 'falling' = better prices ahead, postpone if possible. 'stable' = doesn't matter, start when convenient. Pair with price_trajectory_2h to distinguish 'falling now, rising later' from 'falling throughout'." "usage_tips": "Appliances: 'rising' = start now, you're at a good price. 'falling' = better prices coming in 2h, postpone if possible. 'stable' = doesn't matter, start when convenient. Don't wait for 'stable' — if it's 'rising', NOW is actually the best time."
}, },
"price_outlook_3h": { "price_trend_3h": {
"description": "Price outlook for the next 3 hours", "description": "Price trend for the next 3 hours",
"long_description": "Compares your current price with the average of all intervals in the next 3 hours (12 intervals). 'rising' = current price is below the 3h window average; 'falling' = window average is cheaper than now. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).", "long_description": "Compares your current price with the average of all intervals in the next 3 hours (12 intervals) — the entire window from now, not just the later part. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Eco programs: 'rising' = start the eco cycle now, prices go up from here on average. 'falling' = wait, cheaper overall window coming. Combine with avg sensor for price cap: start when outlook is 'rising' or 'stable' AND avg < your limit." "usage_tips": "Eco programs: 'rising' = start the eco cycle now, prices go up from here. 'falling' = wait, cheaper window coming. Combine with avg sensor for price cap: start when trend is 'rising' or 'stable' AND avg < your limit. Works in any season."
}, },
"price_outlook_4h": { "price_trend_4h": {
"description": "Price outlook for the next 4 hours", "description": "Price trend for the next 4 hours",
"long_description": "Compares your current price with the average of all intervals in the next 4 hours (16 intervals). 'rising' = current price is below the 4h window average; 'falling' = window average is cheaper than now. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).", "long_description": "Compares your current price with the average of all intervals in the next 4 hours (16 intervals) — the entire window from now, not just the later part. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Heat pump/battery: 'rising' = charge now, you're at a relative low. 'falling' = wait for better window on average. Important: 'strongly_falling' at a price minimum means the 4h average is much cheaper — but combine with price_trajectory_4h to tell if prices are still dropping or already bouncing back." "usage_tips": "Heat pump/battery: 'rising' = charge now, you're at a relative low. 'falling' = wait for better charging window. 'stable' = charge when needed. Works regardless of absolute price — finds relative best time whether prices are 10 or 50 cents."
}, },
"price_outlook_5h": { "price_trend_5h": {
"description": "Price outlook for the next 5 hours", "description": "Price trend for the next 5 hours",
"long_description": "Compares your current price with the average of all intervals in the next 5 hours (20 intervals). 'rising' = current price is below the 5h window average; 'falling' = window average is cheaper than now. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).", "long_description": "Compares your current price with the average of all intervals in the next 5 hours (20 intervals) — the entire window from now, not just the later part. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Extended operations: 'rising' or 'stable' = good time to start, prices won't be cheaper on average. 'falling' = wait if your schedule allows. Adapts to market conditions — finds best relative timing in any price environment." "usage_tips": "Extended operations: 'rising' or 'stable' = good time to start, prices won't be cheaper. 'falling' = wait if your schedule allows. Adapts to market conditions — finds best relative timing in any price environment."
}, },
"price_outlook_6h": { "price_trend_6h": {
"description": "Price outlook for the next 6 hours", "description": "Price trend for the next 6 hours",
"long_description": "Compares your current price with the average of all intervals in the next 6 hours (24 intervals). 'rising' = current price is below the 6h window average; 'falling' = window average is cheaper than now. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).", "long_description": "Compares your current price with the average of all intervals in the next 6 hours (24 intervals) — the entire window from now, not just the later part. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Evening decisions: 'rising' = use electricity now while it's relatively cheap. 'falling' = evening/night prices will be better on average, wait if possible. Automatically adjusts to winter/summer price levels — no fixed thresholds needed." "usage_tips": "Evening decisions: 'rising' = use electricity now while it's relatively cheap. 'falling' = evening/night prices will be better, wait if possible. Automatically adjusts to winter/summer price levels — no fixed thresholds needed."
}, },
"price_outlook_8h": { "price_trend_8h": {
"description": "Price outlook for the next 8 hours", "description": "Price trend for the next 8 hours",
"long_description": "Compares your current price with the average of all intervals in the next 8 hours (32 intervals). 'rising' = current price is below the 8h window average; 'falling' = window average is cheaper than now. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).", "long_description": "Compares your current price with the average of all intervals in the next 8 hours (32 intervals) — the entire window from now, not just the later part. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Night planning: 'rising' = tonight/tomorrow will be more expensive on average, use power now. 'falling' = night prices will be cheaper, worth waiting. 'stable' = start when convenient. Works year-round without manual threshold adjustments." "usage_tips": "Night planning: 'rising' = tonight/tomorrow will be more expensive, use power now. 'falling' = night prices will be cheaper, worth waiting. 'stable' = start when convenient. Works year-round without manual threshold adjustments."
}, },
"price_outlook_12h": { "price_trend_12h": {
"description": "Price outlook for the next 12 hours", "description": "Price trend for the next 12 hours",
"long_description": "Compares your current price with the average of all intervals in the next 12 hours (48 intervals). 'rising' = current price is below the 12h window average; 'falling' = window average is cheaper than now. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).", "long_description": "Compares your current price with the average of all intervals in the next 12 hours (48 intervals) — the entire window from now, not just the later part. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Strategic decisions: 'rising' = you're at a low point relative to the next 12h, good time for high-consumption tasks. 'falling' = significantly better prices coming on average, wait if possible. Best combined with avg sensor price cap for absolute limits." "usage_tips": "Strategic decisions: 'rising' = you're at a low point, good time for high-consumption tasks. 'falling' = significantly better prices coming, wait if possible. Best combined with avg sensor price cap for absolute limits."
},
"price_trajectory_2h": {
"description": "Price direction within the next 2-hour window",
"long_description": "Compares the average of the first hour (4 intervals) with the average of the second hour (4 intervals) within the next 2-hour window. 'rising' = second half more expensive than first half — prices are climbing within the window. 'falling' = second half cheaper — prices are dropping within the window. Reveals the direction of price movement inside the outlook window. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "At a price minimum: price_outlook_2h may show 'falling' (because window average is below current), but price_trajectory_2h shows 'rising' (second half more expensive than first) — revealing the upcoming reversal. Power combination: 'outlook: falling + trajectory: rising' = you're AT the minimum, act now. 'outlook: falling + trajectory: falling' = prices still dropping, keep waiting."
},
"price_trajectory_3h": {
"description": "Price direction within the next 3-hour window",
"long_description": "Compares the average of the first 1.5 hours with the average of the second 1.5 hours within the next 3-hour window. 'rising' = prices are climbing over the 3h window; 'falling' = prices are dropping. Reveals the direction of price movement independent of the current price level. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Appliance timing: 'outlook: strongly_falling + trajectory: rising' = you're at or past the minimum, the window average is low but prices are already recovering — start now. 'outlook: stable + trajectory: rising' = quiet market with an upward drift — no urgency but slight favor to starting now."
},
"price_trajectory_4h": {
"description": "Price direction within the next 4-hour window",
"long_description": "Compares the average of the first 2 hours with the average of the second 2 hours within the next 4-hour window. 'rising' = prices are climbing over the 4h window; 'falling' = prices are dropping. Complementary to price_outlook_4h: outlook answers 'is NOW cheap vs the window average?', trajectory answers 'are prices rising or falling within the window?'. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Heat pump charging: 'rising' = first half window is cheaper, charge now before prices climb. 'falling' = second half window is cheaper, wait if you can. 'stable' = prices flat throughout 4h — charge when convenient. Combine with outlook: if both rising, very strong signal to act now."
},
"price_trajectory_5h": {
"description": "Price direction within the next 5-hour window",
"long_description": "Compares the average of the first 2.5 hours with the average of the second 2.5 hours within the next 5-hour window. 'rising' = prices are climbing over the 5h window; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Eco/long cycles: combine both sensors — 'outlook: rising + trajectory: rising' = clear signal to start the long program now. 'outlook: falling + trajectory: rising' = you're near the bottom, good time to start before costs climb. 'outlook: falling + trajectory: falling' = wait, still getting cheaper."
},
"price_trajectory_6h": {
"description": "Price direction within the next 6-hour window",
"long_description": "Compares the average of the first 3 hours with the average of the second 3 hours within the next 6-hour window. 'rising' = prices are climbing over the 6h window; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Evening/overnight planning: 'falling' at evening peak = overnight will be cheaper, postpone. 'rising' in the morning = current morning prices are the day's low — good time for high consumption. Pairs well with daily volatility sensors to decide if 6h window optimization is worthwhile."
},
"price_trajectory_8h": {
"description": "Price direction within the next 8-hour window",
"long_description": "Compares the average of the first 4 hours with the average of the second 4 hours within the next 8-hour window. 'rising' = prices are climbing over the 8h window; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Overnight charging: 'rising' during evening = first half of night is cheapest, start charging earlier. 'falling' = second half of night will be cheapest, delay start. Useful for EV smart charging where you only need half the night's window."
},
"price_trajectory_12h": {
"description": "Price direction within the next 12-hour window",
"long_description": "Compares the average of the first 6 hours with the average of the second 6 hours within the next 12-hour window. 'rising' = prices are climbing over the 12h window; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Day-ahead planning: 'rising' at midnight = first half of day is cheaper, schedule morning loads. 'falling' = prices drop in the second part of the day, afternoon/evening scheduling is better. Useful for large scale decisions: hot water systems, home battery charge scheduling."
}, },
"current_price_trend": { "current_price_trend": {
"description": "Current price trend direction and how long it will last", "description": "Current price trend direction and how long it will last",

View file

@ -227,80 +227,45 @@
"long_description": "Viser gjennomsnittsprisen for de neste 48 intervallene (12 timer) fra og med neste 15-minutters intervall.", "long_description": "Viser gjennomsnittsprisen for de neste 48 intervallene (12 timer) fra og med neste 15-minutters intervall.",
"usage_tips": "Absolutt pristerskel: Strategiske beslutninger med pristak. Fortsett kun hvis 12t gjennomsnitt er under din maksimalt akseptable pris. Bra for utsettbare store belastninger." "usage_tips": "Absolutt pristerskel: Strategiske beslutninger med pristak. Fortsett kun hvis 12t gjennomsnitt er under din maksimalt akseptable pris. Bra for utsettbare store belastninger."
}, },
"price_outlook_1h": { "price_trend_1h": {
"description": "Prisutblikk for neste time", "description": "Pristrend for neste time",
"long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller den neste timen (4 intervaller). Alle utblikk-sensorer (1t12t) har samme utgangspunkt: din nåværende pris — de skiller seg bare i vindustørrelse. Større vinduer dekker flere fremtidige timer og jevner ut kortsiktige topper. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).", "long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller den neste timen (4 intervaller). Alle trendsensorer (1t12t) har samme utgangspunkt: din nåværende pris — de skiller seg bare i vindustørrelse. Større vinduer dekker flere fremtidige timer og jevner ut kortsiktige topper. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).",
"usage_tips": "Beslutningshjelp: 'stigende' = HANDLE NÅ, din nåværende pris er gunstigere enn vindusgjennomsnittet. 'fallende' = VENT, vindusgjennomsnittet er billigere enn nå. 'stabil' = timing spiller ingen rolle. Vanlig misforståelse: 'stigende' betyr IKKE 'for sent' — det betyr at nå er en god pris!" "usage_tips": "Beslutningshjelp: 'stigende' = HANDLE NÅ, din nåværende pris er gunstigere enn neste time. 'fallende' = VENT, billigere priser kommer. 'stabil' = timing spiller ingen rolle. Vanlig misforståelse: 'stigende' betyr IKKE 'for sent' — det betyr at nå er en god pris! Fungerer uavhengig av absolutt prisnivå."
}, },
"price_outlook_2h": { "price_trend_2h": {
"description": "Prisutblikk for neste 2 timer", "description": "Pristrend for neste 2 timer",
"long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 2 timene (8 intervaller). 'stigende' = nåværende pris er under 2t-vindusgjennomsnittet; 'fallende' = vindusgjennomsnittet er billigere enn nå. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).", "long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 2 timene (8 intervaller) — hele vinduet fra nå, ikke bare den senere delen. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).",
"usage_tips": "Apparater: 'stigende' = start nå, du har en god pris i forhold til neste 2t. 'fallende' = bedre priser venter, utsett hvis mulig. Kombiner med price_trajectory_2h for å skille 'faller nå, stiger senere' fra 'faller gjennom hele vinduet'." "usage_tips": "Apparater: 'stigende' = start nå, du har en god pris. 'fallende' = bedre priser kommer om 2t, utsett hvis mulig. 'stabil' = spiller ingen rolle, start når det passer. Ikke vent på 'stabil' — ved 'stigende' er NÅ det beste tidspunktet."
}, },
"price_outlook_3h": { "price_trend_3h": {
"description": "Prisutblikk for neste 3 timer", "description": "Pristrend for neste 3 timer",
"long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 3 timene (12 intervaller). 'stigende' = nåværende pris er under 3t-vindusgjennomsnittet; 'fallende' = vindusgjennomsnittet er billigere enn nå. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).", "long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 3 timene (12 intervaller) — hele vinduet fra nå, ikke bare den senere delen. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).",
"usage_tips": "Eco-programmer: 'stigende' = start eco-syklusen nå, prisene er i snitt høyere fremover. 'fallende' = vent, billigere gjennomsnitt kommer. Kombiner med avg-sensor: start når utblikk er 'stigende' eller 'stabil' OG avg < din grense." "usage_tips": "Eco-programmer: 'stigende' = start eco-syklusen nå, prisene stiger herfra. 'fallende' = vent, billigere vindu kommer. Kombiner med avg-sensor: start når trend er 'stigende' eller 'stabil' OG avg < din grense. Fungerer i enhver sesong."
}, },
"price_outlook_4h": { "price_trend_4h": {
"description": "Prisutblikk for neste 4 timer", "description": "Pristrend for neste 4 timer",
"long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 4 timene (16 intervaller). 'stigende' = nåværende pris er under 4t-vindusgjennomsnittet; 'fallende' = vindusgjennomsnittet er billigere enn nå. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).", "long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 4 timene (16 intervaller) — hele vinduet fra nå, ikke bare den senere delen. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).",
"usage_tips": "Varmepumpe/batteri: 'stigende' = lad nå, du er på et relativt lavpunkt. 'fallende' = vent på bedre gjennomsnittsvindu. Kombiner med price_trajectory_4h for å se om prisene fortsatt faller eller allerede stiger igjen." "usage_tips": "Varmepumpe/batteri: 'stigende' = lad nå, du er på et relativt lavpunkt. 'fallende' = vent på bedre ladevindu. 'stabil' = lad etter behov. Fungerer uavhengig av prisnivå — finner relativ beste tid enten prisene er 10 eller 50 øre."
}, },
"price_outlook_5h": { "price_trend_5h": {
"description": "Prisutblikk for neste 5 timer", "description": "Pristrend for neste 5 timer",
"long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 5 timene (20 intervaller). 'stigende' = nåværende pris er under 5t-vindusgjennomsnittet; 'fallende' = vindusgjennomsnittet er billigere enn nå. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).", "long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 5 timene (20 intervaller) — hele vinduet fra nå, ikke bare den senere delen. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).",
"usage_tips": "Utvidede sykluser: 'stigende' eller 'stabil' = godt tidspunkt å starte. 'fallende' = vent hvis planen din tillater det." "usage_tips": "Utvidede sykluser: 'stigende' eller 'stabil' = godt tidspunkt å starte, prisene blir ikke billigere. 'fallende' = vent hvis planen din tillater det. Tilpasser seg markedet — finner beste relative timing i ethvert prismiljø."
}, },
"price_outlook_6h": { "price_trend_6h": {
"description": "Prisutblikk for neste 6 timer", "description": "Pristrend for neste 6 timer",
"long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 6 timene (24 intervaller). 'stigende' = nåværende pris er under 6t-vindusgjennomsnittet; 'fallende' = vindusgjennomsnittet er billigere enn nå. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).", "long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 6 timene (24 intervaller) — hele vinduet fra nå, ikke bare den senere delen. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).",
"usage_tips": "Kveldsbeslutninger: 'stigende' = bruk strøm nå mens den er relativt billig. 'fallende' = kvelds-/nattprisene blir bedre i snitt, vent hvis mulig." "usage_tips": "Kveldsbeslutninger: 'stigende' = bruk strøm nå mens den er relativt billig. 'fallende' = kvelds-/nattprisene blir bedre, vent hvis mulig. Justerer automatisk til vinter/sommer prisnivåer — ingen faste terskler nødvendig."
}, },
"price_outlook_8h": { "price_trend_8h": {
"description": "Prisutblikk for neste 8 timer", "description": "Pristrend for neste 8 timer",
"long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 8 timene (32 intervaller). 'stigende' = nåværende pris er under 8t-vindusgjennomsnittet; 'fallende' = vindusgjennomsnittet er billigere enn nå. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).", "long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 8 timene (32 intervaller) — hele vinduet fra nå, ikke bare den senere delen. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).",
"usage_tips": "Nattplanlegging: 'stigende' = i natt/i morgen blir dyrere i snitt, bruk strøm nå. 'fallende' = nattprisene blir billigere, verdt å vente." "usage_tips": "Nattplanlegging: 'stigende' = i natt/i morgen blir dyrere, bruk strøm nå. 'fallende' = nattprisene blir billigere, verdt å vente. 'stabil' = start etter behov. Fungerer hele året uten manuelle terskeljusteringer."
}, },
"price_outlook_12h": { "price_trend_12h": {
"description": "Prisutblikk for de neste 12 timene", "description": "Pristrend for de neste 12 timene",
"long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 12 timene (48 intervaller). 'stigende' = nåværende pris er under 12t-vindusgjennomsnittet; 'fallende' = vindusgjennomsnittet er billigere enn nå. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).", "long_description": "Sammenligner din nåværende pris med gjennomsnittet av alle intervaller de neste 12 timene (48 intervaller) — hele vinduet fra nå, ikke bare den senere delen. Stigende/fallende ved ±3%, sterkt ved ±9% (konfigurerbart, volatilitetstilpasset).",
"usage_tips": "Strategiske beslutninger: 'stigende' = du er på et lavpunkt relativt til neste 12t, godt tidspunkt for strømkrevende oppgaver. 'fallende' = betydelig bedre priser i snitt venter, vent hvis mulig." "usage_tips": "Strategiske beslutninger: 'stigende' = du er på et lavpunkt, godt tidspunkt for strømkrevende oppgaver. 'fallende' = betydelig bedre priser kommer, vent hvis mulig. Best kombinert med avg-sensor for absolutte prisgrenser."
},
"price_trajectory_2h": {
"description": "Prisretning innenfor neste 2-timers vindu",
"long_description": "Compares the average of the first hour (4 intervals) with the average of the second hour (4 intervals) within the next 2-hour window. 'rising' = second half more expensive than first half — prices are climbing within the window. 'falling' = second half cheaper — prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "At a price minimum: price_outlook_2h may show 'falling' (window average is below current), but price_trajectory_2h shows 'rising' (second half more expensive than first) — revealing the upcoming reversal. 'outlook: falling + trajectory: rising' = you're AT the minimum, act now."
},
"price_trajectory_3h": {
"description": "Prisretning innenfor neste 3-timers vindu",
"long_description": "Compares the average of the first 1.5 hours with the average of the second 1.5 hours within the next 3-hour window. 'rising' = prices are climbing over the 3h window; 'falling' = prices are dropping. Reveals direction of movement independent of current price level. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Appliance timing: 'outlook: strongly_falling + trajectory: rising' = you're at or past the minimum, prices are already recovering — start now."
},
"price_trajectory_4h": {
"description": "Prisretning innenfor neste 4-timers vindu",
"long_description": "Compares the average of the first 2 hours with the average of the second 2 hours within the next 4-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Complements price_outlook_4h: outlook answers 'is NOW cheap vs window average?', trajectory answers 'are prices rising or falling within the window?'. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Heat pump charging: 'rising' = first half is cheaper, charge now. 'falling' = second half is cheaper, wait. Combine with outlook: if both rising, very strong signal to act now."
},
"price_trajectory_5h": {
"description": "Prisretning innenfor neste 5-timers vindu",
"long_description": "Compares the average of the first 2.5 hours with the average of the second 2.5 hours within the next 5-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Long cycles: 'outlook: rising + trajectory: rising' = clear signal to start now. 'outlook: falling + trajectory: rising' = you're near the bottom, good time to start before costs climb."
},
"price_trajectory_6h": {
"description": "Prisretning innenfor neste 6-timers vindu",
"long_description": "Compares the average of the first 3 hours with the average of the second 3 hours within the next 6-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Evening/overnight planning: 'falling' at evening peak = overnight will be cheaper, postpone. 'rising' in the morning = current morning prices are the low — good time for high consumption."
},
"price_trajectory_8h": {
"description": "Prisretning innenfor neste 8-timers vindu",
"long_description": "Compares the average of the first 4 hours with the average of the second 4 hours within the next 8-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Overnight charging: 'rising' during evening = first half of night is cheapest, start charging earlier. 'falling' = second half of night will be cheapest, delay start."
},
"price_trajectory_12h": {
"description": "Prisretning innenfor neste 12-timers vindu",
"long_description": "Compares the average of the first 6 hours with the average of the second 6 hours within the next 12-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "Day-ahead planning: 'rising' at midnight = first half of day is cheaper, schedule morning loads. 'falling' = prices drop in second part of the day, afternoon/evening scheduling is better."
}, },
"current_price_trend": { "current_price_trend": {
"description": "Nåværende pristrend-retning og hvor lenge den varer", "description": "Nåværende pristrend-retning og hvor lenge den varer",

View file

@ -227,80 +227,45 @@
"long_description": "Toont de gemiddelde prijs voor de volgende 48 intervallen (12 uur) vanaf het volgende 15-minuten interval.", "long_description": "Toont de gemiddelde prijs voor de volgende 48 intervallen (12 uur) vanaf het volgende 15-minuten interval.",
"usage_tips": "Absolute prijsdrempel: Strategische beslissingen met prijslimieten. Ga alleen door als 12u gemiddelde onder je maximaal acceptabele prijs is. Goed voor uitgestelde grote belastingen." "usage_tips": "Absolute prijsdrempel: Strategische beslissingen met prijslimieten. Ga alleen door als 12u gemiddelde onder je maximaal acceptabele prijs is. Goed voor uitgestelde grote belastingen."
}, },
"price_outlook_1h": { "price_trend_1h": {
"description": "Prijsvooruitzicht voor het volgende uur", "description": "Prijstrend voor het volgende uur",
"long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in het volgende uur (4 intervallen). Alle vooruitzicht-sensoren (1u12u) delen hetzelfde uitgangspunt: je huidige prijs — ze verschillen alleen in venstergrootte. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).", "long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in het volgende uur (4 intervallen). Alle trendsensoren (1u12u) delen hetzelfde uitgangspunt: je huidige prijs — ze verschillen alleen in venstergrootte. Grotere vensters omvatten meer toekomstige uren en vlakken kortstondige pieken af. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).",
"usage_tips": "Beslissingshulp: 'stijgend' = HANDEL NU, je huidige prijs is goedkoper dan het venstergemiddelde. 'dalend' = WACHT, het venstergemiddelde is goedkoper dan nu. 'stabiel' = timing maakt niet uit." "usage_tips": "Beslissingshulp: 'stijgend' = HANDEL NU, je huidige prijs is goedkoper dan het volgende uur. 'dalend' = WACHT, goedkopere prijzen komen. 'stabiel' = timing maakt niet uit. Veelvoorkomend misverstand: 'stijgend' betekent NIET 'te laat' — het betekent dat nu een goede prijs is! Werkt onafhankelijk van absoluut prijsniveau."
}, },
"price_outlook_2h": { "price_trend_2h": {
"description": "Prijsvooruitzicht voor de volgende 2 uur", "description": "Prijstrend voor de volgende 2 uur",
"long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 2 uur (8 intervallen). 'stijgend' = huidige prijs is onder het 2u-venstergemiddelde; 'dalend' = venstergemiddelde is goedkoper dan nu. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).", "long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 2 uur (8 intervallen) — het hele venster vanaf nu, niet alleen het latere deel. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).",
"usage_tips": "Apparaten: 'stijgend' = start nu, je hebt een goede prijs t.o.v. de volgende 2u. 'dalend' = betere gemiddelde prijzen komen, stel uit indien mogelijk. Combineer met price_trajectory_2h om te onderscheiden of prijzen nog dalen of al stijgen." "usage_tips": "Apparaten: 'stijgend' = start nu, je hebt een goede prijs. 'dalend' = betere prijzen komen over 2u, stel uit indien mogelijk. 'stabiel' = maakt niet uit, start wanneer het uitkomt. Wacht niet op 'stabiel' — bij 'stijgend' is NU het beste moment."
}, },
"price_outlook_3h": { "price_trend_3h": {
"description": "Prijsvooruitzicht voor de volgende 3 uur", "description": "Prijstrend voor de volgende 3 uur",
"long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 3 uur (12 intervallen). 'stijgend' = huidige prijs is onder het 3u-venstergemiddelde; 'dalend' = venstergemiddelde is goedkoper dan nu. Stijgend/dalend bij ±3%, sterk bij ±9%.", "long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 3 uur (12 intervallen) — het hele venster vanaf nu, niet alleen het latere deel. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).",
"usage_tips": "Eco-programma's: 'stijgend' = start de eco-cyclus nu, prijzen zijn gemiddeld hoger in het venster. 'dalend' = wacht, goedkoper gemiddeld venster komt." "usage_tips": "Eco-programma's: 'stijgend' = start de eco-cyclus nu, prijzen stijgen vanaf hier. 'dalend' = wacht, goedkoper venster komt. Combineer met avg-sensor: start wanneer trend 'stijgend' of 'stabiel' EN avg < je limiet. Werkt in elk seizoen."
}, },
"price_outlook_4h": { "price_trend_4h": {
"description": "Prijsvooruitzicht voor de volgende 4 uur", "description": "Prijstrend voor de volgende 4 uur",
"long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 4 uur (16 intervallen). 'stijgend' = huidige prijs is onder het 4u-venstergemiddelde; 'dalend' = venstergemiddelde is goedkoper dan nu. Stijgend/dalend bij ±3%, sterk bij ±9%.", "long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 4 uur (16 intervallen) — het hele venster vanaf nu, niet alleen het latere deel. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).",
"usage_tips": "Warmtepomp/batterij: 'stijgend' = laad nu, je zit op een relatief dieptepunt. Combineer met price_trajectory_4h om te zien of prijzen nog dalen of al stijgen." "usage_tips": "Warmtepomp/batterij: 'stijgend' = laad nu, je zit op een relatief dieptepunt. 'dalend' = wacht op beter laadvenster. 'stabiel' = laad wanneer nodig. Werkt ongeacht prijsniveau — vindt relatief beste tijd of prijzen nu 10 of 50 cent zijn."
}, },
"price_outlook_5h": { "price_trend_5h": {
"description": "Prijsvooruitzicht voor de volgende 5 uur", "description": "Prijstrend voor de volgende 5 uur",
"long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 5 uur (20 intervallen). 'stijgend' = huidige prijs is onder het 5u-venstergemiddelde; 'dalend' = venstergemiddelde is goedkoper dan nu. Stijgend/dalend bij ±3%, sterk bij ±9%.", "long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 5 uur (20 intervallen) — het hele venster vanaf nu, niet alleen het latere deel. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).",
"usage_tips": "Uitgebreide cycli: 'stijgend' of 'stabiel' = goed moment om te starten. 'dalend' = wacht als je planning het toelaat." "usage_tips": "Uitgebreide cycli: 'stijgend' of 'stabiel' = goed moment om te starten, prijzen worden niet goedkoper. 'dalend' = wacht als je planning het toelaat. Past zich aan de markt aan — vindt beste relatieve timing in elke prijsomgeving."
}, },
"price_outlook_6h": { "price_trend_6h": {
"description": "Prijsvooruitzicht voor de volgende 6 uur", "description": "Prijstrend voor de volgende 6 uur",
"long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 6 uur (24 intervallen). 'stijgend' = huidige prijs is onder het 6u-venstergemiddelde; 'dalend' = venstergemiddelde is goedkoper dan nu. Stijgend/dalend bij ±3%, sterk bij ±9%.", "long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 6 uur (24 intervallen) — het hele venster vanaf nu, niet alleen het latere deel. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).",
"usage_tips": "Avandbeslissingen: 'stijgend' = gebruik stroom nu terwijl het relatief goedkoop is. 'dalend' = avond-/nachtprijzen worden gemiddeld beter, wacht indien mogelijk." "usage_tips": "Avandbeslissingen: 'stijgend' = gebruik stroom nu terwijl het relatief goedkoop is. 'dalend' = avond-/nachtprijzen worden beter, wacht indien mogelijk. Past automatisch aan winter/zomer prijsniveaus aan — geen vaste drempels nodig."
}, },
"price_outlook_8h": { "price_trend_8h": {
"description": "Prijsvooruitzicht voor de volgende 8 uur", "description": "Prijstrend voor de volgende 8 uur",
"long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 8 uur (32 intervallen). 'stijgend' = huidige prijs is onder het 8u-venstergemiddelde; 'dalend' = venstergemiddelde is goedkoper dan nu. Stijgend/dalend bij ±3%, sterk bij ±9%.", "long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de volgende 8 uur (32 intervallen) — het hele venster vanaf nu, niet alleen het latere deel. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).",
"usage_tips": "Nachtplanning: 'stijgend' = vanavond/morgen wordt gemiddeld duurder, gebruik stroom nu. 'dalend' = nachtprijzen worden goedkoper, wachten loont." "usage_tips": "Nachtplanning: 'stijgend' = vanavond/morgen wordt duurder, gebruik stroom nu. 'dalend' = nachtprijzen worden goedkoper, wachten loont. 'stabiel' = start wanneer het uitkomt. Werkt het hele jaar door zonder handmatige drempelaanpassingen."
}, },
"price_outlook_12h": { "price_trend_12h": {
"description": "Prijsvooruitzicht voor de komende 12 uur", "description": "Prijstrend voor de komende 12 uur",
"long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de komende 12 uur (48 intervallen). 'stijgend' = huidige prijs is onder het 12u-venstergemiddelde; 'dalend' = venstergemiddelde is goedkoper dan nu. Stijgend/dalend bij ±3%, sterk bij ±9%.", "long_description": "Vergelijkt je huidige prijs met het gemiddelde van alle intervallen in de komende 12 uur (48 intervallen) — het hele venster vanaf nu, niet alleen het latere deel. Stijgend/dalend bij ±3%, sterk bij ±9% (configureerbaar, volatiliteitsadaptief).",
"usage_tips": "Strategische beslissingen: 'stijgend' = je zit op een dieptepunt t.o.v. de volgende 12u, goed moment voor stroomintensieve taken. 'dalend' = aanzienlijk betere gemiddelde prijzen komen." "usage_tips": "Strategische beslissingen: 'stijgend' = je zit op een dieptepunt, goed moment voor stroomintensieve taken. 'dalend' = aanzienlijk betere prijzen komen, wacht indien mogelijk. Het beste gecombineerd met avg-sensor voor absolute prijslimieten."
},
"price_trajectory_2h": {
"description": "Prijsrichting binnen het volgende 2-uurs venster",
"long_description": "Compares the average of the first hour with the average of the second hour within the next 2-hour window. 'rising' = second half more expensive than first half. 'falling' = second half cheaper. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "At a price minimum: price_outlook_2h may show 'falling' (window average is below current), but price_trajectory_2h shows 'rising' (second half more expensive) — revealing the upcoming reversal. 'outlook: falling + trajectory: rising' = you're AT the minimum, act now."
},
"price_trajectory_3h": {
"description": "Prijsrichting binnen het volgende 3-uurs venster",
"long_description": "Compares the average of the first 1.5 hours with the average of the second 1.5 hours within the next 3-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Appliance timing: 'outlook: strongly_falling + trajectory: rising' = you're at or past the minimum, prices are already recovering — start now."
},
"price_trajectory_4h": {
"description": "Prijsrichting binnen het volgende 4-uurs venster",
"long_description": "Compares the average of the first 2 hours with the average of the second 2 hours within the next 4-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Complements price_outlook_4h. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Heat pump charging: 'rising' = first half is cheaper, charge now. 'falling' = second half is cheaper, wait. Combine with outlook: if both rising, very strong signal to act now."
},
"price_trajectory_5h": {
"description": "Prijsrichting binnen het volgende 5-uurs venster",
"long_description": "Compares the average of the first 2.5 hours with the average of the second 2.5 hours within the next 5-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Long cycles: 'outlook: rising + trajectory: rising' = clear signal to start now. 'outlook: falling + trajectory: rising' = you're near the bottom, good time to start."
},
"price_trajectory_6h": {
"description": "Prijsrichting binnen het volgende 6-uurs venster",
"long_description": "Compares the average of the first 3 hours with the average of the second 3 hours within the next 6-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Evening/overnight planning: 'falling' at evening peak = overnight will be cheaper, postpone. 'rising' in the morning = current morning prices are the low."
},
"price_trajectory_8h": {
"description": "Prijsrichting binnen het volgende 8-uurs venster",
"long_description": "Compares the average of the first 4 hours with the average of the second 4 hours within the next 8-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Overnight charging: 'rising' during evening = first half of night is cheapest, start charging earlier. 'falling' = second half will be cheapest, delay start."
},
"price_trajectory_12h": {
"description": "Prijsrichting binnen het volgende 12-uurs venster",
"long_description": "Compares the average of the first 6 hours with the average of the second 6 hours within the next 12-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Day-ahead planning: 'rising' at midnight = first half of day is cheaper, schedule morning loads. 'falling' = afternoon/evening scheduling is better."
}, },
"current_price_trend": { "current_price_trend": {
"description": "Huidige prijstrend-richting en hoe lang deze aanhoudt", "description": "Huidige prijstrend-richting en hoe lang deze aanhoudt",
@ -310,7 +275,7 @@
"next_price_trend_change": { "next_price_trend_change": {
"description": "Wanneer de volgende significante prijstrendwijziging zal plaatsvinden", "description": "Wanneer de volgende significante prijstrendwijziging zal plaatsvinden",
"long_description": "Scant de komende 24 uur (96 intervallen) om te vinden wanneer de prijstrend-richting zal veranderen. Alleen richtingswijzigingen tellen: stijgend/sterk stijgend vormen één groep, dalend/sterk dalend een andere, stabiel is apart. Een verandering van stijgend naar sterk stijgend is GEEN trendwijziging. Gebruikt volatiliteit-adaptieve drempelwaarden (standaard: ±3%/±9%) met hysterese (standaard: 3 opeenvolgende intervallen). Retourneert het tijdstempel wanneer de wijziging wordt verwacht.", "long_description": "Scant de komende 24 uur (96 intervallen) om te vinden wanneer de prijstrend-richting zal veranderen. Alleen richtingswijzigingen tellen: stijgend/sterk stijgend vormen één groep, dalend/sterk dalend een andere, stabiel is apart. Een verandering van stijgend naar sterk stijgend is GEEN trendwijziging. Gebruikt volatiliteit-adaptieve drempelwaarden (standaard: ±3%/±9%) met hysterese (standaard: 3 opeenvolgende intervallen). Retourneert het tijdstempel wanneer de wijziging wordt verwacht.",
"usage_tips": "Gebeurtenisgestuurde automatisering: Trigger acties WANNEER trend wijzigt, niet OVER X uur. Voorbeeld: 'Laad EV wanneer volgende trendwijziging dalende prijzen toont' of 'Start vaatwasser voordat prijzen stijgen'. Vult tijdvenster-sensors aan (price_outlook_Xh) die beantwoorden 'ZULLEN prijzen over X uur hoger zijn?'" "usage_tips": "Gebeurtenisgestuurde automatisering: Trigger acties WANNEER trend wijzigt, niet OVER X uur. Voorbeeld: 'Laad EV wanneer volgende trendwijziging dalende prijzen toont' of 'Start vaatwasser voordat prijzen stijgen'. Vult tijdvenster-sensors aan (price_trend_Xh) die beantwoorden 'ZULLEN prijzen over X uur hoger zijn?'"
}, },
"trend_change_in_minutes": { "trend_change_in_minutes": {
"description": "Tijd tot de volgende prijstrendwijziging", "description": "Tijd tot de volgende prijstrendwijziging",

View file

@ -227,80 +227,45 @@
"long_description": "Visar genomsnittspriset för nästa 48 intervaller (12 timmar) från och med nästa 15-minuters intervall.", "long_description": "Visar genomsnittspriset för nästa 48 intervaller (12 timmar) från och med nästa 15-minuters intervall.",
"usage_tips": "Absolut priströskel: Strategiska beslut med pristak. Fortsätt endast om 12t genomsnitt är under ditt maximalt acceptabla pris. Bra för uppskjutbara stora laster." "usage_tips": "Absolut priströskel: Strategiska beslut med pristak. Fortsätt endast om 12t genomsnitt är under ditt maximalt acceptabla pris. Bra för uppskjutbara stora laster."
}, },
"price_outlook_1h": { "price_trend_1h": {
"description": "Prisöversikt för nästa timme", "description": "Pristrend för nästa timme",
"long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under nästa timme (4 intervaller). Alla översiktssensorer (1h12h) delar samma utgångspunkt: ditt nuvarande pris — de skiljer sig bara i fönsterstorlek. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).", "long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under nästa timme (4 intervaller). Alla trendsensorer (1t12t) delar samma utgångspunkt: ditt nuvarande pris — de skiljer sig bara i fönsterstorlek. Större fönster täcker fler framtida timmar och jämnar ut kortsiktiga toppar. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).",
"usage_tips": "Beslutstöd: 'stigande' = AGERA NU, ditt nuvarande pris är billigare än fönstergenomsnittet. 'fallande' = VÄNTA, fönstergenomsnittet är billigare än nu. 'stabil' = timing spelar ingen roll." "usage_tips": "Beslutsstöd: 'stigande' = AGERA NU, ditt nuvarande pris är billigare än nästa timme. 'fallande' = VÄNTA, billigare priser kommer. 'stabil' = timing spelar ingen roll. Vanligt missförstånd: 'stigande' betyder INTE 'för sent' — det betyder att nu är ett bra pris! Fungerar oberoende av absolut prisnivå."
}, },
"price_outlook_2h": { "price_trend_2h": {
"description": "Prisöversikt för nästa 2 timmar", "description": "Pristrend för nästa 2 timmar",
"long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 2 timmarna (8 intervaller). 'stigande' = nuvarande pris under 2h-fönstergenomsnittet; 'fallande' = fönstergenomsnittet är billigare än nu. Stigande/fallande vid ±3%, kraftigt vid ±9%.", "long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 2 timmarna (8 intervaller) — hela fönstret från nu, inte bara den senare delen. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).",
"usage_tips": "Apparater: 'stigande' = starta nu, du har ett bra pris jämfört med nästa 2h. Kombinera med price_trajectory_2h för att se om priserna fortfarande faller eller redan stiger." "usage_tips": "Apparater: 'stigande' = starta nu, du har ett bra pris. 'fallande' = bättre priser kommer om 2t, skjut upp om möjligt. 'stabil' = spelar ingen roll, starta när det passar. Vänta inte på 'stabil' — vid 'stigande' är NU den bästa tiden."
}, },
"price_outlook_3h": { "price_trend_3h": {
"description": "Prisöversikt för nästa 3 timmar", "description": "Pristrend för nästa 3 timmar",
"long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 3 timmarna (12 intervaller). 'stigande' = nuvarande pris under 3h-fönstergenomsnittet; 'fallande' = fönstergenomsnittet är billigare än nu. Stigande/fallande vid ±3%, kraftigt vid ±9%.", "long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 3 timmarna (12 intervaller) — hela fönstret från nu, inte bara den senare delen. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).",
"usage_tips": "Eco-program: 'stigande' = starta eco-cykeln nu, priser är i genomsnitt högre i fönstret. 'fallande' = vänta, billigare genomsnittligt fönster kommer." "usage_tips": "Eco-program: 'stigande' = starta eco-cykeln nu, priserna stiger härifrån. 'fallande' = vänta, billigare fönster kommer. Kombinera med avg-sensor: starta när trend är 'stigande' eller 'stabil' OCH avg < din gräns. Fungerar under alla säsonger."
}, },
"price_outlook_4h": { "price_trend_4h": {
"description": "Prisöversikt för nästa 4 timmar", "description": "Pristrend för nästa 4 timmar",
"long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 4 timmarna (16 intervaller). 'stigande' = nuvarande pris under 4h-fönstergenomsnittet; 'fallande' = fönstergenomsnittet är billigare än nu. Stigande/fallande vid ±3%, kraftigt vid ±9%.", "long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 4 timmarna (16 intervaller) — hela fönstret från nu, inte bara den senare delen. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).",
"usage_tips": "Värmepump/batteri: 'stigande' = ladda nu, du är på en relativ lågpunkt. Kombinera med price_trajectory_4h för att se om priserna fortfarande faller eller redan stiger." "usage_tips": "Värmepump/batteri: 'stigande' = ladda nu, du är på en relativ lågpunkt. 'fallande' = vänta på bättre laddningsfönster. 'stabil' = ladda efter behov. Fungerar oavsett prisnivå — hittar relativ bästa tid oavsett om priserna är 10 eller 50 öre."
}, },
"price_outlook_5h": { "price_trend_5h": {
"description": "Prisöversikt för nästa 5 timmar", "description": "Pristrend för nästa 5 timmar",
"long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 5 timmarna (20 intervaller). 'stigande' = nuvarande pris under 5h-fönstergenomsnittet; 'fallande' = fönstergenomsnittet är billigare än nu. Stigande/fallande vid ±3%, kraftigt vid ±9%.", "long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 5 timmarna (20 intervaller) — hela fönstret från nu, inte bara den senare delen. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).",
"usage_tips": "Utökade cykler: 'stigande' eller 'stabil' = bra tid att starta. 'fallande' = vänta om din planering tillåter det." "usage_tips": "Utökade cykler: 'stigande' eller 'stabil' = bra tid att starta, priserna blir inte billigare. 'fallande' = vänta om din planering tillåter det. Anpassar sig till marknaden — hittar bästa relativa timing i vilken prismiljö som helst."
}, },
"price_outlook_6h": { "price_trend_6h": {
"description": "Prisöversikt för nästa 6 timmar", "description": "Pristrend för nästa 6 timmar",
"long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 6 timmarna (24 intervaller). 'stigande' = nuvarande pris under 6h-fönstergenomsnittet; 'fallande' = fönstergenomsnittet är billigare än nu. Stigande/fallande vid ±3%, kraftigt vid ±9%.", "long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 6 timmarna (24 intervaller) — hela fönstret från nu, inte bara den senare delen. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).",
"usage_tips": "Kvällsbeslut: 'stigande' = använd el nu medan den är relativt billig. 'fallande' = kvälls-/nattpriserna blir bättre i genomsnitt, vänta om möjligt." "usage_tips": "Kvällsbeslut: 'stigande' = använd el nu medan den är relativt billig. 'fallande' = kvälls-/nattpriserna blir bättre, vänta om möjligt. Justerar automatiskt till vinter/sommar prisnivåer — inga fasta trösklar behövs."
}, },
"price_outlook_8h": { "price_trend_8h": {
"description": "Prisöversikt för nästa 8 timmar", "description": "Pristrend för nästa 8 timmar",
"long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 8 timmarna (32 intervaller). 'stigande' = nuvarande pris under 8h-fönstergenomsnittet; 'fallande' = fönstergenomsnittet är billigare än nu. Stigande/fallande vid ±3%, kraftigt vid ±9%.", "long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 8 timmarna (32 intervaller) — hela fönstret från nu, inte bara den senare delen. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).",
"usage_tips": "Nattplanering: 'stigande' = ikväll/imorgon blir dyrare i genomsnitt, använd el nu. 'fallande' = nattpriserna blir billigare, värt att vänta." "usage_tips": "Nattplanering: 'stigande' = ikväll/imorgon blir dyrare, använd el nu. 'fallande' = nattpriserna blir billigare, värt att vänta. 'stabil' = starta efter behov. Fungerar året runt utan manuella tröskeljusteringar."
}, },
"price_outlook_12h": { "price_trend_12h": {
"description": "Prisöversikt för nästa 12 timmar", "description": "Pristrend för nästa 12 timmar",
"long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 12 timmarna (48 intervaller). 'stigande' = nuvarande pris under 12h-fönstergenomsnittet; 'fallande' = fönstergenomsnittet är billigare än nu. Stigande/fallande vid ±3%, kraftigt vid ±9%.", "long_description": "Jämför ditt nuvarande pris med genomsnittet av alla intervaller under de nästa 12 timmarna (48 intervaller) — hela fönstret från nu, inte bara den senare delen. Stigande/fallande vid ±3%, kraftigt vid ±9% (konfigurerbart, volatilitetsadaptivt).",
"usage_tips": "Strategiska beslut: 'stigande' = du är på en lågpunkt relativt till nästa 12h, bra tid för strömkrävande uppgifter. 'fallande' = avsevärt bättre genomsnittspriser kommer." "usage_tips": "Strategiska beslut: 'stigande' = du är på en lågpunkt, bra tid för strömkrävande uppgifter. 'fallande' = avsevärt bättre priser kommer, vänta om möjligt. Bäst kombinerad med avg-sensor för absoluta prisgränser."
},
"price_trajectory_2h": {
"description": "Prisutveckling inom nästa 2-timmars fönster",
"long_description": "Compares the average of the first hour with the average of the second hour within the next 2-hour window. 'rising' = second half more expensive than first half. 'falling' = second half cheaper. Rising/falling at ±3%, strongly at ±9% (configurable, volatility-adaptive).",
"usage_tips": "At a price minimum: price_outlook_2h may show 'falling' (window average is below current), but price_trajectory_2h shows 'rising' (second half more expensive) — revealing the upcoming reversal. 'outlook: falling + trajectory: rising' = you're AT the minimum, act now."
},
"price_trajectory_3h": {
"description": "Prisutveckling inom nästa 3-timmars fönster",
"long_description": "Compares the average of the first 1.5 hours with the average of the second 1.5 hours within the next 3-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Appliance timing: 'outlook: strongly_falling + trajectory: rising' = you're at or past the minimum, prices are already recovering — start now."
},
"price_trajectory_4h": {
"description": "Prisutveckling inom nästa 4-timmars fönster",
"long_description": "Compares the average of the first 2 hours with the average of the second 2 hours within the next 4-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Complements price_outlook_4h. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Heat pump charging: 'rising' = first half is cheaper, charge now. 'falling' = second half is cheaper, wait. Combine with outlook: if both rising, very strong signal to act now."
},
"price_trajectory_5h": {
"description": "Prisutveckling inom nästa 5-timmars fönster",
"long_description": "Compares the average of the first 2.5 hours with the average of the second 2.5 hours within the next 5-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Long cycles: 'outlook: rising + trajectory: rising' = clear signal to start now. 'outlook: falling + trajectory: rising' = you're near the bottom, good time to start."
},
"price_trajectory_6h": {
"description": "Prisutveckling inom nästa 6-timmars fönster",
"long_description": "Compares the average of the first 3 hours with the average of the second 3 hours within the next 6-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Evening/overnight planning: 'falling' at evening peak = overnight will be cheaper, postpone. 'rising' in the morning = current morning prices are the low."
},
"price_trajectory_8h": {
"description": "Prisutveckling inom nästa 8-timmars fönster",
"long_description": "Compares the average of the first 4 hours with the average of the second 4 hours within the next 8-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Overnight charging: 'rising' during evening = first half of night is cheapest, start charging earlier. 'falling' = second half will be cheapest, delay start."
},
"price_trajectory_12h": {
"description": "Prisutveckling inom nästa 12-timmars fönster",
"long_description": "Compares the average of the first 6 hours with the average of the second 6 hours within the next 12-hour window. 'rising' = prices are climbing; 'falling' = prices are dropping. Rising/falling at ±3%, strongly at ±9%.",
"usage_tips": "Day-ahead planning: 'rising' at midnight = first half of day is cheaper, schedule morning loads. 'falling' = afternoon/evening scheduling is better."
}, },
"current_price_trend": { "current_price_trend": {
"description": "Nuvarande pristrend-riktning och hur länge den varar", "description": "Nuvarande pristrend-riktning och hur länge den varar",
@ -310,7 +275,7 @@
"next_price_trend_change": { "next_price_trend_change": {
"description": "När nästa betydande pristrendändring kommer att inträffa", "description": "När nästa betydande pristrendändring kommer att inträffa",
"long_description": "Skannar de nästa 24 timmarna (96 intervaller) för att hitta när pristrend-riktningen kommer att ändras. Bara riktningsändringar räknas: stigande/kraftigt stigande är en grupp, fallande/kraftigt fallande en annan, stabil är egen. En ändring från stigande till kraftigt stigande är INTE en trendändring. Använder volatilitetsadaptiva tröskelvärden (standard: ±3%/±9%) med hysteres (standard: 3 på varandra följande intervaller). Returnerar tidstämpeln när ändringen förväntas.", "long_description": "Skannar de nästa 24 timmarna (96 intervaller) för att hitta när pristrend-riktningen kommer att ändras. Bara riktningsändringar räknas: stigande/kraftigt stigande är en grupp, fallande/kraftigt fallande en annan, stabil är egen. En ändring från stigande till kraftigt stigande är INTE en trendändring. Använder volatilitetsadaptiva tröskelvärden (standard: ±3%/±9%) med hysteres (standard: 3 på varandra följande intervaller). Returnerar tidstämpeln när ändringen förväntas.",
"usage_tips": "Händelsestyrd automatisering: Utlös åtgärder NÄR trenden ändras, inte OM X timmar. Exempel: 'Ladda EV när nästa trendändring visar fallande priser' eller 'Starta diskmaskin innan priserna stiger'. Kompletterar tidsfönster-sensorer (price_outlook_Xh) som svarar på 'KOMMER priserna att vara högre om X timmar?'" "usage_tips": "Händelsestyrd automatisering: Utlös åtgärder NÄR trenden ändras, inte OM X timmar. Exempel: 'Ladda EV när nästa trendändring visar fallande priser' eller 'Starta diskmaskin innan priserna stiger'. Kompletterar tidsfönster-sensorer (price_trend_Xh) som svarar på 'KOMMER priserna att vara högre om X timmar?'"
}, },
"trend_change_in_minutes": { "trend_change_in_minutes": {
"description": "Tid till nästa pristrendändring", "description": "Tid till nästa pristrendändring",

View file

@ -28,10 +28,8 @@ def _add_timing_or_volatility_attributes(
def _add_cached_trend_attributes(attributes: dict, key: str, cached_data: dict) -> None: def _add_cached_trend_attributes(attributes: dict, key: str, cached_data: dict) -> None:
"""Add cached trend attributes if available.""" """Add cached trend attributes if available."""
if key.startswith("price_outlook_") and cached_data.get("trend_attributes"): if key.startswith("price_trend_") and cached_data.get("trend_attributes"):
attributes.update(cached_data["trend_attributes"]) attributes.update(cached_data["trend_attributes"])
elif key.startswith("price_trajectory_") and cached_data.get("trajectory_attributes"):
attributes.update(cached_data["trajectory_attributes"])
elif key == "current_price_trend" and cached_data.get("current_trend_attributes"): elif key == "current_price_trend" and cached_data.get("current_trend_attributes"):
# Add cached attributes (timestamp already set by platform) # Add cached attributes (timestamp already set by platform)
attributes.update(cached_data["current_trend_attributes"]) attributes.update(cached_data["current_trend_attributes"])

View file

@ -2,14 +2,13 @@
Trend calculator for price trend analysis sensors. Trend calculator for price trend analysis sensors.
This module handles all trend-related calculations: This module handles all trend-related calculations:
- Price outlook (1h-12h): Current price vs average of the next N hours - Simple price trends (1h-12h future comparison)
- Price trajectory (2h-12h): First-half vs second-half average in the window (shows turning points)
- Current trend (pure future-based 3h outlook with volatility adjustment) - Current trend (pure future-based 3h outlook with volatility adjustment)
- Next trend change prediction (with configurable N-interval hysteresis, default 3) - Next trend change prediction (with configurable N-interval hysteresis, default 3)
- Trend duration tracking (lightweight price direction scan with noise tolerance) - Trend duration tracking (lightweight price direction scan with noise tolerance)
Caching strategy: Caching strategy:
- Outlook/Trajectory: Cached per sensor update to ensure consistency between state and attributes - Simple trends: Cached per sensor update to ensure consistency between state and attributes
- Current trend + next change: Cached centrally for 60s to avoid duplicate calculations - Current trend + next change: Cached centrally for 60s to avoid duplicate calculations
""" """
@ -32,7 +31,7 @@ if TYPE_CHECKING:
) )
# Constants # Constants
MIN_HOURS_FOR_LATER_HALF = 1 # Minimum hours needed to calculate half-window averages (activates at 2h+) MIN_HOURS_FOR_LATER_HALF = 3 # Minimum hours needed to calculate later half average
class TibberPricesTrendCalculator(TibberPricesBaseCalculator): class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
@ -40,10 +39,9 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
Calculator for price trend sensors. Calculator for price trend sensors.
Handles three types of trend analysis: Handles three types of trend analysis:
1. Outlook sensors (price_outlook_1h-12h): Current vs next N hours average 1. Simple trends (price_trend_1h-12h): Current vs next N hours average
2. Trajectory sensors (price_trajectory_2h-12h): First half vs second half of window 2. Current trend (current_price_trend): Pure future-based 3h outlook with volatility adjustment
3. Current trend (current_price_trend): Pure future-based 3h outlook with volatility adjustment 3. Next change (next_price_trend_change): Scan forward with configurable N-interval hysteresis (default 3)
4. Next change (next_price_trend_change): Scan forward with configurable N-interval hysteresis (default 3)
Caching: Caching:
- Simple trends: Per-sensor cache (_cached_trend_value, _trend_attributes) - Simple trends: Per-sensor cache (_cached_trend_value, _trend_attributes)
@ -64,10 +62,9 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
def __init__(self, coordinator: "TibberPricesDataUpdateCoordinator") -> None: def __init__(self, coordinator: "TibberPricesDataUpdateCoordinator") -> None:
"""Initialize trend calculator with caching state.""" """Initialize trend calculator with caching state."""
super().__init__(coordinator) super().__init__(coordinator)
# Per-sensor caches (for price_outlook_Xh and price_trajectory_Xh sensors) # Per-sensor trend caches (for price_trend_Nh sensors)
self._cached_trend_value: str | None = None self._cached_trend_value: str | None = None
self._trend_attributes: dict[str, Any] = {} self._trend_attributes: dict[str, Any] = {}
self._trajectory_attributes: dict[str, Any] = {}
# Centralized trend calculation cache (for current_price_trend + next_price_trend_change) # Centralized trend calculation cache (for current_price_trend + next_price_trend_change)
self._trend_calculation_cache: dict[str, Any] | None = None self._trend_calculation_cache: dict[str, Any] | None = None
self._trend_calculation_timestamp: datetime | None = None self._trend_calculation_timestamp: datetime | None = None
@ -75,12 +72,11 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
self._current_trend_attributes: dict[str, Any] | None = None self._current_trend_attributes: dict[str, Any] | None = None
self._trend_change_attributes: dict[str, Any] | None = None self._trend_change_attributes: dict[str, Any] | None = None
def get_price_outlook_value(self, *, hours: int) -> str | None: def get_price_trend_value(self, *, hours: int) -> str | None:
""" """
Calculate price outlook comparing current interval vs average of the next N hours. Calculate price trend comparing current interval vs next N hours average.
This is for price_outlook_Xh sensors. Answers: "Is the average of the next Xh This is for simple trend sensors (price_trend_1h through price_trend_12h).
cheaper or more expensive than right now?"
Results are cached per sensor to ensure consistency between state and attributes. Results are cached per sensor to ensure consistency between state and attributes.
Args: Args:
@ -288,131 +284,10 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
return trend_info["minutes_until_change"] return trend_info["minutes_until_change"]
def get_price_trajectory_value(self, *, hours: int) -> str | None:
"""
Calculate price trajectory by comparing first-half vs second-half window average.
This is for price_trajectory_Xh sensors. Answers: "Are prices rising or falling
within the next Xh window?" — revealing turning points that price_outlook_Xh misses.
Example at a price minimum (12:00):
- price_outlook_4h: "strongly_falling" (Ø next 4h is below current high)
- price_trajectory_4h: "rising" (second half is more expensive than first half)
Combined signal: act now, reversal is coming within the window.
Args:
hours: Number of hours in the window (must be >= 2)
Returns:
Trend state: "rising" | "falling" | "stable", or None if unavailable
"""
if hours < 2: # noqa: PLR2004
return None
if not self.has_data():
return None
current_interval = self.coordinator.get_current_interval()
if not current_interval or "total" not in current_interval:
return None
current_interval_price = float(current_interval["total"])
time = self.coordinator.time
current_starts_at = time.get_interval_time(current_interval)
if current_starts_at is None:
return None
next_interval_start = time.get_next_interval_start()
# Get first-half and second-half averages
first_half_avg = self._calculate_first_half_average(hours, next_interval_start)
second_half_avg = self._calculate_later_half_average(hours, next_interval_start)
if first_half_avg is None or second_half_avg is None:
return None
# Get configured thresholds (same as outlook sensors for consistency)
threshold_rising = self.config.get("price_trend_threshold_rising", 3.0)
threshold_falling = self.config.get("price_trend_threshold_falling", -3.0)
threshold_strongly_rising = self.config.get("price_trend_threshold_strongly_rising", 9.0)
threshold_strongly_falling = self.config.get("price_trend_threshold_strongly_falling", -9.0)
volatility_threshold_moderate = self.config.get("volatility_threshold_moderate", 15.0)
volatility_threshold_high = self.config.get("volatility_threshold_high", 30.0)
min_abs_diff = self.config.get("price_trend_min_price_change", 0.005)
min_abs_diff_strongly = self.config.get("price_trend_min_price_change_strongly", 0.015)
# Build volatility window from full outlook period
today_prices = self.intervals_today
tomorrow_prices = self.intervals_tomorrow
all_intervals = today_prices + tomorrow_prices
lookahead_intervals = self.coordinator.time.minutes_to_intervals(hours * 60)
current_idx = None
for idx, interval in enumerate(all_intervals):
if time.get_interval_time(interval) == current_starts_at:
current_idx = idx
break
if current_idx is not None:
volatility_window = all_intervals[current_idx : current_idx + lookahead_intervals]
else:
volatility_window = all_intervals[:lookahead_intervals]
# Compare first half vs second half: does price rise or fall across the window?
trajectory_state, diff_pct, trend_value, vol_factor = calculate_price_trend(
first_half_avg,
second_half_avg,
threshold_rising=threshold_rising,
threshold_falling=threshold_falling,
threshold_strongly_rising=threshold_strongly_rising,
threshold_strongly_falling=threshold_strongly_falling,
min_abs_diff=min_abs_diff,
min_abs_diff_strongly=min_abs_diff_strongly,
volatility_adjustment=True,
lookahead_intervals=lookahead_intervals,
all_intervals=volatility_window,
volatility_threshold_moderate=volatility_threshold_moderate,
volatility_threshold_high=volatility_threshold_high,
)
factor = get_display_unit_factor(self.config_entry)
time_obj = self.coordinator.time
total_intervals = time_obj.minutes_to_intervals(hours * 60)
first_half_count = total_intervals // 2
second_half_count = total_intervals - first_half_count
self._trajectory_attributes = {
"timestamp": next_interval_start,
"trend_value": trend_value,
f"trajectory_{hours}h_%": round(diff_pct, 1),
f"first_half_{hours}h_avg": round(first_half_avg * factor, 2),
f"second_half_{hours}h_avg": round(second_half_avg * factor, 2),
f"first_half_{hours}h_diff_from_current_%": round(
((first_half_avg - current_interval_price) / abs(current_interval_price)) * 100, 1
)
if current_interval_price != 0
else None,
f"second_half_{hours}h_diff_from_current_%": round(
((second_half_avg - current_interval_price) / abs(current_interval_price)) * 100, 1
)
if current_interval_price != 0
else None,
"first_half_interval_count": first_half_count,
"second_half_interval_count": second_half_count,
"volatility_factor": vol_factor,
}
return trajectory_state
def get_trend_attributes(self) -> dict[str, Any]: def get_trend_attributes(self) -> dict[str, Any]:
"""Get cached outlook attributes for price_outlook_Xh sensors.""" """Get cached trend attributes for simple trend sensors (price_trend_Nh)."""
return self._trend_attributes return self._trend_attributes
def get_trajectory_attributes(self) -> dict[str, Any]:
"""Get cached trajectory attributes for price_trajectory_Xh sensors."""
return self._trajectory_attributes
def get_current_trend_attributes(self) -> dict[str, Any] | None: def get_current_trend_attributes(self) -> dict[str, Any] | None:
"""Get cached attributes for current_price_trend sensor.""" """Get cached attributes for current_price_trend sensor."""
return self._current_trend_attributes return self._current_trend_attributes
@ -422,10 +297,9 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
return self._trend_change_attributes return self._trend_change_attributes
def clear_trend_cache(self) -> None: def clear_trend_cache(self) -> None:
"""Clear outlook/trajectory trend cache (called on coordinator update).""" """Clear simple trend cache (called on coordinator update)."""
self._cached_trend_value = None self._cached_trend_value = None
self._trend_attributes = {} self._trend_attributes = {}
self._trajectory_attributes = {}
def clear_calculation_cache(self) -> None: def clear_calculation_cache(self) -> None:
"""Clear centralized trend calculation cache (called on coordinator update).""" """Clear centralized trend calculation cache (called on coordinator update)."""
@ -436,54 +310,6 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
# PRIVATE HELPER METHODS # PRIVATE HELPER METHODS
# ======================================================================== # ========================================================================
def _calculate_first_half_average(self, hours: int, next_interval_start: datetime) -> float | None:
"""
Calculate average price for the first half of the future time window.
This is the counterpart to _calculate_later_half_average and together they
enable trajectory calculation (first half vs second half comparison).
Args:
hours: Total hours in the prediction window
next_interval_start: Start timestamp of the next interval
Returns:
Average price for the first half intervals, or None if insufficient data
"""
if not self.has_data():
return None
today_prices = self.intervals_today
tomorrow_prices = self.intervals_tomorrow
all_prices = today_prices + tomorrow_prices
if not all_prices:
return None
time = self.coordinator.time
total_intervals = time.minutes_to_intervals(hours * 60)
first_half_intervals = total_intervals // 2
interval_duration = time.get_interval_duration()
first_half_end = next_interval_start + (interval_duration * first_half_intervals)
# Collect prices in the first half: [next_interval_start, first_half_end)
first_prices = []
for price_data in all_prices:
starts_at = time.get_interval_time(price_data)
if starts_at is None:
continue
if next_interval_start <= starts_at < first_half_end:
price = price_data.get("total")
if price is not None:
first_prices.append(float(price))
if first_prices:
return calculate_mean(first_prices)
return None
def _calculate_later_half_average(self, hours: int, next_interval_start: datetime) -> float | None: def _calculate_later_half_average(self, hours: int, next_interval_start: datetime) -> float | None:
""" """
Calculate average price for the later half of the future time window. Calculate average price for the later half of the future time window.

View file

@ -326,7 +326,7 @@ class TibberPricesSensor(TibberPricesEntity, RestoreSensor):
self.coordinator.time = time_service self.coordinator.time = time_service
# Clear cached trend values on time-sensitive updates # Clear cached trend values on time-sensitive updates
if self.entity_description.key.startswith(("price_outlook_", "price_trajectory_")): if self.entity_description.key.startswith("price_trend_"):
self._trend_calculator.clear_trend_cache() self._trend_calculator.clear_trend_cache()
# Clear trend calculation cache for trend sensors # Clear trend calculation cache for trend sensors
elif self.entity_description.key in ( elif self.entity_description.key in (
@ -366,7 +366,7 @@ class TibberPricesSensor(TibberPricesEntity, RestoreSensor):
def _handle_coordinator_update(self) -> None: def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator.""" """Handle updated data from the coordinator."""
# Clear cached trend values when coordinator data changes # Clear cached trend values when coordinator data changes
if self.entity_description.key.startswith(("price_outlook_", "price_trajectory_")): if self.entity_description.key.startswith("price_trend_"):
self._trend_calculator.clear_trend_cache() self._trend_calculator.clear_trend_cache()
# Also clear calculation cache (e.g., when threshold config changes) # Also clear calculation cache (e.g., when threshold config changes)
self._trend_calculator.clear_calculation_cache() self._trend_calculator.clear_calculation_cache()
@ -1140,7 +1140,6 @@ class TibberPricesSensor(TibberPricesEntity, RestoreSensor):
cached_data.update( cached_data.update(
{ {
"trend_attributes": self._trend_calculator.get_trend_attributes(), "trend_attributes": self._trend_calculator.get_trend_attributes(),
"trajectory_attributes": self._trend_calculator.get_trajectory_attributes(),
"current_trend_attributes": self._trend_calculator.get_current_trend_attributes(), "current_trend_attributes": self._trend_calculator.get_current_trend_attributes(),
"trend_change_attributes": self._trend_calculator.get_trend_change_attributes(), "trend_change_attributes": self._trend_calculator.get_trend_change_attributes(),
"volatility_attributes": self._volatility_calculator.get_volatility_attributes(), "volatility_attributes": self._volatility_calculator.get_volatility_attributes(),

View file

@ -529,11 +529,11 @@ FUTURE_TREND_SENSORS = (
suggested_display_precision=2, suggested_display_precision=2,
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
# Price outlook forecast sensors (is the average of the next Xh cheaper/more expensive than now?) # Price trend forecast sensors (will prices be higher/lower in X hours?)
# Default enabled: 1h-5h # Default enabled: 1h-5h
SensorEntityDescription( SensorEntityDescription(
key="price_outlook_1h", key="price_trend_1h",
translation_key="price_outlook_1h", translation_key="price_trend_1h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics state_class=None, # Enum values: no statistics
@ -541,8 +541,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
SensorEntityDescription( SensorEntityDescription(
key="price_outlook_2h", key="price_trend_2h",
translation_key="price_outlook_2h", translation_key="price_trend_2h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics state_class=None, # Enum values: no statistics
@ -550,8 +550,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
SensorEntityDescription( SensorEntityDescription(
key="price_outlook_3h", key="price_trend_3h",
translation_key="price_outlook_3h", translation_key="price_trend_3h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics state_class=None, # Enum values: no statistics
@ -559,8 +559,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
SensorEntityDescription( SensorEntityDescription(
key="price_outlook_4h", key="price_trend_4h",
translation_key="price_outlook_4h", translation_key="price_trend_4h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics state_class=None, # Enum values: no statistics
@ -568,8 +568,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=True, entity_registry_enabled_default=True,
), ),
SensorEntityDescription( SensorEntityDescription(
key="price_outlook_5h", key="price_trend_5h",
translation_key="price_outlook_5h", translation_key="price_trend_5h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics state_class=None, # Enum values: no statistics
@ -578,8 +578,8 @@ FUTURE_TREND_SENSORS = (
), ),
# Disabled by default: 6h, 8h, 12h # Disabled by default: 6h, 8h, 12h
SensorEntityDescription( SensorEntityDescription(
key="price_outlook_6h", key="price_trend_6h",
translation_key="price_outlook_6h", translation_key="price_trend_6h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics state_class=None, # Enum values: no statistics
@ -587,8 +587,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
), ),
SensorEntityDescription( SensorEntityDescription(
key="price_outlook_8h", key="price_trend_8h",
translation_key="price_outlook_8h", translation_key="price_trend_8h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics state_class=None, # Enum values: no statistics
@ -596,89 +596,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
), ),
SensorEntityDescription( SensorEntityDescription(
key="price_outlook_12h", key="price_trend_12h",
translation_key="price_outlook_12h", translation_key="price_trend_12h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=False,
),
)
# ----------------------------------------------------------------------------
# 5b. PRICE TRAJECTORY SENSORS (first-half vs second-half window comparison)
# ----------------------------------------------------------------------------
# These sensors reveal turning points: is the price rising or falling WITHIN
# the window? Complements price_outlook_Xh sensors.
#
# Example at a price minimum (12:00):
# - price_outlook_4h: "strongly_falling" (Ø next 4h is below current high)
# - price_trajectory_4h: "rising" (second half avg > first half avg)
# → Combined: act now, reversal is coming within the window.
#
# Coverage starts at 2h (minimum for meaningful first/second half split).
# Default enabled: 2h-5h
PRICE_TRAJECTORY_SENSORS = (
SensorEntityDescription(
key="price_trajectory_2h",
translation_key="price_trajectory_2h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trajectory_3h",
translation_key="price_trajectory_3h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trajectory_4h",
translation_key="price_trajectory_4h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trajectory_5h",
translation_key="price_trajectory_5h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
# Disabled by default: 6h, 8h, 12h
SensorEntityDescription(
key="price_trajectory_6h",
translation_key="price_trajectory_6h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="price_trajectory_8h",
translation_key="price_trajectory_8h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="price_trajectory_12h",
translation_key="price_trajectory_12h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM, device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics state_class=None, # Enum values: no statistics
@ -1051,7 +970,6 @@ ENTITY_DESCRIPTIONS = (
*WINDOW_24H_SENSORS, *WINDOW_24H_SENSORS,
*FUTURE_MEAN_SENSORS, *FUTURE_MEAN_SENSORS,
*FUTURE_TREND_SENSORS, *FUTURE_TREND_SENSORS,
*PRICE_TRAJECTORY_SENSORS,
*VOLATILITY_SENSORS, *VOLATILITY_SENSORS,
*BEST_PRICE_TIMING_SENSORS, *BEST_PRICE_TIMING_SENSORS,
*PEAK_PRICE_TIMING_SENSORS, *PEAK_PRICE_TIMING_SENSORS,

View file

@ -206,23 +206,15 @@ def get_value_getter_mapping( # noqa: PLR0913 - needs all calculators as parame
"current_price_trend": trend_calculator.get_current_trend_value, "current_price_trend": trend_calculator.get_current_trend_value,
"next_price_trend_change": trend_calculator.get_next_trend_change_value, "next_price_trend_change": trend_calculator.get_next_trend_change_value,
"trend_change_in_minutes": lambda: _minutes_to_hours(trend_calculator.get_trend_change_in_minutes_value()), "trend_change_in_minutes": lambda: _minutes_to_hours(trend_calculator.get_trend_change_in_minutes_value()),
# Price outlook sensors (current price vs average of next Xh) # Price trend sensors
"price_outlook_1h": lambda: trend_calculator.get_price_outlook_value(hours=1), "price_trend_1h": lambda: trend_calculator.get_price_trend_value(hours=1),
"price_outlook_2h": lambda: trend_calculator.get_price_outlook_value(hours=2), "price_trend_2h": lambda: trend_calculator.get_price_trend_value(hours=2),
"price_outlook_3h": lambda: trend_calculator.get_price_outlook_value(hours=3), "price_trend_3h": lambda: trend_calculator.get_price_trend_value(hours=3),
"price_outlook_4h": lambda: trend_calculator.get_price_outlook_value(hours=4), "price_trend_4h": lambda: trend_calculator.get_price_trend_value(hours=4),
"price_outlook_5h": lambda: trend_calculator.get_price_outlook_value(hours=5), "price_trend_5h": lambda: trend_calculator.get_price_trend_value(hours=5),
"price_outlook_6h": lambda: trend_calculator.get_price_outlook_value(hours=6), "price_trend_6h": lambda: trend_calculator.get_price_trend_value(hours=6),
"price_outlook_8h": lambda: trend_calculator.get_price_outlook_value(hours=8), "price_trend_8h": lambda: trend_calculator.get_price_trend_value(hours=8),
"price_outlook_12h": lambda: trend_calculator.get_price_outlook_value(hours=12), "price_trend_12h": lambda: trend_calculator.get_price_trend_value(hours=12),
# Price trajectory sensors (first-half vs second-half window, reveals turning points)
"price_trajectory_2h": lambda: trend_calculator.get_price_trajectory_value(hours=2),
"price_trajectory_3h": lambda: trend_calculator.get_price_trajectory_value(hours=3),
"price_trajectory_4h": lambda: trend_calculator.get_price_trajectory_value(hours=4),
"price_trajectory_5h": lambda: trend_calculator.get_price_trajectory_value(hours=5),
"price_trajectory_6h": lambda: trend_calculator.get_price_trajectory_value(hours=6),
"price_trajectory_8h": lambda: trend_calculator.get_price_trajectory_value(hours=8),
"price_trajectory_12h": lambda: trend_calculator.get_price_trajectory_value(hours=12),
# Diagnostic sensors # Diagnostic sensors
"data_timestamp": get_data_timestamp, "data_timestamp": get_data_timestamp,
# Data lifecycle status sensor # Data lifecycle status sensor

View file

@ -619,8 +619,8 @@
"next_avg_12h": { "next_avg_12h": {
"name": "⌀ Preis nächste 12h" "name": "⌀ Preis nächste 12h"
}, },
"price_outlook_1h": { "price_trend_1h": {
"name": "Preisausblick (1h)", "name": "Preistrend (1h)",
"state": { "state": {
"strongly_rising": "Stark steigend", "strongly_rising": "Stark steigend",
"rising": "Steigend", "rising": "Steigend",
@ -629,8 +629,8 @@
"strongly_falling": "Stark fallend" "strongly_falling": "Stark fallend"
} }
}, },
"price_outlook_2h": { "price_trend_2h": {
"name": "Preisausblick (2h)", "name": "Preistrend (2h)",
"state": { "state": {
"strongly_rising": "Stark steigend", "strongly_rising": "Stark steigend",
"rising": "Steigend", "rising": "Steigend",
@ -639,8 +639,8 @@
"strongly_falling": "Stark fallend" "strongly_falling": "Stark fallend"
} }
}, },
"price_outlook_3h": { "price_trend_3h": {
"name": "Preisausblick (3h)", "name": "Preistrend (3h)",
"state": { "state": {
"strongly_rising": "Stark steigend", "strongly_rising": "Stark steigend",
"rising": "Steigend", "rising": "Steigend",
@ -649,8 +649,8 @@
"strongly_falling": "Stark fallend" "strongly_falling": "Stark fallend"
} }
}, },
"price_outlook_4h": { "price_trend_4h": {
"name": "Preisausblick (4h)", "name": "Preistrend (4h)",
"state": { "state": {
"strongly_rising": "Stark steigend", "strongly_rising": "Stark steigend",
"rising": "Steigend", "rising": "Steigend",
@ -659,8 +659,8 @@
"strongly_falling": "Stark fallend" "strongly_falling": "Stark fallend"
} }
}, },
"price_outlook_5h": { "price_trend_5h": {
"name": "Preisausblick (5h)", "name": "Preistrend (5h)",
"state": { "state": {
"strongly_rising": "Stark steigend", "strongly_rising": "Stark steigend",
"rising": "Steigend", "rising": "Steigend",
@ -669,8 +669,8 @@
"strongly_falling": "Stark fallend" "strongly_falling": "Stark fallend"
} }
}, },
"price_outlook_6h": { "price_trend_6h": {
"name": "Preisausblick (6h)", "name": "Preistrend (6h)",
"state": { "state": {
"strongly_rising": "Stark steigend", "strongly_rising": "Stark steigend",
"rising": "Steigend", "rising": "Steigend",
@ -679,8 +679,8 @@
"strongly_falling": "Stark fallend" "strongly_falling": "Stark fallend"
} }
}, },
"price_outlook_8h": { "price_trend_8h": {
"name": "Preisausblick (8h)", "name": "Preistrend (8h)",
"state": { "state": {
"strongly_rising": "Stark steigend", "strongly_rising": "Stark steigend",
"rising": "Steigend", "rising": "Steigend",
@ -689,78 +689,8 @@
"strongly_falling": "Stark fallend" "strongly_falling": "Stark fallend"
} }
}, },
"price_outlook_12h": { "price_trend_12h": {
"name": "Preisausblick (12h)", "name": "Preistrend (12h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
"stable": "Stabil",
"falling": "Fallend",
"strongly_falling": "Stark fallend"
}
},
"price_trajectory_2h": {
"name": "Preisverlauf (2h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
"stable": "Stabil",
"falling": "Fallend",
"strongly_falling": "Stark fallend"
}
},
"price_trajectory_3h": {
"name": "Preisverlauf (3h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
"stable": "Stabil",
"falling": "Fallend",
"strongly_falling": "Stark fallend"
}
},
"price_trajectory_4h": {
"name": "Preisverlauf (4h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
"stable": "Stabil",
"falling": "Fallend",
"strongly_falling": "Stark fallend"
}
},
"price_trajectory_5h": {
"name": "Preisverlauf (5h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
"stable": "Stabil",
"falling": "Fallend",
"strongly_falling": "Stark fallend"
}
},
"price_trajectory_6h": {
"name": "Preisverlauf (6h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
"stable": "Stabil",
"falling": "Fallend",
"strongly_falling": "Stark fallend"
}
},
"price_trajectory_8h": {
"name": "Preisverlauf (8h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
"stable": "Stabil",
"falling": "Fallend",
"strongly_falling": "Stark fallend"
}
},
"price_trajectory_12h": {
"name": "Preisverlauf (12h)",
"state": { "state": {
"strongly_rising": "Stark steigend", "strongly_rising": "Stark steigend",
"rising": "Steigend", "rising": "Steigend",

View file

@ -619,8 +619,8 @@
"next_avg_12h": { "next_avg_12h": {
"name": "⌀ Price Next 12h" "name": "⌀ Price Next 12h"
}, },
"price_outlook_1h": { "price_trend_1h": {
"name": "Price Outlook (1h)", "name": "Price Trend (1h)",
"state": { "state": {
"strongly_rising": "Strongly Rising", "strongly_rising": "Strongly Rising",
"rising": "Rising", "rising": "Rising",
@ -629,8 +629,8 @@
"strongly_falling": "Strongly Falling" "strongly_falling": "Strongly Falling"
} }
}, },
"price_outlook_2h": { "price_trend_2h": {
"name": "Price Outlook (2h)", "name": "Price Trend (2h)",
"state": { "state": {
"strongly_rising": "Strongly Rising", "strongly_rising": "Strongly Rising",
"rising": "Rising", "rising": "Rising",
@ -639,8 +639,8 @@
"strongly_falling": "Strongly Falling" "strongly_falling": "Strongly Falling"
} }
}, },
"price_outlook_3h": { "price_trend_3h": {
"name": "Price Outlook (3h)", "name": "Price Trend (3h)",
"state": { "state": {
"strongly_rising": "Strongly Rising", "strongly_rising": "Strongly Rising",
"rising": "Rising", "rising": "Rising",
@ -649,8 +649,8 @@
"strongly_falling": "Strongly Falling" "strongly_falling": "Strongly Falling"
} }
}, },
"price_outlook_4h": { "price_trend_4h": {
"name": "Price Outlook (4h)", "name": "Price Trend (4h)",
"state": { "state": {
"strongly_rising": "Strongly Rising", "strongly_rising": "Strongly Rising",
"rising": "Rising", "rising": "Rising",
@ -659,8 +659,8 @@
"strongly_falling": "Strongly Falling" "strongly_falling": "Strongly Falling"
} }
}, },
"price_outlook_5h": { "price_trend_5h": {
"name": "Price Outlook (5h)", "name": "Price Trend (5h)",
"state": { "state": {
"strongly_rising": "Strongly Rising", "strongly_rising": "Strongly Rising",
"rising": "Rising", "rising": "Rising",
@ -669,8 +669,8 @@
"strongly_falling": "Strongly Falling" "strongly_falling": "Strongly Falling"
} }
}, },
"price_outlook_6h": { "price_trend_6h": {
"name": "Price Outlook (6h)", "name": "Price Trend (6h)",
"state": { "state": {
"strongly_rising": "Strongly Rising", "strongly_rising": "Strongly Rising",
"rising": "Rising", "rising": "Rising",
@ -679,8 +679,8 @@
"strongly_falling": "Strongly Falling" "strongly_falling": "Strongly Falling"
} }
}, },
"price_outlook_8h": { "price_trend_8h": {
"name": "Price Outlook (8h)", "name": "Price Trend (8h)",
"state": { "state": {
"strongly_rising": "Strongly Rising", "strongly_rising": "Strongly Rising",
"rising": "Rising", "rising": "Rising",
@ -689,78 +689,8 @@
"strongly_falling": "Strongly Falling" "strongly_falling": "Strongly Falling"
} }
}, },
"price_outlook_12h": { "price_trend_12h": {
"name": "Price Outlook (12h)", "name": "Price Trend (12h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
"stable": "Stable",
"falling": "Falling",
"strongly_falling": "Strongly Falling"
}
},
"price_trajectory_2h": {
"name": "Price Trajectory (2h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
"stable": "Stable",
"falling": "Falling",
"strongly_falling": "Strongly Falling"
}
},
"price_trajectory_3h": {
"name": "Price Trajectory (3h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
"stable": "Stable",
"falling": "Falling",
"strongly_falling": "Strongly Falling"
}
},
"price_trajectory_4h": {
"name": "Price Trajectory (4h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
"stable": "Stable",
"falling": "Falling",
"strongly_falling": "Strongly Falling"
}
},
"price_trajectory_5h": {
"name": "Price Trajectory (5h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
"stable": "Stable",
"falling": "Falling",
"strongly_falling": "Strongly Falling"
}
},
"price_trajectory_6h": {
"name": "Price Trajectory (6h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
"stable": "Stable",
"falling": "Falling",
"strongly_falling": "Strongly Falling"
}
},
"price_trajectory_8h": {
"name": "Price Trajectory (8h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
"stable": "Stable",
"falling": "Falling",
"strongly_falling": "Strongly Falling"
}
},
"price_trajectory_12h": {
"name": "Price Trajectory (12h)",
"state": { "state": {
"strongly_rising": "Strongly Rising", "strongly_rising": "Strongly Rising",
"rising": "Rising", "rising": "Rising",

View file

@ -619,8 +619,8 @@
"next_avg_12h": { "next_avg_12h": {
"name": "⌀ Pris neste 12t" "name": "⌀ Pris neste 12t"
}, },
"price_outlook_1h": { "price_trend_1h": {
"name": "Prisutblikk (1t)", "name": "Pristrend (1t)",
"state": { "state": {
"strongly_rising": "Sterkt stigende", "strongly_rising": "Sterkt stigende",
"rising": "Stigende", "rising": "Stigende",
@ -629,8 +629,8 @@
"strongly_falling": "Sterkt fallende" "strongly_falling": "Sterkt fallende"
} }
}, },
"price_outlook_2h": { "price_trend_2h": {
"name": "Prisutblikk (2t)", "name": "Pristrend (2t)",
"state": { "state": {
"strongly_rising": "Sterkt stigende", "strongly_rising": "Sterkt stigende",
"rising": "Stigende", "rising": "Stigende",
@ -639,8 +639,8 @@
"strongly_falling": "Sterkt fallende" "strongly_falling": "Sterkt fallende"
} }
}, },
"price_outlook_3h": { "price_trend_3h": {
"name": "Prisutblikk (3t)", "name": "Pristrend (3t)",
"state": { "state": {
"strongly_rising": "Sterkt stigende", "strongly_rising": "Sterkt stigende",
"rising": "Stigende", "rising": "Stigende",
@ -649,8 +649,8 @@
"strongly_falling": "Sterkt fallende" "strongly_falling": "Sterkt fallende"
} }
}, },
"price_outlook_4h": { "price_trend_4h": {
"name": "Prisutblikk (4t)", "name": "Pristrend (4t)",
"state": { "state": {
"strongly_rising": "Sterkt stigende", "strongly_rising": "Sterkt stigende",
"rising": "Stigende", "rising": "Stigende",
@ -659,8 +659,8 @@
"strongly_falling": "Sterkt fallende" "strongly_falling": "Sterkt fallende"
} }
}, },
"price_outlook_5h": { "price_trend_5h": {
"name": "Prisutblikk (5t)", "name": "Pristrend (5t)",
"state": { "state": {
"strongly_rising": "Sterkt stigende", "strongly_rising": "Sterkt stigende",
"rising": "Stigende", "rising": "Stigende",
@ -669,8 +669,8 @@
"strongly_falling": "Sterkt fallende" "strongly_falling": "Sterkt fallende"
} }
}, },
"price_outlook_6h": { "price_trend_6h": {
"name": "Prisutblikk (6t)", "name": "Pristrend (6t)",
"state": { "state": {
"strongly_rising": "Sterkt stigende", "strongly_rising": "Sterkt stigende",
"rising": "Stigende", "rising": "Stigende",
@ -679,8 +679,8 @@
"strongly_falling": "Sterkt fallende" "strongly_falling": "Sterkt fallende"
} }
}, },
"price_outlook_8h": { "price_trend_8h": {
"name": "Prisutblikk (8t)", "name": "Pristrend (8t)",
"state": { "state": {
"strongly_rising": "Sterkt stigende", "strongly_rising": "Sterkt stigende",
"rising": "Stigende", "rising": "Stigende",
@ -689,78 +689,8 @@
"strongly_falling": "Sterkt fallende" "strongly_falling": "Sterkt fallende"
} }
}, },
"price_outlook_12h": { "price_trend_12h": {
"name": "Prisutblikk (12t)", "name": "Pristrend (12t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
"stable": "Stabil",
"falling": "Fallende",
"strongly_falling": "Sterkt fallende"
}
},
"price_trajectory_2h": {
"name": "Prisforløp (2t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
"stable": "Stabil",
"falling": "Fallende",
"strongly_falling": "Sterkt fallende"
}
},
"price_trajectory_3h": {
"name": "Prisforløp (3t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
"stable": "Stabil",
"falling": "Fallende",
"strongly_falling": "Sterkt fallende"
}
},
"price_trajectory_4h": {
"name": "Prisforløp (4t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
"stable": "Stabil",
"falling": "Fallende",
"strongly_falling": "Sterkt fallende"
}
},
"price_trajectory_5h": {
"name": "Prisforløp (5t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
"stable": "Stabil",
"falling": "Fallende",
"strongly_falling": "Sterkt fallende"
}
},
"price_trajectory_6h": {
"name": "Prisforløp (6t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
"stable": "Stabil",
"falling": "Fallende",
"strongly_falling": "Sterkt fallende"
}
},
"price_trajectory_8h": {
"name": "Prisforløp (8t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
"stable": "Stabil",
"falling": "Fallende",
"strongly_falling": "Sterkt fallende"
}
},
"price_trajectory_12h": {
"name": "Prisforløp (12t)",
"state": { "state": {
"strongly_rising": "Sterkt stigende", "strongly_rising": "Sterkt stigende",
"rising": "Stigende", "rising": "Stigende",

View file

@ -619,8 +619,8 @@
"next_avg_12h": { "next_avg_12h": {
"name": "⌀ Prijs Komende 12u" "name": "⌀ Prijs Komende 12u"
}, },
"price_outlook_1h": { "price_trend_1h": {
"name": "Prijsvooruitzicht (1u)", "name": "Prijstrend (1u)",
"state": { "state": {
"strongly_rising": "Sterk stijgend", "strongly_rising": "Sterk stijgend",
"rising": "Stijgend", "rising": "Stijgend",
@ -629,8 +629,8 @@
"strongly_falling": "Sterk dalend" "strongly_falling": "Sterk dalend"
} }
}, },
"price_outlook_2h": { "price_trend_2h": {
"name": "Prijsvooruitzicht (2u)", "name": "Prijstrend (2u)",
"state": { "state": {
"strongly_rising": "Sterk stijgend", "strongly_rising": "Sterk stijgend",
"rising": "Stijgend", "rising": "Stijgend",
@ -639,8 +639,8 @@
"strongly_falling": "Sterk dalend" "strongly_falling": "Sterk dalend"
} }
}, },
"price_outlook_3h": { "price_trend_3h": {
"name": "Prijsvooruitzicht (3u)", "name": "Prijstrend (3u)",
"state": { "state": {
"strongly_rising": "Sterk stijgend", "strongly_rising": "Sterk stijgend",
"rising": "Stijgend", "rising": "Stijgend",
@ -649,8 +649,8 @@
"strongly_falling": "Sterk dalend" "strongly_falling": "Sterk dalend"
} }
}, },
"price_outlook_4h": { "price_trend_4h": {
"name": "Prijsvooruitzicht (4u)", "name": "Prijstrend (4u)",
"state": { "state": {
"strongly_rising": "Sterk stijgend", "strongly_rising": "Sterk stijgend",
"rising": "Stijgend", "rising": "Stijgend",
@ -659,8 +659,8 @@
"strongly_falling": "Sterk dalend" "strongly_falling": "Sterk dalend"
} }
}, },
"price_outlook_5h": { "price_trend_5h": {
"name": "Prijsvooruitzicht (5u)", "name": "Prijstrend (5u)",
"state": { "state": {
"strongly_rising": "Sterk stijgend", "strongly_rising": "Sterk stijgend",
"rising": "Stijgend", "rising": "Stijgend",
@ -669,8 +669,8 @@
"strongly_falling": "Sterk dalend" "strongly_falling": "Sterk dalend"
} }
}, },
"price_outlook_6h": { "price_trend_6h": {
"name": "Prijsvooruitzicht (6u)", "name": "Prijstrend (6u)",
"state": { "state": {
"strongly_rising": "Sterk stijgend", "strongly_rising": "Sterk stijgend",
"rising": "Stijgend", "rising": "Stijgend",
@ -679,8 +679,8 @@
"strongly_falling": "Sterk dalend" "strongly_falling": "Sterk dalend"
} }
}, },
"price_outlook_8h": { "price_trend_8h": {
"name": "Prijsvooruitzicht (8u)", "name": "Prijstrend (8u)",
"state": { "state": {
"strongly_rising": "Sterk stijgend", "strongly_rising": "Sterk stijgend",
"rising": "Stijgend", "rising": "Stijgend",
@ -689,78 +689,8 @@
"strongly_falling": "Sterk dalend" "strongly_falling": "Sterk dalend"
} }
}, },
"price_outlook_12h": { "price_trend_12h": {
"name": "Prijsvooruitzicht (12u)", "name": "Prijstrend (12u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
"stable": "Stabiel",
"falling": "Dalend",
"strongly_falling": "Sterk dalend"
}
},
"price_trajectory_2h": {
"name": "Prijstrajectorie (2u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
"stable": "Stabiel",
"falling": "Dalend",
"strongly_falling": "Sterk dalend"
}
},
"price_trajectory_3h": {
"name": "Prijstrajectorie (3u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
"stable": "Stabiel",
"falling": "Dalend",
"strongly_falling": "Sterk dalend"
}
},
"price_trajectory_4h": {
"name": "Prijstrajectorie (4u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
"stable": "Stabiel",
"falling": "Dalend",
"strongly_falling": "Sterk dalend"
}
},
"price_trajectory_5h": {
"name": "Prijstrajectorie (5u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
"stable": "Stabiel",
"falling": "Dalend",
"strongly_falling": "Sterk dalend"
}
},
"price_trajectory_6h": {
"name": "Prijstrajectorie (6u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
"stable": "Stabiel",
"falling": "Dalend",
"strongly_falling": "Sterk dalend"
}
},
"price_trajectory_8h": {
"name": "Prijstrajectorie (8u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
"stable": "Stabiel",
"falling": "Dalend",
"strongly_falling": "Sterk dalend"
}
},
"price_trajectory_12h": {
"name": "Prijstrajectorie (12u)",
"state": { "state": {
"strongly_rising": "Sterk stijgend", "strongly_rising": "Sterk stijgend",
"rising": "Stijgend", "rising": "Stijgend",

View file

@ -619,8 +619,8 @@
"next_avg_12h": { "next_avg_12h": {
"name": "⌀ Pris nästa 12h" "name": "⌀ Pris nästa 12h"
}, },
"price_outlook_1h": { "price_trend_1h": {
"name": "Prisöversikt (1h)", "name": "Pristrend (1h)",
"state": { "state": {
"strongly_rising": "Kraftigt stigande", "strongly_rising": "Kraftigt stigande",
"rising": "Stigande", "rising": "Stigande",
@ -629,8 +629,8 @@
"strongly_falling": "Kraftigt fallande" "strongly_falling": "Kraftigt fallande"
} }
}, },
"price_outlook_2h": { "price_trend_2h": {
"name": "Prisöversikt (2h)", "name": "Pristrend (2h)",
"state": { "state": {
"strongly_rising": "Kraftigt stigande", "strongly_rising": "Kraftigt stigande",
"rising": "Stigande", "rising": "Stigande",
@ -639,8 +639,8 @@
"strongly_falling": "Kraftigt fallande" "strongly_falling": "Kraftigt fallande"
} }
}, },
"price_outlook_3h": { "price_trend_3h": {
"name": "Prisöversikt (3h)", "name": "Pristrend (3h)",
"state": { "state": {
"strongly_rising": "Kraftigt stigande", "strongly_rising": "Kraftigt stigande",
"rising": "Stigande", "rising": "Stigande",
@ -649,8 +649,8 @@
"strongly_falling": "Kraftigt fallande" "strongly_falling": "Kraftigt fallande"
} }
}, },
"price_outlook_4h": { "price_trend_4h": {
"name": "Prisöversikt (4h)", "name": "Pristrend (4h)",
"state": { "state": {
"strongly_rising": "Kraftigt stigande", "strongly_rising": "Kraftigt stigande",
"rising": "Stigande", "rising": "Stigande",
@ -659,8 +659,8 @@
"strongly_falling": "Kraftigt fallande" "strongly_falling": "Kraftigt fallande"
} }
}, },
"price_outlook_5h": { "price_trend_5h": {
"name": "Prisöversikt (5h)", "name": "Pristrend (5h)",
"state": { "state": {
"strongly_rising": "Kraftigt stigande", "strongly_rising": "Kraftigt stigande",
"rising": "Stigande", "rising": "Stigande",
@ -669,8 +669,8 @@
"strongly_falling": "Kraftigt fallande" "strongly_falling": "Kraftigt fallande"
} }
}, },
"price_outlook_6h": { "price_trend_6h": {
"name": "Prisöversikt (6h)", "name": "Pristrend (6h)",
"state": { "state": {
"strongly_rising": "Kraftigt stigande", "strongly_rising": "Kraftigt stigande",
"rising": "Stigande", "rising": "Stigande",
@ -679,8 +679,8 @@
"strongly_falling": "Kraftigt fallande" "strongly_falling": "Kraftigt fallande"
} }
}, },
"price_outlook_8h": { "price_trend_8h": {
"name": "Prisöversikt (8h)", "name": "Pristrend (8h)",
"state": { "state": {
"strongly_rising": "Kraftigt stigande", "strongly_rising": "Kraftigt stigande",
"rising": "Stigande", "rising": "Stigande",
@ -689,78 +689,8 @@
"strongly_falling": "Kraftigt fallande" "strongly_falling": "Kraftigt fallande"
} }
}, },
"price_outlook_12h": { "price_trend_12h": {
"name": "Prisöversikt (12h)", "name": "Pristrend (12h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
"stable": "Stabil",
"falling": "Fallande",
"strongly_falling": "Kraftigt fallande"
}
},
"price_trajectory_2h": {
"name": "Prisutveckling (2h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
"stable": "Stabil",
"falling": "Fallande",
"strongly_falling": "Kraftigt fallande"
}
},
"price_trajectory_3h": {
"name": "Prisutveckling (3h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
"stable": "Stabil",
"falling": "Fallande",
"strongly_falling": "Kraftigt fallande"
}
},
"price_trajectory_4h": {
"name": "Prisutveckling (4h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
"stable": "Stabil",
"falling": "Fallande",
"strongly_falling": "Kraftigt fallande"
}
},
"price_trajectory_5h": {
"name": "Prisutveckling (5h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
"stable": "Stabil",
"falling": "Fallande",
"strongly_falling": "Kraftigt fallande"
}
},
"price_trajectory_6h": {
"name": "Prisutveckling (6h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
"stable": "Stabil",
"falling": "Fallande",
"strongly_falling": "Kraftigt fallande"
}
},
"price_trajectory_8h": {
"name": "Prisutveckling (8h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
"stable": "Stabil",
"falling": "Fallande",
"strongly_falling": "Kraftigt fallande"
}
},
"price_trajectory_12h": {
"name": "Prisutveckling (12h)",
"state": { "state": {
"strongly_rising": "Kraftigt stigande", "strongly_rising": "Kraftigt stigande",
"rising": "Stigande", "rising": "Stigande",

View file

@ -124,14 +124,14 @@ automation:
# Conditions: Prices are falling AND we're approaching cheap levels # Conditions: Prices are falling AND we're approaching cheap levels
- condition: template - condition: template
value_template: > value_template: >
{% set trend_value = state_attr('sensor.<home_name>_price_outlook_3h', 'trend_value') | int(0) %} {% set trend_value = state_attr('sensor.<home_name>_price_trend_3h', 'trend_value') | int(0) %}
{% set level = state_attr('sensor.<home_name>_current_electricity_price', 'rating_level') %} {% set level = state_attr('sensor.<home_name>_current_electricity_price', 'rating_level') %}
{{ trend_value <= -1 and level in ['CHEAP', 'NORMAL'] }} {{ trend_value <= -1 and level in ['CHEAP', 'NORMAL'] }}
# AND: The next 3 hours will be cheaper on average # AND: The next 3 hours will be cheaper on average
- condition: template - condition: template
value_template: > value_template: >
{% set current = states('sensor.<home_name>_current_electricity_price') | float %} {% set current = states('sensor.<home_name>_current_electricity_price') | float %}
{% set future_avg = state_attr('sensor.<home_name>_price_outlook_3h', 'next_3h_avg') | float %} {% set future_avg = state_attr('sensor.<home_name>_price_trend_3h', 'next_3h_avg') | float %}
{{ future_avg < current }} {{ future_avg < current }}
action: action:
- service: water_heater.set_temperature - service: water_heater.set_temperature
@ -189,9 +189,9 @@ automation:
Falling = current price is HIGHER than future average = wait. Falling = current price is HIGHER than future average = wait.
trigger: trigger:
- platform: state - platform: state
entity_id: sensor.<home_name>_price_outlook_1h entity_id: sensor.<home_name>_price_trend_1h
- platform: state - platform: state
entity_id: sensor.<home_name>_price_outlook_6h entity_id: sensor.<home_name>_price_trend_6h
condition: condition:
# Only consider if best price period is NOT active # Only consider if best price period is NOT active
# (if it IS active, a separate automation handles it) # (if it IS active, a separate automation handles it)
@ -204,8 +204,8 @@ automation:
- conditions: - conditions:
- condition: template - condition: template
value_template: > value_template: >
{% set t1 = state_attr('sensor.<home_name>_price_outlook_1h', 'trend_value') | int(0) %} {% set t1 = state_attr('sensor.<home_name>_price_trend_1h', 'trend_value') | int(0) %}
{% set t6 = state_attr('sensor.<home_name>_price_outlook_6h', 'trend_value') | int(0) %} {% set t6 = state_attr('sensor.<home_name>_price_trend_6h', 'trend_value') | int(0) %}
{{ t1 >= 1 and t6 >= 1 }} {{ t1 >= 1 and t6 >= 1 }}
sequence: sequence:
- service: climate.set_temperature - service: climate.set_temperature
@ -217,8 +217,8 @@ automation:
- conditions: - conditions:
- condition: template - condition: template
value_template: > value_template: >
{% set t1 = state_attr('sensor.<home_name>_price_outlook_1h', 'trend_value') | int(0) %} {% set t1 = state_attr('sensor.<home_name>_price_trend_1h', 'trend_value') | int(0) %}
{% set t6 = state_attr('sensor.<home_name>_price_outlook_6h', 'trend_value') | int(0) %} {% set t6 = state_attr('sensor.<home_name>_price_trend_6h', 'trend_value') | int(0) %}
{{ t1 <= -1 and t6 >= 1 }} {{ t1 <= -1 and t6 >= 1 }}
sequence: sequence:
# Short-term dip — wait for it to bottom out # Short-term dip — wait for it to bottom out
@ -231,7 +231,7 @@ automation:
- conditions: - conditions:
- condition: template - condition: template
value_template: > value_template: >
{% set t6 = state_attr('sensor.<home_name>_price_outlook_6h', 'trend_value') | int(0) %} {% set t6 = state_attr('sensor.<home_name>_price_trend_6h', 'trend_value') | int(0) %}
{{ t6 <= -1 }} {{ t6 <= -1 }}
sequence: sequence:
- service: climate.set_temperature - service: climate.set_temperature
@ -414,8 +414,8 @@ automation:
action: action:
# Compare different future windows to find cheapest start # Compare different future windows to find cheapest start
- variables: - variables:
next_2h: "{{ state_attr('sensor.<home_name>_price_outlook_2h', 'next_2h_avg') | float(999) }}" next_2h: "{{ state_attr('sensor.<home_name>_price_trend_2h', 'next_2h_avg') | float(999) }}"
next_4h: "{{ state_attr('sensor.<home_name>_price_outlook_4h', 'next_4h_avg') | float(999) }}" next_4h: "{{ state_attr('sensor.<home_name>_price_trend_4h', 'next_4h_avg') | float(999) }}"
daily_avg: "{{ state_attr('sensor.<home_name>_price_today', 'price_median') | float(999) }}" daily_avg: "{{ state_attr('sensor.<home_name>_price_today', 'price_median') | float(999) }}"
- service: notify.mobile_app - service: notify.mobile_app
data: data:

View file

@ -43,7 +43,7 @@ Many sensors provide the `icon_color` attribute for dynamic styling. To see if a
- Price level sensors (e.g., `current_price_level`) - Price level sensors (e.g., `current_price_level`)
- Price rating sensors (e.g., `current_price_rating`) - Price rating sensors (e.g., `current_price_rating`)
- Volatility sensors (e.g., `today_s_price_volatility`) - Volatility sensors (e.g., `today_s_price_volatility`)
- Price outlook sensors (e.g., `price_outlook_3h`) - Price trend sensors (e.g., `price_trend_3h`)
- Binary sensors (e.g., `best_price_period`, `peak_price_period`) - Binary sensors (e.g., `best_price_period`, `peak_price_period`)
- Timing sensors (e.g., `best_price_time_until_start`, `best_price_progress`) - Timing sensors (e.g., `best_price_time_until_start`, `best_price_progress`)
@ -282,8 +282,8 @@ cards:
]]] ]]]
- type: custom:button-card - type: custom:button-card
entity: sensor.<home_name>_price_outlook_3h entity: sensor.<home_name>_price_trend_3h
name: Next 3h Outlook name: Next 3h Trend
show_state: true show_state: true
styles: styles:
icon: icon:

View file

@ -552,35 +552,32 @@ automation:
## Trend Sensors ## Trend Sensors
Trend sensors help you understand **whether to act now or wait**. The integration provides two complementary families: Trend sensors help you understand **where prices are heading**. They answer the question: "Should I use electricity now, or wait?"
- **Price Outlook Sensors (1h12h):** Compare current price vs. future window average — "Is now cheaper than the next Nh on average?" The integration provides two families of trend sensors for different use cases:
- **Price Trajectory Sensors (2h12h):** Compare first half vs. second half of the window — "Are prices rising or falling *within* the window?"
### Price Outlook Sensors (1h12h) ### Simple Trend Sensors (1h12h)
These sensors compare the **current price** with the **average price** of the next N hours: These sensors compare the **current price** with the **average price** of the next N hours:
| Sensor | Compares Against | | Sensor | Compares Against |
|--------|-----------------| |--------|-----------------|
| **Price Outlook (1h)** | Average of next 1 hour | | **Price Trend (1h)** | Average of next 1 hour |
| **Price Outlook (2h)** | Average of next 2 hours | | **Price Trend (2h)** | Average of next 2 hours |
| **Price Outlook (3h)** | Average of next 3 hours | | **Price Trend (3h)** | Average of next 3 hours |
| **Price Outlook (4h)** | Average of next 4 hours | | **Price Trend (4h)** | Average of next 4 hours |
| **Price Outlook (5h)** | Average of next 5 hours | | **Price Trend (5h)** | Average of next 5 hours |
| **Price Outlook (6h)** | Average of next 6 hours | | **Price Trend (6h)** | Average of next 6 hours |
| **Price Outlook (8h)** | Average of next 8 hours | | **Price Trend (8h)** | Average of next 8 hours |
| **Price Outlook (12h)** | Average of next 12 hours | | **Price Trend (12h)** | Average of next 12 hours |
:::info Same Starting Point — All Outlook Sensors Use Your Current Price :::info Same Starting Point — All Sensors Use Your Current Price
All outlook sensors share the **same base: your current 15-minute price**. They differ only in how far ahead they average. The windows **overlap** — the 3h average includes ALL intervals from the 1h and 2h windows, plus one more hour. All trend sensors share the **same base: your current 15-minute price**. They differ only in how far ahead they average. The windows **overlap** — the 3h average includes ALL intervals from the 1h and 2h windows, plus one more hour.
**This means:** **This means:**
- `price_outlook_3h` shows "current price vs. average of the **entire** next 3 hours" — **not** "what happens between hour 2 and hour 3" - `price_trend_3h` shows "current price vs. average of the **entire** next 3 hours" — **not** "what happens between hour 2 and hour 3"
- If 1h shows `falling` but 6h shows `rising`: near-term prices are below your current price, but looking at the full 6h window (which includes expensive evening hours), the overall average is above your current price - If 1h shows `falling` but 6h shows `rising`: near-term prices are below your current price, but looking at the full 6h window (which includes expensive evening hours), the overall average is above your current price
- Larger windows smooth out short-term fluctuations — a 30-minute price spike affects the 1h average more than the 6h average - Larger windows smooth out short-term fluctuations — a 30-minute price spike affects the 1h average more than the 6h average
**⚠️ At a price minimum, outlook sensors can be misleading!** If you're at the minimum and prices are about to rise, `price_outlook_3h` may still show `strongly_falling` because the cheap minimum pulls the 3h average below your current high price. Use `price_trajectory_3h` to see the direction *within* the window.
::: :::
**States:** Each sensor has one of five states: **States:** Each sensor has one of five states:
@ -629,42 +626,6 @@ stateDiagram-v2
**Tip:** The `trend_value` attribute (`-2` to `+2`) is ideal for automations — use numeric comparisons instead of matching translated state strings. **Tip:** The `trend_value` attribute (`-2` to `+2`) is ideal for automations — use numeric comparisons instead of matching translated state strings.
### Price Trajectory Sensors (2h12h)
These sensors compare the **first half** of the future window against the **second half** — revealing the price *direction within* the window.
| Sensor | Compares |
|--------|----------|
| **Price Trajectory (2h)** | Avg of hour 1 vs avg of hour 2 |
| **Price Trajectory (3h)** | Avg of first 1.5h vs avg of second 1.5h |
| **Price Trajectory (4h)** | Avg of first 2h vs avg of second 2h |
| **Price Trajectory (5h)** | Avg of first 2.5h vs avg of second 2.5h |
| **Price Trajectory (6h)** | Avg of first 3h vs avg of second 3h |
| **Price Trajectory (8h)** | Avg of first 4h vs avg of second 4h |
| **Price Trajectory (12h)** | Avg of first 6h vs avg of second 6h |
**States:** Same 5-level scale as outlook sensors (`strongly_falling` → `strongly_rising`).
:::info Why trajectory sensors complement outlook sensors
**At a price minimum** — the exact moment you should act — `price_outlook_3h` may show `strongly_falling` because the cheap minimum pulls the entire 3h average below your current high price. But `price_trajectory_3h` shows `rising` because the second half (after the minimum) is more expensive than the first half.
| Combination | Interpretation |
|-------------|----------------|
| Outlook `falling` + Trajectory `rising` | **You're AT the minimum** — act now |
| Outlook `falling` + Trajectory `falling` | Prices still dropping — wait |
| Outlook `rising` + Trajectory `rising` | Strong signal to act now |
| Outlook `rising` + Trajectory `falling` | Short spike, then cheaper — wait |
:::
**Key attributes:**
| Attribute | Description | Example |
|-----------|-------------|---------|
| `trend_value` | Numeric value for automations (-2 to +2) | `1` |
| `first_half_avg` | Average price in first half of window | `12.4` |
| `second_half_avg` | Average price in second half of window | `18.1` |
| `half_diff_%` | Percentage difference (second vs first half) | `46.0` |
### Current Price Trend ### Current Price Trend
**Entity ID:** `sensor.<home_name>_current_price_trend` **Entity ID:** `sensor.<home_name>_current_price_trend`
@ -740,16 +701,16 @@ A natural intuition is to treat trend states like a stock ticker:
#### Basic Automation Pattern #### Basic Automation Pattern
For most appliances (dishwasher, washing machine, dryer), a single outlook sensor is enough: For most appliances (dishwasher, washing machine, dryer), a single trend sensor is enough:
```yaml ```yaml
# Example: Start dishwasher when prices are favorable # Example: Start dishwasher when prices are favorable
trigger: trigger:
- platform: state - platform: state
entity_id: sensor.my_home_price_outlook_3h entity_id: sensor.my_home_price_trend_3h
condition: condition:
- condition: numeric_state - condition: numeric_state
entity_id: sensor.my_home_price_outlook_3h entity_id: sensor.my_home_price_trend_3h
attribute: trend_value attribute: trend_value
# rising (1) or strongly_rising (2) = act now # rising (1) or strongly_rising (2) = act now
above: 0 above: 0
@ -763,7 +724,7 @@ action:
When short-term and long-term trends disagree, you get richer insight: When short-term and long-term trends disagree, you get richer insight:
| 1h Outlook | 6h Outlook | Interpretation | Recommendation | | 1h Trend | 6h Trend | Interpretation | Recommendation |
|----------|----------|---------------|----------------| |----------|----------|---------------|----------------|
| `rising` | `rising` | Prices going up across the board | **Start now** | | `rising` | `rising` | Prices going up across the board | **Start now** |
| `falling` | `falling` | Prices dropping across the board | **Wait** | | `falling` | `falling` | Prices dropping across the board | **Wait** |
@ -779,12 +740,12 @@ On your dashboard, trend sensors give an instant overview:
- 🔴 All **rising/strongly_rising** → "Start everything you can — it only gets more expensive" - 🔴 All **rising/strongly_rising** → "Start everything you can — it only gets more expensive"
- 🟡 **Mixed** → Compare short-term vs. long-term sensors, or check the Best Price Period sensor - 🟡 **Mixed** → Compare short-term vs. long-term sensors, or check the Best Price Period sensor
### Outlook & Trajectory Sensors vs Average Sensors ### Trend Sensors vs Average Sensors
Both sensor families provide future price information, but serve different purposes: Both sensor families provide future price information, but serve different purposes:
| | Outlook/Trajectory Sensors | Average Sensors | | | Trend Sensors | Average Sensors |
|--|---------------------------|-----------------| |--|---------------|-----------------|
| **Purpose** | Dashboard display, quick visual overview | Automations, precise numeric comparisons | | **Purpose** | Dashboard display, quick visual overview | Automations, precise numeric comparisons |
| **Output** | Classification (falling/stable/rising) | Exact price values (ct/kWh) | | **Output** | Classification (falling/stable/rising) | Exact price values (ct/kWh) |
| **Best for** | "Should I worry about prices?" | "Is the future average below 15 ct?" | | **Best for** | "Should I worry about prices?" | "Is the future average below 15 ct?" |

View file

@ -54,8 +54,8 @@ uv pip install --requirement requirements.txt
############################################################################### ###############################################################################
# HA_VERSION can be overridden from the environment, e.g.: # HA_VERSION can be overridden from the environment, e.g.:
# HA_VERSION=2026.4.1 script/bootstrap # HA_VERSION=2026.3.4 script/bootstrap
HA_VERSION=${HA_VERSION:-"2026.4.1"} HA_VERSION=${HA_VERSION:-"2026.3.4"}
HA_CORE_BASE_URL="https://raw.githubusercontent.com/home-assistant/core/${HA_VERSION}" HA_CORE_BASE_URL="https://raw.githubusercontent.com/home-assistant/core/${HA_VERSION}"
HA_TMP_DIR="$HOME/.ha_requirements" HA_TMP_DIR="$HOME/.ha_requirements"

View file

@ -143,7 +143,7 @@ def test_future_mean_sensors_use_quarter_hour_timer() -> None:
def test_trend_sensors_use_quarter_hour_timer() -> None: def test_trend_sensors_use_quarter_hour_timer() -> None:
""" """
Test that price outlook/trajectory sensors use Timer #2. Test that price trend sensors use Timer #2.
Trend analysis depends on current interval position and Trend analysis depends on current interval position and
needs updates at quarter-hour boundaries. needs updates at quarter-hour boundaries.
@ -151,21 +151,14 @@ def test_trend_sensors_use_quarter_hour_timer() -> None:
trend_sensors = [ trend_sensors = [
"current_price_trend", "current_price_trend",
"next_price_trend_change", "next_price_trend_change",
"price_outlook_1h", "price_trend_1h",
"price_outlook_2h", "price_trend_2h",
"price_outlook_3h", "price_trend_3h",
"price_outlook_4h", "price_trend_4h",
"price_outlook_5h", "price_trend_5h",
"price_outlook_6h", "price_trend_6h",
"price_outlook_8h", "price_trend_8h",
"price_outlook_12h", "price_trend_12h",
"price_trajectory_2h",
"price_trajectory_3h",
"price_trajectory_4h",
"price_trajectory_5h",
"price_trajectory_6h",
"price_trajectory_8h",
"price_trajectory_12h",
] ]
for sensor_key in trend_sensors: for sensor_key in trend_sensors: