Update tests, rename variable, and change conversion (#3546)
parent
0ff500ca25
commit
4c86721e70
|
@ -314,6 +314,7 @@ omit =
|
|||
homeassistant/components/thermostat/proliphix.py
|
||||
homeassistant/components/thermostat/radiotherm.py
|
||||
homeassistant/components/upnp.py
|
||||
homeassistant/components/weather/openweathermap.py
|
||||
homeassistant/components/zeroconf.py
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
"""
|
||||
Weather component that handles meteorological data for your location.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/weather/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.util.temperature import convert as convert_temperature
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = []
|
||||
DOMAIN = 'weather'
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
|
||||
ATTR_CONDITION_CLASS = 'condition_class'
|
||||
ATTR_WEATHER_ATTRIBUTION = 'attribution'
|
||||
ATTR_WEATHER_HUMIDITY = 'humidity'
|
||||
ATTR_WEATHER_OZONE = 'ozone'
|
||||
ATTR_WEATHER_PRESSURE = 'pressure'
|
||||
ATTR_WEATHER_TEMPERATURE = 'temperature'
|
||||
ATTR_WEATHER_WIND_BEARING = 'wind_bearing'
|
||||
ATTR_WEATHER_WIND_SPEED = 'wind_speed'
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup the weather component."""
|
||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
component.setup(config)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# pylint: disable=no-member, no-self-use, too-many-return-statements
|
||||
class WeatherEntity(Entity):
|
||||
"""ABC for a weather data."""
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
"""Return the platform temperature."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def pressure(self):
|
||||
"""Return the pressure."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
"""Return the humidity."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def wind_speed(self):
|
||||
"""Return the wind speed."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def wind_bearing(self):
|
||||
"""Return the wind bearing."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def ozone(self):
|
||||
"""Return the ozone level."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
"""Return the attribution."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
data = {
|
||||
ATTR_WEATHER_TEMPERATURE:
|
||||
convert_temperature(
|
||||
self.temperature, self.temperature_unit,
|
||||
self.hass.config.units.temperature_unit),
|
||||
ATTR_WEATHER_HUMIDITY: self.humidity,
|
||||
}
|
||||
|
||||
ozone = self.ozone
|
||||
if ozone is not None:
|
||||
data[ATTR_WEATHER_OZONE] = ozone
|
||||
|
||||
pressure = self.pressure
|
||||
if pressure is not None:
|
||||
data[ATTR_WEATHER_PRESSURE] = pressure
|
||||
|
||||
wind_bearing = self.wind_bearing
|
||||
if wind_bearing is not None:
|
||||
data[ATTR_WEATHER_WIND_BEARING] = wind_bearing
|
||||
|
||||
wind_speed = self.wind_speed
|
||||
if wind_speed is not None:
|
||||
data[ATTR_WEATHER_WIND_SPEED] = wind_speed
|
||||
|
||||
attribution = self.attribution
|
||||
if attribution is not None:
|
||||
data[ATTR_WEATHER_ATTRIBUTION] = attribution
|
||||
|
||||
return data
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the current state."""
|
||||
return self.condition
|
||||
|
||||
@property
|
||||
def condition(self):
|
||||
"""Return the current condition."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement."""
|
||||
return None
|
|
@ -0,0 +1,95 @@
|
|||
"""
|
||||
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 homeassistant.const import (TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
|
||||
CONDITION_CLASSES = {
|
||||
'cloudy': [],
|
||||
'fog': [],
|
||||
'hail': [],
|
||||
'lightning': [],
|
||||
'lightning-rainy': [],
|
||||
'partlycloudy': [],
|
||||
'pouring': [],
|
||||
'rainy': ['shower rain'],
|
||||
'snowy': [],
|
||||
'snowy-rainy': [],
|
||||
'sunny': ['sunshine'],
|
||||
'windy': [],
|
||||
'windy-variant': [],
|
||||
'exceptional': [],
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
])
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
class DemoWeather(WeatherEntity):
|
||||
"""Representation of a weather condition."""
|
||||
|
||||
def __init__(self, name, condition, temperature, humidity, pressure,
|
||||
wind_speed, temperature_unit):
|
||||
"""Initialize the Demo weather."""
|
||||
self._name = name
|
||||
self._condition = condition
|
||||
self._temperature = temperature
|
||||
self._temperature_unit = temperature_unit
|
||||
self._humidity = humidity
|
||||
self._pressure = pressure
|
||||
self._wind_speed = wind_speed
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return '{} {}'.format('Demo Weather', self._name)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""No polling needed for a demo weather condition."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
"""Return the temperature."""
|
||||
return self._temperature
|
||||
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement."""
|
||||
return self._temperature_unit
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
"""Return the humidity."""
|
||||
return self._humidity
|
||||
|
||||
@property
|
||||
def wind_speed(self):
|
||||
"""Return the wind speed."""
|
||||
return self._wind_speed
|
||||
|
||||
@property
|
||||
def pressure(self):
|
||||
"""Return the wind speed."""
|
||||
return self._pressure
|
||||
|
||||
@property
|
||||
def condition(self):
|
||||
"""Return the weather condition."""
|
||||
return [k for k, v in CONDITION_CLASSES.items() if
|
||||
self._condition.lower() in v][0]
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
"""Return the attribution."""
|
||||
return 'Powered by Home Assistant'
|
|
@ -0,0 +1,159 @@
|
|||
"""
|
||||
Support for the OpenWeatherMap (OWM) service.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/weather.openweathermap/
|
||||
"""
|
||||
import logging
|
||||
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)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
REQUIREMENTS = ['pyowm==2.5.0']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = 'OpenWeatherMap'
|
||||
ATTRIBUTION = 'Data provided by OpenWeatherMap'
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=10)
|
||||
|
||||
CONDITION_CLASSES = {
|
||||
'cloudy': [804],
|
||||
'fog': [701, 741],
|
||||
'hail': [906],
|
||||
'lightning': [210, 211, 212, 221],
|
||||
'lightning-rainy': [200, 201, 202, 230, 231, 232],
|
||||
'partlycloudy': [801, 802, 803],
|
||||
'pouring': [504, 314, 502, 503, 522],
|
||||
'rainy': [300, 301, 302, 310, 311, 312, 313, 500, 501, 520, 521],
|
||||
'snowy': [600, 601, 602, 611, 612, 620, 621, 622],
|
||||
'snowy-rainy': [511, 615, 616],
|
||||
'sunny': [800],
|
||||
'windy': [905, 951, 952, 953, 954, 955, 956, 957],
|
||||
'windy-variant': [958, 959, 960, 961],
|
||||
'exceptional': [711, 721, 731, 751, 761, 762, 771, 900, 901, 962, 903,
|
||||
904],
|
||||
}
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_API_KEY): cv.string,
|
||||
vol.Optional(CONF_LATITUDE): cv.latitude,
|
||||
vol.Optional(CONF_LONGITUDE): cv.longitude,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the OpenWeatherMap weather platform."""
|
||||
import pyowm
|
||||
|
||||
longitude = config.get(CONF_LONGITUDE, round(hass.config.longitude, 5))
|
||||
latitude = config.get(CONF_LATITUDE, round(hass.config.latitude, 5))
|
||||
name = config.get(CONF_NAME)
|
||||
|
||||
try:
|
||||
owm = pyowm.OWM(config.get(CONF_API_KEY))
|
||||
except pyowm.exceptions.api_call_error.APICallError:
|
||||
_LOGGER.error("Error while connecting to OpenWeatherMap")
|
||||
return False
|
||||
|
||||
data = WeatherData(owm, latitude, longitude)
|
||||
|
||||
add_devices([OpenWeatherMapWeather(
|
||||
name, data, hass.config.units.temperature_unit)])
|
||||
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
class OpenWeatherMapWeather(WeatherEntity):
|
||||
"""Implementation of an OpenWeatherMap sensor."""
|
||||
|
||||
def __init__(self, name, owm, temperature_unit):
|
||||
"""Initialize the sensor."""
|
||||
self._name = name
|
||||
self._owm = owm
|
||||
self._temperature_unit = temperature_unit
|
||||
self.date = None
|
||||
self.update()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def condition(self):
|
||||
"""Return the current condition."""
|
||||
try:
|
||||
return [k for k, v in CONDITION_CLASSES.items() if
|
||||
self.data.get_weather_code() in v][0]
|
||||
except IndexError:
|
||||
return STATE_UNKNOWN
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
"""Return the temperature."""
|
||||
return self.data.get_temperature('celsius')['temp']
|
||||
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement."""
|
||||
return self._temperature_unit
|
||||
|
||||
@property
|
||||
def pressure(self):
|
||||
"""Return the pressure."""
|
||||
return self.data.get_pressure()['press']
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
"""Return the humidity."""
|
||||
return self.data.get_humidity()
|
||||
|
||||
@property
|
||||
def wind_speed(self):
|
||||
"""Return the wind speed."""
|
||||
return self.data.get_wind()['speed']
|
||||
|
||||
@property
|
||||
def wind_bearing(self):
|
||||
"""Return the wind bearing."""
|
||||
return self.data.get_wind()['deg']
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
"""Return the attribution."""
|
||||
return ATTRIBUTION
|
||||
|
||||
# pylint: disable=too-many-branches
|
||||
def update(self):
|
||||
"""Get the latest data from OWM and updates the states."""
|
||||
self._owm.update()
|
||||
self.data = self._owm.data
|
||||
|
||||
|
||||
class WeatherData(object):
|
||||
"""Get the latest data from OpenWeatherMap."""
|
||||
|
||||
def __init__(self, owm, latitude, longitude):
|
||||
"""Initialize the data object."""
|
||||
self.owm = owm
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.data = None
|
||||
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
"""Get the latest data from OpenWeatherMap."""
|
||||
obs = self.owm.weather_at_coords(self.latitude, self.longitude)
|
||||
if obs is None:
|
||||
_LOGGER.warning("Failed to fetch data from OWM")
|
||||
return
|
||||
|
||||
self.data = obs.get_weather()
|
|
@ -384,6 +384,7 @@ pynetio==0.1.6
|
|||
pynx584==0.2
|
||||
|
||||
# homeassistant.components.sensor.openweathermap
|
||||
# homeassistant.components.weather.openweathermap
|
||||
pyowm==2.5.0
|
||||
|
||||
# homeassistant.components.switch.acer_projector
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
"""The tests for Weather platforms."""
|
|
@ -0,0 +1,57 @@
|
|||
"""The tests for the Weather component."""
|
||||
import unittest
|
||||
|
||||
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)
|
||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||
from homeassistant.bootstrap import setup_component
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
|
||||
class TestWeather(unittest.TestCase):
|
||||
"""Test the Weather component."""
|
||||
|
||||
def setUp(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
self.hass.config.units = METRIC_SYSTEM
|
||||
self.assertTrue(setup_component(self.hass, weather.DOMAIN, {
|
||||
'weather': {
|
||||
'platform': 'demo',
|
||||
}
|
||||
}))
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop down everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_attributes(self):
|
||||
"""Test weather attributes."""
|
||||
state = self.hass.states.get('weather.demo_weather_south')
|
||||
assert state is not None
|
||||
|
||||
assert state.state == 'sunny'
|
||||
|
||||
data = state.attributes
|
||||
assert data.get(ATTR_WEATHER_TEMPERATURE) == 21
|
||||
assert data.get(ATTR_WEATHER_HUMIDITY) == 92
|
||||
assert data.get(ATTR_WEATHER_PRESSURE) == 1099
|
||||
assert data.get(ATTR_WEATHER_WIND_SPEED) == 0.5
|
||||
assert data.get(ATTR_WEATHER_WIND_BEARING) is None
|
||||
assert data.get(ATTR_WEATHER_OZONE) is None
|
||||
assert data.get(ATTR_WEATHER_ATTRIBUTION) == \
|
||||
'Powered by Home Assistant'
|
||||
|
||||
def test_temperature_convert(self):
|
||||
"""Test temperature conversion."""
|
||||
state = self.hass.states.get('weather.demo_weather_north')
|
||||
assert state is not None
|
||||
|
||||
assert state.state == 'rainy'
|
||||
|
||||
data = state.attributes
|
||||
assert data.get(ATTR_WEATHER_TEMPERATURE) == -24.4
|
Loading…
Reference in New Issue