Move Freebox reboot service to a button entity (#65501)

* Add restart button to freebox

* Add warning

* restart => reboot

* Add button tests

Co-authored-by: epenet <epenet@users.noreply.github.com>
This commit is contained in:
epenet 2022-02-09 22:20:24 +01:00 committed by GitHub
parent f4aaa981a1
commit cc5bb556c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 130 additions and 1 deletions

View file

@ -1,5 +1,6 @@
"""Support for Freebox devices (Freebox v6 and Freebox mini 4K).""" """Support for Freebox devices (Freebox v6 and Freebox mini 4K)."""
from datetime import timedelta from datetime import timedelta
import logging
from freebox_api.exceptions import HttpRequestError from freebox_api.exceptions import HttpRequestError
import voluptuous as vol import voluptuous as vol
@ -29,6 +30,8 @@ CONFIG_SCHEMA = vol.Schema(
SCAN_INTERVAL = timedelta(seconds=30) SCAN_INTERVAL = timedelta(seconds=30)
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Freebox integration.""" """Set up the Freebox integration."""
@ -67,6 +70,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Services # Services
async def async_reboot(call: ServiceCall) -> None: async def async_reboot(call: ServiceCall) -> None:
"""Handle reboot service call.""" """Handle reboot service call."""
# The Freebox reboot service has been replaced by a
# dedicated button entity and marked as deprecated
_LOGGER.warning(
"The 'freebox.reboot' service is deprecated and "
"replaced by a dedicated reboot button entity; please "
"use that entity to reboot the freebox instead"
)
await router.reboot() await router.reboot()
hass.services.async_register(DOMAIN, SERVICE_REBOOT, async_reboot) hass.services.async_register(DOMAIN, SERVICE_REBOOT, async_reboot)

View file

@ -0,0 +1,76 @@
"""Support for Freebox devices (Freebox v6 and Freebox mini 4K)."""
from __future__ import annotations
from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from homeassistant.components.button import (
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .router import FreeboxRouter
@dataclass
class FreeboxButtonRequiredKeysMixin:
"""Mixin for required keys."""
async_press: Callable[[FreeboxRouter], Awaitable]
@dataclass
class FreeboxButtonEntityDescription(
ButtonEntityDescription, FreeboxButtonRequiredKeysMixin
):
"""Class describing Freebox button entities."""
BUTTON_DESCRIPTIONS: tuple[FreeboxButtonEntityDescription, ...] = (
FreeboxButtonEntityDescription(
key="reboot",
name="Reboot Freebox",
device_class=ButtonDeviceClass.RESTART,
async_press=lambda router: router.reboot(),
),
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the buttons."""
router: FreeboxRouter = hass.data[DOMAIN][entry.unique_id]
entities = [
FreeboxButton(router, description) for description in BUTTON_DESCRIPTIONS
]
async_add_entities(entities, True)
class FreeboxButton(ButtonEntity):
"""Representation of a Freebox button."""
entity_description: FreeboxButtonEntityDescription
def __init__(
self, router: FreeboxRouter, description: FreeboxButtonEntityDescription
) -> None:
"""Initialize a Freebox button."""
self.entity_description = description
self._router = router
self._attr_unique_id = f"{router.mac} {description.name}"
@property
def device_info(self) -> DeviceInfo:
"""Return the device information."""
return self._router.device_info
async def async_press(self) -> None:
"""Press the button."""
await self.entity_description.async_press(self._router)

View file

@ -17,7 +17,7 @@ APP_DESC = {
} }
API_VERSION = "v6" API_VERSION = "v6"
PLATFORMS = [Platform.DEVICE_TRACKER, Platform.SENSOR, Platform.SWITCH] PLATFORMS = [Platform.BUTTON, Platform.DEVICE_TRACKER, Platform.SENSOR, Platform.SWITCH]
DEFAULT_DEVICE_NAME = "Unknown device" DEFAULT_DEVICE_NAME = "Unknown device"

View file

@ -0,0 +1,43 @@
"""Tests for the Freebox config flow."""
from unittest.mock import Mock, patch
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN
from homeassistant.components.button.const import SERVICE_PRESS
from homeassistant.components.freebox.const import DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .const import MOCK_HOST, MOCK_PORT
from tests.common import MockConfigEntry
async def test_reboot_button(hass: HomeAssistant, router: Mock):
"""Test reboot button."""
entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
unique_id=MOCK_HOST,
)
entry.add_to_hass(hass)
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert hass.config_entries.async_entries() == [entry]
assert router.call_count == 1
assert router().open.call_count == 1
with patch(
"homeassistant.components.freebox.router.FreeboxRouter.reboot"
) as mock_service:
await hass.services.async_call(
BUTTON_DOMAIN,
SERVICE_PRESS,
service_data={
ATTR_ENTITY_ID: "button.reboot_freebox",
},
blocking=True,
)
await hass.async_block_till_done()
mock_service.assert_called_once()