Home Assistant Unofficial Reference 2024.12.1
weather.py
Go to the documentation of this file.
1 """Support for displaying weather info from Ecobee API."""
2 
3 from __future__ import annotations
4 
5 from datetime import timedelta
6 
7 from pyecobee.const import ECOBEE_STATE_UNKNOWN
8 
10  ATTR_FORECAST_CONDITION,
11  ATTR_FORECAST_NATIVE_TEMP,
12  ATTR_FORECAST_NATIVE_TEMP_LOW,
13  ATTR_FORECAST_NATIVE_WIND_SPEED,
14  ATTR_FORECAST_TIME,
15  ATTR_FORECAST_WIND_BEARING,
16  Forecast,
17  WeatherEntity,
18  WeatherEntityFeature,
19 )
20 from homeassistant.config_entries import ConfigEntry
21 from homeassistant.const import (
22  UnitOfLength,
23  UnitOfPressure,
24  UnitOfSpeed,
25  UnitOfTemperature,
26 )
27 from homeassistant.core import HomeAssistant
28 from homeassistant.helpers.device_registry import DeviceInfo
29 from homeassistant.helpers.entity_platform import AddEntitiesCallback
30 from homeassistant.util import dt as dt_util
31 
32 from .const import (
33  DOMAIN,
34  ECOBEE_MODEL_TO_NAME,
35  ECOBEE_WEATHER_SYMBOL_TO_HASS,
36  MANUFACTURER,
37 )
38 
39 
41  hass: HomeAssistant,
42  config_entry: ConfigEntry,
43  async_add_entities: AddEntitiesCallback,
44 ) -> None:
45  """Set up the ecobee weather platform."""
46  data = hass.data[DOMAIN]
47  dev = []
48  for index in range(len(data.ecobee.thermostats)):
49  thermostat = data.ecobee.get_thermostat(index)
50  if "weather" in thermostat:
51  dev.append(EcobeeWeather(data, thermostat["name"], index))
52 
53  async_add_entities(dev, True)
54 
55 
57  """Representation of Ecobee weather data."""
58 
59  _attr_native_pressure_unit = UnitOfPressure.HPA
60  _attr_native_temperature_unit = UnitOfTemperature.FAHRENHEIT
61  _attr_native_visibility_unit = UnitOfLength.METERS
62  _attr_native_wind_speed_unit = UnitOfSpeed.MILES_PER_HOUR
63  _attr_has_entity_name = True
64  _attr_name = None
65  _attr_supported_features = WeatherEntityFeature.FORECAST_DAILY
66 
67  def __init__(self, data, name, index):
68  """Initialize the Ecobee weather platform."""
69  self.datadata = data
70  self._name_name = name
71  self._index_index = index
72  self.weatherweather = None
73  self._attr_unique_id_attr_unique_id = data.ecobee.get_thermostat(self._index_index)["identifier"]
74 
75  def get_forecast(self, index, param):
76  """Retrieve forecast parameter."""
77  try:
78  forecast = self.weatherweather["forecasts"][index]
79  return forecast[param]
80  except (IndexError, KeyError) as err:
81  raise ValueError from err
82 
83  @property
84  def device_info(self) -> DeviceInfo:
85  """Return device information for the ecobee weather platform."""
86  thermostat = self.datadata.ecobee.get_thermostat(self._index_index)
87  model: str | None
88  try:
89  model = f"{ECOBEE_MODEL_TO_NAME[thermostat['modelNumber']]} Thermostat"
90  except KeyError:
91  # Ecobee model is not in our list
92  model = None
93 
94  return DeviceInfo(
95  identifiers={(DOMAIN, thermostat["identifier"])},
96  manufacturer=MANUFACTURER,
97  model=model,
98  name=self._name_name,
99  )
100 
101  @property
102  def condition(self):
103  """Return the current condition."""
104  try:
105  return ECOBEE_WEATHER_SYMBOL_TO_HASS[self.get_forecastget_forecast(0, "weatherSymbol")]
106  except ValueError:
107  return None
108 
109  @property
111  """Return the temperature."""
112  try:
113  return float(self.get_forecastget_forecast(0, "temperature")) / 10
114  except ValueError:
115  return None
116 
117  @property
118  def native_pressure(self):
119  """Return the pressure."""
120  try:
121  pressure = self.get_forecastget_forecast(0, "pressure")
122  return round(pressure)
123  except ValueError:
124  return None
125 
126  @property
127  def humidity(self):
128  """Return the humidity."""
129  try:
130  return int(self.get_forecastget_forecast(0, "relativeHumidity"))
131  except ValueError:
132  return None
133 
134  @property
135  def native_visibility(self):
136  """Return the visibility."""
137  try:
138  return int(self.get_forecastget_forecast(0, "visibility"))
139  except ValueError:
140  return None
141 
142  @property
143  def native_wind_speed(self):
144  """Return the wind speed."""
145  try:
146  return int(self.get_forecastget_forecast(0, "windSpeed"))
147  except ValueError:
148  return None
149 
150  @property
151  def wind_bearing(self):
152  """Return the wind direction."""
153  try:
154  return int(self.get_forecastget_forecast(0, "windBearing"))
155  except ValueError:
156  return None
157 
158  @property
159  def attribution(self):
160  """Return the attribution."""
161  if not self.weatherweather:
162  return None
163 
164  station = self.weatherweather.get("weatherStation", "UNKNOWN")
165  time = self.weatherweather.get("timestamp", "UNKNOWN")
166  return f"Ecobee weather provided by {station} at {time} UTC"
167 
168  def _forecast(self) -> list[Forecast] | None:
169  """Return the forecast array."""
170  if "forecasts" not in self.weatherweather:
171  return None
172 
173  forecasts: list[Forecast] = []
174  date = dt_util.utcnow()
175  for day in range(5):
176  forecast = _process_forecast(self.weatherweather["forecasts"][day])
177  if forecast is None:
178  continue
179  forecast[ATTR_FORECAST_TIME] = date.isoformat()
180  date += timedelta(days=1)
181  forecasts.append(forecast)
182 
183  if forecasts:
184  return forecasts
185  return None
186 
187  async def async_forecast_daily(self) -> list[Forecast] | None:
188  """Return the daily forecast in native units."""
189  return self._forecast_forecast()
190 
191  async def async_update(self) -> None:
192  """Get the latest weather data."""
193  await self.datadata.update()
194  thermostat = self.datadata.ecobee.get_thermostat(self._index_index)
195  self.weatherweather = thermostat.get("weather")
196  await self.async_update_listenersasync_update_listeners(("daily",))
197 
198 
200  """Process a single ecobee API forecast to return expected values."""
201  forecast = {}
202  try:
203  forecast[ATTR_FORECAST_CONDITION] = ECOBEE_WEATHER_SYMBOL_TO_HASS[
204  json["weatherSymbol"]
205  ]
206  if json["tempHigh"] != ECOBEE_STATE_UNKNOWN:
207  forecast[ATTR_FORECAST_NATIVE_TEMP] = float(json["tempHigh"]) / 10
208  if json["tempLow"] != ECOBEE_STATE_UNKNOWN:
209  forecast[ATTR_FORECAST_NATIVE_TEMP_LOW] = float(json["tempLow"]) / 10
210  if json["windBearing"] != ECOBEE_STATE_UNKNOWN:
211  forecast[ATTR_FORECAST_WIND_BEARING] = int(json["windBearing"])
212  if json["windSpeed"] != ECOBEE_STATE_UNKNOWN:
213  forecast[ATTR_FORECAST_NATIVE_WIND_SPEED] = int(json["windSpeed"])
214 
215  except (ValueError, IndexError, KeyError):
216  return None
217 
218  if forecast:
219  return forecast
220  return None
None async_update_listeners(self, Iterable[Literal["daily", "hourly", "twice_daily"]]|None forecast_types)
Definition: __init__.py:961
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: weather.py:44
IssData update(pyiss.ISS iss)
Definition: __init__.py:33