mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 00:37:53 +00:00
Weather platform Forecast (#4721)
* Added forecast functionality to the weather platform, updated OWM to get forecast data * style fixes * Changed returned forecast data to a more managable dict * Fixed line length * forecast will always be collected, data returned from owm is based on metric setting * use list comprehension to create the forecast data * Added forecast data to the weather demo * Create dict directly in list comprehension * Minor variable change in weather demo platform * Convert forecast temperatures in weather component * set forecast attributes as constants * Style fixes and tests * Copied forecast_entry instead of mutating data
This commit is contained in:
parent
5d8e219448
commit
66d8787d47
@ -29,6 +29,9 @@ ATTR_WEATHER_PRESSURE = 'pressure'
|
||||
ATTR_WEATHER_TEMPERATURE = 'temperature'
|
||||
ATTR_WEATHER_WIND_BEARING = 'wind_bearing'
|
||||
ATTR_WEATHER_WIND_SPEED = 'wind_speed'
|
||||
ATTR_FORECAST = 'forecast'
|
||||
ATTR_FORECAST_TEMP = 'temperature'
|
||||
ATTR_FORECAST_TIME = 'datetime'
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
@ -84,11 +87,16 @@ class WeatherEntity(Entity):
|
||||
"""Return the attribution."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def forecast(self):
|
||||
"""Return the forecast."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
data = {
|
||||
ATTR_WEATHER_TEMPERATURE: self._temp_for_display,
|
||||
ATTR_WEATHER_TEMPERATURE: self._temp_for_display(self.temperature),
|
||||
ATTR_WEATHER_HUMIDITY: self.humidity,
|
||||
}
|
||||
|
||||
@ -112,6 +120,16 @@ class WeatherEntity(Entity):
|
||||
if attribution is not None:
|
||||
data[ATTR_WEATHER_ATTRIBUTION] = attribution
|
||||
|
||||
if self.forecast is not None:
|
||||
forecast = []
|
||||
for forecast_entry in self.forecast:
|
||||
forecast_entry = dict(forecast_entry)
|
||||
forecast_entry[ATTR_FORECAST_TEMP] = self._temp_for_display(
|
||||
forecast_entry[ATTR_FORECAST_TEMP])
|
||||
forecast.append(forecast_entry)
|
||||
|
||||
data[ATTR_FORECAST] = forecast
|
||||
|
||||
return data
|
||||
|
||||
@property
|
||||
@ -124,10 +142,8 @@ class WeatherEntity(Entity):
|
||||
"""Return the current condition."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def _temp_for_display(self):
|
||||
def _temp_for_display(self, temp):
|
||||
"""Convert temperature into preferred units for display purposes."""
|
||||
temp = self.temperature
|
||||
unit = self.temperature_unit
|
||||
hass_unit = self.hass.config.units.temperature_unit
|
||||
|
||||
|
@ -4,7 +4,10 @@ Demo platform that offers fake meteorological data.
|
||||
For more details about this platform, please refer to the documentation
|
||||
https://home-assistant.io/components/demo/
|
||||
"""
|
||||
from homeassistant.components.weather import WeatherEntity
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
WeatherEntity, ATTR_FORECAST_TEMP, ATTR_FORECAST_TIME)
|
||||
from homeassistant.const import (TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
|
||||
CONDITION_CLASSES = {
|
||||
@ -28,8 +31,10 @@ CONDITION_CLASSES = {
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Demo weather."""
|
||||
add_devices([
|
||||
DemoWeather('South', 'Sunshine', 21, 92, 1099, 0.5, TEMP_CELSIUS),
|
||||
DemoWeather('North', 'Shower rain', -12, 54, 987, 4.8, TEMP_FAHRENHEIT)
|
||||
DemoWeather('South', 'Sunshine', 21, 92, 1099, 0.5, TEMP_CELSIUS,
|
||||
[22, 19, 15, 12, 14, 18, 21]),
|
||||
DemoWeather('North', 'Shower rain', -12, 54, 987, 4.8, TEMP_FAHRENHEIT,
|
||||
[-10, -13, -18, -23, -19, -14, -9])
|
||||
])
|
||||
|
||||
|
||||
@ -37,7 +42,7 @@ class DemoWeather(WeatherEntity):
|
||||
"""Representation of a weather condition."""
|
||||
|
||||
def __init__(self, name, condition, temperature, humidity, pressure,
|
||||
wind_speed, temperature_unit):
|
||||
wind_speed, temperature_unit, forecast):
|
||||
"""Initialize the Demo weather."""
|
||||
self._name = name
|
||||
self._condition = condition
|
||||
@ -46,6 +51,7 @@ class DemoWeather(WeatherEntity):
|
||||
self._humidity = humidity
|
||||
self._pressure = pressure
|
||||
self._wind_speed = wind_speed
|
||||
self._forecast = forecast
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -92,3 +98,19 @@ class DemoWeather(WeatherEntity):
|
||||
def attribution(self):
|
||||
"""Return the attribution."""
|
||||
return 'Powered by Home Assistant'
|
||||
|
||||
@property
|
||||
def forecast(self):
|
||||
"""Return the forecast."""
|
||||
reftime = datetime.now().replace(hour=16, minute=00)
|
||||
|
||||
forecast_data = []
|
||||
for entry in self._forecast:
|
||||
data_dict = {
|
||||
ATTR_FORECAST_TIME: reftime.isoformat(),
|
||||
ATTR_FORECAST_TEMP: entry
|
||||
}
|
||||
reftime = reftime + timedelta(hours=4)
|
||||
forecast_data.append(data_dict)
|
||||
|
||||
return forecast_data
|
||||
|
@ -9,9 +9,10 @@ from datetime import timedelta
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.weather import WeatherEntity, PLATFORM_SCHEMA
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY, CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, STATE_UNKNOWN)
|
||||
from homeassistant.components.weather import (
|
||||
WeatherEntity, PLATFORM_SCHEMA, ATTR_FORECAST_TEMP, ATTR_FORECAST_TIME)
|
||||
from homeassistant.const import (CONF_API_KEY, CONF_NAME, CONF_LATITUDE,
|
||||
CONF_LONGITUDE, STATE_UNKNOWN, TEMP_CELSIUS)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
@ -23,6 +24,7 @@ DEFAULT_NAME = 'OpenWeatherMap'
|
||||
ATTRIBUTION = 'Data provided by OpenWeatherMap'
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
|
||||
MIN_TIME_BETWEEN_FORECAST_UPDATES = timedelta(minutes=30)
|
||||
|
||||
CONDITION_CLASSES = {
|
||||
'cloudy': [804],
|
||||
@ -79,6 +81,7 @@ class OpenWeatherMapWeather(WeatherEntity):
|
||||
self._owm = owm
|
||||
self._temperature_unit = temperature_unit
|
||||
self.data = None
|
||||
self.forecast_data = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -102,7 +105,7 @@ class OpenWeatherMapWeather(WeatherEntity):
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement."""
|
||||
return self._temperature_unit
|
||||
return TEMP_CELSIUS
|
||||
|
||||
@property
|
||||
def pressure(self):
|
||||
@ -129,10 +132,20 @@ class OpenWeatherMapWeather(WeatherEntity):
|
||||
"""Return the attribution."""
|
||||
return ATTRIBUTION
|
||||
|
||||
@property
|
||||
def forecast(self):
|
||||
"""Return the forecast array."""
|
||||
return [{
|
||||
ATTR_FORECAST_TIME: entry.get_reference_time('iso'),
|
||||
ATTR_FORECAST_TEMP: entry.get_temperature('celsius').get('temp')}
|
||||
for entry in self.forecast_data.get_weathers()]
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data from OWM and updates the states."""
|
||||
self._owm.update()
|
||||
self._owm.update_forecast()
|
||||
self.data = self._owm.data
|
||||
self.forecast_data = self._owm.forecast_data
|
||||
|
||||
|
||||
class WeatherData(object):
|
||||
@ -144,6 +157,7 @@ class WeatherData(object):
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.data = None
|
||||
self.forecast_data = None
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
@ -154,3 +168,15 @@ class WeatherData(object):
|
||||
return
|
||||
|
||||
self.data = obs.get_weather()
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_FORECAST_UPDATES)
|
||||
def update_forecast(self):
|
||||
"""Get the lastest forecast from OpenWeatherMap."""
|
||||
fcd = self.owm.three_hours_forecast_at_coords(
|
||||
self.latitude, self.longitude)
|
||||
|
||||
if fcd is None:
|
||||
_LOGGER.warning("Failed to fetch forecast data from OWM")
|
||||
return
|
||||
|
||||
self.forecast_data = fcd.get_forecast()
|
||||
|
@ -5,7 +5,7 @@ from homeassistant.components import weather
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_WEATHER_ATTRIBUTION, ATTR_WEATHER_HUMIDITY, ATTR_WEATHER_OZONE,
|
||||
ATTR_WEATHER_PRESSURE, ATTR_WEATHER_TEMPERATURE, ATTR_WEATHER_WIND_BEARING,
|
||||
ATTR_WEATHER_WIND_SPEED)
|
||||
ATTR_WEATHER_WIND_SPEED, ATTR_FORECAST, ATTR_FORECAST_TEMP)
|
||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||
from homeassistant.bootstrap import setup_component
|
||||
|
||||
@ -45,6 +45,9 @@ class TestWeather(unittest.TestCase):
|
||||
assert data.get(ATTR_WEATHER_OZONE) is None
|
||||
assert data.get(ATTR_WEATHER_ATTRIBUTION) == \
|
||||
'Powered by Home Assistant'
|
||||
assert data.get(ATTR_FORECAST)[0].get(ATTR_FORECAST_TEMP) == 22
|
||||
assert data.get(ATTR_FORECAST)[6].get(ATTR_FORECAST_TEMP) == 21
|
||||
assert len(data.get(ATTR_FORECAST)) == 7
|
||||
|
||||
def test_temperature_convert(self):
|
||||
"""Test temperature conversion."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user