Compare commits

...

2 Commits

Author SHA1 Message Date
epenet
dbcc46b4b4 Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-02 08:27:51 +01:00
epenet
af68334d8c Move meteo_france coordinators to separate module 2026-03-02 07:21:51 +00:00
4 changed files with 148 additions and 61 deletions

View File

@@ -1,71 +1,46 @@
"""Support for Meteo-France weather data."""
from datetime import timedelta
import logging
from meteofrance_api.client import MeteoFranceClient
from meteofrance_api.helpers import is_valid_warning_department
from meteofrance_api.model import CurrentPhenomenons, Forecast, Rain
from requests import RequestException
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import (
CONF_CITY,
COORDINATOR_ALERT,
COORDINATOR_FORECAST,
COORDINATOR_RAIN,
DOMAIN,
PLATFORMS,
)
from .coordinator import (
MeteoFranceAlertUpdateCoordinator,
MeteoFranceForecastUpdateCoordinator,
MeteoFranceRainUpdateCoordinator,
)
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL_RAIN = timedelta(minutes=5)
SCAN_INTERVAL = timedelta(minutes=15)
CITY_SCHEMA = vol.Schema({vol.Required(CONF_CITY): cv.string})
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up an Meteo-France account from a config entry."""
"""Set up a Meteo-France account from a config entry."""
hass.data.setdefault(DOMAIN, {})
client = MeteoFranceClient()
latitude = entry.data[CONF_LATITUDE]
longitude = entry.data[CONF_LONGITUDE]
async def _async_update_data_forecast_forecast() -> Forecast:
"""Fetch data from API endpoint."""
return await hass.async_add_executor_job(
client.get_forecast, latitude, longitude
)
async def _async_update_data_rain() -> Rain:
"""Fetch data from API endpoint."""
return await hass.async_add_executor_job(client.get_rain, latitude, longitude)
async def _async_update_data_alert() -> CurrentPhenomenons:
"""Fetch data from API endpoint."""
assert isinstance(department, str)
return await hass.async_add_executor_job(
client.get_warning_current_phenomenons, department, 0, True
)
coordinator_forecast = DataUpdateCoordinator(
coordinator_forecast = MeteoFranceForecastUpdateCoordinator(
hass,
_LOGGER,
name=f"Météo-France forecast for city {entry.title}",
config_entry=entry,
update_method=_async_update_data_forecast_forecast,
update_interval=SCAN_INTERVAL,
entry,
client,
latitude,
longitude,
)
coordinator_rain = None
coordinator_alert = None
@@ -77,13 +52,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
raise ConfigEntryNotReady
# Check rain forecast.
coordinator_rain = DataUpdateCoordinator(
coordinator_rain = MeteoFranceRainUpdateCoordinator(
hass,
_LOGGER,
name=f"Météo-France rain for city {entry.title}",
config_entry=entry,
update_method=_async_update_data_rain,
update_interval=SCAN_INTERVAL_RAIN,
entry,
client,
latitude,
longitude,
)
try:
await coordinator_rain._async_refresh(log_failures=False) # noqa: SLF001
@@ -101,13 +75,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
if department is not None and is_valid_warning_department(department):
if not hass.data[DOMAIN].get(department):
coordinator_alert = DataUpdateCoordinator(
coordinator_alert = MeteoFranceAlertUpdateCoordinator(
hass,
_LOGGER,
name=f"Météo-France alert for department {department}",
config_entry=entry,
update_method=_async_update_data_alert,
update_interval=SCAN_INTERVAL,
entry,
client,
department,
)
await coordinator_alert.async_refresh()

View File

@@ -0,0 +1,110 @@
"""Support for Meteo-France weather data."""
from datetime import timedelta
import logging
from meteofrance_api.client import MeteoFranceClient
from meteofrance_api.model import CurrentPhenomenons, Forecast, Rain
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL_RAIN = timedelta(minutes=5)
SCAN_INTERVAL = timedelta(minutes=15)
class MeteoFranceForecastUpdateCoordinator(DataUpdateCoordinator[Forecast]):
"""Coordinator for Meteo-France forecast data."""
config_entry: ConfigEntry
def __init__(
self,
hass: HomeAssistant,
entry: ConfigEntry,
client: MeteoFranceClient,
latitude: float,
longitude: float,
) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
name=f"Météo-France forecast for city {entry.title}",
config_entry=entry,
update_interval=SCAN_INTERVAL,
)
self._client = client
self._latitude = latitude
self._longitude = longitude
async def _async_update_data(self) -> Forecast:
"""Get data from Meteo-France forecast."""
return await self.hass.async_add_executor_job(
self._client.get_forecast, self._latitude, self._longitude
)
class MeteoFranceRainUpdateCoordinator(DataUpdateCoordinator[Rain]):
"""Coordinator for Meteo-France rain data."""
config_entry: ConfigEntry
def __init__(
self,
hass: HomeAssistant,
entry: ConfigEntry,
client: MeteoFranceClient,
latitude: float,
longitude: float,
) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
name=f"Météo-France rain for city {entry.title}",
config_entry=entry,
update_interval=SCAN_INTERVAL_RAIN,
)
self._client = client
self._latitude = latitude
self._longitude = longitude
async def _async_update_data(self) -> Rain:
"""Get data from Meteo-France rain."""
return await self.hass.async_add_executor_job(
self._client.get_rain, self._latitude, self._longitude
)
class MeteoFranceAlertUpdateCoordinator(DataUpdateCoordinator[CurrentPhenomenons]):
"""Coordinator for Meteo-France alert data."""
config_entry: ConfigEntry
def __init__(
self,
hass: HomeAssistant,
entry: ConfigEntry,
client: MeteoFranceClient,
department: str,
) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
name=f"Météo-France alert for department {department}",
config_entry=entry,
update_interval=SCAN_INTERVAL,
)
self._client = client
self._department = department
async def _async_update_data(self) -> CurrentPhenomenons:
"""Get data from Meteo-France alert."""
return await self.hass.async_add_executor_job(
self._client.get_warning_current_phenomenons, self._department, 0, True
)

View File

@@ -48,6 +48,11 @@ from .const import (
MANUFACTURER,
MODEL,
)
from .coordinator import (
MeteoFranceAlertUpdateCoordinator,
MeteoFranceForecastUpdateCoordinator,
MeteoFranceRainUpdateCoordinator,
)
@dataclass(frozen=True, kw_only=True)
@@ -188,9 +193,13 @@ async def async_setup_entry(
) -> None:
"""Set up the Meteo-France sensor platform."""
data = hass.data[DOMAIN][entry.entry_id]
coordinator_forecast: DataUpdateCoordinator[Forecast] = data[COORDINATOR_FORECAST]
coordinator_rain: DataUpdateCoordinator[Rain] | None = data.get(COORDINATOR_RAIN)
coordinator_alert: DataUpdateCoordinator[CurrentPhenomenons] | None = data.get(
coordinator_forecast: MeteoFranceForecastUpdateCoordinator = data[
COORDINATOR_FORECAST
]
coordinator_rain: MeteoFranceRainUpdateCoordinator | None = data.get(
COORDINATOR_RAIN
)
coordinator_alert: MeteoFranceAlertUpdateCoordinator | None = data.get(
COORDINATOR_ALERT
)
@@ -316,7 +325,7 @@ class MeteoFranceAlertSensor(MeteoFranceSensor[CurrentPhenomenons]):
def __init__(
self,
coordinator: DataUpdateCoordinator[CurrentPhenomenons],
coordinator: MeteoFranceAlertUpdateCoordinator,
description: MeteoFranceSensorEntityDescription,
) -> None:
"""Initialize the Meteo-France sensor."""

View File

@@ -3,8 +3,6 @@
import logging
import time
from meteofrance_api.model.forecast import Forecast as MeteoFranceForecast
from homeassistant.components.weather import (
ATTR_CONDITION_CLEAR_NIGHT,
ATTR_CONDITION_SUNNY,
@@ -31,10 +29,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import dt as dt_util
from .const import (
@@ -47,6 +42,7 @@ from .const import (
MANUFACTURER,
MODEL,
)
from .coordinator import MeteoFranceForecastUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
@@ -66,7 +62,7 @@ async def async_setup_entry(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Meteo-France weather platform."""
coordinator: DataUpdateCoordinator[MeteoFranceForecast] = hass.data[DOMAIN][
coordinator: MeteoFranceForecastUpdateCoordinator = hass.data[DOMAIN][
entry.entry_id
][COORDINATOR_FORECAST]
@@ -87,7 +83,7 @@ async def async_setup_entry(
class MeteoFranceWeather(
CoordinatorEntity[DataUpdateCoordinator[MeteoFranceForecast]], WeatherEntity
CoordinatorEntity[MeteoFranceForecastUpdateCoordinator], WeatherEntity
):
"""Representation of a weather condition."""
@@ -101,7 +97,7 @@ class MeteoFranceWeather(
)
def __init__(
self, coordinator: DataUpdateCoordinator[MeteoFranceForecast], mode: str
self, coordinator: MeteoFranceForecastUpdateCoordinator, mode: str
) -> None:
"""Initialise the platform with a data instance and station name."""
super().__init__(coordinator)