Skip to content

Commit bf76c16

Browse files
authored
Align Shelly event naming paradigm (home-assistant#156774)
Signed-off-by: David Rapan <[email protected]>
1 parent e572f8d commit bf76c16

File tree

4 files changed

+92
-40
lines changed

4 files changed

+92
-40
lines changed

homeassistant/components/shelly/event.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,16 @@
3232
async_remove_shelly_entity,
3333
get_block_channel,
3434
get_block_custom_name,
35+
get_block_number_of_channels,
3536
get_device_entry_gen,
36-
get_rpc_component_name,
37+
get_rpc_custom_name,
3738
get_rpc_entity_name,
39+
get_rpc_key,
40+
get_rpc_key_id,
3841
get_rpc_key_instances,
42+
get_rpc_number_of_channels,
3943
is_block_momentary_input,
44+
is_block_single_device,
4045
is_rpc_momentary_input,
4146
)
4247

@@ -158,8 +163,7 @@ def _async_setup_rpc_entry(
158163
if script_name == BLE_SCRIPT_NAME:
159164
continue
160165

161-
script_id = int(script.split(":")[-1])
162-
if script_events and (event_types := script_events[script_id]):
166+
if script_events and (event_types := script_events[get_rpc_key_id(script)]):
163167
entities.append(ShellyRpcScriptEvent(coordinator, script, event_types))
164168

165169
# If a script is removed, from the device configuration, we need to remove orphaned entities
@@ -197,13 +201,15 @@ def __init__(
197201
self._attr_event_types = list(BASIC_INPUTS_EVENTS_TYPES)
198202
self.entity_description = description
199203

200-
if (
201-
hasattr(self, "_attr_name")
202-
and self._attr_name
203-
and not get_block_custom_name(coordinator.device, block)
204+
if hasattr(self, "_attr_name") and not (
205+
(single := is_block_single_device(coordinator.device, block))
206+
and get_block_custom_name(coordinator.device, block)
204207
):
205208
self._attr_translation_placeholders = {
206209
"input_number": get_block_channel(block)
210+
if single
211+
and get_block_number_of_channels(coordinator.device, block) > 1
212+
else ""
207213
}
208214

209215
delattr(self, "_attr_name")
@@ -237,22 +243,24 @@ def __init__(
237243
) -> None:
238244
"""Initialize Shelly entity."""
239245
super().__init__(coordinator)
240-
self.event_id = int(key.split(":")[-1])
241246
self._attr_device_info = get_entity_rpc_device_info(coordinator, key)
242247
self._attr_unique_id = f"{coordinator.mac}-{key}"
243248
self.entity_description = description
244249

245250
if description.key == "input":
246-
component = key.split(":")[0]
247-
component_id = key.split(":")[-1]
248-
if not get_rpc_component_name(coordinator.device, key) and (
249-
component.lower() == "input" and component_id.isnumeric()
250-
):
251-
self._attr_translation_placeholders = {"input_number": component_id}
251+
_, component, component_id = get_rpc_key(key)
252+
if not get_rpc_custom_name(coordinator.device, key):
253+
self._attr_translation_placeholders = {
254+
"input_number": component_id
255+
if get_rpc_number_of_channels(coordinator.device, component) > 1
256+
else ""
257+
}
252258
else:
253259
self._attr_name = get_rpc_entity_name(coordinator.device, key)
260+
self.event_id = int(component_id)
254261
elif description.key == "script":
255262
self._attr_name = get_rpc_entity_name(coordinator.device, key)
263+
self.event_id = get_rpc_key_id(key)
256264

257265
async def async_added_to_hass(self) -> None:
258266
"""When entity is added to hass."""

homeassistant/components/shelly/utils.py

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ def async_remove_shelly_entity(
9393
entity_reg.async_remove(entity_id)
9494

9595

96-
def get_number_of_channels(device: BlockDevice, block: Block) -> int:
97-
"""Get number of channels for block type."""
96+
def get_block_number_of_channels(device: BlockDevice, block: Block) -> int:
97+
"""Get number of channels."""
9898
channels = None
9999

100100
if block.type == "input":
@@ -154,7 +154,7 @@ def get_block_channel_name(device: BlockDevice, block: Block | None) -> str | No
154154
if (
155155
not block
156156
or block.type in ("device", "light", "relay", "emeter")
157-
or get_number_of_channels(device, block) == 1
157+
or get_block_number_of_channels(device, block) == 1
158158
):
159159
return None
160160

@@ -253,7 +253,7 @@ def get_block_input_triggers(
253253
if not is_block_momentary_input(device.settings, block, True):
254254
return []
255255

256-
if block.type == "device" or get_number_of_channels(device, block) == 1:
256+
if block.type == "device" or get_block_number_of_channels(device, block) == 1:
257257
subtype = "button"
258258
else:
259259
assert block.channel
@@ -397,8 +397,13 @@ def get_rpc_key(value: str) -> tuple[bool, str, str]:
397397
return len(parts) > 1, parts[0], parts[-1]
398398

399399

400+
def get_rpc_key_id(value: str) -> int:
401+
"""Get id from device key."""
402+
return int(get_rpc_key(value)[-1])
403+
404+
400405
def get_rpc_custom_name(device: RpcDevice, key: str) -> str | None:
401-
"""Get component name from device config."""
406+
"""Get custom name from device config."""
402407
if (
403408
key in device.config
404409
and key != "em:0" # workaround for Pro 3EM, we don't want to get name for em:0
@@ -409,27 +414,26 @@ def get_rpc_custom_name(device: RpcDevice, key: str) -> str | None:
409414
return None
410415

411416

412-
def get_rpc_component_name(device: RpcDevice, key: str) -> str | None:
413-
"""Get component name from device config."""
414-
return get_rpc_custom_name(device, key)
417+
def get_rpc_number_of_channels(device: RpcDevice, component: str) -> int:
418+
"""Get number of channels."""
419+
return len(get_rpc_key_instances(device.status, component, all_lights=True))
415420

416421

417422
def get_rpc_channel_name(device: RpcDevice, key: str) -> str | None:
418423
"""Get name based on device and channel name."""
419424
if BLU_TRV_IDENTIFIER in key:
420425
return None
421426

422-
instances = len(
423-
get_rpc_key_instances(device.status, key.split(":")[0], all_lights=True)
424-
)
425427
component = key.split(":")[0]
426428
component_id = key.split(":")[-1]
427429

428430
if custom_name := get_rpc_custom_name(device, key):
429431
if component in (*VIRTUAL_COMPONENTS, "input", "presencezone", "script"):
430432
return custom_name
431433

432-
return custom_name if instances == 1 else None
434+
channels = get_rpc_number_of_channels(device, component)
435+
436+
return custom_name if channels == 1 else None
433437

434438
if component in (*VIRTUAL_COMPONENTS, "input"):
435439
return f"{component.title()} {component_id}"
@@ -890,7 +894,7 @@ def get_rpc_device_info(
890894
and get_irrigation_zone_id(device, key) is None
891895
)
892896
or idx is None
893-
or len(get_rpc_key_instances(device.status, component, all_lights=True)) < 2
897+
or get_rpc_number_of_channels(device, component) < 2
894898
):
895899
return DeviceInfo(connections={(CONNECTION_NETWORK_MAC, mac)})
896900

@@ -923,6 +927,15 @@ def get_blu_trv_device_info(
923927
)
924928

925929

930+
def is_block_single_device(device: BlockDevice, block: Block | None = None) -> bool:
931+
"""Return true if block is single device."""
932+
return (
933+
block is None
934+
or block.type not in ("light", "relay", "emeter")
935+
or device.settings.get("mode") == "roller"
936+
)
937+
938+
926939
def get_block_device_info(
927940
device: BlockDevice,
928941
mac: str,
@@ -933,14 +946,14 @@ def get_block_device_info(
933946
suggested_area: str | None = None,
934947
) -> DeviceInfo:
935948
"""Return device info for Block device."""
936-
if (
937-
block is None
938-
or block.type not in ("light", "relay", "emeter")
939-
or device.settings.get("mode") == "roller"
940-
or get_number_of_channels(device, block) < 2
949+
if is_block_single_device(device, block) or (
950+
block is not None and get_block_number_of_channels(device, block) < 2
941951
):
942952
return DeviceInfo(connections={(CONNECTION_NETWORK_MAC, mac)})
943953

954+
if TYPE_CHECKING:
955+
assert block
956+
944957
return DeviceInfo(
945958
identifiers={(DOMAIN, f"{mac}-{block.description}")},
946959
name=get_block_sub_device_name(device, block),

tests/components/shelly/test_event.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ async def test_block_event(
195195
"""Test block device event."""
196196
await init_integration(hass, 1)
197197
# num_outputs is 2, device name and channel name is used
198-
entity_id = "event.test_name_channel_1"
198+
entity_id = "event.test_name_channel_1_input"
199199

200200
assert (state := hass.states.get(entity_id))
201201
assert state.state == STATE_UNKNOWN
@@ -226,7 +226,38 @@ async def test_block_event_single_output(
226226
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
227227
await init_integration(hass, 1)
228228

229-
assert hass.states.get("event.test_name")
229+
assert hass.states.get("event.test_name_input")
230+
231+
232+
async def test_block_event_custom_name(
233+
hass: HomeAssistant,
234+
monkeypatch: pytest.MonkeyPatch,
235+
mock_block_device: Mock,
236+
) -> None:
237+
"""Test block device event with custom name."""
238+
monkeypatch.setitem(
239+
mock_block_device.settings,
240+
"relays",
241+
[{"name": "test channel", "btn_type": "momentary"}, {"btn_type": "toggle"}],
242+
)
243+
await init_integration(hass, 1)
244+
# num_outputs is 2, device name and custom name is used
245+
assert hass.states.get("event.test_channel_input")
246+
247+
248+
async def test_block_event_custom_name_single_output(
249+
hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch
250+
) -> None:
251+
"""Test block device event with custom name when num_outputs is 1."""
252+
monkeypatch.setitem(mock_block_device.shelly, "num_outputs", 1)
253+
monkeypatch.setitem(
254+
mock_block_device.settings,
255+
"relays",
256+
[{"name": "test channel", "btn_type": "momentary"}, {"btn_type": "toggle"}],
257+
)
258+
await init_integration(hass, 1)
259+
260+
assert hass.states.get("event.test_name_input")
230261

231262

232263
async def test_block_event_shix3_1(

tests/components/shelly/test_utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
get_block_channel_name,
2828
get_block_device_sleep_period,
2929
get_block_input_triggers,
30+
get_block_number_of_channels,
3031
get_device_uptime,
3132
get_host,
32-
get_number_of_channels,
3333
get_release_url,
3434
get_rpc_channel_name,
3535
get_rpc_input_triggers,
@@ -41,15 +41,15 @@
4141
DEVICE_BLOCK_ID = 4
4242

4343

44-
async def test_block_get_number_of_channels(
44+
async def test_block_get_block_number_of_channels(
4545
mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch
4646
) -> None:
47-
"""Test block get number of channels."""
47+
"""Test block get block number of channels."""
4848
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "type", "emeter")
4949
monkeypatch.setitem(mock_block_device.shelly, "num_emeters", 3)
5050

5151
assert (
52-
get_number_of_channels(
52+
get_block_number_of_channels(
5353
mock_block_device,
5454
mock_block_device.blocks[DEVICE_BLOCK_ID],
5555
)
@@ -59,7 +59,7 @@ async def test_block_get_number_of_channels(
5959
monkeypatch.setitem(mock_block_device.shelly, "num_inputs", 4)
6060
monkeypatch.setattr(mock_block_device.blocks[DEVICE_BLOCK_ID], "type", "input")
6161
assert (
62-
get_number_of_channels(
62+
get_block_number_of_channels(
6363
mock_block_device,
6464
mock_block_device.blocks[DEVICE_BLOCK_ID],
6565
)
@@ -68,7 +68,7 @@ async def test_block_get_number_of_channels(
6868

6969
monkeypatch.setitem(mock_block_device.settings["device"], "type", MODEL_DIMMER_2)
7070
assert (
71-
get_number_of_channels(
71+
get_block_number_of_channels(
7272
mock_block_device,
7373
mock_block_device.blocks[DEVICE_BLOCK_ID],
7474
)

0 commit comments

Comments
 (0)