From 7e0ad9ba01044ca4cd6b0c78762fd22a74b1bdad Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 15 May 2018 21:28:24 +0200 Subject: [PATCH 1/5] Option to load or not to load clip sensors on start --- homeassistant/components/binary_sensor/deconz.py | 6 +++++- homeassistant/components/sensor/deconz.py | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/binary_sensor/deconz.py b/homeassistant/components/binary_sensor/deconz.py index 9faa703d13c000..07f8277b2e91c3 100644 --- a/homeassistant/components/binary_sensor/deconz.py +++ b/homeassistant/components/binary_sensor/deconz.py @@ -22,15 +22,19 @@ async def async_setup_platform(hass, config, async_add_devices, async def async_setup_entry(hass, config_entry, async_add_devices): """Set up the deCONZ binary sensor.""" + allow_clip_sensor = config_entry.data.get('clip_sensor', True) + @callback def async_add_sensor(sensors): """Add binary sensor from deCONZ.""" from pydeconz.sensor import DECONZ_BINARY_SENSOR entities = [] for sensor in sensors: - if sensor.type in DECONZ_BINARY_SENSOR: + if sensor.type in DECONZ_BINARY_SENSOR and \ + not (not allow_clip_sensor and sensor.type.startswith('CLIP')): entities.append(DeconzBinarySensor(sensor)) async_add_devices(entities, True) + hass.data[DATA_DECONZ_UNSUB].append( async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor)) diff --git a/homeassistant/components/sensor/deconz.py b/homeassistant/components/sensor/deconz.py index 221cdf2129e823..76b3a148513bc2 100644 --- a/homeassistant/components/sensor/deconz.py +++ b/homeassistant/components/sensor/deconz.py @@ -28,19 +28,23 @@ async def async_setup_platform(hass, config, async_add_devices, async def async_setup_entry(hass, config_entry, async_add_devices): """Set up the deCONZ sensors.""" + allow_clip_sensor = config_entry.data.get('clip_sensor', True) + @callback def async_add_sensor(sensors): """Add sensors from deCONZ.""" from pydeconz.sensor import DECONZ_SENSOR, SWITCH as DECONZ_REMOTE entities = [] for sensor in sensors: - if sensor.type in DECONZ_SENSOR: + if sensor.type in DECONZ_SENSOR and \ + not (not allow_clip_sensor and sensor.type.startswith('CLIP')): if sensor.type in DECONZ_REMOTE: if sensor.battery: entities.append(DeconzBattery(sensor)) else: entities.append(DeconzSensor(sensor)) async_add_devices(entities, True) + hass.data[DATA_DECONZ_UNSUB].append( async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_sensor)) From ad7f6d7b38ee99ec0e9b6c41432d2bc32ca21c7c Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 15 May 2018 23:54:53 +0200 Subject: [PATCH 2/5] Full flow --- .../components/binary_sensor/deconz.py | 6 ++-- .../components/deconz/.translations/en.json | 8 ++++- homeassistant/components/deconz/__init__.py | 8 +++-- .../components/deconz/config_flow.py | 31 +++++++++++++++++-- homeassistant/components/deconz/const.py | 2 ++ homeassistant/components/deconz/strings.json | 8 ++++- homeassistant/components/sensor/deconz.py | 6 ++-- 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/binary_sensor/deconz.py b/homeassistant/components/binary_sensor/deconz.py index 07f8277b2e91c3..f0508a3d1f352a 100644 --- a/homeassistant/components/binary_sensor/deconz.py +++ b/homeassistant/components/binary_sensor/deconz.py @@ -6,7 +6,8 @@ """ from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.deconz import ( - DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, DATA_DECONZ_UNSUB) + CONF_CLIP_SENSOR, DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, + DATA_DECONZ_UNSUB) from homeassistant.const import ATTR_BATTERY_LEVEL from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -22,13 +23,12 @@ async def async_setup_platform(hass, config, async_add_devices, async def async_setup_entry(hass, config_entry, async_add_devices): """Set up the deCONZ binary sensor.""" - allow_clip_sensor = config_entry.data.get('clip_sensor', True) - @callback def async_add_sensor(sensors): """Add binary sensor from deCONZ.""" from pydeconz.sensor import DECONZ_BINARY_SENSOR entities = [] + allow_clip_sensor = config_entry.data.get(CONF_CLIP_SENSOR, True) for sensor in sensors: if sensor.type in DECONZ_BINARY_SENSOR and \ not (not allow_clip_sensor and sensor.type.startswith('CLIP')): diff --git a/homeassistant/components/deconz/.translations/en.json b/homeassistant/components/deconz/.translations/en.json index 0009986d45f48e..dcbe99b844a230 100644 --- a/homeassistant/components/deconz/.translations/en.json +++ b/homeassistant/components/deconz/.translations/en.json @@ -19,8 +19,14 @@ "link": { "description": "Unlock your deCONZ gateway to register with Home Assistant.\n\n1. Go to deCONZ system settings\n2. Press \"Unlock Gateway\" button", "title": "Link with deCONZ" + }, + "options": { + "title": "Extra configuration options for deCONZ", + "data": { + "allow_clip_sensor": "Allow virtual sensors" + } } }, - "title": "deCONZ" + "title": "deCONZ Zigbee gateway" } } \ No newline at end of file diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index bbab4029d7edfe..c603c336d380cd 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -19,8 +19,8 @@ # Loading the config flow file will register the flow from .config_flow import configured_hosts from .const import ( - CONFIG_FILE, DATA_DECONZ_EVENT, DATA_DECONZ_ID, - DATA_DECONZ_UNSUB, DOMAIN, _LOGGER) + CONF_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT, + DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER) REQUIREMENTS = ['pydeconz==38'] @@ -104,8 +104,10 @@ def async_add_device_callback(device_type, device): def async_add_remote(sensors): """Setup remote from deCONZ.""" from pydeconz.sensor import SWITCH as DECONZ_REMOTE + allow_clip_sensor = config_entry.data.get(CONF_CLIP_SENSOR, True) for sensor in sensors: - if sensor.type in DECONZ_REMOTE: + if sensor.type in DECONZ_REMOTE and \ + not (not allow_clip_sensor and sensor.type.startswith('CLIP')): hass.data[DATA_DECONZ_EVENT].append(DeconzEvent(hass, sensor)) hass.data[DATA_DECONZ_UNSUB].append( async_dispatcher_connect(hass, 'deconz_new_sensor', async_add_remote)) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index e900782ea658d8..eaadc12c6b6884 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -8,7 +8,7 @@ from homeassistant.helpers import aiohttp_client from homeassistant.util.json import load_json -from .const import CONFIG_FILE, DOMAIN +from .const import CONF_CLIP_SENSOR, CONFIG_FILE, DOMAIN @callback @@ -30,7 +30,12 @@ def __init__(self): self.deconz_config = {} async def async_step_init(self, user_input=None): - """Handle a deCONZ config flow start.""" + """Handle a deCONZ config flow start. + + Only allows one instance to be set up. + If only one bridge is found go to link step. + If more than one bridge is found let user choose bridge to link. + """ from pydeconz.utils import async_discovery if configured_hosts(self.hass): @@ -78,6 +83,7 @@ async def async_step_link(self, user_input=None): if 'bridgeid' not in self.deconz_config: self.deconz_config['bridgeid'] = await async_get_bridgeid( session, **self.deconz_config) + return await self.async_step_options() return self.async_create_entry( title='deCONZ-' + self.deconz_config['bridgeid'], data=self.deconz_config @@ -89,6 +95,27 @@ async def async_step_link(self, user_input=None): errors=errors, ) + async def async_step_options(self, user_input=None): + """[summary] + + Keyword Arguments: + user_input {[type]} -- [description] (default: {None}) + """ + if user_input is not None: + self.deconz_config[CONF_CLIP_SENSOR] = user_input[CONF_CLIP_SENSOR] + return self.async_create_entry( + title='deCONZ-' + self.deconz_config['bridgeid'], + data=self.deconz_config + ) + + return self.async_show_form( + step_id='options', + data_schema=vol.Schema({ + vol.Optional(CONF_CLIP_SENSOR): bool, + }), + ) + + async def async_step_discovery(self, discovery_info): """Prepare configuration for a discovered deCONZ bridge. diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index 48e5ea75d684c3..a5d70e12833276 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -8,3 +8,5 @@ DATA_DECONZ_EVENT = 'deconz_events' DATA_DECONZ_ID = 'deconz_entities' DATA_DECONZ_UNSUB = 'deconz_dispatchers' + +CONF_CLIP_SENSOR = 'allow_clip_sensor' diff --git a/homeassistant/components/deconz/strings.json b/homeassistant/components/deconz/strings.json index 7ea68af01c1dc4..ac394e4f4d303a 100644 --- a/homeassistant/components/deconz/strings.json +++ b/homeassistant/components/deconz/strings.json @@ -1,6 +1,6 @@ { "config": { - "title": "deCONZ", + "title": "deCONZ Zigbee gateway", "step": { "init": { "title": "Define deCONZ gateway", @@ -12,6 +12,12 @@ "link": { "title": "Link with deCONZ", "description": "Unlock your deCONZ gateway to register with Home Assistant.\n\n1. Go to deCONZ system settings\n2. Press \"Unlock Gateway\" button" + }, + "options": { + "title": "Extra configuration options for deCONZ", + "data":{ + "allow_clip_sensor": "Allow virtual sensors" + } } }, "error": { diff --git a/homeassistant/components/sensor/deconz.py b/homeassistant/components/sensor/deconz.py index 76b3a148513bc2..49c77fa0ac07ab 100644 --- a/homeassistant/components/sensor/deconz.py +++ b/homeassistant/components/sensor/deconz.py @@ -5,7 +5,8 @@ https://home-assistant.io/components/sensor.deconz/ """ from homeassistant.components.deconz import ( - DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, DATA_DECONZ_UNSUB) + CONF_CLIP_SENSOR, DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, + DATA_DECONZ_UNSUB) from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_VOLTAGE, DEVICE_CLASS_BATTERY) from homeassistant.core import callback @@ -28,13 +29,12 @@ async def async_setup_platform(hass, config, async_add_devices, async def async_setup_entry(hass, config_entry, async_add_devices): """Set up the deCONZ sensors.""" - allow_clip_sensor = config_entry.data.get('clip_sensor', True) - @callback def async_add_sensor(sensors): """Add sensors from deCONZ.""" from pydeconz.sensor import DECONZ_SENSOR, SWITCH as DECONZ_REMOTE entities = [] + allow_clip_sensor = config_entry.data.get(CONF_CLIP_SENSOR, True) for sensor in sensors: if sensor.type in DECONZ_SENSOR and \ not (not allow_clip_sensor and sensor.type.startswith('CLIP')): From af222c78d092fc10e94c4a965628ef383d3241f1 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 16 May 2018 21:24:27 +0200 Subject: [PATCH 3/5] Fix config flow and add tests --- .../components/deconz/config_flow.py | 48 ++++++++----------- tests/components/binary_sensor/test_deconz.py | 18 ++++++- tests/components/deconz/test_config_flow.py | 43 ++++++++++++++--- tests/components/deconz/test_init.py | 18 +++++++ tests/components/sensor/test_deconz.py | 18 ++++++- 5 files changed, 106 insertions(+), 39 deletions(-) diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index eaadc12c6b6884..3e2667ea9e9211 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -10,11 +10,13 @@ from .const import CONF_CLIP_SENSOR, CONFIG_FILE, DOMAIN +CONF_BRIDGEID = 'bridgeid' + @callback def configured_hosts(hass): """Return a set of the configured hosts.""" - return set(entry.data['host'] for entry + return set(entry.data[CONF_HOST] for entry in hass.config_entries.async_entries(DOMAIN)) @@ -70,7 +72,7 @@ async def async_step_init(self, user_input=None): async def async_step_link(self, user_input=None): """Attempt to link with the deCONZ bridge.""" - from pydeconz.utils import async_get_api_key, async_get_bridgeid + from pydeconz.utils import async_get_api_key errors = {} if user_input is not None: @@ -80,14 +82,7 @@ async def async_step_link(self, user_input=None): api_key = await async_get_api_key(session, **self.deconz_config) if api_key: self.deconz_config[CONF_API_KEY] = api_key - if 'bridgeid' not in self.deconz_config: - self.deconz_config['bridgeid'] = await async_get_bridgeid( - session, **self.deconz_config) return await self.async_step_options() - return self.async_create_entry( - title='deCONZ-' + self.deconz_config['bridgeid'], - data=self.deconz_config - ) errors['base'] = 'no_key' return self.async_show_form( @@ -96,15 +91,22 @@ async def async_step_link(self, user_input=None): ) async def async_step_options(self, user_input=None): - """[summary] + """Extra options for deCONZ. - Keyword Arguments: - user_input {[type]} -- [description] (default: {None}) + CONF_CLIP_SENSOR -- Allow user to choose if they want clip sensors. """ + from pydeconz.utils import async_get_bridgeid + if user_input is not None: self.deconz_config[CONF_CLIP_SENSOR] = user_input[CONF_CLIP_SENSOR] + + if CONF_BRIDGEID not in self.deconz_config: + session = aiohttp_client.async_get_clientsession(self.hass) + self.deconz_config[CONF_BRIDGEID] = await async_get_bridgeid( + session, **self.deconz_config) + return self.async_create_entry( - title='deCONZ-' + self.deconz_config['bridgeid'], + title='deCONZ-' + self.deconz_config[CONF_BRIDGEID], data=self.deconz_config ) @@ -115,7 +117,6 @@ async def async_step_options(self, user_input=None): }), ) - async def async_step_discovery(self, discovery_info): """Prepare configuration for a discovered deCONZ bridge. @@ -124,7 +125,7 @@ async def async_step_discovery(self, discovery_info): deconz_config = {} deconz_config[CONF_HOST] = discovery_info.get(CONF_HOST) deconz_config[CONF_PORT] = discovery_info.get(CONF_PORT) - deconz_config['bridgeid'] = discovery_info.get('serial') + deconz_config[CONF_BRIDGEID] = discovery_info.get('serial') config_file = await self.hass.async_add_job( load_json, self.hass.config.path(CONFIG_FILE)) @@ -148,19 +149,10 @@ async def async_step_import(self, import_config): Otherwise we will delegate to `link` step which will ask user to link the bridge. """ - from pydeconz.utils import async_get_bridgeid - if configured_hosts(self.hass): return self.async_abort(reason='one_instance_only') - elif CONF_API_KEY not in import_config: - self.deconz_config = import_config - return await self.async_step_link() - if 'bridgeid' not in import_config: - session = aiohttp_client.async_get_clientsession(self.hass) - import_config['bridgeid'] = await async_get_bridgeid( - session, **import_config) - return self.async_create_entry( - title='deCONZ-' + import_config['bridgeid'], - data=import_config - ) + self.deconz_config = import_config + if CONF_API_KEY not in import_config: + return await self.async_step_link() + return await self.async_step_options() diff --git a/tests/components/binary_sensor/test_deconz.py b/tests/components/binary_sensor/test_deconz.py index 88dd0dae737af0..e0f1a776f91bf2 100644 --- a/tests/components/binary_sensor/test_deconz.py +++ b/tests/components/binary_sensor/test_deconz.py @@ -26,7 +26,7 @@ } -async def setup_bridge(hass, data): +async def setup_bridge(hass, data, allow_clip_sensor=True): """Load the deCONZ binary sensor platform.""" from pydeconz import DeconzSession loop = Mock() @@ -41,7 +41,8 @@ async def setup_bridge(hass, data): 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') + 1, deconz.DOMAIN, 'Mock Title', + {'host': 'mock-host', 'allow_clip_sensor': allow_clip_sensor}, 'test') await hass.config_entries.async_forward_entry_setup( config_entry, 'binary_sensor') # To flush out the service call to update the group @@ -77,3 +78,16 @@ async def test_add_new_sensor(hass): async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) await hass.async_block_till_done() assert "binary_sensor.name" in hass.data[deconz.DATA_DECONZ_ID] + + +async def test_do_not_add_clip_sensor(hass): + """Test that clip sensors can be ignored.""" + data = {} + await setup_bridge(hass, data, allow_clip_sensor=False) + sensor = Mock() + sensor.name = 'name' + sensor.type = 'CLIPPresence' + sensor.register_async_callback = Mock() + async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + await hass.async_block_till_done() + assert len(hass.data[deconz.DATA_DECONZ_ID]) == 0 diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index d86475b35ef023..b759c351439cb4 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -21,7 +21,9 @@ async def test_flow_works(hass, aioclient_mock): flow = config_flow.DeconzFlowHandler() flow.hass = hass await flow.async_step_init() - result = await flow.async_step_link(user_input={}) + await flow.async_step_link(user_input={}) + result = await flow.async_step_options( + user_input={'allow_clip_sensor': True}) assert result['type'] == 'create_entry' assert result['title'] == 'deCONZ-id' @@ -29,7 +31,8 @@ async def test_flow_works(hass, aioclient_mock): 'bridgeid': 'id', 'host': '1.2.3.4', 'port': 80, - 'api_key': '1234567890ABCDEF' + 'api_key': '1234567890ABCDEF', + 'allow_clip_sensor': True } @@ -141,19 +144,21 @@ async def test_bridge_discovery_config_file(hass): return_value={'host': '1.2.3.4', 'port': 8080, 'api_key': '1234567890ABCDEF'}): - result = await flow.async_step_discovery({ + await flow.async_step_discovery({ 'host': '1.2.3.4', 'port': 80, 'serial': 'id' }) - + result = await flow.async_step_options( + user_input={'allow_clip_sensor': True}) assert result['type'] == 'create_entry' assert result['title'] == 'deCONZ-id' assert result['data'] == { 'bridgeid': 'id', 'host': '1.2.3.4', 'port': 80, - 'api_key': '1234567890ABCDEF' + 'api_key': '1234567890ABCDEF', + 'allow_clip_sensor': True } @@ -208,18 +213,42 @@ async def test_import_with_api_key(hass): flow = config_flow.DeconzFlowHandler() flow.hass = hass - result = await flow.async_step_import({ + await flow.async_step_import({ 'bridgeid': 'id', 'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF' }) + result = await flow.async_step_options( + user_input={'allow_clip_sensor': True}) + assert result['type'] == 'create_entry' + assert result['title'] == 'deCONZ-id' + assert result['data'] == { + 'bridgeid': 'id', + 'host': '1.2.3.4', + 'port': 80, + 'api_key': '1234567890ABCDEF', + 'allow_clip_sensor': True + } + +async def test_options(hass, aioclient_mock): + """Test that options work and that bridgeid can be requested.""" + aioclient_mock.get('http://1.2.3.4:80/api/1234567890ABCDEF/config', + json={"bridgeid": "id"}) + flow = config_flow.DeconzFlowHandler() + flow.hass = hass + flow.deconz_config = {'host': '1.2.3.4', + 'port': 80, + 'api_key': '1234567890ABCDEF'} + result = await flow.async_step_options( + user_input={'allow_clip_sensor': False}) assert result['type'] == 'create_entry' assert result['title'] == 'deCONZ-id' assert result['data'] == { 'bridgeid': 'id', 'host': '1.2.3.4', 'port': 80, - 'api_key': '1234567890ABCDEF' + 'api_key': '1234567890ABCDEF', + 'allow_clip_sensor': False } diff --git a/tests/components/deconz/test_init.py b/tests/components/deconz/test_init.py index 888094deea61cd..3e7f87b1774370 100644 --- a/tests/components/deconz/test_init.py +++ b/tests/components/deconz/test_init.py @@ -172,3 +172,21 @@ async def test_add_new_remote(hass): async_dispatcher_send(hass, 'deconz_new_sensor', [remote]) await hass.async_block_till_done() assert len(hass.data[deconz.DATA_DECONZ_EVENT]) == 1 + + +async def test_do_not_add_clip_sensor(hass): + """Test that clip sensors can be ignored.""" + entry = Mock() + entry.data = {'host': '1.2.3.4', 'port': 80, + 'api_key': '1234567890ABCDEF', 'allow_clip_sensor': False} + remote = Mock() + remote.name = 'name' + remote.type = 'CLIPSwitch' + remote.register_async_callback = Mock() + with patch('pydeconz.DeconzSession.async_load_parameters', + return_value=mock_coro(True)): + assert await deconz.async_setup_entry(hass, entry) is True + + async_dispatcher_send(hass, 'deconz_new_sensor', [remote]) + await hass.async_block_till_done() + assert len(hass.data[deconz.DATA_DECONZ_EVENT]) == 0 diff --git a/tests/components/sensor/test_deconz.py b/tests/components/sensor/test_deconz.py index 8f6a53e6e6510f..5ec7fc8973135b 100644 --- a/tests/components/sensor/test_deconz.py +++ b/tests/components/sensor/test_deconz.py @@ -41,7 +41,7 @@ } -async def setup_bridge(hass, data): +async def setup_bridge(hass, data, allow_clip_sensor=True): """Load the deCONZ sensor platform.""" from pydeconz import DeconzSession loop = Mock() @@ -57,7 +57,8 @@ async def setup_bridge(hass, data): hass.data[deconz.DATA_DECONZ_EVENT] = [] hass.data[deconz.DATA_DECONZ_ID] = {} config_entry = config_entries.ConfigEntry( - 1, deconz.DOMAIN, 'Mock Title', {'host': 'mock-host'}, 'test') + 1, deconz.DOMAIN, 'Mock Title', + {'host': 'mock-host', 'allow_clip_sensor': allow_clip_sensor}, 'test') await hass.config_entries.async_forward_entry_setup(config_entry, 'sensor') # To flush out the service call to update the group await hass.async_block_till_done() @@ -97,3 +98,16 @@ async def test_add_new_sensor(hass): async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) await hass.async_block_till_done() assert "sensor.name" in hass.data[deconz.DATA_DECONZ_ID] + + +async def test_do_not_add_clipsensor(hass): + """Test that clip sensors can be ignored.""" + data = {} + await setup_bridge(hass, data, allow_clip_sensor=False) + sensor = Mock() + sensor.name = 'name' + sensor.type = 'CLIPTemperature' + sensor.register_async_callback = Mock() + async_dispatcher_send(hass, 'deconz_new_sensor', [sensor]) + await hass.async_block_till_done() + assert len(hass.data[deconz.DATA_DECONZ_ID]) == 0 From b03e0eb06a24ebafb5fb5cf3fd62812d9150bec9 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 18 May 2018 17:19:59 +0200 Subject: [PATCH 4/5] Fix attribute dark reporting properly --- homeassistant/components/binary_sensor/deconz.py | 2 +- homeassistant/components/sensor/deconz.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/binary_sensor/deconz.py b/homeassistant/components/binary_sensor/deconz.py index f0508a3d1f352a..980781eb49b77d 100644 --- a/homeassistant/components/binary_sensor/deconz.py +++ b/homeassistant/components/binary_sensor/deconz.py @@ -107,6 +107,6 @@ def device_state_attributes(self): attr = {} if self._sensor.battery: attr[ATTR_BATTERY_LEVEL] = self._sensor.battery - if self._sensor.type in PRESENCE and self._sensor.dark: + if self._sensor.type in PRESENCE and self._sensor.dark is not None: attr['dark'] = self._sensor.dark return attr diff --git a/homeassistant/components/sensor/deconz.py b/homeassistant/components/sensor/deconz.py index 49c77fa0ac07ab..692ad40f1fc791 100644 --- a/homeassistant/components/sensor/deconz.py +++ b/homeassistant/components/sensor/deconz.py @@ -118,9 +118,12 @@ def should_poll(self): @property def device_state_attributes(self): """Return the state attributes of the sensor.""" + from pydeconz.sensor import LIGHTLEVEL attr = {} if self._sensor.battery: attr[ATTR_BATTERY_LEVEL] = self._sensor.battery + if self._sensor.type in LIGHTLEVEL and self._sensor.dark is not None: + attr['dark'] = self._sensor.dark if self.unit_of_measurement == 'Watts': attr[ATTR_CURRENT] = self._sensor.current attr[ATTR_VOLTAGE] = self._sensor.voltage From d5419ca29ef8decc075f6e7d0235339532d024f2 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 18 May 2018 23:15:21 +0200 Subject: [PATCH 5/5] Imported and properly configured deCONZ shouldn't need extra input to create config entry --- homeassistant/components/binary_sensor/deconz.py | 4 ++-- .../components/deconz/.translations/en.json | 2 +- homeassistant/components/deconz/__init__.py | 4 ++-- homeassistant/components/deconz/config_flow.py | 14 ++++++++++---- homeassistant/components/deconz/const.py | 2 +- homeassistant/components/deconz/strings.json | 2 +- homeassistant/components/sensor/deconz.py | 4 ++-- tests/components/binary_sensor/test_deconz.py | 2 +- tests/components/deconz/test_config_flow.py | 8 ++------ tests/components/deconz/test_init.py | 2 +- tests/components/sensor/test_deconz.py | 2 +- 11 files changed, 24 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/binary_sensor/deconz.py b/homeassistant/components/binary_sensor/deconz.py index 980781eb49b77d..6f59da0755ae37 100644 --- a/homeassistant/components/binary_sensor/deconz.py +++ b/homeassistant/components/binary_sensor/deconz.py @@ -6,7 +6,7 @@ """ from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.deconz import ( - CONF_CLIP_SENSOR, DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, + CONF_ALLOW_CLIP_SENSOR, DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, DATA_DECONZ_UNSUB) from homeassistant.const import ATTR_BATTERY_LEVEL from homeassistant.core import callback @@ -28,7 +28,7 @@ def async_add_sensor(sensors): """Add binary sensor from deCONZ.""" from pydeconz.sensor import DECONZ_BINARY_SENSOR entities = [] - allow_clip_sensor = config_entry.data.get(CONF_CLIP_SENSOR, True) + allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) for sensor in sensors: if sensor.type in DECONZ_BINARY_SENSOR and \ not (not allow_clip_sensor and sensor.type.startswith('CLIP')): diff --git a/homeassistant/components/deconz/.translations/en.json b/homeassistant/components/deconz/.translations/en.json index dcbe99b844a230..a2f90e49e3ad36 100644 --- a/homeassistant/components/deconz/.translations/en.json +++ b/homeassistant/components/deconz/.translations/en.json @@ -23,7 +23,7 @@ "options": { "title": "Extra configuration options for deCONZ", "data": { - "allow_clip_sensor": "Allow virtual sensors" + "allow_clip_sensor": "Allow importing virtual sensors" } } }, diff --git a/homeassistant/components/deconz/__init__.py b/homeassistant/components/deconz/__init__.py index c603c336d380cd..850645225d0018 100644 --- a/homeassistant/components/deconz/__init__.py +++ b/homeassistant/components/deconz/__init__.py @@ -19,7 +19,7 @@ # Loading the config flow file will register the flow from .config_flow import configured_hosts from .const import ( - CONF_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT, + CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DATA_DECONZ_EVENT, DATA_DECONZ_ID, DATA_DECONZ_UNSUB, DOMAIN, _LOGGER) REQUIREMENTS = ['pydeconz==38'] @@ -104,7 +104,7 @@ def async_add_device_callback(device_type, device): def async_add_remote(sensors): """Setup remote from deCONZ.""" from pydeconz.sensor import SWITCH as DECONZ_REMOTE - allow_clip_sensor = config_entry.data.get(CONF_CLIP_SENSOR, True) + allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) for sensor in sensors: if sensor.type in DECONZ_REMOTE and \ not (not allow_clip_sensor and sensor.type.startswith('CLIP')): diff --git a/homeassistant/components/deconz/config_flow.py b/homeassistant/components/deconz/config_flow.py index 3e2667ea9e9211..cb7c3aad7fdc6a 100644 --- a/homeassistant/components/deconz/config_flow.py +++ b/homeassistant/components/deconz/config_flow.py @@ -8,7 +8,7 @@ from homeassistant.helpers import aiohttp_client from homeassistant.util.json import load_json -from .const import CONF_CLIP_SENSOR, CONFIG_FILE, DOMAIN +from .const import CONF_ALLOW_CLIP_SENSOR, CONFIG_FILE, DOMAIN CONF_BRIDGEID = 'bridgeid' @@ -98,7 +98,8 @@ async def async_step_options(self, user_input=None): from pydeconz.utils import async_get_bridgeid if user_input is not None: - self.deconz_config[CONF_CLIP_SENSOR] = user_input[CONF_CLIP_SENSOR] + self.deconz_config[CONF_ALLOW_CLIP_SENSOR] = \ + user_input[CONF_ALLOW_CLIP_SENSOR] if CONF_BRIDGEID not in self.deconz_config: session = aiohttp_client.async_get_clientsession(self.hass) @@ -113,7 +114,7 @@ async def async_step_options(self, user_input=None): return self.async_show_form( step_id='options', data_schema=vol.Schema({ - vol.Optional(CONF_CLIP_SENSOR): bool, + vol.Optional(CONF_ALLOW_CLIP_SENSOR): bool, }), ) @@ -155,4 +156,9 @@ async def async_step_import(self, import_config): self.deconz_config = import_config if CONF_API_KEY not in import_config: return await self.async_step_link() - return await self.async_step_options() + + self.deconz_config[CONF_ALLOW_CLIP_SENSOR] = True + return self.async_create_entry( + title='deCONZ-' + self.deconz_config[CONF_BRIDGEID], + data=self.deconz_config + ) diff --git a/homeassistant/components/deconz/const.py b/homeassistant/components/deconz/const.py index a5d70e12833276..43f3c6441da0de 100644 --- a/homeassistant/components/deconz/const.py +++ b/homeassistant/components/deconz/const.py @@ -9,4 +9,4 @@ DATA_DECONZ_ID = 'deconz_entities' DATA_DECONZ_UNSUB = 'deconz_dispatchers' -CONF_CLIP_SENSOR = 'allow_clip_sensor' +CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor' diff --git a/homeassistant/components/deconz/strings.json b/homeassistant/components/deconz/strings.json index ac394e4f4d303a..cabe58694d2a76 100644 --- a/homeassistant/components/deconz/strings.json +++ b/homeassistant/components/deconz/strings.json @@ -16,7 +16,7 @@ "options": { "title": "Extra configuration options for deCONZ", "data":{ - "allow_clip_sensor": "Allow virtual sensors" + "allow_clip_sensor": "Allow importing virtual sensors" } } }, diff --git a/homeassistant/components/sensor/deconz.py b/homeassistant/components/sensor/deconz.py index 692ad40f1fc791..0db06622ad8338 100644 --- a/homeassistant/components/sensor/deconz.py +++ b/homeassistant/components/sensor/deconz.py @@ -5,7 +5,7 @@ https://home-assistant.io/components/sensor.deconz/ """ from homeassistant.components.deconz import ( - CONF_CLIP_SENSOR, DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, + CONF_ALLOW_CLIP_SENSOR, DOMAIN as DATA_DECONZ, DATA_DECONZ_ID, DATA_DECONZ_UNSUB) from homeassistant.const import ( ATTR_BATTERY_LEVEL, ATTR_VOLTAGE, DEVICE_CLASS_BATTERY) @@ -34,7 +34,7 @@ def async_add_sensor(sensors): """Add sensors from deCONZ.""" from pydeconz.sensor import DECONZ_SENSOR, SWITCH as DECONZ_REMOTE entities = [] - allow_clip_sensor = config_entry.data.get(CONF_CLIP_SENSOR, True) + allow_clip_sensor = config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True) for sensor in sensors: if sensor.type in DECONZ_SENSOR and \ not (not allow_clip_sensor and sensor.type.startswith('CLIP')): diff --git a/tests/components/binary_sensor/test_deconz.py b/tests/components/binary_sensor/test_deconz.py index e0f1a776f91bf2..2e33e28fa578cc 100644 --- a/tests/components/binary_sensor/test_deconz.py +++ b/tests/components/binary_sensor/test_deconz.py @@ -80,7 +80,7 @@ async def test_add_new_sensor(hass): assert "binary_sensor.name" in hass.data[deconz.DATA_DECONZ_ID] -async def test_do_not_add_clip_sensor(hass): +async def test_do_not_allow_clip_sensor(hass): """Test that clip sensors can be ignored.""" data = {} await setup_bridge(hass, data, allow_clip_sensor=False) diff --git a/tests/components/deconz/test_config_flow.py b/tests/components/deconz/test_config_flow.py index b759c351439cb4..df3310f3d6f233 100644 --- a/tests/components/deconz/test_config_flow.py +++ b/tests/components/deconz/test_config_flow.py @@ -144,13 +144,11 @@ async def test_bridge_discovery_config_file(hass): return_value={'host': '1.2.3.4', 'port': 8080, 'api_key': '1234567890ABCDEF'}): - await flow.async_step_discovery({ + result = await flow.async_step_discovery({ 'host': '1.2.3.4', 'port': 80, 'serial': 'id' }) - result = await flow.async_step_options( - user_input={'allow_clip_sensor': True}) assert result['type'] == 'create_entry' assert result['title'] == 'deCONZ-id' assert result['data'] == { @@ -213,14 +211,12 @@ async def test_import_with_api_key(hass): flow = config_flow.DeconzFlowHandler() flow.hass = hass - await flow.async_step_import({ + result = await flow.async_step_import({ 'bridgeid': 'id', 'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF' }) - result = await flow.async_step_options( - user_input={'allow_clip_sensor': True}) assert result['type'] == 'create_entry' assert result['title'] == 'deCONZ-id' assert result['data'] == { diff --git a/tests/components/deconz/test_init.py b/tests/components/deconz/test_init.py index 3e7f87b1774370..1cee08feb0a27a 100644 --- a/tests/components/deconz/test_init.py +++ b/tests/components/deconz/test_init.py @@ -174,7 +174,7 @@ async def test_add_new_remote(hass): assert len(hass.data[deconz.DATA_DECONZ_EVENT]) == 1 -async def test_do_not_add_clip_sensor(hass): +async def test_do_not_allow_clip_sensor(hass): """Test that clip sensors can be ignored.""" entry = Mock() entry.data = {'host': '1.2.3.4', 'port': 80, diff --git a/tests/components/sensor/test_deconz.py b/tests/components/sensor/test_deconz.py index 5ec7fc8973135b..d7cdb458646154 100644 --- a/tests/components/sensor/test_deconz.py +++ b/tests/components/sensor/test_deconz.py @@ -100,7 +100,7 @@ async def test_add_new_sensor(hass): assert "sensor.name" in hass.data[deconz.DATA_DECONZ_ID] -async def test_do_not_add_clipsensor(hass): +async def test_do_not_allow_clipsensor(hass): """Test that clip sensors can be ignored.""" data = {} await setup_bridge(hass, data, allow_clip_sensor=False)