mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Fix yeelight connection issue (#40251)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
08342a1e05
commit
f23fcfcd9b
@ -7,7 +7,7 @@ from typing import Optional
|
|||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from yeelight import Bulb, BulbException, discover_bulbs
|
from yeelight import Bulb, BulbException, discover_bulbs
|
||||||
|
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry, ConfigEntryNotReady
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICES,
|
CONF_DEVICES,
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
@ -180,8 +180,8 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
|||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Yeelight from a config entry."""
|
"""Set up Yeelight from a config entry."""
|
||||||
|
|
||||||
async def _initialize(host: str) -> None:
|
async def _initialize(host: str, capabilities: Optional[dict] = None) -> None:
|
||||||
device = await _async_setup_device(hass, host, entry.options)
|
device = await _async_setup_device(hass, host, entry, capabilities)
|
||||||
hass.data[DOMAIN][DATA_CONFIG_ENTRIES][entry.entry_id][DATA_DEVICE] = device
|
hass.data[DOMAIN][DATA_CONFIG_ENTRIES][entry.entry_id][DATA_DEVICE] = device
|
||||||
for component in PLATFORMS:
|
for component in PLATFORMS:
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
@ -252,20 +252,33 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
async def _async_setup_device(
|
async def _async_setup_device(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
host: str,
|
host: str,
|
||||||
config: dict,
|
entry: ConfigEntry,
|
||||||
|
capabilities: Optional[dict],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
# Get model from config and capabilities
|
||||||
|
model = entry.options.get(CONF_MODEL)
|
||||||
|
if not model and capabilities is not None:
|
||||||
|
model = capabilities.get("model")
|
||||||
|
|
||||||
# Set up device
|
# Set up device
|
||||||
bulb = Bulb(host, model=config.get(CONF_MODEL) or None)
|
bulb = Bulb(host, model=model or None)
|
||||||
|
if capabilities is None:
|
||||||
capabilities = await hass.async_add_executor_job(bulb.get_capabilities)
|
capabilities = await hass.async_add_executor_job(bulb.get_capabilities)
|
||||||
if capabilities is None: # timeout
|
|
||||||
_LOGGER.error("Failed to get capabilities from %s", host)
|
device = YeelightDevice(hass, host, entry.options, bulb, capabilities)
|
||||||
raise ConfigEntryNotReady
|
|
||||||
device = YeelightDevice(hass, host, config, bulb)
|
|
||||||
await hass.async_add_executor_job(device.update)
|
await hass.async_add_executor_job(device.update)
|
||||||
await device.async_setup()
|
await device.async_setup()
|
||||||
return device
|
return device
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_unique_name(capabilities: dict) -> str:
|
||||||
|
"""Generate name from capabilities."""
|
||||||
|
model = capabilities["model"]
|
||||||
|
unique_id = capabilities["id"]
|
||||||
|
return f"yeelight_{model}_{unique_id}"
|
||||||
|
|
||||||
|
|
||||||
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry):
|
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
"""Handle options update."""
|
"""Handle options update."""
|
||||||
await hass.config_entries.async_reload(entry.entry_id)
|
await hass.config_entries.async_reload(entry.entry_id)
|
||||||
@ -332,7 +345,7 @@ class YeelightScanner:
|
|||||||
"""Register callback function."""
|
"""Register callback function."""
|
||||||
host = self._seen.get(unique_id)
|
host = self._seen.get(unique_id)
|
||||||
if host is not None:
|
if host is not None:
|
||||||
self._hass.async_add_job(callback_func(host))
|
self._hass.async_create_task(callback_func(host))
|
||||||
else:
|
else:
|
||||||
self._callbacks[unique_id] = callback_func
|
self._callbacks[unique_id] = callback_func
|
||||||
if len(self._callbacks) == 1:
|
if len(self._callbacks) == 1:
|
||||||
@ -351,18 +364,25 @@ class YeelightScanner:
|
|||||||
class YeelightDevice:
|
class YeelightDevice:
|
||||||
"""Represents single Yeelight device."""
|
"""Represents single Yeelight device."""
|
||||||
|
|
||||||
def __init__(self, hass, host, config, bulb):
|
def __init__(self, hass, host, config, bulb, capabilities):
|
||||||
"""Initialize device."""
|
"""Initialize device."""
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._config = config
|
self._config = config
|
||||||
self._host = host
|
self._host = host
|
||||||
unique_id = bulb.capabilities.get("id")
|
|
||||||
self._name = config.get(CONF_NAME) or f"yeelight_{bulb.model}_{unique_id}"
|
|
||||||
self._bulb_device = bulb
|
self._bulb_device = bulb
|
||||||
|
self._capabilities = capabilities or {}
|
||||||
self._device_type = None
|
self._device_type = None
|
||||||
self._available = False
|
self._available = False
|
||||||
self._remove_time_tracker = None
|
self._remove_time_tracker = None
|
||||||
|
|
||||||
|
self._name = host # Default name is host
|
||||||
|
if capabilities:
|
||||||
|
# Generate name from model and id when capabilities is available
|
||||||
|
self._name = _async_unique_name(capabilities)
|
||||||
|
if config.get(CONF_NAME):
|
||||||
|
# Override default name when name is set in config
|
||||||
|
self._name = config[CONF_NAME]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bulb(self):
|
def bulb(self):
|
||||||
"""Return bulb device."""
|
"""Return bulb device."""
|
||||||
@ -396,7 +416,7 @@ class YeelightDevice:
|
|||||||
@property
|
@property
|
||||||
def fw_version(self):
|
def fw_version(self):
|
||||||
"""Return the firmware version."""
|
"""Return the firmware version."""
|
||||||
return self._bulb_device.capabilities.get("fw_ver")
|
return self._capabilities.get("fw_ver")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_nightlight_supported(self) -> bool:
|
def is_nightlight_supported(self) -> bool:
|
||||||
@ -449,11 +469,6 @@ class YeelightDevice:
|
|||||||
|
|
||||||
return self._device_type
|
return self._device_type
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> Optional[str]:
|
|
||||||
"""Return a unique ID."""
|
|
||||||
return self.bulb.capabilities.get("id")
|
|
||||||
|
|
||||||
def turn_on(self, duration=DEFAULT_TRANSITION, light_type=None, power_mode=None):
|
def turn_on(self, duration=DEFAULT_TRANSITION, light_type=None, power_mode=None):
|
||||||
"""Turn on device."""
|
"""Turn on device."""
|
||||||
try:
|
try:
|
||||||
@ -532,15 +547,24 @@ class YeelightDevice:
|
|||||||
class YeelightEntity(Entity):
|
class YeelightEntity(Entity):
|
||||||
"""Represents single Yeelight entity."""
|
"""Represents single Yeelight entity."""
|
||||||
|
|
||||||
def __init__(self, device: YeelightDevice):
|
def __init__(self, device: YeelightDevice, entry: ConfigEntry):
|
||||||
"""Initialize the entity."""
|
"""Initialize the entity."""
|
||||||
self._device = device
|
self._device = device
|
||||||
|
self._unique_id = entry.entry_id
|
||||||
|
if entry.unique_id is not None:
|
||||||
|
# Use entry unique id (device id) whenever possible
|
||||||
|
self._unique_id = entry.unique_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self) -> str:
|
||||||
|
"""Return the unique ID."""
|
||||||
|
return self._unique_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_info(self) -> dict:
|
def device_info(self) -> dict:
|
||||||
"""Return the device info."""
|
"""Return the device info."""
|
||||||
return {
|
return {
|
||||||
"identifiers": {(DOMAIN, self._device.unique_id)},
|
"identifiers": {(DOMAIN, self._unique_id)},
|
||||||
"name": self._device.name,
|
"name": self._device.name,
|
||||||
"manufacturer": "Yeelight",
|
"manufacturer": "Yeelight",
|
||||||
"model": self._device.model,
|
"model": self._device.model,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Sensor platform support for yeelight."""
|
"""Sensor platform support for yeelight."""
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@ -19,7 +18,7 @@ async def async_setup_entry(
|
|||||||
device = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][config_entry.entry_id][DATA_DEVICE]
|
device = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][config_entry.entry_id][DATA_DEVICE]
|
||||||
if device.is_nightlight_supported:
|
if device.is_nightlight_supported:
|
||||||
_LOGGER.debug("Adding nightlight mode sensor for %s", device.name)
|
_LOGGER.debug("Adding nightlight mode sensor for %s", device.name)
|
||||||
async_add_entities([YeelightNightlightModeSensor(device)])
|
async_add_entities([YeelightNightlightModeSensor(device, config_entry)])
|
||||||
|
|
||||||
|
|
||||||
class YeelightNightlightModeSensor(YeelightEntity, BinarySensorEntity):
|
class YeelightNightlightModeSensor(YeelightEntity, BinarySensorEntity):
|
||||||
@ -35,16 +34,6 @@ class YeelightNightlightModeSensor(YeelightEntity, BinarySensorEntity):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> Optional[str]:
|
|
||||||
"""Return a unique ID."""
|
|
||||||
unique = self._device.unique_id
|
|
||||||
|
|
||||||
if unique:
|
|
||||||
return unique + "-nightlight_sensor"
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the sensor."""
|
"""Return the name of the sensor."""
|
||||||
|
@ -18,6 +18,7 @@ from . import (
|
|||||||
CONF_SAVE_ON_CHANGE,
|
CONF_SAVE_ON_CHANGE,
|
||||||
CONF_TRANSITION,
|
CONF_TRANSITION,
|
||||||
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
|
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
|
||||||
|
_async_unique_name,
|
||||||
)
|
)
|
||||||
from . import DOMAIN # pylint:disable=unused-import
|
from . import DOMAIN # pylint:disable=unused-import
|
||||||
|
|
||||||
@ -38,7 +39,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize the config flow."""
|
"""Initialize the config flow."""
|
||||||
self._capabilities = None
|
|
||||||
self._discovered_devices = {}
|
self._discovered_devices = {}
|
||||||
|
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
@ -49,7 +49,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
try:
|
try:
|
||||||
await self._async_try_connect(user_input[CONF_HOST])
|
await self._async_try_connect(user_input[CONF_HOST])
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=self._async_default_name(),
|
title=user_input[CONF_HOST],
|
||||||
data=user_input,
|
data=user_input,
|
||||||
)
|
)
|
||||||
except CannotConnect:
|
except CannotConnect:
|
||||||
@ -59,9 +59,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
else:
|
else:
|
||||||
return await self.async_step_pick_device()
|
return await self.async_step_pick_device()
|
||||||
|
|
||||||
|
user_input = user_input or {}
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
data_schema=vol.Schema({vol.Optional(CONF_HOST): str}),
|
data_schema=vol.Schema(
|
||||||
|
{vol.Optional(CONF_HOST, default=user_input.get(CONF_HOST, "")): str}
|
||||||
|
),
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -69,9 +72,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
"""Handle the step to pick discovered device."""
|
"""Handle the step to pick discovered device."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
unique_id = user_input[CONF_DEVICE]
|
unique_id = user_input[CONF_DEVICE]
|
||||||
self._capabilities = self._discovered_devices[unique_id]
|
capabilities = self._discovered_devices[unique_id]
|
||||||
|
await self.async_set_unique_id(unique_id)
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=self._async_default_name(),
|
title=_async_unique_name(capabilities),
|
||||||
data={CONF_ID: unique_id},
|
data={CONF_ID: unique_id},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -122,25 +127,32 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
async def _async_try_connect(self, host):
|
async def _async_try_connect(self, host):
|
||||||
"""Set up with options."""
|
"""Set up with options."""
|
||||||
|
for entry in self._async_current_entries():
|
||||||
|
if entry.data.get(CONF_HOST) == host:
|
||||||
|
raise AlreadyConfigured
|
||||||
|
|
||||||
bulb = yeelight.Bulb(host)
|
bulb = yeelight.Bulb(host)
|
||||||
try:
|
try:
|
||||||
capabilities = await self.hass.async_add_executor_job(bulb.get_capabilities)
|
capabilities = await self.hass.async_add_executor_job(bulb.get_capabilities)
|
||||||
if capabilities is None: # timeout
|
if capabilities is None: # timeout
|
||||||
_LOGGER.error("Failed to get capabilities from %s: timeout", host)
|
_LOGGER.debug("Failed to get capabilities from %s: timeout", host)
|
||||||
raise CannotConnect
|
else:
|
||||||
except OSError as err:
|
|
||||||
_LOGGER.error("Failed to get capabilities from %s: %s", host, err)
|
|
||||||
raise CannotConnect from err
|
|
||||||
_LOGGER.debug("Get capabilities: %s", capabilities)
|
_LOGGER.debug("Get capabilities: %s", capabilities)
|
||||||
self._capabilities = capabilities
|
|
||||||
await self.async_set_unique_id(capabilities["id"])
|
await self.async_set_unique_id(capabilities["id"])
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
return
|
||||||
|
except OSError as err:
|
||||||
|
_LOGGER.debug("Failed to get capabilities from %s: %s", host, err)
|
||||||
|
# Ignore the error since get_capabilities uses UDP discovery packet
|
||||||
|
# which does not work in all network environments
|
||||||
|
|
||||||
@callback
|
# Fallback to get properties
|
||||||
def _async_default_name(self):
|
try:
|
||||||
model = self._capabilities["model"]
|
await self.hass.async_add_executor_job(bulb.get_properties)
|
||||||
unique_id = self._capabilities["id"]
|
except yeelight.BulbException as err:
|
||||||
return f"yeelight_{model}_{unique_id}"
|
_LOGGER.error("Failed to get properties from %s: %s", host, err)
|
||||||
|
raise CannotConnect from err
|
||||||
|
_LOGGER.debug("Get properties: %s", bulb.last_properties)
|
||||||
|
|
||||||
|
|
||||||
class OptionsFlowHandler(config_entries.OptionsFlow):
|
class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||||
@ -153,11 +165,8 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
async def async_step_init(self, user_input=None):
|
async def async_step_init(self, user_input=None):
|
||||||
"""Handle the initial step."""
|
"""Handle the initial step."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
# keep the name from imported entries
|
options = {**self._config_entry.options}
|
||||||
options = {
|
options.update(user_input)
|
||||||
CONF_NAME: self._config_entry.options.get(CONF_NAME),
|
|
||||||
**user_input,
|
|
||||||
}
|
|
||||||
return self.async_create_entry(title="", data=options)
|
return self.async_create_entry(title="", data=options)
|
||||||
|
|
||||||
options = self._config_entry.options
|
options = self._config_entry.options
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""Light platform support for yeelight."""
|
"""Light platform support for yeelight."""
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
import yeelight
|
import yeelight
|
||||||
@ -241,7 +240,7 @@ async def async_setup_entry(
|
|||||||
device_type = device.type
|
device_type = device.type
|
||||||
|
|
||||||
def _lights_setup_helper(klass):
|
def _lights_setup_helper(klass):
|
||||||
lights.append(klass(device, custom_effects=custom_effects))
|
lights.append(klass(device, config_entry, custom_effects=custom_effects))
|
||||||
|
|
||||||
if device_type == BulbType.White:
|
if device_type == BulbType.White:
|
||||||
_lights_setup_helper(YeelightGenericLight)
|
_lights_setup_helper(YeelightGenericLight)
|
||||||
@ -382,9 +381,9 @@ def _async_setup_services(hass: HomeAssistant):
|
|||||||
class YeelightGenericLight(YeelightEntity, LightEntity):
|
class YeelightGenericLight(YeelightEntity, LightEntity):
|
||||||
"""Representation of a Yeelight generic light."""
|
"""Representation of a Yeelight generic light."""
|
||||||
|
|
||||||
def __init__(self, device, custom_effects=None):
|
def __init__(self, device, entry, custom_effects=None):
|
||||||
"""Initialize the Yeelight light."""
|
"""Initialize the Yeelight light."""
|
||||||
super().__init__(device)
|
super().__init__(device, entry)
|
||||||
|
|
||||||
self.config = device.config
|
self.config = device.config
|
||||||
|
|
||||||
@ -418,12 +417,6 @@ class YeelightGenericLight(YeelightEntity, LightEntity):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> Optional[str]:
|
|
||||||
"""Return a unique ID."""
|
|
||||||
|
|
||||||
return self.device.unique_id
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
"""Flag supported features."""
|
"""Flag supported features."""
|
||||||
@ -852,14 +845,10 @@ class YeelightNightLightMode(YeelightGenericLight):
|
|||||||
"""Representation of a Yeelight when in nightlight mode."""
|
"""Representation of a Yeelight when in nightlight mode."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self) -> Optional[str]:
|
def unique_id(self) -> str:
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
unique = super().unique_id
|
unique = super().unique_id
|
||||||
|
return f"{unique}-nightlight"
|
||||||
if unique:
|
|
||||||
return unique + "-nightlight"
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
@ -945,12 +934,10 @@ class YeelightAmbientLight(YeelightColorLightWithoutNightlightSwitch):
|
|||||||
self._light_type = LightType.Ambient
|
self._light_type = LightType.Ambient
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self) -> Optional[str]:
|
def unique_id(self) -> str:
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
unique = super().unique_id
|
unique = super().unique_id
|
||||||
|
return f"{unique}-ambilight"
|
||||||
if unique:
|
|
||||||
return unique + "-ambilight"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
"""Tests for the Yeelight integration."""
|
"""Tests for the Yeelight integration."""
|
||||||
from yeelight import BulbType
|
from yeelight import BulbException, BulbType
|
||||||
from yeelight.main import _MODEL_SPECS
|
from yeelight.main import _MODEL_SPECS
|
||||||
|
|
||||||
from homeassistant.components.yeelight import (
|
from homeassistant.components.yeelight import (
|
||||||
@ -8,6 +8,7 @@ from homeassistant.components.yeelight import (
|
|||||||
CONF_SAVE_ON_CHANGE,
|
CONF_SAVE_ON_CHANGE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
|
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
|
||||||
|
YeelightScanner,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_DEVICES, CONF_ID, CONF_NAME
|
from homeassistant.const import CONF_DEVICES, CONF_ID, CONF_NAME
|
||||||
|
|
||||||
@ -27,7 +28,8 @@ CAPABILITIES = {
|
|||||||
"name": "",
|
"name": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
NAME = f"yeelight_{MODEL}_{ID}"
|
NAME = "name"
|
||||||
|
UNIQUE_NAME = f"yeelight_{MODEL}_{ID}"
|
||||||
|
|
||||||
MODULE = "homeassistant.components.yeelight"
|
MODULE = "homeassistant.components.yeelight"
|
||||||
MODULE_CONFIG_FLOW = f"{MODULE}.config_flow"
|
MODULE_CONFIG_FLOW = f"{MODULE}.config_flow"
|
||||||
@ -53,9 +55,10 @@ PROPERTIES = {
|
|||||||
"current_brightness": "30",
|
"current_brightness": "30",
|
||||||
}
|
}
|
||||||
|
|
||||||
ENTITY_BINARY_SENSOR = f"binary_sensor.{NAME}_nightlight"
|
ENTITY_BINARY_SENSOR = f"binary_sensor.{UNIQUE_NAME}_nightlight"
|
||||||
ENTITY_LIGHT = f"light.{NAME}"
|
ENTITY_LIGHT = f"light.{UNIQUE_NAME}"
|
||||||
ENTITY_NIGHTLIGHT = f"light.{NAME}_nightlight"
|
ENTITY_NIGHTLIGHT = f"light.{UNIQUE_NAME}_nightlight"
|
||||||
|
ENTITY_AMBILIGHT = f"light.{UNIQUE_NAME}_ambilight"
|
||||||
|
|
||||||
YAML_CONFIGURATION = {
|
YAML_CONFIGURATION = {
|
||||||
DOMAIN: {
|
DOMAIN: {
|
||||||
@ -80,6 +83,9 @@ def _mocked_bulb(cannot_connect=False):
|
|||||||
type(bulb).get_capabilities = MagicMock(
|
type(bulb).get_capabilities = MagicMock(
|
||||||
return_value=None if cannot_connect else CAPABILITIES
|
return_value=None if cannot_connect else CAPABILITIES
|
||||||
)
|
)
|
||||||
|
type(bulb).get_properties = MagicMock(
|
||||||
|
side_effect=BulbException if cannot_connect else None
|
||||||
|
)
|
||||||
type(bulb).get_model_specs = MagicMock(return_value=_MODEL_SPECS[MODEL])
|
type(bulb).get_model_specs = MagicMock(return_value=_MODEL_SPECS[MODEL])
|
||||||
|
|
||||||
bulb.capabilities = CAPABILITIES
|
bulb.capabilities = CAPABILITIES
|
||||||
@ -92,6 +98,8 @@ def _mocked_bulb(cannot_connect=False):
|
|||||||
|
|
||||||
|
|
||||||
def _patch_discovery(prefix, no_device=False):
|
def _patch_discovery(prefix, no_device=False):
|
||||||
|
YeelightScanner._scanner = None # Clear class scanner to reset hass
|
||||||
|
|
||||||
def _mocked_discovery(timeout=2, interface=False):
|
def _mocked_discovery(timeout=2, interface=False):
|
||||||
if no_device:
|
if no_device:
|
||||||
return []
|
return []
|
||||||
|
@ -4,10 +4,12 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.helpers import entity_component
|
from homeassistant.helpers import entity_component
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import ENTITY_BINARY_SENSOR, MODULE, PROPERTIES, YAML_CONFIGURATION, _mocked_bulb
|
from . import MODULE, NAME, PROPERTIES, YAML_CONFIGURATION, _mocked_bulb
|
||||||
|
|
||||||
from tests.async_mock import patch
|
from tests.async_mock import patch
|
||||||
|
|
||||||
|
ENTITY_BINARY_SENSOR = f"binary_sensor.{NAME}_nightlight"
|
||||||
|
|
||||||
|
|
||||||
async def test_nightlight(hass: HomeAssistant):
|
async def test_nightlight(hass: HomeAssistant):
|
||||||
"""Test nightlight sensor."""
|
"""Test nightlight sensor."""
|
||||||
|
@ -25,6 +25,7 @@ from . import (
|
|||||||
MODULE,
|
MODULE,
|
||||||
MODULE_CONFIG_FLOW,
|
MODULE_CONFIG_FLOW,
|
||||||
NAME,
|
NAME,
|
||||||
|
UNIQUE_NAME,
|
||||||
_mocked_bulb,
|
_mocked_bulb,
|
||||||
_patch_discovery,
|
_patch_discovery,
|
||||||
)
|
)
|
||||||
@ -33,7 +34,6 @@ from tests.async_mock import MagicMock, patch
|
|||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
CONF_NAME: NAME,
|
|
||||||
CONF_MODEL: "",
|
CONF_MODEL: "",
|
||||||
CONF_TRANSITION: DEFAULT_TRANSITION,
|
CONF_TRANSITION: DEFAULT_TRANSITION,
|
||||||
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
|
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
|
||||||
@ -67,9 +67,8 @@ async def test_discovery(hass: HomeAssistant):
|
|||||||
result3 = await hass.config_entries.flow.async_configure(
|
result3 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], {CONF_DEVICE: ID}
|
result["flow_id"], {CONF_DEVICE: ID}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result3["type"] == "create_entry"
|
assert result3["type"] == "create_entry"
|
||||||
assert result3["title"] == NAME
|
assert result3["title"] == UNIQUE_NAME
|
||||||
assert result3["data"] == {CONF_ID: ID}
|
assert result3["data"] == {CONF_ID: ID}
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
mock_setup.assert_called_once()
|
mock_setup.assert_called_once()
|
||||||
@ -126,6 +125,7 @@ async def test_import(hass: HomeAssistant):
|
|||||||
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=config
|
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=config
|
||||||
)
|
)
|
||||||
type(mocked_bulb).get_capabilities.assert_called_once()
|
type(mocked_bulb).get_capabilities.assert_called_once()
|
||||||
|
type(mocked_bulb).get_properties.assert_called_once()
|
||||||
assert result["type"] == "abort"
|
assert result["type"] == "abort"
|
||||||
assert result["reason"] == "cannot_connect"
|
assert result["reason"] == "cannot_connect"
|
||||||
|
|
||||||
@ -203,7 +203,9 @@ async def test_manual(hass: HomeAssistant):
|
|||||||
result4 = await hass.config_entries.flow.async_configure(
|
result4 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], {CONF_HOST: IP_ADDRESS}
|
result["flow_id"], {CONF_HOST: IP_ADDRESS}
|
||||||
)
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
assert result4["type"] == "create_entry"
|
assert result4["type"] == "create_entry"
|
||||||
|
assert result4["title"] == IP_ADDRESS
|
||||||
assert result4["data"] == {CONF_HOST: IP_ADDRESS}
|
assert result4["data"] == {CONF_HOST: IP_ADDRESS}
|
||||||
|
|
||||||
# Duplicate
|
# Duplicate
|
||||||
@ -221,7 +223,9 @@ async def test_manual(hass: HomeAssistant):
|
|||||||
|
|
||||||
async def test_options(hass: HomeAssistant):
|
async def test_options(hass: HomeAssistant):
|
||||||
"""Test options flow."""
|
"""Test options flow."""
|
||||||
config_entry = MockConfigEntry(domain=DOMAIN, data={CONF_HOST: IP_ADDRESS})
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data={CONF_HOST: IP_ADDRESS, CONF_NAME: NAME}
|
||||||
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
mocked_bulb = _mocked_bulb()
|
mocked_bulb = _mocked_bulb()
|
||||||
@ -230,16 +234,14 @@ async def test_options(hass: HomeAssistant):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
CONF_NAME: NAME,
|
||||||
CONF_MODEL: "",
|
CONF_MODEL: "",
|
||||||
CONF_TRANSITION: DEFAULT_TRANSITION,
|
CONF_TRANSITION: DEFAULT_TRANSITION,
|
||||||
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
|
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
|
||||||
CONF_SAVE_ON_CHANGE: DEFAULT_SAVE_ON_CHANGE,
|
CONF_SAVE_ON_CHANGE: DEFAULT_SAVE_ON_CHANGE,
|
||||||
CONF_NIGHTLIGHT_SWITCH: DEFAULT_NIGHTLIGHT_SWITCH,
|
CONF_NIGHTLIGHT_SWITCH: DEFAULT_NIGHTLIGHT_SWITCH,
|
||||||
}
|
}
|
||||||
assert config_entry.options == {
|
assert config_entry.options == config
|
||||||
CONF_NAME: "",
|
|
||||||
**config,
|
|
||||||
}
|
|
||||||
assert hass.states.get(f"light.{NAME}_nightlight") is None
|
assert hass.states.get(f"light.{NAME}_nightlight") is None
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
@ -247,15 +249,40 @@ async def test_options(hass: HomeAssistant):
|
|||||||
assert result["step_id"] == "init"
|
assert result["step_id"] == "init"
|
||||||
|
|
||||||
config[CONF_NIGHTLIGHT_SWITCH] = True
|
config[CONF_NIGHTLIGHT_SWITCH] = True
|
||||||
|
user_input = {**config}
|
||||||
|
user_input.pop(CONF_NAME)
|
||||||
with patch(f"{MODULE}.Bulb", return_value=mocked_bulb):
|
with patch(f"{MODULE}.Bulb", return_value=mocked_bulb):
|
||||||
result2 = await hass.config_entries.options.async_configure(
|
result2 = await hass.config_entries.options.async_configure(
|
||||||
result["flow_id"], config
|
result["flow_id"], user_input
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result2["type"] == "create_entry"
|
assert result2["type"] == "create_entry"
|
||||||
assert result2["data"] == {
|
assert result2["data"] == config
|
||||||
CONF_NAME: "",
|
|
||||||
**config,
|
|
||||||
}
|
|
||||||
assert result2["data"] == config_entry.options
|
assert result2["data"] == config_entry.options
|
||||||
assert hass.states.get(f"light.{NAME}_nightlight") is not None
|
assert hass.states.get(f"light.{NAME}_nightlight") is not None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_manual_no_capabilities(hass: HomeAssistant):
|
||||||
|
"""Test manually setup without successful get_capabilities."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
assert not result["errors"]
|
||||||
|
|
||||||
|
mocked_bulb = _mocked_bulb()
|
||||||
|
type(mocked_bulb).get_capabilities = MagicMock(return_value=None)
|
||||||
|
with patch(f"{MODULE_CONFIG_FLOW}.yeelight.Bulb", return_value=mocked_bulb), patch(
|
||||||
|
f"{MODULE}.async_setup", return_value=True
|
||||||
|
), patch(
|
||||||
|
f"{MODULE}.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {CONF_HOST: IP_ADDRESS}
|
||||||
|
)
|
||||||
|
type(mocked_bulb).get_capabilities.assert_called_once()
|
||||||
|
type(mocked_bulb).get_properties.assert_called_once()
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["data"] == {CONF_HOST: IP_ADDRESS}
|
||||||
|
@ -1,19 +1,27 @@
|
|||||||
"""Test Yeelight."""
|
"""Test Yeelight."""
|
||||||
|
from yeelight import BulbType
|
||||||
|
|
||||||
from homeassistant.components.yeelight import (
|
from homeassistant.components.yeelight import (
|
||||||
|
CONF_NIGHTLIGHT_SWITCH,
|
||||||
CONF_NIGHTLIGHT_SWITCH_TYPE,
|
CONF_NIGHTLIGHT_SWITCH_TYPE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
|
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_DEVICES, CONF_NAME
|
from homeassistant.const import CONF_DEVICES, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
CONFIG_ENTRY_DATA,
|
CONFIG_ENTRY_DATA,
|
||||||
|
ENTITY_AMBILIGHT,
|
||||||
|
ENTITY_BINARY_SENSOR,
|
||||||
|
ENTITY_LIGHT,
|
||||||
|
ENTITY_NIGHTLIGHT,
|
||||||
|
ID,
|
||||||
IP_ADDRESS,
|
IP_ADDRESS,
|
||||||
MODULE,
|
MODULE,
|
||||||
MODULE_CONFIG_FLOW,
|
MODULE_CONFIG_FLOW,
|
||||||
NAME,
|
|
||||||
_mocked_bulb,
|
_mocked_bulb,
|
||||||
_patch_discovery,
|
_patch_discovery,
|
||||||
)
|
)
|
||||||
@ -32,13 +40,13 @@ async def test_setup_discovery(hass: HomeAssistant):
|
|||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get(f"binary_sensor.{NAME}_nightlight") is not None
|
assert hass.states.get(ENTITY_BINARY_SENSOR) is not None
|
||||||
assert hass.states.get(f"light.{NAME}") is not None
|
assert hass.states.get(ENTITY_LIGHT) is not None
|
||||||
|
|
||||||
# Unload
|
# Unload
|
||||||
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
assert hass.states.get(f"binary_sensor.{NAME}_nightlight") is None
|
assert hass.states.get(ENTITY_BINARY_SENSOR) is None
|
||||||
assert hass.states.get(f"light.{NAME}") is None
|
assert hass.states.get(ENTITY_LIGHT) is None
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_import(hass: HomeAssistant):
|
async def test_setup_import(hass: HomeAssistant):
|
||||||
@ -67,3 +75,57 @@ async def test_setup_import(hass: HomeAssistant):
|
|||||||
assert hass.states.get(f"binary_sensor.{name}_nightlight") is not None
|
assert hass.states.get(f"binary_sensor.{name}_nightlight") is not None
|
||||||
assert hass.states.get(f"light.{name}") is not None
|
assert hass.states.get(f"light.{name}") is not None
|
||||||
assert hass.states.get(f"light.{name}_nightlight") is not None
|
assert hass.states.get(f"light.{name}_nightlight") is not None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_ids_device(hass: HomeAssistant):
|
||||||
|
"""Test Yeelight unique IDs from yeelight device IDs."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
**CONFIG_ENTRY_DATA,
|
||||||
|
CONF_NIGHTLIGHT_SWITCH: True,
|
||||||
|
},
|
||||||
|
unique_id=ID,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
mocked_bulb = _mocked_bulb()
|
||||||
|
mocked_bulb.bulb_type = BulbType.WhiteTempMood
|
||||||
|
with _patch_discovery(MODULE), patch(f"{MODULE}.Bulb", return_value=mocked_bulb):
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
er = await entity_registry.async_get_registry(hass)
|
||||||
|
assert er.async_get(ENTITY_BINARY_SENSOR).unique_id == ID
|
||||||
|
assert er.async_get(ENTITY_LIGHT).unique_id == ID
|
||||||
|
assert er.async_get(ENTITY_NIGHTLIGHT).unique_id == f"{ID}-nightlight"
|
||||||
|
assert er.async_get(ENTITY_AMBILIGHT).unique_id == f"{ID}-ambilight"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_ids_entry(hass: HomeAssistant):
|
||||||
|
"""Test Yeelight unique IDs from entry IDs."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
**CONFIG_ENTRY_DATA,
|
||||||
|
CONF_NIGHTLIGHT_SWITCH: True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
mocked_bulb = _mocked_bulb()
|
||||||
|
mocked_bulb.bulb_type = BulbType.WhiteTempMood
|
||||||
|
with _patch_discovery(MODULE), patch(f"{MODULE}.Bulb", return_value=mocked_bulb):
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
er = await entity_registry.async_get_registry(hass)
|
||||||
|
assert er.async_get(ENTITY_BINARY_SENSOR).unique_id == config_entry.entry_id
|
||||||
|
assert er.async_get(ENTITY_LIGHT).unique_id == config_entry.entry_id
|
||||||
|
assert (
|
||||||
|
er.async_get(ENTITY_NIGHTLIGHT).unique_id
|
||||||
|
== f"{config_entry.entry_id}-nightlight"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
er.async_get(ENTITY_AMBILIGHT).unique_id == f"{config_entry.entry_id}-ambilight"
|
||||||
|
)
|
||||||
|
@ -71,8 +71,9 @@ from homeassistant.components.yeelight.light import (
|
|||||||
YEELIGHT_MONO_EFFECT_LIST,
|
YEELIGHT_MONO_EFFECT_LIST,
|
||||||
YEELIGHT_TEMP_ONLY_EFFECT_LIST,
|
YEELIGHT_TEMP_ONLY_EFFECT_LIST,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_ID, CONF_NAME
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util.color import (
|
from homeassistant.util.color import (
|
||||||
color_hs_to_RGB,
|
color_hs_to_RGB,
|
||||||
@ -90,6 +91,7 @@ from . import (
|
|||||||
MODULE,
|
MODULE,
|
||||||
NAME,
|
NAME,
|
||||||
PROPERTIES,
|
PROPERTIES,
|
||||||
|
UNIQUE_NAME,
|
||||||
_mocked_bulb,
|
_mocked_bulb,
|
||||||
_patch_discovery,
|
_patch_discovery,
|
||||||
)
|
)
|
||||||
@ -97,15 +99,21 @@ from . import (
|
|||||||
from tests.async_mock import MagicMock, patch
|
from tests.async_mock import MagicMock, patch
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
CONFIG_ENTRY_DATA = {
|
||||||
|
CONF_HOST: IP_ADDRESS,
|
||||||
|
CONF_TRANSITION: DEFAULT_TRANSITION,
|
||||||
|
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
|
||||||
|
CONF_SAVE_ON_CHANGE: DEFAULT_SAVE_ON_CHANGE,
|
||||||
|
CONF_NIGHTLIGHT_SWITCH: DEFAULT_NIGHTLIGHT_SWITCH,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_services(hass: HomeAssistant, caplog):
|
async def test_services(hass: HomeAssistant, caplog):
|
||||||
"""Test Yeelight services."""
|
"""Test Yeelight services."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
CONF_ID: "",
|
**CONFIG_ENTRY_DATA,
|
||||||
CONF_HOST: IP_ADDRESS,
|
|
||||||
CONF_TRANSITION: DEFAULT_TRANSITION,
|
|
||||||
CONF_MODE_MUSIC: True,
|
CONF_MODE_MUSIC: True,
|
||||||
CONF_SAVE_ON_CHANGE: True,
|
CONF_SAVE_ON_CHANGE: True,
|
||||||
CONF_NIGHTLIGHT_SWITCH: True,
|
CONF_NIGHTLIGHT_SWITCH: True,
|
||||||
@ -299,17 +307,13 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
model,
|
model,
|
||||||
target_properties,
|
target_properties,
|
||||||
nightlight_properties=None,
|
nightlight_properties=None,
|
||||||
name=NAME,
|
name=UNIQUE_NAME,
|
||||||
entity_id=ENTITY_LIGHT,
|
entity_id=ENTITY_LIGHT,
|
||||||
):
|
):
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
CONF_ID: "",
|
**CONFIG_ENTRY_DATA,
|
||||||
CONF_HOST: IP_ADDRESS,
|
|
||||||
CONF_TRANSITION: DEFAULT_TRANSITION,
|
|
||||||
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
|
|
||||||
CONF_SAVE_ON_CHANGE: DEFAULT_SAVE_ON_CHANGE,
|
|
||||||
CONF_NIGHTLIGHT_SWITCH: False,
|
CONF_NIGHTLIGHT_SWITCH: False,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -329,6 +333,8 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await config_entry.async_remove(hass)
|
await config_entry.async_remove(hass)
|
||||||
|
registry = await entity_registry.async_get_registry(hass)
|
||||||
|
registry.async_clear_config_entry(config_entry.entry_id)
|
||||||
|
|
||||||
# nightlight
|
# nightlight
|
||||||
if nightlight_properties is None:
|
if nightlight_properties is None:
|
||||||
@ -336,11 +342,7 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
CONF_ID: "",
|
**CONFIG_ENTRY_DATA,
|
||||||
CONF_HOST: IP_ADDRESS,
|
|
||||||
CONF_TRANSITION: DEFAULT_TRANSITION,
|
|
||||||
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
|
|
||||||
CONF_SAVE_ON_CHANGE: DEFAULT_SAVE_ON_CHANGE,
|
|
||||||
CONF_NIGHTLIGHT_SWITCH: True,
|
CONF_NIGHTLIGHT_SWITCH: True,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -358,6 +360,7 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
await config_entry.async_remove(hass)
|
await config_entry.async_remove(hass)
|
||||||
|
registry.async_clear_config_entry(config_entry.entry_id)
|
||||||
|
|
||||||
bright = round(255 * int(PROPERTIES["bright"]) / 100)
|
bright = round(255 * int(PROPERTIES["bright"]) / 100)
|
||||||
current_brightness = round(255 * int(PROPERTIES["current_brightness"]) / 100)
|
current_brightness = round(255 * int(PROPERTIES["current_brightness"]) / 100)
|
||||||
@ -486,7 +489,7 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
"rgb_color": bg_rgb_color,
|
"rgb_color": bg_rgb_color,
|
||||||
"xy_color": bg_xy_color,
|
"xy_color": bg_xy_color,
|
||||||
},
|
},
|
||||||
name=f"{NAME} ambilight",
|
name=f"{UNIQUE_NAME} ambilight",
|
||||||
entity_id=f"{ENTITY_LIGHT}_ambilight",
|
entity_id=f"{ENTITY_LIGHT}_ambilight",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -518,14 +521,7 @@ async def test_effects(hass: HomeAssistant):
|
|||||||
|
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data=CONFIG_ENTRY_DATA,
|
||||||
CONF_ID: "",
|
|
||||||
CONF_HOST: IP_ADDRESS,
|
|
||||||
CONF_TRANSITION: DEFAULT_TRANSITION,
|
|
||||||
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
|
|
||||||
CONF_SAVE_ON_CHANGE: DEFAULT_SAVE_ON_CHANGE,
|
|
||||||
CONF_NIGHTLIGHT_SWITCH: DEFAULT_NIGHTLIGHT_SWITCH,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user