Change dynamic segment handling of WLED (#52018)
This commit is contained in:
parent
cc00617cd5
commit
a67ca08124
4 changed files with 93 additions and 97 deletions
|
@ -5,7 +5,6 @@ from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
|
||||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_HOST
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
@ -16,7 +15,7 @@ PLATFORMS = (LIGHT_DOMAIN, SENSOR_DOMAIN, SWITCH_DOMAIN)
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up WLED from a config entry."""
|
"""Set up WLED from a config entry."""
|
||||||
coordinator = WLEDDataUpdateCoordinator(hass, host=entry.data[CONF_HOST])
|
coordinator = WLEDDataUpdateCoordinator(hass, entry=entry)
|
||||||
await coordinator.async_config_entry_first_refresh()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
|
|
@ -6,25 +6,37 @@ from typing import Callable
|
||||||
|
|
||||||
from wled import WLED, Device as WLEDDevice, WLEDConnectionClosed, WLEDError
|
from wled import WLED, Device as WLEDDevice, WLEDConnectionClosed, WLEDError
|
||||||
|
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import CONF_HOST, EVENT_HOMEASSISTANT_STOP
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import DOMAIN, LOGGER, SCAN_INTERVAL
|
from .const import (
|
||||||
|
CONF_KEEP_MASTER_LIGHT,
|
||||||
|
DEFAULT_KEEP_MASTER_LIGHT,
|
||||||
|
DOMAIN,
|
||||||
|
LOGGER,
|
||||||
|
SCAN_INTERVAL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WLEDDataUpdateCoordinator(DataUpdateCoordinator[WLEDDevice]):
|
class WLEDDataUpdateCoordinator(DataUpdateCoordinator[WLEDDevice]):
|
||||||
"""Class to manage fetching WLED data from single endpoint."""
|
"""Class to manage fetching WLED data from single endpoint."""
|
||||||
|
|
||||||
|
keep_master_light: bool
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
*,
|
*,
|
||||||
host: str,
|
entry: ConfigEntry,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize global WLED data updater."""
|
"""Initialize global WLED data updater."""
|
||||||
self.wled = WLED(host, session=async_get_clientsession(hass))
|
self.keep_master_light = entry.options.get(
|
||||||
|
CONF_KEEP_MASTER_LIGHT, DEFAULT_KEEP_MASTER_LIGHT
|
||||||
|
)
|
||||||
|
self.wled = WLED(entry.data[CONF_HOST], session=async_get_clientsession(hass))
|
||||||
self.unsub: Callable | None = None
|
self.unsub: Callable | None = None
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
|
@ -34,6 +46,13 @@ class WLEDDataUpdateCoordinator(DataUpdateCoordinator[WLEDDevice]):
|
||||||
update_interval=SCAN_INTERVAL,
|
update_interval=SCAN_INTERVAL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_master_light(self) -> bool:
|
||||||
|
"""Return if the coordinated device has an master light."""
|
||||||
|
return self.keep_master_light or (
|
||||||
|
self.data is not None and len(self.data.state.segments) > 1
|
||||||
|
)
|
||||||
|
|
||||||
def update_listeners(self) -> None:
|
def update_listeners(self) -> None:
|
||||||
"""Call update on all listeners."""
|
"""Call update on all listeners."""
|
||||||
for update_callback in self._listeners:
|
for update_callback in self._listeners:
|
||||||
|
|
|
@ -24,9 +24,6 @@ from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.entity_registry import (
|
|
||||||
async_get_registry as async_get_entity_registry,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_COLOR_PRIMARY,
|
ATTR_COLOR_PRIMARY,
|
||||||
|
@ -38,8 +35,6 @@ from .const import (
|
||||||
ATTR_REVERSE,
|
ATTR_REVERSE,
|
||||||
ATTR_SEGMENT_ID,
|
ATTR_SEGMENT_ID,
|
||||||
ATTR_SPEED,
|
ATTR_SPEED,
|
||||||
CONF_KEEP_MASTER_LIGHT,
|
|
||||||
DEFAULT_KEEP_MASTER_LIGHT,
|
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
SERVICE_PRESET,
|
SERVICE_PRESET,
|
||||||
|
@ -87,17 +82,13 @@ async def async_setup_entry(
|
||||||
"async_preset",
|
"async_preset",
|
||||||
)
|
)
|
||||||
|
|
||||||
keep_master_light = entry.options.get(
|
if coordinator.keep_master_light:
|
||||||
CONF_KEEP_MASTER_LIGHT, DEFAULT_KEEP_MASTER_LIGHT
|
|
||||||
)
|
|
||||||
if keep_master_light:
|
|
||||||
async_add_entities([WLEDMasterLight(coordinator=coordinator)])
|
async_add_entities([WLEDMasterLight(coordinator=coordinator)])
|
||||||
|
|
||||||
update_segments = partial(
|
update_segments = partial(
|
||||||
async_update_segments,
|
async_update_segments,
|
||||||
entry,
|
entry,
|
||||||
coordinator,
|
coordinator,
|
||||||
keep_master_light,
|
|
||||||
{},
|
{},
|
||||||
async_add_entities,
|
async_add_entities,
|
||||||
)
|
)
|
||||||
|
@ -130,6 +121,11 @@ class WLEDMasterLight(WLEDEntity, LightEntity):
|
||||||
"""Return the state of the light."""
|
"""Return the state of the light."""
|
||||||
return bool(self.coordinator.data.state.on)
|
return bool(self.coordinator.data.state.on)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
"""Return if this master light is available or not."""
|
||||||
|
return self.coordinator.has_master_light and super().available
|
||||||
|
|
||||||
@wled_exception_handler
|
@wled_exception_handler
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off the light."""
|
"""Turn off the light."""
|
||||||
|
@ -182,18 +178,17 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
|
||||||
self,
|
self,
|
||||||
coordinator: WLEDDataUpdateCoordinator,
|
coordinator: WLEDDataUpdateCoordinator,
|
||||||
segment: int,
|
segment: int,
|
||||||
keep_master_light: bool,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize WLED segment light."""
|
"""Initialize WLED segment light."""
|
||||||
super().__init__(coordinator=coordinator)
|
super().__init__(coordinator=coordinator)
|
||||||
self._keep_master_light = keep_master_light
|
|
||||||
self._rgbw = coordinator.data.info.leds.rgbw
|
self._rgbw = coordinator.data.info.leds.rgbw
|
||||||
self._wv = coordinator.data.info.leds.wv
|
self._wv = coordinator.data.info.leds.wv
|
||||||
self._segment = segment
|
self._segment = segment
|
||||||
|
|
||||||
# If this is the one and only segment, use a simpler name
|
# Segment 0 uses a simpler name, which is more natural for when using
|
||||||
|
# a single segment / using WLED with one big LED strip.
|
||||||
self._attr_name = f"{coordinator.data.info.name} Segment {segment}"
|
self._attr_name = f"{coordinator.data.info.name} Segment {segment}"
|
||||||
if len(coordinator.data.state.segments) == 1:
|
if segment == 0:
|
||||||
self._attr_name = coordinator.data.info.name
|
self._attr_name = coordinator.data.info.name
|
||||||
|
|
||||||
self._attr_unique_id = (
|
self._attr_unique_id = (
|
||||||
|
@ -264,7 +259,7 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
|
||||||
|
|
||||||
# If this is the one and only segment, calculate brightness based
|
# If this is the one and only segment, calculate brightness based
|
||||||
# on the master and segment brightness
|
# on the master and segment brightness
|
||||||
if not self._keep_master_light and len(state.segments) == 1:
|
if not self.coordinator.has_master_light:
|
||||||
return int(
|
return int(
|
||||||
(state.segments[self._segment].brightness * state.brightness) / 255
|
(state.segments[self._segment].brightness * state.brightness) / 255
|
||||||
)
|
)
|
||||||
|
@ -281,8 +276,9 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
|
||||||
"""Return the state of the light."""
|
"""Return the state of the light."""
|
||||||
state = self.coordinator.data.state
|
state = self.coordinator.data.state
|
||||||
|
|
||||||
# If there is a single segment, take master into account
|
# If there is no master, we take the master state into account
|
||||||
if len(state.segments) == 1 and not state.on:
|
# on the segment level.
|
||||||
|
if not self.coordinator.has_master_light and not state.on:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return bool(state.segments[self._segment].on)
|
return bool(state.segments[self._segment].on)
|
||||||
|
@ -295,11 +291,8 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
|
||||||
# WLED uses 100ms per unit, so 10 = 1 second.
|
# WLED uses 100ms per unit, so 10 = 1 second.
|
||||||
transition = round(kwargs[ATTR_TRANSITION] * 10)
|
transition = round(kwargs[ATTR_TRANSITION] * 10)
|
||||||
|
|
||||||
# If there is a single segment, control via the master
|
# If there is no master control, and only 1 segment, handle the
|
||||||
if (
|
if not self.coordinator.has_master_light:
|
||||||
not self._keep_master_light
|
|
||||||
and len(self.coordinator.data.state.segments) == 1
|
|
||||||
):
|
|
||||||
await self.coordinator.wled.master(on=False, transition=transition)
|
await self.coordinator.wled.master(on=False, transition=transition)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -331,12 +324,8 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
|
||||||
if ATTR_EFFECT in kwargs:
|
if ATTR_EFFECT in kwargs:
|
||||||
data[ATTR_EFFECT] = kwargs[ATTR_EFFECT]
|
data[ATTR_EFFECT] = kwargs[ATTR_EFFECT]
|
||||||
|
|
||||||
# When only 1 segment is present, switch along the master, and use
|
# If there is no master control, and only 1 segment, handle the master
|
||||||
# the master for power/brightness control.
|
if not self.coordinator.has_master_light:
|
||||||
if (
|
|
||||||
not self._keep_master_light
|
|
||||||
and len(self.coordinator.data.state.segments) == 1
|
|
||||||
):
|
|
||||||
master_data = {ATTR_ON: True}
|
master_data = {ATTR_ON: True}
|
||||||
if ATTR_BRIGHTNESS in data:
|
if ATTR_BRIGHTNESS in data:
|
||||||
master_data[ATTR_BRIGHTNESS] = data[ATTR_BRIGHTNESS]
|
master_data[ATTR_BRIGHTNESS] = data[ATTR_BRIGHTNESS]
|
||||||
|
@ -384,56 +373,28 @@ class WLEDSegmentLight(WLEDEntity, LightEntity):
|
||||||
def async_update_segments(
|
def async_update_segments(
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
coordinator: WLEDDataUpdateCoordinator,
|
coordinator: WLEDDataUpdateCoordinator,
|
||||||
keep_master_light: bool,
|
|
||||||
current: dict[int, WLEDSegmentLight | WLEDMasterLight],
|
current: dict[int, WLEDSegmentLight | WLEDMasterLight],
|
||||||
async_add_entities,
|
async_add_entities,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Update segments."""
|
"""Update segments."""
|
||||||
segment_ids = {light.segment_id for light in coordinator.data.state.segments}
|
segment_ids = {light.segment_id for light in coordinator.data.state.segments}
|
||||||
current_ids = set(current)
|
current_ids = set(current)
|
||||||
|
new_entities = []
|
||||||
|
|
||||||
# Discard master (if present)
|
# Discard master (if present)
|
||||||
current_ids.discard(-1)
|
current_ids.discard(-1)
|
||||||
|
|
||||||
new_entities = []
|
|
||||||
|
|
||||||
# Process new segments, add them to Home Assistant
|
# Process new segments, add them to Home Assistant
|
||||||
for segment_id in segment_ids - current_ids:
|
for segment_id in segment_ids - current_ids:
|
||||||
current[segment_id] = WLEDSegmentLight(
|
current[segment_id] = WLEDSegmentLight(coordinator, segment_id)
|
||||||
coordinator, segment_id, keep_master_light
|
|
||||||
)
|
|
||||||
new_entities.append(current[segment_id])
|
new_entities.append(current[segment_id])
|
||||||
|
|
||||||
# More than 1 segment now? Add master controls
|
# More than 1 segment now? No master? Add master controls
|
||||||
if not keep_master_light and (len(current_ids) < 2 and len(segment_ids) > 1):
|
if not coordinator.keep_master_light and (
|
||||||
|
len(current_ids) < 2 and len(segment_ids) > 1
|
||||||
|
):
|
||||||
current[-1] = WLEDMasterLight(coordinator)
|
current[-1] = WLEDMasterLight(coordinator)
|
||||||
new_entities.append(current[-1])
|
new_entities.append(current[-1])
|
||||||
|
|
||||||
if new_entities:
|
if new_entities:
|
||||||
async_add_entities(new_entities)
|
async_add_entities(new_entities)
|
||||||
|
|
||||||
# Process deleted segments, remove them from Home Assistant
|
|
||||||
for segment_id in current_ids - segment_ids:
|
|
||||||
coordinator.hass.async_create_task(
|
|
||||||
async_remove_entity(segment_id, coordinator, current)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Remove master if there is only 1 segment left
|
|
||||||
if not keep_master_light and len(current_ids) > 1 and len(segment_ids) < 2:
|
|
||||||
coordinator.hass.async_create_task(
|
|
||||||
async_remove_entity(-1, coordinator, current)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_remove_entity(
|
|
||||||
index: int,
|
|
||||||
coordinator: WLEDDataUpdateCoordinator,
|
|
||||||
current: dict[int, WLEDSegmentLight | WLEDMasterLight],
|
|
||||||
) -> None:
|
|
||||||
"""Remove WLED segment light from Home Assistant."""
|
|
||||||
entity = current[index]
|
|
||||||
await entity.async_remove(force_remove=True)
|
|
||||||
registry = await async_get_entity_registry(coordinator.hass)
|
|
||||||
if entity.entity_id in registry.entities:
|
|
||||||
registry.async_remove(entity.entity_id)
|
|
||||||
del current[index]
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ async def test_rgb_light_state(
|
||||||
entity_registry = er.async_get(hass)
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
# First segment of the strip
|
# First segment of the strip
|
||||||
state = hass.states.get("light.wled_rgb_light_segment_0")
|
state = hass.states.get("light.wled_rgb_light")
|
||||||
assert state
|
assert state
|
||||||
assert state.attributes.get(ATTR_BRIGHTNESS) == 127
|
assert state.attributes.get(ATTR_BRIGHTNESS) == 127
|
||||||
assert state.attributes.get(ATTR_EFFECT) == "Solid"
|
assert state.attributes.get(ATTR_EFFECT) == "Solid"
|
||||||
|
@ -64,7 +64,7 @@ async def test_rgb_light_state(
|
||||||
assert state.attributes.get(ATTR_SPEED) == 32
|
assert state.attributes.get(ATTR_SPEED) == 32
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
entry = entity_registry.async_get("light.wled_rgb_light_segment_0")
|
entry = entity_registry.async_get("light.wled_rgb_light")
|
||||||
assert entry
|
assert entry
|
||||||
assert entry.unique_id == "aabbccddeeff_0"
|
assert entry.unique_id == "aabbccddeeff_0"
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ async def test_segment_change_state(
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_TRANSITION: 5},
|
{ATTR_ENTITY_ID: "light.wled_rgb_light", ATTR_TRANSITION: 5},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -124,7 +124,7 @@ async def test_segment_change_state(
|
||||||
{
|
{
|
||||||
ATTR_BRIGHTNESS: 42,
|
ATTR_BRIGHTNESS: 42,
|
||||||
ATTR_EFFECT: "Chase",
|
ATTR_EFFECT: "Chase",
|
||||||
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
|
ATTR_ENTITY_ID: "light.wled_rgb_light",
|
||||||
ATTR_RGB_COLOR: [255, 0, 0],
|
ATTR_RGB_COLOR: [255, 0, 0],
|
||||||
ATTR_TRANSITION: 5,
|
ATTR_TRANSITION: 5,
|
||||||
},
|
},
|
||||||
|
@ -211,36 +211,53 @@ async def test_master_change_state(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("mock_wled", ["wled/rgb_single_segment.json"], indirect=True)
|
||||||
async def test_dynamically_handle_segments(
|
async def test_dynamically_handle_segments(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
init_integration: MockConfigEntry,
|
init_integration: MockConfigEntry,
|
||||||
mock_wled: MagicMock,
|
mock_wled: MagicMock,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test if a new/deleted segment is dynamically added/removed."""
|
"""Test if a new/deleted segment is dynamically added/removed."""
|
||||||
assert hass.states.get("light.wled_rgb_light_master")
|
master = hass.states.get("light.wled_rgb_light_master")
|
||||||
assert hass.states.get("light.wled_rgb_light_segment_0")
|
segment0 = hass.states.get("light.wled_rgb_light")
|
||||||
assert hass.states.get("light.wled_rgb_light_segment_1")
|
segment1 = hass.states.get("light.wled_rgb_light_segment_1")
|
||||||
|
assert segment0
|
||||||
|
assert segment0.state == STATE_ON
|
||||||
|
assert not master
|
||||||
|
assert not segment1
|
||||||
|
|
||||||
return_value = mock_wled.update.return_value
|
return_value = mock_wled.update.return_value
|
||||||
mock_wled.update.return_value = WLEDDevice(
|
mock_wled.update.return_value = WLEDDevice(
|
||||||
json.loads(load_fixture("wled/rgb_single_segment.json"))
|
json.loads(load_fixture("wled/rgb.json"))
|
||||||
)
|
)
|
||||||
|
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
|
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get("light.wled_rgb_light_segment_0")
|
master = hass.states.get("light.wled_rgb_light_master")
|
||||||
assert not hass.states.get("light.wled_rgb_light_segment_1")
|
segment0 = hass.states.get("light.wled_rgb_light")
|
||||||
assert not hass.states.get("light.wled_rgb_light_master")
|
segment1 = hass.states.get("light.wled_rgb_light_segment_1")
|
||||||
|
assert master
|
||||||
|
assert master.state == STATE_ON
|
||||||
|
assert segment0
|
||||||
|
assert segment0.state == STATE_ON
|
||||||
|
assert segment1
|
||||||
|
assert segment1.state == STATE_ON
|
||||||
|
|
||||||
# Test adding if segment shows up again, including the master entity
|
# Test adding if segment shows up again, including the master entity
|
||||||
mock_wled.update.return_value = return_value
|
mock_wled.update.return_value = return_value
|
||||||
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
|
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get("light.wled_rgb_light_master")
|
master = hass.states.get("light.wled_rgb_light_master")
|
||||||
assert hass.states.get("light.wled_rgb_light_segment_0")
|
segment0 = hass.states.get("light.wled_rgb_light")
|
||||||
assert hass.states.get("light.wled_rgb_light_segment_1")
|
segment1 = hass.states.get("light.wled_rgb_light_segment_1")
|
||||||
|
assert master
|
||||||
|
assert master.state == STATE_UNAVAILABLE
|
||||||
|
assert segment0
|
||||||
|
assert segment0.state == STATE_ON
|
||||||
|
assert segment1
|
||||||
|
assert segment1.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("mock_wled", ["wled/rgb_single_segment.json"], indirect=True)
|
@pytest.mark.parametrize("mock_wled", ["wled/rgb_single_segment.json"], indirect=True)
|
||||||
|
@ -320,12 +337,12 @@ async def test_light_error(
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0"},
|
{ATTR_ENTITY_ID: "light.wled_rgb_light"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get("light.wled_rgb_light_segment_0")
|
state = hass.states.get("light.wled_rgb_light")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert "Invalid response from API" in caplog.text
|
assert "Invalid response from API" in caplog.text
|
||||||
|
@ -345,12 +362,12 @@ async def test_light_connection_error(
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
LIGHT_DOMAIN,
|
LIGHT_DOMAIN,
|
||||||
SERVICE_TURN_OFF,
|
SERVICE_TURN_OFF,
|
||||||
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0"},
|
{ATTR_ENTITY_ID: "light.wled_rgb_light"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get("light.wled_rgb_light_segment_0")
|
state = hass.states.get("light.wled_rgb_light")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
assert "Error communicating with API" in caplog.text
|
assert "Error communicating with API" in caplog.text
|
||||||
|
@ -395,7 +412,7 @@ async def test_effect_service(
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
{
|
{
|
||||||
ATTR_EFFECT: "Rainbow",
|
ATTR_EFFECT: "Rainbow",
|
||||||
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
|
ATTR_ENTITY_ID: "light.wled_rgb_light",
|
||||||
ATTR_INTENSITY: 200,
|
ATTR_INTENSITY: 200,
|
||||||
ATTR_PALETTE: "Tiamat",
|
ATTR_PALETTE: "Tiamat",
|
||||||
ATTR_REVERSE: True,
|
ATTR_REVERSE: True,
|
||||||
|
@ -417,7 +434,7 @@ async def test_effect_service(
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_EFFECT: 9},
|
{ATTR_ENTITY_ID: "light.wled_rgb_light", ATTR_EFFECT: 9},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -435,7 +452,7 @@ async def test_effect_service(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
{
|
{
|
||||||
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
|
ATTR_ENTITY_ID: "light.wled_rgb_light",
|
||||||
ATTR_INTENSITY: 200,
|
ATTR_INTENSITY: 200,
|
||||||
ATTR_REVERSE: True,
|
ATTR_REVERSE: True,
|
||||||
ATTR_SPEED: 100,
|
ATTR_SPEED: 100,
|
||||||
|
@ -458,7 +475,7 @@ async def test_effect_service(
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
{
|
{
|
||||||
ATTR_EFFECT: "Rainbow",
|
ATTR_EFFECT: "Rainbow",
|
||||||
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
|
ATTR_ENTITY_ID: "light.wled_rgb_light",
|
||||||
ATTR_PALETTE: "Tiamat",
|
ATTR_PALETTE: "Tiamat",
|
||||||
ATTR_REVERSE: True,
|
ATTR_REVERSE: True,
|
||||||
ATTR_SPEED: 100,
|
ATTR_SPEED: 100,
|
||||||
|
@ -481,7 +498,7 @@ async def test_effect_service(
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
{
|
{
|
||||||
ATTR_EFFECT: "Rainbow",
|
ATTR_EFFECT: "Rainbow",
|
||||||
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
|
ATTR_ENTITY_ID: "light.wled_rgb_light",
|
||||||
ATTR_INTENSITY: 200,
|
ATTR_INTENSITY: 200,
|
||||||
ATTR_SPEED: 100,
|
ATTR_SPEED: 100,
|
||||||
},
|
},
|
||||||
|
@ -503,7 +520,7 @@ async def test_effect_service(
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
{
|
{
|
||||||
ATTR_EFFECT: "Rainbow",
|
ATTR_EFFECT: "Rainbow",
|
||||||
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
|
ATTR_ENTITY_ID: "light.wled_rgb_light",
|
||||||
ATTR_INTENSITY: 200,
|
ATTR_INTENSITY: 200,
|
||||||
ATTR_REVERSE: True,
|
ATTR_REVERSE: True,
|
||||||
},
|
},
|
||||||
|
@ -533,12 +550,12 @@ async def test_effect_service_error(
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_EFFECT,
|
SERVICE_EFFECT,
|
||||||
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_EFFECT: 9},
|
{ATTR_ENTITY_ID: "light.wled_rgb_light", ATTR_EFFECT: 9},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get("light.wled_rgb_light_segment_0")
|
state = hass.states.get("light.wled_rgb_light")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert "Invalid response from API" in caplog.text
|
assert "Invalid response from API" in caplog.text
|
||||||
|
@ -556,7 +573,7 @@ async def test_preset_service(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_PRESET,
|
SERVICE_PRESET,
|
||||||
{
|
{
|
||||||
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
|
ATTR_ENTITY_ID: "light.wled_rgb_light",
|
||||||
ATTR_PRESET: 1,
|
ATTR_PRESET: 1,
|
||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
|
@ -591,12 +608,12 @@ async def test_preset_service_error(
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_PRESET,
|
SERVICE_PRESET,
|
||||||
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_PRESET: 1},
|
{ATTR_ENTITY_ID: "light.wled_rgb_light", ATTR_PRESET: 1},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
state = hass.states.get("light.wled_rgb_light_segment_0")
|
state = hass.states.get("light.wled_rgb_light")
|
||||||
assert state
|
assert state
|
||||||
assert state.state == STATE_ON
|
assert state.state == STATE_ON
|
||||||
assert "Invalid response from API" in caplog.text
|
assert "Invalid response from API" in caplog.text
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue