From 8e93d0a7a28efc446354080aee2475bd8ded0988 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sat, 8 Jun 2019 17:45:10 +0200 Subject: [PATCH 01/12] deCONZ fix retry set state(#24410) --- homeassistant/components/deconz/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/deconz/manifest.json b/homeassistant/components/deconz/manifest.json index 56ea52b7693..1c7c0ac8aea 100644 --- a/homeassistant/components/deconz/manifest.json +++ b/homeassistant/components/deconz/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/components/deconz", "requirements": [ - "pydeconz==59" + "pydeconz==60" ], "ssdp": { "manufacturer": [ diff --git a/requirements_all.txt b/requirements_all.txt index 8b635326058..e097b075833 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1045,7 +1045,7 @@ pydaikin==1.4.6 pydanfossair==0.1.0 # homeassistant.components.deconz -pydeconz==59 +pydeconz==60 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 18562f53554..42d29717037 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -230,7 +230,7 @@ pyHS100==0.3.5 pyblackbird==0.5 # homeassistant.components.deconz -pydeconz==59 +pydeconz==60 # homeassistant.components.zwave pydispatcher==2.0.5 From b0985bb459868d4c29b854f5167ea15592305d28 Mon Sep 17 00:00:00 2001 From: Julien Brochet <556303+aerialls@users.noreply.github.com> Date: Mon, 10 Jun 2019 18:11:07 +0200 Subject: [PATCH 02/12] Load the SSDP component only when it's needed (#24420) * fix(hue): Load the SSDP component only when it's needed * fix(deconz): Don't load the SSDP component when it's not needed * Update config_flow.py * Update test_config_flow.py --- homeassistant/components/deconz/config_flow.py | 4 +++- homeassistant/components/hue/config_flow.py | 3 ++- tests/components/deconz/test_config_flow.py | 11 ++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index ea93cc590e2..1e5eabd0a99 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -9,7 +9,6 @@ from pydeconz.utils import ( async_discovery, async_get_api_key, async_get_bridgeid) from homeassistant import config_entries -from homeassistant.components.ssdp import ATTR_MANUFACTURERURL, ATTR_SERIAL from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT from homeassistant.core import callback from homeassistant.helpers import aiohttp_client @@ -154,6 +153,9 @@ class DeconzFlowHandler(config_entries.ConfigFlow): async def async_step_ssdp(self, discovery_info): """Handle a discovered deCONZ bridge.""" + from homeassistant.components.ssdp import ( + ATTR_MANUFACTURERURL, ATTR_SERIAL) + if discovery_info[ATTR_MANUFACTURERURL] != DECONZ_MANUFACTURERURL: return self.async_abort(reason='not_deconz_bridge') diff --git a/homeassistant/components/hue/config_flow.py b/homeassistant/components/hue/config_flow.py index d57706f7ac8..76a46d13ed5 100644 --- a/homeassistant/components/hue/config_flow.py +++ b/homeassistant/components/hue/config_flow.py @@ -8,7 +8,6 @@ import async_timeout import voluptuous as vol from homeassistant import config_entries -from homeassistant.components.ssdp import ATTR_MANUFACTURERURL from homeassistant.core import callback from homeassistant.helpers import aiohttp_client @@ -146,6 +145,8 @@ class HueFlowHandler(config_entries.ConfigFlow): This flow is triggered by the SSDP component. It will check if the host is already configured and delegate to the import step if not. """ + from homeassistant.components.ssdp import ATTR_MANUFACTURERURL + if discovery_info[ATTR_MANUFACTURERURL] != HUE_MANUFACTURERURL: return self.async_abort(reason='not_hue_bridge') diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index ac22c964151..398ea19f164 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -4,6 +4,7 @@ from unittest.mock import Mock, patch import asyncio from homeassistant.components.deconz import config_flow +from homeassistant.components.ssdp import ATTR_MANUFACTURERURL, ATTR_SERIAL from tests.common import MockConfigEntry import pydeconz @@ -175,8 +176,8 @@ async def test_bridge_ssdp_discovery(hass): data={ config_flow.CONF_HOST: '1.2.3.4', config_flow.CONF_PORT: 80, - config_flow.ATTR_SERIAL: 'id', - config_flow.ATTR_MANUFACTURERURL: + ATTR_SERIAL: 'id', + ATTR_MANUFACTURERURL: config_flow.DECONZ_MANUFACTURERURL, config_flow.ATTR_UUID: 'uuid:1234' }, @@ -192,7 +193,7 @@ async def test_bridge_ssdp_discovery_not_deconz_bridge(hass): result = await hass.config_entries.flow.async_init( config_flow.DOMAIN, data={ - config_flow.ATTR_MANUFACTURERURL: 'not deconz bridge' + ATTR_MANUFACTURERURL: 'not deconz bridge' }, context={'source': 'ssdp'} ) @@ -217,8 +218,8 @@ async def test_bridge_discovery_update_existing_entry(hass): config_flow.DOMAIN, data={ config_flow.CONF_HOST: 'mock-deconz', - config_flow.ATTR_SERIAL: 'id', - config_flow.ATTR_MANUFACTURERURL: + ATTR_SERIAL: 'id', + ATTR_MANUFACTURERURL: config_flow.DECONZ_MANUFACTURERURL, config_flow.ATTR_UUID: 'uuid:1234' }, From 3c6235bee5d90e3c60c8b90f5ac959c3bb46b653 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Mon, 10 Jun 2019 18:12:17 +0200 Subject: [PATCH 03/12] Axis discovery MAC filter (#24442) * Make sure to abort if the MAC is not from Axis * Fix tests * Andrew Sayre suggestion Co-Authored-By: Andrew Sayre <6730289+andrewsayre@users.noreply.github.com> --- homeassistant/components/axis/config_flow.py | 8 ++++- homeassistant/components/axis/strings.json | 5 +-- tests/components/axis/test_config_flow.py | 34 +++++++++++++++----- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/axis/config_flow.py b/homeassistant/components/axis/config_flow.py index 2aa5c4de16e..410fb62c139 100644 --- a/homeassistant/components/axis/config_flow.py +++ b/homeassistant/components/axis/config_flow.py @@ -14,6 +14,8 @@ from .const import CONF_MODEL, DOMAIN from .device import get_device from .errors import AlreadyConfigured, AuthenticationRequired, CannotConnect +AXIS_OUI = {'00408C', 'ACCC8E', 'B8A44F'} + CONFIG_FILE = 'axis.conf' EVENT_TYPES = ['motion', 'vmd3', 'pir', 'sound', @@ -151,10 +153,14 @@ class AxisFlowHandler(config_entries.ConfigFlow): This flow is triggered by the discovery component. """ + serialnumber = discovery_info['properties']['macaddress'] + + if serialnumber[:6] not in AXIS_OUI: + return self.async_abort(reason='not_axis_device') + if discovery_info[CONF_HOST].startswith('169.254'): return self.async_abort(reason='link_local_address') - serialnumber = discovery_info['properties']['macaddress'] # pylint: disable=unsupported-assignment-operation self.context['macaddress'] = serialnumber diff --git a/homeassistant/components/axis/strings.json b/homeassistant/components/axis/strings.json index ebefbecf311..29fe09b7e5b 100644 --- a/homeassistant/components/axis/strings.json +++ b/homeassistant/components/axis/strings.json @@ -21,7 +21,8 @@ "abort": { "already_configured": "Device is already configured", "bad_config_file": "Bad data from config file", - "link_local_address": "Link local addresses are not supported" + "link_local_address": "Link local addresses are not supported", + "not_axis_device": "Discovered device not an Axis device" } } -} \ No newline at end of file +} diff --git a/tests/components/axis/test_config_flow.py b/tests/components/axis/test_config_flow.py index ebd2062ee0f..d6f8b7c6042 100644 --- a/tests/components/axis/test_config_flow.py +++ b/tests/components/axis/test_config_flow.py @@ -169,7 +169,7 @@ async def test_zeroconf_flow(hass): data={ config_flow.CONF_HOST: '1.2.3.4', config_flow.CONF_PORT: 80, - 'properties': {'macaddress': '1234'} + 'properties': {'macaddress': '00408C12345'} }, context={'source': 'zeroconf'} ) @@ -184,7 +184,7 @@ async def test_zeroconf_flow_known_device(hass): This is legacy support from devices registered with configurator. """ with patch('homeassistant.components.axis.config_flow.load_json', - return_value={'1234ABCD': { + return_value={'00408C12345': { config_flow.CONF_HOST: '2.3.4.5', config_flow.CONF_USERNAME: 'user', config_flow.CONF_PASSWORD: 'pass', @@ -208,7 +208,7 @@ async def test_zeroconf_flow_known_device(hass): config_flow.CONF_HOST: '1.2.3.4', config_flow.CONF_PORT: 80, 'hostname': 'name', - 'properties': {'macaddress': '1234ABCD'} + 'properties': {'macaddress': '00408C12345'} }, context={'source': 'zeroconf'} ) @@ -221,7 +221,7 @@ async def test_zeroconf_flow_already_configured(hass): entry = MockConfigEntry( domain=axis.DOMAIN, data={axis.CONF_DEVICE: {axis.config_flow.CONF_HOST: '1.2.3.4'}, - axis.config_flow.CONF_MAC: '1234ABCD'} + axis.config_flow.CONF_MAC: '00408C12345'} ) entry.add_to_hass(hass) @@ -233,7 +233,7 @@ async def test_zeroconf_flow_already_configured(hass): config_flow.CONF_PASSWORD: 'pass', config_flow.CONF_PORT: 80, 'hostname': 'name', - 'properties': {'macaddress': '1234ABCD'} + 'properties': {'macaddress': '00408C12345'} }, context={'source': 'zeroconf'} ) @@ -242,11 +242,29 @@ async def test_zeroconf_flow_already_configured(hass): assert result['reason'] == 'already_configured' +async def test_zeroconf_flow_ignore_non_axis_device(hass): + """Test that zeroconf doesn't setup devices with link local addresses.""" + result = await hass.config_entries.flow.async_init( + config_flow.DOMAIN, + data={ + config_flow.CONF_HOST: '169.254.3.4', + 'properties': {'macaddress': '01234567890'} + }, + context={'source': 'zeroconf'} + ) + + assert result['type'] == 'abort' + assert result['reason'] == 'not_axis_device' + + async def test_zeroconf_flow_ignore_link_local_address(hass): """Test that zeroconf doesn't setup devices with link local addresses.""" result = await hass.config_entries.flow.async_init( config_flow.DOMAIN, - data={config_flow.CONF_HOST: '169.254.3.4'}, + data={ + config_flow.CONF_HOST: '169.254.3.4', + 'properties': {'macaddress': '00408C12345'} + }, context={'source': 'zeroconf'} ) @@ -257,7 +275,7 @@ async def test_zeroconf_flow_ignore_link_local_address(hass): async def test_zeroconf_flow_bad_config_file(hass): """Test that zeroconf discovery with bad config files abort.""" with patch('homeassistant.components.axis.config_flow.load_json', - return_value={'1234ABCD': { + return_value={'00408C12345': { config_flow.CONF_HOST: '2.3.4.5', config_flow.CONF_USERNAME: 'user', config_flow.CONF_PASSWORD: 'pass', @@ -268,7 +286,7 @@ async def test_zeroconf_flow_bad_config_file(hass): config_flow.DOMAIN, data={ config_flow.CONF_HOST: '1.2.3.4', - 'properties': {'macaddress': '1234ABCD'} + 'properties': {'macaddress': '00408C12345'} }, context={'source': 'zeroconf'} ) From 21d04b3e14fefa067c1096b25217196b92dce990 Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Mon, 10 Jun 2019 21:46:38 +0200 Subject: [PATCH 04/12] Remember gpslogger entities across restarts (fixes #24432) (#24444) * Remember gpslogger entities across restarts (fixes #24432) * oops, missed those changes * Remove to do and set defaults to `None` --- .../components/gpslogger/__init__.py | 18 ++--- homeassistant/components/gpslogger/const.py | 8 +++ .../components/gpslogger/device_tracker.py | 71 ++++++++++++++++++- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/gpslogger/__init__.py b/homeassistant/components/gpslogger/__init__.py index 2123421334a..869b4b66987 100644 --- a/homeassistant/components/gpslogger/__init__.py +++ b/homeassistant/components/gpslogger/__init__.py @@ -11,19 +11,21 @@ from homeassistant.const import HTTP_UNPROCESSABLE_ENTITY, \ from homeassistant.helpers import config_entry_flow from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER -from .const import DOMAIN +from .const import ( + DOMAIN, + ATTR_ALTITUDE, + ATTR_ACCURACY, + ATTR_ACTIVITY, + ATTR_DEVICE, + ATTR_DIRECTION, + ATTR_PROVIDER, + ATTR_SPEED, +) _LOGGER = logging.getLogger(__name__) TRACKER_UPDATE = '{}_tracker_update'.format(DOMAIN) -ATTR_ALTITUDE = 'altitude' -ATTR_ACCURACY = 'accuracy' -ATTR_ACTIVITY = 'activity' -ATTR_DEVICE = 'device' -ATTR_DIRECTION = 'direction' -ATTR_PROVIDER = 'provider' -ATTR_SPEED = 'speed' DEFAULT_ACCURACY = 200 DEFAULT_BATTERY = -1 diff --git a/homeassistant/components/gpslogger/const.py b/homeassistant/components/gpslogger/const.py index e37c7f0d77b..870c5310f29 100644 --- a/homeassistant/components/gpslogger/const.py +++ b/homeassistant/components/gpslogger/const.py @@ -1,3 +1,11 @@ """Const for GPSLogger.""" DOMAIN = 'gpslogger' + +ATTR_ALTITUDE = 'altitude' +ATTR_ACCURACY = 'accuracy' +ATTR_ACTIVITY = 'activity' +ATTR_DEVICE = 'device' +ATTR_DIRECTION = 'direction' +ATTR_PROVIDER = 'provider' +ATTR_SPEED = 'speed' diff --git a/homeassistant/components/gpslogger/device_tracker.py b/homeassistant/components/gpslogger/device_tracker.py index 49d421cbc8c..d4b6b3c53cc 100644 --- a/homeassistant/components/gpslogger/device_tracker.py +++ b/homeassistant/components/gpslogger/device_tracker.py @@ -2,14 +2,29 @@ import logging from homeassistant.core import callback +from homeassistant.const import ( + ATTR_BATTERY_LEVEL, + ATTR_GPS_ACCURACY, + ATTR_LATITUDE, + ATTR_LONGITUDE, +) from homeassistant.components.device_tracker import SOURCE_TYPE_GPS from homeassistant.components.device_tracker.config_entry import ( DeviceTrackerEntity ) +from homeassistant.helpers import device_registry from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.helpers.typing import HomeAssistantType from . import DOMAIN as GPL_DOMAIN, TRACKER_UPDATE +from .const import ( + ATTR_ACTIVITY, + ATTR_ALTITUDE, + ATTR_DIRECTION, + ATTR_PROVIDER, + ATTR_SPEED, +) _LOGGER = logging.getLogger(__name__) @@ -32,8 +47,27 @@ async def async_setup_entry(hass: HomeAssistantType, entry, hass.data[GPL_DOMAIN]['unsub_device_tracker'][entry.entry_id] = \ async_dispatcher_connect(hass, TRACKER_UPDATE, _receive_data) + # Restore previously loaded devices + dev_reg = await device_registry.async_get_registry(hass) + dev_ids = { + identifier[1] + for device in dev_reg.devices.values() + for identifier in device.identifiers + if identifier[0] == GPL_DOMAIN + } + if not dev_ids: + return -class GPSLoggerEntity(DeviceTrackerEntity): + entities = [] + for dev_id in dev_ids: + hass.data[GPL_DOMAIN]['devices'].add(dev_id) + entity = GPSLoggerEntity(dev_id, None, None, None, None) + entities.append(entity) + + async_add_entities(entities) + + +class GPSLoggerEntity(DeviceTrackerEntity, RestoreEntity): """Represent a tracked device.""" def __init__( @@ -102,11 +136,46 @@ class GPSLoggerEntity(DeviceTrackerEntity): async def async_added_to_hass(self): """Register state update callback.""" + await super().async_added_to_hass() self._unsub_dispatcher = async_dispatcher_connect( self.hass, TRACKER_UPDATE, self._async_receive_data) + # don't restore if we got created with data + if self._location is not None: + return + + state = await self.async_get_last_state() + if state is None: + self._location = (None, None) + self._accuracy = None + self._attributes = { + ATTR_ALTITUDE: None, + ATTR_ACTIVITY: None, + ATTR_DIRECTION: None, + ATTR_PROVIDER: None, + ATTR_SPEED: None, + } + self._battery = None + return + + attr = state.attributes + self._location = ( + attr.get(ATTR_LATITUDE), + attr.get(ATTR_LONGITUDE), + ) + self._accuracy = attr.get(ATTR_GPS_ACCURACY) + self._attributes = { + ATTR_ALTITUDE: attr.get(ATTR_ALTITUDE), + ATTR_ACTIVITY: attr.get(ATTR_ACTIVITY), + ATTR_DIRECTION: attr.get(ATTR_DIRECTION), + ATTR_PROVIDER: attr.get(ATTR_PROVIDER), + ATTR_SPEED: attr.get(ATTR_SPEED), + } + self._battery = attr.get(ATTR_BATTERY_LEVEL) + async def async_will_remove_from_hass(self): """Clean up after entity before removal.""" + await super().async_will_remove_from_hass() self._unsub_dispatcher() @callback From 7292f2be69745f24dc5ce4251bc0bcc8bfeaaa2e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 10 Jun 2019 16:05:43 -0700 Subject: [PATCH 05/12] Update Hass.io when core config is updated (#24461) * Update Hass.io when core config is updated * Lint * Fix tests * Lint sigh --- homeassistant/components/hassio/__init__.py | 12 +++++-- homeassistant/components/hassio/handler.py | 6 ++-- tests/components/hassio/conftest.py | 15 +++++--- tests/components/hassio/test_addon_panel.py | 4 +-- tests/components/hassio/test_init.py | 40 +++++++++++---------- 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index c8c0f6c9f19..7e8afdc5312 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -9,7 +9,8 @@ from homeassistant.auth.const import GROUP_ID_ADMIN from homeassistant.components.homeassistant import SERVICE_CHECK_CONFIG import homeassistant.config as conf_util from homeassistant.const import ( - ATTR_NAME, SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP) + ATTR_NAME, SERVICE_HOMEASSISTANT_RESTART, SERVICE_HOMEASSISTANT_STOP, + EVENT_CORE_CONFIG_UPDATE) from homeassistant.core import DOMAIN as HASS_DOMAIN, callback from homeassistant.exceptions import HomeAssistantError import homeassistant.helpers.config_validation as cv @@ -194,8 +195,13 @@ async def async_setup(hass, config): await hassio.update_hass_api(config.get('http', {}), refresh_token.token) - if 'homeassistant' in config: - await hassio.update_hass_timezone(config['homeassistant']) + async def push_config(_): + """Push core config to Hass.io.""" + await hassio.update_hass_timezone(str(hass.config.time_zone)) + + hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, push_config) + + await push_config(None) async def async_service_handler(service): """Handle service calls for Hass.io.""" diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 1e6e1c2fffe..5e7932acbae 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -11,7 +11,7 @@ from homeassistant.components.http import ( CONF_SERVER_PORT, CONF_SSL_CERTIFICATE, ) -from homeassistant.const import CONF_TIME_ZONE, SERVER_PORT +from homeassistant.const import SERVER_PORT from .const import X_HASSIO @@ -140,13 +140,13 @@ class HassIO: payload=options) @_api_bool - def update_hass_timezone(self, core_config): + def update_hass_timezone(self, timezone): """Update Home-Assistant timezone data on Hass.io. This method return a coroutine. """ return self.send_command("/supervisor/options", payload={ - 'timezone': core_config.get(CONF_TIME_ZONE) + 'timezone': timezone }) async def send_command(self, command, method="post", payload=None, diff --git a/tests/components/hassio/conftest.py b/tests/components/hassio/conftest.py index f69be17a9e7..7f3a9a32dd9 100644 --- a/tests/components/hassio/conftest.py +++ b/tests/components/hassio/conftest.py @@ -29,11 +29,16 @@ def hassio_env(): @pytest.fixture def hassio_stubs(hassio_env, hass, hass_client, aioclient_mock): """Create mock hassio http client.""" - with patch('homeassistant.components.hassio.HassIO.update_hass_api', - Mock(return_value=mock_coro({"result": "ok"}))), \ - patch('homeassistant.components.hassio.HassIO.' - 'get_homeassistant_info', - Mock(side_effect=HassioAPIError())): + with patch( + 'homeassistant.components.hassio.HassIO.update_hass_api', + return_value=mock_coro({"result": "ok"}) + ), patch( + 'homeassistant.components.hassio.HassIO.update_hass_timezone', + return_value=mock_coro({"result": "ok"}) + ), patch( + 'homeassistant.components.hassio.HassIO.get_homeassistant_info', + side_effect=HassioAPIError() + ): hass.state = CoreState.starting hass.loop.run_until_complete(async_setup_component(hass, 'hassio', { 'http': { diff --git a/tests/components/hassio/test_addon_panel.py b/tests/components/hassio/test_addon_panel.py index 05915218659..d765b6ac173 100644 --- a/tests/components/hassio/test_addon_panel.py +++ b/tests/components/hassio/test_addon_panel.py @@ -56,7 +56,7 @@ async def test_hassio_addon_panel_startup(hass, aioclient_mock, hassio_env): }) await hass.async_block_till_done() - assert aioclient_mock.call_count == 2 + assert aioclient_mock.call_count == 3 assert mock_panel.called mock_panel.assert_called_with( hass, 'test1', { @@ -98,7 +98,7 @@ async def test_hassio_addon_panel_api(hass, aioclient_mock, hassio_env, }) await hass.async_block_till_done() - assert aioclient_mock.call_count == 2 + assert aioclient_mock.call_count == 3 assert mock_panel.called mock_panel.assert_called_with( hass, 'test1', { diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 7b8fad3ec09..da8360a4834 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -43,7 +43,7 @@ def test_setup_api_ping(hass, aioclient_mock): result = yield from async_setup_component(hass, 'hassio', {}) assert result - assert aioclient_mock.call_count == 4 + assert aioclient_mock.call_count == 5 assert hass.components.hassio.get_homeassistant_version() == "10.0" assert hass.components.hassio.is_hassio() @@ -82,7 +82,7 @@ def test_setup_api_push_api_data(hass, aioclient_mock): }) assert result - assert aioclient_mock.call_count == 4 + assert aioclient_mock.call_count == 5 assert not aioclient_mock.mock_calls[1][2]['ssl'] assert aioclient_mock.mock_calls[1][2]['port'] == 9999 assert aioclient_mock.mock_calls[1][2]['watchdog'] @@ -101,7 +101,7 @@ def test_setup_api_push_api_data_server_host(hass, aioclient_mock): }) assert result - assert aioclient_mock.call_count == 4 + assert aioclient_mock.call_count == 5 assert not aioclient_mock.mock_calls[1][2]['ssl'] assert aioclient_mock.mock_calls[1][2]['port'] == 9999 assert not aioclient_mock.mock_calls[1][2]['watchdog'] @@ -117,7 +117,7 @@ async def test_setup_api_push_api_data_default(hass, aioclient_mock, }) assert result - assert aioclient_mock.call_count == 4 + assert aioclient_mock.call_count == 5 assert not aioclient_mock.mock_calls[1][2]['ssl'] assert aioclient_mock.mock_calls[1][2]['port'] == 8123 refresh_token = aioclient_mock.mock_calls[1][2]['refresh_token'] @@ -177,27 +177,29 @@ async def test_setup_api_existing_hassio_user(hass, aioclient_mock, }) assert result - assert aioclient_mock.call_count == 4 + assert aioclient_mock.call_count == 5 assert not aioclient_mock.mock_calls[1][2]['ssl'] assert aioclient_mock.mock_calls[1][2]['port'] == 8123 assert aioclient_mock.mock_calls[1][2]['refresh_token'] == token.token -@asyncio.coroutine -def test_setup_core_push_timezone(hass, aioclient_mock): +async def test_setup_core_push_timezone(hass, aioclient_mock): """Test setup with API push default data.""" + hass.config.time_zone = 'testzone' + with patch.dict(os.environ, MOCK_ENVIRON): - result = yield from async_setup_component(hass, 'hassio', { + result = await async_setup_component(hass, 'hassio', { 'hassio': {}, - 'homeassistant': { - 'time_zone': 'testzone', - }, }) assert result assert aioclient_mock.call_count == 5 assert aioclient_mock.mock_calls[2][2]['timezone'] == "testzone" + await hass.config.async_update(time_zone='America/New_York') + await hass.async_block_till_done() + assert aioclient_mock.mock_calls[-1][2]['timezone'] == "America/New_York" + @asyncio.coroutine def test_setup_hassio_no_additional_data(hass, aioclient_mock): @@ -209,7 +211,7 @@ def test_setup_hassio_no_additional_data(hass, aioclient_mock): }) assert result - assert aioclient_mock.call_count == 4 + assert aioclient_mock.call_count == 5 assert aioclient_mock.mock_calls[-1][3]['X-Hassio-Key'] == "123456" @@ -288,14 +290,14 @@ def test_service_calls(hassio_env, hass, aioclient_mock): 'hassio', 'addon_stdin', {'addon': 'test', 'input': 'test'}) yield from hass.async_block_till_done() - assert aioclient_mock.call_count == 6 + assert aioclient_mock.call_count == 7 assert aioclient_mock.mock_calls[-1][2] == 'test' yield from hass.services.async_call('hassio', 'host_shutdown', {}) yield from hass.services.async_call('hassio', 'host_reboot', {}) yield from hass.async_block_till_done() - assert aioclient_mock.call_count == 8 + assert aioclient_mock.call_count == 9 yield from hass.services.async_call('hassio', 'snapshot_full', {}) yield from hass.services.async_call('hassio', 'snapshot_partial', { @@ -305,7 +307,7 @@ def test_service_calls(hassio_env, hass, aioclient_mock): }) yield from hass.async_block_till_done() - assert aioclient_mock.call_count == 10 + assert aioclient_mock.call_count == 11 assert aioclient_mock.mock_calls[-1][2] == { 'addons': ['test'], 'folders': ['ssl'], 'password': "123456"} @@ -321,7 +323,7 @@ def test_service_calls(hassio_env, hass, aioclient_mock): }) yield from hass.async_block_till_done() - assert aioclient_mock.call_count == 12 + assert aioclient_mock.call_count == 13 assert aioclient_mock.mock_calls[-1][2] == { 'addons': ['test'], 'folders': ['ssl'], 'homeassistant': False, 'password': "123456" @@ -341,12 +343,12 @@ def test_service_calls_core(hassio_env, hass, aioclient_mock): yield from hass.services.async_call('homeassistant', 'stop') yield from hass.async_block_till_done() - assert aioclient_mock.call_count == 3 + assert aioclient_mock.call_count == 4 yield from hass.services.async_call('homeassistant', 'check_config') yield from hass.async_block_till_done() - assert aioclient_mock.call_count == 3 + assert aioclient_mock.call_count == 4 with patch( 'homeassistant.config.async_check_ha_config_file', @@ -356,4 +358,4 @@ def test_service_calls_core(hassio_env, hass, aioclient_mock): yield from hass.async_block_till_done() assert mock_check_config.called - assert aioclient_mock.call_count == 4 + assert aioclient_mock.call_count == 5 From bd803465929fde30f7000f5b9a6acadfd4a4003a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 10 Jun 2019 16:05:32 -0700 Subject: [PATCH 06/12] Sun listener to adapt to core config updates (#24464) * Adaptable sun listener * Lint --- homeassistant/helpers/event.py | 117 ++++++++++++++++++++------------- tests/helpers/test_event.py | 62 +++++++++++++++++ 2 files changed, 134 insertions(+), 45 deletions(-) diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index 5e262a47565..009c2b1e898 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -1,15 +1,18 @@ """Helpers for listening to events.""" from datetime import timedelta import functools as ft +from typing import Callable + +import attr from homeassistant.loader import bind_hass from homeassistant.helpers.sun import get_astral_event_next -from ..core import HomeAssistant, callback -from ..const import ( +from homeassistant.core import HomeAssistant, callback +from homeassistant.const import ( ATTR_NOW, EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL, - SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET) -from ..util import dt as dt_util -from ..util.async_ import run_callback_threadsafe + SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET, EVENT_CORE_CONFIG_UPDATE) +from homeassistant.util import dt as dt_util +from homeassistant.util.async_ import run_callback_threadsafe # PyLint does not like the use of threaded_listener_factory # pylint: disable=invalid-name @@ -263,30 +266,71 @@ def async_track_time_interval(hass, action, interval): track_time_interval = threaded_listener_factory(async_track_time_interval) +@attr.s +class SunListener: + """Helper class to help listen to sun events.""" + + hass = attr.ib(type=HomeAssistant) + action = attr.ib(type=Callable) + event = attr.ib(type=str) + offset = attr.ib(type=timedelta) + _unsub_sun = attr.ib(default=None) + _unsub_config = attr.ib(default=None) + + @callback + def async_attach(self): + """Attach a sun listener.""" + assert self._unsub_config is None + + self._unsub_config = self.hass.bus.async_listen( + EVENT_CORE_CONFIG_UPDATE, self._handle_config_event) + + self._listen_next_sun_event() + + @callback + def async_detach(self): + """Detach the sun listener.""" + assert self._unsub_sun is not None + assert self._unsub_config is not None + + self._unsub_sun() + self._unsub_sun = None + self._unsub_config() + self._unsub_config = None + + @callback + def _listen_next_sun_event(self): + """Set up the sun event listener.""" + assert self._unsub_sun is None + + self._unsub_sun = async_track_point_in_utc_time( + self.hass, self._handle_sun_event, + get_astral_event_next(self.hass, self.event, offset=self.offset) + ) + + @callback + def _handle_sun_event(self, _now): + """Handle solar event.""" + self._unsub_sun = None + self._listen_next_sun_event() + self.hass.async_run_job(self.action) + + @callback + def _handle_config_event(self, _event): + """Handle core config update.""" + assert self._unsub_sun is not None + self._unsub_sun() + self._unsub_sun = None + self._listen_next_sun_event() + + @callback @bind_hass def async_track_sunrise(hass, action, offset=None): """Add a listener that will fire a specified offset from sunrise daily.""" - remove = None - - @callback - def sunrise_automation_listener(now): - """Handle points in time to execute actions.""" - nonlocal remove - remove = async_track_point_in_utc_time( - hass, sunrise_automation_listener, get_astral_event_next( - hass, SUN_EVENT_SUNRISE, offset=offset)) - hass.async_run_job(action) - - remove = async_track_point_in_utc_time( - hass, sunrise_automation_listener, get_astral_event_next( - hass, SUN_EVENT_SUNRISE, offset=offset)) - - def remove_listener(): - """Remove sunset listener.""" - remove() - - return remove_listener + listener = SunListener(hass, action, SUN_EVENT_SUNRISE, offset) + listener.async_attach() + return listener.async_detach track_sunrise = threaded_listener_factory(async_track_sunrise) @@ -296,26 +340,9 @@ track_sunrise = threaded_listener_factory(async_track_sunrise) @bind_hass def async_track_sunset(hass, action, offset=None): """Add a listener that will fire a specified offset from sunset daily.""" - remove = None - - @callback - def sunset_automation_listener(now): - """Handle points in time to execute actions.""" - nonlocal remove - remove = async_track_point_in_utc_time( - hass, sunset_automation_listener, get_astral_event_next( - hass, SUN_EVENT_SUNSET, offset=offset)) - hass.async_run_job(action) - - remove = async_track_point_in_utc_time( - hass, sunset_automation_listener, get_astral_event_next( - hass, SUN_EVENT_SUNSET, offset=offset)) - - def remove_listener(): - """Remove sunset listener.""" - remove() - - return remove_listener + listener = SunListener(hass, action, SUN_EVENT_SUNSET, offset) + listener.async_attach() + return listener.async_detach track_sunset = threaded_listener_factory(async_track_sunset) diff --git a/tests/helpers/test_event.py b/tests/helpers/test_event.py index 0756bab2eec..55900b7c80a 100644 --- a/tests/helpers/test_event.py +++ b/tests/helpers/test_event.py @@ -436,6 +436,68 @@ async def test_track_sunrise(hass): assert len(offset_runs) == 1 +async def test_track_sunrise_update_location(hass): + """Test track the sunrise.""" + # Setup sun component + hass.config.latitude = 32.87336 + hass.config.longitude = 117.22743 + assert await async_setup_component(hass, sun.DOMAIN, { + sun.DOMAIN: {sun.CONF_ELEVATION: 0}}) + + # Get next sunrise + astral = Astral() + utc_now = datetime(2014, 5, 24, 12, 0, 0, tzinfo=dt_util.UTC) + utc_today = utc_now.date() + + mod = -1 + while True: + next_rising = (astral.sunrise_utc( + utc_today + timedelta(days=mod), + hass.config.latitude, hass.config.longitude)) + if next_rising > utc_now: + break + mod += 1 + + # Track sunrise + runs = [] + with patch('homeassistant.util.dt.utcnow', return_value=utc_now): + async_track_sunrise(hass, lambda: runs.append(1)) + + # Mimick sunrise + _send_time_changed(hass, next_rising) + await hass.async_block_till_done() + assert len(runs) == 1 + + # Move! + with patch('homeassistant.util.dt.utcnow', return_value=utc_now): + await hass.config.async_update( + latitude=40.755931, + longitude=-73.984606, + ) + await hass.async_block_till_done() + + # Mimick sunrise + _send_time_changed(hass, next_rising) + await hass.async_block_till_done() + # Did not increase + assert len(runs) == 1 + + # Get next sunrise + mod = -1 + while True: + next_rising = (astral.sunrise_utc( + utc_today + timedelta(days=mod), + hass.config.latitude, hass.config.longitude)) + if next_rising > utc_now: + break + mod += 1 + + # Mimick sunrise at new location + _send_time_changed(hass, next_rising) + await hass.async_block_till_done() + assert len(runs) == 2 + + async def test_track_sunset(hass): """Test track the sunset.""" latitude = 32.87336 From d88d57f3bb44691a61c92d79f2000748a3c786ea Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 10 Jun 2019 16:06:12 -0700 Subject: [PATCH 07/12] Bumped version to 0.94.2 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 52d13ec58a6..fc0d248de9f 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 94 -PATCH_VERSION = '1' +PATCH_VERSION = '2' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3) From 6ea0575a4ae2c49f70c2e0e74284a87687ba4d07 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 31 May 2019 23:03:45 -0700 Subject: [PATCH 08/12] Update home zone when core config updated (#24237) * Update home zone when core config updated * Lint --- homeassistant/components/config/core.py | 2 +- homeassistant/components/zone/__init__.py | 28 +++++++++++++++++------ homeassistant/components/zone/zone.py | 17 ++++++-------- homeassistant/core.py | 12 +++------- tests/components/zone/test_init.py | 21 +++++++++++++++++ tests/test_config.py | 2 +- tests/test_core.py | 6 ++--- 7 files changed, 57 insertions(+), 31 deletions(-) diff --git a/homeassistant/components/config/core.py b/homeassistant/components/config/core.py index 31abb832f23..a83516bdc37 100644 --- a/homeassistant/components/config/core.py +++ b/homeassistant/components/config/core.py @@ -56,7 +56,7 @@ async def websocket_update_config(hass, connection, msg): data.pop('type') try: - await hass.config.update(**data) + await hass.config.async_update(**data) connection.send_result(msg['id']) except ValueError as err: connection.send_error( diff --git a/homeassistant/components/zone/__init__.py b/homeassistant/components/zone/__init__.py index 0340964561c..1ece0dbaaa1 100644 --- a/homeassistant/components/zone/__init__.py +++ b/homeassistant/components/zone/__init__.py @@ -3,10 +3,12 @@ import logging import voluptuous as vol +from homeassistant.core import callback from homeassistant.loader import bind_hass import homeassistant.helpers.config_validation as cv from homeassistant.const import ( - CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_ICON, CONF_RADIUS) + CONF_NAME, CONF_LATITUDE, CONF_LONGITUDE, CONF_ICON, CONF_RADIUS, + EVENT_CORE_CONFIG_UPDATE) from homeassistant.helpers import config_per_platform from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.util import slugify @@ -90,12 +92,24 @@ async def async_setup(hass, config): hass.async_create_task(zone.async_update_ha_state()) entities.add(zone.entity_id) - if ENTITY_ID_HOME not in entities and HOME_ZONE not in zone_entries: - zone = Zone(hass, hass.config.location_name, - hass.config.latitude, hass.config.longitude, - DEFAULT_RADIUS, ICON_HOME, False) - zone.entity_id = ENTITY_ID_HOME - hass.async_create_task(zone.async_update_ha_state()) + if ENTITY_ID_HOME in entities or HOME_ZONE in zone_entries: + return True + + zone = Zone(hass, hass.config.location_name, + hass.config.latitude, hass.config.longitude, + DEFAULT_RADIUS, ICON_HOME, False) + zone.entity_id = ENTITY_ID_HOME + hass.async_create_task(zone.async_update_ha_state()) + + @callback + def core_config_updated(_): + """Handle core config updated.""" + zone.name = hass.config.location_name + zone.latitude = hass.config.latitude + zone.longitude = hass.config.longitude + zone.async_write_ha_state() + + hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, core_config_updated) return True diff --git a/homeassistant/components/zone/zone.py b/homeassistant/components/zone/zone.py index 20155e06311..51e2a623def 100644 --- a/homeassistant/components/zone/zone.py +++ b/homeassistant/components/zone/zone.py @@ -23,21 +23,18 @@ def in_zone(zone, latitude, longitude, radius=0) -> bool: class Zone(Entity): """Representation of a Zone.""" + name = None + def __init__(self, hass, name, latitude, longitude, radius, icon, passive): """Initialize the zone.""" self.hass = hass - self._name = name - self._latitude = latitude - self._longitude = longitude + self.name = name + self.latitude = latitude + self.longitude = longitude self._radius = radius self._icon = icon self._passive = passive - @property - def name(self): - """Return the name of the zone.""" - return self._name - @property def state(self): """Return the state property really does nothing for a zone.""" @@ -53,8 +50,8 @@ class Zone(Entity): """Return the state attributes of the zone.""" data = { ATTR_HIDDEN: True, - ATTR_LATITUDE: self._latitude, - ATTR_LONGITUDE: self._longitude, + ATTR_LATITUDE: self.latitude, + ATTR_LONGITUDE: self.longitude, ATTR_RADIUS: self._radius, } if self._passive: diff --git a/homeassistant/core.py b/homeassistant/core.py index b732eb0d4b3..ef15a4b11a0 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1288,10 +1288,7 @@ class Config: unit_system: Optional[str] = None, location_name: Optional[str] = None, time_zone: Optional[str] = None) -> None: - """Update the configuration from a dictionary. - - Async friendly. - """ + """Update the configuration from a dictionary.""" self.config_source = source if latitude is not None: self.latitude = latitude @@ -1309,11 +1306,8 @@ class Config: if time_zone is not None: self.set_time_zone(time_zone) - async def update(self, **kwargs: Any) -> None: - """Update the configuration from a dictionary. - - Async friendly. - """ + async def async_update(self, **kwargs: Any) -> None: + """Update the configuration from a dictionary.""" self._update(source=SOURCE_STORAGE, **kwargs) await self.async_store() self.hass.bus.async_fire( diff --git a/tests/components/zone/test_init.py b/tests/components/zone/test_init.py index 576be0ce03c..11fe9ae5e66 100644 --- a/tests/components/zone/test_init.py +++ b/tests/components/zone/test_init.py @@ -221,3 +221,24 @@ class TestComponentZone(unittest.TestCase): assert zone.zone.in_zone(self.hass.states.get('zone.passive_zone'), latitude, longitude) + + +async def test_core_config_update(hass): + """Test updating core config will update home zone.""" + assert await setup.async_setup_component(hass, 'zone', {}) + + home = hass.states.get('zone.home') + + await hass.config.async_update( + location_name='Updated Name', + latitude=10, + longitude=20, + ) + await hass.async_block_till_done() + + home_updated = hass.states.get('zone.home') + + assert home is not home_updated + assert home_updated.name == 'Updated Name' + assert home_updated.attributes['latitude'] == 10 + assert home_updated.attributes['longitude'] == 20 diff --git a/tests/test_config.py b/tests/test_config.py index 29058f185ad..8e983c673c5 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -444,7 +444,7 @@ async def test_updating_configuration(hass, hass_storage): hass_storage["core.config"] = dict(core_data) await config_util.async_process_ha_core_config( hass, {'whitelist_external_dirs': '/tmp'}) - await hass.config.update(latitude=50) + await hass.config.async_update(latitude=50) new_core_data = copy.deepcopy(core_data) new_core_data['data']['latitude'] = 50 diff --git a/tests/test_core.py b/tests/test_core.py index 15ab2baf3a9..00bd4265da7 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -955,7 +955,7 @@ async def test_event_on_update(hass, hass_storage): assert hass.config.latitude != 12 - await hass.config.update(latitude=12) + await hass.config.async_update(latitude=12) await hass.async_block_till_done() assert hass.config.latitude == 12 @@ -963,10 +963,10 @@ async def test_event_on_update(hass, hass_storage): assert events[0].data == {'latitude': 12} -def test_bad_timezone_raises_value_error(hass): +async def test_bad_timezone_raises_value_error(hass): """Test bad timezone raises ValueError.""" with pytest.raises(ValueError): - hass.config.set_time_zone('not_a_timezone') + await hass.config.async_update(time_zone='not_a_timezone') @patch('homeassistant.core.monotonic') From 73d6dc6b6a29f89c878e00e2ee8f41b8d2285159 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 12 Jun 2019 09:29:02 -0700 Subject: [PATCH 09/12] Update hass-nabucasa to 0.14 (#24481) * Update hass-nabucasa to 0.14 * Update owner of cloud * Update codeowners --- CODEOWNERS | 2 +- homeassistant/components/cloud/manifest.json | 4 ++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 59bd8c31af1..636c1f5587e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -45,7 +45,7 @@ homeassistant/components/cisco_ios/* @fbradyirl homeassistant/components/cisco_mobility_express/* @fbradyirl homeassistant/components/cisco_webex_teams/* @fbradyirl homeassistant/components/ciscospark/* @fbradyirl -homeassistant/components/cloud/* @home-assistant/core +homeassistant/components/cloud/* @home-assistant/cloud homeassistant/components/cloudflare/* @ludeeus homeassistant/components/config/* @home-assistant/core homeassistant/components/configurator/* @home-assistant/core diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index 982b51133a5..1a4511c8c88 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -3,13 +3,13 @@ "name": "Cloud", "documentation": "https://www.home-assistant.io/components/cloud", "requirements": [ - "hass-nabucasa==0.13" + "hass-nabucasa==0.14" ], "dependencies": [ "http", "webhook" ], "codeowners": [ - "@home-assistant/core" + "@home-assistant/cloud" ] } diff --git a/requirements_all.txt b/requirements_all.txt index e097b075833..ba2b91f0aca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -547,7 +547,7 @@ habitipy==0.2.0 hangups==0.4.9 # homeassistant.components.cloud -hass-nabucasa==0.13 +hass-nabucasa==0.14 # homeassistant.components.mqtt hbmqtt==0.9.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 42d29717037..f87f5890dd7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -136,7 +136,7 @@ ha-ffmpeg==2.0 hangups==0.4.9 # homeassistant.components.cloud -hass-nabucasa==0.13 +hass-nabucasa==0.14 # homeassistant.components.mqtt hbmqtt==0.9.4 From 7260cada904ab759a9ad86acd6bb8f2e1afedd33 Mon Sep 17 00:00:00 2001 From: aidbish <38182386+aidbish@users.noreply.github.com> Date: Thu, 13 Jun 2019 07:21:00 +1000 Subject: [PATCH 10/12] missing comma preventing other voices (#24487) --- homeassistant/components/watson_tts/tts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/watson_tts/tts.py b/homeassistant/components/watson_tts/tts.py index be60908d096..552083854a2 100644 --- a/homeassistant/components/watson_tts/tts.py +++ b/homeassistant/components/watson_tts/tts.py @@ -23,7 +23,7 @@ SUPPORTED_VOICES = [ "de-DE_BirgitVoice", "de-DE_BirgitV2Voice", "de-DE_DieterVoice", - "de-DE_DieterV2Voice" + "de-DE_DieterV2Voice", "en-GB_KateVoice", "en-US_AllisonVoice", "en-US_AllisonV2Voice", From 3da0f5e384e6b3eae6fe32cbf07eedc4fa7fa6b2 Mon Sep 17 00:00:00 2001 From: Phil Bruckner Date: Wed, 12 Jun 2019 13:40:01 -0500 Subject: [PATCH 11/12] Fix owntracks source_type for location messages with default trigger (#24503) Some location update messages do not contain the 't' (trigger) key. Before the change in 0.94 to entity based trackers, these would default to source_type of 'gps' (due to default parameter value in async_see method.) To mirror this behavior in the new entity based tracker, the source_type property should default to SOURCE_TYPE_GPS under the same conditions. --- .../components/owntracks/device_tracker.py | 4 ++-- tests/components/owntracks/test_device_tracker.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/owntracks/device_tracker.py b/homeassistant/components/owntracks/device_tracker.py index 742b7c34435..b573e390a12 100644 --- a/homeassistant/components/owntracks/device_tracker.py +++ b/homeassistant/components/owntracks/device_tracker.py @@ -9,7 +9,7 @@ from homeassistant.const import ( ATTR_BATTERY_LEVEL, ) from homeassistant.components.device_tracker.const import ( - ENTITY_ID_FORMAT, ATTR_SOURCE_TYPE) + ENTITY_ID_FORMAT, ATTR_SOURCE_TYPE, SOURCE_TYPE_GPS) from homeassistant.components.device_tracker.config_entry import ( DeviceTrackerEntity ) @@ -127,7 +127,7 @@ class OwnTracksEntity(DeviceTrackerEntity, RestoreEntity): @property def source_type(self): """Return the source type, eg gps or router, of the device.""" - return self._data.get('source_type') + return self._data.get('source_type', SOURCE_TYPE_GPS) @property def device_info(self): diff --git a/tests/components/owntracks/test_device_tracker.py b/tests/components/owntracks/test_device_tracker.py index 7d8d48de586..5f2bda5957a 100644 --- a/tests/components/owntracks/test_device_tracker.py +++ b/tests/components/owntracks/test_device_tracker.py @@ -411,6 +411,19 @@ async def test_location_update(hass, context): """Test the update of a location.""" await send_message(hass, LOCATION_TOPIC, LOCATION_MESSAGE) + assert_location_source_type(hass, 'gps') + assert_location_latitude(hass, LOCATION_MESSAGE['lat']) + assert_location_accuracy(hass, LOCATION_MESSAGE['acc']) + assert_location_state(hass, 'outer') + + +async def test_location_update_no_t_key(hass, context): + """Test the update of a location when message does not contain 't'.""" + message = LOCATION_MESSAGE.copy() + message.pop('t') + await send_message(hass, LOCATION_TOPIC, message) + + assert_location_source_type(hass, 'gps') assert_location_latitude(hass, LOCATION_MESSAGE['lat']) assert_location_accuracy(hass, LOCATION_MESSAGE['acc']) assert_location_state(hass, 'outer') From 10c8f21f79ac183854441c864bdabed050d48233 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 12 Jun 2019 22:48:17 -0700 Subject: [PATCH 12/12] Bumped version to 0.94.3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index fc0d248de9f..c4575ee5108 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -2,7 +2,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 94 -PATCH_VERSION = '2' +PATCH_VERSION = '3' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) REQUIRED_PYTHON_VER = (3, 5, 3)