ZHA - Event foundation (#19095)
* event foundation * add missing periods to comments * reworked so that entities don't fire events * lint * review commentspull/19099/head
parent
59581786d3
commit
f4f42176bd
|
@ -190,6 +190,10 @@ class Remote(ZhaEntity, BinarySensorDevice):
|
|||
"""Handle ZDO commands on this cluster."""
|
||||
pass
|
||||
|
||||
def zha_send_event(self, cluster, command, args):
|
||||
"""Relay entity events to hass."""
|
||||
pass # don't let entities fire events
|
||||
|
||||
class LevelListener:
|
||||
"""Listener for the LevelControl Zigbee cluster."""
|
||||
|
||||
|
@ -220,6 +224,10 @@ class Remote(ZhaEntity, BinarySensorDevice):
|
|||
"""Handle ZDO commands on this cluster."""
|
||||
pass
|
||||
|
||||
def zha_send_event(self, cluster, command, args):
|
||||
"""Relay entity events to hass."""
|
||||
pass # don't let entities fire events
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize Switch."""
|
||||
super().__init__(**kwargs)
|
||||
|
|
|
@ -7,6 +7,7 @@ https://home-assistant.io/components/zha/
|
|||
import collections
|
||||
import logging
|
||||
import os
|
||||
import types
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
|
@ -20,12 +21,14 @@ from homeassistant.helpers.entity_component import EntityComponent
|
|||
# Loading the config flow file will register the flow
|
||||
from . import config_flow # noqa # pylint: disable=unused-import
|
||||
from . import const as zha_const
|
||||
from .event import ZhaEvent
|
||||
from .const import (
|
||||
COMPONENTS, CONF_BAUDRATE, CONF_DATABASE, CONF_DEVICE_CONFIG,
|
||||
CONF_RADIO_TYPE, CONF_USB_PATH, DATA_ZHA, DATA_ZHA_BRIDGE_ID,
|
||||
DATA_ZHA_CONFIG, DATA_ZHA_CORE_COMPONENT, DATA_ZHA_DISPATCHERS,
|
||||
DATA_ZHA_RADIO, DEFAULT_BAUDRATE, DEFAULT_DATABASE_NAME,
|
||||
DEFAULT_RADIO_TYPE, DOMAIN, ZHA_DISCOVERY_NEW, RadioType)
|
||||
DEFAULT_RADIO_TYPE, DOMAIN, ZHA_DISCOVERY_NEW, RadioType,
|
||||
EVENTABLE_CLUSTERS, DATA_ZHA_CORE_EVENTS)
|
||||
|
||||
REQUIREMENTS = [
|
||||
'bellows==0.7.0',
|
||||
|
@ -130,6 +133,19 @@ async def async_setup_entry(hass, config_entry):
|
|||
database = config[CONF_DATABASE]
|
||||
else:
|
||||
database = os.path.join(hass.config.config_dir, DEFAULT_DATABASE_NAME)
|
||||
|
||||
# patch zigpy listener to prevent flooding logs with warnings due to
|
||||
# how zigpy implemented its listeners
|
||||
from zigpy.appdb import ClusterPersistingListener
|
||||
|
||||
def zha_send_event(self, cluster, command, args):
|
||||
pass
|
||||
|
||||
ClusterPersistingListener.zha_send_event = types.MethodType(
|
||||
zha_send_event,
|
||||
ClusterPersistingListener
|
||||
)
|
||||
|
||||
APPLICATION_CONTROLLER = ControllerApplication(radio, database)
|
||||
listener = ApplicationListener(hass, config)
|
||||
APPLICATION_CONTROLLER.add_listener(listener)
|
||||
|
@ -205,6 +221,9 @@ async def async_unload_entry(hass, config_entry):
|
|||
for entity_id in entity_ids:
|
||||
await component.async_remove_entity(entity_id)
|
||||
|
||||
# clean up events
|
||||
hass.data[DATA_ZHA][DATA_ZHA_CORE_EVENTS].clear()
|
||||
|
||||
_LOGGER.debug("Closing zha radio")
|
||||
hass.data[DATA_ZHA][DATA_ZHA_RADIO].close()
|
||||
|
||||
|
@ -221,6 +240,7 @@ class ApplicationListener:
|
|||
self._config = config
|
||||
self._component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
self._device_registry = collections.defaultdict(list)
|
||||
self._events = {}
|
||||
zha_const.populate_data()
|
||||
|
||||
for component in COMPONENTS:
|
||||
|
@ -228,6 +248,7 @@ class ApplicationListener:
|
|||
hass.data[DATA_ZHA].get(component, {})
|
||||
)
|
||||
hass.data[DATA_ZHA][DATA_ZHA_CORE_COMPONENT] = self._component
|
||||
hass.data[DATA_ZHA][DATA_ZHA_CORE_EVENTS] = self._events
|
||||
|
||||
def device_joined(self, device):
|
||||
"""Handle device joined.
|
||||
|
@ -256,6 +277,8 @@ class ApplicationListener:
|
|||
"""Handle device being removed from the network."""
|
||||
for device_entity in self._device_registry[device.ieee]:
|
||||
self._hass.async_create_task(device_entity.async_remove())
|
||||
if device.ieee in self._events:
|
||||
self._events.pop(device.ieee)
|
||||
|
||||
async def async_device_initialized(self, device, join):
|
||||
"""Handle device joined and basic information discovered (async)."""
|
||||
|
@ -362,6 +385,14 @@ class ApplicationListener:
|
|||
device_classes, discovery_attr,
|
||||
is_new_join):
|
||||
"""Try to set up an entity from a "bare" cluster."""
|
||||
if cluster.cluster_id in EVENTABLE_CLUSTERS:
|
||||
if cluster.endpoint.device.ieee not in self._events:
|
||||
self._events.update({cluster.endpoint.device.ieee: []})
|
||||
self._events[cluster.endpoint.device.ieee].append(ZhaEvent(
|
||||
self._hass,
|
||||
cluster
|
||||
))
|
||||
|
||||
if cluster.cluster_id in profile_clusters:
|
||||
return
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ DATA_ZHA_BRIDGE_ID = 'zha_bridge_id'
|
|||
DATA_ZHA_RADIO = 'zha_radio'
|
||||
DATA_ZHA_DISPATCHERS = 'zha_dispatchers'
|
||||
DATA_ZHA_CORE_COMPONENT = 'zha_core_component'
|
||||
DATA_ZHA_CORE_EVENTS = 'zha_core_events'
|
||||
ZHA_DISCOVERY_NEW = 'zha_discovery_new_{}'
|
||||
|
||||
COMPONENTS = [
|
||||
|
@ -53,6 +54,7 @@ SINGLE_INPUT_CLUSTER_DEVICE_CLASS = {}
|
|||
SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS = {}
|
||||
CUSTOM_CLUSTER_MAPPINGS = {}
|
||||
COMPONENT_CLUSTERS = {}
|
||||
EVENTABLE_CLUSTERS = []
|
||||
|
||||
|
||||
def populate_data():
|
||||
|
@ -70,6 +72,11 @@ def populate_data():
|
|||
if zll.PROFILE_ID not in DEVICE_CLASS:
|
||||
DEVICE_CLASS[zll.PROFILE_ID] = {}
|
||||
|
||||
EVENTABLE_CLUSTERS.append(zcl.clusters.general.AnalogInput.cluster_id)
|
||||
EVENTABLE_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id)
|
||||
EVENTABLE_CLUSTERS.append(zcl.clusters.general.MultistateInput.cluster_id)
|
||||
EVENTABLE_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id)
|
||||
|
||||
DEVICE_CLASS[zha.PROFILE_ID].update({
|
||||
zha.DeviceType.ON_OFF_SWITCH: 'binary_sensor',
|
||||
zha.DeviceType.LEVEL_CONTROL_SWITCH: 'binary_sensor',
|
||||
|
|
|
@ -103,3 +103,8 @@ class ZhaEntity(entity.Entity):
|
|||
'name': self._device_state_attributes['friendly_name'],
|
||||
'via_hub': (DOMAIN, self.hass.data[DATA_ZHA][DATA_ZHA_BRIDGE_ID]),
|
||||
}
|
||||
|
||||
@callback
|
||||
def zha_send_event(self, cluster, command, args):
|
||||
"""Relay entity events to hass."""
|
||||
pass # don't relay events from entities
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
"""
|
||||
Event for Zigbee Home Automation.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/zha/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.core import EventOrigin, callback
|
||||
from homeassistant.util import slugify
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ZhaEvent():
|
||||
"""A base class for ZHA events."""
|
||||
|
||||
def __init__(self, hass, cluster, **kwargs):
|
||||
"""Init ZHA event."""
|
||||
self._hass = hass
|
||||
self._cluster = cluster
|
||||
cluster.add_listener(self)
|
||||
ieee = cluster.endpoint.device.ieee
|
||||
ieeetail = ''.join(['%02x' % (o, ) for o in ieee[-4:]])
|
||||
endpoint = cluster.endpoint
|
||||
if endpoint.manufacturer and endpoint.model is not None:
|
||||
self._unique_id = "{}.{}_{}_{}_{}{}".format(
|
||||
'zha_event',
|
||||
slugify(endpoint.manufacturer),
|
||||
slugify(endpoint.model),
|
||||
ieeetail,
|
||||
cluster.endpoint.endpoint_id,
|
||||
kwargs.get('entity_suffix', ''),
|
||||
)
|
||||
else:
|
||||
self._unique_id = "{}.zha_{}_{}{}".format(
|
||||
'zha_event',
|
||||
ieeetail,
|
||||
cluster.endpoint.endpoint_id,
|
||||
kwargs.get('entity_suffix', ''),
|
||||
)
|
||||
|
||||
@callback
|
||||
def attribute_updated(self, attribute, value):
|
||||
"""Handle an attribute updated on this cluster."""
|
||||
pass
|
||||
|
||||
@callback
|
||||
def zdo_command(self, tsn, command_id, args):
|
||||
"""Handle a ZDO command received on this cluster."""
|
||||
pass
|
||||
|
||||
@callback
|
||||
def cluster_command(self, tsn, command_id, args):
|
||||
"""Handle a cluster command received on this cluster."""
|
||||
pass
|
||||
|
||||
@callback
|
||||
def zha_send_event(self, cluster, command, args):
|
||||
"""Relay entity events to hass."""
|
||||
self._hass.bus.async_fire(
|
||||
'zha_event',
|
||||
{
|
||||
'unique_id': self._unique_id,
|
||||
'command': command,
|
||||
'args': args
|
||||
},
|
||||
EventOrigin.remote
|
||||
)
|
Loading…
Reference in New Issue