From f85c2e5a92d3ca4c37b7353da29c1c101f86e4b8 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 16 Aug 2023 22:10:48 +0200 Subject: [PATCH] Modernize environment_canada weather (#98502) --- .../components/environment_canada/weather.py | 57 ++++++++++++++++--- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/environment_canada/weather.py b/homeassistant/components/environment_canada/weather.py index a9f79907b54..bdc300dc9a3 100644 --- a/homeassistant/components/environment_canada/weather.py +++ b/homeassistant/components/environment_canada/weather.py @@ -21,7 +21,10 @@ from homeassistant.components.weather import ( ATTR_FORECAST_NATIVE_TEMP_LOW, ATTR_FORECAST_PRECIPITATION_PROBABILITY, ATTR_FORECAST_TIME, + DOMAIN as WEATHER_DOMAIN, + Forecast, WeatherEntity, + WeatherEntityFeature, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( @@ -30,7 +33,8 @@ from homeassistant.const import ( UnitOfSpeed, UnitOfTemperature, ) -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt as dt_util @@ -63,7 +67,24 @@ async def async_setup_entry( ) -> None: """Add a weather entity from a config_entry.""" coordinator = hass.data[DOMAIN][config_entry.entry_id]["weather_coordinator"] - async_add_entities([ECWeather(coordinator, False), ECWeather(coordinator, True)]) + entity_registry = er.async_get(hass) + + entities = [ECWeather(coordinator, False)] + + # Add hourly entity to legacy config entries + if entity_registry.async_get_entity_id( + WEATHER_DOMAIN, + DOMAIN, + _calculate_unique_id(config_entry.unique_id, True), + ): + entities.append(ECWeather(coordinator, True)) + + async_add_entities(entities) + + +def _calculate_unique_id(config_entry_unique_id: str | None, hourly: bool) -> str: + """Calculate unique ID.""" + return f"{config_entry_unique_id}{'-hourly' if hourly else '-daily'}" class ECWeather(CoordinatorEntity, WeatherEntity): @@ -74,6 +95,9 @@ class ECWeather(CoordinatorEntity, WeatherEntity): _attr_native_temperature_unit = UnitOfTemperature.CELSIUS _attr_native_visibility_unit = UnitOfLength.KILOMETERS _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR + _attr_supported_features = ( + WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY + ) def __init__(self, coordinator, hourly): """Initialize Environment Canada weather.""" @@ -81,13 +105,22 @@ class ECWeather(CoordinatorEntity, WeatherEntity): self.ec_data = coordinator.ec_data self._attr_attribution = self.ec_data.metadata["attribution"] self._attr_translation_key = "hourly_forecast" if hourly else "forecast" - self._attr_unique_id = ( - f"{coordinator.config_entry.unique_id}{'-hourly' if hourly else '-daily'}" + self._attr_unique_id = _calculate_unique_id( + coordinator.config_entry.unique_id, hourly ) self._attr_entity_registry_enabled_default = not hourly self._hourly = hourly 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 def native_temperature(self): """Return the temperature.""" @@ -155,20 +188,28 @@ class ECWeather(CoordinatorEntity, WeatherEntity): return "" @property - def forecast(self): + def forecast(self) -> list[Forecast] | None: """Return the forecast array.""" return get_forecast(self.ec_data, self._hourly) + async def async_forecast_daily(self) -> list[Forecast] | None: + """Return the daily forecast in native units.""" + return get_forecast(self.ec_data, False) -def get_forecast(ec_data, hourly): + async def async_forecast_hourly(self) -> list[Forecast] | None: + """Return the hourly forecast in native units.""" + return get_forecast(self.ec_data, True) + + +def get_forecast(ec_data, hourly) -> list[Forecast] | None: """Build the forecast array.""" - forecast_array = [] + forecast_array: list[Forecast] = [] if not hourly: if not (half_days := ec_data.daily_forecasts): return None - today = { + today: Forecast = { ATTR_FORECAST_TIME: dt_util.now().isoformat(), ATTR_FORECAST_CONDITION: icon_code_to_condition( int(half_days[0]["icon_code"])