Cleanups in Twinkly code (#64139)
* Cleanup Twinkly code * Add codeowner' * Change const namespull/64159/head
parent
e609f196bc
commit
efe34c8d13
|
@ -973,8 +973,8 @@ homeassistant/components/tuya/* @Tuya @zlinoliver @METISU @frenck
|
|||
tests/components/tuya/* @Tuya @zlinoliver @METISU @frenck
|
||||
homeassistant/components/twentemilieu/* @frenck
|
||||
tests/components/twentemilieu/* @frenck
|
||||
homeassistant/components/twinkly/* @dr1rrb
|
||||
tests/components/twinkly/* @dr1rrb
|
||||
homeassistant/components/twinkly/* @dr1rrb @Robbie1221
|
||||
tests/components/twinkly/* @dr1rrb @Robbie1221
|
||||
homeassistant/components/ubus/* @noltari
|
||||
homeassistant/components/unifi/* @Kane610
|
||||
tests/components/unifi/* @Kane610
|
||||
|
|
|
@ -11,7 +11,7 @@ from homeassistant.core import HomeAssistant
|
|||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import CONF_ENTRY_HOST, CONF_ENTRY_ID, DATA_CLIENT, DATA_DEVICE_INFO, DOMAIN
|
||||
from .const import CONF_HOST, DATA_CLIENT, DATA_DEVICE_INFO, DOMAIN
|
||||
|
||||
PLATFORMS = [Platform.LIGHT]
|
||||
|
||||
|
@ -22,10 +22,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
|
||||
# We setup the client here so if at some point we add any other entity for this device,
|
||||
# we will be able to properly share the connection.
|
||||
uuid = entry.data[CONF_ENTRY_ID]
|
||||
host = entry.data[CONF_ENTRY_HOST]
|
||||
host = entry.data[CONF_HOST]
|
||||
|
||||
hass.data[DOMAIN].setdefault(uuid, {})
|
||||
hass.data[DOMAIN].setdefault(entry.entry_id, {})
|
||||
|
||||
client = Twinkly(host, async_get_clientsession(hass))
|
||||
|
||||
|
@ -34,8 +33,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
except (asyncio.TimeoutError, ClientError) as exception:
|
||||
raise ConfigEntryNotReady from exception
|
||||
|
||||
hass.data[DOMAIN][uuid][DATA_CLIENT] = client
|
||||
hass.data[DOMAIN][uuid][DATA_DEVICE_INFO] = device_info
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] = client
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_DEVICE_INFO] = device_info
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
|
@ -45,9 +44,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Remove a twinkly entry."""
|
||||
|
||||
# For now light entries don't have unload method, so we don't have to async_forward_entry_unload
|
||||
# However we still have to cleanup the shared client!
|
||||
uuid = entry.data[CONF_ENTRY_ID]
|
||||
hass.data[DOMAIN].pop(uuid)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return True
|
||||
return unload_ok
|
||||
|
|
|
@ -14,16 +14,7 @@ from homeassistant.components import dhcp
|
|||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import (
|
||||
CONF_ENTRY_HOST,
|
||||
CONF_ENTRY_ID,
|
||||
CONF_ENTRY_MODEL,
|
||||
CONF_ENTRY_NAME,
|
||||
DEV_ID,
|
||||
DEV_MODEL,
|
||||
DEV_NAME,
|
||||
DOMAIN,
|
||||
)
|
||||
from .const import CONF_ID, CONF_MODEL, CONF_NAME, DEV_ID, DEV_MODEL, DEV_NAME, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -49,16 +40,14 @@ class TwinklyConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
device_info = await Twinkly(
|
||||
host, async_get_clientsession(self.hass)
|
||||
).get_details()
|
||||
|
||||
except (asyncio.TimeoutError, ClientError):
|
||||
errors[CONF_HOST] = "cannot_connect"
|
||||
else:
|
||||
await self.async_set_unique_id(device_info[DEV_ID])
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self._create_entry_from_device(device_info, host)
|
||||
|
||||
except (asyncio.TimeoutError, ClientError) as err:
|
||||
_LOGGER.info("Cannot reach Twinkly '%s' (client)", host, exc_info=err)
|
||||
errors[CONF_HOST] = "cannot_connect"
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=Schema(schema), errors=errors
|
||||
)
|
||||
|
@ -67,14 +56,12 @@ class TwinklyConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
self, discovery_info: dhcp.DhcpServiceInfo
|
||||
) -> data_entry_flow.FlowResult:
|
||||
"""Handle dhcp discovery for twinkly."""
|
||||
self._async_abort_entries_match({CONF_ENTRY_HOST: discovery_info.ip})
|
||||
self._async_abort_entries_match({CONF_HOST: discovery_info.ip})
|
||||
device_info = await Twinkly(
|
||||
discovery_info.ip, async_get_clientsession(self.hass)
|
||||
).get_details()
|
||||
await self.async_set_unique_id(device_info[DEV_ID])
|
||||
self._abort_if_unique_id_configured(
|
||||
updates={CONF_ENTRY_HOST: discovery_info.ip}
|
||||
)
|
||||
self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.ip})
|
||||
|
||||
self._discovered_device = (device_info, discovery_info.ip)
|
||||
return await self.async_step_discovery_confirm()
|
||||
|
@ -106,9 +93,9 @@ class TwinklyConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
return self.async_create_entry(
|
||||
title=device_info[DEV_NAME],
|
||||
data={
|
||||
CONF_ENTRY_HOST: host,
|
||||
CONF_ENTRY_ID: device_info[DEV_ID],
|
||||
CONF_ENTRY_NAME: device_info[DEV_NAME],
|
||||
CONF_ENTRY_MODEL: device_info[DEV_MODEL],
|
||||
CONF_HOST: host,
|
||||
CONF_ID: device_info[DEV_ID],
|
||||
CONF_NAME: device_info[DEV_NAME],
|
||||
CONF_MODEL: device_info[DEV_MODEL],
|
||||
},
|
||||
)
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
DOMAIN = "twinkly"
|
||||
|
||||
# Keys of the config entry
|
||||
CONF_ENTRY_ID = "id"
|
||||
CONF_ENTRY_HOST = "host"
|
||||
CONF_ENTRY_NAME = "name"
|
||||
CONF_ENTRY_MODEL = "model"
|
||||
CONF_ID = "id"
|
||||
CONF_HOST = "host"
|
||||
CONF_NAME = "name"
|
||||
CONF_MODEL = "model"
|
||||
|
||||
# Strongly named HA attributes keys
|
||||
ATTR_HOST = "host"
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import ClientError
|
||||
from ttls.client import Twinkly
|
||||
|
@ -22,11 +23,10 @@ from homeassistant.helpers.entity import DeviceInfo
|
|||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
ATTR_HOST,
|
||||
CONF_ENTRY_HOST,
|
||||
CONF_ENTRY_ID,
|
||||
CONF_ENTRY_MODEL,
|
||||
CONF_ENTRY_NAME,
|
||||
CONF_HOST,
|
||||
CONF_ID,
|
||||
CONF_MODEL,
|
||||
CONF_NAME,
|
||||
DATA_CLIENT,
|
||||
DATA_DEVICE_INFO,
|
||||
DEV_LED_PROFILE,
|
||||
|
@ -48,8 +48,8 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Setups an entity from a config entry (UI config flow)."""
|
||||
|
||||
client = hass.data[DOMAIN][config_entry.data[CONF_ENTRY_ID]][DATA_CLIENT]
|
||||
device_info = hass.data[DOMAIN][config_entry.data[CONF_ENTRY_ID]][DATA_DEVICE_INFO]
|
||||
client = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT]
|
||||
device_info = hass.data[DOMAIN][config_entry.entry_id][DATA_DEVICE_INFO]
|
||||
|
||||
entity = TwinklyLight(config_entry, client, device_info)
|
||||
|
||||
|
@ -66,7 +66,7 @@ class TwinklyLight(LightEntity):
|
|||
device_info,
|
||||
) -> None:
|
||||
"""Initialize a TwinklyLight entity."""
|
||||
self._id = conf.data[CONF_ENTRY_ID]
|
||||
self._id = conf.data[CONF_ID]
|
||||
self._conf = conf
|
||||
|
||||
if device_info.get(DEV_LED_PROFILE) == DEV_PROFILE_RGBW:
|
||||
|
@ -84,20 +84,15 @@ class TwinklyLight(LightEntity):
|
|||
# Those are saved in the config entry in order to have meaningful values even
|
||||
# if the device is currently offline.
|
||||
# They are expected to be updated using the device_info.
|
||||
self.__name = conf.data[CONF_ENTRY_NAME]
|
||||
self.__model = conf.data[CONF_ENTRY_MODEL]
|
||||
self._name = conf.data[CONF_NAME]
|
||||
self._model = conf.data[CONF_MODEL]
|
||||
|
||||
self._client = client
|
||||
|
||||
# Set default state before any update
|
||||
self._is_on = False
|
||||
self._is_available = False
|
||||
self._attributes = {ATTR_HOST: self._client.host}
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Get a boolean which indicates if this entity should be polled."""
|
||||
return True
|
||||
self._attributes: dict[Any, Any] = {}
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
|
@ -112,12 +107,12 @@ class TwinklyLight(LightEntity):
|
|||
@property
|
||||
def name(self) -> str:
|
||||
"""Name of the device."""
|
||||
return self.__name if self.__name else "Twinkly light"
|
||||
return self._name if self._name else "Twinkly light"
|
||||
|
||||
@property
|
||||
def model(self) -> str:
|
||||
"""Name of the device."""
|
||||
return self.__model
|
||||
return self._model
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
|
@ -127,15 +122,11 @@ class TwinklyLight(LightEntity):
|
|||
@property
|
||||
def device_info(self) -> DeviceInfo | None:
|
||||
"""Get device specific attributes."""
|
||||
return (
|
||||
DeviceInfo(
|
||||
identifiers={(DOMAIN, self._id)},
|
||||
manufacturer="LEDWORKS",
|
||||
model=self.model,
|
||||
name=self.name,
|
||||
)
|
||||
if self._id
|
||||
else None # device_info is available only for entities configured from the UI
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self._id)},
|
||||
manufacturer="LEDWORKS",
|
||||
model=self.model,
|
||||
name=self.name,
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -149,10 +140,6 @@ class TwinklyLight(LightEntity):
|
|||
|
||||
attributes = self._attributes
|
||||
|
||||
# Make sure to update any normalized property
|
||||
attributes[ATTR_HOST] = self._client.host
|
||||
attributes[ATTR_BRIGHTNESS] = self._attr_brightness
|
||||
|
||||
return attributes
|
||||
|
||||
async def async_turn_on(self, **kwargs) -> None:
|
||||
|
@ -204,7 +191,7 @@ class TwinklyLight(LightEntity):
|
|||
|
||||
async def async_update(self) -> None:
|
||||
"""Asynchronously updates the device properties."""
|
||||
_LOGGER.info("Updating '%s'", self._client.host)
|
||||
_LOGGER.debug("Updating '%s'", self._client.host)
|
||||
|
||||
try:
|
||||
self._is_on = await self._client.is_on()
|
||||
|
@ -224,25 +211,24 @@ class TwinklyLight(LightEntity):
|
|||
DEV_NAME in device_info
|
||||
and DEV_MODEL in device_info
|
||||
and (
|
||||
device_info[DEV_NAME] != self.__name
|
||||
or device_info[DEV_MODEL] != self.__model
|
||||
device_info[DEV_NAME] != self._name
|
||||
or device_info[DEV_MODEL] != self._model
|
||||
)
|
||||
):
|
||||
self.__name = device_info[DEV_NAME]
|
||||
self.__model = device_info[DEV_MODEL]
|
||||
self._name = device_info[DEV_NAME]
|
||||
self._model = device_info[DEV_MODEL]
|
||||
|
||||
if self._conf is not None:
|
||||
# If the name has changed, persist it in conf entry,
|
||||
# so we will be able to restore this new name if hass is started while the LED string is offline.
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self._conf,
|
||||
data={
|
||||
CONF_ENTRY_HOST: self._client.host, # this cannot change
|
||||
CONF_ENTRY_ID: self._id, # this cannot change
|
||||
CONF_ENTRY_NAME: self.__name,
|
||||
CONF_ENTRY_MODEL: self.__model,
|
||||
},
|
||||
)
|
||||
# If the name has changed, persist it in conf entry,
|
||||
# so we will be able to restore this new name if hass is started while the LED string is offline.
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self._conf,
|
||||
data={
|
||||
CONF_HOST: self._client.host, # this cannot change
|
||||
CONF_ID: self._id, # this cannot change
|
||||
CONF_NAME: self._name,
|
||||
CONF_MODEL: self._model,
|
||||
},
|
||||
)
|
||||
|
||||
for key, value in device_info.items():
|
||||
if key not in HIDDEN_DEV_VALUES:
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
"name": "Twinkly",
|
||||
"documentation": "https://www.home-assistant.io/integrations/twinkly",
|
||||
"requirements": ["ttls==1.4.2"],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@dr1rrb"],
|
||||
"codeowners": ["@dr1rrb", "@Robbie1221"],
|
||||
"config_flow": true,
|
||||
"dhcp": [{ "hostname": "twinkly_*" }],
|
||||
"iot_class": "local_polling"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"title": "Twinkly",
|
||||
"description": "Set up your Twinkly led string",
|
||||
"data": {
|
||||
"host": "Host (or IP address) of your twinkly device"
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
}
|
||||
},
|
||||
"discovery_confirm": {
|
||||
|
|
|
@ -4,10 +4,10 @@ from unittest.mock import patch
|
|||
from homeassistant import config_entries
|
||||
from homeassistant.components import dhcp
|
||||
from homeassistant.components.twinkly.const import (
|
||||
CONF_ENTRY_HOST,
|
||||
CONF_ENTRY_ID,
|
||||
CONF_ENTRY_MODEL,
|
||||
CONF_ENTRY_NAME,
|
||||
CONF_HOST,
|
||||
CONF_ID,
|
||||
CONF_MODEL,
|
||||
CONF_NAME,
|
||||
DOMAIN as TWINKLY_DOMAIN,
|
||||
)
|
||||
|
||||
|
@ -31,12 +31,12 @@ async def test_invalid_host(hass):
|
|||
assert result["errors"] == {}
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_ENTRY_HOST: "dummy"},
|
||||
{CONF_HOST: "dummy"},
|
||||
)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {CONF_ENTRY_HOST: "cannot_connect"}
|
||||
assert result["errors"] == {CONF_HOST: "cannot_connect"}
|
||||
|
||||
|
||||
async def test_success_flow(hass):
|
||||
|
@ -55,16 +55,16 @@ async def test_success_flow(hass):
|
|||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_ENTRY_HOST: "dummy"},
|
||||
{CONF_HOST: "dummy"},
|
||||
)
|
||||
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == client.id
|
||||
assert result["data"] == {
|
||||
CONF_ENTRY_HOST: "dummy",
|
||||
CONF_ENTRY_ID: client.id,
|
||||
CONF_ENTRY_NAME: client.id,
|
||||
CONF_ENTRY_MODEL: TEST_MODEL,
|
||||
CONF_HOST: "dummy",
|
||||
CONF_ID: client.id,
|
||||
CONF_NAME: client.id,
|
||||
CONF_MODEL: TEST_MODEL,
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,10 +114,10 @@ async def test_dhcp_success(hass):
|
|||
assert result["type"] == "create_entry"
|
||||
assert result["title"] == client.id
|
||||
assert result["data"] == {
|
||||
CONF_ENTRY_HOST: "1.2.3.4",
|
||||
CONF_ENTRY_ID: client.id,
|
||||
CONF_ENTRY_NAME: client.id,
|
||||
CONF_ENTRY_MODEL: TEST_MODEL,
|
||||
CONF_HOST: "1.2.3.4",
|
||||
CONF_ID: client.id,
|
||||
CONF_NAME: client.id,
|
||||
CONF_MODEL: TEST_MODEL,
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,10 +128,10 @@ async def test_dhcp_already_exists(hass):
|
|||
entry = MockConfigEntry(
|
||||
domain=TWINKLY_DOMAIN,
|
||||
data={
|
||||
CONF_ENTRY_HOST: "1.2.3.4",
|
||||
CONF_ENTRY_ID: client.id,
|
||||
CONF_ENTRY_NAME: client.id,
|
||||
CONF_ENTRY_MODEL: TEST_MODEL,
|
||||
CONF_HOST: "1.2.3.4",
|
||||
CONF_ID: client.id,
|
||||
CONF_NAME: client.id,
|
||||
CONF_MODEL: TEST_MODEL,
|
||||
},
|
||||
unique_id=client.id,
|
||||
)
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
from unittest.mock import patch
|
||||
from uuid import uuid4
|
||||
|
||||
from homeassistant.components.twinkly import async_setup_entry, async_unload_entry
|
||||
from homeassistant.components.twinkly.const import (
|
||||
CONF_ENTRY_HOST,
|
||||
CONF_ENTRY_ID,
|
||||
CONF_ENTRY_MODEL,
|
||||
CONF_ENTRY_NAME,
|
||||
CONF_HOST,
|
||||
CONF_ID,
|
||||
CONF_MODEL,
|
||||
CONF_NAME,
|
||||
DOMAIN as TWINKLY_DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
|
@ -23,7 +22,7 @@ from tests.components.twinkly import (
|
|||
)
|
||||
|
||||
|
||||
async def test_setup_entry(hass: HomeAssistant):
|
||||
async def test_load_unload_entry(hass: HomeAssistant):
|
||||
"""Validate that setup entry also configure the client."""
|
||||
client = ClientMock()
|
||||
|
||||
|
@ -31,47 +30,24 @@ async def test_setup_entry(hass: HomeAssistant):
|
|||
config_entry = MockConfigEntry(
|
||||
domain=TWINKLY_DOMAIN,
|
||||
data={
|
||||
CONF_ENTRY_HOST: TEST_HOST,
|
||||
CONF_ENTRY_ID: id,
|
||||
CONF_ENTRY_NAME: TEST_NAME_ORIGINAL,
|
||||
CONF_ENTRY_MODEL: TEST_MODEL,
|
||||
CONF_HOST: TEST_HOST,
|
||||
CONF_ID: id,
|
||||
CONF_NAME: TEST_NAME_ORIGINAL,
|
||||
CONF_MODEL: TEST_MODEL,
|
||||
},
|
||||
entry_id=id,
|
||||
)
|
||||
|
||||
def setup_mock(_, __):
|
||||
return True
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.config_entries.ConfigEntries.async_forward_entry_setup",
|
||||
side_effect=setup_mock,
|
||||
), patch("homeassistant.components.twinkly.Twinkly", return_value=client):
|
||||
await async_setup_entry(hass, config_entry)
|
||||
with patch("homeassistant.components.twinkly.Twinkly", return_value=client):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
assert hass.data[TWINKLY_DOMAIN][id] is not None
|
||||
assert config_entry.state == ConfigEntryState.LOADED
|
||||
|
||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
|
||||
async def test_unload_entry(hass: HomeAssistant):
|
||||
"""Validate that unload entry also clear the client."""
|
||||
|
||||
id = str(uuid4())
|
||||
config_entry = MockConfigEntry(
|
||||
domain=TWINKLY_DOMAIN,
|
||||
data={
|
||||
CONF_ENTRY_HOST: TEST_HOST,
|
||||
CONF_ENTRY_ID: id,
|
||||
CONF_ENTRY_NAME: TEST_NAME_ORIGINAL,
|
||||
CONF_ENTRY_MODEL: TEST_MODEL,
|
||||
},
|
||||
entry_id=id,
|
||||
)
|
||||
|
||||
# Put random content at the location where the client should have been placed by setup
|
||||
hass.data.setdefault(TWINKLY_DOMAIN, {})[id] = config_entry
|
||||
|
||||
await async_unload_entry(hass, config_entry)
|
||||
|
||||
assert hass.data[TWINKLY_DOMAIN].get(id) is None
|
||||
assert config_entry.state == ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
async def test_config_entry_not_ready(hass: HomeAssistant):
|
||||
|
@ -82,10 +58,10 @@ async def test_config_entry_not_ready(hass: HomeAssistant):
|
|||
config_entry = MockConfigEntry(
|
||||
domain=TWINKLY_DOMAIN,
|
||||
data={
|
||||
CONF_ENTRY_HOST: TEST_HOST,
|
||||
CONF_ENTRY_ID: id,
|
||||
CONF_ENTRY_NAME: TEST_NAME_ORIGINAL,
|
||||
CONF_ENTRY_MODEL: TEST_MODEL,
|
||||
CONF_HOST: TEST_HOST,
|
||||
CONF_ID: id,
|
||||
CONF_NAME: TEST_NAME_ORIGINAL,
|
||||
CONF_MODEL: TEST_MODEL,
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
@ -3,11 +3,12 @@ from __future__ import annotations
|
|||
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS
|
||||
from homeassistant.components.twinkly.const import (
|
||||
CONF_ENTRY_HOST,
|
||||
CONF_ENTRY_ID,
|
||||
CONF_ENTRY_MODEL,
|
||||
CONF_ENTRY_NAME,
|
||||
CONF_HOST,
|
||||
CONF_ID,
|
||||
CONF_MODEL,
|
||||
CONF_NAME,
|
||||
DOMAIN as TWINKLY_DOMAIN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
@ -16,25 +17,19 @@ from homeassistant.helpers.device_registry import DeviceEntry
|
|||
from homeassistant.helpers.entity_registry import RegistryEntry
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.components.twinkly import (
|
||||
TEST_HOST,
|
||||
TEST_MODEL,
|
||||
TEST_NAME_ORIGINAL,
|
||||
ClientMock,
|
||||
)
|
||||
from tests.components.twinkly import TEST_MODEL, TEST_NAME_ORIGINAL, ClientMock
|
||||
|
||||
|
||||
async def test_initial_state(hass: HomeAssistant):
|
||||
"""Validate that entity and device states are updated on startup."""
|
||||
entity, device, _ = await _create_entries(hass)
|
||||
entity, device, _, _ = await _create_entries(hass)
|
||||
|
||||
state = hass.states.get(entity.entity_id)
|
||||
|
||||
# Basic state properties
|
||||
assert state.name == entity.unique_id
|
||||
assert state.state == "on"
|
||||
assert state.attributes["host"] == TEST_HOST
|
||||
assert state.attributes["brightness"] == 26
|
||||
assert state.attributes[ATTR_BRIGHTNESS] == 26
|
||||
assert state.attributes["friendly_name"] == entity.unique_id
|
||||
assert state.attributes["icon"] == "mdi:string-lights"
|
||||
|
||||
|
@ -54,7 +49,7 @@ async def test_turn_on_off(hass: HomeAssistant):
|
|||
client = ClientMock()
|
||||
client.state = False
|
||||
client.brightness = {"mode": "enabled", "value": 20}
|
||||
entity, _, _ = await _create_entries(hass, client)
|
||||
entity, _, _, _ = await _create_entries(hass, client)
|
||||
|
||||
assert hass.states.get(entity.entity_id).state == "off"
|
||||
|
||||
|
@ -66,7 +61,7 @@ async def test_turn_on_off(hass: HomeAssistant):
|
|||
state = hass.states.get(entity.entity_id)
|
||||
|
||||
assert state.state == "on"
|
||||
assert state.attributes["brightness"] == 51
|
||||
assert state.attributes[ATTR_BRIGHTNESS] == 51
|
||||
|
||||
|
||||
async def test_turn_on_with_brightness(hass: HomeAssistant):
|
||||
|
@ -74,7 +69,7 @@ async def test_turn_on_with_brightness(hass: HomeAssistant):
|
|||
client = ClientMock()
|
||||
client.state = False
|
||||
client.brightness = {"mode": "enabled", "value": 20}
|
||||
entity, _, _ = await _create_entries(hass, client)
|
||||
entity, _, _, _ = await _create_entries(hass, client)
|
||||
|
||||
assert hass.states.get(entity.entity_id).state == "off"
|
||||
|
||||
|
@ -88,7 +83,7 @@ async def test_turn_on_with_brightness(hass: HomeAssistant):
|
|||
state = hass.states.get(entity.entity_id)
|
||||
|
||||
assert state.state == "on"
|
||||
assert state.attributes["brightness"] == 255
|
||||
assert state.attributes[ATTR_BRIGHTNESS] == 255
|
||||
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
|
@ -100,7 +95,6 @@ async def test_turn_on_with_brightness(hass: HomeAssistant):
|
|||
state = hass.states.get(entity.entity_id)
|
||||
|
||||
assert state.state == "off"
|
||||
assert state.attributes["brightness"] == 0
|
||||
|
||||
|
||||
async def test_turn_on_with_color_rgbw(hass: HomeAssistant):
|
||||
|
@ -109,7 +103,7 @@ async def test_turn_on_with_color_rgbw(hass: HomeAssistant):
|
|||
client.state = False
|
||||
client.device_info["led_profile"] = "RGBW"
|
||||
client.brightness = {"mode": "enabled", "value": 255}
|
||||
entity, _, _ = await _create_entries(hass, client)
|
||||
entity, _, _, _ = await _create_entries(hass, client)
|
||||
|
||||
assert hass.states.get(entity.entity_id).state == "off"
|
||||
|
||||
|
@ -132,7 +126,7 @@ async def test_turn_on_with_color_rgb(hass: HomeAssistant):
|
|||
client.state = False
|
||||
client.device_info["led_profile"] = "RGB"
|
||||
client.brightness = {"mode": "enabled", "value": 255}
|
||||
entity, _, _ = await _create_entries(hass, client)
|
||||
entity, _, _, _ = await _create_entries(hass, client)
|
||||
|
||||
assert hass.states.get(entity.entity_id).state == "off"
|
||||
|
||||
|
@ -151,7 +145,7 @@ async def test_turn_on_with_color_rgb(hass: HomeAssistant):
|
|||
|
||||
async def test_turn_off(hass: HomeAssistant):
|
||||
"""Test support of the light.turn_off service."""
|
||||
entity, _, _ = await _create_entries(hass)
|
||||
entity, _, _, _ = await _create_entries(hass)
|
||||
|
||||
assert hass.states.get(entity.entity_id).state == "on"
|
||||
|
||||
|
@ -163,7 +157,6 @@ async def test_turn_off(hass: HomeAssistant):
|
|||
state = hass.states.get(entity.entity_id)
|
||||
|
||||
assert state.state == "off"
|
||||
assert state.attributes["brightness"] == 0
|
||||
|
||||
|
||||
async def test_update_name(hass: HomeAssistant):
|
||||
|
@ -174,15 +167,7 @@ async def test_update_name(hass: HomeAssistant):
|
|||
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 = await _create_entries(hass)
|
||||
|
||||
updated_config_entry = None
|
||||
|
||||
async def on_update(ha, co):
|
||||
nonlocal updated_config_entry
|
||||
updated_config_entry = co
|
||||
|
||||
hass.config_entries.async_get_entry(entity.unique_id).add_update_listener(on_update)
|
||||
entity, _, client, config_entry = await _create_entries(hass)
|
||||
|
||||
client.change_name("new_device_name")
|
||||
await hass.services.async_call(
|
||||
|
@ -192,15 +177,14 @@ async def test_update_name(hass: HomeAssistant):
|
|||
|
||||
state = hass.states.get(entity.entity_id)
|
||||
|
||||
assert updated_config_entry is not None
|
||||
assert updated_config_entry.data[CONF_ENTRY_NAME] == "new_device_name"
|
||||
assert config_entry.data[CONF_NAME] == "new_device_name"
|
||||
assert state.attributes["friendly_name"] == "new_device_name"
|
||||
|
||||
|
||||
async def test_unload(hass: HomeAssistant):
|
||||
"""Validate that entities can be unloaded from the UI."""
|
||||
|
||||
_, _, client = await _create_entries(hass)
|
||||
_, _, client, _ = await _create_entries(hass)
|
||||
entry_id = client.id
|
||||
|
||||
assert await hass.config_entries.async_unload(entry_id)
|
||||
|
@ -215,10 +199,10 @@ async def _create_entries(
|
|||
config_entry = MockConfigEntry(
|
||||
domain=TWINKLY_DOMAIN,
|
||||
data={
|
||||
CONF_ENTRY_HOST: client,
|
||||
CONF_ENTRY_ID: client.id,
|
||||
CONF_ENTRY_NAME: TEST_NAME_ORIGINAL,
|
||||
CONF_ENTRY_MODEL: TEST_MODEL,
|
||||
CONF_HOST: client,
|
||||
CONF_ID: client.id,
|
||||
CONF_NAME: TEST_NAME_ORIGINAL,
|
||||
CONF_MODEL: TEST_MODEL,
|
||||
},
|
||||
entry_id=client.id,
|
||||
)
|
||||
|
@ -230,10 +214,10 @@ async def _create_entries(
|
|||
entity_registry = er.async_get(hass)
|
||||
|
||||
entity_id = entity_registry.async_get_entity_id("light", TWINKLY_DOMAIN, client.id)
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
entity_entry = entity_registry.async_get(entity_id)
|
||||
device = device_registry.async_get_device({(TWINKLY_DOMAIN, client.id)})
|
||||
|
||||
assert entity is not None
|
||||
assert entity_entry is not None
|
||||
assert device is not None
|
||||
|
||||
return entity, device, client
|
||||
return entity_entry, device, client, config_entry
|
Loading…
Reference in New Issue