1 """Platform for retrieving meteorological data from Environment Canada."""
3 from __future__
import annotations
8 ATTR_CONDITION_CLEAR_NIGHT,
12 ATTR_CONDITION_LIGHTNING_RAINY,
13 ATTR_CONDITION_PARTLYCLOUDY,
14 ATTR_CONDITION_POURING,
17 ATTR_CONDITION_SNOWY_RAINY,
20 ATTR_FORECAST_CONDITION,
21 ATTR_FORECAST_NATIVE_TEMP,
22 ATTR_FORECAST_NATIVE_TEMP_LOW,
23 ATTR_FORECAST_PRECIPITATION_PROBABILITY,
25 DOMAIN
as WEATHER_DOMAIN,
27 SingleCoordinatorWeatherEntity,
41 from .
import device_info
42 from .const
import DOMAIN
46 ICON_CONDITION_MAP = {
47 ATTR_CONDITION_SUNNY: [0, 1],
48 ATTR_CONDITION_CLEAR_NIGHT: [30, 31],
49 ATTR_CONDITION_PARTLYCLOUDY: [2, 3, 4, 5, 22, 32, 33, 34, 35],
50 ATTR_CONDITION_CLOUDY: [10],
51 ATTR_CONDITION_RAINY: [6, 9, 11, 12, 28, 36],
52 ATTR_CONDITION_LIGHTNING_RAINY: [19, 39, 46, 47],
53 ATTR_CONDITION_POURING: [13],
54 ATTR_CONDITION_SNOWY_RAINY: [7, 14, 15, 27, 37],
55 ATTR_CONDITION_SNOWY: [8, 16, 17, 18, 25, 26, 38, 40],
56 ATTR_CONDITION_WINDY: [43],
57 ATTR_CONDITION_FOG: [20, 21, 23, 24, 44],
58 ATTR_CONDITION_HAIL: [26, 27],
64 config_entry: ConfigEntry,
65 async_add_entities: AddEntitiesCallback,
67 """Add a weather entity from a config_entry."""
68 coordinator = hass.data[DOMAIN][config_entry.entry_id][
"weather_coordinator"]
69 entity_registry = er.async_get(hass)
72 if hourly_entity_id := entity_registry.async_get_entity_id(
77 entity_registry.async_remove(hourly_entity_id)
83 """Calculate unique ID."""
84 return f
"{config_entry_unique_id}{'-hourly' if hourly else '-daily'}"
88 """Representation of a weather condition."""
90 _attr_has_entity_name =
True
91 _attr_native_pressure_unit = UnitOfPressure.KPA
92 _attr_native_temperature_unit = UnitOfTemperature.CELSIUS
93 _attr_native_visibility_unit = UnitOfLength.KILOMETERS
94 _attr_native_wind_speed_unit = UnitOfSpeed.KILOMETERS_PER_HOUR
95 _attr_supported_features = (
96 WeatherEntityFeature.FORECAST_DAILY | WeatherEntityFeature.FORECAST_HOURLY
100 """Initialize Environment Canada weather."""
106 coordinator.config_entry.unique_id,
False
112 """Return the temperature."""
114 temperature := self.
ec_dataec_data.conditions.get(
"temperature", {}).
get(
"value")
116 return float(temperature)
118 self.
ec_dataec_data.hourly_forecasts
119 and (temperature := self.
ec_dataec_data.hourly_forecasts[0].
get(
"temperature"))
122 return float(temperature)
127 """Return the humidity."""
128 if self.
ec_dataec_data.conditions.get(
"humidity", {}).
get(
"value"):
129 return float(self.
ec_dataec_data.conditions[
"humidity"][
"value"])
134 """Return the wind speed."""
135 if self.
ec_dataec_data.conditions.get(
"wind_speed", {}).
get(
"value"):
136 return float(self.
ec_dataec_data.conditions[
"wind_speed"][
"value"])
141 """Return the wind bearing."""
142 if self.
ec_dataec_data.conditions.get(
"wind_bearing", {}).
get(
"value"):
143 return float(self.
ec_dataec_data.conditions[
"wind_bearing"][
"value"])
148 """Return the pressure."""
149 if self.
ec_dataec_data.conditions.get(
"pressure", {}).
get(
"value"):
150 return float(self.
ec_dataec_data.conditions[
"pressure"][
"value"])
155 """Return the visibility."""
156 if self.
ec_dataec_data.conditions.get(
"visibility", {}).
get(
"value"):
157 return float(self.
ec_dataec_data.conditions[
"visibility"][
"value"])
162 """Return the weather condition."""
165 if self.
ec_dataec_data.conditions.get(
"icon_code", {}).
get(
"value"):
166 icon_code = self.
ec_dataec_data.conditions[
"icon_code"][
"value"]
167 elif self.
ec_dataec_data.hourly_forecasts
and self.
ec_dataec_data.hourly_forecasts[0].
get(
170 icon_code = self.
ec_dataec_data.hourly_forecasts[0][
"icon_code"]
178 """Return the daily forecast in native units."""
183 """Return the hourly forecast in native units."""
188 """Build the forecast array."""
189 forecast_array: list[Forecast] = []
192 if not (half_days := ec_data.daily_forecasts):
195 def get_day_forecast(
196 fcst: list[dict[str, Any]],
198 high_temp =
int(fcst[0][
"temperature"])
if len(fcst) == 2
else None
200 ATTR_FORECAST_TIME: fcst[0][
"timestamp"].isoformat(),
201 ATTR_FORECAST_NATIVE_TEMP: high_temp,
202 ATTR_FORECAST_NATIVE_TEMP_LOW:
int(fcst[-1][
"temperature"]),
203 ATTR_FORECAST_PRECIPITATION_PROBABILITY:
int(
204 fcst[0][
"precip_probability"]
207 int(fcst[0][
"icon_code"])
211 i = 2
if half_days[0][
"temperature_class"] ==
"high" else 1
212 forecast_array.append(get_day_forecast(half_days[0:i]))
213 for i
in range(i, len(half_days) - 1, 2):
214 forecast_array.append(get_day_forecast(half_days[i : i + 2]))
217 forecast_array.extend(
219 ATTR_FORECAST_TIME: hour[
"period"].isoformat(),
220 ATTR_FORECAST_NATIVE_TEMP:
int(hour[
"temperature"]),
222 ATTR_FORECAST_PRECIPITATION_PROBABILITY:
int(
223 hour[
"precip_probability"]
226 for hour
in ec_data.hourly_forecasts
229 return forecast_array
233 """Return the condition corresponding to an icon code."""
234 for condition, codes
in ICON_CONDITION_MAP.items():
235 if icon_code
in codes:
def native_pressure(self)
list[Forecast]|None _async_forecast_hourly(self)
def native_wind_speed(self)
def __init__(self, coordinator)
list[Forecast]|None _async_forecast_daily(self)
def native_temperature(self)
def native_visibility(self)
DeviceInfo|None device_info(self)
web.Response get(self, web.Request request, str config_key)
def icon_code_to_condition(icon_code)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
list[Forecast]|None get_forecast(ec_data, hourly)
str _calculate_unique_id(str|None config_entry_unique_id, bool hourly)