mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
fix(binary_sensor): remove 6-hour lookahead limit for period icons
Simplified _has_future_periods() to check for ANY future periods instead of limiting to 6-hour window. This ensures icons show 'waiting' state whenever periods are scheduled, not just within artificial time limit. Also added pragmatic fallback in timing calculator _find_next_period(): when skip_current=True but only one future period exists, return it anyway instead of showing 'unknown'. This prevents timing sensors from showing unknown during active periods. Changes: - binary_sensor/definitions.py: Removed PERIOD_LOOKAHEAD_HOURS constant - binary_sensor/core.py: Simplified _has_future_periods() logic - sensor/calculators/timing.py: Added pragmatic fallback for single period Impact: Better user experience - icons always show future periods, timing sensors show values even during edge cases.
This commit is contained in:
parent
f373c01fbb
commit
2d0febdab3
3 changed files with 15 additions and 24 deletions
|
|
@ -21,7 +21,6 @@ from .attributes import (
|
||||||
get_price_intervals_attributes,
|
get_price_intervals_attributes,
|
||||||
get_tomorrow_data_available_attributes,
|
get_tomorrow_data_available_attributes,
|
||||||
)
|
)
|
||||||
from .definitions import PERIOD_LOOKAHEAD_HOURS
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
|
@ -46,11 +45,6 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity):
|
||||||
self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{entity_description.key}"
|
self._attr_unique_id = f"{coordinator.config_entry.entry_id}_{entity_description.key}"
|
||||||
self._state_getter: Callable | None = self._get_value_getter()
|
self._state_getter: Callable | None = self._get_value_getter()
|
||||||
self._time_sensitive_remove_listener: Callable | None = None
|
self._time_sensitive_remove_listener: Callable | None = None
|
||||||
self._lifecycle_remove_listener: Callable | None = None
|
|
||||||
|
|
||||||
# Register for lifecycle push updates if this sensor depends on connection state
|
|
||||||
if entity_description.key in ("connection", "tomorrow_data_available"):
|
|
||||||
self._lifecycle_remove_listener = coordinator.register_lifecycle_callback(self.async_write_ha_state)
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
|
|
@ -71,11 +65,6 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity):
|
||||||
self._time_sensitive_remove_listener()
|
self._time_sensitive_remove_listener()
|
||||||
self._time_sensitive_remove_listener = None
|
self._time_sensitive_remove_listener = None
|
||||||
|
|
||||||
# Remove lifecycle listener if registered
|
|
||||||
if self._lifecycle_remove_listener:
|
|
||||||
self._lifecycle_remove_listener()
|
|
||||||
self._lifecycle_remove_listener = None
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _handle_time_sensitive_update(self, time_service: TibberPricesTimeService) -> None:
|
def _handle_time_sensitive_update(self, time_service: TibberPricesTimeService) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -270,10 +259,10 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity):
|
||||||
|
|
||||||
def _has_future_periods(self) -> bool:
|
def _has_future_periods(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check if there are periods starting within the next 6 hours.
|
Check if there are any future periods.
|
||||||
|
|
||||||
Returns True if any period starts between now and PERIOD_LOOKAHEAD_HOURS from now.
|
Returns True if any period starts in the future (no time limit).
|
||||||
This provides a practical planning horizon instead of hard midnight cutoff.
|
This ensures icons show "waiting" state whenever periods are scheduled.
|
||||||
"""
|
"""
|
||||||
attrs = self._get_sensor_attributes()
|
attrs = self._get_sensor_attributes()
|
||||||
if not attrs or "periods" not in attrs:
|
if not attrs or "periods" not in attrs:
|
||||||
|
|
@ -282,15 +271,15 @@ class TibberPricesBinarySensor(TibberPricesEntity, BinarySensorEntity):
|
||||||
time = self.coordinator.time
|
time = self.coordinator.time
|
||||||
periods = attrs.get("periods", [])
|
periods = attrs.get("periods", [])
|
||||||
|
|
||||||
# Check if any period starts within the look-ahead window
|
# Check if any period starts in the future (no time limit)
|
||||||
for period in periods:
|
for period in periods:
|
||||||
start_str = period.get("start")
|
start_str = period.get("start")
|
||||||
if start_str:
|
if start_str:
|
||||||
# Already datetime object (periods come from coordinator.data)
|
# Already datetime object (periods come from coordinator.data)
|
||||||
start_time = start_str if not isinstance(start_str, str) else time.parse_datetime(start_str)
|
start_time = start_str if not isinstance(start_str, str) else time.parse_datetime(start_str)
|
||||||
|
|
||||||
# Period starts in the future but within our horizon
|
# Period starts in the future
|
||||||
if start_time and time.is_time_within_horizon(start_time, hours=PERIOD_LOOKAHEAD_HOURS):
|
if start_time and time.is_in_future(start_time):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@ from homeassistant.components.binary_sensor import (
|
||||||
)
|
)
|
||||||
from homeassistant.const import EntityCategory
|
from homeassistant.const import EntityCategory
|
||||||
|
|
||||||
# Look-ahead window for future period detection (hours)
|
# Period lookahead removed - icons show "waiting" state if ANY future periods exist
|
||||||
# Icons will show "waiting" state if a period starts within this window
|
# No artificial time limit - show all periods until midnight
|
||||||
PERIOD_LOOKAHEAD_HOURS = 6
|
|
||||||
|
|
||||||
ENTITY_DESCRIPTIONS = (
|
ENTITY_DESCRIPTIONS = (
|
||||||
BinarySensorEntityDescription(
|
BinarySensorEntityDescription(
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,8 @@ class TibberPricesTimingCalculator(TibberPricesBaseCalculator):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
periods: List of period dictionaries
|
periods: List of period dictionaries
|
||||||
skip_current: If True, skip the first future period (to get next-next)
|
skip_current: If True, try to skip the first future period (to get next-next)
|
||||||
|
If only one future period exists, return it anyway (pragmatic fallback)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Next period dict or None if no future periods
|
Next period dict or None if no future periods
|
||||||
|
|
@ -173,11 +174,13 @@ class TibberPricesTimingCalculator(TibberPricesBaseCalculator):
|
||||||
# Sort by start time to ensure correct order
|
# Sort by start time to ensure correct order
|
||||||
future_periods.sort(key=lambda p: p["start"])
|
future_periods.sort(key=lambda p: p["start"])
|
||||||
|
|
||||||
# Return second period if skip_current=True (next-next), otherwise first (next)
|
# If skip_current requested and we have multiple periods, return second
|
||||||
|
# If only one period left, return it anyway (pragmatic: better than showing unknown)
|
||||||
if skip_current and len(future_periods) > 1:
|
if skip_current and len(future_periods) > 1:
|
||||||
return future_periods[1]
|
return future_periods[1]
|
||||||
if not skip_current and future_periods:
|
|
||||||
return future_periods[0]
|
# Default: return first future period
|
||||||
|
return future_periods[0] if future_periods else None
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue