From fc58746bc3aaf8dec9188af98de00702dece402d Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 21 May 2019 07:21:31 +0200 Subject: [PATCH] Add websocket API for updating core config (#24009) * Add websocket API for updating core config --- homeassistant/components/config/core.py | 24 ++++++ tests/components/config/test_core.py | 103 +++++++++++++++++++++--- 2 files changed, 118 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/config/core.py b/homeassistant/components/config/core.py index ce7675c41f4..7e9008eb115 100644 --- a/homeassistant/components/config/core.py +++ b/homeassistant/components/config/core.py @@ -1,12 +1,16 @@ """Component to interact with Hassbian tools.""" +import voluptuous as vol + from homeassistant.components.http import HomeAssistantView from homeassistant.config import async_check_ha_config_file +from homeassistant.components import websocket_api async def async_setup(hass): """Set up the Hassbian config.""" hass.http.register_view(CheckConfigView) + hass.components.websocket_api.async_register_command(websocket_core_update) return True @@ -26,3 +30,23 @@ class CheckConfigView(HomeAssistantView): "result": state, "errors": errors, }) + + +@websocket_api.require_admin +@websocket_api.async_response +@websocket_api.websocket_command({ + vol.Required('type'): 'config/core/update', + vol.Optional('latitude'): vol.Coerce(float), + vol.Optional('longitude'): vol.Coerce(float), + vol.Optional('elevation'): vol.Coerce(int), + vol.Optional('unit_system'): vol.Coerce(str), + vol.Optional('location_name'): vol.Coerce(str), + vol.Optional('time_zone'): vol.Coerce(str), +}) +async def websocket_core_update(hass, connection, msg): + """Handle request for account info.""" + data = dict(msg) + data.pop('id') + data.pop('type') + await hass.config.update(**data) + connection.send_result(msg['id']) diff --git a/tests/components/config/test_core.py b/tests/components/config/test_core.py index 4d9063d774b..81b3818a815 100644 --- a/tests/components/config/test_core.py +++ b/tests/components/config/test_core.py @@ -4,35 +4,120 @@ from unittest.mock import patch from homeassistant.bootstrap import async_setup_component from homeassistant.components import config +from homeassistant.components.websocket_api.const import TYPE_RESULT +from homeassistant.const import CONF_UNIT_SYSTEM, CONF_UNIT_SYSTEM_IMPERIAL +import homeassistant.util.dt as dt_util from tests.common import mock_coro +ORIG_TIME_ZONE = dt_util.DEFAULT_TIME_ZONE -@asyncio.coroutine -def test_validate_config_ok(hass, hass_client): + +async def test_validate_config_ok(hass, hass_client): """Test checking config.""" with patch.object(config, 'SECTIONS', ['core']): - yield from async_setup_component(hass, 'config', {}) + await async_setup_component(hass, 'config', {}) - yield from asyncio.sleep(0.1, loop=hass.loop) + await asyncio.sleep(0.1, loop=hass.loop) - client = yield from hass_client() + client = await hass_client() with patch( 'homeassistant.components.config.core.async_check_ha_config_file', return_value=mock_coro()): - resp = yield from client.post('/api/config/core/check_config') + resp = await client.post('/api/config/core/check_config') assert resp.status == 200 - result = yield from resp.json() + result = await resp.json() assert result['result'] == 'valid' assert result['errors'] is None with patch( 'homeassistant.components.config.core.async_check_ha_config_file', return_value=mock_coro('beer')): - resp = yield from client.post('/api/config/core/check_config') + resp = await client.post('/api/config/core/check_config') assert resp.status == 200 - result = yield from resp.json() + result = await resp.json() assert result['result'] == 'invalid' assert result['errors'] == 'beer' + + +async def test_websocket_core_update(hass, hass_ws_client): + """Test core config update websocket command.""" + with patch.object(config, 'SECTIONS', ['core']): + await async_setup_component(hass, 'config', {}) + + assert hass.config.latitude != 60 + assert hass.config.longitude != 50 + assert hass.config.elevation != 25 + assert hass.config.location_name != 'Huis' + assert hass.config.units.name != CONF_UNIT_SYSTEM_IMPERIAL + assert hass.config.time_zone.zone != 'America/New_York' + + client = await hass_ws_client(hass) + await client.send_json({ + 'id': 5, + 'type': 'config/core/update', + 'latitude': 60, + 'longitude': 50, + 'elevation': 25, + 'location_name': 'Huis', + CONF_UNIT_SYSTEM: CONF_UNIT_SYSTEM_IMPERIAL, + 'time_zone': 'America/New_York', + }) + + msg = await client.receive_json() + + assert msg['id'] == 5 + assert msg['type'] == TYPE_RESULT + assert msg['success'] + assert hass.config.latitude == 60 + assert hass.config.longitude == 50 + assert hass.config.elevation == 25 + assert hass.config.location_name == 'Huis' + assert hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL + assert hass.config.time_zone.zone == 'America/New_York' + + dt_util.set_default_time_zone(ORIG_TIME_ZONE) + + +async def test_websocket_core_update_not_admin( + hass, hass_ws_client, hass_admin_user): + """Test core config fails for non admin.""" + hass_admin_user.groups = [] + with patch.object(config, 'SECTIONS', ['core']): + await async_setup_component(hass, 'config', {}) + + client = await hass_ws_client(hass) + await client.send_json({ + 'id': 6, + 'type': 'config/core/update', + 'latitude': 123, + }) + + msg = await client.receive_json() + + assert msg['id'] == 6 + assert msg['type'] == TYPE_RESULT + assert not msg['success'] + assert msg['error']['code'] == 'unauthorized' + + +async def test_websocket_bad_core_update(hass, hass_ws_client): + """Test core config update fails with bad parameters.""" + with patch.object(config, 'SECTIONS', ['core']): + await async_setup_component(hass, 'config', {}) + + client = await hass_ws_client(hass) + await client.send_json({ + 'id': 7, + 'type': 'config/core/update', + 'latituude': 123, + }) + + msg = await client.receive_json() + + assert msg['id'] == 7 + assert msg['type'] == TYPE_RESULT + assert not msg['success'] + assert msg['error']['code'] == 'invalid_format'