lifx-async 4.3.6__py3-none-any.whl → 4.3.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lifx/devices/base.py +49 -6
- lifx/devices/hev.py +12 -2
- lifx/devices/infrared.py +5 -1
- lifx/devices/light.py +18 -4
- lifx/devices/matrix.py +18 -0
- lifx/devices/multizone.py +19 -5
- lifx/network/connection.py +4 -2
- {lifx_async-4.3.6.dist-info → lifx_async-4.3.8.dist-info}/METADATA +1 -1
- {lifx_async-4.3.6.dist-info → lifx_async-4.3.8.dist-info}/RECORD +11 -11
- {lifx_async-4.3.6.dist-info → lifx_async-4.3.8.dist-info}/WHEEL +0 -0
- {lifx_async-4.3.6.dist-info → lifx_async-4.3.8.dist-info}/licenses/LICENSE +0 -0
lifx/devices/base.py
CHANGED
|
@@ -19,7 +19,7 @@ from lifx.const import (
|
|
|
19
19
|
LIFX_LOCATION_NAMESPACE,
|
|
20
20
|
LIFX_UDP_PORT,
|
|
21
21
|
)
|
|
22
|
-
from lifx.exceptions import LifxDeviceNotFoundError
|
|
22
|
+
from lifx.exceptions import LifxDeviceNotFoundError, LifxUnsupportedCommandError
|
|
23
23
|
from lifx.network.connection import DeviceConnection
|
|
24
24
|
from lifx.products.registry import ProductInfo, get_product
|
|
25
25
|
from lifx.protocol import packets
|
|
@@ -152,6 +152,21 @@ class Device:
|
|
|
152
152
|
```
|
|
153
153
|
"""
|
|
154
154
|
|
|
155
|
+
@staticmethod
|
|
156
|
+
def _raise_if_unhandled(response: object) -> None:
|
|
157
|
+
"""Raise LifxUnsupportedCommandError if device doesn't support the command.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
response: The response from connection.request()
|
|
161
|
+
|
|
162
|
+
Raises:
|
|
163
|
+
LifxUnsupportedCommandError: If response is StateUnhandled or False
|
|
164
|
+
"""
|
|
165
|
+
if isinstance(response, packets.Device.StateUnhandled):
|
|
166
|
+
raise LifxUnsupportedCommandError(
|
|
167
|
+
f"Device does not support packet type {response.unhandled_type}"
|
|
168
|
+
)
|
|
169
|
+
|
|
155
170
|
def __init__(
|
|
156
171
|
self,
|
|
157
172
|
serial: str,
|
|
@@ -456,6 +471,7 @@ class Device:
|
|
|
456
471
|
LifxDeviceNotFoundError: If device is not connected
|
|
457
472
|
LifxTimeoutError: If device does not respond
|
|
458
473
|
LifxProtocolError: If response is invalid
|
|
474
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
459
475
|
|
|
460
476
|
Example:
|
|
461
477
|
```python
|
|
@@ -469,6 +485,7 @@ class Device:
|
|
|
469
485
|
"""
|
|
470
486
|
# Request automatically unpacks and decodes label
|
|
471
487
|
state = await self.connection.request(packets.Device.GetLabel())
|
|
488
|
+
self._raise_if_unhandled(state)
|
|
472
489
|
|
|
473
490
|
# Store label
|
|
474
491
|
self._label = state.label
|
|
@@ -492,6 +509,7 @@ class Device:
|
|
|
492
509
|
ValueError: If label is too long
|
|
493
510
|
LifxDeviceNotFoundError: If device is not connected
|
|
494
511
|
LifxTimeoutError: If device does not respond
|
|
512
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
495
513
|
|
|
496
514
|
Example:
|
|
497
515
|
```python
|
|
@@ -508,9 +526,10 @@ class Device:
|
|
|
508
526
|
label_bytes = label_bytes.ljust(32, b"\x00")
|
|
509
527
|
|
|
510
528
|
# Request automatically handles acknowledgement
|
|
511
|
-
await self.connection.request(
|
|
529
|
+
result = await self.connection.request(
|
|
512
530
|
packets.Device.SetLabel(label=label_bytes),
|
|
513
531
|
)
|
|
532
|
+
self._raise_if_unhandled(result)
|
|
514
533
|
|
|
515
534
|
# Update cached state
|
|
516
535
|
self._label = label
|
|
@@ -535,6 +554,7 @@ class Device:
|
|
|
535
554
|
LifxDeviceNotFoundError: If device is not connected
|
|
536
555
|
LifxTimeoutError: If device does not respond
|
|
537
556
|
LifxProtocolError: If response is invalid
|
|
557
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
538
558
|
|
|
539
559
|
Example:
|
|
540
560
|
```python
|
|
@@ -544,6 +564,7 @@ class Device:
|
|
|
544
564
|
"""
|
|
545
565
|
# Request automatically unpacks response
|
|
546
566
|
state = await self.connection.request(packets.Device.GetPower())
|
|
567
|
+
self._raise_if_unhandled(state)
|
|
547
568
|
|
|
548
569
|
# Power level is uint16 (0 or 65535)
|
|
549
570
|
_LOGGER.debug(
|
|
@@ -566,6 +587,7 @@ class Device:
|
|
|
566
587
|
ValueError: If integer value is not 0 or 65535
|
|
567
588
|
LifxDeviceNotFoundError: If device is not connected
|
|
568
589
|
LifxTimeoutError: If device does not respond
|
|
590
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
569
591
|
|
|
570
592
|
Example:
|
|
571
593
|
```python
|
|
@@ -591,9 +613,10 @@ class Device:
|
|
|
591
613
|
raise TypeError(f"Expected bool or int, got {type(level).__name__}")
|
|
592
614
|
|
|
593
615
|
# Request automatically handles acknowledgement
|
|
594
|
-
await self.connection.request(
|
|
616
|
+
result = await self.connection.request(
|
|
595
617
|
packets.Device.SetPower(level=power_level),
|
|
596
618
|
)
|
|
619
|
+
self._raise_if_unhandled(result)
|
|
597
620
|
|
|
598
621
|
_LOGGER.debug(
|
|
599
622
|
{
|
|
@@ -616,6 +639,7 @@ class Device:
|
|
|
616
639
|
LifxDeviceNotFoundError: If device is not connected
|
|
617
640
|
LifxTimeoutError: If device does not respond
|
|
618
641
|
LifxProtocolError: If response is invalid
|
|
642
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
619
643
|
|
|
620
644
|
Example:
|
|
621
645
|
```python
|
|
@@ -625,6 +649,7 @@ class Device:
|
|
|
625
649
|
"""
|
|
626
650
|
# Request automatically unpacks response
|
|
627
651
|
state = await self.connection.request(packets.Device.GetVersion())
|
|
652
|
+
self._raise_if_unhandled(state)
|
|
628
653
|
|
|
629
654
|
version = DeviceVersion(
|
|
630
655
|
vendor=state.vendor,
|
|
@@ -655,6 +680,7 @@ class Device:
|
|
|
655
680
|
LifxDeviceNotFoundError: If device is not connected
|
|
656
681
|
LifxTimeoutError: If device does not respond
|
|
657
682
|
LifxProtocolError: If response is invalid
|
|
683
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
658
684
|
|
|
659
685
|
Example:
|
|
660
686
|
```python
|
|
@@ -665,6 +691,7 @@ class Device:
|
|
|
665
691
|
"""
|
|
666
692
|
# Request automatically unpacks response
|
|
667
693
|
state = await self.connection.request(packets.Device.GetInfo()) # type: ignore
|
|
694
|
+
self._raise_if_unhandled(state)
|
|
668
695
|
|
|
669
696
|
info = DeviceInfo(time=state.time, uptime=state.uptime, downtime=state.downtime)
|
|
670
697
|
|
|
@@ -694,6 +721,7 @@ class Device:
|
|
|
694
721
|
LifxDeviceNotFoundError: If device is not connected
|
|
695
722
|
LifxTimeoutError: If device does not respond
|
|
696
723
|
LifxProtocolError: If response is invalid
|
|
724
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
697
725
|
|
|
698
726
|
Example:
|
|
699
727
|
```python
|
|
@@ -704,6 +732,7 @@ class Device:
|
|
|
704
732
|
"""
|
|
705
733
|
# Request WiFi info from device
|
|
706
734
|
state = await self.connection.request(packets.Device.GetWifiInfo())
|
|
735
|
+
self._raise_if_unhandled(state)
|
|
707
736
|
|
|
708
737
|
# Extract WiFi info from response
|
|
709
738
|
wifi_info = WifiInfo(signal=state.signal)
|
|
@@ -730,6 +759,7 @@ class Device:
|
|
|
730
759
|
LifxDeviceNotFoundError: If device is not connected
|
|
731
760
|
LifxTimeoutError: If device does not respond
|
|
732
761
|
LifxProtocolError: If response is invalid
|
|
762
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
733
763
|
|
|
734
764
|
Example:
|
|
735
765
|
```python
|
|
@@ -739,6 +769,7 @@ class Device:
|
|
|
739
769
|
"""
|
|
740
770
|
# Request automatically unpacks response
|
|
741
771
|
state = await self.connection.request(packets.Device.GetHostFirmware()) # type: ignore
|
|
772
|
+
self._raise_if_unhandled(state)
|
|
742
773
|
|
|
743
774
|
firmware = FirmwareInfo(
|
|
744
775
|
build=state.build,
|
|
@@ -778,6 +809,7 @@ class Device:
|
|
|
778
809
|
LifxDeviceNotFoundError: If device is not connected
|
|
779
810
|
LifxTimeoutError: If device does not respond
|
|
780
811
|
LifxProtocolError: If response is invalid
|
|
812
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
781
813
|
|
|
782
814
|
Example:
|
|
783
815
|
```python
|
|
@@ -787,6 +819,7 @@ class Device:
|
|
|
787
819
|
"""
|
|
788
820
|
# Request automatically unpacks response
|
|
789
821
|
state = await self.connection.request(packets.Device.GetWifiFirmware()) # type: ignore
|
|
822
|
+
self._raise_if_unhandled(state)
|
|
790
823
|
|
|
791
824
|
firmware = FirmwareInfo(
|
|
792
825
|
build=state.build,
|
|
@@ -822,6 +855,7 @@ class Device:
|
|
|
822
855
|
LifxDeviceNotFoundError: If device is not connected
|
|
823
856
|
LifxTimeoutError: If device does not respond
|
|
824
857
|
LifxProtocolError: If response is invalid
|
|
858
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
825
859
|
|
|
826
860
|
Example:
|
|
827
861
|
```python
|
|
@@ -832,6 +866,7 @@ class Device:
|
|
|
832
866
|
"""
|
|
833
867
|
# Request automatically unpacks response
|
|
834
868
|
state = await self.connection.request(packets.Device.GetLocation()) # type: ignore
|
|
869
|
+
self._raise_if_unhandled(state)
|
|
835
870
|
|
|
836
871
|
location = LocationInfo(
|
|
837
872
|
location=state.location,
|
|
@@ -873,6 +908,7 @@ class Device:
|
|
|
873
908
|
LifxDeviceNotFoundError: If device is not connected
|
|
874
909
|
LifxTimeoutError: If device does not respond
|
|
875
910
|
ValueError: If label is invalid
|
|
911
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
876
912
|
|
|
877
913
|
Example:
|
|
878
914
|
```python
|
|
@@ -967,11 +1003,12 @@ class Device:
|
|
|
967
1003
|
updated_at = int(time.time() * 1e9)
|
|
968
1004
|
|
|
969
1005
|
# Update this device
|
|
970
|
-
await self.connection.request(
|
|
1006
|
+
result = await self.connection.request(
|
|
971
1007
|
packets.Device.SetLocation(
|
|
972
1008
|
location=location_uuid_to_use, label=label_bytes, updated_at=updated_at
|
|
973
1009
|
),
|
|
974
1010
|
)
|
|
1011
|
+
self._raise_if_unhandled(result)
|
|
975
1012
|
|
|
976
1013
|
# Update cached state
|
|
977
1014
|
location_info = LocationInfo(
|
|
@@ -1003,6 +1040,7 @@ class Device:
|
|
|
1003
1040
|
LifxDeviceNotFoundError: If device is not connected
|
|
1004
1041
|
LifxTimeoutError: If device does not respond
|
|
1005
1042
|
LifxProtocolError: If response is invalid
|
|
1043
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
1006
1044
|
|
|
1007
1045
|
Example:
|
|
1008
1046
|
```python
|
|
@@ -1013,6 +1051,7 @@ class Device:
|
|
|
1013
1051
|
"""
|
|
1014
1052
|
# Request automatically unpacks response
|
|
1015
1053
|
state = await self.connection.request(packets.Device.GetGroup()) # type: ignore
|
|
1054
|
+
self._raise_if_unhandled(state)
|
|
1016
1055
|
|
|
1017
1056
|
group = GroupInfo(
|
|
1018
1057
|
group=state.group,
|
|
@@ -1054,6 +1093,7 @@ class Device:
|
|
|
1054
1093
|
LifxDeviceNotFoundError: If device is not connected
|
|
1055
1094
|
LifxTimeoutError: If device does not respond
|
|
1056
1095
|
ValueError: If label is invalid
|
|
1096
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
1057
1097
|
|
|
1058
1098
|
Example:
|
|
1059
1099
|
```python
|
|
@@ -1148,11 +1188,12 @@ class Device:
|
|
|
1148
1188
|
updated_at = int(time.time() * 1e9)
|
|
1149
1189
|
|
|
1150
1190
|
# Update this device
|
|
1151
|
-
await self.connection.request(
|
|
1191
|
+
result = await self.connection.request(
|
|
1152
1192
|
packets.Device.SetGroup(
|
|
1153
1193
|
group=group_uuid_to_use, label=label_bytes, updated_at=updated_at
|
|
1154
1194
|
),
|
|
1155
1195
|
)
|
|
1196
|
+
self._raise_if_unhandled(result)
|
|
1156
1197
|
|
|
1157
1198
|
# Update cached state
|
|
1158
1199
|
group_info = GroupInfo(
|
|
@@ -1181,6 +1222,7 @@ class Device:
|
|
|
1181
1222
|
Raises:
|
|
1182
1223
|
LifxDeviceNotFoundError: If device is not connected
|
|
1183
1224
|
LifxTimeoutError: If device does not respond
|
|
1225
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
1184
1226
|
|
|
1185
1227
|
Example:
|
|
1186
1228
|
```python
|
|
@@ -1194,9 +1236,10 @@ class Device:
|
|
|
1194
1236
|
comes back online and is discoverable again.
|
|
1195
1237
|
"""
|
|
1196
1238
|
# Send reboot request
|
|
1197
|
-
await self.connection.request(
|
|
1239
|
+
result = await self.connection.request(
|
|
1198
1240
|
packets.Device.SetReboot(),
|
|
1199
1241
|
)
|
|
1242
|
+
self._raise_if_unhandled(result)
|
|
1200
1243
|
_LOGGER.debug(
|
|
1201
1244
|
{
|
|
1202
1245
|
"class": "Device",
|
lifx/devices/hev.py
CHANGED
|
@@ -70,6 +70,7 @@ class HevLight(Light):
|
|
|
70
70
|
LifxDeviceNotFoundError: If device is not connected
|
|
71
71
|
LifxTimeoutError: If device does not respond
|
|
72
72
|
LifxProtocolError: If response is invalid
|
|
73
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
73
74
|
|
|
74
75
|
Example:
|
|
75
76
|
```python
|
|
@@ -82,6 +83,7 @@ class HevLight(Light):
|
|
|
82
83
|
"""
|
|
83
84
|
# Request HEV cycle state
|
|
84
85
|
state = await self.connection.request(packets.Light.GetHevCycle())
|
|
86
|
+
self._raise_if_unhandled(state)
|
|
85
87
|
|
|
86
88
|
# Create state object
|
|
87
89
|
cycle_state = HevCycleState(
|
|
@@ -116,6 +118,7 @@ class HevLight(Light):
|
|
|
116
118
|
ValueError: If duration is negative
|
|
117
119
|
LifxDeviceNotFoundError: If device is not connected
|
|
118
120
|
LifxTimeoutError: If device does not respond
|
|
121
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
119
122
|
|
|
120
123
|
Example:
|
|
121
124
|
```python
|
|
@@ -130,12 +133,13 @@ class HevLight(Light):
|
|
|
130
133
|
raise ValueError(f"Duration must be non-negative, got {duration_seconds}")
|
|
131
134
|
|
|
132
135
|
# Request automatically handles acknowledgement
|
|
133
|
-
await self.connection.request(
|
|
136
|
+
result = await self.connection.request(
|
|
134
137
|
packets.Light.SetHevCycle(
|
|
135
138
|
enable=enable,
|
|
136
139
|
duration_s=duration_seconds,
|
|
137
140
|
),
|
|
138
141
|
)
|
|
142
|
+
self._raise_if_unhandled(result)
|
|
139
143
|
|
|
140
144
|
_LOGGER.debug(
|
|
141
145
|
{
|
|
@@ -156,6 +160,7 @@ class HevLight(Light):
|
|
|
156
160
|
LifxDeviceNotFoundError: If device is not connected
|
|
157
161
|
LifxTimeoutError: If device does not respond
|
|
158
162
|
LifxProtocolError: If response is invalid
|
|
163
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
159
164
|
|
|
160
165
|
Example:
|
|
161
166
|
```python
|
|
@@ -166,6 +171,7 @@ class HevLight(Light):
|
|
|
166
171
|
"""
|
|
167
172
|
# Request HEV configuration
|
|
168
173
|
state = await self.connection.request(packets.Light.GetHevCycleConfiguration())
|
|
174
|
+
self._raise_if_unhandled(state)
|
|
169
175
|
|
|
170
176
|
# Create config object
|
|
171
177
|
config = HevConfig(
|
|
@@ -201,6 +207,7 @@ class HevLight(Light):
|
|
|
201
207
|
ValueError: If duration is negative
|
|
202
208
|
LifxDeviceNotFoundError: If device is not connected
|
|
203
209
|
LifxTimeoutError: If device does not respond
|
|
210
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
204
211
|
|
|
205
212
|
Example:
|
|
206
213
|
```python
|
|
@@ -212,12 +219,13 @@ class HevLight(Light):
|
|
|
212
219
|
raise ValueError(f"Duration must be non-negative, got {duration_seconds}")
|
|
213
220
|
|
|
214
221
|
# Request automatically handles acknowledgement
|
|
215
|
-
await self.connection.request(
|
|
222
|
+
result = await self.connection.request(
|
|
216
223
|
packets.Light.SetHevCycleConfiguration(
|
|
217
224
|
indication=indication,
|
|
218
225
|
duration_s=duration_seconds,
|
|
219
226
|
),
|
|
220
227
|
)
|
|
228
|
+
self._raise_if_unhandled(result)
|
|
221
229
|
|
|
222
230
|
# Update cached state
|
|
223
231
|
self._hev_config = HevConfig(indication=indication, duration_s=duration_seconds)
|
|
@@ -242,6 +250,7 @@ class HevLight(Light):
|
|
|
242
250
|
LifxDeviceNotFoundError: If device is not connected
|
|
243
251
|
LifxTimeoutError: If device does not respond
|
|
244
252
|
LifxProtocolError: If response is invalid
|
|
253
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
245
254
|
|
|
246
255
|
Example:
|
|
247
256
|
```python
|
|
@@ -254,6 +263,7 @@ class HevLight(Light):
|
|
|
254
263
|
"""
|
|
255
264
|
# Request last HEV result
|
|
256
265
|
state = await self.connection.request(packets.Light.GetLastHevCycleResult())
|
|
266
|
+
self._raise_if_unhandled(state)
|
|
257
267
|
|
|
258
268
|
# Store cached state
|
|
259
269
|
self._hev_result = state.result
|
lifx/devices/infrared.py
CHANGED
|
@@ -58,6 +58,7 @@ class InfraredLight(Light):
|
|
|
58
58
|
LifxDeviceNotFoundError: If device is not connected
|
|
59
59
|
LifxTimeoutError: If device does not respond
|
|
60
60
|
LifxProtocolError: If response is invalid
|
|
61
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
61
62
|
|
|
62
63
|
Example:
|
|
63
64
|
```python
|
|
@@ -68,6 +69,7 @@ class InfraredLight(Light):
|
|
|
68
69
|
"""
|
|
69
70
|
# Request infrared state
|
|
70
71
|
state = await self.connection.request(packets.Light.GetInfrared())
|
|
72
|
+
self._raise_if_unhandled(state)
|
|
71
73
|
|
|
72
74
|
# Convert from uint16 (0-65535) to float (0.0-1.0)
|
|
73
75
|
brightness = state.brightness / 65535.0
|
|
@@ -96,6 +98,7 @@ class InfraredLight(Light):
|
|
|
96
98
|
ValueError: If brightness is out of range
|
|
97
99
|
LifxDeviceNotFoundError: If device is not connected
|
|
98
100
|
LifxTimeoutError: If device does not respond
|
|
101
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
99
102
|
|
|
100
103
|
Example:
|
|
101
104
|
```python
|
|
@@ -115,9 +118,10 @@ class InfraredLight(Light):
|
|
|
115
118
|
brightness_u16 = max(0, min(65535, int(round(brightness * 65535))))
|
|
116
119
|
|
|
117
120
|
# Request automatically handles acknowledgement
|
|
118
|
-
await self.connection.request(
|
|
121
|
+
result = await self.connection.request(
|
|
119
122
|
packets.Light.SetInfrared(brightness=brightness_u16),
|
|
120
123
|
)
|
|
124
|
+
self._raise_if_unhandled(result)
|
|
121
125
|
|
|
122
126
|
# Update cached state
|
|
123
127
|
self._infrared = brightness
|
lifx/devices/light.py
CHANGED
|
@@ -83,6 +83,7 @@ class Light(Device):
|
|
|
83
83
|
LifxDeviceNotFoundError: If device is not connected
|
|
84
84
|
LifxTimeoutError: If device does not respond
|
|
85
85
|
LifxProtocolError: If response is invalid
|
|
86
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
86
87
|
|
|
87
88
|
Example:
|
|
88
89
|
```python
|
|
@@ -92,6 +93,7 @@ class Light(Device):
|
|
|
92
93
|
"""
|
|
93
94
|
# Request automatically unpacks response and decodes labels
|
|
94
95
|
state = await self.connection.request(packets.Light.GetColor())
|
|
96
|
+
self._raise_if_unhandled(state)
|
|
95
97
|
|
|
96
98
|
# Convert from protocol HSBK to user-friendly HSBK
|
|
97
99
|
color = HSBK.from_protocol(state.color)
|
|
@@ -133,6 +135,7 @@ class Light(Device):
|
|
|
133
135
|
Raises:
|
|
134
136
|
LifxDeviceNotFoundError: If device is not connected
|
|
135
137
|
LifxTimeoutError: If device does not respond
|
|
138
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
136
139
|
|
|
137
140
|
Example:
|
|
138
141
|
```python
|
|
@@ -150,12 +153,13 @@ class Light(Device):
|
|
|
150
153
|
duration_ms = int(duration * 1000)
|
|
151
154
|
|
|
152
155
|
# Request automatically handles acknowledgement
|
|
153
|
-
await self.connection.request(
|
|
156
|
+
result = await self.connection.request(
|
|
154
157
|
packets.Light.SetColor(
|
|
155
158
|
color=protocol_color,
|
|
156
159
|
duration=duration_ms,
|
|
157
160
|
),
|
|
158
161
|
)
|
|
162
|
+
self._raise_if_unhandled(result)
|
|
159
163
|
|
|
160
164
|
_LOGGER.debug(
|
|
161
165
|
{
|
|
@@ -357,6 +361,7 @@ class Light(Device):
|
|
|
357
361
|
LifxDeviceNotFoundError: If device is not connected
|
|
358
362
|
LifxTimeoutError: If device does not respond
|
|
359
363
|
LifxProtocolError: If response is invalid
|
|
364
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
360
365
|
|
|
361
366
|
Example:
|
|
362
367
|
```python
|
|
@@ -366,6 +371,7 @@ class Light(Device):
|
|
|
366
371
|
"""
|
|
367
372
|
# Request automatically unpacks response
|
|
368
373
|
state = await self.connection.request(packets.Light.GetPower())
|
|
374
|
+
self._raise_if_unhandled(state)
|
|
369
375
|
|
|
370
376
|
# Power level is uint16 (0 or 65535)
|
|
371
377
|
_LOGGER.debug(
|
|
@@ -394,6 +400,7 @@ class Light(Device):
|
|
|
394
400
|
LifxDeviceNotFoundError: If device is not connected
|
|
395
401
|
LifxTimeoutError: If device does not respond
|
|
396
402
|
LifxProtocolError: If response is invalid
|
|
403
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
397
404
|
|
|
398
405
|
Example:
|
|
399
406
|
```python
|
|
@@ -406,6 +413,7 @@ class Light(Device):
|
|
|
406
413
|
"""
|
|
407
414
|
# Request automatically unpacks response
|
|
408
415
|
state = await self.connection.request(packets.Sensor.GetAmbientLight())
|
|
416
|
+
self._raise_if_unhandled(state)
|
|
409
417
|
|
|
410
418
|
_LOGGER.debug(
|
|
411
419
|
{
|
|
@@ -432,6 +440,7 @@ class Light(Device):
|
|
|
432
440
|
ValueError: If integer value is not 0 or 65535
|
|
433
441
|
LifxDeviceNotFoundError: If device is not connected
|
|
434
442
|
LifxTimeoutError: If device does not respond
|
|
443
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
435
444
|
|
|
436
445
|
Example:
|
|
437
446
|
```python
|
|
@@ -460,9 +469,10 @@ class Light(Device):
|
|
|
460
469
|
duration_ms = int(duration * 1000)
|
|
461
470
|
|
|
462
471
|
# Request automatically handles acknowledgement
|
|
463
|
-
await self.connection.request(
|
|
472
|
+
result = await self.connection.request(
|
|
464
473
|
packets.Light.SetPower(level=power_level, duration=duration_ms),
|
|
465
474
|
)
|
|
475
|
+
self._raise_if_unhandled(result)
|
|
466
476
|
|
|
467
477
|
_LOGGER.debug(
|
|
468
478
|
{
|
|
@@ -499,6 +509,7 @@ class Light(Device):
|
|
|
499
509
|
ValueError: If parameters are out of range
|
|
500
510
|
LifxDeviceNotFoundError: If device is not connected
|
|
501
511
|
LifxTimeoutError: If device does not respond
|
|
512
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
502
513
|
|
|
503
514
|
Example:
|
|
504
515
|
```python
|
|
@@ -537,7 +548,7 @@ class Light(Device):
|
|
|
537
548
|
skew_ratio_i16 = int(skew_ratio * 65535) - 32768 # Convert to int16 range
|
|
538
549
|
|
|
539
550
|
# Send request
|
|
540
|
-
await self.connection.request(
|
|
551
|
+
result = await self.connection.request(
|
|
541
552
|
packets.Light.SetWaveform(
|
|
542
553
|
transient=bool(transient),
|
|
543
554
|
color=protocol_color,
|
|
@@ -547,6 +558,7 @@ class Light(Device):
|
|
|
547
558
|
waveform=waveform,
|
|
548
559
|
),
|
|
549
560
|
)
|
|
561
|
+
self._raise_if_unhandled(result)
|
|
550
562
|
_LOGGER.debug(
|
|
551
563
|
{
|
|
552
564
|
"class": "Device",
|
|
@@ -602,6 +614,7 @@ class Light(Device):
|
|
|
602
614
|
ValueError: If parameters are out of range
|
|
603
615
|
LifxDeviceNotFoundError: If device is not connected
|
|
604
616
|
LifxTimeoutError: If device does not respond
|
|
617
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
605
618
|
|
|
606
619
|
Example:
|
|
607
620
|
```python
|
|
@@ -647,7 +660,7 @@ class Light(Device):
|
|
|
647
660
|
skew_ratio_i16 = int(skew_ratio * 65535) - 32768 # Convert to int16 range
|
|
648
661
|
|
|
649
662
|
# Send request
|
|
650
|
-
await self.connection.request(
|
|
663
|
+
result = await self.connection.request(
|
|
651
664
|
packets.Light.SetWaveformOptional(
|
|
652
665
|
transient=bool(transient),
|
|
653
666
|
color=protocol_color,
|
|
@@ -661,6 +674,7 @@ class Light(Device):
|
|
|
661
674
|
set_kelvin=set_kelvin,
|
|
662
675
|
),
|
|
663
676
|
)
|
|
677
|
+
self._raise_if_unhandled(result)
|
|
664
678
|
_LOGGER.debug(
|
|
665
679
|
{
|
|
666
680
|
"class": "Device",
|
lifx/devices/matrix.py
CHANGED
|
@@ -311,6 +311,11 @@ class MatrixLight(Light):
|
|
|
311
311
|
Returns:
|
|
312
312
|
List of TileInfo objects describing each tile in the chain
|
|
313
313
|
|
|
314
|
+
Raises:
|
|
315
|
+
LifxDeviceNotFoundError: If device is not connected
|
|
316
|
+
LifxTimeoutError: If device does not respond
|
|
317
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
318
|
+
|
|
314
319
|
Example:
|
|
315
320
|
>>> chain = await matrix.get_device_chain()
|
|
316
321
|
>>> for tile in chain:
|
|
@@ -321,6 +326,7 @@ class MatrixLight(Light):
|
|
|
321
326
|
response: packets.Tile.StateDeviceChain = await self.connection.request(
|
|
322
327
|
packets.Tile.GetDeviceChain()
|
|
323
328
|
)
|
|
329
|
+
self._raise_if_unhandled(response)
|
|
324
330
|
|
|
325
331
|
# Parse tiles from response
|
|
326
332
|
tiles = []
|
|
@@ -393,6 +399,11 @@ class MatrixLight(Light):
|
|
|
393
399
|
returns the actual zone count (e.g., 64 for 8x8, 16 for 4x4). For tiles
|
|
394
400
|
with >64 zones (e.g., 128 for 16x8 Ceiling), returns 64 (protocol limit).
|
|
395
401
|
|
|
402
|
+
Raises:
|
|
403
|
+
LifxDeviceNotFoundError: If device is not connected
|
|
404
|
+
LifxTimeoutError: If device does not respond
|
|
405
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
406
|
+
|
|
396
407
|
Example:
|
|
397
408
|
>>> # Get all colors from first tile (no parameters needed)
|
|
398
409
|
>>> colors = await matrix.get64()
|
|
@@ -432,6 +443,7 @@ class MatrixLight(Light):
|
|
|
432
443
|
rect=TileBufferRect(fb_index=0, x=x, y=y, width=width),
|
|
433
444
|
)
|
|
434
445
|
)
|
|
446
|
+
self._raise_if_unhandled(response)
|
|
435
447
|
|
|
436
448
|
max_colors = device_chain[0].width * device_chain[0].height
|
|
437
449
|
|
|
@@ -714,6 +726,11 @@ class MatrixLight(Light):
|
|
|
714
726
|
Returns:
|
|
715
727
|
MatrixEffect describing the current effect state
|
|
716
728
|
|
|
729
|
+
Raises:
|
|
730
|
+
LifxDeviceNotFoundError: If device is not connected
|
|
731
|
+
LifxTimeoutError: If device does not respond
|
|
732
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
733
|
+
|
|
717
734
|
Example:
|
|
718
735
|
>>> effect = await matrix.get_effect()
|
|
719
736
|
>>> print(f"Effect type: {effect.effect_type}")
|
|
@@ -723,6 +740,7 @@ class MatrixLight(Light):
|
|
|
723
740
|
response: packets.Tile.StateEffect = await self.connection.request(
|
|
724
741
|
packets.Tile.GetEffect()
|
|
725
742
|
)
|
|
743
|
+
self._raise_if_unhandled(response)
|
|
726
744
|
|
|
727
745
|
# Convert protocol effect to MatrixEffect
|
|
728
746
|
palette = [
|
lifx/devices/multizone.py
CHANGED
|
@@ -189,6 +189,7 @@ class MultiZoneLight(Light):
|
|
|
189
189
|
LifxDeviceNotFoundError: If device is not connected
|
|
190
190
|
LifxTimeoutError: If device does not respond
|
|
191
191
|
LifxProtocolError: If response is invalid
|
|
192
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
192
193
|
|
|
193
194
|
Example:
|
|
194
195
|
```python
|
|
@@ -205,6 +206,7 @@ class MultiZoneLight(Light):
|
|
|
205
206
|
state = await self.connection.request(
|
|
206
207
|
packets.MultiZone.GetColorZones(start_index=0, end_index=0)
|
|
207
208
|
)
|
|
209
|
+
self._raise_if_unhandled(state)
|
|
208
210
|
|
|
209
211
|
count = state.count
|
|
210
212
|
|
|
@@ -245,6 +247,7 @@ class MultiZoneLight(Light):
|
|
|
245
247
|
LifxDeviceNotFoundError: If device is not connected
|
|
246
248
|
LifxTimeoutError: If device does not respond
|
|
247
249
|
LifxProtocolError: If response is invalid
|
|
250
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
248
251
|
|
|
249
252
|
Example:
|
|
250
253
|
```python
|
|
@@ -279,6 +282,7 @@ class MultiZoneLight(Light):
|
|
|
279
282
|
start_index=current_start, end_index=current_end
|
|
280
283
|
)
|
|
281
284
|
):
|
|
285
|
+
self._raise_if_unhandled(state)
|
|
282
286
|
# Extract colors from response (up to 8 colors)
|
|
283
287
|
zones_in_response = min(8, current_end - current_start + 1)
|
|
284
288
|
for i in range(zones_in_response):
|
|
@@ -336,6 +340,7 @@ class MultiZoneLight(Light):
|
|
|
336
340
|
LifxDeviceNotFoundError: If device is not connected
|
|
337
341
|
LifxTimeoutError: If device does not respond
|
|
338
342
|
LifxProtocolError: If response is invalid
|
|
343
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
339
344
|
|
|
340
345
|
Example:
|
|
341
346
|
```python
|
|
@@ -361,6 +366,7 @@ class MultiZoneLight(Light):
|
|
|
361
366
|
packets.MultiZone.GetExtendedColorZones(),
|
|
362
367
|
timeout=2.0, # Allow time for multiple responses
|
|
363
368
|
):
|
|
369
|
+
self._raise_if_unhandled(packet)
|
|
364
370
|
# Only process valid colors based on colors_count
|
|
365
371
|
for i in range(packet.colors_count):
|
|
366
372
|
if i >= len(packet.colors):
|
|
@@ -449,6 +455,7 @@ class MultiZoneLight(Light):
|
|
|
449
455
|
ValueError: If zone indices are invalid
|
|
450
456
|
LifxDeviceNotFoundError: If device is not connected
|
|
451
457
|
LifxTimeoutError: If device does not respond
|
|
458
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
452
459
|
|
|
453
460
|
Example:
|
|
454
461
|
```python
|
|
@@ -477,7 +484,7 @@ class MultiZoneLight(Light):
|
|
|
477
484
|
duration_ms = int(duration * 1000)
|
|
478
485
|
|
|
479
486
|
# Send request
|
|
480
|
-
await self.connection.request(
|
|
487
|
+
result = await self.connection.request(
|
|
481
488
|
packets.MultiZone.SetColorZones(
|
|
482
489
|
start_index=start,
|
|
483
490
|
end_index=end,
|
|
@@ -486,6 +493,7 @@ class MultiZoneLight(Light):
|
|
|
486
493
|
apply=apply,
|
|
487
494
|
),
|
|
488
495
|
)
|
|
496
|
+
self._raise_if_unhandled(result)
|
|
489
497
|
|
|
490
498
|
_LOGGER.debug(
|
|
491
499
|
{
|
|
@@ -529,6 +537,7 @@ class MultiZoneLight(Light):
|
|
|
529
537
|
ValueError: If colors list is too long or zone index is invalid
|
|
530
538
|
LifxDeviceNotFoundError: If device is not connected
|
|
531
539
|
LifxTimeoutError: If device does not respond
|
|
540
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
532
541
|
|
|
533
542
|
Example:
|
|
534
543
|
```python
|
|
@@ -556,7 +565,7 @@ class MultiZoneLight(Light):
|
|
|
556
565
|
duration_ms = int(duration * 1000)
|
|
557
566
|
|
|
558
567
|
# Send request
|
|
559
|
-
await self.connection.request(
|
|
568
|
+
result = await self.connection.request(
|
|
560
569
|
packets.MultiZone.SetExtendedColorZones(
|
|
561
570
|
duration=duration_ms,
|
|
562
571
|
apply=apply,
|
|
@@ -565,6 +574,7 @@ class MultiZoneLight(Light):
|
|
|
565
574
|
colors=protocol_colors,
|
|
566
575
|
),
|
|
567
576
|
)
|
|
577
|
+
self._raise_if_unhandled(result)
|
|
568
578
|
|
|
569
579
|
_LOGGER.debug(
|
|
570
580
|
{
|
|
@@ -602,6 +612,7 @@ class MultiZoneLight(Light):
|
|
|
602
612
|
LifxDeviceNotFoundError: If device is not connected
|
|
603
613
|
LifxTimeoutError: If device does not respond
|
|
604
614
|
LifxProtocolError: If response is invalid
|
|
615
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
605
616
|
|
|
606
617
|
Example:
|
|
607
618
|
```python
|
|
@@ -616,6 +627,7 @@ class MultiZoneLight(Light):
|
|
|
616
627
|
"""
|
|
617
628
|
# Request automatically unpacks response
|
|
618
629
|
state = await self.connection.request(packets.MultiZone.GetEffect())
|
|
630
|
+
self._raise_if_unhandled(state)
|
|
619
631
|
|
|
620
632
|
settings = state.settings
|
|
621
633
|
effect_type = settings.effect_type
|
|
@@ -672,6 +684,7 @@ class MultiZoneLight(Light):
|
|
|
672
684
|
Raises:
|
|
673
685
|
LifxDeviceNotFoundError: If device is not connected
|
|
674
686
|
LifxTimeoutError: If device does not respond
|
|
687
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
675
688
|
|
|
676
689
|
Example:
|
|
677
690
|
```python
|
|
@@ -701,7 +714,7 @@ class MultiZoneLight(Light):
|
|
|
701
714
|
parameters = parameters[:8]
|
|
702
715
|
|
|
703
716
|
# Send request
|
|
704
|
-
await self.connection.request(
|
|
717
|
+
result = await self.connection.request(
|
|
705
718
|
packets.MultiZone.SetEffect(
|
|
706
719
|
settings=MultiZoneEffectSettings(
|
|
707
720
|
instanceid=0, # 0 for new effect
|
|
@@ -721,10 +734,11 @@ class MultiZoneLight(Light):
|
|
|
721
734
|
),
|
|
722
735
|
),
|
|
723
736
|
)
|
|
737
|
+
self._raise_if_unhandled(result)
|
|
724
738
|
|
|
725
739
|
# Update cached state
|
|
726
|
-
|
|
727
|
-
self._multizone_effect =
|
|
740
|
+
cached_effect = effect if effect.effect_type != FirmwareEffect.OFF else None
|
|
741
|
+
self._multizone_effect = cached_effect
|
|
728
742
|
|
|
729
743
|
_LOGGER.debug(
|
|
730
744
|
{
|
lifx/network/connection.py
CHANGED
|
@@ -22,6 +22,7 @@ from lifx.exceptions import (
|
|
|
22
22
|
LifxConnectionError,
|
|
23
23
|
LifxProtocolError,
|
|
24
24
|
LifxTimeoutError,
|
|
25
|
+
LifxUnsupportedCommandError,
|
|
25
26
|
)
|
|
26
27
|
from lifx.network.message import create_message, parse_message
|
|
27
28
|
from lifx.network.transport import UdpTransport
|
|
@@ -722,8 +723,9 @@ class DeviceConnection:
|
|
|
722
723
|
|
|
723
724
|
# Check for StateUnhandled - return False to indicate unsupported
|
|
724
725
|
if header.pkt_type == _STATE_UNHANDLED_PKT_TYPE:
|
|
725
|
-
|
|
726
|
-
|
|
726
|
+
raise LifxUnsupportedCommandError(
|
|
727
|
+
"Device does not support this command"
|
|
728
|
+
)
|
|
727
729
|
|
|
728
730
|
# ACK received successfully
|
|
729
731
|
yield True
|
|
@@ -5,12 +5,12 @@ lifx/const.py,sha256=dW64lf_jwAD40GSd6hkFkrni5j-w2qkV3pl6YNdCxv4,3426
|
|
|
5
5
|
lifx/exceptions.py,sha256=pikAMppLn7gXyjiQVWM_tSvXKNh-g366nG_UWyqpHhc,815
|
|
6
6
|
lifx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
lifx/devices/__init__.py,sha256=V7hW8sM_RwFgbR4Hv1ByR1JLhYq7Ft1X9pylQjCXYB8,777
|
|
8
|
-
lifx/devices/base.py,sha256=
|
|
9
|
-
lifx/devices/hev.py,sha256=
|
|
10
|
-
lifx/devices/infrared.py,sha256=
|
|
11
|
-
lifx/devices/light.py,sha256=
|
|
12
|
-
lifx/devices/matrix.py,sha256=
|
|
13
|
-
lifx/devices/multizone.py,sha256=
|
|
8
|
+
lifx/devices/base.py,sha256=dVTjzIz_h5dd2tTJkWUTpu5HmQ-H1R2hcTqqzw5_84c,43790
|
|
9
|
+
lifx/devices/hev.py,sha256=ow4AU3eOVAcMK2KKAyQUTB7z6EDoRz7StwVOvwwS4Sk,10124
|
|
10
|
+
lifx/devices/infrared.py,sha256=q8q_cpjdRwojk76jBEdBeIYmqAA4FuTy7ZUquy2yEdg,4498
|
|
11
|
+
lifx/devices/light.py,sha256=EvUeCtjMS23PUoj3cOshFJ8SYT517ksH_3p27J3Sr2o,28452
|
|
12
|
+
lifx/devices/matrix.py,sha256=8VI02LtL_hzIzyXMu1be6QEY8W1e7-jtuitKJ6clFW8,33426
|
|
13
|
+
lifx/devices/multizone.py,sha256=JaKpMvpxz7-RnhkJ1gS6uYrXwEFdJUpyldQsGtXZb_g,28106
|
|
14
14
|
lifx/effects/__init__.py,sha256=4DF31yp7RJic5JoltMlz5dCtF5KQobU6NOUtLUKkVKE,1509
|
|
15
15
|
lifx/effects/base.py,sha256=YO0Hbg2VYHKPtfYnWxmrtzYoPGOi9BUXhn8HVFKv5IM,10283
|
|
16
16
|
lifx/effects/colorloop.py,sha256=kuuyENJS2irAN8vZAFsDa2guQdDbmmc4PJNiyZTfFPE,15840
|
|
@@ -20,7 +20,7 @@ lifx/effects/models.py,sha256=MS5D-cxD0Ar8XhqbqKAc9q2sk38IP1vPkYwd8V7jCr8,2446
|
|
|
20
20
|
lifx/effects/pulse.py,sha256=t5eyjfFWG1xT-RXKghRqHYJ9CG_50tPu4jsDapJZ2mw,8721
|
|
21
21
|
lifx/effects/state_manager.py,sha256=iDfYowiCN5IJqcR1s-pM0mQEJpe-RDsMcOOSMmtPVDE,8983
|
|
22
22
|
lifx/network/__init__.py,sha256=uSyA8r8qISG7qXUHbX8uk9A2E8rvDADgCcf94QIZ9so,499
|
|
23
|
-
lifx/network/connection.py,sha256=
|
|
23
|
+
lifx/network/connection.py,sha256=8CAcmTFScW5P4lO6GQTnhb-SnPLiqa5fiztvSfQgs7g,38392
|
|
24
24
|
lifx/network/discovery.py,sha256=FoFoZcw3dtJs1daESiZiNXytanKQsMTdF9PjOxEgHM0,23804
|
|
25
25
|
lifx/network/message.py,sha256=jCLC9v0tbBi54g5CaHLFM_nP1Izu8kJmo2tt23HHBbA,2600
|
|
26
26
|
lifx/network/transport.py,sha256=8QS0YV32rdP0EDiPEwuvZXbplRWL08pmjKybd87mkZ0,11070
|
|
@@ -40,7 +40,7 @@ lifx/theme/canvas.py,sha256=4h7lgN8iu_OdchObGDgbxTqQLCb-FRKC-M-YCWef_i4,8048
|
|
|
40
40
|
lifx/theme/generators.py,sha256=L0X6_iApLx6XDboGlYunaVsl6nvUCqMfn23VQmRkyCk,6125
|
|
41
41
|
lifx/theme/library.py,sha256=tKlKZNqJp8lRGDnilWyDm_Qr1vCRGGwuvWVS82anNpQ,21326
|
|
42
42
|
lifx/theme/theme.py,sha256=qMEx_8E41C0Cc6f083XHiAXEglTv4YlXW0UFsG1rQKg,5521
|
|
43
|
-
lifx_async-4.3.
|
|
44
|
-
lifx_async-4.3.
|
|
45
|
-
lifx_async-4.3.
|
|
46
|
-
lifx_async-4.3.
|
|
43
|
+
lifx_async-4.3.8.dist-info/METADATA,sha256=h4E8r3J8RxQbmI028hzTWzb9uW0vBe0nwzMycFlNZZE,2609
|
|
44
|
+
lifx_async-4.3.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
45
|
+
lifx_async-4.3.8.dist-info/licenses/LICENSE,sha256=eBz48GRA3gSiWn3rYZAz2Ewp35snnhV9cSqkVBq7g3k,1832
|
|
46
|
+
lifx_async-4.3.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|