From fce42634937ff298c443440fc32904799b72d176 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Fri, 17 May 2024 16:34:47 +0200 Subject: [PATCH] Move p1_monitor coordinator to separate module (#117562) --- .../components/p1_monitor/__init__.py | 79 +----------------- .../components/p1_monitor/coordinator.py | 83 +++++++++++++++++++ .../components/p1_monitor/diagnostics.py | 2 +- homeassistant/components/p1_monitor/sensor.py | 2 +- tests/components/p1_monitor/conftest.py | 4 +- .../components/p1_monitor/test_config_flow.py | 2 +- tests/components/p1_monitor/test_init.py | 2 +- 7 files changed, 93 insertions(+), 81 deletions(-) create mode 100644 homeassistant/components/p1_monitor/coordinator.py diff --git a/homeassistant/components/p1_monitor/__init__.py b/homeassistant/components/p1_monitor/__init__.py index 201e76d4a76..8125e9f7a55 100644 --- a/homeassistant/components/p1_monitor/__init__.py +++ b/homeassistant/components/p1_monitor/__init__.py @@ -2,34 +2,13 @@ from __future__ import annotations -from typing import TypedDict - -from p1monitor import ( - P1Monitor, - P1MonitorConnectionError, - P1MonitorNoDataError, - Phases, - Settings, - SmartMeter, - WaterMeter, -) - from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_HOST, Platform +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator -from .const import ( - DOMAIN, - LOGGER, - SCAN_INTERVAL, - SERVICE_PHASES, - SERVICE_SETTINGS, - SERVICE_SMARTMETER, - SERVICE_WATERMETER, -) +from .const import DOMAIN +from .coordinator import P1MonitorDataUpdateCoordinator PLATFORMS = [Platform.SENSOR] @@ -57,55 +36,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: if unload_ok: del hass.data[DOMAIN][entry.entry_id] return unload_ok - - -class P1MonitorData(TypedDict): - """Class for defining data in dict.""" - - smartmeter: SmartMeter - phases: Phases - settings: Settings - watermeter: WaterMeter | None - - -class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]): # pylint: disable=hass-enforce-coordinator-module - """Class to manage fetching P1 Monitor data from single endpoint.""" - - config_entry: ConfigEntry - has_water_meter: bool | None = None - - def __init__( - self, - hass: HomeAssistant, - ) -> None: - """Initialize global P1 Monitor data updater.""" - super().__init__( - hass, - LOGGER, - name=DOMAIN, - update_interval=SCAN_INTERVAL, - ) - - self.p1monitor = P1Monitor( - self.config_entry.data[CONF_HOST], session=async_get_clientsession(hass) - ) - - async def _async_update_data(self) -> P1MonitorData: - """Fetch data from P1 Monitor.""" - data: P1MonitorData = { - SERVICE_SMARTMETER: await self.p1monitor.smartmeter(), - SERVICE_PHASES: await self.p1monitor.phases(), - SERVICE_SETTINGS: await self.p1monitor.settings(), - SERVICE_WATERMETER: None, - } - - if self.has_water_meter or self.has_water_meter is None: - try: - data[SERVICE_WATERMETER] = await self.p1monitor.watermeter() - self.has_water_meter = True - except (P1MonitorNoDataError, P1MonitorConnectionError): - LOGGER.debug("No water meter data received from P1 Monitor") - if self.has_water_meter is None: - self.has_water_meter = False - - return data diff --git a/homeassistant/components/p1_monitor/coordinator.py b/homeassistant/components/p1_monitor/coordinator.py new file mode 100644 index 00000000000..49844adf39b --- /dev/null +++ b/homeassistant/components/p1_monitor/coordinator.py @@ -0,0 +1,83 @@ +"""Coordinator for the P1 Monitor integration.""" + +from __future__ import annotations + +from typing import TypedDict + +from p1monitor import ( + P1Monitor, + P1MonitorConnectionError, + P1MonitorNoDataError, + Phases, + Settings, + SmartMeter, + WaterMeter, +) + +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_HOST +from homeassistant.core import HomeAssistant +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator + +from .const import ( + DOMAIN, + LOGGER, + SCAN_INTERVAL, + SERVICE_PHASES, + SERVICE_SETTINGS, + SERVICE_SMARTMETER, + SERVICE_WATERMETER, +) + + +class P1MonitorData(TypedDict): + """Class for defining data in dict.""" + + smartmeter: SmartMeter + phases: Phases + settings: Settings + watermeter: WaterMeter | None + + +class P1MonitorDataUpdateCoordinator(DataUpdateCoordinator[P1MonitorData]): + """Class to manage fetching P1 Monitor data from single endpoint.""" + + config_entry: ConfigEntry + has_water_meter: bool | None = None + + def __init__( + self, + hass: HomeAssistant, + ) -> None: + """Initialize global P1 Monitor data updater.""" + super().__init__( + hass, + LOGGER, + name=DOMAIN, + update_interval=SCAN_INTERVAL, + ) + + self.p1monitor = P1Monitor( + self.config_entry.data[CONF_HOST], session=async_get_clientsession(hass) + ) + + async def _async_update_data(self) -> P1MonitorData: + """Fetch data from P1 Monitor.""" + data: P1MonitorData = { + SERVICE_SMARTMETER: await self.p1monitor.smartmeter(), + SERVICE_PHASES: await self.p1monitor.phases(), + SERVICE_SETTINGS: await self.p1monitor.settings(), + SERVICE_WATERMETER: None, + } + + if self.has_water_meter or self.has_water_meter is None: + try: + data[SERVICE_WATERMETER] = await self.p1monitor.watermeter() + self.has_water_meter = True + except (P1MonitorNoDataError, P1MonitorConnectionError): + LOGGER.debug("No water meter data received from P1 Monitor") + if self.has_water_meter is None: + self.has_water_meter = False + + return data diff --git a/homeassistant/components/p1_monitor/diagnostics.py b/homeassistant/components/p1_monitor/diagnostics.py index b1b3bd2a506..5fb8cb472e8 100644 --- a/homeassistant/components/p1_monitor/diagnostics.py +++ b/homeassistant/components/p1_monitor/diagnostics.py @@ -10,7 +10,6 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST from homeassistant.core import HomeAssistant -from . import P1MonitorDataUpdateCoordinator from .const import ( DOMAIN, SERVICE_PHASES, @@ -18,6 +17,7 @@ from .const import ( SERVICE_SMARTMETER, SERVICE_WATERMETER, ) +from .coordinator import P1MonitorDataUpdateCoordinator if TYPE_CHECKING: from _typeshed import DataclassInstance diff --git a/homeassistant/components/p1_monitor/sensor.py b/homeassistant/components/p1_monitor/sensor.py index b97383bdae5..88f6d165f14 100644 --- a/homeassistant/components/p1_monitor/sensor.py +++ b/homeassistant/components/p1_monitor/sensor.py @@ -26,7 +26,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity -from . import P1MonitorDataUpdateCoordinator from .const import ( DOMAIN, SERVICE_PHASES, @@ -34,6 +33,7 @@ from .const import ( SERVICE_SMARTMETER, SERVICE_WATERMETER, ) +from .coordinator import P1MonitorDataUpdateCoordinator SENSORS_SMARTMETER: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( diff --git a/tests/components/p1_monitor/conftest.py b/tests/components/p1_monitor/conftest.py index e95cb245f5e..1d5f349f858 100644 --- a/tests/components/p1_monitor/conftest.py +++ b/tests/components/p1_monitor/conftest.py @@ -27,7 +27,9 @@ def mock_config_entry() -> MockConfigEntry: @pytest.fixture def mock_p1monitor(): """Return a mocked P1 Monitor client.""" - with patch("homeassistant.components.p1_monitor.P1Monitor") as p1monitor_mock: + with patch( + "homeassistant.components.p1_monitor.coordinator.P1Monitor" + ) as p1monitor_mock: client = p1monitor_mock.return_value client.smartmeter = AsyncMock( return_value=SmartMeter.from_dict( diff --git a/tests/components/p1_monitor/test_config_flow.py b/tests/components/p1_monitor/test_config_flow.py index 6f6c2c8f7ec..12a6a6f5d11 100644 --- a/tests/components/p1_monitor/test_config_flow.py +++ b/tests/components/p1_monitor/test_config_flow.py @@ -44,7 +44,7 @@ async def test_full_user_flow(hass: HomeAssistant) -> None: async def test_api_error(hass: HomeAssistant) -> None: """Test we handle cannot connect error.""" with patch( - "homeassistant.components.p1_monitor.P1Monitor.smartmeter", + "homeassistant.components.p1_monitor.coordinator.P1Monitor.smartmeter", side_effect=P1MonitorError, ): result = await hass.config_entries.flow.async_init( diff --git a/tests/components/p1_monitor/test_init.py b/tests/components/p1_monitor/test_init.py index f8de8767a09..02888b5ae97 100644 --- a/tests/components/p1_monitor/test_init.py +++ b/tests/components/p1_monitor/test_init.py @@ -29,7 +29,7 @@ async def test_load_unload_config_entry( @patch( - "homeassistant.components.p1_monitor.P1Monitor._request", + "homeassistant.components.p1_monitor.coordinator.P1Monitor._request", side_effect=P1MonitorConnectionError, ) async def test_config_entry_not_ready(