Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions aiohasupervisor/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@
HomeAssistantStopOptions,
HomeAssistantUpdateOptions,
)
from aiohasupervisor.models.network import (
AccessPoint,
AuthMethod,
DockerNetwork,
InterfaceMethod,
InterfaceType,
IPv4,
IPv4Config,
IPv6,
IPv6Config,
NetworkInfo,
NetworkInterface,
NetworkInterfaceConfig,
Vlan,
VlanConfig,
Wifi,
WifiConfig,
WifiMode,
)
from aiohasupervisor.models.os import (
BootSlot,
BootSlotName,
Expand Down Expand Up @@ -176,4 +195,21 @@
"PartialRestoreOptions",
"Discovery",
"DiscoveryConfig",
"AccessPoint",
"AuthMethod",
"DockerNetwork",
"InterfaceMethod",
"InterfaceType",
"IPv4",
"IPv4Config",
"IPv6",
"IPv6Config",
"NetworkInfo",
"NetworkInterface",
"NetworkInterfaceConfig",
"Vlan",
"VlanConfig",
"Wifi",
"WifiConfig",
"WifiMode",
]
198 changes: 198 additions & 0 deletions aiohasupervisor/models/network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
"""Models for supervisor network."""

from abc import ABC
from dataclasses import dataclass
from enum import StrEnum
from ipaddress import (
IPv4Address,
IPv4Interface,
IPv4Network,
IPv6Address,
IPv6Interface,
)

from .base import Options, Request, ResponseData

# --- ENUMS ----


class InterfaceType(StrEnum):
"""InterfaceType type."""

ETHERNET = "ethernet"
WIRELESS = "wireless"
VLAN = "vlan"


class InterfaceMethod(StrEnum):
"""InterfaceMethod type."""

DISABLED = "disabled"
STATIC = "static"
AUTO = "auto"


class WifiMode(StrEnum):
"""WifiMode type."""

INFRASTRUCTURE = "infrastructure"
MESH = "mesh"
ADHOC = "adhoc"
AP = "ap"


class AuthMethod(StrEnum):
"""AuthMethod type."""

OPEN = "open"
WEP = "wep"
WPA_PSK = "wpa-psk"


# --- OBJECTS ----


@dataclass(frozen=True)
class IpBase(ABC):
"""IpBase ABC type."""

method: InterfaceMethod
ready: bool | None


@dataclass(frozen=True, slots=True)
class IPv4(IpBase, ResponseData):
"""IPv4 model."""

address: list[IPv4Interface]
nameservers: list[IPv4Address]
gateway: IPv4Address | None


@dataclass(frozen=True, slots=True)
class IPv6(IpBase, ResponseData):
"""IPv6 model."""

address: list[IPv6Interface]
nameservers: list[IPv6Address]
gateway: IPv6Address | None


@dataclass(frozen=True, slots=True)
class Wifi(ResponseData):
"""Wifi model."""

mode: WifiMode
auth: AuthMethod
ssid: str
signal: int | None


@dataclass(frozen=True, slots=True)
class Vlan(ResponseData):
"""Vlan model."""

id: int
interface: str


@dataclass(frozen=True, slots=True)
class NetworkInterface(ResponseData):
"""NetworkInterface model."""

interface: str
type: InterfaceType
enabled: bool
connected: bool
primary: bool
mac: str
ipv4: IPv4
ipv6: IPv6
wifi: Wifi | None
vlan: Vlan | None


@dataclass(frozen=True, slots=True)
class DockerNetwork(ResponseData):
"""DockerNetwork model."""

interface: str
address: IPv4Network
gateway: IPv4Address
dns: IPv4Address


@dataclass(frozen=True, slots=True)
class NetworkInfo(ResponseData):
"""NetworkInfo model."""

interfaces: list[NetworkInterface]
docker: DockerNetwork
host_internet: bool | None
supervisor_internet: bool


@dataclass(frozen=True, slots=True)
class IPv4Config(Request):
"""IPv4Config model."""

address: list[IPv4Interface] | None = None
method: InterfaceMethod | None = None
gateway: IPv4Address | None = None
nameservers: list[IPv4Address] | None = None


@dataclass(frozen=True, slots=True)
class IPv6Config(Request):
"""IPv6Config model."""

address: list[IPv6Interface] | None = None
method: InterfaceMethod | None = None
gateway: IPv6Address | None = None
nameservers: list[IPv6Address] | None = None


@dataclass(frozen=True, slots=True)
class WifiConfig(Request):
"""WifiConfig model."""

mode: WifiMode | None = None
method: AuthMethod | None = None
ssid: str | None = None
psk: str | None = None


@dataclass(frozen=True, slots=True)
class NetworkInterfaceConfig(Options):
"""NetworkInterfaceConfig model."""

ipv4: IPv4Config | None = None
ipv6: IPv6Config | None = None
wifi: WifiConfig | None = None
enabled: bool | None = None


@dataclass(frozen=True, slots=True)
class AccessPoint(ResponseData):
"""AccessPoint model."""

mode: WifiMode
ssid: str
frequency: int
signal: int
mac: str


@dataclass(frozen=True, slots=True)
class AccessPointList(ResponseData):
"""AccessPointList model."""

accesspoints: list[AccessPoint]


@dataclass(frozen=True, slots=True)
class VlanConfig(Options):
"""VlanConfig model."""

ipv4: IPv4Config | None = None
ipv6: IPv6Config | None = None
51 changes: 51 additions & 0 deletions aiohasupervisor/network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Network client for supervisor."""

from .client import _SupervisorComponentClient
from .models.network import (
AccessPoint,
AccessPointList,
NetworkInfo,
NetworkInterface,
NetworkInterfaceConfig,
VlanConfig,
)


class NetworkClient(_SupervisorComponentClient):
"""Handles network access in supervisor."""

async def info(self) -> NetworkInfo:
"""Get network info."""
result = await self._client.get("network/info")
return NetworkInfo.from_dict(result.data)

async def reload(self) -> None:
"""Reload network info caches."""
await self._client.post("network/reload")

async def interface_info(self, interface: str) -> NetworkInterface:
"""Get network interface info."""
result = await self._client.get(f"network/interface/{interface}/info")
return NetworkInterface.from_dict(result.data)

async def update_interface(
self, interface: str, config: NetworkInterfaceConfig
) -> None:
"""Update a network interface."""
await self._client.post(
f"network/interface/{interface}/update", json=config.to_dict()
)

async def access_points(self, interface: str) -> list[AccessPoint]:
"""Get access points visible to a wireless interface."""
result = await self._client.get(f"network/interface/{interface}/accesspoints")
return AccessPointList.from_dict(result.data).accesspoints

async def save_vlan(
self, interface: str, vlan: int, config: VlanConfig | None = None
) -> None:
"""Create or update a vlan for an ethernet interface."""
await self._client.post(
f"network/interface/{interface}/vlan/{vlan}",
json=config.to_dict() if config else None,
)
7 changes: 7 additions & 0 deletions aiohasupervisor/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .discovery import DiscoveryClient
from .homeassistant import HomeAssistantClient
from .models.root import AvailableUpdate, AvailableUpdates, RootInfo
from .network import NetworkClient
from .os import OSClient
from .resolution import ResolutionClient
from .store import StoreClient
Expand All @@ -32,6 +33,7 @@ def __init__(
self._os = OSClient(self._client)
self._backups = BackupsClient(self._client)
self._discovery = DiscoveryClient(self._client)
self._network = NetworkClient(self._client)
self._resolution = ResolutionClient(self._client)
self._store = StoreClient(self._client)
self._supervisor = SupervisorManagementClient(self._client)
Expand Down Expand Up @@ -62,6 +64,11 @@ def discovery(self) -> DiscoveryClient:
"""Get discovery component client."""
return self._discovery

@property
def network(self) -> NetworkClient:
"""Get network component client."""
return self._network

@property
def resolution(self) -> ResolutionClient:
"""Get resolution center component client."""
Expand Down
21 changes: 21 additions & 0 deletions tests/fixtures/network_access_points.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"result": "ok",
"data": {
"accesspoints": [
{
"mode": "infrastructure",
"ssid": "UPC4814466",
"frequency": 2462,
"signal": 47,
"mac": "AA:BB:CC:DD:EE:FF"
},
{
"mode": "infrastructure",
"ssid": "VQ@35(55720",
"frequency": 5660,
"signal": 63,
"mac": "FF:EE:DD:CC:BB:AA"
}
]
}
}
39 changes: 39 additions & 0 deletions tests/fixtures/network_info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"result": "ok",
"data": {
"interfaces": [
{
"interface": "end0",
"type": "ethernet",
"enabled": true,
"connected": true,
"primary": true,
"mac": "00:11:22:33:44:55",
"ipv4": {
"method": "static",
"address": ["192.168.1.2/24"],
"nameservers": ["192.168.1.1"],
"gateway": "192.168.1.1",
"ready": true
},
"ipv6": {
"method": "disabled",
"address": ["fe80::819d:c479:d712:7a77/64"],
"nameservers": [],
"gateway": null,
"ready": true
},
"wifi": null,
"vlan": null
}
],
"docker": {
"interface": "hassio",
"address": "172.30.32.0/23",
"gateway": "172.30.32.1",
"dns": "172.30.32.3"
},
"host_internet": true,
"supervisor_internet": true
}
}
Loading
Loading