Добавляем в ZHA поддержку _TZE200_libht6ua и _TZE284_libht6ua

На руках 2 двигателя для рулонных штор. С виду они одинаковые, но по факту разные ревизии: _TZE200_libht6ua и _TZE284_libht6ua

В zigbee2mqtt вроде как поддержка есть, но у меня стоит ZHA и тут они хоть и добавляются, но без управления.

Решаем через quirks

/homeassistant/configuration.yaml

zha:
enable_quirks: true
custom_quirks_path: /config/custom_zha_quirks

/homeassistant/custom_zha_quirks/ts0601_libht6ua_cover.py

from zigpy.profiles import zha
from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time

from zhaquirks import Bus
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
TuyaManufacturerWindowCover,
TuyaManufCluster,
TuyaPowerConfigurationCluster,
TuyaWindowCover,
TuyaWindowCoverControl,
)

BATTERY_CHANGE = «battery_change»
TUYA_DP_TYPE_VALUE = 0x0200
ZEMISMART_BATTERY_ATTR = 0x0D

class TuyaManufacturerWindowCoverBattery(TuyaManufacturerWindowCover):
«»»Tuya cover manufacturer cluster with battery DP support.»»»

def handle_cluster_request(self, hdr, args, *, dst_addressing=None):
super().handle_cluster_request(hdr, args, dst_addressing=dst_addressing)

battery_attr = getattr(self.endpoint.device, «tuya_battery_attr», None)
if battery_attr is None or not args:
return

tuya_payload = args[0]
if hasattr(tuya_payload, «param»):
tuya_payload = tuya_payload.param

command_id = getattr(tuya_payload, «command_id», None)
data = getattr(tuya_payload, «data», None)

if (
command_id == TUYA_DP_TYPE_VALUE + battery_attr
and isinstance(data, (list, tuple))
and len(data) >= 5
):
self.endpoint.device.battery_bus.listener_event(
BATTERY_CHANGE,
data[4],
)

class TuyaWindowCoverBattery(TuyaWindowCover):
«»»Base Tuya cover with battery bus support.»»»

tuya_battery_attr = None

def __init__(self, *args, **kwargs):
self.battery_bus = Bus()
super().__init__(*args, **kwargs)

class TuyaMoesCover0601_libht6ua(TuyaWindowCoverBattery):
«»»Local quirk for TS0601 _TZE200_libht6ua roller blind motor.»»»

tuya_cover_inverted_by_default = True
tuya_battery_attr = ZEMISMART_BATTERY_ATTR

signature = {
MODELS_INFO: [
(«_TZE200_libht6ua», «TS0601»),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [
Ota.cluster_id,
Time.cluster_id,
],
}
},
}

replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaManufacturerWindowCoverBattery,
TuyaWindowCoverControl,
TuyaPowerConfigurationCluster,
],
OUTPUT_CLUSTERS: [
Ota.cluster_id,
Time.cluster_id,
],
}
}
}

class TuyaMoesCover0601_libht6ua_v284(TuyaWindowCover):
«»»Local quirk for TS0601 _TZE284_libht6ua roller blind motor.»»»

tuya_cover_inverted_by_default = True

signature = {
MODELS_INFO: [
(«_TZE284_libht6ua», «TS0601»),
],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
0xED00,
TuyaManufCluster.cluster_id,
],
OUTPUT_CLUSTERS: [
Ota.cluster_id,
Time.cluster_id,
],
}
},
}

replacement = {
ENDPOINTS: {
1: {
DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaManufacturerWindowCover,
TuyaWindowCoverControl,
],
OUTPUT_CLUSTERS: [
Ota.cluster_id,
Time.cluster_id,
],
}
}
}

Перезагружаем Home Assistant и видим, что у нас появились кнопки для управления шторами. Если не появились, то на странице устройства нажимаем Перенастроить.