diff --git a/homeassistant/components/starlink/__init__.py b/homeassistant/components/starlink/__init__.py index b47b4781342..acd85c3595a 100644 --- a/homeassistant/components/starlink/__init__.py +++ b/homeassistant/components/starlink/__init__.py @@ -8,7 +8,7 @@ from homeassistant.core import HomeAssistant from .const import DOMAIN from .coordinator import StarlinkUpdateCoordinator -PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR] +PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.BUTTON, Platform.SENSOR] async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: diff --git a/homeassistant/components/starlink/binary_sensor.py b/homeassistant/components/starlink/binary_sensor.py index 588402c0116..e50da733341 100644 --- a/homeassistant/components/starlink/binary_sensor.py +++ b/homeassistant/components/starlink/binary_sensor.py @@ -16,7 +16,7 @@ from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -from .coordinator import StarlinkData, StarlinkUpdateCoordinator +from .coordinator import StarlinkData from .entity import StarlinkEntity @@ -51,16 +51,6 @@ class StarlinkBinarySensorEntity(StarlinkEntity, BinarySensorEntity): entity_description: StarlinkBinarySensorEntityDescription - def __init__( - self, - coordinator: StarlinkUpdateCoordinator, - description: StarlinkBinarySensorEntityDescription, - ) -> None: - """Initialize the binary sensor.""" - super().__init__(coordinator) - self.entity_description = description - self._attr_unique_id = f"{self.coordinator.data.status['id']}_{description.key}" - @property def is_on(self) -> bool | None: """Calculate the binary sensor value from the entity description.""" diff --git a/homeassistant/components/starlink/button.py b/homeassistant/components/starlink/button.py new file mode 100644 index 00000000000..e9613cb817e --- /dev/null +++ b/homeassistant/components/starlink/button.py @@ -0,0 +1,66 @@ +"""Contains buttons exposed by the Starlink integration.""" + +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 EntityCategory +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import DOMAIN +from .coordinator import StarlinkUpdateCoordinator +from .entity import StarlinkEntity + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Set up all binary sensors for this entry.""" + coordinator = hass.data[DOMAIN][entry.entry_id] + + async_add_entities( + StarlinkButtonEntity(coordinator, description) for description in BUTTONS + ) + + +@dataclass +class StarlinkButtonEntityDescriptionMixin: + """Mixin for required keys.""" + + press_fn: Callable[[StarlinkUpdateCoordinator], Awaitable[None]] + + +@dataclass +class StarlinkButtonEntityDescription( + ButtonEntityDescription, StarlinkButtonEntityDescriptionMixin +): + """Describes a Starlink button entity.""" + + +class StarlinkButtonEntity(StarlinkEntity, ButtonEntity): + """A ButtonEntity for Starlink devices. Handles creating unique IDs.""" + + entity_description: StarlinkButtonEntityDescription + + async def async_press(self) -> None: + """Press the button.""" + return await self.entity_description.press_fn(self.coordinator) + + +BUTTONS = [ + StarlinkButtonEntityDescription( + key="reboot", + name="Reboot", + device_class=ButtonDeviceClass.RESTART, + entity_category=EntityCategory.DIAGNOSTIC, + press_fn=lambda coordinator: coordinator.reboot_starlink(), + ) +] diff --git a/homeassistant/components/starlink/coordinator.py b/homeassistant/components/starlink/coordinator.py index 1bd727aee34..6e63f84b067 100644 --- a/homeassistant/components/starlink/coordinator.py +++ b/homeassistant/components/starlink/coordinator.py @@ -12,10 +12,12 @@ from starlink_grpc import ( GrpcError, ObstructionDict, StatusDict, + reboot, status_data, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed _LOGGER = logging.getLogger(__name__) @@ -53,3 +55,11 @@ class StarlinkUpdateCoordinator(DataUpdateCoordinator[StarlinkData]): return StarlinkData(*status) except GrpcError as exc: raise UpdateFailed from exc + + async def reboot_starlink(self): + """Reboot the Starlink system tied to this coordinator.""" + async with async_timeout.timeout(4): + try: + await self.hass.async_add_executor_job(reboot, self.channel_context) + except GrpcError as exc: + raise HomeAssistantError from exc diff --git a/homeassistant/components/starlink/entity.py b/homeassistant/components/starlink/entity.py index 5631e7a390c..29ef9ba9f08 100644 --- a/homeassistant/components/starlink/entity.py +++ b/homeassistant/components/starlink/entity.py @@ -1,7 +1,7 @@ """Contains base entity classes for Starlink entities.""" from __future__ import annotations -from homeassistant.helpers.entity import DeviceInfo, Entity +from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription from homeassistant.helpers.update_coordinator import CoordinatorEntity from .const import DOMAIN @@ -14,8 +14,7 @@ class StarlinkEntity(CoordinatorEntity[StarlinkUpdateCoordinator], Entity): _attr_has_entity_name = True def __init__( - self, - coordinator: StarlinkUpdateCoordinator, + self, coordinator: StarlinkUpdateCoordinator, description: EntityDescription ) -> None: """Initialize the device info and set the update coordinator.""" super().__init__(coordinator) @@ -30,3 +29,5 @@ class StarlinkEntity(CoordinatorEntity[StarlinkUpdateCoordinator], Entity): manufacturer="SpaceX", model="Starlink", ) + self._attr_unique_id = f"{self.coordinator.data.status['id']}_{description.key}" + self.entity_description = description diff --git a/homeassistant/components/starlink/sensor.py b/homeassistant/components/starlink/sensor.py index f63b606a87b..a94caf0a423 100644 --- a/homeassistant/components/starlink/sensor.py +++ b/homeassistant/components/starlink/sensor.py @@ -19,7 +19,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from .const import DOMAIN -from .coordinator import StarlinkData, StarlinkUpdateCoordinator +from .coordinator import StarlinkData from .entity import StarlinkEntity @@ -53,16 +53,6 @@ class StarlinkSensorEntity(StarlinkEntity, SensorEntity): entity_description: StarlinkSensorEntityDescription - def __init__( - self, - coordinator: StarlinkUpdateCoordinator, - description: StarlinkSensorEntityDescription, - ) -> None: - """Initialize the sensor.""" - super().__init__(coordinator) - self.entity_description = description - self._attr_unique_id = f"{self.coordinator.data.status['id']}_{description.key}" - @property def native_value(self) -> StateType | datetime: """Calculate the sensor value from the entity description."""