mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-05-28 18:43:40 +00:00
Compare commits
9 commits
ee9adce9d5
...
752a0c5dbc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
752a0c5dbc | ||
|
|
2adb64e5a0 | ||
|
|
7629c0f628 | ||
|
|
09edcdb9a3 | ||
|
|
e6ec54d8c5 | ||
|
|
5b5d5e73b0 | ||
|
|
e5474d50ec | ||
|
|
aa3f909814 | ||
|
|
76a3a0f1fd |
33 changed files with 1735 additions and 112 deletions
|
|
@ -14,7 +14,6 @@ import voluptuous as vol
|
|||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN, Platform
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.storage import Store
|
||||
from homeassistant.loader import async_get_loaded_integration
|
||||
|
|
@ -26,7 +25,6 @@ from .const import (
|
|||
CONF_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
|
||||
DATA_CHART_CONFIG,
|
||||
DATA_CHART_METADATA_CONFIG,
|
||||
DATA_STATISTICS_REVIEW_REQUIRED,
|
||||
DISPLAY_MODE_SUBUNIT,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
|
|
@ -214,30 +212,6 @@ def _get_access_token(hass: HomeAssistant, entry: ConfigEntry) -> str:
|
|||
raise ConfigEntryAuthFailed(msg)
|
||||
|
||||
|
||||
def _check_statistics_review_repair(hass: HomeAssistant, entry: TibberPricesConfigEntry) -> None:
|
||||
"""Re-create the statistics-review repair issue fresh on every setup when the flag is set.
|
||||
|
||||
Using delete + create (instead of get_or_create) resets dismissed_version, so the issue
|
||||
reappears in the Repairs panel even if the user had dismissed it before a restart.
|
||||
The flag is cleared from config_entry.data only when the user acknowledges the change
|
||||
by re-saving the currency display settings in the options flow.
|
||||
"""
|
||||
if not entry.data.get(DATA_STATISTICS_REVIEW_REQUIRED):
|
||||
return
|
||||
issue_id = f"currency_display_mode_changed_{entry.entry_id}"
|
||||
ir.async_delete_issue(hass, DOMAIN, issue_id)
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
issue_id,
|
||||
is_fixable=False,
|
||||
is_persistent=True,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="currency_display_mode_changed",
|
||||
translation_placeholders={"home_name": entry.title},
|
||||
)
|
||||
|
||||
|
||||
# https://developers.home-assistant.io/docs/config_entries_index/#setting-up-an-entry
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
|
|
@ -252,9 +226,6 @@ async def async_setup_entry(
|
|||
# Check for entity migrations (renames, breaking changes) and create repairs
|
||||
check_entity_migrations(hass, entry)
|
||||
|
||||
# Re-create statistics review repair issue fresh (resets any previous dismiss)
|
||||
_check_statistics_review_repair(hass, entry)
|
||||
|
||||
# Preload translations to populate the cache
|
||||
await async_load_translations(hass, "en")
|
||||
await async_load_standard_translations(hass, "en")
|
||||
|
|
|
|||
|
|
@ -25,6 +25,51 @@ if TYPE_CHECKING:
|
|||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
def get_current_phase_type(coordinator_data: dict, *, time: TibberPricesTimeService) -> str | None:
|
||||
"""
|
||||
Return the type of the currently active intra-day price phase.
|
||||
|
||||
Walks today's segments and returns the type ("rising", "falling", or "flat")
|
||||
of the last segment whose start time is ≤ now.
|
||||
|
||||
Args:
|
||||
coordinator_data: The coordinator's data dict.
|
||||
time: TibberPricesTimeService instance.
|
||||
|
||||
Returns:
|
||||
Phase type string or None if no segment data is available.
|
||||
|
||||
"""
|
||||
if not coordinator_data:
|
||||
return None
|
||||
|
||||
day_patterns = coordinator_data.get("dayPatterns")
|
||||
if not day_patterns:
|
||||
return None
|
||||
|
||||
today_data = day_patterns.get("today")
|
||||
if not today_data:
|
||||
return None
|
||||
|
||||
segments: list[dict] | None = today_data.get("segments")
|
||||
if not segments:
|
||||
return None
|
||||
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
now = time.now()
|
||||
current_type: str | None = None
|
||||
for segment in segments:
|
||||
seg_start_str: str | None = segment.get("start")
|
||||
if not seg_start_str:
|
||||
continue
|
||||
seg_start = parse_datetime(seg_start_str)
|
||||
if seg_start is not None and now >= seg_start:
|
||||
current_type = segment.get("type")
|
||||
|
||||
return current_type
|
||||
|
||||
|
||||
def get_tomorrow_data_available_attributes(
|
||||
coordinator_data: dict,
|
||||
*,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
|||
from .attributes import (
|
||||
build_async_extra_state_attributes,
|
||||
build_sync_extra_state_attributes,
|
||||
get_current_phase_type,
|
||||
get_price_intervals_attributes,
|
||||
get_tomorrow_data_available_attributes,
|
||||
)
|
||||
|
|
@ -136,6 +137,9 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity, RestoreEn
|
|||
state_getters = {
|
||||
"peak_price_period": self._peak_price_state,
|
||||
"best_price_period": self._best_price_state,
|
||||
"in_rising_price_phase": lambda: self._in_phase_state("rising"),
|
||||
"in_falling_price_phase": lambda: self._in_phase_state("falling"),
|
||||
"in_flat_price_phase": lambda: self._in_phase_state("flat"),
|
||||
"connection": lambda: get_connection_state(self.coordinator),
|
||||
"tomorrow_data_available": self._tomorrow_data_available_state,
|
||||
"has_ventilation_system": self._has_ventilation_system_state,
|
||||
|
|
@ -182,6 +186,15 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity, RestoreEn
|
|||
time = self.coordinator.time
|
||||
return time.is_time_in_period(start, end)
|
||||
|
||||
def _in_phase_state(self, phase_type: str) -> bool | None:
|
||||
"""Return True if the current intra-day price phase matches phase_type."""
|
||||
if not self.coordinator.data:
|
||||
return None
|
||||
current_type = get_current_phase_type(self.coordinator.data, time=self.coordinator.time)
|
||||
if current_type is None:
|
||||
return None
|
||||
return current_type == phase_type
|
||||
|
||||
def _tomorrow_data_available_state(self) -> bool | None:
|
||||
"""Return True if tomorrow's data is fully available, False if not, None if unknown."""
|
||||
# Auth errors: Cannot reliably check - return unknown
|
||||
|
|
|
|||
|
|
@ -19,6 +19,22 @@ ENTITY_DESCRIPTIONS = (
|
|||
translation_key="best_price_period",
|
||||
icon="mdi:clock-check",
|
||||
),
|
||||
# Price phase binary sensors — ON when current intra-day phase matches the type
|
||||
BinarySensorEntityDescription(
|
||||
key="in_rising_price_phase",
|
||||
translation_key="in_rising_price_phase",
|
||||
icon="mdi:trending-up",
|
||||
),
|
||||
BinarySensorEntityDescription(
|
||||
key="in_falling_price_phase",
|
||||
translation_key="in_falling_price_phase",
|
||||
icon="mdi:trending-down",
|
||||
),
|
||||
BinarySensorEntityDescription(
|
||||
key="in_flat_price_phase",
|
||||
translation_key="in_flat_price_phase",
|
||||
icon="mdi:trending-neutral",
|
||||
),
|
||||
BinarySensorEntityDescription(
|
||||
key="connection",
|
||||
translation_key="connection",
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ from custom_components.tibber_prices.const import (
|
|||
CONF_VOLATILITY_THRESHOLD_HIGH,
|
||||
CONF_VOLATILITY_THRESHOLD_MODERATE,
|
||||
CONF_VOLATILITY_THRESHOLD_VERY_HIGH,
|
||||
DATA_STATISTICS_REVIEW_REQUIRED,
|
||||
DEFAULT_VOLATILITY_THRESHOLD_HIGH,
|
||||
DEFAULT_VOLATILITY_THRESHOLD_MODERATE,
|
||||
DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH,
|
||||
|
|
@ -512,33 +511,24 @@ class TibberPricesOptionsFlowHandler(OptionsFlow):
|
|||
# async_create_entry automatically handles change detection and listener triggering
|
||||
self._save_options_if_changed()
|
||||
|
||||
# Handle currency display mode change repair + persistent flag
|
||||
issue_id = f"currency_display_mode_changed_{self.config_entry.entry_id}"
|
||||
mode_changed = old_mode is not None and new_mode is not None and old_mode != new_mode
|
||||
|
||||
if mode_changed:
|
||||
# Set persistent flag so repair issue reappears after dismiss + HA restart
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self.config_entry,
|
||||
data={**self.config_entry.data, DATA_STATISTICS_REVIEW_REQUIRED: True},
|
||||
)
|
||||
# Notify user of currency display mode change via Repairs
|
||||
if old_mode is not None and new_mode is not None and old_mode != new_mode:
|
||||
issue_id = f"currency_display_mode_changed_{self.config_entry.entry_id}"
|
||||
# delete + create resets dismissed_version so the issue is always visible
|
||||
# for a new mode change, even if a previous instance was dismissed.
|
||||
ir.async_delete_issue(self.hass, DOMAIN, issue_id)
|
||||
ir.async_create_issue(
|
||||
self.hass,
|
||||
DOMAIN,
|
||||
issue_id,
|
||||
is_fixable=False,
|
||||
is_persistent=True,
|
||||
is_persistent=False,
|
||||
severity=ir.IssueSeverity.WARNING,
|
||||
translation_key="currency_display_mode_changed",
|
||||
translation_placeholders={
|
||||
"home_name": self.config_entry.title,
|
||||
},
|
||||
)
|
||||
elif self.config_entry.data.get(DATA_STATISTICS_REVIEW_REQUIRED):
|
||||
# User re-saved display settings with same mode = acknowledgement → clear flag
|
||||
new_data = {k: v for k, v in self.config_entry.data.items() if k != DATA_STATISTICS_REVIEW_REQUIRED}
|
||||
self.hass.config_entries.async_update_entry(self.config_entry, data=new_data)
|
||||
ir.async_delete_issue(self.hass, DOMAIN, issue_id)
|
||||
|
||||
# Return to menu for more changes
|
||||
return await self.async_step_init()
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@ DATA_CHART_CONFIG = "chart_config" # Key for chart export config in hass.data
|
|||
DATA_CHART_METADATA_CONFIG = "chart_metadata_config" # Key for chart metadata config in hass.data
|
||||
|
||||
# Config entry data flag: set when user switches currency display mode.
|
||||
# Triggers a fresh (un-dismissed) repair issue on every setup/reload until
|
||||
# the user explicitly re-saves the currency settings to acknowledge.
|
||||
DATA_STATISTICS_REVIEW_REQUIRED = "statistics_review_required"
|
||||
|
||||
# Configuration keys
|
||||
CONF_EXTENDED_DESCRIPTIONS = "extended_descriptions"
|
||||
CONF_VIRTUAL_TIME_OFFSET_DAYS = (
|
||||
|
|
|
|||
|
|
@ -88,11 +88,24 @@ TIME_SENSITIVE_ENTITY_KEYS = frozenset(
|
|||
# Binary sensors that check if current time is in a period
|
||||
"peak_price_period",
|
||||
"best_price_period",
|
||||
# Binary sensors for current intra-day price phase
|
||||
"in_rising_price_phase",
|
||||
"in_falling_price_phase",
|
||||
"in_flat_price_phase",
|
||||
# Best/Peak price timestamp sensors (periods only change at interval boundaries)
|
||||
"best_price_end_time",
|
||||
"best_price_next_start_time",
|
||||
"peak_price_end_time",
|
||||
"peak_price_next_start_time",
|
||||
# Current price phase timing sensors (phase boundaries only change at interval boundaries)
|
||||
"current_price_phase_end_time",
|
||||
"current_price_phase_duration",
|
||||
"next_rising_phase_start_time",
|
||||
"next_falling_phase_start_time",
|
||||
"next_flat_phase_start_time",
|
||||
# Current/next price phase enum sensors
|
||||
"current_price_phase",
|
||||
"next_price_phase",
|
||||
# Price rank sensors (rank of current/next/previous interval within a day scope)
|
||||
"current_interval_price_rank_today",
|
||||
"current_interval_price_rank_tomorrow",
|
||||
|
|
@ -128,6 +141,13 @@ MINUTE_UPDATE_ENTITY_KEYS = frozenset(
|
|||
"peak_price_remaining_minutes",
|
||||
"peak_price_progress",
|
||||
"peak_price_next_in_minutes",
|
||||
# Current price phase countdown/progress sensors (need minute updates)
|
||||
"current_price_phase_remaining_minutes",
|
||||
"current_price_phase_progress",
|
||||
# Next-phase countdown sensors (need minute updates)
|
||||
"next_rising_phase_in_minutes",
|
||||
"next_falling_phase_in_minutes",
|
||||
"next_flat_phase_in_minutes",
|
||||
# Trend change countdown sensor (needs minute updates)
|
||||
"next_price_trend_change_in",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -501,6 +501,66 @@
|
|||
"long_description": "Klassifiziert morgen (sobald Daten verfügbar sind, typisch nach 13 Uhr) in ein Preismuster mit demselben Algorithmus wie heute. Die Attribute valley_start/valley_end oder peak_start/peak_end geben Knickpunktzeiten für das primäre Extremum an.",
|
||||
"usage_tips": "Richte Abendautomationen ein, die das morgige Muster lesen und Wärmepumpe, Autolader oder Warmwasserbereiter für den nächsten Tag vorkonfigurieren. Kombiniere mit dem tomorrow_data_available Binärsensor."
|
||||
},
|
||||
"current_price_phase": {
|
||||
"description": "Ob die Strompreise aktuell steigen, fallen oder stabil sind – innerhalb der tageszeitlichen Preisform",
|
||||
"long_description": "Zeigt die Preisbewegungsrichtung zum aktuellen Zeitpunkt, indem das aktive monotone Segment der heutigen Preiskurve ermittelt wird. Der Tagesverlauf wird in aufeinanderfolgende steigende, fallende oder flache Abschnitte (Phasen) unterteilt. Dieser Sensor zeigt, in welcher Phase du dich gerade befindest. Attribute: Startzeit und Endzeit der Phase, Preisspanne (min/max/mean), Position im Tagesverlauf (segment_index und segment_count) sowie die vollständige Liste aller heutigen Phasen (all_segments). Aktualisierung alle 15 Minuten.",
|
||||
"usage_tips": "Nutzen in Automationen: 'Wenn current_price_phase = fallend, flexible Lasten verschieben, bis der Preis seinen Tiefpunkt erreicht hat'. Kombiniere mit dem Sensor Heutiges Preismuster, um sowohl die Gesamtform des Tages als auch deine aktuelle Position darin zu sehen. Prüfe segment_index und segment_count: z. B. segment_index=0 und Phase=fallend bedeutet, die Preise fallen seit Mitternacht. Nutze all_segments in Templates oder Dashboards, um den vollständigen Tagesverlauf anzuzeigen."
|
||||
},
|
||||
"next_price_phase": {
|
||||
"description": "Die nächste tageszeitliche Preisphase – was nach der aktuellen Preisbewegung kommt",
|
||||
"long_description": "Zeigt die monotone Preisphase, die nach der aktuell aktiven Phase folgt. Das Attribut start zeigt genau, wann die nächste Phase beginnt – ideal für zeitgenaue Automationen. Wenn die aktuelle Phase die letzte des Tages ist (z.B. der abendliche Abwärtstrend), wird dieser Sensor nicht verfügbar. Attribute: start (wann sie beginnt), end, Preisspanne (min/max/mean), segment_index, segment_count. Aktualisierung alle 15 Minuten.",
|
||||
"usage_tips": "Nutzen in Automationen: 'Wenn next_price_phase = steigend und next_price_phase.start in weniger als 1 Stunde, Waschmaschine jetzt starten'. Oder kombiniere mit current_price_phase: 'Wenn current_price_phase = fallend und next_price_phase = flach, nähern wir uns dem Tagestiefpunkt – guter Zeitpunkt für flexible Lasten'. Das Attribut start ist besonders wertvoll: Automationen können exakt dann ausgelöst werden, wenn die nächste Phase beginnt."
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"description": "When the current intra-day price phase ends",
|
||||
"long_description": "Shows the exact timestamp when the currently active rising, falling, or flat price phase will end and transition to the next phase.",
|
||||
"usage_tips": "Use in automations to schedule tasks that must finish before prices change."
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"description": "Minutes remaining in the current price phase",
|
||||
"long_description": "Shows how many minutes are left in the current intra-day price phase. Updates every minute.",
|
||||
"usage_tips": "Use in automations: 'If current_price_phase = falling and remaining < 30, start the dishwasher now'."
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"description": "Total duration of the current price phase",
|
||||
"long_description": "Shows the total length of the currently active price phase in hours.",
|
||||
"usage_tips": "Combine with remaining minutes to understand how far through the phase you are."
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"description": "How far through the current price phase we are",
|
||||
"long_description": "Shows the percentage of the current intra-day price phase that has elapsed (0–100%). Updates every minute.",
|
||||
"usage_tips": "Use in dashboard cards to display a visual progress bar for the current price phase."
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"description": "When the next rising price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming rising price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use to schedule loads before prices start rising again."
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"description": "When the next falling price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming falling price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use to delay flexible loads until the next price drop starts."
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"description": "When the next flat (stable) price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming flat price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use for scheduling loads that need predictable costs over time."
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"description": "Minutes until the next rising price phase begins",
|
||||
"long_description": "Shows how many minutes until the next rising price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use in countdown automations to alert before the next price rise."
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"description": "Minutes until the next falling price phase begins",
|
||||
"long_description": "Shows how many minutes until the next falling price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use to time flexible loads: delay until the upcoming price drop."
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"description": "Minutes until the next flat (stable) price phase begins",
|
||||
"long_description": "Shows how many minutes until the next flat price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use to anticipate price stabilisation after a volatile phase."
|
||||
},
|
||||
"chart_data_export": {
|
||||
"description": "Datenexport für Dashboard-Integrationen",
|
||||
"long_description": "Dieser Sensor ruft den get_chartdata-Service mit deiner konfigurierten YAML-Konfiguration auf und stellt das Ergebnis als Entity-Attribute bereit. Der Status zeigt 'ready' wenn Daten verfügbar sind, 'error' bei Fehlern, oder 'pending' vor dem ersten Aufruf. Perfekt für Dashboard-Integrationen wie ApexCharts, die Preisdaten aus Entity-Attributen lesen.",
|
||||
|
|
@ -537,12 +597,12 @@
|
|||
"usage_tips": "Weitester Vorausblick: 'Wenn next_interval_price_rank_today_tomorrow < 10, ist das nächste Intervall eines der günstigsten im Zweitages-Fenster'."
|
||||
},
|
||||
"previous_interval_price_rank_today": {
|
||||
"description": "Perzentilrang des letzten Intervallpreises in der heutigen Rangliste (0 % = günstigster Moment heute)",
|
||||
"description": "Perzentilrang des vorherigen Intervallpreises in der heutigen Rangliste (0 % = günstigster Moment heute)",
|
||||
"long_description": "Zeigt den Perzentilrang des gerade beendeten Viertelstunden-Intervalls innerhalb der 96 heutigen Slots. Nützlich für Protokollierung. Attribute: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "Für retrospektive Automationen: 'Preisniveau des letzten Intervalls für Energieberichte aufzeichnen'."
|
||||
"usage_tips": "Für retrospektive Automationen: 'Preisniveau des vorherigen Intervalls für Energieberichte aufzeichnen'."
|
||||
},
|
||||
"previous_interval_price_rank_today_tomorrow": {
|
||||
"description": "Perzentilrang des letzten Intervallpreises über heute+morgen zusammen (0 % = günstigstes des Zweitages-Fensters)",
|
||||
"description": "Perzentilrang des vorherigen Intervallpreises über heute+morgen zusammen (0 % = günstigstes des Zweitages-Fensters)",
|
||||
"long_description": "Zeigt den Perzentilrang des gerade beendeten Viertelstunden-Intervalls innerhalb des kombinierten heute+morgen-Pools (bis zu 192 Slots). Fällt auf nur heute zurück. Attribute: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "Für retrospektive Vergleiche über ein Zweitages-Fenster."
|
||||
},
|
||||
|
|
@ -583,6 +643,21 @@
|
|||
"long_description": "Wird aktiviert, wenn der aktuelle Preis in den unteren 20% der heutigen Preise liegt",
|
||||
"usage_tips": "Nutze dies, um Geräte mit hohem Verbrauch während der günstigsten Intervalle zu betreiben"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"description": "Whether prices are currently in a rising phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is rising.",
|
||||
"usage_tips": "Use in automations to delay or avoid running flexible loads during rising prices."
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"description": "Whether prices are currently in a falling phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is falling.",
|
||||
"usage_tips": "Use in automations to take advantage of falling prices for flexible loads."
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"description": "Whether prices are currently in a flat (stable) phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is flat and prices are relatively stable.",
|
||||
"usage_tips": "Use for loads that prefer price stability rather than the lowest price."
|
||||
},
|
||||
"connection": {
|
||||
"description": "Ob die Verbindung zur Tibber API funktioniert",
|
||||
"long_description": "Zeigt an, ob die Integration erfolgreich eine Verbindung zur Tibber API herstellen kann",
|
||||
|
|
|
|||
|
|
@ -501,6 +501,66 @@
|
|||
"long_description": "Classifies tomorrow (once data is available, typically after 13:00) into a price shape using the same algorithm as today. The valley_start / valley_end or peak_start / peak_end attributes give knee-point times for the primary extremum so you can pre-schedule loads the evening before.",
|
||||
"usage_tips": "Set up evening automations that read tomorrow's pattern and pre-configure heat pump schedules, car charging timers, or water heater settings for the following day. Pair with the tomorrow_data_available binary sensor to trigger the automation only when data is ready."
|
||||
},
|
||||
"current_price_phase": {
|
||||
"description": "Whether electricity prices are currently rising, falling, or flat within today's intra-day price shape",
|
||||
"long_description": "Shows the direction of price movement at the current time by identifying which monotone segment of today's price curve you are in. Today's prices are split into consecutively rising, falling, or flat stretches (phases). This sensor tells you which phase is active right now. Attributes include the phase's start and end times, its price range (min/max/mean), its position among all phases of the day (segment_index and segment_count), and the full list of all today's phases (all_segments). Updates every 15 minutes.",
|
||||
"usage_tips": "Use in automations: 'If current_price_phase = falling, delay flexible loads until prices bottom out'. Pair with the Today's Price Pattern sensor to see both the overall shape of the day and your current position in it. Check segment_index and segment_count to understand how far through the intra-day movement you are — e.g. if segment_index=0 and the phase is already falling, prices have been declining since midnight. Use all_segments in templates or dashboards to display the full day ahead."
|
||||
},
|
||||
"next_price_phase": {
|
||||
"description": "The next intra-day price phase — what comes after the current price movement",
|
||||
"long_description": "Shows the monotone price phase that will follow after the currently active phase ends. The start attribute tells you exactly when the next phase begins, making it easy to schedule automations. When the current phase is the last one of the day (e.g. the final evening fall), this sensor becomes unavailable. Attributes: start (when it begins), end, price range (min/max/mean), segment_index, segment_count. Updates every 15 minutes.",
|
||||
"usage_tips": "Use in automations: 'If next_price_phase = rising and next_price_phase.start is within 1 hour, start the washing machine now'. Or combine with current_price_phase: 'If current_price_phase = falling and next_price_phase = flat, we are approaching the daily low — good time for flexible loads'. The start attribute is particularly valuable: trigger automations precisely when the next phase begins."
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"description": "When the current intra-day price phase ends",
|
||||
"long_description": "Shows the exact timestamp when the currently active rising, falling, or flat price phase will end and transition to the next phase. Becomes unavailable when no segment data is available. Updates at interval boundaries (every 15 minutes). Pair with current_price_phase to know both what phase you are in and when it ends.",
|
||||
"usage_tips": "Use in automations to schedule tasks that must finish before prices change: 'Start washing machine if current_price_phase = falling and current_price_phase_end_time is more than 2 hours away'. Or to alert when a cheap phase is about to end."
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"description": "Minutes remaining in the current price phase",
|
||||
"long_description": "Shows how many minutes are left in the current intra-day price phase (rising, falling, or flat). Updates every minute for countdown precision. Returns 0 when no segment data is available. The remaining_minutes attribute mirrors the sensor value in integer minutes for simpler automation templates.",
|
||||
"usage_tips": "Use in automations: 'If current_price_phase = falling and current_price_phase_remaining_minutes < 30, start the dishwasher now before prices level out'. Also useful for dashboard cards showing a countdown bar until the phase transition."
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"description": "Total duration of the current price phase",
|
||||
"long_description": "Shows the total length of the currently active price phase in hours (expressed in minutes internally). Updates at interval boundaries. This tells you how long the current trend segment lasts in total — useful for understanding whether it is a brief fluctuation or an extended period of rising or falling prices.",
|
||||
"usage_tips": "Combine with current_price_phase_remaining_minutes to estimate how far through the phase you are, or compare with current_price_phase_progress to understand the time profile of the current trend."
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"description": "How far through the current price phase we are",
|
||||
"long_description": "Shows the percentage of the current intra-day price phase that has elapsed (0–100%). Updates every minute. A value near 0% means the phase just started; near 100% means it is about to end. Returns 0 when no segment data is available.",
|
||||
"usage_tips": "Use in dashboard cards to display a visual progress bar for the current price phase. In automations: 'If current_price_phase = falling and current_price_phase_progress > 80, the cheapest prices are close — prepare flexible loads now'."
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"description": "When the next rising price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming rising price segment across today's remaining phases and tomorrow's phases. Returns unavailable when no more rising phases exist in today's or tomorrow's data. Updates at interval boundaries (every 15 minutes).",
|
||||
"usage_tips": "Use with in_falling_price_phase and next_rising_phase_start_time to schedule flexible loads: run them now while prices fall, and wrap up before the next rise starts. 'If in_falling_price_phase is ON and next_rising_phase_start_time is less than 1 hour away, start the washing machine immediately'."
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"description": "When the next falling price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming falling price segment across today's remaining phases and tomorrow's phases. Returns unavailable when no more falling phases exist in today's or tomorrow's data. Updates at interval boundaries (every 15 minutes).",
|
||||
"usage_tips": "Use to delay flexible loads until the next price drop starts. 'If next_falling_phase_start_time is within 2 hours, consider waiting before starting the dishwasher or heat pump'."
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"description": "When the next flat (stable) price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming flat price segment (where prices show little variation) across today's remaining phases and tomorrow's phases. Returns unavailable when no more flat phases exist in the available data. Updates at interval boundaries (every 15 minutes).",
|
||||
"usage_tips": "Flat phases indicate price stability — useful for scheduling loads that need a predictable cost over time. 'If next_flat_phase_start_time is within 30 minutes and current_price_phase = rising, the price peak is nearly over'."
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"description": "Minutes until the next rising price phase begins",
|
||||
"long_description": "Shows how many minutes until the next rising price phase starts. Updates every minute. Returns unavailable if no more rising phases exist in today's or tomorrow's data. The next_in_minutes attribute mirrors the sensor value in integer minutes for automation templates.",
|
||||
"usage_tips": "Use in countdown automations: 'Alert me 15 minutes before the next price rise so I can delay flexible loads'. Combine with in_falling_price_phase: if currently falling and a rise is imminent, act before prices start climbing."
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"description": "Minutes until the next falling price phase begins",
|
||||
"long_description": "Shows how many minutes until the next falling price phase starts. Updates every minute. Returns unavailable if no more falling phases exist in today's or tomorrow's data. The next_in_minutes attribute mirrors the sensor value in integer minutes for automation templates.",
|
||||
"usage_tips": "Use to time flexible loads optimally: 'If next_falling_phase_in_minutes < 60, delay the washing machine start to benefit from the upcoming price drop'."
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"description": "Minutes until the next flat (stable) price phase begins",
|
||||
"long_description": "Shows how many minutes until the next flat price phase starts. Updates every minute. Returns unavailable if no more flat phases exist in today's or tomorrow's data. The next_in_minutes attribute mirrors the sensor value in integer minutes.",
|
||||
"usage_tips": "Use to anticipate price stabilisation after a volatile phase. 'If next_flat_phase_in_minutes < 30 and current_price_phase = rising, the price peak will soon level off — consider delaying loads until then'."
|
||||
},
|
||||
"chart_data_export": {
|
||||
"description": "Data export for dashboard integrations",
|
||||
"long_description": "This binary sensor calls the get_chartdata service with your configured YAML parameters and exposes the result as entity attributes. The state is 'on' when the service call succeeds and data is available, 'off' when the call fails or no configuration is set. Perfect for dashboard integrations like ApexCharts that need to read price data from entity attributes.",
|
||||
|
|
@ -538,8 +598,8 @@
|
|||
},
|
||||
"previous_interval_price_rank_today": {
|
||||
"description": "Where the previous interval's price sat in today's ranking (0% = cheapest moment of today)",
|
||||
"long_description": "Shows the percentile rank of the just-ended quarter-hour interval's price within today's 96 slots. Useful for logging how cheap/expensive the last interval was. Attributes: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "Useful for retrospective automations or logging: 'Record the cost tier of the last interval for energy reports'."
|
||||
"long_description": "Shows the percentile rank of the just-ended quarter-hour interval's price within today's 96 slots. Useful for logging how cheap/expensive the previous interval was. Attributes: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "Useful for retrospective automations or logging: 'Record the cost tier of the previous interval for energy reports'."
|
||||
},
|
||||
"previous_interval_price_rank_today_tomorrow": {
|
||||
"description": "Previous interval's percentile rank across today and tomorrow combined (0% = cheapest of the two-day window)",
|
||||
|
|
@ -583,6 +643,21 @@
|
|||
"long_description": "Turns on when the current price is in the bottom 20% of today's prices",
|
||||
"usage_tips": "Use this to run high-consumption appliances during the cheapest intervals"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"description": "Whether prices are currently in a rising phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is rising — i.e. prices have been moving upward since the last phase transition. Exactly one of in_rising_price_phase, in_falling_price_phase, and in_flat_price_phase is ON at any time. Becomes unavailable when no segment data is available.",
|
||||
"usage_tips": "Use in automations to delay or avoid running flexible loads: 'If in_rising_price_phase is ON, postpone the dishwasher'. Pair with next_falling_phase_start_time to know when prices will start dropping again."
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"description": "Whether prices are currently in a falling phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is falling — i.e. prices have been dropping since the last phase transition. This is often a good window to start flexible loads. Exactly one of in_rising_price_phase, in_falling_price_phase, and in_flat_price_phase is ON at any time.",
|
||||
"usage_tips": "Use in automations to take advantage of falling prices: 'If in_falling_price_phase is ON and current_price_phase_remaining_minutes > 60, start the washing machine'. Combine with next_rising_phase_start_time to avoid starting a load that won't finish before prices rise."
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"description": "Whether prices are currently in a flat (stable) phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is flat — i.e. prices are relatively stable with no significant rise or fall. Flat phases indicate predictable costs, making them suitable for loads with uncertain or variable duration. Exactly one of in_rising_price_phase, in_falling_price_phase, and in_flat_price_phase is ON at any time.",
|
||||
"usage_tips": "Use for loads that are indifferent to price direction but prefer stability: heat pumps in steady-state mode, background charging, or water heater top-ups. Combine with current_price_phase_end_time to know how long the stable window lasts."
|
||||
},
|
||||
"connection": {
|
||||
"description": "Whether the connection to the Tibber API is working",
|
||||
"long_description": "Indicates if the integration can successfully connect to the Tibber API",
|
||||
|
|
|
|||
|
|
@ -501,6 +501,66 @@
|
|||
"long_description": "Klassifiserer i morgen (når data er tilgjengelig, typisk etter kl. 13) i et prismønster med samme algoritme som i dag. Attributtene valley_start/valley_end eller peak_start/peak_end gir knepunktstider.",
|
||||
"usage_tips": "Sett opp kveldsautomasjonar som leser morgendagens mønster og forhåndskonfigurerer varmepumpe, billader eller varmtvannsberedere. Kombiner med tomorrow_data_available-binærsensoren."
|
||||
},
|
||||
"current_price_phase": {
|
||||
"description": "Om strømprisene nå stiger, faller eller er stabile – innenfor dagens intra-dag prisform",
|
||||
"long_description": "Viser retningen på prisbevegelsen nå ved å identifisere det aktive monotone segmentet i dagens priskurve. Dagens priser deles inn i fortløpende stigende, fallende eller flate strekninger (faser). Denne sensoren viser hvilken fase som er aktivt akkurat nå. Attributter inkluderer fasens start- og sluttid, prisområde (min/maks/gjennomsnitt), posisjon blant dagens faser (segment_index og segment_count) og den fullstendige listen over alle dagens faser (all_segments). Oppdateres hvert 15. minutt.",
|
||||
"usage_tips": "Bruk i automasjonar: 'Hvis current_price_phase = fallende, vent med fleksible laster til prisene når bunnen'. Kombiner med Dagens Prismønster for å se både den overordnede dagformen og din nåværende posisjon i den. Sjekk segment_index og segment_count for å forstå hvor langt inn i bevegelsen du er. Bruk all_segments i maler eller dashbord for å vise hele dagsforløpet."
|
||||
},
|
||||
"next_price_phase": {
|
||||
"description": "Den neste intra-dag prisfasen – hva som kommer etter den nåværende prisbevegelsen",
|
||||
"long_description": "Viser det monotone prissegmentet som følger etter den for øyeblikket aktive fasen. Attributtet start viser nøyaktig når neste fase begynner, noe som gjør det enkelt å planlegge automasjonar. Når den nåværende fasen er den siste for dagen (f.eks. det siste kveldsfall), blir denne sensoren utilgjengelig. Attributter: start (når den begynner), end, prisområde (min/maks/gjennomsnitt), segment_index, segment_count. Oppdateres hvert 15. minutt.",
|
||||
"usage_tips": "Bruk i automasjonar: 'Hvis next_price_phase = stigende og next_price_phase.start er innen 1 time, start vaskemaskin nå'. Eller kombiner med current_price_phase: 'Hvis current_price_phase = fallende og next_price_phase = flat, nærmer vi oss daglig lavpunkt – godt tidspunkt for fleksible laster'. Attributtet start er spesielt verdifullt: utløs automasjonar nøyaktig når neste fase begynner."
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"description": "When the current intra-day price phase ends",
|
||||
"long_description": "Shows the exact timestamp when the currently active rising, falling, or flat price phase will end and transition to the next phase.",
|
||||
"usage_tips": "Use in automations to schedule tasks that must finish before prices change."
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"description": "Minutes remaining in the current price phase",
|
||||
"long_description": "Shows how many minutes are left in the current intra-day price phase. Updates every minute.",
|
||||
"usage_tips": "Use in automations: 'If current_price_phase = falling and remaining < 30, start the dishwasher now'."
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"description": "Total duration of the current price phase",
|
||||
"long_description": "Shows the total length of the currently active price phase in hours.",
|
||||
"usage_tips": "Combine with remaining minutes to understand how far through the phase you are."
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"description": "How far through the current price phase we are",
|
||||
"long_description": "Shows the percentage of the current intra-day price phase that has elapsed (0–100%). Updates every minute.",
|
||||
"usage_tips": "Use in dashboard cards to display a visual progress bar for the current price phase."
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"description": "When the next rising price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming rising price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use to schedule loads before prices start rising again."
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"description": "When the next falling price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming falling price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use to delay flexible loads until the next price drop starts."
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"description": "When the next flat (stable) price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming flat price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use for scheduling loads that need predictable costs over time."
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"description": "Minutes until the next rising price phase begins",
|
||||
"long_description": "Shows how many minutes until the next rising price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use in countdown automations to alert before the next price rise."
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"description": "Minutes until the next falling price phase begins",
|
||||
"long_description": "Shows how many minutes until the next falling price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use to time flexible loads: delay until the upcoming price drop."
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"description": "Minutes until the next flat (stable) price phase begins",
|
||||
"long_description": "Shows how many minutes until the next flat price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use to anticipate price stabilisation after a volatile phase."
|
||||
},
|
||||
"chart_data_export": {
|
||||
"description": "Dataeksport for dashboardintegrasjoner",
|
||||
"long_description": "Denne sensoren kaller get_chartdata-tjenesten med din konfigurerte YAML-konfigurasjon og eksponerer resultatet som entitetsattributter. Status viser 'ready' når data er tilgjengelig, 'error' ved feil, eller 'pending' før første kall. Perfekt for dashboardintegrasjoner som ApexCharts som trenger å lese prisdata fra entitetsattributter.",
|
||||
|
|
@ -538,12 +598,12 @@
|
|||
},
|
||||
"previous_interval_price_rank_today": {
|
||||
"description": "Prosentilrang for forrige intervalls pris i dagens rangering (0 % = billigste øyeblikk i dag)",
|
||||
"long_description": "Viser prosentilrangen for det nettopp avsluttede kvarter innenfor dagens 96 tidsluker. Nyttig for logging. Attributter: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "For retrospektive automatiseringer: 'Registrer prisnivyå for forrige intervall i energirapporter'."
|
||||
"long_description": "Viser prosentilrangen for det nettopp avsluttede kvarteret innenfor dagens 96 tidsluker. Nyttig for å logge hvor billig eller dyrt det forrige intervallet var. Attributter: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "For retrospektive automatiseringer: 'Registrer prisnivået for forrige intervall i energirapporter'."
|
||||
},
|
||||
"previous_interval_price_rank_today_tomorrow": {
|
||||
"description": "Prosentilrang for forrige intervalls pris over i dag+i morgen samlet (0 % = billigste i to-dagers-vinduet)",
|
||||
"long_description": "Viser prosentilrangen for det nettopp avsluttede kvarter innenfor det kombinerte i dag+i morgen-bassenget (opptil 192 tidsluker). Fæller tilbake til kun i dag. Attributter: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"long_description": "Viser prosentilrangen for det nettopp avsluttede kvarteret innenfor det kombinerte i dag+i morgen-bassenget (opptil 192 tidsluker). Faller tilbake til kun i dag når morgendagens data ikke er tilgjengelig. Attributter: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "For retrospektive sammenligninger over et to-dagers-vindu."
|
||||
},
|
||||
"current_hour_price_rank_today": {
|
||||
|
|
@ -583,6 +643,21 @@
|
|||
"long_description": "Slår seg på når nåværende pris er i bunn 20% av dagens priser",
|
||||
"usage_tips": "Bruk dette til å kjøre høyforbruksapparater i de billigste intervallene"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"description": "Whether prices are currently in a rising phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is rising.",
|
||||
"usage_tips": "Use in automations to delay or avoid running flexible loads during rising prices."
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"description": "Whether prices are currently in a falling phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is falling.",
|
||||
"usage_tips": "Use in automations to take advantage of falling prices for flexible loads."
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"description": "Whether prices are currently in a flat (stable) phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is flat and prices are relatively stable.",
|
||||
"usage_tips": "Use for loads that prefer price stability rather than the lowest price."
|
||||
},
|
||||
"connection": {
|
||||
"description": "Om tilkoblingen til Tibber API fungerer",
|
||||
"long_description": "Indikerer om integrasjonen kan koble til Tibber API",
|
||||
|
|
|
|||
|
|
@ -501,6 +501,66 @@
|
|||
"long_description": "Classificeert morgen (zodra data beschikbaar is, doorgaans na 13:00) in een prijspatroon met hetzelfde algoritme als vandaag. De attributen valley_start/valley_end of peak_start/peak_end geven kniepunttijden voor het primaire extremum.",
|
||||
"usage_tips": "Stel avondautomations in die het patroon van morgen lezen en warmtepomp, autolader of boiler vooraf configureren. Combineer met de tomorrow_data_available binaire sensor."
|
||||
},
|
||||
"current_price_phase": {
|
||||
"description": "Of elektriciteitsprijzen momenteel stijgen, dalen of stabiel zijn – binnen de intra-dag prijsvorm van vandaag",
|
||||
"long_description": "Toont de richting van de prijsbeweging op dit moment door het actieve monotone segment van de prijscurve van vandaag te bepalen. De dagprijzen worden opgesplitst in opeenvolgende stijgende, dalende of vlakke stukken (fasen). Deze sensor toont in welke fase je je nu bevindt. Attributen zijn onder andere de start- en eindtijd van de fase, het prijsbereik (min/max/gemiddelde), de positie binnen de dagelijkse fasen (segment_index en segment_count) en de volledige lijst van alle fasen van vandaag (all_segments). Elke 15 minuten bijgewerkt.",
|
||||
"usage_tips": "Gebruik in automations: 'Als current_price_phase = dalend, wacht met flexibele lasten totdat de prijzen de bodem bereiken'. Combineer met de Prijspatroon Vandaag-sensor om zowel de algemene dagvorm als je huidige positie daarin te zien. Controleer segment_index en segment_count om te begrijpen hoe ver je bent in de intra-dag beweging. Gebruik all_segments in sjablonen of dashboards om het volledige dagverloop te tonen."
|
||||
},
|
||||
"next_price_phase": {
|
||||
"description": "De volgende intra-dag prijsfase – wat er na de huidige prijsbeweging komt",
|
||||
"long_description": "Toont het monotone prijssegment dat volgt na de momenteel actieve fase. Het attribuut start geeft precies aan wanneer de volgende fase begint, wat het eenvoudig maakt om automations nauwkeurig te plannen. Wanneer de huidige fase de laatste van de dag is (bijv. de laatste avonddaling), wordt deze sensor niet beschikbaar. Attributen: start (wanneer het begint), end, prijsbereik (min/max/gemiddelde), segment_index, segment_count. Elke 15 minuten bijgewerkt.",
|
||||
"usage_tips": "Gebruik in automations: 'Als next_price_phase = stijgend en next_price_phase.start binnen 1 uur is, start de wasmachine nu'. Of combineer met current_price_phase: 'Als current_price_phase = dalend en next_price_phase = vlak, naderen we het dagdieptepunt – goed moment voor flexibele lasten'. Het attribuut start is bijzonder waardevol: activeer automations precies wanneer de volgende fase begint."
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"description": "When the current intra-day price phase ends",
|
||||
"long_description": "Shows the exact timestamp when the currently active rising, falling, or flat price phase will end and transition to the next phase.",
|
||||
"usage_tips": "Use in automations to schedule tasks that must finish before prices change."
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"description": "Minutes remaining in the current price phase",
|
||||
"long_description": "Shows how many minutes are left in the current intra-day price phase. Updates every minute.",
|
||||
"usage_tips": "Use in automations: 'If current_price_phase = falling and remaining < 30, start the dishwasher now'."
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"description": "Total duration of the current price phase",
|
||||
"long_description": "Shows the total length of the currently active price phase in hours.",
|
||||
"usage_tips": "Combine with remaining minutes to understand how far through the phase you are."
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"description": "How far through the current price phase we are",
|
||||
"long_description": "Shows the percentage of the current intra-day price phase that has elapsed (0–100%). Updates every minute.",
|
||||
"usage_tips": "Use in dashboard cards to display a visual progress bar for the current price phase."
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"description": "When the next rising price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming rising price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use to schedule loads before prices start rising again."
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"description": "When the next falling price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming falling price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use to delay flexible loads until the next price drop starts."
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"description": "When the next flat (stable) price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming flat price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use for scheduling loads that need predictable costs over time."
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"description": "Minutes until the next rising price phase begins",
|
||||
"long_description": "Shows how many minutes until the next rising price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use in countdown automations to alert before the next price rise."
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"description": "Minutes until the next falling price phase begins",
|
||||
"long_description": "Shows how many minutes until the next falling price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use to time flexible loads: delay until the upcoming price drop."
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"description": "Minutes until the next flat (stable) price phase begins",
|
||||
"long_description": "Shows how many minutes until the next flat price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use to anticipate price stabilisation after a volatile phase."
|
||||
},
|
||||
"chart_data_export": {
|
||||
"description": "Data-export voor dashboard-integraties",
|
||||
"long_description": "Deze sensor roept de get_chartdata-service aan met jouw geconfigureerde YAML-configuratie en stelt het resultaat beschikbaar als entiteitsattributen. De status toont 'ready' wanneer data beschikbaar is, 'error' bij fouten, of 'pending' voor de eerste aanroep. Perfekt voor dashboard-integraties zoals ApexCharts die prijsgegevens uit entiteitsattributen moeten lezen.",
|
||||
|
|
@ -538,8 +598,8 @@
|
|||
},
|
||||
"previous_interval_price_rank_today": {
|
||||
"description": "Percentielrang van de vorige intervalprijs in de ranglijst van vandaag (0% = goedkoopste moment van vandaag)",
|
||||
"long_description": "Toont de percentielrang van het zojuist afgelopen kwartier binnen de 96 slots van vandaag. Nuttig voor logging. Attributen: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "Voor retrospectieve automatiseringen: 'Leg het prijsniveau van het vorige interval vast voor energierapporten'."
|
||||
"long_description": "Toont de percentielrang van het zojuist afgelopen kwartier binnen de 96 slots van vandaag. Handig om te loggen hoe goedkoop of duur het vorige interval was. Attributen: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "Nuttig voor retrospectieve automatiseringen of logging: 'Leg de kostencategorie van het vorige interval vast voor energierapporten'."
|
||||
},
|
||||
"previous_interval_price_rank_today_tomorrow": {
|
||||
"description": "Percentielrang van de vorige intervalprijs over vandaag+morgen samen (0% = goedkoopste van het twee-dagenvenster)",
|
||||
|
|
@ -583,6 +643,21 @@
|
|||
"long_description": "Wordt geactiveerd wanneer de huidige prijs in de onderste 20% van de prijzen van vandaag ligt",
|
||||
"usage_tips": "Gebruik dit om apparaten met hoog verbruik te laten draaien tijdens de goedkoopste intervallen"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"description": "Whether prices are currently in a rising phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is rising.",
|
||||
"usage_tips": "Use in automations to delay or avoid running flexible loads during rising prices."
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"description": "Whether prices are currently in a falling phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is falling.",
|
||||
"usage_tips": "Use in automations to take advantage of falling prices for flexible loads."
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"description": "Whether prices are currently in a flat (stable) phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is flat and prices are relatively stable.",
|
||||
"usage_tips": "Use for loads that prefer price stability rather than the lowest price."
|
||||
},
|
||||
"connection": {
|
||||
"description": "Of de verbinding met de Tibber API werkt",
|
||||
"long_description": "Geeft aan of de integratie succesvol verbinding kan maken met de Tibber API",
|
||||
|
|
|
|||
|
|
@ -501,6 +501,66 @@
|
|||
"long_description": "Klassificerar imorgon (när data finns tillgänglig, vanligtvis efter 13:00) i ett prismönster med samma algoritm som idag. Attributen valley_start/valley_end eller peak_start/peak_end ger knäpunkttider för det primära extremvärdet.",
|
||||
"usage_tips": "Ställ in kvällsautomationer som läser morgondagens mönster och förkonfigurerar värmepump, billaddare eller varmvattenberedare. Kombinera med tomorrow_data_available-binärsensorn."
|
||||
},
|
||||
"current_price_phase": {
|
||||
"description": "Om elpriserna för närvarande stiger, faller eller är stabila – inom dagens intra-dag prisform",
|
||||
"long_description": "Visar riktningen på priserörelsen just nu genom att identifiera det aktiva monotona segmentet i dagens priskurva. Dagens priser delas upp i på varandra följande stigande, fallande eller flata sträckor (faser). Denna sensor visar vilken fas som är aktiv just nu. Attribut inkluderar fasens start- och sluttid, prisintervall (min/max/medel), position bland dagens faser (segment_index och segment_count) samt den fullständiga listan över alla dagens faser (all_segments). Uppdateras var 15:e minut.",
|
||||
"usage_tips": "Använd i automationer: 'Om current_price_phase = fallande, vänta med flexibla laster tills priserna når botten'. Kombinera med Dagens Prismönster för att se både den övergripande dagformen och din aktuella position i den. Kontrollera segment_index och segment_count för att förstå hur långt in i rörelsen du är. Använd all_segments i mallar eller dashboards för att visa hela dagsförloppet."
|
||||
},
|
||||
"next_price_phase": {
|
||||
"description": "Nästa intra-dag prisfas – vad som kommer efter den aktuella prisbevegelsen",
|
||||
"long_description": "Visar det monotona prissegment som följer efter den för närvarande aktiva fasen. Attributet start visar exakt när nästa fas börjar, vilket gör det enkelt att schemalägga automationer. När den aktuella fasen är den sista för dagen (t.ex. den sista kvällsminskningen), blir denna sensor otillgänglig. Attribut: start (när den börjar), end, prisintervall (min/max/medel), segment_index, segment_count. Uppdateras var 15:e minut.",
|
||||
"usage_tips": "Använd i automationer: 'Om next_price_phase = stigande och next_price_phase.start är inom 1 timme, starta tvättmaskinen nu'. Eller kombinera med current_price_phase: 'Om current_price_phase = fallande och next_price_phase = flat, närmar vi oss dagens lågpunkt – bra tid för flexibla laster'. Attributet start är särskilt värdefull: utlös automationer precis när nästa fas börjar."
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"description": "When the current intra-day price phase ends",
|
||||
"long_description": "Shows the exact timestamp when the currently active rising, falling, or flat price phase will end and transition to the next phase.",
|
||||
"usage_tips": "Use in automations to schedule tasks that must finish before prices change."
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"description": "Minutes remaining in the current price phase",
|
||||
"long_description": "Shows how many minutes are left in the current intra-day price phase. Updates every minute.",
|
||||
"usage_tips": "Use in automations: 'If current_price_phase = falling and remaining < 30, start the dishwasher now'."
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"description": "Total duration of the current price phase",
|
||||
"long_description": "Shows the total length of the currently active price phase in hours.",
|
||||
"usage_tips": "Combine with remaining minutes to understand how far through the phase you are."
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"description": "How far through the current price phase we are",
|
||||
"long_description": "Shows the percentage of the current intra-day price phase that has elapsed (0–100%). Updates every minute.",
|
||||
"usage_tips": "Use in dashboard cards to display a visual progress bar for the current price phase."
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"description": "When the next rising price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming rising price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use to schedule loads before prices start rising again."
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"description": "When the next falling price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming falling price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use to delay flexible loads until the next price drop starts."
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"description": "When the next flat (stable) price phase begins",
|
||||
"long_description": "Shows the timestamp of the next upcoming flat price segment across today's remaining phases and tomorrow's phases.",
|
||||
"usage_tips": "Use for scheduling loads that need predictable costs over time."
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"description": "Minutes until the next rising price phase begins",
|
||||
"long_description": "Shows how many minutes until the next rising price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use in countdown automations to alert before the next price rise."
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"description": "Minutes until the next falling price phase begins",
|
||||
"long_description": "Shows how many minutes until the next falling price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use to time flexible loads: delay until the upcoming price drop."
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"description": "Minutes until the next flat (stable) price phase begins",
|
||||
"long_description": "Shows how many minutes until the next flat price phase starts. Updates every minute.",
|
||||
"usage_tips": "Use to anticipate price stabilisation after a volatile phase."
|
||||
},
|
||||
"chart_data_export": {
|
||||
"description": "Dataexport för dashboard-integrationer",
|
||||
"long_description": "Denna sensor anropar get_chartdata-tjänsten med din konfigurerade YAML-konfiguration och exponerar resultatet som entitetsattribut. Statusen visar 'ready' när data är tillgänglig, 'error' vid fel, eller 'pending' före första anropet. Perfekt för dashboard-integrationer som ApexCharts som behöver läsa prisdata från entitetsattribut.",
|
||||
|
|
@ -538,8 +598,8 @@
|
|||
},
|
||||
"previous_interval_price_rank_today": {
|
||||
"description": "Percentilrang för föregående intervalls pris i dagens rangordning (0 % = billigaste tillfället idag)",
|
||||
"long_description": "Visar percentilrangen för det nyligen avslutade kvartalet inom dagens 96 slotar. Användbart för loggning. Attribut: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "För retrospektiva automatiseringar: 'Registrera prisnivån för det förra intervallet i energirapporter'."
|
||||
"long_description": "Visar percentilrangen för det nyligen avslutade kvartalet inom dagens 96 slotar. Användbart för att logga hur billigt eller dyrt det föregående intervallet var. Attribut: `previous_price`, `prices_below_count`, `interval_count`, `reference_min`, `reference_max`, `reference_mean`.",
|
||||
"usage_tips": "Användbart för retrospektiva automatiseringar eller loggning: 'Registrera kostnadsnivån för det föregående intervallet i energirapporter'."
|
||||
},
|
||||
"previous_interval_price_rank_today_tomorrow": {
|
||||
"description": "Percentilrang för föregående intervalls pris över idag+imorgon sammantaget (0 % = billigaste i tvådagarsperioden)",
|
||||
|
|
@ -583,6 +643,21 @@
|
|||
"long_description": "Aktiveras när nuvarande pris ligger i botten 20% av dagens priser",
|
||||
"usage_tips": "Använd detta för att köra högkonsumtionsapparater under de billigaste intervallerna"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"description": "Whether prices are currently in a rising phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is rising.",
|
||||
"usage_tips": "Use in automations to delay or avoid running flexible loads during rising prices."
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"description": "Whether prices are currently in a falling phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is falling.",
|
||||
"usage_tips": "Use in automations to take advantage of falling prices for flexible loads."
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"description": "Whether prices are currently in a flat (stable) phase",
|
||||
"long_description": "Turns ON when the current intra-day price phase is flat and prices are relatively stable.",
|
||||
"usage_tips": "Use for loads that prefer price stability rather than the lowest price."
|
||||
},
|
||||
"connection": {
|
||||
"description": "Om anslutningen till Tibber API fungerar",
|
||||
"long_description": "Indikerar om integrationen framgångsrikt kan ansluta till Tibber API",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ from .daily_stat import add_statistics_attributes
|
|||
from .future import add_next_avg_attributes, get_future_prices
|
||||
from .interval import add_current_interval_price_attributes
|
||||
from .lifecycle import build_lifecycle_attributes
|
||||
from .metadata import get_day_pattern_attributes
|
||||
from .metadata import get_current_price_phase_attributes, get_day_pattern_attributes, get_next_price_phase_attributes
|
||||
from .timing import _is_timing_or_volatility_sensor
|
||||
from .trend import _add_cached_trend_attributes, _add_timing_or_volatility_attributes
|
||||
from .volatility import add_percentile_rank_attributes, add_volatility_type_attributes, get_prices_for_volatility
|
||||
|
|
@ -195,6 +195,16 @@ def build_sensor_attributes(
|
|||
if day_attrs:
|
||||
attributes.update(day_attrs)
|
||||
|
||||
elif key == "current_price_phase":
|
||||
phase_attrs = get_current_price_phase_attributes(coordinator, time=time)
|
||||
if phase_attrs:
|
||||
attributes.update(phase_attrs)
|
||||
|
||||
elif key == "next_price_phase":
|
||||
next_phase_attrs = get_next_price_phase_attributes(coordinator, time=time)
|
||||
if next_phase_attrs:
|
||||
attributes.update(next_phase_attrs)
|
||||
|
||||
# For current_interval_price_level, add the original level as attribute
|
||||
if key == "current_interval_price_level" and cached_data.get("last_price_level") is not None:
|
||||
attributes["level_id"] = cached_data["last_price_level"]
|
||||
|
|
|
|||
|
|
@ -107,3 +107,128 @@ def get_day_pattern_attributes(
|
|||
attrs["segments"] = segments
|
||||
|
||||
return attrs or None
|
||||
|
||||
|
||||
def get_current_price_phase_attributes(
|
||||
coordinator: TibberPricesDataUpdateCoordinator,
|
||||
*,
|
||||
time: TibberPricesTimeService,
|
||||
) -> dict[str, Any] | None:
|
||||
"""
|
||||
Build attributes for the current_price_phase sensor.
|
||||
|
||||
Returns details of the monotone segment that covers the current time,
|
||||
plus contextual position info and the full list of all today's segments.
|
||||
|
||||
Args:
|
||||
coordinator: The data update coordinator.
|
||||
time: TibberPricesTimeService instance.
|
||||
|
||||
Returns:
|
||||
Attribute dict or None if data is unavailable.
|
||||
|
||||
"""
|
||||
if not coordinator.data:
|
||||
return None
|
||||
|
||||
day_patterns = coordinator.data.get("dayPatterns")
|
||||
if not day_patterns:
|
||||
return None
|
||||
|
||||
today_data: dict[str, Any] | None = day_patterns.get("today")
|
||||
if not today_data:
|
||||
return None
|
||||
|
||||
segments: list[dict[str, Any]] | None = today_data.get("segments")
|
||||
if not segments:
|
||||
return None
|
||||
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
now = time.now()
|
||||
current_index: int | None = None
|
||||
for i, segment in enumerate(segments):
|
||||
seg_start_str: str | None = segment.get("start")
|
||||
if not seg_start_str:
|
||||
continue
|
||||
seg_start = parse_datetime(seg_start_str)
|
||||
if seg_start is not None and now >= seg_start:
|
||||
current_index = i
|
||||
|
||||
if current_index is None:
|
||||
return None
|
||||
|
||||
seg = segments[current_index]
|
||||
attrs: dict[str, Any] = {
|
||||
"start": seg.get("start"),
|
||||
"end": seg.get("end"),
|
||||
"price_min": seg.get("price_min"),
|
||||
"price_max": seg.get("price_max"),
|
||||
"price_mean": seg.get("price_mean"),
|
||||
"segment_index": current_index,
|
||||
"segment_count": len(segments),
|
||||
"all_segments": segments,
|
||||
}
|
||||
return attrs
|
||||
|
||||
|
||||
def get_next_price_phase_attributes(
|
||||
coordinator: TibberPricesDataUpdateCoordinator,
|
||||
*,
|
||||
time: TibberPricesTimeService,
|
||||
) -> dict[str, Any] | None:
|
||||
"""
|
||||
Build attributes for the next_price_phase sensor.
|
||||
|
||||
Returns details of the segment that follows the currently active one,
|
||||
including its start time (useful for scheduling automations).
|
||||
|
||||
Args:
|
||||
coordinator: The data update coordinator.
|
||||
time: TibberPricesTimeService instance.
|
||||
|
||||
Returns:
|
||||
Attribute dict or None if no next segment exists.
|
||||
|
||||
"""
|
||||
if not coordinator.data:
|
||||
return None
|
||||
|
||||
day_patterns = coordinator.data.get("dayPatterns")
|
||||
if not day_patterns:
|
||||
return None
|
||||
|
||||
today_data: dict[str, Any] | None = day_patterns.get("today")
|
||||
if not today_data:
|
||||
return None
|
||||
|
||||
segments: list[dict[str, Any]] | None = today_data.get("segments")
|
||||
if not segments:
|
||||
return None
|
||||
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
now = time.now()
|
||||
current_index: int | None = None
|
||||
for i, segment in enumerate(segments):
|
||||
seg_start_str: str | None = segment.get("start")
|
||||
if not seg_start_str:
|
||||
continue
|
||||
seg_start = parse_datetime(seg_start_str)
|
||||
if seg_start is not None and now >= seg_start:
|
||||
current_index = i
|
||||
|
||||
if current_index is None or current_index + 1 >= len(segments):
|
||||
return None
|
||||
|
||||
next_seg = segments[current_index + 1]
|
||||
attrs: dict[str, Any] = {
|
||||
"start": next_seg.get("start"),
|
||||
"end": next_seg.get("end"),
|
||||
"price_min": next_seg.get("price_min"),
|
||||
"price_max": next_seg.get("price_max"),
|
||||
"price_mean": next_seg.get("price_mean"),
|
||||
"segment_index": current_index + 1,
|
||||
"segment_count": len(segments),
|
||||
}
|
||||
return attrs
|
||||
|
|
|
|||
|
|
@ -26,19 +26,29 @@ def _hours_to_minutes(state_value: Any) -> int | None:
|
|||
|
||||
def _is_timing_or_volatility_sensor(key: str) -> bool:
|
||||
"""Check if sensor is a timing or volatility sensor."""
|
||||
return key.endswith("_volatility") or (
|
||||
key.startswith(("best_price_", "peak_price_"))
|
||||
and any(
|
||||
suffix in key
|
||||
for suffix in [
|
||||
"end_time",
|
||||
"remaining_minutes",
|
||||
"progress",
|
||||
"next_start_time",
|
||||
"next_in_minutes",
|
||||
]
|
||||
)
|
||||
if key.endswith("_volatility"):
|
||||
return True
|
||||
# best/peak price timing sensors
|
||||
if key.startswith(("best_price_", "peak_price_")) and any(
|
||||
suffix in key for suffix in ["end_time", "remaining_minutes", "progress", "next_start_time", "next_in_minutes"]
|
||||
):
|
||||
return True
|
||||
# price phase timing sensors
|
||||
_PHASE_TIMING_KEYS = frozenset(
|
||||
{
|
||||
"current_price_phase_end_time",
|
||||
"current_price_phase_remaining_minutes",
|
||||
"current_price_phase_duration",
|
||||
"current_price_phase_progress",
|
||||
"next_rising_phase_start_time",
|
||||
"next_falling_phase_start_time",
|
||||
"next_flat_phase_start_time",
|
||||
"next_rising_phase_in_minutes",
|
||||
"next_falling_phase_in_minutes",
|
||||
"next_flat_phase_in_minutes",
|
||||
}
|
||||
)
|
||||
return key in _PHASE_TIMING_KEYS
|
||||
|
||||
|
||||
def add_period_timing_attributes(
|
||||
|
|
@ -63,7 +73,8 @@ def add_period_timing_attributes(
|
|||
|
||||
"""
|
||||
# Determine if this is a quarter-hour or 30-second update sensor
|
||||
is_quarter_hour_sensor = key.endswith(("_end_time", "_next_start_time"))
|
||||
# Includes *_start_time to catch next_[type]_phase_start_time keys
|
||||
is_quarter_hour_sensor = key.endswith(("_end_time", "_next_start_time", "_start_time"))
|
||||
|
||||
now = time.now()
|
||||
|
||||
|
|
@ -86,9 +97,11 @@ def add_period_timing_attributes(
|
|||
if minute_value is not None:
|
||||
if key.endswith("period_duration"):
|
||||
attributes["period_duration_minutes"] = minute_value
|
||||
elif "phase_duration" in key:
|
||||
attributes["phase_duration_minutes"] = minute_value
|
||||
elif key.endswith("remaining_minutes"):
|
||||
attributes["remaining_minutes"] = minute_value
|
||||
elif key.endswith("next_in_minutes"):
|
||||
elif key.endswith("in_minutes"):
|
||||
attributes["next_in_minutes"] = minute_value
|
||||
|
||||
# Add icon_color for dynamic styling
|
||||
|
|
|
|||
|
|
@ -137,3 +137,247 @@ class TibberPricesMetadataCalculator(TibberPricesBaseCalculator):
|
|||
return None
|
||||
|
||||
return day_data.get("pattern")
|
||||
|
||||
def get_current_price_phase_value(self) -> str | None:
|
||||
"""
|
||||
Get the current intra-day price phase (rising / falling / flat).
|
||||
|
||||
Finds the monotone segment in today's day-pattern that covers the
|
||||
current time and returns its type string.
|
||||
|
||||
Returns:
|
||||
"rising", "falling", or "flat", or None if data is unavailable.
|
||||
|
||||
"""
|
||||
if not self.coordinator.data:
|
||||
return None
|
||||
|
||||
day_patterns = self.coordinator.data.get("dayPatterns")
|
||||
if not day_patterns:
|
||||
return None
|
||||
|
||||
today_data = day_patterns.get("today")
|
||||
if not today_data:
|
||||
return None
|
||||
|
||||
segments: list[dict] | None = today_data.get("segments")
|
||||
if not segments:
|
||||
return None
|
||||
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
now = self.coordinator.time.now()
|
||||
current_segment: dict | None = None
|
||||
for segment in segments:
|
||||
seg_start_str: str | None = segment.get("start")
|
||||
if not seg_start_str:
|
||||
continue
|
||||
seg_start = parse_datetime(seg_start_str)
|
||||
if seg_start is not None and now >= seg_start:
|
||||
current_segment = segment
|
||||
|
||||
if current_segment is None:
|
||||
return None
|
||||
|
||||
return current_segment.get("type")
|
||||
|
||||
def get_next_price_phase_value(self) -> str | None:
|
||||
"""
|
||||
Get the next intra-day price phase (rising / falling / flat).
|
||||
|
||||
Finds the monotone segment in today's day-pattern that starts after
|
||||
the current segment and returns its type string.
|
||||
|
||||
Returns:
|
||||
"rising", "falling", or "flat", or None if no next segment exists.
|
||||
|
||||
"""
|
||||
if not self.coordinator.data:
|
||||
return None
|
||||
|
||||
day_patterns = self.coordinator.data.get("dayPatterns")
|
||||
if not day_patterns:
|
||||
return None
|
||||
|
||||
today_data = day_patterns.get("today")
|
||||
if not today_data:
|
||||
return None
|
||||
|
||||
segments: list[dict] | None = today_data.get("segments")
|
||||
if not segments:
|
||||
return None
|
||||
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
now = self.coordinator.time.now()
|
||||
current_index: int | None = None
|
||||
for i, segment in enumerate(segments):
|
||||
seg_start_str: str | None = segment.get("start")
|
||||
if not seg_start_str:
|
||||
continue
|
||||
seg_start = parse_datetime(seg_start_str)
|
||||
if seg_start is not None and now >= seg_start:
|
||||
current_index = i
|
||||
|
||||
if current_index is None or current_index + 1 >= len(segments):
|
||||
return None
|
||||
|
||||
return segments[current_index + 1].get("type")
|
||||
|
||||
def _find_current_segment(self) -> tuple[int, list[dict]] | tuple[None, None]:
|
||||
"""
|
||||
Find the currently active segment in today's day pattern.
|
||||
|
||||
Returns:
|
||||
Tuple of (current_index, segments) or (None, None) if unavailable.
|
||||
|
||||
"""
|
||||
if not self.coordinator.data:
|
||||
return None, None
|
||||
|
||||
day_patterns = self.coordinator.data.get("dayPatterns")
|
||||
if not day_patterns:
|
||||
return None, None
|
||||
|
||||
today_data = day_patterns.get("today")
|
||||
if not today_data:
|
||||
return None, None
|
||||
|
||||
segments: list[dict] | None = today_data.get("segments")
|
||||
if not segments:
|
||||
return None, None
|
||||
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
now = self.coordinator.time.now()
|
||||
current_index: int | None = None
|
||||
for i, segment in enumerate(segments):
|
||||
seg_start_str: str | None = segment.get("start")
|
||||
if not seg_start_str:
|
||||
continue
|
||||
seg_start = parse_datetime(seg_start_str)
|
||||
if seg_start is not None and now >= seg_start:
|
||||
current_index = i
|
||||
|
||||
if current_index is None:
|
||||
return None, None
|
||||
|
||||
return current_index, segments
|
||||
|
||||
def get_price_phase_timing_value(self, value_type: str) -> object:
|
||||
"""
|
||||
Get timing-related values for the current intra-day price phase segment.
|
||||
|
||||
Args:
|
||||
value_type: One of "end_time", "remaining_minutes", "duration", "progress".
|
||||
|
||||
Returns:
|
||||
- datetime for "end_time"
|
||||
- int/float for duration/remaining/progress (0 when no active segment)
|
||||
- None for timestamps when no segment is available
|
||||
|
||||
"""
|
||||
current_index, segments = self._find_current_segment()
|
||||
if current_index is None or segments is None:
|
||||
return 0 if value_type in ("remaining_minutes", "duration", "progress") else None
|
||||
|
||||
seg = segments[current_index]
|
||||
time = self.coordinator.time
|
||||
|
||||
if value_type == "end_time":
|
||||
end_str: str | None = seg.get("end")
|
||||
if not end_str:
|
||||
return None
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
return parse_datetime(end_str)
|
||||
|
||||
if value_type == "remaining_minutes":
|
||||
end_str = seg.get("end")
|
||||
if not end_str:
|
||||
return 0
|
||||
minutes = time.minutes_until_rounded(end_str)
|
||||
return max(0, minutes)
|
||||
|
||||
if value_type == "duration":
|
||||
start_str: str | None = seg.get("start")
|
||||
end_str = seg.get("end")
|
||||
if not start_str or not end_str:
|
||||
return None
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
start_dt = parse_datetime(start_str)
|
||||
end_dt = parse_datetime(end_str)
|
||||
if start_dt is None or end_dt is None:
|
||||
return None
|
||||
return max(0.0, (end_dt - start_dt).total_seconds() / 60)
|
||||
|
||||
if value_type == "progress":
|
||||
start_str = seg.get("start")
|
||||
end_str = seg.get("end")
|
||||
if not start_str or not end_str:
|
||||
return 0
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
start_dt = parse_datetime(start_str)
|
||||
end_dt = parse_datetime(end_str)
|
||||
if start_dt is None or end_dt is None:
|
||||
return 0
|
||||
now = time.now()
|
||||
total = (end_dt - start_dt).total_seconds()
|
||||
if total <= 0:
|
||||
return 0
|
||||
elapsed = (now - start_dt).total_seconds()
|
||||
return min(100.0, max(0.0, (elapsed / total) * 100))
|
||||
|
||||
return None
|
||||
|
||||
def get_next_phase_of_type_value(self, phase_type: str, value_type: str) -> object:
|
||||
"""
|
||||
Find the next intra-day phase of a given type and return its timing.
|
||||
|
||||
Searches remaining segments for today (after current) plus all of tomorrow.
|
||||
|
||||
Args:
|
||||
phase_type: "rising", "falling", or "flat".
|
||||
value_type: "start_time" (returns datetime) or "in_minutes" (returns int).
|
||||
|
||||
Returns:
|
||||
- datetime for "start_time"
|
||||
- int for "in_minutes"
|
||||
- None if no future segment of this type exists
|
||||
|
||||
"""
|
||||
if not self.coordinator.data:
|
||||
return None
|
||||
|
||||
day_patterns = self.coordinator.data.get("dayPatterns")
|
||||
if not day_patterns:
|
||||
return None
|
||||
|
||||
current_index, today_segments = self._find_current_segment()
|
||||
if today_segments is None:
|
||||
return None
|
||||
|
||||
# Remaining segments in today (after current index)
|
||||
start_idx = (current_index + 1) if current_index is not None else 0
|
||||
remaining_today: list[dict] = today_segments[start_idx:]
|
||||
|
||||
# All segments in tomorrow (if available)
|
||||
tomorrow_data = day_patterns.get("tomorrow")
|
||||
tomorrow_segments: list[dict] = tomorrow_data.get("segments", []) if tomorrow_data else []
|
||||
|
||||
# Search in order: remaining today → all tomorrow
|
||||
for segment in (*remaining_today, *tomorrow_segments):
|
||||
if segment.get("type") == phase_type:
|
||||
start_str: str | None = segment.get("start")
|
||||
if not start_str:
|
||||
continue
|
||||
if value_type == "start_time":
|
||||
from homeassistant.util.dt import parse_datetime # noqa: PLC0415
|
||||
|
||||
return parse_datetime(start_str)
|
||||
if value_type == "in_minutes":
|
||||
return self.coordinator.time.minutes_until_rounded(start_str)
|
||||
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -1045,6 +1045,137 @@ DAY_PATTERN_SENSORS = (
|
|||
state_class=None,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="current_price_phase",
|
||||
translation_key="current_price_phase",
|
||||
icon="mdi:chart-timeline-variant",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["rising", "falling", "flat"],
|
||||
state_class=None,
|
||||
entity_registry_enabled_default=True,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="next_price_phase",
|
||||
translation_key="next_price_phase",
|
||||
icon="mdi:chart-timeline-variant",
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
options=["rising", "falling", "flat"],
|
||||
state_class=None,
|
||||
entity_registry_enabled_default=True,
|
||||
),
|
||||
)
|
||||
|
||||
# 8b. PRICE PHASE TIMING SENSORS (current phase duration/progress + next-phase-by-type)
|
||||
# ----------------------------------------------------------------------------
|
||||
#
|
||||
# When current phase is active:
|
||||
# - end_time: Timestamp when current phase ends
|
||||
# - remaining_minutes: Minutes until current phase ends
|
||||
# - duration: Total length of current phase (disabled by default)
|
||||
# - progress: Percentage of current phase completed (disabled by default)
|
||||
#
|
||||
# Next occurrence of a specific phase type (after current segment, today or tomorrow):
|
||||
# - next_*_phase_start_time: Timestamp when next rising/falling/flat phase starts
|
||||
# - next_*_phase_in_minutes: Minutes until that phase starts
|
||||
#
|
||||
# All return None/Unknown when no segment data is available.
|
||||
|
||||
PRICE_PHASE_TIMING_SENSORS = (
|
||||
SensorEntityDescription(
|
||||
key="current_price_phase_end_time",
|
||||
translation_key="current_price_phase_end_time",
|
||||
icon="mdi:clock-end",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state_class=None, # Timestamps: no statistics
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="current_price_phase_remaining_minutes",
|
||||
translation_key="current_price_phase_remaining_minutes",
|
||||
icon="mdi:timer-sand",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
suggested_unit_of_measurement=UnitOfTime.HOURS,
|
||||
state_class=None, # Countdown timers excluded from statistics
|
||||
suggested_display_precision=2,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="current_price_phase_duration",
|
||||
translation_key="current_price_phase_duration",
|
||||
icon="mdi:timer",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
suggested_unit_of_measurement=UnitOfTime.HOURS,
|
||||
state_class=None, # Duration not needed in long-term statistics
|
||||
suggested_display_precision=2,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="current_price_phase_progress",
|
||||
translation_key="current_price_phase_progress",
|
||||
icon="mdi:percent",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=None, # Progress counter: no statistics
|
||||
suggested_display_precision=0,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
# Next occurrence of each phase type (across remaining today + tomorrow)
|
||||
SensorEntityDescription(
|
||||
key="next_rising_phase_start_time",
|
||||
translation_key="next_rising_phase_start_time",
|
||||
icon="mdi:trending-up",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state_class=None,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="next_falling_phase_start_time",
|
||||
translation_key="next_falling_phase_start_time",
|
||||
icon="mdi:trending-down",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state_class=None,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="next_flat_phase_start_time",
|
||||
translation_key="next_flat_phase_start_time",
|
||||
icon="mdi:trending-neutral",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
state_class=None,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="next_rising_phase_in_minutes",
|
||||
translation_key="next_rising_phase_in_minutes",
|
||||
icon="mdi:timer-outline",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
suggested_unit_of_measurement=UnitOfTime.HOURS,
|
||||
state_class=None,
|
||||
suggested_display_precision=2,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="next_falling_phase_in_minutes",
|
||||
translation_key="next_falling_phase_in_minutes",
|
||||
icon="mdi:timer-outline",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
suggested_unit_of_measurement=UnitOfTime.HOURS,
|
||||
state_class=None,
|
||||
suggested_display_precision=2,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key="next_flat_phase_in_minutes",
|
||||
translation_key="next_flat_phase_in_minutes",
|
||||
icon="mdi:timer-outline",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.MINUTES,
|
||||
suggested_unit_of_measurement=UnitOfTime.HOURS,
|
||||
state_class=None,
|
||||
suggested_display_precision=2,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
)
|
||||
|
||||
# 9. DIAGNOSTIC SENSORS (data availability and metadata)
|
||||
|
|
@ -1239,5 +1370,6 @@ ENTITY_DESCRIPTIONS = (
|
|||
*BEST_PRICE_TIMING_SENSORS,
|
||||
*PEAK_PRICE_TIMING_SENSORS,
|
||||
*DAY_PATTERN_SENSORS,
|
||||
*PRICE_PHASE_TIMING_SENSORS,
|
||||
*DIAGNOSTIC_SENSORS,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -242,6 +242,29 @@ def get_value_getter_mapping(
|
|||
"day_pattern_yesterday": lambda: metadata_calculator.get_day_pattern_value("yesterday"),
|
||||
"day_pattern_today": lambda: metadata_calculator.get_day_pattern_value("today"),
|
||||
"day_pattern_tomorrow": lambda: metadata_calculator.get_day_pattern_value("tomorrow"),
|
||||
"current_price_phase": lambda: metadata_calculator.get_current_price_phase_value(),
|
||||
"next_price_phase": lambda: metadata_calculator.get_next_price_phase_value(),
|
||||
# Price phase timing sensors (current phase duration/progress + next-phase-by-type)
|
||||
"current_price_phase_end_time": lambda: metadata_calculator.get_price_phase_timing_value("end_time"),
|
||||
"current_price_phase_remaining_minutes": lambda: metadata_calculator.get_price_phase_timing_value(
|
||||
"remaining_minutes"
|
||||
),
|
||||
"current_price_phase_duration": lambda: metadata_calculator.get_price_phase_timing_value("duration"),
|
||||
"current_price_phase_progress": lambda: metadata_calculator.get_price_phase_timing_value("progress"),
|
||||
"next_rising_phase_start_time": lambda: metadata_calculator.get_next_phase_of_type_value(
|
||||
"rising", "start_time"
|
||||
),
|
||||
"next_falling_phase_start_time": lambda: metadata_calculator.get_next_phase_of_type_value(
|
||||
"falling", "start_time"
|
||||
),
|
||||
"next_flat_phase_start_time": lambda: metadata_calculator.get_next_phase_of_type_value("flat", "start_time"),
|
||||
"next_rising_phase_in_minutes": lambda: metadata_calculator.get_next_phase_of_type_value(
|
||||
"rising", "in_minutes"
|
||||
),
|
||||
"next_falling_phase_in_minutes": lambda: metadata_calculator.get_next_phase_of_type_value(
|
||||
"falling", "in_minutes"
|
||||
),
|
||||
"next_flat_phase_in_minutes": lambda: metadata_calculator.get_next_phase_of_type_value("flat", "in_minutes"),
|
||||
# Volatility sensors (via VolatilityCalculator)
|
||||
"today_volatility": lambda: volatility_calculator.get_volatility_value(volatility_type="today"),
|
||||
"tomorrow_volatility": lambda: volatility_calculator.get_volatility_value(volatility_type="tomorrow"),
|
||||
|
|
|
|||
|
|
@ -1012,6 +1012,52 @@
|
|||
"mixed": "Gemischt"
|
||||
}
|
||||
},
|
||||
"current_price_phase": {
|
||||
"name": "Aktuelle Preisphase",
|
||||
"state": {
|
||||
"rising": "Steigend",
|
||||
"falling": "Fallend",
|
||||
"flat": "Flach"
|
||||
}
|
||||
},
|
||||
"next_price_phase": {
|
||||
"name": "Nächste Preisphase",
|
||||
"state": {
|
||||
"rising": "Steigend",
|
||||
"falling": "Fallend",
|
||||
"flat": "Flach"
|
||||
}
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"name": "Current Phase End Time"
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"name": "Current Phase Remaining"
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"name": "Current Phase Duration"
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"name": "Current Phase Progress"
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"name": "Next Rising Phase Start"
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"name": "Next Falling Phase Start"
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"name": "Next Flat Phase Start"
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"name": "Time to Next Rising Phase"
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"name": "Time to Next Falling Phase"
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"name": "Time to Next Flat Phase"
|
||||
},
|
||||
"chart_data_export": {
|
||||
"name": "Diagramm-Datenexport",
|
||||
"state": {
|
||||
|
|
@ -1044,10 +1090,10 @@
|
|||
"name": "Nächster Preisrang (heute+morgen)"
|
||||
},
|
||||
"previous_interval_price_rank_today": {
|
||||
"name": "Letzter Preisrang (heute)"
|
||||
"name": "Vorheriger Preisrang (heute)"
|
||||
},
|
||||
"previous_interval_price_rank_today_tomorrow": {
|
||||
"name": "Letzter Preisrang (heute+morgen)"
|
||||
"name": "Vorheriger Preisrang (heute+morgen)"
|
||||
},
|
||||
"current_hour_price_rank_today": {
|
||||
"name": "⌀ Stündlicher Preisrang Aktuell (heute)"
|
||||
|
|
@ -1069,6 +1115,15 @@
|
|||
"best_price_period": {
|
||||
"name": "Bestpreis-Zeitraum"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"name": "In Rising Price Phase"
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"name": "In Falling Price Phase"
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"name": "In Flat Price Phase"
|
||||
},
|
||||
"connection": {
|
||||
"name": "Tibber-API-Verbindung"
|
||||
},
|
||||
|
|
@ -1156,7 +1211,7 @@
|
|||
},
|
||||
"currency_display_mode_changed": {
|
||||
"title": "Währungsanzeigeeinheit für {home_name} geändert",
|
||||
"description": "Du hast den Währungsanzeigemodus für **{home_name}** geändert. Alle Preissensor-Werte und -Attribute verwenden jetzt die neue Einheit (z.B. 25,34 ct → 0,2534 € oder umgekehrt).\n\nDer Recorder von Home Assistant zeigt separat einen Dialog **„Die Einheit hat sich geändert“** für betroffene Sensoren — das kann einige Minuten dauern oder bis zum nächsten Statistik-Durchlauf (Warnungen im Log erscheinen früher). Wähle dann **Alle alten Statistikdaten löschen** für einen sauberen Neustart. Wähle nicht „Einheit aktualisieren ohne Konvertierung“: das benennt die alten Zahlen nur um, ohne die Werte anzupassen, und macht die historischen Daten inhaltlich falsch.\n\n**Manuell prüfen:**\n\n1. **Automationen & Templates:** Aktualisiere alle Automationen und Template-Sensoren mit numerischen Preis-Schwellwerten.\n2. **Dashboard-Karten:** Aktualisiere alle Karten mit fest codierten Schwellwerten oder Einheitenbezeichnungen.\n\nSchließe diesen Hinweis, nachdem du deine Automationen und Dashboards überprüft hast."
|
||||
"description": "Du hast den Währungsanzeigemodus für **{home_name}** geändert. Alle Preissensor-Werte und -Attribute verwenden jetzt die neue Einheit (z.B. 25,34 ct → 0,2534 € oder umgekehrt).\n\nDer Recorder von Home Assistant zeigt separat einen Dialog **„Die Einheit hat sich geändert“** für betroffene Sensoren — das kann einige Minuten dauern oder bis zum nächsten Statistik-Durchlauf (Warnungen im Log erscheinen früher). Wähle dann **Alle alten Statistikdaten löschen** für einen sauberen Neustart. Wähle nicht „Einheit aktualisieren ohne Konvertierung“: das benennt die alten Zahlen nur um, ohne die Werte anzupassen, und macht die historischen Daten inhaltlich falsch.\n\n**Manuell prüfen:**\n\n1. **Automationen & Templates:** Aktualisiere alle Automationen und Template-Sensoren mit numerischen Preis-Schwellwerten.\n2. **Dashboard-Karten:** Aktualisiere alle Karten mit fest codierten Schwellwerten oder Einheitenbezeichnungen.\n\nSchließe diese Meldung, sobald du deine Automationen, Dashboards und Statistiken überprüft hast."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
|
|
|
|||
|
|
@ -1012,6 +1012,52 @@
|
|||
"mixed": "Mixed"
|
||||
}
|
||||
},
|
||||
"current_price_phase": {
|
||||
"name": "Current Price Phase",
|
||||
"state": {
|
||||
"rising": "Rising",
|
||||
"falling": "Falling",
|
||||
"flat": "Flat"
|
||||
}
|
||||
},
|
||||
"next_price_phase": {
|
||||
"name": "Next Price Phase",
|
||||
"state": {
|
||||
"rising": "Rising",
|
||||
"falling": "Falling",
|
||||
"flat": "Flat"
|
||||
}
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"name": "Current Phase End Time"
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"name": "Current Phase Remaining"
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"name": "Current Phase Duration"
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"name": "Current Phase Progress"
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"name": "Next Rising Phase Start"
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"name": "Next Falling Phase Start"
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"name": "Next Flat Phase Start"
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"name": "Time to Next Rising Phase"
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"name": "Time to Next Falling Phase"
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"name": "Time to Next Flat Phase"
|
||||
},
|
||||
"chart_data_export": {
|
||||
"name": "Chart Data Export",
|
||||
"state": {
|
||||
|
|
@ -1044,10 +1090,10 @@
|
|||
"name": "Next Price Rank (Today+Tomorrow)"
|
||||
},
|
||||
"previous_interval_price_rank_today": {
|
||||
"name": "Last Price Rank (Today)"
|
||||
"name": "Previous Price Rank (Today)"
|
||||
},
|
||||
"previous_interval_price_rank_today_tomorrow": {
|
||||
"name": "Last Price Rank (Today+Tomorrow)"
|
||||
"name": "Previous Price Rank (Today+Tomorrow)"
|
||||
},
|
||||
"current_hour_price_rank_today": {
|
||||
"name": "⌀ Hourly Price Current Rank (Today)"
|
||||
|
|
@ -1069,6 +1115,15 @@
|
|||
"best_price_period": {
|
||||
"name": "Best Price Period"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"name": "In Rising Price Phase"
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"name": "In Falling Price Phase"
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"name": "In Flat Price Phase"
|
||||
},
|
||||
"connection": {
|
||||
"name": "Tibber API Connection"
|
||||
},
|
||||
|
|
@ -1156,7 +1211,7 @@
|
|||
},
|
||||
"currency_display_mode_changed": {
|
||||
"title": "Currency display unit changed for {home_name}",
|
||||
"description": "You changed the currency display mode for **{home_name}**. All price sensor values and attributes now use the new unit (e.g. 25.34 ct → 0.2534 € or vice versa).\n\nHome Assistant’s Recorder will separately show a **“The unit has changed”** dialog for affected sensors — this may take a few minutes or until the next statistics run (log warnings appear earlier). When it appears, choose **Delete all old statistic data** to start fresh. Do not choose “Update the unit without converting”: that re-labels the old numbers without adjusting their values, making the historic data factually incorrect.\n\n**Review manually:**\n\n1. **Automations & Templates:** Update all automations and template sensors that use numeric price thresholds.\n2. **Dashboard Cards:** Update any cards with hardcoded thresholds or unit labels.\n\nDismiss this notice after reviewing your automations and dashboards."
|
||||
"description": "You changed the currency display mode for **{home_name}**. All price sensor values and attributes now use the new unit (e.g. 25.34 ct → 0.2534 € or vice versa).\n\nHome Assistant’s Recorder will separately show a **“The unit has changed”** dialog for affected sensors — this may take a few minutes or until the next statistics run (log warnings appear earlier). When it appears, choose **Delete all old statistic data** to start fresh. Do not choose “Update the unit without converting”: that re-labels the old numbers without adjusting their values, making the historic data factually incorrect.\n\n**Review manually:**\n\n1. **Automations & Templates:** Update all automations and template sensors that use numeric price thresholds.\n2. **Dashboard Cards:** Update any cards with hardcoded thresholds or unit labels.\n\nDismiss this notice once you have reviewed your automations, dashboards, and statistics."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
|
|
|
|||
|
|
@ -1012,6 +1012,52 @@
|
|||
"mixed": "Blandet"
|
||||
}
|
||||
},
|
||||
"current_price_phase": {
|
||||
"name": "Gjeldende Prisfase",
|
||||
"state": {
|
||||
"rising": "Stigende",
|
||||
"falling": "Fallende",
|
||||
"flat": "Flat"
|
||||
}
|
||||
},
|
||||
"next_price_phase": {
|
||||
"name": "Neste Prisfase",
|
||||
"state": {
|
||||
"rising": "Stigende",
|
||||
"falling": "Fallende",
|
||||
"flat": "Flat"
|
||||
}
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"name": "Current Phase End Time"
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"name": "Current Phase Remaining"
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"name": "Current Phase Duration"
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"name": "Current Phase Progress"
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"name": "Next Rising Phase Start"
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"name": "Next Falling Phase Start"
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"name": "Next Flat Phase Start"
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"name": "Time to Next Rising Phase"
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"name": "Time to Next Falling Phase"
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"name": "Time to Next Flat Phase"
|
||||
},
|
||||
"chart_data_export": {
|
||||
"name": "Diagramdataeksport",
|
||||
"state": {
|
||||
|
|
@ -1069,6 +1115,15 @@
|
|||
"best_price_period": {
|
||||
"name": "Lavpris-periode"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"name": "In Rising Price Phase"
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"name": "In Falling Price Phase"
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"name": "In Flat Price Phase"
|
||||
},
|
||||
"connection": {
|
||||
"name": "Tibber API-tilkobling"
|
||||
},
|
||||
|
|
@ -1156,7 +1211,7 @@
|
|||
},
|
||||
"currency_display_mode_changed": {
|
||||
"title": "Valutavisningsenhet endret for {home_name}",
|
||||
"description": "Du endret valutavisningsmodusen for **{home_name}**. Alle prissensorverdier og -attributter bruker nå den nye enheten (f.eks. 25,34 øre → 0,2534 kr eller omvendt).\n\nHome Assistants Recorder viser separat en **„Enheten har endret seg“**-dialog for berørte sensorer — dette kan ta noen minutter eller til neste statistikkjøring (advarsler i loggen dukker opp tidligere). Når den vises, velg **Slett alle gamle statistikkdata** for en ren start. Ikke velg „Oppdater enheten uten konvertering“: det beholder de gamle tallene med ny enhet uten å justere verdiene, og gjør de historiske dataene faktisk feil.\n\n**Gjennomgå manuelt:**\n\n1. **Automatiseringer & maler:** Oppdater alle automatiseringer og malsensorer som bruker numeriske pristerskler.\n2. **Dashboard-kort:** Oppdater kort med hardkodede terskelverdier eller enhetsetiketter.\n\nAvvis dette varselet etter å ha gjennomgått automatiseringene og dashboardene dine."
|
||||
"description": "Du endret valutavisningsmodusen for **{home_name}**. Alle prissensorverdier og -attributter bruker nå den nye enheten (f.eks. 25,34 øre → 0,2534 kr eller omvendt).\n\nHome Assistants Recorder viser separat en **„Enheten har endret seg“**-dialog for berørte sensorer — dette kan ta noen minutter eller til neste statistikkjøring (advarsler i loggen dukker opp tidligere). Når den vises, velg **Slett alle gamle statistikkdata** for en ren start. Ikke velg „Oppdater enheten uten konvertering“: det beholder de gamle tallene med ny enhet uten å justere verdiene, og gjør de historiske dataene faktisk feil.\n\n**Gjennomgå manuelt:**\n\n1. **Automatiseringer & maler:** Oppdater alle automatiseringer og malsensorer som bruker numeriske pristerskler.\n2. **Dashboard-kort:** Oppdater kort med hardkodede terskelverdier eller enhetsetiketter.\n\nAvvis denne meldingen når du har gjennomgått automatiseringer, dashboards og statistikk."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
|
|
|
|||
|
|
@ -1012,6 +1012,52 @@
|
|||
"mixed": "Gemengd"
|
||||
}
|
||||
},
|
||||
"current_price_phase": {
|
||||
"name": "Huidige Prijsfase",
|
||||
"state": {
|
||||
"rising": "Stijgend",
|
||||
"falling": "Dalend",
|
||||
"flat": "Vlak"
|
||||
}
|
||||
},
|
||||
"next_price_phase": {
|
||||
"name": "Volgende Prijsfase",
|
||||
"state": {
|
||||
"rising": "Stijgend",
|
||||
"falling": "Dalend",
|
||||
"flat": "Vlak"
|
||||
}
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"name": "Current Phase End Time"
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"name": "Current Phase Remaining"
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"name": "Current Phase Duration"
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"name": "Current Phase Progress"
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"name": "Next Rising Phase Start"
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"name": "Next Falling Phase Start"
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"name": "Next Flat Phase Start"
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"name": "Time to Next Rising Phase"
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"name": "Time to Next Falling Phase"
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"name": "Time to Next Flat Phase"
|
||||
},
|
||||
"chart_data_export": {
|
||||
"name": "Grafiekdata Export",
|
||||
"state": {
|
||||
|
|
@ -1069,6 +1115,15 @@
|
|||
"best_price_period": {
|
||||
"name": "Beste Prijs Periode"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"name": "In Rising Price Phase"
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"name": "In Falling Price Phase"
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"name": "In Flat Price Phase"
|
||||
},
|
||||
"connection": {
|
||||
"name": "Tibber API Verbinding"
|
||||
},
|
||||
|
|
@ -1156,7 +1211,7 @@
|
|||
},
|
||||
"currency_display_mode_changed": {
|
||||
"title": "Valutaweergave-eenheid gewijzigd voor {home_name}",
|
||||
"description": "Je hebt de valutaweergavemodus voor **{home_name}** gewijzigd. Alle prijssensorwaarden en -attributen gebruiken nu de nieuwe eenheid (bijv. 25,34 ct → 0,2534 € of andersom).\n\nHome Assistant’s Recorder toont afzonderlijk een **„De eenheid is gewijzigd“**-dialoogvenster voor getroffen sensoren — dit kan enkele minuten duren of tot de volgende statistiekenrun (logwaarschuwingen verschijnen eerder). Kies dan **Alle oude statistiekgegevens verwijderen** voor een schone start. Kies niet „Eenheid bijwerken zonder conversie“: dat hernoemt de oude getallen zonder de waarden aan te passen, waardoor de historische gegevens inhoudelijk onjuist worden.\n\n**Handmatig controleren:**\n\n1. **Automatiseringen & templates:** Werk alle automatiseringen en template-sensoren bij die numerieke prijsdrempels gebruiken.\n2. **Dashboard-kaarten:** Werk kaarten bij met hardgecodeerde drempelwaarden of eenheidslabels.\n\nSluit deze melding nadat je je automatiseringen en dashboards hebt gecontroleerd."
|
||||
"description": "Je hebt de valutaweergavemodus voor **{home_name}** gewijzigd. Alle prijssensorwaarden en -attributen gebruiken nu de nieuwe eenheid (bijv. 25,34 ct → 0,2534 € of andersom).\n\nHome Assistant’s Recorder toont afzonderlijk een **„De eenheid is gewijzigd“**-dialoogvenster voor getroffen sensoren — dit kan enkele minuten duren of tot de volgende statistiekenrun (logwaarschuwingen verschijnen eerder). Kies dan **Alle oude statistiekgegevens verwijderen** voor een schone start. Kies niet „Eenheid bijwerken zonder conversie“: dat hernoemt de oude getallen zonder de waarden aan te passen, waardoor de historische gegevens inhoudelijk onjuist worden.\n\n**Handmatig controleren:**\n\n1. **Automatiseringen & templates:** Werk alle automatiseringen en template-sensoren bij die numerieke prijsdrempels gebruiken.\n2. **Dashboard-kaarten:** Werk kaarten bij met hardgecodeerde drempelwaarden of eenheidslabels.\n\nSluit deze melding nadat je je automatiseringen, dashboards en statistieken hebt gecontroleerd."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
|
|
|
|||
|
|
@ -1012,6 +1012,52 @@
|
|||
"mixed": "Blandad"
|
||||
}
|
||||
},
|
||||
"current_price_phase": {
|
||||
"name": "Aktuell Prisfas",
|
||||
"state": {
|
||||
"rising": "Stigande",
|
||||
"falling": "Fallande",
|
||||
"flat": "Flat"
|
||||
}
|
||||
},
|
||||
"next_price_phase": {
|
||||
"name": "Nästa Prisfas",
|
||||
"state": {
|
||||
"rising": "Stigande",
|
||||
"falling": "Fallande",
|
||||
"flat": "Flat"
|
||||
}
|
||||
},
|
||||
"current_price_phase_end_time": {
|
||||
"name": "Current Phase End Time"
|
||||
},
|
||||
"current_price_phase_remaining_minutes": {
|
||||
"name": "Current Phase Remaining"
|
||||
},
|
||||
"current_price_phase_duration": {
|
||||
"name": "Current Phase Duration"
|
||||
},
|
||||
"current_price_phase_progress": {
|
||||
"name": "Current Phase Progress"
|
||||
},
|
||||
"next_rising_phase_start_time": {
|
||||
"name": "Next Rising Phase Start"
|
||||
},
|
||||
"next_falling_phase_start_time": {
|
||||
"name": "Next Falling Phase Start"
|
||||
},
|
||||
"next_flat_phase_start_time": {
|
||||
"name": "Next Flat Phase Start"
|
||||
},
|
||||
"next_rising_phase_in_minutes": {
|
||||
"name": "Time to Next Rising Phase"
|
||||
},
|
||||
"next_falling_phase_in_minutes": {
|
||||
"name": "Time to Next Falling Phase"
|
||||
},
|
||||
"next_flat_phase_in_minutes": {
|
||||
"name": "Time to Next Flat Phase"
|
||||
},
|
||||
"chart_data_export": {
|
||||
"name": "Diagramdataexport",
|
||||
"state": {
|
||||
|
|
@ -1069,6 +1115,15 @@
|
|||
"best_price_period": {
|
||||
"name": "Bästa Prisperiod"
|
||||
},
|
||||
"in_rising_price_phase": {
|
||||
"name": "In Rising Price Phase"
|
||||
},
|
||||
"in_falling_price_phase": {
|
||||
"name": "In Falling Price Phase"
|
||||
},
|
||||
"in_flat_price_phase": {
|
||||
"name": "In Flat Price Phase"
|
||||
},
|
||||
"connection": {
|
||||
"name": "Tibber API-anslutning"
|
||||
},
|
||||
|
|
@ -1156,7 +1211,7 @@
|
|||
},
|
||||
"currency_display_mode_changed": {
|
||||
"title": "Valutavisningsenhet ändrad för {home_name}",
|
||||
"description": "Du ändrade valutavisningsläget för **{home_name}**. Alla prissensorvärden och -attribut använder nu den nya enheten (t.ex. 25,34 öre → 0,2534 kr eller tvärtom).\n\nHome Assistants Recorder visar separat en **„Enheten har ändrats“**-dialog för berörda sensorer — det kan ta några minuter eller till nästa statistikkörning (loggvarningar dyker upp tidigare). När den visas, välj **Ta bort alla gamla statistikdata** för en ren start. Välj inte „Uppdatera enheten utan konvertering“: det behåller de gamla talen med ny enhet utan att justera värdena, vilket gör historiska data faktiskt felaktiga.\n\n**Granska manuellt:**\n\n1. **Automatiseringar & mallar:** Uppdatera alla automatiseringar och mallsensorer som använder numeriska priströsklar.\n2. **Dashboard-kort:** Uppdatera kort med hårdkodade tröskelvärden eller enhetsetiketter.\n\nStäng detta meddelande efter att du har granskat dina automatiseringar och dashboards."
|
||||
"description": "Du ändrade valutavisningsläget för **{home_name}**. Alla prissensorvärden och -attribut använder nu den nya enheten (t.ex. 25,34 öre → 0,2534 kr eller tvärtom).\n\nHome Assistants Recorder visar separat en **„Enheten har ändrats“**-dialog för berörda sensorer — det kan ta några minuter eller till nästa statistikkörning (loggvarningar dyker upp tidigare). När den visas, välj **Ta bort alla gamla statistikdata** för en ren start. Välj inte „Uppdatera enheten utan konvertering“: det behåller de gamla talen med ny enhet utan att justera värdena, vilket gör historiska data faktiskt felaktiga.\n\n**Granska manuellt:**\n\n1. **Automatiseringar & mallar:** Uppdatera alla automatiseringar och mallsensorer som använder numeriska priströsklar.\n2. **Dashboard-kort:** Uppdatera kort med hårdkodade tröskelvärden eller enhetsetiketter.\n\nStäng det här meddelandet när du har granskat dina automatiseringar, instrumentpaneler och statistik."
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// This file is not used in compilation. It is here just for a nice editor experience.
|
||||
"extends": "@docusaurus/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
"ignoreDeprecations": "6.0"
|
||||
},
|
||||
"exclude": [".docusaurus", "build"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ This means **all intervals meet your criteria** (very cheap day!):
|
|||
- **Base currency**: €/kWh, kr/kWh (stored at 4 decimal places, default display: 2 decimals, e.g., 0.25)
|
||||
- **Subunit**: ct/kWh, øre/kWh (stored at 2 decimal places, default display: 1 decimal, e.g., 25.3)
|
||||
- Smart defaults: EUR → subunit, NOK/SEK/DKK → base currency
|
||||
- You can increase the displayed decimals per entity in the HA UI (see [Currency Display](configuration.md#step-2-currency-display))
|
||||
- You can increase the displayed decimals per entity in the HA UI (see [Currency Display](config-currency.md))
|
||||
|
||||
If you see unexpected units, check your configuration in the integration options.
|
||||
|
||||
|
|
|
|||
|
|
@ -226,8 +226,8 @@ explanations of each sensor's purpose, attributes, and automation examples.
|
|||
| <span id="ref-next_hour_price_rank_today_tomorrow" class="entity-anchor" data-refs="sensors-volatility#available-sensors"></span>`next_hour_price_rank_today_tomorrow` | ⌀ Hourly Price Next Rank (Today+Tomorrow) | ⌀ Stündlicher Preisrang Nächste (heute+morgen) | ⌀ Timesprisrang neste (i dag+i morgen) | ⌀ Uurlijkse prijsrang volgende (vandaag+morgen) | ⌀ Timprisrang nästa (idag+imorgon) | ❌ |
|
||||
| <span id="ref-next_interval_price_rank_today" class="entity-anchor" data-refs="sensors-volatility#available-sensors"></span>`next_interval_price_rank_today` | Next Price Rank (Today) | Nächster Preisrang (heute) | Neste prisrang (i dag) | Volgende prijsrang (vandaag) | Nästa prisrang (idag) | ❌ |
|
||||
| <span id="ref-next_interval_price_rank_today_tomorrow" class="entity-anchor" data-refs="sensors-volatility#available-sensors"></span>`next_interval_price_rank_today_tomorrow` | Next Price Rank (Today+Tomorrow) | Nächster Preisrang (heute+morgen) | Neste prisrang (i dag+i morgen) | Volgende prijsrang (vandaag+morgen) | Nästa prisrang (idag+imorgon) | ❌ |
|
||||
| <span id="ref-previous_interval_price_rank_today" class="entity-anchor" data-refs="sensors-volatility#available-sensors"></span>`previous_interval_price_rank_today` | Last Price Rank (Today) | Letzter Preisrang (heute) | Forrige prisrang (i dag) | Vorige prijsrang (vandaag) | Förra prisrang (idag) | ❌ |
|
||||
| <span id="ref-previous_interval_price_rank_today_tomorrow" class="entity-anchor" data-refs="sensors-volatility#available-sensors"></span>`previous_interval_price_rank_today_tomorrow` | Last Price Rank (Today+Tomorrow) | Letzter Preisrang (heute+morgen) | Forrige prisrang (i dag+i morgen) | Vorige prijsrang (vandaag+morgen) | Förra prisrang (idag+imorgon) | ❌ |
|
||||
| <span id="ref-previous_interval_price_rank_today" class="entity-anchor" data-refs="sensors-volatility#available-sensors"></span>`previous_interval_price_rank_today` | Previous Price Rank (Today) | Vorheriger Preisrang (heute) | Forrige prisrang (i dag) | Vorige prijsrang (vandaag) | Förra prisrang (idag) | ❌ |
|
||||
| <span id="ref-previous_interval_price_rank_today_tomorrow" class="entity-anchor" data-refs="sensors-volatility#available-sensors"></span>`previous_interval_price_rank_today_tomorrow` | Previous Price Rank (Today+Tomorrow) | Vorheriger Preisrang (heute+morgen) | Forrige prisrang (i dag+i morgen) | Vorige prijsrang (vandaag+morgen) | Förra prisrang (idag+imorgon) | ❌ |
|
||||
## Binary Sensors
|
||||
|
||||
### Binary Sensors
|
||||
|
|
@ -250,11 +250,11 @@ explanations of each sensor's purpose, attributes, and automation examples.
|
|||
|
||||
| Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default |
|
||||
|---|---|---|---|---|---|---|
|
||||
| <span id="ref-best_price_flex_override" class="entity-anchor" data-refs="configuration#best-price-period-settings"></span>`best_price_flex_override` | Best Price: Flexibility | Bestpreis: Flexibilität | Beste pris: Fleksibilitet | Beste prijs: Flexibiliteit | Bästa pris: Flexibilitet | ❌ |
|
||||
| <span id="ref-best_price_min_distance_override" class="entity-anchor" data-refs="configuration#best-price-period-settings"></span>`best_price_min_distance_override` | Best Price: Minimum Distance | Bestpreis: Mindestabstand | Beste pris: Minimumsavstand | Beste prijs: Minimale afstand | Bästa pris: Minimiavstånd | ❌ |
|
||||
| <span id="ref-best_price_min_period_length_override" class="entity-anchor" data-refs="configuration#best-price-period-settings"></span>`best_price_min_period_length_override` | Best Price: Minimum Period Length | Bestpreis: Mindestperiodenlänge | Beste pris: Minimum periodelengde | Beste prijs: Minimale periodelengte | Bästa pris: Minsta periodlängd | ❌ |
|
||||
| <span id="ref-best_price_min_periods_override" class="entity-anchor" data-refs="configuration#best-price-period-settings"></span>`best_price_min_periods_override` | Best Price: Minimum Periods | Bestpreis: Mindestperioden | Beste pris: Minimum perioder | Beste prijs: Minimum periodes | Bästa pris: Minsta antal perioder | ❌ |
|
||||
| <span id="ref-best_price_relaxation_attempts_override" class="entity-anchor" data-refs="configuration#best-price-period-settings"></span>`best_price_relaxation_attempts_override` | Best Price: Relaxation Attempts | Bestpreis: Lockerungsversuche | Beste pris: Lemping forsøk | Beste prijs: Versoepeling pogingen | Bästa pris: Lättnadsförsök | ❌ |
|
||||
| <span id="ref-best_price_flex_override" class="entity-anchor"></span>`best_price_flex_override` | Best Price: Flexibility | Bestpreis: Flexibilität | Beste pris: Fleksibilitet | Beste prijs: Flexibiliteit | Bästa pris: Flexibilitet | ❌ |
|
||||
| <span id="ref-best_price_min_distance_override" class="entity-anchor"></span>`best_price_min_distance_override` | Best Price: Minimum Distance | Bestpreis: Mindestabstand | Beste pris: Minimumsavstand | Beste prijs: Minimale afstand | Bästa pris: Minimiavstånd | ❌ |
|
||||
| <span id="ref-best_price_min_period_length_override" class="entity-anchor"></span>`best_price_min_period_length_override` | Best Price: Minimum Period Length | Bestpreis: Mindestperiodenlänge | Beste pris: Minimum periodelengde | Beste prijs: Minimale periodelengte | Bästa pris: Minsta periodlängd | ❌ |
|
||||
| <span id="ref-best_price_min_periods_override" class="entity-anchor"></span>`best_price_min_periods_override` | Best Price: Minimum Periods | Bestpreis: Mindestperioden | Beste pris: Minimum perioder | Beste prijs: Minimum periodes | Bästa pris: Minsta antal perioder | ❌ |
|
||||
| <span id="ref-best_price_relaxation_attempts_override" class="entity-anchor"></span>`best_price_relaxation_attempts_override` | Best Price: Relaxation Attempts | Bestpreis: Lockerungsversuche | Beste pris: Lemping forsøk | Beste prijs: Versoepeling pogingen | Bästa pris: Lättnadsförsök | ❌ |
|
||||
| <span id="ref-best_price_gap_count_override" class="entity-anchor"></span>`best_price_gap_count_override` | Best Price: Gap Tolerance | Bestpreis: Lückentoleranz | Beste pris: Gaptoleranse | Beste prijs: Gap tolerantie | Bästa pris: Glaptolerans | ❌ |
|
||||
|
||||
### Peak Price Configuration
|
||||
|
|
@ -262,11 +262,11 @@ explanations of each sensor's purpose, attributes, and automation examples.
|
|||
|
||||
| Entity ID suffix | 🇬🇧 English | 🇩🇪 Deutsch | 🇳🇴 Norsk | 🇳🇱 Nederlands | 🇸🇪 Svenska | Default |
|
||||
|---|---|---|---|---|---|---|
|
||||
| <span id="ref-peak_price_flex_override" class="entity-anchor" data-refs="configuration#peak-price-period-settings"></span>`peak_price_flex_override` | Peak Price: Flexibility | Spitzenpreis: Flexibilität | Topppris: Fleksibilitet | Piekprijs: Flexibiliteit | Topppris: Flexibilitet | ❌ |
|
||||
| <span id="ref-peak_price_min_distance_override" class="entity-anchor" data-refs="configuration#peak-price-period-settings"></span>`peak_price_min_distance_override` | Peak Price: Minimum Distance | Spitzenpreis: Mindestabstand | Topppris: Minimumsavstand | Piekprijs: Minimale afstand | Topppris: Minimiavstånd | ❌ |
|
||||
| <span id="ref-peak_price_min_period_length_override" class="entity-anchor" data-refs="configuration#peak-price-period-settings"></span>`peak_price_min_period_length_override` | Peak Price: Minimum Period Length | Spitzenpreis: Mindestperiodenlänge | Topppris: Minimum periodelengde | Piekprijs: Minimale periodelengte | Topppris: Minsta periodlängd | ❌ |
|
||||
| <span id="ref-peak_price_min_periods_override" class="entity-anchor" data-refs="configuration#peak-price-period-settings"></span>`peak_price_min_periods_override` | Peak Price: Minimum Periods | Spitzenpreis: Mindestperioden | Topppris: Minimum perioder | Piekprijs: Minimum periodes | Topppris: Minsta antal perioder | ❌ |
|
||||
| <span id="ref-peak_price_relaxation_attempts_override" class="entity-anchor" data-refs="configuration#peak-price-period-settings"></span>`peak_price_relaxation_attempts_override` | Peak Price: Relaxation Attempts | Spitzenpreis: Lockerungsversuche | Topppris: Lemping forsøk | Piekprijs: Versoepeling pogingen | Topppris: Lättnadsförsök | ❌ |
|
||||
| <span id="ref-peak_price_flex_override" class="entity-anchor"></span>`peak_price_flex_override` | Peak Price: Flexibility | Spitzenpreis: Flexibilität | Topppris: Fleksibilitet | Piekprijs: Flexibiliteit | Topppris: Flexibilitet | ❌ |
|
||||
| <span id="ref-peak_price_min_distance_override" class="entity-anchor"></span>`peak_price_min_distance_override` | Peak Price: Minimum Distance | Spitzenpreis: Mindestabstand | Topppris: Minimumsavstand | Piekprijs: Minimale afstand | Topppris: Minimiavstånd | ❌ |
|
||||
| <span id="ref-peak_price_min_period_length_override" class="entity-anchor"></span>`peak_price_min_period_length_override` | Peak Price: Minimum Period Length | Spitzenpreis: Mindestperiodenlänge | Topppris: Minimum periodelengde | Piekprijs: Minimale periodelengte | Topppris: Minsta periodlängd | ❌ |
|
||||
| <span id="ref-peak_price_min_periods_override" class="entity-anchor"></span>`peak_price_min_periods_override` | Peak Price: Minimum Periods | Spitzenpreis: Mindestperioden | Topppris: Minimum perioder | Piekprijs: Minimum periodes | Topppris: Minsta antal perioder | ❌ |
|
||||
| <span id="ref-peak_price_relaxation_attempts_override" class="entity-anchor"></span>`peak_price_relaxation_attempts_override` | Peak Price: Relaxation Attempts | Spitzenpreis: Lockerungsversuche | Topppris: Lemping forsøk | Piekprijs: Versoepeling pogingen | Topppris: Lättnadsförsök | ❌ |
|
||||
| <span id="ref-peak_price_gap_count_override" class="entity-anchor"></span>`peak_price_gap_count_override` | Peak Price: Gap Tolerance | Spitzenpreis: Lückentoleranz | Topppris: Gaptoleranse | Piekprijs: Gap tolerantie | Topppris: Glaptolerans | ❌ |
|
||||
## Switch Entities (Configuration Overrides)
|
||||
|
||||
|
|
|
|||
365
docs/user/docs/sensors-price-phases.md
Normal file
365
docs/user/docs/sensors-price-phases.md
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
# Price Phase Sensors
|
||||
|
||||
:::tip Entity ID tip
|
||||
`<home_name>` is a placeholder for your Tibber home display name in Home Assistant. Entity IDs are derived from the displayed name (localized), so the exact slug may differ. **Can't find a sensor?** Use the **[Entity Reference (All Languages)](sensor-reference.md)** to search by name in your language.
|
||||
:::
|
||||
|
||||
Price Phase sensors tell you **where you are in the intra-day price curve** — whether prices are currently rising, falling, or flat, how long that phase lasts, and when specific phase types will occur next. They complement the [Trend Sensors](sensors-trends.md) (which compare current price against future averages) by giving you the structural shape of the day.
|
||||
|
||||
---
|
||||
|
||||
## Price Phases vs. Price Trends — What's the Difference?
|
||||
|
||||
Both sensor families answer "What are prices doing?" — but from different angles:
|
||||
|
||||
| | Price Phase Sensors | Trend Sensors |
|
||||
|--|---------------------|--------------|
|
||||
| **What they answer** | "Are we in a rising or falling stretch right now?" | "Is now cheaper or more expensive than the next N hours?" |
|
||||
| **Based on** | Structural shape of the intra-day price curve | Comparison of current price vs. future window average |
|
||||
| **Best for** | Understanding position in the day's price arc | Deciding whether to act now or wait |
|
||||
| **Example** | "We're in a falling phase that ends at 15:30" | "Current price is 12% below the next 3h average" |
|
||||
|
||||
Think of price phases as the **skeleton of the day** and trend sensors as **real-time navigation**. Phases show you the map; trends show you which direction to drive.
|
||||
|
||||
---
|
||||
|
||||
## The Three Price Phases
|
||||
|
||||
Prices within a day are split into consecutive monotone segments — stretches where the direction is consistently one of:
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
direction LR
|
||||
|
||||
R: 📈 rising<br/><small>prices moving up</small>
|
||||
F: 📉 falling<br/><small>prices moving down</small>
|
||||
FL: ➡️ flat<br/><small>prices stable</small>
|
||||
|
||||
R --> F: local peak reached
|
||||
F --> R: local valley reached
|
||||
F --> FL: price levels out
|
||||
FL --> R: new upward trend starts
|
||||
R --> FL: momentum fades
|
||||
FL --> F: new downward trend starts
|
||||
```
|
||||
|
||||
**Exactly one phase is always active.** The three [binary sensors](#binary-phase-sensors) mirror this — exactly one of `in_rising_price_phase`, `in_falling_price_phase`, and `in_flat_price_phase` is ON at any time.
|
||||
|
||||
:::info Why only 3 phases?
|
||||
Each phase segment is **monotone by definition** — it is either purely rising, purely falling, or flat. A "double valley" (W-shape) is not a single phase; it is a sequence of: _falling → flat → rising → flat → falling_. The [Day Pattern sensor](#day-pattern-sensors) gives you the composite day shape; the phase sensors tell you your current position within it.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Day Pattern Sensors
|
||||
|
||||
These sensors classify the **overall shape** of the day's price curve:
|
||||
|
||||
| Sensor | Entity ID | Default |
|
||||
|--------|-----------|---------|
|
||||
| **Today's Price Pattern** (`day_pattern_today`) | `sensor.<home_name>_day_pattern_today` | ✅ enabled |
|
||||
| **Tomorrow's Price Pattern** (`day_pattern_tomorrow`) | `sensor.<home_name>_day_pattern_tomorrow` | ❌ disabled |
|
||||
| **Yesterday's Price Pattern** (`day_pattern_yesterday`) | `sensor.<home_name>_day_pattern_yesterday` | ❌ disabled |
|
||||
|
||||
**States:**
|
||||
|
||||
| State | Shape | Description |
|
||||
|-------|-------|-------------|
|
||||
| `valley` | ∪ | Cheap in the middle of the day (typical summer midday solar effect) |
|
||||
| `peak` | ∩ | Expensive in the middle — cheap mornings and evenings |
|
||||
| `double_valley` | W | Two cheap windows — classic with cheap morning + cheap midday |
|
||||
| `double_peak` | M | Two expensive peaks — common on workdays with morning and evening demand |
|
||||
| `flat` | ─ | Little variation throughout the day |
|
||||
| `rising` | / | Prices climb steadily through the day |
|
||||
| `falling` | \ | Prices drop steadily through the day |
|
||||
| `mixed` | ∿ | Irregular shape that doesn't fit a clean category |
|
||||
|
||||
**Key attributes:**
|
||||
|
||||
| Attribute | Description | Example |
|
||||
|-----------|-------------|---------|
|
||||
| `confidence` | Detection reliability (0–1) | `0.87` |
|
||||
| `coefficient_of_variation` | Relative price spread (volatility measure) | `0.24` |
|
||||
| `valley_start` / `valley_end` | Start/end of the primary cheap window | `11:00` / `15:00` |
|
||||
| `peak_start` / `peak_end` | Start/end of the primary expensive window | `07:00` / `09:00` |
|
||||
| `segment_count` | Number of intra-day phase segments | `4` |
|
||||
|
||||
**Example — Schedule based on tomorrow's pattern:**
|
||||
|
||||
<details>
|
||||
<summary>Show YAML: Pre-schedule heat pump based on tomorrow's valley</summary>
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
- alias: "Pre-schedule: Heat pump tomorrow's valley"
|
||||
trigger:
|
||||
- platform: time
|
||||
at: "20:00:00"
|
||||
condition:
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ states('sensor.<home_name>_day_pattern_tomorrow') in ['valley', 'double_valley'] }}
|
||||
- condition: template
|
||||
value_template: >
|
||||
{{ is_state('binary_sensor.<home_name>_tomorrow_data_available', 'on') }}
|
||||
action:
|
||||
- service: notify.mobile_app
|
||||
data:
|
||||
message: >
|
||||
Tomorrow is a {{ states('sensor.<home_name>_day_pattern_tomorrow') }} day.
|
||||
Cheap window: {{ state_attr('sensor.<home_name>_day_pattern_tomorrow', 'valley_start') }}
|
||||
to {{ state_attr('sensor.<home_name>_day_pattern_tomorrow', 'valley_end') }}.
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Current and Next Price Phase
|
||||
|
||||
### Current Price Phase
|
||||
|
||||
**Entity ID:** `sensor.<home_name>_current_price_phase`
|
||||
|
||||
Shows which price phase is active right now: `rising`, `falling`, or `flat`. Along with the [binary phase sensors](#binary-phase-sensors) and [timing sensors](#phase-timing-sensors), this gives you a complete picture of your position in the day's price arc.
|
||||
|
||||
**Key attributes:**
|
||||
|
||||
| Attribute | Description | Example |
|
||||
|-----------|-------------|---------|
|
||||
| `start` | When this phase started | `2025-11-08T12:00:00+01:00` |
|
||||
| `end` | When this phase ends | `2025-11-08T15:30:00+01:00` |
|
||||
| `price_min` | Lowest price in this phase | `11.2` |
|
||||
| `price_max` | Highest price in this phase | `18.7` |
|
||||
| `price_mean` | Average price in this phase | `14.5` |
|
||||
| `segment_index` | Position of this phase (0-based) | `1` |
|
||||
| `segment_count` | Total number of phases today | `4` |
|
||||
| `all_segments` | Full list of today's phases with times and prices | `[...]` |
|
||||
|
||||
**Tip:** `segment_index` and `segment_count` tell you your position in the day. If `segment_index=0` and the phase is `rising`, prices have been rising since midnight. If `segment_index=segment_count-1`, this is the final phase of the day.
|
||||
|
||||
### Next Price Phase
|
||||
|
||||
**Entity ID:** `sensor.<home_name>_next_price_phase`
|
||||
|
||||
Shows the phase that will follow after the current one ends. When the current phase is the last of the day, this sensor becomes unavailable. Same states and attributes as the Current Price Phase sensor.
|
||||
|
||||
**Use case:** Combine current and next to anticipate transitions:
|
||||
|
||||
| Current → Next | Interpretation |
|
||||
|----------------|----------------|
|
||||
| `rising` → `flat` | Peak is about to level off — consider acting now before it falls further |
|
||||
| `falling` → `rising` | We're at or near the daily minimum — best window to start flexible loads |
|
||||
| `falling` → `flat` | Approaching the stable bottom — prices won't drop much more |
|
||||
| `flat` → `rising` | Stable prices are ending — prices will start climbing |
|
||||
| `flat` → `falling` | Further drops are coming — wait if you can |
|
||||
|
||||
---
|
||||
|
||||
## Binary Phase Sensors
|
||||
|
||||
Three binary sensors let you trigger automations directly on the current phase type:
|
||||
|
||||
| Sensor | Entity ID | Default |
|
||||
|--------|-----------|---------|
|
||||
| <EntityRef id="in_rising_price_phase">In Rising Price Phase</EntityRef> | `binary_sensor.<home_name>_in_rising_price_phase` | ✅ enabled |
|
||||
| <EntityRef id="in_falling_price_phase">In Falling Price Phase</EntityRef> | `binary_sensor.<home_name>_in_falling_price_phase` | ✅ enabled |
|
||||
| <EntityRef id="in_flat_price_phase">In Flat Price Phase</EntityRef> | `binary_sensor.<home_name>_in_flat_price_phase` | ✅ enabled |
|
||||
|
||||
**Exactly one** of these is ON at any time — they are mutually exclusive mirrors of the `current_price_phase` sensor state.
|
||||
|
||||
**Example — Only run the dishwasher when prices are falling:**
|
||||
|
||||
<details>
|
||||
<summary>Show YAML: Start dishwasher during falling price phase</summary>
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
- alias: "Dishwasher: Start during falling phase"
|
||||
trigger:
|
||||
- platform: state
|
||||
entity_id: binary_sensor.<home_name>_in_falling_price_phase
|
||||
to: "on"
|
||||
condition:
|
||||
- condition: state
|
||||
entity_id: binary_sensor.dishwasher_finished
|
||||
state: "off"
|
||||
# Make sure there's enough time left before prices turn
|
||||
- condition: numeric_state
|
||||
entity_id: sensor.<home_name>_current_price_phase_remaining_minutes
|
||||
# dishwasher ECO cycle takes ~2 hours
|
||||
above: 0.33 # displayed in hours → ~20 minutes minimum
|
||||
action:
|
||||
- service: switch.turn_on
|
||||
target:
|
||||
entity_id: switch.dishwasher
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Phase Timing Sensors
|
||||
|
||||
These sensors mirror the Best/Peak Price [timing sensors](sensors-timing.md) — but for the current price phase instead of best/peak price periods.
|
||||
|
||||
### Current Phase Timing (4 sensors)
|
||||
|
||||
| Sensor | Entity ID | Default | Updates |
|
||||
|--------|-----------|---------|---------|
|
||||
| <EntityRef id="current_price_phase_end_time">Phase End Time</EntityRef> | `sensor.<home_name>_current_price_phase_end_time` | ✅ enabled | every 15 min |
|
||||
| <EntityRef id="current_price_phase_remaining_minutes">Phase Remaining Minutes</EntityRef> | `sensor.<home_name>_current_price_phase_remaining_minutes` | ✅ enabled | every minute |
|
||||
| <EntityRef id="current_price_phase_duration">Phase Duration</EntityRef> | `sensor.<home_name>_current_price_phase_duration` | ❌ disabled | every 15 min |
|
||||
| <EntityRef id="current_price_phase_progress">Phase Progress</EntityRef> | `sensor.<home_name>_current_price_phase_progress` | ❌ disabled | every minute |
|
||||
|
||||
**Duration vs. Remaining:** Duration is the *total* length of the current phase (doesn't change minute-by-minute). Remaining is the *countdown* (decreases every minute). Together they tell you both "how long is this phase?" and "how much is left?".
|
||||
|
||||
**Progress** (0–100%) = `(duration − remaining) / duration × 100`.
|
||||
|
||||
:::tip Know how far through a phase you are without maths
|
||||
Enable `current_price_phase_progress` and use it in a dashboard bar card to visualise how far through the current phase you are. Near 100% means the phase is ending soon.
|
||||
:::
|
||||
|
||||
### Next Phase by Type (6 sensors)
|
||||
|
||||
These sensors scan the remaining phases of today **and** tomorrow to find the next occurrence of each specific phase type:
|
||||
|
||||
| Sensor | Entity ID | Default | Updates |
|
||||
|--------|-----------|---------|---------|
|
||||
| <EntityRef id="next_rising_phase_start_time">Next Rising Phase Start Time</EntityRef> | `sensor.<home_name>_next_rising_phase_start_time` | ❌ disabled | every 15 min |
|
||||
| <EntityRef id="next_falling_phase_start_time">Next Falling Phase Start Time</EntityRef> | `sensor.<home_name>_next_falling_phase_start_time` | ❌ disabled | every 15 min |
|
||||
| <EntityRef id="next_flat_phase_start_time">Next Flat Phase Start Time</EntityRef> | `sensor.<home_name>_next_flat_phase_start_time` | ❌ disabled | every 15 min |
|
||||
| <EntityRef id="next_rising_phase_in_minutes">Next Rising Phase In Minutes</EntityRef> | `sensor.<home_name>_next_rising_phase_in_minutes` | ❌ disabled | every minute |
|
||||
| <EntityRef id="next_falling_phase_in_minutes">Next Falling Phase In Minutes</EntityRef> | `sensor.<home_name>_next_falling_phase_in_minutes` | ❌ disabled | every minute |
|
||||
| <EntityRef id="next_flat_phase_in_minutes">Next Flat Phase In Minutes</EntityRef> | `sensor.<home_name>_next_flat_phase_in_minutes` | ❌ disabled | every minute |
|
||||
|
||||
:::info Start time vs. in minutes — which to use?
|
||||
- Use the **start time sensors** (timestamp) when you need to show or compare a specific clock time: "The next price drop starts at 14:15."
|
||||
- Use the **in minutes sensors** (duration/countdown) for automations with numeric comparisons: "Trigger 30 minutes before the next price rise."
|
||||
|
||||
Both are updated from the same data — choose whichever fits your automation logic.
|
||||
:::
|
||||
|
||||
**These sensors are disabled by default** because they are only needed for advanced automation scenarios. Enable the ones you need in **Settings → Devices & Services → Tibber Prices → [your home] → Entities**.
|
||||
|
||||
---
|
||||
|
||||
## How to Use Phase Sensors Together
|
||||
|
||||
### Pattern: "Run only during falling phases with enough time left"
|
||||
|
||||
The most common use case: start a flexible appliance (dishwasher, washing machine) when prices are falling AND there's at least N minutes left before the phase ends.
|
||||
|
||||
<details>
|
||||
<summary>Show YAML: Start when falling, at least 90 min remaining</summary>
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
- alias: "Washing machine: Optimal start"
|
||||
trigger:
|
||||
- platform: state
|
||||
entity_id: binary_sensor.<home_name>_in_falling_price_phase
|
||||
to: "on"
|
||||
- platform: time_pattern
|
||||
minutes: "/15"
|
||||
condition:
|
||||
- condition: state
|
||||
entity_id: binary_sensor.<home_name>_in_falling_price_phase
|
||||
state: "on"
|
||||
- condition: numeric_state
|
||||
entity_id: sensor.<home_name>_current_price_phase_remaining_minutes
|
||||
# washing machine cycle ≈ 1.5 hours; sensor shows hours
|
||||
above: 1.5
|
||||
- condition: state
|
||||
entity_id: input_boolean.washing_machine_needs_run
|
||||
state: "on"
|
||||
action:
|
||||
- service: switch.turn_on
|
||||
target:
|
||||
entity_id: switch.washing_machine
|
||||
- service: input_boolean.turn_off
|
||||
target:
|
||||
entity_id: input_boolean.washing_machine_needs_run
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Pattern: "Alert before prices start rising"
|
||||
|
||||
Enable `next_rising_phase_in_minutes` and trigger a notification when the next price rise is imminent:
|
||||
|
||||
<details>
|
||||
<summary>Show YAML: Alert 30 min before prices start rising</summary>
|
||||
|
||||
```yaml
|
||||
automation:
|
||||
- alias: "Alert: Price rise imminent"
|
||||
trigger:
|
||||
- platform: numeric_state
|
||||
entity_id: sensor.<home_name>_next_rising_phase_in_minutes
|
||||
below: 0.5 # 30 minutes (displayed in hours)
|
||||
condition:
|
||||
- condition: state
|
||||
entity_id: binary_sensor.<home_name>_in_falling_price_phase
|
||||
state: "on"
|
||||
action:
|
||||
- service: notify.mobile_app
|
||||
data:
|
||||
title: "Prices rising in 30 minutes"
|
||||
message: >
|
||||
The current falling phase ends at
|
||||
{{ state_attr('sensor.<home_name>_current_price_phase', 'end') | as_timestamp | timestamp_custom('%H:%M') }}.
|
||||
Start flexible loads now.
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Pattern: "Complete this combination for best results"
|
||||
|
||||
For maximum precision, combine phase sensors with Best Price Period or trend sensors:
|
||||
|
||||
| You want to... | Combine with |
|
||||
|----------------|-------------|
|
||||
| Find the cheapest window today | `in_falling_price_phase` + [Best Price Period](sensors-timing.md) |
|
||||
| Confirm price direction | `current_price_phase` + [Price Outlook sensors](sensors-trends.md) |
|
||||
| Plan tomorrow | `day_pattern_tomorrow` + `next_falling_phase_start_time` |
|
||||
| Know if near the bottom | Current = `falling`, Next = `rising` |
|
||||
| Know if near the top | Current = `rising`, Next = `falling` |
|
||||
| Act before costs climb | `in_falling_price_phase` + `next_rising_phase_in_minutes` |
|
||||
|
||||
---
|
||||
|
||||
## Sensor Summary Table
|
||||
|
||||
### Sensors (enabled by default)
|
||||
|
||||
| Sensor | Entity ID | What It Shows |
|
||||
|--------|-----------|---------------|
|
||||
| Today's Price Pattern | `sensor.<home_name>_day_pattern_today` | Overall shape of today's price curve |
|
||||
| Current Price Phase | `sensor.<home_name>_current_price_phase` | Active phase: rising / falling / flat |
|
||||
| Next Price Phase | `sensor.<home_name>_next_price_phase` | Phase coming after the current one |
|
||||
| Phase End Time | `sensor.<home_name>_current_price_phase_end_time` | Timestamp when current phase ends |
|
||||
| Phase Remaining Minutes | `sensor.<home_name>_current_price_phase_remaining_minutes` | Countdown to end of current phase |
|
||||
|
||||
### Binary Sensors (all enabled by default)
|
||||
|
||||
| Sensor | Entity ID | ON when... |
|
||||
|--------|-----------|-----------|
|
||||
| In Rising Price Phase | `binary_sensor.<home_name>_in_rising_price_phase` | Prices are going up right now |
|
||||
| In Falling Price Phase | `binary_sensor.<home_name>_in_falling_price_phase` | Prices are going down right now |
|
||||
| In Flat Price Phase | `binary_sensor.<home_name>_in_flat_price_phase` | Prices are stable right now |
|
||||
|
||||
### Sensors (disabled by default)
|
||||
|
||||
| Sensor | Entity ID | What It Shows |
|
||||
|--------|-----------|---------------|
|
||||
| Yesterday's Price Pattern | `sensor.<home_name>_day_pattern_yesterday` | Yesterday's price shape (reference) |
|
||||
| Tomorrow's Price Pattern | `sensor.<home_name>_day_pattern_tomorrow` | Tomorrow's price shape (once available) |
|
||||
| Phase Duration | `sensor.<home_name>_current_price_phase_duration` | Total length of current phase |
|
||||
| Phase Progress | `sensor.<home_name>_current_price_phase_progress` | 0–100% through current phase |
|
||||
| Next Rising Phase Start Time | `sensor.<home_name>_next_rising_phase_start_time` | When the next rising phase begins |
|
||||
| Next Falling Phase Start Time | `sensor.<home_name>_next_falling_phase_start_time` | When the next falling phase begins |
|
||||
| Next Flat Phase Start Time | `sensor.<home_name>_next_flat_phase_start_time` | When the next flat phase begins |
|
||||
| Next Rising Phase In Minutes | `sensor.<home_name>_next_rising_phase_in_minutes` | Minutes until next rising phase |
|
||||
| Next Falling Phase In Minutes | `sensor.<home_name>_next_falling_phase_in_minutes` | Minutes until next falling phase |
|
||||
| Next Flat Phase In Minutes | `sensor.<home_name>_next_flat_phase_in_minutes` | Minutes until next flat phase |
|
||||
|
|
@ -120,7 +120,7 @@ Rating thresholds can be adjusted in the options flow:
|
|||
2. Navigate to **Price Rating Thresholds**
|
||||
3. Adjust LOW/HIGH thresholds, hysteresis, and gap tolerance
|
||||
|
||||
See [Configuration](configuration.md#step-3-price-rating-thresholds) for details.
|
||||
See [Price Rating Thresholds](config-price-rating.md) for details.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -149,4 +149,4 @@ Level sensors show the **Tibber API's own price classification** with a 5-level
|
|||
| <EntityRef id="today_price_level">Today's Price Level</EntityRef> | Calendar day (aggregated) |
|
||||
| <EntityRef id="tomorrow_price_level">Tomorrow's Price Level</EntityRef> | Calendar day (aggregated) |
|
||||
|
||||
**Gap tolerance** smoothing is applied to prevent isolated level flickers (e.g., a single NORMAL between two CHEAPs → corrected to CHEAP). Configure in [options flow](configuration.md#step-4-price-level-gap-tolerance).
|
||||
**Gap tolerance** smoothing is applied to prevent isolated level flickers (e.g., a single NORMAL between two CHEAPs → corrected to CHEAP). Configure in [Price Level Gap Tolerance](config-price-level.md).
|
||||
|
|
|
|||
|
|
@ -186,8 +186,8 @@ This means:
|
|||
|
||||
| Sensor | Reference Set | Enabled by Default |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | ------------------ |
|
||||
| <EntityRef id="previous_interval_price_rank_today">Last Price Rank (Today)</EntityRef> | Today's 96 quarter-hour intervals | ❌ No |
|
||||
| <EntityRef id="previous_interval_price_rank_today_tomorrow">Last Price Rank (Today+Tomorrow)</EntityRef> | Combined pool (up to 192 intervals) | ❌ No |
|
||||
| <EntityRef id="previous_interval_price_rank_today">Previous Price Rank (Today)</EntityRef> | Today's 96 quarter-hour intervals | ❌ No |
|
||||
| <EntityRef id="previous_interval_price_rank_today_tomorrow">Previous Price Rank (Today+Tomorrow)</EntityRef> | Combined pool (up to 192 intervals) | ❌ No |
|
||||
|
||||
**Rolling hourly average** (5-interval window, ~1 hour):
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ const sidebars: SidebarsConfig = {
|
|||
'sensors-ratings-levels',
|
||||
'sensors-volatility',
|
||||
'sensors-trends',
|
||||
'sensors-price-phases',
|
||||
'sensors-timing',
|
||||
'sensors-energy-tax',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// This file is not used in compilation. It is here just for a nice editor experience.
|
||||
"extends": "@docusaurus/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
"ignoreDeprecations": "6.0"
|
||||
},
|
||||
"exclude": [".docusaurus", "build"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -781,11 +781,20 @@ CURRENT_TITLE=""
|
|||
CURRENT_URL=""
|
||||
|
||||
if [ -z "$CI" ] && [ -z "$GITHUB_ACTIONS" ] && command -v gh >/dev/null 2>&1 && [ "$TO_TAG" != "HEAD" ]; then
|
||||
# Check if TO_TAG is a valid tag and release exists
|
||||
if git rev-parse "$TO_TAG" >/dev/null 2>&1 && gh release view "$TO_TAG" >/dev/null 2>&1; then
|
||||
AUTO_UPDATE_AVAILABLE=true
|
||||
CURRENT_TITLE=$(gh release view "$TO_TAG" --json name --jq '.name' 2>/dev/null || echo "$TO_TAG")
|
||||
CURRENT_URL=$(gh release view "$TO_TAG" --json url --jq '.url' 2>/dev/null || echo "")
|
||||
# Only check for release if the tag exists locally
|
||||
if git rev-parse "$TO_TAG" >/dev/null 2>&1; then
|
||||
# Check if gh is authenticated before querying the release
|
||||
if ! gh auth status >/dev/null 2>&1; then
|
||||
log_info "${YELLOW}Note: GitHub CLI is not authenticated — skipping auto-update check.${NC}"
|
||||
log_info "${YELLOW} Run 'gh auth login' to enable updating existing releases.${NC}"
|
||||
elif gh release view "$TO_TAG" >/dev/null 2>&1; then
|
||||
AUTO_UPDATE_AVAILABLE=true
|
||||
CURRENT_TITLE=$(gh release view "$TO_TAG" --json name --jq '.name' 2>/dev/null || echo "$TO_TAG")
|
||||
CURRENT_URL=$(gh release view "$TO_TAG" --json url --jq '.url' 2>/dev/null || echo "")
|
||||
else
|
||||
log_info "${YELLOW}Note: No GitHub release found for ${CYAN}${TO_TAG}${YELLOW}.${NC}"
|
||||
log_info "${YELLOW} Create one with: gh release create ${TO_TAG}${NC}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -450,9 +450,9 @@ def test_timer_group_sizes() -> None:
|
|||
This isn't a strict requirement, but significant changes in group sizes
|
||||
might indicate accidental additions/removals.
|
||||
"""
|
||||
# As of Nov 2025
|
||||
# As of Apr 2026 (added 5 price-phase countdown sensors)
|
||||
expected_time_sensitive_min = 40 # At least 40 sensors
|
||||
expected_minute_update = 7 # Exactly 7 timing sensors
|
||||
expected_minute_update = 12 # 7 original + 5 price-phase countdown sensors
|
||||
|
||||
assert len(TIME_SENSITIVE_ENTITY_KEYS) >= expected_time_sensitive_min, (
|
||||
f"Expected at least {expected_time_sensitive_min} TIME_SENSITIVE sensors, got {len(TIME_SENSITIVE_ENTITY_KEYS)}"
|
||||
|
|
|
|||
Loading…
Reference in a new issue