Skip to content

Commit 31ca332

Browse files
authored
Increase Shelly code coverage for Gen1 EM3 (home-assistant#156752)
Signed-off-by: David Rapan <[email protected]>
1 parent bf76c16 commit 31ca332

File tree

4 files changed

+73
-27
lines changed

4 files changed

+73
-27
lines changed

homeassistant/components/shelly/utils.py

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -424,23 +424,30 @@ def get_rpc_channel_name(device: RpcDevice, key: str) -> str | None:
424424
if BLU_TRV_IDENTIFIER in key:
425425
return None
426426

427-
component = key.split(":")[0]
428-
component_id = key.split(":")[-1]
427+
_, component, component_id = get_rpc_key(key)
429428

430429
if custom_name := get_rpc_custom_name(device, key):
431430
if component in (*VIRTUAL_COMPONENTS, "input", "presencezone", "script"):
432431
return custom_name
433432

434-
channels = get_rpc_number_of_channels(device, component)
435-
436-
return custom_name if channels == 1 else None
433+
return (
434+
custom_name if get_rpc_number_of_channels(device, component) == 1 else None
435+
)
437436

438437
if component in (*VIRTUAL_COMPONENTS, "input"):
439438
return f"{component.title()} {component_id}"
440439

441440
return None
442441

443442

443+
def get_rpc_key_normalized(key: str) -> str:
444+
"""Get normalized key. Workaround for Pro EM50 and Pro 3EM."""
445+
# workaround for Pro EM50
446+
key = key.replace("em1data", "em1")
447+
# workaround for Pro 3EM
448+
return key.replace("emdata", "em")
449+
450+
444451
def get_rpc_sub_device_name(
445452
device: RpcDevice, key: str, emeter_phase: str | None = None
446453
) -> str:
@@ -455,11 +462,7 @@ def get_rpc_sub_device_name(
455462
if entity_name := device.config[key].get("name"):
456463
return cast(str, entity_name)
457464

458-
key = key.replace("emdata", "em")
459-
key = key.replace("em1data", "em1")
460-
461-
component = key.split(":")[0]
462-
component_id = key.split(":")[-1]
465+
_, component, component_id = get_rpc_key(get_rpc_key_normalized(key))
463466

464467
if component in ("cct", "rgb", "rgbw"):
465468
return f"{device.name} {component.upper()} light {component_id}"
@@ -528,7 +531,7 @@ def get_rpc_key_instances(
528531

529532
def get_rpc_key_ids(keys_dict: dict[str, Any], key: str) -> list[int]:
530533
"""Return list of key ids for RPC device from a dict."""
531-
return [int(k.split(":")[1]) for k in keys_dict if k.startswith(f"{key}:")]
534+
return [get_rpc_key_id(k) for k in keys_dict if k.startswith(f"{key}:")]
532535

533536

534537
def get_rpc_key_by_role(keys_dict: dict[str, Any], role: str) -> str | None:
@@ -810,11 +813,10 @@ def is_rpc_exclude_from_relay(
810813
settings: dict[str, Any], status: dict[str, Any], channel: str
811814
) -> bool:
812815
"""Return true if rpc channel should be excludeed from switch platform."""
813-
ch = int(channel.split(":")[1])
814816
if is_rpc_thermostat_internal_actuator(status):
815817
return True
816818

817-
return is_rpc_channel_type_light(settings, ch)
819+
return is_rpc_channel_type_light(settings, get_rpc_key_id(channel))
818820

819821

820822
def get_shelly_air_lamp_life(lamp_seconds: int) -> float:
@@ -836,7 +838,7 @@ async def get_rpc_scripts_event_types(
836838
if script_name in ignore_scripts:
837839
continue
838840

839-
script_id = int(script.split(":")[-1])
841+
script_id = get_rpc_key_id(script)
840842
script_events[script_id] = await get_rpc_script_event_types(device, script_id)
841843

842844
return script_events
@@ -867,14 +869,8 @@ def get_rpc_device_info(
867869
if key is None:
868870
return DeviceInfo(connections={(CONNECTION_NETWORK_MAC, mac)})
869871

870-
# workaround for Pro EM50
871-
key = key.replace("em1data", "em1")
872-
# workaround for Pro 3EM
873-
key = key.replace("emdata", "em")
874-
875-
key_parts = key.split(":")
876-
component = key_parts[0]
877-
idx = key_parts[1] if len(key_parts) > 1 else None
872+
key = get_rpc_key_normalized(key)
873+
has_id, component, _ = get_rpc_key(key)
878874

879875
if emeter_phase is not None:
880876
return DeviceInfo(
@@ -893,7 +889,7 @@ def get_rpc_device_info(
893889
component not in (*All_LIGHT_TYPES, "cover", "em1", "switch")
894890
and get_irrigation_zone_id(device, key) is None
895891
)
896-
or idx is None
892+
or not has_id
897893
or get_rpc_number_of_channels(device, component) < 2
898894
):
899895
return DeviceInfo(connections={(CONNECTION_NETWORK_MAC, mac)})

tests/components/shelly/test_event.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Tests for Shelly button platform."""
22

3-
import copy
3+
from copy import deepcopy
44
from unittest.mock import Mock
55

66
from aioshelly.ble.const import BLE_SCRIPT_NAME
@@ -264,7 +264,7 @@ async def test_block_event_shix3_1(
264264
hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch
265265
) -> None:
266266
"""Test block device event for SHIX3-1."""
267-
blocks = copy.deepcopy(MOCK_BLOCKS)
267+
blocks = deepcopy(MOCK_BLOCKS)
268268
blocks[0] = Mock(
269269
sensor_ids={
270270
"inputEvent": "S",

tests/components/shelly/test_sensor.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from copy import deepcopy
44
from unittest.mock import Mock, PropertyMock
55

6-
from aioshelly.const import MODEL_BLU_GATEWAY_G3
6+
from aioshelly.const import MODEL_BLU_GATEWAY_G3, MODEL_EM3
77
from aioshelly.exceptions import NotInitialized
88
from freezegun.api import FrozenDateTimeFactory
99
import pytest
@@ -53,7 +53,7 @@
5353
register_device,
5454
register_entity,
5555
)
56-
from .conftest import MOCK_CONFIG, MOCK_SHELLY_RPC, MOCK_STATUS_RPC
56+
from .conftest import MOCK_BLOCKS, MOCK_CONFIG, MOCK_SHELLY_RPC, MOCK_STATUS_RPC
5757

5858
from tests.common import (
5959
async_fire_time_changed,
@@ -97,6 +97,55 @@ async def test_block_sensor(
9797
assert entry.unique_id == "123456789ABC-relay_0-power"
9898

9999

100+
async def test_block_sensor_em3(
101+
hass: HomeAssistant, mock_block_device: Mock, monkeypatch: pytest.MonkeyPatch
102+
) -> None:
103+
"""Test block sensor of EM3."""
104+
monkeypatch.setitem(mock_block_device.shelly, "num_emeters", 3)
105+
monkeypatch.setitem(mock_block_device.settings["device"], "type", MODEL_EM3)
106+
monkeypatch.setitem(
107+
mock_block_device.settings,
108+
"emeters",
109+
[
110+
{"name": "Grid L1", "appliance_type": "General", "max_power": 0},
111+
{"appliance_type": "General", "max_power": 0},
112+
{"appliance_type": "General", "max_power": 0},
113+
],
114+
)
115+
blocks = deepcopy(MOCK_BLOCKS)
116+
blocks[5] = Mock(
117+
sensor_ids={"power": 20},
118+
channel="0",
119+
power=20,
120+
description="emeter_0",
121+
type="emeter",
122+
)
123+
blocks.append(
124+
Mock(
125+
sensor_ids={"power": 20},
126+
channel="1",
127+
power=20,
128+
description="emeter_1",
129+
type="emeter",
130+
)
131+
)
132+
blocks.append(
133+
Mock(
134+
sensor_ids={"power": 20},
135+
channel="2",
136+
power=20,
137+
description="emeter_2",
138+
type="emeter",
139+
)
140+
)
141+
monkeypatch.setattr(mock_block_device, "blocks", blocks)
142+
await init_integration(hass, 1, model=MODEL_EM3)
143+
144+
assert hass.states.get(f"{SENSOR_DOMAIN}.grid_l1_power")
145+
assert hass.states.get(f"{SENSOR_DOMAIN}.test_name_phase_b_power")
146+
assert hass.states.get(f"{SENSOR_DOMAIN}.test_name_phase_c_power")
147+
148+
100149
async def test_energy_sensor(
101150
hass: HomeAssistant, mock_block_device: Mock, entity_registry: EntityRegistry
102151
) -> None:

tests/components/shelly/test_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ async def test_is_block_momentary_input(
147147
is False
148148
)
149149

150+
monkeypatch.delitem(mock_block_device.settings, "inputs")
150151
monkeypatch.delitem(mock_block_device.settings, "relays")
151152
monkeypatch.delitem(mock_block_device.settings, "rollers")
152153
assert (

0 commit comments

Comments
 (0)