lifx-async 4.5.0__tar.gz → 4.6.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.5.0 → lifx_async-4.6.0}/.github/workflows/ci.yml +4 -4
- {lifx_async-4.5.0 → lifx_async-4.6.0}/.github/workflows/docs.yml +3 -3
- {lifx_async-4.5.0 → lifx_async-4.6.0}/PKG-INFO +1 -1
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/devices.md +57 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/changelog.md +16 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/user-guide/advanced-usage.md +6 -0
- lifx_async-4.6.0/docs/user-guide/ceiling-lights.md +330 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/mkdocs.yml +2 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/pyproject.toml +1 -1
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/__init__.py +4 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/devices/__init__.py +2 -1
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/devices/base.py +17 -3
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/devices/ceiling.py +150 -2
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/devices/matrix.py +1 -4
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_ceiling.py +743 -0
- lifx_async-4.6.0/tests/test_devices/test_state_ceiling.py +426 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/uv.lock +1 -1
- {lifx_async-4.5.0 → lifx_async-4.6.0}/.claude/settings.json +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/.github/dependabot.yml +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/.github/labeler.yml +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/.github/workflows/pr-automation.yml +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/.gitignore +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/.pre-commit-config.yaml +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/CLAUDE.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/LICENSE +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/README.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/context7.json +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/colors.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/effects.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/exceptions.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/high-level.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/index.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/network.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/protocol.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/api/themes.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/architecture/effects-architecture.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/architecture/overview.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/faq.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/getting-started/effects.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/getting-started/installation.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/getting-started/quickstart.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/getting-started/themes.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/index.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/migration/effect-api-changes.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/stylesheets/extra.css +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/user-guide/effects-custom.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/user-guide/effects-troubleshooting.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/user-guide/protocol-deep-dive.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/user-guide/themes.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/docs/user-guide/troubleshooting.md +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/01_simple_discovery.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/02_simple_control.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/03_waveforms.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/04_logging.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/06_pulse_effect.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/07_colorloop_effect.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/08_custom_effect.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/09_background_effect.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/10_find_specific_devices.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/11_matrix_basic.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/12_matrix_effects.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/examples/13_matrix_large.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/renovate.json +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/api.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/color.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/const.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/devices/hev.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/devices/infrared.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/devices/light.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/devices/multizone.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/effects/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/effects/base.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/effects/colorloop.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/effects/conductor.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/effects/const.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/effects/models.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/effects/pulse.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/effects/state_manager.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/exceptions.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/network/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/network/connection.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/network/discovery.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/network/message.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/network/transport.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/products/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/products/generator.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/products/quirks.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/products/registry.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/protocol/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/protocol/base.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/protocol/generator.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/protocol/header.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/protocol/models.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/protocol/packets.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/protocol/protocol_types.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/protocol/serializer.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/py.typed +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/theme/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/theme/canvas.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/theme/generators.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/theme/library.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/src/lifx/theme/theme.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/conftest.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_api/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_api/test_api_apply_theme.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_api/test_api_batch_errors.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_api/test_api_batch_operations.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_api/test_api_discovery.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_api/test_api_organization.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_color.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/conftest.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_base.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_hev.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_infrared.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_light.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_mac_address.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_matrix.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_multizone.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_state_hev.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_state_infrared.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_state_light.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_state_management.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_state_matrix.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_devices/test_state_multizone.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_effects/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_effects/test_base.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_effects/test_capability_filtering.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_effects/test_colorloop.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_effects/test_integration.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_effects/test_models.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_effects/test_pulse.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_effects/test_state_manager.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_network/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_network/test_concurrent_requests.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_network/test_connection.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_network/test_discovery_devices.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_network/test_discovery_errors.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_network/test_message.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_network/test_message_advanced.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_network/test_transport.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_products/test_product_generator.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_products/test_registry.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_protocol/test_generated.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_protocol/test_header.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_protocol/test_protocol_generator.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_protocol/test_serializer.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_theme/__init__.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_theme/conftest.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_theme/test_apply_theme.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_theme/test_canvas.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_theme/test_generators.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_theme/test_library.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.0}/tests/test_theme/test_theme.py +0 -0
- {lifx_async-4.5.0 → lifx_async-4.6.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@ed21f2f24f8dd64503750218de024bcf64c7250a # 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@ed21f2f24f8dd64503750218de024bcf64c7250a # 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@ed21f2f24f8dd64503750218de024bcf64c7250a # 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@ed21f2f24f8dd64503750218de024bcf64c7250a # 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@ed21f2f24f8dd64503750218de024bcf64c7250a # 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@ed21f2f24f8dd64503750218de024bcf64c7250a # 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@ed21f2f24f8dd64503750218de024bcf64c7250a # v7
|
|
101
101
|
with:
|
|
102
102
|
version: ${{ env.UV_VERSION }}
|
|
103
103
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
@@ -81,6 +81,32 @@ The `MatrixLight` class controls LIFX matrix devices (tiles, candle, path) with
|
|
|
81
81
|
filters:
|
|
82
82
|
- "!^_"
|
|
83
83
|
|
|
84
|
+
## Ceiling Light
|
|
85
|
+
|
|
86
|
+
The `CeilingLight` class extends `MatrixLight` with independent control over uplight and downlight components for LIFX Ceiling fixtures.
|
|
87
|
+
|
|
88
|
+
::: lifx.devices.ceiling.CeilingLight
|
|
89
|
+
options:
|
|
90
|
+
show_root_heading: true
|
|
91
|
+
heading_level: 3
|
|
92
|
+
members_order: source
|
|
93
|
+
show_if_no_docstring: false
|
|
94
|
+
filters:
|
|
95
|
+
- "!^_"
|
|
96
|
+
|
|
97
|
+
### CeilingLightState
|
|
98
|
+
|
|
99
|
+
The `CeilingLightState` dataclass extends `MatrixLightState` with ceiling-specific component information. It is returned by `CeilingLight.state` after connecting to a device.
|
|
100
|
+
|
|
101
|
+
::: lifx.devices.ceiling.CeilingLightState
|
|
102
|
+
options:
|
|
103
|
+
show_root_heading: true
|
|
104
|
+
heading_level: 4
|
|
105
|
+
members_order: source
|
|
106
|
+
show_if_no_docstring: false
|
|
107
|
+
filters:
|
|
108
|
+
- "!^_"
|
|
109
|
+
|
|
84
110
|
## Device Properties
|
|
85
111
|
|
|
86
112
|
### MAC Address
|
|
@@ -268,3 +294,34 @@ async def main():
|
|
|
268
294
|
# Stop the effect
|
|
269
295
|
await light.set_effect(effect_type=FirmwareEffect.OFF)
|
|
270
296
|
```
|
|
297
|
+
|
|
298
|
+
### Ceiling Light Control
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
from lifx import CeilingLight, HSBK
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
async def main():
|
|
305
|
+
async with await CeilingLight.from_ip("192.168.1.100") as ceiling:
|
|
306
|
+
# Set downlight to warm white
|
|
307
|
+
await ceiling.set_downlight_colors(
|
|
308
|
+
HSBK(hue=0, saturation=0, brightness=0.8, kelvin=3000)
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Set uplight to a dim ambient glow
|
|
312
|
+
await ceiling.set_uplight_color(
|
|
313
|
+
HSBK(hue=30, saturation=0.2, brightness=0.3, kelvin=2700)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Turn uplight off (stores color for later restoration)
|
|
317
|
+
await ceiling.turn_uplight_off()
|
|
318
|
+
|
|
319
|
+
# Turn uplight back on (restores previous color)
|
|
320
|
+
await ceiling.turn_uplight_on()
|
|
321
|
+
|
|
322
|
+
# Check component state
|
|
323
|
+
if ceiling.downlight_is_on:
|
|
324
|
+
print("Downlight is currently on")
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
For detailed CeilingLight usage, see the [Ceiling Lights User Guide](../user-guide/ceiling-lights.md).
|
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v4.6.0 (2025-12-11)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- **devices**: Add CeilingLightState dataclass for ceiling component state
|
|
10
|
+
([`607f15c`](https://github.com/Djelibeybi/lifx-async/commit/607f15c3ed3508a883523ecee940959806d49400))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v4.5.1 (2025-12-11)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- **devices**: Export CeilingLight add add user guide and API documentation
|
|
18
|
+
([`10e0089`](https://github.com/Djelibeybi/lifx-async/commit/10e008983ffd8b233dd2427a4a4f64661c8f14bd))
|
|
19
|
+
|
|
20
|
+
|
|
5
21
|
## v4.5.0 (2025-12-08)
|
|
6
22
|
|
|
7
23
|
### Features
|
|
@@ -119,6 +119,12 @@ async def use_cached_or_fetch():
|
|
|
119
119
|
- `MatrixLight.device_chain` - Details of each tile on the chain
|
|
120
120
|
- `MatrixLight.tile_effect` - Either MORPH, FLAME, SKY or OFF
|
|
121
121
|
|
|
122
|
+
#### CeilingLight properties:
|
|
123
|
+
|
|
124
|
+
- `CeilingLight.uplight_zone` - Zone index of the uplight component
|
|
125
|
+
- `CeilingLight.downlight_zones` - Slice representing downlight zones
|
|
126
|
+
- `CeilingLight.uplight_is_on` - True if uplight has brightness > 0 (requires recent data)
|
|
127
|
+
- `CeilingLight.downlight_is_on` - True if any downlight zone has brightness > 0 (requires recent data)
|
|
122
128
|
|
|
123
129
|
**Note**: Volatile state properties (power, color, hev_cycle, zones, tile_colors) have been removed. Always use `get_*()` methods to fetch these values from devices as they change too frequently to benefit from caching.
|
|
124
130
|
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# Ceiling Lights
|
|
2
|
+
|
|
3
|
+
LIFX Ceiling lights are unique fixtures that combine two lighting components in one device:
|
|
4
|
+
|
|
5
|
+
- **Downlight**: Main illumination with multiple addressable zones (63 or 127 zones)
|
|
6
|
+
- **Uplight**: Ambient/indirect lighting via a single zone
|
|
7
|
+
|
|
8
|
+
The `CeilingLight` class provides high-level control over these components while inheriting full matrix functionality from `MatrixLight`.
|
|
9
|
+
|
|
10
|
+
## Supported Devices
|
|
11
|
+
|
|
12
|
+
| Product | Zones | Layout |
|
|
13
|
+
|---------|-------|--------|
|
|
14
|
+
| LIFX Ceiling (US/Intl) | 64 | 8x8 grid, zone 63 = uplight |
|
|
15
|
+
| LIFX Ceiling Capsule (US/Intl) | 128 | 16x8 grid, zone 127 = uplight |
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from lifx import CeilingLight
|
|
21
|
+
from lifx.color import HSBK
|
|
22
|
+
|
|
23
|
+
async def main():
|
|
24
|
+
async with await CeilingLight.from_ip("192.168.1.100") as ceiling:
|
|
25
|
+
# Set downlight to warm white
|
|
26
|
+
await ceiling.set_downlight_colors(
|
|
27
|
+
HSBK(hue=0, saturation=0, brightness=1.0, kelvin=3000)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Set uplight to a dim, warm ambient glow
|
|
31
|
+
await ceiling.set_uplight_color(
|
|
32
|
+
HSBK(hue=30, saturation=0.2, brightness=0.3, kelvin=2700)
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Component Control
|
|
37
|
+
|
|
38
|
+
### Setting Colors
|
|
39
|
+
|
|
40
|
+
#### Downlight
|
|
41
|
+
|
|
42
|
+
Set all downlight zones to the same color:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
# Single color for all zones
|
|
46
|
+
await ceiling.set_downlight_colors(
|
|
47
|
+
HSBK(hue=0, saturation=0, brightness=0.8, kelvin=4000)
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or set each zone individually:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# Create a gradient across all zones
|
|
55
|
+
zone_count = len(range(*ceiling.downlight_zones.indices(256)))
|
|
56
|
+
colors = [
|
|
57
|
+
HSBK(hue=(i * 360 / zone_count), saturation=1.0, brightness=0.5, kelvin=3500)
|
|
58
|
+
for i in range(zone_count)
|
|
59
|
+
]
|
|
60
|
+
await ceiling.set_downlight_colors(colors)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### Uplight
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
await ceiling.set_uplight_color(
|
|
67
|
+
HSBK(hue=30, saturation=0.1, brightness=0.4, kelvin=2700)
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Reading Current Colors
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# Get current uplight color
|
|
75
|
+
uplight_color = await ceiling.get_uplight_color()
|
|
76
|
+
print(f"Uplight: H={uplight_color.hue}, B={uplight_color.brightness}")
|
|
77
|
+
|
|
78
|
+
# Get all downlight colors
|
|
79
|
+
downlight_colors = await ceiling.get_downlight_colors()
|
|
80
|
+
print(f"Downlight zones: {len(downlight_colors)}")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Turning Components On/Off
|
|
84
|
+
|
|
85
|
+
The `turn_*_on()` and `turn_*_off()` methods provide smart state management:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
# Turn off uplight (stores current color for later restoration)
|
|
89
|
+
await ceiling.turn_uplight_off()
|
|
90
|
+
|
|
91
|
+
# Turn uplight back on (restores previous color)
|
|
92
|
+
await ceiling.turn_uplight_on()
|
|
93
|
+
|
|
94
|
+
# Turn on with a specific color
|
|
95
|
+
await ceiling.turn_uplight_on(
|
|
96
|
+
color=HSBK(hue=0, saturation=0, brightness=1.0, kelvin=3500)
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The same pattern works for downlights:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Turn off downlight
|
|
104
|
+
await ceiling.turn_downlight_off()
|
|
105
|
+
|
|
106
|
+
# Turn downlight back on
|
|
107
|
+
await ceiling.turn_downlight_on()
|
|
108
|
+
|
|
109
|
+
# Turn on with specific colors
|
|
110
|
+
await ceiling.turn_downlight_on(
|
|
111
|
+
colors=HSBK(hue=0, saturation=0, brightness=0.8, kelvin=4000)
|
|
112
|
+
)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Checking Component State
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# Check if components are on
|
|
119
|
+
if ceiling.uplight_is_on:
|
|
120
|
+
print("Uplight is on")
|
|
121
|
+
|
|
122
|
+
if ceiling.downlight_is_on:
|
|
123
|
+
print("Downlight is on")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
!!! note "State Properties Require Recent Data"
|
|
127
|
+
The `uplight_is_on` and `downlight_is_on` properties rely on cached data.
|
|
128
|
+
Call `get_uplight_color()` or `get_downlight_colors()` first to ensure
|
|
129
|
+
accurate state.
|
|
130
|
+
|
|
131
|
+
## Device State
|
|
132
|
+
|
|
133
|
+
After connecting to a CeilingLight, you can access the complete device state via the `state` property, which returns a `CeilingLightState` dataclass:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from lifx import CeilingLight, CeilingLightState
|
|
137
|
+
|
|
138
|
+
async with await CeilingLight.from_ip("192.168.1.100") as ceiling:
|
|
139
|
+
state: CeilingLightState = ceiling.state
|
|
140
|
+
|
|
141
|
+
# Access ceiling-specific state
|
|
142
|
+
print(f"Uplight color: {state.uplight_color}")
|
|
143
|
+
print(f"Uplight is on: {state.uplight_is_on}")
|
|
144
|
+
print(f"Downlight zones: {len(state.downlight_colors)}")
|
|
145
|
+
print(f"Downlight is on: {state.downlight_is_on}")
|
|
146
|
+
|
|
147
|
+
# Access inherited state from MatrixLightState/LightState
|
|
148
|
+
print(f"Device label: {state.label}")
|
|
149
|
+
print(f"Power: {'on' if state.power else 'off'}")
|
|
150
|
+
print(f"Model: {state.model}")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### CeilingLightState Attributes
|
|
154
|
+
|
|
155
|
+
`CeilingLightState` extends `MatrixLightState` with ceiling-specific attributes:
|
|
156
|
+
|
|
157
|
+
| Attribute | Type | Description |
|
|
158
|
+
|-----------|------|-------------|
|
|
159
|
+
| `uplight_color` | `HSBK` | Current color of the uplight component |
|
|
160
|
+
| `downlight_colors` | `list[HSBK]` | Colors for each downlight zone (63 or 127) |
|
|
161
|
+
| `uplight_is_on` | `bool` | True if uplight brightness > 0 |
|
|
162
|
+
| `downlight_is_on` | `bool` | True if any downlight zone brightness > 0 |
|
|
163
|
+
| `uplight_zone` | `int` | Zone index for uplight (63 or 127) |
|
|
164
|
+
| `downlight_zones` | `slice` | Slice for downlight zones |
|
|
165
|
+
|
|
166
|
+
Plus all attributes inherited from `MatrixLightState`: `chain`, `tile_colors`, `tile_count`, `effect`, and from `LightState`: `color`, `power`, `label`, `model`, `serial`, `mac_address`, `capabilities`, etc.
|
|
167
|
+
|
|
168
|
+
## Zone Layout
|
|
169
|
+
|
|
170
|
+
Access the component zone indices directly:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
async with await CeilingLight.from_ip("192.168.1.100") as ceiling:
|
|
174
|
+
# Get uplight zone index (63 or 127 depending on model)
|
|
175
|
+
uplight_idx = ceiling.uplight_zone
|
|
176
|
+
print(f"Uplight zone: {uplight_idx}")
|
|
177
|
+
|
|
178
|
+
# Get downlight zones as a slice
|
|
179
|
+
downlight_slice = ceiling.downlight_zones
|
|
180
|
+
print(f"Downlight zones: {downlight_slice}") # slice(0, 63) or slice(0, 127)
|
|
181
|
+
|
|
182
|
+
# Calculate number of downlight zones
|
|
183
|
+
zone_count = len(range(*downlight_slice.indices(256)))
|
|
184
|
+
print(f"Number of downlight zones: {zone_count}")
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## State Persistence
|
|
188
|
+
|
|
189
|
+
CeilingLight supports optional state persistence to preserve component colors across sessions:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
async with await CeilingLight.from_ip(
|
|
193
|
+
"192.168.1.100",
|
|
194
|
+
state_file="~/.lifx/ceiling_state.json"
|
|
195
|
+
) as ceiling:
|
|
196
|
+
# Colors are automatically loaded from file on connection
|
|
197
|
+
# and saved when using turn_*_off() methods
|
|
198
|
+
|
|
199
|
+
await ceiling.turn_uplight_off() # Saves current color to file
|
|
200
|
+
# ... later ...
|
|
201
|
+
await ceiling.turn_uplight_on() # Restores from file if available
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The state file stores colors per device serial number, supporting multiple devices:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"d073d5123456": {
|
|
209
|
+
"uplight": {
|
|
210
|
+
"hue": 30.0,
|
|
211
|
+
"saturation": 0.2,
|
|
212
|
+
"brightness": 0.4,
|
|
213
|
+
"kelvin": 2700
|
|
214
|
+
},
|
|
215
|
+
"downlight": [
|
|
216
|
+
{"hue": 0.0, "saturation": 0.0, "brightness": 0.8, "kelvin": 4000}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Brightness Determination
|
|
223
|
+
|
|
224
|
+
When calling `turn_uplight_on()` or `turn_downlight_on()` without a color parameter, CeilingLight uses the following priority to determine brightness:
|
|
225
|
+
|
|
226
|
+
1. **Stored state**: If a color was previously saved (via `turn_*_off()` or `set_*_color()`)
|
|
227
|
+
2. **Infer from other component**: Average brightness of the other component
|
|
228
|
+
3. **Default**: 80% brightness
|
|
229
|
+
|
|
230
|
+
This ensures a reasonable brightness level even when no state is available.
|
|
231
|
+
|
|
232
|
+
## Transition Duration
|
|
233
|
+
|
|
234
|
+
All color-setting methods support smooth transitions:
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
# 2-second transition to new color
|
|
238
|
+
await ceiling.set_uplight_color(
|
|
239
|
+
HSBK(hue=0, saturation=0, brightness=1.0, kelvin=3500),
|
|
240
|
+
duration=2.0 # seconds
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Instant change (default)
|
|
244
|
+
await ceiling.set_downlight_colors(
|
|
245
|
+
HSBK(hue=240, saturation=1.0, brightness=0.5, kelvin=3500),
|
|
246
|
+
duration=0.0
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## MatrixLight Compatibility
|
|
251
|
+
|
|
252
|
+
CeilingLight extends `MatrixLight`, so all matrix operations are available:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
async with await CeilingLight.from_ip("192.168.1.100") as ceiling:
|
|
256
|
+
# Use MatrixLight methods directly
|
|
257
|
+
all_colors = await ceiling.get_all_tile_colors()
|
|
258
|
+
tile_chain = await ceiling.get_tile_chain()
|
|
259
|
+
|
|
260
|
+
# Set raw matrix colors (bypasses component abstraction)
|
|
261
|
+
await ceiling.set_matrix_colors(0, colors)
|
|
262
|
+
|
|
263
|
+
# Apply effects
|
|
264
|
+
from lifx.protocol.protocol_types import TileEffectType
|
|
265
|
+
await ceiling.set_tile_effect(
|
|
266
|
+
effect_type=TileEffectType.MORPH,
|
|
267
|
+
speed=5000,
|
|
268
|
+
)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Example: Night Mode
|
|
272
|
+
|
|
273
|
+
Create a subtle night light with dim uplight and downlight off:
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
from lifx import CeilingLight
|
|
277
|
+
from lifx.color import HSBK
|
|
278
|
+
|
|
279
|
+
async def night_mode(ip: str):
|
|
280
|
+
async with await CeilingLight.from_ip(ip) as ceiling:
|
|
281
|
+
# Store current colors before turning off
|
|
282
|
+
await ceiling.turn_downlight_off()
|
|
283
|
+
|
|
284
|
+
# Set uplight to very dim warm glow
|
|
285
|
+
await ceiling.set_uplight_color(
|
|
286
|
+
HSBK(hue=30, saturation=0.3, brightness=0.05, kelvin=2200),
|
|
287
|
+
duration=2.0
|
|
288
|
+
)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Example: Daytime Productivity
|
|
292
|
+
|
|
293
|
+
Bright, cool white for focus:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
async def daytime_mode(ip: str):
|
|
297
|
+
async with await CeilingLight.from_ip(ip) as ceiling:
|
|
298
|
+
# Bright cool downlight for task lighting
|
|
299
|
+
await ceiling.set_downlight_colors(
|
|
300
|
+
HSBK(hue=0, saturation=0, brightness=1.0, kelvin=5500),
|
|
301
|
+
duration=1.0
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Turn off uplight during the day
|
|
305
|
+
await ceiling.turn_uplight_off(duration=1.0)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Example: Evening Ambiance
|
|
309
|
+
|
|
310
|
+
Warm tones with accent uplight:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
async def evening_mode(ip: str):
|
|
314
|
+
async with await CeilingLight.from_ip(ip) as ceiling:
|
|
315
|
+
# Dimmed warm downlight
|
|
316
|
+
await ceiling.set_downlight_colors(
|
|
317
|
+
HSBK(hue=30, saturation=0.1, brightness=0.4, kelvin=2700),
|
|
318
|
+
duration=2.0
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# Colorful uplight accent
|
|
322
|
+
await ceiling.set_uplight_color(
|
|
323
|
+
HSBK(hue=280, saturation=0.6, brightness=0.3, kelvin=3500),
|
|
324
|
+
duration=2.0
|
|
325
|
+
)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## API Reference
|
|
329
|
+
|
|
330
|
+
See [CeilingLight API Reference](../api/devices.md#ceiling-light) for complete method documentation.
|
|
@@ -130,6 +130,7 @@ plugins:
|
|
|
130
130
|
- getting-started/effects.md: Light effects and animations
|
|
131
131
|
User Guide:
|
|
132
132
|
- user-guide/themes.md: Working with color themes
|
|
133
|
+
- user-guide/ceiling-lights.md: Controlling LIFX Ceiling uplight and downlight components
|
|
133
134
|
- user-guide/advanced-usage.md: Advanced usage patterns and best practices
|
|
134
135
|
- user-guide/effects-custom.md: Creating custom light effects
|
|
135
136
|
- user-guide/troubleshooting.md: Common issues and solutions
|
|
@@ -199,6 +200,7 @@ nav:
|
|
|
199
200
|
- Light Effects: getting-started/effects.md
|
|
200
201
|
- User Guide:
|
|
201
202
|
- Themes: user-guide/themes.md
|
|
203
|
+
- Ceiling Lights: user-guide/ceiling-lights.md
|
|
202
204
|
- Advanced Usage: user-guide/advanced-usage.md
|
|
203
205
|
- Custom Effects: user-guide/effects-custom.md
|
|
204
206
|
- Troubleshooting: user-guide/troubleshooting.md
|
|
@@ -16,6 +16,8 @@ from lifx.api import (
|
|
|
16
16
|
)
|
|
17
17
|
from lifx.color import HSBK, Colors
|
|
18
18
|
from lifx.devices import (
|
|
19
|
+
CeilingLight,
|
|
20
|
+
CeilingLightState,
|
|
19
21
|
Device,
|
|
20
22
|
DeviceInfo,
|
|
21
23
|
DeviceVersion,
|
|
@@ -71,6 +73,8 @@ __all__ = [
|
|
|
71
73
|
"MultiZoneLightState",
|
|
72
74
|
"MatrixLight",
|
|
73
75
|
"MatrixLightState",
|
|
76
|
+
"CeilingLight",
|
|
77
|
+
"CeilingLightState",
|
|
74
78
|
# Color
|
|
75
79
|
"HSBK",
|
|
76
80
|
"Colors",
|
|
@@ -10,7 +10,7 @@ from lifx.devices.base import (
|
|
|
10
10
|
FirmwareInfo,
|
|
11
11
|
WifiInfo,
|
|
12
12
|
)
|
|
13
|
-
from lifx.devices.ceiling import CeilingLight
|
|
13
|
+
from lifx.devices.ceiling import CeilingLight, CeilingLightState
|
|
14
14
|
from lifx.devices.hev import HevLight, HevLightState
|
|
15
15
|
from lifx.devices.infrared import InfraredLight, InfraredLightState
|
|
16
16
|
from lifx.devices.light import Light, LightState
|
|
@@ -19,6 +19,7 @@ from lifx.devices.multizone import MultiZoneEffect, MultiZoneLight, MultiZoneLig
|
|
|
19
19
|
|
|
20
20
|
__all__ = [
|
|
21
21
|
"CeilingLight",
|
|
22
|
+
"CeilingLightState",
|
|
22
23
|
"CollectionInfo",
|
|
23
24
|
"Device",
|
|
24
25
|
"DeviceInfo",
|
|
@@ -26,7 +26,14 @@ from lifx.protocol import packets
|
|
|
26
26
|
from lifx.protocol.models import Serial
|
|
27
27
|
|
|
28
28
|
if TYPE_CHECKING:
|
|
29
|
-
from lifx.devices import
|
|
29
|
+
from lifx.devices import (
|
|
30
|
+
CeilingLight,
|
|
31
|
+
HevLight,
|
|
32
|
+
InfraredLight,
|
|
33
|
+
Light,
|
|
34
|
+
MatrixLight,
|
|
35
|
+
MultiZoneLight,
|
|
36
|
+
)
|
|
30
37
|
|
|
31
38
|
_LOGGER = logging.getLogger(__name__)
|
|
32
39
|
|
|
@@ -509,7 +516,7 @@ class Device(Generic[StateT]):
|
|
|
509
516
|
port: int = LIFX_UDP_PORT,
|
|
510
517
|
timeout: float = DEFAULT_REQUEST_TIMEOUT,
|
|
511
518
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
512
|
-
) -> Light | HevLight | InfraredLight | MultiZoneLight | MatrixLight:
|
|
519
|
+
) -> Light | HevLight | InfraredLight | MultiZoneLight | MatrixLight | CeilingLight:
|
|
513
520
|
"""Create and return a fully initialized device instance.
|
|
514
521
|
|
|
515
522
|
This factory method creates the appropriate device type (Light, etc)
|
|
@@ -610,7 +617,14 @@ class Device(Generic[StateT]):
|
|
|
610
617
|
|
|
611
618
|
device_class: type[Device] = cls
|
|
612
619
|
|
|
613
|
-
|
|
620
|
+
# Check for ceiling products first (subset of matrix devices)
|
|
621
|
+
from lifx.products import is_ceiling_product
|
|
622
|
+
|
|
623
|
+
if is_ceiling_product(version.product):
|
|
624
|
+
from lifx.devices.ceiling import CeilingLight
|
|
625
|
+
|
|
626
|
+
device_class = CeilingLight
|
|
627
|
+
elif product_info.has_matrix:
|
|
614
628
|
from lifx.devices.matrix import MatrixLight
|
|
615
629
|
|
|
616
630
|
device_class = MatrixLight
|