From a9ba568154e49f4f0366aa83780f74355409dc96 Mon Sep 17 00:00:00 2001 From: Brian Towles Date: Sun, 30 Aug 2026 12:26:03 -0500 Subject: [PATCH] Add a restart button to Modern Forms (#180527) Co-authored-by: Claude Opus 5 --- .../components/modern_forms/__init__.py | 1 + .../components/modern_forms/button.py | 43 ++++++++++++++ tests/components/modern_forms/test_button.py | 59 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 homeassistant/components/modern_forms/button.py create mode 100644 tests/components/modern_forms/test_button.py diff --git a/homeassistant/components/modern_forms/__init__.py b/homeassistant/components/modern_forms/__init__.py index de310200735d..4c0e013a850a 100644 --- a/homeassistant/components/modern_forms/__init__.py +++ b/homeassistant/components/modern_forms/__init__.py @@ -15,6 +15,7 @@ from .entity import ModernFormsDeviceEntity PLATFORMS = [ Platform.BINARY_SENSOR, + Platform.BUTTON, Platform.FAN, Platform.LIGHT, Platform.NUMBER, diff --git a/homeassistant/components/modern_forms/button.py b/homeassistant/components/modern_forms/button.py new file mode 100644 index 000000000000..617bfb211c12 --- /dev/null +++ b/homeassistant/components/modern_forms/button.py @@ -0,0 +1,43 @@ +"""Support for Modern Forms buttons.""" + +from typing import override + +from homeassistant.components.button import ButtonDeviceClass, ButtonEntity +from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from . import modernforms_exception_handler +from .coordinator import ModernFormsConfigEntry, ModernFormsDataUpdateCoordinator +from .entity import ModernFormsDeviceEntity + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ModernFormsConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up Modern Forms buttons based on a config entry.""" + coordinator = config_entry.runtime_data + + async_add_entities([ModernFormsRestartButton(config_entry.entry_id, coordinator)]) + + +class ModernFormsRestartButton(ModernFormsDeviceEntity, ButtonEntity): + """Defines a Modern Forms restart button.""" + + _attr_device_class = ButtonDeviceClass.RESTART + _attr_entity_category = EntityCategory.CONFIG + + def __init__( + self, entry_id: str, coordinator: ModernFormsDataUpdateCoordinator + ) -> None: + """Initialize the restart button.""" + super().__init__(entry_id=entry_id, coordinator=coordinator) + self._attr_unique_id = f"{self.coordinator.data.info.mac_address}_restart" + + @modernforms_exception_handler + @override + async def async_press(self) -> None: + """Reboot the fan.""" + await self.coordinator.modern_forms.reboot() diff --git a/tests/components/modern_forms/test_button.py b/tests/components/modern_forms/test_button.py new file mode 100644 index 000000000000..c28bedb1aa0a --- /dev/null +++ b/tests/components/modern_forms/test_button.py @@ -0,0 +1,59 @@ +"""Tests for the Modern Forms button platform.""" + +from unittest.mock import patch + +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import init_integration, init_integration_gen4 + +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_restart_button( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test the creation of the restart button on a legacy fan.""" + await init_integration(hass, aioclient_mock) + + state = hass.states.get("button.modernformsfan_restart") + assert state + entry = entity_registry.async_get("button.modernformsfan_restart") + assert entry + assert entry.unique_id == "AA:BB:CC:DD:EE:FF_restart" + + +async def test_restart_button_gen4( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test the creation of the restart button on a Gen4 fan.""" + await init_integration_gen4(hass, aioclient_mock) + + state = hass.states.get("button.modernformsfan_restart") + assert state + entry = entity_registry.async_get("button.modernformsfan_restart") + assert entry + assert entry.unique_id == "AA:BB:CC:00:11:22_restart" + + +async def test_restart_button_press( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +) -> None: + """Test pressing the restart button.""" + await init_integration(hass, aioclient_mock) + + with patch("aiomodernforms.ModernFormsDevice.reboot") as reboot_mock: + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: "button.modernformsfan_restart"}, + blocking=True, + ) + await hass.async_block_till_done() + reboot_mock.assert_called_once_with()