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 quirk for the TS0207 rain solar sensor #3475

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
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
186 changes: 186 additions & 0 deletions zhaquirks/tuya/ts0207.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
"""Quirk for TS0207 rain sensors."""

import logging

from zigpy.profiles import zha
from zigpy.quirks import CustomCluster
import zigpy.types as t
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 import Bus
from zhaquirks.const import (
DEVICE_TYPE,
ENDPOINTS,
INPUT_CLUSTERS,
MODELS_INFO,
OUTPUT_CLUSTERS,
PROFILE_ID,
)
from zhaquirks.tuya import (
DPToAttributeMapping,
EnchantedDevice,
TuyaNoBindPowerConfigurationCluster,
)
from zhaquirks.tuya.mcu import TuyaMCUCluster

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)
if attrid == 0xEF65 and value is not None and value >= 0:
self.endpoint.device.illuminance_bus.listener_event(

Check warning on line 91 in zhaquirks/tuya/ts0207.py

View check run for this annotation

Codecov / codecov/patch

zhaquirks/tuya/ts0207.py#L89-L91

Added lines #L89 - L91 were not covered by tests
"illuminance_reported", value
)


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...")
self.endpoint.device.illuminance_bus.add_listener(self)
LOGGER.debug("TuyaIlluminanceCluster attached to listener.")

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

calibrated_value = value * self.CALIBRATION_FACTOR
self._update_attribute(self.MEASURED_VALUE_ATTR_ID, calibrated_value)
LOGGER.debug("measured_value attribute updated.")

Check warning on line 122 in zhaquirks/tuya/ts0207.py

View check run for this annotation

Codecov / codecov/patch

zhaquirks/tuya/ts0207.py#L120-L122

Added lines #L120 - L122 were not covered by tests


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("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,
],
},
},
}