commit changes

This commit is contained in:
Julian Pawlowski 2025-04-21 19:59:03 +00:00
parent 23a46faecc
commit eeff264ae7
2 changed files with 112 additions and 30 deletions

View file

@ -10,8 +10,9 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription, BinarySensorEntityDescription,
) )
from homeassistant.const import EntityCategory
from .const import NAME from .const import NAME, DOMAIN
from .entity import TibberPricesEntity from .entity import TibberPricesEntity
if TYPE_CHECKING: if TYPE_CHECKING:
@ -25,22 +26,21 @@ ENTITY_DESCRIPTIONS = (
BinarySensorEntityDescription( BinarySensorEntityDescription(
key="peak_hour", key="peak_hour",
translation_key="peak_hour", translation_key="peak_hour",
name="Electricity Peak Hour", name="Peak Hour",
device_class=BinarySensorDeviceClass.POWER, icon="mdi:clock-alert",
icon="mdi:flash-alert",
), ),
BinarySensorEntityDescription( BinarySensorEntityDescription(
key="best_price_hour", key="best_price_hour",
translation_key="best_price_hour", translation_key="best_price_hour",
name="Best Electricity Price Hour", name="Best Price Hour",
device_class=BinarySensorDeviceClass.POWER, icon="mdi:clock-check",
icon="mdi:flash-outline",
), ),
BinarySensorEntityDescription( BinarySensorEntityDescription(
key="connection", key="connection",
translation_key="connection", translation_key="connection",
name="Tibber API Connection", name="Tibber API Connection",
device_class=BinarySensorDeviceClass.CONNECTIVITY, device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
), ),
) )

View file

@ -11,7 +11,7 @@ from homeassistant.components.sensor import (
SensorEntityDescription, SensorEntityDescription,
SensorStateClass, SensorStateClass,
) )
from homeassistant.const import CURRENCY_EURO, EntityCategory from homeassistant.const import EntityCategory
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from .const import DOMAIN from .const import DOMAIN
@ -24,25 +24,44 @@ if TYPE_CHECKING:
from .coordinator import TibberPricesDataUpdateCoordinator from .coordinator import TibberPricesDataUpdateCoordinator
from .data import TibberPricesConfigEntry from .data import TibberPricesConfigEntry
PRICE_UNIT = "ct/kWh"
CURRENCY_EURO = "EUR/kWh"
# Main price sensors that users will typically use in automations # Main price sensors that users will typically use in automations
PRICE_SENSORS = ( PRICE_SENSORS = (
SensorEntityDescription( SensorEntityDescription(
key="current_price", key="current_price_eur",
translation_key="current_price", translation_key="current_price",
name="Current Electricity Price", name="Current Electricity Price",
icon="mdi:currency-eur", icon="mdi:currency-eur",
device_class=SensorDeviceClass.MONETARY, device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement=CURRENCY_EURO, native_unit_of_measurement=CURRENCY_EURO,
entity_registry_enabled_default=False, # Hidden by default as it's mainly for the Energy Dashboard
), ),
SensorEntityDescription( SensorEntityDescription(
key="next_hour_price", key="current_price",
translation_key="current_price_cents",
name="Current Electricity Price",
icon="mdi:currency-eur",
device_class=SensorDeviceClass.MONETARY,
native_unit_of_measurement="ct/kWh",
),
SensorEntityDescription(
key="next_hour_price_eur",
translation_key="next_hour_price", translation_key="next_hour_price",
name="Next Hour Electricity Price", name="Next Hour Electricity Price",
icon="mdi:currency-eur-off", icon="mdi:currency-eur-off",
device_class=SensorDeviceClass.MONETARY, device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement=CURRENCY_EURO, native_unit_of_measurement=CURRENCY_EURO,
entity_registry_enabled_default=False, # Hidden by default as it's mainly for the Energy Dashboard
),
SensorEntityDescription(
key="next_hour_price",
translation_key="next_hour_price_cents",
name="Next Hour Electricity Price",
icon="mdi:currency-eur-off",
device_class=SensorDeviceClass.MONETARY,
native_unit_of_measurement="ct/kWh",
), ),
SensorEntityDescription( SensorEntityDescription(
key="price_level", key="price_level",
@ -55,34 +74,55 @@ PRICE_SENSORS = (
# Statistical price sensors # Statistical price sensors
STATISTICS_SENSORS = ( STATISTICS_SENSORS = (
SensorEntityDescription( SensorEntityDescription(
key="lowest_price_today", key="lowest_price_today_eur",
translation_key="lowest_price_today", translation_key="lowest_price_today",
name="Today's Lowest Price", name="Today's Lowest Price",
icon="mdi:currency-eur", icon="mdi:currency-eur",
device_class=SensorDeviceClass.MONETARY, device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement=CURRENCY_EURO, native_unit_of_measurement=CURRENCY_EURO,
entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, # Hidden by default as it's mainly for the Energy Dashboard
), ),
SensorEntityDescription( SensorEntityDescription(
key="highest_price_today", key="lowest_price_today",
translation_key="lowest_price_today_cents",
name="Today's Lowest Price",
icon="mdi:currency-eur",
device_class=SensorDeviceClass.MONETARY,
native_unit_of_measurement="ct/kWh",
),
SensorEntityDescription(
key="highest_price_today_eur",
translation_key="highest_price_today", translation_key="highest_price_today",
name="Today's Highest Price", name="Today's Highest Price",
icon="mdi:currency-eur", icon="mdi:currency-eur",
device_class=SensorDeviceClass.MONETARY, device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement=CURRENCY_EURO, native_unit_of_measurement=CURRENCY_EURO,
entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, # Hidden by default as it's mainly for the Energy Dashboard
), ),
SensorEntityDescription( SensorEntityDescription(
key="average_price_today", key="highest_price_today",
translation_key="highest_price_today_cents",
name="Today's Highest Price",
icon="mdi:currency-eur",
device_class=SensorDeviceClass.MONETARY,
native_unit_of_measurement="ct/kWh",
),
SensorEntityDescription(
key="average_price_today_eur",
translation_key="average_price_today", translation_key="average_price_today",
name="Today's Average Price", name="Today's Average Price",
icon="mdi:currency-eur", icon="mdi:currency-eur",
device_class=SensorDeviceClass.MONETARY, device_class=SensorDeviceClass.MONETARY,
state_class=SensorStateClass.TOTAL,
native_unit_of_measurement=CURRENCY_EURO, native_unit_of_measurement=CURRENCY_EURO,
entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, # Hidden by default as it's mainly for the Energy Dashboard
),
SensorEntityDescription(
key="average_price_today",
translation_key="average_price_today_cents",
name="Today's Average Price",
icon="mdi:currency-eur",
device_class=SensorDeviceClass.MONETARY,
native_unit_of_measurement="ct/kWh",
), ),
) )
@ -94,7 +134,6 @@ RATING_SENSORS = (
name="Hourly Price Rating", name="Hourly Price Rating",
icon="mdi:clock-outline", icon="mdi:clock-outline",
native_unit_of_measurement="%", native_unit_of_measurement="%",
entity_category=EntityCategory.DIAGNOSTIC,
), ),
SensorEntityDescription( SensorEntityDescription(
key="daily_rating", key="daily_rating",
@ -102,7 +141,6 @@ RATING_SENSORS = (
name="Daily Price Rating", name="Daily Price Rating",
icon="mdi:calendar-today", icon="mdi:calendar-today",
native_unit_of_measurement="%", native_unit_of_measurement="%",
entity_category=EntityCategory.DIAGNOSTIC,
), ),
SensorEntityDescription( SensorEntityDescription(
key="monthly_rating", key="monthly_rating",
@ -110,7 +148,6 @@ RATING_SENSORS = (
name="Monthly Price Rating", name="Monthly Price Rating",
icon="mdi:calendar-month", icon="mdi:calendar-month",
native_unit_of_measurement="%", native_unit_of_measurement="%",
entity_category=EntityCategory.DIAGNOSTIC,
), ),
) )
@ -191,10 +228,25 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity):
current_hour_data = price_data current_hour_data = price_data
break break
# Helper function to convert price based on unit
def get_price_value(price: float) -> float:
if self.entity_description.native_unit_of_measurement == "ct/kWh":
return price * 100
return price
if self.entity_description.key == "current_price": if self.entity_description.key == "current_price":
return get_price_value(float(current_hour_data["total"])) if current_hour_data else None
elif self.entity_description.key == "current_price_eur":
return float(current_hour_data["total"]) if current_hour_data else None return float(current_hour_data["total"]) if current_hour_data else None
elif self.entity_description.key == "next_hour_price": elif self.entity_description.key == "next_hour_price":
next_hour = (now.hour + 1) % 24
for price_data in price_info.get("today", []):
starts_at = datetime.fromisoformat(price_data["startsAt"])
if starts_at.hour == next_hour:
return get_price_value(float(price_data["total"]))
return None
elif self.entity_description.key == "next_hour_price_eur":
next_hour = (now.hour + 1) % 24 next_hour = (now.hour + 1) % 24
for price_data in price_info.get("today", []): for price_data in price_info.get("today", []):
starts_at = datetime.fromisoformat(price_data["startsAt"]) starts_at = datetime.fromisoformat(price_data["startsAt"])
@ -203,18 +255,34 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity):
return None return None
elif self.entity_description.key == "lowest_price_today": elif self.entity_description.key == "lowest_price_today":
today_prices = price_info.get("today", [])
if not today_prices:
return None
return get_price_value(min(float(price["total"]) for price in today_prices))
elif self.entity_description.key == "lowest_price_today_eur":
today_prices = price_info.get("today", []) today_prices = price_info.get("today", [])
if not today_prices: if not today_prices:
return None return None
return min(float(price["total"]) for price in today_prices) return min(float(price["total"]) for price in today_prices)
elif self.entity_description.key == "highest_price_today": elif self.entity_description.key == "highest_price_today":
today_prices = price_info.get("today", [])
if not today_prices:
return None
return get_price_value(max(float(price["total"]) for price in today_prices))
elif self.entity_description.key == "highest_price_today_eur":
today_prices = price_info.get("today", []) today_prices = price_info.get("today", [])
if not today_prices: if not today_prices:
return None return None
return max(float(price["total"]) for price in today_prices) return max(float(price["total"]) for price in today_prices)
elif self.entity_description.key == "average_price_today": elif self.entity_description.key == "average_price_today":
today_prices = price_info.get("today", [])
if not today_prices:
return None
avg = sum(float(price["total"]) for price in today_prices) / len(today_prices)
return get_price_value(avg)
elif self.entity_description.key == "average_price_today_eur":
today_prices = price_info.get("today", []) today_prices = price_info.get("today", [])
if not today_prices: if not today_prices:
return None return None
@ -305,12 +373,26 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity):
attributes = {} attributes = {}
if self.entity_description.key == "current_price": # Get current hour's data for timestamp
attributes["timestamp"] = price_info.get("current", {}).get("startsAt") now = datetime.now()
elif self.entity_description.key == "next_hour_price": current_hour_data = None
attributes["timestamp"] = price_info.get("current", {}).get("startsAt") for price_data in price_info.get("today", []):
starts_at = datetime.fromisoformat(price_data["startsAt"])
if starts_at.hour == now.hour:
current_hour_data = price_data
break
if self.entity_description.key in ["current_price", "current_price_eur"]:
attributes["timestamp"] = current_hour_data["startsAt"] if current_hour_data else None
elif self.entity_description.key in ["next_hour_price", "next_hour_price_eur"]:
next_hour = (now.hour + 1) % 24
for price_data in price_info.get("today", []):
starts_at = datetime.fromisoformat(price_data["startsAt"])
if starts_at.hour == next_hour:
attributes["timestamp"] = price_data["startsAt"]
break
elif self.entity_description.key == "price_level": elif self.entity_description.key == "price_level":
attributes["timestamp"] = price_info.get("current", {}).get("startsAt") attributes["timestamp"] = current_hour_data["startsAt"] if current_hour_data else None
elif self.entity_description.key == "lowest_price_today": elif self.entity_description.key == "lowest_price_today":
attributes["timestamp"] = price_info.get("today", [{}])[0].get("startsAt") attributes["timestamp"] = price_info.get("today", [{}])[0].get("startsAt")
elif self.entity_description.key == "highest_price_today": elif self.entity_description.key == "highest_price_today":
@ -318,7 +400,7 @@ class TibberPricesSensor(TibberPricesEntity, SensorEntity):
elif self.entity_description.key == "average_price_today": elif self.entity_description.key == "average_price_today":
attributes["timestamp"] = price_info.get("today", [{}])[0].get("startsAt") attributes["timestamp"] = price_info.get("today", [{}])[0].get("startsAt")
elif self.entity_description.key == "hourly_rating": elif self.entity_description.key == "hourly_rating":
attributes["timestamp"] = price_info.get("current", {}).get("startsAt") attributes["timestamp"] = current_hour_data["startsAt"] if current_hour_data else None
elif self.entity_description.key == "daily_rating": elif self.entity_description.key == "daily_rating":
attributes["timestamp"] = price_info.get("today", [{}])[0].get("startsAt") attributes["timestamp"] = price_info.get("today", [{}])[0].get("startsAt")
elif self.entity_description.key == "monthly_rating": elif self.entity_description.key == "monthly_rating":