1
1
"""Tests for Sinope."""
2
2
3
+ from unittest import mock
4
+
3
5
import pytest
6
+ from zigpy .device import Device
7
+ import zigpy .types as t
8
+ from zigpy .zcl import foundation
4
9
from zigpy .zcl .clusters .general import DeviceTemperature
5
10
from zigpy .zcl .clusters .measurement import FlowMeasurement
6
11
7
12
from tests .common import ClusterListener
8
- import zhaquirks .sinope .switch
13
+ import zhaquirks
14
+ from zhaquirks .const import COMMAND_BUTTON_DOUBLE , COMMAND_BUTTON_HOLD
15
+ from zhaquirks .sinope import SINOPE_MANUFACTURER_CLUSTER_ID
16
+ from zhaquirks .sinope .light import (
17
+ SinopeTechnologieslight ,
18
+ SinopeTechnologiesManufacturerCluster ,
19
+ )
20
+ from zhaquirks .sinope .switch import SinopeTechnologiesCalypso , SinopeTechnologiesValveG2
9
21
10
22
zhaquirks .setup ()
11
23
24
+ ButtonAction = SinopeTechnologiesManufacturerCluster .Action
25
+
26
+ SINOPE_MANUFACTURER_ID = 4508 # 0x119C
12
27
13
- @pytest .mark .parametrize ("quirk" , (zhaquirks .sinope .switch .SinopeTechnologiesCalypso ,))
28
+
29
+ @pytest .mark .parametrize ("quirk" , (SinopeTechnologiesCalypso ,))
14
30
async def test_sinope_device_temp (zigpy_device_from_quirk , quirk ):
15
31
"""Test that device temperature is multiplied."""
16
32
device = zigpy_device_from_quirk (quirk )
@@ -33,7 +49,7 @@ async def test_sinope_device_temp(zigpy_device_from_quirk, quirk):
33
49
assert dev_temp_listener .attribute_updates [1 ][1 ] == 25 # not modified
34
50
35
51
36
- @pytest .mark .parametrize ("quirk" , (zhaquirks . sinope . switch . SinopeTechnologiesValveG2 ,))
52
+ @pytest .mark .parametrize ("quirk" , (SinopeTechnologiesValveG2 ,))
37
53
async def test_sinope_flow_measurement (zigpy_device_from_quirk , quirk ):
38
54
"""Test that flow measurement measured value is divided."""
39
55
device = zigpy_device_from_quirk (quirk )
@@ -57,3 +73,133 @@ async def test_sinope_flow_measurement(zigpy_device_from_quirk, quirk):
57
73
== flow_measurement_other_attr_id
58
74
)
59
75
assert flow_measurement_listener .attribute_updates [1 ][1 ] == 25 # not modified
76
+
77
+
78
+ def _get_packet_data (
79
+ command : foundation .GeneralCommand ,
80
+ attr : foundation .Attribute | None = None ,
81
+ dirc : foundation .Direction = foundation .Direction .Server_to_Client ,
82
+ ) -> bytes :
83
+ hdr = foundation .ZCLHeader .general (
84
+ 1 , command , SINOPE_MANUFACTURER_ID , dirc
85
+ ).serialize ()
86
+ if attr is not None :
87
+ cmd = foundation .GENERAL_COMMANDS [command ].schema ([attr ]).serialize ()
88
+ else :
89
+ cmd = b""
90
+ return t .SerializableBytes (hdr + cmd ).serialize ()
91
+
92
+
93
+ @pytest .mark .parametrize ("quirk" , (SinopeTechnologieslight ,))
94
+ @pytest .mark .parametrize (
95
+ "press_type,exp_event" ,
96
+ (
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 ),
103
+ # Should gracefully handle broken actions.
104
+ (t .uint8_t (0x00 ), None ),
105
+ ),
106
+ )
107
+ async def test_sinope_light_switch (
108
+ zigpy_device_from_quirk , quirk , press_type , exp_event
109
+ ):
110
+ """Test that button presses are sent as events."""
111
+ device : Device = zigpy_device_from_quirk (quirk )
112
+ cluster_id = SINOPE_MANUFACTURER_CLUSTER_ID
113
+ endpoint_id = 1
114
+
115
+ class Listener :
116
+ zha_send_event = mock .MagicMock ()
117
+
118
+ cluster_listener = Listener ()
119
+ device .endpoints [endpoint_id ].in_clusters [cluster_id ].add_listener (cluster_listener )
120
+
121
+ attr = foundation .Attribute (
122
+ attrid = 0x54 , # "action_report" attribute
123
+ value = foundation .TypeValue (
124
+ type = t .enum8 (0x30 ),
125
+ value = press_type ,
126
+ ),
127
+ )
128
+ data = _get_packet_data (foundation .GeneralCommand .Report_Attributes , attr )
129
+ device .handle_message (260 , cluster_id , endpoint_id , endpoint_id , data )
130
+
131
+ if exp_event is None :
132
+ assert cluster_listener .zha_send_event .call_count == 0
133
+ else :
134
+ assert cluster_listener .zha_send_event .call_count == 1
135
+ assert cluster_listener .zha_send_event .call_args == mock .call (
136
+ exp_event ,
137
+ {
138
+ "attribute_id" : 84 ,
139
+ "attribute_name" : "action_report" ,
140
+ "value" : press_type .value ,
141
+ },
142
+ )
143
+
144
+
145
+ @pytest .mark .parametrize ("quirk" , (SinopeTechnologieslight ,))
146
+ async def test_sinope_light_switch_non_action_report (zigpy_device_from_quirk , quirk ):
147
+ """Test commands not handled by custom handler.
148
+
149
+ Make sure that non attribute report commands and attribute reports that don't
150
+ concern action_report are passed through to base class.
151
+ """
152
+
153
+ device : Device = zigpy_device_from_quirk (quirk )
154
+ cluster_id = SINOPE_MANUFACTURER_CLUSTER_ID
155
+ endpoint_id = 1
156
+
157
+ class Listener :
158
+ zha_send_event = mock .MagicMock ()
159
+
160
+ cluster_listener = Listener ()
161
+ device .endpoints [endpoint_id ].in_clusters [cluster_id ].add_listener (cluster_listener )
162
+
163
+ # read attributes general command
164
+ data = _get_packet_data (foundation .GeneralCommand .Read_Attributes )
165
+ device .handle_message (260 , cluster_id , endpoint_id , endpoint_id , data )
166
+ # no ZHA events emitted because we only handle Report_Attributes
167
+ assert cluster_listener .zha_send_event .call_count == 0
168
+
169
+ # report attributes command, but not "action_report"
170
+ attr = foundation .Attribute (
171
+ attrid = 0x10 , # "on_intensity" attribute
172
+ value = foundation .TypeValue (
173
+ type = t .int16s (0x29 ), value = t .int16s (50 )
174
+ ), # 0x29 = t.int16s
175
+ )
176
+ data = _get_packet_data (foundation .GeneralCommand .Report_Attributes , attr )
177
+ device .handle_message (260 , cluster_id , endpoint_id , endpoint_id , data )
178
+ # ZHA event emitted because we pass non "action_report"
179
+ # reports to the base class handler.
180
+ assert cluster_listener .zha_send_event .call_count == 1
181
+
182
+
183
+ @pytest .mark .parametrize ("quirk" , (SinopeTechnologieslight ,))
184
+ async def test_sinope_light_switch_reporting (zigpy_device_from_quirk , quirk ):
185
+ """Test that configuring reporting for action_report works."""
186
+ device : Device = zigpy_device_from_quirk (quirk )
187
+
188
+ manu_cluster = device .endpoints [1 ].in_clusters [SINOPE_MANUFACTURER_CLUSTER_ID ]
189
+
190
+ request_patch = mock .patch ("zigpy.zcl.Cluster.request" , mock .AsyncMock ())
191
+ bind_patch = mock .patch ("zigpy.zcl.Cluster.bind" , mock .AsyncMock ())
192
+
193
+ with request_patch as request_mock , bind_patch as bind_mock :
194
+ request_mock .return_value = (foundation .Status .SUCCESS , "done" )
195
+
196
+ await manu_cluster .bind ()
197
+ await manu_cluster .configure_reporting (
198
+ SinopeTechnologiesManufacturerCluster .AttributeDefs .action_report .id ,
199
+ 3600 ,
200
+ 10800 ,
201
+ 1 ,
202
+ )
203
+
204
+ assert len (request_mock .mock_calls ) == 1
205
+ assert len (bind_mock .mock_calls ) == 1
0 commit comments