From eb04dda19791c4a57fbf767fc70a99ac982a4bd4 Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Fri, 10 Oct 2025 10:24:08 +0300 Subject: [PATCH] Use Entity Description in Shelly BLU TRV button (#154118) --- homeassistant/components/shelly/button.py | 108 ++++++++++------------ tests/components/shelly/test_button.py | 2 +- 2 files changed, 48 insertions(+), 62 deletions(-) diff --git a/homeassistant/components/shelly/button.py b/homeassistant/components/shelly/button.py index 4c6d5695f335..cf467209c396 100644 --- a/homeassistant/components/shelly/button.py +++ b/homeassistant/components/shelly/button.py @@ -97,36 +97,6 @@ BUTTONS: Final[list[ShellyButtonDescription[Any]]] = [ ), ] -BLU_TRV_BUTTONS: Final[list[ShellyButtonDescription]] = [ - ShellyButtonDescription[ShellyRpcCoordinator]( - key="calibrate", - name="Calibrate", - translation_key="calibrate", - entity_category=EntityCategory.CONFIG, - press_action="trigger_blu_trv_calibration", - supported=lambda coordinator: coordinator.model == MODEL_BLU_GATEWAY_G3, - ), -] - -RPC_VIRTUAL_BUTTONS = { - "button_generic": RpcButtonDescription( - key="button", - role="generic", - ), - "button_open": RpcButtonDescription( - key="button", - entity_registry_enabled_default=False, - role="open", - models={MODEL_FRANKEVER_WATER_VALVE}, - ), - "button_close": RpcButtonDescription( - key="button", - entity_registry_enabled_default=False, - role="close", - models={MODEL_FRANKEVER_WATER_VALVE}, - ), -} - @callback def async_migrate_unique_ids( @@ -218,7 +188,7 @@ async def async_setup_entry( hass, config_entry.entry_id, partial(async_migrate_unique_ids, coordinator) ) - entities: list[ShellyButton | ShellyBluTrvButton] = [] + entities: list[ShellyButton] = [] entities.extend( ShellyButton(coordinator, button) @@ -226,26 +196,16 @@ async def async_setup_entry( if button.supported(coordinator) ) + async_add_entities(entities) + if not isinstance(coordinator, ShellyRpcCoordinator): - async_add_entities(entities) return - # add virtual buttons + # add RPC buttons async_setup_entry_rpc( - hass, config_entry, async_add_entities, RPC_VIRTUAL_BUTTONS, RpcVirtualButton + hass, config_entry, async_add_entities, RPC_BUTTONS, RpcVirtualButton ) - # add BLU TRV buttons - if blutrv_key_ids := get_rpc_key_ids(coordinator.device.status, BLU_TRV_IDENTIFIER): - entities.extend( - ShellyBluTrvButton(coordinator, button, id_) - for id_ in blutrv_key_ids - for button in BLU_TRV_BUTTONS - if button.supported(coordinator) - ) - - async_add_entities(entities) - # the user can remove virtual components from the device configuration, so # we need to remove orphaned entities virtual_button_component_ids = get_virtual_component_ids( @@ -342,37 +302,35 @@ class ShellyButton(ShellyBaseButton): await method() -class ShellyBluTrvButton(ShellyBaseButton): +class ShellyBluTrvButton(ShellyRpcAttributeEntity, ButtonEntity): """Represent a Shelly BLU TRV button.""" + entity_description: RpcButtonDescription + _id: int + def __init__( self, coordinator: ShellyRpcCoordinator, - description: ShellyButtonDescription, - id_: int, + key: str, + attribute: str, + description: RpcEntityDescription, ) -> None: - """Initialize.""" - super().__init__(coordinator, description) + """Initialize button.""" + super().__init__(coordinator, key, attribute, description) - key = f"{BLU_TRV_IDENTIFIER}:{id_}" config = coordinator.device.config[key] ble_addr: str = config["addr"] fw_ver = coordinator.device.status[key].get("fw_ver") - self._attr_unique_id = f"{format_ble_addr(ble_addr)}-{key}-{description.key}" + self._attr_unique_id = f"{format_ble_addr(ble_addr)}-{key}-{attribute}" self._attr_device_info = get_blu_trv_device_info( config, ble_addr, coordinator.mac, fw_ver ) - self._id = id_ - async def _press_method(self) -> None: - """Press method.""" - method = getattr(self.coordinator.device, self.entity_description.press_action) - - if TYPE_CHECKING: - assert method is not None - - await method(self._id) + @rpc_call + async def async_press(self) -> None: + """Triggers the Shelly button press service.""" + await self.coordinator.device.trigger_blu_trv_calibration(self._id) class RpcVirtualButton(ShellyRpcAttributeEntity, ButtonEntity): @@ -388,3 +346,31 @@ class RpcVirtualButton(ShellyRpcAttributeEntity, ButtonEntity): assert isinstance(self.coordinator, ShellyRpcCoordinator) await self.coordinator.device.button_trigger(self._id, "single_push") + + +RPC_BUTTONS = { + "button_generic": RpcButtonDescription( + key="button", + role="generic", + ), + "button_open": RpcButtonDescription( + key="button", + entity_registry_enabled_default=False, + role="open", + models={MODEL_FRANKEVER_WATER_VALVE}, + ), + "button_close": RpcButtonDescription( + key="button", + entity_registry_enabled_default=False, + role="close", + models={MODEL_FRANKEVER_WATER_VALVE}, + ), + "calibrate": RpcButtonDescription( + key="blutrv", + name="Calibrate", + translation_key="calibrate", + entity_category=EntityCategory.CONFIG, + entity_class=ShellyBluTrvButton, + models={MODEL_BLU_GATEWAY_G3}, + ), +} diff --git a/tests/components/shelly/test_button.py b/tests/components/shelly/test_button.py index dd1f56872e10..b59227acf7f0 100644 --- a/tests/components/shelly/test_button.py +++ b/tests/components/shelly/test_button.py @@ -219,7 +219,7 @@ async def test_rpc_blu_trv_button( {ATTR_ENTITY_ID: entity_id}, blocking=True, ) - assert mock_blu_trv.trigger_blu_trv_calibration.call_count == 1 + mock_blu_trv.trigger_blu_trv_calibration.assert_called_once_with(200) @pytest.mark.parametrize(