This commit is contained in:
J. Nick Koston 2025-07-05 13:41:09 -05:00
parent 6dbdeeb59b
commit e99b8d2daf
No known key found for this signature in database
2 changed files with 269 additions and 0 deletions

View File

@ -0,0 +1,80 @@
esphome:
name: light-calls-test
host:
api: # Port will be automatically injected
logger:
level: DEBUG
# Test outputs for RGBCW light
output:
- platform: template
id: test_red
type: float
write_action:
- logger.log:
format: "Red output: %.2f"
args: [state]
- platform: template
id: test_green
type: float
write_action:
- logger.log:
format: "Green output: %.2f"
args: [state]
- platform: template
id: test_blue
type: float
write_action:
- logger.log:
format: "Blue output: %.2f"
args: [state]
- platform: template
id: test_cold_white
type: float
write_action:
- logger.log:
format: "Cold white output: %.2f"
args: [state]
- platform: template
id: test_warm_white
type: float
write_action:
- logger.log:
format: "Warm white output: %.2f"
args: [state]
light:
- platform: rgbww
name: "Test RGBCW Light"
id: test_light
red: test_red
green: test_green
blue: test_blue
cold_white: test_cold_white
warm_white: test_warm_white
cold_white_color_temperature: 6536 K
warm_white_color_temperature: 2000 K
constant_brightness: true
effects:
- random:
name: "Random Effect"
transition_length: 100ms
update_interval: 200ms
- strobe:
name: "Strobe Effect"
- pulse:
name: "Pulse Effect"
transition_length: 100ms
# Additional lights to test memory with multiple instances
- platform: rgb
name: "Test RGB Light"
id: test_rgb_light
red: test_red
green: test_green
blue: test_blue
- platform: binary
name: "Test Binary Light"
id: test_binary_light
output: test_red

View File

@ -0,0 +1,189 @@
"""Integration test for all light call combinations.
Tests that LightCall handles all possible light operations correctly
including RGB, color temperature, effects, transitions, and flash.
"""
import asyncio
from typing import Any
import pytest
from .types import APIClientConnectedFactory, RunCompiledFunction
@pytest.mark.asyncio
async def test_light_calls(
yaml_config: str,
run_compiled: RunCompiledFunction,
api_client_connected: APIClientConnectedFactory,
) -> None:
"""Test all possible LightCall operations and combinations."""
async with run_compiled(yaml_config), api_client_connected() as client:
# Track state changes with futures
state_futures: dict[int, asyncio.Future[Any]] = {}
states: dict[int, Any] = {}
def on_state(state):
states[state.key] = state
if state.key in state_futures and not state_futures[state.key].done():
state_futures[state.key].set_result(state)
client.subscribe_states(on_state)
# Get the light entities
entities = await client.list_entities_services()
lights = [e for e in entities[0] if e.object_id.startswith("test_")]
assert len(lights) >= 2 # Should have RGBCW and RGB lights
rgbcw_light = next(light for light in lights if "RGBCW" in light.name)
rgb_light = next(light for light in lights if "RGB Light" in light.name)
async def wait_for_state_change(key, timeout=1.0):
"""Wait for a state change for the given entity key."""
loop = asyncio.get_event_loop()
state_futures[key] = loop.create_future()
try:
return await asyncio.wait_for(state_futures[key], timeout)
finally:
state_futures.pop(key, None)
# Test all individual parameters first
# Test 1: state only
client.light_command(key=rgbcw_light.key, state=True)
state = await wait_for_state_change(rgbcw_light.key)
assert state.state is True
# Test 2: brightness only
client.light_command(key=rgbcw_light.key, brightness=0.5)
state = await wait_for_state_change(rgbcw_light.key)
assert state.brightness == pytest.approx(0.5)
# Test 3: color_brightness only
client.light_command(key=rgbcw_light.key, color_brightness=0.8)
state = await wait_for_state_change(rgbcw_light.key)
assert state.color_brightness == pytest.approx(0.8)
# Test 4-7: RGB values must be set together via rgb parameter
client.light_command(key=rgbcw_light.key, rgb=(0.7, 0.3, 0.9))
state = await wait_for_state_change(rgbcw_light.key)
assert state.red == pytest.approx(0.7, abs=0.1)
assert state.green == pytest.approx(0.3, abs=0.1)
assert state.blue == pytest.approx(0.9, abs=0.1)
# Test 7: white value
client.light_command(key=rgbcw_light.key, white=0.6)
state = await wait_for_state_change(rgbcw_light.key)
# White might need more tolerance or might not be directly settable
if hasattr(state, "white"):
assert state.white == pytest.approx(0.6, abs=0.1)
# Test 8: color_temperature only
client.light_command(key=rgbcw_light.key, color_temperature=300)
state = await wait_for_state_change(rgbcw_light.key)
assert state.color_temperature == pytest.approx(300)
# Test 9: cold_white only
client.light_command(key=rgbcw_light.key, cold_white=0.8)
state = await wait_for_state_change(rgbcw_light.key)
assert state.cold_white == pytest.approx(0.8)
# Test 10: warm_white only
client.light_command(key=rgbcw_light.key, warm_white=0.2)
state = await wait_for_state_change(rgbcw_light.key)
assert state.warm_white == pytest.approx(0.2)
# Test 11: transition_length with state change
client.light_command(key=rgbcw_light.key, state=False, transition_length=0.1)
state = await wait_for_state_change(rgbcw_light.key)
assert state.state is False
# Test 12: flash_length
client.light_command(key=rgbcw_light.key, state=True, flash_length=0.2)
state = await wait_for_state_change(rgbcw_light.key)
# Flash starts
assert state.state is True
# Wait for flash to end
state = await wait_for_state_change(rgbcw_light.key)
# Test 13: effect only
# First ensure light is on
client.light_command(key=rgbcw_light.key, state=True)
state = await wait_for_state_change(rgbcw_light.key)
# Now set effect
client.light_command(key=rgbcw_light.key, effect="Random Effect")
state = await wait_for_state_change(rgbcw_light.key)
assert state.effect == "Random Effect"
# Test 14: stop effect
client.light_command(key=rgbcw_light.key, effect="None")
state = await wait_for_state_change(rgbcw_light.key)
assert state.effect == "None"
# Test 15: color_mode parameter
client.light_command(
key=rgbcw_light.key, state=True, color_mode=5
) # COLD_WARM_WHITE
state = await wait_for_state_change(rgbcw_light.key)
assert state.state is True
# Now test common combinations
# Test 16: RGB combination (set_rgb) - RGB values get normalized
client.light_command(key=rgbcw_light.key, rgb=(1.0, 0.0, 0.5))
state = await wait_for_state_change(rgbcw_light.key)
# RGB values get normalized - in this case red is already 1.0
assert state.red == pytest.approx(1.0, abs=0.1)
assert state.green == pytest.approx(0.0, abs=0.1)
assert state.blue == pytest.approx(0.5, abs=0.1)
# Test 17: Multiple RGB changes to test transitions
client.light_command(key=rgbcw_light.key, rgb=(0.2, 0.8, 0.4))
state = await wait_for_state_change(rgbcw_light.key)
# RGB values get normalized so green (highest) becomes 1.0
# Expected: (0.2/0.8, 0.8/0.8, 0.4/0.8) = (0.25, 1.0, 0.5)
assert state.red == pytest.approx(0.25, abs=0.01)
assert state.green == pytest.approx(1.0, abs=0.01)
assert state.blue == pytest.approx(0.5, abs=0.01)
# Test 18: State + brightness + transition
client.light_command(
key=rgbcw_light.key, state=True, brightness=0.7, transition_length=0.1
)
state = await wait_for_state_change(rgbcw_light.key)
assert state.state is True
assert state.brightness == pytest.approx(0.7)
# Test 19: RGB + brightness + color_brightness
client.light_command(
key=rgb_light.key,
state=True,
brightness=0.8,
color_brightness=0.9,
rgb=(0.2, 0.4, 0.6),
)
state = await wait_for_state_change(rgb_light.key)
assert state.state is True
assert state.brightness == pytest.approx(0.8)
# Test 20: Color temp + cold/warm white
client.light_command(
key=rgbcw_light.key, color_temperature=250, cold_white=0.7, warm_white=0.3
)
state = await wait_for_state_change(rgbcw_light.key)
assert state.color_temperature == pytest.approx(250)
# Test 21: Turn RGB light off
client.light_command(key=rgb_light.key, state=False)
state = await wait_for_state_change(rgb_light.key)
assert state.state is False
# Final cleanup - turn all lights off
for light in lights:
client.light_command(
key=light.key,
state=False,
)
state = await wait_for_state_change(light.key)
assert state.state is False