lifx-async 5.0.0__py3-none-any.whl → 5.1.0__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/__init__.py +4 -0
- lifx/animation/__init__.py +87 -0
- lifx/animation/animator.py +323 -0
- lifx/animation/framebuffer.py +395 -0
- lifx/animation/orientation.py +159 -0
- lifx/animation/packets.py +497 -0
- lifx/api.py +0 -8
- lifx/const.py +18 -0
- lifx/devices/ceiling.py +7 -8
- lifx/devices/matrix.py +2 -19
- lifx/effects/colorloop.py +2 -2
- lifx/network/connection.py +8 -7
- lifx/network/discovery.py +3 -3
- lifx/network/mdns/transport.py +2 -2
- lifx/network/transport.py +8 -3
- lifx/network/utils.py +15 -0
- lifx/protocol/serializer.py +0 -85
- {lifx_async-5.0.0.dist-info → lifx_async-5.1.0.dist-info}/METADATA +1 -1
- {lifx_async-5.0.0.dist-info → lifx_async-5.1.0.dist-info}/RECORD +21 -15
- {lifx_async-5.0.0.dist-info → lifx_async-5.1.0.dist-info}/WHEEL +0 -0
- {lifx_async-5.0.0.dist-info → lifx_async-5.1.0.dist-info}/licenses/LICENSE +0 -0
lifx/network/connection.py
CHANGED
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
7
|
import random
|
|
8
|
-
import secrets
|
|
9
8
|
import time
|
|
10
9
|
from collections.abc import AsyncGenerator
|
|
11
10
|
from typing import TYPE_CHECKING, Any, TypeVar
|
|
@@ -17,6 +16,7 @@ from lifx.const import (
|
|
|
17
16
|
DEFAULT_MAX_RETRIES,
|
|
18
17
|
DEFAULT_REQUEST_TIMEOUT,
|
|
19
18
|
LIFX_UDP_PORT,
|
|
19
|
+
TIMEOUT_ERRORS,
|
|
20
20
|
)
|
|
21
21
|
from lifx.exceptions import (
|
|
22
22
|
LifxConnectionError,
|
|
@@ -26,6 +26,7 @@ from lifx.exceptions import (
|
|
|
26
26
|
)
|
|
27
27
|
from lifx.network.message import create_message, parse_message
|
|
28
28
|
from lifx.network.transport import UdpTransport
|
|
29
|
+
from lifx.network.utils import allocate_source
|
|
29
30
|
from lifx.protocol.header import LifxHeader
|
|
30
31
|
from lifx.protocol.models import Serial
|
|
31
32
|
|
|
@@ -193,7 +194,7 @@ class DeviceConnection:
|
|
|
193
194
|
await asyncio.wait_for(
|
|
194
195
|
self._receiver_task, timeout=_RECEIVER_SHUTDOWN_TIMEOUT
|
|
195
196
|
)
|
|
196
|
-
except
|
|
197
|
+
except TIMEOUT_ERRORS:
|
|
197
198
|
self._receiver_task.cancel()
|
|
198
199
|
try:
|
|
199
200
|
await self._receiver_task
|
|
@@ -335,7 +336,7 @@ class DeviceConnection:
|
|
|
335
336
|
Returns:
|
|
336
337
|
Unique source identifier (range: 2 to 4294967295)
|
|
337
338
|
"""
|
|
338
|
-
return
|
|
339
|
+
return allocate_source()
|
|
339
340
|
|
|
340
341
|
async def _background_receiver(self) -> None:
|
|
341
342
|
"""Background task to receive and route packets.
|
|
@@ -559,7 +560,7 @@ class DeviceConnection:
|
|
|
559
560
|
header, payload = await asyncio.wait_for(
|
|
560
561
|
response_queue.get(), timeout=remaining_time
|
|
561
562
|
)
|
|
562
|
-
except
|
|
563
|
+
except TIMEOUT_ERRORS:
|
|
563
564
|
if not has_yielded:
|
|
564
565
|
# No response this attempt, retry
|
|
565
566
|
raise TimeoutError(
|
|
@@ -603,7 +604,7 @@ class DeviceConnection:
|
|
|
603
604
|
|
|
604
605
|
# Continue loop to wait for more responses
|
|
605
606
|
|
|
606
|
-
except
|
|
607
|
+
except TIMEOUT_ERRORS as e:
|
|
607
608
|
last_error = LifxTimeoutError(str(e))
|
|
608
609
|
if attempt < max_retries:
|
|
609
610
|
# Sleep with jitter before retry
|
|
@@ -702,7 +703,7 @@ class DeviceConnection:
|
|
|
702
703
|
header, _payload = await asyncio.wait_for(
|
|
703
704
|
response_queue.get(), timeout=current_timeout
|
|
704
705
|
)
|
|
705
|
-
except
|
|
706
|
+
except TIMEOUT_ERRORS:
|
|
706
707
|
raise TimeoutError(
|
|
707
708
|
f"No acknowledgement within {current_timeout:.3f}s "
|
|
708
709
|
f"(attempt {attempt + 1}/{max_retries + 1})"
|
|
@@ -731,7 +732,7 @@ class DeviceConnection:
|
|
|
731
732
|
yield True
|
|
732
733
|
return
|
|
733
734
|
|
|
734
|
-
except
|
|
735
|
+
except TIMEOUT_ERRORS as e:
|
|
735
736
|
last_error = LifxTimeoutError(str(e))
|
|
736
737
|
if attempt < max_retries:
|
|
737
738
|
# Sleep with jitter before retry
|
lifx/network/discovery.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
-
import secrets
|
|
7
6
|
import time
|
|
8
7
|
from collections.abc import AsyncGenerator
|
|
9
8
|
from dataclasses import dataclass, field
|
|
@@ -20,6 +19,7 @@ from lifx.const import (
|
|
|
20
19
|
from lifx.exceptions import LifxProtocolError, LifxTimeoutError
|
|
21
20
|
from lifx.network.message import create_message, parse_message
|
|
22
21
|
from lifx.network.transport import UdpTransport
|
|
22
|
+
from lifx.network.utils import allocate_source
|
|
23
23
|
from lifx.protocol.base import Packet
|
|
24
24
|
from lifx.protocol.models import Serial
|
|
25
25
|
from lifx.protocol.packets import Device as DevicePackets
|
|
@@ -219,7 +219,7 @@ async def _discover_with_packet(
|
|
|
219
219
|
|
|
220
220
|
async with UdpTransport(port=0, broadcast=True) as transport:
|
|
221
221
|
# Allocate unique source for this discovery session
|
|
222
|
-
discovery_source =
|
|
222
|
+
discovery_source = allocate_source()
|
|
223
223
|
|
|
224
224
|
message = create_message(
|
|
225
225
|
packet=packet,
|
|
@@ -470,7 +470,7 @@ async def discover_devices(
|
|
|
470
470
|
# Create transport with broadcast enabled
|
|
471
471
|
async with UdpTransport(port=0, broadcast=True) as transport:
|
|
472
472
|
# Allocate unique source for this discovery session
|
|
473
|
-
discovery_source =
|
|
473
|
+
discovery_source = allocate_source()
|
|
474
474
|
|
|
475
475
|
# Create discovery message
|
|
476
476
|
discovery_packet = DevicePackets.GetService()
|
lifx/network/mdns/transport.py
CHANGED
|
@@ -13,7 +13,7 @@ import struct
|
|
|
13
13
|
from asyncio import DatagramTransport
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
15
|
|
|
16
|
-
from lifx.const import MDNS_ADDRESS, MDNS_PORT
|
|
16
|
+
from lifx.const import MDNS_ADDRESS, MDNS_PORT, TIMEOUT_ERRORS
|
|
17
17
|
from lifx.exceptions import LifxNetworkError, LifxTimeoutError
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
@@ -259,7 +259,7 @@ class MdnsTransport:
|
|
|
259
259
|
self._protocol.queue.get(), timeout=timeout
|
|
260
260
|
)
|
|
261
261
|
return data, addr
|
|
262
|
-
except
|
|
262
|
+
except TIMEOUT_ERRORS as e:
|
|
263
263
|
raise LifxTimeoutError(f"No mDNS data received within {timeout}s") from e
|
|
264
264
|
except OSError as e:
|
|
265
265
|
_LOGGER.debug(
|
lifx/network/transport.py
CHANGED
|
@@ -6,7 +6,12 @@ import asyncio
|
|
|
6
6
|
import logging
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
|
-
from lifx.const import
|
|
9
|
+
from lifx.const import (
|
|
10
|
+
DEFAULT_IP_ADDRESS,
|
|
11
|
+
MAX_PACKET_SIZE,
|
|
12
|
+
MIN_PACKET_SIZE,
|
|
13
|
+
TIMEOUT_ERRORS,
|
|
14
|
+
)
|
|
10
15
|
from lifx.exceptions import LifxNetworkError
|
|
11
16
|
from lifx.exceptions import LifxTimeoutError as LifxTimeoutError
|
|
12
17
|
|
|
@@ -208,7 +213,7 @@ class UdpTransport:
|
|
|
208
213
|
data, addr = await asyncio.wait_for(
|
|
209
214
|
self._protocol.queue.get(), timeout=timeout
|
|
210
215
|
)
|
|
211
|
-
except
|
|
216
|
+
except TIMEOUT_ERRORS as e:
|
|
212
217
|
raise LifxTimeoutError(f"No data received within {timeout}s") from e
|
|
213
218
|
except OSError as e:
|
|
214
219
|
_LOGGER.error(
|
|
@@ -302,7 +307,7 @@ class UdpTransport:
|
|
|
302
307
|
continue
|
|
303
308
|
|
|
304
309
|
packets.append((data, addr))
|
|
305
|
-
except
|
|
310
|
+
except TIMEOUT_ERRORS:
|
|
306
311
|
# Timeout is expected - return what we collected
|
|
307
312
|
break
|
|
308
313
|
except OSError:
|
lifx/network/utils.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Network utilities for LIFX protocol communication."""
|
|
2
|
+
|
|
3
|
+
import secrets
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def allocate_source() -> int:
|
|
7
|
+
"""Allocate unique source identifier for a LIFX protocol request.
|
|
8
|
+
|
|
9
|
+
LIFX protocol defines source as Uint32, with 0 and 1 reserved.
|
|
10
|
+
We generate values in range [2, 0xFFFFFFFF].
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
Unique source identifier (range: 2 to 4294967295)
|
|
14
|
+
"""
|
|
15
|
+
return secrets.randbelow(0xFFFFFFFF - 1) + 2
|
lifx/protocol/serializer.py
CHANGED
|
@@ -281,88 +281,3 @@ def unpack_bytes(data: bytes, length: int, offset: int = 0) -> tuple[bytes, int]
|
|
|
281
281
|
|
|
282
282
|
raw_bytes = data[offset : offset + length]
|
|
283
283
|
return raw_bytes, offset + length
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
class FieldSerializer:
|
|
287
|
-
"""Serializer for structured fields with nested types."""
|
|
288
|
-
|
|
289
|
-
def __init__(self, field_definitions: dict[str, dict[str, str]]):
|
|
290
|
-
"""Initialize serializer with field definitions.
|
|
291
|
-
|
|
292
|
-
Args:
|
|
293
|
-
field_definitions: Dict mapping field names to structure definitions
|
|
294
|
-
"""
|
|
295
|
-
self.field_definitions = field_definitions
|
|
296
|
-
|
|
297
|
-
def pack_field(self, field_data: dict[str, Any], field_name: str) -> bytes:
|
|
298
|
-
"""Pack a structured field.
|
|
299
|
-
|
|
300
|
-
Args:
|
|
301
|
-
field_data: Dictionary of field values
|
|
302
|
-
field_name: Name of the field structure (e.g., "HSBK")
|
|
303
|
-
|
|
304
|
-
Returns:
|
|
305
|
-
Packed bytes
|
|
306
|
-
|
|
307
|
-
Raises:
|
|
308
|
-
ValueError: If field_name is unknown
|
|
309
|
-
"""
|
|
310
|
-
if field_name not in self.field_definitions:
|
|
311
|
-
raise ValueError(f"Unknown field: {field_name}")
|
|
312
|
-
|
|
313
|
-
field_def = self.field_definitions[field_name]
|
|
314
|
-
result = b""
|
|
315
|
-
|
|
316
|
-
for attr_name, attr_type in field_def.items():
|
|
317
|
-
if attr_name not in field_data:
|
|
318
|
-
raise ValueError(f"Missing attribute {attr_name} in {field_name}")
|
|
319
|
-
result += pack_value(field_data[attr_name], attr_type)
|
|
320
|
-
|
|
321
|
-
return result
|
|
322
|
-
|
|
323
|
-
def unpack_field(
|
|
324
|
-
self, data: bytes, field_name: str, offset: int = 0
|
|
325
|
-
) -> tuple[dict[str, Any], int]:
|
|
326
|
-
"""Unpack a structured field.
|
|
327
|
-
|
|
328
|
-
Args:
|
|
329
|
-
data: Bytes to unpack from
|
|
330
|
-
field_name: Name of the field structure
|
|
331
|
-
offset: Offset to start unpacking
|
|
332
|
-
|
|
333
|
-
Returns:
|
|
334
|
-
Tuple of (field_dict, new_offset)
|
|
335
|
-
|
|
336
|
-
Raises:
|
|
337
|
-
ValueError: If field_name is unknown
|
|
338
|
-
"""
|
|
339
|
-
if field_name not in self.field_definitions:
|
|
340
|
-
raise ValueError(f"Unknown field: {field_name}")
|
|
341
|
-
|
|
342
|
-
field_def = self.field_definitions[field_name]
|
|
343
|
-
field_data: dict[str, Any] = {}
|
|
344
|
-
current_offset = offset
|
|
345
|
-
|
|
346
|
-
for attr_name, attr_type in field_def.items():
|
|
347
|
-
value, current_offset = unpack_value(data, attr_type, current_offset)
|
|
348
|
-
field_data[attr_name] = value
|
|
349
|
-
|
|
350
|
-
return field_data, current_offset
|
|
351
|
-
|
|
352
|
-
def get_field_size(self, field_name: str) -> int:
|
|
353
|
-
"""Get the size in bytes of a field structure.
|
|
354
|
-
|
|
355
|
-
Args:
|
|
356
|
-
field_name: Name of the field structure
|
|
357
|
-
|
|
358
|
-
Returns:
|
|
359
|
-
Size in bytes
|
|
360
|
-
|
|
361
|
-
Raises:
|
|
362
|
-
ValueError: If field_name is unknown
|
|
363
|
-
"""
|
|
364
|
-
if field_name not in self.field_definitions:
|
|
365
|
-
raise ValueError(f"Unknown field: {field_name}")
|
|
366
|
-
|
|
367
|
-
field_def = self.field_definitions[field_name]
|
|
368
|
-
return sum(TYPE_SIZES[attr_type] for attr_type in field_def.values())
|
|
@@ -1,34 +1,40 @@
|
|
|
1
|
-
lifx/__init__.py,sha256=
|
|
2
|
-
lifx/api.py,sha256=
|
|
1
|
+
lifx/__init__.py,sha256=GVGsRhvFvKMFmZmdZm9yFwbYh6RywOumGRsa5CyTK84,2914
|
|
2
|
+
lifx/api.py,sha256=A2cNGVewJR_ovzYYffkUT7Lknj0nzruwuhhoOtcAxwM,35134
|
|
3
3
|
lifx/color.py,sha256=UfeoOiFgFih5edl2Ei-0wSzvZXRTI47yUm9GlNJZeTw,26041
|
|
4
|
-
lifx/const.py,sha256=
|
|
4
|
+
lifx/const.py,sha256=M_6d1yFaWzalUAWzrVHv6BOIOu_1f6JJslP267Vd_kg,4573
|
|
5
5
|
lifx/exceptions.py,sha256=pikAMppLn7gXyjiQVWM_tSvXKNh-g366nG_UWyqpHhc,815
|
|
6
6
|
lifx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
lifx/animation/__init__.py,sha256=1EqPk26BTEADYR5qLD1UKb7cNnVfnLo5vfAnix6yyu0,2307
|
|
8
|
+
lifx/animation/animator.py,sha256=LyruwE4Z0gHnkGJfgug017-Mt0R-3FCrDoEsqGjE5ps,10154
|
|
9
|
+
lifx/animation/framebuffer.py,sha256=ya3mbYtu5hepn2Fxfc3wEck5KhmmAfcbmv0RoIO-yC4,14116
|
|
10
|
+
lifx/animation/orientation.py,sha256=bUMV4czLn5PH3inMObfyvbYD-jaSSH-LWaLOby-mvTs,5839
|
|
11
|
+
lifx/animation/packets.py,sha256=jMNfe0EKb9oMn96UdYqJshlncYTx61IX6OzKo4SjFcg,17017
|
|
7
12
|
lifx/devices/__init__.py,sha256=4b5QtO0EFWxIqN2lUYgM8uLjWyHI5hUcReiF9QCjCGw,1061
|
|
8
13
|
lifx/devices/base.py,sha256=mhNLX6FoLBaZtYo9InleneYdb0dk3B2Ze8Z2eqXCNHo,63180
|
|
9
|
-
lifx/devices/ceiling.py,sha256=
|
|
14
|
+
lifx/devices/ceiling.py,sha256=cmGeEyads2O5e2H2VBsk6n0An4dZtT59HQvN2F9b4gA,45771
|
|
10
15
|
lifx/devices/hev.py,sha256=kTRJRYnWyIY8Pkg_jOn978N-_1YXy9fRmBiGgEWscXw,15194
|
|
11
16
|
lifx/devices/infrared.py,sha256=ePk9qxX_s-hv5gQMvio1Vv8FYiCd68HF0ySbWgSrvuU,8130
|
|
12
17
|
lifx/devices/light.py,sha256=ZhC7zuruZ9nzmnAR_st2KMUH8UNQAcNK-eQUYnKXm-8,33833
|
|
13
|
-
lifx/devices/matrix.py,sha256=
|
|
18
|
+
lifx/devices/matrix.py,sha256=reB6cS2_cFe3qZKg584oCO-JGLbNTJDWwW9FZ0NLxq0,41693
|
|
14
19
|
lifx/devices/multizone.py,sha256=7Te5Z_X9hDvdypjMqPGGM2TG0P9QltzFVi7UUxRdbGI,33326
|
|
15
20
|
lifx/effects/__init__.py,sha256=4DF31yp7RJic5JoltMlz5dCtF5KQobU6NOUtLUKkVKE,1509
|
|
16
21
|
lifx/effects/base.py,sha256=tKgX5PsV6hipffD2B236rOzudkMwWq59-eQGnfvNKdU,10354
|
|
17
|
-
lifx/effects/colorloop.py,sha256=
|
|
22
|
+
lifx/effects/colorloop.py,sha256=LYlljT7XfaQ-S3mWNMWZ-3q7ce1cfl8nmyLb-VuXTZE,15592
|
|
18
23
|
lifx/effects/conductor.py,sha256=Oaq-6m1kdUF6bma_U9GcA9onZzh6YRjpExBr-OGHQJI,14552
|
|
19
24
|
lifx/effects/const.py,sha256=03LfL8v9PtoUs6-2IR3aa6nkyA4Otdno51SFJtntC-U,795
|
|
20
25
|
lifx/effects/models.py,sha256=MS5D-cxD0Ar8XhqbqKAc9q2sk38IP1vPkYwd8V7jCr8,2446
|
|
21
26
|
lifx/effects/pulse.py,sha256=k4dtBhhgVHyuwzqzx89jYVKbSRUVQdZj91cklyKarbE,8455
|
|
22
27
|
lifx/effects/state_manager.py,sha256=iDfYowiCN5IJqcR1s-pM0mQEJpe-RDsMcOOSMmtPVDE,8983
|
|
23
28
|
lifx/network/__init__.py,sha256=uSyA8r8qISG7qXUHbX8uk9A2E8rvDADgCcf94QIZ9so,499
|
|
24
|
-
lifx/network/connection.py,sha256=
|
|
25
|
-
lifx/network/discovery.py,sha256=
|
|
29
|
+
lifx/network/connection.py,sha256=_DiXNKN80ki266gg2e4kTWvcPxgYQJPJJUP8nVC6woU,38454
|
|
30
|
+
lifx/network/discovery.py,sha256=J0B3yRkbZKx7g01CCIEnjv3gtqSORhmdTYQ6w0ea4WI,24178
|
|
26
31
|
lifx/network/message.py,sha256=jCLC9v0tbBi54g5CaHLFM_nP1Izu8kJmo2tt23HHBbA,2600
|
|
27
|
-
lifx/network/transport.py,sha256=
|
|
32
|
+
lifx/network/transport.py,sha256=EykhKmvjAcdiepgCxgzDTj8Fc0b7kAQdyR8AumGXohg,10953
|
|
33
|
+
lifx/network/utils.py,sha256=WLxyPh0JbA4SUHgcSqENMSBB2R8eW_J2fKCpAKZUxt4,421
|
|
28
34
|
lifx/network/mdns/__init__.py,sha256=LlZgsFe6q5_SIXvXqtuZ_O9tJbcJZ-nsFkD2_wD8_TM,1412
|
|
29
35
|
lifx/network/mdns/discovery.py,sha256=EZ2zlJmy96rMDmu5J-68ystXJ2gYa18zTYP3iqmTGgU,13200
|
|
30
36
|
lifx/network/mdns/dns.py,sha256=OsvNSxLepIG3Nhw-kkQF3JrBYI-ikod5SHD2HO5_yGE,9363
|
|
31
|
-
lifx/network/mdns/transport.py,sha256=
|
|
37
|
+
lifx/network/mdns/transport.py,sha256=x1IzvOYSsMcTY1zGIg0Cv694KU6Pyp8CaY0YQUI_-Nc,10268
|
|
32
38
|
lifx/network/mdns/types.py,sha256=9fhH5iuMQxLkFPhmFTf2-kOcUNoWEu7LrN15Qr9tFE0,990
|
|
33
39
|
lifx/products/__init__.py,sha256=pf2O-fzt6nOrQd-wmzhiog91tMiGa-dDbaSNtU2ZQfE,764
|
|
34
40
|
lifx/products/generator.py,sha256=DsTCJcEVPmn9sfXSbXYdFZjqMfIbodnIQL46DRASs0g,15731
|
|
@@ -41,13 +47,13 @@ lifx/protocol/header.py,sha256=HaYQ5wEjAMgefO3dIxKb0w4VG4fLcfLj-fnHVwfp1ao,7174
|
|
|
41
47
|
lifx/protocol/models.py,sha256=eOvOSAWbglR1SYWcC_YpicewtsdbVlQ6E2lfcC4NQrk,8172
|
|
42
48
|
lifx/protocol/packets.py,sha256=ENp3irGITdV5rGah3eUzgsXqihI95upAPh7AdTsP7sk,43303
|
|
43
49
|
lifx/protocol/protocol_types.py,sha256=m15A82zVrwAXomTqo-GfNmAIynVRDSV94UqHDkWgiJI,23781
|
|
44
|
-
lifx/protocol/serializer.py,sha256=
|
|
50
|
+
lifx/protocol/serializer.py,sha256=IEnKKeCRy4Cwz03zkWhg-2pV3Lrro6cMa-3fhWYOngg,7714
|
|
45
51
|
lifx/theme/__init__.py,sha256=dg4Y25dYq22EemFyxQ1fyb3D_bP2hhxGCd9BE1g_hvk,1320
|
|
46
52
|
lifx/theme/canvas.py,sha256=4h7lgN8iu_OdchObGDgbxTqQLCb-FRKC-M-YCWef_i4,8048
|
|
47
53
|
lifx/theme/generators.py,sha256=nq3Yvntq_h-eFHbmmow3LcAdA_hEbRRaP5mv9Bydrjk,6435
|
|
48
54
|
lifx/theme/library.py,sha256=tKlKZNqJp8lRGDnilWyDm_Qr1vCRGGwuvWVS82anNpQ,21326
|
|
49
55
|
lifx/theme/theme.py,sha256=qMEx_8E41C0Cc6f083XHiAXEglTv4YlXW0UFsG1rQKg,5521
|
|
50
|
-
lifx_async-5.
|
|
51
|
-
lifx_async-5.
|
|
52
|
-
lifx_async-5.
|
|
53
|
-
lifx_async-5.
|
|
56
|
+
lifx_async-5.1.0.dist-info/METADATA,sha256=bT9ZFTUQCHcsnPwdyskaOeOfBQ_HP71_e-6dvtGn6SE,2660
|
|
57
|
+
lifx_async-5.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
58
|
+
lifx_async-5.1.0.dist-info/licenses/LICENSE,sha256=eBz48GRA3gSiWn3rYZAz2Ewp35snnhV9cSqkVBq7g3k,1832
|
|
59
|
+
lifx_async-5.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|