Add CoordinatorWeatherEntity (#98777)

This commit is contained in:
Erik Montnemery 2023-08-21 23:10:16 +02:00 committed by GitHub
parent 92258b8e6f
commit 3e7ec88703
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 40 additions and 68 deletions

View File

@ -11,8 +11,8 @@ from homeassistant.components.weather import (
ATTR_FORECAST_TIME, ATTR_FORECAST_TIME,
ATTR_FORECAST_WIND_BEARING, ATTR_FORECAST_WIND_BEARING,
DOMAIN as WEATHER_DOMAIN, DOMAIN as WEATHER_DOMAIN,
CoordinatorWeatherEntity,
Forecast, Forecast,
WeatherEntity,
WeatherEntityFeature, WeatherEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -22,10 +22,9 @@ from homeassistant.const import (
UnitOfSpeed, UnitOfSpeed,
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import ( from .const import (
ATTR_API_CONDITION, ATTR_API_CONDITION,
@ -111,7 +110,7 @@ async def async_setup_entry(
async_add_entities(entities, False) async_add_entities(entities, False)
class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity): class AemetWeather(CoordinatorWeatherEntity[WeatherUpdateCoordinator]):
"""Implementation of an AEMET OpenData sensor.""" """Implementation of an AEMET OpenData sensor."""
_attr_attribution = ATTRIBUTION _attr_attribution = ATTRIBUTION
@ -139,15 +138,6 @@ class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity):
self._attr_name = name self._attr_name = name
self._attr_unique_id = unique_id self._attr_unique_id = unique_id
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
super()._handle_coordinator_update()
assert self.platform.config_entry
self.platform.config_entry.async_create_task(
self.hass, self.async_update_listeners(("daily", "hourly"))
)
@property @property
def condition(self): def condition(self):
"""Return the current condition.""" """Return the current condition."""

View File

@ -22,8 +22,8 @@ from homeassistant.components.weather import (
ATTR_FORECAST_PRECIPITATION_PROBABILITY, ATTR_FORECAST_PRECIPITATION_PROBABILITY,
ATTR_FORECAST_TIME, ATTR_FORECAST_TIME,
DOMAIN as WEATHER_DOMAIN, DOMAIN as WEATHER_DOMAIN,
CoordinatorWeatherEntity,
Forecast, Forecast,
WeatherEntity,
WeatherEntityFeature, WeatherEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -33,10 +33,9 @@ from homeassistant.const import (
UnitOfSpeed, UnitOfSpeed,
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import device_info from . import device_info
@ -87,7 +86,7 @@ def _calculate_unique_id(config_entry_unique_id: str | None, hourly: bool) -> st
return f"{config_entry_unique_id}{'-hourly' if hourly else '-daily'}" return f"{config_entry_unique_id}{'-hourly' if hourly else '-daily'}"
class ECWeather(CoordinatorEntity, WeatherEntity): class ECWeather(CoordinatorWeatherEntity):
"""Representation of a weather condition.""" """Representation of a weather condition."""
_attr_has_entity_name = True _attr_has_entity_name = True
@ -112,15 +111,6 @@ class ECWeather(CoordinatorEntity, WeatherEntity):
self._hourly = hourly self._hourly = hourly
self._attr_device_info = device_info(coordinator.config_entry) self._attr_device_info = device_info(coordinator.config_entry)
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
super()._handle_coordinator_update()
assert self.platform.config_entry
self.platform.config_entry.async_create_task(
self.hass, self.async_update_listeners(("daily", "hourly"))
)
@property @property
def native_temperature(self): def native_temperature(self):
"""Return the temperature.""" """Return the temperature."""

View File

@ -16,8 +16,8 @@ from homeassistant.components.weather import (
ATTR_WEATHER_WIND_GUST_SPEED, ATTR_WEATHER_WIND_GUST_SPEED,
ATTR_WEATHER_WIND_SPEED, ATTR_WEATHER_WIND_SPEED,
DOMAIN as WEATHER_DOMAIN, DOMAIN as WEATHER_DOMAIN,
CoordinatorWeatherEntity,
Forecast, Forecast,
WeatherEntity,
WeatherEntityFeature, WeatherEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -30,11 +30,10 @@ from homeassistant.const import (
UnitOfSpeed, UnitOfSpeed,
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.unit_system import METRIC_SYSTEM from homeassistant.util.unit_system import METRIC_SYSTEM
from . import MetDataUpdateCoordinator from . import MetDataUpdateCoordinator
@ -92,7 +91,7 @@ def format_condition(condition: str) -> str:
return condition return condition
class MetWeather(CoordinatorEntity[MetDataUpdateCoordinator], WeatherEntity): class MetWeather(CoordinatorWeatherEntity[MetDataUpdateCoordinator]):
"""Implementation of a Met.no weather condition.""" """Implementation of a Met.no weather condition."""
_attr_attribution = ( _attr_attribution = (
@ -148,15 +147,6 @@ class MetWeather(CoordinatorEntity[MetDataUpdateCoordinator], WeatherEntity):
"""Return if the entity should be enabled when first added to the entity registry.""" """Return if the entity should be enabled when first added to the entity registry."""
return not self._hourly return not self._hourly
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
super()._handle_coordinator_update()
assert self.platform.config_entry
self.platform.config_entry.async_create_task(
self.hass, self.async_update_listeners(("daily", "hourly"))
)
@property @property
def condition(self) -> str | None: def condition(self) -> str | None:
"""Return the current condition.""" """Return the current condition."""

View File

@ -7,8 +7,8 @@ from homeassistant.components.weather import (
ATTR_FORECAST_CONDITION, ATTR_FORECAST_CONDITION,
ATTR_FORECAST_TIME, ATTR_FORECAST_TIME,
DOMAIN as WEATHER_DOMAIN, DOMAIN as WEATHER_DOMAIN,
CoordinatorWeatherEntity,
Forecast, Forecast,
WeatherEntity,
WeatherEntityFeature, WeatherEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -21,14 +21,11 @@ from homeassistant.const import (
UnitOfSpeed, UnitOfSpeed,
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import ( from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from . import MetEireannWeatherData from . import MetEireannWeatherData
@ -78,7 +75,7 @@ def _calculate_unique_id(config: MappingProxyType[str, Any], hourly: bool) -> st
class MetEireannWeather( class MetEireannWeather(
CoordinatorEntity[DataUpdateCoordinator[MetEireannWeatherData]], WeatherEntity CoordinatorWeatherEntity[DataUpdateCoordinator[MetEireannWeatherData]]
): ):
"""Implementation of a Met Éireann weather condition.""" """Implementation of a Met Éireann weather condition."""
@ -98,15 +95,6 @@ class MetEireannWeather(
self._config = config self._config = config
self._hourly = hourly self._hourly = hourly
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
super()._handle_coordinator_update()
assert self.platform.config_entry
self.platform.config_entry.async_create_task(
self.hass, self.async_update_listeners(("daily", "hourly"))
)
@property @property
def name(self): def name(self):
"""Return the name of the sensor.""" """Return the name of the sensor."""

View File

@ -17,8 +17,8 @@ from homeassistant.components.weather import (
ATTR_FORECAST_TIME, ATTR_FORECAST_TIME,
ATTR_FORECAST_WIND_BEARING, ATTR_FORECAST_WIND_BEARING,
DOMAIN as WEATHER_DOMAIN, DOMAIN as WEATHER_DOMAIN,
CoordinatorWeatherEntity,
Forecast, Forecast,
WeatherEntity,
WeatherEntityFeature, WeatherEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
@ -31,7 +31,7 @@ from homeassistant.const import (
UnitOfSpeed, UnitOfSpeed,
UnitOfTemperature, UnitOfTemperature,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.sun import is_up from homeassistant.helpers.sun import is_up
@ -93,7 +93,7 @@ def _calculate_unique_id(config_entry_unique_id: str | None, forecast_type: str)
return f"{config_entry_unique_id}_{forecast_type}" return f"{config_entry_unique_id}_{forecast_type}"
class TomorrowioWeatherEntity(TomorrowioEntity, WeatherEntity): class TomorrowioWeatherEntity(TomorrowioEntity, CoordinatorWeatherEntity):
"""Entity that talks to Tomorrow.io v4 API to retrieve weather data.""" """Entity that talks to Tomorrow.io v4 API to retrieve weather data."""
_attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS _attr_native_precipitation_unit = UnitOfPrecipitationDepth.MILLIMETERS
@ -123,15 +123,6 @@ class TomorrowioWeatherEntity(TomorrowioEntity, WeatherEntity):
config_entry.unique_id, forecast_type config_entry.unique_id, forecast_type
) )
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
super()._handle_coordinator_update()
assert self.platform.config_entry
self.platform.config_entry.async_create_task(
self.hass, self.async_update_listeners(("daily", "hourly"))
)
def _forecast_dict( def _forecast_dict(
self, self,
forecast_dt: datetime, forecast_dt: datetime,

View File

@ -8,7 +8,7 @@ from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
import inspect import inspect
import logging import logging
from typing import Any, Final, Literal, Required, TypedDict, final from typing import Any, Final, Literal, Required, TypedDict, TypeVar, final
import voluptuous as vol import voluptuous as vol
@ -37,6 +37,10 @@ from homeassistant.helpers.config_validation import ( # noqa: F401
from homeassistant.helpers.entity import Entity, EntityDescription from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from homeassistant.util.json import JsonValueType from homeassistant.util.json import JsonValueType
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
@ -117,6 +121,10 @@ ROUNDING_PRECISION = 2
SERVICE_GET_FORECAST: Final = "get_forecast" SERVICE_GET_FORECAST: Final = "get_forecast"
_DataUpdateCoordinatorT = TypeVar(
"_DataUpdateCoordinatorT", bound="DataUpdateCoordinator[Any]"
)
# mypy: disallow-any-generics # mypy: disallow-any-generics
@ -1155,3 +1163,18 @@ async def async_get_forecast_service(
return { return {
"forecast": converted_forecast_list, "forecast": converted_forecast_list,
} }
class CoordinatorWeatherEntity(
CoordinatorEntity[_DataUpdateCoordinatorT], WeatherEntity
):
"""A class for weather entities using a single DataUpdateCoordinator."""
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
super()._handle_coordinator_update()
assert self.coordinator.config_entry
self.coordinator.config_entry.async_create_task(
self.hass, self.async_update_listeners(None)
)