lifx-async 4.8.1__tar.gz → 5.0.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.
- {lifx_async-4.8.1 → lifx_async-5.0.0}/.github/workflows/ci.yml +4 -4
- {lifx_async-4.8.1 → lifx_async-5.0.0}/.github/workflows/docs.yml +3 -3
- {lifx_async-4.8.1 → lifx_async-5.0.0}/PKG-INFO +3 -2
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/changelog.md +21 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/pyproject.toml +9 -5
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/api.py +34 -36
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/color.py +223 -23
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/devices/base.py +25 -22
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/devices/hev.py +14 -18
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/devices/light.py +15 -15
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/devices/multizone.py +8 -12
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/effects/base.py +11 -7
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/effects/colorloop.py +4 -10
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/effects/conductor.py +43 -46
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/effects/pulse.py +4 -10
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/connection.py +1 -1
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/mdns/transport.py +4 -3
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/transport.py +70 -63
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/conftest.py +29 -6
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_api/test_api_batch_errors.py +30 -53
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_matrix.py +8 -6
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_connection.py +9 -7
- {lifx_async-4.8.1 → lifx_async-5.0.0}/uv.lock +127 -6
- {lifx_async-4.8.1 → lifx_async-5.0.0}/.claude/settings.json +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/.github/dependabot.yml +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/.github/labeler.yml +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/.github/workflows/pr-automation.yml +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/.gitignore +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/.pre-commit-config.yaml +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/CLAUDE.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/LICENSE +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/README.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/context7.json +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/colors.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/devices.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/effects.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/exceptions.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/high-level.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/index.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/network.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/protocol.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/api/themes.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/architecture/effects-architecture.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/architecture/overview.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/faq.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/getting-started/effects.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/getting-started/installation.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/getting-started/quickstart.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/getting-started/themes.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/index.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/migration/effect-api-changes.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/stylesheets/extra.css +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/user-guide/advanced-usage.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/user-guide/ceiling-lights.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/user-guide/effects-custom.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/user-guide/effects-troubleshooting.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/user-guide/protocol-deep-dive.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/user-guide/themes.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/docs/user-guide/troubleshooting.md +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/01_simple_discovery.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/02_simple_control.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/03_waveforms.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/04_logging.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/06_pulse_effect.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/07_colorloop_effect.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/08_custom_effect.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/09_background_effect.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/10_find_specific_devices.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/11_matrix_basic.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/12_matrix_effects.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/13_matrix_large.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/examples/14_mdns_discovery.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/mkdocs.yml +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/renovate.json +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/scripts/mdns_probe.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/const.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/devices/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/devices/ceiling.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/devices/infrared.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/devices/matrix.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/effects/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/effects/const.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/effects/models.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/effects/state_manager.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/exceptions.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/discovery.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/mdns/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/mdns/discovery.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/mdns/dns.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/mdns/types.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/network/message.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/products/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/products/generator.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/products/quirks.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/products/registry.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/protocol/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/protocol/base.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/protocol/generator.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/protocol/header.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/protocol/models.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/protocol/packets.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/protocol/protocol_types.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/protocol/serializer.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/py.typed +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/theme/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/theme/canvas.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/theme/generators.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/theme/library.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/src/lifx/theme/theme.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_api/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_api/test_api_apply_theme.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_api/test_api_batch_operations.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_api/test_api_discovery.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_api/test_api_organization.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_color.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/conftest.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_base.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_ceiling.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_hev.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_infrared.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_light.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_mac_address.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_multizone.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_state_ceiling.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_state_hev.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_state_infrared.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_state_light.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_state_management.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_state_matrix.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_devices/test_state_multizone.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_effects/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_effects/test_base.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_effects/test_capability_filtering.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_effects/test_colorloop.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_effects/test_integration.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_effects/test_models.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_effects/test_pulse.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_effects/test_state_manager.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_concurrent_requests.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_discovery_devices.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_discovery_errors.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_mdns/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_mdns/conftest.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_mdns/test_discovery.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_mdns/test_dns.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_mdns/test_transport.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_message.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_message_advanced.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_network/test_transport.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_products/test_product_generator.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_products/test_registry.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_protocol/test_generated.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_protocol/test_header.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_protocol/test_protocol_generator.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_protocol/test_serializer.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_theme/__init__.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_theme/conftest.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_theme/test_apply_theme.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_theme/test_canvas.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_theme/test_generators.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_theme/test_library.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_theme/test_theme.py +0 -0
- {lifx_async-4.8.1 → lifx_async-5.0.0}/tests/test_utils.py +0 -0
|
@@ -40,7 +40,7 @@ jobs:
|
|
|
40
40
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
41
41
|
|
|
42
42
|
- name: Install uv
|
|
43
|
-
uses: astral-sh/setup-uv@
|
|
43
|
+
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
|
|
44
44
|
with:
|
|
45
45
|
version: ${{ env.UV_VERSION }}
|
|
46
46
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
@@ -82,7 +82,7 @@ jobs:
|
|
|
82
82
|
python-version: ${{ matrix.python-version }}
|
|
83
83
|
|
|
84
84
|
- name: Install uv
|
|
85
|
-
uses: astral-sh/setup-uv@
|
|
85
|
+
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
|
|
86
86
|
with:
|
|
87
87
|
version: ${{ env.UV_VERSION }}
|
|
88
88
|
python-version: ${{ matrix.python-version }}
|
|
@@ -134,7 +134,7 @@ jobs:
|
|
|
134
134
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
135
135
|
|
|
136
136
|
- name: Install uv
|
|
137
|
-
uses: astral-sh/setup-uv@
|
|
137
|
+
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
|
|
138
138
|
with:
|
|
139
139
|
version: ${{ env.UV_VERSION }}
|
|
140
140
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
@@ -198,7 +198,7 @@ jobs:
|
|
|
198
198
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
199
199
|
|
|
200
200
|
- name: Install uv
|
|
201
|
-
uses: astral-sh/setup-uv@
|
|
201
|
+
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
|
|
202
202
|
with:
|
|
203
203
|
version: ${{ env.UV_VERSION }}
|
|
204
204
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
@@ -35,7 +35,7 @@ jobs:
|
|
|
35
35
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
36
36
|
|
|
37
37
|
- name: Install uv
|
|
38
|
-
uses: astral-sh/setup-uv@
|
|
38
|
+
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
|
|
39
39
|
with:
|
|
40
40
|
version: ${{ env.UV_VERSION }}
|
|
41
41
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
@@ -69,7 +69,7 @@ jobs:
|
|
|
69
69
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
70
70
|
|
|
71
71
|
- name: Install uv
|
|
72
|
-
uses: astral-sh/setup-uv@
|
|
72
|
+
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
|
|
73
73
|
with:
|
|
74
74
|
version: ${{ env.UV_VERSION }}
|
|
75
75
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
@@ -97,7 +97,7 @@ jobs:
|
|
|
97
97
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
98
98
|
|
|
99
99
|
- name: Install uv
|
|
100
|
-
uses: astral-sh/setup-uv@
|
|
100
|
+
uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7
|
|
101
101
|
with:
|
|
102
102
|
version: ${{ env.UV_VERSION }}
|
|
103
103
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lifx-async
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.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>
|
|
@@ -11,6 +11,7 @@ Classifier: Framework :: Pytest
|
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Natural Language :: English
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
@@ -18,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
18
19
|
Classifier: Topic :: Software Development :: Libraries
|
|
19
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
21
|
Classifier: Typing :: Typed
|
|
21
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.10
|
|
22
23
|
Description-Content-Type: text/markdown
|
|
23
24
|
|
|
24
25
|
# lifx-async
|
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v5.0.0 (2026-01-12)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- Add Python 3.10 support
|
|
10
|
+
([`7c39131`](https://github.com/Djelibeybi/lifx-async/commit/7c391314305bb856d8bbcd23a5e481b729a5ad04))
|
|
11
|
+
|
|
12
|
+
### Breaking Changes
|
|
13
|
+
|
|
14
|
+
- Batch operations now raise first exception immediately (asyncio.gather behavior) instead of
|
|
15
|
+
collecting all exceptions into an ExceptionGroup (TaskGroup behavior).
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## v4.9.0 (2025-12-30)
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
- **api**: Add HTML named colors and kelvin temperature presets
|
|
23
|
+
([`b631d43`](https://github.com/Djelibeybi/lifx-async/commit/b631d43f9ba61db5f77e233a6bc0745bb3fed8b8))
|
|
24
|
+
|
|
25
|
+
|
|
5
26
|
## v4.8.1 (2025-12-24)
|
|
6
27
|
|
|
7
28
|
### Bug Fixes
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lifx-async"
|
|
3
|
-
version = "
|
|
3
|
+
version = "5.0.0"
|
|
4
4
|
description = "A modern, type-safe, async Python library for controlling LIFX lights"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
7
|
dependencies = []
|
|
8
8
|
license = "UPL-1.0"
|
|
9
9
|
license-files = ["LICENSE"]
|
|
@@ -19,6 +19,7 @@ classifiers = [
|
|
|
19
19
|
"Intended Audience :: Developers",
|
|
20
20
|
"Natural Language :: English",
|
|
21
21
|
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
22
23
|
"Programming Language :: Python :: 3.11",
|
|
23
24
|
"Programming Language :: Python :: 3.12",
|
|
24
25
|
"Programming Language :: Python :: 3.13",
|
|
@@ -31,7 +32,7 @@ classifiers = [
|
|
|
31
32
|
[dependency-groups]
|
|
32
33
|
dev = [
|
|
33
34
|
"hatchling>=1.27.0",
|
|
34
|
-
"lifx-emulator-core>=3.0
|
|
35
|
+
"lifx-emulator-core>=3.1.0",
|
|
35
36
|
"mkdocs-git-revision-date-localized-plugin>=1.4.7",
|
|
36
37
|
"mkdocs-llmstxt>=0.4.0",
|
|
37
38
|
"mkdocs-material>=9.6.22",
|
|
@@ -40,9 +41,11 @@ dev = [
|
|
|
40
41
|
"pytest>=8.4.2",
|
|
41
42
|
"pytest-asyncio>=0.24.0",
|
|
42
43
|
"pytest-cov>=7.0.0",
|
|
44
|
+
"pytest-retry>=1.7.0",
|
|
43
45
|
"pytest-sugar>=1.1.1",
|
|
44
46
|
"pyyaml>=6.0.3",
|
|
45
47
|
"ruff>=0.14.2",
|
|
48
|
+
"typing-extensions>=4.15.0",
|
|
46
49
|
]
|
|
47
50
|
|
|
48
51
|
[build-system]
|
|
@@ -55,7 +58,7 @@ packages = ["src/lifx"]
|
|
|
55
58
|
[tool.ruff]
|
|
56
59
|
line-length = 88
|
|
57
60
|
indent-width = 4
|
|
58
|
-
target-version = "
|
|
61
|
+
target-version = "py310"
|
|
59
62
|
|
|
60
63
|
[tool.ruff.format]
|
|
61
64
|
quote-style = "double"
|
|
@@ -71,10 +74,11 @@ ignore = []
|
|
|
71
74
|
"src/lifx/{protocol,products}/generator.py" = ["E501"]
|
|
72
75
|
"src/lifx/protocol/packets.py" = ["E501"]
|
|
73
76
|
"src/lifx/products/registry.py" = ["E501"]
|
|
77
|
+
"benchmarks/*.py" = ["E501"]
|
|
74
78
|
|
|
75
79
|
[tool.pyright]
|
|
76
80
|
typeCheckingMode = "standard"
|
|
77
|
-
pythonVersion = "3.
|
|
81
|
+
pythonVersion = "3.10"
|
|
78
82
|
include = ["src"]
|
|
79
83
|
exclude = [
|
|
80
84
|
"**/__pycache__",
|
|
@@ -198,9 +198,7 @@ class DeviceGroup:
|
|
|
198
198
|
await group.set_power(True, duration=1.0)
|
|
199
199
|
```
|
|
200
200
|
"""
|
|
201
|
-
|
|
202
|
-
for light in self.lights:
|
|
203
|
-
tg.create_task(light.set_power(on, duration))
|
|
201
|
+
await asyncio.gather(*(light.set_power(on, duration) for light in self.lights))
|
|
204
202
|
|
|
205
203
|
async def set_color(self, color: HSBK, duration: float = 0.0) -> None:
|
|
206
204
|
"""Set color for all Light devices in the group.
|
|
@@ -218,9 +216,9 @@ class DeviceGroup:
|
|
|
218
216
|
await group.set_color(HSBK.from_rgb(255, 0, 0), duration=2.0)
|
|
219
217
|
```
|
|
220
218
|
"""
|
|
221
|
-
|
|
222
|
-
for light in self.lights
|
|
223
|
-
|
|
219
|
+
await asyncio.gather(
|
|
220
|
+
*(light.set_color(color, duration) for light in self.lights)
|
|
221
|
+
)
|
|
224
222
|
|
|
225
223
|
async def set_brightness(self, brightness: float, duration: float = 0.0) -> None:
|
|
226
224
|
"""Set brightness for all Light devices in the group.
|
|
@@ -238,9 +236,9 @@ class DeviceGroup:
|
|
|
238
236
|
await group.set_brightness(0.5, duration=1.0)
|
|
239
237
|
```
|
|
240
238
|
"""
|
|
241
|
-
|
|
242
|
-
for light in self.lights
|
|
243
|
-
|
|
239
|
+
await asyncio.gather(
|
|
240
|
+
*(light.set_brightness(brightness, duration) for light in self.lights)
|
|
241
|
+
)
|
|
244
242
|
|
|
245
243
|
async def pulse(
|
|
246
244
|
self, color: HSBK, period: float = 1.0, cycles: float = 1.0
|
|
@@ -261,9 +259,9 @@ class DeviceGroup:
|
|
|
261
259
|
await group.pulse(Colors.RED, period=1.0, cycles=1.0)
|
|
262
260
|
```
|
|
263
261
|
"""
|
|
264
|
-
|
|
265
|
-
for light in self.lights
|
|
266
|
-
|
|
262
|
+
await asyncio.gather(
|
|
263
|
+
*(light.pulse(color, period, cycles) for light in self.lights)
|
|
264
|
+
)
|
|
267
265
|
|
|
268
266
|
# Location and Group Organization Methods
|
|
269
267
|
|
|
@@ -280,14 +278,13 @@ class DeviceGroup:
|
|
|
280
278
|
)
|
|
281
279
|
|
|
282
280
|
# Fetch all location info concurrently
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
tasks[device.serial] = tg.create_task(device.get_location())
|
|
281
|
+
location_results = await asyncio.gather(
|
|
282
|
+
*(device.get_location() for device in self._devices)
|
|
283
|
+
)
|
|
287
284
|
|
|
288
|
-
results: list[tuple[Device, CollectionInfo | None]] =
|
|
289
|
-
|
|
290
|
-
|
|
285
|
+
results: list[tuple[Device, CollectionInfo | None]] = list(
|
|
286
|
+
zip(self._devices, location_results)
|
|
287
|
+
)
|
|
291
288
|
|
|
292
289
|
# Group by location UUID
|
|
293
290
|
for device, location_info in results:
|
|
@@ -332,15 +329,14 @@ class DeviceGroup:
|
|
|
332
329
|
# Collect group info from all devices concurrently
|
|
333
330
|
group_data: dict[str, list[tuple[Device, CollectionInfo]]] = defaultdict(list)
|
|
334
331
|
|
|
335
|
-
tasks: dict[str, asyncio.Task[CollectionInfo | None]] = {}
|
|
336
|
-
async with asyncio.TaskGroup() as tg:
|
|
337
|
-
for device in self._devices:
|
|
338
|
-
tasks[device.serial] = tg.create_task(device.get_group())
|
|
339
|
-
|
|
340
332
|
# Fetch all group info concurrently
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
333
|
+
group_results = await asyncio.gather(
|
|
334
|
+
*(device.get_group() for device in self._devices)
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
results: list[tuple[Device, CollectionInfo | None]] = list(
|
|
338
|
+
zip(self._devices, group_results)
|
|
339
|
+
)
|
|
344
340
|
|
|
345
341
|
# Group by group UUID
|
|
346
342
|
for device, group_info in results:
|
|
@@ -712,18 +708,20 @@ class DeviceGroup:
|
|
|
712
708
|
await group.apply_theme(evening, power_on=True, duration=1.0)
|
|
713
709
|
```
|
|
714
710
|
"""
|
|
715
|
-
|
|
711
|
+
await asyncio.gather(
|
|
716
712
|
# Apply theme to all lights
|
|
717
|
-
for light in self.lights
|
|
718
|
-
tg.create_task(light.apply_theme(theme, power_on, duration))
|
|
719
|
-
|
|
713
|
+
*(light.apply_theme(theme, power_on, duration) for light in self.lights),
|
|
720
714
|
# Apply theme to all multizone lights
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
715
|
+
*(
|
|
716
|
+
multizone.apply_theme(theme, power_on, duration)
|
|
717
|
+
for multizone in self.multizone_lights
|
|
718
|
+
),
|
|
724
719
|
# Apply theme to all matrix light devices
|
|
725
|
-
|
|
726
|
-
|
|
720
|
+
*(
|
|
721
|
+
matrix.apply_theme(theme, power_on, duration)
|
|
722
|
+
for matrix in self.matrix_lights
|
|
723
|
+
),
|
|
724
|
+
)
|
|
727
725
|
|
|
728
726
|
def invalidate_metadata_cache(self) -> None:
|
|
729
727
|
"""Clear all cached location and group metadata.
|
|
@@ -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
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
@@ -9,7 +9,7 @@ import time
|
|
|
9
9
|
import uuid
|
|
10
10
|
from dataclasses import dataclass, field
|
|
11
11
|
from math import floor, log10
|
|
12
|
-
from typing import TYPE_CHECKING, Generic,
|
|
12
|
+
from typing import TYPE_CHECKING, Generic, TypeVar, cast
|
|
13
13
|
|
|
14
14
|
from lifx.const import (
|
|
15
15
|
DEFAULT_MAX_RETRIES,
|
|
@@ -26,6 +26,8 @@ from lifx.protocol import packets
|
|
|
26
26
|
from lifx.protocol.models import Serial
|
|
27
27
|
|
|
28
28
|
if TYPE_CHECKING:
|
|
29
|
+
from typing_extensions import Self
|
|
30
|
+
|
|
29
31
|
from lifx.devices import (
|
|
30
32
|
CeilingLight,
|
|
31
33
|
HevLight,
|
|
@@ -681,12 +683,13 @@ class Device(Generic[StateT]):
|
|
|
681
683
|
async def _setup(self) -> None:
|
|
682
684
|
"""Populate device capabilities, state and metadata."""
|
|
683
685
|
await self._ensure_capabilities()
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
686
|
+
await asyncio.gather(
|
|
687
|
+
self.get_host_firmware(),
|
|
688
|
+
self.get_wifi_firmware(),
|
|
689
|
+
self.get_label(),
|
|
690
|
+
self.get_location(),
|
|
691
|
+
self.get_group(),
|
|
692
|
+
)
|
|
690
693
|
|
|
691
694
|
async def get_mac_address(self) -> str:
|
|
692
695
|
"""Calculate and return the MAC address for this device."""
|
|
@@ -1669,21 +1672,21 @@ class Device(Generic[StateT]):
|
|
|
1669
1672
|
|
|
1670
1673
|
# Fetch semi-static and volatile state in parallel
|
|
1671
1674
|
# get_color returns color, power, and label in one request
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1675
|
+
(
|
|
1676
|
+
label,
|
|
1677
|
+
power,
|
|
1678
|
+
host_firmware,
|
|
1679
|
+
wifi_firmware,
|
|
1680
|
+
location_info,
|
|
1681
|
+
group_info,
|
|
1682
|
+
) = await asyncio.gather(
|
|
1683
|
+
self.get_label(),
|
|
1684
|
+
self.get_power(),
|
|
1685
|
+
self.get_host_firmware(),
|
|
1686
|
+
self.get_wifi_firmware(),
|
|
1687
|
+
self.get_location(),
|
|
1688
|
+
self.get_group(),
|
|
1689
|
+
)
|
|
1687
1690
|
|
|
1688
1691
|
# Get MAC address (already calculated in get_host_firmware)
|
|
1689
1692
|
mac_address = await self.get_mac_address()
|