Change naming of Shelly entities to correspond with HA guidelines (#97533)
* Do not use the device name to create the entity name * Remove unnecessary return * Fix mypy complains * Gen1 * Uncapitalize description.name if channel name is used * Fix for climate and button * switch_3 -> switch 3 * Add _attr_has_entity_name to ShellyRestAttributeEntity * Capitalize channel namepull/98682/head
parent
7059252164
commit
c526d23686
|
@ -154,6 +154,7 @@ class ShellyButton(
|
|||
entity_description: ShellyButtonDescription[
|
||||
ShellyRpcCoordinator | ShellyBlockCoordinator
|
||||
]
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -166,7 +167,6 @@ class ShellyButton(
|
|||
super().__init__(coordinator)
|
||||
self.entity_description = description
|
||||
|
||||
self._attr_name = f"{coordinator.device.name} {description.name}"
|
||||
self._attr_unique_id = f"{coordinator.mac}_{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, coordinator.mac)}
|
||||
|
|
|
@ -130,6 +130,7 @@ class BlockSleepingClimate(
|
|||
)
|
||||
_attr_target_temperature_step = SHTRV_01_TEMPERATURE_SETTINGS["step"]
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -173,11 +174,6 @@ class BlockSleepingClimate(
|
|||
"""Set unique id of entity."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Name of entity."""
|
||||
return self.coordinator.name
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
"""Set target temperature."""
|
||||
|
@ -354,7 +350,7 @@ class BlockSleepingClimate(
|
|||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key="device_not_calibrated",
|
||||
translation_placeholders={
|
||||
"device_name": self.name,
|
||||
"device_name": self.coordinator.name,
|
||||
"ip_address": self.coordinator.device.ip_address,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -321,6 +321,8 @@ class RestEntityDescription(EntityDescription):
|
|||
class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]):
|
||||
"""Helper class to represent a block entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, coordinator: ShellyBlockCoordinator, block: Block) -> None:
|
||||
"""Initialize Shelly entity."""
|
||||
super().__init__(coordinator)
|
||||
|
@ -359,6 +361,8 @@ class ShellyBlockEntity(CoordinatorEntity[ShellyBlockCoordinator]):
|
|||
class ShellyRpcEntity(CoordinatorEntity[ShellyRpcCoordinator]):
|
||||
"""Helper class to represent a rpc entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(self, coordinator: ShellyRpcCoordinator, key: str) -> None:
|
||||
"""Initialize Shelly entity."""
|
||||
super().__init__(coordinator)
|
||||
|
@ -462,6 +466,7 @@ class ShellyRestAttributeEntity(CoordinatorEntity[ShellyBlockCoordinator]):
|
|||
"""Class to load info from REST."""
|
||||
|
||||
entity_description: RestEntityDescription
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -42,7 +42,8 @@ def async_describe_events(
|
|||
rpc_coordinator = get_rpc_coordinator_by_device_id(hass, device_id)
|
||||
if rpc_coordinator and rpc_coordinator.device.initialized:
|
||||
key = f"input:{channel-1}"
|
||||
input_name = get_rpc_entity_name(rpc_coordinator.device, key)
|
||||
if iname := get_rpc_entity_name(rpc_coordinator.device, key):
|
||||
input_name = iname
|
||||
|
||||
elif click_type in BLOCK_INPUTS_EVENTS_TYPES:
|
||||
block_coordinator = get_block_coordinator_by_device_id(hass, device_id)
|
||||
|
|
|
@ -72,26 +72,26 @@ def get_block_entity_name(
|
|||
device: BlockDevice,
|
||||
block: Block | None,
|
||||
description: str | None = None,
|
||||
) -> str:
|
||||
) -> str | None:
|
||||
"""Naming for block based switch and sensors."""
|
||||
channel_name = get_block_channel_name(device, block)
|
||||
|
||||
if description and channel_name:
|
||||
return f"{channel_name} {uncapitalize(description)}"
|
||||
if description:
|
||||
return f"{channel_name} {description.lower()}"
|
||||
return description
|
||||
|
||||
return channel_name
|
||||
|
||||
|
||||
def get_block_channel_name(device: BlockDevice, block: Block | None) -> str:
|
||||
def get_block_channel_name(device: BlockDevice, block: Block | None) -> str | None:
|
||||
"""Get name based on device and channel name."""
|
||||
entity_name = device.name
|
||||
|
||||
if (
|
||||
not block
|
||||
or block.type == "device"
|
||||
or get_number_of_channels(device, block) == 1
|
||||
):
|
||||
return entity_name
|
||||
return None
|
||||
|
||||
assert block.channel
|
||||
|
||||
|
@ -108,7 +108,7 @@ def get_block_channel_name(device: BlockDevice, block: Block | None) -> str:
|
|||
else:
|
||||
base = ord("1")
|
||||
|
||||
return f"{entity_name} channel {chr(int(block.channel)+base)}"
|
||||
return f"Channel {chr(int(block.channel)+base)}"
|
||||
|
||||
|
||||
def is_block_momentary_input(
|
||||
|
@ -285,32 +285,32 @@ def get_model_name(info: dict[str, Any]) -> str:
|
|||
return cast(str, MODEL_NAMES.get(info["type"], info["type"]))
|
||||
|
||||
|
||||
def get_rpc_channel_name(device: RpcDevice, key: str) -> str:
|
||||
def get_rpc_channel_name(device: RpcDevice, key: str) -> str | None:
|
||||
"""Get name based on device and channel name."""
|
||||
key = key.replace("emdata", "em")
|
||||
if device.config.get("switch:0"):
|
||||
key = key.replace("input", "switch")
|
||||
device_name = device.name
|
||||
entity_name: str | None = None
|
||||
if key in device.config:
|
||||
entity_name = device.config[key].get("name", device_name)
|
||||
entity_name = device.config[key].get("name")
|
||||
|
||||
if entity_name is None:
|
||||
if key.startswith(("input:", "light:", "switch:")):
|
||||
return f"{device_name} {key.replace(':', '_')}"
|
||||
return device_name
|
||||
return key.replace(":", " ").capitalize()
|
||||
|
||||
return entity_name
|
||||
|
||||
|
||||
def get_rpc_entity_name(
|
||||
device: RpcDevice, key: str, description: str | None = None
|
||||
) -> str:
|
||||
) -> str | None:
|
||||
"""Naming for RPC based switch and sensors."""
|
||||
channel_name = get_rpc_channel_name(device, key)
|
||||
|
||||
if description and channel_name:
|
||||
return f"{channel_name} {uncapitalize(description)}"
|
||||
if description:
|
||||
return f"{channel_name} {description.lower()}"
|
||||
return description
|
||||
|
||||
return channel_name
|
||||
|
||||
|
@ -405,3 +405,8 @@ def mac_address_from_name(name: str) -> str | None:
|
|||
"""Convert a name to a mac address."""
|
||||
mac = name.partition(".")[0].partition("-")[-1]
|
||||
return mac.upper() if len(mac) == 12 else None
|
||||
|
||||
|
||||
def uncapitalize(description: str) -> str:
|
||||
"""Uncapitalize the first letter of a description."""
|
||||
return description[:1].lower() + description[1:]
|
||||
|
|
|
@ -165,7 +165,7 @@ async def test_rpc_binary_sensor(
|
|||
hass: HomeAssistant, mock_rpc_device, monkeypatch
|
||||
) -> None:
|
||||
"""Test RPC binary sensor."""
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_cover_0_overpowering"
|
||||
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_test_cover_0_overpowering"
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
|
|
@ -353,7 +353,7 @@ async def test_rpc_reload_on_cfg_change(
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_switch_0") is not None
|
||||
assert hass.states.get("switch.test_name_test_switch_0") is not None
|
||||
|
||||
# Wait for debouncer
|
||||
async_fire_time_changed(
|
||||
|
@ -361,7 +361,7 @@ async def test_rpc_reload_on_cfg_change(
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_switch_0") is None
|
||||
assert hass.states.get("switch.test_name_test_switch_0") is None
|
||||
|
||||
|
||||
async def test_rpc_reload_with_invalid_auth(
|
||||
|
@ -588,7 +588,7 @@ async def test_rpc_reconnect_error(
|
|||
"""Test RPC reconnect error."""
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert hass.states.get("switch.test_switch_0").state == STATE_ON
|
||||
assert hass.states.get("switch.test_name_test_switch_0").state == STATE_ON
|
||||
|
||||
monkeypatch.setattr(mock_rpc_device, "connected", False)
|
||||
monkeypatch.setattr(
|
||||
|
@ -605,7 +605,7 @@ async def test_rpc_reconnect_error(
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("switch.test_switch_0").state == STATE_UNAVAILABLE
|
||||
assert hass.states.get("switch.test_name_test_switch_0").state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_rpc_polling_connection_error(
|
||||
|
|
|
@ -97,10 +97,10 @@ async def test_rpc_device_services(
|
|||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_SET_COVER_POSITION,
|
||||
{ATTR_ENTITY_ID: "cover.test_cover_0", ATTR_POSITION: 50},
|
||||
{ATTR_ENTITY_ID: "cover.test_name_test_cover_0", ATTR_POSITION: 50},
|
||||
blocking=True,
|
||||
)
|
||||
state = hass.states.get("cover.test_cover_0")
|
||||
state = hass.states.get("cover.test_name_test_cover_0")
|
||||
assert state.attributes[ATTR_CURRENT_POSITION] == 50
|
||||
|
||||
mutate_rpc_device_status(
|
||||
|
@ -109,11 +109,11 @@ async def test_rpc_device_services(
|
|||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_OPEN_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.test_cover_0"},
|
||||
{ATTR_ENTITY_ID: "cover.test_name_test_cover_0"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_rpc_device.mock_update()
|
||||
assert hass.states.get("cover.test_cover_0").state == STATE_OPENING
|
||||
assert hass.states.get("cover.test_name_test_cover_0").state == STATE_OPENING
|
||||
|
||||
mutate_rpc_device_status(
|
||||
monkeypatch, mock_rpc_device, "cover:0", "state", "closing"
|
||||
|
@ -121,21 +121,21 @@ async def test_rpc_device_services(
|
|||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_CLOSE_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.test_cover_0"},
|
||||
{ATTR_ENTITY_ID: "cover.test_name_test_cover_0"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_rpc_device.mock_update()
|
||||
assert hass.states.get("cover.test_cover_0").state == STATE_CLOSING
|
||||
assert hass.states.get("cover.test_name_test_cover_0").state == STATE_CLOSING
|
||||
|
||||
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "closed")
|
||||
await hass.services.async_call(
|
||||
COVER_DOMAIN,
|
||||
SERVICE_STOP_COVER,
|
||||
{ATTR_ENTITY_ID: "cover.test_cover_0"},
|
||||
{ATTR_ENTITY_ID: "cover.test_name_test_cover_0"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_rpc_device.mock_update()
|
||||
assert hass.states.get("cover.test_cover_0").state == STATE_CLOSED
|
||||
assert hass.states.get("cover.test_name_test_cover_0").state == STATE_CLOSED
|
||||
|
||||
|
||||
async def test_rpc_device_no_cover_keys(
|
||||
|
@ -144,7 +144,7 @@ async def test_rpc_device_no_cover_keys(
|
|||
"""Test RPC device without cover keys."""
|
||||
monkeypatch.delitem(mock_rpc_device.status, "cover:0")
|
||||
await init_integration(hass, 2)
|
||||
assert hass.states.get("cover.test_cover_0") is None
|
||||
assert hass.states.get("cover.test_name_test_cover_0") is None
|
||||
|
||||
|
||||
async def test_rpc_device_update(
|
||||
|
@ -153,11 +153,11 @@ async def test_rpc_device_update(
|
|||
"""Test RPC device update."""
|
||||
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "closed")
|
||||
await init_integration(hass, 2)
|
||||
assert hass.states.get("cover.test_cover_0").state == STATE_CLOSED
|
||||
assert hass.states.get("cover.test_name_test_cover_0").state == STATE_CLOSED
|
||||
|
||||
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cover:0", "state", "open")
|
||||
mock_rpc_device.mock_update()
|
||||
assert hass.states.get("cover.test_cover_0").state == STATE_OPEN
|
||||
assert hass.states.get("cover.test_name_test_cover_0").state == STATE_OPEN
|
||||
|
||||
|
||||
async def test_rpc_device_no_position_control(
|
||||
|
@ -168,4 +168,4 @@ async def test_rpc_device_no_position_control(
|
|||
monkeypatch, mock_rpc_device, "cover:0", "pos_control", False
|
||||
)
|
||||
await init_integration(hass, 2)
|
||||
assert hass.states.get("cover.test_cover_0").state == STATE_OPEN
|
||||
assert hass.states.get("cover.test_name_test_cover_0").state == STATE_OPEN
|
||||
|
|
|
@ -175,7 +175,7 @@ async def test_sleeping_rpc_device_online_new_firmware(
|
|||
("gen", "entity_id"),
|
||||
[
|
||||
(1, "switch.test_name_channel_1"),
|
||||
(2, "switch.test_switch_0"),
|
||||
(2, "switch.test_name_test_switch_0"),
|
||||
],
|
||||
)
|
||||
async def test_entry_unload(
|
||||
|
@ -198,7 +198,7 @@ async def test_entry_unload(
|
|||
("gen", "entity_id"),
|
||||
[
|
||||
(1, "switch.test_name_channel_1"),
|
||||
(2, "switch.test_switch_0"),
|
||||
(2, "switch.test_name_test_switch_0"),
|
||||
],
|
||||
)
|
||||
async def test_entry_unload_device_not_ready(
|
||||
|
@ -226,7 +226,7 @@ async def test_entry_unload_not_connected(
|
|||
entry = await init_integration(
|
||||
hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE}
|
||||
)
|
||||
entity_id = "switch.test_switch_0"
|
||||
entity_id = "switch.test_name_test_switch_0"
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert hass.states.get(entity_id).state is STATE_ON
|
||||
|
@ -252,7 +252,7 @@ async def test_entry_unload_not_connected_but_we_think_we_are(
|
|||
entry = await init_integration(
|
||||
hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE}
|
||||
)
|
||||
entity_id = "switch.test_switch_0"
|
||||
entity_id = "switch.test_name_test_switch_0"
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert hass.states.get(entity_id).state is STATE_ON
|
||||
|
|
|
@ -385,25 +385,25 @@ async def test_rpc_device_switch_type_lights_mode(
|
|||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "light.test_switch_0"},
|
||||
{ATTR_ENTITY_ID: "light.test_name_test_switch_0"},
|
||||
blocking=True,
|
||||
)
|
||||
assert hass.states.get("light.test_switch_0").state == STATE_ON
|
||||
assert hass.states.get("light.test_name_test_switch_0").state == STATE_ON
|
||||
|
||||
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "switch:0", "output", False)
|
||||
await hass.services.async_call(
|
||||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "light.test_switch_0"},
|
||||
{ATTR_ENTITY_ID: "light.test_name_test_switch_0"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_rpc_device.mock_update()
|
||||
assert hass.states.get("light.test_switch_0").state == STATE_OFF
|
||||
assert hass.states.get("light.test_name_test_switch_0").state == STATE_OFF
|
||||
|
||||
|
||||
async def test_rpc_light(hass: HomeAssistant, mock_rpc_device, monkeypatch) -> None:
|
||||
"""Test RPC light."""
|
||||
entity_id = f"{LIGHT_DOMAIN}.test_light_0"
|
||||
entity_id = f"{LIGHT_DOMAIN}.test_name_test_light_0"
|
||||
monkeypatch.delitem(mock_rpc_device.status, "switch:0")
|
||||
await init_integration(hass, 2)
|
||||
|
||||
|
|
|
@ -262,7 +262,7 @@ async def test_block_sensor_unknown_value(
|
|||
|
||||
async def test_rpc_sensor(hass: HomeAssistant, mock_rpc_device, monkeypatch) -> None:
|
||||
"""Test RPC sensor."""
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_cover_0_power"
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_test_cover_0_power"
|
||||
await init_integration(hass, 2)
|
||||
|
||||
assert hass.states.get(entity_id).state == "85.3"
|
||||
|
|
|
@ -149,20 +149,20 @@ async def test_rpc_device_services(
|
|||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "switch.test_switch_0"},
|
||||
{ATTR_ENTITY_ID: "switch.test_name_test_switch_0"},
|
||||
blocking=True,
|
||||
)
|
||||
assert hass.states.get("switch.test_switch_0").state == STATE_ON
|
||||
assert hass.states.get("switch.test_name_test_switch_0").state == STATE_ON
|
||||
|
||||
monkeypatch.setitem(mock_rpc_device.status["switch:0"], "output", False)
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "switch.test_switch_0"},
|
||||
{ATTR_ENTITY_ID: "switch.test_name_test_switch_0"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_rpc_device.mock_update()
|
||||
assert hass.states.get("switch.test_switch_0").state == STATE_OFF
|
||||
assert hass.states.get("switch.test_name_test_switch_0").state == STATE_OFF
|
||||
|
||||
|
||||
async def test_rpc_device_switch_type_lights_mode(
|
||||
|
@ -173,7 +173,7 @@ async def test_rpc_device_switch_type_lights_mode(
|
|||
mock_rpc_device.config["sys"]["ui_data"], "consumption_types", ["lights"]
|
||||
)
|
||||
await init_integration(hass, 2)
|
||||
assert hass.states.get("switch.test_switch_0") is None
|
||||
assert hass.states.get("switch.test_name_test_switch_0") is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exc", [DeviceConnectionError, RpcCallError(-1, "error")])
|
||||
|
@ -188,7 +188,7 @@ async def test_rpc_set_state_errors(
|
|||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "switch.test_switch_0"},
|
||||
{ATTR_ENTITY_ID: "switch.test_name_test_switch_0"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
@ -209,7 +209,7 @@ async def test_rpc_auth_error(
|
|||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "switch.test_switch_0"},
|
||||
{ATTR_ENTITY_ID: "switch.test_name_test_switch_0"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
|
|
@ -58,7 +58,7 @@ async def test_block_get_block_channel_name(mock_block_device, monkeypatch) -> N
|
|||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
== "Test name channel 1"
|
||||
== "Channel 1"
|
||||
)
|
||||
|
||||
monkeypatch.setitem(mock_block_device.settings["device"], "type", "SHEM-3")
|
||||
|
@ -68,7 +68,7 @@ async def test_block_get_block_channel_name(mock_block_device, monkeypatch) -> N
|
|||
mock_block_device,
|
||||
mock_block_device.blocks[DEVICE_BLOCK_ID],
|
||||
)
|
||||
== "Test name channel A"
|
||||
== "Channel A"
|
||||
)
|
||||
|
||||
monkeypatch.setitem(
|
||||
|
@ -207,7 +207,7 @@ async def test_get_block_input_triggers(mock_block_device, monkeypatch) -> None:
|
|||
async def test_get_rpc_channel_name(mock_rpc_device) -> None:
|
||||
"""Test get RPC channel name."""
|
||||
assert get_rpc_channel_name(mock_rpc_device, "input:0") == "test switch_0"
|
||||
assert get_rpc_channel_name(mock_rpc_device, "input:3") == "Test name switch_3"
|
||||
assert get_rpc_channel_name(mock_rpc_device, "input:3") == "Switch 3"
|
||||
|
||||
|
||||
async def test_get_rpc_input_triggers(mock_rpc_device, monkeypatch) -> None:
|
||||
|
|
Loading…
Reference in New Issue