deCONZ - use entity registry disabled_by to control available entities (#26219)
* First draft * Support enabling disabled entities * Clean up * Move import * Local entity enabled replaced during rebase * Add option flow test * Mark options properties with option
This commit is contained in:
parent
b1c2a5fa08
commit
518d2c31bb
19 changed files with 191 additions and 82 deletions
|
@ -12,15 +12,7 @@ from homeassistant.helpers import config_validation as cv
|
||||||
|
|
||||||
# Loading the config flow file will register the flow
|
# Loading the config flow file will register the flow
|
||||||
from .config_flow import get_master_gateway
|
from .config_flow import get_master_gateway
|
||||||
from .const import (
|
from .const import CONF_BRIDGEID, CONF_MASTER_GATEWAY, DEFAULT_PORT, DOMAIN, _LOGGER
|
||||||
CONF_ALLOW_CLIP_SENSOR,
|
|
||||||
CONF_ALLOW_DECONZ_GROUPS,
|
|
||||||
CONF_BRIDGEID,
|
|
||||||
CONF_MASTER_GATEWAY,
|
|
||||||
DEFAULT_PORT,
|
|
||||||
DOMAIN,
|
|
||||||
_LOGGER,
|
|
||||||
)
|
|
||||||
from .gateway import DeconzGateway
|
from .gateway import DeconzGateway
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
|
@ -86,7 +78,7 @@ async def async_setup_entry(hass, config_entry):
|
||||||
hass.data[DOMAIN] = {}
|
hass.data[DOMAIN] = {}
|
||||||
|
|
||||||
if not config_entry.options:
|
if not config_entry.options:
|
||||||
await async_populate_options(hass, config_entry)
|
await async_update_master_gateway(hass, config_entry)
|
||||||
|
|
||||||
gateway = DeconzGateway(hass, config_entry)
|
gateway = DeconzGateway(hass, config_entry)
|
||||||
|
|
||||||
|
@ -203,25 +195,25 @@ async def async_unload_entry(hass, config_entry):
|
||||||
hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH)
|
hass.services.async_remove(DOMAIN, SERVICE_DEVICE_REFRESH)
|
||||||
|
|
||||||
elif gateway.master:
|
elif gateway.master:
|
||||||
await async_populate_options(hass, config_entry)
|
await async_update_master_gateway(hass, config_entry)
|
||||||
new_master_gateway = next(iter(hass.data[DOMAIN].values()))
|
new_master_gateway = next(iter(hass.data[DOMAIN].values()))
|
||||||
await async_populate_options(hass, new_master_gateway.config_entry)
|
await async_update_master_gateway(hass, new_master_gateway.config_entry)
|
||||||
|
|
||||||
return await gateway.async_reset()
|
return await gateway.async_reset()
|
||||||
|
|
||||||
|
|
||||||
async def async_populate_options(hass, config_entry):
|
async def async_update_master_gateway(hass, config_entry):
|
||||||
"""Populate default options for gateway.
|
"""Update master gateway boolean.
|
||||||
|
|
||||||
Called by setup_entry and unload_entry.
|
Called by setup_entry and unload_entry.
|
||||||
Makes sure there is always one master available.
|
Makes sure there is always one master available.
|
||||||
"""
|
"""
|
||||||
master = not get_master_gateway(hass)
|
master = not get_master_gateway(hass)
|
||||||
|
|
||||||
options = {
|
old_options = dict(config_entry.options)
|
||||||
CONF_MASTER_GATEWAY: master,
|
|
||||||
CONF_ALLOW_CLIP_SENSOR: config_entry.data.get(CONF_ALLOW_CLIP_SENSOR, False),
|
new_options = {CONF_MASTER_GATEWAY: master}
|
||||||
CONF_ALLOW_DECONZ_GROUPS: config_entry.data.get(CONF_ALLOW_DECONZ_GROUPS, True),
|
|
||||||
}
|
options = {**old_options, **new_options}
|
||||||
|
|
||||||
hass.config_entries.async_update_entry(config_entry, options=options)
|
hass.config_entries.async_update_entry(config_entry, options=options)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
|
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
|
||||||
from .deconz_device import DeconzDevice
|
from .deconz_device import DeconzDevice
|
||||||
from .gateway import get_gateway_from_config_entry
|
from .gateway import get_gateway_from_config_entry, DeconzEntityHandler
|
||||||
|
|
||||||
ATTR_ORIENTATION = "orientation"
|
ATTR_ORIENTATION = "orientation"
|
||||||
ATTR_TILTANGLE = "tiltangle"
|
ATTR_TILTANGLE = "tiltangle"
|
||||||
|
@ -24,6 +24,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the deCONZ binary sensor."""
|
"""Set up the deCONZ binary sensor."""
|
||||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||||
|
|
||||||
|
entity_handler = DeconzEntityHandler(gateway)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_sensor(sensors):
|
def async_add_sensor(sensors):
|
||||||
"""Add binary sensor from deCONZ."""
|
"""Add binary sensor from deCONZ."""
|
||||||
|
@ -31,17 +33,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
|
|
||||||
if sensor.BINARY and not (
|
if sensor.BINARY:
|
||||||
not gateway.allow_clip_sensor and sensor.type.startswith("CLIP")
|
new_sensor = DeconzBinarySensor(sensor, gateway)
|
||||||
):
|
entity_handler.add_entity(new_sensor)
|
||||||
|
entities.append(new_sensor)
|
||||||
entities.append(DeconzBinarySensor(sensor, gateway))
|
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
gateway.listeners.append(
|
gateway.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, gateway.async_event_new_device(NEW_SENSOR), async_add_sensor
|
hass, gateway.async_signal_new_device(NEW_SENSOR), async_add_sensor
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -38,17 +38,14 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
|
|
||||||
if sensor.type in Thermostat.ZHATYPE and not (
|
if sensor.type in Thermostat.ZHATYPE:
|
||||||
not gateway.allow_clip_sensor and sensor.type.startswith("CLIP")
|
|
||||||
):
|
|
||||||
|
|
||||||
entities.append(DeconzThermostat(sensor, gateway))
|
entities.append(DeconzThermostat(sensor, gateway))
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
gateway.listeners.append(
|
gateway.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, gateway.async_event_new_device(NEW_SENSOR), async_add_climate
|
hass, gateway.async_signal_new_device(NEW_SENSOR), async_add_climate
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
"""Config flow to configure deCONZ component."""
|
"""Config flow to configure deCONZ component."""
|
||||||
import asyncio
|
import asyncio
|
||||||
from copy import copy
|
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
@ -17,6 +16,8 @@ from .const import (
|
||||||
CONF_ALLOW_CLIP_SENSOR,
|
CONF_ALLOW_CLIP_SENSOR,
|
||||||
CONF_ALLOW_DECONZ_GROUPS,
|
CONF_ALLOW_DECONZ_GROUPS,
|
||||||
CONF_BRIDGEID,
|
CONF_BRIDGEID,
|
||||||
|
DEFAULT_ALLOW_CLIP_SENSOR,
|
||||||
|
DEFAULT_ALLOW_DECONZ_GROUPS,
|
||||||
DEFAULT_PORT,
|
DEFAULT_PORT,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
@ -256,7 +257,7 @@ class DeconzOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
def __init__(self, config_entry):
|
def __init__(self, config_entry):
|
||||||
"""Initialize deCONZ options flow."""
|
"""Initialize deCONZ options flow."""
|
||||||
self.config_entry = config_entry
|
self.config_entry = config_entry
|
||||||
self.options = copy(config_entry.options)
|
self.options = dict(config_entry.options)
|
||||||
|
|
||||||
async def async_step_init(self, user_input=None):
|
async def async_step_init(self, user_input=None):
|
||||||
"""Manage the deCONZ options."""
|
"""Manage the deCONZ options."""
|
||||||
|
@ -277,11 +278,15 @@ class DeconzOptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
{
|
{
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_ALLOW_CLIP_SENSOR,
|
CONF_ALLOW_CLIP_SENSOR,
|
||||||
default=self.config_entry.options[CONF_ALLOW_CLIP_SENSOR],
|
default=self.config_entry.options.get(
|
||||||
|
CONF_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_CLIP_SENSOR
|
||||||
|
),
|
||||||
): bool,
|
): bool,
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_ALLOW_DECONZ_GROUPS,
|
CONF_ALLOW_DECONZ_GROUPS,
|
||||||
default=self.config_entry.options[CONF_ALLOW_DECONZ_GROUPS],
|
default=self.config_entry.options.get(
|
||||||
|
CONF_ALLOW_DECONZ_GROUPS, DEFAULT_ALLOW_DECONZ_GROUPS
|
||||||
|
),
|
||||||
): bool,
|
): bool,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
|
@ -7,7 +7,7 @@ DOMAIN = "deconz"
|
||||||
|
|
||||||
DEFAULT_PORT = 80
|
DEFAULT_PORT = 80
|
||||||
DEFAULT_ALLOW_CLIP_SENSOR = False
|
DEFAULT_ALLOW_CLIP_SENSOR = False
|
||||||
DEFAULT_ALLOW_DECONZ_GROUPS = False
|
DEFAULT_ALLOW_DECONZ_GROUPS = True
|
||||||
|
|
||||||
CONF_ALLOW_CLIP_SENSOR = "allow_clip_sensor"
|
CONF_ALLOW_CLIP_SENSOR = "allow_clip_sensor"
|
||||||
CONF_ALLOW_DECONZ_GROUPS = "allow_deconz_groups"
|
CONF_ALLOW_DECONZ_GROUPS = "allow_deconz_groups"
|
||||||
|
|
|
@ -40,7 +40,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
gateway.listeners.append(
|
gateway.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, gateway.async_event_new_device(NEW_LIGHT), async_add_cover
|
hass, gateway.async_signal_new_device(NEW_LIGHT), async_add_cover
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -14,21 +14,40 @@ class DeconzDevice(Entity):
|
||||||
"""Set up device and add update callback to get data from websocket."""
|
"""Set up device and add update callback to get data from websocket."""
|
||||||
self._device = device
|
self._device = device
|
||||||
self.gateway = gateway
|
self.gateway = gateway
|
||||||
self.unsub_dispatcher = None
|
self.listeners = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def entity_registry_enabled_default(self):
|
||||||
|
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||||
|
if not self.gateway.option_allow_clip_sensor and self._device.type.startswith(
|
||||||
|
"CLIP"
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if (
|
||||||
|
not self.gateway.option_allow_deconz_groups
|
||||||
|
and self._device.type == "LightGroup"
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe to device events."""
|
"""Subscribe to device events."""
|
||||||
self._device.register_async_callback(self.async_update_callback)
|
self._device.register_async_callback(self.async_update_callback)
|
||||||
self.gateway.deconz_ids[self.entity_id] = self._device.deconz_id
|
self.gateway.deconz_ids[self.entity_id] = self._device.deconz_id
|
||||||
self.unsub_dispatcher = async_dispatcher_connect(
|
self.listeners.append(
|
||||||
self.hass, self.gateway.event_reachable, self.async_update_callback
|
async_dispatcher_connect(
|
||||||
|
self.hass, self.gateway.signal_reachable, self.async_update_callback
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
"""Disconnect device object when removed."""
|
"""Disconnect device object when removed."""
|
||||||
self._device.remove_callback(self.async_update_callback)
|
self._device.remove_callback(self.async_update_callback)
|
||||||
del self.gateway.deconz_ids[self.entity_id]
|
del self.gateway.deconz_ids[self.entity_id]
|
||||||
self.unsub_dispatcher()
|
for unsub_dispatcher in self.listeners:
|
||||||
|
unsub_dispatcher()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self, force_update=False):
|
def async_update_callback(self, force_update=False):
|
||||||
|
|
|
@ -14,6 +14,10 @@ from homeassistant.helpers.dispatcher import (
|
||||||
async_dispatcher_connect,
|
async_dispatcher_connect,
|
||||||
async_dispatcher_send,
|
async_dispatcher_send,
|
||||||
)
|
)
|
||||||
|
from homeassistant.helpers.entity_registry import (
|
||||||
|
async_get_registry,
|
||||||
|
DISABLED_CONFIG_ENTRY,
|
||||||
|
)
|
||||||
from homeassistant.util import slugify
|
from homeassistant.util import slugify
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
@ -22,6 +26,8 @@ from .const import (
|
||||||
CONF_ALLOW_DECONZ_GROUPS,
|
CONF_ALLOW_DECONZ_GROUPS,
|
||||||
CONF_BRIDGEID,
|
CONF_BRIDGEID,
|
||||||
CONF_MASTER_GATEWAY,
|
CONF_MASTER_GATEWAY,
|
||||||
|
DEFAULT_ALLOW_CLIP_SENSOR,
|
||||||
|
DEFAULT_ALLOW_DECONZ_GROUPS,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
NEW_DEVICE,
|
NEW_DEVICE,
|
||||||
NEW_SENSOR,
|
NEW_SENSOR,
|
||||||
|
@ -61,14 +67,18 @@ class DeconzGateway:
|
||||||
return self.config_entry.options[CONF_MASTER_GATEWAY]
|
return self.config_entry.options[CONF_MASTER_GATEWAY]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_clip_sensor(self) -> bool:
|
def option_allow_clip_sensor(self) -> bool:
|
||||||
"""Allow loading clip sensor from gateway."""
|
"""Allow loading clip sensor from gateway."""
|
||||||
return self.config_entry.options.get(CONF_ALLOW_CLIP_SENSOR, True)
|
return self.config_entry.options.get(
|
||||||
|
CONF_ALLOW_CLIP_SENSOR, DEFAULT_ALLOW_CLIP_SENSOR
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allow_deconz_groups(self) -> bool:
|
def option_allow_deconz_groups(self) -> bool:
|
||||||
"""Allow loading deCONZ groups from gateway."""
|
"""Allow loading deCONZ groups from gateway."""
|
||||||
return self.config_entry.options.get(CONF_ALLOW_DECONZ_GROUPS, True)
|
return self.config_entry.options.get(
|
||||||
|
CONF_ALLOW_DECONZ_GROUPS, DEFAULT_ALLOW_DECONZ_GROUPS
|
||||||
|
)
|
||||||
|
|
||||||
async def async_update_device_registry(self):
|
async def async_update_device_registry(self):
|
||||||
"""Update device registry."""
|
"""Update device registry."""
|
||||||
|
@ -111,7 +121,7 @@ class DeconzGateway:
|
||||||
|
|
||||||
self.listeners.append(
|
self.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, self.async_event_new_device(NEW_SENSOR), self.async_add_remote
|
hass, self.async_signal_new_device(NEW_SENSOR), self.async_add_remote
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -119,35 +129,50 @@ class DeconzGateway:
|
||||||
|
|
||||||
self.api.start()
|
self.api.start()
|
||||||
|
|
||||||
self.config_entry.add_update_listener(self.async_new_address_callback)
|
self.config_entry.add_update_listener(self.async_new_address)
|
||||||
|
self.config_entry.add_update_listener(self.async_options_updated)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def async_new_address_callback(hass, entry):
|
async def async_new_address(hass, entry):
|
||||||
"""Handle signals of gateway getting new address.
|
"""Handle signals of gateway getting new address.
|
||||||
|
|
||||||
This is a static method because a class method (bound method),
|
This is a static method because a class method (bound method),
|
||||||
can not be used with weak references.
|
can not be used with weak references.
|
||||||
"""
|
"""
|
||||||
gateway = hass.data[DOMAIN][entry.data[CONF_BRIDGEID]]
|
gateway = get_gateway_from_config_entry(hass, entry)
|
||||||
gateway.api.close()
|
if gateway.api.host != entry.data[CONF_HOST]:
|
||||||
gateway.api.host = entry.data[CONF_HOST]
|
gateway.api.close()
|
||||||
gateway.api.start()
|
gateway.api.host = entry.data[CONF_HOST]
|
||||||
|
gateway.api.start()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def event_reachable(self):
|
def signal_reachable(self):
|
||||||
"""Gateway specific event to signal a change in connection status."""
|
"""Gateway specific event to signal a change in connection status."""
|
||||||
return f"deconz_reachable_{self.bridgeid}"
|
return f"deconz-reachable-{self.bridgeid}"
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_connection_status_callback(self, available):
|
def async_connection_status_callback(self, available):
|
||||||
"""Handle signals of gateway connection status."""
|
"""Handle signals of gateway connection status."""
|
||||||
self.available = available
|
self.available = available
|
||||||
async_dispatcher_send(self.hass, self.event_reachable, True)
|
async_dispatcher_send(self.hass, self.signal_reachable, True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def signal_options_update(self):
|
||||||
|
"""Event specific per deCONZ entry to signal new options."""
|
||||||
|
return f"deconz-options-{self.bridgeid}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def async_options_updated(hass, entry):
|
||||||
|
"""Triggered by config entry options updates."""
|
||||||
|
gateway = get_gateway_from_config_entry(hass, entry)
|
||||||
|
|
||||||
|
registry = await async_get_registry(hass)
|
||||||
|
async_dispatcher_send(hass, gateway.signal_options_update, registry)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_event_new_device(self, device_type):
|
def async_signal_new_device(self, device_type):
|
||||||
"""Gateway specific event to signal new device."""
|
"""Gateway specific event to signal new device."""
|
||||||
return NEW_DEVICE[device_type].format(self.bridgeid)
|
return NEW_DEVICE[device_type].format(self.bridgeid)
|
||||||
|
|
||||||
|
@ -157,7 +182,7 @@ class DeconzGateway:
|
||||||
if not isinstance(device, list):
|
if not isinstance(device, list):
|
||||||
device = [device]
|
device = [device]
|
||||||
async_dispatcher_send(
|
async_dispatcher_send(
|
||||||
self.hass, self.async_event_new_device(device_type), device
|
self.hass, self.async_signal_new_device(device_type), device
|
||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -165,7 +190,7 @@ class DeconzGateway:
|
||||||
"""Set up remote from deCONZ."""
|
"""Set up remote from deCONZ."""
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
if sensor.type in Switch.ZHATYPE and not (
|
if sensor.type in Switch.ZHATYPE and not (
|
||||||
not self.allow_clip_sensor and sensor.type.startswith("CLIP")
|
not self.option_allow_clip_sensor and sensor.type.startswith("CLIP")
|
||||||
):
|
):
|
||||||
self.events.append(DeconzEvent(self.hass, sensor))
|
self.events.append(DeconzEvent(self.hass, sensor))
|
||||||
|
|
||||||
|
@ -183,6 +208,7 @@ class DeconzGateway:
|
||||||
Will cancel any scheduled setup retry and will unload
|
Will cancel any scheduled setup retry and will unload
|
||||||
the config entry.
|
the config entry.
|
||||||
"""
|
"""
|
||||||
|
self.api.async_connection_status_callback = None
|
||||||
self.api.close()
|
self.api.close()
|
||||||
|
|
||||||
for component in SUPPORTED_PLATFORMS:
|
for component in SUPPORTED_PLATFORMS:
|
||||||
|
@ -229,6 +255,41 @@ async def get_gateway(
|
||||||
raise CannotConnect
|
raise CannotConnect
|
||||||
|
|
||||||
|
|
||||||
|
class DeconzEntityHandler:
|
||||||
|
"""Platform entity handler to help with updating disabled by."""
|
||||||
|
|
||||||
|
def __init__(self, gateway):
|
||||||
|
"""Create an entity handler."""
|
||||||
|
self.gateway = gateway
|
||||||
|
self._entities = []
|
||||||
|
|
||||||
|
gateway.listeners.append(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
gateway.hass, gateway.signal_options_update, self.update_entity_registry
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def add_entity(self, entity):
|
||||||
|
"""Add a new entity to handler."""
|
||||||
|
self._entities.append(entity)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def update_entity_registry(self, entity_registry):
|
||||||
|
"""Update entity registry disabled by status."""
|
||||||
|
for entity in self._entities:
|
||||||
|
|
||||||
|
if entity.entity_registry_enabled_default != entity.enabled:
|
||||||
|
disabled_by = None
|
||||||
|
|
||||||
|
if entity.enabled:
|
||||||
|
disabled_by = DISABLED_CONFIG_ENTRY
|
||||||
|
|
||||||
|
entity_registry.async_update_entity(
|
||||||
|
entity.registry_entry.entity_id, disabled_by=disabled_by
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeconzEvent:
|
class DeconzEvent:
|
||||||
"""When you want signals instead of entities.
|
"""When you want signals instead of entities.
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ from .const import (
|
||||||
SWITCH_TYPES,
|
SWITCH_TYPES,
|
||||||
)
|
)
|
||||||
from .deconz_device import DeconzDevice
|
from .deconz_device import DeconzDevice
|
||||||
from .gateway import get_gateway_from_config_entry
|
from .gateway import get_gateway_from_config_entry, DeconzEntityHandler
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||||
|
@ -41,6 +41,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the deCONZ lights and groups from a config entry."""
|
"""Set up the deCONZ lights and groups from a config entry."""
|
||||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||||
|
|
||||||
|
entity_handler = DeconzEntityHandler(gateway)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_light(lights):
|
def async_add_light(lights):
|
||||||
"""Add light from deCONZ."""
|
"""Add light from deCONZ."""
|
||||||
|
@ -54,7 +56,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
gateway.listeners.append(
|
gateway.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, gateway.async_event_new_device(NEW_LIGHT), async_add_light
|
hass, gateway.async_signal_new_device(NEW_LIGHT), async_add_light
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,14 +66,16 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
for group in groups:
|
for group in groups:
|
||||||
if group.lights and gateway.allow_deconz_groups:
|
if group.lights:
|
||||||
entities.append(DeconzGroup(group, gateway))
|
new_group = DeconzGroup(group, gateway)
|
||||||
|
entity_handler.add_entity(new_group)
|
||||||
|
entities.append(new_group)
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
gateway.listeners.append(
|
gateway.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, gateway.async_event_new_device(NEW_GROUP), async_add_group
|
hass, gateway.async_signal_new_device(NEW_GROUP), async_add_group
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
gateway.listeners.append(
|
gateway.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, gateway.async_event_new_device(NEW_SCENE), async_add_scene
|
hass, gateway.async_signal_new_device(NEW_SCENE), async_add_scene
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ class DeconzScene(Scene):
|
||||||
|
|
||||||
async def async_will_remove_from_hass(self) -> None:
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
"""Disconnect scene object when removed."""
|
"""Disconnect scene object when removed."""
|
||||||
|
del self.gateway.deconz_ids[self.entity_id]
|
||||||
self._scene = None
|
self._scene = None
|
||||||
|
|
||||||
async def async_activate(self):
|
async def async_activate(self):
|
||||||
|
|
|
@ -13,7 +13,7 @@ from homeassistant.util import slugify
|
||||||
|
|
||||||
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
|
from .const import ATTR_DARK, ATTR_ON, NEW_SENSOR
|
||||||
from .deconz_device import DeconzDevice
|
from .deconz_device import DeconzDevice
|
||||||
from .gateway import get_gateway_from_config_entry
|
from .gateway import get_gateway_from_config_entry, DeconzEntityHandler
|
||||||
|
|
||||||
ATTR_CURRENT = "current"
|
ATTR_CURRENT = "current"
|
||||||
ATTR_POWER = "power"
|
ATTR_POWER = "power"
|
||||||
|
@ -30,6 +30,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up the deCONZ sensors."""
|
"""Set up the deCONZ sensors."""
|
||||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||||
|
|
||||||
|
entity_handler = DeconzEntityHandler(gateway)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_sensor(sensors):
|
def async_add_sensor(sensors):
|
||||||
"""Add sensors from deCONZ."""
|
"""Add sensors from deCONZ."""
|
||||||
|
@ -37,22 +39,22 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
|
|
||||||
if not sensor.BINARY and not (
|
if not sensor.BINARY:
|
||||||
not gateway.allow_clip_sensor and sensor.type.startswith("CLIP")
|
|
||||||
):
|
|
||||||
|
|
||||||
if sensor.type in Switch.ZHATYPE:
|
if sensor.type in Switch.ZHATYPE:
|
||||||
if sensor.battery:
|
if sensor.battery:
|
||||||
entities.append(DeconzBattery(sensor, gateway))
|
entities.append(DeconzBattery(sensor, gateway))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
entities.append(DeconzSensor(sensor, gateway))
|
new_sensor = DeconzSensor(sensor, gateway)
|
||||||
|
entity_handler.add_entity(new_sensor)
|
||||||
|
entities.append(new_sensor)
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
gateway.listeners.append(
|
gateway.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, gateway.async_event_new_device(NEW_SENSOR), async_add_sensor
|
hass, gateway.async_signal_new_device(NEW_SENSOR), async_add_sensor
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
|
||||||
gateway.listeners.append(
|
gateway.listeners.append(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
hass, gateway.async_event_new_device(NEW_LIGHT), async_add_switch
|
hass, gateway.async_signal_new_device(NEW_LIGHT), async_add_switch
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ async def test_add_new_sensor(hass):
|
||||||
sensor.BINARY = True
|
sensor.BINARY = True
|
||||||
sensor.uniqueid = "1"
|
sensor.uniqueid = "1"
|
||||||
sensor.register_async_callback = Mock()
|
sensor.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("sensor"), [sensor])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("sensor"), [sensor])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "binary_sensor.name" in gateway.deconz_ids
|
assert "binary_sensor.name" in gateway.deconz_ids
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ async def test_do_not_allow_clip_sensor(hass):
|
||||||
sensor.name = "name"
|
sensor.name = "name"
|
||||||
sensor.type = "CLIPPresence"
|
sensor.type = "CLIPPresence"
|
||||||
sensor.register_async_callback = Mock()
|
sensor.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("sensor"), [sensor])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("sensor"), [sensor])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(gateway.deconz_ids) == 0
|
assert len(gateway.deconz_ids) == 0
|
||||||
|
|
||||||
|
|
|
@ -191,7 +191,7 @@ async def test_add_new_climate_device(hass):
|
||||||
sensor.type = "ZHAThermostat"
|
sensor.type = "ZHAThermostat"
|
||||||
sensor.uniqueid = "1"
|
sensor.uniqueid = "1"
|
||||||
sensor.register_async_callback = Mock()
|
sensor.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("sensor"), [sensor])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("sensor"), [sensor])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "climate.name" in gateway.deconz_ids
|
assert "climate.name" in gateway.deconz_ids
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ async def test_do_not_allow_clipsensor(hass):
|
||||||
sensor.name = "name"
|
sensor.name = "name"
|
||||||
sensor.type = "CLIPThermostat"
|
sensor.type = "CLIPThermostat"
|
||||||
sensor.register_async_callback = Mock()
|
sensor.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("sensor"), [sensor])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("sensor"), [sensor])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(gateway.deconz_ids) == 0
|
assert len(gateway.deconz_ids) == 0
|
||||||
|
|
||||||
|
|
|
@ -382,3 +382,29 @@ async def test_hassio_confirm(hass):
|
||||||
config_flow.CONF_BRIDGEID: "id",
|
config_flow.CONF_BRIDGEID: "id",
|
||||||
config_flow.CONF_API_KEY: "1234567890ABCDEF",
|
config_flow.CONF_API_KEY: "1234567890ABCDEF",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_option_flow(hass):
|
||||||
|
"""Test config flow selection of one of two bridges."""
|
||||||
|
entry = MockConfigEntry(domain=config_flow.DOMAIN, data={}, options=None)
|
||||||
|
hass.config_entries._entries.append(entry)
|
||||||
|
|
||||||
|
flow = await hass.config_entries.options._async_create_flow(
|
||||||
|
entry.entry_id, context={"source": "test"}, data=None
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await flow.async_step_init()
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "deconz_devices"
|
||||||
|
|
||||||
|
result = await flow.async_step_deconz_devices(
|
||||||
|
user_input={
|
||||||
|
config_flow.CONF_ALLOW_CLIP_SENSOR: False,
|
||||||
|
config_flow.CONF_ALLOW_DECONZ_GROUPS: False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["data"] == {
|
||||||
|
config_flow.CONF_ALLOW_CLIP_SENSOR: False,
|
||||||
|
config_flow.CONF_ALLOW_DECONZ_GROUPS: False,
|
||||||
|
}
|
||||||
|
|
|
@ -135,7 +135,7 @@ async def test_add_new_cover(hass):
|
||||||
cover.type = "Level controllable output"
|
cover.type = "Level controllable output"
|
||||||
cover.uniqueid = "1"
|
cover.uniqueid = "1"
|
||||||
cover.register_async_callback = Mock()
|
cover.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("light"), [cover])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("light"), [cover])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "cover.name" in gateway.deconz_ids
|
assert "cover.name" in gateway.deconz_ids
|
||||||
|
|
||||||
|
|
|
@ -193,7 +193,7 @@ async def test_add_new_light(hass):
|
||||||
light.name = "name"
|
light.name = "name"
|
||||||
light.uniqueid = "1"
|
light.uniqueid = "1"
|
||||||
light.register_async_callback = Mock()
|
light.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("light"), [light])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("light"), [light])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "light.name" in gateway.deconz_ids
|
assert "light.name" in gateway.deconz_ids
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ async def test_add_new_group(hass):
|
||||||
group = Mock()
|
group = Mock()
|
||||||
group.name = "name"
|
group.name = "name"
|
||||||
group.register_async_callback = Mock()
|
group.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("group"), [group])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("group"), [group])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "light.name" in gateway.deconz_ids
|
assert "light.name" in gateway.deconz_ids
|
||||||
|
|
||||||
|
@ -214,8 +214,9 @@ async def test_do_not_add_deconz_groups(hass):
|
||||||
gateway = await setup_gateway(hass, {}, allow_deconz_groups=False)
|
gateway = await setup_gateway(hass, {}, allow_deconz_groups=False)
|
||||||
group = Mock()
|
group = Mock()
|
||||||
group.name = "name"
|
group.name = "name"
|
||||||
|
group.type = "LightGroup"
|
||||||
group.register_async_callback = Mock()
|
group.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("group"), [group])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("group"), [group])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(gateway.deconz_ids) == 0
|
assert len(gateway.deconz_ids) == 0
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ async def test_add_new_sensor(hass):
|
||||||
sensor.uniqueid = "1"
|
sensor.uniqueid = "1"
|
||||||
sensor.BINARY = False
|
sensor.BINARY = False
|
||||||
sensor.register_async_callback = Mock()
|
sensor.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("sensor"), [sensor])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("sensor"), [sensor])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "sensor.name" in gateway.deconz_ids
|
assert "sensor.name" in gateway.deconz_ids
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ async def test_do_not_allow_clipsensor(hass):
|
||||||
sensor.name = "name"
|
sensor.name = "name"
|
||||||
sensor.type = "CLIPTemperature"
|
sensor.type = "CLIPTemperature"
|
||||||
sensor.register_async_callback = Mock()
|
sensor.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("sensor"), [sensor])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("sensor"), [sensor])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(gateway.deconz_ids) == 0
|
assert len(gateway.deconz_ids) == 0
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ async def test_add_new_switch(hass):
|
||||||
switch.type = "Smart plug"
|
switch.type = "Smart plug"
|
||||||
switch.uniqueid = "1"
|
switch.uniqueid = "1"
|
||||||
switch.register_async_callback = Mock()
|
switch.register_async_callback = Mock()
|
||||||
async_dispatcher_send(hass, gateway.async_event_new_device("light"), [switch])
|
async_dispatcher_send(hass, gateway.async_signal_new_device("light"), [switch])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert "switch.name" in gateway.deconz_ids
|
assert "switch.name" in gateway.deconz_ids
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue