lifx-async 4.3.2__py3-none-any.whl → 4.3.4__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 +30 -6
- lifx/devices/matrix.py +75 -31
- lifx/network/connection.py +22 -11
- {lifx_async-4.3.2.dist-info → lifx_async-4.3.4.dist-info}/METADATA +1 -1
- {lifx_async-4.3.2.dist-info → lifx_async-4.3.4.dist-info}/RECORD +7 -7
- {lifx_async-4.3.2.dist-info → lifx_async-4.3.4.dist-info}/WHEEL +0 -0
- {lifx_async-4.3.2.dist-info → lifx_async-4.3.4.dist-info}/licenses/LICENSE +0 -0
lifx/devices/base.py
CHANGED
|
@@ -252,6 +252,8 @@ class Device:
|
|
|
252
252
|
self.serial = serial_obj.to_string()
|
|
253
253
|
self.ip = ip
|
|
254
254
|
self.port = port
|
|
255
|
+
self._timeout = timeout
|
|
256
|
+
self._max_retries = max_retries
|
|
255
257
|
|
|
256
258
|
# Create lightweight connection handle - connection pooling is internal
|
|
257
259
|
self.connection = DeviceConnection(
|
|
@@ -304,10 +306,16 @@ class Device:
|
|
|
304
306
|
```
|
|
305
307
|
"""
|
|
306
308
|
if serial is None:
|
|
307
|
-
temp_conn = DeviceConnection(
|
|
309
|
+
temp_conn = DeviceConnection(
|
|
310
|
+
serial="000000000000",
|
|
311
|
+
ip=ip,
|
|
312
|
+
port=port,
|
|
313
|
+
timeout=timeout,
|
|
314
|
+
max_retries=max_retries,
|
|
315
|
+
)
|
|
308
316
|
try:
|
|
309
317
|
response = await temp_conn.request(
|
|
310
|
-
packets.Device.GetService(), timeout=
|
|
318
|
+
packets.Device.GetService(), timeout=timeout
|
|
311
319
|
)
|
|
312
320
|
if response and isinstance(response, packets.Device.StateService):
|
|
313
321
|
if temp_conn.serial and temp_conn.serial != "000000000000":
|
|
@@ -890,9 +898,17 @@ class Device:
|
|
|
890
898
|
|
|
891
899
|
try:
|
|
892
900
|
# Check each device for the target label
|
|
893
|
-
async for disc in discover_devices(
|
|
901
|
+
async for disc in discover_devices(
|
|
902
|
+
timeout=discover_timeout,
|
|
903
|
+
device_timeout=self._timeout,
|
|
904
|
+
max_retries=self._max_retries,
|
|
905
|
+
):
|
|
894
906
|
temp_conn = DeviceConnection(
|
|
895
|
-
serial=disc.serial,
|
|
907
|
+
serial=disc.serial,
|
|
908
|
+
ip=disc.ip,
|
|
909
|
+
port=disc.port,
|
|
910
|
+
timeout=self._timeout,
|
|
911
|
+
max_retries=self._max_retries,
|
|
896
912
|
)
|
|
897
913
|
|
|
898
914
|
try:
|
|
@@ -1063,9 +1079,17 @@ class Device:
|
|
|
1063
1079
|
|
|
1064
1080
|
try:
|
|
1065
1081
|
# Check each device for the target label
|
|
1066
|
-
async for disc in discover_devices(
|
|
1082
|
+
async for disc in discover_devices(
|
|
1083
|
+
timeout=discover_timeout,
|
|
1084
|
+
device_timeout=self._timeout,
|
|
1085
|
+
max_retries=self._max_retries,
|
|
1086
|
+
):
|
|
1067
1087
|
temp_conn = DeviceConnection(
|
|
1068
|
-
serial=disc.serial,
|
|
1088
|
+
serial=disc.serial,
|
|
1089
|
+
ip=disc.ip,
|
|
1090
|
+
port=disc.port,
|
|
1091
|
+
timeout=self._timeout,
|
|
1092
|
+
max_retries=self._max_retries,
|
|
1069
1093
|
)
|
|
1070
1094
|
|
|
1071
1095
|
try:
|
lifx/devices/matrix.py
CHANGED
|
@@ -371,45 +371,58 @@ class MatrixLight(Light):
|
|
|
371
371
|
|
|
372
372
|
async def get64(
|
|
373
373
|
self,
|
|
374
|
-
tile_index: int,
|
|
375
|
-
length: int,
|
|
376
|
-
x: int,
|
|
377
|
-
y: int,
|
|
378
|
-
width: int,
|
|
379
|
-
fb_index: int = 0,
|
|
374
|
+
tile_index: int = 0,
|
|
375
|
+
length: int = 1,
|
|
376
|
+
x: int = 0,
|
|
377
|
+
y: int = 0,
|
|
378
|
+
width: int | None = None,
|
|
380
379
|
) -> list[HSBK]:
|
|
381
380
|
"""Get up to 64 zones of color state from a tile.
|
|
382
381
|
|
|
382
|
+
For devices with ≤64 zones, returns all zones. For devices with >64 zones,
|
|
383
|
+
returns up to 64 zones due to protocol limitations.
|
|
384
|
+
|
|
383
385
|
Args:
|
|
384
|
-
tile_index: Index of the tile (0-based)
|
|
385
|
-
length: Number of tiles to query (usually 1)
|
|
386
|
-
x: X coordinate of the rectangle (0-based)
|
|
387
|
-
y: Y coordinate of the rectangle (0-based)
|
|
388
|
-
width: Width of the rectangle in zones
|
|
389
|
-
fb_index: Frame buffer index (0 for display, 1 for temp buffer)
|
|
386
|
+
tile_index: Index of the tile (0-based). Defaults to 0.
|
|
387
|
+
length: Number of tiles to query (usually 1). Defaults to 1.
|
|
388
|
+
x: X coordinate of the rectangle (0-based). Defaults to 0.
|
|
389
|
+
y: Y coordinate of the rectangle (0-based). Defaults to 0.
|
|
390
|
+
width: Width of the rectangle in zones. Defaults to tile width.
|
|
390
391
|
|
|
391
392
|
Returns:
|
|
392
|
-
List of HSBK colors for the requested zones
|
|
393
|
+
List of HSBK colors for the requested zones. For tiles with ≤64 zones,
|
|
394
|
+
returns the actual zone count (e.g., 64 for 8x8, 16 for 4x4). For tiles
|
|
395
|
+
with >64 zones (e.g., 128 for 16x8 Ceiling), returns 64 (protocol limit).
|
|
393
396
|
|
|
394
397
|
Example:
|
|
395
|
-
>>> # Get colors from
|
|
396
|
-
>>> colors = await matrix.get64(
|
|
398
|
+
>>> # Get all colors from first tile (no parameters needed)
|
|
399
|
+
>>> colors = await matrix.get64()
|
|
400
|
+
>>>
|
|
401
|
+
>>> # Get colors from specific region
|
|
402
|
+
>>> colors = await matrix.get64(y=4) # Start at row 4
|
|
397
403
|
"""
|
|
398
404
|
# Validate parameters
|
|
399
405
|
if x < 0:
|
|
400
406
|
raise ValueError(f"x coordinate must be non-negative, got {x}")
|
|
401
407
|
if y < 0:
|
|
402
408
|
raise ValueError(f"y coordinate must be non-negative, got {y}")
|
|
403
|
-
if width <= 0:
|
|
409
|
+
if width is not None and width <= 0:
|
|
404
410
|
raise ValueError(f"width must be positive, got {width}")
|
|
405
411
|
|
|
412
|
+
if self._device_chain is None:
|
|
413
|
+
device_chain = await self.get_device_chain()
|
|
414
|
+
else:
|
|
415
|
+
device_chain = self._device_chain
|
|
416
|
+
|
|
417
|
+
if width is None:
|
|
418
|
+
width = device_chain[0].width
|
|
419
|
+
|
|
406
420
|
_LOGGER.debug(
|
|
407
|
-
"Getting 64 zones from tile %d (x=%d, y=%d, width=%d
|
|
421
|
+
"Getting 64 zones from tile %d (x=%d, y=%d, width=%d) for %s",
|
|
408
422
|
tile_index,
|
|
409
423
|
x,
|
|
410
424
|
y,
|
|
411
425
|
width,
|
|
412
|
-
fb_index,
|
|
413
426
|
self.label or self.serial,
|
|
414
427
|
)
|
|
415
428
|
|
|
@@ -417,12 +430,17 @@ class MatrixLight(Light):
|
|
|
417
430
|
packets.Tile.Get64(
|
|
418
431
|
tile_index=tile_index,
|
|
419
432
|
length=length,
|
|
420
|
-
rect=TileBufferRect(fb_index=
|
|
433
|
+
rect=TileBufferRect(fb_index=0, x=x, y=y, width=width),
|
|
421
434
|
)
|
|
422
435
|
)
|
|
423
436
|
|
|
437
|
+
max_colors = device_chain[0].width * device_chain[0].height
|
|
438
|
+
|
|
424
439
|
# Convert protocol colors to HSBK
|
|
425
|
-
return [
|
|
440
|
+
return [
|
|
441
|
+
HSBK.from_protocol(proto_color)
|
|
442
|
+
for proto_color in response.colors[:max_colors]
|
|
443
|
+
]
|
|
426
444
|
|
|
427
445
|
async def set64(
|
|
428
446
|
self,
|
|
@@ -504,7 +522,11 @@ class MatrixLight(Light):
|
|
|
504
522
|
)
|
|
505
523
|
|
|
506
524
|
async def copy_frame_buffer(
|
|
507
|
-
self,
|
|
525
|
+
self,
|
|
526
|
+
tile_index: int,
|
|
527
|
+
source_fb: int = 1,
|
|
528
|
+
target_fb: int = 0,
|
|
529
|
+
duration: float = 0.0,
|
|
508
530
|
) -> None:
|
|
509
531
|
"""Copy frame buffer (for tiles with >64 zones).
|
|
510
532
|
|
|
@@ -515,6 +537,7 @@ class MatrixLight(Light):
|
|
|
515
537
|
tile_index: Index of the tile (0-based)
|
|
516
538
|
source_fb: Source frame buffer index (usually 1)
|
|
517
539
|
target_fb: Target frame buffer index (usually 0)
|
|
540
|
+
duration: time in seconds to transition if target_fb is 0
|
|
518
541
|
|
|
519
542
|
Example:
|
|
520
543
|
>>> # For 16x8 tile (128 zones):
|
|
@@ -541,7 +564,9 @@ class MatrixLight(Light):
|
|
|
541
564
|
... fb_index=1,
|
|
542
565
|
... )
|
|
543
566
|
>>> # 3. Copy buffer 1 to buffer 0 (display)
|
|
544
|
-
>>> await matrix.copy_frame_buffer(
|
|
567
|
+
>>> await matrix.copy_frame_buffer(
|
|
568
|
+
... tile_index=0, source_fb=1, target_fb=0, duration=2.0
|
|
569
|
+
... )
|
|
545
570
|
"""
|
|
546
571
|
_LOGGER.debug(
|
|
547
572
|
"Copying frame buffer %d -> %d for tile %d on %s",
|
|
@@ -559,6 +584,7 @@ class MatrixLight(Light):
|
|
|
559
584
|
raise ValueError(f"Invalid tile_index {tile_index}")
|
|
560
585
|
|
|
561
586
|
tile = self._device_chain[tile_index]
|
|
587
|
+
duration_ms = round(duration * 1000 if duration else 0)
|
|
562
588
|
|
|
563
589
|
await self.connection.send_packet(
|
|
564
590
|
packets.Tile.CopyFrameBuffer(
|
|
@@ -572,7 +598,7 @@ class MatrixLight(Light):
|
|
|
572
598
|
dst_y=0,
|
|
573
599
|
width=tile.width,
|
|
574
600
|
height=tile.height,
|
|
575
|
-
duration=
|
|
601
|
+
duration=duration_ms,
|
|
576
602
|
)
|
|
577
603
|
)
|
|
578
604
|
|
|
@@ -723,7 +749,7 @@ class MatrixLight(Light):
|
|
|
723
749
|
async def set_effect(
|
|
724
750
|
self,
|
|
725
751
|
effect_type: FirmwareEffect,
|
|
726
|
-
speed:
|
|
752
|
+
speed: float = 3.0,
|
|
727
753
|
duration: int = 0,
|
|
728
754
|
palette: list[HSBK] | None = None,
|
|
729
755
|
sky_type: TileEffectSkyType = TileEffectSkyType.SUNRISE,
|
|
@@ -734,7 +760,7 @@ class MatrixLight(Light):
|
|
|
734
760
|
|
|
735
761
|
Args:
|
|
736
762
|
effect_type: Type of effect (OFF, MORPH, FLAME, SKY)
|
|
737
|
-
speed: Effect speed in
|
|
763
|
+
speed: Effect speed in seconds (default: 3)
|
|
738
764
|
duration: Total effect duration in nanoseconds (0 for infinite)
|
|
739
765
|
palette: Color palette for the effect (max 16 colors)
|
|
740
766
|
sky_type: Sky effect type (SUNRISE, SUNSET, CLOUDS)
|
|
@@ -751,7 +777,7 @@ class MatrixLight(Light):
|
|
|
751
777
|
... ]
|
|
752
778
|
>>> await matrix.set_effect(
|
|
753
779
|
... effect_type=FirmwareEffect.MORPH,
|
|
754
|
-
... speed=
|
|
780
|
+
... speed=5.0,
|
|
755
781
|
... palette=rainbow,
|
|
756
782
|
... )
|
|
757
783
|
"""
|
|
@@ -761,11 +787,12 @@ class MatrixLight(Light):
|
|
|
761
787
|
speed,
|
|
762
788
|
self.label or self.serial,
|
|
763
789
|
)
|
|
790
|
+
speed_ms = round(speed * 1000) if speed else 3000
|
|
764
791
|
|
|
765
792
|
# Create and validate MatrixEffect
|
|
766
793
|
effect = MatrixEffect(
|
|
767
794
|
effect_type=effect_type,
|
|
768
|
-
speed=
|
|
795
|
+
speed=speed_ms,
|
|
769
796
|
duration=duration,
|
|
770
797
|
palette=palette,
|
|
771
798
|
sky_type=sky_type,
|
|
@@ -843,9 +870,23 @@ class MatrixLight(Light):
|
|
|
843
870
|
# Create canvas and populate with theme colors
|
|
844
871
|
canvas = Canvas()
|
|
845
872
|
for tile in tiles:
|
|
846
|
-
canvas.add_points_for_tile(
|
|
847
|
-
|
|
848
|
-
|
|
873
|
+
canvas.add_points_for_tile((int(tile.user_x), int(tile.user_y)), theme)
|
|
874
|
+
canvas.shuffle_points()
|
|
875
|
+
canvas.blur_by_distance()
|
|
876
|
+
|
|
877
|
+
# Create tile canvas and fill in gaps for smooth interpolation
|
|
878
|
+
tile_canvas = Canvas()
|
|
879
|
+
for tile in tiles:
|
|
880
|
+
tile_canvas.fill_in_points(
|
|
881
|
+
canvas,
|
|
882
|
+
int(tile.user_x),
|
|
883
|
+
int(tile.user_y),
|
|
884
|
+
tile.width,
|
|
885
|
+
tile.height,
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
# Final blur for smooth gradients
|
|
889
|
+
tile_canvas.blur()
|
|
849
890
|
|
|
850
891
|
# Check if light is on
|
|
851
892
|
is_on = await self.get_power()
|
|
@@ -853,7 +894,10 @@ class MatrixLight(Light):
|
|
|
853
894
|
# Apply colors to each tile
|
|
854
895
|
for tile in tiles:
|
|
855
896
|
# Extract tile colors from canvas as 1D list
|
|
856
|
-
|
|
897
|
+
tile_coords = (int(tile.user_x), int(tile.user_y))
|
|
898
|
+
colors = tile_canvas.points_for_tile(
|
|
899
|
+
tile_coords, width=tile.width, height=tile.height
|
|
900
|
+
)
|
|
857
901
|
|
|
858
902
|
# Apply with appropriate timing
|
|
859
903
|
if power_on and not is_on:
|
lifx/network/connection.py
CHANGED
|
@@ -467,13 +467,10 @@ class DeviceConnection:
|
|
|
467
467
|
correlation_keys: list[tuple[int, int, str]] = []
|
|
468
468
|
|
|
469
469
|
# Calculate per-attempt timeouts with exponential backoff
|
|
470
|
-
#
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
else:
|
|
475
|
-
# Only one attempt total, use entire timeout
|
|
476
|
-
attempt_timeout = timeout
|
|
470
|
+
# Use proper exponential backoff distribution: timeout / (2^(n+1) - 1)
|
|
471
|
+
# This ensures total of all attempt timeouts equals the overall timeout budget
|
|
472
|
+
total_weight = (2 ** (max_retries + 1)) - 1
|
|
473
|
+
base_timeout = timeout / total_weight
|
|
477
474
|
|
|
478
475
|
# Idle timeout for multi-response protocols
|
|
479
476
|
# Stop streaming if no responses for this long after first response
|
|
@@ -482,14 +479,17 @@ class DeviceConnection:
|
|
|
482
479
|
last_error: Exception | None = None
|
|
483
480
|
has_yielded = False
|
|
484
481
|
overall_start = time.monotonic()
|
|
482
|
+
total_sleep_time = 0.0 # Track sleep time to exclude from timeout budget
|
|
485
483
|
|
|
486
484
|
try:
|
|
487
485
|
for attempt in range(max_retries + 1):
|
|
488
486
|
# Calculate current attempt timeout with exponential backoff
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
487
|
+
# Exclude sleep time from elapsed time to preserve timeout budget
|
|
488
|
+
elapsed_response_time = (
|
|
489
|
+
time.monotonic() - overall_start - total_sleep_time
|
|
492
490
|
)
|
|
491
|
+
ideal_timeout = base_timeout * (2**attempt)
|
|
492
|
+
current_timeout = min(ideal_timeout, timeout - elapsed_response_time)
|
|
493
493
|
|
|
494
494
|
# Check if we've exceeded overall timeout budget
|
|
495
495
|
if current_timeout <= 0:
|
|
@@ -616,6 +616,9 @@ class DeviceConnection:
|
|
|
616
616
|
# Sleep with jitter before retry
|
|
617
617
|
sleep_time = self._calculate_retry_sleep_with_jitter(attempt)
|
|
618
618
|
await asyncio.sleep(sleep_time)
|
|
619
|
+
total_sleep_time += (
|
|
620
|
+
sleep_time # Track sleep to exclude from timeout
|
|
621
|
+
)
|
|
619
622
|
continue
|
|
620
623
|
else:
|
|
621
624
|
# All retries exhausted
|
|
@@ -673,9 +676,14 @@ class DeviceConnection:
|
|
|
673
676
|
base_timeout = timeout / total_weight
|
|
674
677
|
|
|
675
678
|
last_error: Exception | None = None
|
|
679
|
+
total_sleep_time = 0.0 # Track sleep time to exclude from timeout budget
|
|
680
|
+
overall_start = time.monotonic()
|
|
676
681
|
|
|
677
682
|
for attempt in range(max_retries + 1):
|
|
678
|
-
|
|
683
|
+
# Calculate timeout with budget remaining after excluding sleep time
|
|
684
|
+
elapsed_response_time = time.monotonic() - overall_start - total_sleep_time
|
|
685
|
+
ideal_timeout = base_timeout * (2**attempt)
|
|
686
|
+
current_timeout = min(ideal_timeout, timeout - elapsed_response_time)
|
|
679
687
|
sequence = attempt
|
|
680
688
|
|
|
681
689
|
# Correlation key: (source, sequence, serial)
|
|
@@ -737,6 +745,9 @@ class DeviceConnection:
|
|
|
737
745
|
# Sleep with jitter before retry
|
|
738
746
|
sleep_time = self._calculate_retry_sleep_with_jitter(attempt)
|
|
739
747
|
await asyncio.sleep(sleep_time)
|
|
748
|
+
total_sleep_time += (
|
|
749
|
+
sleep_time # Track sleep to exclude from timeout
|
|
750
|
+
)
|
|
740
751
|
continue
|
|
741
752
|
else:
|
|
742
753
|
break
|
|
@@ -5,11 +5,11 @@ 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=
|
|
8
|
+
lifx/devices/base.py,sha256=uD3hQe2kjycRZneSptON6psOhoEgPRHVelCoLgdHbFw,41482
|
|
9
9
|
lifx/devices/hev.py,sha256=2zZNYm3TFrL755B4cRPNdYtcDLZEQwGl_22112WsSZc,9504
|
|
10
10
|
lifx/devices/infrared.py,sha256=TrCgJyEioIPlFumMmcSmuGYmRsSGlQ5Rllg6_9Wtg4Y,4248
|
|
11
11
|
lifx/devices/light.py,sha256=9rL24fa44Y7QrRBDSQuG6xpWsaPbQTTm4ExvrDnYWHo,27572
|
|
12
|
-
lifx/devices/matrix.py,sha256=
|
|
12
|
+
lifx/devices/matrix.py,sha256=zj_AE2C0WfSjMZw8X7Y6KkG1PUr0ViLC7zvVrVnWMPI,32504
|
|
13
13
|
lifx/devices/multizone.py,sha256=c-lXcp8c1Mhs8me6smGkqQFrOOxdoGjWrOO5HnAVooY,27209
|
|
14
14
|
lifx/effects/__init__.py,sha256=4DF31yp7RJic5JoltMlz5dCtF5KQobU6NOUtLUKkVKE,1509
|
|
15
15
|
lifx/effects/base.py,sha256=YO0Hbg2VYHKPtfYnWxmrtzYoPGOi9BUXhn8HVFKv5IM,10283
|
|
@@ -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=K5zzwYWKBvSBMqcP6a5Fp9JkVIiOLXJSAi1XMT4g-Go,38191
|
|
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.4.dist-info/METADATA,sha256=MkrET2BAkIwK9D5rlzSmJyM9pp2DyHzBbSjHLvF27AM,2609
|
|
44
|
+
lifx_async-4.3.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
45
|
+
lifx_async-4.3.4.dist-info/licenses/LICENSE,sha256=eBz48GRA3gSiWn3rYZAz2Ewp35snnhV9cSqkVBq7g3k,1832
|
|
46
|
+
lifx_async-4.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|