Cleanup devices in Nord Pool from reconfiguration (#134043)

* Cleanup devices in Nord Pool from reconfiguration

* Mods

* Mod
This commit is contained in:
G Johansson 2024-12-27 21:33:37 +01:00 committed by GitHub
parent 24ce3d7daa
commit d676169b04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 366 additions and 10 deletions

View File

@ -5,11 +5,11 @@ from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import dt as dt_util
from .const import DOMAIN, PLATFORMS
from .const import CONF_AREAS, DOMAIN, LOGGER, PLATFORMS
from .coordinator import NordPoolDataUpdateCoordinator
from .services import async_setup_services
@ -25,10 +25,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
return True
async def async_setup_entry(hass: HomeAssistant, entry: NordPoolConfigEntry) -> bool:
async def async_setup_entry(
hass: HomeAssistant, config_entry: NordPoolConfigEntry
) -> bool:
"""Set up Nord Pool from a config entry."""
coordinator = NordPoolDataUpdateCoordinator(hass, entry)
await cleanup_device(hass, config_entry)
coordinator = NordPoolDataUpdateCoordinator(hass, config_entry)
await coordinator.fetch_data(dt_util.utcnow())
if not coordinator.last_update_success:
raise ConfigEntryNotReady(
@ -36,13 +40,33 @@ async def async_setup_entry(hass: HomeAssistant, entry: NordPoolConfigEntry) ->
translation_key="initial_update_failed",
translation_placeholders={"error": str(coordinator.last_exception)},
)
entry.runtime_data = coordinator
config_entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: NordPoolConfigEntry) -> bool:
async def async_unload_entry(
hass: HomeAssistant, config_entry: NordPoolConfigEntry
) -> bool:
"""Unload Nord Pool config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
async def cleanup_device(
hass: HomeAssistant, config_entry: NordPoolConfigEntry
) -> None:
"""Cleanup device and entities."""
device_reg = dr.async_get(hass)
entries = dr.async_entries_for_config_entry(device_reg, config_entry.entry_id)
for area in config_entry.data[CONF_AREAS]:
for entry in entries:
if entry.identifiers == {(DOMAIN, area)}:
continue
LOGGER.debug("Removing device %s", entry.name)
device_reg.async_update_device(
entry.id, remove_config_entry_id=config_entry.entry_id
)

View File

@ -0,0 +1,229 @@
{
"deliveryDateCET": "2024-11-05",
"version": 2,
"updatedAt": "2024-11-04T11:58:10.7711584Z",
"deliveryAreas": ["NL"],
"market": "DayAhead",
"multiAreaEntries": [
{
"deliveryStart": "2024-11-04T23:00:00Z",
"deliveryEnd": "2024-11-05T00:00:00Z",
"entryPerArea": {
"NL": 83.63
}
},
{
"deliveryStart": "2024-11-05T00:00:00Z",
"deliveryEnd": "2024-11-05T01:00:00Z",
"entryPerArea": {
"NL": 94.0
}
},
{
"deliveryStart": "2024-11-05T01:00:00Z",
"deliveryEnd": "2024-11-05T02:00:00Z",
"entryPerArea": {
"NL": 90.68
}
},
{
"deliveryStart": "2024-11-05T02:00:00Z",
"deliveryEnd": "2024-11-05T03:00:00Z",
"entryPerArea": {
"NL": 91.3
}
},
{
"deliveryStart": "2024-11-05T03:00:00Z",
"deliveryEnd": "2024-11-05T04:00:00Z",
"entryPerArea": {
"NL": 94.0
}
},
{
"deliveryStart": "2024-11-05T04:00:00Z",
"deliveryEnd": "2024-11-05T05:00:00Z",
"entryPerArea": {
"NL": 96.09
}
},
{
"deliveryStart": "2024-11-05T05:00:00Z",
"deliveryEnd": "2024-11-05T06:00:00Z",
"entryPerArea": {
"NL": 106.0
}
},
{
"deliveryStart": "2024-11-05T06:00:00Z",
"deliveryEnd": "2024-11-05T07:00:00Z",
"entryPerArea": {
"NL": 135.99
}
},
{
"deliveryStart": "2024-11-05T07:00:00Z",
"deliveryEnd": "2024-11-05T08:00:00Z",
"entryPerArea": {
"NL": 136.21
}
},
{
"deliveryStart": "2024-11-05T08:00:00Z",
"deliveryEnd": "2024-11-05T09:00:00Z",
"entryPerArea": {
"NL": 118.23
}
},
{
"deliveryStart": "2024-11-05T09:00:00Z",
"deliveryEnd": "2024-11-05T10:00:00Z",
"entryPerArea": {
"NL": 105.87
}
},
{
"deliveryStart": "2024-11-05T10:00:00Z",
"deliveryEnd": "2024-11-05T11:00:00Z",
"entryPerArea": {
"NL": 95.28
}
},
{
"deliveryStart": "2024-11-05T11:00:00Z",
"deliveryEnd": "2024-11-05T12:00:00Z",
"entryPerArea": {
"NL": 94.92
}
},
{
"deliveryStart": "2024-11-05T12:00:00Z",
"deliveryEnd": "2024-11-05T13:00:00Z",
"entryPerArea": {
"NL": 99.25
}
},
{
"deliveryStart": "2024-11-05T13:00:00Z",
"deliveryEnd": "2024-11-05T14:00:00Z",
"entryPerArea": {
"NL": 107.98
}
},
{
"deliveryStart": "2024-11-05T14:00:00Z",
"deliveryEnd": "2024-11-05T15:00:00Z",
"entryPerArea": {
"NL": 149.86
}
},
{
"deliveryStart": "2024-11-05T15:00:00Z",
"deliveryEnd": "2024-11-05T16:00:00Z",
"entryPerArea": {
"NL": 303.24
}
},
{
"deliveryStart": "2024-11-05T16:00:00Z",
"deliveryEnd": "2024-11-05T17:00:00Z",
"entryPerArea": {
"NL": 472.99
}
},
{
"deliveryStart": "2024-11-05T17:00:00Z",
"deliveryEnd": "2024-11-05T18:00:00Z",
"entryPerArea": {
"NL": 431.02
}
},
{
"deliveryStart": "2024-11-05T18:00:00Z",
"deliveryEnd": "2024-11-05T19:00:00Z",
"entryPerArea": {
"NL": 320.33
}
},
{
"deliveryStart": "2024-11-05T19:00:00Z",
"deliveryEnd": "2024-11-05T20:00:00Z",
"entryPerArea": {
"NL": 169.7
}
},
{
"deliveryStart": "2024-11-05T20:00:00Z",
"deliveryEnd": "2024-11-05T21:00:00Z",
"entryPerArea": {
"NL": 129.9
}
},
{
"deliveryStart": "2024-11-05T21:00:00Z",
"deliveryEnd": "2024-11-05T22:00:00Z",
"entryPerArea": {
"NL": 117.77
}
},
{
"deliveryStart": "2024-11-05T22:00:00Z",
"deliveryEnd": "2024-11-05T23:00:00Z",
"entryPerArea": {
"NL": 110.03
}
}
],
"blockPriceAggregates": [
{
"blockName": "Off-peak 1",
"deliveryStart": "2024-11-04T23:00:00Z",
"deliveryEnd": "2024-11-05T07:00:00Z",
"averagePricePerArea": {
"NL": {
"average": 98.96,
"min": 83.63,
"max": 135.99
}
}
},
{
"blockName": "Peak",
"deliveryStart": "2024-11-05T07:00:00Z",
"deliveryEnd": "2024-11-05T19:00:00Z",
"averagePricePerArea": {
"NL": {
"average": 202.93,
"min": 94.92,
"max": 472.99
}
}
},
{
"blockName": "Off-peak 2",
"deliveryStart": "2024-11-05T19:00:00Z",
"deliveryEnd": "2024-11-05T23:00:00Z",
"averagePricePerArea": {
"NL": {
"average": 131.85,
"min": 110.03,
"max": 169.7
}
}
}
],
"currency": "EUR",
"exchangeRate": 1,
"areaStates": [
{
"state": "Final",
"areas": ["NL"]
}
],
"areaAverages": [
{
"areaCode": "NL",
"price": 156.43
}
]
}

View File

@ -2,9 +2,11 @@
from __future__ import annotations
import json
from unittest.mock import patch
from pynordpool import (
API,
NordPoolClient,
NordPoolConnectionError,
NordPoolEmptyResponseError,
@ -13,13 +15,17 @@ from pynordpool import (
)
import pytest
from homeassistant.components.nordpool.const import DOMAIN
from homeassistant.components.nordpool.const import CONF_AREAS, DOMAIN
from homeassistant.config_entries import SOURCE_USER, ConfigEntryState
from homeassistant.const import CONF_CURRENCY
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import device_registry as dr, entity_registry as er
from . import ENTRY_CONFIG
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
@pytest.mark.freeze_time("2024-11-05T10:00:00+00:00")
@ -71,3 +77,100 @@ async def test_initial_startup_fails(
await hass.async_block_till_done(wait_background_tasks=True)
assert entry.state is ConfigEntryState.SETUP_RETRY
@pytest.mark.freeze_time("2024-11-05T10:00:00+00:00")
async def test_reconfigure_cleans_up_device(
hass: HomeAssistant,
aioclient_mock: AiohttpClientMocker,
get_client: NordPoolClient,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test clean up devices due to reconfiguration."""
nl_json_file = load_fixture("delivery_period_nl.json", DOMAIN)
load_nl_json = json.loads(nl_json_file)
entry = MockConfigEntry(
domain=DOMAIN,
source=SOURCE_USER,
data=ENTRY_CONFIG,
)
entry.add_to_hass(hass)
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
assert device_registry.async_get_device(identifiers={(DOMAIN, "SE3")})
assert device_registry.async_get_device(identifiers={(DOMAIN, "SE4")})
assert entity_registry.async_get("sensor.nord_pool_se3_current_price")
assert entity_registry.async_get("sensor.nord_pool_se4_current_price")
assert hass.states.get("sensor.nord_pool_se3_current_price")
assert hass.states.get("sensor.nord_pool_se4_current_price")
aioclient_mock.clear_requests()
aioclient_mock.request(
"GET",
url=API + "/DayAheadPrices",
params={
"date": "2024-11-04",
"market": "DayAhead",
"deliveryArea": "NL",
"currency": "EUR",
},
json=load_nl_json,
)
aioclient_mock.request(
"GET",
url=API + "/DayAheadPrices",
params={
"date": "2024-11-05",
"market": "DayAhead",
"deliveryArea": "NL",
"currency": "EUR",
},
json=load_nl_json,
)
aioclient_mock.request(
"GET",
url=API + "/DayAheadPrices",
params={
"date": "2024-11-06",
"market": "DayAhead",
"deliveryArea": "NL",
"currency": "EUR",
},
json=load_nl_json,
)
result = await entry.start_reconfigure_flow(hass)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_AREAS: ["NL"],
CONF_CURRENCY: "EUR",
},
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
assert entry.data == {
"areas": [
"NL",
],
"currency": "EUR",
}
await hass.async_block_till_done(wait_background_tasks=True)
assert device_registry.async_get_device(identifiers={(DOMAIN, "NL")})
assert entity_registry.async_get("sensor.nord_pool_nl_current_price")
assert hass.states.get("sensor.nord_pool_nl_current_price")
assert not device_registry.async_get_device(identifiers={(DOMAIN, "SE3")})
assert not entity_registry.async_get("sensor.nord_pool_se3_current_price")
assert not hass.states.get("sensor.nord_pool_se3_current_price")
assert not device_registry.async_get_device(identifiers={(DOMAIN, "SE4")})
assert not entity_registry.async_get("sensor.nord_pool_se4_current_price")
assert not hass.states.get("sensor.nord_pool_se4_current_price")