Skip to content

Commit 3ce1ef4

Browse files
authored
Use Entity Description in Shelly light platform (home-assistant#154102)
1 parent bde4eb5 commit 3ce1ef4

File tree

3 files changed

+48
-32
lines changed

3 files changed

+48
-32
lines changed

homeassistant/components/shelly/light.py

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,15 @@
4040
)
4141
from .coordinator import ShellyBlockCoordinator, ShellyConfigEntry, ShellyRpcCoordinator
4242
from .entity import (
43+
BlockEntityDescription,
4344
RpcEntityDescription,
44-
ShellyBlockEntity,
45+
ShellyBlockAttributeEntity,
4546
ShellyRpcAttributeEntity,
47+
async_setup_entry_attribute_entities,
4648
async_setup_entry_rpc,
4749
)
4850
from .utils import (
4951
async_remove_orphaned_entities,
50-
async_remove_shelly_entity,
5152
brightness_to_percentage,
5253
get_device_entry_gen,
5354
is_block_channel_type_light,
@@ -58,6 +59,24 @@
5859
PARALLEL_UPDATES = 0
5960

6061

62+
@dataclass(frozen=True, kw_only=True)
63+
class BlockLightDescription(BlockEntityDescription, LightEntityDescription):
64+
"""Description for a Shelly BLOCK light entity."""
65+
66+
67+
BLOCK_LIGHTS = {
68+
("light", "output"): BlockLightDescription(
69+
key="light|output",
70+
),
71+
("relay", "output"): BlockLightDescription(
72+
key="relay|output",
73+
removal_condition=lambda settings, block: not is_block_channel_type_light(
74+
settings, block
75+
),
76+
),
77+
}
78+
79+
6180
async def async_setup_entry(
6281
hass: HomeAssistant,
6382
config_entry: ShellyConfigEntry,
@@ -79,36 +98,29 @@ def async_setup_block_entry(
7998
"""Set up entities for block device."""
8099
coordinator = config_entry.runtime_data.block
81100
assert coordinator
82-
blocks = []
83-
assert coordinator.device.blocks
84-
for block in coordinator.device.blocks:
85-
if block.type == "light":
86-
blocks.append(block)
87-
elif block.type == "relay" and block.channel is not None:
88-
if not is_block_channel_type_light(
89-
coordinator.device.settings, int(block.channel)
90-
):
91-
continue
92101

93-
blocks.append(block)
94-
unique_id = f"{coordinator.mac}-{block.type}_{block.channel}"
95-
async_remove_shelly_entity(hass, "switch", unique_id)
96-
97-
if not blocks:
98-
return
99-
100-
async_add_entities(BlockShellyLight(coordinator, block) for block in blocks)
102+
async_setup_entry_attribute_entities(
103+
hass, config_entry, async_add_entities, BLOCK_LIGHTS, BlockShellyLight
104+
)
101105

102106

103-
class BlockShellyLight(ShellyBlockEntity, LightEntity):
107+
class BlockShellyLight(ShellyBlockAttributeEntity, LightEntity):
104108
"""Entity that controls a light on block based Shelly devices."""
105109

110+
entity_description: BlockLightDescription
106111
_attr_supported_color_modes: set[str]
107112

108-
def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None:
109-
"""Initialize light."""
110-
super().__init__(coordinator, block)
113+
def __init__(
114+
self,
115+
coordinator: ShellyBlockCoordinator,
116+
block: Block,
117+
attribute: str,
118+
description: BlockLightDescription,
119+
) -> None:
120+
"""Initialize block light."""
121+
super().__init__(coordinator, block, attribute, description)
111122
self.control_result: dict[str, Any] | None = None
123+
self._attr_unique_id: str = f"{coordinator.mac}-{block.description}"
112124
self._attr_supported_color_modes = set()
113125
self._attr_min_color_temp_kelvin = KELVIN_MIN_VALUE_WHITE
114126
self._attr_max_color_temp_kelvin = KELVIN_MAX_VALUE
@@ -347,7 +359,7 @@ def _update_callback(self) -> None:
347359

348360
@dataclass(frozen=True, kw_only=True)
349361
class RpcLightDescription(RpcEntityDescription, LightEntityDescription):
350-
"""Description for a Shelly RPC number entity."""
362+
"""Description for a Shelly RPC light entity."""
351363

352364

353365
class RpcShellyLightBase(ShellyRpcAttributeEntity, LightEntity):

homeassistant/components/shelly/utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,7 @@ def is_block_exclude_from_relay(settings: dict[str, Any], block: Block) -> bool:
211211
if settings.get("mode") == "roller":
212212
return True
213213

214-
if TYPE_CHECKING:
215-
assert block.channel is not None
216-
217-
return is_block_channel_type_light(settings, int(block.channel))
214+
return is_block_channel_type_light(settings, block)
218215

219216

220217
def get_device_uptime(uptime: float, last_uptime: datetime | None) -> datetime:
@@ -501,9 +498,12 @@ def is_rpc_momentary_input(
501498
return cast(bool, config[key]["type"] == "button")
502499

503500

504-
def is_block_channel_type_light(settings: dict[str, Any], channel: int) -> bool:
501+
def is_block_channel_type_light(settings: dict[str, Any], block: Block) -> bool:
505502
"""Return true if block channel appliance type is set to light."""
506-
app_type = settings["relays"][channel].get("appliance_type")
503+
if TYPE_CHECKING:
504+
assert block.channel is not None
505+
506+
app_type = settings["relays"][int(block.channel)].get("appliance_type")
507507
return app_type is not None and app_type.lower().startswith("light")
508508

509509

tests/components/shelly/conftest.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,11 @@ def mock_white_light_set_state(
127127
),
128128
),
129129
Mock(
130-
sensor_ids={"mode": "color", "effect": 0},
130+
sensor_ids={
131+
"output": mock_light_set_state()["ison"],
132+
"mode": "color",
133+
"effect": 0,
134+
},
131135
channel="0",
132136
output=mock_light_set_state()["ison"],
133137
colorTemp=mock_light_set_state()["temp"],

0 commit comments

Comments
 (0)