mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
deCONZ use forward entry setup (#13990)
* Use forward entry setup with light platform * Move sensor to forward entry setup * Use forward entry setup with binary sensors * Use forward entry setup with scene platform * Remove import of unused functionality * Move deconz setup in to setup entry Create initial negative tests for setup entry * Fix hound comment * Improved tests * Add test for scene platform * Add test for binary sensor platform * Add test for light platform * Add test for light platform * Add test for sensor platform * Fix hound comment * More asserts on sensor types
This commit is contained in:
parent
5fe4053021
commit
8a10fcd985
@ -50,13 +50,18 @@ DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Track states and offer events for binary sensors."""
|
||||
component = EntityComponent(
|
||||
component = hass.data[DOMAIN] = EntityComponent(
|
||||
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
|
||||
|
||||
await component.async_setup(config)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Setup a config entry."""
|
||||
return await hass.data[DOMAIN].async_setup_entry(entry)
|
||||
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
class BinarySensorDevice(Entity):
|
||||
"""Represent a binary sensor."""
|
||||
|
@ -15,10 +15,12 @@ DEPENDENCIES = ['deconz']
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_devices,
|
||||
discovery_info=None):
|
||||
"""Set up the deCONZ binary sensor."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
"""Old way of setting up deCONZ binary sensors."""
|
||||
pass
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||
"""Set up the deCONZ binary sensor."""
|
||||
from pydeconz.sensor import DECONZ_BINARY_SENSOR
|
||||
sensors = hass.data[DATA_DECONZ].sensors
|
||||
entities = []
|
||||
|
@ -9,8 +9,7 @@ import voluptuous as vol
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY, CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import (
|
||||
aiohttp_client, discovery, config_validation as cv)
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
||||
from homeassistant.util.json import load_json
|
||||
|
||||
# Loading the config flow file will register the flow
|
||||
@ -58,28 +57,20 @@ async def async_setup(hass, config):
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Set up a deCONZ bridge for a config entry."""
|
||||
if DOMAIN in hass.data:
|
||||
_LOGGER.error(
|
||||
"Config entry failed since one deCONZ instance already exists")
|
||||
return False
|
||||
result = await async_setup_deconz(hass, None, entry.data)
|
||||
if result:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
async def async_setup_deconz(hass, config, deconz_config):
|
||||
"""Set up a deCONZ session.
|
||||
async def async_setup_entry(hass, config_entry):
|
||||
"""Set up a deCONZ bridge for a config entry.
|
||||
|
||||
Load config, group, light and sensor data for server information.
|
||||
Start websocket for push notification of state changes from deCONZ.
|
||||
"""
|
||||
_LOGGER.debug("deCONZ config %s", deconz_config)
|
||||
from pydeconz import DeconzSession
|
||||
if DOMAIN in hass.data:
|
||||
_LOGGER.error(
|
||||
"Config entry failed since one deCONZ instance already exists")
|
||||
return False
|
||||
|
||||
session = aiohttp_client.async_get_clientsession(hass)
|
||||
deconz = DeconzSession(hass.loop, session, **deconz_config)
|
||||
deconz = DeconzSession(hass.loop, session, **config_entry.data)
|
||||
result = await deconz.async_load_parameters()
|
||||
if result is False:
|
||||
_LOGGER.error("Failed to communicate with deCONZ")
|
||||
@ -89,8 +80,8 @@ async def async_setup_deconz(hass, config, deconz_config):
|
||||
hass.data[DATA_DECONZ_ID] = {}
|
||||
|
||||
for component in ['binary_sensor', 'light', 'scene', 'sensor']:
|
||||
hass.async_add_job(discovery.async_load_platform(
|
||||
hass, component, DOMAIN, {}, config))
|
||||
hass.async_add_job(hass.config_entries.async_forward_entry_setup(
|
||||
config_entry, component))
|
||||
deconz.start()
|
||||
|
||||
async def async_configure(call):
|
||||
|
@ -19,10 +19,12 @@ DEPENDENCIES = ['deconz']
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_devices,
|
||||
discovery_info=None):
|
||||
"""Set up the deCONZ light."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
"""Old way of setting up deCONZ lights."""
|
||||
pass
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||
"""Set up the deCONZ lights from a config entry."""
|
||||
lights = hass.data[DATA_DECONZ].lights
|
||||
groups = hass.data[DATA_DECONZ].groups
|
||||
entities = []
|
||||
|
@ -71,7 +71,7 @@ def activate(hass, entity_id=None):
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the scenes."""
|
||||
logger = logging.getLogger(__name__)
|
||||
component = EntityComponent(logger, DOMAIN, hass)
|
||||
component = hass.data[DOMAIN] = EntityComponent(logger, DOMAIN, hass)
|
||||
|
||||
await component.async_setup(config)
|
||||
|
||||
@ -90,6 +90,11 @@ async def async_setup(hass, config):
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Setup a config entry."""
|
||||
return await hass.data[DOMAIN].async_setup_entry(entry)
|
||||
|
||||
|
||||
class Scene(Entity):
|
||||
"""A scene is a group of entities and the states we want them to be."""
|
||||
|
||||
|
@ -13,10 +13,12 @@ DEPENDENCIES = ['deconz']
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_devices,
|
||||
discovery_info=None):
|
||||
"""Set up scenes for deCONZ component."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
"""Old way of setting up deCONZ scenes."""
|
||||
pass
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||
"""Set up scenes for deCONZ component."""
|
||||
scenes = hass.data[DATA_DECONZ].scenes
|
||||
entities = []
|
||||
|
||||
|
@ -31,8 +31,13 @@ DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES))
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Track states and offer events for sensors."""
|
||||
component = EntityComponent(
|
||||
component = hass.data[DOMAIN] = EntityComponent(
|
||||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
|
||||
|
||||
await component.async_setup(config)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Setup a config entry."""
|
||||
return await hass.data[DOMAIN].async_setup_entry(entry)
|
||||
|
@ -22,10 +22,12 @@ ATTR_EVENT_ID = 'event_id'
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_devices,
|
||||
discovery_info=None):
|
||||
"""Set up the deCONZ sensors."""
|
||||
if discovery_info is None:
|
||||
return
|
||||
"""Old way of setting up deCONZ sensors."""
|
||||
pass
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_devices):
|
||||
"""Set up the deCONZ sensors."""
|
||||
from pydeconz.sensor import DECONZ_SENSOR, SWITCH as DECONZ_REMOTE
|
||||
sensors = hass.data[DATA_DECONZ].sensors
|
||||
entities = []
|
||||
|
55
tests/components/binary_sensor/test_deconz.py
Normal file
55
tests/components/binary_sensor/test_deconz.py
Normal file
@ -0,0 +1,55 @@
|
||||
"""deCONZ binary sensor platform tests."""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import deconz
|
||||
|
||||
from tests.common import mock_coro
|
||||
|
||||
|
||||
SENSOR = {
|
||||
"1": {
|
||||
"id": "Sensor 1 id",
|
||||
"name": "Sensor 1 name",
|
||||
"type": "ZHAPresence",
|
||||
"state": {"presence": False},
|
||||
"config": {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def setup_bridge(hass, data):
|
||||
"""Load the deCONZ binary sensor platform."""
|
||||
from pydeconz import DeconzSession
|
||||
loop = Mock()
|
||||
session = Mock()
|
||||
entry = Mock()
|
||||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'}
|
||||
bridge = DeconzSession(loop, session, **entry.data)
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
await bridge.async_load_parameters()
|
||||
hass.data[deconz.DOMAIN] = bridge
|
||||
hass.data[deconz.DATA_DECONZ_ID] = {}
|
||||
config_entry = config_entries.ConfigEntry(
|
||||
1, deconz.DOMAIN, 'Mock Title', {'host': 'mock-host'}, 'test')
|
||||
await hass.config_entries.async_forward_entry_setup(
|
||||
config_entry, 'binary_sensor')
|
||||
# To flush out the service call to update the group
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_no_binary_sensors(hass):
|
||||
"""Test the update_lights function with some lights."""
|
||||
data = {}
|
||||
await setup_bridge(hass, data)
|
||||
assert len(hass.data[deconz.DATA_DECONZ_ID]) == 0
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_binary_sensors(hass):
|
||||
"""Test the update_lights function with some lights."""
|
||||
data = {"sensors": SENSOR}
|
||||
await setup_bridge(hass, data)
|
||||
assert "binary_sensor.sensor_1_name" in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert len(hass.states.async_all()) == 1
|
@ -1,9 +1,11 @@
|
||||
"""Test deCONZ component setup process."""
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components import deconz
|
||||
|
||||
from tests.common import mock_coro
|
||||
|
||||
|
||||
async def test_config_with_host_passed_to_config_entry(hass):
|
||||
"""Test that configured options for a host are loaded via config entry."""
|
||||
@ -67,3 +69,41 @@ async def test_config_discovery(hass):
|
||||
assert await async_setup_component(hass, deconz.DOMAIN, {}) is True
|
||||
# No flow started
|
||||
assert len(mock_config_entries.flow.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_setup_entry_already_registered_bridge(hass):
|
||||
"""Test setup entry doesn't allow more than one instance of deCONZ."""
|
||||
hass.data[deconz.DOMAIN] = True
|
||||
assert await deconz.async_setup_entry(hass, {}) is False
|
||||
|
||||
|
||||
async def test_setup_entry_no_available_bridge(hass):
|
||||
"""Test setup entry fails if deCONZ is not available."""
|
||||
entry = Mock()
|
||||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'}
|
||||
with patch('pydeconz.DeconzSession.async_load_parameters',
|
||||
return_value=mock_coro(False)):
|
||||
assert await deconz.async_setup_entry(hass, entry) is False
|
||||
|
||||
|
||||
async def test_setup_entry_successful(hass):
|
||||
"""Test setup entry is successful."""
|
||||
entry = Mock()
|
||||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'}
|
||||
with patch.object(hass, 'async_add_job') as mock_add_job, \
|
||||
patch.object(hass, 'config_entries') as mock_config_entries, \
|
||||
patch('pydeconz.DeconzSession.async_load_parameters',
|
||||
return_value=mock_coro(True)):
|
||||
assert await deconz.async_setup_entry(hass, entry) is True
|
||||
assert hass.data[deconz.DOMAIN]
|
||||
assert hass.data[deconz.DATA_DECONZ_ID] == {}
|
||||
assert len(mock_add_job.mock_calls) == 4
|
||||
assert len(mock_config_entries.async_forward_entry_setup.mock_calls) == 4
|
||||
assert mock_config_entries.async_forward_entry_setup.mock_calls[0][1] == \
|
||||
(entry, 'binary_sensor')
|
||||
assert mock_config_entries.async_forward_entry_setup.mock_calls[1][1] == \
|
||||
(entry, 'light')
|
||||
assert mock_config_entries.async_forward_entry_setup.mock_calls[2][1] == \
|
||||
(entry, 'scene')
|
||||
assert mock_config_entries.async_forward_entry_setup.mock_calls[3][1] == \
|
||||
(entry, 'sensor')
|
||||
|
74
tests/components/light/test_deconz.py
Normal file
74
tests/components/light/test_deconz.py
Normal file
@ -0,0 +1,74 @@
|
||||
"""deCONZ light platform tests."""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import deconz
|
||||
|
||||
from tests.common import mock_coro
|
||||
|
||||
|
||||
LIGHT = {
|
||||
"1": {
|
||||
"id": "Light 1 id",
|
||||
"name": "Light 1 name",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
|
||||
GROUP = {
|
||||
"1": {
|
||||
"id": "Group 1 id",
|
||||
"name": "Group 1 name",
|
||||
"state": {},
|
||||
"action": {},
|
||||
"scenes": [],
|
||||
"lights": [
|
||||
"1",
|
||||
"2"
|
||||
]
|
||||
},
|
||||
"2": {
|
||||
"id": "Group 2 id",
|
||||
"name": "Group 2 name",
|
||||
"state": {},
|
||||
"action": {},
|
||||
"scenes": []
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def setup_bridge(hass, data):
|
||||
"""Load the deCONZ light platform."""
|
||||
from pydeconz import DeconzSession
|
||||
loop = Mock()
|
||||
session = Mock()
|
||||
entry = Mock()
|
||||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'}
|
||||
bridge = DeconzSession(loop, session, **entry.data)
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
await bridge.async_load_parameters()
|
||||
hass.data[deconz.DOMAIN] = bridge
|
||||
hass.data[deconz.DATA_DECONZ_ID] = {}
|
||||
config_entry = config_entries.ConfigEntry(
|
||||
1, deconz.DOMAIN, 'Mock Title', {'host': 'mock-host'}, 'test')
|
||||
await hass.config_entries.async_forward_entry_setup(config_entry, 'light')
|
||||
# To flush out the service call to update the group
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_no_lights_or_groups(hass):
|
||||
"""Test the update_lights function with some lights."""
|
||||
data = {}
|
||||
await setup_bridge(hass, data)
|
||||
assert len(hass.data[deconz.DATA_DECONZ_ID]) == 0
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_lights_and_groups(hass):
|
||||
"""Test the update_lights function with some lights."""
|
||||
await setup_bridge(hass, {"lights": LIGHT, "groups": GROUP})
|
||||
assert "light.light_1_name" in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert "light.group_1_name" in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert "light.group_2_name" not in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert len(hass.states.async_all()) == 3
|
57
tests/components/scene/test_deconz.py
Normal file
57
tests/components/scene/test_deconz.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""deCONZ scenes platform tests."""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import deconz
|
||||
|
||||
from tests.common import mock_coro
|
||||
|
||||
|
||||
GROUP = {
|
||||
"1": {
|
||||
"id": "Group 1 id",
|
||||
"name": "Group 1 name",
|
||||
"state": {},
|
||||
"action": {},
|
||||
"scenes": [{
|
||||
"id": "1",
|
||||
"name": "Scene 1"
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def setup_bridge(hass, data):
|
||||
"""Load the deCONZ scene platform."""
|
||||
from pydeconz import DeconzSession
|
||||
loop = Mock()
|
||||
session = Mock()
|
||||
entry = Mock()
|
||||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'}
|
||||
bridge = DeconzSession(loop, session, **entry.data)
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
await bridge.async_load_parameters()
|
||||
hass.data[deconz.DOMAIN] = bridge
|
||||
hass.data[deconz.DATA_DECONZ_ID] = {}
|
||||
config_entry = config_entries.ConfigEntry(
|
||||
1, deconz.DOMAIN, 'Mock Title', {'host': 'mock-host'}, 'test')
|
||||
await hass.config_entries.async_forward_entry_setup(config_entry, 'scene')
|
||||
# To flush out the service call to update the group
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_no_scenes(hass):
|
||||
"""Test the update_lights function with some lights."""
|
||||
data = {}
|
||||
await setup_bridge(hass, data)
|
||||
assert len(hass.data[deconz.DATA_DECONZ_ID]) == 0
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_scenes(hass):
|
||||
"""Test the update_lights function with some lights."""
|
||||
data = {"groups": GROUP}
|
||||
await setup_bridge(hass, data)
|
||||
assert "scene.group_1_name_scene_1" in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert len(hass.states.async_all()) == 1
|
82
tests/components/sensor/test_deconz.py
Normal file
82
tests/components/sensor/test_deconz.py
Normal file
@ -0,0 +1,82 @@
|
||||
"""deCONZ sensor platform tests."""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import deconz
|
||||
|
||||
from tests.common import mock_coro
|
||||
|
||||
|
||||
SENSOR = {
|
||||
"1": {
|
||||
"id": "Sensor 1 id",
|
||||
"name": "Sensor 1 name",
|
||||
"type": "ZHATemperature",
|
||||
"state": {"temperature": False},
|
||||
"config": {}
|
||||
},
|
||||
"2": {
|
||||
"id": "Sensor 2 id",
|
||||
"name": "Sensor 2 name",
|
||||
"type": "ZHAPresence",
|
||||
"state": {"presence": False},
|
||||
"config": {}
|
||||
},
|
||||
"3": {
|
||||
"id": "Sensor 3 id",
|
||||
"name": "Sensor 3 name",
|
||||
"type": "ZHASwitch",
|
||||
"state": {"buttonevent": 1000},
|
||||
"config": {}
|
||||
},
|
||||
"4": {
|
||||
"id": "Sensor 4 id",
|
||||
"name": "Sensor 4 name",
|
||||
"type": "ZHASwitch",
|
||||
"state": {"buttonevent": 1000},
|
||||
"config": {"battery": 100}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async def setup_bridge(hass, data):
|
||||
"""Load the deCONZ sensor platform."""
|
||||
from pydeconz import DeconzSession
|
||||
loop = Mock()
|
||||
session = Mock()
|
||||
entry = Mock()
|
||||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'}
|
||||
bridge = DeconzSession(loop, session, **entry.data)
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
await bridge.async_load_parameters()
|
||||
hass.data[deconz.DOMAIN] = bridge
|
||||
hass.data[deconz.DATA_DECONZ_ID] = {}
|
||||
config_entry = config_entries.ConfigEntry(
|
||||
1, deconz.DOMAIN, 'Mock Title', {'host': 'mock-host'}, '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()
|
||||
|
||||
|
||||
async def test_no_sensors(hass):
|
||||
"""Test the update_lights function with some lights."""
|
||||
data = {}
|
||||
await setup_bridge(hass, data)
|
||||
assert len(hass.data[deconz.DATA_DECONZ_ID]) == 0
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_binary_sensors(hass):
|
||||
"""Test the update_lights function with some lights."""
|
||||
data = {"sensors": SENSOR}
|
||||
await setup_bridge(hass, data)
|
||||
assert "sensor.sensor_1_name" in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert "sensor.sensor_2_name" not in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert "sensor.sensor_3_name" not in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert "sensor.sensor_3_name_battery_level" not in \
|
||||
hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert "sensor.sensor_4_name" not in hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert "sensor.sensor_4_name_battery_level" in \
|
||||
hass.data[deconz.DATA_DECONZ_ID]
|
||||
assert len(hass.states.async_all()) == 2
|
Loading…
x
Reference in New Issue
Block a user