mirror of
https://github.com/home-assistant/core.git
synced 2025-04-28 11:17:53 +00:00
Improve Yeelight code (#39543)
* Rename ipaddr to ip_addr * Move custom services to entity services * Remove platform data * Change service setup to callback * Rename ip_addr to host * Use _host inside class
This commit is contained in:
parent
04c849b0ee
commit
7b3182fa8f
@ -9,10 +9,9 @@ from yeelight import Bulb, BulbException, discover_bulbs
|
|||||||
|
|
||||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry, ConfigEntryNotReady
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry, ConfigEntryNotReady
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
|
||||||
CONF_DEVICES,
|
CONF_DEVICES,
|
||||||
|
CONF_HOST,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_IP_ADDRESS,
|
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_SCAN_INTERVAL,
|
CONF_SCAN_INTERVAL,
|
||||||
)
|
)
|
||||||
@ -126,8 +125,6 @@ CONFIG_SCHEMA = vol.Schema(
|
|||||||
extra=vol.ALLOW_EXTRA,
|
extra=vol.ALLOW_EXTRA,
|
||||||
)
|
)
|
||||||
|
|
||||||
YEELIGHT_SERVICE_SCHEMA = vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.entity_ids})
|
|
||||||
|
|
||||||
UPDATE_REQUEST_PROPERTIES = [
|
UPDATE_REQUEST_PROPERTIES = [
|
||||||
"power",
|
"power",
|
||||||
"main_power",
|
"main_power",
|
||||||
@ -163,10 +160,10 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Import manually configured devices
|
# Import manually configured devices
|
||||||
for ipaddr, device_config in config.get(DOMAIN, {}).get(CONF_DEVICES, {}).items():
|
for host, device_config in config.get(DOMAIN, {}).get(CONF_DEVICES, {}).items():
|
||||||
_LOGGER.debug("Importing configured %s", ipaddr)
|
_LOGGER.debug("Importing configured %s", host)
|
||||||
entry_config = {
|
entry_config = {
|
||||||
CONF_IP_ADDRESS: ipaddr,
|
CONF_HOST: host,
|
||||||
**device_config,
|
**device_config,
|
||||||
}
|
}
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
@ -183,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(ipaddr: str) -> None:
|
async def _initialize(host: str) -> None:
|
||||||
device = await _async_setup_device(hass, ipaddr, entry.options)
|
device = await _async_setup_device(hass, host, entry.options)
|
||||||
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(
|
||||||
@ -197,7 +194,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
hass.config_entries.async_update_entry(
|
hass.config_entries.async_update_entry(
|
||||||
entry,
|
entry,
|
||||||
data={
|
data={
|
||||||
CONF_IP_ADDRESS: entry.data.get(CONF_IP_ADDRESS),
|
CONF_HOST: entry.data.get(CONF_HOST),
|
||||||
CONF_ID: entry.data.get(CONF_ID),
|
CONF_ID: entry.data.get(CONF_ID),
|
||||||
},
|
},
|
||||||
options={
|
options={
|
||||||
@ -218,9 +215,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
DATA_UNSUB_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener)
|
DATA_UNSUB_UPDATE_LISTENER: entry.add_update_listener(_async_update_listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
if entry.data.get(CONF_IP_ADDRESS):
|
if entry.data.get(CONF_HOST):
|
||||||
# manually added device
|
# manually added device
|
||||||
await _initialize(entry.data[CONF_IP_ADDRESS])
|
await _initialize(entry.data[CONF_HOST])
|
||||||
else:
|
else:
|
||||||
# discovery
|
# discovery
|
||||||
scanner = YeelightScanner.async_get(hass)
|
scanner = YeelightScanner.async_get(hass)
|
||||||
@ -254,16 +251,16 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
|
|
||||||
async def _async_setup_device(
|
async def _async_setup_device(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
ipaddr: str,
|
host: str,
|
||||||
config: dict,
|
config: dict,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Set up device
|
# Set up device
|
||||||
bulb = Bulb(ipaddr, model=config.get(CONF_MODEL) or None)
|
bulb = Bulb(host, model=config.get(CONF_MODEL) or 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
|
if capabilities is None: # timeout
|
||||||
_LOGGER.error("Failed to get capabilities from %s", ipaddr)
|
_LOGGER.error("Failed to get capabilities from %s", host)
|
||||||
raise ConfigEntryNotReady
|
raise ConfigEntryNotReady
|
||||||
device = YeelightDevice(hass, ipaddr, config, bulb)
|
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
|
||||||
@ -303,11 +300,11 @@ class YeelightScanner:
|
|||||||
unique_id = device["capabilities"]["id"]
|
unique_id = device["capabilities"]["id"]
|
||||||
if unique_id in self._seen:
|
if unique_id in self._seen:
|
||||||
continue
|
continue
|
||||||
ipaddr = device["ip"]
|
host = device["ip"]
|
||||||
self._seen[unique_id] = ipaddr
|
self._seen[unique_id] = host
|
||||||
_LOGGER.debug("Yeelight discovered at %s", ipaddr)
|
_LOGGER.debug("Yeelight discovered at %s", host)
|
||||||
if unique_id in self._callbacks:
|
if unique_id in self._callbacks:
|
||||||
self._hass.async_create_task(self._callbacks[unique_id](ipaddr))
|
self._hass.async_create_task(self._callbacks[unique_id](host))
|
||||||
self._callbacks.pop(unique_id)
|
self._callbacks.pop(unique_id)
|
||||||
if len(self._callbacks) == 0:
|
if len(self._callbacks) == 0:
|
||||||
self._async_stop_scan()
|
self._async_stop_scan()
|
||||||
@ -333,9 +330,9 @@ class YeelightScanner:
|
|||||||
@callback
|
@callback
|
||||||
def async_register_callback(self, unique_id, callback_func):
|
def async_register_callback(self, unique_id, callback_func):
|
||||||
"""Register callback function."""
|
"""Register callback function."""
|
||||||
ipaddr = self._seen.get(unique_id)
|
host = self._seen.get(unique_id)
|
||||||
if ipaddr is not None:
|
if host is not None:
|
||||||
self._hass.async_add_job(callback_func(ipaddr))
|
self._hass.async_add_job(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:
|
||||||
@ -354,11 +351,11 @@ class YeelightScanner:
|
|||||||
class YeelightDevice:
|
class YeelightDevice:
|
||||||
"""Represents single Yeelight device."""
|
"""Represents single Yeelight device."""
|
||||||
|
|
||||||
def __init__(self, hass, ipaddr, config, bulb):
|
def __init__(self, hass, host, config, bulb):
|
||||||
"""Initialize device."""
|
"""Initialize device."""
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._config = config
|
self._config = config
|
||||||
self._ipaddr = ipaddr
|
self._host = host
|
||||||
unique_id = bulb.capabilities.get("id")
|
unique_id = bulb.capabilities.get("id")
|
||||||
self._name = config.get(CONF_NAME) or f"yeelight_{bulb.model}_{unique_id}"
|
self._name = config.get(CONF_NAME) or f"yeelight_{bulb.model}_{unique_id}"
|
||||||
self._bulb_device = bulb
|
self._bulb_device = bulb
|
||||||
@ -382,9 +379,9 @@ class YeelightDevice:
|
|||||||
return self._config
|
return self._config
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ipaddr(self):
|
def host(self):
|
||||||
"""Return ip address."""
|
"""Return hostname."""
|
||||||
return self._ipaddr
|
return self._host
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
@ -472,7 +469,7 @@ class YeelightDevice:
|
|||||||
self.bulb.turn_off(duration=duration, light_type=light_type)
|
self.bulb.turn_off(duration=duration, light_type=light_type)
|
||||||
except BulbException as ex:
|
except BulbException as ex:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Unable to turn the bulb off: %s, %s: %s", self.ipaddr, self.name, ex
|
"Unable to turn the bulb off: %s, %s: %s", self._host, self.name, ex
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update_properties(self):
|
def _update_properties(self):
|
||||||
@ -486,7 +483,7 @@ class YeelightDevice:
|
|||||||
except BulbException as ex:
|
except BulbException as ex:
|
||||||
if self._available: # just inform once
|
if self._available: # just inform once
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Unable to update device %s, %s: %s", self.ipaddr, self.name, ex
|
"Unable to update device %s, %s: %s", self._host, self.name, ex
|
||||||
)
|
)
|
||||||
self._available = False
|
self._available = False
|
||||||
|
|
||||||
@ -498,14 +495,14 @@ class YeelightDevice:
|
|||||||
self.bulb.get_capabilities()
|
self.bulb.get_capabilities()
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Device %s, %s capabilities: %s",
|
"Device %s, %s capabilities: %s",
|
||||||
self.ipaddr,
|
self._host,
|
||||||
self.name,
|
self.name,
|
||||||
self.bulb.capabilities,
|
self.bulb.capabilities,
|
||||||
)
|
)
|
||||||
except BulbException as ex:
|
except BulbException as ex:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Unable to get device capabilities %s, %s: %s",
|
"Unable to get device capabilities %s, %s: %s",
|
||||||
self.ipaddr,
|
self._host,
|
||||||
self.name,
|
self.name,
|
||||||
ex,
|
ex,
|
||||||
)
|
)
|
||||||
@ -513,7 +510,7 @@ class YeelightDevice:
|
|||||||
def update(self):
|
def update(self):
|
||||||
"""Update device properties and send data updated signal."""
|
"""Update device properties and send data updated signal."""
|
||||||
self._update_properties()
|
self._update_properties()
|
||||||
dispatcher_send(self._hass, DATA_UPDATED.format(self._ipaddr))
|
dispatcher_send(self._hass, DATA_UPDATED.format(self._host))
|
||||||
|
|
||||||
async def async_setup(self):
|
async def async_setup(self):
|
||||||
"""Set up the device."""
|
"""Set up the device."""
|
||||||
|
@ -30,7 +30,7 @@ class YeelightNightlightModeSensor(YeelightEntity, BinarySensorEntity):
|
|||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
self.hass,
|
self.hass,
|
||||||
DATA_UPDATED.format(self._device.ipaddr),
|
DATA_UPDATED.format(self._device.host),
|
||||||
self.async_write_ha_state,
|
self.async_write_ha_state,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -5,7 +5,7 @@ import voluptuous as vol
|
|||||||
import yeelight
|
import yeelight
|
||||||
|
|
||||||
from homeassistant import config_entries, exceptions
|
from homeassistant import config_entries, exceptions
|
||||||
from homeassistant.const import CONF_ID, CONF_IP_ADDRESS, CONF_NAME
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_NAME
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
@ -45,9 +45,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
"""Handle the initial step."""
|
"""Handle the initial step."""
|
||||||
errors = {}
|
errors = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
if user_input.get(CONF_IP_ADDRESS):
|
if user_input.get(CONF_HOST):
|
||||||
try:
|
try:
|
||||||
await self._async_try_connect(user_input[CONF_IP_ADDRESS])
|
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=self._async_default_name(),
|
||||||
data=user_input,
|
data=user_input,
|
||||||
@ -61,7 +61,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
data_schema=vol.Schema({vol.Optional(CONF_IP_ADDRESS): str}),
|
data_schema=vol.Schema({vol.Optional(CONF_HOST): str}),
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -90,8 +90,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
if unique_id in configured_devices:
|
if unique_id in configured_devices:
|
||||||
continue # ignore configured devices
|
continue # ignore configured devices
|
||||||
model = capabilities["model"]
|
model = capabilities["model"]
|
||||||
ipaddr = device["ip"]
|
host = device["ip"]
|
||||||
name = f"{ipaddr} {model} {unique_id}"
|
name = f"{host} {model} {unique_id}"
|
||||||
self._discovered_devices[unique_id] = capabilities
|
self._discovered_devices[unique_id] = capabilities
|
||||||
devices_name[unique_id] = name
|
devices_name[unique_id] = name
|
||||||
|
|
||||||
@ -105,11 +105,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
async def async_step_import(self, user_input=None):
|
async def async_step_import(self, user_input=None):
|
||||||
"""Handle import step."""
|
"""Handle import step."""
|
||||||
ipaddr = user_input[CONF_IP_ADDRESS]
|
host = user_input[CONF_HOST]
|
||||||
try:
|
try:
|
||||||
await self._async_try_connect(ipaddr)
|
await self._async_try_connect(host)
|
||||||
except CannotConnect:
|
except CannotConnect:
|
||||||
_LOGGER.error("Failed to import %s: cannot connect", ipaddr)
|
_LOGGER.error("Failed to import %s: cannot connect", host)
|
||||||
return self.async_abort(reason="cannot_connect")
|
return self.async_abort(reason="cannot_connect")
|
||||||
except AlreadyConfigured:
|
except AlreadyConfigured:
|
||||||
return self.async_abort(reason="already_configured")
|
return self.async_abort(reason="already_configured")
|
||||||
@ -120,16 +120,16 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
)
|
)
|
||||||
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
|
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
|
||||||
|
|
||||||
async def _async_try_connect(self, ipaddr):
|
async def _async_try_connect(self, host):
|
||||||
"""Set up with options."""
|
"""Set up with options."""
|
||||||
bulb = yeelight.Bulb(ipaddr)
|
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", ipaddr)
|
_LOGGER.error("Failed to get capabilities from %s: timeout", host)
|
||||||
raise CannotConnect
|
raise CannotConnect
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
_LOGGER.error("Failed to get capabilities from %s: %s", ipaddr, err)
|
_LOGGER.error("Failed to get capabilities from %s: %s", host, err)
|
||||||
raise CannotConnect from err
|
raise CannotConnect from err
|
||||||
_LOGGER.debug("Get capabilities: %s", capabilities)
|
_LOGGER.debug("Get capabilities: %s", capabilities)
|
||||||
self._capabilities = capabilities
|
self._capabilities = capabilities
|
||||||
|
@ -36,9 +36,9 @@ from homeassistant.components.light import (
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_MODE, CONF_NAME
|
from homeassistant.const import ATTR_ENTITY_ID, ATTR_MODE, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers import entity_platform
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.service import extract_entity_ids
|
|
||||||
import homeassistant.util.color as color_util
|
import homeassistant.util.color as color_util
|
||||||
from homeassistant.util.color import (
|
from homeassistant.util.color import (
|
||||||
color_temperature_kelvin_to_mired as kelvin_to_mired,
|
color_temperature_kelvin_to_mired as kelvin_to_mired,
|
||||||
@ -59,17 +59,13 @@ from . import (
|
|||||||
DATA_CUSTOM_EFFECTS,
|
DATA_CUSTOM_EFFECTS,
|
||||||
DATA_DEVICE,
|
DATA_DEVICE,
|
||||||
DATA_UPDATED,
|
DATA_UPDATED,
|
||||||
DATA_YEELIGHT,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
YEELIGHT_FLOW_TRANSITION_SCHEMA,
|
YEELIGHT_FLOW_TRANSITION_SCHEMA,
|
||||||
YEELIGHT_SERVICE_SCHEMA,
|
|
||||||
YeelightEntity,
|
YeelightEntity,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
PLATFORM_DATA_KEY = f"{DATA_YEELIGHT}_lights"
|
|
||||||
|
|
||||||
SUPPORT_YEELIGHT = (
|
SUPPORT_YEELIGHT = (
|
||||||
SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_FLASH | SUPPORT_EFFECT
|
SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION | SUPPORT_FLASH | SUPPORT_EFFECT
|
||||||
)
|
)
|
||||||
@ -148,25 +144,20 @@ EFFECTS_MAP = {
|
|||||||
|
|
||||||
VALID_BRIGHTNESS = vol.All(vol.Coerce(int), vol.Range(min=1, max=100))
|
VALID_BRIGHTNESS = vol.All(vol.Coerce(int), vol.Range(min=1, max=100))
|
||||||
|
|
||||||
SERVICE_SCHEMA_SET_MODE = YEELIGHT_SERVICE_SCHEMA.extend(
|
SERVICE_SCHEMA_SET_MODE = {
|
||||||
{vol.Required(ATTR_MODE): vol.In([mode.name.lower() for mode in PowerMode])}
|
vol.Required(ATTR_MODE): vol.In([mode.name.lower() for mode in PowerMode])
|
||||||
)
|
}
|
||||||
|
|
||||||
SERVICE_SCHEMA_START_FLOW = YEELIGHT_SERVICE_SCHEMA.extend(
|
SERVICE_SCHEMA_START_FLOW = YEELIGHT_FLOW_TRANSITION_SCHEMA
|
||||||
YEELIGHT_FLOW_TRANSITION_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
SERVICE_SCHEMA_SET_COLOR_SCENE = YEELIGHT_SERVICE_SCHEMA.extend(
|
SERVICE_SCHEMA_SET_COLOR_SCENE = {
|
||||||
{
|
|
||||||
vol.Required(ATTR_RGB_COLOR): vol.All(
|
vol.Required(ATTR_RGB_COLOR): vol.All(
|
||||||
vol.ExactSequence((cv.byte, cv.byte, cv.byte)), vol.Coerce(tuple)
|
vol.ExactSequence((cv.byte, cv.byte, cv.byte)), vol.Coerce(tuple)
|
||||||
),
|
),
|
||||||
vol.Required(ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
vol.Required(ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
SERVICE_SCHEMA_SET_HSV_SCENE = YEELIGHT_SERVICE_SCHEMA.extend(
|
SERVICE_SCHEMA_SET_HSV_SCENE = {
|
||||||
{
|
|
||||||
vol.Required(ATTR_HS_COLOR): vol.All(
|
vol.Required(ATTR_HS_COLOR): vol.All(
|
||||||
vol.ExactSequence(
|
vol.ExactSequence(
|
||||||
(
|
(
|
||||||
@ -177,30 +168,22 @@ SERVICE_SCHEMA_SET_HSV_SCENE = YEELIGHT_SERVICE_SCHEMA.extend(
|
|||||||
vol.Coerce(tuple),
|
vol.Coerce(tuple),
|
||||||
),
|
),
|
||||||
vol.Required(ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
vol.Required(ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
SERVICE_SCHEMA_SET_COLOR_TEMP_SCENE = YEELIGHT_SERVICE_SCHEMA.extend(
|
SERVICE_SCHEMA_SET_COLOR_TEMP_SCENE = {
|
||||||
{
|
vol.Required(ATTR_KELVIN): vol.All(vol.Coerce(int), vol.Range(min=1700, max=6500)),
|
||||||
vol.Required(ATTR_KELVIN): vol.All(
|
|
||||||
vol.Coerce(int), vol.Range(min=1700, max=6500)
|
|
||||||
),
|
|
||||||
vol.Required(ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
vol.Required(ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
SERVICE_SCHEMA_SET_COLOR_FLOW_SCENE = YEELIGHT_SERVICE_SCHEMA.extend(
|
SERVICE_SCHEMA_SET_COLOR_FLOW_SCENE = YEELIGHT_FLOW_TRANSITION_SCHEMA
|
||||||
YEELIGHT_FLOW_TRANSITION_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
SERVICE_SCHEMA_SET_AUTO_DELAY_OFF = YEELIGHT_SERVICE_SCHEMA.extend(
|
SERVICE_SCHEMA_SET_AUTO_DELAY_OFF_SCENE = {
|
||||||
{
|
|
||||||
vol.Required(ATTR_MINUTES): vol.All(vol.Coerce(int), vol.Range(min=1, max=60)),
|
vol.Required(ATTR_MINUTES): vol.All(vol.Coerce(int), vol.Range(min=1, max=60)),
|
||||||
vol.Required(ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
vol.Required(ATTR_BRIGHTNESS): VALID_BRIGHTNESS,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
def _transitions_config_parser(transitions):
|
def _transitions_config_parser(transitions):
|
||||||
"""Parse transitions config into initialized objects."""
|
"""Parse transitions config into initialized objects."""
|
||||||
transition_objects = []
|
transition_objects = []
|
||||||
@ -211,6 +194,7 @@ def _transitions_config_parser(transitions):
|
|||||||
return transition_objects
|
return transition_objects
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
def _parse_custom_effects(effects_config):
|
def _parse_custom_effects(effects_config):
|
||||||
effects = {}
|
effects = {}
|
||||||
for config in effects_config:
|
for config in effects_config:
|
||||||
@ -245,9 +229,6 @@ async def async_setup_entry(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Set up Yeelight from a config entry."""
|
"""Set up Yeelight from a config entry."""
|
||||||
|
|
||||||
if PLATFORM_DATA_KEY not in hass.data:
|
|
||||||
hass.data[PLATFORM_DATA_KEY] = []
|
|
||||||
|
|
||||||
custom_effects = _parse_custom_effects(hass.data[DOMAIN][DATA_CUSTOM_EFFECTS])
|
custom_effects = _parse_custom_effects(hass.data[DOMAIN][DATA_CUSTOM_EFFECTS])
|
||||||
|
|
||||||
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]
|
||||||
@ -287,124 +268,114 @@ async def async_setup_entry(
|
|||||||
_lights_setup_helper(YeelightGenericLight)
|
_lights_setup_helper(YeelightGenericLight)
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Cannot determine device type for %s, %s. Falling back to white only",
|
"Cannot determine device type for %s, %s. Falling back to white only",
|
||||||
device.ipaddr,
|
device.host,
|
||||||
device.name,
|
device.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.data[PLATFORM_DATA_KEY] += lights
|
|
||||||
async_add_entities(lights, True)
|
async_add_entities(lights, True)
|
||||||
await hass.async_add_executor_job(partial(setup_services, hass))
|
_async_setup_services(hass)
|
||||||
|
|
||||||
|
|
||||||
def setup_services(hass):
|
@callback
|
||||||
"""Set up the service listeners."""
|
def _async_setup_services(hass: HomeAssistant):
|
||||||
|
"""Set up custom services."""
|
||||||
|
|
||||||
def service_call(func):
|
async def _async_start_flow(entity, service_call):
|
||||||
def service_to_entities(service):
|
params = {**service_call.data}
|
||||||
"""Return the known entities that a service call mentions."""
|
params.pop(ATTR_ENTITY_ID)
|
||||||
|
|
||||||
entity_ids = extract_entity_ids(hass, service)
|
|
||||||
target_devices = [
|
|
||||||
light
|
|
||||||
for light in hass.data[PLATFORM_DATA_KEY]
|
|
||||||
if light.entity_id in entity_ids
|
|
||||||
]
|
|
||||||
|
|
||||||
return target_devices
|
|
||||||
|
|
||||||
def service_to_params(service):
|
|
||||||
"""Return service call params, without entity_id."""
|
|
||||||
return {
|
|
||||||
key: value
|
|
||||||
for key, value in service.data.items()
|
|
||||||
if key != ATTR_ENTITY_ID
|
|
||||||
}
|
|
||||||
|
|
||||||
def wrapper(service):
|
|
||||||
params = service_to_params(service)
|
|
||||||
target_devices = service_to_entities(service)
|
|
||||||
for device in target_devices:
|
|
||||||
func(device, params)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
@service_call
|
|
||||||
def service_set_mode(target_device, params):
|
|
||||||
target_device.set_mode(**params)
|
|
||||||
|
|
||||||
@service_call
|
|
||||||
def service_start_flow(target_devices, params):
|
|
||||||
params[ATTR_TRANSITIONS] = _transitions_config_parser(params[ATTR_TRANSITIONS])
|
params[ATTR_TRANSITIONS] = _transitions_config_parser(params[ATTR_TRANSITIONS])
|
||||||
target_devices.start_flow(**params)
|
await hass.async_add_executor_job(partial(entity.start_flow, **params))
|
||||||
|
|
||||||
@service_call
|
async def _async_set_color_scene(entity, service_call):
|
||||||
def service_set_color_scene(target_device, params):
|
await hass.async_add_executor_job(
|
||||||
target_device.set_scene(
|
partial(
|
||||||
SceneClass.COLOR, *[*params[ATTR_RGB_COLOR], params[ATTR_BRIGHTNESS]]
|
entity.set_scene,
|
||||||
|
SceneClass.COLOR,
|
||||||
|
*service_call.data[ATTR_RGB_COLOR],
|
||||||
|
service_call.data[ATTR_BRIGHTNESS],
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@service_call
|
async def _async_set_hsv_scene(entity, service_call):
|
||||||
def service_set_hsv_scene(target_device, params):
|
await hass.async_add_executor_job(
|
||||||
target_device.set_scene(
|
partial(
|
||||||
SceneClass.HSV, *[*params[ATTR_HS_COLOR], params[ATTR_BRIGHTNESS]]
|
entity.set_scene,
|
||||||
|
SceneClass.HSV,
|
||||||
|
*service_call.data[ATTR_HS_COLOR],
|
||||||
|
service_call.data[ATTR_BRIGHTNESS],
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@service_call
|
async def _async_set_color_temp_scene(entity, service_call):
|
||||||
def service_set_color_temp_scene(target_device, params):
|
await hass.async_add_executor_job(
|
||||||
target_device.set_scene(
|
partial(
|
||||||
SceneClass.CT, params[ATTR_KELVIN], params[ATTR_BRIGHTNESS]
|
entity.set_scene,
|
||||||
|
SceneClass.CT,
|
||||||
|
service_call.data[ATTR_KELVIN],
|
||||||
|
service_call.data[ATTR_BRIGHTNESS],
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@service_call
|
async def _async_set_color_flow_scene(entity, service_call):
|
||||||
def service_set_color_flow_scene(target_device, params):
|
|
||||||
flow = Flow(
|
flow = Flow(
|
||||||
count=params[ATTR_COUNT],
|
count=service_call.data[ATTR_COUNT],
|
||||||
action=Flow.actions[params[ATTR_ACTION]],
|
action=Flow.actions[service_call.data[ATTR_ACTION]],
|
||||||
transitions=_transitions_config_parser(params[ATTR_TRANSITIONS]),
|
transitions=_transitions_config_parser(service_call.data[ATTR_TRANSITIONS]),
|
||||||
|
)
|
||||||
|
await hass.async_add_executor_job(
|
||||||
|
partial(
|
||||||
|
entity.set_scene,
|
||||||
|
SceneClass.CF,
|
||||||
|
flow,
|
||||||
)
|
)
|
||||||
target_device.set_scene(SceneClass.CF, flow)
|
|
||||||
|
|
||||||
@service_call
|
|
||||||
def service_set_auto_delay_off_scene(target_device, params):
|
|
||||||
target_device.set_scene(
|
|
||||||
SceneClass.AUTO_DELAY_OFF, params[ATTR_BRIGHTNESS], params[ATTR_MINUTES]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.services.register(
|
async def _async_set_auto_delay_off_scene(entity, service_call):
|
||||||
DOMAIN, SERVICE_SET_MODE, service_set_mode, schema=SERVICE_SCHEMA_SET_MODE
|
await hass.async_add_executor_job(
|
||||||
|
partial(
|
||||||
|
entity.set_scene,
|
||||||
|
SceneClass.AUTO_DELAY_OFF,
|
||||||
|
service_call.data[ATTR_BRIGHTNESS],
|
||||||
|
service_call.data[ATTR_MINUTES],
|
||||||
)
|
)
|
||||||
hass.services.register(
|
|
||||||
DOMAIN, SERVICE_START_FLOW, service_start_flow, schema=SERVICE_SCHEMA_START_FLOW
|
|
||||||
)
|
)
|
||||||
hass.services.register(
|
|
||||||
DOMAIN,
|
platform = entity_platform.current_platform.get()
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_SET_MODE,
|
||||||
|
SERVICE_SCHEMA_SET_MODE,
|
||||||
|
"set_mode",
|
||||||
|
)
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_START_FLOW,
|
||||||
|
SERVICE_SCHEMA_START_FLOW,
|
||||||
|
_async_start_flow,
|
||||||
|
)
|
||||||
|
platform.async_register_entity_service(
|
||||||
SERVICE_SET_COLOR_SCENE,
|
SERVICE_SET_COLOR_SCENE,
|
||||||
service_set_color_scene,
|
SERVICE_SCHEMA_SET_COLOR_SCENE,
|
||||||
schema=SERVICE_SCHEMA_SET_COLOR_SCENE,
|
_async_set_color_scene,
|
||||||
)
|
)
|
||||||
hass.services.register(
|
platform.async_register_entity_service(
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_HSV_SCENE,
|
SERVICE_SET_HSV_SCENE,
|
||||||
service_set_hsv_scene,
|
SERVICE_SCHEMA_SET_HSV_SCENE,
|
||||||
schema=SERVICE_SCHEMA_SET_HSV_SCENE,
|
_async_set_hsv_scene,
|
||||||
)
|
)
|
||||||
hass.services.register(
|
platform.async_register_entity_service(
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_COLOR_TEMP_SCENE,
|
SERVICE_SET_COLOR_TEMP_SCENE,
|
||||||
service_set_color_temp_scene,
|
SERVICE_SCHEMA_SET_COLOR_TEMP_SCENE,
|
||||||
schema=SERVICE_SCHEMA_SET_COLOR_TEMP_SCENE,
|
_async_set_color_temp_scene,
|
||||||
)
|
)
|
||||||
hass.services.register(
|
platform.async_register_entity_service(
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_COLOR_FLOW_SCENE,
|
SERVICE_SET_COLOR_FLOW_SCENE,
|
||||||
service_set_color_flow_scene,
|
SERVICE_SCHEMA_SET_COLOR_FLOW_SCENE,
|
||||||
schema=SERVICE_SCHEMA_SET_COLOR_FLOW_SCENE,
|
_async_set_color_flow_scene,
|
||||||
)
|
)
|
||||||
hass.services.register(
|
platform.async_register_entity_service(
|
||||||
DOMAIN,
|
|
||||||
SERVICE_SET_AUTO_DELAY_OFF_SCENE,
|
SERVICE_SET_AUTO_DELAY_OFF_SCENE,
|
||||||
service_set_auto_delay_off_scene,
|
SERVICE_SCHEMA_SET_AUTO_DELAY_OFF_SCENE,
|
||||||
schema=SERVICE_SCHEMA_SET_AUTO_DELAY_OFF,
|
_async_set_auto_delay_off_scene,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -442,7 +413,7 @@ class YeelightGenericLight(YeelightEntity, LightEntity):
|
|||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
async_dispatcher_connect(
|
async_dispatcher_connect(
|
||||||
self.hass,
|
self.hass,
|
||||||
DATA_UPDATED.format(self._device.ipaddr),
|
DATA_UPDATED.format(self._device.host),
|
||||||
self._schedule_immediate_update,
|
self._schedule_immediate_update,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"description": "If you leave IP address empty, discovery will be used to find devices.",
|
"description": "If you leave the host empty, discovery will be used to find devices.",
|
||||||
"data": {
|
"data": {
|
||||||
"ip_address": "[%key:common::config_flow::data::ip%]"
|
"host": "[%key:common::config_flow::data::host%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pick_device": {
|
"pick_device": {
|
||||||
|
@ -16,7 +16,7 @@ from homeassistant.components.yeelight import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
|
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_ID, CONF_IP_ADDRESS, CONF_NAME
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
@ -112,7 +112,7 @@ async def test_import(hass: HomeAssistant):
|
|||||||
"""Test import from yaml."""
|
"""Test import from yaml."""
|
||||||
config = {
|
config = {
|
||||||
CONF_NAME: DEFAULT_NAME,
|
CONF_NAME: DEFAULT_NAME,
|
||||||
CONF_IP_ADDRESS: IP_ADDRESS,
|
CONF_HOST: IP_ADDRESS,
|
||||||
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,
|
||||||
@ -145,7 +145,7 @@ async def test_import(hass: HomeAssistant):
|
|||||||
assert result["title"] == DEFAULT_NAME
|
assert result["title"] == DEFAULT_NAME
|
||||||
assert result["data"] == {
|
assert result["data"] == {
|
||||||
CONF_NAME: DEFAULT_NAME,
|
CONF_NAME: DEFAULT_NAME,
|
||||||
CONF_IP_ADDRESS: IP_ADDRESS,
|
CONF_HOST: IP_ADDRESS,
|
||||||
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,
|
||||||
@ -178,7 +178,7 @@ async def test_manual(hass: HomeAssistant):
|
|||||||
mocked_bulb = _mocked_bulb(cannot_connect=True)
|
mocked_bulb = _mocked_bulb(cannot_connect=True)
|
||||||
with patch(f"{MODULE_CONFIG_FLOW}.yeelight.Bulb", return_value=mocked_bulb):
|
with patch(f"{MODULE_CONFIG_FLOW}.yeelight.Bulb", return_value=mocked_bulb):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], {CONF_IP_ADDRESS: IP_ADDRESS}
|
result["flow_id"], {CONF_HOST: IP_ADDRESS}
|
||||||
)
|
)
|
||||||
assert result2["type"] == "form"
|
assert result2["type"] == "form"
|
||||||
assert result2["step_id"] == "user"
|
assert result2["step_id"] == "user"
|
||||||
@ -188,7 +188,7 @@ async def test_manual(hass: HomeAssistant):
|
|||||||
type(mocked_bulb).get_capabilities = MagicMock(side_effect=OSError)
|
type(mocked_bulb).get_capabilities = MagicMock(side_effect=OSError)
|
||||||
with patch(f"{MODULE_CONFIG_FLOW}.yeelight.Bulb", return_value=mocked_bulb):
|
with patch(f"{MODULE_CONFIG_FLOW}.yeelight.Bulb", return_value=mocked_bulb):
|
||||||
result3 = await hass.config_entries.flow.async_configure(
|
result3 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], {CONF_IP_ADDRESS: IP_ADDRESS}
|
result["flow_id"], {CONF_HOST: IP_ADDRESS}
|
||||||
)
|
)
|
||||||
assert result3["errors"] == {"base": "cannot_connect"}
|
assert result3["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
@ -201,10 +201,10 @@ async def test_manual(hass: HomeAssistant):
|
|||||||
return_value=True,
|
return_value=True,
|
||||||
):
|
):
|
||||||
result4 = await hass.config_entries.flow.async_configure(
|
result4 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], {CONF_IP_ADDRESS: IP_ADDRESS}
|
result["flow_id"], {CONF_HOST: IP_ADDRESS}
|
||||||
)
|
)
|
||||||
assert result4["type"] == "create_entry"
|
assert result4["type"] == "create_entry"
|
||||||
assert result4["data"] == {CONF_IP_ADDRESS: IP_ADDRESS}
|
assert result4["data"] == {CONF_HOST: IP_ADDRESS}
|
||||||
|
|
||||||
# Duplicate
|
# Duplicate
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
@ -213,7 +213,7 @@ async def test_manual(hass: HomeAssistant):
|
|||||||
mocked_bulb = _mocked_bulb()
|
mocked_bulb = _mocked_bulb()
|
||||||
with patch(f"{MODULE_CONFIG_FLOW}.yeelight.Bulb", return_value=mocked_bulb):
|
with patch(f"{MODULE_CONFIG_FLOW}.yeelight.Bulb", return_value=mocked_bulb):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], {CONF_IP_ADDRESS: IP_ADDRESS}
|
result["flow_id"], {CONF_HOST: IP_ADDRESS}
|
||||||
)
|
)
|
||||||
assert result2["type"] == "abort"
|
assert result2["type"] == "abort"
|
||||||
assert result2["reason"] == "already_configured"
|
assert result2["reason"] == "already_configured"
|
||||||
@ -221,7 +221,7 @@ 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_IP_ADDRESS: IP_ADDRESS})
|
config_entry = MockConfigEntry(domain=DOMAIN, data={CONF_HOST: IP_ADDRESS})
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
mocked_bulb = _mocked_bulb()
|
mocked_bulb = _mocked_bulb()
|
||||||
|
@ -71,7 +71,7 @@ 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_ID, CONF_IP_ADDRESS, CONF_NAME
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_ID, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util.color import (
|
from homeassistant.util.color import (
|
||||||
@ -104,7 +104,7 @@ async def test_services(hass: HomeAssistant, caplog):
|
|||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
CONF_ID: "",
|
CONF_ID: "",
|
||||||
CONF_IP_ADDRESS: IP_ADDRESS,
|
CONF_HOST: IP_ADDRESS,
|
||||||
CONF_TRANSITION: DEFAULT_TRANSITION,
|
CONF_TRANSITION: DEFAULT_TRANSITION,
|
||||||
CONF_MODE_MUSIC: True,
|
CONF_MODE_MUSIC: True,
|
||||||
CONF_SAVE_ON_CHANGE: True,
|
CONF_SAVE_ON_CHANGE: True,
|
||||||
@ -306,7 +306,7 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
CONF_ID: "",
|
CONF_ID: "",
|
||||||
CONF_IP_ADDRESS: IP_ADDRESS,
|
CONF_HOST: IP_ADDRESS,
|
||||||
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,
|
||||||
@ -337,7 +337,7 @@ async def test_device_types(hass: HomeAssistant):
|
|||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
CONF_ID: "",
|
CONF_ID: "",
|
||||||
CONF_IP_ADDRESS: IP_ADDRESS,
|
CONF_HOST: IP_ADDRESS,
|
||||||
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,
|
||||||
@ -520,7 +520,7 @@ async def test_effects(hass: HomeAssistant):
|
|||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
CONF_ID: "",
|
CONF_ID: "",
|
||||||
CONF_IP_ADDRESS: IP_ADDRESS,
|
CONF_HOST: IP_ADDRESS,
|
||||||
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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user