Cleanup Shelly RGBW light entities (#114410)

pull/114764/head
Shay Levy 2024-03-30 18:48:57 +03:00 committed by Paulus Schoutsen
parent 11b8b01cde
commit e8ee2fd25c
6 changed files with 134 additions and 3 deletions

View File

@ -234,3 +234,5 @@ DEVICES_WITHOUT_FIRMWARE_CHANGELOG = (
)
CONF_GEN = "gen"
SHELLY_PLUS_RGBW_CHANNELS = 4

View File

@ -14,6 +14,7 @@ from homeassistant.components.light import (
ATTR_RGB_COLOR,
ATTR_RGBW_COLOR,
ATTR_TRANSITION,
DOMAIN as LIGHT_DOMAIN,
ColorMode,
LightEntity,
LightEntityFeature,
@ -34,12 +35,14 @@ from .const import (
RGBW_MODELS,
RPC_MIN_TRANSITION_TIME_SEC,
SHBLB_1_RGB_EFFECTS,
SHELLY_PLUS_RGBW_CHANNELS,
STANDARD_RGB_EFFECTS,
)
from .coordinator import ShellyBlockCoordinator, ShellyRpcCoordinator, get_entry_data
from .entity import ShellyBlockEntity, ShellyRpcEntity
from .utils import (
async_remove_shelly_entity,
async_remove_shelly_rpc_entities,
brightness_to_percentage,
get_device_entry_gen,
get_rpc_key_ids,
@ -118,14 +121,28 @@ def async_setup_rpc_entry(
return
if light_key_ids := get_rpc_key_ids(coordinator.device.status, "light"):
# Light mode remove RGB & RGBW entities, add light entities
async_remove_shelly_rpc_entities(
hass, LIGHT_DOMAIN, coordinator.mac, ["rgb:0", "rgbw:0"]
)
async_add_entities(RpcShellyLight(coordinator, id_) for id_ in light_key_ids)
return
light_keys = [f"light:{i}" for i in range(SHELLY_PLUS_RGBW_CHANNELS)]
if rgb_key_ids := get_rpc_key_ids(coordinator.device.status, "rgb"):
# RGB mode remove light & RGBW entities, add RGB entity
async_remove_shelly_rpc_entities(
hass, LIGHT_DOMAIN, coordinator.mac, [*light_keys, "rgbw:0"]
)
async_add_entities(RpcShellyRgbLight(coordinator, id_) for id_ in rgb_key_ids)
return
if rgbw_key_ids := get_rpc_key_ids(coordinator.device.status, "rgbw"):
# RGBW mode remove light & RGB entities, add RGBW entity
async_remove_shelly_rpc_entities(
hass, LIGHT_DOMAIN, coordinator.mac, [*light_keys, "rgb:0"]
)
async_add_entities(RpcShellyRgbwLight(coordinator, id_) for id_ in rgbw_key_ids)

View File

@ -488,3 +488,15 @@ async def async_shutdown_device(device: BlockDevice | RpcDevice) -> None:
await device.shutdown()
if isinstance(device, BlockDevice):
device.shutdown()
@callback
def async_remove_shelly_rpc_entities(
hass: HomeAssistant, domain: str, mac: str, keys: list[str]
) -> None:
"""Remove RPC based Shelly entity."""
entity_reg = er_async_get(hass)
for key in keys:
if entity_id := entity_reg.async_get_entity_id(domain, DOMAIN, f"{mac}-{key}"):
LOGGER.debug("Removing entity: %s", entity_id)
entity_reg.async_remove(entity_id)

View File

@ -126,6 +126,18 @@ def register_entity(
return f"{domain}.{object_id}"
def get_entity(
hass: HomeAssistant,
domain: str,
unique_id: str,
) -> str | None:
"""Get Shelly entity."""
entity_registry = async_get(hass)
return entity_registry.async_get_entity_id(
domain, DOMAIN, f"{MOCK_MAC}-{unique_id}"
)
def get_entity_state(hass: HomeAssistant, entity_id: str) -> str:
"""Return entity state."""
entity = hass.states.get(entity_id)

View File

@ -169,6 +169,9 @@ MOCK_CONFIG = {
"input:1": {"id": 1, "type": "analog", "enable": True},
"input:2": {"id": 2, "name": "Gas", "type": "count", "enable": True},
"light:0": {"name": "test light_0"},
"light:1": {"name": "test light_1"},
"light:2": {"name": "test light_2"},
"light:3": {"name": "test light_3"},
"rgb:0": {"name": "test rgb_0"},
"rgbw:0": {"name": "test rgbw_0"},
"switch:0": {"name": "test switch_0"},
@ -225,6 +228,9 @@ MOCK_STATUS_RPC = {
"input:1": {"id": 1, "percent": 89, "xpercent": 8.9},
"input:2": {"id": 2, "counts": {"total": 56174, "xtotal": 561.74}},
"light:0": {"output": True, "brightness": 53.0},
"light:1": {"output": True, "brightness": 53.0},
"light:2": {"output": True, "brightness": 53.0},
"light:3": {"output": True, "brightness": 53.0},
"rgb:0": {"output": True, "brightness": 53.0, "rgb": [45, 55, 65]},
"rgbw:0": {"output": True, "brightness": 53.0, "rgb": [21, 22, 23], "white": 120},
"cloud": {"connected": False},

View File

@ -29,6 +29,7 @@ from homeassistant.components.light import (
ColorMode,
LightEntityFeature,
)
from homeassistant.components.shelly.const import SHELLY_PLUS_RGBW_CHANNELS
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
@ -38,7 +39,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_registry import EntityRegistry
from . import init_integration, mutate_rpc_device_status
from . import get_entity, init_integration, mutate_rpc_device_status, register_entity
from .conftest import mock_white_light_set_state
RELAY_BLOCK_ID = 0
@ -587,7 +588,8 @@ async def test_rpc_device_rgb_profile(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test RPC device in RGB profile."""
monkeypatch.delitem(mock_rpc_device.status, "light:0")
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
monkeypatch.delitem(mock_rpc_device.status, f"light:{i}")
monkeypatch.delitem(mock_rpc_device.status, "rgbw:0")
entity_id = "light.test_rgb_0"
await init_integration(hass, 2)
@ -633,7 +635,8 @@ async def test_rpc_device_rgbw_profile(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test RPC device in RGBW profile."""
monkeypatch.delitem(mock_rpc_device.status, "light:0")
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
monkeypatch.delitem(mock_rpc_device.status, f"light:{i}")
monkeypatch.delitem(mock_rpc_device.status, "rgb:0")
entity_id = "light.test_rgbw_0"
await init_integration(hass, 2)
@ -673,3 +676,82 @@ async def test_rpc_device_rgbw_profile(
entry = entity_registry.async_get(entity_id)
assert entry
assert entry.unique_id == "123456789ABC-rgbw:0"
async def test_rpc_rgbw_device_light_mode_remove_others(
hass: HomeAssistant,
mock_rpc_device: Mock,
entity_registry: EntityRegistry,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test Shelly RPC RGBW device in light mode removes RGB/RGBW entities."""
# register lights
monkeypatch.delitem(mock_rpc_device.status, "rgb:0")
monkeypatch.delitem(mock_rpc_device.status, "rgbw:0")
register_entity(hass, LIGHT_DOMAIN, "test_rgb_0", "rgb:0")
register_entity(hass, LIGHT_DOMAIN, "test_rgbw_0", "rgbw:0")
# verify RGB & RGBW entities created
assert get_entity(hass, LIGHT_DOMAIN, "rgb:0") is not None
assert get_entity(hass, LIGHT_DOMAIN, "rgbw:0") is not None
# init to remove RGB & RGBW
await init_integration(hass, 2)
# verify we have 4 lights
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
entity_id = f"light.test_light_{i}"
assert hass.states.get(entity_id).state == STATE_ON
entry = entity_registry.async_get(entity_id)
assert entry
assert entry.unique_id == f"123456789ABC-light:{i}"
# verify RGB & RGBW entities removed
assert get_entity(hass, LIGHT_DOMAIN, "rgb:0") is None
assert get_entity(hass, LIGHT_DOMAIN, "rgbw:0") is None
@pytest.mark.parametrize(
("active_mode", "removed_mode"),
[
("rgb", "rgbw"),
("rgbw", "rgb"),
],
)
async def test_rpc_rgbw_device_rgb_w_modes_remove_others(
hass: HomeAssistant,
mock_rpc_device: Mock,
entity_registry: EntityRegistry,
monkeypatch: pytest.MonkeyPatch,
active_mode: str,
removed_mode: str,
) -> None:
"""Test Shelly RPC RGBW device in RGB/W modes other lights."""
removed_key = f"{removed_mode}:0"
# register lights
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
monkeypatch.delitem(mock_rpc_device.status, f"light:{i}")
entity_id = f"light.test_light_{i}"
register_entity(hass, LIGHT_DOMAIN, entity_id, f"light:{i}")
monkeypatch.delitem(mock_rpc_device.status, f"{removed_mode}:0")
register_entity(hass, LIGHT_DOMAIN, f"test_{removed_key}", removed_key)
# verify lights entities created
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
assert get_entity(hass, LIGHT_DOMAIN, f"light:{i}") is not None
assert get_entity(hass, LIGHT_DOMAIN, removed_key) is not None
await init_integration(hass, 2)
# verify we have RGB/w light
entity_id = f"light.test_{active_mode}_0"
assert hass.states.get(entity_id).state == STATE_ON
entry = entity_registry.async_get(entity_id)
assert entry
assert entry.unique_id == f"123456789ABC-{active_mode}:0"
# verify light & RGB/W entities removed
for i in range(SHELLY_PLUS_RGBW_CHANNELS):
assert get_entity(hass, LIGHT_DOMAIN, f"light:{i}") is None
assert get_entity(hass, LIGHT_DOMAIN, removed_key) is None