Skip to content

Commit

Permalink
Add TuyaNoBindPowerConfigurationCluster for Tuya remotes to stop ba…
Browse files Browse the repository at this point in the history
…ttery drain (#2288)

* Add `TuyaNoBindPowerConfigurationCluster` for Tuya remotes

This prevents battery drain

* Add test to confirm that power config isn't bound
  • Loading branch information
TheJulianJES authored Mar 24, 2023
1 parent 8e91d98 commit b729f10
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 37 deletions.
30 changes: 30 additions & 0 deletions tests/test_tuya.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from zigpy.quirks import CustomDevice, get_device
import zigpy.types as t
from zigpy.zcl import foundation
from zigpy.zcl.clusters.general import PowerConfiguration

import zhaquirks
from zhaquirks.const import (
Expand All @@ -24,6 +25,7 @@
)
from zhaquirks.tuya import Data, TuyaManufClusterAttributes, TuyaNewManufCluster
import zhaquirks.tuya.sm0202_motion
import zhaquirks.tuya.ts0041
import zhaquirks.tuya.ts0042
import zhaquirks.tuya.ts0043
import zhaquirks.tuya.ts0501_fan_switch
Expand Down Expand Up @@ -1547,3 +1549,31 @@ async def test_sm0202_motion_sensor_signature(assert_signature_matches_quirk):
"class": "zhaquirks.tuya.lh992zb.TuyaMotionSM0202",
}
assert_signature_matches_quirk(zhaquirks.tuya.sm0202_motion.SM0202Motion, signature)


@pytest.mark.parametrize(
"quirk",
(zhaquirks.tuya.ts0041.TuyaSmartRemote0041TOPlusA,),
)
async def test_power_config_no_bind(zigpy_device_from_quirk, quirk):
"""Test that the power configuration cluster is not bound and no attribute reporting is set up."""

device = zigpy_device_from_quirk(quirk)
power_cluster = device.endpoints[1].power

request_patch = mock.patch("zigpy.zcl.Cluster.request", mock.AsyncMock())
bind_patch = mock.patch("zigpy.zcl.Cluster.bind", mock.AsyncMock())

with request_patch as request_mock, bind_patch as bind_mock:
request_mock.return_value = (foundation.Status.SUCCESS, "done")

await power_cluster.bind()
await power_cluster.configure_reporting(
PowerConfiguration.attributes_by_name["battery_percentage_remaining"].id,
3600,
10800,
1,
)

assert len(request_mock.mock_calls) == 0
assert len(bind_mock.mock_calls) == 0
12 changes: 12 additions & 0 deletions zhaquirks/tuya/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,18 @@ def update_attribute(self, attr_name: str, value: Any) -> None:
return self._update_attribute(attr.id, value)


class TuyaNoBindPowerConfigurationCluster(PowerConfiguration, CustomCluster):
"""PowerConfiguration cluster that prevents setting up binding/attribute reports in order to stop battery drain."""

async def bind(self):
"""Prevent bind."""
return (foundation.Status.SUCCESS,)

async def _configure_reporting(self, *args, **kwargs): # pylint: disable=W0221
"""Prevent remote configure reporting."""
return (foundation.ConfigureReportingResponse.deserialize(b"\x00")[0],)


class TuyaPowerConfigurationCluster(PowerConfiguration, TuyaLocalCluster):
"""PowerConfiguration cluster for battery-operated thermostats."""

Expand Down
12 changes: 8 additions & 4 deletions zhaquirks/tuya/ts0041.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
PROFILE_ID,
SHORT_PRESS,
)
from zhaquirks.tuya import TuyaSmartRemoteOnOffCluster, TuyaZBE000Cluster
from zhaquirks.tuya import (
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
TuyaZBE000Cluster,
)


class TuyaSmartRemote0041TO(CustomDevice):
Expand Down Expand Up @@ -47,7 +51,7 @@ class TuyaSmartRemote0041TO(CustomDevice):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [Ota.cluster_id, Time.cluster_id],
Expand Down Expand Up @@ -89,7 +93,7 @@ class TuyaSmartRemote0041TI(CustomDevice):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
Time.cluster_id,
],
Expand Down Expand Up @@ -163,7 +167,7 @@ class TuyaSmartRemote0041TOPlusA(CustomDevice):
DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaZBE000Cluster,
],
OUTPUT_CLUSTERS: [
Expand Down
16 changes: 10 additions & 6 deletions zhaquirks/tuya/ts0042.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@
PROFILE_ID,
SHORT_PRESS,
)
from zhaquirks.tuya import TuyaSmartRemoteOnOffCluster, TuyaZBE000Cluster
from zhaquirks.tuya import (
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
TuyaZBE000Cluster,
)


class TuyaSmartRemote0042TI(CustomDevice):
Expand Down Expand Up @@ -59,7 +63,7 @@ class TuyaSmartRemote0042TI(CustomDevice):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
Time.cluster_id,
],
Expand All @@ -69,7 +73,7 @@ class TuyaSmartRemote0042TI(CustomDevice):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand Down Expand Up @@ -123,7 +127,7 @@ class TuyaSmartRemote0042TO(CustomDevice):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
Expand All @@ -132,7 +136,7 @@ class TuyaSmartRemote0042TO(CustomDevice):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand Down Expand Up @@ -207,7 +211,7 @@ class TuyaSmartRemote0042TOPlusA(CustomDevice):
DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaZBE000Cluster,
],
OUTPUT_CLUSTERS: [
Expand Down
26 changes: 15 additions & 11 deletions zhaquirks/tuya/ts0043.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
PROFILE_ID,
SHORT_PRESS,
)
from zhaquirks.tuya import TuyaSmartRemoteOnOffCluster, TuyaZBE000Cluster
from zhaquirks.tuya import (
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
TuyaZBE000Cluster,
)


class TuyaSmartRemote0043TI(CustomDevice):
Expand Down Expand Up @@ -70,7 +74,7 @@ class TuyaSmartRemote0043TI(CustomDevice):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
Time.cluster_id,
],
Expand All @@ -80,7 +84,7 @@ class TuyaSmartRemote0043TI(CustomDevice):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -89,7 +93,7 @@ class TuyaSmartRemote0043TI(CustomDevice):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand Down Expand Up @@ -156,7 +160,7 @@ class TuyaSmartRemote0043TO(CustomDevice):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
Expand All @@ -165,7 +169,7 @@ class TuyaSmartRemote0043TO(CustomDevice):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -174,7 +178,7 @@ class TuyaSmartRemote0043TO(CustomDevice):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand Down Expand Up @@ -252,7 +256,7 @@ class TuyaSmartRemote0043TOPlusA(CustomDevice):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
TuyaZBE000Cluster,
],
Expand All @@ -262,7 +266,7 @@ class TuyaSmartRemote0043TOPlusA(CustomDevice):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -271,7 +275,7 @@ class TuyaSmartRemote0043TOPlusA(CustomDevice):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand Down Expand Up @@ -340,7 +344,7 @@ class TuyaSmartRemote0043TOPlusB(CustomDevice):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
],
OUTPUT_CLUSTERS: [
Time.cluster_id,
Expand Down
32 changes: 18 additions & 14 deletions zhaquirks/tuya/ts0044.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
PROFILE_ID,
SHORT_PRESS,
)
from zhaquirks.tuya import TuyaSmartRemoteOnOffCluster, TuyaZBE000Cluster
from zhaquirks.tuya import (
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
TuyaZBE000Cluster,
)


class Tuya4ButtonTriggers:
Expand Down Expand Up @@ -100,7 +104,7 @@ class TuyaSmartRemote0044TI(CustomDevice, Tuya4ButtonTriggers):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
Time.cluster_id,
],
Expand All @@ -110,7 +114,7 @@ class TuyaSmartRemote0044TI(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -119,7 +123,7 @@ class TuyaSmartRemote0044TI(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -128,7 +132,7 @@ class TuyaSmartRemote0044TI(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand Down Expand Up @@ -193,7 +197,7 @@ class TuyaSmartRemote0044TO(CustomDevice, Tuya4ButtonTriggers):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
Expand All @@ -202,7 +206,7 @@ class TuyaSmartRemote0044TO(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -211,7 +215,7 @@ class TuyaSmartRemote0044TO(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -220,7 +224,7 @@ class TuyaSmartRemote0044TO(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand Down Expand Up @@ -286,7 +290,7 @@ class TuyaSmartRemote0044TOPlusA(CustomDevice, Tuya4ButtonTriggers):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
TuyaZBE000Cluster,
],
Expand All @@ -296,7 +300,7 @@ class TuyaSmartRemote0044TOPlusA(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -305,7 +309,7 @@ class TuyaSmartRemote0044TOPlusA(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand All @@ -314,7 +318,7 @@ class TuyaSmartRemote0044TOPlusA(CustomDevice, Tuya4ButtonTriggers):
PROFILE_ID: zha.PROFILE_ID,
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
TuyaSmartRemoteOnOffCluster,
],
OUTPUT_CLUSTERS: [],
Expand Down Expand Up @@ -382,7 +386,7 @@ class TuyaSmartRemote0044TOPlusB(CustomDevice, Tuya4ButtonTriggers):
DEVICE_TYPE: zha.DeviceType.REMOTE_CONTROL,
INPUT_CLUSTERS: [
Basic.cluster_id,
PowerConfiguration.cluster_id,
TuyaNoBindPowerConfigurationCluster,
],
OUTPUT_CLUSTERS: [
Time.cluster_id,
Expand Down
Loading

0 comments on commit b729f10

Please sign in to comment.