mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 17:31:15 -04:00
Co-authored-by: Daniel O'Connor <daniel.oconnor@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
255 lines
7.7 KiB
Python
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
|