lifx-async 4.8.1__tar.gz → 4.9.0__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 (168) hide show
  1. {lifx_async-4.8.1 → lifx_async-4.9.0}/PKG-INFO +1 -1
  2. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/changelog.md +8 -0
  3. {lifx_async-4.8.1 → lifx_async-4.9.0}/pyproject.toml +2 -1
  4. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/color.py +223 -23
  5. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/conftest.py +29 -6
  6. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_matrix.py +8 -6
  7. {lifx_async-4.8.1 → lifx_async-4.9.0}/uv.lock +15 -1
  8. {lifx_async-4.8.1 → lifx_async-4.9.0}/.claude/settings.json +0 -0
  9. {lifx_async-4.8.1 → lifx_async-4.9.0}/.github/dependabot.yml +0 -0
  10. {lifx_async-4.8.1 → lifx_async-4.9.0}/.github/labeler.yml +0 -0
  11. {lifx_async-4.8.1 → lifx_async-4.9.0}/.github/workflows/ci.yml +0 -0
  12. {lifx_async-4.8.1 → lifx_async-4.9.0}/.github/workflows/docs.yml +0 -0
  13. {lifx_async-4.8.1 → lifx_async-4.9.0}/.github/workflows/pr-automation.yml +0 -0
  14. {lifx_async-4.8.1 → lifx_async-4.9.0}/.gitignore +0 -0
  15. {lifx_async-4.8.1 → lifx_async-4.9.0}/.pre-commit-config.yaml +0 -0
  16. {lifx_async-4.8.1 → lifx_async-4.9.0}/CLAUDE.md +0 -0
  17. {lifx_async-4.8.1 → lifx_async-4.9.0}/LICENSE +0 -0
  18. {lifx_async-4.8.1 → lifx_async-4.9.0}/README.md +0 -0
  19. {lifx_async-4.8.1 → lifx_async-4.9.0}/context7.json +0 -0
  20. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/colors.md +0 -0
  21. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/devices.md +0 -0
  22. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/effects.md +0 -0
  23. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/exceptions.md +0 -0
  24. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/high-level.md +0 -0
  25. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/index.md +0 -0
  26. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/network.md +0 -0
  27. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/protocol.md +0 -0
  28. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/api/themes.md +0 -0
  29. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/architecture/effects-architecture.md +0 -0
  30. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/architecture/overview.md +0 -0
  31. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/faq.md +0 -0
  32. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/getting-started/effects.md +0 -0
  33. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/getting-started/installation.md +0 -0
  34. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/getting-started/quickstart.md +0 -0
  35. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/getting-started/themes.md +0 -0
  36. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/index.md +0 -0
  37. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/migration/effect-api-changes.md +0 -0
  38. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/stylesheets/extra.css +0 -0
  39. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/user-guide/advanced-usage.md +0 -0
  40. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/user-guide/ceiling-lights.md +0 -0
  41. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/user-guide/effects-custom.md +0 -0
  42. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/user-guide/effects-troubleshooting.md +0 -0
  43. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/user-guide/protocol-deep-dive.md +0 -0
  44. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/user-guide/themes.md +0 -0
  45. {lifx_async-4.8.1 → lifx_async-4.9.0}/docs/user-guide/troubleshooting.md +0 -0
  46. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/01_simple_discovery.py +0 -0
  47. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/02_simple_control.py +0 -0
  48. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/03_waveforms.py +0 -0
  49. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/04_logging.py +0 -0
  50. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/06_pulse_effect.py +0 -0
  51. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/07_colorloop_effect.py +0 -0
  52. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/08_custom_effect.py +0 -0
  53. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/09_background_effect.py +0 -0
  54. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/10_find_specific_devices.py +0 -0
  55. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/11_matrix_basic.py +0 -0
  56. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/12_matrix_effects.py +0 -0
  57. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/13_matrix_large.py +0 -0
  58. {lifx_async-4.8.1 → lifx_async-4.9.0}/examples/14_mdns_discovery.py +0 -0
  59. {lifx_async-4.8.1 → lifx_async-4.9.0}/mkdocs.yml +0 -0
  60. {lifx_async-4.8.1 → lifx_async-4.9.0}/renovate.json +0 -0
  61. {lifx_async-4.8.1 → lifx_async-4.9.0}/scripts/mdns_probe.py +0 -0
  62. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/__init__.py +0 -0
  63. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/api.py +0 -0
  64. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/const.py +0 -0
  65. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/devices/__init__.py +0 -0
  66. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/devices/base.py +0 -0
  67. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/devices/ceiling.py +0 -0
  68. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/devices/hev.py +0 -0
  69. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/devices/infrared.py +0 -0
  70. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/devices/light.py +0 -0
  71. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/devices/matrix.py +0 -0
  72. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/devices/multizone.py +0 -0
  73. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/effects/__init__.py +0 -0
  74. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/effects/base.py +0 -0
  75. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/effects/colorloop.py +0 -0
  76. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/effects/conductor.py +0 -0
  77. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/effects/const.py +0 -0
  78. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/effects/models.py +0 -0
  79. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/effects/pulse.py +0 -0
  80. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/effects/state_manager.py +0 -0
  81. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/exceptions.py +0 -0
  82. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/__init__.py +0 -0
  83. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/connection.py +0 -0
  84. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/discovery.py +0 -0
  85. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/mdns/__init__.py +0 -0
  86. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/mdns/discovery.py +0 -0
  87. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/mdns/dns.py +0 -0
  88. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/mdns/transport.py +0 -0
  89. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/mdns/types.py +0 -0
  90. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/message.py +0 -0
  91. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/network/transport.py +0 -0
  92. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/products/__init__.py +0 -0
  93. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/products/generator.py +0 -0
  94. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/products/quirks.py +0 -0
  95. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/products/registry.py +0 -0
  96. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/protocol/__init__.py +0 -0
  97. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/protocol/base.py +0 -0
  98. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/protocol/generator.py +0 -0
  99. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/protocol/header.py +0 -0
  100. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/protocol/models.py +0 -0
  101. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/protocol/packets.py +0 -0
  102. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/protocol/protocol_types.py +0 -0
  103. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/protocol/serializer.py +0 -0
  104. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/py.typed +0 -0
  105. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/theme/__init__.py +0 -0
  106. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/theme/canvas.py +0 -0
  107. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/theme/generators.py +0 -0
  108. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/theme/library.py +0 -0
  109. {lifx_async-4.8.1 → lifx_async-4.9.0}/src/lifx/theme/theme.py +0 -0
  110. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/__init__.py +0 -0
  111. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_api/__init__.py +0 -0
  112. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_api/test_api_apply_theme.py +0 -0
  113. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_api/test_api_batch_errors.py +0 -0
  114. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_api/test_api_batch_operations.py +0 -0
  115. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_api/test_api_discovery.py +0 -0
  116. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_api/test_api_organization.py +0 -0
  117. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_color.py +0 -0
  118. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/__init__.py +0 -0
  119. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/conftest.py +0 -0
  120. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_base.py +0 -0
  121. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_ceiling.py +0 -0
  122. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_hev.py +0 -0
  123. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_infrared.py +0 -0
  124. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_light.py +0 -0
  125. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_mac_address.py +0 -0
  126. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_multizone.py +0 -0
  127. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_state_ceiling.py +0 -0
  128. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_state_hev.py +0 -0
  129. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_state_infrared.py +0 -0
  130. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_state_light.py +0 -0
  131. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_state_management.py +0 -0
  132. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_state_matrix.py +0 -0
  133. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_devices/test_state_multizone.py +0 -0
  134. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_effects/__init__.py +0 -0
  135. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_effects/test_base.py +0 -0
  136. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_effects/test_capability_filtering.py +0 -0
  137. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_effects/test_colorloop.py +0 -0
  138. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_effects/test_integration.py +0 -0
  139. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_effects/test_models.py +0 -0
  140. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_effects/test_pulse.py +0 -0
  141. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_effects/test_state_manager.py +0 -0
  142. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/__init__.py +0 -0
  143. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_concurrent_requests.py +0 -0
  144. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_connection.py +0 -0
  145. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_discovery_devices.py +0 -0
  146. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_discovery_errors.py +0 -0
  147. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_mdns/__init__.py +0 -0
  148. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_mdns/conftest.py +0 -0
  149. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_mdns/test_discovery.py +0 -0
  150. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_mdns/test_dns.py +0 -0
  151. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_mdns/test_transport.py +0 -0
  152. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_message.py +0 -0
  153. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_message_advanced.py +0 -0
  154. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_network/test_transport.py +0 -0
  155. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_products/test_product_generator.py +0 -0
  156. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_products/test_registry.py +0 -0
  157. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_protocol/test_generated.py +0 -0
  158. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_protocol/test_header.py +0 -0
  159. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_protocol/test_protocol_generator.py +0 -0
  160. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_protocol/test_serializer.py +0 -0
  161. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_theme/__init__.py +0 -0
  162. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_theme/conftest.py +0 -0
  163. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_theme/test_apply_theme.py +0 -0
  164. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_theme/test_canvas.py +0 -0
  165. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_theme/test_generators.py +0 -0
  166. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_theme/test_library.py +0 -0
  167. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_theme/test_theme.py +0 -0
  168. {lifx_async-4.8.1 → lifx_async-4.9.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lifx-async
3
- Version: 4.8.1
3
+ Version: 4.9.0
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.9.0 (2025-12-30)
6
+
7
+ ### Features
8
+
9
+ - **api**: Add HTML named colors and kelvin temperature presets
10
+ ([`b631d43`](https://github.com/Djelibeybi/lifx-async/commit/b631d43f9ba61db5f77e233a6bc0745bb3fed8b8))
11
+
12
+
5
13
  ## v4.8.1 (2025-12-24)
6
14
 
7
15
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lifx-async"
3
- version = "4.8.1"
3
+ version = "4.9.0"
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"
@@ -40,6 +40,7 @@ dev = [
40
40
  "pytest>=8.4.2",
41
41
  "pytest-asyncio>=0.24.0",
42
42
  "pytest-cov>=7.0.0",
43
+ "pytest-retry>=1.7.0",
43
44
  "pytest-sugar>=1.1.1",
44
45
  "pyyaml>=6.0.3",
45
46
  "ruff>=0.14.2",
@@ -10,9 +10,23 @@ import colorsys
10
10
  import math
11
11
 
12
12
  from lifx.const import (
13
+ KELVIN_AMBER,
14
+ KELVIN_BLUE_DAYLIGHT,
15
+ KELVIN_BLUE_ICE,
16
+ KELVIN_BLUE_OVERCAST,
17
+ KELVIN_BRIGHT_DAYLIGHT,
18
+ KELVIN_CANDLELIGHT,
19
+ KELVIN_CLOUDY_DAYLIGHT,
13
20
  KELVIN_COOL,
21
+ KELVIN_COOL_DAYLIGHT,
14
22
  KELVIN_DAYLIGHT,
23
+ KELVIN_INCANDESCENT,
15
24
  KELVIN_NEUTRAL,
25
+ KELVIN_NEUTRAL_WARM,
26
+ KELVIN_NOON_DAYLIGHT,
27
+ KELVIN_SOFT_DAYLIGHT,
28
+ KELVIN_SUNSET,
29
+ KELVIN_ULTRA_WARM,
16
30
  KELVIN_WARM,
17
31
  MAX_BRIGHTNESS,
18
32
  MAX_HUE,
@@ -520,34 +534,220 @@ class HSBK:
520
534
 
521
535
  # Common color presets
522
536
  class Colors:
523
- """Common color presets for convenience."""
524
-
525
- # Primary colors
526
- RED = HSBK(hue=0, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
527
- ORANGE = HSBK(hue=30, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
528
- YELLOW = HSBK(hue=60, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
529
- GREEN = HSBK(hue=120, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
530
- CYAN = HSBK(hue=180, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
531
- BLUE = HSBK(hue=240, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
532
- PURPLE = HSBK(hue=270, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
533
- MAGENTA = HSBK(hue=300, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
534
- PINK = HSBK(hue=330, saturation=1.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
535
-
536
- # White variants
537
- WHITE_WARM = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_WARM)
538
- WHITE_NEUTRAL = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
539
- WHITE_COOL = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_COOL)
540
- WHITE_DAYLIGHT = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_DAYLIGHT)
541
-
542
- # Pastels
537
+ """Common color presets for convenience.
538
+
539
+ Includes all 140 standard HTML/CSS named colors plus white temperature
540
+ variants and pastel variations.
541
+
542
+ HTML color reference: https://www.w3.org/TR/css-color-3/#svg-color
543
+ """
544
+
545
+ # Off / Black (brightness=0 turns light off or sets zone to black)
546
+ OFF = HSBK(hue=0, saturation=0.0, brightness=0.0, kelvin=KELVIN_NEUTRAL)
547
+
548
+ # HTML Named Colors (alphabetical order)
549
+ ALICE_BLUE = HSBK.from_rgb(240, 248, 255)
550
+ ANTIQUE_WHITE = HSBK.from_rgb(250, 235, 215)
551
+ AQUA = HSBK.from_rgb(0, 255, 255)
552
+ AQUAMARINE = HSBK.from_rgb(127, 255, 212)
553
+ AZURE = HSBK.from_rgb(240, 255, 255)
554
+ BEIGE = HSBK.from_rgb(245, 245, 220)
555
+ BISQUE = HSBK.from_rgb(255, 228, 196)
556
+ BLACK = HSBK.from_rgb(0, 0, 0)
557
+ BLANCHED_ALMOND = HSBK.from_rgb(255, 235, 205)
558
+ BLUE = HSBK.from_rgb(0, 0, 255)
559
+ BLUE_VIOLET = HSBK.from_rgb(138, 43, 226)
560
+ BROWN = HSBK.from_rgb(165, 42, 42)
561
+ BURLYWOOD = HSBK.from_rgb(222, 184, 135)
562
+ CADET_BLUE = HSBK.from_rgb(95, 158, 160)
563
+ CHARTREUSE = HSBK.from_rgb(127, 255, 0)
564
+ CHOCOLATE = HSBK.from_rgb(210, 105, 30)
565
+ CORAL = HSBK.from_rgb(255, 127, 80)
566
+ CORNFLOWER_BLUE = HSBK.from_rgb(100, 149, 237)
567
+ CORNSILK = HSBK.from_rgb(255, 248, 220)
568
+ CRIMSON = HSBK.from_rgb(220, 20, 60)
569
+ CYAN = HSBK.from_rgb(0, 255, 255)
570
+ DARK_BLUE = HSBK.from_rgb(0, 0, 139)
571
+ DARK_CYAN = HSBK.from_rgb(0, 139, 139)
572
+ DARK_GOLDENROD = HSBK.from_rgb(184, 134, 11)
573
+ DARK_GRAY = HSBK.from_rgb(169, 169, 169)
574
+ DARK_GREEN = HSBK.from_rgb(0, 100, 0)
575
+ DARK_GREY = HSBK.from_rgb(169, 169, 169)
576
+ DARK_KHAKI = HSBK.from_rgb(189, 183, 107)
577
+ DARK_MAGENTA = HSBK.from_rgb(139, 0, 139)
578
+ DARK_OLIVE_GREEN = HSBK.from_rgb(85, 107, 47)
579
+ DARK_ORANGE = HSBK.from_rgb(255, 140, 0)
580
+ DARK_ORCHID = HSBK.from_rgb(153, 50, 204)
581
+ DARK_RED = HSBK.from_rgb(139, 0, 0)
582
+ DARK_SALMON = HSBK.from_rgb(233, 150, 122)
583
+ DARK_SEA_GREEN = HSBK.from_rgb(143, 188, 143)
584
+ DARK_SLATE_BLUE = HSBK.from_rgb(72, 61, 139)
585
+ DARK_SLATE_GRAY = HSBK.from_rgb(47, 79, 79)
586
+ DARK_SLATE_GREY = HSBK.from_rgb(47, 79, 79)
587
+ DARK_TURQUOISE = HSBK.from_rgb(0, 206, 209)
588
+ DARK_VIOLET = HSBK.from_rgb(148, 0, 211)
589
+ DEEP_PINK = HSBK.from_rgb(255, 20, 147)
590
+ DEEP_SKY_BLUE = HSBK.from_rgb(0, 191, 255)
591
+ DIM_GRAY = HSBK.from_rgb(105, 105, 105)
592
+ DIM_GREY = HSBK.from_rgb(105, 105, 105)
593
+ DODGER_BLUE = HSBK.from_rgb(30, 144, 255)
594
+ FIREBRICK = HSBK.from_rgb(178, 34, 34)
595
+ FLORAL_WHITE = HSBK.from_rgb(255, 250, 240)
596
+ FOREST_GREEN = HSBK.from_rgb(34, 139, 34)
597
+ FUCHSIA = HSBK.from_rgb(255, 0, 255)
598
+ GAINSBORO = HSBK.from_rgb(220, 220, 220)
599
+ GHOST_WHITE = HSBK.from_rgb(248, 248, 255)
600
+ GOLD = HSBK.from_rgb(255, 215, 0)
601
+ GOLDENROD = HSBK.from_rgb(218, 165, 32)
602
+ GRAY = HSBK.from_rgb(128, 128, 128)
603
+ GREEN = HSBK.from_rgb(0, 128, 0)
604
+ GREEN_YELLOW = HSBK.from_rgb(173, 255, 47)
605
+ GREY = HSBK.from_rgb(128, 128, 128)
606
+ HONEYDEW = HSBK.from_rgb(240, 255, 240)
607
+ HOT_PINK = HSBK.from_rgb(255, 105, 180)
608
+ INDIAN_RED = HSBK.from_rgb(205, 92, 92)
609
+ INDIGO = HSBK.from_rgb(75, 0, 130)
610
+ IVORY = HSBK.from_rgb(255, 255, 240)
611
+ KHAKI = HSBK.from_rgb(240, 230, 140)
612
+ LAVENDER = HSBK.from_rgb(230, 230, 250)
613
+ LAVENDER_BLUSH = HSBK.from_rgb(255, 240, 245)
614
+ LAWN_GREEN = HSBK.from_rgb(124, 252, 0)
615
+ LEMON_CHIFFON = HSBK.from_rgb(255, 250, 205)
616
+ LIGHT_BLUE = HSBK.from_rgb(173, 216, 230)
617
+ LIGHT_CORAL = HSBK.from_rgb(240, 128, 128)
618
+ LIGHT_CYAN = HSBK.from_rgb(224, 255, 255)
619
+ LIGHT_GOLDENROD_YELLOW = HSBK.from_rgb(250, 250, 210)
620
+ LIGHT_GRAY = HSBK.from_rgb(211, 211, 211)
621
+ LIGHT_GREEN = HSBK.from_rgb(144, 238, 144)
622
+ LIGHT_GREY = HSBK.from_rgb(211, 211, 211)
623
+ LIGHT_PINK = HSBK.from_rgb(255, 182, 193)
624
+ LIGHT_SALMON = HSBK.from_rgb(255, 160, 122)
625
+ LIGHT_SEA_GREEN = HSBK.from_rgb(32, 178, 170)
626
+ LIGHT_SKY_BLUE = HSBK.from_rgb(135, 206, 250)
627
+ LIGHT_SLATE_GRAY = HSBK.from_rgb(119, 136, 153)
628
+ LIGHT_SLATE_GREY = HSBK.from_rgb(119, 136, 153)
629
+ LIGHT_STEEL_BLUE = HSBK.from_rgb(176, 196, 222)
630
+ LIGHT_YELLOW = HSBK.from_rgb(255, 255, 224)
631
+ LIME = HSBK.from_rgb(0, 255, 0)
632
+ LIME_GREEN = HSBK.from_rgb(50, 205, 50)
633
+ LINEN = HSBK.from_rgb(250, 240, 230)
634
+ MAGENTA = HSBK.from_rgb(255, 0, 255)
635
+ MAROON = HSBK.from_rgb(128, 0, 0)
636
+ MEDIUM_AQUAMARINE = HSBK.from_rgb(102, 205, 170)
637
+ MEDIUM_BLUE = HSBK.from_rgb(0, 0, 205)
638
+ MEDIUM_ORCHID = HSBK.from_rgb(186, 85, 211)
639
+ MEDIUM_PURPLE = HSBK.from_rgb(147, 112, 219)
640
+ MEDIUM_SEA_GREEN = HSBK.from_rgb(60, 179, 113)
641
+ MEDIUM_SLATE_BLUE = HSBK.from_rgb(123, 104, 238)
642
+ MEDIUM_SPRING_GREEN = HSBK.from_rgb(0, 250, 154)
643
+ MEDIUM_TURQUOISE = HSBK.from_rgb(72, 209, 204)
644
+ MEDIUM_VIOLET_RED = HSBK.from_rgb(199, 21, 133)
645
+ MIDNIGHT_BLUE = HSBK.from_rgb(25, 25, 112)
646
+ MINT_CREAM = HSBK.from_rgb(245, 255, 250)
647
+ MISTY_ROSE = HSBK.from_rgb(255, 228, 225)
648
+ MOCCASIN = HSBK.from_rgb(255, 228, 181)
649
+ NAVAJO_WHITE = HSBK.from_rgb(255, 222, 173)
650
+ NAVY = HSBK.from_rgb(0, 0, 128)
651
+ OLD_LACE = HSBK.from_rgb(253, 245, 230)
652
+ OLIVE = HSBK.from_rgb(128, 128, 0)
653
+ OLIVE_DRAB = HSBK.from_rgb(107, 142, 35)
654
+ ORANGE = HSBK.from_rgb(255, 165, 0)
655
+ ORANGE_RED = HSBK.from_rgb(255, 69, 0)
656
+ ORCHID = HSBK.from_rgb(218, 112, 214)
657
+ PALE_GOLDENROD = HSBK.from_rgb(238, 232, 170)
658
+ PALE_GREEN = HSBK.from_rgb(152, 251, 152)
659
+ PALE_TURQUOISE = HSBK.from_rgb(175, 238, 238)
660
+ PALE_VIOLET_RED = HSBK.from_rgb(219, 112, 147)
661
+ PAPAYA_WHIP = HSBK.from_rgb(255, 239, 213)
662
+ PEACH_PUFF = HSBK.from_rgb(255, 218, 185)
663
+ PERU = HSBK.from_rgb(205, 133, 63)
664
+ PINK = HSBK.from_rgb(255, 192, 203)
665
+ PLUM = HSBK.from_rgb(221, 160, 221)
666
+ POWDER_BLUE = HSBK.from_rgb(176, 224, 230)
667
+ PURPLE = HSBK.from_rgb(128, 0, 128)
668
+ REBECCA_PURPLE = HSBK.from_rgb(102, 51, 153)
669
+ RED = HSBK.from_rgb(255, 0, 0)
670
+ ROSY_BROWN = HSBK.from_rgb(188, 143, 143)
671
+ ROYAL_BLUE = HSBK.from_rgb(65, 105, 225)
672
+ SADDLE_BROWN = HSBK.from_rgb(139, 69, 19)
673
+ SALMON = HSBK.from_rgb(250, 128, 114)
674
+ SANDY_BROWN = HSBK.from_rgb(244, 164, 96)
675
+ SEA_GREEN = HSBK.from_rgb(46, 139, 87)
676
+ SEASHELL = HSBK.from_rgb(255, 245, 238)
677
+ SIENNA = HSBK.from_rgb(160, 82, 45)
678
+ SILVER = HSBK.from_rgb(192, 192, 192)
679
+ SKY_BLUE = HSBK.from_rgb(135, 206, 235)
680
+ SLATE_BLUE = HSBK.from_rgb(106, 90, 205)
681
+ SLATE_GRAY = HSBK.from_rgb(112, 128, 144)
682
+ SLATE_GREY = HSBK.from_rgb(112, 128, 144)
683
+ SNOW = HSBK.from_rgb(255, 250, 250)
684
+ SPRING_GREEN = HSBK.from_rgb(0, 255, 127)
685
+ STEEL_BLUE = HSBK.from_rgb(70, 130, 180)
686
+ TAN = HSBK.from_rgb(210, 180, 140)
687
+ TEAL = HSBK.from_rgb(0, 128, 128)
688
+ THISTLE = HSBK.from_rgb(216, 191, 216)
689
+ TOMATO = HSBK.from_rgb(255, 99, 71)
690
+ TURQUOISE = HSBK.from_rgb(64, 224, 208)
691
+ VIOLET = HSBK.from_rgb(238, 130, 238)
692
+ WHEAT = HSBK.from_rgb(245, 222, 179)
693
+ WHITE = HSBK.from_rgb(255, 255, 255)
694
+ WHITE_SMOKE = HSBK.from_rgb(245, 245, 245)
695
+ YELLOW = HSBK.from_rgb(255, 255, 0)
696
+ YELLOW_GREEN = HSBK.from_rgb(154, 205, 50)
697
+
698
+ # White temperature variants (kelvin-based, warmest to coolest)
699
+ CANDLELIGHT = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_CANDLELIGHT)
700
+ SUNSET = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_SUNSET)
701
+ AMBER = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_AMBER)
702
+ ULTRA_WARM = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_ULTRA_WARM)
703
+ INCANDESCENT = HSBK(
704
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_INCANDESCENT
705
+ )
706
+ WARM = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_WARM)
707
+ NEUTRAL_WARM = HSBK(
708
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_NEUTRAL_WARM
709
+ )
710
+ NEUTRAL = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_NEUTRAL)
711
+ COOL = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_COOL)
712
+ COOL_DAYLIGHT = HSBK(
713
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_COOL_DAYLIGHT
714
+ )
715
+ SOFT_DAYLIGHT = HSBK(
716
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_SOFT_DAYLIGHT
717
+ )
718
+ DAYLIGHT = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_DAYLIGHT)
719
+ NOON_DAYLIGHT = HSBK(
720
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_NOON_DAYLIGHT
721
+ )
722
+ BRIGHT_DAYLIGHT = HSBK(
723
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_BRIGHT_DAYLIGHT
724
+ )
725
+ CLOUDY_DAYLIGHT = HSBK(
726
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_CLOUDY_DAYLIGHT
727
+ )
728
+ BLUE_DAYLIGHT = HSBK(
729
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_BLUE_DAYLIGHT
730
+ )
731
+ BLUE_OVERCAST = HSBK(
732
+ hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_BLUE_OVERCAST
733
+ )
734
+ BLUE_ICE = HSBK(hue=0, saturation=0.0, brightness=1.0, kelvin=KELVIN_BLUE_ICE)
735
+
736
+ # Pastel variants (reduced saturation)
543
737
  PASTEL_RED = HSBK(hue=0, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
544
- PASTEL_ORANGE = HSBK(hue=30, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
738
+ PASTEL_ORANGE = HSBK(hue=39, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
545
739
  PASTEL_YELLOW = HSBK(hue=60, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
546
740
  PASTEL_GREEN = HSBK(hue=120, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
547
741
  PASTEL_CYAN = HSBK(hue=180, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
548
742
  PASTEL_BLUE = HSBK(hue=240, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
549
- PASTEL_PURPLE = HSBK(hue=270, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
743
+ PASTEL_PURPLE = HSBK(hue=300, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
550
744
  PASTEL_MAGENTA = HSBK(
551
745
  hue=300, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL
552
746
  )
553
- PASTEL_PINK = HSBK(hue=330, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
747
+ PASTEL_PINK = HSBK(hue=350, saturation=0.3, brightness=1.0, kelvin=KELVIN_NEUTRAL)
748
+
749
+ # Backwards compatibility aliases
750
+ WHITE_WARM = WARM
751
+ WHITE_NEUTRAL = NEUTRAL
752
+ WHITE_COOL = COOL
753
+ WHITE_DAYLIGHT = DAYLIGHT
@@ -10,8 +10,6 @@ from collections.abc import Generator
10
10
  from contextlib import contextmanager
11
11
 
12
12
  import pytest
13
-
14
- # Import emulator library components
15
13
  from lifx_emulator import EmulatedLifxServer
16
14
  from lifx_emulator.devices import DeviceManager
17
15
  from lifx_emulator.factories import (
@@ -33,9 +31,29 @@ from lifx.devices import HevLight, InfraredLight, Light, MultiZoneLight
33
31
  from lifx.devices.base import Device
34
32
  from lifx.devices.ceiling import CeilingLight
35
33
  from lifx.devices.matrix import MatrixLight
34
+ from lifx.exceptions import LifxConnectionError, LifxTimeoutError
36
35
  from lifx.network.connection import DeviceConnection
37
36
 
38
37
 
38
+ def pytest_addoption(parser: pytest.Parser) -> None:
39
+ """Add custom command-line options for pytest."""
40
+ parser.addoption(
41
+ "--disable-emulator",
42
+ action="store_true",
43
+ default=False,
44
+ help="Disable lifx-emulator tests for this test run",
45
+ )
46
+
47
+
48
+ def pytest_set_filtered_exceptions() -> list[type[Exception]]:
49
+ """Configure pytest-retry to only retry on network-related exceptions.
50
+
51
+ Tests that fail with LifxTimeoutError or LifxConnectionError will be
52
+ retried automatically, as these are typically transient network issues.
53
+ """
54
+ return [LifxTimeoutError, LifxConnectionError]
55
+
56
+
39
57
  def get_free_port() -> int:
40
58
  """Get a free UDP port."""
41
59
  with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
@@ -84,15 +102,20 @@ class EmulatorRunner:
84
102
 
85
103
 
86
104
  @pytest.fixture(scope="session")
87
- def emulator_available() -> bool:
105
+ def emulator_available(request: pytest.FixtureRequest) -> bool:
88
106
  """Check if lifx-emulator-core is available.
89
107
 
90
108
  The library is always available as it's a direct dependency.
91
- This fixture exists for backwards compatibility with test markers.
109
+ Emulator tests are enabled on all platforms by default. Use --disable-emulator
110
+ to disable them if needed.
111
+
112
+ Args:
113
+ request: Pytest fixture request for accessing command-line options
92
114
  """
93
- import sys
115
+ # Check command-line flags
116
+ disable_emulator = request.config.getoption("--disable-emulator", default=False)
94
117
 
95
- if sys.platform != "linux":
118
+ if disable_emulator:
96
119
  return False
97
120
 
98
121
  try:
@@ -563,7 +563,7 @@ class TestMatrixLight:
563
563
  2. Send two set64() messages to fb_index=1 (non-visible buffer)
564
564
  3. Send copy_frame_buffer() to copy fb_index=1 -> fb_index=0
565
565
  4. Send two get64() messages to retrieve all 128 zones from fb_index=0
566
- 5. Verify the colors match what we set (blue top half, green bottom half)
566
+ 5. Verify the colors match what we set (blue top half, lime bottom half)
567
567
  """
568
568
  matrix = ceiling_device
569
569
  async with matrix:
@@ -581,9 +581,11 @@ class TestMatrixLight:
581
581
  tile_index=0, colors=white_colors, duration=0
582
582
  )
583
583
 
584
- # Create colors: first 64 zones blue, second 64 zones green
584
+ # Create colors: first 64 zones blue, second 64 zones lime
585
+ # Note: Colors.LIME (RGB 0,255,0) has full brightness, while
586
+ # Colors.GREEN (RGB 0,128,0) follows CSS3 spec with 50% brightness
585
587
  blue_colors = [Colors.BLUE] * 64
586
- green_colors = [Colors.GREEN] * 64
588
+ lime_colors = [Colors.LIME] * 64
587
589
 
588
590
  # Step 1: Set first 64 zones (rows 0-3) to blue in frame buffer 1
589
591
  await matrix.set64(
@@ -597,7 +599,7 @@ class TestMatrixLight:
597
599
  fb_index=1, # Write to non-visible buffer
598
600
  )
599
601
 
600
- # Step 2: Set second 64 zones (rows 4-7) to green in frame buffer 1
602
+ # Step 2: Set second 64 zones (rows 4-7) to lime in frame buffer 1
601
603
  await matrix.set64(
602
604
  tile_index=0,
603
605
  length=1,
@@ -605,7 +607,7 @@ class TestMatrixLight:
605
607
  y=4, # Start at row 4 (after first 64 zones)
606
608
  width=tile.width,
607
609
  duration=0,
608
- colors=green_colors,
610
+ colors=lime_colors,
609
611
  fb_index=1, # Write to non-visible buffer
610
612
  )
611
613
 
@@ -628,7 +630,7 @@ class TestMatrixLight:
628
630
  assert first_64_colors[0].saturation > 0.9
629
631
  assert first_64_colors[0].brightness > 0.9
630
632
 
631
- # Verify second 64 zones are green (hue ~120)
633
+ # Verify second 64 zones are lime (hue ~120)
632
634
  assert 110 < second_64_colors[0].hue < 130
633
635
  assert second_64_colors[0].saturation > 0.9
634
636
  assert second_64_colors[0].brightness > 0.9
@@ -337,7 +337,7 @@ wheels = [
337
337
 
338
338
  [[package]]
339
339
  name = "lifx-async"
340
- version = "4.8.1"
340
+ version = "4.9.0"
341
341
  source = { editable = "." }
342
342
 
343
343
  [package.dev-dependencies]
@@ -352,6 +352,7 @@ dev = [
352
352
  { name = "pytest" },
353
353
  { name = "pytest-asyncio" },
354
354
  { name = "pytest-cov" },
355
+ { name = "pytest-retry" },
355
356
  { name = "pytest-sugar" },
356
357
  { name = "pyyaml" },
357
358
  { name = "ruff" },
@@ -371,6 +372,7 @@ dev = [
371
372
  { name = "pytest", specifier = ">=8.4.2" },
372
373
  { name = "pytest-asyncio", specifier = ">=0.24.0" },
373
374
  { name = "pytest-cov", specifier = ">=7.0.0" },
375
+ { name = "pytest-retry", specifier = ">=1.7.0" },
374
376
  { name = "pytest-sugar", specifier = ">=1.1.1" },
375
377
  { name = "pyyaml", specifier = ">=6.0.3" },
376
378
  { name = "ruff", specifier = ">=0.14.2" },
@@ -933,6 +935,18 @@ wheels = [
933
935
  { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" },
934
936
  ]
935
937
 
938
+ [[package]]
939
+ name = "pytest-retry"
940
+ version = "1.7.0"
941
+ source = { registry = "https://pypi.org/simple" }
942
+ dependencies = [
943
+ { name = "pytest" },
944
+ ]
945
+ sdist = { url = "https://files.pythonhosted.org/packages/c5/5b/607b017994cca28de3a1ad22a3eee8418e5d428dcd8ec25b26b18e995a73/pytest_retry-1.7.0.tar.gz", hash = "sha256:f8d52339f01e949df47c11ba9ee8d5b362f5824dff580d3870ec9ae0057df80f", size = 19977, upload-time = "2025-01-19T01:56:13.115Z" }
946
+ wheels = [
947
+ { url = "https://files.pythonhosted.org/packages/7c/ff/3266c8a73b9b93c4b14160a7e2b31d1e1088e28ed29f4c2d93ae34093bfd/pytest_retry-1.7.0-py3-none-any.whl", hash = "sha256:a2dac85b79a4e2375943f1429479c65beb6c69553e7dae6b8332be47a60954f4", size = 13775, upload-time = "2025-01-19T01:56:11.199Z" },
948
+ ]
949
+
936
950
  [[package]]
937
951
  name = "pytest-sugar"
938
952
  version = "1.1.1"
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