mirror of
https://github.com/home-assistant/core.git
synced 2026-09-27 01:46:11 -04:00
Fix LIFX effect action defaults and clamp values above zero (#182923)
Signed-off-by: Avi Miller <me@dje.li> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e8bab8c41f
commit
c52809cec7
@@ -115,8 +115,13 @@ LIFX_EFFECT_SCHEMA = {
|
||||
LIFX_EFFECT_PULSE_SCHEMA = cv.make_entity_service_schema(
|
||||
{
|
||||
**LIFX_EFFECT_SCHEMA,
|
||||
probatio.Exclusive(ATTR_BRIGHTNESS, ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
||||
probatio.Exclusive(ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS): VALID_BRIGHTNESS_PCT,
|
||||
# A brightness of zero would pulse to black
|
||||
probatio.Exclusive(ATTR_BRIGHTNESS, ATTR_BRIGHTNESS): probatio.All(
|
||||
VALID_BRIGHTNESS, probatio.Clamp(min=1)
|
||||
),
|
||||
probatio.Exclusive(ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS): probatio.All(
|
||||
VALID_BRIGHTNESS_PCT, probatio.Clamp(min=1)
|
||||
),
|
||||
probatio.Exclusive(ATTR_COLOR_NAME, COLOR_GROUP): cv.string,
|
||||
probatio.Exclusive(ATTR_RGB_COLOR, COLOR_GROUP): probatio.All(
|
||||
probatio.Coerce(tuple), probatio.ExactSequence((cv.byte, cv.byte, cv.byte))
|
||||
@@ -152,11 +157,12 @@ LIFX_EFFECT_COLORLOOP_SCHEMA = cv.make_entity_service_schema(
|
||||
**LIFX_EFFECT_SCHEMA,
|
||||
probatio.Exclusive(ATTR_BRIGHTNESS, ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
||||
probatio.Exclusive(ATTR_BRIGHTNESS_PCT, ATTR_BRIGHTNESS): VALID_BRIGHTNESS_PCT,
|
||||
# A saturation of zero switches the bulb to color temperature mode
|
||||
ATTR_SATURATION_MAX: probatio.All(
|
||||
probatio.Coerce(int), probatio.Clamp(min=0, max=100)
|
||||
probatio.Coerce(int), probatio.Clamp(min=1, max=100)
|
||||
),
|
||||
ATTR_SATURATION_MIN: probatio.All(
|
||||
probatio.Coerce(int), probatio.Clamp(min=0, max=100)
|
||||
probatio.Coerce(int), probatio.Clamp(min=1, max=100)
|
||||
),
|
||||
ATTR_PERIOD: probatio.All(probatio.Coerce(float), probatio.Clamp(min=0.05)),
|
||||
ATTR_CHANGE: probatio.All(
|
||||
|
||||
@@ -186,7 +186,6 @@ effect_move:
|
||||
- left
|
||||
theme:
|
||||
example: exciting
|
||||
default: exciting
|
||||
selector:
|
||||
select:
|
||||
mode: dropdown
|
||||
|
||||
@@ -341,6 +341,33 @@ async def test_effect_pulse_reads_color_before_merging(
|
||||
assert effect.color == HSBK(120.0, 1.0, 128 / 255, 3500)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("brightness", "expected"),
|
||||
[
|
||||
pytest.param({ATTR_BRIGHTNESS: 0}, 1 / 255, id="brightness"),
|
||||
pytest.param({ATTR_BRIGHTNESS_PCT: 0}, 0.01, id="brightness_pct"),
|
||||
],
|
||||
)
|
||||
async def test_effect_pulse_clamps_brightness_above_zero(
|
||||
hass: HomeAssistant,
|
||||
mock_effect_conductor: MagicMock,
|
||||
brightness: dict[str, int],
|
||||
expected: float,
|
||||
) -> None:
|
||||
"""Test a zero brightness is raised, as it would pulse to black."""
|
||||
await async_setup_lifx_entry(hass, create_mock_light())
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_EFFECT_PULSE,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_HS_COLOR: (120.0, 50.0), **brightness},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
effect, _ = mock_effect_conductor.start.await_args.args
|
||||
assert effect.color == HSBK(120.0, 0.5, expected, 3500)
|
||||
|
||||
|
||||
async def test_effect_colorloop_uses_public_effect_defaults(
|
||||
hass: HomeAssistant, mock_effect_conductor: MagicMock
|
||||
) -> None:
|
||||
@@ -390,6 +417,24 @@ async def test_effect_colorloop_orders_saturation_bounds(
|
||||
assert effect.saturation_max == 0.8
|
||||
|
||||
|
||||
async def test_effect_colorloop_clamps_saturation_above_zero(
|
||||
hass: HomeAssistant, mock_effect_conductor: MagicMock
|
||||
) -> None:
|
||||
"""Test a zero saturation is raised, as it switches the bulb to white."""
|
||||
await async_setup_lifx_entry(hass, create_mock_light())
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_EFFECT_COLORLOOP,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_SATURATION_MIN: 0, ATTR_SATURATION_MAX: 0},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
effect = mock_effect_conductor.start.await_args.args[0]
|
||||
assert effect.saturation_min == 0.01
|
||||
assert effect.saturation_max == 0.01
|
||||
|
||||
|
||||
async def test_effect_colorloop_accepts_absolute_brightness(
|
||||
hass: HomeAssistant, mock_effect_conductor: MagicMock
|
||||
) -> None:
|
||||
@@ -1670,6 +1715,26 @@ def test_theme_selector_options_match_the_library(service: str) -> None:
|
||||
assert options == sorted(ThemeLibrary.get_available_themes())
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service", "default"),
|
||||
[
|
||||
pytest.param(SERVICE_EFFECT_MOVE, None, id="move"),
|
||||
pytest.param(SERVICE_PAINT_THEME, "exciting", id="paint_theme"),
|
||||
],
|
||||
)
|
||||
def test_theme_field_default_matches_the_action(
|
||||
service: str, default: str | None
|
||||
) -> None:
|
||||
"""Test the UI pre-fills the theme the action applies when none is given.
|
||||
|
||||
Move applies no theme without one, so a default would suggest a color
|
||||
change the action does not otherwise make.
|
||||
"""
|
||||
services = load_yaml_dict(f"{lifx.__path__[0]}/services.yaml")
|
||||
|
||||
assert services[service]["fields"][ATTR_THEME].get("default") == default
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service", "factory", "palette"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user