mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Enable the move firmware effect on multizone lights (#78918)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
75104159c6
commit
691028dfb4
@ -3,10 +3,11 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from enum import IntEnum
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from aiolifx.aiolifx import Light
|
from aiolifx.aiolifx import Light, MultiZoneDirection, MultiZoneEffectType
|
||||||
from aiolifx.connection import LIFXConnection
|
from aiolifx.connection import LIFXConnection
|
||||||
|
|
||||||
from homeassistant.const import Platform
|
from homeassistant.const import Platform
|
||||||
@ -37,6 +38,15 @@ REQUEST_REFRESH_DELAY = 0.35
|
|||||||
LIFX_IDENTIFY_DELAY = 3.0
|
LIFX_IDENTIFY_DELAY = 3.0
|
||||||
|
|
||||||
|
|
||||||
|
class FirmwareEffect(IntEnum):
|
||||||
|
"""Enumeration of LIFX firmware effects."""
|
||||||
|
|
||||||
|
OFF = 0
|
||||||
|
MOVE = 1
|
||||||
|
MORPH = 2
|
||||||
|
FLAME = 3
|
||||||
|
|
||||||
|
|
||||||
class LIFXUpdateCoordinator(DataUpdateCoordinator):
|
class LIFXUpdateCoordinator(DataUpdateCoordinator):
|
||||||
"""DataUpdateCoordinator to gather data for a specific lifx device."""
|
"""DataUpdateCoordinator to gather data for a specific lifx device."""
|
||||||
|
|
||||||
@ -51,7 +61,9 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.device: Light = connection.device
|
self.device: Light = connection.device
|
||||||
self.lock = asyncio.Lock()
|
self.lock = asyncio.Lock()
|
||||||
|
self.active_effect = FirmwareEffect.OFF
|
||||||
update_interval = timedelta(seconds=10)
|
update_interval = timedelta(seconds=10)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
@ -139,6 +151,7 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
# Update model-specific configuration
|
# Update model-specific configuration
|
||||||
if lifx_features(self.device)["multizone"]:
|
if lifx_features(self.device)["multizone"]:
|
||||||
await self.async_update_color_zones()
|
await self.async_update_color_zones()
|
||||||
|
await self.async_update_multizone_effect()
|
||||||
|
|
||||||
if lifx_features(self.device)["hev"]:
|
if lifx_features(self.device)["hev"]:
|
||||||
await self.async_get_hev_cycle()
|
await self.async_get_hev_cycle()
|
||||||
@ -219,6 +232,33 @@ class LIFXUpdateCoordinator(DataUpdateCoordinator):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_update_multizone_effect(self) -> None:
|
||||||
|
"""Update the device firmware effect running state."""
|
||||||
|
await async_execute_lifx(self.device.get_multizone_effect)
|
||||||
|
self.active_effect = FirmwareEffect[self.device.effect.get("effect", "OFF")]
|
||||||
|
|
||||||
|
async def async_set_multizone_effect(
|
||||||
|
self, effect: str, speed: float, direction: str, power_on: bool = True
|
||||||
|
) -> None:
|
||||||
|
"""Control the firmware-based Move effect on a multizone device."""
|
||||||
|
if lifx_features(self.device)["multizone"] is True:
|
||||||
|
if power_on and self.device.power_level == 0:
|
||||||
|
await self.async_set_power(True, 0)
|
||||||
|
|
||||||
|
await async_execute_lifx(
|
||||||
|
partial(
|
||||||
|
self.device.set_multizone_effect,
|
||||||
|
effect=MultiZoneEffectType[effect.upper()].value,
|
||||||
|
speed=speed,
|
||||||
|
direction=MultiZoneDirection[direction.upper()].value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.active_effect = FirmwareEffect[effect.upper()]
|
||||||
|
|
||||||
|
def async_get_active_effect(self) -> int:
|
||||||
|
"""Return the enum value of the currently active firmware effect."""
|
||||||
|
return self.active_effect.value
|
||||||
|
|
||||||
async def async_set_hev_cycle_state(self, enable: bool, duration: int = 0) -> None:
|
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."""
|
"""Start or stop an HEV cycle on a LIFX Clean bulb."""
|
||||||
if lifx_features(self.device)["hev"]:
|
if lifx_features(self.device)["hev"]:
|
||||||
|
@ -38,10 +38,11 @@ from .const import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
INFRARED_BRIGHTNESS,
|
INFRARED_BRIGHTNESS,
|
||||||
)
|
)
|
||||||
from .coordinator import LIFXUpdateCoordinator
|
from .coordinator import FirmwareEffect, LIFXUpdateCoordinator
|
||||||
from .entity import LIFXEntity
|
from .entity import LIFXEntity
|
||||||
from .manager import (
|
from .manager import (
|
||||||
SERVICE_EFFECT_COLORLOOP,
|
SERVICE_EFFECT_COLORLOOP,
|
||||||
|
SERVICE_EFFECT_MOVE,
|
||||||
SERVICE_EFFECT_PULSE,
|
SERVICE_EFFECT_PULSE,
|
||||||
SERVICE_EFFECT_STOP,
|
SERVICE_EFFECT_STOP,
|
||||||
LIFXManager,
|
LIFXManager,
|
||||||
@ -139,6 +140,7 @@ class LIFXLight(LIFXEntity, LightEntity):
|
|||||||
color_mode = ColorMode.BRIGHTNESS
|
color_mode = ColorMode.BRIGHTNESS
|
||||||
self._attr_color_mode = color_mode
|
self._attr_color_mode = color_mode
|
||||||
self._attr_supported_color_modes = {color_mode}
|
self._attr_supported_color_modes = {color_mode}
|
||||||
|
self._attr_effect = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brightness(self) -> int:
|
def brightness(self) -> int:
|
||||||
@ -163,6 +165,8 @@ class LIFXLight(LIFXEntity, LightEntity):
|
|||||||
"""Return the name of the currently running effect."""
|
"""Return the name of the currently running effect."""
|
||||||
if effect := self.effects_conductor.effect(self.bulb):
|
if effect := self.effects_conductor.effect(self.bulb):
|
||||||
return f"effect_{effect.name}"
|
return f"effect_{effect.name}"
|
||||||
|
if effect := self.coordinator.async_get_active_effect():
|
||||||
|
return f"effect_{FirmwareEffect(effect).name.lower()}"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def update_during_transition(self, when: int) -> None:
|
async def update_during_transition(self, when: int) -> None:
|
||||||
@ -361,6 +365,13 @@ class LIFXColor(LIFXLight):
|
|||||||
class LIFXStrip(LIFXColor):
|
class LIFXStrip(LIFXColor):
|
||||||
"""Representation of a LIFX light strip with multiple zones."""
|
"""Representation of a LIFX light strip with multiple zones."""
|
||||||
|
|
||||||
|
_attr_effect_list = [
|
||||||
|
SERVICE_EFFECT_COLORLOOP,
|
||||||
|
SERVICE_EFFECT_PULSE,
|
||||||
|
SERVICE_EFFECT_MOVE,
|
||||||
|
SERVICE_EFFECT_STOP,
|
||||||
|
]
|
||||||
|
|
||||||
async def set_color(
|
async def set_color(
|
||||||
self,
|
self,
|
||||||
hsbk: list[float | int | None],
|
hsbk: list[float | int | None],
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Support for LIFX lights."""
|
"""Support for LIFX lights."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -28,21 +29,35 @@ from homeassistant.core import HomeAssistant, ServiceCall, callback
|
|||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.service import async_extract_referenced_entity_ids
|
from homeassistant.helpers.service import async_extract_referenced_entity_ids
|
||||||
|
|
||||||
from .const import _LOGGER, DATA_LIFX_MANAGER, DOMAIN
|
from .const import DATA_LIFX_MANAGER, DOMAIN
|
||||||
|
from .coordinator import LIFXUpdateCoordinator, Light
|
||||||
from .util import convert_8_to_16, find_hsbk
|
from .util import convert_8_to_16, find_hsbk
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(seconds=10)
|
SCAN_INTERVAL = timedelta(seconds=10)
|
||||||
|
|
||||||
|
|
||||||
SERVICE_EFFECT_PULSE = "effect_pulse"
|
SERVICE_EFFECT_PULSE = "effect_pulse"
|
||||||
SERVICE_EFFECT_COLORLOOP = "effect_colorloop"
|
SERVICE_EFFECT_COLORLOOP = "effect_colorloop"
|
||||||
|
SERVICE_EFFECT_MOVE = "effect_move"
|
||||||
SERVICE_EFFECT_STOP = "effect_stop"
|
SERVICE_EFFECT_STOP = "effect_stop"
|
||||||
|
|
||||||
|
ATTR_POWER_OFF = "power_off"
|
||||||
ATTR_POWER_ON = "power_on"
|
ATTR_POWER_ON = "power_on"
|
||||||
ATTR_PERIOD = "period"
|
ATTR_PERIOD = "period"
|
||||||
ATTR_CYCLES = "cycles"
|
ATTR_CYCLES = "cycles"
|
||||||
ATTR_SPREAD = "spread"
|
ATTR_SPREAD = "spread"
|
||||||
ATTR_CHANGE = "change"
|
ATTR_CHANGE = "change"
|
||||||
|
ATTR_DIRECTION = "direction"
|
||||||
|
ATTR_SPEED = "speed"
|
||||||
|
|
||||||
|
EFFECT_MOVE = "MOVE"
|
||||||
|
EFFECT_OFF = "OFF"
|
||||||
|
|
||||||
|
EFFECT_MOVE_DEFAULT_SPEED = 3.0
|
||||||
|
EFFECT_MOVE_DEFAULT_DIRECTION = "right"
|
||||||
|
EFFECT_MOVE_DIRECTION_RIGHT = "right"
|
||||||
|
EFFECT_MOVE_DIRECTION_LEFT = "left"
|
||||||
|
|
||||||
|
EFFECT_MOVE_DIRECTIONS = [EFFECT_MOVE_DIRECTION_LEFT, EFFECT_MOVE_DIRECTION_RIGHT]
|
||||||
|
|
||||||
PULSE_MODE_BLINK = "blink"
|
PULSE_MODE_BLINK = "blink"
|
||||||
PULSE_MODE_BREATHE = "breathe"
|
PULSE_MODE_BREATHE = "breathe"
|
||||||
@ -110,10 +125,20 @@ LIFX_EFFECT_STOP_SCHEMA = cv.make_entity_service_schema({})
|
|||||||
SERVICES = (
|
SERVICES = (
|
||||||
SERVICE_EFFECT_STOP,
|
SERVICE_EFFECT_STOP,
|
||||||
SERVICE_EFFECT_PULSE,
|
SERVICE_EFFECT_PULSE,
|
||||||
|
SERVICE_EFFECT_MOVE,
|
||||||
SERVICE_EFFECT_COLORLOOP,
|
SERVICE_EFFECT_COLORLOOP,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LIFX_EFFECT_MOVE_SCHEMA = cv.make_entity_service_schema(
|
||||||
|
{
|
||||||
|
**LIFX_EFFECT_SCHEMA,
|
||||||
|
ATTR_SPEED: vol.All(vol.Coerce(float), vol.Clamp(min=0.1, max=60)),
|
||||||
|
ATTR_DIRECTION: vol.In(EFFECT_MOVE_DIRECTIONS),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LIFXManager:
|
class LIFXManager:
|
||||||
"""Representation of all known LIFX entities."""
|
"""Representation of all known LIFX entities."""
|
||||||
|
|
||||||
@ -168,6 +193,13 @@ class LIFXManager:
|
|||||||
schema=LIFX_EFFECT_COLORLOOP_SCHEMA,
|
schema=LIFX_EFFECT_COLORLOOP_SCHEMA,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_EFFECT_MOVE,
|
||||||
|
service_handler,
|
||||||
|
schema=LIFX_EFFECT_MOVE_SCHEMA,
|
||||||
|
)
|
||||||
|
|
||||||
self.hass.services.async_register(
|
self.hass.services.async_register(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_EFFECT_STOP,
|
SERVICE_EFFECT_STOP,
|
||||||
@ -179,15 +211,35 @@ class LIFXManager:
|
|||||||
self, entity_ids: set[str], service: str, **kwargs: Any
|
self, entity_ids: set[str], service: str, **kwargs: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Start a light effect on entities."""
|
"""Start a light effect on entities."""
|
||||||
bulbs = [
|
|
||||||
coordinator.device
|
|
||||||
for entry_id, coordinator in self.hass.data[DOMAIN].items()
|
|
||||||
if entry_id != DATA_LIFX_MANAGER
|
|
||||||
and self.entry_id_to_entity_id[entry_id] in entity_ids
|
|
||||||
]
|
|
||||||
_LOGGER.debug("Starting effect %s on %s", service, bulbs)
|
|
||||||
|
|
||||||
if service == SERVICE_EFFECT_PULSE:
|
coordinators: list[LIFXUpdateCoordinator] = []
|
||||||
|
bulbs: list[Light] = []
|
||||||
|
|
||||||
|
for entry_id, coordinator in self.hass.data[DOMAIN].items():
|
||||||
|
if (
|
||||||
|
entry_id != DATA_LIFX_MANAGER
|
||||||
|
and self.entry_id_to_entity_id[entry_id] in entity_ids
|
||||||
|
):
|
||||||
|
coordinators.append(coordinator)
|
||||||
|
bulbs.append(coordinator.device)
|
||||||
|
|
||||||
|
if service == SERVICE_EFFECT_MOVE:
|
||||||
|
await asyncio.gather(
|
||||||
|
*(
|
||||||
|
coordinator.async_set_multizone_effect(
|
||||||
|
effect=EFFECT_MOVE,
|
||||||
|
speed=kwargs.get(ATTR_SPEED, EFFECT_MOVE_DEFAULT_SPEED),
|
||||||
|
direction=kwargs.get(
|
||||||
|
ATTR_DIRECTION, EFFECT_MOVE_DEFAULT_DIRECTION
|
||||||
|
),
|
||||||
|
power_on=kwargs.get(ATTR_POWER_ON, False),
|
||||||
|
)
|
||||||
|
for coordinator in coordinators
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif service == SERVICE_EFFECT_PULSE:
|
||||||
|
|
||||||
effect = aiolifx_effects.EffectPulse(
|
effect = aiolifx_effects.EffectPulse(
|
||||||
power_on=kwargs.get(ATTR_POWER_ON),
|
power_on=kwargs.get(ATTR_POWER_ON),
|
||||||
period=kwargs.get(ATTR_PERIOD),
|
period=kwargs.get(ATTR_PERIOD),
|
||||||
@ -196,6 +248,7 @@ class LIFXManager:
|
|||||||
hsbk=find_hsbk(self.hass, **kwargs),
|
hsbk=find_hsbk(self.hass, **kwargs),
|
||||||
)
|
)
|
||||||
await self.effects_conductor.start(effect, bulbs)
|
await self.effects_conductor.start(effect, bulbs)
|
||||||
|
|
||||||
elif service == SERVICE_EFFECT_COLORLOOP:
|
elif service == SERVICE_EFFECT_COLORLOOP:
|
||||||
preprocess_turn_on_alternatives(self.hass, kwargs)
|
preprocess_turn_on_alternatives(self.hass, kwargs)
|
||||||
|
|
||||||
@ -212,5 +265,15 @@ class LIFXManager:
|
|||||||
brightness=brightness,
|
brightness=brightness,
|
||||||
)
|
)
|
||||||
await self.effects_conductor.start(effect, bulbs)
|
await self.effects_conductor.start(effect, bulbs)
|
||||||
|
|
||||||
elif service == SERVICE_EFFECT_STOP:
|
elif service == SERVICE_EFFECT_STOP:
|
||||||
|
|
||||||
await self.effects_conductor.stop(bulbs)
|
await self.effects_conductor.stop(bulbs)
|
||||||
|
|
||||||
|
for coordinator in coordinators:
|
||||||
|
await coordinator.async_set_multizone_effect(
|
||||||
|
effect=EFFECT_OFF,
|
||||||
|
speed=EFFECT_MOVE_DEFAULT_SPEED,
|
||||||
|
direction=EFFECT_MOVE_DEFAULT_DIRECTION,
|
||||||
|
power_on=False,
|
||||||
|
)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "LIFX",
|
"name": "LIFX",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/lifx",
|
"documentation": "https://www.home-assistant.io/integrations/lifx",
|
||||||
"requirements": ["aiolifx==0.8.4", "aiolifx_effects==0.2.2"],
|
"requirements": ["aiolifx==0.8.5", "aiolifx_effects==0.2.2"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"dependencies": ["network"],
|
"dependencies": ["network"],
|
||||||
"homekit": {
|
"homekit": {
|
||||||
|
@ -171,6 +171,40 @@ effect_colorloop:
|
|||||||
default: true
|
default: true
|
||||||
selector:
|
selector:
|
||||||
boolean:
|
boolean:
|
||||||
|
effect_move:
|
||||||
|
name: Move effect
|
||||||
|
description: Start the firmware-based Move effect on a LIFX Z, Lightstrip or Beam.
|
||||||
|
target:
|
||||||
|
entity:
|
||||||
|
integration: lifx
|
||||||
|
domain: light
|
||||||
|
fields:
|
||||||
|
speed:
|
||||||
|
name: Speed
|
||||||
|
description: How long in seconds for the effect to move across the length of the light.
|
||||||
|
default: 3.0
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0.1
|
||||||
|
max: 60
|
||||||
|
step: 0.1
|
||||||
|
unit_of_measurement: seconds
|
||||||
|
direction:
|
||||||
|
name: Direction
|
||||||
|
description: Direction the effect will move across the device.
|
||||||
|
default: right
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
mode: dropdown
|
||||||
|
options:
|
||||||
|
- right
|
||||||
|
- left
|
||||||
|
power_on:
|
||||||
|
name: Power on
|
||||||
|
description: Powered off lights will be turned on before starting the effect.
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
|
||||||
effect_stop:
|
effect_stop:
|
||||||
name: Stop effect
|
name: Stop effect
|
||||||
|
@ -193,7 +193,7 @@ aiokafka==0.7.2
|
|||||||
aiokef==0.2.16
|
aiokef==0.2.16
|
||||||
|
|
||||||
# homeassistant.components.lifx
|
# homeassistant.components.lifx
|
||||||
aiolifx==0.8.4
|
aiolifx==0.8.5
|
||||||
|
|
||||||
# homeassistant.components.lifx
|
# homeassistant.components.lifx
|
||||||
aiolifx_effects==0.2.2
|
aiolifx_effects==0.2.2
|
||||||
|
@ -171,7 +171,7 @@ aiohue==4.5.0
|
|||||||
aiokafka==0.7.2
|
aiokafka==0.7.2
|
||||||
|
|
||||||
# homeassistant.components.lifx
|
# homeassistant.components.lifx
|
||||||
aiolifx==0.8.4
|
aiolifx==0.8.5
|
||||||
|
|
||||||
# homeassistant.components.lifx
|
# homeassistant.components.lifx
|
||||||
aiolifx_effects==0.2.2
|
aiolifx_effects==0.2.2
|
||||||
|
@ -145,9 +145,13 @@ def _mocked_infrared_bulb() -> Light:
|
|||||||
def _mocked_light_strip() -> Light:
|
def _mocked_light_strip() -> Light:
|
||||||
bulb = _mocked_bulb()
|
bulb = _mocked_bulb()
|
||||||
bulb.product = 31 # LIFX Z
|
bulb.product = 31 # LIFX Z
|
||||||
|
bulb.color_zones = [MagicMock(), MagicMock()]
|
||||||
|
bulb.effect = {"effect": "MOVE", "speed": 3, "duration": 0, "direction": "RIGHT"}
|
||||||
bulb.get_color_zones = MockLifxCommand(bulb)
|
bulb.get_color_zones = MockLifxCommand(bulb)
|
||||||
bulb.set_color_zones = MockLifxCommand(bulb)
|
bulb.set_color_zones = MockLifxCommand(bulb)
|
||||||
bulb.color_zones = [MagicMock(), MagicMock()]
|
bulb.get_multizone_effect = MockLifxCommand(bulb)
|
||||||
|
bulb.set_multizone_effect = MockLifxCommand(bulb)
|
||||||
|
|
||||||
return bulb
|
return bulb
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,12 @@ from homeassistant.components import lifx
|
|||||||
from homeassistant.components.lifx import DOMAIN
|
from homeassistant.components.lifx import DOMAIN
|
||||||
from homeassistant.components.lifx.const import ATTR_POWER
|
from homeassistant.components.lifx.const import ATTR_POWER
|
||||||
from homeassistant.components.lifx.light import ATTR_INFRARED, ATTR_ZONES
|
from homeassistant.components.lifx.light import ATTR_INFRARED, ATTR_ZONES
|
||||||
from homeassistant.components.lifx.manager import SERVICE_EFFECT_COLORLOOP
|
from homeassistant.components.lifx.manager import (
|
||||||
|
ATTR_DIRECTION,
|
||||||
|
ATTR_SPEED,
|
||||||
|
SERVICE_EFFECT_COLORLOOP,
|
||||||
|
SERVICE_EFFECT_MOVE,
|
||||||
|
)
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
ATTR_BRIGHTNESS,
|
ATTR_BRIGHTNESS,
|
||||||
ATTR_COLOR_MODE,
|
ATTR_COLOR_MODE,
|
||||||
@ -24,7 +29,13 @@ from homeassistant.components.light import (
|
|||||||
DOMAIN as LIGHT_DOMAIN,
|
DOMAIN as LIGHT_DOMAIN,
|
||||||
ColorMode,
|
ColorMode,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, STATE_OFF, STATE_UNAVAILABLE
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
CONF_HOST,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
STATE_UNAVAILABLE,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
@ -401,6 +412,93 @@ async def test_light_strip(hass: HomeAssistant) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_lightstrip_move_effect(hass: HomeAssistant) -> None:
|
||||||
|
"""Test the firmware move effect on a light strip."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data={CONF_HOST: "127.0.0.1"}, unique_id=SERIAL
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
bulb = _mocked_light_strip()
|
||||||
|
bulb.power_level = 0
|
||||||
|
bulb.color = [65535, 65535, 65535, 65535]
|
||||||
|
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"
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "effect_move"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(bulb.set_power.calls) == 1
|
||||||
|
assert len(bulb.set_multizone_effect.calls) == 1
|
||||||
|
|
||||||
|
call_dict = bulb.set_multizone_effect.calls[0][1]
|
||||||
|
call_dict.pop("callb")
|
||||||
|
assert call_dict == {
|
||||||
|
"effect": 1,
|
||||||
|
"speed": 3.0,
|
||||||
|
"direction": 0,
|
||||||
|
}
|
||||||
|
bulb.get_multizone_effect.reset_mock()
|
||||||
|
bulb.set_multizone_effect.reset_mock()
|
||||||
|
bulb.set_power.reset_mock()
|
||||||
|
|
||||||
|
bulb.power_level = 0
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_EFFECT_MOVE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_SPEED: 4.5, ATTR_DIRECTION: "left"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
bulb.power_level = 65535
|
||||||
|
bulb.effect = {"name": "effect_move", "enable": 1}
|
||||||
|
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=30))
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
assert len(bulb.set_power.calls) == 1
|
||||||
|
assert len(bulb.set_multizone_effect.calls) == 1
|
||||||
|
call_dict = bulb.set_multizone_effect.calls[0][1]
|
||||||
|
call_dict.pop("callb")
|
||||||
|
assert call_dict == {
|
||||||
|
"effect": 1,
|
||||||
|
"speed": 4.5,
|
||||||
|
"direction": 1,
|
||||||
|
}
|
||||||
|
bulb.get_multizone_effect.reset_mock()
|
||||||
|
bulb.set_multizone_effect.reset_mock()
|
||||||
|
bulb.set_power.reset_mock()
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
LIGHT_DOMAIN,
|
||||||
|
"turn_on",
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_EFFECT: "effect_stop"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert len(bulb.set_power.calls) == 0
|
||||||
|
assert len(bulb.set_multizone_effect.calls) == 1
|
||||||
|
call_dict = bulb.set_multizone_effect.calls[0][1]
|
||||||
|
call_dict.pop("callb")
|
||||||
|
assert call_dict == {
|
||||||
|
"effect": 0,
|
||||||
|
"speed": 3.0,
|
||||||
|
"direction": 0,
|
||||||
|
}
|
||||||
|
bulb.get_multizone_effect.reset_mock()
|
||||||
|
bulb.set_multizone_effect.reset_mock()
|
||||||
|
bulb.set_power.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
async def test_color_light_with_temp(
|
async def test_color_light_with_temp(
|
||||||
hass: HomeAssistant, mock_effect_conductor
|
hass: HomeAssistant, mock_effect_conductor
|
||||||
) -> None:
|
) -> None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user