Skip to content

Commit 2fae2c5

Browse files
authored
Add network APIs from supervisor (#20)
* Add network APIs from supervisor * TypeError to ValueError and test none for vlan config
1 parent 03114e5 commit 2fae2c5

File tree

8 files changed

+537
-0
lines changed

8 files changed

+537
-0
lines changed

aiohasupervisor/models/__init__.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@
5353
HomeAssistantStopOptions,
5454
HomeAssistantUpdateOptions,
5555
)
56+
from aiohasupervisor.models.network import (
57+
AccessPoint,
58+
AuthMethod,
59+
DockerNetwork,
60+
InterfaceMethod,
61+
InterfaceType,
62+
IPv4,
63+
IPv4Config,
64+
IPv6,
65+
IPv6Config,
66+
NetworkInfo,
67+
NetworkInterface,
68+
NetworkInterfaceConfig,
69+
Vlan,
70+
VlanConfig,
71+
Wifi,
72+
WifiConfig,
73+
WifiMode,
74+
)
5675
from aiohasupervisor.models.os import (
5776
BootSlot,
5877
BootSlotName,
@@ -176,4 +195,21 @@
176195
"PartialRestoreOptions",
177196
"Discovery",
178197
"DiscoveryConfig",
198+
"AccessPoint",
199+
"AuthMethod",
200+
"DockerNetwork",
201+
"InterfaceMethod",
202+
"InterfaceType",
203+
"IPv4",
204+
"IPv4Config",
205+
"IPv6",
206+
"IPv6Config",
207+
"NetworkInfo",
208+
"NetworkInterface",
209+
"NetworkInterfaceConfig",
210+
"Vlan",
211+
"VlanConfig",
212+
"Wifi",
213+
"WifiConfig",
214+
"WifiMode",
179215
]

aiohasupervisor/models/network.py

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
"""Models for supervisor network."""
2+
3+
from abc import ABC
4+
from dataclasses import dataclass
5+
from enum import StrEnum
6+
from ipaddress import (
7+
IPv4Address,
8+
IPv4Interface,
9+
IPv4Network,
10+
IPv6Address,
11+
IPv6Interface,
12+
)
13+
14+
from .base import Options, Request, ResponseData
15+
16+
# --- ENUMS ----
17+
18+
19+
class InterfaceType(StrEnum):
20+
"""InterfaceType type."""
21+
22+
ETHERNET = "ethernet"
23+
WIRELESS = "wireless"
24+
VLAN = "vlan"
25+
26+
27+
class InterfaceMethod(StrEnum):
28+
"""InterfaceMethod type."""
29+
30+
DISABLED = "disabled"
31+
STATIC = "static"
32+
AUTO = "auto"
33+
34+
35+
class WifiMode(StrEnum):
36+
"""WifiMode type."""
37+
38+
INFRASTRUCTURE = "infrastructure"
39+
MESH = "mesh"
40+
ADHOC = "adhoc"
41+
AP = "ap"
42+
43+
44+
class AuthMethod(StrEnum):
45+
"""AuthMethod type."""
46+
47+
OPEN = "open"
48+
WEP = "wep"
49+
WPA_PSK = "wpa-psk"
50+
51+
52+
# --- OBJECTS ----
53+
54+
55+
@dataclass(frozen=True)
56+
class IpBase(ABC):
57+
"""IpBase ABC type."""
58+
59+
method: InterfaceMethod
60+
ready: bool | None
61+
62+
63+
@dataclass(frozen=True, slots=True)
64+
class IPv4(IpBase, ResponseData):
65+
"""IPv4 model."""
66+
67+
address: list[IPv4Interface]
68+
nameservers: list[IPv4Address]
69+
gateway: IPv4Address | None
70+
71+
72+
@dataclass(frozen=True, slots=True)
73+
class IPv6(IpBase, ResponseData):
74+
"""IPv6 model."""
75+
76+
address: list[IPv6Interface]
77+
nameservers: list[IPv6Address]
78+
gateway: IPv6Address | None
79+
80+
81+
@dataclass(frozen=True, slots=True)
82+
class Wifi(ResponseData):
83+
"""Wifi model."""
84+
85+
mode: WifiMode
86+
auth: AuthMethod
87+
ssid: str
88+
signal: int | None
89+
90+
91+
@dataclass(frozen=True, slots=True)
92+
class Vlan(ResponseData):
93+
"""Vlan model."""
94+
95+
id: int
96+
interface: str
97+
98+
99+
@dataclass(frozen=True, slots=True)
100+
class NetworkInterface(ResponseData):
101+
"""NetworkInterface model."""
102+
103+
interface: str
104+
type: InterfaceType
105+
enabled: bool
106+
connected: bool
107+
primary: bool
108+
mac: str
109+
ipv4: IPv4
110+
ipv6: IPv6
111+
wifi: Wifi | None
112+
vlan: Vlan | None
113+
114+
115+
@dataclass(frozen=True, slots=True)
116+
class DockerNetwork(ResponseData):
117+
"""DockerNetwork model."""
118+
119+
interface: str
120+
address: IPv4Network
121+
gateway: IPv4Address
122+
dns: IPv4Address
123+
124+
125+
@dataclass(frozen=True, slots=True)
126+
class NetworkInfo(ResponseData):
127+
"""NetworkInfo model."""
128+
129+
interfaces: list[NetworkInterface]
130+
docker: DockerNetwork
131+
host_internet: bool | None
132+
supervisor_internet: bool
133+
134+
135+
@dataclass(frozen=True, slots=True)
136+
class IPv4Config(Request):
137+
"""IPv4Config model."""
138+
139+
address: list[IPv4Interface] | None = None
140+
method: InterfaceMethod | None = None
141+
gateway: IPv4Address | None = None
142+
nameservers: list[IPv4Address] | None = None
143+
144+
145+
@dataclass(frozen=True, slots=True)
146+
class IPv6Config(Request):
147+
"""IPv6Config model."""
148+
149+
address: list[IPv6Interface] | None = None
150+
method: InterfaceMethod | None = None
151+
gateway: IPv6Address | None = None
152+
nameservers: list[IPv6Address] | None = None
153+
154+
155+
@dataclass(frozen=True, slots=True)
156+
class WifiConfig(Request):
157+
"""WifiConfig model."""
158+
159+
mode: WifiMode | None = None
160+
method: AuthMethod | None = None
161+
ssid: str | None = None
162+
psk: str | None = None
163+
164+
165+
@dataclass(frozen=True, slots=True)
166+
class NetworkInterfaceConfig(Options):
167+
"""NetworkInterfaceConfig model."""
168+
169+
ipv4: IPv4Config | None = None
170+
ipv6: IPv6Config | None = None
171+
wifi: WifiConfig | None = None
172+
enabled: bool | None = None
173+
174+
175+
@dataclass(frozen=True, slots=True)
176+
class AccessPoint(ResponseData):
177+
"""AccessPoint model."""
178+
179+
mode: WifiMode
180+
ssid: str
181+
frequency: int
182+
signal: int
183+
mac: str
184+
185+
186+
@dataclass(frozen=True, slots=True)
187+
class AccessPointList(ResponseData):
188+
"""AccessPointList model."""
189+
190+
accesspoints: list[AccessPoint]
191+
192+
193+
@dataclass(frozen=True, slots=True)
194+
class VlanConfig(Options):
195+
"""VlanConfig model."""
196+
197+
ipv4: IPv4Config | None = None
198+
ipv6: IPv6Config | None = None

aiohasupervisor/network.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Network client for supervisor."""
2+
3+
from .client import _SupervisorComponentClient
4+
from .models.network import (
5+
AccessPoint,
6+
AccessPointList,
7+
NetworkInfo,
8+
NetworkInterface,
9+
NetworkInterfaceConfig,
10+
VlanConfig,
11+
)
12+
13+
14+
class NetworkClient(_SupervisorComponentClient):
15+
"""Handles network access in supervisor."""
16+
17+
async def info(self) -> NetworkInfo:
18+
"""Get network info."""
19+
result = await self._client.get("network/info")
20+
return NetworkInfo.from_dict(result.data)
21+
22+
async def reload(self) -> None:
23+
"""Reload network info caches."""
24+
await self._client.post("network/reload")
25+
26+
async def interface_info(self, interface: str) -> NetworkInterface:
27+
"""Get network interface info."""
28+
result = await self._client.get(f"network/interface/{interface}/info")
29+
return NetworkInterface.from_dict(result.data)
30+
31+
async def update_interface(
32+
self, interface: str, config: NetworkInterfaceConfig
33+
) -> None:
34+
"""Update a network interface."""
35+
await self._client.post(
36+
f"network/interface/{interface}/update", json=config.to_dict()
37+
)
38+
39+
async def access_points(self, interface: str) -> list[AccessPoint]:
40+
"""Get access points visible to a wireless interface."""
41+
result = await self._client.get(f"network/interface/{interface}/accesspoints")
42+
return AccessPointList.from_dict(result.data).accesspoints
43+
44+
async def save_vlan(
45+
self, interface: str, vlan: int, config: VlanConfig | None = None
46+
) -> None:
47+
"""Create or update a vlan for an ethernet interface."""
48+
await self._client.post(
49+
f"network/interface/{interface}/vlan/{vlan}",
50+
json=config.to_dict() if config else None,
51+
)

aiohasupervisor/root.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from .discovery import DiscoveryClient
1111
from .homeassistant import HomeAssistantClient
1212
from .models.root import AvailableUpdate, AvailableUpdates, RootInfo
13+
from .network import NetworkClient
1314
from .os import OSClient
1415
from .resolution import ResolutionClient
1516
from .store import StoreClient
@@ -32,6 +33,7 @@ def __init__(
3233
self._os = OSClient(self._client)
3334
self._backups = BackupsClient(self._client)
3435
self._discovery = DiscoveryClient(self._client)
36+
self._network = NetworkClient(self._client)
3537
self._resolution = ResolutionClient(self._client)
3638
self._store = StoreClient(self._client)
3739
self._supervisor = SupervisorManagementClient(self._client)
@@ -62,6 +64,11 @@ def discovery(self) -> DiscoveryClient:
6264
"""Get discovery component client."""
6365
return self._discovery
6466

67+
@property
68+
def network(self) -> NetworkClient:
69+
"""Get network component client."""
70+
return self._network
71+
6572
@property
6673
def resolution(self) -> ResolutionClient:
6774
"""Get resolution center component client."""
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"accesspoints": [
5+
{
6+
"mode": "infrastructure",
7+
"ssid": "UPC4814466",
8+
"frequency": 2462,
9+
"signal": 47,
10+
"mac": "AA:BB:CC:DD:EE:FF"
11+
},
12+
{
13+
"mode": "infrastructure",
14+
"ssid": "VQ@35(55720",
15+
"frequency": 5660,
16+
"signal": 63,
17+
"mac": "FF:EE:DD:CC:BB:AA"
18+
}
19+
]
20+
}
21+
}

tests/fixtures/network_info.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"interfaces": [
5+
{
6+
"interface": "end0",
7+
"type": "ethernet",
8+
"enabled": true,
9+
"connected": true,
10+
"primary": true,
11+
"mac": "00:11:22:33:44:55",
12+
"ipv4": {
13+
"method": "static",
14+
"address": ["192.168.1.2/24"],
15+
"nameservers": ["192.168.1.1"],
16+
"gateway": "192.168.1.1",
17+
"ready": true
18+
},
19+
"ipv6": {
20+
"method": "disabled",
21+
"address": ["fe80::819d:c479:d712:7a77/64"],
22+
"nameservers": [],
23+
"gateway": null,
24+
"ready": true
25+
},
26+
"wifi": null,
27+
"vlan": null
28+
}
29+
],
30+
"docker": {
31+
"interface": "hassio",
32+
"address": "172.30.32.0/23",
33+
"gateway": "172.30.32.1",
34+
"dns": "172.30.32.3"
35+
},
36+
"host_internet": true,
37+
"supervisor_internet": true
38+
}
39+
}

0 commit comments

Comments
 (0)