mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Fix Nord Pool empty response (#134033)
* Fix Nord Pool empty response * Mods * reset validate prices
This commit is contained in:
parent
1957ab1ccf
commit
c11bdcc949
@ -2,7 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING
|
||||
@ -73,7 +72,7 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodsData]):
|
||||
self.hass, self.fetch_data, self.get_next_interval(dt_util.utcnow())
|
||||
)
|
||||
data = await self.api_call()
|
||||
if data:
|
||||
if data and data.entries:
|
||||
self.async_set_updated_data(data)
|
||||
|
||||
async def api_call(self, retry: int = 3) -> DeliveryPeriodsData | None:
|
||||
@ -90,18 +89,20 @@ class NordPoolDataUpdateCoordinator(DataUpdateCoordinator[DeliveryPeriodsData]):
|
||||
self.config_entry.data[CONF_AREAS],
|
||||
)
|
||||
except (
|
||||
NordPoolEmptyResponseError,
|
||||
NordPoolResponseError,
|
||||
NordPoolError,
|
||||
) as error:
|
||||
LOGGER.debug("Connection error: %s", error)
|
||||
if retry > 0:
|
||||
next_run = (4 - retry) * 15
|
||||
LOGGER.debug("Wait %d seconds for next try", next_run)
|
||||
await asyncio.sleep(next_run)
|
||||
return await self.api_call(retry - 1)
|
||||
self.async_set_update_error(error)
|
||||
|
||||
if data:
|
||||
current_day = dt_util.utcnow().strftime("%Y-%m-%d")
|
||||
for entry in data.entries:
|
||||
if entry.requested_date == current_day:
|
||||
LOGGER.debug("Data for current day found")
|
||||
return data
|
||||
|
||||
self.async_set_update_error(NordPoolEmptyResponseError("No current day data"))
|
||||
return data
|
||||
|
||||
def merge_price_entries(self) -> list[DeliveryPeriodEntry]:
|
||||
|
@ -8,6 +8,6 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pynordpool"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pynordpool==0.2.3"],
|
||||
"requirements": ["pynordpool==0.2.4"],
|
||||
"single_config_entry": true
|
||||
}
|
||||
|
@ -2118,7 +2118,7 @@ pynetio==0.1.9.1
|
||||
pynobo==1.8.1
|
||||
|
||||
# homeassistant.components.nordpool
|
||||
pynordpool==0.2.3
|
||||
pynordpool==0.2.4
|
||||
|
||||
# homeassistant.components.nuki
|
||||
pynuki==1.6.3
|
||||
|
@ -1720,7 +1720,7 @@ pynetgear==0.10.10
|
||||
pynobo==1.8.1
|
||||
|
||||
# homeassistant.components.nordpool
|
||||
pynordpool==0.2.3
|
||||
pynordpool==0.2.4
|
||||
|
||||
# homeassistant.components.nuki
|
||||
pynuki==1.6.3
|
||||
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
||||
from collections.abc import AsyncGenerator
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from pynordpool import API, NordPoolClient
|
||||
import pytest
|
||||
@ -20,13 +19,6 @@ from tests.common import MockConfigEntry, load_fixture
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def no_sleep() -> AsyncGenerator[None]:
|
||||
"""No sleeping."""
|
||||
with patch("homeassistant.components.nordpool.coordinator.asyncio.sleep"):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def load_int(hass: HomeAssistant, get_client: NordPoolClient) -> MockConfigEntry:
|
||||
"""Set up the Nord Pool integration in Home Assistant."""
|
||||
|
@ -55,7 +55,7 @@ async def test_coordinator(
|
||||
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
|
||||
assert mock_data.call_count == 1
|
||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
@ -69,7 +69,7 @@ async def test_coordinator(
|
||||
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
|
||||
assert mock_data.call_count == 1
|
||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
assert "Authentication error" in caplog.text
|
||||
@ -84,7 +84,8 @@ async def test_coordinator(
|
||||
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
|
||||
# Empty responses does not raise
|
||||
assert mock_data.call_count == 3
|
||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
assert "Empty response" in caplog.text
|
||||
@ -99,7 +100,7 @@ async def test_coordinator(
|
||||
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
|
||||
assert mock_data.call_count == 1
|
||||
state = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
assert "Response error" in caplog.text
|
||||
|
@ -2,14 +2,22 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from http import HTTPStatus
|
||||
from typing import Any
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pynordpool import API
|
||||
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
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
|
||||
|
||||
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
|
||||
@ -59,3 +67,132 @@ async def test_sensor_no_previous_price(
|
||||
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-05T11:00:01+01:00")
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_sensor_empty_response(
|
||||
hass: HomeAssistant,
|
||||
load_int: ConfigEntry,
|
||||
load_json: list[dict[str, Any]],
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test the Nord Pool sensor with empty response."""
|
||||
|
||||
responses = list(load_json)
|
||||
|
||||
current_price = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
last_price = hass.states.get("sensor.nord_pool_se3_previous_price")
|
||||
next_price = hass.states.get("sensor.nord_pool_se3_next_price")
|
||||
assert current_price is not None
|
||||
assert last_price is not None
|
||||
assert next_price is not None
|
||||
assert current_price.state == "0.92737"
|
||||
assert last_price.state == "1.03132"
|
||||
assert next_price.state == "0.92505"
|
||||
|
||||
aioclient_mock.clear_requests()
|
||||
aioclient_mock.request(
|
||||
"GET",
|
||||
url=API + "/DayAheadPrices",
|
||||
params={
|
||||
"date": "2024-11-04",
|
||||
"market": "DayAhead",
|
||||
"deliveryArea": "SE3,SE4",
|
||||
"currency": "SEK",
|
||||
},
|
||||
json=responses[1],
|
||||
)
|
||||
aioclient_mock.request(
|
||||
"GET",
|
||||
url=API + "/DayAheadPrices",
|
||||
params={
|
||||
"date": "2024-11-05",
|
||||
"market": "DayAhead",
|
||||
"deliveryArea": "SE3,SE4",
|
||||
"currency": "SEK",
|
||||
},
|
||||
json=responses[0],
|
||||
)
|
||||
# Future date without data should return 204
|
||||
aioclient_mock.request(
|
||||
"GET",
|
||||
url=API + "/DayAheadPrices",
|
||||
params={
|
||||
"date": "2024-11-06",
|
||||
"market": "DayAhead",
|
||||
"deliveryArea": "SE3,SE4",
|
||||
"currency": "SEK",
|
||||
},
|
||||
status=HTTPStatus.NO_CONTENT,
|
||||
)
|
||||
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
# All prices should be known as tomorrow is not loaded by sensors
|
||||
|
||||
current_price = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
last_price = hass.states.get("sensor.nord_pool_se3_previous_price")
|
||||
next_price = hass.states.get("sensor.nord_pool_se3_next_price")
|
||||
assert current_price is not None
|
||||
assert last_price is not None
|
||||
assert next_price is not None
|
||||
assert current_price.state == "0.92505"
|
||||
assert last_price.state == "0.92737"
|
||||
assert next_price.state == "0.94949"
|
||||
|
||||
aioclient_mock.clear_requests()
|
||||
aioclient_mock.request(
|
||||
"GET",
|
||||
url=API + "/DayAheadPrices",
|
||||
params={
|
||||
"date": "2024-11-04",
|
||||
"market": "DayAhead",
|
||||
"deliveryArea": "SE3,SE4",
|
||||
"currency": "SEK",
|
||||
},
|
||||
json=responses[1],
|
||||
)
|
||||
aioclient_mock.request(
|
||||
"GET",
|
||||
url=API + "/DayAheadPrices",
|
||||
params={
|
||||
"date": "2024-11-05",
|
||||
"market": "DayAhead",
|
||||
"deliveryArea": "SE3,SE4",
|
||||
"currency": "SEK",
|
||||
},
|
||||
json=responses[0],
|
||||
)
|
||||
# Future date without data should return 204
|
||||
aioclient_mock.request(
|
||||
"GET",
|
||||
url=API + "/DayAheadPrices",
|
||||
params={
|
||||
"date": "2024-11-06",
|
||||
"market": "DayAhead",
|
||||
"deliveryArea": "SE3,SE4",
|
||||
"currency": "SEK",
|
||||
},
|
||||
status=HTTPStatus.NO_CONTENT,
|
||||
)
|
||||
|
||||
freezer.move_to("2024-11-05T22:00:01+00:00")
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
# Current and last price should be known, next price should be unknown
|
||||
# as api responds with empty data (204)
|
||||
|
||||
current_price = hass.states.get("sensor.nord_pool_se3_current_price")
|
||||
last_price = hass.states.get("sensor.nord_pool_se3_previous_price")
|
||||
next_price = hass.states.get("sensor.nord_pool_se3_next_price")
|
||||
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.5223"
|
||||
assert next_price.state == STATE_UNKNOWN
|
||||
|
Loading…
x
Reference in New Issue
Block a user