diff --git a/aiohasupervisor/host.py b/aiohasupervisor/host.py index 558dce4..c160092 100644 --- a/aiohasupervisor/host.py +++ b/aiohasupervisor/host.py @@ -3,6 +3,7 @@ from .client import _SupervisorComponentClient from .const import TIMEOUT_60_SECONDS from .models.host import ( + DiskUsage, HostInfo, HostOptions, RebootOptions, @@ -47,4 +48,12 @@ async def services(self) -> list[Service]: result = await self._client.get("host/services") return ServiceList.from_dict(result.data).services + async def get_disk_usage(self, max_depth: int = 1) -> DiskUsage: + """Get disk usage.""" + result = await self._client.get( + "host/disks/default/usage", + params={"max_depth": str(max_depth)}, + ) + return DiskUsage.from_dict(result.data) + # Omitted for now - Log endpoints diff --git a/aiohasupervisor/models/host.py b/aiohasupervisor/models/host.py index 3f393ca..9562eee 100644 --- a/aiohasupervisor/models/host.py +++ b/aiohasupervisor/models/host.py @@ -96,3 +96,14 @@ class ServiceList(ResponseData): """ServiceList model.""" services: list[Service] + + +@dataclass(frozen=True, slots=True) +class DiskUsage(ResponseData): + """DiskUsage model.""" + + id: str + used_bytes: int + label: str | None = None + total_bytes: int | None = None + children: list["DiskUsage"] | None = None diff --git a/tests/fixtures/host_disk_usage.json b/tests/fixtures/host_disk_usage.json new file mode 100644 index 0000000..a7bf20f --- /dev/null +++ b/tests/fixtures/host_disk_usage.json @@ -0,0 +1,149 @@ +{ + "result": "ok", + "data": { + "id": "default", + "label": "Default", + "total_bytes": 503312781312, + "used_bytes": 430243422208, + "children": [ + { + "id": "addons_data", + "label": "Addons Data", + "used_bytes": 42347618720, + "children": [ + { + "id": "77f1785d_remote_api", + "label": "Remote API", + "used_bytes": 2, + "children": null + }, + { + "id": "core_samba", + "label": "Core Samba", + "used_bytes": 517, + "children": null + }, + { + "id": "a0d7b954_plex", + "label": "Plex", + "used_bytes": 757750613, + "children": null + }, + { + "id": "core_whisper", + "label": "Core Whisper", + "used_bytes": 560933406, + "children": null + } + ] + }, + { + "id": "addons_config", + "label": "Addons Config", + "used_bytes": 5283318814, + "children": [ + { + "id": "core_zwave_js", + "label": "Core Z-Wave JS", + "used_bytes": 258064, + "children": null + }, + { + "id": "db21ed7f_qbittorrent", + "label": "QBittorrent", + "used_bytes": 9508283, + "children": null + } + ] + }, + { + "id": "media", + "label": "Media", + "used_bytes": 476680019, + "children": [ + { + "id": "jellyfin", + "label": "Jellyfin", + "used_bytes": 409122725, + "children": null + } + ] + }, + { + "id": "share", + "label": "Share", + "used_bytes": 37477206419, + "children": [ + { + "id": "supervisor", + "label": "Supervisor", + "used_bytes": 929143825, + "children": null + }, + { + "id": "openwakeword", + "label": "OpenWakeword", + "used_bytes": 2263836, + "children": null + } + ] + }, + { + "id": "backup", + "label": "Backup", + "used_bytes": 268350699520, + "children": null + }, + { + "id": "ssl", + "label": "SSL", + "used_bytes": 202912633, + "children": [ + { + "id": "nginxproxymanager", + "label": "Nginx Proxy Manager", + "used_bytes": 202879859, + "children": null + }, + { + "id": "openvpn_server", + "label": "OpenVPN Server", + "used_bytes": 27500, + "children": null + } + ] + }, + { + "id": "homeassistant", + "label": "Home Assistant", + "used_bytes": 444089236, + "children": [ + { + "id": "image", + "label": "Image", + "used_bytes": 571875, + "children": null + }, + { + "id": "custom_components", + "label": "Custom Components", + "used_bytes": 58343146, + "children": null + }, + { + "id": "www", + "label": "www", + "used_bytes": 70562846, + "children": null + } + ] + }, + { + "id": "system", + "label": "System", + "used_bytes": 75660896847, + "children": null + } + ] + } +} diff --git a/tests/test_host.py b/tests/test_host.py index 1dd4cb0..05af9ef 100644 --- a/tests/test_host.py +++ b/tests/test_host.py @@ -111,3 +111,94 @@ async def test_host_services( assert result[-1].name == "systemd-resolved.service" assert result[-1].description == "Network Name Resolution" assert result[-1].state == "active" + + +async def test_host_disk_usage( + responses: aioresponses, supervisor_client: SupervisorClient +) -> None: + """Test host disk usage API.""" + responses.get( + f"{SUPERVISOR_URL}/host/disks/default/usage?max_depth=1", + status=200, + body=load_fixture("host_disk_usage.json"), + ) + result = await supervisor_client.host.get_disk_usage() + + # Test top-level properties + assert result.total_bytes == 503312781312 + assert result.used_bytes == 430243422208 + assert result.children is not None + + # Test children structure + children = result.children + assert children is not None + assert len(children) == 8 # Should have 8 children + + # Find specific children by id + assert ( + addons_data := next(child for child in children if child.id == "addons_data") + ) + assert next(child for child in children if child.id == "addons_config") + assert next(child for child in children if child.id == "media") + assert next(child for child in children if child.id == "share") + assert (backup := next(child for child in children if child.id == "backup")) + assert next(child for child in children if child.id == "ssl") + assert ( + homeassistant := next( + child for child in children if child.id == "homeassistant" + ) + ) + assert next(child for child in children if child.id == "system") + + # Test nested children (recursive structure) + assert addons_data.used_bytes == 42347618720 + assert addons_data.children is not None + assert len(addons_data.children) == 4 + + # Find specific nested children + assert next( + child for child in addons_data.children if child.id == "77f1785d_remote_api" + ) + assert next(child for child in addons_data.children if child.id == "core_samba") + assert ( + plex_addon := next( + child for child in addons_data.children if child.id == "a0d7b954_plex" + ) + ) + assert next(child for child in addons_data.children if child.id == "core_whisper") + + # Test deeper nesting + assert plex_addon.used_bytes == 757750613 + assert plex_addon.children is None # Leaf node + + # Test another branch + assert homeassistant.used_bytes == 444089236 + assert homeassistant.children is not None + assert len(homeassistant.children) == 3 + + # Find specific homeassistant children + assert next(child for child in homeassistant.children if child.id == "image") + assert next( + child for child in homeassistant.children if child.id == "custom_components" + ) + assert next(child for child in homeassistant.children if child.id == "www") + + # Test leaf node without children + assert backup.used_bytes == 268350699520 + assert backup.children is None + + +async def test_host_disk_usage_with_custom_depth( + responses: aioresponses, supervisor_client: SupervisorClient +) -> None: + """Test host disk usage API with custom max_depth.""" + responses.get( + f"{SUPERVISOR_URL}/host/disks/default/usage?max_depth=3", + status=200, + body=load_fixture("host_disk_usage.json"), + ) + result = await supervisor_client.host.get_disk_usage(max_depth=3) + + # Test that the custom max_depth parameter was used + assert result.total_bytes == 503312781312 + assert result.used_bytes == 430243422208