1 """Sensor platform for Nord Pool integration."""
3 from __future__
import annotations
5 from collections.abc
import Callable
6 from dataclasses
import dataclass
7 from datetime
import datetime, timedelta
9 from pynordpool
import DeliveryPeriodData
15 SensorEntityDescription,
22 from .
import NordPoolConfigEntry
23 from .const
import LOGGER
24 from .coordinator
import NordPoolDataUpdateCoordinator
25 from .entity
import NordpoolBaseEntity
31 data: DeliveryPeriodData,
32 ) -> dict[str, tuple[float |
None, float, float |
None]]:
33 """Return previous, current and next prices.
35 Output: {"SE3": (10.0, 10.5, 12.1)}
37 last_price_entries: dict[str, float] = {}
38 current_price_entries: dict[str, float] = {}
39 next_price_entries: dict[str, float] = {}
40 current_time = dt_util.utcnow()
41 previous_time = current_time -
timedelta(hours=1)
42 next_time = current_time +
timedelta(hours=1)
43 price_data = data.entries
44 LOGGER.debug(
"Price data: %s", price_data)
45 for entry
in price_data:
46 if entry.start <= current_time <= entry.end:
47 current_price_entries = entry.entry
48 if entry.start <= previous_time <= entry.end:
49 last_price_entries = entry.entry
50 if entry.start <= next_time <= entry.end:
51 next_price_entries = entry.entry
53 "Last price %s, current price %s, next price %s",
55 current_price_entries,
60 for area, price
in current_price_entries.items():
62 last_price_entries.get(area),
64 next_price_entries.get(area),
66 LOGGER.debug(
"Prices: %s", result)
71 data: DeliveryPeriodData,
72 ) -> dict[str, dict[str, tuple[datetime, datetime, float, float, float]]]:
73 """Return average, min and max for block prices.
75 Output: {"SE3": {"Off-peak 1": (_datetime_, _datetime_, 9.3, 10.5, 12.1)}}
77 result: dict[str, dict[str, tuple[datetime, datetime, float, float, float]]] = {}
78 block_prices = data.block_prices
79 for entry
in block_prices:
80 for _area
in entry.average:
81 if _area
not in result:
83 result[_area][entry.name] = (
86 entry.average[_area][
"average"],
87 entry.average[_area][
"min"],
88 entry.average[_area][
"max"],
91 LOGGER.debug(
"Block prices: %s", result)
95 @dataclass(frozen=True, kw_only=True)
97 """Describes Nord Pool default sensor entity."""
99 value_fn: Callable[[DeliveryPeriodData], str | float | datetime |
None]
102 @dataclass(frozen=True, kw_only=True)
104 """Describes Nord Pool prices sensor entity."""
106 value_fn: Callable[[tuple[float |
None, float, float |
None]], float |
None]
109 @dataclass(frozen=True, kw_only=True)
111 """Describes Nord Pool block prices sensor entity."""
114 [tuple[datetime, datetime, float, float, float]], float | datetime |
None
118 DEFAULT_SENSOR_TYPES: tuple[NordpoolDefaultSensorEntityDescription, ...] = (
121 translation_key=
"updated_at",
122 device_class=SensorDeviceClass.TIMESTAMP,
123 value_fn=
lambda data: data.updated_at,
124 entity_category=EntityCategory.DIAGNOSTIC,
128 translation_key=
"currency",
129 value_fn=
lambda data: data.currency,
130 entity_category=EntityCategory.DIAGNOSTIC,
134 translation_key=
"exchange_rate",
135 value_fn=
lambda data: data.exchange_rate,
136 state_class=SensorStateClass.MEASUREMENT,
137 entity_registry_enabled_default=
False,
138 entity_category=EntityCategory.DIAGNOSTIC,
141 PRICES_SENSOR_TYPES: tuple[NordpoolPricesSensorEntityDescription, ...] = (
144 translation_key=
"current_price",
145 value_fn=
lambda data: data[1] / 1000,
146 state_class=SensorStateClass.MEASUREMENT,
147 suggested_display_precision=2,
151 translation_key=
"last_price",
152 value_fn=
lambda data: data[0] / 1000
if data[0]
else None,
153 suggested_display_precision=2,
157 translation_key=
"next_price",
158 value_fn=
lambda data: data[2] / 1000
if data[2]
else None,
159 suggested_display_precision=2,
162 BLOCK_PRICES_SENSOR_TYPES: tuple[NordpoolBlockPricesSensorEntityDescription, ...] = (
165 translation_key=
"block_average",
166 value_fn=
lambda data: data[2] / 1000,
167 state_class=SensorStateClass.MEASUREMENT,
168 suggested_display_precision=2,
169 entity_registry_enabled_default=
False,
173 translation_key=
"block_min",
174 value_fn=
lambda data: data[3] / 1000,
175 state_class=SensorStateClass.MEASUREMENT,
176 suggested_display_precision=2,
177 entity_registry_enabled_default=
False,
181 translation_key=
"block_max",
182 value_fn=
lambda data: data[4] / 1000,
183 state_class=SensorStateClass.MEASUREMENT,
184 suggested_display_precision=2,
185 entity_registry_enabled_default=
False,
188 key=
"block_start_time",
189 translation_key=
"block_start_time",
190 value_fn=
lambda data: data[0],
191 device_class=SensorDeviceClass.TIMESTAMP,
192 entity_registry_enabled_default=
False,
195 key=
"block_end_time",
196 translation_key=
"block_end_time",
197 value_fn=
lambda data: data[1],
198 device_class=SensorDeviceClass.TIMESTAMP,
199 entity_registry_enabled_default=
False,
202 DAILY_AVERAGE_PRICES_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
205 translation_key=
"daily_average",
206 state_class=SensorStateClass.MEASUREMENT,
207 suggested_display_precision=2,
208 entity_registry_enabled_default=
False,
215 entry: NordPoolConfigEntry,
216 async_add_entities: AddEntitiesCallback,
218 """Set up Nord Pool sensor platform."""
220 coordinator = entry.runtime_data
222 entities: list[NordpoolBaseEntity] = []
223 currency = entry.runtime_data.data.currency
225 for area
in get_prices(entry.runtime_data.data):
226 LOGGER.debug(
"Setting up base sensors for area %s", area)
229 for description
in DEFAULT_SENSOR_TYPES
232 "Setting up price sensors for area %s with currency %s", area, currency
236 for description
in PRICES_SENSOR_TYPES
240 for description
in DAILY_AVERAGE_PRICES_SENSOR_TYPES
244 "Setting up block price sensors for area %s with currency %s in block %s",
251 coordinator, description, area, currency, block_name
253 for description
in BLOCK_PRICES_SENSOR_TYPES
259 """Representation of a Nord Pool sensor."""
261 entity_description: NordpoolDefaultSensorEntityDescription
265 """Return value of sensor."""
270 """Representation of a Nord Pool price sensor."""
272 entity_description: NordpoolPricesSensorEntityDescription
276 coordinator: NordPoolDataUpdateCoordinator,
277 entity_description: NordpoolPricesSensorEntityDescription,
281 """Initiate Nord Pool sensor."""
282 super().
__init__(coordinator, entity_description, area)
287 """Return value of sensor."""
294 """Representation of a Nord Pool block price sensor."""
296 entity_description: NordpoolBlockPricesSensorEntityDescription
300 coordinator: NordPoolDataUpdateCoordinator,
301 entity_description: NordpoolBlockPricesSensorEntityDescription,
306 """Initiate Nord Pool sensor."""
307 super().
__init__(coordinator, entity_description, area)
308 if entity_description.device_class
is not SensorDeviceClass.TIMESTAMP:
316 """Return value of sensor."""
323 """Representation of a Nord Pool daily average price sensor."""
325 entity_description: SensorEntityDescription
329 coordinator: NordPoolDataUpdateCoordinator,
330 entity_description: SensorEntityDescription,
334 """Initiate Nord Pool sensor."""
335 super().
__init__(coordinator, entity_description, area)
340 """Return value of sensor."""
341 return self.coordinator.data.area_average[self.
areaarea] / 1000
None __init__(self, NordPoolDataUpdateCoordinator coordinator, NordpoolBlockPricesSensorEntityDescription entity_description, str area, str currency, str block_name)
float|datetime|None native_value(self)
_attr_translation_placeholders
_attr_native_unit_of_measurement
float|None native_value(self)
_attr_native_unit_of_measurement
None __init__(self, NordPoolDataUpdateCoordinator coordinator, SensorEntityDescription entity_description, str area, str currency)
None __init__(self, NordPoolDataUpdateCoordinator coordinator, NordpoolPricesSensorEntityDescription entity_description, str area, str currency)
float|None native_value(self)
_attr_native_unit_of_measurement
str|float|datetime|None native_value(self)
dict[str, tuple[float|None, float, float|None]] get_prices(DeliveryPeriodData data)
None async_setup_entry(HomeAssistant hass, NordPoolConfigEntry entry, AddEntitiesCallback async_add_entities)
dict[str, dict[str, tuple[datetime, datetime, float, float, float]]] get_blockprices(DeliveryPeriodData data)