mirror of
https://github.com/home-assistant/core.git
synced 2026-01-16 20:46:56 +01:00
Compare commits
1 Commits
multi_doma
...
supportpac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
374cb0e69d |
@@ -19,6 +19,7 @@ import attr
|
||||
from hass_nabucasa import AlreadyConnectedError, Cloud, auth
|
||||
from hass_nabucasa.const import STATE_DISCONNECTED
|
||||
from hass_nabucasa.voice_data import TTS_VOICES
|
||||
import psutil_home_assistant as ha_psutil
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import websocket_api
|
||||
@@ -27,6 +28,7 @@ from homeassistant.components.alexa import (
|
||||
errors as alexa_errors,
|
||||
)
|
||||
from homeassistant.components.google_assistant import helpers as google_helpers
|
||||
from homeassistant.components.hassio import get_addons_stats, get_supervisor_info
|
||||
from homeassistant.components.homeassistant import exposed_entities
|
||||
from homeassistant.components.http import KEY_HASS, HomeAssistantView, require_admin
|
||||
from homeassistant.components.http.data_validator import RequestDataValidator
|
||||
@@ -37,6 +39,7 @@ from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.hassio import is_hassio
|
||||
from homeassistant.loader import (
|
||||
async_get_custom_components,
|
||||
async_get_loaded_integration,
|
||||
@@ -571,6 +574,11 @@ class DownloadSupportPackageView(HomeAssistantView):
|
||||
"</details>\n\n"
|
||||
)
|
||||
|
||||
markdown += await self._get_host_resources_markdown(hass)
|
||||
|
||||
if is_hassio(hass):
|
||||
markdown += await self._get_addon_resources_markdown(hass)
|
||||
|
||||
log_handler = hass.data[DATA_CLOUD_LOG_HANDLER]
|
||||
logs = "\n".join(await log_handler.get_logs(hass))
|
||||
markdown += (
|
||||
@@ -584,6 +592,103 @@ class DownloadSupportPackageView(HomeAssistantView):
|
||||
|
||||
return markdown
|
||||
|
||||
async def _get_host_resources_markdown(self, hass: HomeAssistant) -> str:
|
||||
"""Get host resource usage markdown using psutil."""
|
||||
|
||||
def _collect_system_stats() -> dict[str, Any]:
|
||||
"""Collect system stats."""
|
||||
psutil_wrapper = ha_psutil.PsutilWrapper()
|
||||
psutil_mod = psutil_wrapper.psutil
|
||||
|
||||
cpu_percent = psutil_mod.cpu_percent(interval=0.1)
|
||||
memory = psutil_mod.virtual_memory()
|
||||
disk = psutil_mod.disk_usage("/")
|
||||
|
||||
return {
|
||||
"cpu_percent": cpu_percent,
|
||||
"memory_total": memory.total,
|
||||
"memory_used": memory.used,
|
||||
"memory_available": memory.available,
|
||||
"memory_percent": memory.percent,
|
||||
"disk_total": disk.total,
|
||||
"disk_used": disk.used,
|
||||
"disk_free": disk.free,
|
||||
"disk_percent": disk.percent,
|
||||
}
|
||||
|
||||
markdown = ""
|
||||
try:
|
||||
stats = await hass.async_add_executor_job(_collect_system_stats)
|
||||
|
||||
markdown += "## Host resource usage\n\n"
|
||||
markdown += "Resource | Value\n"
|
||||
markdown += "--- | ---\n"
|
||||
|
||||
markdown += f"CPU usage | {stats['cpu_percent']}%\n"
|
||||
|
||||
memory_total_gb = round(stats["memory_total"] / (1024**3), 2)
|
||||
memory_used_gb = round(stats["memory_used"] / (1024**3), 2)
|
||||
memory_available_gb = round(stats["memory_available"] / (1024**3), 2)
|
||||
markdown += f"Memory total | {memory_total_gb} GB\n"
|
||||
markdown += (
|
||||
f"Memory used | {memory_used_gb} GB ({stats['memory_percent']}%)\n"
|
||||
)
|
||||
markdown += f"Memory available | {memory_available_gb} GB\n"
|
||||
|
||||
disk_total_gb = round(stats["disk_total"] / (1024**3), 2)
|
||||
disk_used_gb = round(stats["disk_used"] / (1024**3), 2)
|
||||
disk_free_gb = round(stats["disk_free"] / (1024**3), 2)
|
||||
markdown += f"Disk total | {disk_total_gb} GB\n"
|
||||
markdown += f"Disk used | {disk_used_gb} GB ({stats['disk_percent']}%)\n"
|
||||
markdown += f"Disk free | {disk_free_gb} GB\n"
|
||||
|
||||
markdown += "\n"
|
||||
except Exception: # noqa: BLE001
|
||||
# Broad exception catch for robustness in support package generation
|
||||
markdown += "## Host resource usage\n\n"
|
||||
markdown += "Unable to collect host resource information\n\n"
|
||||
|
||||
return markdown
|
||||
|
||||
async def _get_addon_resources_markdown(self, hass: HomeAssistant) -> str:
|
||||
"""Get add-on resource usage markdown for hassio."""
|
||||
markdown = ""
|
||||
try:
|
||||
supervisor_info = get_supervisor_info(hass) or {}
|
||||
addons_stats = get_addons_stats(hass)
|
||||
addons = supervisor_info.get("addons", [])
|
||||
|
||||
if addons:
|
||||
markdown += "## Add-on resource usage\n\n"
|
||||
markdown += "<details><summary>Add-on resources</summary>\n\n"
|
||||
markdown += "Add-on | Version | State | CPU | Memory\n"
|
||||
markdown += "--- | --- | --- | --- | ---\n"
|
||||
|
||||
for addon in addons:
|
||||
slug = addon.get("slug", "unknown")
|
||||
name = addon.get("name", slug)
|
||||
version = addon.get("version", "unknown")
|
||||
state = addon.get("state", "unknown")
|
||||
|
||||
addon_stats = addons_stats.get(slug, {})
|
||||
cpu = addon_stats.get("cpu_percent")
|
||||
memory = addon_stats.get("memory_percent")
|
||||
|
||||
cpu_str = f"{cpu}%" if cpu is not None else "N/A"
|
||||
memory_str = f"{memory}%" if memory is not None else "N/A"
|
||||
|
||||
markdown += (
|
||||
f"{name} | {version} | {state} | {cpu_str} | {memory_str}\n"
|
||||
)
|
||||
|
||||
markdown += "\n</details>\n\n"
|
||||
except Exception: # noqa: BLE001
|
||||
# Broad exception catch for robustness in support package generation
|
||||
markdown += "## Add-on resource usage\n\n"
|
||||
markdown += "Unable to collect add-on resource information\n\n"
|
||||
|
||||
return markdown
|
||||
|
||||
async def get(self, request: web.Request) -> web.Response:
|
||||
"""Download support package file."""
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"alexa",
|
||||
"assist_pipeline",
|
||||
"backup",
|
||||
"google_assistant"
|
||||
"google_assistant",
|
||||
"hassio"
|
||||
],
|
||||
"codeowners": ["@home-assistant/cloud"],
|
||||
"dependencies": ["auth", "http", "repairs", "webhook", "web_rtc"],
|
||||
@@ -13,6 +14,6 @@
|
||||
"integration_type": "system",
|
||||
"iot_class": "cloud_push",
|
||||
"loggers": ["acme", "hass_nabucasa", "snitun"],
|
||||
"requirements": ["hass-nabucasa==1.9.0"],
|
||||
"requirements": ["hass-nabucasa==1.9.0", "psutil-home-assistant==0.0.1"],
|
||||
"single_config_entry": true
|
||||
}
|
||||
|
||||
1
requirements_all.txt
generated
1
requirements_all.txt
generated
@@ -1776,6 +1776,7 @@ prowlpy==1.1.1
|
||||
# homeassistant.components.proxmoxve
|
||||
proxmoxer==2.0.1
|
||||
|
||||
# homeassistant.components.cloud
|
||||
# homeassistant.components.hardware
|
||||
# homeassistant.components.recorder
|
||||
# homeassistant.components.systemmonitor
|
||||
|
||||
1
requirements_test_all.txt
generated
1
requirements_test_all.txt
generated
@@ -1522,6 +1522,7 @@ prometheus-client==0.21.0
|
||||
# homeassistant.components.prowl
|
||||
prowlpy==1.1.1
|
||||
|
||||
# homeassistant.components.cloud
|
||||
# homeassistant.components.hardware
|
||||
# homeassistant.components.recorder
|
||||
# homeassistant.components.systemmonitor
|
||||
|
||||
@@ -87,6 +87,18 @@
|
||||
|
||||
</details>
|
||||
|
||||
## Host resource usage
|
||||
|
||||
Resource | Value
|
||||
--- | ---
|
||||
CPU usage | 25.5%
|
||||
Memory total | 16.0 GB
|
||||
Memory used | 8.0 GB (50.0%)
|
||||
Memory available | 8.0 GB
|
||||
Disk total | 500.0 GB
|
||||
Disk used | 200.0 GB (40.0%)
|
||||
Disk free | 300.0 GB
|
||||
|
||||
## Full logs
|
||||
|
||||
<details><summary>Logs</summary>
|
||||
@@ -181,6 +193,18 @@
|
||||
|
||||
</details>
|
||||
|
||||
## Host resource usage
|
||||
|
||||
Resource | Value
|
||||
--- | ---
|
||||
CPU usage | 25.5%
|
||||
Memory total | 16.0 GB
|
||||
Memory used | 8.0 GB (50.0%)
|
||||
Memory available | 8.0 GB
|
||||
Disk total | 500.0 GB
|
||||
Disk used | 200.0 GB (40.0%)
|
||||
Disk free | 300.0 GB
|
||||
|
||||
## Full logs
|
||||
|
||||
<details><summary>Logs</summary>
|
||||
@@ -196,6 +220,252 @@
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_download_support_package_hassio
|
||||
'''
|
||||
## System Information
|
||||
|
||||
version | core-2025.2.0
|
||||
--- | ---
|
||||
installation_type | Home Assistant OS
|
||||
dev | False
|
||||
hassio | True
|
||||
docker | True
|
||||
container_arch | aarch64
|
||||
user | root
|
||||
virtualenv | False
|
||||
python_version | 3.13.1
|
||||
os_name | Linux
|
||||
os_version | 6.12.9
|
||||
arch | aarch64
|
||||
timezone | US/Pacific
|
||||
config_dir | config
|
||||
|
||||
## Active Integrations
|
||||
|
||||
Built-in integrations: 23
|
||||
Custom integrations: 1
|
||||
|
||||
<details><summary>Built-in integrations</summary>
|
||||
|
||||
Domain | Name
|
||||
--- | ---
|
||||
ai_task | AI Task
|
||||
auth | Auth
|
||||
binary_sensor | Binary Sensor
|
||||
cloud | Home Assistant Cloud
|
||||
cloud.ai_task | Unknown
|
||||
cloud.binary_sensor | Unknown
|
||||
cloud.conversation | Unknown
|
||||
cloud.stt | Unknown
|
||||
cloud.tts | Unknown
|
||||
conversation | Conversation
|
||||
ffmpeg | FFmpeg
|
||||
hassio | hassio
|
||||
homeassistant | Home Assistant Core Integration
|
||||
http | HTTP
|
||||
intent | Intent
|
||||
media_source | Media Source
|
||||
mock_no_info_integration | mock_no_info_integration
|
||||
repairs | Repairs
|
||||
stt | Speech-to-text (STT)
|
||||
system_health | System Health
|
||||
tts | Text-to-speech (TTS)
|
||||
web_rtc | WebRTC
|
||||
webhook | Webhook
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>Custom integrations</summary>
|
||||
|
||||
Domain | Name | Version | Documentation
|
||||
--- | --- | --- | ---
|
||||
test | Test Components | 1.2.3 | http://example.com
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>hassio</summary>
|
||||
|
||||
host_os | Home Assistant OS 14.0
|
||||
--- | ---
|
||||
update_channel | stable
|
||||
supervisor_version | supervisor-2025.01.0
|
||||
agent_version | 1.6.0
|
||||
docker_version | 27.4.1
|
||||
disk_total | 128.5 GB
|
||||
disk_used | 45.2 GB
|
||||
healthy | True
|
||||
supported | True
|
||||
host_connectivity | True
|
||||
supervisor_connectivity | True
|
||||
board | green
|
||||
supervisor_api | ok
|
||||
version_api | ok
|
||||
installed_addons | Mosquitto broker (6.4.1), Samba share (12.3.2), Visual Studio Code (5.21.2)
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>mock_no_info_integration</summary>
|
||||
|
||||
No information available
|
||||
</details>
|
||||
|
||||
<details><summary>cloud</summary>
|
||||
|
||||
logged_in | True
|
||||
--- | ---
|
||||
subscription_expiration | 2025-01-17T11:19:31+00:00
|
||||
relayer_connected | True
|
||||
relayer_region | xx-earth-616
|
||||
remote_enabled | True
|
||||
remote_connected | False
|
||||
alexa_enabled | True
|
||||
google_enabled | False
|
||||
cloud_ice_servers_enabled | True
|
||||
remote_server | us-west-1
|
||||
certificate_status | ready
|
||||
instance_id | 12345678901234567890
|
||||
can_reach_cert_server | Exception: Unexpected exception
|
||||
can_reach_cloud_auth | Failed: unreachable
|
||||
can_reach_cloud | ok
|
||||
|
||||
</details>
|
||||
|
||||
## Host resource usage
|
||||
|
||||
Resource | Value
|
||||
--- | ---
|
||||
CPU usage | 25.5%
|
||||
Memory total | 16.0 GB
|
||||
Memory used | 8.0 GB (50.0%)
|
||||
Memory available | 8.0 GB
|
||||
Disk total | 500.0 GB
|
||||
Disk used | 200.0 GB (40.0%)
|
||||
Disk free | 300.0 GB
|
||||
|
||||
## Add-on resource usage
|
||||
|
||||
<details><summary>Add-on resources</summary>
|
||||
|
||||
Add-on | Version | State | CPU | Memory
|
||||
--- | --- | --- | --- | ---
|
||||
Mosquitto broker | 6.4.1 | started | 0.5% | 1.2%
|
||||
Samba share | 12.3.2 | started | 0.1% | 0.8%
|
||||
Visual Studio Code | 5.21.2 | stopped | N/A | N/A
|
||||
|
||||
</details>
|
||||
|
||||
## Full logs
|
||||
|
||||
<details><summary>Logs</summary>
|
||||
|
||||
```logs
|
||||
2025-02-10 12:00:00.000 INFO (MainThread) [hass_nabucasa.iot] Hass nabucasa log
|
||||
2025-02-10 12:00:00.000 WARNING (MainThread) [snitun.utils.aiohttp_client] Snitun log
|
||||
2025-02-10 12:00:00.000 ERROR (MainThread) [homeassistant.components.cloud.client] Cloud log
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_download_support_package_host_resources
|
||||
'''
|
||||
## System Information
|
||||
|
||||
version | core-2025.2.0
|
||||
--- | ---
|
||||
installation_type | Home Assistant Container
|
||||
dev | False
|
||||
hassio | False
|
||||
docker | True
|
||||
container_arch | x86_64
|
||||
user | root
|
||||
virtualenv | False
|
||||
python_version | 3.13.1
|
||||
os_name | Linux
|
||||
os_version | 6.12.9
|
||||
arch | x86_64
|
||||
timezone | US/Pacific
|
||||
config_dir | config
|
||||
|
||||
## Active Integrations
|
||||
|
||||
Built-in integrations: 21
|
||||
Custom integrations: 0
|
||||
|
||||
<details><summary>Built-in integrations</summary>
|
||||
|
||||
Domain | Name
|
||||
--- | ---
|
||||
ai_task | AI Task
|
||||
auth | Auth
|
||||
binary_sensor | Binary Sensor
|
||||
cloud | Home Assistant Cloud
|
||||
cloud.ai_task | Unknown
|
||||
cloud.binary_sensor | Unknown
|
||||
cloud.conversation | Unknown
|
||||
cloud.stt | Unknown
|
||||
cloud.tts | Unknown
|
||||
conversation | Conversation
|
||||
ffmpeg | FFmpeg
|
||||
homeassistant | Home Assistant Core Integration
|
||||
http | HTTP
|
||||
intent | Intent
|
||||
media_source | Media Source
|
||||
repairs | Repairs
|
||||
stt | Speech-to-text (STT)
|
||||
system_health | System Health
|
||||
tts | Text-to-speech (TTS)
|
||||
web_rtc | WebRTC
|
||||
webhook | Webhook
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>cloud</summary>
|
||||
|
||||
logged_in | True
|
||||
--- | ---
|
||||
subscription_expiration | 2025-01-17T11:19:31+00:00
|
||||
relayer_connected | True
|
||||
relayer_region | xx-earth-616
|
||||
remote_enabled | True
|
||||
remote_connected | False
|
||||
alexa_enabled | True
|
||||
google_enabled | False
|
||||
cloud_ice_servers_enabled | True
|
||||
remote_server | us-west-1
|
||||
certificate_status | ready
|
||||
instance_id | 12345678901234567890
|
||||
can_reach_cert_server | Exception: Unexpected exception
|
||||
can_reach_cloud_auth | Failed: unreachable
|
||||
can_reach_cloud | ok
|
||||
|
||||
</details>
|
||||
|
||||
## Host resource usage
|
||||
|
||||
Resource | Value
|
||||
--- | ---
|
||||
CPU usage | 25.5%
|
||||
Memory total | 16.0 GB
|
||||
Memory used | 8.0 GB (50.0%)
|
||||
Memory available | 8.0 GB
|
||||
Disk total | 500.0 GB
|
||||
Disk used | 200.0 GB (40.0%)
|
||||
Disk free | 300.0 GB
|
||||
|
||||
## Full logs
|
||||
|
||||
<details><summary>Logs</summary>
|
||||
|
||||
```logs
|
||||
2025-02-10 12:00:00.000 INFO (MainThread) [hass_nabucasa.iot] Hass nabucasa log
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
'''
|
||||
# ---
|
||||
# name: test_download_support_package_integration_load_error
|
||||
'''
|
||||
## System Information
|
||||
@@ -246,6 +516,18 @@
|
||||
|
||||
</details>
|
||||
|
||||
## Host resource usage
|
||||
|
||||
Resource | Value
|
||||
--- | ---
|
||||
CPU usage | 25.5%
|
||||
Memory total | 16.0 GB
|
||||
Memory used | 8.0 GB (50.0%)
|
||||
Memory available | 8.0 GB
|
||||
Disk total | 500.0 GB
|
||||
Disk used | 200.0 GB (40.0%)
|
||||
Disk free | 300.0 GB
|
||||
|
||||
## Full logs
|
||||
|
||||
<details><summary>Logs</summary>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Tests for the HTTP API for the cloud component."""
|
||||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from collections.abc import Callable, Coroutine, Generator
|
||||
from copy import deepcopy
|
||||
import datetime
|
||||
from http import HTTPStatus
|
||||
@@ -114,6 +114,36 @@ PIPELINE_DATA_OTHER = {
|
||||
SUBSCRIPTION_INFO_URL = "https://api-test.hass.io/payments/subscription_info"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_psutil_wrapper() -> Generator[MagicMock]:
|
||||
"""Fixture to mock psutil for support package tests."""
|
||||
mock_memory = MagicMock()
|
||||
mock_memory.total = 16 * 1024**3 # 16 GB
|
||||
mock_memory.used = 8 * 1024**3 # 8 GB
|
||||
mock_memory.available = 8 * 1024**3 # 8 GB
|
||||
mock_memory.percent = 50.0
|
||||
|
||||
mock_disk = MagicMock()
|
||||
mock_disk.total = 500 * 1024**3 # 500 GB
|
||||
mock_disk.used = 200 * 1024**3 # 200 GB
|
||||
mock_disk.free = 300 * 1024**3 # 300 GB
|
||||
mock_disk.percent = 40.0
|
||||
|
||||
mock_psutil = MagicMock()
|
||||
mock_psutil.cpu_percent = MagicMock(return_value=25.5)
|
||||
mock_psutil.virtual_memory = MagicMock(return_value=mock_memory)
|
||||
mock_psutil.disk_usage = MagicMock(return_value=mock_disk)
|
||||
|
||||
mock_wrapper = MagicMock()
|
||||
mock_wrapper.psutil = mock_psutil
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.cloud.http_api.ha_psutil.PsutilWrapper",
|
||||
return_value=mock_wrapper,
|
||||
):
|
||||
yield mock_wrapper
|
||||
|
||||
|
||||
@pytest.fixture(name="setup_cloud")
|
||||
async def setup_cloud_fixture(hass: HomeAssistant, cloud: MagicMock) -> None:
|
||||
"""Fixture that sets up cloud."""
|
||||
@@ -1846,7 +1876,7 @@ async def test_logout_view_dispatch_event(
|
||||
|
||||
|
||||
@patch("homeassistant.components.cloud.helpers.FixedSizeQueueLogHandler.MAX_RECORDS", 3)
|
||||
@pytest.mark.usefixtures("enable_custom_integrations")
|
||||
@pytest.mark.usefixtures("enable_custom_integrations", "mock_psutil_wrapper")
|
||||
async def test_download_support_package(
|
||||
hass: HomeAssistant,
|
||||
cloud: MagicMock,
|
||||
@@ -1959,7 +1989,7 @@ async def test_download_support_package(
|
||||
assert await req.text() == snapshot
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("enable_custom_integrations")
|
||||
@pytest.mark.usefixtures("enable_custom_integrations", "mock_psutil_wrapper")
|
||||
async def test_download_support_package_custom_components_error(
|
||||
hass: HomeAssistant,
|
||||
cloud: MagicMock,
|
||||
@@ -1986,7 +2016,7 @@ async def test_download_support_package_custom_components_error(
|
||||
async def mock_empty_info(hass: HomeAssistant) -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
register.async_register_info(mock_empty_info, "/config/mock_integration")
|
||||
register.async_register_info(mock_empty_info, "/mock_integration")
|
||||
|
||||
mock_platform(
|
||||
hass,
|
||||
@@ -2071,7 +2101,7 @@ async def test_download_support_package_custom_components_error(
|
||||
assert await req.text() == snapshot
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("enable_custom_integrations")
|
||||
@pytest.mark.usefixtures("enable_custom_integrations", "mock_psutil_wrapper")
|
||||
async def test_download_support_package_integration_load_error(
|
||||
hass: HomeAssistant,
|
||||
cloud: MagicMock,
|
||||
@@ -2098,7 +2128,7 @@ async def test_download_support_package_integration_load_error(
|
||||
async def mock_empty_info(hass: HomeAssistant) -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
register.async_register_info(mock_empty_info, "/config/mock_integration")
|
||||
register.async_register_info(mock_empty_info, "/mock_integration")
|
||||
|
||||
mock_platform(
|
||||
hass,
|
||||
@@ -2188,6 +2218,277 @@ async def test_download_support_package_integration_load_error(
|
||||
assert await req.text() == snapshot
|
||||
|
||||
|
||||
@patch("homeassistant.components.cloud.helpers.FixedSizeQueueLogHandler.MAX_RECORDS", 3)
|
||||
@pytest.mark.usefixtures("enable_custom_integrations", "mock_psutil_wrapper")
|
||||
async def test_download_support_package_hassio(
|
||||
hass: HomeAssistant,
|
||||
cloud: MagicMock,
|
||||
set_cloud_prefs: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
|
||||
hass_client: ClientSessionGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test downloading a support package file with hassio resources."""
|
||||
|
||||
aioclient_mock.get("https://cloud.bla.com/status", text="")
|
||||
aioclient_mock.get(
|
||||
"https://cert-server/directory", exc=Exception("Unexpected exception")
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"https://cognito-idp.us-east-1.amazonaws.com/AAAA/.well-known/jwks.json",
|
||||
exc=aiohttp.ClientError,
|
||||
)
|
||||
|
||||
def async_register_hassio_platform(
|
||||
hass: HomeAssistant,
|
||||
register: system_health.SystemHealthRegistration,
|
||||
) -> None:
|
||||
async def mock_hassio_info(hass: HomeAssistant) -> dict[str, Any]:
|
||||
return {
|
||||
"host_os": "Home Assistant OS 14.0",
|
||||
"update_channel": "stable",
|
||||
"supervisor_version": "supervisor-2025.01.0",
|
||||
"agent_version": "1.6.0",
|
||||
"docker_version": "27.4.1",
|
||||
"disk_total": "128.5 GB",
|
||||
"disk_used": "45.2 GB",
|
||||
"healthy": True,
|
||||
"supported": True,
|
||||
"host_connectivity": True,
|
||||
"supervisor_connectivity": True,
|
||||
"board": "green",
|
||||
"supervisor_api": "ok",
|
||||
"version_api": "ok",
|
||||
"installed_addons": "Mosquitto broker (6.4.1), Samba share (12.3.2), Visual Studio Code (5.21.2)",
|
||||
}
|
||||
|
||||
register.async_register_info(mock_hassio_info, "/hassio/system")
|
||||
|
||||
mock_platform(
|
||||
hass,
|
||||
"hassio.system_health",
|
||||
MagicMock(async_register=async_register_hassio_platform),
|
||||
)
|
||||
hass.config.components.add("hassio")
|
||||
|
||||
def async_register_mock_platform(
|
||||
hass: HomeAssistant,
|
||||
register: system_health.SystemHealthRegistration,
|
||||
) -> None:
|
||||
async def mock_empty_info(hass: HomeAssistant) -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
register.async_register_info(mock_empty_info, "/config/mock_integration")
|
||||
|
||||
mock_platform(
|
||||
hass,
|
||||
"mock_no_info_integration.system_health",
|
||||
MagicMock(async_register=async_register_mock_platform),
|
||||
)
|
||||
hass.config.components.add("mock_no_info_integration")
|
||||
hass.config.components.add("test")
|
||||
|
||||
assert await async_setup_component(hass, "system_health", {})
|
||||
|
||||
with patch("uuid.UUID.hex", new_callable=PropertyMock) as hexmock:
|
||||
hexmock.return_value = "12345678901234567890"
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
DOMAIN: {
|
||||
"user_pool_id": "AAAA",
|
||||
"region": "us-east-1",
|
||||
"acme_server": "cert-server",
|
||||
"relayer_server": "cloud.bla.com",
|
||||
},
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await cloud.login("test-user", "test-pass")
|
||||
|
||||
cloud.remote.snitun_server = "us-west-1"
|
||||
cloud.remote.certificate_status = CertificateStatus.READY
|
||||
cloud.expiration_date = dt_util.parse_datetime("2025-01-17T11:19:31.0+00:00")
|
||||
|
||||
await cloud.client.async_system_message({"region": "xx-earth-616"})
|
||||
await set_cloud_prefs(
|
||||
{
|
||||
"alexa_enabled": True,
|
||||
"google_enabled": False,
|
||||
"remote_enabled": True,
|
||||
"cloud_ice_servers_enabled": True,
|
||||
}
|
||||
)
|
||||
|
||||
now = dt_util.utcnow()
|
||||
tz = now.astimezone().tzinfo
|
||||
freezer.move_to(datetime.datetime(2025, 2, 10, 12, 0, 0, tzinfo=tz))
|
||||
logging.getLogger("hass_nabucasa.iot").info(
|
||||
"This message will be dropped since this test patches MAX_RECORDS"
|
||||
)
|
||||
logging.getLogger("hass_nabucasa.iot").info("Hass nabucasa log")
|
||||
logging.getLogger("snitun.utils.aiohttp_client").warning("Snitun log")
|
||||
logging.getLogger("homeassistant.components.cloud.client").error("Cloud log")
|
||||
freezer.move_to(now)
|
||||
|
||||
cloud_client = await hass_client()
|
||||
|
||||
with (
|
||||
patch.object(hass.config, "config_dir", new="config"),
|
||||
patch(
|
||||
"homeassistant.components.homeassistant.system_health.system_info.async_get_system_info",
|
||||
return_value={
|
||||
"installation_type": "Home Assistant OS",
|
||||
"version": "2025.2.0",
|
||||
"dev": False,
|
||||
"hassio": True,
|
||||
"virtualenv": False,
|
||||
"python_version": "3.13.1",
|
||||
"docker": True,
|
||||
"container_arch": "aarch64",
|
||||
"arch": "aarch64",
|
||||
"timezone": "US/Pacific",
|
||||
"os_name": "Linux",
|
||||
"os_version": "6.12.9",
|
||||
"user": "root",
|
||||
},
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.cloud.http_api.get_supervisor_info",
|
||||
return_value={
|
||||
"addons": [
|
||||
{
|
||||
"slug": "core_mosquitto",
|
||||
"name": "Mosquitto broker",
|
||||
"version": "6.4.1",
|
||||
"state": "started",
|
||||
},
|
||||
{
|
||||
"slug": "core_samba",
|
||||
"name": "Samba share",
|
||||
"version": "12.3.2",
|
||||
"state": "started",
|
||||
},
|
||||
{
|
||||
"slug": "a0d7b954_vscode",
|
||||
"name": "Visual Studio Code",
|
||||
"version": "5.21.2",
|
||||
"state": "stopped",
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.cloud.http_api.get_addons_stats",
|
||||
return_value={
|
||||
"core_mosquitto": {
|
||||
"cpu_percent": 0.5,
|
||||
"memory_percent": 1.2,
|
||||
},
|
||||
"core_samba": {
|
||||
"cpu_percent": 0.1,
|
||||
"memory_percent": 0.8,
|
||||
},
|
||||
# No stats for vscode (stopped)
|
||||
},
|
||||
),
|
||||
):
|
||||
req = await cloud_client.get("/api/cloud/support_package")
|
||||
assert req.status == HTTPStatus.OK
|
||||
assert await req.text() == snapshot
|
||||
|
||||
|
||||
@patch("homeassistant.components.cloud.helpers.FixedSizeQueueLogHandler.MAX_RECORDS", 3)
|
||||
@pytest.mark.usefixtures("mock_psutil_wrapper")
|
||||
async def test_download_support_package_host_resources(
|
||||
hass: HomeAssistant,
|
||||
cloud: MagicMock,
|
||||
set_cloud_prefs: Callable[[dict[str, Any]], Coroutine[Any, Any, None]],
|
||||
hass_client: ClientSessionGenerator,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test downloading a support package file with psutil host resources (non-hassio)."""
|
||||
aioclient_mock.get("https://cloud.bla.com/status", text="")
|
||||
aioclient_mock.get(
|
||||
"https://cert-server/directory", exc=Exception("Unexpected exception")
|
||||
)
|
||||
aioclient_mock.get(
|
||||
"https://cognito-idp.us-east-1.amazonaws.com/AAAA/.well-known/jwks.json",
|
||||
exc=aiohttp.ClientError,
|
||||
)
|
||||
|
||||
assert await async_setup_component(hass, "system_health", {})
|
||||
|
||||
with patch("uuid.UUID.hex", new_callable=PropertyMock) as hexmock:
|
||||
hexmock.return_value = "12345678901234567890"
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
DOMAIN: {
|
||||
"user_pool_id": "AAAA",
|
||||
"region": "us-east-1",
|
||||
"acme_server": "cert-server",
|
||||
"relayer_server": "cloud.bla.com",
|
||||
},
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await cloud.login("test-user", "test-pass")
|
||||
|
||||
cloud.remote.snitun_server = "us-west-1"
|
||||
cloud.remote.certificate_status = CertificateStatus.READY
|
||||
cloud.expiration_date = dt_util.parse_datetime("2025-01-17T11:19:31.0+00:00")
|
||||
|
||||
await cloud.client.async_system_message({"region": "xx-earth-616"})
|
||||
await set_cloud_prefs(
|
||||
{
|
||||
"alexa_enabled": True,
|
||||
"google_enabled": False,
|
||||
"remote_enabled": True,
|
||||
"cloud_ice_servers_enabled": True,
|
||||
}
|
||||
)
|
||||
|
||||
now = dt_util.utcnow()
|
||||
tz = now.astimezone().tzinfo
|
||||
freezer.move_to(datetime.datetime(2025, 2, 10, 12, 0, 0, tzinfo=tz))
|
||||
logging.getLogger("hass_nabucasa.iot").info("Hass nabucasa log")
|
||||
freezer.move_to(now)
|
||||
|
||||
cloud_client = await hass_client()
|
||||
with (
|
||||
patch.object(hass.config, "config_dir", new="config"),
|
||||
patch(
|
||||
"homeassistant.components.homeassistant.system_health.system_info.async_get_system_info",
|
||||
return_value={
|
||||
"installation_type": "Home Assistant Container",
|
||||
"version": "2025.2.0",
|
||||
"dev": False,
|
||||
"hassio": False,
|
||||
"virtualenv": False,
|
||||
"python_version": "3.13.1",
|
||||
"docker": True,
|
||||
"container_arch": "x86_64",
|
||||
"arch": "x86_64",
|
||||
"timezone": "US/Pacific",
|
||||
"os_name": "Linux",
|
||||
"os_version": "6.12.9",
|
||||
"user": "root",
|
||||
},
|
||||
),
|
||||
):
|
||||
req = await cloud_client.get("/api/cloud/support_package")
|
||||
assert req.status == HTTPStatus.OK
|
||||
assert await req.text() == snapshot
|
||||
|
||||
|
||||
async def test_websocket_ice_servers(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
|
||||
Reference in New Issue
Block a user