Files
core/tests/components/lutron_caseta/test_light.py
2026-06-08 11:41:06 +02:00

255 lines
7.7 KiB
Python

"""Tests for the Lutron Caseta integration."""
from unittest.mock import patch
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP_KELVIN,
ATTR_WHITE,
DOMAIN as LIGHT_DOMAIN,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.const import ATTR_ENTITY_ID, STATE_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_component import async_update_entity
from . import MockBridge, MockBridgeWithColorLight, async_setup_integration
async def test_light_unique_id(
hass: HomeAssistant, entity_registry: er.EntityRegistry
) -> None:
"""Test a light unique id."""
await async_setup_integration(hass, MockBridge)
ra3_entity_id = "light.basement_bedroom_basement_bedroom_main_lights"
caseta_entity_id = "light.kitchen_kitchen_main_lights"
# Assert that RA3 lights will have the bridge serial hash
# and the zone id as the uniqueID
assert entity_registry.async_get(ra3_entity_id).unique_id == "000004d2_801"
# Assert that Caseta lights will have the serial number as the uniqueID
assert entity_registry.async_get(caseta_entity_id).unique_id == "5442321"
state = hass.states.get(ra3_entity_id)
assert state.state == STATE_ON
async def test_previous_brightness(
hass: HomeAssistant,
) -> None:
"""Test brightness tracked and restored."""
await async_setup_integration(hass, MockBridge)
caseta_entity_id = "light.kitchen_kitchen_other_lights"
# 1. Turn on with explicit brightness 25
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_BRIGHTNESS: 25},
target={ATTR_ENTITY_ID: caseta_entity_id},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(caseta_entity_id)
assert state is not None
assert state.state == STATE_ON
assert state.attributes.get(ATTR_BRIGHTNESS) == 25
# 2. Turn off
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
target={ATTR_ENTITY_ID: caseta_entity_id},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(caseta_entity_id)
# 3. Turn on again without brightness → expect 25
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{},
target={ATTR_ENTITY_ID: caseta_entity_id},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(caseta_entity_id)
assert state is not None
assert state.attributes.get(ATTR_BRIGHTNESS) == 25
async def test_previous_brightness_physical_switch(
hass: HomeAssistant,
) -> None:
"""Test that brightness set via a physical switch is restored on next turn-on."""
mock_entry = await async_setup_integration(hass, MockBridge)
caseta_entity_id = "light.kitchen_kitchen_other_lights"
bridge = mock_entry.runtime_data.bridge
# Simulate the physical dimmer setting brightness to 72 (Lutron 0-100 scale).
bridge.devices["902"]["current_state"] = 72
bridge.call_subscribers("902")
await hass.async_block_till_done()
# Turn off via HA.
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
target={ATTR_ENTITY_ID: caseta_entity_id},
blocking=True,
)
await hass.async_block_till_done()
# Turn on via HA without an explicit brightness → expect the physical level.
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{},
target={ATTR_ENTITY_ID: caseta_entity_id},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(caseta_entity_id)
assert state is not None
# to_hass_level(72) == (72 * 255) // 100 == 183
assert state.attributes.get(ATTR_BRIGHTNESS) == 183
async def test_previous_brightness_zero_not_remembered(
hass: HomeAssistant,
) -> None:
"""Test that a zero brightness is not remembered as the restore level."""
await async_setup_integration(hass, MockBridge)
caseta_entity_id = "light.kitchen_kitchen_other_lights"
# 1. Establish a non-zero previous brightness of 25
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_BRIGHTNESS: 25},
target={ATTR_ENTITY_ID: caseta_entity_id},
blocking=True,
)
await hass.async_block_till_done()
# 2. Turn on with an explicit brightness of 0 (effectively off)
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_BRIGHTNESS: 0},
target={ATTR_ENTITY_ID: caseta_entity_id},
blocking=True,
)
await hass.async_block_till_done()
# 3. Turn on without brightness → the 0 is ignored and 25 is restored
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{},
target={ATTR_ENTITY_ID: caseta_entity_id},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(caseta_entity_id)
assert state is not None
assert state.state == STATE_ON
assert state.attributes.get(ATTR_BRIGHTNESS) == 25
async def test_color_only_turn_on_preserves_brightness(
hass: HomeAssistant,
) -> None:
"""Test a color-only turn-on does not override the current brightness."""
mock_entry = await async_setup_integration(hass, MockBridgeWithColorLight)
entity_id = "light.kitchen_kitchen_color_light"
bridge = mock_entry.runtime_data.bridge
with patch.object(bridge, "set_value", wraps=bridge.set_value) as mock_set_value:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_COLOR_TEMP_KELVIN: 3000},
target={ATTR_ENTITY_ID: entity_id},
blocking=True,
)
await hass.async_block_till_done()
# A color-only change must leave brightness untouched, i.e. value=None
assert mock_set_value.call_args is not None
assert mock_set_value.call_args.kwargs["value"] is None
async def test_white_mode_turn_on_remembers_brightness(
hass: HomeAssistant,
) -> None:
"""Test turning on in white (warm dim) mode tracks the brightness."""
await async_setup_integration(hass, MockBridgeWithColorLight)
entity_id = "light.kitchen_kitchen_spectrum_light"
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_WHITE: 100},
target={ATTR_ENTITY_ID: entity_id},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state is not None
assert state.state == STATE_ON
async def test_async_update_syncs_previous_brightness(
hass: HomeAssistant,
) -> None:
"""Test forcing an update keeps the previous brightness in sync."""
mock_entry = await async_setup_integration(hass, MockBridge)
entity_id = "light.kitchen_kitchen_other_lights"
bridge = mock_entry.runtime_data.bridge
# Change the level on the device and force an entity refresh.
bridge.devices["902"]["current_state"] = 60
await async_update_entity(hass, entity_id)
await hass.async_block_till_done()
# Turn off, then on without brightness → the synced level is restored.
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
target={ATTR_ENTITY_ID: entity_id},
blocking=True,
)
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{},
target={ATTR_ENTITY_ID: entity_id},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get(entity_id)
assert state is not None
# to_hass_level(60) == (60 * 255) // 100 == 153
assert state.attributes.get(ATTR_BRIGHTNESS) == 153