Add power source to device and clean up zha listeners (#21174)

check available and add comments

ensure order on API test
This commit is contained in:
David F. Mulcahey 2019-02-18 10:55:41 -05:00 committed by Paulus Schoutsen
parent 8b5aff63ae
commit d1fa341a78
12 changed files with 179 additions and 58 deletions

View File

@ -62,7 +62,6 @@ ILLUMINANCE = 'illuminance'
PRESSURE = 'pressure' PRESSURE = 'pressure'
METERING = 'metering' METERING = 'metering'
ELECTRICAL_MEASUREMENT = 'electrical_measurement' ELECTRICAL_MEASUREMENT = 'electrical_measurement'
POWER_CONFIGURATION = 'power_configuration'
GENERIC = 'generic' GENERIC = 'generic'
UNKNOWN = 'unknown' UNKNOWN = 'unknown'
OPENING = 'opening' OPENING = 'opening'
@ -73,6 +72,7 @@ ATTR_LEVEL = 'level'
LISTENER_ON_OFF = 'on_off' LISTENER_ON_OFF = 'on_off'
LISTENER_ATTRIBUTE = 'attribute' LISTENER_ATTRIBUTE = 'attribute'
LISTENER_BASIC = 'basic'
LISTENER_COLOR = 'color' LISTENER_COLOR = 'color'
LISTENER_FAN = 'fan' LISTENER_FAN = 'fan'
LISTENER_LEVEL = ATTR_LEVEL LISTENER_LEVEL = ATTR_LEVEL
@ -113,6 +113,7 @@ CLUSTER_REPORT_CONFIGS = {}
CUSTOM_CLUSTER_MAPPINGS = {} CUSTOM_CLUSTER_MAPPINGS = {}
COMPONENT_CLUSTERS = {} COMPONENT_CLUSTERS = {}
EVENT_RELAY_CLUSTERS = [] EVENT_RELAY_CLUSTERS = []
NO_SENSOR_CLUSTERS = []
REPORT_CONFIG_MAX_INT = 900 REPORT_CONFIG_MAX_INT = 900
REPORT_CONFIG_MAX_INT_BATTERY_SAVE = 10800 REPORT_CONFIG_MAX_INT_BATTERY_SAVE = 10800

View File

@ -15,9 +15,9 @@ from .const import (
ATTR_CLUSTER_ID, ATTR_ATTRIBUTE, ATTR_VALUE, ATTR_COMMAND, SERVER, ATTR_CLUSTER_ID, ATTR_ATTRIBUTE, ATTR_VALUE, ATTR_COMMAND, SERVER,
ATTR_COMMAND_TYPE, ATTR_ARGS, CLIENT_COMMANDS, SERVER_COMMANDS, ATTR_COMMAND_TYPE, ATTR_ARGS, CLIENT_COMMANDS, SERVER_COMMANDS,
ATTR_ENDPOINT_ID, IEEE, MODEL, NAME, UNKNOWN, QUIRK_APPLIED, ATTR_ENDPOINT_ID, IEEE, MODEL, NAME, UNKNOWN, QUIRK_APPLIED,
QUIRK_CLASS QUIRK_CLASS, LISTENER_BASIC
) )
from .listeners import EventRelayListener from .listeners import EventRelayListener, BasicListener
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -59,6 +59,7 @@ class ZHADevice:
self._zigpy_device.__class__.__module__, self._zigpy_device.__class__.__module__,
self._zigpy_device.__class__.__name__ self._zigpy_device.__class__.__name__
) )
self.power_source = None
@property @property
def name(self): def name(self):
@ -177,6 +178,13 @@ class ZHADevice:
"""Initialize listeners.""" """Initialize listeners."""
_LOGGER.debug('%s: started initialization', self.name) _LOGGER.debug('%s: started initialization', self.name)
await self._execute_listener_tasks('async_initialize', from_cache) await self._execute_listener_tasks('async_initialize', from_cache)
self.power_source = self.cluster_listeners.get(
LISTENER_BASIC).get_power_source()
_LOGGER.debug(
'%s: power source: %s',
self.name,
BasicListener.POWER_SOURCES.get(self.power_source)
)
_LOGGER.debug('%s: completed initialization', self.name) _LOGGER.debug('%s: completed initialization', self.name)
async def _execute_listener_tasks(self, task_name, *args): async def _execute_listener_tasks(self, task_name, *args):

View File

@ -18,15 +18,15 @@ from .const import (
ZHA_DISCOVERY_NEW, DEVICE_CLASS, SINGLE_INPUT_CLUSTER_DEVICE_CLASS, ZHA_DISCOVERY_NEW, DEVICE_CLASS, SINGLE_INPUT_CLUSTER_DEVICE_CLASS,
SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, COMPONENT_CLUSTERS, HUMIDITY, SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, COMPONENT_CLUSTERS, HUMIDITY,
TEMPERATURE, ILLUMINANCE, PRESSURE, METERING, ELECTRICAL_MEASUREMENT, TEMPERATURE, ILLUMINANCE, PRESSURE, METERING, ELECTRICAL_MEASUREMENT,
POWER_CONFIGURATION, GENERIC, SENSOR_TYPE, EVENT_RELAY_CLUSTERS, GENERIC, SENSOR_TYPE, EVENT_RELAY_CLUSTERS, LISTENER_BATTERY, UNKNOWN,
LISTENER_BATTERY, UNKNOWN, OPENING, ZONE, OCCUPANCY, OPENING, ZONE, OCCUPANCY, CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_IMMEDIATE,
CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_IMMEDIATE, REPORT_CONFIG_ASAP, REPORT_CONFIG_ASAP, REPORT_CONFIG_DEFAULT, REPORT_CONFIG_MIN_INT,
REPORT_CONFIG_DEFAULT, REPORT_CONFIG_MIN_INT, REPORT_CONFIG_MAX_INT, REPORT_CONFIG_MAX_INT, REPORT_CONFIG_OP, SIGNAL_REMOVE, NO_SENSOR_CLUSTERS)
REPORT_CONFIG_OP, SIGNAL_REMOVE)
from .device import ZHADevice from .device import ZHADevice
from ..device_entity import ZhaDeviceEntity from ..device_entity import ZhaDeviceEntity
from .listeners import ( from .listeners import (
LISTENER_REGISTRY, AttributeListener, EventRelayListener, ZDOListener) LISTENER_REGISTRY, AttributeListener, EventRelayListener, ZDOListener,
BasicListener)
from .helpers import convert_ieee from .helpers import convert_ieee
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -165,7 +165,21 @@ class ZHAGateway:
await self._component.async_add_entities([device_entity]) await self._component.async_add_entities([device_entity])
if is_new_join: if is_new_join:
# because it's a new join we can immediately mark the device as
# available and we already loaded fresh state above
zha_device.update_available(True) zha_device.update_available(True)
elif not zha_device.available and zha_device.power_source is not None\
and zha_device.power_source != BasicListener.BATTERY:
# the device is currently marked unavailable and it isn't a battery
# powered device so we should be able to update it now
_LOGGER.debug(
"attempting to request fresh state for %s %s",
zha_device.name,
"with power source: {}".format(
BasicListener.POWER_SOURCES.get(zha_device.power_source)
)
)
await zha_device.async_initialize(from_cache=False)
async def _async_process_endpoint( async def _async_process_endpoint(
self, endpoint_id, endpoint, discovery_infos, device, zha_device, self, endpoint_id, endpoint, discovery_infos, device, zha_device,
@ -312,6 +326,13 @@ async def _handle_single_cluster_matches(hass, endpoint, zha_device,
is_new_join, is_new_join,
)) ))
if cluster.cluster_id in NO_SENSOR_CLUSTERS:
cluster_match_tasks.append(_handle_listener_only_cluster_match(
zha_device,
cluster,
is_new_join,
))
for cluster in endpoint.out_clusters.values(): for cluster in endpoint.out_clusters.values():
if cluster.cluster_id not in profile_clusters[1]: if cluster.cluster_id not in profile_clusters[1]:
cluster_match_tasks.append(_handle_single_cluster_match( cluster_match_tasks.append(_handle_single_cluster_match(
@ -338,6 +359,12 @@ async def _handle_single_cluster_matches(hass, endpoint, zha_device,
return cluster_matches return cluster_matches
async def _handle_listener_only_cluster_match(
zha_device, cluster, is_new_join):
"""Handle a listener only cluster match."""
await _create_cluster_listener(cluster, zha_device, is_new_join)
async def _handle_single_cluster_match(hass, zha_device, cluster, device_key, async def _handle_single_cluster_match(hass, zha_device, cluster, device_key,
device_classes, is_new_join): device_classes, is_new_join):
"""Dispatch a single cluster match to a HA component.""" """Dispatch a single cluster match to a HA component."""
@ -352,11 +379,6 @@ async def _handle_single_cluster_match(hass, zha_device, cluster, device_key,
listeners = [] listeners = []
await _create_cluster_listener(cluster, zha_device, is_new_join, await _create_cluster_listener(cluster, zha_device, is_new_join,
listeners=listeners) listeners=listeners)
# don't actually create entities for PowerConfiguration
# find a better way to do this without abusing single cluster reg
from zigpy.zcl.clusters.general import PowerConfiguration
if cluster.cluster_id == PowerConfiguration.cluster_id:
return
cluster_key = "{}-{}".format(device_key, cluster.cluster_id) cluster_key = "{}-{}".format(device_key, cluster.cluster_id)
discovery_info = { discovery_info = {
@ -405,6 +427,10 @@ def establish_device_mappings():
EVENT_RELAY_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id) EVENT_RELAY_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id)
EVENT_RELAY_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id) EVENT_RELAY_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id)
NO_SENSOR_CLUSTERS.append(zcl.clusters.general.Basic.cluster_id)
NO_SENSOR_CLUSTERS.append(
zcl.clusters.general.PowerConfiguration.cluster_id)
DEVICE_CLASS[zha.PROFILE_ID].update({ DEVICE_CLASS[zha.PROFILE_ID].update({
zha.DeviceType.ON_OFF_SWITCH: 'binary_sensor', zha.DeviceType.ON_OFF_SWITCH: 'binary_sensor',
zha.DeviceType.LEVEL_CONTROL_SWITCH: 'binary_sensor', zha.DeviceType.LEVEL_CONTROL_SWITCH: 'binary_sensor',
@ -442,7 +468,6 @@ def establish_device_mappings():
zcl.clusters.measurement.IlluminanceMeasurement: 'sensor', zcl.clusters.measurement.IlluminanceMeasurement: 'sensor',
zcl.clusters.smartenergy.Metering: 'sensor', zcl.clusters.smartenergy.Metering: 'sensor',
zcl.clusters.homeautomation.ElectricalMeasurement: 'sensor', zcl.clusters.homeautomation.ElectricalMeasurement: 'sensor',
zcl.clusters.general.PowerConfiguration: 'sensor',
zcl.clusters.security.IasZone: 'binary_sensor', zcl.clusters.security.IasZone: 'binary_sensor',
zcl.clusters.measurement.OccupancySensing: 'binary_sensor', zcl.clusters.measurement.OccupancySensing: 'binary_sensor',
zcl.clusters.hvac.Fan: 'fan', zcl.clusters.hvac.Fan: 'fan',
@ -462,8 +487,6 @@ def establish_device_mappings():
zcl.clusters.smartenergy.Metering.cluster_id: METERING, zcl.clusters.smartenergy.Metering.cluster_id: METERING,
zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id: zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id:
ELECTRICAL_MEASUREMENT, ELECTRICAL_MEASUREMENT,
zcl.clusters.general.PowerConfiguration.cluster_id:
POWER_CONFIGURATION,
}) })
BINARY_SENSOR_TYPES.update({ BINARY_SENSOR_TYPES.update({
@ -473,6 +496,19 @@ def establish_device_mappings():
}) })
CLUSTER_REPORT_CONFIGS.update({ CLUSTER_REPORT_CONFIGS.update({
zcl.clusters.general.Alarms.cluster_id: [],
zcl.clusters.general.Basic.cluster_id: [],
zcl.clusters.general.Commissioning.cluster_id: [],
zcl.clusters.general.Identify.cluster_id: [],
zcl.clusters.general.Groups.cluster_id: [],
zcl.clusters.general.Scenes.cluster_id: [],
zcl.clusters.general.Partition.cluster_id: [],
zcl.clusters.general.Ota.cluster_id: [],
zcl.clusters.general.PowerProfile.cluster_id: [],
zcl.clusters.general.ApplianceControl.cluster_id: [],
zcl.clusters.general.PollControl.cluster_id: [],
zcl.clusters.general.GreenPowerProxy.cluster_id: [],
zcl.clusters.general.OnOffConfiguration.cluster_id: [],
zcl.clusters.general.OnOff.cluster_id: [{ zcl.clusters.general.OnOff.cluster_id: [{
'attr': 'on_off', 'attr': 'on_off',
'config': REPORT_CONFIG_IMMEDIATE 'config': REPORT_CONFIG_IMMEDIATE

View File

@ -18,7 +18,10 @@ from .helpers import (
safe_read, get_attr_id_by_name, bind_cluster) safe_read, get_attr_id_by_name, bind_cluster)
from .const import ( from .const import (
CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_DEFAULT, SIGNAL_ATTR_UPDATED, CLUSTER_REPORT_CONFIGS, REPORT_CONFIG_DEFAULT, SIGNAL_ATTR_UPDATED,
SIGNAL_MOVE_LEVEL, SIGNAL_SET_LEVEL, SIGNAL_STATE_ATTR, ATTR_LEVEL SIGNAL_MOVE_LEVEL, SIGNAL_SET_LEVEL, SIGNAL_STATE_ATTR, LISTENER_BASIC,
LISTENER_ATTRIBUTE, LISTENER_ON_OFF, LISTENER_COLOR, LISTENER_FAN,
LISTENER_LEVEL, LISTENER_ZONE, LISTENER_ACTIVE_POWER, LISTENER_BATTERY,
LISTENER_EVENT_RELAY
) )
LISTENER_REGISTRY = {} LISTENER_REGISTRY = {}
@ -30,12 +33,25 @@ def populate_listener_registry():
"""Populate the listener registry.""" """Populate the listener registry."""
from zigpy import zcl from zigpy import zcl
LISTENER_REGISTRY.update({ LISTENER_REGISTRY.update({
zcl.clusters.general.Alarms.cluster_id: ClusterListener,
zcl.clusters.general.Commissioning.cluster_id: ClusterListener,
zcl.clusters.general.Identify.cluster_id: ClusterListener,
zcl.clusters.general.Groups.cluster_id: ClusterListener,
zcl.clusters.general.Scenes.cluster_id: ClusterListener,
zcl.clusters.general.Partition.cluster_id: ClusterListener,
zcl.clusters.general.Ota.cluster_id: ClusterListener,
zcl.clusters.general.PowerProfile.cluster_id: ClusterListener,
zcl.clusters.general.ApplianceControl.cluster_id: ClusterListener,
zcl.clusters.general.PollControl.cluster_id: ClusterListener,
zcl.clusters.general.GreenPowerProxy.cluster_id: ClusterListener,
zcl.clusters.general.OnOffConfiguration.cluster_id: ClusterListener,
zcl.clusters.general.OnOff.cluster_id: OnOffListener, zcl.clusters.general.OnOff.cluster_id: OnOffListener,
zcl.clusters.general.LevelControl.cluster_id: LevelListener, zcl.clusters.general.LevelControl.cluster_id: LevelListener,
zcl.clusters.lighting.Color.cluster_id: ColorListener, zcl.clusters.lighting.Color.cluster_id: ColorListener,
zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id: zcl.clusters.homeautomation.ElectricalMeasurement.cluster_id:
ActivePowerListener, ActivePowerListener,
zcl.clusters.general.PowerConfiguration.cluster_id: BatteryListener, zcl.clusters.general.PowerConfiguration.cluster_id: BatteryListener,
zcl.clusters.general.Basic.cluster_id: BasicListener,
zcl.clusters.security.IasZone.cluster_id: IASZoneListener, zcl.clusters.security.IasZone.cluster_id: IASZoneListener,
zcl.clusters.hvac.Fan.cluster_id: FanListener, zcl.clusters.hvac.Fan.cluster_id: FanListener,
}) })
@ -92,6 +108,7 @@ class ClusterListener:
def __init__(self, cluster, device): def __init__(self, cluster, device):
"""Initialize ClusterListener.""" """Initialize ClusterListener."""
self.name = 'cluster_{}'.format(cluster.cluster_id)
self._cluster = cluster self._cluster = cluster
self._zha_device = device self._zha_device = device
self._unique_id = construct_unique_id(cluster) self._unique_id = construct_unique_id(cluster)
@ -216,11 +233,10 @@ class ClusterListener:
class AttributeListener(ClusterListener): class AttributeListener(ClusterListener):
"""Listener for the attribute reports cluster.""" """Listener for the attribute reports cluster."""
name = 'attribute'
def __init__(self, cluster, device): def __init__(self, cluster, device):
"""Initialize AttributeListener.""" """Initialize AttributeListener."""
super().__init__(cluster, device) super().__init__(cluster, device)
self.name = LISTENER_ATTRIBUTE
attr = self._report_config[0].get('attr') attr = self._report_config[0].get('attr')
if isinstance(attr, str): if isinstance(attr, str):
self._value_attribute = get_attr_id_by_name(self.cluster, attr) self._value_attribute = get_attr_id_by_name(self.cluster, attr)
@ -247,13 +263,12 @@ class AttributeListener(ClusterListener):
class OnOffListener(ClusterListener): class OnOffListener(ClusterListener):
"""Listener for the OnOff Zigbee cluster.""" """Listener for the OnOff Zigbee cluster."""
name = 'on_off'
ON_OFF = 0 ON_OFF = 0
def __init__(self, cluster, device): def __init__(self, cluster, device):
"""Initialize ClusterListener.""" """Initialize OnOffListener."""
super().__init__(cluster, device) super().__init__(cluster, device)
self.name = LISTENER_ON_OFF
self._state = None self._state = None
@callback @callback
@ -295,10 +310,13 @@ class OnOffListener(ClusterListener):
class LevelListener(ClusterListener): class LevelListener(ClusterListener):
"""Listener for the LevelControl Zigbee cluster.""" """Listener for the LevelControl Zigbee cluster."""
name = ATTR_LEVEL
CURRENT_LEVEL = 0 CURRENT_LEVEL = 0
def __init__(self, cluster, device):
"""Initialize LevelListener."""
super().__init__(cluster, device)
self.name = LISTENER_LEVEL
@callback @callback
def cluster_command(self, tsn, command_id, args): def cluster_command(self, tsn, command_id, args):
"""Handle commands received to this cluster.""" """Handle commands received to this cluster."""
@ -350,7 +368,10 @@ class LevelListener(ClusterListener):
class IASZoneListener(ClusterListener): class IASZoneListener(ClusterListener):
"""Listener for the IASZone Zigbee cluster.""" """Listener for the IASZone Zigbee cluster."""
name = 'zone' def __init__(self, cluster, device):
"""Initialize LevelListener."""
super().__init__(cluster, device)
self.name = LISTENER_ZONE
@callback @callback
def cluster_command(self, tsn, command_id, args): def cluster_command(self, tsn, command_id, args):
@ -415,7 +436,10 @@ class IASZoneListener(ClusterListener):
class ActivePowerListener(AttributeListener): class ActivePowerListener(AttributeListener):
"""Listener that polls active power level.""" """Listener that polls active power level."""
name = 'active_power' def __init__(self, cluster, device):
"""Initialize ActivePowerListener."""
super().__init__(cluster, device)
self.name = LISTENER_ACTIVE_POWER
async def async_update(self): async def async_update(self):
"""Retrieve latest state.""" """Retrieve latest state."""
@ -423,7 +447,7 @@ class ActivePowerListener(AttributeListener):
# This is a polling listener. Don't allow cache. # This is a polling listener. Don't allow cache.
result = await self.get_attribute_value( result = await self.get_attribute_value(
'active_power', from_cache=False) LISTENER_ACTIVE_POWER, from_cache=False)
async_dispatcher_send( async_dispatcher_send(
self._zha_device.hass, self._zha_device.hass,
"{}_{}".format(self.unique_id, SIGNAL_ATTR_UPDATED), "{}_{}".format(self.unique_id, SIGNAL_ATTR_UPDATED),
@ -433,14 +457,53 @@ class ActivePowerListener(AttributeListener):
async def async_initialize(self, from_cache): async def async_initialize(self, from_cache):
"""Initialize listener.""" """Initialize listener."""
await self.get_attribute_value( await self.get_attribute_value(
'active_power', from_cache=from_cache) LISTENER_ACTIVE_POWER, from_cache=from_cache)
await super().async_initialize(from_cache) await super().async_initialize(from_cache)
class BasicListener(ClusterListener):
"""Listener to interact with the basic cluster."""
BATTERY = 3
POWER_SOURCES = {
0: 'Unknown',
1: 'Mains (single phase)',
2: 'Mains (3 phase)',
BATTERY: 'Battery',
4: 'DC source',
5: 'Emergency mains constantly powered',
6: 'Emergency mains and transfer switch'
}
def __init__(self, cluster, device):
"""Initialize BasicListener."""
super().__init__(cluster, device)
self.name = LISTENER_BASIC
self._power_source = None
async def async_configure(self):
"""Configure this listener."""
await super().async_configure()
await self.async_initialize(False)
async def async_initialize(self, from_cache):
"""Initialize listener."""
self._power_source = await self.get_attribute_value(
'power_source', from_cache=from_cache)
await super().async_initialize(from_cache)
def get_power_source(self):
"""Get the power source."""
return self._power_source
class BatteryListener(ClusterListener): class BatteryListener(ClusterListener):
"""Listener that polls active power level.""" """Listener that polls active power level."""
name = 'battery' def __init__(self, cluster, device):
"""Initialize BatteryListener."""
super().__init__(cluster, device)
self.name = LISTENER_BATTERY
@callback @callback
def attribute_updated(self, attrid, value): def attribute_updated(self, attrid, value):
@ -480,7 +543,10 @@ class BatteryListener(ClusterListener):
class EventRelayListener(ClusterListener): class EventRelayListener(ClusterListener):
"""Event relay that can be attached to zigbee clusters.""" """Event relay that can be attached to zigbee clusters."""
name = 'event_relay' def __init__(self, cluster, device):
"""Initialize EventRelayListener."""
super().__init__(cluster, device)
self.name = LISTENER_EVENT_RELAY
@callback @callback
def attribute_updated(self, attrid, value): def attribute_updated(self, attrid, value):
@ -512,15 +578,14 @@ class EventRelayListener(ClusterListener):
class ColorListener(ClusterListener): class ColorListener(ClusterListener):
"""Color listener.""" """Color listener."""
name = 'color'
CAPABILITIES_COLOR_XY = 0x08 CAPABILITIES_COLOR_XY = 0x08
CAPABILITIES_COLOR_TEMP = 0x10 CAPABILITIES_COLOR_TEMP = 0x10
UNSUPPORTED_ATTRIBUTE = 0x86 UNSUPPORTED_ATTRIBUTE = 0x86
def __init__(self, cluster, device): def __init__(self, cluster, device):
"""Initialize ClusterListener.""" """Initialize ColorListener."""
super().__init__(cluster, device) super().__init__(cluster, device)
self.name = LISTENER_COLOR
self._color_capabilities = None self._color_capabilities = None
def get_color_capabilities(self): def get_color_capabilities(self):
@ -550,10 +615,13 @@ class ColorListener(ClusterListener):
class FanListener(ClusterListener): class FanListener(ClusterListener):
"""Fan listener.""" """Fan listener."""
name = 'fan'
_value_attribute = 0 _value_attribute = 0
def __init__(self, cluster, device):
"""Initialize FanListener."""
super().__init__(cluster, device)
self.name = LISTENER_FAN
async def async_set_speed(self, value) -> None: async def async_set_speed(self, value) -> None:
"""Set the speed of the fan.""" """Set the speed of the fan."""
from zigpy.exceptions import DeliveryError from zigpy.exceptions import DeliveryError
@ -595,10 +663,9 @@ class FanListener(ClusterListener):
class ZDOListener: class ZDOListener:
"""Listener for ZDO events.""" """Listener for ZDO events."""
name = 'zdo'
def __init__(self, cluster, device): def __init__(self, cluster, device):
"""Initialize ClusterListener.""" """Initialize ZDOListener."""
self.name = 'zdo'
self._cluster = cluster self._cluster = cluster
self._zha_device = device self._zha_device = device
self._status = ListenerStatus.CREATED self._status = ListenerStatus.CREATED

View File

@ -12,8 +12,8 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .core.const import ( from .core.const import (
DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW, HUMIDITY, TEMPERATURE, DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW, HUMIDITY, TEMPERATURE,
ILLUMINANCE, PRESSURE, METERING, ELECTRICAL_MEASUREMENT, ILLUMINANCE, PRESSURE, METERING, ELECTRICAL_MEASUREMENT,
POWER_CONFIGURATION, GENERIC, SENSOR_TYPE, LISTENER_ATTRIBUTE, GENERIC, SENSOR_TYPE, LISTENER_ATTRIBUTE, LISTENER_ACTIVE_POWER,
LISTENER_ACTIVE_POWER, SIGNAL_ATTR_UPDATED, SIGNAL_STATE_ATTR) SIGNAL_ATTR_UPDATED, SIGNAL_STATE_ATTR)
from .entity import ZhaEntity from .entity import ZhaEntity
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -71,7 +71,6 @@ UNIT_REGISTRY = {
ILLUMINANCE: 'lx', ILLUMINANCE: 'lx',
METERING: 'W', METERING: 'W',
ELECTRICAL_MEASUREMENT: 'W', ELECTRICAL_MEASUREMENT: 'W',
POWER_CONFIGURATION: '%',
GENERIC: None GENERIC: None
} }

View File

@ -174,6 +174,7 @@ async def async_test_device_join(
only trigger during device joins can be tested. only trigger during device joins can be tested.
""" """
from zigpy.zcl.foundation import Status from zigpy.zcl.foundation import Status
from zigpy.zcl.clusters.general import Basic
# create zigpy device mocking out the zigbee network operations # create zigpy device mocking out the zigbee network operations
with patch( with patch(
'zigpy.zcl.Cluster.configure_reporting', 'zigpy.zcl.Cluster.configure_reporting',
@ -182,7 +183,8 @@ async def async_test_device_join(
'zigpy.zcl.Cluster.bind', 'zigpy.zcl.Cluster.bind',
return_value=mock_coro([Status.SUCCESS, Status.SUCCESS])): return_value=mock_coro([Status.SUCCESS, Status.SUCCESS])):
zigpy_device = await async_init_zigpy_device( zigpy_device = await async_init_zigpy_device(
hass, [cluster_id], [], device_type, zha_gateway, hass, [cluster_id, Basic.cluster_id], [], device_type,
zha_gateway,
ieee="00:0d:6f:00:0a:90:69:f7", ieee="00:0d:6f:00:0a:90:69:f7",
manufacturer="FakeMan{}".format(cluster_id), manufacturer="FakeMan{}".format(cluster_id),
model="FakeMod{}".format(cluster_id), model="FakeMod{}".format(cluster_id),

View File

@ -17,14 +17,14 @@ from .common import async_init_zigpy_device
@pytest.fixture @pytest.fixture
async def zha_client(hass, config_entry, zha_gateway, hass_ws_client): async def zha_client(hass, config_entry, zha_gateway, hass_ws_client):
"""Test zha switch platform.""" """Test zha switch platform."""
from zigpy.zcl.clusters.general import OnOff from zigpy.zcl.clusters.general import OnOff, Basic
# load the ZHA API # load the ZHA API
async_load_api(hass, Mock(), zha_gateway) async_load_api(hass, Mock(), zha_gateway)
# create zigpy device # create zigpy device
await async_init_zigpy_device( await async_init_zigpy_device(
hass, [OnOff.cluster_id], [], None, zha_gateway) hass, [OnOff.cluster_id, Basic.cluster_id], [], None, zha_gateway)
# load up switch domain # load up switch domain
await hass.config_entries.async_forward_entry_setup( await hass.config_entries.async_forward_entry_setup(
@ -44,10 +44,16 @@ async def test_device_clusters(hass, config_entry, zha_gateway, zha_client):
msg = await zha_client.receive_json() msg = await zha_client.receive_json()
assert len(msg['result']) == 1 assert len(msg['result']) == 2
cluster_info = msg['result'][0] cluster_infos = sorted(msg['result'], key=lambda k: k[ID])
cluster_info = cluster_infos[0]
assert cluster_info[TYPE] == IN
assert cluster_info[ID] == 0
assert cluster_info[NAME] == 'Basic'
cluster_info = cluster_infos[1]
assert cluster_info[TYPE] == IN assert cluster_info[TYPE] == IN
assert cluster_info[ID] == 6 assert cluster_info[ID] == 6
assert cluster_info[NAME] == 'OnOff' assert cluster_info[NAME] == 'OnOff'

View File

@ -11,13 +11,13 @@ async def test_binary_sensor(hass, config_entry, zha_gateway):
"""Test zha binary_sensor platform.""" """Test zha binary_sensor platform."""
from zigpy.zcl.clusters.security import IasZone from zigpy.zcl.clusters.security import IasZone
from zigpy.zcl.clusters.measurement import OccupancySensing from zigpy.zcl.clusters.measurement import OccupancySensing
from zigpy.zcl.clusters.general import OnOff, LevelControl from zigpy.zcl.clusters.general import OnOff, LevelControl, Basic
from zigpy.profiles.zha import DeviceType from zigpy.profiles.zha import DeviceType
# create zigpy devices # create zigpy devices
zigpy_device_zone = await async_init_zigpy_device( zigpy_device_zone = await async_init_zigpy_device(
hass, hass,
[IasZone.cluster_id], [IasZone.cluster_id, Basic.cluster_id],
[], [],
None, None,
zha_gateway zha_gateway
@ -25,7 +25,7 @@ async def test_binary_sensor(hass, config_entry, zha_gateway):
zigpy_device_remote = await async_init_zigpy_device( zigpy_device_remote = await async_init_zigpy_device(
hass, hass,
[], [Basic.cluster_id],
[OnOff.cluster_id, LevelControl.cluster_id], [OnOff.cluster_id, LevelControl.cluster_id],
DeviceType.LEVEL_CONTROL_SWITCH, DeviceType.LEVEL_CONTROL_SWITCH,
zha_gateway, zha_gateway,
@ -36,7 +36,7 @@ async def test_binary_sensor(hass, config_entry, zha_gateway):
zigpy_device_occupancy = await async_init_zigpy_device( zigpy_device_occupancy = await async_init_zigpy_device(
hass, hass,
[OccupancySensing.cluster_id], [OccupancySensing.cluster_id, Basic.cluster_id],
[], [],
None, None,
zha_gateway, zha_gateway,

View File

@ -17,11 +17,12 @@ from .common import (
async def test_fan(hass, config_entry, zha_gateway): async def test_fan(hass, config_entry, zha_gateway):
"""Test zha fan platform.""" """Test zha fan platform."""
from zigpy.zcl.clusters.hvac import Fan from zigpy.zcl.clusters.hvac import Fan
from zigpy.zcl.clusters.general import Basic
from zigpy.zcl.foundation import Status from zigpy.zcl.foundation import Status
# create zigpy device # create zigpy device
zigpy_device = await async_init_zigpy_device( zigpy_device = await async_init_zigpy_device(
hass, [Fan.cluster_id], [], None, zha_gateway) hass, [Fan.cluster_id, Basic.cluster_id], [], None, zha_gateway)
# load up fan domain # load up fan domain
await hass.config_entries.async_forward_entry_setup( await hass.config_entries.async_forward_entry_setup(

View File

@ -14,13 +14,13 @@ OFF = 0
async def test_light(hass, config_entry, zha_gateway): async def test_light(hass, config_entry, zha_gateway):
"""Test zha light platform.""" """Test zha light platform."""
from zigpy.zcl.clusters.general import OnOff, LevelControl from zigpy.zcl.clusters.general import OnOff, LevelControl, Basic
from zigpy.profiles.zha import DeviceType from zigpy.profiles.zha import DeviceType
# create zigpy devices # create zigpy devices
zigpy_device_on_off = await async_init_zigpy_device( zigpy_device_on_off = await async_init_zigpy_device(
hass, hass,
[OnOff.cluster_id], [OnOff.cluster_id, Basic.cluster_id],
[], [],
DeviceType.ON_OFF_LIGHT, DeviceType.ON_OFF_LIGHT,
zha_gateway zha_gateway
@ -28,7 +28,7 @@ async def test_light(hass, config_entry, zha_gateway):
zigpy_device_level = await async_init_zigpy_device( zigpy_device_level = await async_init_zigpy_device(
hass, hass,
[OnOff.cluster_id, LevelControl.cluster_id], [OnOff.cluster_id, LevelControl.cluster_id, Basic.cluster_id],
[], [],
DeviceType.ON_OFF_LIGHT, DeviceType.ON_OFF_LIGHT,
zha_gateway, zha_gateway,

View File

@ -86,6 +86,7 @@ async def async_build_devices(hass, zha_gateway, config_entry, cluster_ids):
A dict containing relevant device info for testing is returned. It contains A dict containing relevant device info for testing is returned. It contains
the entity id, zigpy device, and the zigbee cluster for the sensor. the entity id, zigpy device, and the zigbee cluster for the sensor.
""" """
from zigpy.zcl.clusters.general import Basic
device_infos = {} device_infos = {}
counter = 0 counter = 0
for cluster_id in cluster_ids: for cluster_id in cluster_ids:
@ -93,7 +94,7 @@ async def async_build_devices(hass, zha_gateway, config_entry, cluster_ids):
device_infos[cluster_id] = {"zigpy_device": None} device_infos[cluster_id] = {"zigpy_device": None}
device_infos[cluster_id]["zigpy_device"] = await \ device_infos[cluster_id]["zigpy_device"] = await \
async_init_zigpy_device( async_init_zigpy_device(
hass, [cluster_id], [], None, zha_gateway, hass, [cluster_id, Basic.cluster_id], [], None, zha_gateway,
ieee="{}0:15:8d:00:02:32:4f:32".format(counter), ieee="{}0:15:8d:00:02:32:4f:32".format(counter),
manufacturer="Fake{}".format(cluster_id), manufacturer="Fake{}".format(cluster_id),
model="FakeModel{}".format(cluster_id)) model="FakeModel{}".format(cluster_id))

View File

@ -14,12 +14,12 @@ OFF = 0
async def test_switch(hass, config_entry, zha_gateway): async def test_switch(hass, config_entry, zha_gateway):
"""Test zha switch platform.""" """Test zha switch platform."""
from zigpy.zcl.clusters.general import OnOff from zigpy.zcl.clusters.general import OnOff, Basic
from zigpy.zcl.foundation import Status from zigpy.zcl.foundation import Status
# create zigpy device # create zigpy device
zigpy_device = await async_init_zigpy_device( zigpy_device = await async_init_zigpy_device(
hass, [OnOff.cluster_id], [], None, zha_gateway) hass, [OnOff.cluster_id, Basic.cluster_id], [], None, zha_gateway)
# load up switch domain # load up switch domain
await hass.config_entries.async_forward_entry_setup( await hass.config_entries.async_forward_entry_setup(