From e9cdbe5d8c5b659a3c80dbd1346192418a6839d8 Mon Sep 17 00:00:00 2001 From: cgtobi Date: Thu, 22 Mar 2018 13:34:02 +0100 Subject: [PATCH] Add language parameter to darksky sensor (#13297) --- .coveragerc | 1 - homeassistant/components/sensor/darksky.py | 24 ++++- tests/components/sensor/test_darksky.py | 120 ++++++++++++++++++--- 3 files changed, 125 insertions(+), 20 deletions(-) diff --git a/.coveragerc b/.coveragerc index 1dcde0ded14..a2c0dde77b1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -561,7 +561,6 @@ omit = homeassistant/components/sensor/crimereports.py homeassistant/components/sensor/cups.py homeassistant/components/sensor/currencylayer.py - homeassistant/components/sensor/darksky.py homeassistant/components/sensor/deluge.py homeassistant/components/sensor/deutsche_bahn.py homeassistant/components/sensor/dht.py diff --git a/homeassistant/components/sensor/darksky.py b/homeassistant/components/sensor/darksky.py index 3049415c754..261e0a62409 100644 --- a/homeassistant/components/sensor/darksky.py +++ b/homeassistant/components/sensor/darksky.py @@ -27,6 +27,9 @@ CONF_ATTRIBUTION = "Powered by Dark Sky" CONF_UNITS = 'units' CONF_UPDATE_INTERVAL = 'update_interval' CONF_FORECAST = 'forecast' +CONF_LANGUAGE = 'language' + +DEFAULT_LANGUAGE = 'en' DEFAULT_NAME = 'Dark Sky' @@ -118,6 +121,16 @@ CONDITION_PICTURES = { '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({ vol.Required(CONF_MONITORED_CONDITIONS): @@ -125,6 +138,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_API_KEY): 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_LANGUAGE, + default=DEFAULT_LANGUAGE): vol.In(LANGUAGE_CODES), vol.Inclusive(CONF_LATITUDE, 'coordinates', 'Latitude and longitude must exist together'): cv.latitude, 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.""" latitude = config.get(CONF_LATITUDE, hass.config.latitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude) + language = config.get(CONF_LANGUAGE) if CONF_UNITS in config: units = config[CONF_UNITS] @@ -153,6 +169,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): latitude=latitude, longitude=longitude, units=units, + language=language, interval=config.get(CONF_UPDATE_INTERVAL)) forecast_data.update() forecast_data.update_currently() @@ -332,12 +349,14 @@ def convert_to_camel(data): class DarkSkyData(object): """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.""" self._api_key = api_key self.latitude = latitude self.longitude = longitude self.units = units + self.language = language self.data = None self.unit_system = None @@ -359,7 +378,8 @@ class DarkSkyData(object): try: 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: _LOGGER.error("Unable to connect to Dark Sky. %s", error) self.data = None diff --git a/tests/components/sensor/test_darksky.py b/tests/components/sensor/test_darksky.py index 7ee04b0df4c..9300ecef432 100644 --- a/tests/components/sensor/test_darksky.py +++ b/tests/components/sensor/test_darksky.py @@ -2,16 +2,69 @@ import re import unittest from unittest.mock import MagicMock, patch +from datetime import timedelta -import forecastio from requests.exceptions import HTTPError import requests_mock -from datetime import timedelta + +import forecastio from homeassistant.components.sensor import darksky 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): @@ -30,12 +83,6 @@ class TestDarkSkySetup(unittest.TestCase): """Initialize values for this testcase class.""" self.hass = get_test_home_assistant() 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.lon = self.hass.config.longitude = -122.423 self.entities = [] @@ -44,10 +91,41 @@ class TestDarkSkySetup(unittest.TestCase): """Stop everything that was started.""" 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.""" - self.assertTrue( - setup_component(self.hass, 'sensor', {'darksky': self.config})) + setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL) + + 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') 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) 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) @requests_mock.Mocker() @@ -69,9 +148,16 @@ class TestDarkSkySetup(unittest.TestCase): """Test for successfully setting up the forecast.io platform.""" uri = (r'https://api.(darksky.net|forecast.io)\/forecast\/(\w+)\/' r'(-?\d+\.?\d*),(-?\d+\.?\d*)') - mock_req.get(re.compile(uri), - text=load_fixture('darksky.json')) - darksky.setup_platform(self.hass, self.config, self.add_entities) + mock_req.get(re.compile(uri), text=load_fixture('darksky.json')) + + assert setup_component(self.hass, 'sensor', VALID_CONFIG_MINIMAL) + self.assertTrue(mock_get_forecast.called) 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')