Skip to content

Commit d6ae0c1

Browse files
mdegat01agners
andauthored
Refactor diagnostics, create backup and green/yellow settings from handler (home-assistant#154098)
Co-authored-by: Stefan Agner <[email protected]>
1 parent 58182a3 commit d6ae0c1

File tree

13 files changed

+154
-321
lines changed

13 files changed

+154
-321
lines changed

homeassistant/components/hassio/__init__.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from typing import Any, NamedTuple
1414

1515
from aiohasupervisor import SupervisorError
16+
from aiohasupervisor.models import GreenOptions, YellowOptions # noqa: F401
1617
import voluptuous as vol
1718

1819
from homeassistant.auth.const import GROUP_ID_ADMIN
@@ -123,11 +124,6 @@
123124
from .handler import ( # noqa: F401
124125
HassIO,
125126
HassioAPIError,
126-
async_create_backup,
127-
async_get_green_settings,
128-
async_get_yellow_settings,
129-
async_set_green_settings,
130-
async_set_yellow_settings,
131127
async_update_diagnostics,
132128
get_supervisor_client,
133129
)

homeassistant/components/hassio/addon_manager.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
AddonsOptions,
1616
AddonState as SupervisorAddonState,
1717
InstalledAddonComplete,
18+
PartialBackupOptions,
1819
StoreAddonUpdate,
1920
)
2021

2122
from homeassistant.core import HomeAssistant, callback
2223
from homeassistant.exceptions import HomeAssistantError
2324

24-
from .handler import HassioAPIError, async_create_backup, get_supervisor_client
25+
from .handler import HassioAPIError, get_supervisor_client
2526

2627
type _FuncType[_T, **_P, _R] = Callable[Concatenate[_T, _P], Awaitable[_R]]
2728
type _ReturnFuncType[_T, **_P, _R] = Callable[
@@ -261,17 +262,18 @@ async def async_stop_addon(self) -> None:
261262
"""Stop the managed add-on."""
262263
await self._supervisor_client.addons.stop_addon(self.addon_slug)
263264

264-
@api_error("Failed to create a backup of the {addon_name} add-on")
265+
@api_error(
266+
"Failed to create a backup of the {addon_name} add-on",
267+
expected_error_type=SupervisorError,
268+
)
265269
async def async_create_backup(self) -> None:
266270
"""Create a partial backup of the managed add-on."""
267271
addon_info = await self.async_get_addon_info()
268272
name = f"addon_{self.addon_slug}_{addon_info.version}"
269273

270274
self._logger.debug("Creating backup: %s", name)
271-
await async_create_backup(
272-
self._hass,
273-
{"name": name, "addons": [self.addon_slug]},
274-
partial=True,
275+
await self._supervisor_client.backups.partial_backup(
276+
PartialBackupOptions(name=name, addons={self.addon_slug})
275277
)
276278

277279
async def async_configure_addon(

homeassistant/components/hassio/handler.py

Lines changed: 11 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from typing import Any
1111

1212
from aiohasupervisor import SupervisorClient
13+
from aiohasupervisor.models import SupervisorOptions
1314
import aiohttp
1415
from yarl import URL
1516

@@ -22,7 +23,6 @@
2223
from homeassistant.const import SERVER_PORT
2324
from homeassistant.core import HomeAssistant
2425
from homeassistant.helpers.singleton import singleton
25-
from homeassistant.loader import bind_hass
2626

2727
from .const import ATTR_MESSAGE, ATTR_RESULT, DATA_COMPONENT, X_HASS_SOURCE
2828

@@ -66,73 +66,6 @@ async def _wrapper(*argv: _P.args, **kwargs: _P.kwargs) -> Any:
6666
return _wrapper
6767

6868

69-
@bind_hass
70-
async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> bool:
71-
"""Update Supervisor diagnostics toggle.
72-
73-
The caller of the function should handle HassioAPIError.
74-
"""
75-
hassio = hass.data[DATA_COMPONENT]
76-
return await hassio.update_diagnostics(diagnostics)
77-
78-
79-
@bind_hass
80-
@api_data
81-
async def async_create_backup(
82-
hass: HomeAssistant, payload: dict, partial: bool = False
83-
) -> dict:
84-
"""Create a full or partial backup.
85-
86-
The caller of the function should handle HassioAPIError.
87-
"""
88-
hassio = hass.data[DATA_COMPONENT]
89-
backup_type = "partial" if partial else "full"
90-
command = f"/backups/new/{backup_type}"
91-
return await hassio.send_command(command, payload=payload, timeout=None)
92-
93-
94-
@api_data
95-
async def async_get_green_settings(hass: HomeAssistant) -> dict[str, bool]:
96-
"""Return settings specific to Home Assistant Green."""
97-
hassio = hass.data[DATA_COMPONENT]
98-
return await hassio.send_command("/os/boards/green", method="get")
99-
100-
101-
@api_data
102-
async def async_set_green_settings(
103-
hass: HomeAssistant, settings: dict[str, bool]
104-
) -> dict:
105-
"""Set settings specific to Home Assistant Green.
106-
107-
Returns an empty dict.
108-
"""
109-
hassio = hass.data[DATA_COMPONENT]
110-
return await hassio.send_command(
111-
"/os/boards/green", method="post", payload=settings
112-
)
113-
114-
115-
@api_data
116-
async def async_get_yellow_settings(hass: HomeAssistant) -> dict[str, bool]:
117-
"""Return settings specific to Home Assistant Yellow."""
118-
hassio = hass.data[DATA_COMPONENT]
119-
return await hassio.send_command("/os/boards/yellow", method="get")
120-
121-
122-
@api_data
123-
async def async_set_yellow_settings(
124-
hass: HomeAssistant, settings: dict[str, bool]
125-
) -> dict:
126-
"""Set settings specific to Home Assistant Yellow.
127-
128-
Returns an empty dict.
129-
"""
130-
hassio = hass.data[DATA_COMPONENT]
131-
return await hassio.send_command(
132-
"/os/boards/yellow", method="post", payload=settings
133-
)
134-
135-
13669
class HassIO:
13770
"""Small API wrapper for Hass.io."""
13871

@@ -257,16 +190,6 @@ def update_hass_config(self, timezone: str, country: str | None) -> Coroutine:
257190
"/supervisor/options", payload={"timezone": timezone, "country": country}
258191
)
259192

260-
@_api_bool
261-
def update_diagnostics(self, diagnostics: bool) -> Coroutine:
262-
"""Update Supervisor diagnostics setting.
263-
264-
This method returns a coroutine.
265-
"""
266-
return self.send_command(
267-
"/supervisor/options", payload={"diagnostics": diagnostics}
268-
)
269-
270193
async def send_command(
271194
self,
272195
command: str,
@@ -341,3 +264,13 @@ def get_supervisor_client(hass: HomeAssistant) -> SupervisorClient:
341264
os.environ.get("SUPERVISOR_TOKEN", ""),
342265
session=hassio.websession,
343266
)
267+
268+
269+
async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> None:
270+
"""Update Supervisor diagnostics toggle.
271+
272+
The caller of the function should handle SupervisorError.
273+
"""
274+
await get_supervisor_client(hass).supervisor.set_options(
275+
SupervisorOptions(diagnostics=diagnostics)
276+
)

homeassistant/components/homeassistant_green/config_flow.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,20 @@
66
import logging
77
from typing import Any
88

9-
import aiohttp
109
import voluptuous as vol
1110

1211
from homeassistant.components.hassio import (
13-
HassioAPIError,
14-
async_get_green_settings,
15-
async_set_green_settings,
12+
GreenOptions,
13+
SupervisorError,
14+
get_supervisor_client,
1615
)
1716
from homeassistant.config_entries import (
1817
ConfigEntry,
1918
ConfigFlow,
2019
ConfigFlowResult,
2120
OptionsFlow,
2221
)
23-
from homeassistant.core import callback
22+
from homeassistant.core import HomeAssistant, async_get_hass, callback
2423
from homeassistant.helpers import selector
2524
from homeassistant.helpers.hassio import is_hassio
2625

@@ -49,7 +48,7 @@ def async_get_options_flow(
4948
config_entry: ConfigEntry,
5049
) -> HomeAssistantGreenOptionsFlow:
5150
"""Return the options flow."""
52-
return HomeAssistantGreenOptionsFlow()
51+
return HomeAssistantGreenOptionsFlow(async_get_hass())
5352

5453
async def async_step_system(
5554
self, data: dict[str, Any] | None = None
@@ -63,6 +62,11 @@ class HomeAssistantGreenOptionsFlow(OptionsFlow):
6362

6463
_hw_settings: dict[str, bool] | None = None
6564

65+
def __init__(self, hass: HomeAssistant, *args: Any, **kwargs: Any) -> None:
66+
"""Instantiate options flow."""
67+
super().__init__(*args, **kwargs)
68+
self._supervisor_client = get_supervisor_client(hass)
69+
6670
async def async_step_init(
6771
self, user_input: dict[str, Any] | None = None
6872
) -> ConfigFlowResult:
@@ -76,27 +80,27 @@ async def async_step_hardware_settings(
7680
self, user_input: dict[str, Any] | None = None
7781
) -> ConfigFlowResult:
7882
"""Handle hardware settings."""
79-
8083
if user_input is not None:
8184
if self._hw_settings == user_input:
8285
return self.async_create_entry(data={})
8386
try:
8487
async with asyncio.timeout(10):
85-
await async_set_green_settings(self.hass, user_input)
86-
except (aiohttp.ClientError, TimeoutError, HassioAPIError) as err:
88+
await self._supervisor_client.os.set_green_options(
89+
GreenOptions.from_dict(user_input)
90+
)
91+
except (TimeoutError, SupervisorError) as err:
8792
_LOGGER.warning("Failed to write hardware settings", exc_info=err)
8893
return self.async_abort(reason="write_hw_settings_error")
8994
return self.async_create_entry(data={})
9095

9196
try:
9297
async with asyncio.timeout(10):
93-
self._hw_settings: dict[str, bool] = await async_get_green_settings(
94-
self.hass
95-
)
96-
except (aiohttp.ClientError, TimeoutError, HassioAPIError) as err:
98+
green_info = await self._supervisor_client.os.green_info()
99+
except (TimeoutError, SupervisorError) as err:
97100
_LOGGER.warning("Failed to read hardware settings", exc_info=err)
98101
return self.async_abort(reason="read_hw_settings_error")
99102

103+
self._hw_settings: dict[str, bool] = green_info.to_dict()
100104
schema = self.add_suggested_values_to_schema(
101105
STEP_HW_SETTINGS_SCHEMA, self._hw_settings
102106
)

homeassistant/components/homeassistant_yellow/config_flow.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@
77
import logging
88
from typing import TYPE_CHECKING, Any, Protocol, final
99

10-
import aiohttp
1110
import voluptuous as vol
1211

1312
from homeassistant.components.hassio import (
14-
HassioAPIError,
15-
async_get_yellow_settings,
16-
async_set_yellow_settings,
13+
SupervisorError,
14+
YellowOptions,
1715
get_supervisor_client,
1816
)
1917
from homeassistant.components.homeassistant_hardware.firmware_config_flow import (
@@ -222,21 +220,22 @@ async def async_step_hardware_settings(
222220
return self.async_create_entry(data={})
223221
try:
224222
async with asyncio.timeout(10):
225-
await async_set_yellow_settings(self.hass, user_input)
226-
except (aiohttp.ClientError, TimeoutError, HassioAPIError) as err:
223+
await self._supervisor_client.os.set_yellow_options(
224+
YellowOptions.from_dict(user_input)
225+
)
226+
except (TimeoutError, SupervisorError) as err:
227227
_LOGGER.warning("Failed to write hardware settings", exc_info=err)
228228
return self.async_abort(reason="write_hw_settings_error")
229229
return await self.async_step_reboot_menu()
230230

231231
try:
232232
async with asyncio.timeout(10):
233-
self._hw_settings: dict[str, bool] = await async_get_yellow_settings(
234-
self.hass
235-
)
236-
except (aiohttp.ClientError, TimeoutError, HassioAPIError) as err:
233+
yellow_info = await self._supervisor_client.os.yellow_info()
234+
except (TimeoutError, SupervisorError) as err:
237235
_LOGGER.warning("Failed to read hardware settings", exc_info=err)
238236
return self.async_abort(reason="read_hw_settings_error")
239237

238+
self._hw_settings: dict[str, bool] = yellow_info.to_dict()
240239
schema = self.add_suggested_values_to_schema(
241240
STEP_HW_SETTINGS_SCHEMA, self._hw_settings
242241
)

tests/components/conftest.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414

1515
from aiohasupervisor.models import (
1616
Discovery,
17+
GreenInfo,
1718
JobsInfo,
1819
Repository,
1920
ResolutionInfo,
2021
StoreAddon,
2122
StoreInfo,
23+
YellowInfo,
2224
)
2325
import pytest
2426
import voluptuous as vol
@@ -430,11 +432,9 @@ def uninstall_addon_fixture(supervisor_client: AsyncMock) -> AsyncMock:
430432

431433

432434
@pytest.fixture(name="create_backup")
433-
def create_backup_fixture() -> Generator[AsyncMock]:
435+
def create_backup_fixture(supervisor_client: AsyncMock) -> AsyncMock:
434436
"""Mock create backup."""
435-
from .hassio.common import mock_create_backup # noqa: PLC0415
436-
437-
yield from mock_create_backup()
437+
return supervisor_client.backups.partial_backup
438438

439439

440440
@pytest.fixture(name="update_addon")
@@ -517,6 +517,24 @@ def jobs_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
517517
return supervisor_client.jobs.info
518518

519519

520+
@pytest.fixture(name="os_yellow_info")
521+
def os_yellow_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
522+
"""Mock yellow info API from supervisor OS."""
523+
supervisor_client.os.yellow_info.return_value = YellowInfo(
524+
disk_led=True, heartbeat_led=True, power_led=True
525+
)
526+
return supervisor_client.os.yellow_info
527+
528+
529+
@pytest.fixture(name="os_green_info")
530+
def os_green_info_fixture(supervisor_client: AsyncMock) -> AsyncMock:
531+
"""Mock green info API from supervisor OS."""
532+
supervisor_client.os.green_info.return_value = GreenInfo(
533+
activity_led=True, power_led=True, system_health_led=True
534+
)
535+
return supervisor_client.os.green_info
536+
537+
520538
@pytest.fixture(name="supervisor_client")
521539
def supervisor_client() -> Generator[AsyncMock]:
522540
"""Mock the supervisor client."""

tests/components/hassio/common.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
from __future__ import annotations
44

5-
from collections.abc import Generator
65
from dataclasses import fields
76
import logging
87
from types import MethodType
98
from typing import Any
10-
from unittest.mock import AsyncMock, Mock, patch
9+
from unittest.mock import AsyncMock, Mock
1110

1211
from aiohasupervisor.models import (
1312
AddonsOptions,
@@ -197,14 +196,6 @@ async def set_addon_options(slug: str, options: AddonsOptions) -> None:
197196
return set_addon_options
198197

199198

200-
def mock_create_backup() -> Generator[AsyncMock]:
201-
"""Mock create backup."""
202-
with patch(
203-
"homeassistant.components.hassio.addon_manager.async_create_backup"
204-
) as create_backup:
205-
yield create_backup
206-
207-
208199
def mock_addon_stats(supervisor_client: AsyncMock) -> AsyncMock:
209200
"""Mock addon stats."""
210201
supervisor_client.addons.addon_stats.return_value = addon_stats = Mock(

0 commit comments

Comments
 (0)