Add set_hev_cycle_state service to LIFX integration (#77546)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
23a579d1c9
commit
168d122db4
5 changed files with 135 additions and 15 deletions
|
@ -118,9 +118,6 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
|
|||
await self.async_update_color_zones()
|
||||
|
||||
if lifx_features(self.device)["hev"]:
|
||||
if self.device.hev_cycle_configuration is None:
|
||||
self.device.get_hev_configuration()
|
||||
|
||||
await self.async_get_hev_cycle()
|
||||
|
||||
async def async_update_color_zones(self) -> None:
|
||||
|
@ -195,3 +192,10 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
|
|||
apply=apply,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_set_hev_cycle_state(self, enable: bool, duration: int = 0) -> None:
|
||||
"""Start or stop an HEV cycle on a LIFX Clean bulb."""
|
||||
if lifx_features(self.device)["hev"]:
|
||||
await async_execute_lifx(
|
||||
partial(self.device.set_hev_cycle, enable=enable, duration=duration)
|
||||
)
|
||||
|
|
|
@ -28,7 +28,14 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||
import homeassistant.util.color as color_util
|
||||
|
||||
from .const import ATTR_INFRARED, ATTR_POWER, ATTR_ZONES, DATA_LIFX_MANAGER, DOMAIN
|
||||
from .const import (
|
||||
ATTR_DURATION,
|
||||
ATTR_INFRARED,
|
||||
ATTR_POWER,
|
||||
ATTR_ZONES,
|
||||
DATA_LIFX_MANAGER,
|
||||
DOMAIN,
|
||||
)
|
||||
from .coordinator import LIFXUpdateCoordinator
|
||||
from .entity import LIFXEntity
|
||||
from .manager import (
|
||||
|
@ -43,14 +50,20 @@ LIFX_STATE_SETTLE_DELAY = 0.3
|
|||
|
||||
SERVICE_LIFX_SET_STATE = "set_state"
|
||||
|
||||
LIFX_SET_STATE_SCHEMA = cv.make_entity_service_schema(
|
||||
{
|
||||
**LIGHT_TURN_ON_SCHEMA,
|
||||
ATTR_INFRARED: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=255)),
|
||||
ATTR_ZONES: vol.All(cv.ensure_list, [cv.positive_int]),
|
||||
ATTR_POWER: cv.boolean,
|
||||
}
|
||||
)
|
||||
LIFX_SET_STATE_SCHEMA = {
|
||||
**LIGHT_TURN_ON_SCHEMA,
|
||||
ATTR_INFRARED: vol.All(vol.Coerce(int), vol.Clamp(min=0, max=255)),
|
||||
ATTR_ZONES: vol.All(cv.ensure_list, [cv.positive_int]),
|
||||
ATTR_POWER: cv.boolean,
|
||||
}
|
||||
|
||||
|
||||
SERVICE_LIFX_SET_HEV_CYCLE_STATE = "set_hev_cycle_state"
|
||||
|
||||
LIFX_SET_HEV_CYCLE_STATE_SCHEMA = {
|
||||
ATTR_POWER: vol.Required(cv.boolean),
|
||||
ATTR_DURATION: vol.All(vol.Coerce(float), vol.Clamp(min=0, max=86400)),
|
||||
}
|
||||
|
||||
HSBK_HUE = 0
|
||||
HSBK_SATURATION = 1
|
||||
|
@ -74,6 +87,11 @@ async def async_setup_entry(
|
|||
LIFX_SET_STATE_SCHEMA,
|
||||
"set_state",
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_LIFX_SET_HEV_CYCLE_STATE,
|
||||
LIFX_SET_HEV_CYCLE_STATE_SCHEMA,
|
||||
"set_hev_cycle_state",
|
||||
)
|
||||
if lifx_features(device)["multizone"]:
|
||||
entity: LIFXLight = LIFXStrip(coordinator, manager, entry)
|
||||
elif lifx_features(device)["color"]:
|
||||
|
@ -237,6 +255,18 @@ class LIFXLight(LIFXEntity, LightEntity):
|
|||
# Update when the transition starts and ends
|
||||
await self.update_during_transition(fade)
|
||||
|
||||
async def set_hev_cycle_state(
|
||||
self, power: bool, duration: int | None = None
|
||||
) -> None:
|
||||
"""Set the state of the HEV LEDs on a LIFX Clean bulb."""
|
||||
if lifx_features(self.bulb)["hev"] is False:
|
||||
raise HomeAssistantError(
|
||||
"This device does not support setting HEV cycle state"
|
||||
)
|
||||
|
||||
await self.coordinator.async_set_hev_cycle_state(power, duration or 0)
|
||||
await self.update_during_transition(duration or 0)
|
||||
|
||||
async def set_power(
|
||||
self,
|
||||
pwr: bool,
|
||||
|
|
|
@ -1,3 +1,29 @@
|
|||
set_hev_cycle_state:
|
||||
name: Set HEV cycle state
|
||||
description: Control the HEV LEDs on a LIFX Clean bulb.
|
||||
target:
|
||||
entity:
|
||||
integration: lifx
|
||||
domain: light
|
||||
fields:
|
||||
power:
|
||||
name: enable
|
||||
description: Start or stop a Clean cycle.
|
||||
required: true
|
||||
example: true
|
||||
selector:
|
||||
boolean:
|
||||
duration:
|
||||
name: Duration
|
||||
description: How long the HEV LEDs will remain on. Uses the configured default duration if not specified.
|
||||
required: false
|
||||
default: 7200
|
||||
example: 3600
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
max: 86400
|
||||
unit_of_measurement: seconds
|
||||
set_state:
|
||||
name: Set State
|
||||
description: Set a color/brightness and possibly turn the light on/off.
|
||||
|
|
|
@ -118,9 +118,9 @@ def _mocked_brightness_bulb() -> Light:
|
|||
|
||||
def _mocked_clean_bulb() -> Light:
|
||||
bulb = _mocked_bulb()
|
||||
bulb.get_hev_cycle = MockLifxCommand(
|
||||
bulb, duration=7200, remaining=0, last_power=False
|
||||
)
|
||||
bulb.get_hev_cycle = MockLifxCommand(bulb)
|
||||
bulb.set_hev_cycle = MockLifxCommand(bulb)
|
||||
bulb.hev_cycle_configuration = {"duration": 7200, "indication": False}
|
||||
bulb.hev_cycle = {
|
||||
"duration": 7200,
|
||||
"remaining": 30,
|
||||
|
|
|
@ -8,6 +8,7 @@ import pytest
|
|||
|
||||
from homeassistant.components import lifx
|
||||
from homeassistant.components.lifx import DOMAIN
|
||||
from homeassistant.components.lifx.const import ATTR_POWER
|
||||
from homeassistant.components.lifx.light import ATTR_INFRARED, ATTR_ZONES
|
||||
from homeassistant.components.lifx.manager import SERVICE_EFFECT_COLORLOOP
|
||||
from homeassistant.components.light import (
|
||||
|
@ -40,6 +41,7 @@ from . import (
|
|||
_mocked_brightness_bulb,
|
||||
_mocked_bulb,
|
||||
_mocked_bulb_new_firmware,
|
||||
_mocked_clean_bulb,
|
||||
_mocked_light_strip,
|
||||
_mocked_white_bulb,
|
||||
_patch_config_flow_try_connect,
|
||||
|
@ -997,3 +999,61 @@ async def test_color_bulb_is_actually_off(hass: HomeAssistant) -> None:
|
|||
)
|
||||
assert bulb.set_color.calls[0][0][0] == [0, 0, 25700, 3500]
|
||||
assert len(bulb.set_power.calls) == 1
|
||||
|
||||
|
||||
async def test_clean_bulb(hass: HomeAssistant) -> None:
|
||||
"""Test setting HEV cycle state on Clean bulbs."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=SERIAL
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
bulb = _mocked_clean_bulb()
|
||||
bulb.power_level = 0
|
||||
bulb.hev_cycle = {"duration": 7200, "remaining": 0, "last_power": False}
|
||||
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
|
||||
device=bulb
|
||||
), _patch_device(device=bulb):
|
||||
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_id = "light.my_bulb"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "off"
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"set_hev_cycle_state",
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_POWER: True},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
call_dict = bulb.set_hev_cycle.calls[0][1]
|
||||
call_dict.pop("callb")
|
||||
assert call_dict == {"duration": 0, "enable": True}
|
||||
bulb.set_hev_cycle.reset_mock()
|
||||
|
||||
|
||||
async def test_set_hev_cycle_state_fails_for_color_bulb(hass: HomeAssistant) -> None:
|
||||
"""Test that set_hev_cycle_state fails for a non-Clean bulb."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=SERIAL
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
bulb = _mocked_bulb()
|
||||
bulb.power_level = 0
|
||||
with _patch_discovery(device=bulb), _patch_config_flow_try_connect(
|
||||
device=bulb
|
||||
), _patch_device(device=bulb):
|
||||
await async_setup_component(hass, lifx.DOMAIN, {lifx.DOMAIN: {}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_id = "light.my_bulb"
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "off"
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"set_hev_cycle_state",
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_POWER: True},
|
||||
blocking=True,
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue