Compare commits

...

6 commits

Author SHA1 Message Date
Julian Pawlowski
e1da4cfa89 chroe(dev): update Home Assistant version in bootstrap script to 2026.4.1
Some checks are pending
Deploy Docusaurus Documentation (Dual Sites) / Build and Deploy Documentation Sites (push) Waiting to run
Lint / Ruff (push) Waiting to run
Validate / Hassfest validation (push) Waiting to run
Validate / HACS validation (push) Waiting to run
2026-04-09 17:13:03 +00:00
Julian Pawlowski
50dc874274 feat(brand): add local brand images for HA brands proxy API
Added brand/ directory to custom_components/tibber_prices/ with all
8 supported PNG variants, generated from existing SVGs in images/:

- icon.png / dark_icon.png (256×256)
- icon@2x.png / dark_icon@2x.png (512×512)
- logo.png / dark_logo.png (500×128)
- logo@2x.png / dark_logo@2x.png (1000×256)

Local brand images automatically take priority over CDN images and
are served via the HA brands proxy API (/api/brands/integration/).
Silently ignored on HA < 2026.3, no changes to manifest.json needed.

Updated AGENTS.md to document the brand/ directory under "ALLOWED in root".

Impact: Integration icon and logo now display correctly in HA ≥ 2026.3
without requiring a separate submission to the HA brands repository.
2026-04-09 17:06:13 +00:00
Julian Pawlowski
124824a2ea test(sensors): update timer assignment tests for renamed/new trend sensors
test_trend_sensors_use_quarter_hour_timer():
  - Replaced price_trend_Xh keys with price_outlook_Xh
  - Added 7 price_trajectory_Xh keys to the assertion list
  - Updated docstring from "price trend" to "price outlook/trajectory"

Impact: Test suite passes with renamed and new sensor keys.
2026-04-09 16:09:14 +00:00
Julian Pawlowski
cbf5e1a3fe docs(sensors): rename trend→outlook, document price_trajectory sensors
Updated user documentation to reflect renamed and new sensors.

sensors.md:
  - Section renamed "Simple Trend Sensors" → "Price Outlook Sensors (1h–12h)"
  - All price_trend_Xh entity references → price_outlook_Xh
  - Callout box updated: explains that outlook sensors can mislead at a
    price minimum and recommends combining with trajectory sensors
  - New section "Price Trajectory Sensors (2h–12h)" added before
    "Current Price Trend":
    - Table showing which halves are compared per window
    - Callout box with the 4 outlook+trajectory combination patterns
      (falling+rising = AT the minimum, etc.)
    - Key attributes table (first_half_avg, second_half_avg, half_diff_%)
  - "Trend Sensors vs Average Sensors" → "Outlook & Trajectory Sensors vs
    Average Sensors"

icon-colors.md:
  - "Price trend sensors (e.g., price_trend_3h)" → "Price outlook sensors
    (e.g., price_outlook_3h)"
  - Example entity updated to sensor.<home_name>_price_outlook_3h

automation-examples.md:
  - All price_trend_1h/2h/3h/4h/6h references → price_outlook_Xh
  - current_price_trend and next_price_trend_change unchanged (correct names)

Impact: Documentation matches actual entity names. New trajectory section
helps users understand when to use outlook vs trajectory sensors together.
2026-04-09 16:09:04 +00:00
Julian Pawlowski
2b96ccc650 feat(translations): add price_outlook_Xh and price_trajectory_Xh strings
Renamed 8 price_trend_Xh entries to price_outlook_Xh and added 15 new
price_trajectory_Xh entries (2h–12h) in all 5 languages (de, en, nb, nl, sv).

translations/ (HA-native: name + 5 states per sensor):
  - EN: "Price Outlook (Xh)" / "Price Trajectory (Xh)"
  - DE: "Preisausblick (Xh)" / "Preisverlauf (Xh)"
  - NB: "Prisutblikk (Xt)" / "Prisforløp (Xt)"
  - NL: "Prijsvooruitzicht (Xu)" / "Prijstrajectorie (Xu)"
  - SV: "Prisöversikt (Xh)" / "Prisutveckling (Xh)"

custom_translations/ (description + long_description + usage_tips):
  - Outlook descriptions updated to explain window-average comparison
    semantics (not price direction)
  - Trajectory descriptions explain first-half vs second-half logic and
    the "outlook: falling + trajectory: rising = you're AT the minimum" pattern
  - Trajectory long_description and usage_tips in English for all languages;
    description field in native language

Impact: Entity display names update to reflect the corrected semantic meaning.
2026-04-09 16:08:54 +00:00
Julian Pawlowski
33f57ff077 feat(sensors)!: rename price_trend_Xh → price_outlook_Xh, add price_trajectory_Xh
Renamed 8 sensors to clarify what they actually measure, and added 7 new
sensors for a different (and often more useful) calculation.

--- WHY THE RENAME ---

The old name "price_trend_Xh" implied the sensor shows where prices are
heading. It doesn't — it compares CURRENT price vs the FUTURE WINDOW AVERAGE.
At a price minimum, it shows "strongly_falling" (because the cheap minimum
pulls the average below your current high price), which is the opposite of
intuitive. The name "price_outlook_Xh" correctly conveys: "is now cheaper
or more expensive than the next Nh on average?"

--- NEW: price_trajectory_Xh ---

These sensors compare FIRST HALF vs SECOND HALF of the window, revealing
actual price direction within the window:

  price_trajectory_2h: avg(hour 1) vs avg(hour 2)
  price_trajectory_3h: avg(first 1.5h) vs avg(second 1.5h)
  price_trajectory_4h: avg(first 2h) vs avg(second 2h)
  price_trajectory_5h: avg(first 2.5h) vs avg(second 2.5h)
  price_trajectory_6h: avg(first 3h) vs avg(second 3h)
  price_trajectory_8h: avg(first 4h) vs avg(second 4h)
  price_trajectory_12h: avg(first 6h) vs avg(second 6h)

The key use case: at a price minimum, price_outlook_Xh shows "strongly_falling"
but price_trajectory_Xh shows "rising" — correctly revealing the upcoming
reversal. "outlook: falling + trajectory: rising" = you're AT the minimum.

--- IMPLEMENTATION ---

sensor/calculators/trend.py:
  - get_price_outlook_value() (was: get_price_trend_value())
  - New: get_price_trajectory_value(*, hours: int)
  - New: _calculate_first_half_average(hours, next_interval_start)
  - New: get_trajectory_attributes() → first_half_avg, second_half_avg, half_diff_%
  - clear_trend_cache() also resets _trajectory_attributes

sensor/definitions.py:
  - 8 SensorEntityDescription entries: key/translation_key price_trend_Xh → price_outlook_Xh
  - New PRICE_TRAJECTORY_SENSORS tuple (2h–5h enabled by default, 6h/8h/12h disabled)

sensor/value_getters.py:
  - 8 lambda entries renamed
  - 7 new trajectory lambda entries added

sensor/attributes/trend.py:
  - startswith("price_trend_") → startswith("price_outlook_")
  - New elif branch routing price_trajectory_* to cached trajectory_attributes

sensor/core.py:
  - startswith checks updated for both prefix families
  - cached_data dict extended with "trajectory_attributes"

coordinator/constants.py:
  - TIME_SENSITIVE_ENTITY_KEYS: 8 renamed + 7 new trajectory keys added

config_flow_handlers/entity_check.py:
  - volatility + price_trend affected-entity lists: 8 renamed + 7 new

BREAKING CHANGE: Sensors price_trend_1h, price_trend_2h, price_trend_3h,
price_trend_4h, price_trend_5h, price_trend_6h, price_trend_8h,
price_trend_12h have been removed without a deprecation period.

Migration:
  Replace price_trend_Xh → price_outlook_Xh everywhere (automations,
  dashboards, templates). Behavior is identical — only the entity name
  changed. If you want to detect actual price direction within the window
  (e.g. "are prices rising or falling right now?"), use the new
  price_trajectory_Xh sensors instead.

Impact: Users must update automations and dashboards. Entity IDs change from
sensor.<home>_price_trend_Xh to sensor.<home>_price_outlook_Xh. New
price_trajectory_Xh sensors provide complementary direction information.
2026-04-09 16:08:42 +00:00
31 changed files with 1214 additions and 353 deletions

View file

@ -313,6 +313,7 @@ After successful refactoring:
- 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`
- 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:**
- Utility modules (use `/utils/` package instead)

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -69,14 +69,21 @@ STEP_TO_SENSOR_KEYS: dict[str, list[str]] = {
# Also affects trend sensors (adaptive thresholds)
"current_price_trend",
"next_price_trend_change",
"price_trend_1h",
"price_trend_2h",
"price_trend_3h",
"price_trend_4h",
"price_trend_5h",
"price_trend_6h",
"price_trend_8h",
"price_trend_12h",
"price_outlook_1h",
"price_outlook_2h",
"price_outlook_3h",
"price_outlook_4h",
"price_outlook_5h",
"price_outlook_6h",
"price_outlook_8h",
"price_outlook_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": [
@ -106,14 +113,21 @@ STEP_TO_SENSOR_KEYS: dict[str, list[str]] = {
"price_trend": [
"current_price_trend",
"next_price_trend_change",
"price_trend_1h",
"price_trend_2h",
"price_trend_3h",
"price_trend_4h",
"price_trend_5h",
"price_trend_6h",
"price_trend_8h",
"price_trend_12h",
"price_outlook_1h",
"price_outlook_2h",
"price_outlook_3h",
"price_outlook_4h",
"price_outlook_5h",
"price_outlook_6h",
"price_outlook_8h",
"price_outlook_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,14 +62,22 @@ TIME_SENSITIVE_ENTITY_KEYS = frozenset(
"current_price_trend",
"next_price_trend_change",
# Price trend sensors
"price_trend_1h",
"price_trend_2h",
"price_trend_3h",
"price_trend_4h",
"price_trend_5h",
"price_trend_6h",
"price_trend_8h",
"price_trend_12h",
"price_outlook_1h",
"price_outlook_2h",
"price_outlook_3h",
"price_outlook_4h",
"price_outlook_5h",
"price_outlook_6h",
"price_outlook_8h",
"price_outlook_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_price_average",
"leading_price_average",

View file

@ -227,45 +227,80 @@
"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."
},
"price_trend_1h": {
"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 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 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_1h": {
"description": "Preisausblick 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).",
"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."
},
"price_trend_2h": {
"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) — 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 in 2h, verschiebe wenn möglich. 'stabil' = egal, starte nach Bedarf. Nicht auf 'stabil' warten — bei 'steigend' ist JETZT der beste Zeitpunkt."
"price_outlook_2h": {
"description": "Preisausblick 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).",
"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."
},
"price_trend_3h": {
"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) — 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 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_3h": {
"description": "Preisausblick 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).",
"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."
},
"price_trend_4h": {
"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) — 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 Ladefenster. 'stabil' = laden nach Bedarf. Funktioniert unabhängig vom Preisniveau — findet relative beste Zeit ob 10 oder 50 Cent."
"price_outlook_4h": {
"description": "Preisausblick 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).",
"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."
},
"price_trend_5h": {
"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) — 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 nicht günstiger. 'fallend' = warten wenn dein Zeitplan es erlaubt. Passt sich dem Markt an — findet bestes relatives Timing in jedem Preisumfeld."
"price_outlook_5h": {
"description": "Preisausblick 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).",
"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."
},
"price_trend_6h": {
"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) — 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 besser, warte wenn möglich. Passt sich automatisch an Winter/Sommer-Preisniveaus an — keine festen Schwellenwerte nötig."
"price_outlook_6h": {
"description": "Preisausblick 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).",
"usage_tips": "Abendentscheidungen: 'steigend' = Strom jetzt nutzen, solange er relativ günstig ist. 'fallend' = Abend-/Nachtpreise werden im Durchschnitt besser, warte wenn möglich."
},
"price_trend_8h": {
"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) — 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 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_8h": {
"description": "Preisausblick 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).",
"usage_tips": "Nachtplanung: 'steigend' = heute Nacht/morgen wird im Schnitt teurer, Strom jetzt nutzen. 'fallend' = Nachtpreise werden im Schnitt günstiger, Warten lohnt sich."
},
"price_trend_12h": {
"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) — 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, 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_outlook_12h": {
"description": "Preisausblick 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).",
"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."
},
"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": {
"description": "Aktuelle Preistrend-Richtung und wie lange sie anhält",

View file

@ -227,45 +227,80 @@
"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."
},
"price_trend_1h": {
"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 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. '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_1h": {
"description": "Price outlook 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).",
"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."
},
"price_trend_2h": {
"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) — 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. '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_2h": {
"description": "Price outlook 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).",
"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'."
},
"price_trend_3h": {
"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) — 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. '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_3h": {
"description": "Price outlook 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).",
"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."
},
"price_trend_4h": {
"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) — 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 charging window. 'stable' = charge when needed. Works regardless of absolute price — finds relative best time whether prices are 10 or 50 cents."
"price_outlook_4h": {
"description": "Price outlook 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).",
"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."
},
"price_trend_5h": {
"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) — 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. 'falling' = wait if your schedule allows. Adapts to market conditions — finds best relative timing in any price environment."
"price_outlook_5h": {
"description": "Price outlook 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).",
"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."
},
"price_trend_6h": {
"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) — 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, wait if possible. Automatically adjusts to winter/summer price levels — no fixed thresholds needed."
"price_outlook_6h": {
"description": "Price outlook 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).",
"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."
},
"price_trend_8h": {
"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) — 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, use power now. 'falling' = night prices will be cheaper, worth waiting. 'stable' = start when convenient. Works year-round without manual threshold adjustments."
"price_outlook_8h": {
"description": "Price outlook 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).",
"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."
},
"price_trend_12h": {
"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) — 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, 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_outlook_12h": {
"description": "Price outlook 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).",
"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."
},
"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": {
"description": "Current price trend direction and how long it will last",

View file

@ -227,45 +227,80 @@
"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."
},
"price_trend_1h": {
"description": "Pristrend for neste time",
"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 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_1h": {
"description": "Prisutblikk 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).",
"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!"
},
"price_trend_2h": {
"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) — 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. '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_2h": {
"description": "Prisutblikk 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).",
"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'."
},
"price_trend_3h": {
"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) — 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 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_3h": {
"description": "Prisutblikk 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).",
"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."
},
"price_trend_4h": {
"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) — 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 ladevindu. 'stabil' = lad etter behov. Fungerer uavhengig av prisnivå — finner relativ beste tid enten prisene er 10 eller 50 øre."
"price_outlook_4h": {
"description": "Prisutblikk 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).",
"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."
},
"price_trend_5h": {
"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) — 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, prisene blir ikke billigere. 'fallende' = vent hvis planen din tillater det. Tilpasser seg markedet — finner beste relative timing i ethvert prismiljø."
"price_outlook_5h": {
"description": "Prisutblikk 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).",
"usage_tips": "Utvidede sykluser: 'stigende' eller 'stabil' = godt tidspunkt å starte. 'fallende' = vent hvis planen din tillater det."
},
"price_trend_6h": {
"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) — 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, vent hvis mulig. Justerer automatisk til vinter/sommer prisnivåer — ingen faste terskler nødvendig."
"price_outlook_6h": {
"description": "Prisutblikk 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).",
"usage_tips": "Kveldsbeslutninger: 'stigende' = bruk strøm nå mens den er relativt billig. 'fallende' = kvelds-/nattprisene blir bedre i snitt, vent hvis mulig."
},
"price_trend_8h": {
"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) — 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, bruk strøm nå. 'fallende' = nattprisene blir billigere, verdt å vente. 'stabil' = start etter behov. Fungerer hele året uten manuelle terskeljusteringer."
"price_outlook_8h": {
"description": "Prisutblikk 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).",
"usage_tips": "Nattplanlegging: 'stigende' = i natt/i morgen blir dyrere i snitt, bruk strøm nå. 'fallende' = nattprisene blir billigere, verdt å vente."
},
"price_trend_12h": {
"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) — 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, godt tidspunkt for strømkrevende oppgaver. 'fallende' = betydelig bedre priser kommer, vent hvis mulig. Best kombinert med avg-sensor for absolutte prisgrenser."
"price_outlook_12h": {
"description": "Prisutblikk 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).",
"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."
},
"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": {
"description": "Nåværende pristrend-retning og hvor lenge den varer",

View file

@ -227,45 +227,80 @@
"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."
},
"price_trend_1h": {
"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 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 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_1h": {
"description": "Prijsvooruitzicht 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).",
"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."
},
"price_trend_2h": {
"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) — 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. '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_2h": {
"description": "Prijsvooruitzicht 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).",
"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."
},
"price_trend_3h": {
"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) — 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 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_3h": {
"description": "Prijsvooruitzicht 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%.",
"usage_tips": "Eco-programma's: 'stijgend' = start de eco-cyclus nu, prijzen zijn gemiddeld hoger in het venster. 'dalend' = wacht, goedkoper gemiddeld venster komt."
},
"price_trend_4h": {
"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) — 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. '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_4h": {
"description": "Prijsvooruitzicht 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%.",
"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."
},
"price_trend_5h": {
"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) — 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, 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_5h": {
"description": "Prijsvooruitzicht 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%.",
"usage_tips": "Uitgebreide cycli: 'stijgend' of 'stabiel' = goed moment om te starten. 'dalend' = wacht als je planning het toelaat."
},
"price_trend_6h": {
"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) — 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 beter, wacht indien mogelijk. Past automatisch aan winter/zomer prijsniveaus aan — geen vaste drempels nodig."
"price_outlook_6h": {
"description": "Prijsvooruitzicht 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%.",
"usage_tips": "Avandbeslissingen: 'stijgend' = gebruik stroom nu terwijl het relatief goedkoop is. 'dalend' = avond-/nachtprijzen worden gemiddeld beter, wacht indien mogelijk."
},
"price_trend_8h": {
"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) — 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 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_8h": {
"description": "Prijsvooruitzicht 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%.",
"usage_tips": "Nachtplanning: 'stijgend' = vanavond/morgen wordt gemiddeld duurder, gebruik stroom nu. 'dalend' = nachtprijzen worden goedkoper, wachten loont."
},
"price_trend_12h": {
"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) — 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, goed moment voor stroomintensieve taken. 'dalend' = aanzienlijk betere prijzen komen, wacht indien mogelijk. Het beste gecombineerd met avg-sensor voor absolute prijslimieten."
"price_outlook_12h": {
"description": "Prijsvooruitzicht 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%.",
"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."
},
"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": {
"description": "Huidige prijstrend-richting en hoe lang deze aanhoudt",
@ -275,7 +310,7 @@
"next_price_trend_change": {
"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.",
"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?'"
"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?'"
},
"trend_change_in_minutes": {
"description": "Tijd tot de volgende prijstrendwijziging",

View file

@ -227,45 +227,80 @@
"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."
},
"price_trend_1h": {
"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 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": "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_1h": {
"description": "Prisöversikt 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).",
"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."
},
"price_trend_2h": {
"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) — 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. '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_2h": {
"description": "Prisöversikt 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%.",
"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."
},
"price_trend_3h": {
"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) — 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, 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_3h": {
"description": "Prisöversikt 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%.",
"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."
},
"price_trend_4h": {
"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) — 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. '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_4h": {
"description": "Prisöversikt 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%.",
"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."
},
"price_trend_5h": {
"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) — 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, 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_5h": {
"description": "Prisöversikt 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%.",
"usage_tips": "Utökade cykler: 'stigande' eller 'stabil' = bra tid att starta. 'fallande' = vänta om din planering tillåter det."
},
"price_trend_6h": {
"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) — 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, vänta om möjligt. Justerar automatiskt till vinter/sommar prisnivåer — inga fasta trösklar behövs."
"price_outlook_6h": {
"description": "Prisöversikt 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%.",
"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."
},
"price_trend_8h": {
"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) — 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, 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_8h": {
"description": "Prisöversikt 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%.",
"usage_tips": "Nattplanering: 'stigande' = ikväll/imorgon blir dyrare i genomsnitt, använd el nu. 'fallande' = nattpriserna blir billigare, värt att vänta."
},
"price_trend_12h": {
"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) — 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, 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_outlook_12h": {
"description": "Prisöversikt 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%.",
"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."
},
"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": {
"description": "Nuvarande pristrend-riktning och hur länge den varar",
@ -275,7 +310,7 @@
"next_price_trend_change": {
"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.",
"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?'"
"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?'"
},
"trend_change_in_minutes": {
"description": "Tid till nästa pristrendändring",

View file

@ -28,8 +28,10 @@ def _add_timing_or_volatility_attributes(
def _add_cached_trend_attributes(attributes: dict, key: str, cached_data: dict) -> None:
"""Add cached trend attributes if available."""
if key.startswith("price_trend_") and cached_data.get("trend_attributes"):
if key.startswith("price_outlook_") and cached_data.get("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"):
# Add cached attributes (timestamp already set by platform)
attributes.update(cached_data["current_trend_attributes"])

View file

@ -2,13 +2,14 @@
Trend calculator for price trend analysis sensors.
This module handles all trend-related calculations:
- Simple price trends (1h-12h future comparison)
- Price outlook (1h-12h): Current price vs average of the next N hours
- 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)
- Next trend change prediction (with configurable N-interval hysteresis, default 3)
- Trend duration tracking (lightweight price direction scan with noise tolerance)
Caching strategy:
- Simple trends: Cached per sensor update to ensure consistency between state and attributes
- Outlook/Trajectory: Cached per sensor update to ensure consistency between state and attributes
- Current trend + next change: Cached centrally for 60s to avoid duplicate calculations
"""
@ -31,7 +32,7 @@ if TYPE_CHECKING:
)
# Constants
MIN_HOURS_FOR_LATER_HALF = 3 # Minimum hours needed to calculate later half average
MIN_HOURS_FOR_LATER_HALF = 1 # Minimum hours needed to calculate half-window averages (activates at 2h+)
class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
@ -39,9 +40,10 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
Calculator for price trend sensors.
Handles three types of trend analysis:
1. Simple trends (price_trend_1h-12h): Current vs next N hours average
2. 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)
1. Outlook sensors (price_outlook_1h-12h): Current vs next N hours average
2. Trajectory sensors (price_trajectory_2h-12h): First half vs second half of window
3. Current trend (current_price_trend): Pure future-based 3h outlook with volatility adjustment
4. Next change (next_price_trend_change): Scan forward with configurable N-interval hysteresis (default 3)
Caching:
- Simple trends: Per-sensor cache (_cached_trend_value, _trend_attributes)
@ -62,9 +64,10 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
def __init__(self, coordinator: "TibberPricesDataUpdateCoordinator") -> None:
"""Initialize trend calculator with caching state."""
super().__init__(coordinator)
# Per-sensor trend caches (for price_trend_Nh sensors)
# Per-sensor caches (for price_outlook_Xh and price_trajectory_Xh sensors)
self._cached_trend_value: str | None = None
self._trend_attributes: dict[str, Any] = {}
self._trajectory_attributes: dict[str, Any] = {}
# Centralized trend calculation cache (for current_price_trend + next_price_trend_change)
self._trend_calculation_cache: dict[str, Any] | None = None
self._trend_calculation_timestamp: datetime | None = None
@ -72,11 +75,12 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
self._current_trend_attributes: dict[str, Any] | None = None
self._trend_change_attributes: dict[str, Any] | None = None
def get_price_trend_value(self, *, hours: int) -> str | None:
def get_price_outlook_value(self, *, hours: int) -> str | None:
"""
Calculate price trend comparing current interval vs next N hours average.
Calculate price outlook comparing current interval vs average of the next N hours.
This is for simple trend sensors (price_trend_1h through price_trend_12h).
This is for price_outlook_Xh sensors. Answers: "Is the average of the next Xh
cheaper or more expensive than right now?"
Results are cached per sensor to ensure consistency between state and attributes.
Args:
@ -284,10 +288,131 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
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]:
"""Get cached trend attributes for simple trend sensors (price_trend_Nh)."""
"""Get cached outlook attributes for price_outlook_Xh sensors."""
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:
"""Get cached attributes for current_price_trend sensor."""
return self._current_trend_attributes
@ -297,9 +422,10 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
return self._trend_change_attributes
def clear_trend_cache(self) -> None:
"""Clear simple trend cache (called on coordinator update)."""
"""Clear outlook/trajectory trend cache (called on coordinator update)."""
self._cached_trend_value = None
self._trend_attributes = {}
self._trajectory_attributes = {}
def clear_calculation_cache(self) -> None:
"""Clear centralized trend calculation cache (called on coordinator update)."""
@ -310,6 +436,54 @@ class TibberPricesTrendCalculator(TibberPricesBaseCalculator):
# 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:
"""
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
# Clear cached trend values on time-sensitive updates
if self.entity_description.key.startswith("price_trend_"):
if self.entity_description.key.startswith(("price_outlook_", "price_trajectory_")):
self._trend_calculator.clear_trend_cache()
# Clear trend calculation cache for trend sensors
elif self.entity_description.key in (
@ -366,7 +366,7 @@ class TibberPricesSensor(TibberPricesEntity, RestoreSensor):
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
# Clear cached trend values when coordinator data changes
if self.entity_description.key.startswith("price_trend_"):
if self.entity_description.key.startswith(("price_outlook_", "price_trajectory_")):
self._trend_calculator.clear_trend_cache()
# Also clear calculation cache (e.g., when threshold config changes)
self._trend_calculator.clear_calculation_cache()
@ -1140,6 +1140,7 @@ class TibberPricesSensor(TibberPricesEntity, RestoreSensor):
cached_data.update(
{
"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(),
"trend_change_attributes": self._trend_calculator.get_trend_change_attributes(),
"volatility_attributes": self._volatility_calculator.get_volatility_attributes(),

View file

@ -529,11 +529,11 @@ FUTURE_TREND_SENSORS = (
suggested_display_precision=2,
entity_registry_enabled_default=True,
),
# Price trend forecast sensors (will prices be higher/lower in X hours?)
# Price outlook forecast sensors (is the average of the next Xh cheaper/more expensive than now?)
# Default enabled: 1h-5h
SensorEntityDescription(
key="price_trend_1h",
translation_key="price_trend_1h",
key="price_outlook_1h",
translation_key="price_outlook_1h",
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
@ -541,8 +541,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trend_2h",
translation_key="price_trend_2h",
key="price_outlook_2h",
translation_key="price_outlook_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
@ -550,8 +550,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trend_3h",
translation_key="price_trend_3h",
key="price_outlook_3h",
translation_key="price_outlook_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
@ -559,8 +559,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trend_4h",
translation_key="price_trend_4h",
key="price_outlook_4h",
translation_key="price_outlook_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
@ -568,8 +568,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trend_5h",
translation_key="price_trend_5h",
key="price_outlook_5h",
translation_key="price_outlook_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
@ -578,8 +578,8 @@ FUTURE_TREND_SENSORS = (
),
# Disabled by default: 6h, 8h, 12h
SensorEntityDescription(
key="price_trend_6h",
translation_key="price_trend_6h",
key="price_outlook_6h",
translation_key="price_outlook_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
@ -587,8 +587,8 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="price_trend_8h",
translation_key="price_trend_8h",
key="price_outlook_8h",
translation_key="price_outlook_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
@ -596,8 +596,89 @@ FUTURE_TREND_SENSORS = (
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="price_trend_12h",
translation_key="price_trend_12h",
key="price_outlook_12h",
translation_key="price_outlook_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
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
@ -970,6 +1051,7 @@ ENTITY_DESCRIPTIONS = (
*WINDOW_24H_SENSORS,
*FUTURE_MEAN_SENSORS,
*FUTURE_TREND_SENSORS,
*PRICE_TRAJECTORY_SENSORS,
*VOLATILITY_SENSORS,
*BEST_PRICE_TIMING_SENSORS,
*PEAK_PRICE_TIMING_SENSORS,

View file

@ -206,15 +206,23 @@ def get_value_getter_mapping( # noqa: PLR0913 - needs all calculators as parame
"current_price_trend": trend_calculator.get_current_trend_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()),
# Price trend sensors
"price_trend_1h": lambda: trend_calculator.get_price_trend_value(hours=1),
"price_trend_2h": lambda: trend_calculator.get_price_trend_value(hours=2),
"price_trend_3h": lambda: trend_calculator.get_price_trend_value(hours=3),
"price_trend_4h": lambda: trend_calculator.get_price_trend_value(hours=4),
"price_trend_5h": lambda: trend_calculator.get_price_trend_value(hours=5),
"price_trend_6h": lambda: trend_calculator.get_price_trend_value(hours=6),
"price_trend_8h": lambda: trend_calculator.get_price_trend_value(hours=8),
"price_trend_12h": lambda: trend_calculator.get_price_trend_value(hours=12),
# Price outlook sensors (current price vs average of next Xh)
"price_outlook_1h": lambda: trend_calculator.get_price_outlook_value(hours=1),
"price_outlook_2h": lambda: trend_calculator.get_price_outlook_value(hours=2),
"price_outlook_3h": lambda: trend_calculator.get_price_outlook_value(hours=3),
"price_outlook_4h": lambda: trend_calculator.get_price_outlook_value(hours=4),
"price_outlook_5h": lambda: trend_calculator.get_price_outlook_value(hours=5),
"price_outlook_6h": lambda: trend_calculator.get_price_outlook_value(hours=6),
"price_outlook_8h": lambda: trend_calculator.get_price_outlook_value(hours=8),
"price_outlook_12h": lambda: trend_calculator.get_price_outlook_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
"data_timestamp": get_data_timestamp,
# Data lifecycle status sensor

View file

@ -619,8 +619,8 @@
"next_avg_12h": {
"name": "⌀ Preis nächste 12h"
},
"price_trend_1h": {
"name": "Preistrend (1h)",
"price_outlook_1h": {
"name": "Preisausblick (1h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
@ -629,8 +629,8 @@
"strongly_falling": "Stark fallend"
}
},
"price_trend_2h": {
"name": "Preistrend (2h)",
"price_outlook_2h": {
"name": "Preisausblick (2h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
@ -639,8 +639,8 @@
"strongly_falling": "Stark fallend"
}
},
"price_trend_3h": {
"name": "Preistrend (3h)",
"price_outlook_3h": {
"name": "Preisausblick (3h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
@ -649,8 +649,8 @@
"strongly_falling": "Stark fallend"
}
},
"price_trend_4h": {
"name": "Preistrend (4h)",
"price_outlook_4h": {
"name": "Preisausblick (4h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
@ -659,8 +659,8 @@
"strongly_falling": "Stark fallend"
}
},
"price_trend_5h": {
"name": "Preistrend (5h)",
"price_outlook_5h": {
"name": "Preisausblick (5h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
@ -669,8 +669,8 @@
"strongly_falling": "Stark fallend"
}
},
"price_trend_6h": {
"name": "Preistrend (6h)",
"price_outlook_6h": {
"name": "Preisausblick (6h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
@ -679,8 +679,8 @@
"strongly_falling": "Stark fallend"
}
},
"price_trend_8h": {
"name": "Preistrend (8h)",
"price_outlook_8h": {
"name": "Preisausblick (8h)",
"state": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",
@ -689,8 +689,78 @@
"strongly_falling": "Stark fallend"
}
},
"price_trend_12h": {
"name": "Preistrend (12h)",
"price_outlook_12h": {
"name": "Preisausblick (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": {
"strongly_rising": "Stark steigend",
"rising": "Steigend",

View file

@ -619,8 +619,8 @@
"next_avg_12h": {
"name": "⌀ Price Next 12h"
},
"price_trend_1h": {
"name": "Price Trend (1h)",
"price_outlook_1h": {
"name": "Price Outlook (1h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
@ -629,8 +629,8 @@
"strongly_falling": "Strongly Falling"
}
},
"price_trend_2h": {
"name": "Price Trend (2h)",
"price_outlook_2h": {
"name": "Price Outlook (2h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
@ -639,8 +639,8 @@
"strongly_falling": "Strongly Falling"
}
},
"price_trend_3h": {
"name": "Price Trend (3h)",
"price_outlook_3h": {
"name": "Price Outlook (3h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
@ -649,8 +649,8 @@
"strongly_falling": "Strongly Falling"
}
},
"price_trend_4h": {
"name": "Price Trend (4h)",
"price_outlook_4h": {
"name": "Price Outlook (4h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
@ -659,8 +659,8 @@
"strongly_falling": "Strongly Falling"
}
},
"price_trend_5h": {
"name": "Price Trend (5h)",
"price_outlook_5h": {
"name": "Price Outlook (5h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
@ -669,8 +669,8 @@
"strongly_falling": "Strongly Falling"
}
},
"price_trend_6h": {
"name": "Price Trend (6h)",
"price_outlook_6h": {
"name": "Price Outlook (6h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
@ -679,8 +679,8 @@
"strongly_falling": "Strongly Falling"
}
},
"price_trend_8h": {
"name": "Price Trend (8h)",
"price_outlook_8h": {
"name": "Price Outlook (8h)",
"state": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",
@ -689,8 +689,78 @@
"strongly_falling": "Strongly Falling"
}
},
"price_trend_12h": {
"name": "Price Trend (12h)",
"price_outlook_12h": {
"name": "Price Outlook (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": {
"strongly_rising": "Strongly Rising",
"rising": "Rising",

View file

@ -619,8 +619,8 @@
"next_avg_12h": {
"name": "⌀ Pris neste 12t"
},
"price_trend_1h": {
"name": "Pristrend (1t)",
"price_outlook_1h": {
"name": "Prisutblikk (1t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
@ -629,8 +629,8 @@
"strongly_falling": "Sterkt fallende"
}
},
"price_trend_2h": {
"name": "Pristrend (2t)",
"price_outlook_2h": {
"name": "Prisutblikk (2t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
@ -639,8 +639,8 @@
"strongly_falling": "Sterkt fallende"
}
},
"price_trend_3h": {
"name": "Pristrend (3t)",
"price_outlook_3h": {
"name": "Prisutblikk (3t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
@ -649,8 +649,8 @@
"strongly_falling": "Sterkt fallende"
}
},
"price_trend_4h": {
"name": "Pristrend (4t)",
"price_outlook_4h": {
"name": "Prisutblikk (4t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
@ -659,8 +659,8 @@
"strongly_falling": "Sterkt fallende"
}
},
"price_trend_5h": {
"name": "Pristrend (5t)",
"price_outlook_5h": {
"name": "Prisutblikk (5t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
@ -669,8 +669,8 @@
"strongly_falling": "Sterkt fallende"
}
},
"price_trend_6h": {
"name": "Pristrend (6t)",
"price_outlook_6h": {
"name": "Prisutblikk (6t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
@ -679,8 +679,8 @@
"strongly_falling": "Sterkt fallende"
}
},
"price_trend_8h": {
"name": "Pristrend (8t)",
"price_outlook_8h": {
"name": "Prisutblikk (8t)",
"state": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",
@ -689,8 +689,78 @@
"strongly_falling": "Sterkt fallende"
}
},
"price_trend_12h": {
"name": "Pristrend (12t)",
"price_outlook_12h": {
"name": "Prisutblikk (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": {
"strongly_rising": "Sterkt stigende",
"rising": "Stigende",

View file

@ -619,8 +619,8 @@
"next_avg_12h": {
"name": "⌀ Prijs Komende 12u"
},
"price_trend_1h": {
"name": "Prijstrend (1u)",
"price_outlook_1h": {
"name": "Prijsvooruitzicht (1u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
@ -629,8 +629,8 @@
"strongly_falling": "Sterk dalend"
}
},
"price_trend_2h": {
"name": "Prijstrend (2u)",
"price_outlook_2h": {
"name": "Prijsvooruitzicht (2u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
@ -639,8 +639,8 @@
"strongly_falling": "Sterk dalend"
}
},
"price_trend_3h": {
"name": "Prijstrend (3u)",
"price_outlook_3h": {
"name": "Prijsvooruitzicht (3u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
@ -649,8 +649,8 @@
"strongly_falling": "Sterk dalend"
}
},
"price_trend_4h": {
"name": "Prijstrend (4u)",
"price_outlook_4h": {
"name": "Prijsvooruitzicht (4u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
@ -659,8 +659,8 @@
"strongly_falling": "Sterk dalend"
}
},
"price_trend_5h": {
"name": "Prijstrend (5u)",
"price_outlook_5h": {
"name": "Prijsvooruitzicht (5u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
@ -669,8 +669,8 @@
"strongly_falling": "Sterk dalend"
}
},
"price_trend_6h": {
"name": "Prijstrend (6u)",
"price_outlook_6h": {
"name": "Prijsvooruitzicht (6u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
@ -679,8 +679,8 @@
"strongly_falling": "Sterk dalend"
}
},
"price_trend_8h": {
"name": "Prijstrend (8u)",
"price_outlook_8h": {
"name": "Prijsvooruitzicht (8u)",
"state": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",
@ -689,8 +689,78 @@
"strongly_falling": "Sterk dalend"
}
},
"price_trend_12h": {
"name": "Prijstrend (12u)",
"price_outlook_12h": {
"name": "Prijsvooruitzicht (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": {
"strongly_rising": "Sterk stijgend",
"rising": "Stijgend",

View file

@ -619,8 +619,8 @@
"next_avg_12h": {
"name": "⌀ Pris nästa 12h"
},
"price_trend_1h": {
"name": "Pristrend (1h)",
"price_outlook_1h": {
"name": "Prisöversikt (1h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
@ -629,8 +629,8 @@
"strongly_falling": "Kraftigt fallande"
}
},
"price_trend_2h": {
"name": "Pristrend (2h)",
"price_outlook_2h": {
"name": "Prisöversikt (2h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
@ -639,8 +639,8 @@
"strongly_falling": "Kraftigt fallande"
}
},
"price_trend_3h": {
"name": "Pristrend (3h)",
"price_outlook_3h": {
"name": "Prisöversikt (3h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
@ -649,8 +649,8 @@
"strongly_falling": "Kraftigt fallande"
}
},
"price_trend_4h": {
"name": "Pristrend (4h)",
"price_outlook_4h": {
"name": "Prisöversikt (4h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
@ -659,8 +659,8 @@
"strongly_falling": "Kraftigt fallande"
}
},
"price_trend_5h": {
"name": "Pristrend (5h)",
"price_outlook_5h": {
"name": "Prisöversikt (5h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
@ -669,8 +669,8 @@
"strongly_falling": "Kraftigt fallande"
}
},
"price_trend_6h": {
"name": "Pristrend (6h)",
"price_outlook_6h": {
"name": "Prisöversikt (6h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
@ -679,8 +679,8 @@
"strongly_falling": "Kraftigt fallande"
}
},
"price_trend_8h": {
"name": "Pristrend (8h)",
"price_outlook_8h": {
"name": "Prisöversikt (8h)",
"state": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",
@ -689,8 +689,78 @@
"strongly_falling": "Kraftigt fallande"
}
},
"price_trend_12h": {
"name": "Pristrend (12h)",
"price_outlook_12h": {
"name": "Prisöversikt (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": {
"strongly_rising": "Kraftigt stigande",
"rising": "Stigande",

View file

@ -124,14 +124,14 @@ automation:
# Conditions: Prices are falling AND we're approaching cheap levels
- condition: template
value_template: >
{% set trend_value = state_attr('sensor.<home_name>_price_trend_3h', 'trend_value') | int(0) %}
{% set trend_value = state_attr('sensor.<home_name>_price_outlook_3h', 'trend_value') | int(0) %}
{% set level = state_attr('sensor.<home_name>_current_electricity_price', 'rating_level') %}
{{ trend_value <= -1 and level in ['CHEAP', 'NORMAL'] }}
# AND: The next 3 hours will be cheaper on average
- condition: template
value_template: >
{% set current = states('sensor.<home_name>_current_electricity_price') | float %}
{% set future_avg = state_attr('sensor.<home_name>_price_trend_3h', 'next_3h_avg') | float %}
{% set future_avg = state_attr('sensor.<home_name>_price_outlook_3h', 'next_3h_avg') | float %}
{{ future_avg < current }}
action:
- service: water_heater.set_temperature
@ -189,9 +189,9 @@ automation:
Falling = current price is HIGHER than future average = wait.
trigger:
- platform: state
entity_id: sensor.<home_name>_price_trend_1h
entity_id: sensor.<home_name>_price_outlook_1h
- platform: state
entity_id: sensor.<home_name>_price_trend_6h
entity_id: sensor.<home_name>_price_outlook_6h
condition:
# Only consider if best price period is NOT active
# (if it IS active, a separate automation handles it)
@ -204,8 +204,8 @@ automation:
- conditions:
- condition: template
value_template: >
{% set t1 = state_attr('sensor.<home_name>_price_trend_1h', 'trend_value') | int(0) %}
{% set t6 = state_attr('sensor.<home_name>_price_trend_6h', 'trend_value') | int(0) %}
{% set t1 = state_attr('sensor.<home_name>_price_outlook_1h', 'trend_value') | int(0) %}
{% set t6 = state_attr('sensor.<home_name>_price_outlook_6h', 'trend_value') | int(0) %}
{{ t1 >= 1 and t6 >= 1 }}
sequence:
- service: climate.set_temperature
@ -217,8 +217,8 @@ automation:
- conditions:
- condition: template
value_template: >
{% set t1 = state_attr('sensor.<home_name>_price_trend_1h', 'trend_value') | int(0) %}
{% set t6 = state_attr('sensor.<home_name>_price_trend_6h', 'trend_value') | int(0) %}
{% set t1 = state_attr('sensor.<home_name>_price_outlook_1h', 'trend_value') | int(0) %}
{% set t6 = state_attr('sensor.<home_name>_price_outlook_6h', 'trend_value') | int(0) %}
{{ t1 <= -1 and t6 >= 1 }}
sequence:
# Short-term dip — wait for it to bottom out
@ -231,7 +231,7 @@ automation:
- conditions:
- condition: template
value_template: >
{% set t6 = state_attr('sensor.<home_name>_price_trend_6h', 'trend_value') | int(0) %}
{% set t6 = state_attr('sensor.<home_name>_price_outlook_6h', 'trend_value') | int(0) %}
{{ t6 <= -1 }}
sequence:
- service: climate.set_temperature
@ -414,8 +414,8 @@ automation:
action:
# Compare different future windows to find cheapest start
- variables:
next_2h: "{{ state_attr('sensor.<home_name>_price_trend_2h', 'next_2h_avg') | float(999) }}"
next_4h: "{{ state_attr('sensor.<home_name>_price_trend_4h', 'next_4h_avg') | float(999) }}"
next_2h: "{{ state_attr('sensor.<home_name>_price_outlook_2h', 'next_2h_avg') | float(999) }}"
next_4h: "{{ state_attr('sensor.<home_name>_price_outlook_4h', 'next_4h_avg') | float(999) }}"
daily_avg: "{{ state_attr('sensor.<home_name>_price_today', 'price_median') | float(999) }}"
- service: notify.mobile_app
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 rating sensors (e.g., `current_price_rating`)
- Volatility sensors (e.g., `today_s_price_volatility`)
- Price trend sensors (e.g., `price_trend_3h`)
- Price outlook sensors (e.g., `price_outlook_3h`)
- Binary sensors (e.g., `best_price_period`, `peak_price_period`)
- Timing sensors (e.g., `best_price_time_until_start`, `best_price_progress`)
@ -282,8 +282,8 @@ cards:
]]]
- type: custom:button-card
entity: sensor.<home_name>_price_trend_3h
name: Next 3h Trend
entity: sensor.<home_name>_price_outlook_3h
name: Next 3h Outlook
show_state: true
styles:
icon:

View file

@ -552,32 +552,35 @@ automation:
## Trend Sensors
Trend sensors help you understand **where prices are heading**. They answer the question: "Should I use electricity now, or wait?"
Trend sensors help you understand **whether to act now or wait**. The integration provides two complementary families:
The integration provides two families of trend sensors for different use cases:
- **Price Outlook Sensors (1h12h):** Compare current price vs. future window average — "Is now cheaper than the next Nh on average?"
- **Price Trajectory Sensors (2h12h):** Compare first half vs. second half of the window — "Are prices rising or falling *within* the window?"
### Simple Trend Sensors (1h12h)
### Price Outlook Sensors (1h12h)
These sensors compare the **current price** with the **average price** of the next N hours:
| Sensor | Compares Against |
|--------|-----------------|
| **Price Trend (1h)** | Average of next 1 hour |
| **Price Trend (2h)** | Average of next 2 hours |
| **Price Trend (3h)** | Average of next 3 hours |
| **Price Trend (4h)** | Average of next 4 hours |
| **Price Trend (5h)** | Average of next 5 hours |
| **Price Trend (6h)** | Average of next 6 hours |
| **Price Trend (8h)** | Average of next 8 hours |
| **Price Trend (12h)** | Average of next 12 hours |
| **Price Outlook (1h)** | Average of next 1 hour |
| **Price Outlook (2h)** | Average of next 2 hours |
| **Price Outlook (3h)** | Average of next 3 hours |
| **Price Outlook (4h)** | Average of next 4 hours |
| **Price Outlook (5h)** | Average of next 5 hours |
| **Price Outlook (6h)** | Average of next 6 hours |
| **Price Outlook (8h)** | Average of next 8 hours |
| **Price Outlook (12h)** | Average of next 12 hours |
:::info Same Starting Point — All Sensors Use Your Current Price
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.
:::info Same Starting Point — All Outlook 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.
**This means:**
- `price_trend_3h` shows "current price vs. average of the **entire** next 3 hours" — **not** "what happens between hour 2 and hour 3"
- `price_outlook_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
- 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:
@ -626,6 +629,42 @@ stateDiagram-v2
**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
**Entity ID:** `sensor.<home_name>_current_price_trend`
@ -701,16 +740,16 @@ A natural intuition is to treat trend states like a stock ticker:
#### Basic Automation Pattern
For most appliances (dishwasher, washing machine, dryer), a single trend sensor is enough:
For most appliances (dishwasher, washing machine, dryer), a single outlook sensor is enough:
```yaml
# Example: Start dishwasher when prices are favorable
trigger:
- platform: state
entity_id: sensor.my_home_price_trend_3h
entity_id: sensor.my_home_price_outlook_3h
condition:
- condition: numeric_state
entity_id: sensor.my_home_price_trend_3h
entity_id: sensor.my_home_price_outlook_3h
attribute: trend_value
# rising (1) or strongly_rising (2) = act now
above: 0
@ -724,7 +763,7 @@ action:
When short-term and long-term trends disagree, you get richer insight:
| 1h Trend | 6h Trend | Interpretation | Recommendation |
| 1h Outlook | 6h Outlook | Interpretation | Recommendation |
|----------|----------|---------------|----------------|
| `rising` | `rising` | Prices going up across the board | **Start now** |
| `falling` | `falling` | Prices dropping across the board | **Wait** |
@ -740,12 +779,12 @@ On your dashboard, trend sensors give an instant overview:
- 🔴 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
### Trend Sensors vs Average Sensors
### Outlook & Trajectory Sensors vs Average Sensors
Both sensor families provide future price information, but serve different purposes:
| | Trend Sensors | Average Sensors |
|--|---------------|-----------------|
| | Outlook/Trajectory Sensors | Average Sensors |
|--|---------------------------|-----------------|
| **Purpose** | Dashboard display, quick visual overview | Automations, precise numeric comparisons |
| **Output** | Classification (falling/stable/rising) | Exact price values (ct/kWh) |
| **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=2026.3.4 script/bootstrap
HA_VERSION=${HA_VERSION:-"2026.3.4"}
# HA_VERSION=2026.4.1 script/bootstrap
HA_VERSION=${HA_VERSION:-"2026.4.1"}
HA_CORE_BASE_URL="https://raw.githubusercontent.com/home-assistant/core/${HA_VERSION}"
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:
"""
Test that price trend sensors use Timer #2.
Test that price outlook/trajectory sensors use Timer #2.
Trend analysis depends on current interval position and
needs updates at quarter-hour boundaries.
@ -151,14 +151,21 @@ def test_trend_sensors_use_quarter_hour_timer() -> None:
trend_sensors = [
"current_price_trend",
"next_price_trend_change",
"price_trend_1h",
"price_trend_2h",
"price_trend_3h",
"price_trend_4h",
"price_trend_5h",
"price_trend_6h",
"price_trend_8h",
"price_trend_12h",
"price_outlook_1h",
"price_outlook_2h",
"price_outlook_3h",
"price_outlook_4h",
"price_outlook_5h",
"price_outlook_6h",
"price_outlook_8h",
"price_outlook_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: