lifx-async 4.7.3__py3-none-any.whl → 4.7.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lifx/devices/ceiling.py +356 -114
- {lifx_async-4.7.3.dist-info → lifx_async-4.7.5.dist-info}/METADATA +1 -1
- {lifx_async-4.7.3.dist-info → lifx_async-4.7.5.dist-info}/RECORD +5 -5
- {lifx_async-4.7.3.dist-info → lifx_async-4.7.5.dist-info}/WHEEL +0 -0
- {lifx_async-4.7.3.dist-info → lifx_async-4.7.5.dist-info}/licenses/LICENSE +0 -0
lifx/devices/ceiling.py
CHANGED
|
@@ -201,9 +201,15 @@ class CeilingLight(MatrixLight):
|
|
|
201
201
|
"""
|
|
202
202
|
matrix_state = await super()._initialize_state()
|
|
203
203
|
|
|
204
|
-
#
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
# Extract ceiling component colors from already-fetched tile_colors
|
|
205
|
+
# (parent _initialize_state already called get_all_tile_colors)
|
|
206
|
+
tile_colors = matrix_state.tile_colors
|
|
207
|
+
uplight_color = tile_colors[self.uplight_zone]
|
|
208
|
+
downlight_colors = list(tile_colors[self.downlight_zones])
|
|
209
|
+
|
|
210
|
+
# Cache for is_on properties
|
|
211
|
+
self._last_uplight_color = uplight_color
|
|
212
|
+
self._last_downlight_colors = downlight_colors
|
|
207
213
|
|
|
208
214
|
# Create ceiling state from matrix state
|
|
209
215
|
ceiling_state = CeilingLightState.from_matrix_state(
|
|
@@ -231,9 +237,15 @@ class CeilingLight(MatrixLight):
|
|
|
231
237
|
"""
|
|
232
238
|
await super().refresh_state()
|
|
233
239
|
|
|
234
|
-
#
|
|
235
|
-
|
|
236
|
-
|
|
240
|
+
# Extract ceiling component colors from already-fetched tile_colors
|
|
241
|
+
# (parent refresh_state already called get_all_tile_colors)
|
|
242
|
+
tile_colors = self._state.tile_colors
|
|
243
|
+
uplight_color = tile_colors[self.uplight_zone]
|
|
244
|
+
downlight_colors = list(tile_colors[self.downlight_zones])
|
|
245
|
+
|
|
246
|
+
# Cache for is_on properties
|
|
247
|
+
self._last_uplight_color = uplight_color
|
|
248
|
+
self._last_downlight_colors = downlight_colors
|
|
237
249
|
|
|
238
250
|
# Update ceiling-specific state fields
|
|
239
251
|
state = cast(CeilingLightState, self._state)
|
|
@@ -335,6 +347,22 @@ class CeilingLight(MatrixLight):
|
|
|
335
347
|
|
|
336
348
|
return layout.downlight_zones
|
|
337
349
|
|
|
350
|
+
@property
|
|
351
|
+
def downlight_zone_count(self) -> int:
|
|
352
|
+
"""Number of downlight zones.
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
Zone count (63 for standard 8x8, 127 for Capsule 16x8)
|
|
356
|
+
|
|
357
|
+
Raises:
|
|
358
|
+
LifxError: If device version is not available or not a Ceiling product
|
|
359
|
+
"""
|
|
360
|
+
# downlight_zones is slice(0, N), so stop equals the count
|
|
361
|
+
stop = self.downlight_zones.stop
|
|
362
|
+
if stop is None:
|
|
363
|
+
raise LifxError("Invalid downlight zones configuration")
|
|
364
|
+
return stop
|
|
365
|
+
|
|
338
366
|
@property
|
|
339
367
|
def uplight_is_on(self) -> bool:
|
|
340
368
|
"""True if uplight component is currently on.
|
|
@@ -484,7 +512,7 @@ class CeilingLight(MatrixLight):
|
|
|
484
512
|
"Cannot set downlight color with brightness=0. "
|
|
485
513
|
"Use turn_downlight_off() instead."
|
|
486
514
|
)
|
|
487
|
-
downlight_colors = [colors] *
|
|
515
|
+
downlight_colors = [colors] * self.downlight_zone_count
|
|
488
516
|
else:
|
|
489
517
|
if all(c.brightness == 0 for c in colors):
|
|
490
518
|
raise ValueError(
|
|
@@ -492,10 +520,10 @@ class CeilingLight(MatrixLight):
|
|
|
492
520
|
"Use turn_downlight_off() instead."
|
|
493
521
|
)
|
|
494
522
|
|
|
495
|
-
|
|
496
|
-
if len(colors) != expected_count:
|
|
523
|
+
if len(colors) != self.downlight_zone_count:
|
|
497
524
|
raise ValueError(
|
|
498
|
-
f"Expected {
|
|
525
|
+
f"Expected {self.downlight_zone_count} colors for downlight, "
|
|
526
|
+
f"got {len(colors)}"
|
|
499
527
|
)
|
|
500
528
|
downlight_colors = colors
|
|
501
529
|
|
|
@@ -522,6 +550,10 @@ class CeilingLight(MatrixLight):
|
|
|
522
550
|
) -> None:
|
|
523
551
|
"""Turn uplight component on.
|
|
524
552
|
|
|
553
|
+
If the entire light is off, this will set the color instantly and then
|
|
554
|
+
turn on the light with the specified duration, so the light fades to
|
|
555
|
+
the target color instead of flashing to its previous state.
|
|
556
|
+
|
|
525
557
|
Args:
|
|
526
558
|
color: Optional HSBK color. If provided:
|
|
527
559
|
- Uses this color immediately
|
|
@@ -533,14 +565,61 @@ class CeilingLight(MatrixLight):
|
|
|
533
565
|
ValueError: If color.brightness == 0
|
|
534
566
|
LifxTimeoutError: Device did not respond
|
|
535
567
|
"""
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
568
|
+
# Validate provided color early
|
|
569
|
+
if color is not None and color.brightness == 0:
|
|
570
|
+
raise ValueError("Cannot turn on uplight with brightness=0")
|
|
571
|
+
|
|
572
|
+
# Check if light is off first to determine which path to take
|
|
573
|
+
if await self.get_power() == 0:
|
|
574
|
+
# Light is off - single fetch for both determining color and modification
|
|
575
|
+
all_colors = await self.get_all_tile_colors()
|
|
576
|
+
tile_colors = all_colors[0]
|
|
577
|
+
|
|
578
|
+
# Determine target color (pass pre-fetched colors to avoid extra fetch)
|
|
579
|
+
if color is not None:
|
|
580
|
+
target_color = color
|
|
581
|
+
else:
|
|
582
|
+
target_color = await self._determine_uplight_brightness(tile_colors)
|
|
583
|
+
|
|
584
|
+
# Store current downlight colors BEFORE zeroing them out
|
|
585
|
+
# This allows turn_downlight_on() to restore them later
|
|
586
|
+
downlight_colors = tile_colors[self.downlight_zones]
|
|
587
|
+
self._stored_downlight_state = list(downlight_colors)
|
|
588
|
+
|
|
589
|
+
# Set uplight zone to target color
|
|
590
|
+
tile_colors[self.uplight_zone] = target_color
|
|
591
|
+
|
|
592
|
+
# Zero out downlight zones so they stay off when power turns on
|
|
593
|
+
for i in range(*self.downlight_zones.indices(len(tile_colors))):
|
|
594
|
+
tile_colors[i] = HSBK(
|
|
595
|
+
hue=tile_colors[i].hue,
|
|
596
|
+
saturation=tile_colors[i].saturation,
|
|
597
|
+
brightness=0.0,
|
|
598
|
+
kelvin=tile_colors[i].kelvin,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
# Set all colors instantly (duration=0) while light is off
|
|
602
|
+
await self.set_matrix_colors(0, tile_colors, duration=0)
|
|
603
|
+
|
|
604
|
+
# Update stored state for uplight
|
|
605
|
+
self._stored_uplight_state = target_color
|
|
606
|
+
self._last_uplight_color = target_color
|
|
607
|
+
|
|
608
|
+
# Turn on with the requested duration - light fades on to target color
|
|
609
|
+
await super().set_power(True, duration)
|
|
610
|
+
|
|
611
|
+
# Persist AFTER device operations complete
|
|
612
|
+
if self._state_file:
|
|
613
|
+
self._save_state_to_file()
|
|
540
614
|
else:
|
|
541
|
-
#
|
|
542
|
-
|
|
543
|
-
|
|
615
|
+
# Light is already on - determine target color first, then set
|
|
616
|
+
if color is not None:
|
|
617
|
+
target_color = color
|
|
618
|
+
else:
|
|
619
|
+
target_color = await self._determine_uplight_brightness()
|
|
620
|
+
|
|
621
|
+
# set_uplight_color will fetch and modify (single fetch in that method)
|
|
622
|
+
await self.set_uplight_color(target_color, duration)
|
|
544
623
|
|
|
545
624
|
async def turn_uplight_off(
|
|
546
625
|
self, color: HSBK | None = None, duration: float = 0.0
|
|
@@ -560,30 +639,35 @@ class CeilingLight(MatrixLight):
|
|
|
560
639
|
Note:
|
|
561
640
|
Sets uplight zone brightness to 0 on device while preserving H, S, K.
|
|
562
641
|
"""
|
|
642
|
+
if color is not None and color.brightness == 0:
|
|
643
|
+
raise ValueError(
|
|
644
|
+
"Provided color cannot have brightness=0. "
|
|
645
|
+
"Omit the parameter to use current color."
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
# Fetch current state once and reuse to calculate brightness
|
|
649
|
+
all_colors = await self.get_all_tile_colors()
|
|
650
|
+
tile_colors = all_colors[0]
|
|
651
|
+
|
|
652
|
+
# Determine which color to store
|
|
563
653
|
if color is not None:
|
|
564
|
-
|
|
565
|
-
raise ValueError(
|
|
566
|
-
"Provided color cannot have brightness=0. "
|
|
567
|
-
"Omit the parameter to use current color."
|
|
568
|
-
)
|
|
569
|
-
# Store the provided color
|
|
570
|
-
self._stored_uplight_state = color
|
|
654
|
+
stored_color = color
|
|
571
655
|
else:
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
656
|
+
stored_color = tile_colors[self.uplight_zone]
|
|
657
|
+
self._last_uplight_color = stored_color
|
|
658
|
+
|
|
659
|
+
# Store for future restoration
|
|
660
|
+
self._stored_uplight_state = stored_color
|
|
575
661
|
|
|
576
662
|
# Create color with brightness=0 for device
|
|
577
663
|
off_color = HSBK(
|
|
578
|
-
hue=
|
|
579
|
-
saturation=
|
|
664
|
+
hue=stored_color.hue,
|
|
665
|
+
saturation=stored_color.saturation,
|
|
580
666
|
brightness=0.0,
|
|
581
|
-
kelvin=
|
|
667
|
+
kelvin=stored_color.kelvin,
|
|
582
668
|
)
|
|
583
669
|
|
|
584
|
-
#
|
|
585
|
-
all_colors = await self.get_all_tile_colors()
|
|
586
|
-
tile_colors = all_colors[0]
|
|
670
|
+
# Update uplight zone and send immediately
|
|
587
671
|
tile_colors[self.uplight_zone] = off_color
|
|
588
672
|
await self.set_matrix_colors(0, tile_colors, duration=int(duration * 1000))
|
|
589
673
|
|
|
@@ -599,6 +683,10 @@ class CeilingLight(MatrixLight):
|
|
|
599
683
|
) -> None:
|
|
600
684
|
"""Turn downlight component on.
|
|
601
685
|
|
|
686
|
+
If the entire light is off, this will set the colors instantly and then
|
|
687
|
+
turn on the light with the specified duration, so the light fades to
|
|
688
|
+
the target colors instead of flashing to its previous state.
|
|
689
|
+
|
|
602
690
|
Args:
|
|
603
691
|
colors: Optional colors. Can be:
|
|
604
692
|
- None: uses brightness determination logic
|
|
@@ -612,12 +700,76 @@ class CeilingLight(MatrixLight):
|
|
|
612
700
|
ValueError: If list length doesn't match downlight zone count
|
|
613
701
|
LifxTimeoutError: Device did not respond
|
|
614
702
|
"""
|
|
703
|
+
# Validate provided colors early
|
|
615
704
|
if colors is not None:
|
|
616
|
-
|
|
705
|
+
if isinstance(colors, HSBK):
|
|
706
|
+
if colors.brightness == 0:
|
|
707
|
+
raise ValueError("Cannot turn on downlight with brightness=0")
|
|
708
|
+
else:
|
|
709
|
+
if all(c.brightness == 0 for c in colors):
|
|
710
|
+
raise ValueError("Cannot turn on downlight with brightness=0")
|
|
711
|
+
if len(colors) != self.downlight_zone_count:
|
|
712
|
+
raise ValueError(
|
|
713
|
+
f"Expected {self.downlight_zone_count} colors for downlight, "
|
|
714
|
+
f"got {len(colors)}"
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
# Check if light is off first to determine which path to take
|
|
718
|
+
if await self.get_power() == 0:
|
|
719
|
+
# Light is off - single fetch for both determining colors and modification
|
|
720
|
+
all_colors = await self.get_all_tile_colors()
|
|
721
|
+
tile_colors = all_colors[0]
|
|
722
|
+
|
|
723
|
+
# Determine target colors (pass pre-fetched colors to avoid extra fetch)
|
|
724
|
+
if colors is not None:
|
|
725
|
+
if isinstance(colors, HSBK):
|
|
726
|
+
target_colors = [colors] * self.downlight_zone_count
|
|
727
|
+
else:
|
|
728
|
+
target_colors = list(colors)
|
|
729
|
+
else:
|
|
730
|
+
target_colors = await self._determine_downlight_brightness(tile_colors)
|
|
731
|
+
|
|
732
|
+
# Store current uplight color BEFORE zeroing it out
|
|
733
|
+
# This allows turn_uplight_on() to restore it later
|
|
734
|
+
self._stored_uplight_state = tile_colors[self.uplight_zone]
|
|
735
|
+
|
|
736
|
+
# Set downlight zones to target colors
|
|
737
|
+
tile_colors[self.downlight_zones] = target_colors
|
|
738
|
+
|
|
739
|
+
# Zero out uplight zone so it stays off when power turns on
|
|
740
|
+
uplight_color = tile_colors[self.uplight_zone]
|
|
741
|
+
tile_colors[self.uplight_zone] = HSBK(
|
|
742
|
+
hue=uplight_color.hue,
|
|
743
|
+
saturation=uplight_color.saturation,
|
|
744
|
+
brightness=0.0,
|
|
745
|
+
kelvin=uplight_color.kelvin,
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
# Set all colors instantly (duration=0) while light is off
|
|
749
|
+
await self.set_matrix_colors(0, tile_colors, duration=0)
|
|
750
|
+
|
|
751
|
+
# Update stored state for downlight
|
|
752
|
+
self._stored_downlight_state = target_colors
|
|
753
|
+
self._last_downlight_colors = target_colors
|
|
754
|
+
|
|
755
|
+
# Turn on with the requested duration - light fades on to target colors
|
|
756
|
+
await super().set_power(True, duration)
|
|
757
|
+
|
|
758
|
+
# Persist AFTER device operations complete
|
|
759
|
+
if self._state_file:
|
|
760
|
+
self._save_state_to_file()
|
|
617
761
|
else:
|
|
618
|
-
#
|
|
619
|
-
|
|
620
|
-
|
|
762
|
+
# Light is already on - determine target colors first, then set
|
|
763
|
+
if colors is not None:
|
|
764
|
+
if isinstance(colors, HSBK):
|
|
765
|
+
target_colors = [colors] * self.downlight_zone_count
|
|
766
|
+
else:
|
|
767
|
+
target_colors = list(colors)
|
|
768
|
+
else:
|
|
769
|
+
target_colors = await self._determine_downlight_brightness()
|
|
770
|
+
|
|
771
|
+
# set_downlight_colors will fetch and modify (single fetch in that method)
|
|
772
|
+
await self.set_downlight_colors(target_colors, duration)
|
|
621
773
|
|
|
622
774
|
async def set_power(self, level: bool | int, duration: float = 0.0) -> None:
|
|
623
775
|
"""Set light power state, capturing component colors before turning off.
|
|
@@ -663,21 +815,75 @@ class CeilingLight(MatrixLight):
|
|
|
663
815
|
else:
|
|
664
816
|
raise TypeError(f"Expected bool or int, got {type(level).__name__}")
|
|
665
817
|
|
|
666
|
-
# If turning off, capture current colors for both components
|
|
818
|
+
# If turning off, capture current colors for both components with single fetch
|
|
667
819
|
if turning_off:
|
|
668
|
-
#
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
self._stored_uplight_state = await self.get_uplight_color()
|
|
672
|
-
self._stored_downlight_state = await self.get_downlight_colors()
|
|
820
|
+
# Single fetch to capture both uplight and downlight colors
|
|
821
|
+
all_colors = await self.get_all_tile_colors()
|
|
822
|
+
tile_colors = all_colors[0]
|
|
673
823
|
|
|
674
|
-
#
|
|
675
|
-
|
|
676
|
-
|
|
824
|
+
# Extract and store both component colors
|
|
825
|
+
self._stored_uplight_state = tile_colors[self.uplight_zone]
|
|
826
|
+
self._stored_downlight_state = list(tile_colors[self.downlight_zones])
|
|
827
|
+
|
|
828
|
+
# Also update cache for is_on properties
|
|
829
|
+
self._last_uplight_color = self._stored_uplight_state
|
|
830
|
+
self._last_downlight_colors = self._stored_downlight_state
|
|
677
831
|
|
|
678
832
|
# Call parent to perform actual power change
|
|
679
833
|
await super().set_power(level, duration)
|
|
680
834
|
|
|
835
|
+
# Persist AFTER device operation completes
|
|
836
|
+
if turning_off and self._state_file:
|
|
837
|
+
self._save_state_to_file()
|
|
838
|
+
|
|
839
|
+
async def set_color(self, color: HSBK, duration: float = 0.0) -> None:
|
|
840
|
+
"""Set light color, updating component state tracking.
|
|
841
|
+
|
|
842
|
+
Overrides Light.set_color() to track the color change in the ceiling
|
|
843
|
+
light's component state. When set_color() is called, all zones (uplight
|
|
844
|
+
and downlight) are set to the same color. This override ensures that
|
|
845
|
+
the cached component colors stay in sync so that subsequent component
|
|
846
|
+
control methods (like turn_uplight_on or turn_downlight_on) use the
|
|
847
|
+
correct color values.
|
|
848
|
+
|
|
849
|
+
Args:
|
|
850
|
+
color: HSBK color to set for the entire light
|
|
851
|
+
duration: Transition duration in seconds (default 0.0)
|
|
852
|
+
|
|
853
|
+
Raises:
|
|
854
|
+
LifxDeviceNotFoundError: If device is not connected
|
|
855
|
+
LifxTimeoutError: If device does not respond
|
|
856
|
+
LifxUnsupportedCommandError: If device doesn't support this command
|
|
857
|
+
|
|
858
|
+
Example:
|
|
859
|
+
```python
|
|
860
|
+
from lifx.color import HSBK
|
|
861
|
+
|
|
862
|
+
# Set entire ceiling light to warm white
|
|
863
|
+
await ceiling.set_color(
|
|
864
|
+
HSBK(hue=0, saturation=0, brightness=1.0, kelvin=2700)
|
|
865
|
+
)
|
|
866
|
+
|
|
867
|
+
# Later component control will use this color
|
|
868
|
+
await ceiling.turn_uplight_off() # Uplight off
|
|
869
|
+
await ceiling.turn_uplight_on() # Restores to warm white
|
|
870
|
+
```
|
|
871
|
+
"""
|
|
872
|
+
# Call parent to perform actual color change
|
|
873
|
+
await super().set_color(color, duration)
|
|
874
|
+
|
|
875
|
+
# Update cached component colors - all zones now have the same color
|
|
876
|
+
self._last_uplight_color = color
|
|
877
|
+
self._last_downlight_colors = [color] * self.downlight_zone_count
|
|
878
|
+
|
|
879
|
+
# Also update stored state for restoration
|
|
880
|
+
self._stored_uplight_state = color
|
|
881
|
+
self._stored_downlight_state = [color] * self.downlight_zone_count
|
|
882
|
+
|
|
883
|
+
# Persist if enabled
|
|
884
|
+
if self._state_file:
|
|
885
|
+
self._save_state_to_file()
|
|
886
|
+
|
|
681
887
|
async def turn_downlight_off(
|
|
682
888
|
self, colors: HSBK | list[HSBK] | None = None, duration: float = 0.0
|
|
683
889
|
) -> None:
|
|
@@ -699,35 +905,40 @@ class CeilingLight(MatrixLight):
|
|
|
699
905
|
Note:
|
|
700
906
|
Sets all downlight zone brightness to 0 on device while preserving H, S, K.
|
|
701
907
|
"""
|
|
702
|
-
|
|
703
|
-
|
|
908
|
+
# Validate provided colors early (before fetching)
|
|
909
|
+
stored_colors: list[HSBK] | None = None
|
|
704
910
|
if colors is not None:
|
|
705
|
-
# Validate and normalize provided colors
|
|
706
911
|
if isinstance(colors, HSBK):
|
|
707
912
|
if colors.brightness == 0:
|
|
708
913
|
raise ValueError(
|
|
709
914
|
"Provided color cannot have brightness=0. "
|
|
710
915
|
"Omit the parameter to use current colors."
|
|
711
916
|
)
|
|
712
|
-
|
|
917
|
+
stored_colors = [colors] * self.downlight_zone_count
|
|
713
918
|
else:
|
|
714
919
|
if all(c.brightness == 0 for c in colors):
|
|
715
920
|
raise ValueError(
|
|
716
921
|
"Provided colors cannot have brightness=0. "
|
|
717
922
|
"Omit the parameter to use current colors."
|
|
718
923
|
)
|
|
719
|
-
if len(colors) !=
|
|
924
|
+
if len(colors) != self.downlight_zone_count:
|
|
720
925
|
raise ValueError(
|
|
721
|
-
f"Expected {
|
|
926
|
+
f"Expected {self.downlight_zone_count} colors for downlight, "
|
|
722
927
|
f"got {len(colors)}"
|
|
723
928
|
)
|
|
724
|
-
|
|
929
|
+
stored_colors = list(colors)
|
|
725
930
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
931
|
+
# Fetch current state once and reuse to calculate brightness
|
|
932
|
+
all_colors = await self.get_all_tile_colors()
|
|
933
|
+
tile_colors = all_colors[0]
|
|
934
|
+
|
|
935
|
+
# If colors not provided, extract from fetched data
|
|
936
|
+
if stored_colors is None:
|
|
937
|
+
stored_colors = list(tile_colors[self.downlight_zones])
|
|
938
|
+
self._last_downlight_colors = stored_colors
|
|
939
|
+
|
|
940
|
+
# Store for future restoration
|
|
941
|
+
self._stored_downlight_state = stored_colors
|
|
731
942
|
|
|
732
943
|
# Create colors with brightness=0 for device
|
|
733
944
|
off_colors = [
|
|
@@ -737,12 +948,10 @@ class CeilingLight(MatrixLight):
|
|
|
737
948
|
brightness=0.0,
|
|
738
949
|
kelvin=c.kelvin,
|
|
739
950
|
)
|
|
740
|
-
for c in
|
|
951
|
+
for c in stored_colors
|
|
741
952
|
]
|
|
742
953
|
|
|
743
|
-
#
|
|
744
|
-
all_colors = await self.get_all_tile_colors()
|
|
745
|
-
tile_colors = all_colors[0]
|
|
954
|
+
# Update downlight zones and send immediately
|
|
746
955
|
tile_colors[self.downlight_zones] = off_colors
|
|
747
956
|
await self.set_matrix_colors(0, tile_colors, duration=int(duration * 1000))
|
|
748
957
|
|
|
@@ -753,89 +962,122 @@ class CeilingLight(MatrixLight):
|
|
|
753
962
|
if self._state_file:
|
|
754
963
|
self._save_state_to_file()
|
|
755
964
|
|
|
756
|
-
async def _determine_uplight_brightness(
|
|
965
|
+
async def _determine_uplight_brightness(
|
|
966
|
+
self, tile_colors: list[HSBK] | None = None
|
|
967
|
+
) -> HSBK:
|
|
757
968
|
"""Determine uplight brightness using priority logic.
|
|
758
969
|
|
|
759
970
|
Priority order:
|
|
760
|
-
1. Stored state (if available)
|
|
761
|
-
2. Infer from downlight average brightness
|
|
971
|
+
1. Stored state (if available AND brightness > 0)
|
|
972
|
+
2. Infer from downlight average brightness (using stored H, S, K if available)
|
|
762
973
|
3. Hardcoded default (0.8)
|
|
763
974
|
|
|
975
|
+
Args:
|
|
976
|
+
tile_colors: Optional pre-fetched tile colors to avoid redundant fetch.
|
|
977
|
+
If None, will fetch from device.
|
|
978
|
+
|
|
764
979
|
Returns:
|
|
765
980
|
HSBK color for uplight
|
|
766
981
|
"""
|
|
767
|
-
# 1. Stored state
|
|
768
|
-
if
|
|
982
|
+
# 1. Stored state (only if brightness > 0)
|
|
983
|
+
if (
|
|
984
|
+
self._stored_uplight_state is not None
|
|
985
|
+
and self._stored_uplight_state.brightness > 0
|
|
986
|
+
):
|
|
769
987
|
return self._stored_uplight_state
|
|
770
988
|
|
|
771
|
-
# Get current
|
|
772
|
-
|
|
989
|
+
# Get current colors (use pre-fetched if available)
|
|
990
|
+
if tile_colors is None:
|
|
991
|
+
all_colors = await self.get_all_tile_colors()
|
|
992
|
+
tile_colors = all_colors[0]
|
|
993
|
+
|
|
994
|
+
current_uplight = tile_colors[self.uplight_zone]
|
|
995
|
+
downlight_colors = tile_colors[self.downlight_zones]
|
|
996
|
+
|
|
997
|
+
# Cache for is_on properties
|
|
998
|
+
self._last_uplight_color = current_uplight
|
|
999
|
+
self._last_downlight_colors = list(downlight_colors)
|
|
1000
|
+
|
|
1001
|
+
# Determine which color source to use for H, S, K
|
|
1002
|
+
source_color = self._stored_uplight_state or current_uplight
|
|
773
1003
|
|
|
774
1004
|
# 2. Infer from downlight average brightness
|
|
775
|
-
|
|
776
|
-
downlight_colors
|
|
777
|
-
|
|
778
|
-
downlight_colors
|
|
779
|
-
)
|
|
1005
|
+
avg_brightness = sum(c.brightness for c in downlight_colors) / len(
|
|
1006
|
+
downlight_colors
|
|
1007
|
+
)
|
|
780
1008
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
except Exception: # nosec B110
|
|
791
|
-
# If inference fails, fall through to default
|
|
792
|
-
pass
|
|
1009
|
+
# Only use inferred brightness if it's > 0
|
|
1010
|
+
# If all downlights are off (brightness=0), skip to default
|
|
1011
|
+
if avg_brightness > 0:
|
|
1012
|
+
return HSBK(
|
|
1013
|
+
hue=source_color.hue,
|
|
1014
|
+
saturation=source_color.saturation,
|
|
1015
|
+
brightness=avg_brightness,
|
|
1016
|
+
kelvin=source_color.kelvin,
|
|
1017
|
+
)
|
|
793
1018
|
|
|
794
1019
|
# 3. Hardcoded default (0.8)
|
|
795
1020
|
return HSBK(
|
|
796
|
-
hue=
|
|
797
|
-
saturation=
|
|
1021
|
+
hue=source_color.hue,
|
|
1022
|
+
saturation=source_color.saturation,
|
|
798
1023
|
brightness=0.8,
|
|
799
|
-
kelvin=
|
|
1024
|
+
kelvin=source_color.kelvin,
|
|
800
1025
|
)
|
|
801
1026
|
|
|
802
|
-
async def _determine_downlight_brightness(
|
|
1027
|
+
async def _determine_downlight_brightness(
|
|
1028
|
+
self, tile_colors: list[HSBK] | None = None
|
|
1029
|
+
) -> list[HSBK]:
|
|
803
1030
|
"""Determine downlight brightness using priority logic.
|
|
804
1031
|
|
|
805
1032
|
Priority order:
|
|
806
|
-
1. Stored state (if available)
|
|
1033
|
+
1. Stored state (if available AND any brightness > 0)
|
|
807
1034
|
2. Infer from uplight brightness
|
|
808
1035
|
3. Hardcoded default (0.8)
|
|
809
1036
|
|
|
1037
|
+
Args:
|
|
1038
|
+
tile_colors: Optional pre-fetched tile colors to avoid redundant fetch.
|
|
1039
|
+
If None, will fetch from device.
|
|
1040
|
+
|
|
810
1041
|
Returns:
|
|
811
1042
|
List of HSBK colors for downlight zones
|
|
812
1043
|
"""
|
|
813
|
-
# 1. Stored state
|
|
1044
|
+
# 1. Stored state (only if any color has brightness > 0)
|
|
814
1045
|
if self._stored_downlight_state is not None:
|
|
815
|
-
|
|
1046
|
+
if any(c.brightness > 0 for c in self._stored_downlight_state):
|
|
1047
|
+
return self._stored_downlight_state
|
|
816
1048
|
|
|
817
|
-
# Get current
|
|
818
|
-
|
|
1049
|
+
# Get current colors (use pre-fetched if available)
|
|
1050
|
+
if tile_colors is None:
|
|
1051
|
+
all_colors = await self.get_all_tile_colors()
|
|
1052
|
+
tile_colors = all_colors[0]
|
|
819
1053
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
uplight_color = await self.get_uplight_color()
|
|
1054
|
+
current_downlight = list(tile_colors[self.downlight_zones])
|
|
1055
|
+
uplight_color = tile_colors[self.uplight_zone]
|
|
823
1056
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1057
|
+
# Cache for is_on properties
|
|
1058
|
+
self._last_downlight_colors = current_downlight
|
|
1059
|
+
self._last_uplight_color = uplight_color
|
|
1060
|
+
|
|
1061
|
+
# Prefer stored H, S, K if available, otherwise use current
|
|
1062
|
+
source_colors: list[HSBK] = (
|
|
1063
|
+
self._stored_downlight_state
|
|
1064
|
+
if self._stored_downlight_state is not None
|
|
1065
|
+
else current_downlight
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
# 2. Infer from uplight brightness
|
|
1069
|
+
# Only use inferred brightness if it's > 0
|
|
1070
|
+
# If uplight is off (brightness=0), skip to default
|
|
1071
|
+
if uplight_color.brightness > 0:
|
|
1072
|
+
return [
|
|
1073
|
+
HSBK(
|
|
1074
|
+
hue=c.hue,
|
|
1075
|
+
saturation=c.saturation,
|
|
1076
|
+
brightness=uplight_color.brightness,
|
|
1077
|
+
kelvin=c.kelvin,
|
|
1078
|
+
)
|
|
1079
|
+
for c in source_colors
|
|
1080
|
+
]
|
|
839
1081
|
|
|
840
1082
|
# 3. Hardcoded default (0.8)
|
|
841
1083
|
return [
|
|
@@ -845,7 +1087,7 @@ class CeilingLight(MatrixLight):
|
|
|
845
1087
|
brightness=0.8,
|
|
846
1088
|
kelvin=c.kelvin,
|
|
847
1089
|
)
|
|
848
|
-
for c in
|
|
1090
|
+
for c in source_colors
|
|
849
1091
|
]
|
|
850
1092
|
|
|
851
1093
|
def _is_stored_state_valid(
|
|
@@ -6,7 +6,7 @@ lifx/exceptions.py,sha256=pikAMppLn7gXyjiQVWM_tSvXKNh-g366nG_UWyqpHhc,815
|
|
|
6
6
|
lifx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
lifx/devices/__init__.py,sha256=4b5QtO0EFWxIqN2lUYgM8uLjWyHI5hUcReiF9QCjCGw,1061
|
|
8
8
|
lifx/devices/base.py,sha256=0G2PCJRNeIPkMCIw68x0ijn6gUIwh2jFlex8SN4Hs1Y,63530
|
|
9
|
-
lifx/devices/ceiling.py,sha256=
|
|
9
|
+
lifx/devices/ceiling.py,sha256=bLAurvqTNmhKMFUUJmLqn1vDFawapYju2i4G0pHOH_4,45790
|
|
10
10
|
lifx/devices/hev.py,sha256=T5hvt2q_vdgPBvThx_-M7n5pZu9pL0y9Fs3Zz_KL0NM,15588
|
|
11
11
|
lifx/devices/infrared.py,sha256=ePk9qxX_s-hv5gQMvio1Vv8FYiCd68HF0ySbWgSrvuU,8130
|
|
12
12
|
lifx/devices/light.py,sha256=gk92lhViUWINGaxDWbs4qn8Stnn2fGCfRkC5Kk0Q-hI,34087
|
|
@@ -42,7 +42,7 @@ lifx/theme/canvas.py,sha256=4h7lgN8iu_OdchObGDgbxTqQLCb-FRKC-M-YCWef_i4,8048
|
|
|
42
42
|
lifx/theme/generators.py,sha256=nq3Yvntq_h-eFHbmmow3LcAdA_hEbRRaP5mv9Bydrjk,6435
|
|
43
43
|
lifx/theme/library.py,sha256=tKlKZNqJp8lRGDnilWyDm_Qr1vCRGGwuvWVS82anNpQ,21326
|
|
44
44
|
lifx/theme/theme.py,sha256=qMEx_8E41C0Cc6f083XHiAXEglTv4YlXW0UFsG1rQKg,5521
|
|
45
|
-
lifx_async-4.7.
|
|
46
|
-
lifx_async-4.7.
|
|
47
|
-
lifx_async-4.7.
|
|
48
|
-
lifx_async-4.7.
|
|
45
|
+
lifx_async-4.7.5.dist-info/METADATA,sha256=1SN5XqtWLHrF_JtLHyWGNSCCeoajcu32g0MWFC48VIM,2609
|
|
46
|
+
lifx_async-4.7.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
47
|
+
lifx_async-4.7.5.dist-info/licenses/LICENSE,sha256=eBz48GRA3gSiWn3rYZAz2Ewp35snnhV9cSqkVBq7g3k,1832
|
|
48
|
+
lifx_async-4.7.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|