2022-08-28 12:05:31 -07:00
|
|
|
"""Tests for Adaptive Lighting integration."""
|
2024-02-05 14:13:13 -08:00
|
|
|
|
2026-09-06 05:04:18 -06:00
|
|
|
import pytest
|
|
|
|
|
import voluptuous.error
|
2024-04-06 10:14:54 +02:00
|
|
|
from homeassistant.components import adaptive_lighting
|
|
|
|
|
from homeassistant.components.adaptive_lighting.const import (
|
2026-09-06 05:04:18 -06:00
|
|
|
CONF_LIGHTS,
|
2024-04-06 10:14:54 +02:00
|
|
|
DEFAULT_NAME,
|
2026-09-06 05:04:18 -06:00
|
|
|
SERVICE_APPLY,
|
|
|
|
|
SERVICE_CHANGE_SWITCH_SETTINGS,
|
|
|
|
|
SERVICE_SET_MANUAL_CONTROL,
|
2024-04-06 10:14:54 +02:00
|
|
|
UNDO_UPDATE_LISTENER,
|
|
|
|
|
)
|
2026-09-06 05:04:18 -06:00
|
|
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
2022-08-28 14:30:24 -07:00
|
|
|
from homeassistant.config_entries import ConfigEntryState
|
2026-09-06 05:04:18 -06:00
|
|
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_NAME
|
|
|
|
|
from homeassistant.exceptions import ServiceValidationError
|
|
|
|
|
from homeassistant.helpers import service
|
2022-08-28 12:05:31 -07:00
|
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
|
|
2024-04-06 10:14:54 +02:00
|
|
|
from tests.common import MockConfigEntry
|
2022-08-28 12:05:31 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_setup_with_config(hass):
|
|
|
|
|
"""Test that we import the config and setup the integration."""
|
|
|
|
|
config = {
|
|
|
|
|
adaptive_lighting.DOMAIN: {
|
|
|
|
|
adaptive_lighting.CONF_NAME: DEFAULT_NAME,
|
2024-03-03 15:09:18 -08:00
|
|
|
},
|
2022-08-28 12:05:31 -07:00
|
|
|
}
|
|
|
|
|
assert await async_setup_component(hass, adaptive_lighting.DOMAIN, config)
|
|
|
|
|
assert adaptive_lighting.DOMAIN in hass.data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_successful_config_entry(hass):
|
|
|
|
|
"""Test that Adaptive Lighting is configured successfully."""
|
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
|
domain=adaptive_lighting.DOMAIN,
|
|
|
|
|
data={CONF_NAME: DEFAULT_NAME},
|
|
|
|
|
)
|
|
|
|
|
entry.add_to_hass(hass)
|
|
|
|
|
|
|
|
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
|
|
|
|
2022-08-28 14:30:24 -07:00
|
|
|
assert entry.state == ConfigEntryState.LOADED
|
2022-08-28 12:05:31 -07:00
|
|
|
|
|
|
|
|
assert UNDO_UPDATE_LISTENER in hass.data[adaptive_lighting.DOMAIN][entry.entry_id]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_unload_entry(hass):
|
|
|
|
|
"""Test removing Adaptive Lighting."""
|
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
|
domain=adaptive_lighting.DOMAIN,
|
|
|
|
|
data={CONF_NAME: DEFAULT_NAME},
|
|
|
|
|
)
|
|
|
|
|
entry.add_to_hass(hass)
|
|
|
|
|
|
|
|
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
|
|
|
|
|
|
|
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
2022-08-28 14:30:24 -07:00
|
|
|
assert entry.state == ConfigEntryState.NOT_LOADED
|
2022-08-28 12:05:31 -07:00
|
|
|
assert adaptive_lighting.DOMAIN not in hass.data
|
2026-09-06 05:04:18 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_services_survive_entry_unload_and_reload(hass):
|
|
|
|
|
"""Test integration services remain registered across entry lifecycle."""
|
|
|
|
|
assert await async_setup_component(hass, adaptive_lighting.DOMAIN, {})
|
|
|
|
|
service_names = (
|
|
|
|
|
SERVICE_APPLY,
|
|
|
|
|
SERVICE_CHANGE_SWITCH_SETTINGS,
|
|
|
|
|
SERVICE_SET_MANUAL_CONTROL,
|
|
|
|
|
)
|
|
|
|
|
services = hass.services.async_services()[adaptive_lighting.DOMAIN]
|
|
|
|
|
assert SERVICE_APPLY in services
|
|
|
|
|
assert SERVICE_SET_MANUAL_CONTROL in services
|
|
|
|
|
if hasattr(service, "async_register_platform_entity_service"):
|
|
|
|
|
assert SERVICE_CHANGE_SWITCH_SETTINGS in services
|
|
|
|
|
else:
|
|
|
|
|
assert SERVICE_CHANGE_SWITCH_SETTINGS not in services
|
|
|
|
|
|
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
|
domain=adaptive_lighting.DOMAIN,
|
|
|
|
|
data={CONF_NAME: DEFAULT_NAME},
|
|
|
|
|
)
|
|
|
|
|
entry.add_to_hass(hass)
|
|
|
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
|
|
|
registered = {
|
|
|
|
|
name: hass.services.async_services()[adaptive_lighting.DOMAIN][name]
|
|
|
|
|
for name in service_names
|
|
|
|
|
}
|
|
|
|
|
switch = hass.data[adaptive_lighting.DOMAIN][entry.entry_id][SWITCH_DOMAIN]
|
|
|
|
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
|
|
|
|
|
|
|
|
|
for name in service_names:
|
|
|
|
|
assert (
|
|
|
|
|
hass.services.async_services()[adaptive_lighting.DOMAIN][name]
|
|
|
|
|
is registered[name]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(ServiceValidationError, match="No Adaptive Lighting"):
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
|
adaptive_lighting.DOMAIN,
|
|
|
|
|
SERVICE_APPLY,
|
|
|
|
|
{ATTR_ENTITY_ID: switch.entity_id},
|
|
|
|
|
blocking=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
|
|
|
for name in service_names:
|
|
|
|
|
assert (
|
|
|
|
|
hass.services.async_services()[adaptive_lighting.DOMAIN][name]
|
|
|
|
|
is registered[name]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
|
adaptive_lighting.DOMAIN,
|
|
|
|
|
SERVICE_CHANGE_SWITCH_SETTINGS,
|
|
|
|
|
{ATTR_ENTITY_ID: switch.entity_id},
|
|
|
|
|
blocking=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_service_call_without_loaded_entry(hass):
|
|
|
|
|
"""Test global services reject calls when no profile is loaded."""
|
|
|
|
|
assert await async_setup_component(hass, adaptive_lighting.DOMAIN, {})
|
|
|
|
|
|
|
|
|
|
with pytest.raises(ServiceValidationError, match="No Adaptive Lighting"):
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
|
adaptive_lighting.DOMAIN,
|
|
|
|
|
SERVICE_APPLY,
|
|
|
|
|
{CONF_LIGHTS: ["light.test"]},
|
|
|
|
|
blocking=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
pending_entry = MockConfigEntry(
|
|
|
|
|
domain=adaptive_lighting.DOMAIN,
|
|
|
|
|
data={CONF_NAME: "pending"},
|
|
|
|
|
)
|
|
|
|
|
pending_entry.add_to_hass(hass)
|
|
|
|
|
hass.data[adaptive_lighting.DOMAIN] = {pending_entry.entry_id: {}}
|
|
|
|
|
with pytest.raises(ServiceValidationError, match="not found in any switch"):
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
|
adaptive_lighting.DOMAIN,
|
|
|
|
|
SERVICE_APPLY,
|
|
|
|
|
{CONF_LIGHTS: ["light.test"]},
|
|
|
|
|
blocking=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_apply_rejects_unknown_light(hass):
|
|
|
|
|
"""Test the apply service rejects an unknown light target."""
|
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
|
domain=adaptive_lighting.DOMAIN,
|
|
|
|
|
data={CONF_NAME: DEFAULT_NAME},
|
|
|
|
|
)
|
|
|
|
|
entry.add_to_hass(hass)
|
|
|
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
|
|
|
with pytest.raises(ServiceValidationError, match="not found in any switch"):
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
|
adaptive_lighting.DOMAIN,
|
|
|
|
|
SERVICE_APPLY,
|
|
|
|
|
{CONF_LIGHTS: ["light.does_not_exist"]},
|
|
|
|
|
blocking=True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_change_switch_settings_requires_entity_target(hass):
|
|
|
|
|
"""Test change_switch_settings rejects a missing entity target."""
|
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
|
domain=adaptive_lighting.DOMAIN,
|
|
|
|
|
data={CONF_NAME: DEFAULT_NAME},
|
|
|
|
|
)
|
|
|
|
|
entry.add_to_hass(hass)
|
|
|
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(
|
|
|
|
|
voluptuous.error.MultipleInvalid,
|
|
|
|
|
match=r"must contain at least one of entity_id.*area_id",
|
|
|
|
|
):
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
|
adaptive_lighting.DOMAIN,
|
|
|
|
|
SERVICE_CHANGE_SWITCH_SETTINGS,
|
|
|
|
|
{},
|
|
|
|
|
blocking=True,
|
|
|
|
|
)
|