Load data for multiple days in Nord Pool (#133371)
* Load data for multiple days in Nord Pool * Fix current day * Fix tests * Fix services * Fix fixtures * Mod get_data_current_day * Mods * simplify furtherpull/133825/head^2
parent
26180486e7
commit
368e958457
|
@ -10,6 +10,8 @@ from typing import TYPE_CHECKING
|
||||||
from pynordpool import (
|
from pynordpool import (
|
||||||
Currency,
|
Currency,
|
||||||
DeliveryPeriodData,
|
DeliveryPeriodData,
|
||||||
|
DeliveryPeriodEntry,
|
||||||
|
DeliveryPeriodsData,
|
||||||
NordPoolClient,
|
NordPoolClient,
|
||||||
NordPoolEmptyResponseError,
|
NordPoolEmptyResponseError,
|
||||||
NordPoolError,
|
NordPoolError,
|
||||||
|
@ -29,7 +31,7 @@ if TYPE_CHECKING:
|
||||||
from . import NordPoolConfigEntry
|
from . import NordPoolConfigEntry
|
||||||
|
|
||||||
|
|
||||||
class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
|
class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodsData]):
|
||||||
"""A Nord Pool Data Update Coordinator."""
|
"""A Nord Pool Data Update Coordinator."""
|
||||||
|
|
||||||
config_entry: NordPoolConfigEntry
|
config_entry: NordPoolConfigEntry
|
||||||
|
@ -74,12 +76,16 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
|
||||||
if data:
|
if data:
|
||||||
self.async_set_updated_data(data)
|
self.async_set_updated_data(data)
|
||||||
|
|
||||||
async def api_call(self, retry: int = 3) -> DeliveryPeriodData | None:
|
async def api_call(self, retry: int = 3) -> DeliveryPeriodsData | None:
|
||||||
"""Make api call to retrieve data with retry if failure."""
|
"""Make api call to retrieve data with retry if failure."""
|
||||||
data = None
|
data = None
|
||||||
try:
|
try:
|
||||||
data = await self.client.async_get_delivery_period(
|
data = await self.client.async_get_delivery_periods(
|
||||||
dt_util.now(),
|
[
|
||||||
|
dt_util.now() - timedelta(days=1),
|
||||||
|
dt_util.now(),
|
||||||
|
dt_util.now() + timedelta(days=1),
|
||||||
|
],
|
||||||
Currency(self.config_entry.data[CONF_CURRENCY]),
|
Currency(self.config_entry.data[CONF_CURRENCY]),
|
||||||
self.config_entry.data[CONF_AREAS],
|
self.config_entry.data[CONF_AREAS],
|
||||||
)
|
)
|
||||||
|
@ -97,3 +103,20 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
|
||||||
self.async_set_update_error(error)
|
self.async_set_update_error(error)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def merge_price_entries(self) -> list[DeliveryPeriodEntry]:
|
||||||
|
"""Return the merged price entries."""
|
||||||
|
merged_entries: list[DeliveryPeriodEntry] = []
|
||||||
|
for del_period in self.data.entries:
|
||||||
|
merged_entries.extend(del_period.entries)
|
||||||
|
return merged_entries
|
||||||
|
|
||||||
|
def get_data_current_day(self) -> DeliveryPeriodData:
|
||||||
|
"""Return the current day data."""
|
||||||
|
current_day = dt_util.utcnow().strftime("%Y-%m-%d")
|
||||||
|
delivery_period: DeliveryPeriodData = self.data.entries[0]
|
||||||
|
for del_period in self.data.entries:
|
||||||
|
if del_period.requested_date == current_day:
|
||||||
|
delivery_period = del_period
|
||||||
|
break
|
||||||
|
return delivery_period
|
||||||
|
|
|
@ -6,8 +6,6 @@ from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from pynordpool import DeliveryPeriodData
|
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
EntityCategory,
|
EntityCategory,
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
|
@ -29,34 +27,34 @@ PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
def validate_prices(
|
def validate_prices(
|
||||||
func: Callable[
|
func: Callable[
|
||||||
[DeliveryPeriodData], dict[str, tuple[float | None, float, float | None]]
|
[NordpoolPriceSensor], dict[str, tuple[float | None, float, float | None]]
|
||||||
],
|
],
|
||||||
data: DeliveryPeriodData,
|
entity: NordpoolPriceSensor,
|
||||||
area: str,
|
area: str,
|
||||||
index: int,
|
index: int,
|
||||||
) -> float | None:
|
) -> float | None:
|
||||||
"""Validate and return."""
|
"""Validate and return."""
|
||||||
if result := func(data)[area][index]:
|
if result := func(entity)[area][index]:
|
||||||
return result / 1000
|
return result / 1000
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_prices(
|
def get_prices(
|
||||||
data: DeliveryPeriodData,
|
entity: NordpoolPriceSensor,
|
||||||
) -> dict[str, tuple[float | None, float, float | None]]:
|
) -> dict[str, tuple[float | None, float, float | None]]:
|
||||||
"""Return previous, current and next prices.
|
"""Return previous, current and next prices.
|
||||||
|
|
||||||
Output: {"SE3": (10.0, 10.5, 12.1)}
|
Output: {"SE3": (10.0, 10.5, 12.1)}
|
||||||
"""
|
"""
|
||||||
|
data = entity.coordinator.merge_price_entries()
|
||||||
last_price_entries: dict[str, float] = {}
|
last_price_entries: dict[str, float] = {}
|
||||||
current_price_entries: dict[str, float] = {}
|
current_price_entries: dict[str, float] = {}
|
||||||
next_price_entries: dict[str, float] = {}
|
next_price_entries: dict[str, float] = {}
|
||||||
current_time = dt_util.utcnow()
|
current_time = dt_util.utcnow()
|
||||||
previous_time = current_time - timedelta(hours=1)
|
previous_time = current_time - timedelta(hours=1)
|
||||||
next_time = current_time + timedelta(hours=1)
|
next_time = current_time + timedelta(hours=1)
|
||||||
price_data = data.entries
|
LOGGER.debug("Price data: %s", data)
|
||||||
LOGGER.debug("Price data: %s", price_data)
|
for entry in data:
|
||||||
for entry in price_data:
|
|
||||||
if entry.start <= current_time <= entry.end:
|
if entry.start <= current_time <= entry.end:
|
||||||
current_price_entries = entry.entry
|
current_price_entries = entry.entry
|
||||||
if entry.start <= previous_time <= entry.end:
|
if entry.start <= previous_time <= entry.end:
|
||||||
|
@ -82,11 +80,12 @@ def get_prices(
|
||||||
|
|
||||||
|
|
||||||
def get_min_max_price(
|
def get_min_max_price(
|
||||||
data: DeliveryPeriodData,
|
entity: NordpoolPriceSensor,
|
||||||
area: str,
|
|
||||||
func: Callable[[float, float], float],
|
func: Callable[[float, float], float],
|
||||||
) -> tuple[float, datetime, datetime]:
|
) -> tuple[float, datetime, datetime]:
|
||||||
"""Get the lowest price from the data."""
|
"""Get the lowest price from the data."""
|
||||||
|
data = entity.coordinator.get_data_current_day()
|
||||||
|
area = entity.area
|
||||||
price_data = data.entries
|
price_data = data.entries
|
||||||
price: float = price_data[0].entry[area]
|
price: float = price_data[0].entry[area]
|
||||||
start: datetime = price_data[0].start
|
start: datetime = price_data[0].start
|
||||||
|
@ -102,12 +101,13 @@ def get_min_max_price(
|
||||||
|
|
||||||
|
|
||||||
def get_blockprices(
|
def get_blockprices(
|
||||||
data: DeliveryPeriodData,
|
entity: NordpoolBlockPriceSensor,
|
||||||
) -> dict[str, dict[str, tuple[datetime, datetime, float, float, float]]]:
|
) -> dict[str, dict[str, tuple[datetime, datetime, float, float, float]]]:
|
||||||
"""Return average, min and max for block prices.
|
"""Return average, min and max for block prices.
|
||||||
|
|
||||||
Output: {"SE3": {"Off-peak 1": (_datetime_, _datetime_, 9.3, 10.5, 12.1)}}
|
Output: {"SE3": {"Off-peak 1": (_datetime_, _datetime_, 9.3, 10.5, 12.1)}}
|
||||||
"""
|
"""
|
||||||
|
data = entity.coordinator.get_data_current_day()
|
||||||
result: dict[str, dict[str, tuple[datetime, datetime, float, float, float]]] = {}
|
result: dict[str, dict[str, tuple[datetime, datetime, float, float, float]]] = {}
|
||||||
block_prices = data.block_prices
|
block_prices = data.block_prices
|
||||||
for entry in block_prices:
|
for entry in block_prices:
|
||||||
|
@ -130,15 +130,15 @@ def get_blockprices(
|
||||||
class NordpoolDefaultSensorEntityDescription(SensorEntityDescription):
|
class NordpoolDefaultSensorEntityDescription(SensorEntityDescription):
|
||||||
"""Describes Nord Pool default sensor entity."""
|
"""Describes Nord Pool default sensor entity."""
|
||||||
|
|
||||||
value_fn: Callable[[DeliveryPeriodData], str | float | datetime | None]
|
value_fn: Callable[[NordpoolSensor], str | float | datetime | None]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
class NordpoolPricesSensorEntityDescription(SensorEntityDescription):
|
class NordpoolPricesSensorEntityDescription(SensorEntityDescription):
|
||||||
"""Describes Nord Pool prices sensor entity."""
|
"""Describes Nord Pool prices sensor entity."""
|
||||||
|
|
||||||
value_fn: Callable[[DeliveryPeriodData, str], float | None]
|
value_fn: Callable[[NordpoolPriceSensor], float | None]
|
||||||
extra_fn: Callable[[DeliveryPeriodData, str], dict[str, str] | None]
|
extra_fn: Callable[[NordpoolPriceSensor], dict[str, str] | None]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
@ -155,19 +155,19 @@ DEFAULT_SENSOR_TYPES: tuple[NordpoolDefaultSensorEntityDescription, ...] = (
|
||||||
key="updated_at",
|
key="updated_at",
|
||||||
translation_key="updated_at",
|
translation_key="updated_at",
|
||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
value_fn=lambda data: data.updated_at,
|
value_fn=lambda entity: entity.coordinator.get_data_current_day().updated_at,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
NordpoolDefaultSensorEntityDescription(
|
NordpoolDefaultSensorEntityDescription(
|
||||||
key="currency",
|
key="currency",
|
||||||
translation_key="currency",
|
translation_key="currency",
|
||||||
value_fn=lambda data: data.currency,
|
value_fn=lambda entity: entity.coordinator.get_data_current_day().currency,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
NordpoolDefaultSensorEntityDescription(
|
NordpoolDefaultSensorEntityDescription(
|
||||||
key="exchange_rate",
|
key="exchange_rate",
|
||||||
translation_key="exchange_rate",
|
translation_key="exchange_rate",
|
||||||
value_fn=lambda data: data.exchange_rate,
|
value_fn=lambda entity: entity.coordinator.get_data_current_day().exchange_rate,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
@ -177,42 +177,42 @@ PRICES_SENSOR_TYPES: tuple[NordpoolPricesSensorEntityDescription, ...] = (
|
||||||
NordpoolPricesSensorEntityDescription(
|
NordpoolPricesSensorEntityDescription(
|
||||||
key="current_price",
|
key="current_price",
|
||||||
translation_key="current_price",
|
translation_key="current_price",
|
||||||
value_fn=lambda data, area: validate_prices(get_prices, data, area, 1),
|
value_fn=lambda entity: validate_prices(get_prices, entity, entity.area, 1),
|
||||||
extra_fn=lambda data, area: None,
|
extra_fn=lambda entity: None,
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
suggested_display_precision=2,
|
suggested_display_precision=2,
|
||||||
),
|
),
|
||||||
NordpoolPricesSensorEntityDescription(
|
NordpoolPricesSensorEntityDescription(
|
||||||
key="last_price",
|
key="last_price",
|
||||||
translation_key="last_price",
|
translation_key="last_price",
|
||||||
value_fn=lambda data, area: validate_prices(get_prices, data, area, 0),
|
value_fn=lambda entity: validate_prices(get_prices, entity, entity.area, 0),
|
||||||
extra_fn=lambda data, area: None,
|
extra_fn=lambda entity: None,
|
||||||
suggested_display_precision=2,
|
suggested_display_precision=2,
|
||||||
),
|
),
|
||||||
NordpoolPricesSensorEntityDescription(
|
NordpoolPricesSensorEntityDescription(
|
||||||
key="next_price",
|
key="next_price",
|
||||||
translation_key="next_price",
|
translation_key="next_price",
|
||||||
value_fn=lambda data, area: validate_prices(get_prices, data, area, 2),
|
value_fn=lambda entity: validate_prices(get_prices, entity, entity.area, 2),
|
||||||
extra_fn=lambda data, area: None,
|
extra_fn=lambda entity: None,
|
||||||
suggested_display_precision=2,
|
suggested_display_precision=2,
|
||||||
),
|
),
|
||||||
NordpoolPricesSensorEntityDescription(
|
NordpoolPricesSensorEntityDescription(
|
||||||
key="lowest_price",
|
key="lowest_price",
|
||||||
translation_key="lowest_price",
|
translation_key="lowest_price",
|
||||||
value_fn=lambda data, area: get_min_max_price(data, area, min)[0] / 1000,
|
value_fn=lambda entity: get_min_max_price(entity, min)[0] / 1000,
|
||||||
extra_fn=lambda data, area: {
|
extra_fn=lambda entity: {
|
||||||
"start": get_min_max_price(data, area, min)[1].isoformat(),
|
"start": get_min_max_price(entity, min)[1].isoformat(),
|
||||||
"end": get_min_max_price(data, area, min)[2].isoformat(),
|
"end": get_min_max_price(entity, min)[2].isoformat(),
|
||||||
},
|
},
|
||||||
suggested_display_precision=2,
|
suggested_display_precision=2,
|
||||||
),
|
),
|
||||||
NordpoolPricesSensorEntityDescription(
|
NordpoolPricesSensorEntityDescription(
|
||||||
key="highest_price",
|
key="highest_price",
|
||||||
translation_key="highest_price",
|
translation_key="highest_price",
|
||||||
value_fn=lambda data, area: get_min_max_price(data, area, max)[0] / 1000,
|
value_fn=lambda entity: get_min_max_price(entity, max)[0] / 1000,
|
||||||
extra_fn=lambda data, area: {
|
extra_fn=lambda entity: {
|
||||||
"start": get_min_max_price(data, area, max)[1].isoformat(),
|
"start": get_min_max_price(entity, max)[1].isoformat(),
|
||||||
"end": get_min_max_price(data, area, max)[2].isoformat(),
|
"end": get_min_max_price(entity, max)[2].isoformat(),
|
||||||
},
|
},
|
||||||
suggested_display_precision=2,
|
suggested_display_precision=2,
|
||||||
),
|
),
|
||||||
|
@ -276,11 +276,12 @@ async def async_setup_entry(
|
||||||
"""Set up Nord Pool sensor platform."""
|
"""Set up Nord Pool sensor platform."""
|
||||||
|
|
||||||
coordinator = entry.runtime_data
|
coordinator = entry.runtime_data
|
||||||
|
current_day_data = entry.runtime_data.get_data_current_day()
|
||||||
|
|
||||||
entities: list[NordpoolBaseEntity] = []
|
entities: list[NordpoolBaseEntity] = []
|
||||||
currency = entry.runtime_data.data.currency
|
currency = current_day_data.currency
|
||||||
|
|
||||||
for area in get_prices(entry.runtime_data.data):
|
for area in current_day_data.area_average:
|
||||||
LOGGER.debug("Setting up base sensors for area %s", area)
|
LOGGER.debug("Setting up base sensors for area %s", area)
|
||||||
entities.extend(
|
entities.extend(
|
||||||
NordpoolSensor(coordinator, description, area)
|
NordpoolSensor(coordinator, description, area)
|
||||||
|
@ -297,16 +298,16 @@ async def async_setup_entry(
|
||||||
NordpoolDailyAveragePriceSensor(coordinator, description, area, currency)
|
NordpoolDailyAveragePriceSensor(coordinator, description, area, currency)
|
||||||
for description in DAILY_AVERAGE_PRICES_SENSOR_TYPES
|
for description in DAILY_AVERAGE_PRICES_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
for block_name in get_blockprices(coordinator.data)[area]:
|
for block_prices in entry.runtime_data.get_data_current_day().block_prices:
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Setting up block price sensors for area %s with currency %s in block %s",
|
"Setting up block price sensors for area %s with currency %s in block %s",
|
||||||
area,
|
area,
|
||||||
currency,
|
currency,
|
||||||
block_name,
|
block_prices.name,
|
||||||
)
|
)
|
||||||
entities.extend(
|
entities.extend(
|
||||||
NordpoolBlockPriceSensor(
|
NordpoolBlockPriceSensor(
|
||||||
coordinator, description, area, currency, block_name
|
coordinator, description, area, currency, block_prices.name
|
||||||
)
|
)
|
||||||
for description in BLOCK_PRICES_SENSOR_TYPES
|
for description in BLOCK_PRICES_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
|
@ -321,7 +322,7 @@ class NordpoolSensor(NordpoolBaseEntity, SensorEntity):
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> str | float | datetime | None:
|
def native_value(self) -> str | float | datetime | None:
|
||||||
"""Return value of sensor."""
|
"""Return value of sensor."""
|
||||||
return self.entity_description.value_fn(self.coordinator.data)
|
return self.entity_description.value_fn(self)
|
||||||
|
|
||||||
|
|
||||||
class NordpoolPriceSensor(NordpoolBaseEntity, SensorEntity):
|
class NordpoolPriceSensor(NordpoolBaseEntity, SensorEntity):
|
||||||
|
@ -343,12 +344,12 @@ class NordpoolPriceSensor(NordpoolBaseEntity, SensorEntity):
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> float | None:
|
def native_value(self) -> float | None:
|
||||||
"""Return value of sensor."""
|
"""Return value of sensor."""
|
||||||
return self.entity_description.value_fn(self.coordinator.data, self.area)
|
return self.entity_description.value_fn(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||||
"""Return the extra state attributes."""
|
"""Return the extra state attributes."""
|
||||||
return self.entity_description.extra_fn(self.coordinator.data, self.area)
|
return self.entity_description.extra_fn(self)
|
||||||
|
|
||||||
|
|
||||||
class NordpoolBlockPriceSensor(NordpoolBaseEntity, SensorEntity):
|
class NordpoolBlockPriceSensor(NordpoolBaseEntity, SensorEntity):
|
||||||
|
@ -376,7 +377,7 @@ class NordpoolBlockPriceSensor(NordpoolBaseEntity, SensorEntity):
|
||||||
def native_value(self) -> float | datetime | None:
|
def native_value(self) -> float | datetime | None:
|
||||||
"""Return value of sensor."""
|
"""Return value of sensor."""
|
||||||
return self.entity_description.value_fn(
|
return self.entity_description.value_fn(
|
||||||
get_blockprices(self.coordinator.data)[self.area][self.block_name]
|
get_blockprices(self)[self.area][self.block_name]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -399,4 +400,5 @@ class NordpoolDailyAveragePriceSensor(NordpoolBaseEntity, SensorEntity):
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> float | None:
|
def native_value(self) -> float | None:
|
||||||
"""Return value of sensor."""
|
"""Return value of sensor."""
|
||||||
return self.coordinator.data.area_average[self.area] / 1000
|
data = self.coordinator.get_data_current_day()
|
||||||
|
return data.area_average[self.area] / 1000
|
||||||
|
|
|
@ -3,20 +3,16 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
from datetime import datetime
|
|
||||||
import json
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from pynordpool import NordPoolClient
|
from pynordpool import API, NordPoolClient
|
||||||
from pynordpool.const import Currency
|
|
||||||
from pynordpool.model import DeliveryPeriodData
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.nordpool.const import DOMAIN
|
from homeassistant.components.nordpool.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_USER
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.util import dt as dt_util
|
|
||||||
|
|
||||||
from . import ENTRY_CONFIG
|
from . import ENTRY_CONFIG
|
||||||
|
|
||||||
|
@ -32,9 +28,7 @@ async def no_sleep() -> AsyncGenerator[None]:
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def load_int(
|
async def load_int(hass: HomeAssistant, get_client: NordPoolClient) -> MockConfigEntry:
|
||||||
hass: HomeAssistant, get_data: DeliveryPeriodData
|
|
||||||
) -> MockConfigEntry:
|
|
||||||
"""Set up the Nord Pool integration in Home Assistant."""
|
"""Set up the Nord Pool integration in Home Assistant."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
|
@ -44,40 +38,83 @@ async def load_int(
|
||||||
|
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
with (
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
patch(
|
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
|
||||||
return_value=get_data,
|
|
||||||
),
|
|
||||||
):
|
|
||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
return config_entry
|
return config_entry
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="get_data")
|
@pytest.fixture(name="get_client")
|
||||||
async def get_data_from_library(
|
async def get_data_from_library(
|
||||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, load_json: dict[str, Any]
|
hass: HomeAssistant,
|
||||||
) -> DeliveryPeriodData:
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
load_json: list[dict[str, Any]],
|
||||||
|
) -> AsyncGenerator[NordPoolClient]:
|
||||||
"""Retrieve data from Nord Pool library."""
|
"""Retrieve data from Nord Pool library."""
|
||||||
|
aioclient_mock.request(
|
||||||
|
"GET",
|
||||||
|
url=API + "/DayAheadPrices",
|
||||||
|
params={
|
||||||
|
"date": "2024-11-05",
|
||||||
|
"market": "DayAhead",
|
||||||
|
"deliveryArea": "SE3,SE4",
|
||||||
|
"currency": "SEK",
|
||||||
|
},
|
||||||
|
json=load_json[0],
|
||||||
|
)
|
||||||
|
aioclient_mock.request(
|
||||||
|
"GET",
|
||||||
|
url=API + "/DayAheadPrices",
|
||||||
|
params={
|
||||||
|
"date": "2024-11-05",
|
||||||
|
"market": "DayAhead",
|
||||||
|
"deliveryArea": "SE3",
|
||||||
|
"currency": "EUR",
|
||||||
|
},
|
||||||
|
json=load_json[0],
|
||||||
|
)
|
||||||
|
aioclient_mock.request(
|
||||||
|
"GET",
|
||||||
|
url=API + "/DayAheadPrices",
|
||||||
|
params={
|
||||||
|
"date": "2024-11-04",
|
||||||
|
"market": "DayAhead",
|
||||||
|
"deliveryArea": "SE3,SE4",
|
||||||
|
"currency": "SEK",
|
||||||
|
},
|
||||||
|
json=load_json[1],
|
||||||
|
)
|
||||||
|
aioclient_mock.request(
|
||||||
|
"GET",
|
||||||
|
url=API + "/DayAheadPrices",
|
||||||
|
params={
|
||||||
|
"date": "2024-11-06",
|
||||||
|
"market": "DayAhead",
|
||||||
|
"deliveryArea": "SE3,SE4",
|
||||||
|
"currency": "SEK",
|
||||||
|
},
|
||||||
|
json=load_json[2],
|
||||||
|
)
|
||||||
client = NordPoolClient(aioclient_mock.create_session(hass.loop))
|
client = NordPoolClient(aioclient_mock.create_session(hass.loop))
|
||||||
with patch("pynordpool.NordPoolClient._get", return_value=load_json):
|
yield client
|
||||||
output = await client.async_get_delivery_period(
|
|
||||||
datetime(2024, 11, 5, 13, tzinfo=dt_util.UTC), Currency.SEK, ["SE3", "SE4"]
|
|
||||||
)
|
|
||||||
await client._session.close()
|
await client._session.close()
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="load_json")
|
@pytest.fixture(name="load_json")
|
||||||
def load_json_from_fixture(load_data: str) -> dict[str, Any]:
|
def load_json_from_fixture(load_data: list[str, str, str]) -> list[dict[str, Any]]:
|
||||||
"""Load fixture with json data and return."""
|
"""Load fixture with json data and return."""
|
||||||
return json.loads(load_data)
|
return [
|
||||||
|
json.loads(load_data[0]),
|
||||||
|
json.loads(load_data[1]),
|
||||||
|
json.loads(load_data[2]),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="load_data", scope="package")
|
@pytest.fixture(name="load_data", scope="package")
|
||||||
def load_data_from_fixture() -> str:
|
def load_data_from_fixture() -> list[str, str, str]:
|
||||||
"""Load fixture with fixture data and return."""
|
"""Load fixture with fixture data and return."""
|
||||||
return load_fixture("delivery_period.json", DOMAIN)
|
return [
|
||||||
|
load_fixture("delivery_period_today.json", DOMAIN),
|
||||||
|
load_fixture("delivery_period_yesterday.json", DOMAIN),
|
||||||
|
load_fixture("delivery_period_tomorrow.json", DOMAIN),
|
||||||
|
]
|
||||||
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
{
|
||||||
|
"deliveryDateCET": "2024-11-06",
|
||||||
|
"version": 3,
|
||||||
|
"updatedAt": "2024-11-05T12:12:51.9853434Z",
|
||||||
|
"deliveryAreas": ["SE3", "SE4"],
|
||||||
|
"market": "DayAhead",
|
||||||
|
"multiAreaEntries": [
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-05T23:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T00:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 126.66,
|
||||||
|
"SE4": 275.6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T00:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T01:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 74.06,
|
||||||
|
"SE4": 157.34
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T01:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T02:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 78.38,
|
||||||
|
"SE4": 165.62
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T02:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T03:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 92.37,
|
||||||
|
"SE4": 196.17
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T03:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T04:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 99.14,
|
||||||
|
"SE4": 190.58
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T04:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T05:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 447.51,
|
||||||
|
"SE4": 932.93
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T05:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T06:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 641.47,
|
||||||
|
"SE4": 1284.69
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T06:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T07:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1820.5,
|
||||||
|
"SE4": 2449.96
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T07:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T08:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1723.0,
|
||||||
|
"SE4": 2244.22
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T08:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T09:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1298.57,
|
||||||
|
"SE4": 1643.45
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T09:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T10:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1099.25,
|
||||||
|
"SE4": 1507.23
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T10:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T11:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 903.31,
|
||||||
|
"SE4": 1362.84
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T11:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T12:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 959.99,
|
||||||
|
"SE4": 1376.13
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T12:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T13:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1186.61,
|
||||||
|
"SE4": 1449.96
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T13:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T14:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1307.67,
|
||||||
|
"SE4": 1608.35
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T14:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T15:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1385.46,
|
||||||
|
"SE4": 2110.8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T15:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T16:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1366.8,
|
||||||
|
"SE4": 3031.25
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T16:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T17:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 2366.57,
|
||||||
|
"SE4": 5511.77
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T17:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T18:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1481.92,
|
||||||
|
"SE4": 3351.64
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T18:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T19:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1082.69,
|
||||||
|
"SE4": 2484.95
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T19:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T20:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 716.82,
|
||||||
|
"SE4": 1624.33
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T20:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T21:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 583.16,
|
||||||
|
"SE4": 1306.27
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T21:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T22:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 523.09,
|
||||||
|
"SE4": 1142.99
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-06T22:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T23:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 250.64,
|
||||||
|
"SE4": 539.42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"blockPriceAggregates": [
|
||||||
|
{
|
||||||
|
"blockName": "Off-peak 1",
|
||||||
|
"deliveryStart": "2024-11-05T23:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T07:00:00Z",
|
||||||
|
"averagePricePerArea": {
|
||||||
|
"SE3": {
|
||||||
|
"average": 422.51,
|
||||||
|
"min": 74.06,
|
||||||
|
"max": 1820.5
|
||||||
|
},
|
||||||
|
"SE4": {
|
||||||
|
"average": 706.61,
|
||||||
|
"min": 157.34,
|
||||||
|
"max": 2449.96
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockName": "Peak",
|
||||||
|
"deliveryStart": "2024-11-06T07:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T19:00:00Z",
|
||||||
|
"averagePricePerArea": {
|
||||||
|
"SE3": {
|
||||||
|
"average": 1346.82,
|
||||||
|
"min": 903.31,
|
||||||
|
"max": 2366.57
|
||||||
|
},
|
||||||
|
"SE4": {
|
||||||
|
"average": 2306.88,
|
||||||
|
"min": 1362.84,
|
||||||
|
"max": 5511.77
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockName": "Off-peak 2",
|
||||||
|
"deliveryStart": "2024-11-06T19:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-06T23:00:00Z",
|
||||||
|
"averagePricePerArea": {
|
||||||
|
"SE3": {
|
||||||
|
"average": 518.43,
|
||||||
|
"min": 250.64,
|
||||||
|
"max": 716.82
|
||||||
|
},
|
||||||
|
"SE4": {
|
||||||
|
"average": 1153.25,
|
||||||
|
"min": 539.42,
|
||||||
|
"max": 1624.33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currency": "SEK",
|
||||||
|
"exchangeRate": 11.66314,
|
||||||
|
"areaStates": [
|
||||||
|
{
|
||||||
|
"state": "Final",
|
||||||
|
"areas": ["SE3", "SE4"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"areaAverages": [
|
||||||
|
{
|
||||||
|
"areaCode": "SE3",
|
||||||
|
"price": 900.65
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"areaCode": "SE4",
|
||||||
|
"price": 1581.19
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
{
|
||||||
|
"deliveryDateCET": "2024-11-04",
|
||||||
|
"version": 3,
|
||||||
|
"updatedAt": "2024-11-04T08:09:11.1931991Z",
|
||||||
|
"deliveryAreas": ["SE3", "SE4"],
|
||||||
|
"market": "DayAhead",
|
||||||
|
"multiAreaEntries": [
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-03T23:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T00:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 66.13,
|
||||||
|
"SE4": 78.59
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T00:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T01:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 72.54,
|
||||||
|
"SE4": 86.51
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T01:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T02:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 73.12,
|
||||||
|
"SE4": 84.88
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T02:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T03:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 171.97,
|
||||||
|
"SE4": 217.26
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T03:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T04:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 181.05,
|
||||||
|
"SE4": 227.74
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T04:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T05:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 360.71,
|
||||||
|
"SE4": 414.61
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T05:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T06:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 917.83,
|
||||||
|
"SE4": 1439.33
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T06:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T07:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1426.17,
|
||||||
|
"SE4": 1695.95
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T07:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T08:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1350.96,
|
||||||
|
"SE4": 1605.13
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T08:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T09:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1195.06,
|
||||||
|
"SE4": 1393.46
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T09:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T10:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 992.35,
|
||||||
|
"SE4": 1126.71
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T10:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T11:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 976.63,
|
||||||
|
"SE4": 1107.97
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T11:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T12:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 952.76,
|
||||||
|
"SE4": 1085.73
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T12:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T13:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1029.37,
|
||||||
|
"SE4": 1177.71
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T13:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T14:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1043.35,
|
||||||
|
"SE4": 1194.59
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T14:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T15:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1359.57,
|
||||||
|
"SE4": 1561.12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T15:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T16:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1848.35,
|
||||||
|
"SE4": 2145.84
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T16:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T17:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 2812.53,
|
||||||
|
"SE4": 3313.53
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T17:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T18:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 2351.69,
|
||||||
|
"SE4": 2751.87
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T18:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T19:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1553.08,
|
||||||
|
"SE4": 1842.77
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T19:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T20:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1165.02,
|
||||||
|
"SE4": 1398.35
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T20:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T21:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 1007.48,
|
||||||
|
"SE4": 1172.35
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T21:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T22:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 792.09,
|
||||||
|
"SE4": 920.28
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"deliveryStart": "2024-11-04T22:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T23:00:00Z",
|
||||||
|
"entryPerArea": {
|
||||||
|
"SE3": 465.38,
|
||||||
|
"SE4": 528.83
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"blockPriceAggregates": [
|
||||||
|
{
|
||||||
|
"blockName": "Off-peak 1",
|
||||||
|
"deliveryStart": "2024-11-03T23:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T07:00:00Z",
|
||||||
|
"averagePricePerArea": {
|
||||||
|
"SE3": {
|
||||||
|
"average": 408.69,
|
||||||
|
"min": 66.13,
|
||||||
|
"max": 1426.17
|
||||||
|
},
|
||||||
|
"SE4": {
|
||||||
|
"average": 530.61,
|
||||||
|
"min": 78.59,
|
||||||
|
"max": 1695.95
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockName": "Peak",
|
||||||
|
"deliveryStart": "2024-11-04T07:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T19:00:00Z",
|
||||||
|
"averagePricePerArea": {
|
||||||
|
"SE3": {
|
||||||
|
"average": 1455.48,
|
||||||
|
"min": 952.76,
|
||||||
|
"max": 2812.53
|
||||||
|
},
|
||||||
|
"SE4": {
|
||||||
|
"average": 1692.2,
|
||||||
|
"min": 1085.73,
|
||||||
|
"max": 3313.53
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockName": "Off-peak 2",
|
||||||
|
"deliveryStart": "2024-11-04T19:00:00Z",
|
||||||
|
"deliveryEnd": "2024-11-04T23:00:00Z",
|
||||||
|
"averagePricePerArea": {
|
||||||
|
"SE3": {
|
||||||
|
"average": 857.49,
|
||||||
|
"min": 465.38,
|
||||||
|
"max": 1165.02
|
||||||
|
},
|
||||||
|
"SE4": {
|
||||||
|
"average": 1004.95,
|
||||||
|
"min": 528.83,
|
||||||
|
"max": 1398.35
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"currency": "SEK",
|
||||||
|
"exchangeRate": 11.64318,
|
||||||
|
"areaStates": [
|
||||||
|
{
|
||||||
|
"state": "Final",
|
||||||
|
"areas": ["SE3", "SE4"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"areaAverages": [
|
||||||
|
{
|
||||||
|
"areaCode": "SE3",
|
||||||
|
"price": 1006.88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"areaCode": "SE4",
|
||||||
|
"price": 1190.46
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from pynordpool import (
|
from pynordpool import (
|
||||||
DeliveryPeriodData,
|
NordPoolClient,
|
||||||
NordPoolConnectionError,
|
NordPoolConnectionError,
|
||||||
NordPoolEmptyResponseError,
|
NordPoolEmptyResponseError,
|
||||||
NordPoolError,
|
NordPoolError,
|
||||||
|
@ -22,10 +23,11 @@ from homeassistant.data_entry_flow import FlowResultType
|
||||||
from . import ENTRY_CONFIG
|
from . import ENTRY_CONFIG
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
|
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
|
||||||
async def test_form(hass: HomeAssistant, get_data: DeliveryPeriodData) -> None:
|
async def test_form(hass: HomeAssistant, get_client: NordPoolClient) -> None:
|
||||||
"""Test we get the form."""
|
"""Test we get the form."""
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
@ -34,17 +36,11 @@ async def test_form(hass: HomeAssistant, get_data: DeliveryPeriodData) -> None:
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
|
||||||
with (
|
result = await hass.config_entries.flow.async_configure(
|
||||||
patch(
|
result["flow_id"],
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
ENTRY_CONFIG,
|
||||||
return_value=get_data,
|
)
|
||||||
),
|
await hass.async_block_till_done()
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
ENTRY_CONFIG,
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert result["version"] == 1
|
assert result["version"] == 1
|
||||||
|
@ -54,7 +50,7 @@ async def test_form(hass: HomeAssistant, get_data: DeliveryPeriodData) -> None:
|
||||||
|
|
||||||
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
|
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
|
||||||
async def test_single_config_entry(
|
async def test_single_config_entry(
|
||||||
hass: HomeAssistant, load_int: None, get_data: DeliveryPeriodData
|
hass: HomeAssistant, load_int: None, get_client: NordPoolClient
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test abort for single config entry."""
|
"""Test abort for single config entry."""
|
||||||
|
|
||||||
|
@ -77,7 +73,7 @@ async def test_single_config_entry(
|
||||||
)
|
)
|
||||||
async def test_cannot_connect(
|
async def test_cannot_connect(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
get_data: DeliveryPeriodData,
|
get_client: NordPoolClient,
|
||||||
error_message: Exception,
|
error_message: Exception,
|
||||||
p_error: str,
|
p_error: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -101,14 +97,10 @@ async def test_cannot_connect(
|
||||||
|
|
||||||
assert result["errors"] == {"base": p_error}
|
assert result["errors"] == {"base": p_error}
|
||||||
|
|
||||||
with patch(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
result["flow_id"],
|
||||||
return_value=get_data,
|
user_input=ENTRY_CONFIG,
|
||||||
):
|
)
|
||||||
result = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
user_input=ENTRY_CONFIG,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert result["title"] == "Nord Pool"
|
assert result["title"] == "Nord Pool"
|
||||||
|
@ -119,25 +111,18 @@ async def test_cannot_connect(
|
||||||
async def test_reconfigure(
|
async def test_reconfigure(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
load_int: MockConfigEntry,
|
load_int: MockConfigEntry,
|
||||||
get_data: DeliveryPeriodData,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test reconfiguration."""
|
"""Test reconfiguration."""
|
||||||
|
|
||||||
result = await load_int.start_reconfigure_flow(hass)
|
result = await load_int.start_reconfigure_flow(hass)
|
||||||
|
|
||||||
with (
|
result = await hass.config_entries.flow.async_configure(
|
||||||
patch(
|
result["flow_id"],
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
{
|
||||||
return_value=get_data,
|
CONF_AREAS: ["SE3"],
|
||||||
),
|
CONF_CURRENCY: "EUR",
|
||||||
):
|
},
|
||||||
result = await hass.config_entries.flow.async_configure(
|
)
|
||||||
result["flow_id"],
|
|
||||||
{
|
|
||||||
CONF_AREAS: ["SE3"],
|
|
||||||
CONF_CURRENCY: "EUR",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
assert result["reason"] == "reconfigure_successful"
|
assert result["reason"] == "reconfigure_successful"
|
||||||
|
@ -162,7 +147,8 @@ async def test_reconfigure(
|
||||||
async def test_reconfigure_cannot_connect(
|
async def test_reconfigure_cannot_connect(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
load_int: MockConfigEntry,
|
load_int: MockConfigEntry,
|
||||||
get_data: DeliveryPeriodData,
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
load_json: list[dict[str, Any]],
|
||||||
error_message: Exception,
|
error_message: Exception,
|
||||||
p_error: str,
|
p_error: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -184,17 +170,13 @@ async def test_reconfigure_cannot_connect(
|
||||||
|
|
||||||
assert result["errors"] == {"base": p_error}
|
assert result["errors"] == {"base": p_error}
|
||||||
|
|
||||||
with patch(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
result["flow_id"],
|
||||||
return_value=get_data,
|
user_input={
|
||||||
):
|
CONF_AREAS: ["SE3"],
|
||||||
result = await hass.config_entries.flow.async_configure(
|
CONF_CURRENCY: "EUR",
|
||||||
result["flow_id"],
|
},
|
||||||
user_input={
|
)
|
||||||
CONF_AREAS: ["SE3"],
|
|
||||||
CONF_CURRENCY: "EUR",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
assert result["reason"] == "reconfigure_successful"
|
assert result["reason"] == "reconfigure_successful"
|
||||||
|
|
|
@ -7,8 +7,8 @@ from unittest.mock import patch
|
||||||
|
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
from pynordpool import (
|
from pynordpool import (
|
||||||
DeliveryPeriodData,
|
|
||||||
NordPoolAuthenticationError,
|
NordPoolAuthenticationError,
|
||||||
|
NordPoolClient,
|
||||||
NordPoolEmptyResponseError,
|
NordPoolEmptyResponseError,
|
||||||
NordPoolError,
|
NordPoolError,
|
||||||
NordPoolResponseError,
|
NordPoolResponseError,
|
||||||
|
@ -28,7 +28,7 @@ from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
@pytest.mark.freeze_time("2024-11-05T10:00:00+00:00")
|
@pytest.mark.freeze_time("2024-11-05T10:00:00+00:00")
|
||||||
async def test_coordinator(
|
async def test_coordinator(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
get_data: DeliveryPeriodData,
|
get_client: NordPoolClient,
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -41,30 +41,31 @@ async def test_coordinator(
|
||||||
|
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||||
|
assert state.state == "0.92737"
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
side_effect=NordPoolError("error"),
|
||||||
) as mock_data,
|
) as mock_data,
|
||||||
):
|
):
|
||||||
mock_data.return_value = get_data
|
|
||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
mock_data.assert_called_once()
|
|
||||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
|
||||||
assert state.state == "0.92737"
|
|
||||||
mock_data.reset_mock()
|
|
||||||
|
|
||||||
mock_data.side_effect = NordPoolError("error")
|
|
||||||
freezer.tick(timedelta(hours=1))
|
freezer.tick(timedelta(hours=1))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
assert mock_data.call_count == 4
|
assert mock_data.call_count == 4
|
||||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
mock_data.reset_mock()
|
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
side_effect=NordPoolAuthenticationError("Authentication error"),
|
||||||
|
) as mock_data,
|
||||||
|
):
|
||||||
assert "Authentication error" not in caplog.text
|
assert "Authentication error" not in caplog.text
|
||||||
mock_data.side_effect = NordPoolAuthenticationError("Authentication error")
|
|
||||||
freezer.tick(timedelta(hours=1))
|
freezer.tick(timedelta(hours=1))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
@ -72,10 +73,14 @@ async def test_coordinator(
|
||||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
assert "Authentication error" in caplog.text
|
assert "Authentication error" in caplog.text
|
||||||
mock_data.reset_mock()
|
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
side_effect=NordPoolEmptyResponseError("Empty response"),
|
||||||
|
) as mock_data,
|
||||||
|
):
|
||||||
assert "Empty response" not in caplog.text
|
assert "Empty response" not in caplog.text
|
||||||
mock_data.side_effect = NordPoolEmptyResponseError("Empty response")
|
|
||||||
freezer.tick(timedelta(hours=1))
|
freezer.tick(timedelta(hours=1))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
@ -83,10 +88,14 @@ async def test_coordinator(
|
||||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
assert "Empty response" in caplog.text
|
assert "Empty response" in caplog.text
|
||||||
mock_data.reset_mock()
|
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
side_effect=NordPoolResponseError("Response error"),
|
||||||
|
) as mock_data,
|
||||||
|
):
|
||||||
assert "Response error" not in caplog.text
|
assert "Response error" not in caplog.text
|
||||||
mock_data.side_effect = NordPoolResponseError("Response error")
|
|
||||||
freezer.tick(timedelta(hours=1))
|
freezer.tick(timedelta(hours=1))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
@ -94,13 +103,9 @@ async def test_coordinator(
|
||||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
assert "Response error" in caplog.text
|
assert "Response error" in caplog.text
|
||||||
mock_data.reset_mock()
|
|
||||||
|
|
||||||
mock_data.return_value = get_data
|
freezer.tick(timedelta(hours=1))
|
||||||
mock_data.side_effect = None
|
async_fire_time_changed(hass)
|
||||||
freezer.tick(timedelta(hours=1))
|
await hass.async_block_till_done()
|
||||||
async_fire_time_changed(hass)
|
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||||
await hass.async_block_till_done()
|
assert state.state == "1.81645"
|
||||||
mock_data.assert_called_once()
|
|
||||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
|
||||||
assert state.state == "1.81645"
|
|
||||||
|
|
|
@ -2,19 +2,21 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
from syrupy.assertion import SnapshotAssertion
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||||
from tests.typing import ClientSessionGenerator
|
from tests.typing import ClientSessionGenerator
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.freeze_time("2024-11-05T10:00:00+00:00")
|
||||||
async def test_diagnostics(
|
async def test_diagnostics(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
hass_client: ClientSessionGenerator,
|
hass_client: ClientSessionGenerator,
|
||||||
load_int: ConfigEntry,
|
load_int: MockConfigEntry,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test generating diagnostics for a config entry."""
|
"""Test generating diagnostics for a config entry."""
|
||||||
|
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from pynordpool import (
|
from pynordpool import (
|
||||||
DeliveryPeriodData,
|
NordPoolClient,
|
||||||
NordPoolConnectionError,
|
NordPoolConnectionError,
|
||||||
NordPoolEmptyResponseError,
|
NordPoolEmptyResponseError,
|
||||||
NordPoolError,
|
NordPoolError,
|
||||||
|
@ -22,7 +22,8 @@ from . import ENTRY_CONFIG
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_unload_entry(hass: HomeAssistant, get_data: DeliveryPeriodData) -> None:
|
@pytest.mark.freeze_time("2024-11-05T10:00:00+00:00")
|
||||||
|
async def test_unload_entry(hass: HomeAssistant, get_client: NordPoolClient) -> None:
|
||||||
"""Test load and unload an entry."""
|
"""Test load and unload an entry."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
|
@ -31,13 +32,7 @@ async def test_unload_entry(hass: HomeAssistant, get_data: DeliveryPeriodData) -
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
with (
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
patch(
|
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
|
||||||
return_value=get_data,
|
|
||||||
),
|
|
||||||
):
|
|
||||||
await hass.config_entries.async_setup(entry.entry_id)
|
|
||||||
await hass.async_block_till_done(wait_background_tasks=True)
|
await hass.async_block_till_done(wait_background_tasks=True)
|
||||||
|
|
||||||
assert entry.state is ConfigEntryState.LOADED
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
@ -56,7 +51,7 @@ async def test_unload_entry(hass: HomeAssistant, get_data: DeliveryPeriodData) -
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_initial_startup_fails(
|
async def test_initial_startup_fails(
|
||||||
hass: HomeAssistant, get_data: DeliveryPeriodData, error: Exception
|
hass: HomeAssistant, get_client: NordPoolClient, error: Exception
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test load and unload an entry."""
|
"""Test load and unload an entry."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
|
|
|
@ -6,7 +6,6 @@ import pytest
|
||||||
from syrupy.assertion import SnapshotAssertion
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import STATE_UNKNOWN
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
@ -38,12 +37,12 @@ async def test_sensor_no_next_price(hass: HomeAssistant, load_int: ConfigEntry)
|
||||||
assert current_price is not None
|
assert current_price is not None
|
||||||
assert last_price is not None
|
assert last_price is not None
|
||||||
assert next_price is not None
|
assert next_price is not None
|
||||||
assert current_price.state == "0.28914"
|
assert current_price.state == "0.12666" # SE3 2024-11-05T23:00:00Z
|
||||||
assert last_price.state == "0.28914"
|
assert last_price.state == "0.28914" # SE3 2024-11-05T22:00:00Z
|
||||||
assert next_price.state == STATE_UNKNOWN
|
assert next_price.state == "0.07406" # SE3 2024-11-06T00:00:00Z"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.freeze_time("2024-11-05T00:00:00+01:00")
|
@pytest.mark.freeze_time("2024-11-06T00:00:00+01:00")
|
||||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
async def test_sensor_no_previous_price(
|
async def test_sensor_no_previous_price(
|
||||||
hass: HomeAssistant, load_int: ConfigEntry
|
hass: HomeAssistant, load_int: ConfigEntry
|
||||||
|
@ -57,6 +56,6 @@ async def test_sensor_no_previous_price(
|
||||||
assert current_price is not None
|
assert current_price is not None
|
||||||
assert last_price is not None
|
assert last_price is not None
|
||||||
assert next_price is not None
|
assert next_price is not None
|
||||||
assert current_price.state == "0.25073"
|
assert current_price.state == "0.12666" # SE3 2024-11-05T23:00:00Z
|
||||||
assert last_price.state == STATE_UNKNOWN
|
assert last_price.state == "0.28914" # SE3 2024-11-05T22:00:00Z
|
||||||
assert next_price.state == "0.07636"
|
assert next_price.state == "0.07406" # SE3 2024-11-06T00:00:00Z
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from pynordpool import (
|
from pynordpool import (
|
||||||
DeliveryPeriodData,
|
|
||||||
NordPoolAuthenticationError,
|
NordPoolAuthenticationError,
|
||||||
NordPoolEmptyResponseError,
|
NordPoolEmptyResponseError,
|
||||||
NordPoolError,
|
NordPoolError,
|
||||||
|
@ -28,7 +27,7 @@ TEST_SERVICE_DATA = {
|
||||||
ATTR_CONFIG_ENTRY: "to_replace",
|
ATTR_CONFIG_ENTRY: "to_replace",
|
||||||
ATTR_DATE: "2024-11-05",
|
ATTR_DATE: "2024-11-05",
|
||||||
ATTR_AREAS: "SE3",
|
ATTR_AREAS: "SE3",
|
||||||
ATTR_CURRENCY: "SEK",
|
ATTR_CURRENCY: "EUR",
|
||||||
}
|
}
|
||||||
TEST_SERVICE_DATA_USE_DEFAULTS = {
|
TEST_SERVICE_DATA_USE_DEFAULTS = {
|
||||||
ATTR_CONFIG_ENTRY: "to_replace",
|
ATTR_CONFIG_ENTRY: "to_replace",
|
||||||
|
@ -40,45 +39,32 @@ TEST_SERVICE_DATA_USE_DEFAULTS = {
|
||||||
async def test_service_call(
|
async def test_service_call(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
load_int: MockConfigEntry,
|
load_int: MockConfigEntry,
|
||||||
get_data: DeliveryPeriodData,
|
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test get_prices_for_date service call."""
|
"""Test get_prices_for_date service call."""
|
||||||
|
|
||||||
with (
|
service_data = TEST_SERVICE_DATA.copy()
|
||||||
patch(
|
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
response = await hass.services.async_call(
|
||||||
return_value=get_data,
|
DOMAIN,
|
||||||
),
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
):
|
service_data,
|
||||||
service_data = TEST_SERVICE_DATA.copy()
|
blocking=True,
|
||||||
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
return_response=True,
|
||||||
response = await hass.services.async_call(
|
)
|
||||||
DOMAIN,
|
|
||||||
SERVICE_GET_PRICES_FOR_DATE,
|
|
||||||
service_data,
|
|
||||||
blocking=True,
|
|
||||||
return_response=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response == snapshot
|
assert response == snapshot
|
||||||
price_value = response["SE3"][0]["price"]
|
price_value = response["SE3"][0]["price"]
|
||||||
|
|
||||||
with (
|
service_data = TEST_SERVICE_DATA_USE_DEFAULTS.copy()
|
||||||
patch(
|
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
response = await hass.services.async_call(
|
||||||
return_value=get_data,
|
DOMAIN,
|
||||||
),
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
):
|
service_data,
|
||||||
service_data = TEST_SERVICE_DATA_USE_DEFAULTS.copy()
|
blocking=True,
|
||||||
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
return_response=True,
|
||||||
response = await hass.services.async_call(
|
)
|
||||||
DOMAIN,
|
|
||||||
SERVICE_GET_PRICES_FOR_DATE,
|
|
||||||
service_data,
|
|
||||||
blocking=True,
|
|
||||||
return_response=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert "SE3" in response
|
assert "SE3" in response
|
||||||
assert response["SE3"][0]["price"] == price_value
|
assert response["SE3"][0]["price"] == price_value
|
||||||
|
@ -124,17 +110,10 @@ async def test_service_call_failures(
|
||||||
async def test_service_call_config_entry_bad_state(
|
async def test_service_call_config_entry_bad_state(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
load_int: MockConfigEntry,
|
load_int: MockConfigEntry,
|
||||||
get_data: DeliveryPeriodData,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test get_prices_for_date service call when config entry bad state."""
|
"""Test get_prices_for_date service call when config entry bad state."""
|
||||||
|
|
||||||
with (
|
with pytest.raises(ServiceValidationError) as err:
|
||||||
patch(
|
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
|
||||||
return_value=get_data,
|
|
||||||
),
|
|
||||||
pytest.raises(ServiceValidationError) as err,
|
|
||||||
):
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_GET_PRICES_FOR_DATE,
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
@ -149,13 +128,7 @@ async def test_service_call_config_entry_bad_state(
|
||||||
await hass.config_entries.async_unload(load_int.entry_id)
|
await hass.config_entries.async_unload(load_int.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
with (
|
with pytest.raises(ServiceValidationError) as err:
|
||||||
patch(
|
|
||||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
|
||||||
return_value=get_data,
|
|
||||||
),
|
|
||||||
pytest.raises(ServiceValidationError) as err,
|
|
||||||
):
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_GET_PRICES_FOR_DATE,
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
|
Loading…
Reference in New Issue