mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Add yeelight start_flow service and ability to declare custom effects (#20107)
This commit is contained in:
parent
0be922dc9c
commit
2559bc4226
@ -192,3 +192,16 @@ yeelight_set_mode:
|
|||||||
mode:
|
mode:
|
||||||
description: Operation mode. Valid values are 'last', 'normal', 'rgb', 'hsv', 'color_flow', 'moonlight'.
|
description: Operation mode. Valid values are 'last', 'normal', 'rgb', 'hsv', 'color_flow', 'moonlight'.
|
||||||
example: 'moonlight'
|
example: 'moonlight'
|
||||||
|
|
||||||
|
yeelight_start_flow:
|
||||||
|
description: Start a custom flow, using transitions from https://yeelight.readthedocs.io/en/stable/yeelight.html#flow-objects
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: Name of the light entity.
|
||||||
|
example: 'light.yeelight'
|
||||||
|
count:
|
||||||
|
description: The number of times to run this flow (0 to run forever).
|
||||||
|
example: 0
|
||||||
|
transitions:
|
||||||
|
description: Array of transitions, for desired effect. Examples https://yeelight.readthedocs.io/en/stable/flow.html
|
||||||
|
example: '[{ "TemperatureTransition": [1900, 1000, 80] }, { "TemperatureTransition": [1900, 1000, 10] }]'
|
||||||
|
@ -39,15 +39,48 @@ CONF_MODEL = 'model'
|
|||||||
CONF_TRANSITION = 'transition'
|
CONF_TRANSITION = 'transition'
|
||||||
CONF_SAVE_ON_CHANGE = 'save_on_change'
|
CONF_SAVE_ON_CHANGE = 'save_on_change'
|
||||||
CONF_MODE_MUSIC = 'use_music_mode'
|
CONF_MODE_MUSIC = 'use_music_mode'
|
||||||
|
CONF_CUSTOM_EFFECTS = 'custom_effects'
|
||||||
|
CONF_FLOW_PARAMS = 'flow_params'
|
||||||
|
|
||||||
DATA_KEY = 'light.yeelight'
|
DATA_KEY = 'light.yeelight'
|
||||||
|
|
||||||
|
ATTR_MODE = 'mode'
|
||||||
|
ATTR_COUNT = 'count'
|
||||||
|
ATTR_TRANSITIONS = 'transitions'
|
||||||
|
|
||||||
|
YEELIGHT_RGB_TRANSITION = 'RGBTransition'
|
||||||
|
YEELIGHT_HSV_TRANSACTION = 'HSVTransition'
|
||||||
|
YEELIGHT_TEMPERATURE_TRANSACTION = 'TemperatureTransition'
|
||||||
|
YEELIGHT_SLEEP_TRANSACTION = 'SleepTransition'
|
||||||
|
|
||||||
|
YEELIGHT_SERVICE_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
YEELIGHT_FLOW_TRANSITION_SCHEMA = {
|
||||||
|
vol.Optional(ATTR_COUNT, default=0): cv.positive_int,
|
||||||
|
vol.Required(ATTR_TRANSITIONS): [{
|
||||||
|
vol.Exclusive(YEELIGHT_RGB_TRANSITION, CONF_TRANSITION):
|
||||||
|
vol.All(cv.ensure_list, [cv.positive_int]),
|
||||||
|
vol.Exclusive(YEELIGHT_HSV_TRANSACTION, CONF_TRANSITION):
|
||||||
|
vol.All(cv.ensure_list, [cv.positive_int]),
|
||||||
|
vol.Exclusive(YEELIGHT_TEMPERATURE_TRANSACTION, CONF_TRANSITION):
|
||||||
|
vol.All(cv.ensure_list, [cv.positive_int]),
|
||||||
|
vol.Exclusive(YEELIGHT_SLEEP_TRANSACTION, CONF_TRANSITION):
|
||||||
|
vol.All(cv.ensure_list, [cv.positive_int]),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
DEVICE_SCHEMA = vol.Schema({
|
DEVICE_SCHEMA = vol.Schema({
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_TRANSITION, default=DEFAULT_TRANSITION): cv.positive_int,
|
vol.Optional(CONF_TRANSITION, default=DEFAULT_TRANSITION): cv.positive_int,
|
||||||
vol.Optional(CONF_MODE_MUSIC, default=False): cv.boolean,
|
vol.Optional(CONF_MODE_MUSIC, default=False): cv.boolean,
|
||||||
vol.Optional(CONF_SAVE_ON_CHANGE, default=False): cv.boolean,
|
vol.Optional(CONF_SAVE_ON_CHANGE, default=False): cv.boolean,
|
||||||
vol.Optional(CONF_MODEL): cv.string,
|
vol.Optional(CONF_MODEL): cv.string,
|
||||||
|
vol.Optional(CONF_CUSTOM_EFFECTS): [{
|
||||||
|
vol.Required(CONF_NAME): cv.string,
|
||||||
|
vol.Required(CONF_FLOW_PARAMS): YEELIGHT_FLOW_TRANSITION_SCHEMA
|
||||||
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
@ -103,11 +136,7 @@ YEELIGHT_EFFECT_LIST = [
|
|||||||
EFFECT_STOP]
|
EFFECT_STOP]
|
||||||
|
|
||||||
SERVICE_SET_MODE = 'yeelight_set_mode'
|
SERVICE_SET_MODE = 'yeelight_set_mode'
|
||||||
ATTR_MODE = 'mode'
|
SERVICE_START_FLOW = 'yeelight_start_flow'
|
||||||
|
|
||||||
YEELIGHT_SERVICE_SCHEMA = vol.Schema({
|
|
||||||
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def _cmd(func):
|
def _cmd(func):
|
||||||
@ -123,6 +152,19 @@ def _cmd(func):
|
|||||||
return _wrap
|
return _wrap
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_custom_effects(effects_config):
|
||||||
|
effects = {}
|
||||||
|
for config in effects_config:
|
||||||
|
params = config[CONF_FLOW_PARAMS]
|
||||||
|
transitions = YeelightLight.transitions_config_parser(
|
||||||
|
params[ATTR_TRANSITIONS])
|
||||||
|
|
||||||
|
effects[config[CONF_NAME]] = \
|
||||||
|
{ATTR_COUNT: params[ATTR_COUNT], ATTR_TRANSITIONS: transitions}
|
||||||
|
|
||||||
|
return effects
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
"""Set up the Yeelight bulbs."""
|
"""Set up the Yeelight bulbs."""
|
||||||
from yeelight.enums import PowerMode
|
from yeelight.enums import PowerMode
|
||||||
@ -151,8 +193,10 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
name = device_config[CONF_NAME]
|
name = device_config[CONF_NAME]
|
||||||
_LOGGER.debug("Adding configured %s", name)
|
_LOGGER.debug("Adding configured %s", name)
|
||||||
|
|
||||||
|
custom_effects = _parse_custom_effects(config[CONF_CUSTOM_EFFECTS])
|
||||||
device = {'name': name, 'ipaddr': ipaddr}
|
device = {'name': name, 'ipaddr': ipaddr}
|
||||||
light = YeelightLight(device, device_config)
|
light = YeelightLight(device, device_config,
|
||||||
|
custom_effects=custom_effects)
|
||||||
lights.append(light)
|
lights.append(light)
|
||||||
hass.data[DATA_KEY][name] = light
|
hass.data[DATA_KEY][name] = light
|
||||||
|
|
||||||
@ -163,15 +207,14 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
params = {key: value for key, value in service.data.items()
|
params = {key: value for key, value in service.data.items()
|
||||||
if key != ATTR_ENTITY_ID}
|
if key != ATTR_ENTITY_ID}
|
||||||
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
entity_ids = service.data.get(ATTR_ENTITY_ID)
|
||||||
if entity_ids:
|
|
||||||
target_devices = [dev for dev in hass.data[DATA_KEY].values()
|
target_devices = [dev for dev in hass.data[DATA_KEY].values()
|
||||||
if dev.entity_id in entity_ids]
|
if dev.entity_id in entity_ids]
|
||||||
else:
|
|
||||||
target_devices = hass.data[DATA_KEY].values()
|
|
||||||
|
|
||||||
for target_device in target_devices:
|
for target_device in target_devices:
|
||||||
if service.service == SERVICE_SET_MODE:
|
if service.service == SERVICE_SET_MODE:
|
||||||
target_device.set_mode(**params)
|
target_device.set_mode(**params)
|
||||||
|
elif service.service == SERVICE_START_FLOW:
|
||||||
|
target_device.start_flow(**params)
|
||||||
|
|
||||||
service_schema_set_mode = YEELIGHT_SERVICE_SCHEMA.extend({
|
service_schema_set_mode = YEELIGHT_SERVICE_SCHEMA.extend({
|
||||||
vol.Required(ATTR_MODE):
|
vol.Required(ATTR_MODE):
|
||||||
@ -181,11 +224,18 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||||||
DOMAIN, SERVICE_SET_MODE, service_handler,
|
DOMAIN, SERVICE_SET_MODE, service_handler,
|
||||||
schema=service_schema_set_mode)
|
schema=service_schema_set_mode)
|
||||||
|
|
||||||
|
service_schema_start_flow = YEELIGHT_SERVICE_SCHEMA.extend(
|
||||||
|
YEELIGHT_FLOW_TRANSITION_SCHEMA
|
||||||
|
)
|
||||||
|
hass.services.register(
|
||||||
|
DOMAIN, SERVICE_START_FLOW, service_handler,
|
||||||
|
schema=service_schema_start_flow)
|
||||||
|
|
||||||
|
|
||||||
class YeelightLight(Light):
|
class YeelightLight(Light):
|
||||||
"""Representation of a Yeelight light."""
|
"""Representation of a Yeelight light."""
|
||||||
|
|
||||||
def __init__(self, device, config):
|
def __init__(self, device, config, custom_effects=None):
|
||||||
"""Initialize the Yeelight light."""
|
"""Initialize the Yeelight light."""
|
||||||
self.config = config
|
self.config = config
|
||||||
self._name = device['name']
|
self._name = device['name']
|
||||||
@ -204,6 +254,11 @@ class YeelightLight(Light):
|
|||||||
self._min_mireds = None
|
self._min_mireds = None
|
||||||
self._max_mireds = None
|
self._max_mireds = None
|
||||||
|
|
||||||
|
if custom_effects:
|
||||||
|
self._custom_effects = custom_effects
|
||||||
|
else:
|
||||||
|
self._custom_effects = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return if bulb is available."""
|
"""Return if bulb is available."""
|
||||||
@ -217,7 +272,7 @@ class YeelightLight(Light):
|
|||||||
@property
|
@property
|
||||||
def effect_list(self):
|
def effect_list(self):
|
||||||
"""Return the list of supported effects."""
|
"""Return the list of supported effects."""
|
||||||
return YEELIGHT_EFFECT_LIST
|
return YEELIGHT_EFFECT_LIST + self.custom_effects_names
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color_temp(self) -> int:
|
def color_temp(self) -> int:
|
||||||
@ -249,6 +304,16 @@ class YeelightLight(Light):
|
|||||||
"""Return maximum supported color temperature."""
|
"""Return maximum supported color temperature."""
|
||||||
return self._max_mireds
|
return self._max_mireds
|
||||||
|
|
||||||
|
@property
|
||||||
|
def custom_effects(self):
|
||||||
|
"""Return dict with custom effects."""
|
||||||
|
return self._custom_effects
|
||||||
|
|
||||||
|
@property
|
||||||
|
def custom_effects_names(self):
|
||||||
|
"""Return list with custom effects names."""
|
||||||
|
return list(self.custom_effects.keys())
|
||||||
|
|
||||||
def _get_hs_from_properties(self):
|
def _get_hs_from_properties(self):
|
||||||
rgb = self._properties.get('rgb', None)
|
rgb = self._properties.get('rgb', None)
|
||||||
color_mode = self._properties.get('color_mode', None)
|
color_mode = self._properties.get('color_mode', None)
|
||||||
@ -435,15 +500,17 @@ class YeelightLight(Light):
|
|||||||
EFFECT_SLOWDOWN: slowdown,
|
EFFECT_SLOWDOWN: slowdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
if effect in effects_map:
|
if effect in self.custom_effects_names:
|
||||||
|
flow = Flow(**self.custom_effects[effect])
|
||||||
|
elif effect in effects_map:
|
||||||
flow = Flow(count=0, transitions=effects_map[effect]())
|
flow = Flow(count=0, transitions=effects_map[effect]())
|
||||||
if effect == EFFECT_FAST_RANDOM_LOOP:
|
elif effect == EFFECT_FAST_RANDOM_LOOP:
|
||||||
flow = Flow(count=0, transitions=randomloop(duration=250))
|
flow = Flow(count=0, transitions=randomloop(duration=250))
|
||||||
if effect == EFFECT_WHATSAPP:
|
elif effect == EFFECT_WHATSAPP:
|
||||||
flow = Flow(count=2, transitions=pulse(37, 211, 102))
|
flow = Flow(count=2, transitions=pulse(37, 211, 102))
|
||||||
if effect == EFFECT_FACEBOOK:
|
elif effect == EFFECT_FACEBOOK:
|
||||||
flow = Flow(count=2, transitions=pulse(59, 89, 152))
|
flow = Flow(count=2, transitions=pulse(59, 89, 152))
|
||||||
if effect == EFFECT_TWITTER:
|
elif effect == EFFECT_TWITTER:
|
||||||
flow = Flow(count=2, transitions=pulse(0, 172, 237))
|
flow = Flow(count=2, transitions=pulse(0, 172, 237))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -518,3 +585,28 @@ class YeelightLight(Light):
|
|||||||
self.async_schedule_update_ha_state(True)
|
self.async_schedule_update_ha_state(True)
|
||||||
except yeelight.BulbException as ex:
|
except yeelight.BulbException as ex:
|
||||||
_LOGGER.error("Unable to set the power mode: %s", ex)
|
_LOGGER.error("Unable to set the power mode: %s", ex)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def transitions_config_parser(transitions):
|
||||||
|
"""Parse transitions config into initialized objects."""
|
||||||
|
import yeelight
|
||||||
|
|
||||||
|
transition_objects = []
|
||||||
|
for transition_config in transitions:
|
||||||
|
transition, params = list(transition_config.items())[0]
|
||||||
|
transition_objects.append(getattr(yeelight, transition)(*params))
|
||||||
|
|
||||||
|
return transition_objects
|
||||||
|
|
||||||
|
def start_flow(self, transitions, count=0):
|
||||||
|
"""Start flow."""
|
||||||
|
import yeelight
|
||||||
|
|
||||||
|
try:
|
||||||
|
flow = yeelight.Flow(
|
||||||
|
count=count,
|
||||||
|
transitions=self.transitions_config_parser(transitions))
|
||||||
|
|
||||||
|
self._bulb.start_flow(flow)
|
||||||
|
except yeelight.BulbException as ex:
|
||||||
|
_LOGGER.error("Unable to set effect: %s", ex)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user