Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tuya rain sensor _TZ3210_tgvtvdoc #3656

Merged
merged 8 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions tests/test_tuya_rain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Test for Tuya rain sensor."""

import pytest
from zigpy.zcl.clusters.security import IasZone

from tests.common import ClusterListener
import zhaquirks

zhaquirks.setup()


ZCL_TUYA_RAIN_MV_01 = b"\tp\x02\x00\x02i\x02\x00\x04\x00\x00\x00\x20" # 32mv
ZCL_TUYA_RAIN_MV_02 = b"\tp\x02\x00\x02i\x02\x00\x04\x00\x00\x01\xf4" # 500mv


@pytest.mark.parametrize(
"frame,value,rain_detected",
[(ZCL_TUYA_RAIN_MV_01, 32, False), (ZCL_TUYA_RAIN_MV_02, 500, True)],
)
async def test_rain_sensor_state_report(
zigpy_device_from_v2_quirk, frame, value, rain_detected
):
"""Test tuya rain sensor standard state reporting."""

rain_dev = zigpy_device_from_v2_quirk("_TZ3210_tgvtvdoc", "TS0207")
tuya_cluster = rain_dev.endpoints[1].tuya_manufacturer

ias_listener = ClusterListener(rain_dev.endpoints[1].ias_zone)
rain_listener = ClusterListener(tuya_cluster)

hdr, args = tuya_cluster.deserialize(frame)
tuya_cluster.handle_message(hdr, args)

assert len(ias_listener.cluster_commands) == 0
assert len(ias_listener.attribute_updates) == 1
assert ias_listener.attribute_updates[0][0] == IasZone.AttributeDefs.zone_status.id
assert ias_listener.attribute_updates[0][1] == (
IasZone.ZoneStatus.Alarm_1 if rain_detected else 0
)

assert len(rain_listener.cluster_commands) == 1
assert len(rain_listener.attribute_updates) == 1
assert rain_listener.attribute_updates[0][0] == 0xEF69
assert rain_listener.attribute_updates[0][1] == value
93 changes: 93 additions & 0 deletions zhaquirks/tuya/tuya_rain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Quirk for TS0207 rain sensors."""

from zigpy.quirks.v2.homeassistant import LIGHT_LUX, EntityType, UnitOfElectricPotential
from zigpy.quirks.v2.homeassistant.binary_sensor import BinarySensorDeviceClass
from zigpy.quirks.v2.homeassistant.sensor import SensorDeviceClass, SensorStateClass
import zigpy.types as t
from zigpy.zcl.clusters.security import IasZone

from zhaquirks.tuya import (
TUYA_CLUSTER_ID,
BatterySize,
DPToAttributeMapping,
TuyaLocalCluster,
)
from zhaquirks.tuya.builder import TuyaQuirkBuilder
from zhaquirks.tuya.mcu import TuyaMCUCluster


class TuyaIasZone(IasZone, TuyaLocalCluster):
"""IAS Zone for rain sensors."""

_CONSTANT_ATTRIBUTES = {
IasZone.AttributeDefs.zone_type.id: IasZone.ZoneType.Water_Sensor
}


(
TuyaQuirkBuilder("_TZ3210_tgvtvdoc", "TS0207")
.tuya_battery(
dp_id=4, battery_type=BatterySize.Other, battery_qty=1, battery_voltage=30
)
.tuya_illuminance(dp_id=101)
.tuya_sensor(
dp_id=102,
attribute_name="average_light_intensity_20mins",
type=t.uint32_t,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DURATION,
unit=LIGHT_LUX,
entity_type=EntityType.DIAGNOSTIC,
translation_key="average_light_intensity_20mins",
fallback_name="Average light intensity last 20 min",
)
.tuya_sensor(
dp_id=103,
attribute_name="todays_max_light_intensity",
type=t.uint32_t,
state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.DURATION,
unit=LIGHT_LUX,
entity_type=EntityType.DIAGNOSTIC,
translation_key="todays_max_light_intensity",
fallback_name="Today's max light intensity",
)
.tuya_binary_sensor(
dp_id=104,
attribute_name="cleaning_reminder",
device_class=BinarySensorDeviceClass.PROBLEM,
fallback_name="Cleaning reminder",
)
.tuya_dp_multi(
dp_id=105,
attribute_mapping=[
DPToAttributeMapping(
ep_attribute=TuyaIasZone.ep_attribute,
attribute_name=TuyaIasZone.AttributeDefs.zone_status.name,
converter=lambda x: IasZone.ZoneStatus.Alarm_1 if x > 100 else 0,
),
DPToAttributeMapping(
ep_attribute=TuyaMCUCluster.ep_attribute,
attribute_name="rain_intensity",
),
],
)
.tuya_attribute(
dp_id=105,
attribute_name="rain_intensity",
type=t.uint32_t,
is_manufacturer_specific=True,
)
.sensor(
"rain_intensity",
TUYA_CLUSTER_ID,
device_class=SensorDeviceClass.VOLTAGE,
state_class=SensorStateClass.MEASUREMENT,
unit=UnitOfElectricPotential.MILLIVOLT,
entity_type=EntityType.STANDARD,
fallback_name="Rain intensity",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we might wanna change this to "Voltage" to reflect the HA name, since that's what HA will use.
But not important for now, as it won't be used.

)
.adds(TuyaIasZone)
.skip_configuration()
.add_to_registry()
)
Loading