Skip to content

Commit

Permalink
Add quirk for the TS0207 rain solar sensor
Browse files Browse the repository at this point in the history
Adds a quirk that fixes this device by changing the "moving" and
"closed" sensors to the rain and illuminance sensors that the device
actually has.

Most of the credit for this code goes to @hadeshimself from
[this](zigpy#3249 (comment))
comment.
  • Loading branch information
aarongorka committed Oct 30, 2024
1 parent 728ee42 commit 32b99cc
Showing 1 changed file with 197 additions and 0 deletions.
197 changes: 197 additions & 0 deletions zhaquirks/tuya/ts0207.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
"""Quirk for TS0207 rain sensors."""

import zigpy.types as t
from zigpy.profiles import zha
from zigpy.quirks import CustomCluster
from zigpy.zcl.clusters.general import (
Basic,
Groups,
Identify,
OnOff,
Ota,
PowerConfiguration,
Scenes,
Time,
)
from zigpy.zcl.clusters.lightlink import LightLink
from zigpy.zcl.clusters.measurement import IlluminanceMeasurement
from zigpy.zcl.clusters.security import IasZone
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya.mcu import TuyaMCUCluster
from zhaquirks.tuya import (
DPToAttributeMapping,
EnchantedDevice,
TuyaNoBindPowerConfigurationCluster,
)
from zhaquirks import Bus
import logging

ZONE_TYPE = 0x0001

LOGGER = logging.getLogger(__name__)


class TuyaSolarRainSensorCluster(TuyaMCUCluster, CustomCluster):
"""Tuya manufacturer cluster."""

attributes = TuyaMCUCluster.attributes.copy()
attributes.update(
{
0xEF65: ("light_intensity", t.uint32_t, True),
0xEF66: ("average_light_intensity_20mins", t.uint32_t, True),
0xEF67: ("todays_max_light_intensity", t.uint32_t, True),
0xEF68: ("cleaning_reminder", t.Bool, True),
0xEF69: ("rain_sensor_voltage", t.uint32_t, True),
}
)

dp_to_attribute: dict[int, DPToAttributeMapping] = {
101: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"light_intensity",
),
102: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"average_light_intensity_20mins",
),
103: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"todays_max_light_intensity",
),
104: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"cleaning_reminder",
),
105: DPToAttributeMapping(
TuyaMCUCluster.ep_attribute,
"rain_sensor_voltage",
),
}

data_point_handlers = {
101: "_dp_2_attr_update",
102: "_dp_2_attr_update",
103: "_dp_2_attr_update",
104: "_dp_2_attr_update",
105: "_dp_2_attr_update",
}

def _update_attribute(self, attrid, value):
super()._update_attribute(attrid, value)
LOGGER.debug(f"Updating attr {hex(attrid)}...")
if attrid == 0xEF65 and value is not None and value >= 0:
LOGGER.debug(
f"Creating illuminance_reported event on illuminance_bus with attrid {hex(attrid)}..."
)
try:
self.endpoint.device.illuminance_bus.listener_event(
"illuminance_reported", value
)
except:
LOGGER.exception("Failed to create illuminance_reported event...")
pass


class TuyaIlluminanceCluster(CustomCluster, IlluminanceMeasurement):
"""Tuya Illuminance cluster."""

cluster_id = IlluminanceMeasurement.cluster_id
MEASURED_VALUE_ATTR_ID = 0x0000
MIN_MEASURED_VALUE_ATTR_ID = 0x0001
MAX_MEASURED_VALUE_ATTR_ID = 0x0001

# The value gets passed through `round(pow(10, ((value - 1) / 10000)))` by ZHA
# https://github.com/zigpy/zha/blob/927e249134c87bd7805139c8fb61e048593ec155/zha/application/platforms/sensor/__init__.py#L782C9-L782C53
CALIBRATION_FACTOR = 7 # very approximate adjustment, do not expect this to be accurate

def __init__(self, *args, **kwargs):
"""Init."""
super().__init__(*args, **kwargs)
LOGGER.debug("Attaching TuyaIlluminanceCluster to listener...")
try:
self.endpoint.device.illuminance_bus.add_listener(self)
LOGGER.debug("TuyaIlluminanceCluster attached to listener.")
except:
LOGGER.exception("Failed to add listener.")
pass

def illuminance_reported(self, value):
"""Illuminance reported."""

calibrated_value = value * self.CALIBRATION_FACTOR
LOGGER.debug(
f"Received illuminance_reported event with value {value}, updating attribute with calibrated value {calibrated_value}..."
)
self._update_attribute(self.MEASURED_VALUE_ATTR_ID, calibrated_value)
LOGGER.debug(f"measured_value attribute updated.")


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

_CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Water_Sensor}


class TuyaSolarRainSensor(EnchantedDevice):
"""TS0207 Rain sensor quirk."""

def __init__(self, *args, **kwargs) -> None:
"""Init."""
self.illuminance_bus = Bus()
LOGGER.debug(f"Bus created.")
super().__init__(*args, **kwargs)

signature = {
MODELS_INFO: [("_TZ3210_tgvtvdoc", "TS0207")],
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
IasZone.cluster_id,
TuyaMCUCluster.cluster_id,
],
OUTPUT_CLUSTERS: [
Identify.cluster_id,
Groups.cluster_id,
OnOff.cluster_id,
Time.cluster_id,
Ota.cluster_id,
LightLink.cluster_id,
],
},
},
}

replacement = {
ENDPOINTS: {
1: {
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
INPUT_CLUSTERS: [
Basic.cluster_id,
Groups.cluster_id,
Scenes.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaIasZone,
TuyaSolarRainSensorCluster,
TuyaIlluminanceCluster,
],
OUTPUT_CLUSTERS: [
Time.cluster_id,
Ota.cluster_id,
],
},
},
}

0 comments on commit 32b99cc

Please sign in to comment.