Support multiple deCONZ gateways (#22449)
* Store gateways inside a dict in deconz domain * Make reachable events gateway specific * Gateway shall always exist * Adapt new device signalling to support multiple gateways * Services follow gateway master * Working on unload entry * Make unload and master handover work Improve tests for init * Fix config flow * Fix linting * Clean up init tests * Clean up hassio discovery to fit with the rest * Store gateways inside a dict in deconz domain * Make reachable events gateway specific * Gateway shall always exist * Adapt new device signalling to support multiple gateways * Services follow gateway master * Working on unload entry * Make unload and master handover work Improve tests for init * Fix config flow * Fix linting * Clean up init tests * Clean up hassio discovery to fit with the rest * Add support for services to specify bridgeidpull/22751/head
parent
b9ec623ad9
commit
b50afec5f1
|
@ -4,12 +4,14 @@ import voluptuous as vol
|
|||
from homeassistant import config_entries
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY, CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
|
||||
# Loading the config flow file will register the flow
|
||||
from .config_flow import configured_hosts
|
||||
from .const import DEFAULT_PORT, DOMAIN, _LOGGER
|
||||
from .config_flow import get_master_gateway
|
||||
from .const import (
|
||||
CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID,
|
||||
CONF_MASTER_GATEWAY, DEFAULT_PORT, DOMAIN, _LOGGER)
|
||||
from .gateway import DeconzGateway
|
||||
|
||||
REQUIREMENTS = ['pydeconz==54']
|
||||
|
@ -32,26 +34,27 @@ SERVICE_SCHEMA = vol.All(vol.Schema({
|
|||
vol.Optional(SERVICE_ENTITY): cv.entity_id,
|
||||
vol.Optional(SERVICE_FIELD): cv.matches_regex('/.*'),
|
||||
vol.Required(SERVICE_DATA): dict,
|
||||
vol.Optional(CONF_BRIDGEID): str
|
||||
}), cv.has_at_least_one_key(SERVICE_ENTITY, SERVICE_FIELD))
|
||||
|
||||
SERVICE_DEVICE_REFRESH = 'device_refresh'
|
||||
|
||||
SERVICE_DEVICE_REFRESCH_SCHEMA = vol.All(vol.Schema({
|
||||
vol.Optional(CONF_BRIDGEID): str
|
||||
}))
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Load configuration for deCONZ component.
|
||||
|
||||
Discovery has loaded the component if DOMAIN is not present in config.
|
||||
"""
|
||||
if DOMAIN in config:
|
||||
deconz_config = None
|
||||
if CONF_HOST in config[DOMAIN]:
|
||||
deconz_config = config[DOMAIN]
|
||||
if deconz_config and not configured_hosts(hass):
|
||||
hass.async_add_job(hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={'source': config_entries.SOURCE_IMPORT},
|
||||
data=deconz_config
|
||||
))
|
||||
if not hass.config_entries.async_entries(DOMAIN) and DOMAIN in config:
|
||||
deconz_config = config[DOMAIN]
|
||||
hass.async_add_job(hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={'source': config_entries.SOURCE_IMPORT},
|
||||
data=deconz_config
|
||||
))
|
||||
return True
|
||||
|
||||
|
||||
|
@ -61,26 +64,20 @@ async def async_setup_entry(hass, config_entry):
|
|||
Load config, group, light and sensor data for server information.
|
||||
Start websocket for push notification of state changes from deCONZ.
|
||||
"""
|
||||
if DOMAIN in hass.data:
|
||||
_LOGGER.error(
|
||||
"Config entry failed since one deCONZ instance already exists")
|
||||
return False
|
||||
if DOMAIN not in hass.data:
|
||||
hass.data[DOMAIN] = {}
|
||||
|
||||
if not config_entry.options:
|
||||
await async_populate_options(hass, config_entry)
|
||||
|
||||
gateway = DeconzGateway(hass, config_entry)
|
||||
|
||||
if not await gateway.async_setup():
|
||||
return False
|
||||
|
||||
hass.data[DOMAIN] = gateway
|
||||
hass.data[DOMAIN][gateway.bridgeid] = gateway
|
||||
|
||||
device_registry = await \
|
||||
hass.helpers.device_registry.async_get_registry()
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(CONNECTION_NETWORK_MAC, gateway.api.config.mac)},
|
||||
identifiers={(DOMAIN, gateway.api.config.bridgeid)},
|
||||
manufacturer='Dresden Elektronik', model=gateway.api.config.modelid,
|
||||
name=gateway.api.config.name, sw_version=gateway.api.config.swversion)
|
||||
await gateway.async_update_device_registry()
|
||||
|
||||
async def async_configure(call):
|
||||
"""Set attribute of device in deCONZ.
|
||||
|
@ -100,8 +97,11 @@ async def async_setup_entry(hass, config_entry):
|
|||
"""
|
||||
field = call.data.get(SERVICE_FIELD, '')
|
||||
entity_id = call.data.get(SERVICE_ENTITY)
|
||||
data = call.data.get(SERVICE_DATA)
|
||||
gateway = hass.data[DOMAIN]
|
||||
data = call.data[SERVICE_DATA]
|
||||
|
||||
gateway = get_master_gateway(hass)
|
||||
if CONF_BRIDGEID in call.data:
|
||||
gateway = hass.data[DOMAIN][call.data[CONF_BRIDGEID]]
|
||||
|
||||
if entity_id:
|
||||
try:
|
||||
|
@ -117,7 +117,9 @@ async def async_setup_entry(hass, config_entry):
|
|||
|
||||
async def async_refresh_devices(call):
|
||||
"""Refresh available devices from deCONZ."""
|
||||
gateway = hass.data[DOMAIN]
|
||||
gateway = get_master_gateway(hass)
|
||||
if CONF_BRIDGEID in call.data:
|
||||
gateway = hass.data[DOMAIN][call.data[CONF_BRIDGEID]]
|
||||
|
||||
groups = set(gateway.api.groups.keys())
|
||||
lights = set(gateway.api.lights.keys())
|
||||
|
@ -151,7 +153,8 @@ async def async_setup_entry(hass, config_entry):
|
|||
)
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN, SERVICE_DEVICE_REFRESH, async_refresh_devices)
|
||||
DOMAIN, SERVICE_DEVICE_REFRESH, async_refresh_devices,
|
||||
schema=SERVICE_DEVICE_REFRESCH_SCHEMA)
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, gateway.shutdown)
|
||||
return True
|
||||
|
@ -159,7 +162,34 @@ async def async_setup_entry(hass, config_entry):
|
|||
|
||||
async def async_unload_entry(hass, config_entry):
|
||||
"""Unload deCONZ config entry."""
|
||||
gateway = hass.data.pop(DOMAIN)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_DECONZ)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH)
|
||||
gateway = hass.data[DOMAIN].pop(config_entry.data[CONF_BRIDGEID])
|
||||
|
||||
if not hass.data[DOMAIN]:
|
||||
hass.services.async_remove(DOMAIN, SERVICE_DECONZ)
|
||||
hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH)
|
||||
elif gateway.master:
|
||||
await async_populate_options(hass, config_entry)
|
||||
new_master_gateway = next(iter(hass.data[DOMAIN].values()))
|
||||
await async_populate_options(hass, new_master_gateway.config_entry)
|
||||
|
||||
return await gateway.async_reset()
|
||||
|
||||
|
||||
@callback
|
||||
async def async_populate_options(hass, config_entry):
|
||||
"""Populate default options for gateway.
|
||||
|
||||
Called by setup_entry and unload_entry.
|
||||
Makes sure there is always one master available.
|
||||
"""
|
||||
master = not get_master_gateway(hass)
|
||||
|
||||
options = {
|
||||
CONF_MASTER_GATEWAY: master,
|
||||
CONF_ALLOW_CLIP_SENSOR: config_entry.data.get(
|
||||
CONF_ALLOW_CLIP_SENSOR, False),
|
||||
CONF_ALLOW_DECONZ_GROUPS: config_entry.data.get(
|
||||
CONF_ALLOW_DECONZ_GROUPS, True)
|
||||
}
|
||||
|
||||
hass.config_entries.async_update_entry(config_entry, options=options)
|
||||
|
|
|
@ -4,10 +4,9 @@ from homeassistant.const import ATTR_BATTERY_LEVEL
|
|||
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,
|
||||
NEW_SENSOR)
|
||||
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
|
||||
DEPENDENCIES = ['deconz']
|
||||
|
||||
|
@ -24,22 +23,26 @@ async def async_setup_platform(
|
|||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the deCONZ binary sensor."""
|
||||
gateway = hass.data[DECONZ_DOMAIN]
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
|
||||
@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_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')):
|
||||
not (not gateway.allow_clip_sensor and
|
||||
sensor.type.startswith('CLIP')):
|
||||
|
||||
entities.append(DeconzBinarySensor(sensor, gateway))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
gateway.listeners.append(
|
||||
async_dispatcher_connect(hass, NEW_SENSOR, async_add_sensor))
|
||||
gateway.listeners.append(async_dispatcher_connect(
|
||||
hass, gateway.async_event_new_device(NEW_SENSOR), async_add_sensor))
|
||||
|
||||
async_add_sensor(gateway.api.sensors.values())
|
||||
|
||||
|
|
|
@ -7,10 +7,9 @@ from homeassistant.const import (
|
|||
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 .const import ATTR_OFFSET, ATTR_VALVE, NEW_SENSOR
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
|
||||
DEPENDENCIES = ['deconz']
|
||||
|
||||
|
@ -20,22 +19,26 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
|
||||
Thermostats are based on the same device class as sensors in deCONZ.
|
||||
"""
|
||||
gateway = hass.data[DECONZ_DOMAIN]
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
|
||||
@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')):
|
||||
not (not gateway.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))
|
||||
gateway.listeners.append(async_dispatcher_connect(
|
||||
hass, gateway.async_event_new_device(NEW_SENSOR), async_add_climate))
|
||||
|
||||
async_add_climate(gateway.api.sensors.values())
|
||||
|
||||
|
|
|
@ -9,10 +9,7 @@ from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
|
|||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
|
||||
from .const import (
|
||||
CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID,
|
||||
DEFAULT_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_DECONZ_GROUPS, DEFAULT_PORT,
|
||||
DOMAIN)
|
||||
from .const import CONF_BRIDGEID, DEFAULT_PORT, DOMAIN
|
||||
|
||||
|
||||
@callback
|
||||
|
@ -22,6 +19,14 @@ def configured_hosts(hass):
|
|||
in hass.config_entries.async_entries(DOMAIN))
|
||||
|
||||
|
||||
@callback
|
||||
def get_master_gateway(hass):
|
||||
"""Return a bool telling if this is the master gateway."""
|
||||
for gateway in hass.data[DOMAIN].values():
|
||||
if gateway.master:
|
||||
return gateway
|
||||
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class DeconzFlowHandler(config_entries.ConfigFlow):
|
||||
"""Handle a deCONZ config flow."""
|
||||
|
@ -39,16 +44,12 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
|||
async def async_step_user(self, user_input=None):
|
||||
"""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.
|
||||
If no bridge is found allow user to manually input configuration.
|
||||
"""
|
||||
from pydeconz.utils import async_discovery
|
||||
|
||||
if configured_hosts(self.hass):
|
||||
return self.async_abort(reason='one_instance_only')
|
||||
|
||||
if user_input is not None:
|
||||
for bridge in self.bridges:
|
||||
if bridge[CONF_HOST] == user_input[CONF_HOST]:
|
||||
|
@ -99,9 +100,6 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
|||
errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
if configured_hosts(self.hass):
|
||||
return self.async_abort(reason='one_instance_only')
|
||||
|
||||
session = aiohttp_client.async_get_clientsession(self.hass)
|
||||
|
||||
try:
|
||||
|
@ -114,51 +112,32 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
|||
|
||||
else:
|
||||
self.deconz_config[CONF_API_KEY] = api_key
|
||||
return await self.async_step_options()
|
||||
return await self._create_entry()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id='link',
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_options(self, user_input=None):
|
||||
"""Extra options for deCONZ.
|
||||
|
||||
CONF_CLIP_SENSOR -- Allow user to choose if they want clip sensors.
|
||||
CONF_DECONZ_GROUPS -- Allow user to choose if they want deCONZ groups.
|
||||
"""
|
||||
async def _create_entry(self):
|
||||
"""Create entry for gateway."""
|
||||
from pydeconz.utils import async_get_bridgeid
|
||||
|
||||
if user_input is not None:
|
||||
self.deconz_config[CONF_ALLOW_CLIP_SENSOR] = \
|
||||
user_input[CONF_ALLOW_CLIP_SENSOR]
|
||||
self.deconz_config[CONF_ALLOW_DECONZ_GROUPS] = \
|
||||
user_input[CONF_ALLOW_DECONZ_GROUPS]
|
||||
if CONF_BRIDGEID not in self.deconz_config:
|
||||
session = aiohttp_client.async_get_clientsession(self.hass)
|
||||
|
||||
if CONF_BRIDGEID not in self.deconz_config:
|
||||
session = aiohttp_client.async_get_clientsession(self.hass)
|
||||
try:
|
||||
with async_timeout.timeout(10):
|
||||
self.deconz_config[CONF_BRIDGEID] = \
|
||||
await async_get_bridgeid(
|
||||
session, **self.deconz_config)
|
||||
try:
|
||||
with async_timeout.timeout(10):
|
||||
self.deconz_config[CONF_BRIDGEID] = \
|
||||
await async_get_bridgeid(
|
||||
session, **self.deconz_config)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
return self.async_abort(reason='no_bridges')
|
||||
except asyncio.TimeoutError:
|
||||
return self.async_abort(reason='no_bridges')
|
||||
|
||||
return self.async_create_entry(
|
||||
title='deCONZ-' + self.deconz_config[CONF_BRIDGEID],
|
||||
data=self.deconz_config
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id='options',
|
||||
data_schema=vol.Schema({
|
||||
vol.Optional(CONF_ALLOW_CLIP_SENSOR,
|
||||
default=DEFAULT_ALLOW_CLIP_SENSOR): bool,
|
||||
vol.Optional(CONF_ALLOW_DECONZ_GROUPS,
|
||||
default=DEFAULT_ALLOW_DECONZ_GROUPS): bool,
|
||||
}),
|
||||
return self.async_create_entry(
|
||||
title='deCONZ-' + self.deconz_config[CONF_BRIDGEID],
|
||||
data=self.deconz_config
|
||||
)
|
||||
|
||||
async def async_step_discovery(self, discovery_info):
|
||||
|
@ -166,10 +145,14 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
|||
|
||||
This flow is triggered by the discovery component.
|
||||
"""
|
||||
deconz_config = {}
|
||||
deconz_config[CONF_HOST] = discovery_info.get(CONF_HOST)
|
||||
deconz_config[CONF_PORT] = discovery_info.get(CONF_PORT)
|
||||
deconz_config[CONF_BRIDGEID] = discovery_info.get('serial')
|
||||
deconz_config = {
|
||||
CONF_HOST: discovery_info[CONF_HOST],
|
||||
CONF_PORT: discovery_info[CONF_PORT],
|
||||
CONF_BRIDGEID: discovery_info['serial']
|
||||
}
|
||||
|
||||
if deconz_config[CONF_HOST] in configured_hosts(self.hass):
|
||||
return self.async_abort(reason='one_instance_only')
|
||||
|
||||
return await self.async_step_import(deconz_config)
|
||||
|
||||
|
@ -186,16 +169,11 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
|||
Otherwise we will delegate to `link` step which
|
||||
will ask user to link the bridge.
|
||||
"""
|
||||
if configured_hosts(self.hass):
|
||||
return self.async_abort(reason='one_instance_only')
|
||||
|
||||
self.deconz_config = import_config
|
||||
if CONF_API_KEY not in import_config:
|
||||
return await self.async_step_link()
|
||||
|
||||
user_input = {CONF_ALLOW_CLIP_SENSOR: True,
|
||||
CONF_ALLOW_DECONZ_GROUPS: True}
|
||||
return await self.async_step_options(user_input=user_input)
|
||||
return await self._create_entry()
|
||||
|
||||
async def async_step_hassio(self, user_input=None):
|
||||
"""Prepare configuration for a Hass.io deCONZ bridge.
|
||||
|
@ -212,29 +190,18 @@ class DeconzFlowHandler(config_entries.ConfigFlow):
|
|||
async def async_step_hassio_confirm(self, user_input=None):
|
||||
"""Confirm a Hass.io discovery."""
|
||||
if user_input is not None:
|
||||
data = self._hassio_discovery
|
||||
self.deconz_config = {
|
||||
CONF_HOST: self._hassio_discovery[CONF_HOST],
|
||||
CONF_PORT: self._hassio_discovery[CONF_PORT],
|
||||
CONF_BRIDGEID: self._hassio_discovery['serial'],
|
||||
CONF_API_KEY: self._hassio_discovery[CONF_API_KEY]
|
||||
}
|
||||
|
||||
return self.async_create_entry(
|
||||
title=data['addon'], data={
|
||||
CONF_HOST: data[CONF_HOST],
|
||||
CONF_PORT: data[CONF_PORT],
|
||||
CONF_BRIDGEID: data['serial'],
|
||||
CONF_API_KEY: data[CONF_API_KEY],
|
||||
CONF_ALLOW_CLIP_SENSOR:
|
||||
user_input[CONF_ALLOW_CLIP_SENSOR],
|
||||
CONF_ALLOW_DECONZ_GROUPS:
|
||||
user_input[CONF_ALLOW_DECONZ_GROUPS],
|
||||
})
|
||||
return await self._create_entry()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id='hassio_confirm',
|
||||
description_placeholders={
|
||||
'addon': self._hassio_discovery['addon']
|
||||
},
|
||||
data_schema=vol.Schema({
|
||||
vol.Optional(CONF_ALLOW_CLIP_SENSOR,
|
||||
default=DEFAULT_ALLOW_CLIP_SENSOR): bool,
|
||||
vol.Optional(CONF_ALLOW_DECONZ_GROUPS,
|
||||
default=DEFAULT_ALLOW_DECONZ_GROUPS): bool,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -12,22 +12,21 @@ DEFAULT_ALLOW_DECONZ_GROUPS = False
|
|||
CONF_ALLOW_CLIP_SENSOR = 'allow_clip_sensor'
|
||||
CONF_ALLOW_DECONZ_GROUPS = 'allow_deconz_groups'
|
||||
CONF_BRIDGEID = 'bridgeid'
|
||||
CONF_MASTER_GATEWAY = 'master'
|
||||
|
||||
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_GROUP = 'group'
|
||||
NEW_LIGHT = 'light'
|
||||
NEW_SCENE = 'scene'
|
||||
NEW_SENSOR = 'sensor'
|
||||
|
||||
NEW_DEVICE = {
|
||||
'group': NEW_GROUP,
|
||||
'light': NEW_LIGHT,
|
||||
'scene': NEW_SCENE,
|
||||
'sensor': NEW_SENSOR
|
||||
NEW_GROUP: 'deconz_new_group_{}',
|
||||
NEW_LIGHT: 'deconz_new_light_{}',
|
||||
NEW_SCENE: 'deconz_new_scene_{}',
|
||||
NEW_SENSOR: 'deconz_new_sensor_{}'
|
||||
}
|
||||
|
||||
ATTR_DARK = 'dark'
|
||||
|
|
|
@ -5,9 +5,9 @@ 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, NEW_LIGHT, WINDOW_COVERS)
|
||||
from .const import COVER_TYPES, DAMPERS, NEW_LIGHT, WINDOW_COVERS
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
|
||||
DEPENDENCIES = ['deconz']
|
||||
|
||||
|
@ -25,22 +25,26 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
|
||||
Covers are based on same device class as lights in deCONZ.
|
||||
"""
|
||||
gateway = hass.data[DECONZ_DOMAIN]
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
|
||||
@callback
|
||||
def async_add_cover(lights):
|
||||
"""Add cover from deCONZ."""
|
||||
entities = []
|
||||
|
||||
for light in lights:
|
||||
|
||||
if light.type in COVER_TYPES:
|
||||
if light.modelid in ZIGBEE_SPEC:
|
||||
entities.append(DeconzCoverZigbeeSpec(light, gateway))
|
||||
|
||||
else:
|
||||
entities.append(DeconzCover(light, gateway))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
gateway.listeners.append(
|
||||
async_dispatcher_connect(hass, NEW_LIGHT, async_add_cover))
|
||||
gateway.listeners.append(async_dispatcher_connect(
|
||||
hass, gateway.async_event_new_device(NEW_LIGHT), async_add_cover))
|
||||
|
||||
async_add_cover(gateway.api.lights.values())
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
|
|||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from .const import DECONZ_REACHABLE, DOMAIN as DECONZ_DOMAIN
|
||||
from .const import DOMAIN as DECONZ_DOMAIN
|
||||
|
||||
|
||||
class DeconzDevice(Entity):
|
||||
|
@ -21,7 +21,8 @@ class DeconzDevice(Entity):
|
|||
self._device.register_async_callback(self.async_update_callback)
|
||||
self.gateway.deconz_ids[self.entity_id] = self._device.deconz_id
|
||||
self.unsub_dispatcher = async_dispatcher_connect(
|
||||
self.hass, DECONZ_REACHABLE, self.async_update_callback)
|
||||
self.hass, self.gateway.event_reachable,
|
||||
self.async_update_callback)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Disconnect device object when removed."""
|
||||
|
|
|
@ -6,16 +6,23 @@ from homeassistant.exceptions import ConfigEntryNotReady
|
|||
from homeassistant.const import CONF_EVENT, CONF_HOST, CONF_ID
|
||||
from homeassistant.core import EventOrigin, callback
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect, async_dispatcher_send)
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from .const import (
|
||||
_LOGGER, DECONZ_REACHABLE, CONF_ALLOW_CLIP_SENSOR, NEW_DEVICE, NEW_SENSOR,
|
||||
SUPPORTED_PLATFORMS)
|
||||
_LOGGER, CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_DECONZ_GROUPS, CONF_BRIDGEID,
|
||||
CONF_MASTER_GATEWAY, DOMAIN, NEW_DEVICE, NEW_SENSOR, SUPPORTED_PLATFORMS)
|
||||
from .errors import AuthenticationRequired, CannotConnect
|
||||
|
||||
|
||||
@callback
|
||||
def get_gateway_from_config_entry(hass, config_entry):
|
||||
"""Return gateway with a matching bridge id."""
|
||||
return hass.data[DOMAIN][config_entry.data[CONF_BRIDGEID]]
|
||||
|
||||
|
||||
class DeconzGateway:
|
||||
"""Manages a single deCONZ gateway."""
|
||||
|
||||
|
@ -30,6 +37,40 @@ class DeconzGateway:
|
|||
self.events = []
|
||||
self.listeners = []
|
||||
|
||||
@property
|
||||
def bridgeid(self) -> str:
|
||||
"""Return the unique identifier of the gateway."""
|
||||
return self.config_entry.data[CONF_BRIDGEID]
|
||||
|
||||
@property
|
||||
def master(self) -> bool:
|
||||
"""Gateway which is used with deCONZ services without defining id."""
|
||||
return self.config_entry.options[CONF_MASTER_GATEWAY]
|
||||
|
||||
@property
|
||||
def allow_clip_sensor(self) -> bool:
|
||||
"""Allow loading clip sensor from gateway."""
|
||||
return self.config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, True)
|
||||
|
||||
@property
|
||||
def allow_deconz_groups(self) -> bool:
|
||||
"""Allow loading deCONZ groups from gateway."""
|
||||
return self.config_entry.data.get(CONF_ALLOW_DECONZ_GROUPS, True)
|
||||
|
||||
async def async_update_device_registry(self):
|
||||
"""Update device registry."""
|
||||
device_registry = await \
|
||||
self.hass.helpers.device_registry.async_get_registry()
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=self.config_entry.entry_id,
|
||||
connections={(CONNECTION_NETWORK_MAC, self.api.config.mac)},
|
||||
identifiers={(DOMAIN, self.api.config.bridgeid)},
|
||||
manufacturer='Dresden Elektronik',
|
||||
model=self.api.config.modelid,
|
||||
name=self.api.config.name,
|
||||
sw_version=self.api.config.swversion
|
||||
)
|
||||
|
||||
async def async_setup(self):
|
||||
"""Set up a deCONZ gateway."""
|
||||
hass = self.hass
|
||||
|
@ -52,9 +93,9 @@ class DeconzGateway:
|
|||
hass.config_entries.async_forward_entry_setup(
|
||||
self.config_entry, component))
|
||||
|
||||
self.listeners.append(
|
||||
async_dispatcher_connect(
|
||||
hass, NEW_SENSOR, self.async_add_remote))
|
||||
self.listeners.append(async_dispatcher_connect(
|
||||
hass, self.async_event_new_device(NEW_SENSOR),
|
||||
self.async_add_remote))
|
||||
|
||||
self.async_add_remote(self.api.sensors.values())
|
||||
|
||||
|
@ -62,29 +103,39 @@ class DeconzGateway:
|
|||
|
||||
return True
|
||||
|
||||
@property
|
||||
def event_reachable(self):
|
||||
"""Gateway specific event to signal a change in connection status."""
|
||||
return 'deconz_reachable_{}'.format(self.bridgeid)
|
||||
|
||||
@callback
|
||||
def async_connection_status_callback(self, available):
|
||||
"""Handle signals of gateway connection status."""
|
||||
self.available = available
|
||||
async_dispatcher_send(
|
||||
self.hass, DECONZ_REACHABLE, {'state': True, 'attr': 'reachable'})
|
||||
async_dispatcher_send(self.hass, self.event_reachable,
|
||||
{'state': True, 'attr': 'reachable'})
|
||||
|
||||
@callback
|
||||
def async_event_new_device(self, device_type):
|
||||
"""Gateway specific event to signal new device."""
|
||||
return NEW_DEVICE[device_type].format(self.bridgeid)
|
||||
|
||||
@callback
|
||||
def async_add_device_callback(self, device_type, device):
|
||||
"""Handle event of new device creation in deCONZ."""
|
||||
if not isinstance(device, list):
|
||||
device = [device]
|
||||
async_dispatcher_send(self.hass, NEW_DEVICE[device_type], device)
|
||||
async_dispatcher_send(
|
||||
self.hass, self.async_event_new_device(device_type), device)
|
||||
|
||||
@callback
|
||||
def async_add_remote(self, sensors):
|
||||
"""Set up remote from deCONZ."""
|
||||
from pydeconz.sensor import SWITCH as DECONZ_REMOTE
|
||||
allow_clip_sensor = self.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')):
|
||||
not (not self.allow_clip_sensor and
|
||||
sensor.type.startswith('CLIP')):
|
||||
self.events.append(DeconzEvent(self.hass, sensor))
|
||||
|
||||
@callback
|
||||
|
|
|
@ -8,10 +8,9 @@ from homeassistant.core import callback
|
|||
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, NEW_GROUP,
|
||||
NEW_LIGHT, SWITCH_TYPES)
|
||||
from .const import COVER_TYPES, NEW_GROUP, NEW_LIGHT, SWITCH_TYPES
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
|
||||
DEPENDENCIES = ['deconz']
|
||||
|
||||
|
@ -24,32 +23,35 @@ async def async_setup_platform(
|
|||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the deCONZ lights and groups from a config entry."""
|
||||
gateway = hass.data[DECONZ_DOMAIN]
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
|
||||
@callback
|
||||
def async_add_light(lights):
|
||||
"""Add light from deCONZ."""
|
||||
entities = []
|
||||
|
||||
for light in lights:
|
||||
if light.type not in COVER_TYPES + SWITCH_TYPES:
|
||||
entities.append(DeconzLight(light, gateway))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
gateway.listeners.append(
|
||||
async_dispatcher_connect(hass, NEW_LIGHT, async_add_light))
|
||||
gateway.listeners.append(async_dispatcher_connect(
|
||||
hass, gateway.async_event_new_device(NEW_LIGHT), async_add_light))
|
||||
|
||||
@callback
|
||||
def async_add_group(groups):
|
||||
"""Add group from deCONZ."""
|
||||
entities = []
|
||||
allow_group = config_entry.data.get(CONF_ALLOW_DECONZ_GROUPS, True)
|
||||
|
||||
for group in groups:
|
||||
if group.lights and allow_group:
|
||||
if group.lights and gateway.allow_deconz_groups:
|
||||
entities.append(DeconzLight(group, gateway))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
gateway.listeners.append(
|
||||
async_dispatcher_connect(hass, NEW_GROUP, async_add_group))
|
||||
gateway.listeners.append(async_dispatcher_connect(
|
||||
hass, gateway.async_event_new_device(NEW_GROUP), async_add_group))
|
||||
|
||||
async_add_light(gateway.api.lights.values())
|
||||
async_add_group(gateway.api.groups.values())
|
||||
|
|
|
@ -3,7 +3,8 @@ 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
|
||||
from .const import NEW_SCENE
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
|
||||
DEPENDENCIES = ['deconz']
|
||||
|
||||
|
@ -16,17 +17,20 @@ async def async_setup_platform(
|
|||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up scenes for deCONZ component."""
|
||||
gateway = hass.data[DECONZ_DOMAIN]
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
|
||||
@callback
|
||||
def async_add_scene(scenes):
|
||||
"""Add scene from deCONZ."""
|
||||
entities = []
|
||||
|
||||
for scene in scenes:
|
||||
entities.append(DeconzScene(scene, gateway))
|
||||
|
||||
async_add_entities(entities)
|
||||
gateway.listeners.append(
|
||||
async_dispatcher_connect(hass, NEW_SCENE, async_add_scene))
|
||||
|
||||
gateway.listeners.append(async_dispatcher_connect(
|
||||
hass, gateway.async_event_new_device(NEW_SCENE), async_add_scene))
|
||||
|
||||
async_add_scene(gateway.api.scenes.values())
|
||||
|
||||
|
|
|
@ -5,10 +5,9 @@ from homeassistant.core import callback
|
|||
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,
|
||||
NEW_SENSOR)
|
||||
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
|
||||
DEPENDENCIES = ['deconz']
|
||||
|
||||
|
@ -25,7 +24,7 @@ async def async_setup_platform(
|
|||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the deCONZ sensors."""
|
||||
gateway = hass.data[DECONZ_DOMAIN]
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
|
||||
@callback
|
||||
def async_add_sensor(sensors):
|
||||
|
@ -33,19 +32,24 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
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:
|
||||
|
||||
if sensor.type in DECONZ_SENSOR and \
|
||||
not (not allow_clip_sensor and sensor.type.startswith('CLIP')):
|
||||
not (not gateway.allow_clip_sensor and
|
||||
sensor.type.startswith('CLIP')):
|
||||
|
||||
if sensor.type in DECONZ_REMOTE:
|
||||
if sensor.battery:
|
||||
entities.append(DeconzBattery(sensor, gateway))
|
||||
|
||||
else:
|
||||
entities.append(DeconzSensor(sensor, gateway))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
gateway.listeners.append(
|
||||
async_dispatcher_connect(hass, NEW_SENSOR, async_add_sensor))
|
||||
gateway.listeners.append(async_dispatcher_connect(
|
||||
hass, gateway.async_event_new_device(NEW_SENSOR), async_add_sensor))
|
||||
|
||||
async_add_sensor(gateway.api.sensors.values())
|
||||
|
||||
|
|
|
@ -13,6 +13,12 @@ configure:
|
|||
data:
|
||||
description: Data is a json object with what data you want to alter.
|
||||
example: '{"on": true}'
|
||||
bridgeid:
|
||||
description: (Optional) Bridgeid is a string unique for each deCONZ hardware. It can be found as part of the integration name.
|
||||
example: '00212EFFFF012345'
|
||||
|
||||
device_refresh:
|
||||
description: Refresh device lists from deCONZ.
|
||||
description: Refresh device lists from deCONZ.
|
||||
bridgeid:
|
||||
description: (Optional) Bridgeid is a string unique for each deCONZ hardware. It can be found as part of the integration name.
|
||||
example: '00212EFFFF012345'
|
|
@ -3,8 +3,9 @@ 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, NEW_LIGHT, POWER_PLUGS, SIRENS
|
||||
from .const import NEW_LIGHT, POWER_PLUGS, SIRENS
|
||||
from .deconz_device import DeconzDevice
|
||||
from .gateway import get_gateway_from_config_entry
|
||||
|
||||
DEPENDENCIES = ['deconz']
|
||||
|
||||
|
@ -20,21 +21,25 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
|
||||
Switches are based same device class as lights in deCONZ.
|
||||
"""
|
||||
gateway = hass.data[DECONZ_DOMAIN]
|
||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||
|
||||
@callback
|
||||
def async_add_switch(lights):
|
||||
"""Add switch from deCONZ."""
|
||||
entities = []
|
||||
|
||||
for light in lights:
|
||||
|
||||
if light.type in POWER_PLUGS:
|
||||
entities.append(DeconzPowerPlug(light, gateway))
|
||||
|
||||
elif light.type in SIRENS:
|
||||
entities.append(DeconzSiren(light, gateway))
|
||||
|
||||
async_add_entities(entities, True)
|
||||
|
||||
gateway.listeners.append(
|
||||
async_dispatcher_connect(hass, NEW_LIGHT, async_add_switch))
|
||||
gateway.listeners.append(async_dispatcher_connect(
|
||||
hass, gateway.async_event_new_device(NEW_LIGHT), async_add_switch))
|
||||
|
||||
async_add_switch(gateway.api.lights.values())
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||
gateway.api.config = Mock()
|
||||
hass.data[deconz.DOMAIN] = gateway
|
||||
hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway}
|
||||
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
|
@ -64,6 +64,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||
config_entry, 'binary_sensor')
|
||||
# To flush out the service call to update the group
|
||||
await hass.async_block_till_done()
|
||||
return gateway
|
||||
|
||||
|
||||
async def test_platform_manually_configured(hass):
|
||||
|
@ -79,56 +80,56 @@ async def test_platform_manually_configured(hass):
|
|||
async def test_no_binary_sensors(hass):
|
||||
"""Test that no sensors in deconz results in no sensor entities."""
|
||||
data = {}
|
||||
await setup_gateway(hass, data)
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
gateway = await setup_gateway(hass, data)
|
||||
assert len(hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids) == 0
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_binary_sensors(hass):
|
||||
"""Test successful creation of binary sensor entities."""
|
||||
data = {"sensors": SENSOR}
|
||||
await setup_gateway(hass, data)
|
||||
assert "binary_sensor.sensor_1_name" in \
|
||||
hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "binary_sensor.sensor_2_name" not in \
|
||||
hass.data[deconz.DOMAIN].deconz_ids
|
||||
gateway = await setup_gateway(hass, data)
|
||||
assert "binary_sensor.sensor_1_name" in gateway.deconz_ids
|
||||
assert "binary_sensor.sensor_2_name" not in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
||||
hass.data[deconz.DOMAIN].api.sensors['1'].async_update(
|
||||
hass.data[deconz.DOMAIN][gateway.bridgeid].api.sensors['1'].async_update(
|
||||
{'state': {'on': False}})
|
||||
|
||||
|
||||
async def test_add_new_sensor(hass):
|
||||
"""Test successful creation of sensor entities."""
|
||||
data = {}
|
||||
await setup_gateway(hass, data)
|
||||
gateway = await setup_gateway(hass, data)
|
||||
sensor = Mock()
|
||||
sensor.name = 'name'
|
||||
sensor.type = 'ZHAPresence'
|
||||
sensor.register_async_callback = Mock()
|
||||
async_dispatcher_send(hass, 'deconz_new_sensor', [sensor])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('sensor'), [sensor])
|
||||
await hass.async_block_till_done()
|
||||
assert "binary_sensor.name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "binary_sensor.name" in gateway.deconz_ids
|
||||
|
||||
|
||||
async def test_do_not_allow_clip_sensor(hass):
|
||||
"""Test that clip sensors can be ignored."""
|
||||
data = {}
|
||||
await setup_gateway(hass, data, allow_clip_sensor=False)
|
||||
gateway = await setup_gateway(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])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('sensor'), [sensor])
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
assert len(gateway.deconz_ids) == 0
|
||||
|
||||
|
||||
async def test_unload_switch(hass):
|
||||
"""Test that it works to unload switch entities."""
|
||||
data = {"sensors": SENSOR}
|
||||
await setup_gateway(hass, data)
|
||||
gateway = await setup_gateway(hass, data)
|
||||
|
||||
await hass.data[deconz.DOMAIN].async_reset()
|
||||
await gateway.async_reset()
|
||||
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
|
|
@ -65,7 +65,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||
gateway.api = DeconzSession(hass.loop, session, **config_entry.data)
|
||||
gateway.api.config = Mock()
|
||||
hass.data[deconz.DOMAIN] = gateway
|
||||
hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway}
|
||||
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
|
@ -75,6 +75,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||
config_entry, 'climate')
|
||||
# To flush out the service call to update the group
|
||||
await hass.async_block_till_done()
|
||||
return gateway
|
||||
|
||||
|
||||
async def test_platform_manually_configured(hass):
|
||||
|
@ -89,26 +90,26 @@ async def test_platform_manually_configured(hass):
|
|||
|
||||
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
|
||||
gateway = await setup_gateway(hass, {})
|
||||
assert not hass.data[deconz.DOMAIN][gateway.bridgeid].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
|
||||
gateway = await setup_gateway(hass, {"sensors": SENSOR})
|
||||
assert "climate.climate_1_name" in gateway.deconz_ids
|
||||
assert "sensor.sensor_2_name" not in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
||||
hass.data[deconz.DOMAIN].api.sensors['1'].async_update(
|
||||
gateway.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(
|
||||
gateway.api.session.put.assert_called_with(
|
||||
'http://1.2.3.4:80/api/ABCDEF/sensors/1/config',
|
||||
data='{"mode": "auto"}'
|
||||
)
|
||||
|
@ -117,7 +118,7 @@ async def test_climate_devices(hass):
|
|||
'climate', 'turn_off', {'entity_id': 'climate.climate_1_name'},
|
||||
blocking=True
|
||||
)
|
||||
hass.data[deconz.DOMAIN].api.session.put.assert_called_with(
|
||||
gateway.api.session.put.assert_called_with(
|
||||
'http://1.2.3.4:80/api/ABCDEF/sensors/1/config',
|
||||
data='{"mode": "off"}'
|
||||
)
|
||||
|
@ -127,18 +128,18 @@ async def test_climate_devices(hass):
|
|||
{'entity_id': 'climate.climate_1_name', 'temperature': 20},
|
||||
blocking=True
|
||||
)
|
||||
hass.data[deconz.DOMAIN].api.session.put.assert_called_with(
|
||||
gateway.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
|
||||
assert len(gateway.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
|
||||
gateway = await setup_gateway(hass, {"sensors": SENSOR})
|
||||
assert "climate.climate_1_name" in gateway.deconz_ids
|
||||
|
||||
thermostat = hass.states.get('climate.climate_1_name')
|
||||
assert thermostat.state == 'on'
|
||||
|
@ -150,7 +151,7 @@ async def test_verify_state_update(hass):
|
|||
"id": "1",
|
||||
"config": {"on": False}
|
||||
}
|
||||
hass.data[deconz.DOMAIN].api.async_event_handler(state_update)
|
||||
gateway.api.async_event_handler(state_update)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
@ -161,32 +162,34 @@ async def test_verify_state_update(hass):
|
|||
|
||||
async def test_add_new_climate_device(hass):
|
||||
"""Test successful creation of climate entities."""
|
||||
await setup_gateway(hass, {})
|
||||
gateway = 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])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('sensor'), [sensor])
|
||||
await hass.async_block_till_done()
|
||||
assert "climate.name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "climate.name" in gateway.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)
|
||||
gateway = 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])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('sensor'), [sensor])
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
assert len(gateway.deconz_ids) == 0
|
||||
|
||||
|
||||
async def test_unload_sensor(hass):
|
||||
"""Test that it works to unload sensor entities."""
|
||||
await setup_gateway(hass, {"sensors": SENSOR})
|
||||
gateway = await setup_gateway(hass, {"sensors": SENSOR})
|
||||
|
||||
await hass.data[deconz.DOMAIN].async_reset()
|
||||
await gateway.async_reset()
|
||||
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
|
|
@ -22,10 +22,7 @@ async def test_flow_works(hass, aioclient_mock):
|
|||
flow.hass = hass
|
||||
|
||||
await flow.async_step_user()
|
||||
await flow.async_step_link(user_input={})
|
||||
|
||||
result = await flow.async_step_options(
|
||||
user_input={'allow_clip_sensor': True, 'allow_deconz_groups': True})
|
||||
result = await flow.async_step_link(user_input={})
|
||||
|
||||
assert result['type'] == 'create_entry'
|
||||
assert result['title'] == 'deCONZ-id'
|
||||
|
@ -33,25 +30,10 @@ async def test_flow_works(hass, aioclient_mock):
|
|||
'bridgeid': 'id',
|
||||
'host': '1.2.3.4',
|
||||
'port': 80,
|
||||
'api_key': '1234567890ABCDEF',
|
||||
'allow_clip_sensor': True,
|
||||
'allow_deconz_groups': True
|
||||
'api_key': '1234567890ABCDEF'
|
||||
}
|
||||
|
||||
|
||||
async def test_flow_already_registered_bridge(hass):
|
||||
"""Test config flow don't allow more than one bridge to be registered."""
|
||||
MockConfigEntry(domain='deconz', data={
|
||||
'host': '1.2.3.4'
|
||||
}).add_to_hass(hass)
|
||||
|
||||
flow = config_flow.DeconzFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
result = await flow.async_step_user()
|
||||
assert result['type'] == 'abort'
|
||||
|
||||
|
||||
async def test_flow_bridge_discovery_fails(hass, aioclient_mock):
|
||||
"""Test config flow works when discovery fails."""
|
||||
flow = config_flow.DeconzFlowHandler()
|
||||
|
@ -153,24 +135,6 @@ async def test_link_no_api_key(hass):
|
|||
assert result['errors'] == {'base': 'no_key'}
|
||||
|
||||
|
||||
async def test_link_already_registered_bridge(hass):
|
||||
"""Test that link verifies to only allow one config entry to complete.
|
||||
|
||||
This is possible with discovery which will allow the user to complete
|
||||
a second config entry and then complete the discovered config entry.
|
||||
"""
|
||||
MockConfigEntry(domain='deconz', data={
|
||||
'host': '1.2.3.4'
|
||||
}).add_to_hass(hass)
|
||||
|
||||
flow = config_flow.DeconzFlowHandler()
|
||||
flow.hass = hass
|
||||
flow.deconz_config = {'host': '1.2.3.4', 'port': 80}
|
||||
|
||||
result = await flow.async_step_link(user_input={})
|
||||
assert result['type'] == 'abort'
|
||||
|
||||
|
||||
async def test_bridge_discovery(hass):
|
||||
"""Test a bridge being discovered."""
|
||||
flow = config_flow.DeconzFlowHandler()
|
||||
|
@ -197,6 +161,7 @@ async def test_bridge_discovery_already_configured(hass):
|
|||
|
||||
result = await flow.async_step_discovery({
|
||||
'host': '1.2.3.4',
|
||||
'port': 80,
|
||||
'serial': 'id'
|
||||
})
|
||||
|
||||
|
@ -234,14 +199,12 @@ async def test_import_with_api_key(hass):
|
|||
'bridgeid': 'id',
|
||||
'host': '1.2.3.4',
|
||||
'port': 80,
|
||||
'api_key': '1234567890ABCDEF',
|
||||
'allow_clip_sensor': True,
|
||||
'allow_deconz_groups': True
|
||||
'api_key': '1234567890ABCDEF'
|
||||
}
|
||||
|
||||
|
||||
async def test_options(hass, aioclient_mock):
|
||||
"""Test that options work and that bridgeid can be requested."""
|
||||
async def test_create_entry(hass, aioclient_mock):
|
||||
"""Test that _create_entry work and that bridgeid can be requested."""
|
||||
aioclient_mock.get('http://1.2.3.4:80/api/1234567890ABCDEF/config',
|
||||
json={"bridgeid": "id"},
|
||||
headers={'content-type': 'application/json'})
|
||||
|
@ -252,8 +215,7 @@ async def test_options(hass, aioclient_mock):
|
|||
'port': 80,
|
||||
'api_key': '1234567890ABCDEF'}
|
||||
|
||||
result = await flow.async_step_options(
|
||||
user_input={'allow_clip_sensor': False, 'allow_deconz_groups': False})
|
||||
result = await flow._create_entry()
|
||||
|
||||
assert result['type'] == 'create_entry'
|
||||
assert result['title'] == 'deCONZ-id'
|
||||
|
@ -261,9 +223,7 @@ async def test_options(hass, aioclient_mock):
|
|||
'bridgeid': 'id',
|
||||
'host': '1.2.3.4',
|
||||
'port': 80,
|
||||
'api_key': '1234567890ABCDEF',
|
||||
'allow_clip_sensor': False,
|
||||
'allow_deconz_groups': False
|
||||
'api_key': '1234567890ABCDEF'
|
||||
}
|
||||
|
||||
|
||||
|
@ -286,8 +246,8 @@ async def test_hassio_confirm(hass):
|
|||
data={
|
||||
'addon': 'Mock Addon',
|
||||
'host': 'mock-deconz',
|
||||
'port': 8080,
|
||||
'serial': 'aa:bb',
|
||||
'port': 80,
|
||||
'serial': 'id',
|
||||
'api_key': '1234567890ABCDEF',
|
||||
},
|
||||
context={'source': 'hassio'}
|
||||
|
@ -299,18 +259,13 @@ async def test_hassio_confirm(hass):
|
|||
}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result['flow_id'], {
|
||||
'allow_clip_sensor': True,
|
||||
'allow_deconz_groups': True,
|
||||
}
|
||||
result['flow_id'], user_input={}
|
||||
)
|
||||
|
||||
assert result['type'] == 'create_entry'
|
||||
assert result['result'].data == {
|
||||
'host': 'mock-deconz',
|
||||
'port': 8080,
|
||||
'bridgeid': 'aa:bb',
|
||||
'api_key': '1234567890ABCDEF',
|
||||
'allow_clip_sensor': True,
|
||||
'allow_deconz_groups': True,
|
||||
'port': 80,
|
||||
'bridgeid': 'id',
|
||||
'api_key': '1234567890ABCDEF'
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ async def setup_gateway(hass, data):
|
|||
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||
gateway.api.config = Mock()
|
||||
hass.data[deconz.DOMAIN] = gateway
|
||||
hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway}
|
||||
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
|
@ -70,6 +70,7 @@ async def setup_gateway(hass, data):
|
|||
await hass.config_entries.async_forward_entry_setup(config_entry, 'cover')
|
||||
# To flush out the service call to update the group
|
||||
await hass.async_block_till_done()
|
||||
return gateway
|
||||
|
||||
|
||||
async def test_platform_manually_configured(hass):
|
||||
|
@ -84,8 +85,8 @@ async def test_platform_manually_configured(hass):
|
|||
|
||||
async def test_no_covers(hass):
|
||||
"""Test that no cover entities are created."""
|
||||
await setup_gateway(hass, {})
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
gateway = await setup_gateway(hass, {})
|
||||
assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
|
@ -93,8 +94,8 @@ async def test_cover(hass):
|
|||
"""Test that all supported cover entities are created."""
|
||||
with patch('pydeconz.DeconzSession.async_put_state',
|
||||
return_value=mock_coro(True)):
|
||||
await setup_gateway(hass, {"lights": SUPPORTED_COVERS})
|
||||
assert "cover.cover_1_name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
gateway = await setup_gateway(hass, {"lights": SUPPORTED_COVERS})
|
||||
assert "cover.cover_1_name" in gateway.deconz_ids
|
||||
assert len(SUPPORTED_COVERS) == len(COVER_TYPES)
|
||||
assert len(hass.states.async_all()) == 3
|
||||
|
||||
|
@ -102,7 +103,7 @@ async def test_cover(hass):
|
|||
assert cover_1 is not None
|
||||
assert cover_1.state == 'closed'
|
||||
|
||||
hass.data[deconz.DOMAIN].api.lights['1'].async_update({})
|
||||
gateway.api.lights['1'].async_update({})
|
||||
|
||||
await hass.services.async_call('cover', 'open_cover', {
|
||||
'entity_id': 'cover.cover_1_name'
|
||||
|
@ -122,14 +123,15 @@ async def test_cover(hass):
|
|||
async def test_add_new_cover(hass):
|
||||
"""Test successful creation of cover entity."""
|
||||
data = {}
|
||||
await setup_gateway(hass, data)
|
||||
gateway = await setup_gateway(hass, data)
|
||||
cover = Mock()
|
||||
cover.name = 'name'
|
||||
cover.type = "Level controllable output"
|
||||
cover.register_async_callback = Mock()
|
||||
async_dispatcher_send(hass, 'deconz_new_light', [cover])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('light'), [cover])
|
||||
await hass.async_block_till_done()
|
||||
assert "cover.name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "cover.name" in gateway.deconz_ids
|
||||
|
||||
|
||||
async def test_unsupported_cover(hass):
|
||||
|
@ -140,8 +142,8 @@ async def test_unsupported_cover(hass):
|
|||
|
||||
async def test_unload_cover(hass):
|
||||
"""Test that it works to unload switch entities."""
|
||||
await setup_gateway(hass, {"lights": SUPPORTED_COVERS})
|
||||
gateway = await setup_gateway(hass, {"lights": SUPPORTED_COVERS})
|
||||
|
||||
await hass.data[deconz.DOMAIN].async_reset()
|
||||
await gateway.async_reset()
|
||||
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
|
|
@ -11,56 +11,62 @@ from homeassistant.components import deconz
|
|||
|
||||
from tests.common import mock_coro, MockConfigEntry
|
||||
|
||||
ENTRY1_HOST = '1.2.3.4'
|
||||
ENTRY1_PORT = 80
|
||||
ENTRY1_API_KEY = '1234567890ABCDEF'
|
||||
ENTRY1_BRIDGEID = '12345ABC'
|
||||
|
||||
CONFIG = {
|
||||
"config": {
|
||||
"bridgeid": "0123456789ABCDEF",
|
||||
"mac": "12:34:56:78:90:ab",
|
||||
"modelid": "deCONZ",
|
||||
"name": "Phoscon",
|
||||
"swversion": "2.05.35"
|
||||
}
|
||||
}
|
||||
ENTRY2_HOST = '2.3.4.5'
|
||||
ENTRY2_PORT = 80
|
||||
ENTRY2_API_KEY = '1234567890ABCDEF'
|
||||
ENTRY2_BRIDGEID = '23456DEF'
|
||||
|
||||
|
||||
async def setup_entry(hass, entry):
|
||||
"""Test that setup entry works."""
|
||||
with patch.object(deconz.DeconzGateway, 'async_setup',
|
||||
return_value=mock_coro(True)), \
|
||||
patch.object(deconz.DeconzGateway, 'async_update_device_registry',
|
||||
return_value=mock_coro(True)):
|
||||
assert await deconz.async_setup_entry(hass, entry) is True
|
||||
|
||||
|
||||
async def test_config_with_host_passed_to_config_entry(hass):
|
||||
"""Test that configured options for a host are loaded via config entry."""
|
||||
with patch.object(hass, 'config_entries') as mock_config_entries, \
|
||||
patch.object(deconz, 'configured_hosts', return_value=[]):
|
||||
with patch.object(hass.config_entries, 'flow') as mock_config_flow:
|
||||
assert await async_setup_component(hass, deconz.DOMAIN, {
|
||||
deconz.DOMAIN: {
|
||||
deconz.CONF_HOST: '1.2.3.4',
|
||||
deconz.CONF_PORT: 80
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT
|
||||
}
|
||||
}) is True
|
||||
# Import flow started
|
||||
assert len(mock_config_entries.flow.mock_calls) == 2
|
||||
assert len(mock_config_flow.mock_calls) == 2
|
||||
|
||||
|
||||
async def test_config_without_host_not_passed_to_config_entry(hass):
|
||||
"""Test that a configuration without a host does not initiate an import."""
|
||||
with patch.object(hass, 'config_entries') as mock_config_entries, \
|
||||
patch.object(deconz, 'configured_hosts', return_value=[]):
|
||||
MockConfigEntry(domain=deconz.DOMAIN, data={}).add_to_hass(hass)
|
||||
with patch.object(hass.config_entries, 'flow') as mock_config_flow:
|
||||
assert await async_setup_component(hass, deconz.DOMAIN, {
|
||||
deconz.DOMAIN: {}
|
||||
}) is True
|
||||
# No flow started
|
||||
assert len(mock_config_entries.flow.mock_calls) == 0
|
||||
assert len(mock_config_flow.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_config_already_registered_not_passed_to_config_entry(hass):
|
||||
async def test_config_import_entry_fails_when_entries_exist(hass):
|
||||
"""Test that an already registered host does not initiate an import."""
|
||||
with patch.object(hass, 'config_entries') as mock_config_entries, \
|
||||
patch.object(deconz, 'configured_hosts',
|
||||
return_value=['1.2.3.4']):
|
||||
MockConfigEntry(domain=deconz.DOMAIN, data={}).add_to_hass(hass)
|
||||
with patch.object(hass.config_entries, 'flow') as mock_config_flow:
|
||||
assert await async_setup_component(hass, deconz.DOMAIN, {
|
||||
deconz.DOMAIN: {
|
||||
deconz.CONF_HOST: '1.2.3.4',
|
||||
deconz.CONF_PORT: 80
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT
|
||||
}
|
||||
}) is True
|
||||
# No flow started
|
||||
assert len(mock_config_entries.flow.mock_calls) == 0
|
||||
assert len(mock_config_flow.mock_calls) == 0
|
||||
|
||||
|
||||
async def test_config_discovery(hass):
|
||||
|
@ -71,16 +77,14 @@ async def test_config_discovery(hass):
|
|||
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_fails(hass):
|
||||
"""Test setup entry fails if deCONZ is not available."""
|
||||
entry = Mock()
|
||||
entry.data = {'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'}
|
||||
entry.data = {
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY1_API_KEY
|
||||
}
|
||||
with patch('pydeconz.DeconzSession.async_load_parameters',
|
||||
side_effect=Exception):
|
||||
await deconz.async_setup_entry(hass, entry)
|
||||
|
@ -89,61 +93,121 @@ async def test_setup_entry_fails(hass):
|
|||
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',
|
||||
side_effect=asyncio.TimeoutError
|
||||
), pytest.raises(ConfigEntryNotReady):
|
||||
entry.data = {
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY1_API_KEY
|
||||
}
|
||||
with patch('pydeconz.DeconzSession.async_load_parameters',
|
||||
side_effect=asyncio.TimeoutError),\
|
||||
pytest.raises(ConfigEntryNotReady):
|
||||
await deconz.async_setup_entry(hass, entry)
|
||||
|
||||
|
||||
async def test_setup_entry_successful(hass):
|
||||
"""Test setup entry is successful."""
|
||||
entry = MockConfigEntry(domain=deconz.DOMAIN, data={
|
||||
'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY1_API_KEY,
|
||||
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID
|
||||
})
|
||||
entry.add_to_hass(hass)
|
||||
mock_registry = Mock()
|
||||
with patch.object(deconz, 'DeconzGateway') as mock_gateway, \
|
||||
patch('homeassistant.helpers.device_registry.async_get_registry',
|
||||
return_value=mock_coro(mock_registry)):
|
||||
mock_gateway.return_value.async_setup.return_value = mock_coro(True)
|
||||
assert await deconz.async_setup_entry(hass, entry) is True
|
||||
assert hass.data[deconz.DOMAIN]
|
||||
|
||||
await setup_entry(hass, entry)
|
||||
|
||||
assert ENTRY1_BRIDGEID in hass.data[deconz.DOMAIN]
|
||||
assert hass.data[deconz.DOMAIN][ENTRY1_BRIDGEID].master
|
||||
|
||||
|
||||
async def test_setup_entry_multiple_gateways(hass):
|
||||
"""Test setup entry is successful with multiple gateways."""
|
||||
entry = MockConfigEntry(domain=deconz.DOMAIN, data={
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY1_API_KEY,
|
||||
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID
|
||||
})
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
entry2 = MockConfigEntry(domain=deconz.DOMAIN, data={
|
||||
deconz.CONF_HOST: ENTRY2_HOST,
|
||||
deconz.CONF_PORT: ENTRY2_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY2_API_KEY,
|
||||
deconz.CONF_BRIDGEID: ENTRY2_BRIDGEID
|
||||
})
|
||||
entry2.add_to_hass(hass)
|
||||
|
||||
await setup_entry(hass, entry)
|
||||
await setup_entry(hass, entry2)
|
||||
|
||||
assert ENTRY1_BRIDGEID in hass.data[deconz.DOMAIN]
|
||||
assert hass.data[deconz.DOMAIN][ENTRY1_BRIDGEID].master
|
||||
assert ENTRY2_BRIDGEID in hass.data[deconz.DOMAIN]
|
||||
assert not hass.data[deconz.DOMAIN][ENTRY2_BRIDGEID].master
|
||||
|
||||
|
||||
async def test_unload_entry(hass):
|
||||
"""Test being able to unload an entry."""
|
||||
entry = MockConfigEntry(domain=deconz.DOMAIN, data={
|
||||
'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY1_API_KEY,
|
||||
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID
|
||||
})
|
||||
entry.add_to_hass(hass)
|
||||
mock_registry = Mock()
|
||||
with patch.object(deconz, 'DeconzGateway') as mock_gateway, \
|
||||
patch('homeassistant.helpers.device_registry.async_get_registry',
|
||||
return_value=mock_coro(mock_registry)):
|
||||
mock_gateway.return_value.async_setup.return_value = mock_coro(True)
|
||||
assert await deconz.async_setup_entry(hass, entry) is True
|
||||
|
||||
mock_gateway.return_value.async_reset.return_value = mock_coro(True)
|
||||
assert await deconz.async_unload_entry(hass, entry)
|
||||
assert deconz.DOMAIN not in hass.data
|
||||
await setup_entry(hass, entry)
|
||||
|
||||
with patch.object(deconz.DeconzGateway, 'async_reset',
|
||||
return_value=mock_coro(True)):
|
||||
assert await deconz.async_unload_entry(hass, entry)
|
||||
|
||||
assert not hass.data[deconz.DOMAIN]
|
||||
|
||||
|
||||
async def test_unload_entry_multiple_gateways(hass):
|
||||
"""Test being able to unload an entry and master gateway gets moved."""
|
||||
entry = MockConfigEntry(domain=deconz.DOMAIN, data={
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY1_API_KEY,
|
||||
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID
|
||||
})
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
entry2 = MockConfigEntry(domain=deconz.DOMAIN, data={
|
||||
deconz.CONF_HOST: ENTRY2_HOST,
|
||||
deconz.CONF_PORT: ENTRY2_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY2_API_KEY,
|
||||
deconz.CONF_BRIDGEID: ENTRY2_BRIDGEID
|
||||
})
|
||||
entry2.add_to_hass(hass)
|
||||
|
||||
await setup_entry(hass, entry)
|
||||
await setup_entry(hass, entry2)
|
||||
|
||||
with patch.object(deconz.DeconzGateway, 'async_reset',
|
||||
return_value=mock_coro(True)):
|
||||
assert await deconz.async_unload_entry(hass, entry)
|
||||
|
||||
assert ENTRY2_BRIDGEID in hass.data[deconz.DOMAIN]
|
||||
assert hass.data[deconz.DOMAIN][ENTRY2_BRIDGEID].master
|
||||
|
||||
|
||||
async def test_service_configure(hass):
|
||||
"""Test that service invokes pydeconz with the correct path and data."""
|
||||
entry = MockConfigEntry(domain=deconz.DOMAIN, data={
|
||||
'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY1_API_KEY,
|
||||
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID
|
||||
})
|
||||
entry.add_to_hass(hass)
|
||||
mock_registry = Mock()
|
||||
with patch.object(deconz, 'DeconzGateway') as mock_gateway, \
|
||||
patch('homeassistant.helpers.device_registry.async_get_registry',
|
||||
return_value=mock_coro(mock_registry)):
|
||||
mock_gateway.return_value.async_setup.return_value = mock_coro(True)
|
||||
assert await deconz.async_setup_entry(hass, entry) is True
|
||||
|
||||
hass.data[deconz.DOMAIN].deconz_ids = {
|
||||
await setup_entry(hass, entry)
|
||||
|
||||
hass.data[deconz.DOMAIN][ENTRY1_BRIDGEID].deconz_ids = {
|
||||
'light.test': '/light/1'
|
||||
}
|
||||
data = {'on': True, 'attr1': 10, 'attr2': 20}
|
||||
|
@ -191,25 +255,23 @@ async def test_service_configure(hass):
|
|||
async def test_service_refresh_devices(hass):
|
||||
"""Test that service can refresh devices."""
|
||||
entry = MockConfigEntry(domain=deconz.DOMAIN, data={
|
||||
'host': '1.2.3.4', 'port': 80, 'api_key': '1234567890ABCDEF'
|
||||
deconz.CONF_HOST: ENTRY1_HOST,
|
||||
deconz.CONF_PORT: ENTRY1_PORT,
|
||||
deconz.CONF_API_KEY: ENTRY1_API_KEY,
|
||||
deconz.CONF_BRIDGEID: ENTRY1_BRIDGEID
|
||||
})
|
||||
entry.add_to_hass(hass)
|
||||
mock_registry = Mock()
|
||||
|
||||
with patch.object(deconz, 'DeconzGateway') as mock_gateway, \
|
||||
patch('homeassistant.helpers.device_registry.async_get_registry',
|
||||
return_value=mock_coro(mock_registry)):
|
||||
mock_gateway.return_value.async_setup.return_value = mock_coro(True)
|
||||
assert await deconz.async_setup_entry(hass, entry) is True
|
||||
await setup_entry(hass, entry)
|
||||
|
||||
with patch.object(hass.data[deconz.DOMAIN].api, 'async_load_parameters',
|
||||
return_value=mock_coro(True)):
|
||||
with patch('pydeconz.DeconzSession.async_load_parameters',
|
||||
return_value=mock_coro(True)):
|
||||
await hass.services.async_call(
|
||||
'deconz', 'device_refresh', service_data={})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch.object(hass.data[deconz.DOMAIN].api, 'async_load_parameters',
|
||||
return_value=mock_coro(False)):
|
||||
with patch('pydeconz.DeconzSession.async_load_parameters',
|
||||
return_value=mock_coro(False)):
|
||||
await hass.services.async_call(
|
||||
'deconz', 'device_refresh', service_data={})
|
||||
await hass.async_block_till_done()
|
||||
|
|
|
@ -87,7 +87,7 @@ async def setup_gateway(hass, data, allow_deconz_groups=True):
|
|||
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||
gateway.api.config = Mock()
|
||||
hass.data[deconz.DOMAIN] = gateway
|
||||
hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway}
|
||||
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
|
@ -96,6 +96,7 @@ async def setup_gateway(hass, data, allow_deconz_groups=True):
|
|||
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()
|
||||
return gateway
|
||||
|
||||
|
||||
async def test_platform_manually_configured(hass):
|
||||
|
@ -110,8 +111,8 @@ async def test_platform_manually_configured(hass):
|
|||
|
||||
async def test_no_lights_or_groups(hass):
|
||||
"""Test that no lights or groups entities are created."""
|
||||
await setup_gateway(hass, {})
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
gateway = await setup_gateway(hass, {})
|
||||
assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
|
@ -119,11 +120,12 @@ async def test_lights_and_groups(hass):
|
|||
"""Test that lights or groups entities are created."""
|
||||
with patch('pydeconz.DeconzSession.async_put_state',
|
||||
return_value=mock_coro(True)):
|
||||
await setup_gateway(hass, {"lights": LIGHT, "groups": GROUP})
|
||||
assert "light.light_1_name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "light.light_2_name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "light.group_1_name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "light.group_2_name" not in hass.data[deconz.DOMAIN].deconz_ids
|
||||
gateway = await setup_gateway(
|
||||
hass, {"lights": LIGHT, "groups": GROUP})
|
||||
assert "light.light_1_name" in gateway.deconz_ids
|
||||
assert "light.light_2_name" in gateway.deconz_ids
|
||||
assert "light.group_1_name" in gateway.deconz_ids
|
||||
assert "light.group_2_name" not in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 4
|
||||
|
||||
lamp_1 = hass.states.get('light.light_1_name')
|
||||
|
@ -137,7 +139,7 @@ async def test_lights_and_groups(hass):
|
|||
assert light_2.state == 'on'
|
||||
assert light_2.attributes['color_temp'] == 2500
|
||||
|
||||
hass.data[deconz.DOMAIN].api.lights['1'].async_update({})
|
||||
gateway.api.lights['1'].async_update({})
|
||||
|
||||
await hass.services.async_call('light', 'turn_on', {
|
||||
'entity_id': 'light.light_1_name',
|
||||
|
@ -166,49 +168,52 @@ async def test_lights_and_groups(hass):
|
|||
|
||||
async def test_add_new_light(hass):
|
||||
"""Test successful creation of light entities."""
|
||||
await setup_gateway(hass, {})
|
||||
gateway = await setup_gateway(hass, {})
|
||||
light = Mock()
|
||||
light.name = 'name'
|
||||
light.register_async_callback = Mock()
|
||||
async_dispatcher_send(hass, 'deconz_new_light', [light])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('light'), [light])
|
||||
await hass.async_block_till_done()
|
||||
assert "light.name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "light.name" in gateway.deconz_ids
|
||||
|
||||
|
||||
async def test_add_new_group(hass):
|
||||
"""Test successful creation of group entities."""
|
||||
await setup_gateway(hass, {})
|
||||
gateway = await setup_gateway(hass, {})
|
||||
group = Mock()
|
||||
group.name = 'name'
|
||||
group.register_async_callback = Mock()
|
||||
async_dispatcher_send(hass, 'deconz_new_group', [group])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('group'), [group])
|
||||
await hass.async_block_till_done()
|
||||
assert "light.name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "light.name" in gateway.deconz_ids
|
||||
|
||||
|
||||
async def test_do_not_add_deconz_groups(hass):
|
||||
"""Test that clip sensors can be ignored."""
|
||||
await setup_gateway(hass, {}, allow_deconz_groups=False)
|
||||
gateway = await setup_gateway(hass, {}, allow_deconz_groups=False)
|
||||
group = Mock()
|
||||
group.name = 'name'
|
||||
group.register_async_callback = Mock()
|
||||
async_dispatcher_send(hass, 'deconz_new_group', [group])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('group'), [group])
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
assert len(gateway.deconz_ids) == 0
|
||||
|
||||
|
||||
async def test_no_switch(hass):
|
||||
"""Test that a switch doesn't get created as a light entity."""
|
||||
await setup_gateway(hass, {"lights": SWITCH})
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
gateway = await setup_gateway(hass, {"lights": SWITCH})
|
||||
assert len(gateway.deconz_ids) == 0
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_unload_light(hass):
|
||||
"""Test that it works to unload switch entities."""
|
||||
await setup_gateway(hass, {"lights": LIGHT, "groups": GROUP})
|
||||
gateway = await setup_gateway(hass, {"lights": LIGHT, "groups": GROUP})
|
||||
|
||||
await hass.data[deconz.DOMAIN].async_reset()
|
||||
await gateway.async_reset()
|
||||
|
||||
# Group.all_lights will not be removed
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
|
|
@ -47,7 +47,7 @@ async def setup_gateway(hass, data):
|
|||
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||
gateway.api.config = Mock()
|
||||
hass.data[deconz.DOMAIN] = gateway
|
||||
hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway}
|
||||
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
|
@ -56,6 +56,7 @@ async def setup_gateway(hass, data):
|
|||
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()
|
||||
return gateway
|
||||
|
||||
|
||||
async def test_platform_manually_configured(hass):
|
||||
|
@ -70,8 +71,8 @@ async def test_platform_manually_configured(hass):
|
|||
|
||||
async def test_no_scenes(hass):
|
||||
"""Test that scenes can be loaded without scenes being available."""
|
||||
await setup_gateway(hass, {})
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
gateway = await setup_gateway(hass, {})
|
||||
assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
|
@ -79,8 +80,8 @@ async def test_scenes(hass):
|
|||
"""Test that scenes works."""
|
||||
with patch('pydeconz.DeconzSession.async_put_state',
|
||||
return_value=mock_coro(True)):
|
||||
await setup_gateway(hass, {"groups": GROUP})
|
||||
assert "scene.group_1_name_scene_1" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
gateway = await setup_gateway(hass, {"groups": GROUP})
|
||||
assert "scene.group_1_name_scene_1" in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
||||
await hass.services.async_call('scene', 'turn_on', {
|
||||
|
@ -90,8 +91,8 @@ async def test_scenes(hass):
|
|||
|
||||
async def test_unload_scene(hass):
|
||||
"""Test that it works to unload scene entities."""
|
||||
await setup_gateway(hass, {"groups": GROUP})
|
||||
gateway = await setup_gateway(hass, {"groups": GROUP})
|
||||
|
||||
await hass.data[deconz.DOMAIN].async_reset()
|
||||
await gateway.async_reset()
|
||||
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
|
|
@ -91,7 +91,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||
gateway.api.config = Mock()
|
||||
hass.data[deconz.DOMAIN] = gateway
|
||||
hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway}
|
||||
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
|
@ -101,6 +101,7 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||
config_entry, 'sensor')
|
||||
# To flush out the service call to update the group
|
||||
await hass.async_block_till_done()
|
||||
return gateway
|
||||
|
||||
|
||||
async def test_platform_manually_configured(hass):
|
||||
|
@ -115,58 +116,56 @@ async def test_platform_manually_configured(hass):
|
|||
|
||||
async def test_no_sensors(hass):
|
||||
"""Test that no sensors in deconz results in no sensor entities."""
|
||||
await setup_gateway(hass, {})
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
gateway = await setup_gateway(hass, {})
|
||||
assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_sensors(hass):
|
||||
"""Test successful creation of sensor entities."""
|
||||
await setup_gateway(hass, {"sensors": SENSOR})
|
||||
assert "sensor.sensor_1_name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "sensor.sensor_2_name" not in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "sensor.sensor_3_name" not in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "sensor.sensor_3_name_battery_level" not in \
|
||||
hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "sensor.sensor_4_name" not in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "sensor.sensor_4_name_battery_level" in \
|
||||
hass.data[deconz.DOMAIN].deconz_ids
|
||||
gateway = await setup_gateway(hass, {"sensors": SENSOR})
|
||||
assert "sensor.sensor_1_name" in gateway.deconz_ids
|
||||
assert "sensor.sensor_2_name" not in gateway.deconz_ids
|
||||
assert "sensor.sensor_3_name" not in gateway.deconz_ids
|
||||
assert "sensor.sensor_3_name_battery_level" not in gateway.deconz_ids
|
||||
assert "sensor.sensor_4_name" not in gateway.deconz_ids
|
||||
assert "sensor.sensor_4_name_battery_level" in gateway.deconz_ids
|
||||
assert len(hass.states.async_all()) == 5
|
||||
|
||||
hass.data[deconz.DOMAIN].api.sensors['1'].async_update(
|
||||
{'state': {'on': False}})
|
||||
hass.data[deconz.DOMAIN].api.sensors['4'].async_update(
|
||||
{'config': {'battery': 75}})
|
||||
gateway.api.sensors['1'].async_update({'state': {'on': False}})
|
||||
gateway.api.sensors['4'].async_update({'config': {'battery': 75}})
|
||||
|
||||
|
||||
async def test_add_new_sensor(hass):
|
||||
"""Test successful creation of sensor entities."""
|
||||
await setup_gateway(hass, {})
|
||||
gateway = await setup_gateway(hass, {})
|
||||
sensor = Mock()
|
||||
sensor.name = 'name'
|
||||
sensor.type = 'ZHATemperature'
|
||||
sensor.register_async_callback = Mock()
|
||||
async_dispatcher_send(hass, 'deconz_new_sensor', [sensor])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('sensor'), [sensor])
|
||||
await hass.async_block_till_done()
|
||||
assert "sensor.name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "sensor.name" in gateway.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)
|
||||
gateway = await setup_gateway(hass, {}, 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])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('sensor'), [sensor])
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
assert len(gateway.deconz_ids) == 0
|
||||
|
||||
|
||||
async def test_unload_sensor(hass):
|
||||
"""Test that it works to unload sensor entities."""
|
||||
await setup_gateway(hass, {"sensors": SENSOR})
|
||||
gateway = await setup_gateway(hass, {"sensors": SENSOR})
|
||||
|
||||
await hass.data[deconz.DOMAIN].async_reset()
|
||||
await gateway.async_reset()
|
||||
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
|
|
@ -65,7 +65,7 @@ async def setup_gateway(hass, data):
|
|||
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||
gateway.api.config = Mock()
|
||||
hass.data[deconz.DOMAIN] = gateway
|
||||
hass.data[deconz.DOMAIN] = {gateway.bridgeid: gateway}
|
||||
|
||||
with patch('pydeconz.DeconzSession.async_get_state',
|
||||
return_value=mock_coro(data)):
|
||||
|
@ -74,6 +74,7 @@ async def setup_gateway(hass, data):
|
|||
await hass.config_entries.async_forward_entry_setup(config_entry, 'switch')
|
||||
# To flush out the service call to update the group
|
||||
await hass.async_block_till_done()
|
||||
return gateway
|
||||
|
||||
|
||||
async def test_platform_manually_configured(hass):
|
||||
|
@ -88,8 +89,8 @@ async def test_platform_manually_configured(hass):
|
|||
|
||||
async def test_no_switches(hass):
|
||||
"""Test that no switch entities are created."""
|
||||
await setup_gateway(hass, {})
|
||||
assert len(hass.data[deconz.DOMAIN].deconz_ids) == 0
|
||||
gateway = await setup_gateway(hass, {})
|
||||
assert not hass.data[deconz.DOMAIN][gateway.bridgeid].deconz_ids
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
|
@ -97,10 +98,10 @@ async def test_switches(hass):
|
|||
"""Test that all supported switch entities are created."""
|
||||
with patch('pydeconz.DeconzSession.async_put_state',
|
||||
return_value=mock_coro(True)):
|
||||
await setup_gateway(hass, {"lights": SUPPORTED_SWITCHES})
|
||||
assert "switch.switch_1_name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "switch.switch_2_name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "switch.switch_3_name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
gateway = await setup_gateway(hass, {"lights": SUPPORTED_SWITCHES})
|
||||
assert "switch.switch_1_name" in gateway.deconz_ids
|
||||
assert "switch.switch_2_name" in gateway.deconz_ids
|
||||
assert "switch.switch_3_name" in gateway.deconz_ids
|
||||
assert len(SUPPORTED_SWITCHES) == len(SWITCH_TYPES)
|
||||
assert len(hass.states.async_all()) == 4
|
||||
|
||||
|
@ -111,7 +112,7 @@ async def test_switches(hass):
|
|||
assert switch_3 is not None
|
||||
assert switch_3.state == 'on'
|
||||
|
||||
hass.data[deconz.DOMAIN].api.lights['1'].async_update({})
|
||||
gateway.api.lights['1'].async_update({})
|
||||
|
||||
await hass.services.async_call('switch', 'turn_on', {
|
||||
'entity_id': 'switch.switch_1_name'
|
||||
|
@ -130,14 +131,15 @@ async def test_switches(hass):
|
|||
|
||||
async def test_add_new_switch(hass):
|
||||
"""Test successful creation of switch entity."""
|
||||
await setup_gateway(hass, {})
|
||||
gateway = await setup_gateway(hass, {})
|
||||
switch = Mock()
|
||||
switch.name = 'name'
|
||||
switch.type = "Smart plug"
|
||||
switch.register_async_callback = Mock()
|
||||
async_dispatcher_send(hass, 'deconz_new_light', [switch])
|
||||
async_dispatcher_send(
|
||||
hass, gateway.async_event_new_device('light'), [switch])
|
||||
await hass.async_block_till_done()
|
||||
assert "switch.name" in hass.data[deconz.DOMAIN].deconz_ids
|
||||
assert "switch.name" in gateway.deconz_ids
|
||||
|
||||
|
||||
async def test_unsupported_switch(hass):
|
||||
|
@ -148,8 +150,8 @@ async def test_unsupported_switch(hass):
|
|||
|
||||
async def test_unload_switch(hass):
|
||||
"""Test that it works to unload switch entities."""
|
||||
await setup_gateway(hass, {"lights": SUPPORTED_SWITCHES})
|
||||
gateway = await setup_gateway(hass, {"lights": SUPPORTED_SWITCHES})
|
||||
|
||||
await hass.data[deconz.DOMAIN].async_reset()
|
||||
await gateway.async_reset()
|
||||
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
|
Loading…
Reference in New Issue