Add support for custom configurations in ZHA (#48423)
* initial configuration options * first crack at saving the data * constants * implement initial options * make more dynamic * fix unload and reload of the config entry * update unload
This commit is contained in:
parent
9c11f6547a
commit
fe80afdb86
7 changed files with 142 additions and 12 deletions
|
@ -29,6 +29,7 @@ from .core.const import (
|
||||||
DATA_ZHA_DISPATCHERS,
|
DATA_ZHA_DISPATCHERS,
|
||||||
DATA_ZHA_GATEWAY,
|
DATA_ZHA_GATEWAY,
|
||||||
DATA_ZHA_PLATFORM_LOADED,
|
DATA_ZHA_PLATFORM_LOADED,
|
||||||
|
DATA_ZHA_SHUTDOWN_TASK,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
PLATFORMS,
|
PLATFORMS,
|
||||||
SIGNAL_ADD_ENTITIES,
|
SIGNAL_ADD_ENTITIES,
|
||||||
|
@ -121,7 +122,9 @@ async def async_setup_entry(hass, config_entry):
|
||||||
await zha_data[DATA_ZHA_GATEWAY].shutdown()
|
await zha_data[DATA_ZHA_GATEWAY].shutdown()
|
||||||
await zha_data[DATA_ZHA_GATEWAY].async_update_device_storage()
|
await zha_data[DATA_ZHA_GATEWAY].async_update_device_storage()
|
||||||
|
|
||||||
hass.bus.async_listen_once(ha_const.EVENT_HOMEASSISTANT_STOP, async_zha_shutdown)
|
zha_data[DATA_ZHA_SHUTDOWN_TASK] = hass.bus.async_listen_once(
|
||||||
|
ha_const.EVENT_HOMEASSISTANT_STOP, async_zha_shutdown
|
||||||
|
)
|
||||||
asyncio.create_task(async_load_entities(hass))
|
asyncio.create_task(async_load_entities(hass))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -129,6 +132,7 @@ async def async_setup_entry(hass, config_entry):
|
||||||
async def async_unload_entry(hass, config_entry):
|
async def async_unload_entry(hass, config_entry):
|
||||||
"""Unload ZHA config entry."""
|
"""Unload ZHA config entry."""
|
||||||
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].shutdown()
|
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].shutdown()
|
||||||
|
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].async_update_device_storage()
|
||||||
|
|
||||||
GROUP_PROBE.cleanup()
|
GROUP_PROBE.cleanup()
|
||||||
api.async_unload_api(hass)
|
api.async_unload_api(hass)
|
||||||
|
@ -137,8 +141,15 @@ async def async_unload_entry(hass, config_entry):
|
||||||
for unsub_dispatcher in dispatchers:
|
for unsub_dispatcher in dispatchers:
|
||||||
unsub_dispatcher()
|
unsub_dispatcher()
|
||||||
|
|
||||||
for platform in PLATFORMS:
|
# our components don't have unload methods so no need to look at return values
|
||||||
await hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
await asyncio.gather(
|
||||||
|
*[
|
||||||
|
hass.config_entries.async_forward_entry_unload(config_entry, platform)
|
||||||
|
for platform in PLATFORMS
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.data[DATA_ZHA][DATA_ZHA_SHUTDOWN_TASK]()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
from zigpy.config.validators import cv_boolean
|
||||||
from zigpy.types.named import EUI64
|
from zigpy.types.named import EUI64
|
||||||
import zigpy.zdo.types as zdo_types
|
import zigpy.zdo.types as zdo_types
|
||||||
|
|
||||||
|
@ -40,6 +41,7 @@ from .core.const import (
|
||||||
CLUSTER_COMMANDS_SERVER,
|
CLUSTER_COMMANDS_SERVER,
|
||||||
CLUSTER_TYPE_IN,
|
CLUSTER_TYPE_IN,
|
||||||
CLUSTER_TYPE_OUT,
|
CLUSTER_TYPE_OUT,
|
||||||
|
CUSTOM_CONFIGURATION,
|
||||||
DATA_ZHA,
|
DATA_ZHA,
|
||||||
DATA_ZHA_GATEWAY,
|
DATA_ZHA_GATEWAY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
@ -52,6 +54,7 @@ from .core.const import (
|
||||||
WARNING_DEVICE_SQUAWK_MODE_ARMED,
|
WARNING_DEVICE_SQUAWK_MODE_ARMED,
|
||||||
WARNING_DEVICE_STROBE_HIGH,
|
WARNING_DEVICE_STROBE_HIGH,
|
||||||
WARNING_DEVICE_STROBE_YES,
|
WARNING_DEVICE_STROBE_YES,
|
||||||
|
ZHA_CONFIG_SCHEMAS,
|
||||||
)
|
)
|
||||||
from .core.group import GroupMember
|
from .core.group import GroupMember
|
||||||
from .core.helpers import (
|
from .core.helpers import (
|
||||||
|
@ -882,6 +885,63 @@ async def async_binding_operation(zha_gateway, source_ieee, target_ieee, operati
|
||||||
zdo.debug(fmt, *(log_msg[2] + (outcome,)))
|
zdo.debug(fmt, *(log_msg[2] + (outcome,)))
|
||||||
|
|
||||||
|
|
||||||
|
@websocket_api.require_admin
|
||||||
|
@websocket_api.async_response
|
||||||
|
@websocket_api.websocket_command({vol.Required(TYPE): "zha/configuration"})
|
||||||
|
async def websocket_get_configuration(hass, connection, msg):
|
||||||
|
"""Get ZHA configuration."""
|
||||||
|
zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
|
||||||
|
import voluptuous_serialize # pylint: disable=import-outside-toplevel
|
||||||
|
|
||||||
|
def custom_serializer(schema: Any) -> Any:
|
||||||
|
"""Serialize additional types for voluptuous_serialize."""
|
||||||
|
if schema is cv_boolean:
|
||||||
|
return {"type": "bool"}
|
||||||
|
if schema is vol.Schema:
|
||||||
|
return voluptuous_serialize.convert(
|
||||||
|
schema, custom_serializer=custom_serializer
|
||||||
|
)
|
||||||
|
|
||||||
|
return cv.custom_serializer(schema)
|
||||||
|
|
||||||
|
data = {"schemas": {}, "data": {}}
|
||||||
|
for section, schema in ZHA_CONFIG_SCHEMAS.items():
|
||||||
|
data["schemas"][section] = voluptuous_serialize.convert(
|
||||||
|
schema, custom_serializer=custom_serializer
|
||||||
|
)
|
||||||
|
data["data"][section] = zha_gateway.config_entry.options.get(
|
||||||
|
CUSTOM_CONFIGURATION, {}
|
||||||
|
).get(section, {})
|
||||||
|
connection.send_result(msg[ID], data)
|
||||||
|
|
||||||
|
|
||||||
|
@websocket_api.require_admin
|
||||||
|
@websocket_api.async_response
|
||||||
|
@websocket_api.websocket_command(
|
||||||
|
{
|
||||||
|
vol.Required(TYPE): "zha/configuration/update",
|
||||||
|
vol.Required("data"): ZHA_CONFIG_SCHEMAS,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
async def websocket_update_zha_configuration(hass, connection, msg):
|
||||||
|
"""Update the ZHA configuration."""
|
||||||
|
zha_gateway = hass.data[DATA_ZHA][DATA_ZHA_GATEWAY]
|
||||||
|
options = zha_gateway.config_entry.options
|
||||||
|
data_to_save = {**options, **{CUSTOM_CONFIGURATION: msg["data"]}}
|
||||||
|
|
||||||
|
_LOGGER.info(
|
||||||
|
"Updating ZHA custom configuration options from %s to %s",
|
||||||
|
options,
|
||||||
|
data_to_save,
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.config_entries.async_update_entry(
|
||||||
|
zha_gateway.config_entry, options=data_to_save
|
||||||
|
)
|
||||||
|
status = await hass.config_entries.async_reload(zha_gateway.config_entry.entry_id)
|
||||||
|
connection.send_result(msg[ID], status)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_load_api(hass):
|
def async_load_api(hass):
|
||||||
"""Set up the web socket API."""
|
"""Set up the web socket API."""
|
||||||
|
@ -1189,6 +1249,8 @@ def async_load_api(hass):
|
||||||
websocket_api.async_register_command(hass, websocket_bind_devices)
|
websocket_api.async_register_command(hass, websocket_bind_devices)
|
||||||
websocket_api.async_register_command(hass, websocket_unbind_devices)
|
websocket_api.async_register_command(hass, websocket_unbind_devices)
|
||||||
websocket_api.async_register_command(hass, websocket_update_topology)
|
websocket_api.async_register_command(hass, websocket_update_topology)
|
||||||
|
websocket_api.async_register_command(hass, websocket_get_configuration)
|
||||||
|
websocket_api.async_register_command(hass, websocket_update_zha_configuration)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
|
|
@ -5,6 +5,7 @@ import enum
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import bellows.zigbee.application
|
import bellows.zigbee.application
|
||||||
|
import voluptuous as vol
|
||||||
from zigpy.config import CONF_DEVICE_PATH # noqa: F401 # pylint: disable=unused-import
|
from zigpy.config import CONF_DEVICE_PATH # noqa: F401 # pylint: disable=unused-import
|
||||||
import zigpy_cc.zigbee.application
|
import zigpy_cc.zigbee.application
|
||||||
import zigpy_deconz.zigbee.application
|
import zigpy_deconz.zigbee.application
|
||||||
|
@ -22,6 +23,7 @@ from homeassistant.components.lock import DOMAIN as LOCK
|
||||||
from homeassistant.components.number import DOMAIN as NUMBER
|
from homeassistant.components.number import DOMAIN as NUMBER
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR
|
from homeassistant.components.sensor import DOMAIN as SENSOR
|
||||||
from homeassistant.components.switch import DOMAIN as SWITCH
|
from homeassistant.components.switch import DOMAIN as SWITCH
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
from .typing import CALLABLE_T
|
from .typing import CALLABLE_T
|
||||||
|
|
||||||
|
@ -118,13 +120,24 @@ PLATFORMS = (
|
||||||
|
|
||||||
CONF_BAUDRATE = "baudrate"
|
CONF_BAUDRATE = "baudrate"
|
||||||
CONF_DATABASE = "database_path"
|
CONF_DATABASE = "database_path"
|
||||||
|
CONF_DEFAULT_LIGHT_TRANSITION = "default_light_transition"
|
||||||
CONF_DEVICE_CONFIG = "device_config"
|
CONF_DEVICE_CONFIG = "device_config"
|
||||||
|
CONF_ENABLE_IDENTIFY_ON_JOIN = "enable_identify_on_join"
|
||||||
CONF_ENABLE_QUIRKS = "enable_quirks"
|
CONF_ENABLE_QUIRKS = "enable_quirks"
|
||||||
CONF_FLOWCONTROL = "flow_control"
|
CONF_FLOWCONTROL = "flow_control"
|
||||||
CONF_RADIO_TYPE = "radio_type"
|
CONF_RADIO_TYPE = "radio_type"
|
||||||
CONF_USB_PATH = "usb_path"
|
CONF_USB_PATH = "usb_path"
|
||||||
CONF_ZIGPY = "zigpy_config"
|
CONF_ZIGPY = "zigpy_config"
|
||||||
|
|
||||||
|
CONF_ZHA_OPTIONS_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_DEFAULT_LIGHT_TRANSITION): cv.positive_int,
|
||||||
|
vol.Required(CONF_ENABLE_IDENTIFY_ON_JOIN, default=True): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
CUSTOM_CONFIGURATION = "custom_configuration"
|
||||||
|
|
||||||
DATA_DEVICE_CONFIG = "zha_device_config"
|
DATA_DEVICE_CONFIG = "zha_device_config"
|
||||||
DATA_ZHA = "zha"
|
DATA_ZHA = "zha"
|
||||||
DATA_ZHA_CONFIG = "config"
|
DATA_ZHA_CONFIG = "config"
|
||||||
|
@ -133,6 +146,7 @@ DATA_ZHA_CORE_EVENTS = "zha_core_events"
|
||||||
DATA_ZHA_DISPATCHERS = "zha_dispatchers"
|
DATA_ZHA_DISPATCHERS = "zha_dispatchers"
|
||||||
DATA_ZHA_GATEWAY = "zha_gateway"
|
DATA_ZHA_GATEWAY = "zha_gateway"
|
||||||
DATA_ZHA_PLATFORM_LOADED = "platform_loaded"
|
DATA_ZHA_PLATFORM_LOADED = "platform_loaded"
|
||||||
|
DATA_ZHA_SHUTDOWN_TASK = "zha_shutdown_task"
|
||||||
|
|
||||||
DEBUG_COMP_BELLOWS = "bellows"
|
DEBUG_COMP_BELLOWS = "bellows"
|
||||||
DEBUG_COMP_ZHA = "homeassistant.components.zha"
|
DEBUG_COMP_ZHA = "homeassistant.components.zha"
|
||||||
|
@ -176,6 +190,9 @@ POWER_BATTERY_OR_UNKNOWN = "Battery or Unknown"
|
||||||
PRESET_SCHEDULE = "schedule"
|
PRESET_SCHEDULE = "schedule"
|
||||||
PRESET_COMPLEX = "complex"
|
PRESET_COMPLEX = "complex"
|
||||||
|
|
||||||
|
ZHA_OPTIONS = "zha_options"
|
||||||
|
ZHA_CONFIG_SCHEMAS = {ZHA_OPTIONS: CONF_ZHA_OPTIONS_SCHEMA}
|
||||||
|
|
||||||
|
|
||||||
class RadioType(enum.Enum):
|
class RadioType(enum.Enum):
|
||||||
"""Possible options for radio type."""
|
"""Possible options for radio type."""
|
||||||
|
|
|
@ -56,6 +56,7 @@ from .const import (
|
||||||
CLUSTER_COMMANDS_SERVER,
|
CLUSTER_COMMANDS_SERVER,
|
||||||
CLUSTER_TYPE_IN,
|
CLUSTER_TYPE_IN,
|
||||||
CLUSTER_TYPE_OUT,
|
CLUSTER_TYPE_OUT,
|
||||||
|
CONF_ENABLE_IDENTIFY_ON_JOIN,
|
||||||
EFFECT_DEFAULT_VARIANT,
|
EFFECT_DEFAULT_VARIANT,
|
||||||
EFFECT_OKAY,
|
EFFECT_OKAY,
|
||||||
POWER_BATTERY_OR_UNKNOWN,
|
POWER_BATTERY_OR_UNKNOWN,
|
||||||
|
@ -66,7 +67,7 @@ from .const import (
|
||||||
UNKNOWN_MANUFACTURER,
|
UNKNOWN_MANUFACTURER,
|
||||||
UNKNOWN_MODEL,
|
UNKNOWN_MODEL,
|
||||||
)
|
)
|
||||||
from .helpers import LogMixin
|
from .helpers import LogMixin, async_get_zha_config_value
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
CONSIDER_UNAVAILABLE_MAINS = 60 * 60 * 2 # 2 hours
|
CONSIDER_UNAVAILABLE_MAINS = 60 * 60 * 2 # 2 hours
|
||||||
|
@ -395,13 +396,20 @@ class ZHADevice(LogMixin):
|
||||||
|
|
||||||
async def async_configure(self):
|
async def async_configure(self):
|
||||||
"""Configure the device."""
|
"""Configure the device."""
|
||||||
|
should_identify = async_get_zha_config_value(
|
||||||
|
self._zha_gateway.config_entry, CONF_ENABLE_IDENTIFY_ON_JOIN, True
|
||||||
|
)
|
||||||
self.debug("started configuration")
|
self.debug("started configuration")
|
||||||
await self._channels.async_configure()
|
await self._channels.async_configure()
|
||||||
self.debug("completed configuration")
|
self.debug("completed configuration")
|
||||||
entry = self.gateway.zha_storage.async_create_or_update_device(self)
|
entry = self.gateway.zha_storage.async_create_or_update_device(self)
|
||||||
self.debug("stored in registry: %s", entry)
|
self.debug("stored in registry: %s", entry)
|
||||||
|
|
||||||
if self._channels.identify_ch is not None and not self.skip_configuration:
|
if (
|
||||||
|
should_identify
|
||||||
|
and self._channels.identify_ch is not None
|
||||||
|
and not self.skip_configuration
|
||||||
|
):
|
||||||
await self._channels.identify_ch.trigger_effect(
|
await self._channels.identify_ch.trigger_effect(
|
||||||
EFFECT_OKAY, EFFECT_DEFAULT_VARIANT
|
EFFECT_OKAY, EFFECT_DEFAULT_VARIANT
|
||||||
)
|
)
|
||||||
|
|
|
@ -127,7 +127,7 @@ class ZHAGateway:
|
||||||
}
|
}
|
||||||
self.debug_enabled = False
|
self.debug_enabled = False
|
||||||
self._log_relay_handler = LogRelayHandler(hass, self)
|
self._log_relay_handler = LogRelayHandler(hass, self)
|
||||||
self._config_entry = config_entry
|
self.config_entry = config_entry
|
||||||
self._unsubs = []
|
self._unsubs = []
|
||||||
|
|
||||||
async def async_initialize(self):
|
async def async_initialize(self):
|
||||||
|
@ -139,7 +139,7 @@ class ZHAGateway:
|
||||||
self.ha_device_registry = await get_dev_reg(self._hass)
|
self.ha_device_registry = await get_dev_reg(self._hass)
|
||||||
self.ha_entity_registry = await get_ent_reg(self._hass)
|
self.ha_entity_registry = await get_ent_reg(self._hass)
|
||||||
|
|
||||||
radio_type = self._config_entry.data[CONF_RADIO_TYPE]
|
radio_type = self.config_entry.data[CONF_RADIO_TYPE]
|
||||||
|
|
||||||
app_controller_cls = RadioType[radio_type].controller
|
app_controller_cls = RadioType[radio_type].controller
|
||||||
self.radio_description = RadioType[radio_type].description
|
self.radio_description = RadioType[radio_type].description
|
||||||
|
@ -150,7 +150,7 @@ class ZHAGateway:
|
||||||
os.path.join(self._hass.config.config_dir, DEFAULT_DATABASE_NAME),
|
os.path.join(self._hass.config.config_dir, DEFAULT_DATABASE_NAME),
|
||||||
)
|
)
|
||||||
app_config[CONF_DATABASE] = database
|
app_config[CONF_DATABASE] = database
|
||||||
app_config[CONF_DEVICE] = self._config_entry.data[CONF_DEVICE]
|
app_config[CONF_DEVICE] = self.config_entry.data[CONF_DEVICE]
|
||||||
|
|
||||||
app_config = app_controller_cls.SCHEMA(app_config)
|
app_config = app_controller_cls.SCHEMA(app_config)
|
||||||
try:
|
try:
|
||||||
|
@ -506,7 +506,7 @@ class ZHAGateway:
|
||||||
zha_device = ZHADevice.new(self._hass, zigpy_device, self, restored)
|
zha_device = ZHADevice.new(self._hass, zigpy_device, self, restored)
|
||||||
self._devices[zigpy_device.ieee] = zha_device
|
self._devices[zigpy_device.ieee] = zha_device
|
||||||
device_registry_device = self.ha_device_registry.async_get_or_create(
|
device_registry_device = self.ha_device_registry.async_get_or_create(
|
||||||
config_entry_id=self._config_entry.entry_id,
|
config_entry_id=self.config_entry.entry_id,
|
||||||
connections={(CONNECTION_ZIGBEE, str(zha_device.ieee))},
|
connections={(CONNECTION_ZIGBEE, str(zha_device.ieee))},
|
||||||
identifiers={(DOMAIN, str(zha_device.ieee))},
|
identifiers={(DOMAIN, str(zha_device.ieee))},
|
||||||
name=zha_device.name,
|
name=zha_device.name,
|
||||||
|
|
|
@ -24,7 +24,14 @@ import zigpy.zdo.types as zdo_types
|
||||||
|
|
||||||
from homeassistant.core import State, callback
|
from homeassistant.core import State, callback
|
||||||
|
|
||||||
from .const import CLUSTER_TYPE_IN, CLUSTER_TYPE_OUT, DATA_ZHA, DATA_ZHA_GATEWAY
|
from .const import (
|
||||||
|
CLUSTER_TYPE_IN,
|
||||||
|
CLUSTER_TYPE_OUT,
|
||||||
|
CUSTOM_CONFIGURATION,
|
||||||
|
DATA_ZHA,
|
||||||
|
DATA_ZHA_GATEWAY,
|
||||||
|
ZHA_OPTIONS,
|
||||||
|
)
|
||||||
from .registries import BINDABLE_CLUSTERS
|
from .registries import BINDABLE_CLUSTERS
|
||||||
from .typing import ZhaDeviceType, ZigpyClusterType
|
from .typing import ZhaDeviceType, ZigpyClusterType
|
||||||
|
|
||||||
|
@ -122,6 +129,16 @@ def async_is_bindable_target(source_zha_device, target_zha_device):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_get_zha_config_value(config_entry, config_key, default):
|
||||||
|
"""Get the value for the specified configuration from the zha config entry."""
|
||||||
|
return (
|
||||||
|
config_entry.options.get(CUSTOM_CONFIGURATION, {})
|
||||||
|
.get(ZHA_OPTIONS, {})
|
||||||
|
.get(config_key, default)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_get_zha_device(hass, device_id):
|
async def async_get_zha_device(hass, device_id):
|
||||||
"""Get a ZHA device for the given device registry id."""
|
"""Get a ZHA device for the given device registry id."""
|
||||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||||
|
|
|
@ -47,6 +47,7 @@ from .core.const import (
|
||||||
CHANNEL_COLOR,
|
CHANNEL_COLOR,
|
||||||
CHANNEL_LEVEL,
|
CHANNEL_LEVEL,
|
||||||
CHANNEL_ON_OFF,
|
CHANNEL_ON_OFF,
|
||||||
|
CONF_DEFAULT_LIGHT_TRANSITION,
|
||||||
DATA_ZHA,
|
DATA_ZHA,
|
||||||
DATA_ZHA_DISPATCHERS,
|
DATA_ZHA_DISPATCHERS,
|
||||||
EFFECT_BLINK,
|
EFFECT_BLINK,
|
||||||
|
@ -56,7 +57,7 @@ from .core.const import (
|
||||||
SIGNAL_ATTR_UPDATED,
|
SIGNAL_ATTR_UPDATED,
|
||||||
SIGNAL_SET_LEVEL,
|
SIGNAL_SET_LEVEL,
|
||||||
)
|
)
|
||||||
from .core.helpers import LogMixin
|
from .core.helpers import LogMixin, async_get_zha_config_value
|
||||||
from .core.registries import ZHA_ENTITIES
|
from .core.registries import ZHA_ENTITIES
|
||||||
from .core.typing import ZhaDeviceType
|
from .core.typing import ZhaDeviceType
|
||||||
from .entity import ZhaEntity, ZhaGroupEntity
|
from .entity import ZhaEntity, ZhaGroupEntity
|
||||||
|
@ -139,6 +140,7 @@ class BaseLight(LogMixin, light.LightEntity):
|
||||||
self._level_channel = None
|
self._level_channel = None
|
||||||
self._color_channel = None
|
self._color_channel = None
|
||||||
self._identify_channel = None
|
self._identify_channel = None
|
||||||
|
self._default_transition = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> dict[str, Any]:
|
def extra_state_attributes(self) -> dict[str, Any]:
|
||||||
|
@ -207,7 +209,13 @@ class BaseLight(LogMixin, light.LightEntity):
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
"""Turn the entity on."""
|
"""Turn the entity on."""
|
||||||
transition = kwargs.get(light.ATTR_TRANSITION)
|
transition = kwargs.get(light.ATTR_TRANSITION)
|
||||||
duration = transition * 10 if transition else DEFAULT_TRANSITION
|
duration = (
|
||||||
|
transition * 10
|
||||||
|
if transition
|
||||||
|
else self._default_transition * 10
|
||||||
|
if self._default_transition
|
||||||
|
else DEFAULT_TRANSITION
|
||||||
|
)
|
||||||
brightness = kwargs.get(light.ATTR_BRIGHTNESS)
|
brightness = kwargs.get(light.ATTR_BRIGHTNESS)
|
||||||
effect = kwargs.get(light.ATTR_EFFECT)
|
effect = kwargs.get(light.ATTR_EFFECT)
|
||||||
flash = kwargs.get(light.ATTR_FLASH)
|
flash = kwargs.get(light.ATTR_FLASH)
|
||||||
|
@ -389,6 +397,10 @@ class Light(BaseLight, ZhaEntity):
|
||||||
if effect_list:
|
if effect_list:
|
||||||
self._effect_list = effect_list
|
self._effect_list = effect_list
|
||||||
|
|
||||||
|
self._default_transition = async_get_zha_config_value(
|
||||||
|
zha_device.gateway.config_entry, CONF_DEFAULT_LIGHT_TRANSITION, 0
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_set_state(self, attr_id, attr_name, value):
|
def async_set_state(self, attr_id, attr_name, value):
|
||||||
"""Set the state."""
|
"""Set the state."""
|
||||||
|
@ -544,6 +556,9 @@ class LightGroup(BaseLight, ZhaGroupEntity):
|
||||||
self._color_channel = group.endpoint[Color.cluster_id]
|
self._color_channel = group.endpoint[Color.cluster_id]
|
||||||
self._identify_channel = group.endpoint[Identify.cluster_id]
|
self._identify_channel = group.endpoint[Identify.cluster_id]
|
||||||
self._debounced_member_refresh = None
|
self._debounced_member_refresh = None
|
||||||
|
self._default_transition = async_get_zha_config_value(
|
||||||
|
zha_device.gateway.config_entry, CONF_DEFAULT_LIGHT_TRANSITION, 0
|
||||||
|
)
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Run when about to be added to hass."""
|
"""Run when about to be added to hass."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue