采购Zigbee设备,Tuya涂鸦等国产品牌有许多廉价的选择,但同时一些国内厂商私自增加自有协议也让使用者有一言难尽的感觉(各自为政的小心思,格局能不能打开一点?)。特别是接入开放的ZHA后,一些功能会莫名其妙丢失,导致设备无法正常使用。
比方说,窗帘电机接入ZHA后,缺少该有的“开”、“关”、“停”。又比方说,三开模块接入ZHA后,在ZHA中明明显示有三个开关,但只要开关其中任意一个,另两个就会跳到相同的开关状态。而物理按动开关时又不会受此影响。
这个模块接入ZHA后,显示的设备信息为“TS0003”,制造商代码为“_TZ3000_odzoiovu”。接入ZHA时非常顺利,并且作为一个Zigbee 3.0设备,还可以自动成为Zigbee网络中的中继器(Router),自动中继Zigbee信号,也可以手动从此设备来添加新的Zigbee设备。
这个模块本身的功能是把三开开关变成智能开关。但当此模块接入ZHA所出现的“同开同关”问题,就导致智能性大打折扣了。
幸好经过网上一番搜索,在这个网页中找到了解决方法(法语,来自sylvainbecu):自定义Quirks。
大致操作方法如下:
1、在configuration.yaml同目录下新增“zha_quirks”文件夹;
2、编辑configuration.yaml,加入以下内容:
zha:
enable_quirks: true
custom_quirks_path: /config/zha_quirks
3、新建纯文本文件,并命名为ts000xv2.py,文件内容如下:
import asyncio
from zigpy.profiles import zha
from zigpy.quirks import CustomDevice
from zigpy.zcl.clusters.general import (
Basic,
GreenPowerProxy,
Groups,
Identify,
OnOff,
Ota,
Scenes,
Time,
)
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODEL,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
TuyaZBE000Cluster,
TuyaZBExternalSwitchTypeCluster,
TuyaZBOnOffAttributeCluster,
# modifs GP: ajout
TuyaZBElectricalMeasurement,
TuyaZBMeteringCluster,
# fin ajout
)
class TuyaMagicSpell(CustomDevice):
"""Cast a magic spell for Tuya devices that needed."""
def __init__(self, *args, **kwargs):
"""Initialize with task."""
super().__init__(*args, **kwargs)
# cast_tuya_magic_spell(self, tries=3)
self._init_plug_task = asyncio.create_task(self.spell())
async def spell(self) -> None:
"""Initialize device so that all endpoints become available."""
basic_cluster = self.endpoints[1].in_clusters[0]
self.debug("Device class will cast Tuya Magic Spell")
attr_to_read = [4, 0, 1, 5, 7, 0xFFFE]
await basic_cluster.read_attributes(attr_to_read)
class Switch_2G_GPP(TuyaMagicSpell, CustomDevice):
"""Tuya 2 gang switch module with restore power state support."""
signature = {
MODEL: "TS0002",
ENDPOINTS: {
# <SimpleDescriptor endpoint=1 profile=260 device_type=256
# device_version=1
# modifs GP : input_clusters=[3, 4, 5, 6, 1794, 2820,57344, 57345, 0]
# old : input_clusters=[0, 3, 4, 5, 6, 57344, 57345]
# output_clusters=[10, 25]>
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#Modifs GP: ajout
TuyaZBMeteringCluster.cluster_id,
TuyaZBElectricalMeasurement.cluster_id,
#fin ajout
TuyaZBE000Cluster.cluster_id,
TuyaZBExternalSwitchTypeCluster.cluster_id,
#modif GP: basic déplacé à la fin
Basic.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
# <SimpleDescriptor endpoint=2 profile=260 device_type=256
# device_version=1
# input_clusters=[4, 5, 6, 57345]
# output_clusters=[]>
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster.cluster_id,
],
OUTPUT_CLUSTERS: [],
},
# <SimpleDescriptor endpoint=242 profile=41440 device_type=97
# device_version=0
# input_clusters=[]
# output_clusters=[33]>
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
TuyaZBE000Cluster,
TuyaZBExternalSwitchTypeCluster,
#Modifs GP: ajout
TuyaZBMeteringCluster.cluster_id,
TuyaZBElectricalMeasurement.cluster_id,
#fin ajout
Basic.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster,
],
OUTPUT_CLUSTERS: [],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
class Switch_3G_GPP(TuyaMagicSpell, CustomDevice):
"""Tuya 3 gang switch module with restore power state support."""
signature = {
MODEL: "TS0003",
ENDPOINTS: {
# <SimpleDescriptor endpoint=1 profile=260 device_type=256
# device_version=1
# modifs GP : input_clusters=[3, 4, 5, 6, 1794, 2820,57344, 57345, 0]
# old : input_clusters=[0, 3, 4, 5, 6, 57344, 57345]
# output_clusters=[10, 25]>
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#Modifs GP: ajout
TuyaZBMeteringCluster.cluster_id,
TuyaZBElectricalMeasurement.cluster_id,
#fin ajout
TuyaZBE000Cluster.cluster_id,
TuyaZBExternalSwitchTypeCluster.cluster_id,
#modif GP: basic déplacé à la fin
Basic.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
# <SimpleDescriptor endpoint=2 profile=260 device_type=256
# device_version=1
# input_clusters=[4, 5, 6, 57345]
# output_clusters=[]>
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster.cluster_id,
],
OUTPUT_CLUSTERS: [],
},
# <SimpleDescriptor endpoint=3 profile=260 device_type=256
# device_version=1
# input_clusters=[4, 5, 6, 57345]
# output_clusters=[]>
3: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster.cluster_id,
],
OUTPUT_CLUSTERS: [],
},
# <SimpleDescriptor endpoint=242 profile=41440 device_type=97
# device_version=0
# input_clusters=[]
# output_clusters=[33]>
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
TuyaZBE000Cluster,
TuyaZBExternalSwitchTypeCluster,
#Modifs GP: ajout
TuyaZBMeteringCluster.cluster_id,
TuyaZBElectricalMeasurement.cluster_id,
#fin ajout
Basic.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster,
],
OUTPUT_CLUSTERS: [],
},
3: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster,
],
OUTPUT_CLUSTERS: [],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
class Switch_4G_GPP(TuyaMagicSpell, CustomDevice):
"""Tuya 4 gang switch module with restore power state support."""
signature = {
MODEL: "TS0004",
ENDPOINTS: {
# <SimpleDescriptor endpoint=1 profile=260 device_type=256
# device_version=1
# modifs GP : input_clusters=[3, 4, 5, 6, 1794, 2820,57344, 57345, 0]
# old : input_clusters=[0, 3, 4, 5, 6, 57344, 57345]
# output_clusters=[10, 25]>
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#Modifs GP: ajout
TuyaZBMeteringCluster.cluster_id,
TuyaZBElectricalMeasurement.cluster_id,
#fin ajout
TuyaZBE000Cluster.cluster_id,
TuyaZBExternalSwitchTypeCluster.cluster_id,
#modif GP: basic déplacé à la fin
Basic.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
# <SimpleDescriptor endpoint=2 profile=260 device_type=256
# device_version=1
# input_clusters=[4, 5, 6, 57345]
# output_clusters=[]>
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster.cluster_id,
],
OUTPUT_CLUSTERS: [],
},
# <SimpleDescriptor endpoint=3 profile=260 device_type=256
# device_version=1
# input_clusters=[4, 5, 6, 57345]
# output_clusters=[]>
3: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster.cluster_id,
],
OUTPUT_CLUSTERS: [],
},
# <SimpleDescriptor endpoint=4 profile=260 device_type=256
# device_version=1
# input_clusters=[4, 5, 6, 57345]
# output_clusters=[]>
4: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
OnOff.cluster_id,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster.cluster_id,
],
OUTPUT_CLUSTERS: [],
},
# <SimpleDescriptor endpoint=242 profile=41440 device_type=97
# device_version=0
# input_clusters=[]
# output_clusters=[33]>
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
replacement = {
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Identify.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
TuyaZBE000Cluster,
TuyaZBExternalSwitchTypeCluster,
#Modifs GP: ajout
TuyaZBMeteringCluster.cluster_id,
TuyaZBElectricalMeasurement.cluster_id,
#fin ajout
Basic.cluster_id,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
},
2: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster,
],
OUTPUT_CLUSTERS: [],
},
3: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster,
],
OUTPUT_CLUSTERS: [],
},
4: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
INPUT_CLUSTERS: [
Groups.cluster_id,
Scenes.cluster_id,
TuyaZBOnOffAttributeCluster,
#modif GP: ligne suivante supprimée
#TuyaZBExternalSwitchTypeCluster,
],
OUTPUT_CLUSTERS: [],
},
242: {
PROFILE_ID: 41440,
DEVICE_TYPE: 97,
INPUT_CLUSTERS: [],
OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
},
},
}
4、将ts000xv2.py上传至/config/zha_quirks下;
5、重启HA;
6、打开HA的设备与服务,在Zigbee Home Automation中找到并删除所有信息为“TS0003”,制造商代码为“_TZ3000_odzoiovu”的设备;
7、重新添加这些模块到ZHA。
至此,你可以发现所有的三开模块都已经能够在ZHA中对单个开关进行控制了。
另外,如果您的ZHA网络中有TZ3000_zmy4lslw.TS0002或者TZ3000_ltt60asa.TS0004且存在类似问题,不妨尝试上面第6~7步,应该也可以一并得到解决。
我也有类似问题,我米家4键蓝牙MESH开关接入HA后只显示第一个键位,其他三个无法显示,
蓝牙MESH开关与Zigbee机制不一样,这块我还没有研究过