Misc. ZHA enhancements (#24559)

* add nwk to device info
* input bind only cluster support
* cleanup channel only clusters
* dirty hack to correct xiaomi vibration sensor
* exclude remaining remote binary sensors
* review comments
* fix comment
This commit is contained in:
David F. Mulcahey 2019-06-16 13:17:53 -04:00 committed by Alexei Chetroi
parent b782ed6bbb
commit c173a3be44
4 changed files with 78 additions and 20 deletions

View File

@ -18,7 +18,7 @@ from .const import (
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, ZDO_CHANNEL, MANUFACTURER_CODE, POWER_SOURCE, MAINS_POWERED, QUIRK_CLASS, ZDO_CHANNEL, MANUFACTURER_CODE, POWER_SOURCE, MAINS_POWERED,
BATTERY_OR_UNKNOWN BATTERY_OR_UNKNOWN, NWK
) )
from .channels import EventRelayChannel from .channels import EventRelayChannel
@ -189,6 +189,7 @@ class ZHADevice:
ieee = str(self.ieee) ieee = str(self.ieee)
return { return {
IEEE: ieee, IEEE: ieee,
NWK: self.nwk,
ATTR_MANUFACTURER: self.manufacturer, ATTR_MANUFACTURER: self.manufacturer,
MODEL: self.model, MODEL: self.model,
NAME: self.name or ieee, NAME: self.name or ieee,
@ -390,7 +391,7 @@ class ZHADevice:
manufacturer=manufacturer manufacturer=manufacturer
) )
_LOGGER.debug( _LOGGER.debug(
'set: %s for attr: %s to cluster: %s for entity: %s - res: %s', 'set: %s for attr: %s to cluster: %s for ept: %s - res: %s',
value, value,
attribute, attribute,
cluster_id, cluster_id,

View File

@ -21,9 +21,10 @@ from .const import (
SENSOR_TYPE, UNKNOWN, GENERIC, POWER_CONFIGURATION_CHANNEL SENSOR_TYPE, UNKNOWN, GENERIC, POWER_CONFIGURATION_CHANNEL
) )
from .registries import ( from .registries import (
BINARY_SENSOR_TYPES, NO_SENSOR_CLUSTERS, EVENT_RELAY_CLUSTERS, BINARY_SENSOR_TYPES, CHANNEL_ONLY_CLUSTERS, EVENT_RELAY_CLUSTERS,
SENSOR_TYPES, DEVICE_CLASS, COMPONENT_CLUSTERS, SENSOR_TYPES, DEVICE_CLASS, COMPONENT_CLUSTERS,
SINGLE_INPUT_CLUSTER_DEVICE_CLASS, SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS SINGLE_INPUT_CLUSTER_DEVICE_CLASS, SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS,
OUTPUT_CHANNEL_ONLY_CLUSTERS, REMOTE_DEVICE_TYPES
) )
from ..device_entity import ZhaDeviceEntity from ..device_entity import ZhaDeviceEntity
@ -87,6 +88,12 @@ def async_process_endpoint(
def _async_create_cluster_channel(cluster, zha_device, is_new_join, def _async_create_cluster_channel(cluster, zha_device, is_new_join,
channels=None, channel_class=None): channels=None, channel_class=None):
"""Create a cluster channel and attach it to a device.""" """Create a cluster channel and attach it to a device."""
# really ugly hack to deal with xiaomi using the door lock cluster
# incorrectly.
if hasattr(cluster, 'ep_attribute') and \
cluster.ep_attribute == 'multistate_input':
channel_class = AttributeListeningChannel
# end of ugly hack
if channel_class is None: if channel_class is None:
channel_class = ZIGBEE_CHANNEL_REGISTRY.get(cluster.cluster_id, channel_class = ZIGBEE_CHANNEL_REGISTRY.get(cluster.cluster_id,
AttributeListeningChannel) AttributeListeningChannel)
@ -161,17 +168,18 @@ def _async_handle_single_cluster_matches(hass, endpoint, zha_device,
profile_clusters, device_key, profile_clusters, device_key,
is_new_join): is_new_join):
"""Dispatch single cluster matches to HA components.""" """Dispatch single cluster matches to HA components."""
from zigpy.zcl.clusters.general import OnOff
cluster_matches = [] cluster_matches = []
cluster_match_results = [] cluster_match_results = []
for cluster in endpoint.in_clusters.values(): for cluster in endpoint.in_clusters.values():
# don't let profiles prevent these channels from being created if cluster.cluster_id in CHANNEL_ONLY_CLUSTERS:
if cluster.cluster_id in NO_SENSOR_CLUSTERS:
cluster_match_results.append( cluster_match_results.append(
_async_handle_channel_only_cluster_match( _async_handle_channel_only_cluster_match(
zha_device, zha_device,
cluster, cluster,
is_new_join, is_new_join,
)) ))
continue
if cluster.cluster_id not in profile_clusters: if cluster.cluster_id not in profile_clusters:
cluster_match_results.append(_async_handle_single_cluster_match( cluster_match_results.append(_async_handle_single_cluster_match(
@ -184,15 +192,33 @@ def _async_handle_single_cluster_matches(hass, endpoint, zha_device,
)) ))
for cluster in endpoint.out_clusters.values(): for cluster in endpoint.out_clusters.values():
if cluster.cluster_id in OUTPUT_CHANNEL_ONLY_CLUSTERS:
cluster_match_results.append(
_async_handle_channel_only_cluster_match(
zha_device,
cluster,
is_new_join,
))
continue
device_type = cluster.endpoint.device_type
profile_id = cluster.endpoint.profile_id
if cluster.cluster_id not in profile_clusters: if cluster.cluster_id not in profile_clusters:
cluster_match_results.append(_async_handle_single_cluster_match( # prevent remotes and controllers from getting entities
hass, if not (cluster.cluster_id == OnOff.cluster_id and profile_id in
zha_device, REMOTE_DEVICE_TYPES and device_type in
cluster, REMOTE_DEVICE_TYPES[profile_id]):
device_key, cluster_match_results.append(
SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS, _async_handle_single_cluster_match(
is_new_join, hass,
)) zha_device,
cluster,
device_key,
SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS,
is_new_join,
)
)
if cluster.cluster_id in EVENT_RELAY_CLUSTERS: if cluster.cluster_id in EVENT_RELAY_CLUSTERS:
_async_create_cluster_channel( _async_create_cluster_channel(

View File

@ -32,7 +32,7 @@ from .discovery import (
async_create_device_entity, async_dispatch_discovery_info, async_create_device_entity, async_dispatch_discovery_info,
async_process_endpoint) async_process_endpoint)
from .patches import apply_application_controller_patch from .patches import apply_application_controller_patch
from .registries import RADIO_TYPES from .registries import RADIO_TYPES, INPUT_BIND_ONLY_CLUSTERS
from .store import async_get_registry from .store import async_get_registry
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -274,8 +274,10 @@ class ZHAGateway:
) )
if endpoint_id != 0: if endpoint_id != 0:
for cluster in endpoint.in_clusters.values(): for cluster in endpoint.in_clusters.values():
cluster.bind_only = False cluster.bind_only = \
cluster.cluster_id in INPUT_BIND_ONLY_CLUSTERS
for cluster in endpoint.out_clusters.values(): for cluster in endpoint.out_clusters.values():
# output clusters are always bind only
cluster.bind_only = True cluster.bind_only = True
else: else:
is_rejoin = is_new_join is True is_rejoin = is_new_join is True

View File

@ -30,11 +30,14 @@ SINGLE_OUTPUT_CLUSTER_DEVICE_CLASS = {}
SENSOR_TYPES = {} SENSOR_TYPES = {}
RADIO_TYPES = {} RADIO_TYPES = {}
BINARY_SENSOR_TYPES = {} BINARY_SENSOR_TYPES = {}
REMOTE_DEVICE_TYPES = {}
CLUSTER_REPORT_CONFIGS = {} CLUSTER_REPORT_CONFIGS = {}
CUSTOM_CLUSTER_MAPPINGS = {} CUSTOM_CLUSTER_MAPPINGS = {}
EVENT_RELAY_CLUSTERS = [] EVENT_RELAY_CLUSTERS = []
NO_SENSOR_CLUSTERS = [] CHANNEL_ONLY_CLUSTERS = []
OUTPUT_CHANNEL_ONLY_CLUSTERS = []
BINDABLE_CLUSTERS = [] BINDABLE_CLUSTERS = []
INPUT_BIND_ONLY_CLUSTERS = []
BINARY_SENSOR_CLUSTERS = set() BINARY_SENSOR_CLUSTERS = set()
LIGHT_CLUSTERS = set() LIGHT_CLUSTERS = set()
SWITCH_CLUSTERS = set() SWITCH_CLUSTERS = set()
@ -59,6 +62,11 @@ def establish_device_mappings():
if zll.PROFILE_ID not in DEVICE_CLASS: if zll.PROFILE_ID not in DEVICE_CLASS:
DEVICE_CLASS[zll.PROFILE_ID] = {} DEVICE_CLASS[zll.PROFILE_ID] = {}
if zha.PROFILE_ID not in REMOTE_DEVICE_TYPES:
REMOTE_DEVICE_TYPES[zha.PROFILE_ID] = []
if zll.PROFILE_ID not in REMOTE_DEVICE_TYPES:
REMOTE_DEVICE_TYPES[zll.PROFILE_ID] = []
def get_ezsp_radio(): def get_ezsp_radio():
import bellows.ezsp import bellows.ezsp
from bellows.zigbee.application import ControllerApplication from bellows.zigbee.application import ControllerApplication
@ -101,15 +109,21 @@ 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) CHANNEL_ONLY_CLUSTERS.append(zcl.clusters.general.Basic.cluster_id)
NO_SENSOR_CLUSTERS.append( CHANNEL_ONLY_CLUSTERS.append(
zcl.clusters.general.PowerConfiguration.cluster_id) zcl.clusters.general.PowerConfiguration.cluster_id)
NO_SENSOR_CLUSTERS.append(zcl.clusters.lightlink.LightLink.cluster_id) CHANNEL_ONLY_CLUSTERS.append(zcl.clusters.lightlink.LightLink.cluster_id)
OUTPUT_CHANNEL_ONLY_CLUSTERS.append(zcl.clusters.general.Scenes.cluster_id)
BINDABLE_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id) BINDABLE_CLUSTERS.append(zcl.clusters.general.LevelControl.cluster_id)
BINDABLE_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id) BINDABLE_CLUSTERS.append(zcl.clusters.general.OnOff.cluster_id)
BINDABLE_CLUSTERS.append(zcl.clusters.lighting.Color.cluster_id) BINDABLE_CLUSTERS.append(zcl.clusters.lighting.Color.cluster_id)
INPUT_BIND_ONLY_CLUSTERS.append(
zcl.clusters.lightlink.LightLink.cluster_id
)
DEVICE_CLASS[zha.PROFILE_ID].update({ DEVICE_CLASS[zha.PROFILE_ID].update({
zha.DeviceType.SMART_PLUG: SWITCH, zha.DeviceType.SMART_PLUG: SWITCH,
zha.DeviceType.LEVEL_CONTROLLABLE_OUTPUT: LIGHT, zha.DeviceType.LEVEL_CONTROLLABLE_OUTPUT: LIGHT,
@ -181,6 +195,21 @@ def establish_device_mappings():
SMARTTHINGS_ACCELERATION_CLUSTER: ACCELERATION, SMARTTHINGS_ACCELERATION_CLUSTER: ACCELERATION,
}) })
zhap = zha.PROFILE_ID
REMOTE_DEVICE_TYPES[zhap].append(zha.DeviceType.NON_COLOR_SCENE_CONTROLLER)
REMOTE_DEVICE_TYPES[zhap].append(zha.DeviceType.NON_COLOR_CONTROLLER)
REMOTE_DEVICE_TYPES[zhap].append(zha.DeviceType.COLOR_SCENE_CONTROLLER)
REMOTE_DEVICE_TYPES[zhap].append(zha.DeviceType.COLOR_CONTROLLER)
REMOTE_DEVICE_TYPES[zhap].append(zha.DeviceType.REMOTE_CONTROL)
REMOTE_DEVICE_TYPES[zhap].append(zha.DeviceType.SCENE_SELECTOR)
zllp = zll.PROFILE_ID
REMOTE_DEVICE_TYPES[zllp].append(zll.DeviceType.COLOR_CONTROLLER)
REMOTE_DEVICE_TYPES[zllp].append(zll.DeviceType.COLOR_SCENE_CONTROLLER)
REMOTE_DEVICE_TYPES[zllp].append(zll.DeviceType.CONTROLLER)
REMOTE_DEVICE_TYPES[zllp].append(zll.DeviceType.SCENE_CONTROLLER)
REMOTE_DEVICE_TYPES[zllp].append(zll.DeviceType.CONTROL_BRIDGE)
CLUSTER_REPORT_CONFIGS.update({ CLUSTER_REPORT_CONFIGS.update({
zcl.clusters.general.Alarms.cluster_id: [], zcl.clusters.general.Alarms.cluster_id: [],
zcl.clusters.general.Basic.cluster_id: [], zcl.clusters.general.Basic.cluster_id: [],