mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
Implement a reboot-button for Plugwise (#120554)
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
parent
af9b4b98ca
commit
4defc4a58f
52
homeassistant/components/plugwise/button.py
Normal file
52
homeassistant/components/plugwise/button.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"""Plugwise Button component for Home Assistant."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from homeassistant.components.button import ButtonDeviceClass, ButtonEntity
|
||||||
|
from homeassistant.const import EntityCategory
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import PlugwiseConfigEntry
|
||||||
|
from .const import GATEWAY_ID, REBOOT
|
||||||
|
from .coordinator import PlugwiseDataUpdateCoordinator
|
||||||
|
from .entity import PlugwiseEntity
|
||||||
|
from .util import plugwise_command
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: PlugwiseConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the Plugwise buttons from a ConfigEntry."""
|
||||||
|
coordinator = entry.runtime_data
|
||||||
|
|
||||||
|
gateway = coordinator.data.gateway
|
||||||
|
async_add_entities(
|
||||||
|
PlugwiseButtonEntity(coordinator, device_id)
|
||||||
|
for device_id in coordinator.data.devices
|
||||||
|
if device_id == gateway[GATEWAY_ID] and REBOOT in gateway
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PlugwiseButtonEntity(PlugwiseEntity, ButtonEntity):
|
||||||
|
"""Defines a Plugwise button."""
|
||||||
|
|
||||||
|
_attr_device_class = ButtonDeviceClass.RESTART
|
||||||
|
_attr_entity_category = EntityCategory.CONFIG
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
coordinator: PlugwiseDataUpdateCoordinator,
|
||||||
|
device_id: str,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the button."""
|
||||||
|
super().__init__(coordinator, device_id)
|
||||||
|
self._attr_translation_key = REBOOT
|
||||||
|
self._attr_unique_id = f"{device_id}-reboot"
|
||||||
|
|
||||||
|
@plugwise_command
|
||||||
|
async def async_press(self) -> None:
|
||||||
|
"""Triggers the Plugwise button press service."""
|
||||||
|
await self.coordinator.api.reboot_gateway()
|
@ -17,14 +17,17 @@ FLOW_SMILE: Final = "smile (Adam/Anna/P1)"
|
|||||||
FLOW_STRETCH: Final = "stretch (Stretch)"
|
FLOW_STRETCH: Final = "stretch (Stretch)"
|
||||||
FLOW_TYPE: Final = "flow_type"
|
FLOW_TYPE: Final = "flow_type"
|
||||||
GATEWAY: Final = "gateway"
|
GATEWAY: Final = "gateway"
|
||||||
|
GATEWAY_ID: Final = "gateway_id"
|
||||||
LOCATION: Final = "location"
|
LOCATION: Final = "location"
|
||||||
PW_TYPE: Final = "plugwise_type"
|
PW_TYPE: Final = "plugwise_type"
|
||||||
|
REBOOT: Final = "reboot"
|
||||||
SMILE: Final = "smile"
|
SMILE: Final = "smile"
|
||||||
STRETCH: Final = "stretch"
|
STRETCH: Final = "stretch"
|
||||||
STRETCH_USERNAME: Final = "stretch"
|
STRETCH_USERNAME: Final = "stretch"
|
||||||
|
|
||||||
PLATFORMS: Final[list[str]] = [
|
PLATFORMS: Final[list[str]] = [
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
|
Platform.BUTTON,
|
||||||
Platform.CLIMATE,
|
Platform.CLIMATE,
|
||||||
Platform.NUMBER,
|
Platform.NUMBER,
|
||||||
Platform.SELECT,
|
Platform.SELECT,
|
||||||
|
@ -7,6 +7,7 @@ from plugwise.exceptions import (
|
|||||||
ConnectionFailedError,
|
ConnectionFailedError,
|
||||||
InvalidAuthentication,
|
InvalidAuthentication,
|
||||||
InvalidXMLError,
|
InvalidXMLError,
|
||||||
|
PlugwiseError,
|
||||||
ResponseError,
|
ResponseError,
|
||||||
UnsupportedDeviceError,
|
UnsupportedDeviceError,
|
||||||
)
|
)
|
||||||
@ -64,22 +65,23 @@ class PlugwiseDataUpdateCoordinator(DataUpdateCoordinator[PlugwiseData]):
|
|||||||
|
|
||||||
async def _async_update_data(self) -> PlugwiseData:
|
async def _async_update_data(self) -> PlugwiseData:
|
||||||
"""Fetch data from Plugwise."""
|
"""Fetch data from Plugwise."""
|
||||||
|
data = PlugwiseData({}, {})
|
||||||
try:
|
try:
|
||||||
if not self._connected:
|
if not self._connected:
|
||||||
await self._connect()
|
await self._connect()
|
||||||
data = await self.api.async_update()
|
data = await self.api.async_update()
|
||||||
|
except ConnectionFailedError as err:
|
||||||
|
raise UpdateFailed("Failed to connect") from err
|
||||||
except InvalidAuthentication as err:
|
except InvalidAuthentication as err:
|
||||||
raise ConfigEntryError("Invalid username or Smile ID") from err
|
raise ConfigEntryError("Authentication failed") from err
|
||||||
except (InvalidXMLError, ResponseError) as err:
|
except (InvalidXMLError, ResponseError) as err:
|
||||||
raise UpdateFailed(
|
raise UpdateFailed(
|
||||||
"Invalid XML data, or error indication received for the Plugwise"
|
"Invalid XML data, or error indication received from the Plugwise Adam/Smile/Stretch"
|
||||||
" Adam/Smile/Stretch"
|
|
||||||
) from err
|
) from err
|
||||||
|
except PlugwiseError as err:
|
||||||
|
raise UpdateFailed("Data incomplete or missing") from err
|
||||||
except UnsupportedDeviceError as err:
|
except UnsupportedDeviceError as err:
|
||||||
raise ConfigEntryError("Device with unsupported firmware") from err
|
raise ConfigEntryError("Device with unsupported firmware") from err
|
||||||
except ConnectionFailedError as err:
|
|
||||||
raise UpdateFailed("Failed to connect to the Plugwise Smile") from err
|
|
||||||
else:
|
else:
|
||||||
self.new_devices = set(data.devices) - self._current_devices
|
self.new_devices = set(data.devices) - self._current_devices
|
||||||
self._current_devices = set(data.devices)
|
self._current_devices = set(data.devices)
|
||||||
|
@ -55,6 +55,11 @@
|
|||||||
"name": "Plugwise notification"
|
"name": "Plugwise notification"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"button": {
|
||||||
|
"reboot": {
|
||||||
|
"name": "Reboot"
|
||||||
|
}
|
||||||
|
},
|
||||||
"climate": {
|
"climate": {
|
||||||
"plugwise": {
|
"plugwise": {
|
||||||
"state_attributes": {
|
"state_attributes": {
|
||||||
|
39
tests/components/plugwise/test_button.py
Normal file
39
tests/components/plugwise/test_button.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"""Tests for Plugwise button entities."""
|
||||||
|
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from homeassistant.components.button import (
|
||||||
|
DOMAIN as BUTTON_DOMAIN,
|
||||||
|
SERVICE_PRESS,
|
||||||
|
ButtonDeviceClass,
|
||||||
|
)
|
||||||
|
from homeassistant.const import ATTR_DEVICE_CLASS, ATTR_ENTITY_ID, STATE_UNKNOWN
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_adam_reboot_button(
|
||||||
|
hass: HomeAssistant, mock_smile_adam: MagicMock, init_integration: MockConfigEntry
|
||||||
|
) -> None:
|
||||||
|
"""Test creation of button entities."""
|
||||||
|
state = hass.states.get("button.adam_reboot")
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
assert state.attributes.get(ATTR_DEVICE_CLASS) == ButtonDeviceClass.RESTART
|
||||||
|
|
||||||
|
registry = er.async_get(hass)
|
||||||
|
entry = registry.async_get("button.adam_reboot")
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "fe799307f1624099878210aa0b9f1475-reboot"
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
BUTTON_DOMAIN,
|
||||||
|
SERVICE_PRESS,
|
||||||
|
{ATTR_ENTITY_ID: "button.adam_reboot"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_smile_adam.reboot_gateway.call_count == 1
|
||||||
|
mock_smile_adam.reboot_gateway.assert_called_with()
|
@ -7,6 +7,7 @@ from plugwise.exceptions import (
|
|||||||
ConnectionFailedError,
|
ConnectionFailedError,
|
||||||
InvalidAuthentication,
|
InvalidAuthentication,
|
||||||
InvalidXMLError,
|
InvalidXMLError,
|
||||||
|
PlugwiseError,
|
||||||
ResponseError,
|
ResponseError,
|
||||||
UnsupportedDeviceError,
|
UnsupportedDeviceError,
|
||||||
)
|
)
|
||||||
@ -83,6 +84,7 @@ async def test_load_unload_config_entry(
|
|||||||
(ConnectionFailedError, ConfigEntryState.SETUP_RETRY),
|
(ConnectionFailedError, ConfigEntryState.SETUP_RETRY),
|
||||||
(InvalidAuthentication, ConfigEntryState.SETUP_ERROR),
|
(InvalidAuthentication, ConfigEntryState.SETUP_ERROR),
|
||||||
(InvalidXMLError, ConfigEntryState.SETUP_RETRY),
|
(InvalidXMLError, ConfigEntryState.SETUP_RETRY),
|
||||||
|
(PlugwiseError, ConfigEntryState.SETUP_RETRY),
|
||||||
(ResponseError, ConfigEntryState.SETUP_RETRY),
|
(ResponseError, ConfigEntryState.SETUP_RETRY),
|
||||||
(UnsupportedDeviceError, ConfigEntryState.SETUP_ERROR),
|
(UnsupportedDeviceError, ConfigEntryState.SETUP_ERROR),
|
||||||
],
|
],
|
||||||
@ -219,7 +221,7 @@ async def test_update_device(
|
|||||||
entity_registry, mock_config_entry.entry_id
|
entity_registry, mock_config_entry.entry_id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
== 28
|
== 29
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
len(
|
len(
|
||||||
@ -242,7 +244,7 @@ async def test_update_device(
|
|||||||
entity_registry, mock_config_entry.entry_id
|
entity_registry, mock_config_entry.entry_id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
== 33
|
== 34
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
len(
|
len(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user