Add language parameter to darksky sensor (#13297)

This commit is contained in:
cgtobi 2018-03-22 13:34:02 +01:00 committed by Sebastian Muszynski
parent 6e75c5427c
commit e9cdbe5d8c
3 changed files with 125 additions and 20 deletions

View File

@ -561,7 +561,6 @@ omit =
homeassistant/components/sensor/crimereports.py homeassistant/components/sensor/crimereports.py
homeassistant/components/sensor/cups.py homeassistant/components/sensor/cups.py
homeassistant/components/sensor/currencylayer.py homeassistant/components/sensor/currencylayer.py
homeassistant/components/sensor/darksky.py
homeassistant/components/sensor/deluge.py homeassistant/components/sensor/deluge.py
homeassistant/components/sensor/deutsche_bahn.py homeassistant/components/sensor/deutsche_bahn.py
homeassistant/components/sensor/dht.py homeassistant/components/sensor/dht.py

View File

@ -27,6 +27,9 @@ CONF_ATTRIBUTION = "Powered by Dark Sky"
CONF_UNITS = 'units' CONF_UNITS = 'units'
CONF_UPDATE_INTERVAL = 'update_interval' CONF_UPDATE_INTERVAL = 'update_interval'
CONF_FORECAST = 'forecast' CONF_FORECAST = 'forecast'
CONF_LANGUAGE = 'language'
DEFAULT_LANGUAGE = 'en'
DEFAULT_NAME = 'Dark Sky' DEFAULT_NAME = 'Dark Sky'
@ -118,6 +121,16 @@ CONDITION_PICTURES = {
'partly-cloudy-night': '/static/images/darksky/weather-cloudy.svg', 'partly-cloudy-night': '/static/images/darksky/weather-cloudy.svg',
} }
# Language Supported Codes
LANGUAGE_CODES = [
'ar', 'az', 'be', 'bg', 'bs', 'ca',
'cs', 'da', 'de', 'el', 'en', 'es',
'et', 'fi', 'fr', 'hr', 'hu', 'id',
'is', 'it', 'ja', 'ka', 'kw', 'nb',
'nl', 'pl', 'pt', 'ro', 'ru', 'sk',
'sl', 'sr', 'sv', 'tet', 'tr', 'uk',
'x-pig-latin', 'zh', 'zh-tw',
]
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_MONITORED_CONDITIONS): vol.Required(CONF_MONITORED_CONDITIONS):
@ -125,6 +138,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_API_KEY): cv.string, vol.Required(CONF_API_KEY): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_UNITS): vol.In(['auto', 'si', 'us', 'ca', 'uk', 'uk2']), vol.Optional(CONF_UNITS): vol.In(['auto', 'si', 'us', 'ca', 'uk', 'uk2']),
vol.Optional(CONF_LANGUAGE,
default=DEFAULT_LANGUAGE): vol.In(LANGUAGE_CODES),
vol.Inclusive(CONF_LATITUDE, 'coordinates', vol.Inclusive(CONF_LATITUDE, 'coordinates',
'Latitude and longitude must exist together'): cv.latitude, 'Latitude and longitude must exist together'): cv.latitude,
vol.Inclusive(CONF_LONGITUDE, 'coordinates', vol.Inclusive(CONF_LONGITUDE, 'coordinates',
@ -140,6 +155,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Dark Sky sensor.""" """Set up the Dark Sky sensor."""
latitude = config.get(CONF_LATITUDE, hass.config.latitude) latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
language = config.get(CONF_LANGUAGE)
if CONF_UNITS in config: if CONF_UNITS in config:
units = config[CONF_UNITS] units = config[CONF_UNITS]
@ -153,6 +169,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
latitude=latitude, latitude=latitude,
longitude=longitude, longitude=longitude,
units=units, units=units,
language=language,
interval=config.get(CONF_UPDATE_INTERVAL)) interval=config.get(CONF_UPDATE_INTERVAL))
forecast_data.update() forecast_data.update()
forecast_data.update_currently() forecast_data.update_currently()
@ -332,12 +349,14 @@ def convert_to_camel(data):
class DarkSkyData(object): class DarkSkyData(object):
"""Get the latest data from Darksky.""" """Get the latest data from Darksky."""
def __init__(self, api_key, latitude, longitude, units, interval): def __init__(self, api_key, latitude, longitude, units, language,
interval):
"""Initialize the data object.""" """Initialize the data object."""
self._api_key = api_key self._api_key = api_key
self.latitude = latitude self.latitude = latitude
self.longitude = longitude self.longitude = longitude
self.units = units self.units = units
self.language = language
self.data = None self.data = None
self.unit_system = None self.unit_system = None
@ -359,7 +378,8 @@ class DarkSkyData(object):
try: try:
self.data = forecastio.load_forecast( self.data = forecastio.load_forecast(
self._api_key, self.latitude, self.longitude, units=self.units) self._api_key, self.latitude, self.longitude, units=self.units,
lang=self.language)
except (ConnectError, HTTPError, Timeout, ValueError) as error: except (ConnectError, HTTPError, Timeout, ValueError) as error:
_LOGGER.error("Unable to connect to Dark Sky. %s", error) _LOGGER.error("Unable to connect to Dark Sky. %s", error)
self.data = None self.data = None

View File

@ -2,16 +2,69 @@
import re import re
import unittest import unittest
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from datetime import timedelta
import forecastio
from requests.exceptions import HTTPError from requests.exceptions import HTTPError
import requests_mock import requests_mock
from datetime import timedelta
import forecastio
from homeassistant.components.sensor import darksky from homeassistant.components.sensor import darksky
from homeassistant.setup import setup_component from homeassistant.setup import setup_component
from tests.common import load_fixture, get_test_home_assistant from tests.common import (load_fixture, get_test_home_assistant,
MockDependency)
VALID_CONFIG_MINIMAL = {
'sensor': {
'platform': 'darksky',
'api_key': 'foo',
'forecast': [1, 2],
'monitored_conditions': ['summary', 'icon', 'temperature_max'],
'update_interval': timedelta(seconds=120),
}
}
INVALID_CONFIG_MINIMAL = {
'sensor': {
'platform': 'darksky',
'api_key': 'foo',
'forecast': [1, 2],
'monitored_conditions': ['sumary', 'iocn', 'temperature_max'],
'update_interval': timedelta(seconds=120),
}
}
VALID_CONFIG_LANG_DE = {
'sensor': {
'platform': 'darksky',
'api_key': 'foo',
'forecast': [1, 2],
'units': 'us',
'language': 'de',
'monitored_conditions': ['summary', 'icon', 'temperature_max',
'minutely_summary', 'hourly_summary',
'daily_summary', 'humidity', ],
'update_interval': timedelta(seconds=120),
}
}
INVALID_CONFIG_LANG = {
'sensor': {
'platform': 'darksky',
'api_key': 'foo',
'forecast': [1, 2],
'language': 'yz',
'monitored_conditions': ['summary', 'icon', 'temperature_max'],
'update_interval': timedelta(seconds=120),
}
}
def load_forecastMock(key, lat, lon,
units, lang): # pylint: disable=invalid-name
"""Mock darksky forecast loading."""
return ''
class TestDarkSkySetup(unittest.TestCase): class TestDarkSkySetup(unittest.TestCase):
@ -30,12 +83,6 @@ class TestDarkSkySetup(unittest.TestCase):
"""Initialize values for this testcase class.""" """Initialize values for this testcase class."""
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
self.key = 'foo' self.key = 'foo'
self.config = {
'api_key': 'foo',
'forecast': [1, 2],
'monitored_conditions': ['summary', 'icon', 'temperature_max'],
'update_interval': timedelta(seconds=120),
}
self.lat = self.hass.config.latitude = 37.8267 self.lat = self.hass.config.latitude = 37.8267
self.lon = self.hass.config.longitude = -122.423 self.lon = self.hass.config.longitude = -122.423
self.entities = [] self.entities = []
@ -44,10 +91,41 @@ class TestDarkSkySetup(unittest.TestCase):
"""Stop everything that was started.""" """Stop everything that was started."""
self.hass.stop() self.hass.stop()
def test_setup_with_config(self): @MockDependency('forecastio')
@patch('forecastio.load_forecast', new=load_forecastMock)
def test_setup_with_config(self, mock_forecastio):
"""Test the platform setup with configuration.""" """Test the platform setup with configuration."""
self.assertTrue( setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL)
setup_component(self.hass, 'sensor', {'darksky': self.config}))
state = self.hass.states.get('sensor.dark_sky_summary')
assert state is not None
@MockDependency('forecastio')
@patch('forecastio.load_forecast', new=load_forecastMock)
def test_setup_with_invalid_config(self, mock_forecastio):
"""Test the platform setup with invalid configuration."""
setup_component(self.hass, 'sensor', INVALID_CONFIG_MINIMAL)
state = self.hass.states.get('sensor.dark_sky_summary')
assert state is None
@MockDependency('forecastio')
@patch('forecastio.load_forecast', new=load_forecastMock)
def test_setup_with_language_config(self, mock_forecastio):
"""Test the platform setup with language configuration."""
setup_component(self.hass, 'sensor', VALID_CONFIG_LANG_DE)
state = self.hass.states.get('sensor.dark_sky_summary')
assert state is not None
@MockDependency('forecastio')
@patch('forecastio.load_forecast', new=load_forecastMock)
def test_setup_with_invalid_language_config(self, mock_forecastio):
"""Test the platform setup with language configuration."""
setup_component(self.hass, 'sensor', INVALID_CONFIG_LANG)
state = self.hass.states.get('sensor.dark_sky_summary')
assert state is None
@patch('forecastio.api.get_forecast') @patch('forecastio.api.get_forecast')
def test_setup_bad_api_key(self, mock_get_forecast): def test_setup_bad_api_key(self, mock_get_forecast):
@ -60,7 +138,8 @@ class TestDarkSkySetup(unittest.TestCase):
msg = '400 Client Error: Bad Request for url: {}'.format(url) msg = '400 Client Error: Bad Request for url: {}'.format(url)
mock_get_forecast.side_effect = HTTPError(msg,) mock_get_forecast.side_effect = HTTPError(msg,)
response = darksky.setup_platform(self.hass, self.config, MagicMock()) response = darksky.setup_platform(self.hass, VALID_CONFIG_MINIMAL,
MagicMock())
self.assertFalse(response) self.assertFalse(response)
@requests_mock.Mocker() @requests_mock.Mocker()
@ -69,9 +148,16 @@ class TestDarkSkySetup(unittest.TestCase):
"""Test for successfully setting up the forecast.io platform.""" """Test for successfully setting up the forecast.io platform."""
uri = (r'https://api.(darksky.net|forecast.io)\/forecast\/(\w+)\/' uri = (r'https://api.(darksky.net|forecast.io)\/forecast\/(\w+)\/'
r'(-?\d+\.?\d*),(-?\d+\.?\d*)') r'(-?\d+\.?\d*),(-?\d+\.?\d*)')
mock_req.get(re.compile(uri), mock_req.get(re.compile(uri), text=load_fixture('darksky.json'))
text=load_fixture('darksky.json'))
darksky.setup_platform(self.hass, self.config, self.add_entities) assert setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL)
self.assertTrue(mock_get_forecast.called) self.assertTrue(mock_get_forecast.called)
self.assertEqual(mock_get_forecast.call_count, 1) self.assertEqual(mock_get_forecast.call_count, 1)
self.assertEqual(len(self.entities), 7) self.assertEqual(len(self.hass.states.entity_ids()), 7)
state = self.hass.states.get('sensor.dark_sky_summary')
assert state is not None
self.assertEqual(state.state, 'Clear')
self.assertEqual(state.attributes.get('friendly_name'),
'Dark Sky Summary')