Skip to content

Commit 4a5802b

Browse files
committed
Add discovery APIs from supervisor
1 parent 4f8de4d commit 4a5802b

File tree

8 files changed

+233
-0
lines changed

8 files changed

+233
-0
lines changed

aiohasupervisor/discovery.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Discovery client for supervisor."""
2+
3+
from uuid import UUID
4+
5+
from .client import _SupervisorComponentClient
6+
from .const import ResponseType
7+
from .models.discovery import Discovery, DiscoveryConfig, DiscoveryList, SetDiscovery
8+
9+
10+
class DiscoveryClient(_SupervisorComponentClient):
11+
"""Handles discovery access in supervisor."""
12+
13+
async def list(self) -> DiscoveryList:
14+
"""List discovered and available services."""
15+
result = await self._client.get("discovery")
16+
return DiscoveryList.from_dict(result.data)
17+
18+
async def get(self, uuid: UUID) -> Discovery:
19+
"""Get discovery details for a service."""
20+
result = await self._client.get(f"discovery/{uuid.hex}")
21+
return Discovery.from_dict(result.data)
22+
23+
async def delete(self, uuid: UUID) -> None:
24+
"""Remove discovery for a service."""
25+
await self._client.delete(f"discovery/{uuid.hex}")
26+
27+
async def set(self, config: DiscoveryConfig) -> UUID:
28+
"""Inform supervisor of an available service."""
29+
result = await self._client.post(
30+
"discovery", json=config.to_dict(), response_type=ResponseType.JSON
31+
)
32+
return SetDiscovery.from_dict(result.data).uuid

aiohasupervisor/models/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
PartialBackupOptions,
4141
PartialRestoreOptions,
4242
)
43+
from aiohasupervisor.models.discovery import (
44+
Discovery,
45+
DiscoveryConfig,
46+
DiscoveryList,
47+
)
4348
from aiohasupervisor.models.homeassistant import (
4449
HomeAssistantInfo,
4550
HomeAssistantOptions,
@@ -170,4 +175,7 @@
170175
"NewBackup",
171176
"PartialBackupOptions",
172177
"PartialRestoreOptions",
178+
"Discovery",
179+
"DiscoveryConfig",
180+
"DiscoveryList",
173181
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Models for discovery component."""
2+
3+
from dataclasses import dataclass
4+
from typing import Any
5+
from uuid import UUID
6+
7+
from .base import Request, ResponseData
8+
9+
10+
@dataclass(frozen=True, slots=True)
11+
class DiscoveryConfig(Request):
12+
"""DiscoveryConfig model."""
13+
14+
service: str
15+
config: dict[str, Any]
16+
17+
18+
@dataclass(frozen=True, slots=True)
19+
class Discovery(ResponseData):
20+
"""Discovery model."""
21+
22+
addon: str
23+
service: str
24+
uuid: UUID
25+
config: dict[str, Any]
26+
27+
28+
@dataclass(frozen=True, slots=True)
29+
class DiscoveryList(ResponseData):
30+
"""DiscoveryList model."""
31+
32+
discovery: list[Discovery]
33+
services: dict[str, list[str]]
34+
35+
36+
@dataclass(frozen=True, slots=True)
37+
class SetDiscovery(ResponseData):
38+
"""SetDiscovery model."""
39+
40+
uuid: UUID

aiohasupervisor/root.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .addons import AddonsClient
88
from .backups import BackupsClient
99
from .client import _SupervisorClient
10+
from .discovery import DiscoveryClient
1011
from .homeassistant import HomeAssistantClient
1112
from .models.root import AvailableUpdate, AvailableUpdates, RootInfo
1213
from .os import OSClient
@@ -30,6 +31,7 @@ def __init__(
3031
self._addons = AddonsClient(self._client)
3132
self._os = OSClient(self._client)
3233
self._backups = BackupsClient(self._client)
34+
self._discovery = DiscoveryClient(self._client)
3335
self._resolution = ResolutionClient(self._client)
3436
self._store = StoreClient(self._client)
3537
self._supervisor = SupervisorManagementClient(self._client)
@@ -55,6 +57,11 @@ def backups(self) -> BackupsClient:
5557
"""Get backups component client."""
5658
return self._backups
5759

60+
@property
61+
def discovery(self) -> DiscoveryClient:
62+
"""Get discovery component client."""
63+
return self._discovery
64+
5865
@property
5966
def resolution(self) -> ResolutionClient:
6067
"""Get resolution center component client."""

tests/fixtures/discovery_get.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"addon": "core_mosquitto",
5+
"service": "mqtt",
6+
"uuid": "889ca604cff84004894e53d181655b3a",
7+
"config": {
8+
"host": "core-mosquitto",
9+
"port": 1883,
10+
"ssl": false,
11+
"protocol": "3.1.1",
12+
"username": "homeassistant",
13+
"password": "abc123"
14+
}
15+
}
16+
}

tests/fixtures/discovery_list.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"discovery": [
5+
{
6+
"addon": "core_mosquitto",
7+
"service": "mqtt",
8+
"uuid": "889ca604cff84004894e53d181655b3a",
9+
"config": {
10+
"host": "core-mosquitto",
11+
"port": 1883,
12+
"ssl": false,
13+
"protocol": "3.1.1",
14+
"username": "homeassistant",
15+
"password": "abc123"
16+
}
17+
}
18+
],
19+
"services": {
20+
"vlc_telnet": ["core_vlc"],
21+
"esphome": [
22+
"5c53de3b_esphome-beta",
23+
"5c53de3b_esphome-dev",
24+
"5c53de3b_esphome"
25+
],
26+
"zwave_js": [
27+
"core_zwave_js",
28+
"a0d7b954_zwavejs2mqtt",
29+
"77f1785d_zwave_mock_server"
30+
],
31+
"matter": ["core_matter_server"],
32+
"wyoming": [
33+
"core_whisper",
34+
"core_assist_microphone",
35+
"core_piper",
36+
"core_openwakeword"
37+
],
38+
"otbr": ["core_openthread_border_router", "core_silabs_multiprotocol"],
39+
"mqtt": ["core_mosquitto"],
40+
"motioneye": ["a0d7b954_motioneye"],
41+
"deconz": ["core_deconz"],
42+
"adguard": ["a0d7b954_adguard"]
43+
}
44+
}
45+
}

tests/fixtures/discovery_set.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"uuid": "889ca604cff84004894e53d181655b3a"
5+
}
6+
}

tests/test_discovery.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Test discovery supervisor client."""
2+
3+
from uuid import UUID
4+
5+
from aioresponses import aioresponses
6+
from yarl import URL
7+
8+
from aiohasupervisor import SupervisorClient
9+
from aiohasupervisor.models import DiscoveryConfig
10+
11+
from . import load_fixture
12+
from .const import SUPERVISOR_URL
13+
14+
15+
async def test_discovery_list(
16+
responses: aioresponses, supervisor_client: SupervisorClient
17+
) -> None:
18+
"""Test Discovery list API."""
19+
responses.get(
20+
f"{SUPERVISOR_URL}/discovery",
21+
status=200,
22+
body=load_fixture("discovery_list.json"),
23+
)
24+
disc_list = await supervisor_client.discovery.list()
25+
assert disc_list.discovery[0].addon == "core_mosquitto"
26+
assert disc_list.discovery[0].service == "mqtt"
27+
assert disc_list.discovery[0].uuid.hex == "889ca604cff84004894e53d181655b3a"
28+
assert disc_list.discovery[0].config["host"] == "core-mosquitto"
29+
assert disc_list.services["vlc_telnet"] == ["core_vlc"]
30+
assert disc_list.services["zwave_js"] == [
31+
"core_zwave_js",
32+
"a0d7b954_zwavejs2mqtt",
33+
"77f1785d_zwave_mock_server",
34+
]
35+
36+
37+
async def test_get_discovery(
38+
responses: aioresponses, supervisor_client: SupervisorClient
39+
) -> None:
40+
"""Test Discovery get API."""
41+
uuid = UUID("889ca604cff84004894e53d181655b3a")
42+
responses.get(
43+
f"{SUPERVISOR_URL}/discovery/{uuid.hex}",
44+
status=200,
45+
body=load_fixture("discovery_get.json"),
46+
)
47+
discovery = await supervisor_client.discovery.get(uuid)
48+
assert discovery.addon == "core_mosquitto"
49+
assert discovery.service == "mqtt"
50+
assert discovery.uuid == uuid
51+
assert discovery.config["host"] == "core-mosquitto"
52+
assert discovery.config["port"] == 1883
53+
assert discovery.config["ssl"] is False
54+
55+
56+
async def test_delete_discovery(
57+
responses: aioresponses, supervisor_client: SupervisorClient
58+
) -> None:
59+
"""Test Discovery delete API."""
60+
uuid = UUID("889ca604cff84004894e53d181655b3a")
61+
responses.delete(f"{SUPERVISOR_URL}/discovery/{uuid.hex}", status=200)
62+
assert await supervisor_client.discovery.delete(uuid) is None
63+
assert responses.requests.keys() == {
64+
("DELETE", URL(f"{SUPERVISOR_URL}/discovery/{uuid.hex}"))
65+
}
66+
67+
68+
async def test_set_discovery(
69+
responses: aioresponses, supervisor_client: SupervisorClient
70+
) -> None:
71+
"""Test Discovery set API."""
72+
responses.post(
73+
f"{SUPERVISOR_URL}/discovery",
74+
status=200,
75+
body=load_fixture("discovery_set.json"),
76+
)
77+
assert await supervisor_client.discovery.set(
78+
DiscoveryConfig(service="mqtt", config={})
79+
) == UUID("889ca604cff84004894e53d181655b3a")

0 commit comments

Comments
 (0)