Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions homeassistant/components/wled/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ def has_main_light(self) -> bool:
self.data is not None and len(self.data.state.segments) > 1
)

@property
def segment_ids(self) -> set[int]:
"""Return the set of segment IDs."""
return {
segment.segment_id
for segment in self.data.state.segments.values()
if segment.segment_id is not None
}
Comment thread
mik-laj marked this conversation as resolved.

@callback
def _use_websocket(self) -> None:
"""Use WebSocket for updates, instead of polling."""
Expand Down
25 changes: 20 additions & 5 deletions homeassistant/components/wled/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.group import IntegrationSpecificGroup

from .const import (
ATTR_CCT,
Expand Down Expand Up @@ -61,11 +62,23 @@ class WLEDMainLight(WLEDEntity, LightEntity):
_attr_translation_key = "main"
_attr_supported_features = LightEntityFeature.TRANSITION
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
group: IntegrationSpecificGroup

def __init__(self, coordinator: WLEDDataUpdateCoordinator) -> None:
"""Initialize WLED main light."""
super().__init__(coordinator=coordinator)
self._attr_unique_id = coordinator.data.info.mac_address
self.group = IntegrationSpecificGroup(self, [])
self._update_group_member()

def _update_group_member(self) -> None:
"""Update group members based on current segments."""
segment_unique_ids = [
f"{self.coordinator.data.info.mac_address}_{segment_id}"
for segment_id in self.coordinator.segment_ids
Comment thread
mik-laj marked this conversation as resolved.
Outdated
]
if segment_unique_ids != self.group.member_unique_ids:
self.group.member_unique_ids = segment_unique_ids

@property
def brightness(self) -> int | None:
Expand Down Expand Up @@ -104,6 +117,12 @@ async def async_turn_on(self, **kwargs: Any) -> None:
on=True, brightness=kwargs.get(ATTR_BRIGHTNESS), transition=transition
)

@callback
def _handle_coordinator_update(self) -> None:
"""Update attributes when the coordinator updates."""
self._update_group_member()
super()._handle_coordinator_update()
Comment thread
mik-laj marked this conversation as resolved.


class WLEDSegmentLight(WLEDEntity, LightEntity):
"""Defines a WLED light based on a segment."""
Expand Down Expand Up @@ -280,11 +299,7 @@ def async_update_segments(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Update segments."""
segment_ids = {
light.segment_id
for light in coordinator.data.state.segments.values()
if light.segment_id is not None
}
segment_ids = coordinator.segment_ids
new_entities: list[WLEDMainLight | WLEDSegmentLight] = []

# More than 1 segment now? No main? Add main controls
Expand Down
8 changes: 1 addition & 7 deletions homeassistant/components/wled/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,16 +127,10 @@ def async_update_segments(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Update segments."""
segment_ids = {
segment.segment_id
for segment in coordinator.data.state.segments.values()
if segment.segment_id is not None
}

new_entities: list[WLEDNumber] = []

# Process new segments, add them to Home Assistant
for segment_id in segment_ids - current_ids:
for segment_id in coordinator.segment_ids - current_ids:
current_ids.add(segment_id)
new_entities.extend(
WLEDNumber(coordinator, segment_id, desc) for desc in NUMBERS
Expand Down
8 changes: 1 addition & 7 deletions homeassistant/components/wled/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,10 @@ def async_update_segments(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Update segments."""
segment_ids = {
segment.segment_id
for segment in coordinator.data.state.segments.values()
if segment.segment_id is not None
}

new_entities: list[WLEDPaletteSelect] = []

# Process new segments, add them to Home Assistant
for segment_id in segment_ids - current_ids:
for segment_id in coordinator.segment_ids - current_ids:
current_ids.add(segment_id)
new_entities.append(WLEDPaletteSelect(coordinator, segment_id))

Expand Down
8 changes: 1 addition & 7 deletions homeassistant/components/wled/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,10 @@ def async_update_segments(
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Update segments."""
segment_ids = {
segment.segment_id
for segment in coordinator.data.state.segments.values()
if segment.segment_id is not None
}

new_entities: list[WLEDSegmentSwitch] = []

# Process new segments, add them to Home Assistant
for segment_id in segment_ids - current_ids:
for segment_id in coordinator.segment_ids - current_ids:
current_ids.add(segment_id)
new_entities.extend(
WLEDSegmentSwitch(
Expand Down
8 changes: 8 additions & 0 deletions tests/components/wled/snapshots/test_light.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,10 @@
]),
'area_id': None,
'capabilities': dict({
'group_entities': list([
'light.wled_rgb_light',
'light.wled_rgb_light_segment_1',
]),
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
Expand Down Expand Up @@ -958,6 +962,10 @@
'brightness': 128,
'color_mode': <ColorMode.BRIGHTNESS: 'brightness'>,
'friendly_name': 'WLED RGB Light Main',
'group_entities': list([
'light.wled_rgb_light',
'light.wled_rgb_light_segment_1',
]),
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
Expand Down
50 changes: 50 additions & 0 deletions tests/components/wled/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_GROUP_ENTITIES,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
Expand Down Expand Up @@ -388,3 +389,52 @@ async def test_cct_light(hass: HomeAssistant, mock_wled: MagicMock) -> None:
on=True,
segment_id=0,
)


@pytest.mark.parametrize("device_fixture", ["rgb_single_segment"])
async def test_main_light_group_updates_when_segments_change(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
mock_wled: MagicMock,
init_integration: MockConfigEntry,
) -> None:
"""Test that the main light group field updates when segments are dynamically added or removed."""
single_segment_data = mock_wled.update.return_value
two_segment_data = WLEDDevice.from_dict(
await async_load_json_object_fixture(hass, "rgb.json", DOMAIN)
)

# Enable keep_main_light so the main light persists even with a single segment
hass.config_entries.async_update_entry(
init_integration, options={CONF_KEEP_MAIN_LIGHT: True}
)
await hass.config_entries.async_reload(init_integration.entry_id)
await hass.async_block_till_done()

# 1 segment: group should contain only segment 0
assert (state := hass.states.get("light.wled_rgb_light_main"))
assert state.state == STATE_ON
assert state.attributes[ATTR_GROUP_ENTITIES] == ["light.wled_rgb_light"]

# Add a second segment
mock_wled.update.return_value = two_segment_data
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()

# 2 segments: group should contain both
assert (state := hass.states.get("light.wled_rgb_light_main"))
assert state.attributes[ATTR_GROUP_ENTITIES] == [
"light.wled_rgb_light",
"light.wled_rgb_light_segment_1",
]

# Remove the second segment
mock_wled.update.return_value = single_segment_data
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()

# Back to 1 segment: group should contain only segment 0 again
assert (state := hass.states.get("light.wled_rgb_light_main"))
assert state.attributes[ATTR_GROUP_ENTITIES] == ["light.wled_rgb_light"]
Loading