Files
core/homeassistant/components/enphase_envoy/button.py
T

97 lines
3.2 KiB
Python

"""Button platform for Enphase Envoy solar energy monitor."""
from collections.abc import Callable, Coroutine
from dataclasses import dataclass
from typing import Any, override
from pyenphase import Envoy
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import DOMAIN
from .coordinator import EnphaseConfigEntry
from .entity import EnvoyACBAggregateControlEntity, exception_handler
PARALLEL_UPDATES = 1
@dataclass(frozen=True, kw_only=True)
class EnvoyACBButtonEntityDescription(ButtonEntityDescription):
"""Describes an Envoy ACB battery button entity."""
press_fn: Callable[
[Envoy, list[str], int, int], Coroutine[Any, Any, dict[str, Any]]
]
ACB_BUTTONS = (
EnvoyACBButtonEntityDescription(
key="acb_sleep",
translation_key="acb_sleep",
press_fn=lambda envoy, serials, low, high: envoy.set_acb_sleep(
[
{"serial_num": serial, "sleep_min_soc": low, "sleep_max_soc": high}
for serial in serials
]
),
),
EnvoyACBButtonEntityDescription(
key="acb_wake",
translation_key="acb_wake",
press_fn=lambda envoy, serials, low, high: envoy.clear_acb_sleep(serials),
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: EnphaseConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up envoy button platform."""
coordinator = config_entry.runtime_data
envoy_data = coordinator.envoy.data
assert envoy_data is not None
if not envoy_data.acb_inventory:
return
entities: list[ButtonEntity] = [
EnvoyACBButtonEntity(coordinator, description) for description in ACB_BUTTONS
]
async_add_entities(entities)
class EnvoyACBButtonEntity(EnvoyACBAggregateControlEntity, ButtonEntity):
"""Envoy button that puts all ACB batteries to sleep or wakes them."""
entity_description: EnvoyACBButtonEntityDescription
@exception_handler
@override
async def async_press(self) -> None:
"""Send the sleep or wake request for all ACB batteries."""
acb_inventory = self.data.acb_inventory
assert acb_inventory is not None
low, high = self.coordinator.acb_sleep_soc()
try:
await self.entity_description.press_fn(
self.coordinator.envoy, list(acb_inventory), low, high
)
except ValueError as err:
# pyenphase raises ValueError for validation failures and Envoy
# HTTP 400 responses, which exception_handler does not cover.
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="action_error",
translation_placeholders={
"host": self.coordinator.envoy.host,
"args": err.args[0] if err.args else str(err),
"action": "async_press",
"entity": self.entity_id,
},
) from err
await self.coordinator.async_request_refresh()