Update tests, rename variable, and change conversion (#3546)

pull/4037/head
Fabian Affolter 2016-10-25 06:53:03 +02:00 committed by Paulus Schoutsen
parent 0ff500ca25
commit 4c86721e70
7 changed files with 443 additions and 0 deletions

View File

@ -314,6 +314,7 @@ omit =
homeassistant/components/thermostat/proliphix.py homeassistant/components/thermostat/proliphix.py
homeassistant/components/thermostat/radiotherm.py homeassistant/components/thermostat/radiotherm.py
homeassistant/components/upnp.py homeassistant/components/upnp.py
homeassistant/components/weather/openweathermap.py
homeassistant/components/zeroconf.py homeassistant/components/zeroconf.py

View File

@ -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

View File

@ -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'

View File

@ -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()

View File

@ -384,6 +384,7 @@ pynetio==0.1.6
pynx584==0.2 pynx584==0.2
# homeassistant.components.sensor.openweathermap # homeassistant.components.sensor.openweathermap
# homeassistant.components.weather.openweathermap
pyowm==2.5.0 pyowm==2.5.0
# homeassistant.components.switch.acer_projector # homeassistant.components.switch.acer_projector

View File

@ -0,0 +1 @@
"""The tests for Weather platforms."""

View File

@ -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