Duke Yin's Technology database

土壤湿度传感器COOLO CS-201Z改ZHA

最近买了一个Zigbee土壤湿度传感器,连接HomeAssistant的Zigbee2MQTT之后显示型号为 COOLO CS-201Z

在Zigbee2MQTT连接的情况下工作很正常,能正确读取目前的:

  • 空气湿度
  • 土壤湿度
  • 温度
  • 是否干燥(布尔值)
  • 电池电量

但是我的Home Assistant主要使用Zigbee Home Automation(ZHA)连接Zigbee设备,CS-201Z在ZHA连接下传感器只有:

  • 土壤湿度
  • 温度
  • 电池电量

这3个实体读数。

我不太想为了这一个传感器改成Zigbee2MQTT,家里只有一个Zigbee网关,Zigbee2MQTT和ZHA只能2选1,不能同时存在。

所以我问了GPT老师,发现在ZHA上也能把缺失的数据给弄出来,稍微有点麻烦,以下是步骤:

创建Quirk目录

mkdir -p /config/custom_zha_quirks
ls -ld /config/custom_zha_quirks

正常应看到类似:

drwxr-xr-x 2 root root 4096 Oct 5 15:47 /config/custom_zha_quirks

创建 Quirk 文件

cat > /config/custom_zha_quirks/zbeacon_zg303z.py <<'PY'
import zigpy.types as t
from zigpy.zcl.clusters.measurement import RelativeHumidity

from zhaquirks.builder import (
    PERCENTAGE,
    BinarySensorDeviceClass,
    EntityType,
    NumberDeviceClass,
    UnitOfTemperature,
    UnitOfTime,
)
from zhaquirks.const import BatterySize
from zhaquirks.tuya.builder import TuyaQuirkBuilder
from zhaquirks.tuya.mcu import TuyaMCUCluster


class TuyaTempUnitConvert(t.enum8):
    Celsius = 0x00
    Fahrenheit = 0x01


(
    TuyaQuirkBuilder("Zbeacon", "ZG-303Z")
    .tuya_soil_moisture(dp_id=3)
    .tuya_temperature(dp_id=5, scale=10)
    .tuya_enum(
        dp_id=9,
        attribute_name="display_unit",
        enum_class=TuyaTempUnitConvert,
        translation_key="display_unit",
        fallback_name="Display unit",
    )
    .tuya_battery(dp_id=15, battery_type=BatterySize.AAA)
    .tuya_number(
        dp_id=102,
        attribute_name="soil_moisture_offset",
        type=t.int8s,
        unit=PERCENTAGE,
        min_value=-30,
        max_value=30,
        step=1,
        device_class=NumberDeviceClass.MOISTURE,
        translation_key="soil_moisture_offset",
        fallback_name="Soil moisture offset",
    )
    .tuya_number(
        dp_id=104,
        attribute_name="temperature_offset",
        type=t.int8s,
        unit=UnitOfTemperature.CELSIUS,
        min_value=-2,
        max_value=2,
        step=0.1,
        multiplier=0.1,
        device_class=NumberDeviceClass.TEMPERATURE_DELTA,
        translation_key="temperature_offset",
        fallback_name="Temperature offset",
    )
    .tuya_number(
        dp_id=105,
        attribute_name="humidity_offset",
        type=t.int8s,
        unit=PERCENTAGE,
        min_value=-30,
        max_value=30,
        step=1,
        device_class=NumberDeviceClass.HUMIDITY,
        translation_key="humidity_offset",
        fallback_name="Humidity offset",
    )
    .tuya_dp_attribute(
        dp_id=106,
        attribute_name="moisture",
        converter=lambda x: not bool(x),
    )
    .binary_sensor(
        attribute_name="moisture",
        cluster_id=TuyaMCUCluster.cluster_id,
        endpoint_id=1,
        entity_type=EntityType.STANDARD,
        device_class=BinarySensorDeviceClass.MOISTURE,
        fallback_name="Moisture",
    )
    .removes(RelativeHumidity.cluster_id)
    .adds_endpoint(2)
    .tuya_humidity(dp_id=109, endpoint_id=2)
    .tuya_number(
        dp_id=110,
        attribute_name="alarm_soil_moisture_min",
        type=t.uint16_t,
        unit=PERCENTAGE,
        min_value=0,
        max_value=100,
        step=1,
        device_class=NumberDeviceClass.MOISTURE,
        translation_key="alarm_soil_moisture_min",
        fallback_name="Alarm soil moisture min",
    )
    .tuya_number(
        dp_id=111,
        attribute_name="temperature_report_interval",
        type=t.uint16_t,
        unit=UnitOfTime.SECONDS,
        min_value=5,
        max_value=3600,
        step=1,
        device_class=NumberDeviceClass.DURATION,
        translation_key="temperature_report_interval",
        fallback_name="Temperature report interval",
    )
    .tuya_number(
        dp_id=112,
        attribute_name="soil_moisture_report_interval",
        type=t.uint16_t,
        unit=UnitOfTime.SECONDS,
        min_value=5,
        max_value=3600,
        step=1,
        device_class=NumberDeviceClass.DURATION,
        translation_key="soil_moisture_report_interval",
        fallback_name="Soil moisture report interval",
    )
    .tuya_enchantment(data_query_spell=True)
    .skip_configuration()
    .add_to_registry()
)
PY

python -m py_compile /config/custom_zha_quirks/zbeacon_zg303z.py
echo $?

最后如果显示:

0

说明 Python 语法检查通过。

配置 ZHA 加载自定义 Quirk

现在 /config/configuration.yaml 中没有 zha:,因此直接追加:

cat >> /config/configuration.yaml <<'EOF'

zha:
  custom_quirks_path: /config/custom_zha_quirks
EOF

然后检查:

tail -n 6 /config/configuration.yaml

应该看到:

zha:
  custom_quirks_path: /config/custom_zha_quirks

检查 Home Assistant 配置

ha core check

如果正常通过检查,应该看到

Command completed successfully

重启 Home Assistant

ha core restart

等待几分钟,重启成功后,设置 → 设备与服务 → Zigbee Home Automation → 你的 COOLO CS-201Z / Zbeacon ZG-303Z 重写配置旁边的按钮 → 下载诊断

重点是我们要确认其中是否已经从:

"quirk_applied": false

变成:

"quirk_applied": true

以及 quirk_class 是否已经不再是:

zigpy.device.Device

确认 Home Assistant 实体

现在先进入:

设置 → 设备与服务 → 设备 → Zbeacon ZG-303Z

看看实体列表。

重点确认是否能看到这些实体:

  1. Soil Moisture
  2. Temperature
  3. Humidity
  4. Battery
  5. Moisture
  6. Soil moisture offset
  7. Temperature offset
  8. Humidity offset
  9. Alarm soil moisture min
  10. Temperature report interval
  11. Soil moisture report interval
  12. Display unit

其中 Moisture 是原来 Zigbee2MQTT 中类似 Dry 的那个状态传感器,只是 ZHA 当前使用了 moisture 这个设备类别名称。

哪些文件被修改或创建

Quirk 文件位于:

/config/custom_zha_quirks/zbeacon_zg303z.py

主配置文件被修改

/config/configuration.yaml

以后如果发生误删、升级故障或者需要单独迁移 HA,建议额外把这个文件保存一份。

也就是保留:

/config/custom_zha_quirks/zbeacon_zg303z.py

以及记录

zha:
  custom_quirks_path: /config/custom_zha_quirks

# # # #

发布评论

评论

标注 * 的为必填项。