Add update platform to the Supervisor integration (#68475)
This commit is contained in:
parent
1b955970f8
commit
d17f8e9ed6
10 changed files with 901 additions and 19 deletions
|
@ -51,6 +51,7 @@ from .auth import async_setup_auth_view
|
|||
from .const import (
|
||||
ATTR_ADDON,
|
||||
ATTR_ADDONS,
|
||||
ATTR_CHANGELOG,
|
||||
ATTR_DISCOVERY,
|
||||
ATTR_FOLDERS,
|
||||
ATTR_HOMEASSISTANT,
|
||||
|
@ -63,6 +64,9 @@ from .const import (
|
|||
ATTR_URL,
|
||||
ATTR_VERSION,
|
||||
DATA_KEY_ADDONS,
|
||||
DATA_KEY_CORE,
|
||||
DATA_KEY_OS,
|
||||
DATA_KEY_SUPERVISOR,
|
||||
DOMAIN,
|
||||
SupervisorEntityModel,
|
||||
)
|
||||
|
@ -77,7 +81,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
STORAGE_KEY = DOMAIN
|
||||
STORAGE_VERSION = 1
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.UPDATE]
|
||||
|
||||
CONF_FRONTEND_REPO = "development_repo"
|
||||
|
||||
|
@ -93,6 +97,7 @@ DATA_STORE = "hassio_store"
|
|||
DATA_INFO = "hassio_info"
|
||||
DATA_OS_INFO = "hassio_os_info"
|
||||
DATA_SUPERVISOR_INFO = "hassio_supervisor_info"
|
||||
DATA_ADDONS_CHANGELOGS = "hassio_addons_changelogs"
|
||||
DATA_ADDONS_STATS = "hassio_addons_stats"
|
||||
HASSIO_UPDATE_INTERVAL = timedelta(minutes=5)
|
||||
|
||||
|
@ -239,14 +244,22 @@ async def async_uninstall_addon(hass: HomeAssistant, slug: str) -> dict:
|
|||
|
||||
@bind_hass
|
||||
@api_data
|
||||
async def async_update_addon(hass: HomeAssistant, slug: str) -> dict:
|
||||
async def async_update_addon(
|
||||
hass: HomeAssistant,
|
||||
slug: str,
|
||||
backup: bool = False,
|
||||
) -> dict:
|
||||
"""Update add-on.
|
||||
|
||||
The caller of the function should handle HassioAPIError.
|
||||
"""
|
||||
hassio = hass.data[DOMAIN]
|
||||
command = f"/addons/{slug}/update"
|
||||
return await hassio.send_command(command, timeout=None)
|
||||
return await hassio.send_command(
|
||||
command,
|
||||
payload={"backup": backup},
|
||||
timeout=None,
|
||||
)
|
||||
|
||||
|
||||
@bind_hass
|
||||
|
@ -323,6 +336,52 @@ async def async_create_backup(
|
|||
return await hassio.send_command(command, payload=payload, timeout=None)
|
||||
|
||||
|
||||
@bind_hass
|
||||
@api_data
|
||||
async def async_update_os(hass: HomeAssistant, version: str | None = None) -> dict:
|
||||
"""Update Home Assistant Operating System.
|
||||
|
||||
The caller of the function should handle HassioAPIError.
|
||||
"""
|
||||
hassio = hass.data[DOMAIN]
|
||||
command = "/os/update"
|
||||
return await hassio.send_command(
|
||||
command,
|
||||
payload={"version": version},
|
||||
timeout=None,
|
||||
)
|
||||
|
||||
|
||||
@bind_hass
|
||||
@api_data
|
||||
async def async_update_supervisor(hass: HomeAssistant) -> dict:
|
||||
"""Update Home Assistant Supervisor.
|
||||
|
||||
The caller of the function should handle HassioAPIError.
|
||||
"""
|
||||
hassio = hass.data[DOMAIN]
|
||||
command = "/supervisor/update"
|
||||
return await hassio.send_command(command, timeout=None)
|
||||
|
||||
|
||||
@bind_hass
|
||||
@api_data
|
||||
async def async_update_core(
|
||||
hass: HomeAssistant, version: str | None = None, backup: bool = False
|
||||
) -> dict:
|
||||
"""Update Home Assistant Core.
|
||||
|
||||
The caller of the function should handle HassioAPIError.
|
||||
"""
|
||||
hassio = hass.data[DOMAIN]
|
||||
command = "/core/update"
|
||||
return await hassio.send_command(
|
||||
command,
|
||||
payload={"version": version, "backup": backup},
|
||||
timeout=None,
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
@bind_hass
|
||||
def get_info(hass):
|
||||
|
@ -373,6 +432,16 @@ def get_addons_stats(hass):
|
|||
return hass.data.get(DATA_ADDONS_STATS)
|
||||
|
||||
|
||||
@callback
|
||||
@bind_hass
|
||||
def get_addons_changelogs(hass):
|
||||
"""Return Addons changelogs.
|
||||
|
||||
Async friendly.
|
||||
"""
|
||||
return hass.data.get(DATA_ADDONS_CHANGELOGS)
|
||||
|
||||
|
||||
@callback
|
||||
@bind_hass
|
||||
def get_os_info(hass):
|
||||
|
@ -533,6 +602,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
|
|||
stats = await hassio.get_addon_stats(slug)
|
||||
return (slug, stats)
|
||||
|
||||
async def update_addon_changelog(slug):
|
||||
"""Return the changelog for an add-on."""
|
||||
changelog = await hassio.get_addon_changelog(slug)
|
||||
return (slug, changelog)
|
||||
|
||||
async def update_info_data(now):
|
||||
"""Update last available supervisor information."""
|
||||
|
||||
|
@ -562,6 +636,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
|
|||
*[update_addon_stats(addon[ATTR_SLUG]) for addon in addons]
|
||||
)
|
||||
hass.data[DATA_ADDONS_STATS] = dict(stats_data)
|
||||
hass.data[DATA_ADDONS_CHANGELOGS] = dict(
|
||||
await asyncio.gather(
|
||||
*[update_addon_changelog(addon[ATTR_SLUG]) for addon in addons]
|
||||
)
|
||||
)
|
||||
|
||||
if ADDONS_COORDINATOR in hass.data:
|
||||
await hass.data[ADDONS_COORDINATOR].async_refresh()
|
||||
|
@ -699,6 +778,42 @@ def async_register_os_in_dev_reg(
|
|||
dev_reg.async_get_or_create(config_entry_id=entry_id, **params)
|
||||
|
||||
|
||||
@callback
|
||||
def async_register_core_in_dev_reg(
|
||||
entry_id: str,
|
||||
dev_reg: DeviceRegistry,
|
||||
core_dict: dict[str, Any],
|
||||
) -> None:
|
||||
"""Register OS in the device registry."""
|
||||
params = DeviceInfo(
|
||||
identifiers={(DOMAIN, "core")},
|
||||
manufacturer="Home Assistant",
|
||||
model=SupervisorEntityModel.CORE,
|
||||
sw_version=core_dict[ATTR_VERSION],
|
||||
name="Home Assistant Core",
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
)
|
||||
dev_reg.async_get_or_create(config_entry_id=entry_id, **params)
|
||||
|
||||
|
||||
@callback
|
||||
def async_register_supervisor_in_dev_reg(
|
||||
entry_id: str,
|
||||
dev_reg: DeviceRegistry,
|
||||
supervisor_dict: dict[str, Any],
|
||||
) -> None:
|
||||
"""Register OS in the device registry."""
|
||||
params = DeviceInfo(
|
||||
identifiers={(DOMAIN, "supervisor")},
|
||||
manufacturer="Home Assistant",
|
||||
model=SupervisorEntityModel.SUPERVIOSR,
|
||||
sw_version=supervisor_dict[ATTR_VERSION],
|
||||
name="Home Assistant Supervisor",
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
)
|
||||
dev_reg.async_get_or_create(config_entry_id=entry_id, **params)
|
||||
|
||||
|
||||
@callback
|
||||
def async_remove_addons_from_dev_reg(dev_reg: DeviceRegistry, addons: set[str]) -> None:
|
||||
"""Remove addons from the device registry."""
|
||||
|
@ -720,6 +835,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
|
|||
name=DOMAIN,
|
||||
update_method=self._async_update_data,
|
||||
)
|
||||
self.hassio: HassIO = hass.data[DOMAIN]
|
||||
self.data = {}
|
||||
self.entry_id = config_entry.entry_id
|
||||
self.dev_reg = dev_reg
|
||||
|
@ -730,6 +846,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
|
|||
new_data = {}
|
||||
supervisor_info = get_supervisor_info(self.hass)
|
||||
addons_stats = get_addons_stats(self.hass)
|
||||
addons_changelogs = get_addons_changelogs(self.hass)
|
||||
store_data = get_store(self.hass)
|
||||
|
||||
repositories = {
|
||||
|
@ -741,6 +858,7 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
|
|||
addon[ATTR_SLUG]: {
|
||||
**addon,
|
||||
**((addons_stats or {}).get(addon[ATTR_SLUG], {})),
|
||||
ATTR_CHANGELOG: (addons_changelogs or {}).get(addon[ATTR_SLUG]),
|
||||
ATTR_REPOSITORY: repositories.get(
|
||||
addon.get(ATTR_REPOSITORY), addon.get(ATTR_REPOSITORY, "")
|
||||
),
|
||||
|
@ -748,16 +866,25 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
|
|||
for addon in supervisor_info.get("addons", [])
|
||||
}
|
||||
if self.is_hass_os:
|
||||
new_data["os"] = get_os_info(self.hass)
|
||||
new_data[DATA_KEY_OS] = get_os_info(self.hass)
|
||||
|
||||
new_data[DATA_KEY_CORE] = get_core_info(self.hass)
|
||||
new_data[DATA_KEY_SUPERVISOR] = supervisor_info
|
||||
|
||||
# If this is the initial refresh, register all addons and return the dict
|
||||
if not self.data:
|
||||
async_register_addons_in_dev_reg(
|
||||
self.entry_id, self.dev_reg, new_data[DATA_KEY_ADDONS].values()
|
||||
)
|
||||
async_register_core_in_dev_reg(
|
||||
self.entry_id, self.dev_reg, new_data[DATA_KEY_CORE]
|
||||
)
|
||||
async_register_supervisor_in_dev_reg(
|
||||
self.entry_id, self.dev_reg, new_data[DATA_KEY_SUPERVISOR]
|
||||
)
|
||||
if self.is_hass_os:
|
||||
async_register_os_in_dev_reg(
|
||||
self.entry_id, self.dev_reg, new_data["os"]
|
||||
self.entry_id, self.dev_reg, new_data[DATA_KEY_OS]
|
||||
)
|
||||
|
||||
# Remove add-ons that are no longer installed from device registry
|
||||
|
@ -782,3 +909,8 @@ class HassioDataUpdateCoordinator(DataUpdateCoordinator):
|
|||
return {}
|
||||
|
||||
return new_data
|
||||
|
||||
async def force_info_update_supervisor(self) -> None:
|
||||
"""Force update of the supervisor info."""
|
||||
self.hass.data[DATA_SUPERVISOR_INFO] = await self.hassio.get_supervisor_info()
|
||||
await self.async_refresh()
|
||||
|
|
|
@ -32,6 +32,7 @@ class HassioBinarySensorEntityDescription(BinarySensorEntityDescription):
|
|||
|
||||
COMMON_ENTITY_DESCRIPTIONS = (
|
||||
HassioBinarySensorEntityDescription(
|
||||
# Deprecated, scheduled to be removed in 2022.6
|
||||
device_class=BinarySensorDeviceClass.UPDATE,
|
||||
entity_registry_enabled_default=False,
|
||||
key=ATTR_UPDATE_AVAILABLE,
|
||||
|
|
|
@ -43,6 +43,7 @@ ATTR_VERSION = "version"
|
|||
ATTR_VERSION_LATEST = "version_latest"
|
||||
ATTR_UPDATE_AVAILABLE = "update_available"
|
||||
ATTR_CPU_PERCENT = "cpu_percent"
|
||||
ATTR_CHANGELOG = "changelog"
|
||||
ATTR_MEMORY_PERCENT = "memory_percent"
|
||||
ATTR_SLUG = "slug"
|
||||
ATTR_STATE = "state"
|
||||
|
@ -53,6 +54,8 @@ ATTR_REPOSITORY = "repository"
|
|||
|
||||
DATA_KEY_ADDONS = "addons"
|
||||
DATA_KEY_OS = "os"
|
||||
DATA_KEY_SUPERVISOR = "supervisor"
|
||||
DATA_KEY_CORE = "core"
|
||||
|
||||
|
||||
class SupervisorEntityModel(str, Enum):
|
||||
|
@ -60,3 +63,5 @@ class SupervisorEntityModel(str, Enum):
|
|||
|
||||
ADDON = "Home Assistant Add-on"
|
||||
OS = "Home Assistant Operating System"
|
||||
CORE = "Home Assistant Core"
|
||||
SUPERVIOSR = "Home Assistant Supervisor"
|
||||
|
|
|
@ -8,7 +8,13 @@ from homeassistant.helpers.entity import DeviceInfo, EntityDescription
|
|||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import DOMAIN, HassioDataUpdateCoordinator
|
||||
from .const import ATTR_SLUG, DATA_KEY_ADDONS, DATA_KEY_OS
|
||||
from .const import (
|
||||
ATTR_SLUG,
|
||||
DATA_KEY_ADDONS,
|
||||
DATA_KEY_CORE,
|
||||
DATA_KEY_OS,
|
||||
DATA_KEY_SUPERVISOR,
|
||||
)
|
||||
|
||||
|
||||
class HassioAddonEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
|
||||
|
@ -33,8 +39,9 @@ class HassioAddonEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
|
|||
"""Return True if entity is available."""
|
||||
return (
|
||||
super().available
|
||||
and DATA_KEY_ADDONS in self.coordinator.data
|
||||
and self.entity_description.key
|
||||
in self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug]
|
||||
in self.coordinator.data[DATA_KEY_ADDONS].get(self._addon_slug, {})
|
||||
)
|
||||
|
||||
|
||||
|
@ -58,5 +65,57 @@ class HassioOSEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
|
|||
"""Return True if entity is available."""
|
||||
return (
|
||||
super().available
|
||||
and DATA_KEY_OS in self.coordinator.data
|
||||
and self.entity_description.key in self.coordinator.data[DATA_KEY_OS]
|
||||
)
|
||||
|
||||
|
||||
class HassioSupervisorEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
|
||||
"""Base Entity for Supervisor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: HassioDataUpdateCoordinator,
|
||||
entity_description: EntityDescription,
|
||||
) -> None:
|
||||
"""Initialize base entity."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = entity_description
|
||||
self._attr_name = f"Home Assistant Supervisor: {entity_description.name}"
|
||||
self._attr_unique_id = f"home_assistant_supervisor_{entity_description.key}"
|
||||
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, "supervisor")})
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
return (
|
||||
super().available
|
||||
and DATA_KEY_OS in self.coordinator.data
|
||||
and self.entity_description.key
|
||||
in self.coordinator.data[DATA_KEY_SUPERVISOR]
|
||||
)
|
||||
|
||||
|
||||
class HassioCoreEntity(CoordinatorEntity[HassioDataUpdateCoordinator]):
|
||||
"""Base Entity for Core."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: HassioDataUpdateCoordinator,
|
||||
entity_description: EntityDescription,
|
||||
) -> None:
|
||||
"""Initialize base entity."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = entity_description
|
||||
self._attr_name = f"Home Assistant Core: {entity_description.name}"
|
||||
self._attr_unique_id = f"home_assistant_core_{entity_description.key}"
|
||||
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, "core")})
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
return (
|
||||
super().available
|
||||
and DATA_KEY_CORE in self.coordinator.data
|
||||
and self.entity_description.key in self.coordinator.data[DATA_KEY_CORE]
|
||||
)
|
||||
|
|
|
@ -127,6 +127,15 @@ class HassIO:
|
|||
"""
|
||||
return self.send_command(f"/addons/{addon}/stats", method="get")
|
||||
|
||||
def get_addon_changelog(self, addon):
|
||||
"""Return changelog for an Add-on.
|
||||
|
||||
This method returns a coroutine.
|
||||
"""
|
||||
return self.send_command(
|
||||
f"/addons/{addon}/changelog", method="get", return_text=True
|
||||
)
|
||||
|
||||
@api_data
|
||||
def get_store(self):
|
||||
"""Return data from the store.
|
||||
|
@ -212,7 +221,14 @@ class HassIO:
|
|||
"/supervisor/options", payload={"diagnostics": diagnostics}
|
||||
)
|
||||
|
||||
async def send_command(self, command, method="post", payload=None, timeout=10):
|
||||
async def send_command(
|
||||
self,
|
||||
command,
|
||||
method="post",
|
||||
payload=None,
|
||||
timeout=10,
|
||||
return_text=False,
|
||||
):
|
||||
"""Send API command to Hass.io.
|
||||
|
||||
This method is a coroutine.
|
||||
|
@ -230,8 +246,10 @@ class HassIO:
|
|||
_LOGGER.error("%s return code %d", command, request.status)
|
||||
raise HassioAPIError()
|
||||
|
||||
answer = await request.json()
|
||||
return answer
|
||||
if return_text:
|
||||
return await request.text(encoding="utf-8")
|
||||
|
||||
return await request.json()
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
_LOGGER.error("Timeout on %s request", command)
|
||||
|
|
276
homeassistant/components/hassio/update.py
Normal file
276
homeassistant/components/hassio/update.py
Normal file
|
@ -0,0 +1,276 @@
|
|||
"""Update platform for Supervisor."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
|
||||
|
||||
from homeassistant.components.update import (
|
||||
UpdateEntity,
|
||||
UpdateEntityDescription,
|
||||
UpdateEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ICON, ATTR_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import (
|
||||
ADDONS_COORDINATOR,
|
||||
async_update_addon,
|
||||
async_update_core,
|
||||
async_update_os,
|
||||
async_update_supervisor,
|
||||
)
|
||||
from .const import (
|
||||
ATTR_CHANGELOG,
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
DATA_KEY_ADDONS,
|
||||
DATA_KEY_CORE,
|
||||
DATA_KEY_OS,
|
||||
DATA_KEY_SUPERVISOR,
|
||||
)
|
||||
from .entity import (
|
||||
HassioAddonEntity,
|
||||
HassioCoreEntity,
|
||||
HassioOSEntity,
|
||||
HassioSupervisorEntity,
|
||||
)
|
||||
from .handler import HassioAPIError
|
||||
|
||||
ENTITY_DESCRIPTION = UpdateEntityDescription(
|
||||
name="Update",
|
||||
key=ATTR_VERSION_LATEST,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Supervisor update based on a config entry."""
|
||||
coordinator = hass.data[ADDONS_COORDINATOR]
|
||||
|
||||
entities = [
|
||||
SupervisorSupervisorUpdateEntity(
|
||||
coordinator=coordinator,
|
||||
entity_description=ENTITY_DESCRIPTION,
|
||||
),
|
||||
SupervisorCoreUpdateEntity(
|
||||
coordinator=coordinator,
|
||||
entity_description=ENTITY_DESCRIPTION,
|
||||
),
|
||||
]
|
||||
|
||||
for addon in coordinator.data[DATA_KEY_ADDONS].values():
|
||||
entities.append(
|
||||
SupervisorAddonUpdateEntity(
|
||||
addon=addon,
|
||||
coordinator=coordinator,
|
||||
entity_description=ENTITY_DESCRIPTION,
|
||||
)
|
||||
)
|
||||
|
||||
if coordinator.is_hass_os:
|
||||
entities.append(
|
||||
SupervisorOSUpdateEntity(
|
||||
coordinator=coordinator,
|
||||
entity_description=ENTITY_DESCRIPTION,
|
||||
)
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class SupervisorAddonUpdateEntity(HassioAddonEntity, UpdateEntity):
|
||||
"""Update entity to handle updates for the Supervisor add-ons."""
|
||||
|
||||
_attr_supported_features = UpdateEntityFeature.INSTALL | UpdateEntityFeature.BACKUP
|
||||
|
||||
@property
|
||||
def title(self) -> str | None:
|
||||
"""Return the title of the update."""
|
||||
return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ATTR_NAME]
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str | None:
|
||||
"""Latest version available for install."""
|
||||
return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][
|
||||
ATTR_VERSION_LATEST
|
||||
]
|
||||
|
||||
@property
|
||||
def current_version(self) -> str | None:
|
||||
"""Version currently in use."""
|
||||
return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ATTR_VERSION]
|
||||
|
||||
@property
|
||||
def release_summary(self) -> str | None:
|
||||
"""Release summary for the add-on."""
|
||||
return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ATTR_CHANGELOG]
|
||||
|
||||
@property
|
||||
def entity_picture(self) -> str | None:
|
||||
"""Return the icon of the add-on if any."""
|
||||
if not self.available:
|
||||
return None
|
||||
if self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ATTR_ICON]:
|
||||
return f"/api/hassio/addons/{self._addon_slug}/icon"
|
||||
return None
|
||||
|
||||
async def async_install(
|
||||
self,
|
||||
version: str | None = None,
|
||||
backup: bool | None = False,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Install an update."""
|
||||
try:
|
||||
await async_update_addon(self.hass, slug=self._addon_slug, backup=backup)
|
||||
except HassioAPIError as err:
|
||||
raise HomeAssistantError(f"Error updating {self.title}: {err}") from err
|
||||
else:
|
||||
await self.coordinator.force_info_update_supervisor()
|
||||
|
||||
|
||||
class SupervisorOSUpdateEntity(HassioOSEntity, UpdateEntity):
|
||||
"""Update entity to handle updates for the Home Assistant Operating System."""
|
||||
|
||||
_attr_supported_features = (
|
||||
UpdateEntityFeature.INSTALL | UpdateEntityFeature.SPECIFIC_VERSION
|
||||
)
|
||||
_attr_title = "Home Assistant Operating System"
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
"""Return native value of entity."""
|
||||
return self.coordinator.data[DATA_KEY_OS][ATTR_VERSION_LATEST]
|
||||
|
||||
@property
|
||||
def current_version(self) -> str:
|
||||
"""Return native value of entity."""
|
||||
return self.coordinator.data[DATA_KEY_OS][ATTR_VERSION]
|
||||
|
||||
@property
|
||||
def entity_picture(self) -> str | None:
|
||||
"""Return the iconof the entity."""
|
||||
return "https://brands.home-assistant.io/homeassistant/icon.png"
|
||||
|
||||
@property
|
||||
def release_url(self) -> str | None:
|
||||
"""URL to the full release notes of the latest version available."""
|
||||
version = AwesomeVersion(self.latest_version)
|
||||
if version.dev or version.strategy == AwesomeVersionStrategy.UNKNOWN:
|
||||
return "https://github.com/home-assistant/operating-system/commits/dev"
|
||||
return (
|
||||
f"https://github.com/home-assistant/operating-system/releases/tag/{version}"
|
||||
)
|
||||
|
||||
async def async_install(
|
||||
self,
|
||||
version: str | None = None,
|
||||
backup: bool | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Install an update."""
|
||||
try:
|
||||
await async_update_os(self.hass, version)
|
||||
except HassioAPIError as err:
|
||||
raise HomeAssistantError(
|
||||
f"Error updating Home Assistant Operating System: {err}"
|
||||
) from err
|
||||
|
||||
|
||||
class SupervisorSupervisorUpdateEntity(HassioSupervisorEntity, UpdateEntity):
|
||||
"""Update entity to handle updates for the Home Assistant Supervisor."""
|
||||
|
||||
_attr_supported_features = UpdateEntityFeature.INSTALL
|
||||
_attr_title = "Home Assistant Supervisor"
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
"""Return native value of entity."""
|
||||
return self.coordinator.data[DATA_KEY_SUPERVISOR][ATTR_VERSION_LATEST]
|
||||
|
||||
@property
|
||||
def current_version(self) -> str:
|
||||
"""Return native value of entity."""
|
||||
return self.coordinator.data[DATA_KEY_SUPERVISOR][ATTR_VERSION]
|
||||
|
||||
@property
|
||||
def release_url(self) -> str | None:
|
||||
"""URL to the full release notes of the latest version available."""
|
||||
version = AwesomeVersion(self.latest_version)
|
||||
if version.dev or version.strategy == AwesomeVersionStrategy.UNKNOWN:
|
||||
return "https://github.com/home-assistant/supervisor/commits/main"
|
||||
return f"https://github.com/home-assistant/supervisor/releases/tag/{version}"
|
||||
|
||||
@property
|
||||
def entity_picture(self) -> str | None:
|
||||
"""Return the iconof the entity."""
|
||||
return "https://brands.home-assistant.io/hassio/icon.png"
|
||||
|
||||
async def async_install(
|
||||
self,
|
||||
version: str | None = None,
|
||||
backup: bool | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Install an update."""
|
||||
try:
|
||||
await async_update_supervisor(self.hass)
|
||||
except HassioAPIError as err:
|
||||
raise HomeAssistantError(
|
||||
f"Error updating Home Assistant Supervisor: {err}"
|
||||
) from err
|
||||
|
||||
|
||||
class SupervisorCoreUpdateEntity(HassioCoreEntity, UpdateEntity):
|
||||
"""Update entity to handle updates for Home Assistant Core."""
|
||||
|
||||
_attr_supported_features = (
|
||||
UpdateEntityFeature.INSTALL
|
||||
| UpdateEntityFeature.SPECIFIC_VERSION
|
||||
| UpdateEntityFeature.BACKUP
|
||||
)
|
||||
_attr_title = "Home Assistant Core"
|
||||
|
||||
@property
|
||||
def latest_version(self) -> str:
|
||||
"""Return native value of entity."""
|
||||
return self.coordinator.data[DATA_KEY_CORE][ATTR_VERSION_LATEST]
|
||||
|
||||
@property
|
||||
def current_version(self) -> str:
|
||||
"""Return native value of entity."""
|
||||
return self.coordinator.data[DATA_KEY_CORE][ATTR_VERSION]
|
||||
|
||||
@property
|
||||
def entity_picture(self) -> str | None:
|
||||
"""Return the iconof the entity."""
|
||||
return "https://brands.home-assistant.io/homeassistant/icon.png"
|
||||
|
||||
@property
|
||||
def release_url(self) -> str | None:
|
||||
"""URL to the full release notes of the latest version available."""
|
||||
version = AwesomeVersion(self.latest_version)
|
||||
if version.dev:
|
||||
return "https://github.com/home-assistant/core/commits/dev"
|
||||
return f"https://{'rc' if version.beta else 'www'}.home-assistant.io/latest-release-notes/"
|
||||
|
||||
async def async_install(
|
||||
self,
|
||||
version: str | None = None,
|
||||
backup: bool | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Install an update."""
|
||||
try:
|
||||
await async_update_core(self.hass, version=version, backup=backup)
|
||||
except HassioAPIError as err:
|
||||
raise HomeAssistantError(
|
||||
f"Error updating Home Assistant Core {err}"
|
||||
) from err
|
|
@ -69,6 +69,7 @@ def mock_all(aioclient_mock, request):
|
|||
"result": "ok",
|
||||
"data": {
|
||||
"result": "ok",
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"addons": [
|
||||
{
|
||||
|
@ -113,6 +114,8 @@ def mock_all(aioclient_mock, request):
|
|||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get("http://127.0.0.1/addons/test/changelog", text="")
|
||||
aioclient_mock.get("http://127.0.0.1/addons/test2/changelog", text="")
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
|
||||
)
|
||||
|
|
|
@ -55,7 +55,7 @@ def mock_all(aioclient_mock, request):
|
|||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/core/info",
|
||||
json={"result": "ok", "data": {"version_latest": "1.0.0"}},
|
||||
json={"result": "ok", "data": {"version_latest": "1.0.0", "version": "1.0.0"}},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/os/info",
|
||||
|
@ -65,7 +65,7 @@ def mock_all(aioclient_mock, request):
|
|||
"http://127.0.0.1/supervisor/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {"version_latest": "1.0.0"},
|
||||
"data": {"version_latest": "1.0.0", "version": "1.0.0"},
|
||||
"addons": [
|
||||
{
|
||||
"name": "test",
|
||||
|
@ -138,6 +138,8 @@ def mock_all(aioclient_mock, request):
|
|||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get("http://127.0.0.1/addons/test/changelog", text="")
|
||||
aioclient_mock.get("http://127.0.0.1/addons/test2/changelog", text="")
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
|
||||
)
|
||||
|
@ -496,12 +498,15 @@ async def test_device_registry_calls(hass):
|
|||
"""Test device registry entries for hassio."""
|
||||
dev_reg = async_get(hass)
|
||||
supervisor_mock_data = {
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"addons": [
|
||||
{
|
||||
"name": "test",
|
||||
"state": "started",
|
||||
"slug": "test",
|
||||
"installed": True,
|
||||
"icon": False,
|
||||
"update_available": False,
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
|
@ -513,12 +518,13 @@ async def test_device_registry_calls(hass):
|
|||
"state": "started",
|
||||
"slug": "test2",
|
||||
"installed": True,
|
||||
"icon": False,
|
||||
"update_available": False,
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"url": "https://github.com",
|
||||
},
|
||||
]
|
||||
],
|
||||
}
|
||||
os_mock_data = {
|
||||
"board": "odroid-n2",
|
||||
|
@ -539,21 +545,24 @@ async def test_device_registry_calls(hass):
|
|||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert len(dev_reg.devices) == 3
|
||||
assert len(dev_reg.devices) == 5
|
||||
|
||||
supervisor_mock_data = {
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"addons": [
|
||||
{
|
||||
"name": "test2",
|
||||
"state": "started",
|
||||
"slug": "test2",
|
||||
"installed": True,
|
||||
"icon": False,
|
||||
"update_available": False,
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"url": "https://github.com",
|
||||
},
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
# Test that when addon is removed, next update will remove the add-on and subsequent updates won't
|
||||
|
@ -566,19 +575,22 @@ async def test_device_registry_calls(hass):
|
|||
):
|
||||
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=1))
|
||||
await hass.async_block_till_done()
|
||||
assert len(dev_reg.devices) == 2
|
||||
assert len(dev_reg.devices) == 4
|
||||
|
||||
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=2))
|
||||
await hass.async_block_till_done()
|
||||
assert len(dev_reg.devices) == 2
|
||||
assert len(dev_reg.devices) == 4
|
||||
|
||||
supervisor_mock_data = {
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"addons": [
|
||||
{
|
||||
"name": "test2",
|
||||
"slug": "test2",
|
||||
"state": "started",
|
||||
"installed": True,
|
||||
"icon": False,
|
||||
"update_available": False,
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
|
@ -589,12 +601,13 @@ async def test_device_registry_calls(hass):
|
|||
"slug": "test3",
|
||||
"state": "stopped",
|
||||
"installed": True,
|
||||
"icon": False,
|
||||
"update_available": False,
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"url": "https://github.com",
|
||||
},
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
# Test that when addon is added, next update will reload the entry so we register
|
||||
|
@ -608,4 +621,4 @@ async def test_device_registry_calls(hass):
|
|||
):
|
||||
async_fire_time_changed(hass, dt_util.now() + timedelta(hours=3))
|
||||
await hass.async_block_till_done()
|
||||
assert len(dev_reg.devices) == 3
|
||||
assert len(dev_reg.devices) == 5
|
||||
|
|
|
@ -62,6 +62,7 @@ def mock_all(aioclient_mock, request):
|
|||
"result": "ok",
|
||||
"data": {
|
||||
"result": "ok",
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.0",
|
||||
"addons": [
|
||||
{
|
||||
|
@ -106,6 +107,8 @@ def mock_all(aioclient_mock, request):
|
|||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get("http://127.0.0.1/addons/test/changelog", text="")
|
||||
aioclient_mock.get("http://127.0.0.1/addons/test2/changelog", text="")
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
|
||||
)
|
||||
|
|
372
tests/components/hassio/test_update.py
Normal file
372
tests/components/hassio/test_update.py
Normal file
|
@ -0,0 +1,372 @@
|
|||
"""The tests for the hassio update entities."""
|
||||
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.hassio import DOMAIN
|
||||
from homeassistant.components.hassio.handler import HassioAPIError
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
MOCK_ENVIRON = {"HASSIO": "127.0.0.1", "HASSIO_TOKEN": "abcdefgh"}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_all(aioclient_mock, request):
|
||||
"""Mock all setup requests."""
|
||||
aioclient_mock.post("http://127.0.0.1/homeassistant/options", json={"result": "ok"})
|
||||
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"})
|
||||
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/store",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {"addons": [], "repositories": []},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/host/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"chassis": "vm",
|
||||
"operating_system": "Debian GNU/Linux 10 (buster)",
|
||||
"kernel": "4.19.0-6-amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/core/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {"version_latest": "1.0.0dev222", "version": "1.0.0dev221"},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/os/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"version_latest": "1.0.0dev2222",
|
||||
"version": "1.0.0dev2221",
|
||||
"update_available": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/supervisor/info",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"result": "ok",
|
||||
"version": "1.0.0",
|
||||
"version_latest": "1.0.1dev222",
|
||||
"addons": [
|
||||
{
|
||||
"name": "test",
|
||||
"state": "started",
|
||||
"slug": "test",
|
||||
"installed": True,
|
||||
"update_available": True,
|
||||
"icon": False,
|
||||
"version": "2.0.0",
|
||||
"version_latest": "2.0.1",
|
||||
"repository": "core",
|
||||
"url": "https://github.com/home-assistant/addons/test",
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"state": "stopped",
|
||||
"slug": "test2",
|
||||
"installed": True,
|
||||
"update_available": False,
|
||||
"icon": True,
|
||||
"version": "3.1.0",
|
||||
"version_latest": "3.1.0",
|
||||
"repository": "core",
|
||||
"url": "https://github.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/addons/test/stats",
|
||||
json={
|
||||
"result": "ok",
|
||||
"data": {
|
||||
"cpu_percent": 0.99,
|
||||
"memory_usage": 182611968,
|
||||
"memory_limit": 3977146368,
|
||||
"memory_percent": 4.59,
|
||||
"network_rx": 362570232,
|
||||
"network_tx": 82374138,
|
||||
"blk_read": 46010945536,
|
||||
"blk_write": 15051526144,
|
||||
},
|
||||
},
|
||||
)
|
||||
aioclient_mock.get("http://127.0.0.1/addons/test/changelog", text="")
|
||||
aioclient_mock.get("http://127.0.0.1/addons/test2/changelog", text="")
|
||||
aioclient_mock.get(
|
||||
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"entity_id,expected",
|
||||
[
|
||||
("update.home_assistant_operating_system_update", "on"),
|
||||
("update.home_assistant_supervisor_update", "on"),
|
||||
("update.home_assistant_core_update", "on"),
|
||||
("update.test_update", "on"),
|
||||
("update.test2_update", "off"),
|
||||
],
|
||||
)
|
||||
async def test_update_entities(hass, entity_id, expected, aioclient_mock):
|
||||
"""Test update entities."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
result = await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
assert result
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Verify that the entity have the expected state.
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == expected
|
||||
|
||||
|
||||
async def test_update_addon(hass, aioclient_mock):
|
||||
"""Test updating addon update entity."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
result = await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
assert result
|
||||
await hass.async_block_till_done()
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://127.0.0.1/addons/test/update",
|
||||
json={"result": "ok", "data": {}},
|
||||
)
|
||||
|
||||
assert await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
{"entity_id": "update.test_update"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_os(hass, aioclient_mock):
|
||||
"""Test updating OS update entity."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
result = await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
assert result
|
||||
await hass.async_block_till_done()
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://127.0.0.1/os/update",
|
||||
json={"result": "ok", "data": {}},
|
||||
)
|
||||
|
||||
assert await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
{"entity_id": "update.home_assistant_operating_system_update"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_core(hass, aioclient_mock):
|
||||
"""Test updating core update entity."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
result = await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
assert result
|
||||
await hass.async_block_till_done()
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://127.0.0.1/core/update",
|
||||
json={"result": "ok", "data": {}},
|
||||
)
|
||||
|
||||
assert await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
{"entity_id": "update.home_assistant_os_update"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_supervisor(hass, aioclient_mock):
|
||||
"""Test updating supervisor update entity."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
result = await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
assert result
|
||||
await hass.async_block_till_done()
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://127.0.0.1/supervisor/update",
|
||||
json={"result": "ok", "data": {}},
|
||||
)
|
||||
|
||||
assert await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
{"entity_id": "update.home_assistant_supervisor_update"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_addon_with_error(hass, aioclient_mock):
|
||||
"""Test updating addon update entity with error."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://127.0.0.1/addons/test/update",
|
||||
exc=HassioAPIError,
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
assert not await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
{"entity_id": "update.test_update"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_os_with_error(hass, aioclient_mock):
|
||||
"""Test updating OS update entity with error."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://127.0.0.1/os/update",
|
||||
exc=HassioAPIError,
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
assert not await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
{"entity_id": "update.home_assistant_operating_system_update"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_supervisor_with_error(hass, aioclient_mock):
|
||||
"""Test updating supervisor update entity with error."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://127.0.0.1/supervisor/update",
|
||||
exc=HassioAPIError,
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
assert not await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
{"entity_id": "update.home_assistant_supervisor_update"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_update_core_with_error(hass, aioclient_mock):
|
||||
"""Test updating core update entity with error."""
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
aioclient_mock.post(
|
||||
"http://127.0.0.1/core/update",
|
||||
exc=HassioAPIError,
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
assert not await hass.services.async_call(
|
||||
"update",
|
||||
"install",
|
||||
{"entity_id": "update.home_assistant_core_update"},
|
||||
blocking=True,
|
||||
)
|
Loading…
Add table
Reference in a new issue