mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Refactor Twinkly tests (#133725)
This commit is contained in:
parent
31c6443a9b
commit
7be3cad1db
@ -17,7 +17,7 @@
|
|||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"device_exists": "[%key:common::config_flow::abort::already_configured_device%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,120 +1,13 @@
|
|||||||
"""Constants and mock for the twinkly component tests."""
|
"""Constants and mock for the twinkly component tests."""
|
||||||
|
|
||||||
from aiohttp.client_exceptions import ClientConnectionError
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from homeassistant.components.twinkly.const import DEV_NAME
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
TEST_HOST = "test.twinkly.com"
|
|
||||||
TEST_ID = "twinkly_test_device_id"
|
|
||||||
TEST_UID = "4c8fccf5-e08a-4173-92d5-49bf479252a2"
|
|
||||||
TEST_MAC = "aa:bb:cc:dd:ee:ff"
|
|
||||||
TEST_NAME = "twinkly_test_device_name"
|
|
||||||
TEST_NAME_ORIGINAL = "twinkly_test_original_device_name" # the original (deprecated) name stored in the conf
|
|
||||||
TEST_MODEL = "twinkly_test_device_model"
|
|
||||||
|
|
||||||
|
|
||||||
class ClientMock:
|
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
|
||||||
"""A mock of the ttls.client.Twinkly."""
|
"""Fixture for setting up the component."""
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
"""Create a mocked client."""
|
await hass.async_block_till_done()
|
||||||
self.is_offline = False
|
|
||||||
self.state = True
|
|
||||||
self.brightness = {"mode": "enabled", "value": 10}
|
|
||||||
self.color = None
|
|
||||||
self.movies = [{"id": 1, "name": "Rainbow"}, {"id": 2, "name": "Flare"}]
|
|
||||||
self.current_movie = {}
|
|
||||||
self.default_mode = "movie"
|
|
||||||
self.mode = None
|
|
||||||
self.version = "2.8.10"
|
|
||||||
|
|
||||||
self.id = TEST_UID
|
|
||||||
self.device_info = {
|
|
||||||
"uuid": self.id,
|
|
||||||
"device_name": TEST_NAME,
|
|
||||||
"mac": TEST_MAC,
|
|
||||||
"product_code": TEST_MODEL,
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def host(self) -> str:
|
|
||||||
"""Get the mocked host."""
|
|
||||||
return TEST_HOST
|
|
||||||
|
|
||||||
async def get_details(self):
|
|
||||||
"""Get the mocked device info."""
|
|
||||||
if self.is_offline:
|
|
||||||
raise ClientConnectionError
|
|
||||||
return self.device_info
|
|
||||||
|
|
||||||
async def is_on(self) -> bool:
|
|
||||||
"""Get the mocked on/off state."""
|
|
||||||
if self.is_offline:
|
|
||||||
raise ClientConnectionError
|
|
||||||
return self.state
|
|
||||||
|
|
||||||
async def turn_on(self) -> None:
|
|
||||||
"""Set the mocked on state."""
|
|
||||||
if self.is_offline:
|
|
||||||
raise ClientConnectionError
|
|
||||||
self.state = True
|
|
||||||
self.mode = self.default_mode
|
|
||||||
|
|
||||||
async def turn_off(self) -> None:
|
|
||||||
"""Set the mocked off state."""
|
|
||||||
if self.is_offline:
|
|
||||||
raise ClientConnectionError
|
|
||||||
self.state = False
|
|
||||||
|
|
||||||
async def get_brightness(self) -> int:
|
|
||||||
"""Get the mocked brightness."""
|
|
||||||
if self.is_offline:
|
|
||||||
raise ClientConnectionError
|
|
||||||
return self.brightness
|
|
||||||
|
|
||||||
async def set_brightness(self, brightness: int) -> None:
|
|
||||||
"""Set the mocked brightness."""
|
|
||||||
if self.is_offline:
|
|
||||||
raise ClientConnectionError
|
|
||||||
self.brightness = {"mode": "enabled", "value": brightness}
|
|
||||||
|
|
||||||
def change_name(self, new_name: str) -> None:
|
|
||||||
"""Change the name of this virtual device."""
|
|
||||||
self.device_info[DEV_NAME] = new_name
|
|
||||||
|
|
||||||
async def set_static_colour(self, colour) -> None:
|
|
||||||
"""Set static color."""
|
|
||||||
self.color = colour
|
|
||||||
self.default_mode = "color"
|
|
||||||
|
|
||||||
async def set_cycle_colours(self, colour) -> None:
|
|
||||||
"""Set static color."""
|
|
||||||
self.color = colour
|
|
||||||
self.default_mode = "movie"
|
|
||||||
|
|
||||||
async def interview(self) -> None:
|
|
||||||
"""Interview."""
|
|
||||||
|
|
||||||
async def get_saved_movies(self) -> dict:
|
|
||||||
"""Get saved movies."""
|
|
||||||
return self.movies
|
|
||||||
|
|
||||||
async def get_current_movie(self) -> dict:
|
|
||||||
"""Get current movie."""
|
|
||||||
return self.current_movie
|
|
||||||
|
|
||||||
async def set_current_movie(self, movie_id: int) -> dict:
|
|
||||||
"""Set current movie."""
|
|
||||||
self.current_movie = {"id": movie_id}
|
|
||||||
|
|
||||||
async def set_mode(self, mode: str) -> None:
|
|
||||||
"""Set mode."""
|
|
||||||
if mode == "off":
|
|
||||||
await self.turn_off()
|
|
||||||
else:
|
|
||||||
await self.turn_on()
|
|
||||||
self.mode = mode
|
|
||||||
|
|
||||||
async def get_firmware_version(self) -> dict:
|
|
||||||
"""Get firmware version."""
|
|
||||||
return {"version": self.version}
|
|
||||||
|
@ -1,55 +1,74 @@
|
|||||||
"""Configure tests for the Twinkly integration."""
|
"""Configure tests for the Twinkly integration."""
|
||||||
|
|
||||||
from collections.abc import Awaitable, Callable, Coroutine
|
from collections.abc import Generator
|
||||||
from typing import Any
|
from unittest.mock import AsyncMock, patch
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.components.twinkly import DOMAIN
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
|
||||||
|
|
||||||
from . import TEST_MODEL, TEST_NAME, TEST_UID, ClientMock
|
from .const import TEST_MAC, TEST_MODEL, TEST_NAME
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import (
|
||||||
|
MockConfigEntry,
|
||||||
type ComponentSetup = Callable[[], Awaitable[ClientMock]]
|
load_json_array_fixture,
|
||||||
|
load_json_object_fixture,
|
||||||
DOMAIN = "twinkly"
|
)
|
||||||
TITLE = "Twinkly"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="config_entry")
|
@pytest.fixture
|
||||||
def mock_config_entry() -> MockConfigEntry:
|
def mock_config_entry() -> MockConfigEntry:
|
||||||
"""Create Twinkly entry in Home Assistant."""
|
"""Create Twinkly entry in Home Assistant."""
|
||||||
client = ClientMock()
|
|
||||||
return MockConfigEntry(
|
return MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
title=TITLE,
|
title="Twinkly",
|
||||||
unique_id=TEST_UID,
|
unique_id=TEST_MAC,
|
||||||
entry_id=TEST_UID,
|
|
||||||
data={
|
data={
|
||||||
"host": client.host,
|
CONF_HOST: "192.168.0.123",
|
||||||
"id": client.id,
|
CONF_ID: "497dcba3-ecbf-4587-a2dd-5eb0665e6880",
|
||||||
"name": TEST_NAME,
|
CONF_NAME: TEST_NAME,
|
||||||
"model": TEST_MODEL,
|
CONF_MODEL: TEST_MODEL,
|
||||||
"device_name": TEST_NAME,
|
|
||||||
},
|
},
|
||||||
|
entry_id="01JFMME2P6RA38V5AMPCJ2JYYV",
|
||||||
|
minor_version=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="setup_integration")
|
@pytest.fixture
|
||||||
async def mock_setup_integration(
|
def mock_twinkly_client() -> Generator[AsyncMock]:
|
||||||
hass: HomeAssistant, config_entry: MockConfigEntry
|
"""Mock the Twinkly client."""
|
||||||
) -> Callable[[], Coroutine[Any, Any, ClientMock]]:
|
with (
|
||||||
"""Fixture for setting up the component."""
|
patch(
|
||||||
config_entry.add_to_hass(hass)
|
"homeassistant.components.twinkly.Twinkly",
|
||||||
|
autospec=True,
|
||||||
|
) as mock_client,
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.twinkly.config_flow.Twinkly",
|
||||||
|
new=mock_client,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
client = mock_client.return_value
|
||||||
|
client.get_details.return_value = load_json_object_fixture(
|
||||||
|
"get_details.json", DOMAIN
|
||||||
|
)
|
||||||
|
client.get_firmware_version.return_value = load_json_object_fixture(
|
||||||
|
"get_firmware_version.json", DOMAIN
|
||||||
|
)
|
||||||
|
client.get_saved_movies.return_value = load_json_array_fixture(
|
||||||
|
"get_saved_movies.json", DOMAIN
|
||||||
|
)
|
||||||
|
client.get_current_movie.return_value = load_json_object_fixture(
|
||||||
|
"get_current_movie.json", DOMAIN
|
||||||
|
)
|
||||||
|
client.is_on.return_value = True
|
||||||
|
client.get_brightness.return_value = {"mode": "enabled", "value": 10}
|
||||||
|
client.host = "192.168.0.123"
|
||||||
|
yield client
|
||||||
|
|
||||||
async def func() -> ClientMock:
|
|
||||||
mock = ClientMock()
|
|
||||||
with patch("homeassistant.components.twinkly.Twinkly", return_value=mock):
|
|
||||||
assert await async_setup_component(hass, DOMAIN, {})
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
return mock
|
|
||||||
|
|
||||||
return func
|
@pytest.fixture
|
||||||
|
def mock_setup_entry() -> Generator[None]:
|
||||||
|
"""Mock setting up a config entry."""
|
||||||
|
with patch("homeassistant.components.twinkly.async_setup_entry", return_value=True):
|
||||||
|
yield
|
||||||
|
5
tests/components/twinkly/const.py
Normal file
5
tests/components/twinkly/const.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
"""Constants for the Twinkly tests."""
|
||||||
|
|
||||||
|
TEST_MAC = "00:2d:13:3b:aa:bb"
|
||||||
|
TEST_NAME = "Tree 1"
|
||||||
|
TEST_MODEL = "TW2016"
|
3
tests/components/twinkly/fixtures/get_current_movie.json
Normal file
3
tests/components/twinkly/fixtures/get_current_movie.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"id": 1
|
||||||
|
}
|
23
tests/components/twinkly/fixtures/get_details.json
Normal file
23
tests/components/twinkly/fixtures/get_details.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"product_name": "Twinkly",
|
||||||
|
"product_version": "1",
|
||||||
|
"hardware_version": "1",
|
||||||
|
"flash_size": 4,
|
||||||
|
"led_type": 1,
|
||||||
|
"led_version": "1",
|
||||||
|
"product_code": "TW2016",
|
||||||
|
"device_name": "Tree 1",
|
||||||
|
"uptime": "4087441",
|
||||||
|
"rssi": -78,
|
||||||
|
"hw_id": "002d133b",
|
||||||
|
"mac": "00:2d:13:3b:aa:bb",
|
||||||
|
"uuid": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"max_supported_led": 100,
|
||||||
|
"base_leds_number": 100,
|
||||||
|
"number_of_led": 100,
|
||||||
|
"led_profile": "RGB",
|
||||||
|
"frame_rate": 14,
|
||||||
|
"movie_capacity": 708,
|
||||||
|
"copyright": "LEDWORKS 2017",
|
||||||
|
"code": 1000
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{ "version": "2.7.2" }
|
4
tests/components/twinkly/fixtures/get_saved_movies.json
Normal file
4
tests/components/twinkly/fixtures/get_saved_movies.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[
|
||||||
|
{ "id": 1, "name": "Rainbow" },
|
||||||
|
{ "id": 2, "name": "Flare" }
|
||||||
|
]
|
@ -3,35 +3,64 @@
|
|||||||
dict({
|
dict({
|
||||||
'attributes': dict({
|
'attributes': dict({
|
||||||
'brightness': 26,
|
'brightness': 26,
|
||||||
'color_mode': 'brightness',
|
'color_mode': 'rgb',
|
||||||
'effect': None,
|
'effect': None,
|
||||||
'effect_list': list([
|
'effect_list': list([
|
||||||
]),
|
]),
|
||||||
'friendly_name': 'twinkly_test_device_name',
|
'friendly_name': 'Tree 1',
|
||||||
|
'hs_color': list([
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
]),
|
||||||
|
'rgb_color': list([
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
]),
|
||||||
'supported_color_modes': list([
|
'supported_color_modes': list([
|
||||||
'brightness',
|
'rgb',
|
||||||
]),
|
]),
|
||||||
'supported_features': 4,
|
'supported_features': 4,
|
||||||
|
'xy_color': list([
|
||||||
|
0.323,
|
||||||
|
0.329,
|
||||||
|
]),
|
||||||
}),
|
}),
|
||||||
'device_info': dict({
|
'device_info': dict({
|
||||||
'device_name': 'twinkly_test_device_name',
|
'base_leds_number': 100,
|
||||||
|
'code': 1000,
|
||||||
|
'copyright': 'LEDWORKS 2017',
|
||||||
|
'device_name': 'Tree 1',
|
||||||
|
'flash_size': 4,
|
||||||
|
'frame_rate': 14,
|
||||||
|
'hardware_version': '1',
|
||||||
|
'hw_id': '002d133b',
|
||||||
|
'led_profile': 'RGB',
|
||||||
|
'led_type': 1,
|
||||||
|
'led_version': '1',
|
||||||
'mac': '**REDACTED**',
|
'mac': '**REDACTED**',
|
||||||
'product_code': 'twinkly_test_device_model',
|
'max_supported_led': 100,
|
||||||
'uuid': '4c8fccf5-e08a-4173-92d5-49bf479252a2',
|
'movie_capacity': 708,
|
||||||
|
'number_of_led': 100,
|
||||||
|
'product_code': 'TW2016',
|
||||||
|
'product_name': 'Twinkly',
|
||||||
|
'product_version': '1',
|
||||||
|
'rssi': -78,
|
||||||
|
'uptime': '4087441',
|
||||||
|
'uuid': '00000000-0000-0000-0000-000000000000',
|
||||||
}),
|
}),
|
||||||
'entry': dict({
|
'entry': dict({
|
||||||
'data': dict({
|
'data': dict({
|
||||||
'device_name': 'twinkly_test_device_name',
|
|
||||||
'host': '**REDACTED**',
|
'host': '**REDACTED**',
|
||||||
'id': '4c8fccf5-e08a-4173-92d5-49bf479252a2',
|
'id': '497dcba3-ecbf-4587-a2dd-5eb0665e6880',
|
||||||
'model': 'twinkly_test_device_model',
|
'model': 'TW2016',
|
||||||
'name': 'twinkly_test_device_name',
|
'name': 'Tree 1',
|
||||||
}),
|
}),
|
||||||
'disabled_by': None,
|
'disabled_by': None,
|
||||||
'discovery_keys': dict({
|
'discovery_keys': dict({
|
||||||
}),
|
}),
|
||||||
'domain': 'twinkly',
|
'domain': 'twinkly',
|
||||||
'entry_id': '4c8fccf5-e08a-4173-92d5-49bf479252a2',
|
'entry_id': '01JFMME2P6RA38V5AMPCJ2JYYV',
|
||||||
'minor_version': 2,
|
'minor_version': 2,
|
||||||
'options': dict({
|
'options': dict({
|
||||||
}),
|
}),
|
||||||
@ -39,9 +68,9 @@
|
|||||||
'pref_disable_polling': False,
|
'pref_disable_polling': False,
|
||||||
'source': 'user',
|
'source': 'user',
|
||||||
'title': 'Twinkly',
|
'title': 'Twinkly',
|
||||||
'unique_id': 'aa:bb:cc:dd:ee:ff',
|
'unique_id': '00:2d:13:3b:aa:bb',
|
||||||
'version': 1,
|
'version': 1,
|
||||||
}),
|
}),
|
||||||
'sw_version': '2.8.10',
|
'sw_version': '2.7.2',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
75
tests/components/twinkly/snapshots/test_light.ambr
Normal file
75
tests/components/twinkly/snapshots/test_light.ambr
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_entities[light.tree_1-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': dict({
|
||||||
|
'effect_list': list([
|
||||||
|
]),
|
||||||
|
'supported_color_modes': list([
|
||||||
|
<ColorMode.RGB: 'rgb'>,
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'light',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'light.tree_1',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': None,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': None,
|
||||||
|
'platform': 'twinkly',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': <LightEntityFeature: 4>,
|
||||||
|
'translation_key': 'light',
|
||||||
|
'unique_id': '00:2d:13:3b:aa:bb',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_entities[light.tree_1-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'brightness': 26,
|
||||||
|
'color_mode': <ColorMode.RGB: 'rgb'>,
|
||||||
|
'effect': None,
|
||||||
|
'effect_list': list([
|
||||||
|
]),
|
||||||
|
'friendly_name': 'Tree 1',
|
||||||
|
'hs_color': tuple(
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
),
|
||||||
|
'rgb_color': tuple(
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
),
|
||||||
|
'supported_color_modes': list([
|
||||||
|
<ColorMode.RGB: 'rgb'>,
|
||||||
|
]),
|
||||||
|
'supported_features': <LightEntityFeature: 4>,
|
||||||
|
'xy_color': tuple(
|
||||||
|
0.323,
|
||||||
|
0.329,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'light.tree_1',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
@ -1,196 +1,170 @@
|
|||||||
"""Tests for the config_flow of the twinly component."""
|
"""Tests for the config_flow of the twinly component."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant.components import dhcp
|
from homeassistant.components import dhcp
|
||||||
from homeassistant.components.twinkly.const import DOMAIN as TWINKLY_DOMAIN
|
from homeassistant.components.twinkly.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_USER
|
from homeassistant.config_entries import SOURCE_DHCP, SOURCE_USER
|
||||||
from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
from . import TEST_MODEL, TEST_NAME, ClientMock
|
from .const import TEST_MAC, TEST_MODEL, TEST_NAME
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_invalid_host(hass: HomeAssistant) -> None:
|
@pytest.mark.usefixtures("mock_twinkly_client", "mock_setup_entry")
|
||||||
"""Test the failure when invalid host provided."""
|
async def test_full_flow(hass: HomeAssistant) -> None:
|
||||||
client = ClientMock()
|
"""Test the full flow."""
|
||||||
client.is_offline = True
|
result = await hass.config_entries.flow.async_init(
|
||||||
with patch(
|
DOMAIN,
|
||||||
"homeassistant.components.twinkly.config_flow.Twinkly", return_value=client
|
context={"source": SOURCE_USER},
|
||||||
):
|
)
|
||||||
result = await hass.config_entries.flow.async_init(
|
assert result["type"] is FlowResultType.FORM
|
||||||
TWINKLY_DOMAIN, context={"source": config_entries.SOURCE_USER}
|
assert result["step_id"] == "user"
|
||||||
)
|
assert result["errors"] == {}
|
||||||
assert result["type"] is FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "user"
|
result = await hass.config_entries.flow.async_configure(
|
||||||
assert result["errors"] == {}
|
result["flow_id"],
|
||||||
result = await hass.config_entries.flow.async_configure(
|
{CONF_HOST: "192.168.0.123"},
|
||||||
result["flow_id"],
|
)
|
||||||
{CONF_HOST: "dummy"},
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
)
|
assert result["title"] == TEST_NAME
|
||||||
|
assert result["data"] == {
|
||||||
|
CONF_HOST: "192.168.0.123",
|
||||||
|
CONF_ID: "00000000-0000-0000-0000-000000000000",
|
||||||
|
CONF_NAME: TEST_NAME,
|
||||||
|
CONF_MODEL: TEST_MODEL,
|
||||||
|
}
|
||||||
|
assert result["result"].unique_id == TEST_MAC
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_setup_entry")
|
||||||
|
async def test_exceptions(hass: HomeAssistant, mock_twinkly_client: AsyncMock) -> None:
|
||||||
|
"""Test the failure when raising exceptions."""
|
||||||
|
mock_twinkly_client.get_details.side_effect = TimeoutError
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{CONF_HOST: "192.168.0.123"},
|
||||||
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
assert result["errors"] == {CONF_HOST: "cannot_connect"}
|
assert result["errors"] == {CONF_HOST: "cannot_connect"}
|
||||||
|
|
||||||
|
mock_twinkly_client.get_details.side_effect = None
|
||||||
|
|
||||||
async def test_success_flow(hass: HomeAssistant) -> None:
|
result = await hass.config_entries.flow.async_configure(
|
||||||
"""Test that an entity is created when the flow completes."""
|
result["flow_id"],
|
||||||
client = ClientMock()
|
{CONF_HOST: "192.168.0.123"},
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"homeassistant.components.twinkly.config_flow.Twinkly", return_value=client
|
|
||||||
),
|
|
||||||
patch("homeassistant.components.twinkly.async_setup_entry", return_value=True),
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
TWINKLY_DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
||||||
)
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "user"
|
|
||||||
assert result["errors"] == {}
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
{CONF_HOST: "dummy"},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
||||||
assert result["title"] == TEST_NAME
|
|
||||||
assert result["data"] == {
|
|
||||||
CONF_HOST: "dummy",
|
|
||||||
CONF_ID: client.id,
|
|
||||||
CONF_NAME: TEST_NAME,
|
|
||||||
CONF_MODEL: TEST_MODEL,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_dhcp_can_confirm(hass: HomeAssistant) -> None:
|
|
||||||
"""Test DHCP discovery flow can confirm right away."""
|
|
||||||
client = ClientMock()
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.twinkly.config_flow.Twinkly", return_value=client
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
TWINKLY_DOMAIN,
|
|
||||||
context={"source": config_entries.SOURCE_DHCP},
|
|
||||||
data=dhcp.DhcpServiceInfo(
|
|
||||||
hostname="Twinkly_XYZ",
|
|
||||||
ip="1.2.3.4",
|
|
||||||
macaddress="aabbccddeeff",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "discovery_confirm"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_dhcp_success(hass: HomeAssistant) -> None:
|
|
||||||
"""Test DHCP discovery flow success."""
|
|
||||||
client = ClientMock()
|
|
||||||
with (
|
|
||||||
patch(
|
|
||||||
"homeassistant.components.twinkly.config_flow.Twinkly", return_value=client
|
|
||||||
),
|
|
||||||
patch("homeassistant.components.twinkly.async_setup_entry", return_value=True),
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
TWINKLY_DOMAIN,
|
|
||||||
context={"source": config_entries.SOURCE_DHCP},
|
|
||||||
data=dhcp.DhcpServiceInfo(
|
|
||||||
hostname="Twinkly_XYZ",
|
|
||||||
ip="1.2.3.4",
|
|
||||||
macaddress="aabbccddeeff",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "discovery_confirm"
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
|
||||||
assert result["title"] == TEST_NAME
|
|
||||||
assert result["data"] == {
|
|
||||||
CONF_HOST: "1.2.3.4",
|
|
||||||
CONF_ID: client.id,
|
|
||||||
CONF_NAME: TEST_NAME,
|
|
||||||
CONF_MODEL: TEST_MODEL,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_dhcp_already_exists(hass: HomeAssistant) -> None:
|
|
||||||
"""Test DHCP discovery flow that fails to connect."""
|
|
||||||
client = ClientMock()
|
|
||||||
|
|
||||||
entry = MockConfigEntry(
|
|
||||||
domain=TWINKLY_DOMAIN,
|
|
||||||
data={
|
|
||||||
CONF_HOST: "1.2.3.4",
|
|
||||||
CONF_ID: client.id,
|
|
||||||
CONF_NAME: TEST_NAME,
|
|
||||||
CONF_MODEL: TEST_MODEL,
|
|
||||||
},
|
|
||||||
unique_id=client.id,
|
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.twinkly.config_flow.Twinkly", return_value=client
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
TWINKLY_DOMAIN,
|
|
||||||
context={"source": config_entries.SOURCE_DHCP},
|
|
||||||
data=dhcp.DhcpServiceInfo(
|
|
||||||
hostname="Twinkly_XYZ",
|
|
||||||
ip="1.2.3.4",
|
|
||||||
macaddress="aabbccddeeff",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_twinkly_client", "mock_setup_entry")
|
||||||
|
async def test_already_configured(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test the device is already configured."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {CONF_HOST: "192.168.0.123"}
|
||||||
|
)
|
||||||
assert result["type"] is FlowResultType.ABORT
|
assert result["type"] is FlowResultType.ABORT
|
||||||
assert result["reason"] == "already_configured"
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_twinkly_client", "mock_setup_entry")
|
||||||
|
async def test_dhcp_full_flow(hass: HomeAssistant) -> None:
|
||||||
|
"""Test DHCP discovery flow can confirm right away."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_DHCP},
|
||||||
|
data=dhcp.DhcpServiceInfo(
|
||||||
|
hostname="Twinkly_XYZ",
|
||||||
|
ip="1.2.3.4",
|
||||||
|
macaddress="002d133baabb",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "discovery_confirm"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == TEST_NAME
|
||||||
|
assert result["data"] == {
|
||||||
|
CONF_HOST: "1.2.3.4",
|
||||||
|
CONF_ID: "00000000-0000-0000-0000-000000000000",
|
||||||
|
CONF_NAME: TEST_NAME,
|
||||||
|
CONF_MODEL: TEST_MODEL,
|
||||||
|
}
|
||||||
|
assert result["result"].unique_id == TEST_MAC
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_twinkly_client")
|
||||||
|
async def test_dhcp_already_configured(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test DHCP discovery flow aborts if entry already setup."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_DHCP},
|
||||||
|
data=dhcp.DhcpServiceInfo(
|
||||||
|
hostname="Twinkly_XYZ",
|
||||||
|
ip="1.2.3.4",
|
||||||
|
macaddress="002d133baabb",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
assert mock_config_entry.data[CONF_HOST] == "1.2.3.4"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_twinkly_client", "mock_setup_entry")
|
||||||
async def test_user_flow_works_discovery(hass: HomeAssistant) -> None:
|
async def test_user_flow_works_discovery(hass: HomeAssistant) -> None:
|
||||||
"""Test user flow can continue after discovery happened."""
|
"""Test user flow can continue after discovery happened."""
|
||||||
client = ClientMock()
|
await hass.config_entries.flow.async_init(
|
||||||
with (
|
DOMAIN,
|
||||||
patch(
|
context={"source": SOURCE_DHCP},
|
||||||
"homeassistant.components.twinkly.config_flow.Twinkly", return_value=client
|
data=dhcp.DhcpServiceInfo(
|
||||||
|
hostname="Twinkly_XYZ",
|
||||||
|
ip="1.2.3.4",
|
||||||
|
macaddress="002d133baabb",
|
||||||
),
|
),
|
||||||
patch("homeassistant.components.twinkly.async_setup_entry", return_value=True),
|
)
|
||||||
):
|
result = await hass.config_entries.flow.async_init(
|
||||||
await hass.config_entries.flow.async_init(
|
DOMAIN,
|
||||||
TWINKLY_DOMAIN,
|
context={"source": SOURCE_USER},
|
||||||
context={"source": config_entries.SOURCE_DHCP},
|
)
|
||||||
data=dhcp.DhcpServiceInfo(
|
assert len(hass.config_entries.flow.async_progress(DOMAIN)) == 2
|
||||||
hostname="Twinkly_XYZ",
|
assert result["type"] is FlowResultType.FORM
|
||||||
ip="1.2.3.4",
|
assert result["step_id"] == "user"
|
||||||
macaddress="aabbccddeeff",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
TWINKLY_DOMAIN,
|
|
||||||
context={"source": SOURCE_USER},
|
|
||||||
)
|
|
||||||
assert len(hass.config_entries.flow.async_progress(TWINKLY_DOMAIN)) == 2
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
|
||||||
assert result["step_id"] == "user"
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{CONF_HOST: "10.0.0.131"},
|
{CONF_HOST: "10.0.0.131"},
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
|
||||||
# Verify the discovery flow was aborted
|
# Verify the discovery flow was aborted
|
||||||
assert not hass.config_entries.flow.async_progress(TWINKLY_DOMAIN)
|
assert not hass.config_entries.flow.async_progress(DOMAIN)
|
||||||
|
@ -1,32 +1,28 @@
|
|||||||
"""Tests for the diagnostics of the twinkly component."""
|
"""Tests for the diagnostics of the twinkly component."""
|
||||||
|
|
||||||
from collections.abc import Awaitable, Callable
|
import pytest
|
||||||
|
|
||||||
from syrupy import SnapshotAssertion
|
from syrupy import SnapshotAssertion
|
||||||
from syrupy.filters import props
|
from syrupy.filters import props
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from . import ClientMock
|
from . import setup_integration
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||||
from tests.typing import ClientSessionGenerator
|
from tests.typing import ClientSessionGenerator
|
||||||
|
|
||||||
type ComponentSetup = Callable[[], Awaitable[ClientMock]]
|
|
||||||
|
|
||||||
DOMAIN = "twinkly"
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_twinkly_client")
|
||||||
async def test_diagnostics(
|
async def test_diagnostics(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
hass_client: ClientSessionGenerator,
|
hass_client: ClientSessionGenerator,
|
||||||
setup_integration: ComponentSetup,
|
mock_config_entry: MockConfigEntry,
|
||||||
snapshot: SnapshotAssertion,
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test diagnostics."""
|
"""Test diagnostics."""
|
||||||
await setup_integration()
|
await setup_integration(hass, mock_config_entry)
|
||||||
entry = hass.config_entries.async_entries(DOMAIN)[0]
|
|
||||||
|
|
||||||
assert await get_diagnostics_for_config_entry(hass, hass_client, entry) == snapshot(
|
assert await get_diagnostics_for_config_entry(
|
||||||
exclude=props("created_at", "modified_at")
|
hass, hass_client, mock_config_entry
|
||||||
)
|
) == snapshot(exclude=props("created_at", "modified_at"))
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
"""Tests of the initialization of the twinkly integration."""
|
"""Tests of the initialization of the twinkly integration."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import AsyncMock
|
||||||
from uuid import uuid4
|
|
||||||
|
from aiohttp import ClientConnectionError
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||||
from homeassistant.components.twinkly.const import DOMAIN
|
from homeassistant.components.twinkly.const import DOMAIN
|
||||||
@ -10,82 +12,55 @@ from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
|
|||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
|
|
||||||
from . import TEST_HOST, TEST_MAC, TEST_MODEL, TEST_NAME_ORIGINAL, ClientMock
|
from . import setup_integration
|
||||||
|
from .const import TEST_MAC, TEST_MODEL
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_load_unload_entry(hass: HomeAssistant) -> None:
|
@pytest.mark.usefixtures("mock_twinkly_client")
|
||||||
"""Validate that setup entry also configure the client."""
|
async def test_load_unload_entry(
|
||||||
client = ClientMock()
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test the load/unload of the config entry."""
|
||||||
|
|
||||||
device_id = str(uuid4())
|
await setup_integration(hass, mock_config_entry)
|
||||||
config_entry = MockConfigEntry(
|
|
||||||
domain=DOMAIN,
|
|
||||||
data={
|
|
||||||
CONF_HOST: TEST_HOST,
|
|
||||||
CONF_ID: device_id,
|
|
||||||
CONF_NAME: TEST_NAME_ORIGINAL,
|
|
||||||
CONF_MODEL: TEST_MODEL,
|
|
||||||
},
|
|
||||||
entry_id=device_id,
|
|
||||||
unique_id=TEST_MAC,
|
|
||||||
minor_version=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
config_entry.add_to_hass(hass)
|
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
with patch("homeassistant.components.twinkly.Twinkly", return_value=client):
|
await hass.config_entries.async_unload(mock_config_entry.entry_id)
|
||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
|
||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
|
||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
|
||||||
|
|
||||||
|
|
||||||
async def test_config_entry_not_ready(hass: HomeAssistant) -> None:
|
async def test_config_entry_not_ready(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
"""Validate that config entry is retried."""
|
"""Validate that config entry is retried."""
|
||||||
client = ClientMock()
|
mock_twinkly_client.get_details.side_effect = ClientConnectionError
|
||||||
client.is_offline = True
|
|
||||||
|
|
||||||
config_entry = MockConfigEntry(
|
await setup_integration(hass, mock_config_entry)
|
||||||
domain=DOMAIN,
|
|
||||||
data={
|
|
||||||
CONF_HOST: TEST_HOST,
|
|
||||||
CONF_ID: id,
|
|
||||||
CONF_NAME: TEST_NAME_ORIGINAL,
|
|
||||||
CONF_MODEL: TEST_MODEL,
|
|
||||||
},
|
|
||||||
minor_version=2,
|
|
||||||
unique_id=TEST_MAC,
|
|
||||||
)
|
|
||||||
|
|
||||||
config_entry.add_to_hass(hass)
|
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
with patch("homeassistant.components.twinkly.Twinkly", return_value=client):
|
|
||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.SETUP_RETRY
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("mock_twinkly_client")
|
||||||
async def test_mac_migration(
|
async def test_mac_migration(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
device_registry: dr.DeviceRegistry,
|
device_registry: dr.DeviceRegistry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Validate that the unique_id is migrated to the MAC address."""
|
"""Validate that the unique_id is migrated to the MAC address."""
|
||||||
client = ClientMock()
|
|
||||||
|
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
minor_version=1,
|
minor_version=1,
|
||||||
unique_id="unique_id",
|
unique_id="unique_id",
|
||||||
data={
|
data={
|
||||||
CONF_HOST: TEST_HOST,
|
CONF_HOST: "192.168.0.123",
|
||||||
CONF_ID: id,
|
CONF_ID: id,
|
||||||
CONF_NAME: TEST_NAME_ORIGINAL,
|
CONF_NAME: "Tree 1",
|
||||||
CONF_MODEL: TEST_MODEL,
|
CONF_MODEL: TEST_MODEL,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -100,8 +75,7 @@ async def test_mac_migration(
|
|||||||
identifiers={(DOMAIN, config_entry.unique_id)},
|
identifiers={(DOMAIN, config_entry.unique_id)},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("homeassistant.components.twinkly.Twinkly", return_value=client):
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
|
|
||||||
assert config_entry.state is ConfigEntryState.LOADED
|
assert config_entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
@ -3,290 +3,287 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import patch
|
from typing import Any
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
import pytest
|
||||||
|
from syrupy import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.light import ATTR_BRIGHTNESS, LightEntityFeature
|
from homeassistant.components.light import (
|
||||||
from homeassistant.components.twinkly.const import DOMAIN as TWINKLY_DOMAIN
|
ATTR_BRIGHTNESS,
|
||||||
from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
|
ATTR_EFFECT,
|
||||||
|
ATTR_RGB_COLOR,
|
||||||
|
ATTR_RGBW_COLOR,
|
||||||
|
DOMAIN as LIGHT_DOMAIN,
|
||||||
|
LightEntityFeature,
|
||||||
|
)
|
||||||
|
from homeassistant.components.twinkly import DOMAIN
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
ATTR_SUPPORTED_FEATURES,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
STATE_OFF,
|
||||||
|
Platform,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
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_MAC, TEST_MODEL, TEST_NAME, TEST_NAME_ORIGINAL, ClientMock
|
from . import setup_integration
|
||||||
|
from .const import TEST_MAC
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||||
|
|
||||||
|
|
||||||
async def test_initial_state(hass: HomeAssistant) -> None:
|
@pytest.mark.usefixtures("mock_twinkly_client")
|
||||||
"""Validate that entity and device states are updated on startup."""
|
async def test_entities(
|
||||||
entity, device, _, _ = await _create_entries(hass)
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test the created entities."""
|
||||||
|
with patch("homeassistant.components.twinkly.PLATFORMS", [Platform.LIGHT]):
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_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:
|
async def test_turn_on_off(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
"""Test support of the light.turn_on service."""
|
"""Test support of the light.turn_on service."""
|
||||||
client = ClientMock()
|
mock_twinkly_client.is_on.return_value = False
|
||||||
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 setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
assert hass.states.get("light.tree_1").state == STATE_OFF
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light", "turn_on", service_data={"entity_id": entity.entity_id}, blocking=True
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
service_data={ATTR_ENTITY_ID: "light.tree_1"},
|
||||||
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
mock_twinkly_client.turn_on.assert_called_once_with()
|
||||||
|
|
||||||
assert state.state == "on"
|
|
||||||
assert state.attributes[ATTR_BRIGHTNESS] == 51
|
|
||||||
|
|
||||||
|
|
||||||
async def test_turn_on_with_brightness(hass: HomeAssistant) -> None:
|
async def test_turn_on_with_brightness(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
"""Test support of the light.turn_on service with a brightness parameter."""
|
"""Test support of the light.turn_on service with a brightness parameter."""
|
||||||
client = ClientMock()
|
mock_twinkly_client.is_on.return_value = False
|
||||||
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 setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light",
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
SERVICE_TURN_ON,
|
||||||
service_data={"entity_id": entity.entity_id, "brightness": 255},
|
service_data={ATTR_ENTITY_ID: "light.tree_1", ATTR_BRIGHTNESS: 255},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
mock_twinkly_client.set_brightness.assert_called_once_with(100)
|
||||||
|
mock_twinkly_client.turn_on.assert_called_once_with()
|
||||||
|
|
||||||
assert state.state == "on"
|
|
||||||
assert state.attributes[ATTR_BRIGHTNESS] == 255
|
async def test_brightness_to_zero(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test support of the light.turn_on service with a brightness parameter."""
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light",
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
SERVICE_TURN_ON,
|
||||||
service_data={"entity_id": entity.entity_id, "brightness": 1},
|
service_data={ATTR_ENTITY_ID: "light.tree_1", ATTR_BRIGHTNESS: 1},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
mock_twinkly_client.set_brightness.assert_not_called()
|
||||||
|
mock_twinkly_client.turn_off.assert_called_once_with()
|
||||||
assert state.state == "off"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_turn_on_with_color_rgbw(hass: HomeAssistant) -> None:
|
async def test_turn_on_with_color_rgbw(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
"""Test support of the light.turn_on service with a rgbw parameter."""
|
"""Test support of the light.turn_on service with a rgbw parameter."""
|
||||||
client = ClientMock()
|
mock_twinkly_client.is_on.return_value = False
|
||||||
client.state = False
|
mock_twinkly_client.get_details.return_value["led_profile"] = "RGBW"
|
||||||
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"
|
await setup_integration(hass, mock_config_entry)
|
||||||
assert (
|
assert (
|
||||||
LightEntityFeature.EFFECT
|
LightEntityFeature.EFFECT
|
||||||
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
& hass.states.get("light.tree_1").attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light",
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
SERVICE_TURN_ON,
|
||||||
service_data={"entity_id": entity.entity_id, "rgbw_color": (128, 64, 32, 0)},
|
service_data={
|
||||||
|
ATTR_ENTITY_ID: "light.tree_1",
|
||||||
|
ATTR_RGBW_COLOR: (128, 64, 32, 0),
|
||||||
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
mock_twinkly_client.interview.assert_called_once_with()
|
||||||
|
mock_twinkly_client.set_static_colour.assert_called_once_with((128, 64, 32))
|
||||||
assert state.state == "on"
|
mock_twinkly_client.set_mode.assert_called_once_with("color")
|
||||||
assert client.color == (128, 64, 32)
|
assert mock_twinkly_client.default_mode == "color"
|
||||||
assert client.default_mode == "color"
|
|
||||||
assert client.mode == "color"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_turn_on_with_color_rgb(hass: HomeAssistant) -> None:
|
async def test_turn_on_with_color_rgb(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
"""Test support of the light.turn_on service with a rgb parameter."""
|
"""Test support of the light.turn_on service with a rgb parameter."""
|
||||||
client = ClientMock()
|
mock_twinkly_client.is_on.return_value = False
|
||||||
client.state = False
|
mock_twinkly_client.get_details.return_value["led_profile"] = "RGB"
|
||||||
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"
|
await setup_integration(hass, mock_config_entry)
|
||||||
assert (
|
assert (
|
||||||
LightEntityFeature.EFFECT
|
LightEntityFeature.EFFECT
|
||||||
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
& hass.states.get("light.tree_1").attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light",
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
SERVICE_TURN_ON,
|
||||||
service_data={"entity_id": entity.entity_id, "rgb_color": (128, 64, 32)},
|
service_data={ATTR_ENTITY_ID: "light.tree_1", ATTR_RGB_COLOR: (128, 64, 32)},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
mock_twinkly_client.interview.assert_called_once_with()
|
||||||
|
mock_twinkly_client.set_static_colour.assert_called_once_with((128, 64, 32))
|
||||||
assert state.state == "on"
|
mock_twinkly_client.set_mode.assert_called_once_with("color")
|
||||||
assert client.color == (128, 64, 32)
|
assert mock_twinkly_client.default_mode == "color"
|
||||||
assert client.default_mode == "color"
|
|
||||||
assert client.mode == "color"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_turn_on_with_effect(hass: HomeAssistant) -> None:
|
async def test_turn_on_with_effect(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
|
) -> None:
|
||||||
"""Test support of the light.turn_on service with effects."""
|
"""Test support of the light.turn_on service with effects."""
|
||||||
client = ClientMock()
|
mock_twinkly_client.is_on.return_value = False
|
||||||
client.state = False
|
mock_twinkly_client.get_details.return_value["led_profile"] = "RGB"
|
||||||
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"
|
await setup_integration(hass, mock_config_entry)
|
||||||
assert not client.current_movie
|
|
||||||
assert (
|
assert (
|
||||||
LightEntityFeature.EFFECT
|
LightEntityFeature.EFFECT
|
||||||
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
& hass.states.get("light.tree_1").attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light",
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
SERVICE_TURN_ON,
|
||||||
service_data={"entity_id": entity.entity_id, "effect": "1 Rainbow"},
|
service_data={ATTR_ENTITY_ID: "light.tree_1", ATTR_EFFECT: "2 Rainbow"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
mock_twinkly_client.interview.assert_called_once_with()
|
||||||
|
mock_twinkly_client.set_current_movie.assert_called_once_with(2)
|
||||||
assert state.state == "on"
|
mock_twinkly_client.set_mode.assert_called_once_with("movie")
|
||||||
assert client.current_movie["id"] == 1
|
assert mock_twinkly_client.default_mode == "movie"
|
||||||
assert client.default_mode == "movie"
|
|
||||||
assert client.mode == "movie"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_turn_on_with_color_rgbw_and_missing_effect(hass: HomeAssistant) -> None:
|
@pytest.mark.parametrize(
|
||||||
|
("data"),
|
||||||
|
[
|
||||||
|
{ATTR_RGBW_COLOR: (128, 64, 32, 0)},
|
||||||
|
{ATTR_RGB_COLOR: (128, 64, 32)},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_turn_on_with_missing_effect(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
|
data: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
"""Test support of the light.turn_on service with rgbw color and missing effect support."""
|
"""Test support of the light.turn_on service with rgbw color and missing effect support."""
|
||||||
client = ClientMock()
|
mock_twinkly_client.is_on.return_value = False
|
||||||
client.state = False
|
mock_twinkly_client.get_firmware_version.return_value["version"] = "2.7.0"
|
||||||
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"
|
await setup_integration(hass, mock_config_entry)
|
||||||
assert (
|
assert (
|
||||||
not LightEntityFeature.EFFECT
|
LightEntityFeature.EFFECT
|
||||||
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
^ hass.states.get("light.tree_1").attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light",
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
SERVICE_TURN_ON,
|
||||||
service_data={"entity_id": entity.entity_id, "rgbw_color": (128, 64, 32, 0)},
|
service_data={ATTR_ENTITY_ID: "light.tree_1"} | data,
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
mock_twinkly_client.interview.assert_called_once_with()
|
||||||
|
mock_twinkly_client.set_cycle_colours.assert_called_once_with((128, 64, 32))
|
||||||
assert state.state == "on"
|
mock_twinkly_client.set_mode.assert_called_once_with("movie")
|
||||||
assert client.color == (0, 128, 64, 32)
|
assert mock_twinkly_client.default_mode == "movie"
|
||||||
assert client.mode == "movie"
|
mock_twinkly_client.set_current_movie.assert_not_called()
|
||||||
assert client.default_mode == "movie"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_turn_on_with_color_rgb_and_missing_effect(hass: HomeAssistant) -> None:
|
async def test_turn_on_with_color_rgbw_and_missing_effect(
|
||||||
"""Test support of the light.turn_on service with rgb color and missing effect support."""
|
hass: HomeAssistant,
|
||||||
client = ClientMock()
|
mock_config_entry: MockConfigEntry,
|
||||||
client.state = False
|
mock_twinkly_client: AsyncMock,
|
||||||
client.device_info["led_profile"] = "RGB"
|
) -> None:
|
||||||
client.brightness = {"mode": "enabled", "value": 255}
|
"""Test support of the light.turn_on service with missing effect support."""
|
||||||
client.version = "2.7.0"
|
mock_twinkly_client.is_on.return_value = False
|
||||||
entity, _, _, _ = await _create_entries(hass, client)
|
mock_twinkly_client.get_firmware_version.return_value["version"] = "2.7.0"
|
||||||
|
|
||||||
assert hass.states.get(entity.entity_id).state == "off"
|
await setup_integration(hass, mock_config_entry)
|
||||||
assert (
|
assert (
|
||||||
not LightEntityFeature.EFFECT
|
LightEntityFeature.EFFECT
|
||||||
& hass.states.get(entity.entity_id).attributes["supported_features"]
|
^ hass.states.get("light.tree_1").attributes[ATTR_SUPPORTED_FEATURES]
|
||||||
)
|
)
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light",
|
LIGHT_DOMAIN,
|
||||||
"turn_on",
|
SERVICE_TURN_ON,
|
||||||
service_data={"entity_id": entity.entity_id, "rgb_color": (128, 64, 32)},
|
service_data={ATTR_ENTITY_ID: "light.tree_1", ATTR_EFFECT: "2 Rainbow"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state = hass.states.get(entity.entity_id)
|
mock_twinkly_client.set_current_movie.assert_not_called()
|
||||||
|
|
||||||
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:
|
async def test_turn_off(
|
||||||
"""Test support of the light.turn_on service with effect set even if effects are not supported."""
|
hass: HomeAssistant,
|
||||||
client = ClientMock()
|
mock_config_entry: MockConfigEntry,
|
||||||
client.state = False
|
mock_twinkly_client: AsyncMock,
|
||||||
client.device_info["led_profile"] = "RGB"
|
) -> None:
|
||||||
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."""
|
"""Test support of the light.turn_off service."""
|
||||||
entity, _, _, _ = await _create_entries(hass)
|
|
||||||
|
|
||||||
assert hass.states.get(entity.entity_id).state == "on"
|
|
||||||
|
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"light", "turn_off", service_data={"entity_id": entity.entity_id}, blocking=True
|
LIGHT_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
service_data={ATTR_ENTITY_ID: "light.tree_1"},
|
||||||
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
mock_twinkly_client.turn_off.assert_called_once_with()
|
||||||
state = hass.states.get(entity.entity_id)
|
|
||||||
|
|
||||||
assert state.state == "off"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_name(
|
async def test_update_name(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
device_registry: dr.DeviceRegistry,
|
device_registry: dr.DeviceRegistry,
|
||||||
freezer: FrozenDateTimeFactory,
|
freezer: FrozenDateTimeFactory,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_twinkly_client: AsyncMock,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Validate device's name update behavior.
|
"""Validate device's name update behavior.
|
||||||
|
|
||||||
@ -294,56 +291,15 @@ async def test_update_name(
|
|||||||
then the name of the entity is updated and it's also persisted,
|
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.
|
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")
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
mock_twinkly_client.get_details.return_value["device_name"] = "new_device_name"
|
||||||
|
|
||||||
freezer.tick(timedelta(seconds=30))
|
freezer.tick(timedelta(seconds=30))
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
dev_entry = device_registry.async_get_device({(TWINKLY_DOMAIN, TEST_MAC)})
|
dev_entry = device_registry.async_get_device({(DOMAIN, TEST_MAC)})
|
||||||
|
|
||||||
assert dev_entry.name == "new_device_name"
|
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."""
|
|
||||||
|
|
||||||
_, _, _, entry = await _create_entries(hass)
|
|
||||||
|
|
||||||
assert await hass.config_entries.async_unload(entry.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,
|
|
||||||
},
|
|
||||||
unique_id=TEST_MAC,
|
|
||||||
minor_version=2,
|
|
||||||
)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_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, TEST_MAC)
|
|
||||||
entity_entry = entity_registry.async_get(entity_id)
|
|
||||||
device = device_registry.async_get_device(identifiers={(TWINKLY_DOMAIN, TEST_MAC)})
|
|
||||||
|
|
||||||
assert entity_entry is not None
|
|
||||||
assert device is not None
|
|
||||||
|
|
||||||
return entity_entry, device, client, config_entry
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user