Skip to content

Commit

Permalink
Add Host APIs from supervisor (#19)
Browse files Browse the repository at this point in the history
* Add Host APIs from supervisor

* Separate RebootOptions and test options

* RebootOptions not ShutdownOptions
  • Loading branch information
mdegat01 authored Oct 10, 2024
1 parent 2fae2c5 commit dd6486f
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 0 deletions.
47 changes: 47 additions & 0 deletions aiohasupervisor/host.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Host client for supervisor."""

from .client import _SupervisorComponentClient
from .models.host import (
HostInfo,
HostOptions,
RebootOptions,
Service,
ServiceList,
ShutdownOptions,
)


class HostClient(_SupervisorComponentClient):
"""Handles host access in supervisor."""

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

async def reboot(self, options: RebootOptions | None = None) -> None:
"""Reboot host."""
await self._client.post(
"host/reboot", json=options.to_dict() if options else None
)

async def shutdown(self, options: ShutdownOptions | None = None) -> None:
"""Shutdown host."""
await self._client.post(
"host/shutdown", json=options.to_dict() if options else None
)

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

async def options(self, options: HostOptions) -> None:
"""Set host options."""
await self._client.post("host/options", json=options.to_dict())

async def services(self) -> list[Service]:
"""Get list of available services on host."""
result = await self._client.get("host/services")
return ServiceList.from_dict(result.data).services

# Omitted for now - Log endpoints
14 changes: 14 additions & 0 deletions aiohasupervisor/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
HomeAssistantStopOptions,
HomeAssistantUpdateOptions,
)
from aiohasupervisor.models.host import (
HostInfo,
HostOptions,
RebootOptions,
Service,
ServiceState,
ShutdownOptions,
)
from aiohasupervisor.models.network import (
AccessPoint,
AuthMethod,
Expand Down Expand Up @@ -212,4 +220,10 @@
"Wifi",
"WifiConfig",
"WifiMode",
"HostInfo",
"HostOptions",
"RebootOptions",
"Service",
"ServiceState",
"ShutdownOptions",
]
98 changes: 98 additions & 0 deletions aiohasupervisor/models/host.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""Models for host APIs."""

from dataclasses import dataclass
from datetime import datetime
from enum import StrEnum

from .base import Request, ResponseData
from .root import HostFeature

# --- ENUMS ----


class ServiceState(StrEnum):
"""ServiceState type.
The service state is determined by systemd, not supervisor. The list below
is pulled from `systemctl --state=help`. It may be incomplete and it may
change based on the host. Therefore within a list of services there may be
some with a state not in this list parsed as string. If you find this
please create an issue or pr to get the state added.
"""

ACTIVE = "active"
RELOADING = "reloading"
INACTIVE = "inactive"
FAILED = "failed"
ACTIVATING = "activating"
DEACTIVATING = "deactivating"
MAINTENANCE = "maintenance"


# --- OBJECTS ----


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

agent_version: str | None
apparmor_version: str | None
chassis: str | None
virtualization: str | None
cpe: str | None
deployment: str | None
disk_free: float
disk_total: float
disk_used: float
disk_life_time: float
features: list[HostFeature]
hostname: str | None
llmnr_hostname: str | None
kernel: str | None
operating_system: str | None
timezone: str | None
dt_utc: datetime | None
dt_synchronized: bool | None
use_ntp: bool | None
startup_time: float | None
boot_timestamp: int | None
broadcast_llmnr: bool | None
broadcast_mdns: bool | None


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

force: bool


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

force: bool


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

hostname: str


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

name: str
description: str
state: ServiceState | str


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

services: list[Service]
7 changes: 7 additions & 0 deletions aiohasupervisor/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .client import _SupervisorClient
from .discovery import DiscoveryClient
from .homeassistant import HomeAssistantClient
from .host import HostClient
from .models.root import AvailableUpdate, AvailableUpdates, RootInfo
from .network import NetworkClient
from .os import OSClient
Expand All @@ -34,6 +35,7 @@ def __init__(
self._backups = BackupsClient(self._client)
self._discovery = DiscoveryClient(self._client)
self._network = NetworkClient(self._client)
self._host = HostClient(self._client)
self._resolution = ResolutionClient(self._client)
self._store = StoreClient(self._client)
self._supervisor = SupervisorManagementClient(self._client)
Expand Down Expand Up @@ -69,6 +71,11 @@ def network(self) -> NetworkClient:
"""Get network component client."""
return self._network

@property
def host(self) -> HostClient:
"""Get host component client."""
return self._host

@property
def resolution(self) -> ResolutionClient:
"""Get resolution center component client."""
Expand Down
41 changes: 41 additions & 0 deletions tests/fixtures/host_info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"result": "ok",
"data": {
"agent_version": "1.6.0",
"apparmor_version": "3.1.2",
"chassis": "embedded",
"virtualization": "",
"cpe": "cpe:2.3:o:home-assistant:haos:12.4.dev20240527:*:development:*:*:*:odroid-n2:*",
"deployment": "development",
"disk_free": 20.1,
"disk_total": 27.9,
"disk_used": 6.7,
"disk_life_time": 10.0,
"features": [
"reboot",
"shutdown",
"services",
"network",
"hostname",
"timedate",
"os_agent",
"haos",
"resolved",
"journal",
"disk",
"mount"
],
"hostname": "homeassistant",
"llmnr_hostname": "homeassistant3",
"kernel": "6.6.32-haos",
"operating_system": "Home Assistant OS 12.4.dev20240527",
"timezone": "Etc/UTC",
"dt_utc": "2024-10-03T00:00:00.000000+00:00",
"dt_synchronized": true,
"use_ntp": true,
"startup_time": 1.966311,
"boot_timestamp": 1716927644219811,
"broadcast_llmnr": true,
"broadcast_mdns": true
}
}
47 changes: 47 additions & 0 deletions tests/fixtures/host_services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"result": "ok",
"data": {
"services": [
{
"name": "emergency.service",
"description": "Emergency Shell",
"state": "inactive"
},
{
"name": "bluetooth.service",
"description": "Bluetooth service",
"state": "inactive"
},
{
"name": "haos-swapfile.service",
"description": "HAOS swap",
"state": "inactive"
},
{
"name": "hassos-config.service",
"description": "HassOS Configuration Manager",
"state": "inactive"
},
{
"name": "dropbear.service",
"description": "Dropbear SSH daemon",
"state": "active"
},
{
"name": "systemd-time-wait-sync.service",
"description": "Wait Until Kernel Time Synchronized",
"state": "active"
},
{
"name": "systemd-journald.service",
"description": "Journal Service",
"state": "active"
},
{
"name": "systemd-resolved.service",
"description": "Network Name Resolution",
"state": "active"
}
]
}
}
Loading

0 comments on commit dd6486f

Please sign in to comment.