hass.tibber_prices/custom_components/tibber_prices/sensor/definitions.py
Julian Pawlowski f8fd0f4936 refactor(entities): remove redundant name= from all entity descriptions
All entity descriptions had hardcoded English name= strings that were
never used at runtime: HA always prefers translations via translation_key
(entity.<platform>.<key>.name in translations/en.json), making the name=
fields dead code.

Removed 106 lines across all four platforms:
- sensor/definitions.py: 85 name= lines
- number/definitions.py: 12 name= lines
- binary_sensor/definitions.py: 6 name= lines
- switch/definitions.py: 3 name= lines

No functional change. The unique_id (entry_id + key) and translation_key
remain stable, ensuring entity IDs and friendly names are unaffected.

Impact: Cleaner definitions, no drift between name= strings and
translations. Aligns with HA standard: translations are the single
source of truth for entity names.
2026-04-06 13:16:07 +00:00

959 lines
40 KiB
Python

"""
Sensor entity definitions for Tibber Prices.
This module contains all SensorEntityDescription definitions organized by
calculation method. Sensor definitions are declarative and independent of
the implementation logic.
Organization by calculation pattern:
1. Interval-based: Time offset from current interval
2. Rolling hour: 5-interval aggregation windows
3. Daily statistics: Calendar day min/max/avg
4. 24h windows: Trailing/leading statistics
5. Future forecast: N-hour windows from next interval
6. Volatility: Price variation analysis
7. Best/Peak Price timing: Period-based time tracking
8. Diagnostic: System metadata
"""
from __future__ import annotations
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
PERCENTAGE,
EntityCategory,
UnitOfArea,
UnitOfElectricCurrent,
UnitOfEnergy,
UnitOfTime,
)
# ============================================================================
# SENSOR DEFINITIONS - Grouped by calculation method
# ============================================================================
#
# Sensors are organized by HOW they calculate values, not WHAT they display.
# This groups sensors that share common logic and enables code reuse through
# unified handler methods.
#
# Calculation patterns:
# 1. Interval-based: Use time offset from current interval
# 2. Rolling hour: Aggregate 5-interval window (2 before + center + 2 after)
# 3. Daily statistics: Min/max/avg within calendar day boundaries
# 4. 24h windows: Trailing/leading from current interval
# 5. Future forecast: N-hour windows starting from next interval
# 6. Volatility: Statistical analysis of price variation
# 7. Best/Peak Price timing: Period-based time tracking (requires minute updates)
# 8. Diagnostic: System information and metadata
# ============================================================================
# ----------------------------------------------------------------------------
# 1. INTERVAL-BASED SENSORS (offset: -1, 0, +1 from current interval)
# ----------------------------------------------------------------------------
# All use find_price_data_for_interval() with time offset
# Shared handler: _get_interval_value(interval_offset, value_type)
INTERVAL_PRICE_SENSORS = (
SensorEntityDescription(
key="current_interval_price",
translation_key="current_interval_price",
icon="mdi:cash", # Dynamic: shows cash-multiple/plus/cash/minus/remove based on price level
device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL, # MONETARY requires TOTAL or None
suggested_display_precision=2,
),
SensorEntityDescription(
key="current_interval_price_base",
translation_key="current_interval_price_base",
icon="mdi:cash", # Dynamic: shows cash-multiple/plus/cash/minus/remove based on price level
device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL, # MONETARY requires TOTAL or None for Energy Dashboard
suggested_display_precision=4, # More precision for base currency (e.g., 0.2534 EUR/kWh)
),
SensorEntityDescription(
key="next_interval_price",
translation_key="next_interval_price",
icon="mdi:cash", # Dynamic: shows cash-multiple/plus/cash/minus/remove based on price level
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future value: historical chart not useful
suggested_display_precision=2,
),
SensorEntityDescription(
key="previous_interval_price",
translation_key="previous_interval_price",
icon="mdi:cash-refund", # Static: arrow back indicates "past"
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Past snapshot: historical chart not useful
entity_registry_enabled_default=False,
suggested_display_precision=2,
),
)
# NOTE: Enum options are defined inline (not imported from const.py) to avoid
# import timing issues with Home Assistant's entity platform initialization.
# Keep in sync with PRICE_LEVEL_OPTIONS in const.py!
INTERVAL_LEVEL_SENSORS = (
SensorEntityDescription(
key="current_interval_price_level",
translation_key="current_interval_price_level",
icon="mdi:gauge", # Dynamic: shows gauge/gauge-empty/gauge-low/gauge-full based on level value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"],
),
SensorEntityDescription(
key="next_interval_price_level",
translation_key="next_interval_price_level",
icon="mdi:gauge", # Dynamic: shows gauge/gauge-empty/gauge-low/gauge-full based on level value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"],
),
SensorEntityDescription(
key="previous_interval_price_level",
translation_key="previous_interval_price_level",
icon="mdi:gauge", # Dynamic: shows gauge/gauge-empty/gauge-low/gauge-full based on level value
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"],
),
)
# NOTE: Enum options are defined inline (not imported from const.py) to avoid
# import timing issues with Home Assistant's entity platform initialization.
# Keep in sync with PRICE_RATING_OPTIONS in const.py!
INTERVAL_RATING_SENSORS = (
SensorEntityDescription(
key="current_interval_price_rating",
translation_key="current_interval_price_rating",
icon="mdi:thumbs-up-down", # Dynamic: shows thumbs-up/thumbs-up-down/thumbs-down based on rating value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "normal", "high"],
entity_registry_enabled_default=False, # Level is more commonly used
),
SensorEntityDescription(
key="next_interval_price_rating",
translation_key="next_interval_price_rating",
icon="mdi:thumbs-up-down", # Dynamic: shows thumbs-up/thumbs-up-down/thumbs-down based on rating value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "normal", "high"],
entity_registry_enabled_default=False, # Level is more commonly used
),
SensorEntityDescription(
key="previous_interval_price_rating",
translation_key="previous_interval_price_rating",
icon="mdi:thumbs-up-down", # Dynamic: shows thumbs-up/thumbs-up-down/thumbs-down based on rating value
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "normal", "high"],
),
)
# ----------------------------------------------------------------------------
# 2. ROLLING HOUR SENSORS (5-interval window: 2 before + center + 2 after)
# ----------------------------------------------------------------------------
# All aggregate data from rolling 5-interval window around a specific hour
# Shared handler: _get_rolling_hour_value(hour_offset, value_type)
ROLLING_HOUR_PRICE_SENSORS = (
SensorEntityDescription(
key="current_hour_average_price",
translation_key="current_hour_average_price",
icon="mdi:cash", # Dynamic: shows cash-multiple/plus/cash/minus/remove based on aggregated price level
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Rolling derived value: historical chart not useful
suggested_display_precision=2,
),
SensorEntityDescription(
key="next_hour_average_price",
translation_key="next_hour_average_price",
icon="mdi:cash-fast", # Dynamic: shows cash-multiple/plus/cash/minus/remove based on aggregated price level
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future derived value: historical chart not useful
suggested_display_precision=2,
),
)
# NOTE: Enum options are defined inline (not imported from const.py) to avoid
# import timing issues with Home Assistant's entity platform initialization.
# Keep in sync with PRICE_LEVEL_OPTIONS in const.py!
ROLLING_HOUR_LEVEL_SENSORS = (
SensorEntityDescription(
key="current_hour_price_level",
translation_key="current_hour_price_level",
icon="mdi:gauge", # Dynamic: shows gauge/gauge-empty/gauge-low/gauge-full based on aggregated level value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"],
),
SensorEntityDescription(
key="next_hour_price_level",
translation_key="next_hour_price_level",
icon="mdi:gauge", # Dynamic: shows gauge/gauge-empty/gauge-low/gauge-full based on aggregated level value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"],
),
)
# NOTE: Enum options are defined inline (not imported from const.py) to avoid
# import timing issues with Home Assistant's entity platform initialization.
# Keep in sync with PRICE_RATING_OPTIONS in const.py!
ROLLING_HOUR_RATING_SENSORS = (
SensorEntityDescription(
key="current_hour_price_rating",
translation_key="current_hour_price_rating",
# Dynamic: shows thumbs-up/thumbs-up-down/thumbs-down based on aggregated rating value
icon="mdi:thumbs-up-down",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "normal", "high"],
entity_registry_enabled_default=False, # Level is more commonly used
),
SensorEntityDescription(
key="next_hour_price_rating",
translation_key="next_hour_price_rating",
# Dynamic: shows thumbs-up/thumbs-up-down/thumbs-down based on aggregated rating value
icon="mdi:thumbs-up-down",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "normal", "high"],
entity_registry_enabled_default=False, # Level is more commonly used
),
)
# ----------------------------------------------------------------------------
# 3. DAILY STATISTICS SENSORS (min/max/avg for calendar day boundaries)
# ----------------------------------------------------------------------------
# Calculate statistics for specific calendar days (today/tomorrow)
DAILY_STAT_SENSORS = (
SensorEntityDescription(
key="lowest_price_today",
translation_key="lowest_price_today",
icon="mdi:arrow-collapse-down",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Daily snapshot: stays constant most of the day
suggested_display_precision=2,
),
SensorEntityDescription(
key="highest_price_today",
translation_key="highest_price_today",
icon="mdi:arrow-collapse-up",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Daily snapshot: stays constant most of the day
suggested_display_precision=2,
),
SensorEntityDescription(
key="average_price_today",
translation_key="average_price_today",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL, # Keep TOTAL: useful to track daily avg over weeks/months
suggested_display_precision=2,
),
SensorEntityDescription(
key="lowest_price_tomorrow",
translation_key="lowest_price_tomorrow",
icon="mdi:arrow-collapse-down",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future data: historical chart not useful
suggested_display_precision=2,
),
SensorEntityDescription(
key="highest_price_tomorrow",
translation_key="highest_price_tomorrow",
icon="mdi:arrow-collapse-up",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future data: historical chart not useful
suggested_display_precision=2,
),
SensorEntityDescription(
key="average_price_tomorrow",
translation_key="average_price_tomorrow",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future data: historical chart not useful
suggested_display_precision=2,
),
)
# NOTE: Enum options are defined inline (not imported from const.py) to avoid
# import timing issues with Home Assistant's entity platform initialization.
# Keep in sync with PRICE_LEVEL_OPTIONS in const.py!
DAILY_LEVEL_SENSORS = (
SensorEntityDescription(
key="yesterday_price_level",
translation_key="yesterday_price_level",
icon="mdi:gauge", # Dynamic: shows gauge/gauge-empty/gauge-low/gauge-full based on daily level value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"],
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="today_price_level",
translation_key="today_price_level",
icon="mdi:gauge", # Dynamic: shows gauge/gauge-empty/gauge-low/gauge-full based on daily level value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"],
),
SensorEntityDescription(
key="tomorrow_price_level",
translation_key="tomorrow_price_level",
icon="mdi:gauge", # Dynamic: shows gauge/gauge-empty/gauge-low/gauge-full based on daily level value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["very_cheap", "cheap", "normal", "expensive", "very_expensive"],
),
)
# NOTE: Enum options are defined inline (not imported from const.py) to avoid
# import timing issues with Home Assistant's entity platform initialization.
# Keep in sync with PRICE_RATING_OPTIONS in const.py!
DAILY_RATING_SENSORS = (
SensorEntityDescription(
key="yesterday_price_rating",
translation_key="yesterday_price_rating",
# Dynamic: shows thumbs-up/thumbs-up-down/thumbs-down based on daily rating value
icon="mdi:thumbs-up-down",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "normal", "high"],
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="today_price_rating",
translation_key="today_price_rating",
# Dynamic: shows thumbs-up/thumbs-up-down/thumbs-down based on daily rating value
icon="mdi:thumbs-up-down",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "normal", "high"],
entity_registry_enabled_default=False, # Level is more commonly used
),
SensorEntityDescription(
key="tomorrow_price_rating",
translation_key="tomorrow_price_rating",
# Dynamic: shows thumbs-up/thumbs-up-down/thumbs-down based on daily rating value
icon="mdi:thumbs-up-down",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "normal", "high"],
entity_registry_enabled_default=False, # Level is more commonly used
),
)
# ----------------------------------------------------------------------------
# 4. 24H WINDOW SENSORS (trailing/leading from current interval)
# ----------------------------------------------------------------------------
# Calculate statistics over sliding 24-hour windows
WINDOW_24H_SENSORS = (
SensorEntityDescription(
key="trailing_price_average",
translation_key="trailing_price_average",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Rolling window: value shifts every 15min, history chart misleading
entity_registry_enabled_default=False,
suggested_display_precision=2,
),
SensorEntityDescription(
key="leading_price_average",
translation_key="leading_price_average",
icon="mdi:chart-line-variant",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Rolling window: value shifts every 15min, history chart misleading
entity_registry_enabled_default=False, # Advanced use case
suggested_display_precision=2,
),
SensorEntityDescription(
key="trailing_price_min",
translation_key="trailing_price_min",
icon="mdi:arrow-collapse-down",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Rolling window: value shifts every 15min, history chart misleading
entity_registry_enabled_default=False,
suggested_display_precision=2,
),
SensorEntityDescription(
key="trailing_price_max",
translation_key="trailing_price_max",
icon="mdi:arrow-collapse-up",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Rolling window: value shifts every 15min, history chart misleading
entity_registry_enabled_default=False,
suggested_display_precision=2,
),
SensorEntityDescription(
key="leading_price_min",
translation_key="leading_price_min",
icon="mdi:arrow-collapse-down",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Rolling window: value shifts every 15min, history chart misleading
entity_registry_enabled_default=False, # Advanced use case
suggested_display_precision=2,
),
SensorEntityDescription(
key="leading_price_max",
translation_key="leading_price_max",
icon="mdi:arrow-collapse-up",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Rolling window: value shifts every 15min, history chart misleading
entity_registry_enabled_default=False, # Advanced use case
suggested_display_precision=2,
),
)
# ----------------------------------------------------------------------------
# 5. FUTURE FORECAST SENSORS (N-hour windows starting from next interval)
# ----------------------------------------------------------------------------
# Calculate averages and trends for upcoming time windows
FUTURE_MEAN_SENSORS = (
# Default enabled: 1h-5h
SensorEntityDescription(
key="next_avg_1h",
translation_key="next_avg_1h",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future forecast: historical chart not useful
suggested_display_precision=2,
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="next_avg_2h",
translation_key="next_avg_2h",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future forecast: historical chart not useful
suggested_display_precision=2,
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="next_avg_3h",
translation_key="next_avg_3h",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future forecast: historical chart not useful
suggested_display_precision=2,
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="next_avg_4h",
translation_key="next_avg_4h",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future forecast: historical chart not useful
suggested_display_precision=2,
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="next_avg_5h",
translation_key="next_avg_5h",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future forecast: historical chart not useful
suggested_display_precision=2,
entity_registry_enabled_default=True,
),
# Disabled by default: 6h, 8h, 12h (advanced use cases)
SensorEntityDescription(
key="next_avg_6h",
translation_key="next_avg_6h",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future forecast: historical chart not useful
suggested_display_precision=2,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="next_avg_8h",
translation_key="next_avg_8h",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future forecast: historical chart not useful
suggested_display_precision=2,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="next_avg_12h",
translation_key="next_avg_12h",
icon="mdi:chart-line",
device_class=SensorDeviceClass.MONETARY,
state_class=None, # Future forecast: historical chart not useful
suggested_display_precision=2,
entity_registry_enabled_default=False,
),
)
FUTURE_TREND_SENSORS = (
# Current trend sensor (what is the trend right now, valid until next change?)
SensorEntityDescription(
key="current_price_trend",
translation_key="current_price_trend",
icon="mdi:trending-up", # Dynamic: trending-up/trending-down/trending-neutral based on current trend
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
# Next trend change sensor (when will trend change?)
SensorEntityDescription(
key="next_price_trend_change",
translation_key="next_price_trend_change",
icon="mdi:clock-alert", # Dynamic: trending-up/trending-down/trending-neutral based on direction
device_class=SensorDeviceClass.TIMESTAMP,
state_class=None, # Timestamp: no statistics
entity_registry_enabled_default=True,
),
# Price trend forecast sensors (will prices be higher/lower in X hours?)
# Default enabled: 1h-5h
SensorEntityDescription(
key="price_trend_1h",
translation_key="price_trend_1h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trend_2h",
translation_key="price_trend_2h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trend_3h",
translation_key="price_trend_3h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trend_4h",
translation_key="price_trend_4h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
SensorEntityDescription(
key="price_trend_5h",
translation_key="price_trend_5h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=True,
),
# Disabled by default: 6h, 8h, 12h
SensorEntityDescription(
key="price_trend_6h",
translation_key="price_trend_6h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="price_trend_8h",
translation_key="price_trend_8h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="price_trend_12h",
translation_key="price_trend_12h",
icon="mdi:trending-up", # Dynamic: shows trending-up/trending-down/trending-neutral based on trend value
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["strongly_falling", "falling", "stable", "rising", "strongly_rising"],
entity_registry_enabled_default=False,
),
)
# ----------------------------------------------------------------------------
# 6. VOLATILITY SENSORS (coefficient of variation analysis)
# ----------------------------------------------------------------------------
# NOTE: Enum options are defined inline (not imported from const.py) to avoid
# import timing issues with Home Assistant's entity platform initialization.
# Keep in sync with VOLATILITY_OPTIONS in const.py!
VOLATILITY_SENSORS = (
SensorEntityDescription(
key="today_volatility",
translation_key="today_volatility",
# Dynamic: shows chart-bell-curve/chart-gantt/finance based on volatility level
icon="mdi:chart-bell-curve-cumulative",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "moderate", "high", "very_high"],
),
SensorEntityDescription(
key="tomorrow_volatility",
translation_key="tomorrow_volatility",
# Dynamic: shows chart-bell-curve/chart-gantt/finance based on volatility level
icon="mdi:chart-bell-curve-cumulative",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "moderate", "high", "very_high"],
entity_registry_enabled_default=False, # Today's volatility is usually sufficient
),
SensorEntityDescription(
key="next_24h_volatility",
translation_key="next_24h_volatility",
# Dynamic: shows chart-bell-curve/chart-gantt/finance based on volatility level
icon="mdi:chart-bell-curve-cumulative",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "moderate", "high", "very_high"],
entity_registry_enabled_default=False, # Advanced use case
),
SensorEntityDescription(
key="today_tomorrow_volatility",
translation_key="today_tomorrow_volatility",
# Dynamic: shows chart-bell-curve/chart-gantt/finance based on volatility level
icon="mdi:chart-bell-curve-cumulative",
device_class=SensorDeviceClass.ENUM,
state_class=None, # Enum values: no statistics
options=["low", "moderate", "high", "very_high"],
entity_registry_enabled_default=False, # Advanced use case
),
)
# ----------------------------------------------------------------------------
# 7. BEST/PEAK PRICE TIMING SENSORS (period-based time tracking)
# ----------------------------------------------------------------------------
# These sensors track time relative to best_price/peak_price binary sensor periods.
# They require minute-by-minute updates via async_track_time_interval.
#
# When period is active (binary_sensor ON):
# - end_time: Timestamp when current period ends
# - remaining_minutes: Minutes until period ends
# - progress: Percentage of period completed (0-100%)
#
# When period is inactive (binary_sensor OFF):
# - next_start_time: Timestamp when next period starts
# - next_in_minutes: Minutes until next period starts
#
# All return None/Unknown when no period is active/scheduled.
BEST_PRICE_TIMING_SENSORS = (
SensorEntityDescription(
key="best_price_end_time",
translation_key="best_price_end_time",
icon="mdi:clock-end",
device_class=SensorDeviceClass.TIMESTAMP,
state_class=None, # Timestamps: no statistics
),
SensorEntityDescription(
key="best_price_period_duration",
translation_key="best_price_period_duration",
icon="mdi:timer",
device_class=SensorDeviceClass.DURATION,
native_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="best_price_remaining_minutes",
translation_key="best_price_remaining_minutes",
icon="mdi:timer-sand",
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.HOURS,
state_class=None, # Countdown timers excluded from statistics
suggested_display_precision=2,
),
SensorEntityDescription(
key="best_price_progress",
translation_key="best_price_progress",
icon="mdi:percent", # Dynamic: mdi:percent-0 to mdi:percent-100
native_unit_of_measurement=PERCENTAGE,
state_class=None, # Progress counter: no statistics
suggested_display_precision=0,
),
SensorEntityDescription(
key="best_price_next_start_time",
translation_key="best_price_next_start_time",
icon="mdi:clock-start",
device_class=SensorDeviceClass.TIMESTAMP,
state_class=None, # Timestamps: no statistics
),
SensorEntityDescription(
key="best_price_next_in_minutes",
translation_key="best_price_next_in_minutes",
icon="mdi:timer-outline",
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.HOURS,
state_class=None, # Next-start timers excluded from statistics
suggested_display_precision=2,
),
)
PEAK_PRICE_TIMING_SENSORS = (
SensorEntityDescription(
key="peak_price_end_time",
translation_key="peak_price_end_time",
icon="mdi:clock-end",
device_class=SensorDeviceClass.TIMESTAMP,
state_class=None, # Timestamps: no statistics
),
SensorEntityDescription(
key="peak_price_period_duration",
translation_key="peak_price_period_duration",
icon="mdi:timer",
device_class=SensorDeviceClass.DURATION,
native_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="peak_price_remaining_minutes",
translation_key="peak_price_remaining_minutes",
icon="mdi:timer-sand",
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.HOURS,
state_class=None, # Countdown timers excluded from statistics
suggested_display_precision=2,
),
SensorEntityDescription(
key="peak_price_progress",
translation_key="peak_price_progress",
icon="mdi:percent", # Dynamic: mdi:percent-0 to mdi:percent-100
native_unit_of_measurement=PERCENTAGE,
state_class=None, # Progress counter: no statistics
suggested_display_precision=0,
),
SensorEntityDescription(
key="peak_price_next_start_time",
translation_key="peak_price_next_start_time",
icon="mdi:clock-start",
device_class=SensorDeviceClass.TIMESTAMP,
state_class=None, # Timestamps: no statistics
),
SensorEntityDescription(
key="peak_price_next_in_minutes",
translation_key="peak_price_next_in_minutes",
icon="mdi:timer-outline",
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.HOURS,
state_class=None, # Next-start timers excluded from statistics
suggested_display_precision=2,
),
)
# 8. DIAGNOSTIC SENSORS (data availability and metadata)
# ----------------------------------------------------------------------------
DIAGNOSTIC_SENSORS = (
SensorEntityDescription(
key="data_lifecycle_status",
translation_key="data_lifecycle_status",
icon="mdi:database-sync",
device_class=SensorDeviceClass.ENUM,
options=["cached", "fresh", "refreshing", "searching_tomorrow", "turnover_pending", "error"],
state_class=None, # Status value: no statistics
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=True, # Critical for debugging
),
# Home metadata from user data
SensorEntityDescription(
key="home_type",
translation_key="home_type",
icon="mdi:home-variant",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=["apartment", "rowhouse", "house", "cottage"],
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="home_size",
translation_key="home_size",
icon="mdi:ruler-square",
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
suggested_display_precision=0,
),
SensorEntityDescription(
key="main_fuse_size",
translation_key="main_fuse_size",
icon="mdi:fuse",
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
device_class=SensorDeviceClass.CURRENT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
suggested_display_precision=0,
),
SensorEntityDescription(
key="number_of_residents",
translation_key="number_of_residents",
icon="mdi:account-group",
entity_category=EntityCategory.DIAGNOSTIC,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
suggested_display_precision=0,
),
SensorEntityDescription(
key="primary_heating_source",
translation_key="primary_heating_source",
icon="mdi:heating-coil",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=[
"air2air_heatpump",
"air2water_heatpump",
"boiler",
"central_heating",
"district_heating",
"district_heating",
"district",
"electric_boiler",
"electricity",
"floor",
"gas",
"ground_heatpump",
"ground",
"oil",
"other",
"waste",
],
entity_registry_enabled_default=False,
),
# Metering point data
SensorEntityDescription(
key="grid_company",
translation_key="grid_company",
icon="mdi:transmission-tower",
entity_category=EntityCategory.DIAGNOSTIC,
),
SensorEntityDescription(
key="grid_area_code",
translation_key="grid_area_code",
icon="mdi:map-marker",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="price_area_code",
translation_key="price_area_code",
icon="mdi:currency-eur",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="consumption_ean",
translation_key="consumption_ean",
icon="mdi:barcode",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="production_ean",
translation_key="production_ean",
icon="mdi:solar-power",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="energy_tax_type",
translation_key="energy_tax_type",
icon="mdi:cash",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="vat_type",
translation_key="vat_type",
icon="mdi:percent",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="estimated_annual_consumption",
translation_key="estimated_annual_consumption",
icon="mdi:lightning-bolt",
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
device_class=SensorDeviceClass.ENERGY,
state_class=SensorStateClass.TOTAL,
suggested_display_precision=0,
),
# Subscription data
SensorEntityDescription(
key="subscription_status",
translation_key="subscription_status",
icon="mdi:file-document-check",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=["running", "ended", "pending", "unknown"],
entity_registry_enabled_default=False,
),
SensorEntityDescription(
key="chart_data_export",
translation_key="chart_data_export",
icon="mdi:database-export",
device_class=SensorDeviceClass.ENUM,
options=["pending", "ready", "error"],
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False, # Opt-in
),
SensorEntityDescription(
key="chart_metadata",
translation_key="chart_metadata",
icon="mdi:chart-box-outline",
device_class=SensorDeviceClass.ENUM,
options=["pending", "ready", "error"],
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=True, # Critical for chart features
),
)
# ----------------------------------------------------------------------------
# COMBINED SENSOR DEFINITIONS
# ----------------------------------------------------------------------------
ENTITY_DESCRIPTIONS = (
*INTERVAL_PRICE_SENSORS,
*INTERVAL_LEVEL_SENSORS,
*INTERVAL_RATING_SENSORS,
*ROLLING_HOUR_PRICE_SENSORS,
*ROLLING_HOUR_LEVEL_SENSORS,
*ROLLING_HOUR_RATING_SENSORS,
*DAILY_STAT_SENSORS,
*DAILY_LEVEL_SENSORS,
*DAILY_RATING_SENSORS,
*WINDOW_24H_SENSORS,
*FUTURE_MEAN_SENSORS,
*FUTURE_TREND_SENSORS,
*VOLATILITY_SENSORS,
*BEST_PRICE_TIMING_SENSORS,
*PEAK_PRICE_TIMING_SENSORS,
*DIAGNOSTIC_SENSORS,
)