lifx-async 4.3.6__py3-none-any.whl → 4.3.7__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 +51 -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_async-4.3.6.dist-info → lifx_async-4.3.7.dist-info}/METADATA +1 -1
- {lifx_async-4.3.6.dist-info → lifx_async-4.3.7.dist-info}/RECORD +10 -10
- {lifx_async-4.3.6.dist-info → lifx_async-4.3.7.dist-info}/WHEEL +0 -0
- {lifx_async-4.3.6.dist-info → lifx_async-4.3.7.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,23 @@ 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
|
+
if response is False:
|
|
170
|
+
raise LifxUnsupportedCommandError("Device does not support this command")
|
|
171
|
+
|
|
155
172
|
def __init__(
|
|
156
173
|
self,
|
|
157
174
|
serial: str,
|
|
@@ -456,6 +473,7 @@ class Device:
|
|
|
456
473
|
LifxDeviceNotFoundError: If device is not connected
|
|
457
474
|
LifxTimeoutError: If device does not respond
|
|
458
475
|
LifxProtocolError: If response is invalid
|
|
476
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
459
477
|
|
|
460
478
|
Example:
|
|
461
479
|
```python
|
|
@@ -469,6 +487,7 @@ class Device:
|
|
|
469
487
|
"""
|
|
470
488
|
# Request automatically unpacks and decodes label
|
|
471
489
|
state = await self.connection.request(packets.Device.GetLabel())
|
|
490
|
+
self._raise_if_unhandled(state)
|
|
472
491
|
|
|
473
492
|
# Store label
|
|
474
493
|
self._label = state.label
|
|
@@ -492,6 +511,7 @@ class Device:
|
|
|
492
511
|
ValueError: If label is too long
|
|
493
512
|
LifxDeviceNotFoundError: If device is not connected
|
|
494
513
|
LifxTimeoutError: If device does not respond
|
|
514
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
495
515
|
|
|
496
516
|
Example:
|
|
497
517
|
```python
|
|
@@ -508,9 +528,10 @@ class Device:
|
|
|
508
528
|
label_bytes = label_bytes.ljust(32, b"\x00")
|
|
509
529
|
|
|
510
530
|
# Request automatically handles acknowledgement
|
|
511
|
-
await self.connection.request(
|
|
531
|
+
result = await self.connection.request(
|
|
512
532
|
packets.Device.SetLabel(label=label_bytes),
|
|
513
533
|
)
|
|
534
|
+
self._raise_if_unhandled(result)
|
|
514
535
|
|
|
515
536
|
# Update cached state
|
|
516
537
|
self._label = label
|
|
@@ -535,6 +556,7 @@ class Device:
|
|
|
535
556
|
LifxDeviceNotFoundError: If device is not connected
|
|
536
557
|
LifxTimeoutError: If device does not respond
|
|
537
558
|
LifxProtocolError: If response is invalid
|
|
559
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
538
560
|
|
|
539
561
|
Example:
|
|
540
562
|
```python
|
|
@@ -544,6 +566,7 @@ class Device:
|
|
|
544
566
|
"""
|
|
545
567
|
# Request automatically unpacks response
|
|
546
568
|
state = await self.connection.request(packets.Device.GetPower())
|
|
569
|
+
self._raise_if_unhandled(state)
|
|
547
570
|
|
|
548
571
|
# Power level is uint16 (0 or 65535)
|
|
549
572
|
_LOGGER.debug(
|
|
@@ -566,6 +589,7 @@ class Device:
|
|
|
566
589
|
ValueError: If integer value is not 0 or 65535
|
|
567
590
|
LifxDeviceNotFoundError: If device is not connected
|
|
568
591
|
LifxTimeoutError: If device does not respond
|
|
592
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
569
593
|
|
|
570
594
|
Example:
|
|
571
595
|
```python
|
|
@@ -591,9 +615,10 @@ class Device:
|
|
|
591
615
|
raise TypeError(f"Expected bool or int, got {type(level).__name__}")
|
|
592
616
|
|
|
593
617
|
# Request automatically handles acknowledgement
|
|
594
|
-
await self.connection.request(
|
|
618
|
+
result = await self.connection.request(
|
|
595
619
|
packets.Device.SetPower(level=power_level),
|
|
596
620
|
)
|
|
621
|
+
self._raise_if_unhandled(result)
|
|
597
622
|
|
|
598
623
|
_LOGGER.debug(
|
|
599
624
|
{
|
|
@@ -616,6 +641,7 @@ class Device:
|
|
|
616
641
|
LifxDeviceNotFoundError: If device is not connected
|
|
617
642
|
LifxTimeoutError: If device does not respond
|
|
618
643
|
LifxProtocolError: If response is invalid
|
|
644
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
619
645
|
|
|
620
646
|
Example:
|
|
621
647
|
```python
|
|
@@ -625,6 +651,7 @@ class Device:
|
|
|
625
651
|
"""
|
|
626
652
|
# Request automatically unpacks response
|
|
627
653
|
state = await self.connection.request(packets.Device.GetVersion())
|
|
654
|
+
self._raise_if_unhandled(state)
|
|
628
655
|
|
|
629
656
|
version = DeviceVersion(
|
|
630
657
|
vendor=state.vendor,
|
|
@@ -655,6 +682,7 @@ class Device:
|
|
|
655
682
|
LifxDeviceNotFoundError: If device is not connected
|
|
656
683
|
LifxTimeoutError: If device does not respond
|
|
657
684
|
LifxProtocolError: If response is invalid
|
|
685
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
658
686
|
|
|
659
687
|
Example:
|
|
660
688
|
```python
|
|
@@ -665,6 +693,7 @@ class Device:
|
|
|
665
693
|
"""
|
|
666
694
|
# Request automatically unpacks response
|
|
667
695
|
state = await self.connection.request(packets.Device.GetInfo()) # type: ignore
|
|
696
|
+
self._raise_if_unhandled(state)
|
|
668
697
|
|
|
669
698
|
info = DeviceInfo(time=state.time, uptime=state.uptime, downtime=state.downtime)
|
|
670
699
|
|
|
@@ -694,6 +723,7 @@ class Device:
|
|
|
694
723
|
LifxDeviceNotFoundError: If device is not connected
|
|
695
724
|
LifxTimeoutError: If device does not respond
|
|
696
725
|
LifxProtocolError: If response is invalid
|
|
726
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
697
727
|
|
|
698
728
|
Example:
|
|
699
729
|
```python
|
|
@@ -704,6 +734,7 @@ class Device:
|
|
|
704
734
|
"""
|
|
705
735
|
# Request WiFi info from device
|
|
706
736
|
state = await self.connection.request(packets.Device.GetWifiInfo())
|
|
737
|
+
self._raise_if_unhandled(state)
|
|
707
738
|
|
|
708
739
|
# Extract WiFi info from response
|
|
709
740
|
wifi_info = WifiInfo(signal=state.signal)
|
|
@@ -730,6 +761,7 @@ class Device:
|
|
|
730
761
|
LifxDeviceNotFoundError: If device is not connected
|
|
731
762
|
LifxTimeoutError: If device does not respond
|
|
732
763
|
LifxProtocolError: If response is invalid
|
|
764
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
733
765
|
|
|
734
766
|
Example:
|
|
735
767
|
```python
|
|
@@ -739,6 +771,7 @@ class Device:
|
|
|
739
771
|
"""
|
|
740
772
|
# Request automatically unpacks response
|
|
741
773
|
state = await self.connection.request(packets.Device.GetHostFirmware()) # type: ignore
|
|
774
|
+
self._raise_if_unhandled(state)
|
|
742
775
|
|
|
743
776
|
firmware = FirmwareInfo(
|
|
744
777
|
build=state.build,
|
|
@@ -778,6 +811,7 @@ class Device:
|
|
|
778
811
|
LifxDeviceNotFoundError: If device is not connected
|
|
779
812
|
LifxTimeoutError: If device does not respond
|
|
780
813
|
LifxProtocolError: If response is invalid
|
|
814
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
781
815
|
|
|
782
816
|
Example:
|
|
783
817
|
```python
|
|
@@ -787,6 +821,7 @@ class Device:
|
|
|
787
821
|
"""
|
|
788
822
|
# Request automatically unpacks response
|
|
789
823
|
state = await self.connection.request(packets.Device.GetWifiFirmware()) # type: ignore
|
|
824
|
+
self._raise_if_unhandled(state)
|
|
790
825
|
|
|
791
826
|
firmware = FirmwareInfo(
|
|
792
827
|
build=state.build,
|
|
@@ -822,6 +857,7 @@ class Device:
|
|
|
822
857
|
LifxDeviceNotFoundError: If device is not connected
|
|
823
858
|
LifxTimeoutError: If device does not respond
|
|
824
859
|
LifxProtocolError: If response is invalid
|
|
860
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
825
861
|
|
|
826
862
|
Example:
|
|
827
863
|
```python
|
|
@@ -832,6 +868,7 @@ class Device:
|
|
|
832
868
|
"""
|
|
833
869
|
# Request automatically unpacks response
|
|
834
870
|
state = await self.connection.request(packets.Device.GetLocation()) # type: ignore
|
|
871
|
+
self._raise_if_unhandled(state)
|
|
835
872
|
|
|
836
873
|
location = LocationInfo(
|
|
837
874
|
location=state.location,
|
|
@@ -873,6 +910,7 @@ class Device:
|
|
|
873
910
|
LifxDeviceNotFoundError: If device is not connected
|
|
874
911
|
LifxTimeoutError: If device does not respond
|
|
875
912
|
ValueError: If label is invalid
|
|
913
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
876
914
|
|
|
877
915
|
Example:
|
|
878
916
|
```python
|
|
@@ -967,11 +1005,12 @@ class Device:
|
|
|
967
1005
|
updated_at = int(time.time() * 1e9)
|
|
968
1006
|
|
|
969
1007
|
# Update this device
|
|
970
|
-
await self.connection.request(
|
|
1008
|
+
result = await self.connection.request(
|
|
971
1009
|
packets.Device.SetLocation(
|
|
972
1010
|
location=location_uuid_to_use, label=label_bytes, updated_at=updated_at
|
|
973
1011
|
),
|
|
974
1012
|
)
|
|
1013
|
+
self._raise_if_unhandled(result)
|
|
975
1014
|
|
|
976
1015
|
# Update cached state
|
|
977
1016
|
location_info = LocationInfo(
|
|
@@ -1003,6 +1042,7 @@ class Device:
|
|
|
1003
1042
|
LifxDeviceNotFoundError: If device is not connected
|
|
1004
1043
|
LifxTimeoutError: If device does not respond
|
|
1005
1044
|
LifxProtocolError: If response is invalid
|
|
1045
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
1006
1046
|
|
|
1007
1047
|
Example:
|
|
1008
1048
|
```python
|
|
@@ -1013,6 +1053,7 @@ class Device:
|
|
|
1013
1053
|
"""
|
|
1014
1054
|
# Request automatically unpacks response
|
|
1015
1055
|
state = await self.connection.request(packets.Device.GetGroup()) # type: ignore
|
|
1056
|
+
self._raise_if_unhandled(state)
|
|
1016
1057
|
|
|
1017
1058
|
group = GroupInfo(
|
|
1018
1059
|
group=state.group,
|
|
@@ -1054,6 +1095,7 @@ class Device:
|
|
|
1054
1095
|
LifxDeviceNotFoundError: If device is not connected
|
|
1055
1096
|
LifxTimeoutError: If device does not respond
|
|
1056
1097
|
ValueError: If label is invalid
|
|
1098
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
1057
1099
|
|
|
1058
1100
|
Example:
|
|
1059
1101
|
```python
|
|
@@ -1148,11 +1190,12 @@ class Device:
|
|
|
1148
1190
|
updated_at = int(time.time() * 1e9)
|
|
1149
1191
|
|
|
1150
1192
|
# Update this device
|
|
1151
|
-
await self.connection.request(
|
|
1193
|
+
result = await self.connection.request(
|
|
1152
1194
|
packets.Device.SetGroup(
|
|
1153
1195
|
group=group_uuid_to_use, label=label_bytes, updated_at=updated_at
|
|
1154
1196
|
),
|
|
1155
1197
|
)
|
|
1198
|
+
self._raise_if_unhandled(result)
|
|
1156
1199
|
|
|
1157
1200
|
# Update cached state
|
|
1158
1201
|
group_info = GroupInfo(
|
|
@@ -1181,6 +1224,7 @@ class Device:
|
|
|
1181
1224
|
Raises:
|
|
1182
1225
|
LifxDeviceNotFoundError: If device is not connected
|
|
1183
1226
|
LifxTimeoutError: If device does not respond
|
|
1227
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
1184
1228
|
|
|
1185
1229
|
Example:
|
|
1186
1230
|
```python
|
|
@@ -1194,9 +1238,10 @@ class Device:
|
|
|
1194
1238
|
comes back online and is discoverable again.
|
|
1195
1239
|
"""
|
|
1196
1240
|
# Send reboot request
|
|
1197
|
-
await self.connection.request(
|
|
1241
|
+
result = await self.connection.request(
|
|
1198
1242
|
packets.Device.SetReboot(),
|
|
1199
1243
|
)
|
|
1244
|
+
self._raise_if_unhandled(result)
|
|
1200
1245
|
_LOGGER.debug(
|
|
1201
1246
|
{
|
|
1202
1247
|
"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
|
{
|
|
@@ -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=IxwFbKHnmfjhqzlqW66jqHslLUAHq1ocgSTVYXu6yxc,43906
|
|
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
|
|
@@ -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.7.dist-info/METADATA,sha256=LiDbfeEvXW8ofmnMBzN2OP_4nRa_gSo8Q2RWAqFM6U8,2609
|
|
44
|
+
lifx_async-4.3.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
45
|
+
lifx_async-4.3.7.dist-info/licenses/LICENSE,sha256=eBz48GRA3gSiWn3rYZAz2Ewp35snnhV9cSqkVBq7g3k,1832
|
|
46
|
+
lifx_async-4.3.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|