Skip to content

Commit 5ee1785

Browse files
committed
zhaquirks SINOPE SW2500ZB: Added support for both press and release button events in order to properly support short and long press events.
Also added the pressed button in the event arguments to simplify automations processing it.
1 parent 78b2c87 commit 5ee1785

File tree

6 files changed

+144
-51
lines changed

6 files changed

+144
-51
lines changed

tests/test_sinope.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@
1111

1212
from tests.common import ClusterListener
1313
import zhaquirks
14-
from zhaquirks.const import COMMAND_BUTTON_DOUBLE, COMMAND_BUTTON_HOLD
14+
from zhaquirks.const import (
15+
COMMAND_M_INITIAL_PRESS,
16+
COMMAND_M_LONG_RELEASE,
17+
COMMAND_M_MULTI_PRESS_COMPLETE,
18+
COMMAND_M_SHORT_RELEASE,
19+
TURN_OFF,
20+
TURN_ON,
21+
)
1522
from zhaquirks.sinope import SINOPE_MANUFACTURER_CLUSTER_ID
1623
from zhaquirks.sinope.light import (
1724
SinopeTechnologieslight,
@@ -92,20 +99,22 @@ def _get_packet_data(
9299

93100
@pytest.mark.parametrize("quirk", (SinopeTechnologieslight,))
94101
@pytest.mark.parametrize(
95-
"press_type,exp_event",
102+
"press_type,button,exp_event",
96103
(
97-
(ButtonAction.Single_off, None),
98-
(ButtonAction.Single_on, None),
99-
(ButtonAction.Double_on, COMMAND_BUTTON_DOUBLE),
100-
(ButtonAction.Double_off, COMMAND_BUTTON_DOUBLE),
101-
(ButtonAction.Long_on, COMMAND_BUTTON_HOLD),
102-
(ButtonAction.Long_off, COMMAND_BUTTON_HOLD),
104+
(ButtonAction.Single_off, TURN_OFF, COMMAND_M_INITIAL_PRESS),
105+
(ButtonAction.Single_on, TURN_ON, COMMAND_M_INITIAL_PRESS),
106+
(ButtonAction.Single_release_off, TURN_OFF, COMMAND_M_SHORT_RELEASE),
107+
(ButtonAction.Single_release_on, TURN_ON, COMMAND_M_SHORT_RELEASE),
108+
(ButtonAction.Double_on, TURN_ON, COMMAND_M_MULTI_PRESS_COMPLETE),
109+
(ButtonAction.Double_off, TURN_OFF, COMMAND_M_MULTI_PRESS_COMPLETE),
110+
(ButtonAction.Long_on, TURN_ON, COMMAND_M_LONG_RELEASE),
111+
(ButtonAction.Long_off, TURN_OFF, COMMAND_M_LONG_RELEASE),
103112
# Should gracefully handle broken actions.
104-
(t.uint8_t(0x00), None),
113+
(t.uint8_t(0x00), None, None),
105114
),
106115
)
107116
async def test_sinope_light_switch(
108-
zigpy_device_from_quirk, quirk, press_type, exp_event
117+
zigpy_device_from_quirk, quirk, press_type, button, exp_event
109118
):
110119
"""Test that button presses are sent as events."""
111120
device: Device = zigpy_device_from_quirk(quirk)
@@ -137,6 +146,7 @@ class Listener:
137146
{
138147
"attribute_id": 84,
139148
"attribute_name": "action_report",
149+
"button": button,
140150
"value": press_type.value,
141151
},
142152
)

zhaquirks/sinope/__init__.py

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@
77
ARGS,
88
ATTRIBUTE_ID,
99
ATTRIBUTE_NAME,
10+
BUTTON,
1011
CLUSTER_ID,
1112
COMMAND,
12-
COMMAND_BUTTON_DOUBLE,
13-
COMMAND_BUTTON_HOLD,
14-
COMMAND_BUTTON_SINGLE,
13+
COMMAND_M_INITIAL_PRESS,
14+
COMMAND_M_LONG_RELEASE,
15+
COMMAND_M_MULTI_PRESS_COMPLETE,
16+
COMMAND_M_SHORT_RELEASE,
1517
DOUBLE_PRESS,
1618
ENDPOINT_ID,
17-
LONG_PRESS,
19+
LONG_RELEASE,
1820
SHORT_PRESS,
21+
SHORT_RELEASE,
1922
TURN_OFF,
2023
TURN_ON,
2124
VALUE,
@@ -29,38 +32,90 @@
2932
(SHORT_PRESS, TURN_ON): {
3033
ENDPOINT_ID: 1,
3134
CLUSTER_ID: 65281,
32-
COMMAND: COMMAND_BUTTON_SINGLE,
33-
ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 2},
35+
COMMAND: COMMAND_M_INITIAL_PRESS,
36+
ARGS: {
37+
ATTRIBUTE_ID: 84,
38+
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
39+
BUTTON: TURN_ON,
40+
VALUE: 1,
41+
},
3442
},
3543
(SHORT_PRESS, TURN_OFF): {
3644
ENDPOINT_ID: 1,
3745
CLUSTER_ID: 65281,
38-
COMMAND: COMMAND_BUTTON_SINGLE,
39-
ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 18},
46+
COMMAND: COMMAND_M_INITIAL_PRESS,
47+
ARGS: {
48+
ATTRIBUTE_ID: 84,
49+
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
50+
BUTTON: TURN_OFF,
51+
VALUE: 17,
52+
},
53+
},
54+
(SHORT_RELEASE, TURN_ON): {
55+
ENDPOINT_ID: 1,
56+
CLUSTER_ID: 65281,
57+
COMMAND: COMMAND_M_SHORT_RELEASE,
58+
ARGS: {
59+
ATTRIBUTE_ID: 84,
60+
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
61+
BUTTON: TURN_ON,
62+
VALUE: 2,
63+
},
64+
},
65+
(SHORT_RELEASE, TURN_OFF): {
66+
ENDPOINT_ID: 1,
67+
CLUSTER_ID: 65281,
68+
COMMAND: COMMAND_M_SHORT_RELEASE,
69+
ARGS: {
70+
ATTRIBUTE_ID: 84,
71+
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
72+
BUTTON: TURN_OFF,
73+
VALUE: 18,
74+
},
4075
},
4176
(DOUBLE_PRESS, TURN_ON): {
4277
ENDPOINT_ID: 1,
4378
CLUSTER_ID: 65281,
44-
COMMAND: COMMAND_BUTTON_DOUBLE,
45-
ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 4},
79+
COMMAND: COMMAND_M_MULTI_PRESS_COMPLETE,
80+
ARGS: {
81+
ATTRIBUTE_ID: 84,
82+
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
83+
BUTTON: TURN_ON,
84+
VALUE: 4,
85+
},
4686
},
4787
(DOUBLE_PRESS, TURN_OFF): {
4888
ENDPOINT_ID: 1,
4989
CLUSTER_ID: 65281,
50-
COMMAND: COMMAND_BUTTON_DOUBLE,
51-
ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 20},
90+
COMMAND: COMMAND_M_MULTI_PRESS_COMPLETE,
91+
ARGS: {
92+
ATTRIBUTE_ID: 84,
93+
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
94+
BUTTON: TURN_OFF,
95+
VALUE: 20,
96+
},
5297
},
53-
(LONG_PRESS, TURN_ON): {
98+
(LONG_RELEASE, TURN_ON): {
5499
ENDPOINT_ID: 1,
55100
CLUSTER_ID: 65281,
56-
COMMAND: COMMAND_BUTTON_HOLD,
57-
ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 3},
101+
COMMAND: COMMAND_M_LONG_RELEASE,
102+
ARGS: {
103+
ATTRIBUTE_ID: 84,
104+
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
105+
BUTTON: TURN_ON,
106+
VALUE: 3,
107+
},
58108
},
59-
(LONG_PRESS, TURN_OFF): {
109+
(LONG_RELEASE, TURN_OFF): {
60110
ENDPOINT_ID: 1,
61111
CLUSTER_ID: 65281,
62-
COMMAND: COMMAND_BUTTON_HOLD,
63-
ARGS: {ATTRIBUTE_ID: 84, ATTRIBUTE_NAME: ATTRIBUTE_ACTION, VALUE: 19},
112+
COMMAND: COMMAND_M_LONG_RELEASE,
113+
ARGS: {
114+
ATTRIBUTE_ID: 84,
115+
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
116+
BUTTON: TURN_OFF,
117+
VALUE: 19,
118+
},
64119
},
65120
}
66121

zhaquirks/sinope/light.py

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,24 @@
2929
from zhaquirks.const import (
3030
ATTRIBUTE_ID,
3131
ATTRIBUTE_NAME,
32-
COMMAND_BUTTON_DOUBLE,
33-
COMMAND_BUTTON_HOLD,
32+
BUTTON,
33+
COMMAND_M_INITIAL_PRESS,
34+
COMMAND_M_LONG_RELEASE,
35+
COMMAND_M_MULTI_PRESS_COMPLETE,
36+
COMMAND_M_SHORT_RELEASE,
3437
DEVICE_TYPE,
3538
ENDPOINTS,
3639
INPUT_CLUSTERS,
3740
MODELS_INFO,
3841
OUTPUT_CLUSTERS,
3942
PROFILE_ID,
43+
TURN_OFF,
44+
TURN_ON,
4045
VALUE,
4146
ZHA_SEND_EVENT,
4247
)
43-
from zhaquirks.sinope import (
48+
49+
from . import (
4450
ATTRIBUTE_ACTION,
4551
LIGHT_DEVICE_TRIGGERS,
4652
SINOPE,
@@ -192,28 +198,51 @@ def handle_cluster_general_request(
192198
)
193199

194200
value = attr.value.value
201+
202+
action, button = self._get_command_from_action(self.Action(value))
203+
if not action or not button:
204+
return
205+
195206
event_args = {
196207
ATTRIBUTE_ID: 84,
197208
ATTRIBUTE_NAME: ATTRIBUTE_ACTION,
209+
BUTTON: button,
198210
VALUE: value.value,
199211
}
200-
action = self._get_command_from_action(self.Action(value))
201-
if not action:
202-
return
212+
213+
self.debug(
214+
"SINOPE ZHA_SEND_EVENT action: '%s' event_args: %s",
215+
self.Action(value),
216+
event_args,
217+
)
218+
203219
self.listener_event(ZHA_SEND_EVENT, action, event_args)
204220

205-
def _get_command_from_action(self, action: ButtonAction) -> str | None:
206-
# const lookup = {2: 'up_single', 3: 'up_hold', 4: 'up_double',
207-
# 18: 'down_single', 19: 'down_hold', 20: 'down_double'};
221+
def _get_command_from_action(
222+
self, action: ButtonAction
223+
) -> tuple[str | None, str | None]:
224+
# const lookup = {1: 'up_single', 2: 'up_single_released', 3: 'up_hold', 4: 'up_double',
225+
# 17: 'down_single, 18: 'down_single_released', 19: 'down_hold', 20: 'down_double'};
208226
match action:
209-
case self.Action.Single_off | self.Action.Single_on:
210-
return None
211-
case self.Action.Double_off | self.Action.Double_on:
212-
return COMMAND_BUTTON_DOUBLE
213-
case self.Action.Long_off | self.Action.Long_on:
214-
return COMMAND_BUTTON_HOLD
227+
case self.Action.Single_off:
228+
return COMMAND_M_INITIAL_PRESS, TURN_OFF
229+
case self.Action.Single_on:
230+
return COMMAND_M_INITIAL_PRESS, TURN_ON
231+
case self.Action.Single_release_off:
232+
return COMMAND_M_SHORT_RELEASE, TURN_OFF
233+
case self.Action.Single_release_on:
234+
return COMMAND_M_SHORT_RELEASE, TURN_ON
235+
case self.Action.Double_off:
236+
return COMMAND_M_MULTI_PRESS_COMPLETE, TURN_OFF
237+
case self.Action.Double_on:
238+
return COMMAND_M_MULTI_PRESS_COMPLETE, TURN_ON
239+
case self.Action.Long_off:
240+
return COMMAND_M_LONG_RELEASE, TURN_OFF
241+
case self.Action.Long_on:
242+
return COMMAND_M_LONG_RELEASE, TURN_ON
215243
case _:
216-
return None
244+
self.debug("SINOPE unhandled action: %s", action)
245+
return None, None
217246

218247

219248
class LightManufacturerCluster(EventableCluster, SinopeTechnologiesManufacturerCluster):

zhaquirks/sinope/sensor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
OUTPUT_CLUSTERS,
3131
PROFILE_ID,
3232
)
33-
from zhaquirks.sinope import SINOPE, SINOPE_MANUFACTURER_CLUSTER_ID
33+
34+
from . import SINOPE, SINOPE_MANUFACTURER_CLUSTER_ID
3435

3536

3637
class LeakStatus(t.enum8):

zhaquirks/sinope/switch.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,8 @@
4343
OUTPUT_CLUSTERS,
4444
PROFILE_ID,
4545
)
46-
from zhaquirks.sinope import (
47-
SINOPE,
48-
SINOPE_MANUFACTURER_CLUSTER_ID,
49-
CustomDeviceTemperatureCluster,
50-
)
46+
47+
from . import SINOPE, SINOPE_MANUFACTURER_CLUSTER_ID, CustomDeviceTemperatureCluster
5148

5249

5350
class KeypadLock(t.enum8):

zhaquirks/sinope/thermostat.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
OUTPUT_CLUSTERS,
3434
PROFILE_ID,
3535
)
36-
from zhaquirks.sinope import SINOPE, SINOPE_MANUFACTURER_CLUSTER_ID
36+
37+
from . import SINOPE, SINOPE_MANUFACTURER_CLUSTER_ID
3738

3839

3940
class KeypadLock(t.enum8):

0 commit comments

Comments
 (0)