From 3e6bcf2345936bbf55afd1c1b85e043f72618f9d Mon Sep 17 00:00:00 2001 From: Julian Pawlowski Date: Fri, 26 Dec 2025 16:23:05 +0000 Subject: [PATCH] fix(sensor): synchronize current_interval_price_base with current_interval_price Fixed inconsistency between "Current Electricity Price" and "Current Electricity Price (Energy Dashboard)" sensors that were showing different prices and icons. Changes: - Add current_interval_price_base to TIME_SENSITIVE_ENTITY_KEYS so it updates at quarter-hour boundaries instead of only on API polls. This ensures both sensors update synchronously when a new 15-minute interval starts. - Use interval_data["startsAt"] as timestamp for current interval price sensors (both variants) instead of rounded calculation time. This prevents timestamp divergence when sensors update at slightly different times. - Include current_interval_price_base in icon color attribute mapping so both sensors display the same dynamic cash icon based on current price level. - Include current_interval_price_base in dynamic icon function so it gets the correct icon based on current price level (VERY_CHEAP/CHEAP/NORMAL/EXPENSIVE). Impact: Both sensors now show identical prices, timestamps, and icons as intended. They update synchronously at interval boundaries (00, 15, 30, 45 minutes) and correctly represent the Energy Dashboard compatible variant without lag or inconsistencies. --- .../tibber_prices/coordinator/constants.py | 1 + .../tibber_prices/entity_utils/icons.py | 2 +- .../sensor/attributes/interval.py | 128 ++++++++++-------- 3 files changed, 76 insertions(+), 55 deletions(-) diff --git a/custom_components/tibber_prices/coordinator/constants.py b/custom_components/tibber_prices/coordinator/constants.py index d633486..b3b2f12 100644 --- a/custom_components/tibber_prices/coordinator/constants.py +++ b/custom_components/tibber_prices/coordinator/constants.py @@ -31,6 +31,7 @@ TIME_SENSITIVE_ENTITY_KEYS = frozenset( { # Current/next/previous price sensors "current_interval_price", + "current_interval_price_base", "next_interval_price", "previous_interval_price", # Current/next/previous price levels diff --git a/custom_components/tibber_prices/entity_utils/icons.py b/custom_components/tibber_prices/entity_utils/icons.py index c9a6339..5da61ae 100644 --- a/custom_components/tibber_prices/entity_utils/icons.py +++ b/custom_components/tibber_prices/entity_utils/icons.py @@ -197,7 +197,7 @@ def get_price_sensor_icon( return None # Only current price sensors get dynamic icons - if key == "current_interval_price": + if key in ("current_interval_price", "current_interval_price_base"): level = get_price_level_for_icon(coordinator_data, interval_offset=0, time=time) if level: return PRICE_LEVEL_CASH_ICON_MAPPING.get(level.upper()) diff --git a/custom_components/tibber_prices/sensor/attributes/interval.py b/custom_components/tibber_prices/sensor/attributes/interval.py index d11b9d3..91d2876 100644 --- a/custom_components/tibber_prices/sensor/attributes/interval.py +++ b/custom_components/tibber_prices/sensor/attributes/interval.py @@ -23,6 +23,72 @@ from .helpers import add_alternate_average_attribute from .metadata import get_current_interval_data +def _get_interval_data_for_attributes( + key: str, + coordinator: TibberPricesDataUpdateCoordinator, + attributes: dict, + *, + time: TibberPricesTimeService, +) -> dict | None: + """ + Get interval data and set timestamp based on sensor type. + + Refactored to reduce branch complexity in main function. + + Args: + key: The sensor entity key + coordinator: The data update coordinator + attributes: Attributes dict to update with timestamp if needed + time: TibberPricesTimeService instance + + Returns: + Interval data if found, None otherwise + + """ + now = time.now() + + # Current/next price sensors - override timestamp with interval's startsAt + next_sensors = ["next_interval_price", "next_interval_price_level", "next_interval_price_rating"] + prev_sensors = ["previous_interval_price", "previous_interval_price_level", "previous_interval_price_rating"] + next_hour = ["next_hour_average_price", "next_hour_price_level", "next_hour_price_rating"] + curr_interval = ["current_interval_price", "current_interval_price_base"] + curr_hour = ["current_hour_average_price", "current_hour_price_level", "current_hour_price_rating"] + + if key in next_sensors: + target_time = time.get_next_interval_start() + interval_data = find_price_data_for_interval(coordinator.data, target_time, time=time) + if interval_data: + attributes["timestamp"] = interval_data["startsAt"] + return interval_data + + if key in prev_sensors: + target_time = time.get_interval_offset_time(-1) + interval_data = find_price_data_for_interval(coordinator.data, target_time, time=time) + if interval_data: + attributes["timestamp"] = interval_data["startsAt"] + return interval_data + + if key in next_hour: + target_time = now + timedelta(hours=1) + interval_data = find_price_data_for_interval(coordinator.data, target_time, time=time) + if interval_data: + attributes["timestamp"] = interval_data["startsAt"] + return interval_data + + # Current interval sensors (both variants) + if key in curr_interval: + interval_data = get_current_interval_data(coordinator, time=time) + if interval_data and "startsAt" in interval_data: + attributes["timestamp"] = interval_data["startsAt"] + return interval_data + + # Current hour sensors - keep default timestamp + if key in curr_hour: + return get_current_interval_data(coordinator, time=time) + + return None + + def add_current_interval_price_attributes( # noqa: PLR0913 attributes: dict, key: str, @@ -46,62 +112,16 @@ def add_current_interval_price_attributes( # noqa: PLR0913 config_entry: Config entry for user preferences """ - now = time.now() - - # Determine which interval to use based on sensor type - next_interval_sensors = [ - "next_interval_price", - "next_interval_price_level", - "next_interval_price_rating", - ] - previous_interval_sensors = [ - "previous_interval_price", - "previous_interval_price_level", - "previous_interval_price_rating", - ] - next_hour_sensors = [ - "next_hour_average_price", - "next_hour_price_level", - "next_hour_price_rating", - ] - current_hour_sensors = [ - "current_hour_average_price", - "current_hour_price_level", - "current_hour_price_rating", - ] - - # Set interval data based on sensor type - # For sensors showing data from OTHER intervals (next/previous), override timestamp with that interval's startsAt - # For current interval sensors, keep the default platform timestamp (calculation time) - interval_data = None - if key in next_interval_sensors: - target_time = time.get_next_interval_start() - interval_data = find_price_data_for_interval(coordinator.data, target_time, time=time) - # Override timestamp with the NEXT interval's startsAt (when that interval starts) - if interval_data: - attributes["timestamp"] = interval_data["startsAt"] - elif key in previous_interval_sensors: - target_time = time.get_interval_offset_time(-1) - interval_data = find_price_data_for_interval(coordinator.data, target_time, time=time) - # Override timestamp with the PREVIOUS interval's startsAt - if interval_data: - attributes["timestamp"] = interval_data["startsAt"] - elif key in next_hour_sensors: - target_time = now + timedelta(hours=1) - interval_data = find_price_data_for_interval(coordinator.data, target_time, time=time) - # Override timestamp with the center of the next rolling hour window - if interval_data: - attributes["timestamp"] = interval_data["startsAt"] - elif key in current_hour_sensors: - current_interval_data = get_current_interval_data(coordinator, time=time) - # Keep default timestamp (when calculation was made) for current hour sensors - else: - current_interval_data = get_current_interval_data(coordinator, time=time) - interval_data = current_interval_data # Use current_interval_data as interval_data for current_interval_price - # Keep default timestamp (current calculation time) for current interval sensors + # Get interval data and handle timestamp overrides + interval_data = _get_interval_data_for_attributes(key, coordinator, attributes, time=time) # Add icon_color for price sensors (based on their price level) - if key in ["current_interval_price", "next_interval_price", "previous_interval_price"]: + if key in [ + "current_interval_price", + "current_interval_price_base", + "next_interval_price", + "previous_interval_price", + ]: # For interval-based price sensors, get level from interval_data if interval_data and "level" in interval_data: level = interval_data["level"]