lifx-async 4.7.0__tar.gz → 4.7.1__tar.gz

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.
Files changed (156) hide show
  1. {lifx_async-4.7.0 → lifx_async-4.7.1}/PKG-INFO +1 -1
  2. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/changelog.md +8 -0
  3. {lifx_async-4.7.0 → lifx_async-4.7.1}/pyproject.toml +1 -1
  4. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/devices/matrix.py +10 -2
  5. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_matrix.py +31 -0
  6. {lifx_async-4.7.0 → lifx_async-4.7.1}/uv.lock +1 -1
  7. {lifx_async-4.7.0 → lifx_async-4.7.1}/.claude/settings.json +0 -0
  8. {lifx_async-4.7.0 → lifx_async-4.7.1}/.github/dependabot.yml +0 -0
  9. {lifx_async-4.7.0 → lifx_async-4.7.1}/.github/labeler.yml +0 -0
  10. {lifx_async-4.7.0 → lifx_async-4.7.1}/.github/workflows/ci.yml +0 -0
  11. {lifx_async-4.7.0 → lifx_async-4.7.1}/.github/workflows/docs.yml +0 -0
  12. {lifx_async-4.7.0 → lifx_async-4.7.1}/.github/workflows/pr-automation.yml +0 -0
  13. {lifx_async-4.7.0 → lifx_async-4.7.1}/.gitignore +0 -0
  14. {lifx_async-4.7.0 → lifx_async-4.7.1}/.pre-commit-config.yaml +0 -0
  15. {lifx_async-4.7.0 → lifx_async-4.7.1}/CLAUDE.md +0 -0
  16. {lifx_async-4.7.0 → lifx_async-4.7.1}/LICENSE +0 -0
  17. {lifx_async-4.7.0 → lifx_async-4.7.1}/README.md +0 -0
  18. {lifx_async-4.7.0 → lifx_async-4.7.1}/context7.json +0 -0
  19. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/colors.md +0 -0
  20. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/devices.md +0 -0
  21. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/effects.md +0 -0
  22. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/exceptions.md +0 -0
  23. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/high-level.md +0 -0
  24. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/index.md +0 -0
  25. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/network.md +0 -0
  26. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/protocol.md +0 -0
  27. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/api/themes.md +0 -0
  28. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/architecture/effects-architecture.md +0 -0
  29. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/architecture/overview.md +0 -0
  30. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/faq.md +0 -0
  31. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/getting-started/effects.md +0 -0
  32. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/getting-started/installation.md +0 -0
  33. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/getting-started/quickstart.md +0 -0
  34. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/getting-started/themes.md +0 -0
  35. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/index.md +0 -0
  36. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/migration/effect-api-changes.md +0 -0
  37. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/stylesheets/extra.css +0 -0
  38. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/user-guide/advanced-usage.md +0 -0
  39. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/user-guide/ceiling-lights.md +0 -0
  40. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/user-guide/effects-custom.md +0 -0
  41. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/user-guide/effects-troubleshooting.md +0 -0
  42. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/user-guide/protocol-deep-dive.md +0 -0
  43. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/user-guide/themes.md +0 -0
  44. {lifx_async-4.7.0 → lifx_async-4.7.1}/docs/user-guide/troubleshooting.md +0 -0
  45. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/01_simple_discovery.py +0 -0
  46. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/02_simple_control.py +0 -0
  47. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/03_waveforms.py +0 -0
  48. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/04_logging.py +0 -0
  49. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/06_pulse_effect.py +0 -0
  50. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/07_colorloop_effect.py +0 -0
  51. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/08_custom_effect.py +0 -0
  52. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/09_background_effect.py +0 -0
  53. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/10_find_specific_devices.py +0 -0
  54. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/11_matrix_basic.py +0 -0
  55. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/12_matrix_effects.py +0 -0
  56. {lifx_async-4.7.0 → lifx_async-4.7.1}/examples/13_matrix_large.py +0 -0
  57. {lifx_async-4.7.0 → lifx_async-4.7.1}/mkdocs.yml +0 -0
  58. {lifx_async-4.7.0 → lifx_async-4.7.1}/renovate.json +0 -0
  59. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/__init__.py +0 -0
  60. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/api.py +0 -0
  61. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/color.py +0 -0
  62. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/const.py +0 -0
  63. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/devices/__init__.py +0 -0
  64. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/devices/base.py +0 -0
  65. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/devices/ceiling.py +0 -0
  66. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/devices/hev.py +0 -0
  67. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/devices/infrared.py +0 -0
  68. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/devices/light.py +0 -0
  69. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/devices/multizone.py +0 -0
  70. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/effects/__init__.py +0 -0
  71. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/effects/base.py +0 -0
  72. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/effects/colorloop.py +0 -0
  73. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/effects/conductor.py +0 -0
  74. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/effects/const.py +0 -0
  75. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/effects/models.py +0 -0
  76. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/effects/pulse.py +0 -0
  77. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/effects/state_manager.py +0 -0
  78. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/exceptions.py +0 -0
  79. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/network/__init__.py +0 -0
  80. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/network/connection.py +0 -0
  81. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/network/discovery.py +0 -0
  82. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/network/message.py +0 -0
  83. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/network/transport.py +0 -0
  84. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/products/__init__.py +0 -0
  85. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/products/generator.py +0 -0
  86. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/products/quirks.py +0 -0
  87. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/products/registry.py +0 -0
  88. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/protocol/__init__.py +0 -0
  89. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/protocol/base.py +0 -0
  90. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/protocol/generator.py +0 -0
  91. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/protocol/header.py +0 -0
  92. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/protocol/models.py +0 -0
  93. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/protocol/packets.py +0 -0
  94. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/protocol/protocol_types.py +0 -0
  95. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/protocol/serializer.py +0 -0
  96. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/py.typed +0 -0
  97. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/theme/__init__.py +0 -0
  98. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/theme/canvas.py +0 -0
  99. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/theme/generators.py +0 -0
  100. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/theme/library.py +0 -0
  101. {lifx_async-4.7.0 → lifx_async-4.7.1}/src/lifx/theme/theme.py +0 -0
  102. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/__init__.py +0 -0
  103. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/conftest.py +0 -0
  104. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_api/__init__.py +0 -0
  105. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_api/test_api_apply_theme.py +0 -0
  106. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_api/test_api_batch_errors.py +0 -0
  107. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_api/test_api_batch_operations.py +0 -0
  108. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_api/test_api_discovery.py +0 -0
  109. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_api/test_api_organization.py +0 -0
  110. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_color.py +0 -0
  111. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/__init__.py +0 -0
  112. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/conftest.py +0 -0
  113. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_base.py +0 -0
  114. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_ceiling.py +0 -0
  115. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_hev.py +0 -0
  116. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_infrared.py +0 -0
  117. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_light.py +0 -0
  118. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_mac_address.py +0 -0
  119. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_multizone.py +0 -0
  120. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_state_ceiling.py +0 -0
  121. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_state_hev.py +0 -0
  122. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_state_infrared.py +0 -0
  123. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_state_light.py +0 -0
  124. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_state_management.py +0 -0
  125. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_state_matrix.py +0 -0
  126. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_devices/test_state_multizone.py +0 -0
  127. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_effects/__init__.py +0 -0
  128. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_effects/test_base.py +0 -0
  129. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_effects/test_capability_filtering.py +0 -0
  130. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_effects/test_colorloop.py +0 -0
  131. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_effects/test_integration.py +0 -0
  132. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_effects/test_models.py +0 -0
  133. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_effects/test_pulse.py +0 -0
  134. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_effects/test_state_manager.py +0 -0
  135. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_network/__init__.py +0 -0
  136. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_network/test_concurrent_requests.py +0 -0
  137. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_network/test_connection.py +0 -0
  138. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_network/test_discovery_devices.py +0 -0
  139. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_network/test_discovery_errors.py +0 -0
  140. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_network/test_message.py +0 -0
  141. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_network/test_message_advanced.py +0 -0
  142. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_network/test_transport.py +0 -0
  143. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_products/test_product_generator.py +0 -0
  144. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_products/test_registry.py +0 -0
  145. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_protocol/test_generated.py +0 -0
  146. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_protocol/test_header.py +0 -0
  147. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_protocol/test_protocol_generator.py +0 -0
  148. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_protocol/test_serializer.py +0 -0
  149. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_theme/__init__.py +0 -0
  150. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_theme/conftest.py +0 -0
  151. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_theme/test_apply_theme.py +0 -0
  152. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_theme/test_canvas.py +0 -0
  153. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_theme/test_generators.py +0 -0
  154. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_theme/test_library.py +0 -0
  155. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_theme/test_theme.py +0 -0
  156. {lifx_async-4.7.0 → lifx_async-4.7.1}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lifx-async
3
- Version: 4.7.0
3
+ Version: 4.7.1
4
4
  Summary: A modern, type-safe, async Python library for controlling LIFX lights
5
5
  Author-email: Avi Miller <me@dje.li>
6
6
  Maintainer-email: Avi Miller <me@dje.li>
@@ -2,6 +2,14 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v4.7.1 (2025-12-13)
6
+
7
+ ### Bug Fixes
8
+
9
+ - **devices**: Add length parameter to copy_frame_buffer()
10
+ ([`6a74690`](https://github.com/Djelibeybi/lifx-async/commit/6a746904665d38545e534829c2c690a61e48da54))
11
+
12
+
5
13
  ## v4.7.0 (2025-12-13)
6
14
 
7
15
  ### Features
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lifx-async"
3
- version = "4.7.0"
3
+ version = "4.7.1"
4
4
  description = "A modern, type-safe, async Python library for controlling LIFX lights"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -699,6 +699,7 @@ class MatrixLight(Light):
699
699
  source_fb: int = 1,
700
700
  target_fb: int = 0,
701
701
  duration: float = 0.0,
702
+ length: int = 1,
702
703
  ) -> None:
703
704
  """Copy frame buffer (for tiles with >64 zones).
704
705
 
@@ -710,6 +711,7 @@ class MatrixLight(Light):
710
711
  source_fb: Source frame buffer index (usually 1)
711
712
  target_fb: Target frame buffer index (usually 0)
712
713
  duration: time in seconds to transition if target_fb is 0
714
+ length: Number of tiles to update starting from tile_index (default 1)
713
715
 
714
716
  Example:
715
717
  >>> # For 16x8 tile (128 zones):
@@ -739,12 +741,18 @@ class MatrixLight(Light):
739
741
  >>> await matrix.copy_frame_buffer(
740
742
  ... tile_index=0, source_fb=1, target_fb=0, duration=2.0
741
743
  ... )
744
+
745
+ >>> # For a chain of 5 tiles, update all simultaneously:
746
+ >>> await matrix.copy_frame_buffer(
747
+ ... tile_index=0, source_fb=1, target_fb=0, length=5
748
+ ... )
742
749
  """
743
750
  _LOGGER.debug(
744
- "Copying frame buffer %d -> %d for tile %d on %s",
751
+ "Copying frame buffer %d -> %d for tile %d (length=%d) on %s",
745
752
  source_fb,
746
753
  target_fb,
747
754
  tile_index,
755
+ length,
748
756
  self.label or self.serial,
749
757
  )
750
758
 
@@ -761,7 +769,7 @@ class MatrixLight(Light):
761
769
  await self.connection.send_packet(
762
770
  packets.Tile.CopyFrameBuffer(
763
771
  tile_index=tile_index,
764
- length=1,
772
+ length=length,
765
773
  src_fb_index=source_fb,
766
774
  dst_fb_index=target_fb,
767
775
  src_x=0,
@@ -373,6 +373,37 @@ class TestMatrixLight:
373
373
  assert copied_colors[0].saturation == 1.0
374
374
  assert copied_colors[0].brightness == 1.0
375
375
 
376
+ async def test_copy_frame_buffer_with_length(self, emulator_devices) -> None:
377
+ """Test copy_frame_buffer() with explicit length parameter."""
378
+ matrix = emulator_devices[6]
379
+ async with matrix:
380
+ chain = await matrix.get_device_chain()
381
+ tile = chain[0]
382
+
383
+ # Set pattern on temp buffer (fb_index=1)
384
+ blue_colors = [Colors.BLUE] * 64
385
+ await matrix.set64(
386
+ tile_index=0,
387
+ length=1,
388
+ x=0,
389
+ y=0,
390
+ width=tile.width,
391
+ duration=0,
392
+ colors=blue_colors,
393
+ fb_index=1,
394
+ )
395
+
396
+ # Copy with explicit length=1 (same as default behavior)
397
+ await matrix.copy_frame_buffer(
398
+ tile_index=0, source_fb=1, target_fb=0, length=1
399
+ )
400
+
401
+ # Verify the copy worked
402
+ copied_colors = await matrix.get64()
403
+ assert copied_colors[0].hue == 240 # Blue
404
+ assert copied_colors[0].saturation == 1.0
405
+ assert copied_colors[0].brightness == 1.0
406
+
376
407
  async def test_set_effect_without_palette(self, emulator_devices) -> None:
377
408
  """Test setting effect without a palette (palette_count=0)."""
378
409
  matrix = emulator_devices[6]
@@ -337,7 +337,7 @@ wheels = [
337
337
 
338
338
  [[package]]
339
339
  name = "lifx-async"
340
- version = "4.7.0"
340
+ version = "4.7.1"
341
341
  source = { editable = "." }
342
342
 
343
343
  [package.dev-dependencies]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes