From 8fa999258915f84c0941986f578d81bec7cf5114 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 4 Sep 2018 09:24:42 +0200 Subject: [PATCH] Service to load new deCONZ devices without restart (#16308) * New service to load new devices from deCONZ without restarting HASS * Do not use len to check if list is empty * Add support for scenes to be updated as well * Rework refresh devices method * Fix test --- homeassistant/components/deconz/__init__.py | 54 +++++++++++++++++-- homeassistant/components/deconz/services.yaml | 4 +- homeassistant/components/scene/deconz.py | 19 ++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/scene/test_deconz.py | 1 + 6 files changed, 70 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index e9f797d95f9..6ed0a6e2c11 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -24,7 +24,7 @@ from .const import ( CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT, DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER) -REQUIREMENTS = ['pydeconz==45'] +REQUIREMENTS = ['pydeconz==47'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ @@ -46,6 +46,8 @@ SERVICE_SCHEMA = vol.Schema({ vol.Required(SERVICE_DATA): dict, }) +SERVICE_DEVICE_REFRESH = 'device_refresh' + async def async_setup(hass, config): """Load configuration for deCONZ component. @@ -84,15 +86,17 @@ async def async_setup_entry(hass, config_entry): @callback def async_add_device_callback(device_type, device): """Handle event of new device creation in deCONZ.""" + if not isinstance(device, list): + device = [device] async_dispatcher_send( - hass, 'deconz_new_{}'.format(device_type), [device]) + hass, 'deconz_new_{}'.format(device_type), device) session = aiohttp_client.async_get_clientsession(hass) deconz = DeconzSession(hass.loop, session, **config_entry.data, async_add_device=async_add_device_callback) result = await deconz.async_load_parameters() + if result is False: - _LOGGER.error("Failed to communicate with deCONZ") return False hass.data[DOMAIN] = deconz @@ -149,16 +153,60 @@ async def async_setup_entry(hass, config_entry): data = call.data.get(SERVICE_DATA) deconz = hass.data[DOMAIN] if entity_id: + entities = hass.data.get(DATA_DECONZ_ID) + if entities: field = entities.get(entity_id) + if field is None: _LOGGER.error('Could not find the entity %s', entity_id) return + await deconz.async_put_state(field, data) + hass.services.async_register( DOMAIN, SERVICE_DECONZ, async_configure, schema=SERVICE_SCHEMA) + async def async_refresh_devices(call): + """Refresh available devices from deCONZ.""" + deconz = hass.data[DOMAIN] + + groups = list(deconz.groups.keys()) + lights = list(deconz.lights.keys()) + scenes = list(deconz.scenes.keys()) + sensors = list(deconz.sensors.keys()) + + if not await deconz.async_load_parameters(): + return + + async_add_device_callback( + 'group', [group + for group_id, group in deconz.groups.items() + if group_id not in groups] + ) + + async_add_device_callback( + 'light', [light + for light_id, light in deconz.lights.items() + if light_id not in lights] + ) + + async_add_device_callback( + 'scene', [scene + for scene_id, scene in deconz.scenes.items() + if scene_id not in scenes] + ) + + async_add_device_callback( + 'sensor', [sensor + for sensor_id, sensor in deconz.sensors.items() + if sensor_id not in sensors] + ) + + hass.services.async_register( + DOMAIN, SERVICE_DEVICE_REFRESH, async_refresh_devices) + @callback def deconz_shutdown(event): """ diff --git a/homeassistant/components/deconz/services.yaml b/homeassistant/components/deconz/services.yaml index 78bf7041a93..fa0fb8e14a4 100644 --- a/homeassistant/components/deconz/services.yaml +++ b/homeassistant/components/deconz/services.yaml @@ -1,4 +1,3 @@ - configure: description: Set attribute of device in deCONZ. See https://home-assistant.io/components/deconz/#device-services for details. fields: @@ -11,3 +10,6 @@ configure: data: description: Data is a json object with what data you want to alter. example: '{"on": true}' + +device_refresh: + description: Refresh device lists from deCONZ. \ No newline at end of file diff --git a/homeassistant/components/scene/deconz.py b/homeassistant/components/scene/deconz.py index 5af8f657206..b8fca6d8630 100644 --- a/homeassistant/components/scene/deconz.py +++ b/homeassistant/components/scene/deconz.py @@ -5,8 +5,10 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/scene.deconz/ """ from homeassistant.components.deconz import ( - DOMAIN as DATA_DECONZ, DATA_DECONZ_ID) + DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, DATA_DECONZ_UNSUB) from homeassistant.components.scene import Scene +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect DEPENDENCIES = ['deconz'] @@ -19,12 +21,17 @@ async def async_setup_platform(hass, config, async_add_entities, async def async_setup_entry(hass, config_entry, async_add_entities): """Set up scenes for deCONZ component.""" - scenes = hass.data[DATA_DECONZ].scenes - entities = [] + @callback + def async_add_scene(scenes): + """Add scene from deCONZ.""" + entities = [] + for scene in scenes: + entities.append(DeconzScene(scene)) + async_add_entities(entities) + hass.data[DATA_DECONZ_UNSUB].append( + async_dispatcher_connect(hass, 'deconz_new_scene', async_add_scene)) - for scene in scenes.values(): - entities.append(DeconzScene(scene)) - async_add_entities(entities) + async_add_scene(hass.data[DATA_DECONZ].scenes.values()) class DeconzScene(Scene): diff --git a/requirements_all.txt b/requirements_all.txt index b155612350b..5f4fd64a6a2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -820,7 +820,7 @@ pycsspeechtts==1.0.2 pydaikin==0.4 # homeassistant.components.deconz -pydeconz==45 +pydeconz==47 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b9e44445114..236033e2f19 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -139,7 +139,7 @@ py-canary==0.5.0 pyblackbird==0.5 # homeassistant.components.deconz -pydeconz==45 +pydeconz==47 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/tests/components/scene/test_deconz.py b/tests/components/scene/test_deconz.py index 53f25808be2..8c22f718fa0 100644 --- a/tests/components/scene/test_deconz.py +++ b/tests/components/scene/test_deconz.py @@ -33,6 +33,7 @@ async def setup_bridge(hass, data): return_value=mock_coro(data)): await bridge.async_load_parameters() hass.data[deconz.DOMAIN] = bridge + hass.data[deconz.DATA_DECONZ_UNSUB] = [] hass.data[deconz.DATA_DECONZ_ID] = {} config_entry = config_entries.ConfigEntry( 1, deconz.DOMAIN, 'Mock Title', {'host': 'mock-host'}, 'test')