From 829cffd5def92f53786ba552eb2e2788e28c02bd Mon Sep 17 00:00:00 2001 From: Mark Coombes Date: Thu, 10 Oct 2019 03:05:46 -0400 Subject: [PATCH] Fix ecobee weather platform (#27369) * Fix ecobee weather platform * Remove custom forecast attributes * Tidy up process forecast method * Fix lint complaints * Add missed weather symbol --- homeassistant/components/ecobee/const.py | 28 +++++++ homeassistant/components/ecobee/weather.py | 94 +++++++++++++--------- 2 files changed, 83 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/ecobee/const.py b/homeassistant/components/ecobee/const.py index 411f5ddeeeb..a6141d874f1 100644 --- a/homeassistant/components/ecobee/const.py +++ b/homeassistant/components/ecobee/const.py @@ -24,3 +24,31 @@ ECOBEE_MODEL_TO_NAME = { ECOBEE_PLATFORMS = ["binary_sensor", "climate", "sensor", "weather"] MANUFACTURER = "ecobee" + +# Translates ecobee API weatherSymbol to HASS usable names +# https://www.ecobee.com/home/developer/api/documentation/v1/objects/WeatherForecast.shtml +ECOBEE_WEATHER_SYMBOL_TO_HASS = { + 0: "sunny", + 1: "partlycloudy", + 2: "partlycloudy", + 3: "cloudy", + 4: "cloudy", + 5: "cloudy", + 6: "rainy", + 7: "snowy-rainy", + 8: "pouring", + 9: "hail", + 10: "snowy", + 11: "snowy", + 12: "snowy-rainy", + 13: "snowy-heavy", + 14: "hail", + 15: "lightning-rainy", + 16: "windy", + 17: "tornado", + 18: "fog", + 19: "hazy", + 20: "hazy", + 21: "hazy", + -2: None, +} diff --git a/homeassistant/components/ecobee/weather.py b/homeassistant/components/ecobee/weather.py index 53e9842aae7..7b057f09a0c 100644 --- a/homeassistant/components/ecobee/weather.py +++ b/homeassistant/components/ecobee/weather.py @@ -8,17 +8,19 @@ from homeassistant.components.weather import ( ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, + ATTR_FORECAST_WIND_BEARING, ATTR_FORECAST_WIND_SPEED, WeatherEntity, ) from homeassistant.const import TEMP_FAHRENHEIT -from .const import DOMAIN, ECOBEE_MODEL_TO_NAME, MANUFACTURER, _LOGGER - -ATTR_FORECAST_TEMP_HIGH = "temphigh" -ATTR_FORECAST_PRESSURE = "pressure" -ATTR_FORECAST_VISIBILITY = "visibility" -ATTR_FORECAST_HUMIDITY = "humidity" +from .const import ( + DOMAIN, + ECOBEE_MODEL_TO_NAME, + ECOBEE_WEATHER_SYMBOL_TO_HASS, + MANUFACTURER, + _LOGGER, +) async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): @@ -94,7 +96,7 @@ class EcobeeWeather(WeatherEntity): def condition(self): """Return the current condition.""" try: - return self.get_forecast(0, "condition") + return ECOBEE_WEATHER_SYMBOL_TO_HASS[self.get_forecast(0, "weatherSymbol")] except ValueError: return None @@ -131,7 +133,7 @@ class EcobeeWeather(WeatherEntity): def visibility(self): """Return the visibility.""" try: - return int(self.get_forecast(0, "visibility")) + return int(self.get_forecast(0, "visibility")) / 1000 except ValueError: return None @@ -154,45 +156,59 @@ class EcobeeWeather(WeatherEntity): @property def attribution(self): """Return the attribution.""" - if self.weather: - station = self.weather.get("weatherStation", "UNKNOWN") - time = self.weather.get("timestamp", "UNKNOWN") - return f"Ecobee weather provided by {station} at {time}" - return None + if not self.weather: + return None + + station = self.weather.get("weatherStation", "UNKNOWN") + time = self.weather.get("timestamp", "UNKNOWN") + return f"Ecobee weather provided by {station} at {time} UTC" @property def forecast(self): """Return the forecast array.""" - try: - forecasts = [] - for day in self.weather["forecasts"]: - date_time = datetime.strptime( - day["dateTime"], "%Y-%m-%d %H:%M:%S" - ).isoformat() - forecast = { - ATTR_FORECAST_TIME: date_time, - ATTR_FORECAST_CONDITION: day["condition"], - ATTR_FORECAST_TEMP: float(day["tempHigh"]) / 10, - } - if day["tempHigh"] == ECOBEE_STATE_UNKNOWN: - break - if day["tempLow"] != ECOBEE_STATE_UNKNOWN: - forecast[ATTR_FORECAST_TEMP_LOW] = float(day["tempLow"]) / 10 - if day["pressure"] != ECOBEE_STATE_UNKNOWN: - forecast[ATTR_FORECAST_PRESSURE] = int(day["pressure"]) - if day["windSpeed"] != ECOBEE_STATE_UNKNOWN: - forecast[ATTR_FORECAST_WIND_SPEED] = int(day["windSpeed"]) - if day["visibility"] != ECOBEE_STATE_UNKNOWN: - forecast[ATTR_FORECAST_WIND_SPEED] = int(day["visibility"]) - if day["relativeHumidity"] != ECOBEE_STATE_UNKNOWN: - forecast[ATTR_FORECAST_HUMIDITY] = int(day["relativeHumidity"]) - forecasts.append(forecast) - return forecasts - except (ValueError, IndexError, KeyError): + if "forecasts" not in self.weather: return None + forecasts = list() + for day in range(1, 5): + forecast = _process_forecast(self.weather["forecasts"][day]) + if forecast is None: + continue + forecasts.append(forecast) + + if forecasts: + return forecasts + return None + async def async_update(self): """Get the latest weather data.""" await self.data.update() thermostat = self.data.ecobee.get_thermostat(self._index) self.weather = thermostat.get("weather", None) + + +def _process_forecast(json): + """Process a single ecobee API forecast to return expected values.""" + forecast = dict() + try: + forecast[ATTR_FORECAST_TIME] = datetime.strptime( + json["dateTime"], "%Y-%m-%d %H:%M:%S" + ).isoformat() + forecast[ATTR_FORECAST_CONDITION] = ECOBEE_WEATHER_SYMBOL_TO_HASS[ + json["weatherSymbol"] + ] + if json["tempHigh"] != ECOBEE_STATE_UNKNOWN: + forecast[ATTR_FORECAST_TEMP] = float(json["tempHigh"]) / 10 + if json["tempLow"] != ECOBEE_STATE_UNKNOWN: + forecast[ATTR_FORECAST_TEMP_LOW] = float(json["tempLow"]) / 10 + if json["windBearing"] != ECOBEE_STATE_UNKNOWN: + forecast[ATTR_FORECAST_WIND_BEARING] = int(json["windBearing"]) + if json["windSpeed"] != ECOBEE_STATE_UNKNOWN: + forecast[ATTR_FORECAST_WIND_SPEED] = int(json["windSpeed"]) + + except (ValueError, IndexError, KeyError): + return None + + if forecast: + return forecast + return None