diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index 8015324be13..e640e9ba896 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -12,10 +12,7 @@ from .config_flow import configured_hosts from .const import DEFAULT_PORT, DOMAIN, _LOGGER from .gateway import DeconzGateway -REQUIREMENTS = ['pydeconz==47'] - -SUPPORTED_PLATFORMS = ['binary_sensor', 'cover', - 'light', 'scene', 'sensor', 'switch'] +REQUIREMENTS = ['pydeconz==52'] CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index 77d01c5c40b..cb68b842f4a 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -5,7 +5,8 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from .const import ( - ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DECONZ_DOMAIN) + ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DECONZ_DOMAIN, + NEW_SENSOR) from .deconz_device import DeconzDevice DEPENDENCIES = ['deconz'] @@ -34,7 +35,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(entities, True) gateway.listeners.append( - async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor)) + async_dispatcher_connect(hass, NEW_SENSOR, async_add_sensor)) async_add_sensor(gateway.api.sensors.values()) diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py new file mode 100644 index 00000000000..1f39b8705c7 --- /dev/null +++ b/homeassistant/components/deconz/climate.py @@ -0,0 +1,111 @@ +"""Support for deCONZ climate devices.""" +from homeassistant.components.climate import ClimateDevice +from homeassistant.components.climate.const import ( + SUPPORT_ON_OFF, SUPPORT_TARGET_TEMPERATURE) +from homeassistant.const import ( + ATTR_BATTERY_LEVEL, ATTR_TEMPERATURE, TEMP_CELSIUS) +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from .const import ( + ATTR_OFFSET, ATTR_VALVE, CONF_ALLOW_CLIP_SENSOR, + DOMAIN as DECONZ_DOMAIN, NEW_SENSOR) +from .deconz_device import DeconzDevice + +DEPENDENCIES = ['deconz'] + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up the deCONZ climate devices. + + Thermostats are based on the same device class as sensors in deCONZ. + """ + gateway = hass.data[DECONZ_DOMAIN] + + @callback + def async_add_climate(sensors): + """Add climate devices from deCONZ.""" + from pydeconz.sensor import THERMOSTAT + entities = [] + allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) + for sensor in sensors: + if sensor.type in THERMOSTAT and \ + not (not allow_clip_sensor and sensor.type.startswith('CLIP')): + entities.append(DeconzThermostat(sensor, gateway)) + async_add_entities(entities, True) + + gateway.listeners.append( + async_dispatcher_connect(hass, NEW_SENSOR, async_add_climate)) + + async_add_climate(gateway.api.sensors.values()) + + +class DeconzThermostat(DeconzDevice, ClimateDevice): + """Representation of a deCONZ thermostat.""" + + def __init__(self, device, gateway): + """Set up thermostat device.""" + super().__init__(device, gateway) + + self._features = SUPPORT_ON_OFF + self._features |= SUPPORT_TARGET_TEMPERATURE + + @property + def supported_features(self): + """Return the list of supported features.""" + return self._features + + @property + def is_on(self): + """Return true if on.""" + return self._device.on + + async def async_turn_on(self): + """Turn on switch.""" + data = {'mode': 'auto'} + await self._device.async_set_config(data) + + async def async_turn_off(self): + """Turn off switch.""" + data = {'mode': 'off'} + await self._device.async_set_config(data) + + @property + def current_temperature(self): + """Return the current temperature.""" + return self._device.temperature + + @property + def target_temperature(self): + """Return the target temperature.""" + return self._device.heatsetpoint + + async def async_set_temperature(self, **kwargs): + """Set new target temperature.""" + data = {} + + if ATTR_TEMPERATURE in kwargs: + data['heatsetpoint'] = kwargs[ATTR_TEMPERATURE] * 100 + + await self._device.async_set_config(data) + + @property + def temperature_unit(self): + """Return the unit of measurement.""" + return TEMP_CELSIUS + + @property + def device_state_attributes(self): + """Return the state attributes of the thermostat.""" + attr = {} + + if self._device.battery: + attr[ATTR_BATTERY_LEVEL] = self._device.battery + + if self._device.offset: + attr[ATTR_OFFSET] = self._device.offset + + if self._device.valve is not None: + attr[ATTR_VALVE] = self._device.valve + + return attr diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index b08f3d71824..bf0799d1fa2 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -10,13 +10,27 @@ DEFAULT_PORT = 80 CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor' CONF_ALLOW_DECONZ_GROUPS = 'allow_deconz_groups' -SUPPORTED_PLATFORMS = ['binary_sensor', 'cover', +SUPPORTED_PLATFORMS = ['binary_sensor', 'climate', 'cover', 'light', 'scene', 'sensor', 'switch'] DECONZ_REACHABLE = 'deconz_reachable' +NEW_GROUP = 'deconz_new_group' +NEW_LIGHT = 'deconz_new_light' +NEW_SCENE = 'deconz_new_scene' +NEW_SENSOR = 'deconz_new_sensor' + +NEW_DEVICE = { + 'group': NEW_GROUP, + 'light': NEW_LIGHT, + 'scene': NEW_SCENE, + 'sensor': NEW_SENSOR +} + ATTR_DARK = 'dark' +ATTR_OFFSET = 'offset' ATTR_ON = 'on' +ATTR_VALVE = 'valve' DAMPERS = ["Level controllable output"] WINDOW_COVERS = ["Window covering device"] diff --git a/homeassistant/components/deconz/cover.py b/homeassistant/components/deconz/cover.py index 48f06a894bb..fda4fe4309c 100644 --- a/homeassistant/components/deconz/cover.py +++ b/homeassistant/components/deconz/cover.py @@ -5,7 +5,8 @@ from homeassistant.components.cover import ( from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import COVER_TYPES, DAMPERS, DOMAIN as DECONZ_DOMAIN, WINDOW_COVERS +from .const import ( + COVER_TYPES, DAMPERS, DOMAIN as DECONZ_DOMAIN, NEW_LIGHT, WINDOW_COVERS) from .deconz_device import DeconzDevice DEPENDENCIES = ['deconz'] @@ -39,7 +40,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(entities, True) gateway.listeners.append( - async_dispatcher_connect(hass, 'deconz_new_light', async_add_cover)) + async_dispatcher_connect(hass, NEW_LIGHT, async_add_cover)) async_add_cover(gateway.api.lights.values()) @@ -48,7 +49,7 @@ class DeconzCover(DeconzDevice, CoverDevice): """Representation of a deCONZ cover.""" def __init__(self, device, gateway): - """Set up cover and add update callback to get data from websocket.""" + """Set up cover device.""" super().__init__(device, gateway) self._features = SUPPORT_OPEN diff --git a/homeassistant/components/deconz/gateway.py b/homeassistant/components/deconz/gateway.py index 2b53a0998d0..829485e1e92 100644 --- a/homeassistant/components/deconz/gateway.py +++ b/homeassistant/components/deconz/gateway.py @@ -8,7 +8,8 @@ from homeassistant.helpers.dispatcher import ( from homeassistant.util import slugify from .const import ( - _LOGGER, DECONZ_REACHABLE, CONF_ALLOW_CLIP_SENSOR, SUPPORTED_PLATFORMS) + _LOGGER, DECONZ_REACHABLE, CONF_ALLOW_CLIP_SENSOR, NEW_DEVICE, NEW_SENSOR, + SUPPORTED_PLATFORMS) class DeconzGateway: @@ -44,7 +45,7 @@ class DeconzGateway: self.listeners.append( async_dispatcher_connect( - hass, 'deconz_new_sensor', self.async_add_remote)) + hass, NEW_SENSOR, self.async_add_remote)) self.async_add_remote(self.api.sensors.values()) @@ -64,8 +65,7 @@ class DeconzGateway: """Handle event of new device creation in deCONZ.""" if not isinstance(device, list): device = [device] - async_dispatcher_send( - self.hass, 'deconz_new_{}'.format(device_type), device) + async_dispatcher_send(self.hass, NEW_DEVICE[device_type], device) @callback def async_add_remote(self, sensors): diff --git a/homeassistant/components/deconz/light.py b/homeassistant/components/deconz/light.py index 50e22c84d6f..3b63da8d9f8 100644 --- a/homeassistant/components/deconz/light.py +++ b/homeassistant/components/deconz/light.py @@ -9,8 +9,8 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect import homeassistant.util.color as color_util from .const import ( - CONF_ALLOW_DECONZ_GROUPS, DOMAIN as DECONZ_DOMAIN, COVER_TYPES, - SWITCH_TYPES) + CONF_ALLOW_DECONZ_GROUPS, DOMAIN as DECONZ_DOMAIN, COVER_TYPES, NEW_GROUP, + NEW_LIGHT, SWITCH_TYPES) from .deconz_device import DeconzDevice DEPENDENCIES = ['deconz'] @@ -36,7 +36,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(entities, True) gateway.listeners.append( - async_dispatcher_connect(hass, 'deconz_new_light', async_add_light)) + async_dispatcher_connect(hass, NEW_LIGHT, async_add_light)) @callback def async_add_group(groups): @@ -49,7 +49,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(entities, True) gateway.listeners.append( - async_dispatcher_connect(hass, 'deconz_new_group', async_add_group)) + async_dispatcher_connect(hass, NEW_GROUP, async_add_group)) async_add_light(gateway.api.lights.values()) async_add_group(gateway.api.groups.values()) diff --git a/homeassistant/components/deconz/scene.py b/homeassistant/components/deconz/scene.py index d3a6df810ba..22b4c47f2ab 100644 --- a/homeassistant/components/deconz/scene.py +++ b/homeassistant/components/deconz/scene.py @@ -1,9 +1,10 @@ """Support for deCONZ scenes.""" -from homeassistant.components.deconz import DOMAIN as DECONZ_DOMAIN from homeassistant.components.scene import Scene from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from .const import DOMAIN as DECONZ_DOMAIN, NEW_SCENE + DEPENDENCIES = ['deconz'] @@ -25,7 +26,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): entities.append(DeconzScene(scene, gateway)) async_add_entities(entities) gateway.listeners.append( - async_dispatcher_connect(hass, 'deconz_new_scene', async_add_scene)) + async_dispatcher_connect(hass, NEW_SCENE, async_add_scene)) async_add_scene(gateway.api.scenes.values()) diff --git a/homeassistant/components/deconz/sensor.py b/homeassistant/components/deconz/sensor.py index 3083f0c6732..e6b033906e7 100644 --- a/homeassistant/components/deconz/sensor.py +++ b/homeassistant/components/deconz/sensor.py @@ -6,7 +6,8 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util import slugify from .const import ( - ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DECONZ_DOMAIN) + ATTR_DARK, ATTR_ON, CONF_ALLOW_CLIP_SENSOR, DOMAIN as DECONZ_DOMAIN, + NEW_SENSOR) from .deconz_device import DeconzDevice DEPENDENCIES = ['deconz'] @@ -29,7 +30,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities): @callback def async_add_sensor(sensors): """Add sensors from deCONZ.""" - from pydeconz.sensor import DECONZ_SENSOR, SWITCH as DECONZ_REMOTE + from pydeconz.sensor import ( + DECONZ_SENSOR, SWITCH as DECONZ_REMOTE) entities = [] allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) for sensor in sensors: @@ -43,7 +45,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(entities, True) gateway.listeners.append( - async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor)) + async_dispatcher_connect(hass, NEW_SENSOR, async_add_sensor)) async_add_sensor(gateway.api.sensors.values()) diff --git a/homeassistant/components/deconz/switch.py b/homeassistant/components/deconz/switch.py index c48c7205e01..56d37d504cb 100644 --- a/homeassistant/components/deconz/switch.py +++ b/homeassistant/components/deconz/switch.py @@ -3,7 +3,7 @@ from homeassistant.components.switch import SwitchDevice from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import DOMAIN as DECONZ_DOMAIN, POWER_PLUGS, SIRENS +from .const import DOMAIN as DECONZ_DOMAIN, NEW_LIGHT, POWER_PLUGS, SIRENS from .deconz_device import DeconzDevice DEPENDENCIES = ['deconz'] @@ -34,7 +34,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): async_add_entities(entities, True) gateway.listeners.append( - async_dispatcher_connect(hass, 'deconz_new_light', async_add_switch)) + async_dispatcher_connect(hass, NEW_LIGHT, async_add_switch)) async_add_switch(gateway.api.lights.values()) diff --git a/requirements_all.txt b/requirements_all.txt index 90e6e2b9c34..22124c3910a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -983,7 +983,7 @@ pydaikin==0.9 pydanfossair==0.0.7 # homeassistant.components.deconz -pydeconz==47 +pydeconz==52 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index df4112b71a0..109cf460ab3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -184,7 +184,7 @@ py-canary==0.5.0 pyblackbird==0.5 # homeassistant.components.deconz -pydeconz==47 +pydeconz==52 # homeassistant.components.zwave pydispatcher==2.0.5 diff --git a/tests/components/deconz/test_climate.py b/tests/components/deconz/test_climate.py new file mode 100644 index 00000000000..13083594c8a --- /dev/null +++ b/tests/components/deconz/test_climate.py @@ -0,0 +1,181 @@ +"""deCONZ climate platform tests.""" +from unittest.mock import Mock, patch + +from homeassistant import config_entries +from homeassistant.components import deconz +from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.setup import async_setup_component + +import homeassistant.components.climate as climate + +from tests.common import mock_coro + + +SENSOR = { + "1": { + "id": "Climate 1 id", + "name": "Climate 1 name", + "type": "ZHAThermostat", + "state": {"on": True, "temperature": 2260}, + "config": {"battery": 100, "heatsetpoint": 2200, "mode": "auto", + "offset": 10, "reachable": True, "valve": 30}, + "uniqueid": "00:00:00:00:00:00:00:00-00" + }, + "2": { + "id": "Sensor 2 id", + "name": "Sensor 2 name", + "type": "ZHAPresence", + "state": {"presence": False}, + "config": {} + } +} + +ENTRY_CONFIG = { + deconz.const.CONF_ALLOW_CLIP_SENSOR: True, + deconz.const.CONF_ALLOW_DECONZ_GROUPS: True, + deconz.config_flow.CONF_API_KEY: "ABCDEF", + deconz.config_flow.CONF_BRIDGEID: "0123456789", + deconz.config_flow.CONF_HOST: "1.2.3.4", + deconz.config_flow.CONF_PORT: 80 +} + + +async def setup_gateway(hass, data, allow_clip_sensor=True): + """Load the deCONZ sensor platform.""" + from pydeconz import DeconzSession + loop = Mock() + session = Mock() + + ENTRY_CONFIG[deconz.const.CONF_ALLOW_CLIP_SENSOR] = allow_clip_sensor + + config_entry = config_entries.ConfigEntry( + 1, deconz.DOMAIN, 'Mock Title', ENTRY_CONFIG, 'test', + config_entries.CONN_CLASS_LOCAL_PUSH) + gateway = deconz.DeconzGateway(hass, config_entry) + gateway.api = DeconzSession(loop, session, **config_entry.data) + gateway.api.config = Mock() + hass.data[deconz.DOMAIN] = gateway + + with patch('pydeconz.DeconzSession.async_get_state', + return_value=mock_coro(data)): + await gateway.api.async_load_parameters() + + await hass.config_entries.async_forward_entry_setup( + config_entry, 'climate') + # To flush out the service call to update the group + await hass.async_block_till_done() + + +async def test_platform_manually_configured(hass): + """Test that we do not discover anything or try to set up a gateway.""" + assert await async_setup_component(hass, climate.DOMAIN, { + 'climate': { + 'platform': deconz.DOMAIN + } + }) is True + assert deconz.DOMAIN not in hass.data + + +async def test_no_sensors(hass): + """Test that no sensors in deconz results in no climate entities.""" + await setup_gateway(hass, {}) + assert not hass.data[deconz.DOMAIN].deconz_ids + assert not hass.states.async_all() + + +async def test_climate_devices(hass): + """Test successful creation of sensor entities.""" + await setup_gateway(hass, {"sensors": SENSOR}) + assert "climate.climate_1_name" in hass.data[deconz.DOMAIN].deconz_ids + assert "sensor.sensor_2_name" not in hass.data[deconz.DOMAIN].deconz_ids + assert len(hass.states.async_all()) == 1 + + hass.data[deconz.DOMAIN].api.sensors['1'].async_update( + {'state': {'on': False}}) + + await hass.services.async_call( + 'climate', 'turn_on', {'entity_id': 'climate.climate_1_name'}, + blocking=True + ) + hass.data[deconz.DOMAIN].api.session.put.assert_called_with( + 'http://1.2.3.4:80/api/ABCDEF/sensors/1/config', + data='{"mode": "auto"}' + ) + + await hass.services.async_call( + 'climate', 'turn_off', {'entity_id': 'climate.climate_1_name'}, + blocking=True + ) + hass.data[deconz.DOMAIN].api.session.put.assert_called_with( + 'http://1.2.3.4:80/api/ABCDEF/sensors/1/config', + data='{"mode": "off"}' + ) + + await hass.services.async_call( + 'climate', 'set_temperature', + {'entity_id': 'climate.climate_1_name', 'temperature': 20}, + blocking=True + ) + hass.data[deconz.DOMAIN].api.session.put.assert_called_with( + 'http://1.2.3.4:80/api/ABCDEF/sensors/1/config', + data='{"heatsetpoint": 2000.0}' + ) + + assert len(hass.data[deconz.DOMAIN].api.session.put.mock_calls) == 3 + + +async def test_verify_state_update(hass): + """Test that state update properly.""" + await setup_gateway(hass, {"sensors": SENSOR}) + assert "climate.climate_1_name" in hass.data[deconz.DOMAIN].deconz_ids + + thermostat = hass.states.get('climate.climate_1_name') + assert thermostat.state == 'on' + + state_update = { + "t": "event", + "e": "changed", + "r": "sensors", + "id": "1", + "config": {"on": False} + } + hass.data[deconz.DOMAIN].api.async_event_handler(state_update) + + await hass.async_block_till_done() + assert len(hass.states.async_all()) == 1 + + thermostat = hass.states.get('climate.climate_1_name') + assert thermostat.state == 'off' + + +async def test_add_new_climate_device(hass): + """Test successful creation of climate entities.""" + await setup_gateway(hass, {}) + sensor = Mock() + sensor.name = 'name' + sensor.type = 'ZHAThermostat' + sensor.register_async_callback = Mock() + async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + await hass.async_block_till_done() + assert "climate.name" in hass.data[deconz.DOMAIN].deconz_ids + + +async def test_do_not_allow_clipsensor(hass): + """Test that clip sensors can be ignored.""" + await setup_gateway(hass, {}, allow_clip_sensor=False) + sensor = Mock() + sensor.name = 'name' + sensor.type = 'CLIPThermostat' + sensor.register_async_callback = Mock() + async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + await hass.async_block_till_done() + assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0 + + +async def test_unload_sensor(hass): + """Test that it works to unload sensor entities.""" + await setup_gateway(hass, {"sensors": SENSOR}) + + await hass.data[deconz.DOMAIN].async_reset() + + assert len(hass.states.async_all()) == 0 diff --git a/tests/components/deconz/test_gateway.py b/tests/components/deconz/test_gateway.py index dbc45c955b5..d73f225b2ac 100644 --- a/tests/components/deconz/test_gateway.py +++ b/tests/components/deconz/test_gateway.py @@ -35,18 +35,20 @@ async def test_gateway_setup(): assert await deconz_gateway.async_setup() is True assert deconz_gateway.api is api - assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 6 + assert len(hass.config_entries.async_forward_entry_setup.mock_calls) == 7 assert hass.config_entries.async_forward_entry_setup.mock_calls[0][1] == \ (entry, 'binary_sensor') assert hass.config_entries.async_forward_entry_setup.mock_calls[1][1] == \ - (entry, 'cover') + (entry, 'climate') assert hass.config_entries.async_forward_entry_setup.mock_calls[2][1] == \ - (entry, 'light') + (entry, 'cover') assert hass.config_entries.async_forward_entry_setup.mock_calls[3][1] == \ - (entry, 'scene') + (entry, 'light') assert hass.config_entries.async_forward_entry_setup.mock_calls[4][1] == \ - (entry, 'sensor') + (entry, 'scene') assert hass.config_entries.async_forward_entry_setup.mock_calls[5][1] == \ + (entry, 'sensor') + assert hass.config_entries.async_forward_entry_setup.mock_calls[6][1] == \ (entry, 'switch') assert len(api.start.mock_calls) == 1 @@ -150,7 +152,7 @@ async def test_reset_after_successful_setup(): mock_coro(True) assert await deconz_gateway.async_reset() is True - assert len(hass.config_entries.async_forward_entry_unload.mock_calls) == 6 + assert len(hass.config_entries.async_forward_entry_unload.mock_calls) == 7 assert len(listener.mock_calls) == 1 assert len(deconz_gateway.listeners) == 0 diff --git a/tests/components/deconz/test_light.py b/tests/components/deconz/test_light.py index 081fd61ec4e..49c3f280d8a 100644 --- a/tests/components/deconz/test_light.py +++ b/tests/components/deconz/test_light.py @@ -41,14 +41,15 @@ GROUP = { "lights": [ "1", "2" - ] + ], }, "2": { "id": "Group 2 id", "name": "Group 2 name", "state": {}, "action": {}, - "scenes": [] + "scenes": [], + "lights": [], }, } diff --git a/tests/components/deconz/test_scene.py b/tests/components/deconz/test_scene.py index 788c6dc1c3e..963f1064b35 100644 --- a/tests/components/deconz/test_scene.py +++ b/tests/components/deconz/test_scene.py @@ -20,6 +20,7 @@ GROUP = { "id": "1", "name": "Scene 1" }], + "lights": [], } }