mirror of https://github.com/home-assistant/core
350 lines
11 KiB
Python
350 lines
11 KiB
Python
"""Tests for the integration of a twinly device."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import timedelta
|
|
from unittest.mock import patch
|
|
|
|
from freezegun.api import FrozenDateTimeFactory
|
|
|
|
from homeassistant.components.light import ATTR_BRIGHTNESS, LightEntityFeature
|
|
from homeassistant.components.twinkly.const import DOMAIN as TWINKLY_DOMAIN
|
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
|
from homeassistant.helpers.device_registry import DeviceEntry
|
|
from homeassistant.helpers.entity_registry import RegistryEntry
|
|
|
|
from . import TEST_MODEL, TEST_NAME, TEST_NAME_ORIGINAL, ClientMock
|
|
|
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
|
|
|
|
|
async def test_initial_state(hass: HomeAssistant) -> None:
|
|
"""Validate that entity and device states are updated on startup."""
|
|
entity, device, _, _ = await _create_entries(hass)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
# Basic state properties
|
|
assert state.name == TEST_NAME
|
|
assert state.state == "on"
|
|
assert state.attributes[ATTR_BRIGHTNESS] == 26
|
|
assert state.attributes["friendly_name"] == TEST_NAME
|
|
|
|
assert device.name == TEST_NAME
|
|
assert device.model == TEST_MODEL
|
|
assert device.manufacturer == "LEDWORKS"
|
|
|
|
|
|
async def test_turn_on_off(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_on service."""
|
|
client = ClientMock()
|
|
client.state = False
|
|
client.brightness = {"mode": "enabled", "value": 20}
|
|
entity, _, _, _ = await _create_entries(hass, client)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "off"
|
|
|
|
await hass.services.async_call(
|
|
"light", "turn_on", service_data={"entity_id": entity.entity_id}, blocking=True
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "on"
|
|
assert state.attributes[ATTR_BRIGHTNESS] == 51
|
|
|
|
|
|
async def test_turn_on_with_brightness(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_on service with a brightness parameter."""
|
|
client = ClientMock()
|
|
client.state = False
|
|
client.brightness = {"mode": "enabled", "value": 20}
|
|
entity, _, _, _ = await _create_entries(hass, client)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "off"
|
|
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
service_data={"entity_id": entity.entity_id, "brightness": 255},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "on"
|
|
assert state.attributes[ATTR_BRIGHTNESS] == 255
|
|
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
service_data={"entity_id": entity.entity_id, "brightness": 1},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "off"
|
|
|
|
|
|
async def test_turn_on_with_color_rgbw(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_on service with a rgbw parameter."""
|
|
client = ClientMock()
|
|
client.state = False
|
|
client.device_info["led_profile"] = "RGBW"
|
|
client.brightness = {"mode": "enabled", "value": 255}
|
|
entity, _, _, _ = await _create_entries(hass, client)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "off"
|
|
assert (
|
|
LightEntityFeature.EFFECT
|
|
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
service_data={"entity_id": entity.entity_id, "rgbw_color": (128, 64, 32, 0)},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "on"
|
|
assert client.color == (128, 64, 32)
|
|
assert client.default_mode == "color"
|
|
assert client.mode == "color"
|
|
|
|
|
|
async def test_turn_on_with_color_rgb(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_on service with a rgb parameter."""
|
|
client = ClientMock()
|
|
client.state = False
|
|
client.device_info["led_profile"] = "RGB"
|
|
client.brightness = {"mode": "enabled", "value": 255}
|
|
entity, _, _, _ = await _create_entries(hass, client)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "off"
|
|
assert (
|
|
LightEntityFeature.EFFECT
|
|
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
service_data={"entity_id": entity.entity_id, "rgb_color": (128, 64, 32)},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "on"
|
|
assert client.color == (128, 64, 32)
|
|
assert client.default_mode == "color"
|
|
assert client.mode == "color"
|
|
|
|
|
|
async def test_turn_on_with_effect(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_on service with effects."""
|
|
client = ClientMock()
|
|
client.state = False
|
|
client.device_info["led_profile"] = "RGB"
|
|
client.brightness = {"mode": "enabled", "value": 255}
|
|
entity, _, _, _ = await _create_entries(hass, client)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "off"
|
|
assert not client.current_movie
|
|
assert (
|
|
LightEntityFeature.EFFECT
|
|
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
service_data={"entity_id": entity.entity_id, "effect": "1 Rainbow"},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "on"
|
|
assert client.current_movie["id"] == 1
|
|
assert client.default_mode == "movie"
|
|
assert client.mode == "movie"
|
|
|
|
|
|
async def test_turn_on_with_color_rgbw_and_missing_effect(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_on service with rgbw color and missing effect support."""
|
|
client = ClientMock()
|
|
client.state = False
|
|
client.device_info["led_profile"] = "RGBW"
|
|
client.brightness = {"mode": "enabled", "value": 255}
|
|
client.version = "2.7.0"
|
|
entity, _, _, _ = await _create_entries(hass, client)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "off"
|
|
assert (
|
|
not LightEntityFeature.EFFECT
|
|
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
service_data={"entity_id": entity.entity_id, "rgbw_color": (128, 64, 32, 0)},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "on"
|
|
assert client.color == (0, 128, 64, 32)
|
|
assert client.mode == "movie"
|
|
assert client.default_mode == "movie"
|
|
|
|
|
|
async def test_turn_on_with_color_rgb_and_missing_effect(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_on service with rgb color and missing effect support."""
|
|
client = ClientMock()
|
|
client.state = False
|
|
client.device_info["led_profile"] = "RGB"
|
|
client.brightness = {"mode": "enabled", "value": 255}
|
|
client.version = "2.7.0"
|
|
entity, _, _, _ = await _create_entries(hass, client)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "off"
|
|
assert (
|
|
not LightEntityFeature.EFFECT
|
|
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
service_data={"entity_id": entity.entity_id, "rgb_color": (128, 64, 32)},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "on"
|
|
assert client.color == (128, 64, 32)
|
|
assert client.mode == "movie"
|
|
assert client.default_mode == "movie"
|
|
|
|
|
|
async def test_turn_on_with_effect_missing_effects(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_on service with effect set even if effects are not supported."""
|
|
client = ClientMock()
|
|
client.state = False
|
|
client.device_info["led_profile"] = "RGB"
|
|
client.brightness = {"mode": "enabled", "value": 255}
|
|
client.version = "2.7.0"
|
|
entity, _, _, _ = await _create_entries(hass, client)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "off"
|
|
assert not client.current_movie
|
|
assert (
|
|
not LightEntityFeature.EFFECT
|
|
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
|
)
|
|
|
|
await hass.services.async_call(
|
|
"light",
|
|
"turn_on",
|
|
service_data={"entity_id": entity.entity_id, "effect": "1 Rainbow"},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "on"
|
|
assert not client.current_movie
|
|
assert client.default_mode == "movie"
|
|
assert client.mode == "movie"
|
|
|
|
|
|
async def test_turn_off(hass: HomeAssistant) -> None:
|
|
"""Test support of the light.turn_off service."""
|
|
entity, _, _, _ = await _create_entries(hass)
|
|
|
|
assert hass.states.get(entity.entity_id).state == "on"
|
|
|
|
await hass.services.async_call(
|
|
"light", "turn_off", service_data={"entity_id": entity.entity_id}, blocking=True
|
|
)
|
|
|
|
state = hass.states.get(entity.entity_id)
|
|
|
|
assert state.state == "off"
|
|
|
|
|
|
async def test_update_name(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
freezer: FrozenDateTimeFactory,
|
|
) -> None:
|
|
"""Validate device's name update behavior.
|
|
|
|
Validate that if device name is changed from the Twinkly app,
|
|
then the name of the entity is updated and it's also persisted,
|
|
so it can be restored when starting HA while Twinkly is offline.
|
|
"""
|
|
entity, _, client, config_entry = await _create_entries(hass)
|
|
|
|
client.change_name("new_device_name")
|
|
freezer.tick(timedelta(seconds=30))
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
dev_entry = device_registry.async_get_device({(TWINKLY_DOMAIN, client.id)})
|
|
|
|
assert dev_entry.name == "new_device_name"
|
|
assert config_entry.data[CONF_NAME] == "new_device_name"
|
|
|
|
|
|
async def test_unload(hass: HomeAssistant) -> None:
|
|
"""Validate that entities can be unloaded from the UI."""
|
|
|
|
_, _, client, _ = await _create_entries(hass)
|
|
entry_id = client.id
|
|
|
|
assert await hass.config_entries.async_unload(entry_id)
|
|
|
|
|
|
async def _create_entries(
|
|
hass: HomeAssistant, client=None
|
|
) -> tuple[RegistryEntry, DeviceEntry, ClientMock]:
|
|
client = ClientMock() if client is None else client
|
|
|
|
with patch("homeassistant.components.twinkly.Twinkly", return_value=client):
|
|
config_entry = MockConfigEntry(
|
|
domain=TWINKLY_DOMAIN,
|
|
data={
|
|
CONF_HOST: client,
|
|
CONF_ID: client.id,
|
|
CONF_NAME: TEST_NAME_ORIGINAL,
|
|
CONF_MODEL: TEST_MODEL,
|
|
},
|
|
entry_id=client.id,
|
|
)
|
|
config_entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(client.id)
|
|
await hass.async_block_till_done()
|
|
|
|
device_registry = dr.async_get(hass)
|
|
entity_registry = er.async_get(hass)
|
|
|
|
entity_id = entity_registry.async_get_entity_id("light", TWINKLY_DOMAIN, client.id)
|
|
entity_entry = entity_registry.async_get(entity_id)
|
|
device = device_registry.async_get_device(identifiers={(TWINKLY_DOMAIN, client.id)})
|
|
|
|
assert entity_entry is not None
|
|
assert device is not None
|
|
|
|
return entity_entry, device, client, config_entry
|