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 (
|
||||
Currency,
|
||||
DeliveryPeriodData,
|
||||
DeliveryPeriodEntry,
|
||||
DeliveryPeriodsData,
|
||||
NordPoolClient,
|
||||
NordPoolEmptyResponseError,
|
||||
NordPoolError,
|
||||
|
@ -29,7 +31,7 @@ if TYPE_CHECKING:
|
|||
from . import NordPoolConfigEntry
|
||||
|
||||
|
||||
class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
|
||||
class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodsData]):
|
||||
"""A Nord Pool Data Update Coordinator."""
|
||||
|
||||
config_entry: NordPoolConfigEntry
|
||||
|
@ -74,12 +76,16 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
|
|||
if 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."""
|
||||
data = None
|
||||
try:
|
||||
data = await self.client.async_get_delivery_period(
|
||||
dt_util.now(),
|
||||
data = await self.client.async_get_delivery_periods(
|
||||
[
|
||||
dt_util.now() - timedelta(days=1),
|
||||
dt_util.now(),
|
||||
dt_util.now() + timedelta(days=1),
|
||||
],
|
||||
Currency(self.config_entry.data[CONF_CURRENCY]),
|
||||
self.config_entry.data[CONF_AREAS],
|
||||
)
|
||||
|
@ -97,3 +103,20 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodData]):
|
|||
self.async_set_update_error(error)
|
||||
|
||||
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 datetime import datetime, timedelta
|
||||
|
||||
from pynordpool import DeliveryPeriodData
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
EntityCategory,
|
||||
SensorDeviceClass,
|
||||
|
@ -29,34 +27,34 @@ PARALLEL_UPDATES = 0
|
|||
|
||||
def validate_prices(
|
||||
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,
|
||||
index: int,
|
||||
) -> float | None:
|
||||
"""Validate and return."""
|
||||
if result := func(data)[area][index]:
|
||||
if result := func(entity)[area][index]:
|
||||
return result / 1000
|
||||
return None
|
||||
|
||||
|
||||
def get_prices(
|
||||
data: DeliveryPeriodData,
|
||||
entity: NordpoolPriceSensor,
|
||||
) -> dict[str, tuple[float | None, float, float | None]]:
|
||||
"""Return previous, current and next prices.
|
||||
|
||||
Output: {"SE3": (10.0, 10.5, 12.1)}
|
||||
"""
|
||||
data = entity.coordinator.merge_price_entries()
|
||||
last_price_entries: dict[str, float] = {}
|
||||
current_price_entries: dict[str, float] = {}
|
||||
next_price_entries: dict[str, float] = {}
|
||||
current_time = dt_util.utcnow()
|
||||
previous_time = current_time - timedelta(hours=1)
|
||||
next_time = current_time + timedelta(hours=1)
|
||||
price_data = data.entries
|
||||
LOGGER.debug("Price data: %s", price_data)
|
||||
for entry in price_data:
|
||||
LOGGER.debug("Price data: %s", data)
|
||||
for entry in data:
|
||||
if entry.start <= current_time <= entry.end:
|
||||
current_price_entries = entry.entry
|
||||
if entry.start <= previous_time <= entry.end:
|
||||
|
@ -82,11 +80,12 @@ def get_prices(
|
|||
|
||||
|
||||
def get_min_max_price(
|
||||
data: DeliveryPeriodData,
|
||||
area: str,
|
||||
entity: NordpoolPriceSensor,
|
||||
func: Callable[[float, float], float],
|
||||
) -> tuple[float, datetime, datetime]:
|
||||
"""Get the lowest price from the data."""
|
||||
data = entity.coordinator.get_data_current_day()
|
||||
area = entity.area
|
||||
price_data = data.entries
|
||||
price: float = price_data[0].entry[area]
|
||||
start: datetime = price_data[0].start
|
||||
|
@ -102,12 +101,13 @@ def get_min_max_price(
|
|||
|
||||
|
||||
def get_blockprices(
|
||||
data: DeliveryPeriodData,
|
||||
entity: NordpoolBlockPriceSensor,
|
||||
) -> dict[str, dict[str, tuple[datetime, datetime, float, float, float]]]:
|
||||
"""Return average, min and max for block prices.
|
||||
|
||||
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]]] = {}
|
||||
block_prices = data.block_prices
|
||||
for entry in block_prices:
|
||||
|
@ -130,15 +130,15 @@ def get_blockprices(
|
|||
class NordpoolDefaultSensorEntityDescription(SensorEntityDescription):
|
||||
"""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)
|
||||
class NordpoolPricesSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes Nord Pool prices sensor entity."""
|
||||
|
||||
value_fn: Callable[[DeliveryPeriodData, str], float | None]
|
||||
extra_fn: Callable[[DeliveryPeriodData, str], dict[str, str] | None]
|
||||
value_fn: Callable[[NordpoolPriceSensor], float | None]
|
||||
extra_fn: Callable[[NordpoolPriceSensor], dict[str, str] | None]
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
|
@ -155,19 +155,19 @@ DEFAULT_SENSOR_TYPES: tuple[NordpoolDefaultSensorEntityDescription, ...] = (
|
|||
key="updated_at",
|
||||
translation_key="updated_at",
|
||||
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,
|
||||
),
|
||||
NordpoolDefaultSensorEntityDescription(
|
||||
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,
|
||||
),
|
||||
NordpoolDefaultSensorEntityDescription(
|
||||
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,
|
||||
entity_registry_enabled_default=False,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
|
@ -177,42 +177,42 @@ PRICES_SENSOR_TYPES: tuple[NordpoolPricesSensorEntityDescription, ...] = (
|
|||
NordpoolPricesSensorEntityDescription(
|
||||
key="current_price",
|
||||
translation_key="current_price",
|
||||
value_fn=lambda data, area: validate_prices(get_prices, data, area, 1),
|
||||
extra_fn=lambda data, area: None,
|
||||
value_fn=lambda entity: validate_prices(get_prices, entity, entity.area, 1),
|
||||
extra_fn=lambda entity: None,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=2,
|
||||
),
|
||||
NordpoolPricesSensorEntityDescription(
|
||||
key="last_price",
|
||||
translation_key="last_price",
|
||||
value_fn=lambda data, area: validate_prices(get_prices, data, area, 0),
|
||||
extra_fn=lambda data, area: None,
|
||||
value_fn=lambda entity: validate_prices(get_prices, entity, entity.area, 0),
|
||||
extra_fn=lambda entity: None,
|
||||
suggested_display_precision=2,
|
||||
),
|
||||
NordpoolPricesSensorEntityDescription(
|
||||
key="next_price",
|
||||
translation_key="next_price",
|
||||
value_fn=lambda data, area: validate_prices(get_prices, data, area, 2),
|
||||
extra_fn=lambda data, area: None,
|
||||
value_fn=lambda entity: validate_prices(get_prices, entity, entity.area, 2),
|
||||
extra_fn=lambda entity: None,
|
||||
suggested_display_precision=2,
|
||||
),
|
||||
NordpoolPricesSensorEntityDescription(
|
||||
key="lowest_price",
|
||||
translation_key="lowest_price",
|
||||
value_fn=lambda data, area: get_min_max_price(data, area, min)[0] / 1000,
|
||||
extra_fn=lambda data, area: {
|
||||
"start": get_min_max_price(data, area, min)[1].isoformat(),
|
||||
"end": get_min_max_price(data, area, min)[2].isoformat(),
|
||||
value_fn=lambda entity: get_min_max_price(entity, min)[0] / 1000,
|
||||
extra_fn=lambda entity: {
|
||||
"start": get_min_max_price(entity, min)[1].isoformat(),
|
||||
"end": get_min_max_price(entity, min)[2].isoformat(),
|
||||
},
|
||||
suggested_display_precision=2,
|
||||
),
|
||||
NordpoolPricesSensorEntityDescription(
|
||||
key="highest_price",
|
||||
translation_key="highest_price",
|
||||
value_fn=lambda data, area: get_min_max_price(data, area, max)[0] / 1000,
|
||||
extra_fn=lambda data, area: {
|
||||
"start": get_min_max_price(data, area, max)[1].isoformat(),
|
||||
"end": get_min_max_price(data, area, max)[2].isoformat(),
|
||||
value_fn=lambda entity: get_min_max_price(entity, max)[0] / 1000,
|
||||
extra_fn=lambda entity: {
|
||||
"start": get_min_max_price(entity, max)[1].isoformat(),
|
||||
"end": get_min_max_price(entity, max)[2].isoformat(),
|
||||
},
|
||||
suggested_display_precision=2,
|
||||
),
|
||||
|
@ -276,11 +276,12 @@ async def async_setup_entry(
|
|||
"""Set up Nord Pool sensor platform."""
|
||||
|
||||
coordinator = entry.runtime_data
|
||||
current_day_data = entry.runtime_data.get_data_current_day()
|
||||
|
||||
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)
|
||||
entities.extend(
|
||||
NordpoolSensor(coordinator, description, area)
|
||||
|
@ -297,16 +298,16 @@ async def async_setup_entry(
|
|||
NordpoolDailyAveragePriceSensor(coordinator, description, area, currency)
|
||||
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(
|
||||
"Setting up block price sensors for area %s with currency %s in block %s",
|
||||
area,
|
||||
currency,
|
||||
block_name,
|
||||
block_prices.name,
|
||||
)
|
||||
entities.extend(
|
||||
NordpoolBlockPriceSensor(
|
||||
coordinator, description, area, currency, block_name
|
||||
coordinator, description, area, currency, block_prices.name
|
||||
)
|
||||
for description in BLOCK_PRICES_SENSOR_TYPES
|
||||
)
|
||||
|
@ -321,7 +322,7 @@ class NordpoolSensor(NordpoolBaseEntity, SensorEntity):
|
|||
@property
|
||||
def native_value(self) -> str | float | datetime | None:
|
||||
"""Return value of sensor."""
|
||||
return self.entity_description.value_fn(self.coordinator.data)
|
||||
return self.entity_description.value_fn(self)
|
||||
|
||||
|
||||
class NordpoolPriceSensor(NordpoolBaseEntity, SensorEntity):
|
||||
|
@ -343,12 +344,12 @@ class NordpoolPriceSensor(NordpoolBaseEntity, SensorEntity):
|
|||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return value of sensor."""
|
||||
return self.entity_description.value_fn(self.coordinator.data, self.area)
|
||||
return self.entity_description.value_fn(self)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, str] | None:
|
||||
"""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):
|
||||
|
@ -376,7 +377,7 @@ class NordpoolBlockPriceSensor(NordpoolBaseEntity, SensorEntity):
|
|||
def native_value(self) -> float | datetime | None:
|
||||
"""Return value of sensor."""
|
||||
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
|
||||
def native_value(self) -> float | None:
|
||||
"""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 collections.abc import AsyncGenerator
|
||||
from datetime import datetime
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from pynordpool import NordPoolClient
|
||||
from pynordpool.const import Currency
|
||||
from pynordpool.model import DeliveryPeriodData
|
||||
from pynordpool import API, NordPoolClient
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.nordpool.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import ENTRY_CONFIG
|
||||
|
||||
|
@ -32,9 +28,7 @@ async def no_sleep() -> AsyncGenerator[None]:
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
async def load_int(
|
||||
hass: HomeAssistant, get_data: DeliveryPeriodData
|
||||
) -> MockConfigEntry:
|
||||
async def load_int(hass: HomeAssistant, get_client: NordPoolClient) -> MockConfigEntry:
|
||||
"""Set up the Nord Pool integration in Home Assistant."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -44,40 +38,83 @@ async def load_int(
|
|||
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with (
|
||||
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.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return config_entry
|
||||
|
||||
|
||||
@pytest.fixture(name="get_data")
|
||||
@pytest.fixture(name="get_client")
|
||||
async def get_data_from_library(
|
||||
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, load_json: dict[str, Any]
|
||||
) -> DeliveryPeriodData:
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
load_json: list[dict[str, Any]],
|
||||
) -> AsyncGenerator[NordPoolClient]:
|
||||
"""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))
|
||||
with patch("pynordpool.NordPoolClient._get", return_value=load_json):
|
||||
output = await client.async_get_delivery_period(
|
||||
datetime(2024, 11, 5, 13, tzinfo=dt_util.UTC), Currency.SEK, ["SE3", "SE4"]
|
||||
)
|
||||
yield client
|
||||
await client._session.close()
|
||||
return output
|
||||
|
||||
|
||||
@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."""
|
||||
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")
|
||||
def load_data_from_fixture() -> str:
|
||||
def load_data_from_fixture() -> list[str, str, str]:
|
||||
"""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 typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from pynordpool import (
|
||||
DeliveryPeriodData,
|
||||
NordPoolClient,
|
||||
NordPoolConnectionError,
|
||||
NordPoolEmptyResponseError,
|
||||
NordPoolError,
|
||||
|
@ -22,10 +23,11 @@ from homeassistant.data_entry_flow import FlowResultType
|
|||
from . import ENTRY_CONFIG
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
@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."""
|
||||
|
||||
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["type"] is FlowResultType.FORM
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
return_value=get_data,
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
ENTRY_CONFIG,
|
||||
)
|
||||
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["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")
|
||||
async def test_single_config_entry(
|
||||
hass: HomeAssistant, load_int: None, get_data: DeliveryPeriodData
|
||||
hass: HomeAssistant, load_int: None, get_client: NordPoolClient
|
||||
) -> None:
|
||||
"""Test abort for single config entry."""
|
||||
|
||||
|
@ -77,7 +73,7 @@ async def test_single_config_entry(
|
|||
)
|
||||
async def test_cannot_connect(
|
||||
hass: HomeAssistant,
|
||||
get_data: DeliveryPeriodData,
|
||||
get_client: NordPoolClient,
|
||||
error_message: Exception,
|
||||
p_error: str,
|
||||
) -> None:
|
||||
|
@ -101,14 +97,10 @@ async def test_cannot_connect(
|
|||
|
||||
assert result["errors"] == {"base": p_error}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
return_value=get_data,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
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["title"] == "Nord Pool"
|
||||
|
@ -119,25 +111,18 @@ async def test_cannot_connect(
|
|||
async def test_reconfigure(
|
||||
hass: HomeAssistant,
|
||||
load_int: MockConfigEntry,
|
||||
get_data: DeliveryPeriodData,
|
||||
) -> None:
|
||||
"""Test reconfiguration."""
|
||||
|
||||
result = await load_int.start_reconfigure_flow(hass)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
return_value=get_data,
|
||||
),
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
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["reason"] == "reconfigure_successful"
|
||||
|
@ -162,7 +147,8 @@ async def test_reconfigure(
|
|||
async def test_reconfigure_cannot_connect(
|
||||
hass: HomeAssistant,
|
||||
load_int: MockConfigEntry,
|
||||
get_data: DeliveryPeriodData,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
load_json: list[dict[str, Any]],
|
||||
error_message: Exception,
|
||||
p_error: str,
|
||||
) -> None:
|
||||
|
@ -184,17 +170,13 @@ async def test_reconfigure_cannot_connect(
|
|||
|
||||
assert result["errors"] == {"base": p_error}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
return_value=get_data,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_AREAS: ["SE3"],
|
||||
CONF_CURRENCY: "EUR",
|
||||
},
|
||||
)
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_AREAS: ["SE3"],
|
||||
CONF_CURRENCY: "EUR",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reconfigure_successful"
|
||||
|
|
|
@ -7,8 +7,8 @@ from unittest.mock import patch
|
|||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pynordpool import (
|
||||
DeliveryPeriodData,
|
||||
NordPoolAuthenticationError,
|
||||
NordPoolClient,
|
||||
NordPoolEmptyResponseError,
|
||||
NordPoolError,
|
||||
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")
|
||||
async def test_coordinator(
|
||||
hass: HomeAssistant,
|
||||
get_data: DeliveryPeriodData,
|
||||
get_client: NordPoolClient,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
|
@ -41,30 +41,31 @@ async def test_coordinator(
|
|||
|
||||
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 (
|
||||
patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
side_effect=NordPoolError("error"),
|
||||
) 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))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
assert mock_data.call_count == 4
|
||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
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
|
||||
mock_data.side_effect = NordPoolAuthenticationError("Authentication error")
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
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")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
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
|
||||
mock_data.side_effect = NordPoolEmptyResponseError("Empty response")
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
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")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
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
|
||||
mock_data.side_effect = NordPoolResponseError("Response error")
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
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")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
assert "Response error" in caplog.text
|
||||
mock_data.reset_mock()
|
||||
|
||||
mock_data.return_value = get_data
|
||||
mock_data.side_effect = None
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
mock_data.assert_called_once()
|
||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
assert state.state == "1.81645"
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
assert state.state == "1.81645"
|
||||
|
|
|
@ -2,19 +2,21 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2024-11-05T10:00:00+00:00")
|
||||
async def test_diagnostics(
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
load_int: ConfigEntry,
|
||||
load_int: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test generating diagnostics for a config entry."""
|
||||
|
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
from unittest.mock import patch
|
||||
|
||||
from pynordpool import (
|
||||
DeliveryPeriodData,
|
||||
NordPoolClient,
|
||||
NordPoolConnectionError,
|
||||
NordPoolEmptyResponseError,
|
||||
NordPoolError,
|
||||
|
@ -22,7 +22,8 @@ from . import ENTRY_CONFIG
|
|||
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."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -31,13 +32,7 @@ async def test_unload_entry(hass: HomeAssistant, get_data: DeliveryPeriodData) -
|
|||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with (
|
||||
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.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
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(
|
||||
hass: HomeAssistant, get_data: DeliveryPeriodData, error: Exception
|
||||
hass: HomeAssistant, get_client: NordPoolClient, error: Exception
|
||||
) -> None:
|
||||
"""Test load and unload an entry."""
|
||||
entry = MockConfigEntry(
|
||||
|
|
|
@ -6,7 +6,6 @@ import pytest
|
|||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
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 last_price is not None
|
||||
assert next_price is not None
|
||||
assert current_price.state == "0.28914"
|
||||
assert last_price.state == "0.28914"
|
||||
assert next_price.state == STATE_UNKNOWN
|
||||
assert current_price.state == "0.12666" # SE3 2024-11-05T23:00:00Z
|
||||
assert last_price.state == "0.28914" # SE3 2024-11-05T22:00:00Z
|
||||
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")
|
||||
async def test_sensor_no_previous_price(
|
||||
hass: HomeAssistant, load_int: ConfigEntry
|
||||
|
@ -57,6 +56,6 @@ async def test_sensor_no_previous_price(
|
|||
assert current_price is not None
|
||||
assert last_price is not None
|
||||
assert next_price is not None
|
||||
assert current_price.state == "0.25073"
|
||||
assert last_price.state == STATE_UNKNOWN
|
||||
assert next_price.state == "0.07636"
|
||||
assert current_price.state == "0.12666" # SE3 2024-11-05T23:00:00Z
|
||||
assert last_price.state == "0.28914" # SE3 2024-11-05T22:00:00Z
|
||||
assert next_price.state == "0.07406" # SE3 2024-11-06T00:00:00Z
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
from pynordpool import (
|
||||
DeliveryPeriodData,
|
||||
NordPoolAuthenticationError,
|
||||
NordPoolEmptyResponseError,
|
||||
NordPoolError,
|
||||
|
@ -28,7 +27,7 @@ TEST_SERVICE_DATA = {
|
|||
ATTR_CONFIG_ENTRY: "to_replace",
|
||||
ATTR_DATE: "2024-11-05",
|
||||
ATTR_AREAS: "SE3",
|
||||
ATTR_CURRENCY: "SEK",
|
||||
ATTR_CURRENCY: "EUR",
|
||||
}
|
||||
TEST_SERVICE_DATA_USE_DEFAULTS = {
|
||||
ATTR_CONFIG_ENTRY: "to_replace",
|
||||
|
@ -40,45 +39,32 @@ TEST_SERVICE_DATA_USE_DEFAULTS = {
|
|||
async def test_service_call(
|
||||
hass: HomeAssistant,
|
||||
load_int: MockConfigEntry,
|
||||
get_data: DeliveryPeriodData,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test get_prices_for_date service call."""
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
return_value=get_data,
|
||||
),
|
||||
):
|
||||
service_data = TEST_SERVICE_DATA.copy()
|
||||
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_PRICES_FOR_DATE,
|
||||
service_data,
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
service_data = TEST_SERVICE_DATA.copy()
|
||||
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_PRICES_FOR_DATE,
|
||||
service_data,
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
assert response == snapshot
|
||||
price_value = response["SE3"][0]["price"]
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
return_value=get_data,
|
||||
),
|
||||
):
|
||||
service_data = TEST_SERVICE_DATA_USE_DEFAULTS.copy()
|
||||
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_PRICES_FOR_DATE,
|
||||
service_data,
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
service_data = TEST_SERVICE_DATA_USE_DEFAULTS.copy()
|
||||
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_PRICES_FOR_DATE,
|
||||
service_data,
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
assert "SE3" in response
|
||||
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(
|
||||
hass: HomeAssistant,
|
||||
load_int: MockConfigEntry,
|
||||
get_data: DeliveryPeriodData,
|
||||
) -> None:
|
||||
"""Test get_prices_for_date service call when config entry bad state."""
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
return_value=get_data,
|
||||
),
|
||||
pytest.raises(ServiceValidationError) as err,
|
||||
):
|
||||
with pytest.raises(ServiceValidationError) as err:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
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.async_block_till_done()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||
return_value=get_data,
|
||||
),
|
||||
pytest.raises(ServiceValidationError) as err,
|
||||
):
|
||||
with pytest.raises(ServiceValidationError) as err:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_PRICES_FOR_DATE,
|
||||
|
|
Loading…
Reference in New Issue