core/homeassistant/components/rainmachine/update.py

120 lines
3.5 KiB
Python

"""Support for RainMachine updates."""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import Any
from regenmaschine.errors import RequestError
from homeassistant.components.update import (
UpdateDeviceClass,
UpdateEntity,
UpdateEntityDescription,
UpdateEntityFeature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import RainMachineConfigEntry
from .const import DATA_MACHINE_FIRMWARE_UPDATE_STATUS
from .entity import RainMachineEntity, RainMachineEntityDescription
class UpdateStates(Enum):
"""Define an enum for update states."""
IDLE = 1
CHECKING = 2
DOWNLOADING = 3
UPGRADING = 4
ERROR = 5
REBOOT = 6
UPDATE_STATE_MAP = {
1: UpdateStates.IDLE,
2: UpdateStates.CHECKING,
3: UpdateStates.DOWNLOADING,
4: UpdateStates.UPGRADING,
5: UpdateStates.ERROR,
6: UpdateStates.REBOOT,
}
@dataclass(frozen=True, kw_only=True)
class RainMachineUpdateEntityDescription(
UpdateEntityDescription, RainMachineEntityDescription
):
"""Describe a RainMachine update."""
UPDATE_DESCRIPTION = RainMachineUpdateEntityDescription(
key="update",
api_category=DATA_MACHINE_FIRMWARE_UPDATE_STATUS,
)
async def async_setup_entry(
hass: HomeAssistant,
entry: RainMachineConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Rainmachine update based on a config entry."""
data = entry.runtime_data
async_add_entities([RainMachineUpdateEntity(entry, data, UPDATE_DESCRIPTION)])
class RainMachineUpdateEntity(RainMachineEntity, UpdateEntity):
"""Define a RainMachine update entity."""
_attr_device_class = UpdateDeviceClass.FIRMWARE
_attr_name = None
_attr_supported_features = (
UpdateEntityFeature.INSTALL
| UpdateEntityFeature.PROGRESS
| UpdateEntityFeature.SPECIFIC_VERSION
)
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
"""Install an update."""
try:
await self._data.controller.machine.update_firmware()
except RequestError as err:
raise HomeAssistantError(f"Error while updating firmware: {err}") from err
await self.coordinator.async_refresh()
@callback
def update_from_latest_data(self) -> None:
"""Update the state."""
if version := self._version_coordinator.data["swVer"]:
self._attr_installed_version = version
else:
self._attr_installed_version = None
data = self.coordinator.data
if not data["update"]:
self._attr_in_progress = False
self._attr_latest_version = self._attr_installed_version
return
self._attr_in_progress = UPDATE_STATE_MAP[data["updateStatus"]] in (
UpdateStates.DOWNLOADING,
UpdateStates.UPGRADING,
UpdateStates.REBOOT,
)
# The RainMachine API docs say that multiple "packages" can be updated, but
# don't give details on what types exist (which makes it impossible to have
# update entities per update type); so, we use the first one (with the idea that
# after it succeeds, the entity will show the next update):
package_details = data["packageDetails"][0]
self._attr_latest_version = package_details["newVersion"]
self._attr_title = package_details["packageName"]