forked from zigpy/zha-device-handlers
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add quirk for the TS0207 rain solar sensor
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
1 parent
728ee42
commit 32b99cc
Showing
1 changed file
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
], | ||
}, | ||
}, | ||
} |