Add more supervisor info to system info panel (#115715)

* Add virtualization field fo system info

* Add ntp sync and host connectivity

* Prevent nonetype errors

* Add supervisor_connectivity and fix tests

* Add mock of network info to other fixtures

* Update more fixtures with network/info mock
pull/118317/head
Mike Degatano 2024-05-28 10:14:42 -04:00 committed by GitHub
parent e58d060f82
commit dbcef2e3c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 131 additions and 16 deletions

View File

@ -73,6 +73,7 @@ from .const import (
DATA_HOST_INFO,
DATA_INFO,
DATA_KEY_SUPERVISOR_ISSUES,
DATA_NETWORK_INFO,
DATA_OS_INFO,
DATA_STORE,
DATA_SUPERVISOR_INFO,
@ -429,6 +430,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
hass.data[DATA_CORE_INFO],
hass.data[DATA_SUPERVISOR_INFO],
hass.data[DATA_OS_INFO],
hass.data[DATA_NETWORK_INFO],
) = await asyncio.gather(
create_eager_task(hassio.get_info()),
create_eager_task(hassio.get_host_info()),
@ -436,6 +438,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
create_eager_task(hassio.get_core_info()),
create_eager_task(hassio.get_supervisor_info()),
create_eager_task(hassio.get_os_info()),
create_eager_task(hassio.get_network_info()),
)
except HassioAPIError as err:

View File

@ -70,6 +70,7 @@ DATA_HOST_INFO = "hassio_host_info"
DATA_STORE = "hassio_store"
DATA_INFO = "hassio_info"
DATA_OS_INFO = "hassio_os_info"
DATA_NETWORK_INFO = "hassio_network_info"
DATA_SUPERVISOR_INFO = "hassio_supervisor_info"
DATA_SUPERVISOR_STATS = "hassio_supervisor_stats"
DATA_ADDONS_CHANGELOGS = "hassio_addons_changelogs"

View File

@ -42,6 +42,7 @@ from .const import (
DATA_KEY_OS,
DATA_KEY_SUPERVISOR,
DATA_KEY_SUPERVISOR_ISSUES,
DATA_NETWORK_INFO,
DATA_OS_INFO,
DATA_STORE,
DATA_SUPERVISOR_INFO,
@ -100,6 +101,16 @@ def get_supervisor_info(hass: HomeAssistant) -> dict[str, Any] | None:
return hass.data.get(DATA_SUPERVISOR_INFO)
@callback
@bind_hass
def get_network_info(hass: HomeAssistant) -> dict[str, Any] | None:
"""Return Host Network information.
Async friendly.
"""
return hass.data.get(DATA_NETWORK_INFO)
@callback
@bind_hass
def get_addons_info(hass: HomeAssistant) -> dict[str, dict[str, Any]] | None:

View File

@ -382,6 +382,14 @@ class HassIO:
"""
return self.send_command("/supervisor/info", method="get")
@api_data
def get_network_info(self) -> Coroutine:
"""Return data for the Host Network.
This method returns a coroutine.
"""
return self.send_command("/network/info", method="get")
@api_data
def get_addon_info(self, addon: str) -> Coroutine:
"""Return data for a Add-on.

View File

@ -8,7 +8,13 @@ from typing import Any
from homeassistant.components import system_health
from homeassistant.core import HomeAssistant, callback
from .coordinator import get_host_info, get_info, get_os_info, get_supervisor_info
from .coordinator import (
get_host_info,
get_info,
get_network_info,
get_os_info,
get_supervisor_info,
)
SUPERVISOR_PING = "http://{ip_address}/supervisor/ping"
OBSERVER_URL = "http://{ip_address}:4357"
@ -28,6 +34,7 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
info = get_info(hass) or {}
host_info = get_host_info(hass) or {}
supervisor_info = get_supervisor_info(hass)
network_info = get_network_info(hass) or {}
healthy: bool | dict[str, str]
if supervisor_info is not None and supervisor_info.get("healthy"):
@ -57,6 +64,10 @@ async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
"disk_used": f"{host_info.get('disk_used')} GB",
"healthy": healthy,
"supported": supported,
"host_connectivity": network_info.get("host_internet"),
"supervisor_connectivity": network_info.get("supervisor_internet"),
"ntp_synchronized": host_info.get("dt_synchronized"),
"virtualization": host_info.get("virtualization"),
}
if info.get("hassos") is not None:

View File

@ -308,3 +308,13 @@ def all_setup_requests(
},
},
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)

View File

@ -180,6 +180,16 @@ def mock_all(aioclient_mock, request):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
@pytest.mark.parametrize(

View File

@ -184,6 +184,16 @@ def mock_all(aioclient_mock, request):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
async def test_diagnostics(

View File

@ -237,6 +237,16 @@ def mock_all(aioclient_mock, request, os_info):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
async def test_setup_api_ping(
@ -248,7 +258,7 @@ async def test_setup_api_ping(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 19
assert aioclient_mock.call_count == 20
assert get_core_info(hass)["version_latest"] == "1.0.0"
assert is_hassio(hass)
@ -293,7 +303,7 @@ async def test_setup_api_push_api_data(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 19
assert aioclient_mock.call_count == 20
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 9999
assert "watchdog" not in aioclient_mock.mock_calls[1][2]
@ -312,7 +322,7 @@ async def test_setup_api_push_api_data_server_host(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 19
assert aioclient_mock.call_count == 20
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 9999
assert not aioclient_mock.mock_calls[1][2]["watchdog"]
@ -329,7 +339,7 @@ async def test_setup_api_push_api_data_default(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 19
assert aioclient_mock.call_count == 20
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 8123
refresh_token = aioclient_mock.mock_calls[1][2]["refresh_token"]
@ -409,7 +419,7 @@ async def test_setup_api_existing_hassio_user(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 19
assert aioclient_mock.call_count == 20
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 8123
assert aioclient_mock.mock_calls[1][2]["refresh_token"] == token.token
@ -426,7 +436,7 @@ async def test_setup_core_push_timezone(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 19
assert aioclient_mock.call_count == 20
assert aioclient_mock.mock_calls[2][2]["timezone"] == "testzone"
with patch("homeassistant.util.dt.set_default_time_zone"):
@ -447,7 +457,7 @@ async def test_setup_hassio_no_additional_data(
await hass.async_block_till_done()
assert result
assert aioclient_mock.call_count == 19
assert aioclient_mock.call_count == 20
assert aioclient_mock.mock_calls[-1][3]["Authorization"] == "Bearer 123456"
@ -535,14 +545,14 @@ async def test_service_calls(
)
await hass.async_block_till_done()
assert aioclient_mock.call_count == 23
assert aioclient_mock.call_count == 24
assert aioclient_mock.mock_calls[-1][2] == "test"
await hass.services.async_call("hassio", "host_shutdown", {})
await hass.services.async_call("hassio", "host_reboot", {})
await hass.async_block_till_done()
assert aioclient_mock.call_count == 25
assert aioclient_mock.call_count == 26
await hass.services.async_call("hassio", "backup_full", {})
await hass.services.async_call(
@ -557,7 +567,7 @@ async def test_service_calls(
)
await hass.async_block_till_done()
assert aioclient_mock.call_count == 27
assert aioclient_mock.call_count == 28
assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 03:48:00",
"homeassistant": True,
@ -582,7 +592,7 @@ async def test_service_calls(
)
await hass.async_block_till_done()
assert aioclient_mock.call_count == 29
assert aioclient_mock.call_count == 30
assert aioclient_mock.mock_calls[-1][2] == {
"addons": ["test"],
"folders": ["ssl"],
@ -601,7 +611,7 @@ async def test_service_calls(
)
await hass.async_block_till_done()
assert aioclient_mock.call_count == 30
assert aioclient_mock.call_count == 31
assert aioclient_mock.mock_calls[-1][2] == {
"name": "backup_name",
"location": "backup_share",
@ -617,7 +627,7 @@ async def test_service_calls(
)
await hass.async_block_till_done()
assert aioclient_mock.call_count == 31
assert aioclient_mock.call_count == 32
assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 03:48:00",
"location": None,
@ -636,7 +646,7 @@ async def test_service_calls(
)
await hass.async_block_till_done()
assert aioclient_mock.call_count == 33
assert aioclient_mock.call_count == 34
assert aioclient_mock.mock_calls[-1][2] == {
"name": "2021-11-13 11:48:00",
"location": None,
@ -1101,7 +1111,7 @@ async def test_setup_hardware_integration(
await hass.async_block_till_done(wait_background_tasks=True)
assert result
assert aioclient_mock.call_count == 19
assert aioclient_mock.call_count == 20
assert len(mock_setup_entry.mock_calls) == 1

View File

@ -202,6 +202,16 @@ def _install_default_mocks(aioclient_mock: AiohttpClientMocker):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
@pytest.mark.parametrize(

View File

@ -43,6 +43,8 @@ async def test_hassio_system_health(
"agent_version": "1337",
"disk_total": "32.0",
"disk_used": "30.0",
"dt_synchronized": True,
"virtualization": "qemu",
}
hass.data["hassio_os_info"] = {"board": "odroid-n2"}
hass.data["hassio_supervisor_info"] = {
@ -50,6 +52,10 @@ async def test_hassio_system_health(
"supported": True,
"addons": [{"name": "Awesome Addon", "version": "1.0.0"}],
}
hass.data["hassio_network_info"] = {
"host_internet": True,
"supervisor_internet": True,
}
with patch.dict(os.environ, MOCK_ENVIRON):
info = await get_system_health_info(hass, "hassio")
@ -65,13 +71,17 @@ async def test_hassio_system_health(
"disk_used": "30.0 GB",
"docker_version": "19.0.3",
"healthy": True,
"host_connectivity": True,
"supervisor_connectivity": True,
"host_os": "Home Assistant OS 5.9",
"installed_addons": "Awesome Addon (1.0.0)",
"ntp_synchronized": True,
"supervisor_api": "ok",
"supervisor_version": "supervisor-2020.11.1",
"supported": True,
"update_channel": "stable",
"version_api": "ok",
"virtualization": "qemu",
}
@ -99,6 +109,7 @@ async def test_hassio_system_health_with_issues(
"healthy": False,
"supported": False,
}
hass.data["hassio_network_info"] = {}
with patch.dict(os.environ, MOCK_ENVIRON):
info = await get_system_health_info(hass, "hassio")

View File

@ -189,6 +189,16 @@ def mock_all(aioclient_mock, request):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
@pytest.mark.parametrize(

View File

@ -80,6 +80,16 @@ async def mock_supervisor_fixture(hass, aioclient_mock):
},
},
)
aioclient_mock.get(
"http://127.0.0.1/network/info",
json={
"result": "ok",
"data": {
"host_internet": True,
"supervisor_internet": True,
},
},
)
with (
patch.dict(os.environ, {"SUPERVISOR": "127.0.0.1"}),
patch(