From dbcef2e3c3951336dbfb3b2e8dd20900f430111b Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 28 May 2024 10:14:42 -0400 Subject: [PATCH] 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 --- homeassistant/components/hassio/__init__.py | 3 ++ homeassistant/components/hassio/const.py | 1 + .../components/hassio/coordinator.py | 11 +++++ homeassistant/components/hassio/handler.py | 8 ++++ .../components/hassio/system_health.py | 13 +++++- tests/components/hassio/conftest.py | 10 +++++ tests/components/hassio/test_binary_sensor.py | 10 +++++ tests/components/hassio/test_diagnostics.py | 10 +++++ tests/components/hassio/test_init.py | 40 ++++++++++++------- tests/components/hassio/test_sensor.py | 10 +++++ tests/components/hassio/test_system_health.py | 11 +++++ tests/components/hassio/test_update.py | 10 +++++ tests/components/onboarding/test_views.py | 10 +++++ 13 files changed, 131 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 6a084688e99..34d15501c48 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -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: diff --git a/homeassistant/components/hassio/const.py b/homeassistant/components/hassio/const.py index 46fa1006c61..6e6c9006fca 100644 --- a/homeassistant/components/hassio/const.py +++ b/homeassistant/components/hassio/const.py @@ -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" diff --git a/homeassistant/components/hassio/coordinator.py b/homeassistant/components/hassio/coordinator.py index 0a5c4dba184..024128f4ef8 100644 --- a/homeassistant/components/hassio/coordinator.py +++ b/homeassistant/components/hassio/coordinator.py @@ -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: diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index a7c8d8774de..305b9d4961b 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -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. diff --git a/homeassistant/components/hassio/system_health.py b/homeassistant/components/hassio/system_health.py index 10b75c2e100..bc8da2a2a92 100644 --- a/homeassistant/components/hassio/system_health.py +++ b/homeassistant/components/hassio/system_health.py @@ -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: diff --git a/tests/components/hassio/conftest.py b/tests/components/hassio/conftest.py index 21eeedb89ad..c32e2cb2bfb 100644 --- a/tests/components/hassio/conftest.py +++ b/tests/components/hassio/conftest.py @@ -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, + }, + }, + ) diff --git a/tests/components/hassio/test_binary_sensor.py b/tests/components/hassio/test_binary_sensor.py index d502d6ea730..bbe498223d1 100644 --- a/tests/components/hassio/test_binary_sensor.py +++ b/tests/components/hassio/test_binary_sensor.py @@ -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( diff --git a/tests/components/hassio/test_diagnostics.py b/tests/components/hassio/test_diagnostics.py index 6b0dae170c6..83ddd0dbd33 100644 --- a/tests/components/hassio/test_diagnostics.py +++ b/tests/components/hassio/test_diagnostics.py @@ -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( diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index ff038b620eb..d4ec2d0149c 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -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 diff --git a/tests/components/hassio/test_sensor.py b/tests/components/hassio/test_sensor.py index 55cec90ec58..8780d57da45 100644 --- a/tests/components/hassio/test_sensor.py +++ b/tests/components/hassio/test_sensor.py @@ -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( diff --git a/tests/components/hassio/test_system_health.py b/tests/components/hassio/test_system_health.py index 873365aa3a0..c4c2b861e6e 100644 --- a/tests/components/hassio/test_system_health.py +++ b/tests/components/hassio/test_system_health.py @@ -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") diff --git a/tests/components/hassio/test_update.py b/tests/components/hassio/test_update.py index 0a823f33592..e79e975a52f 100644 --- a/tests/components/hassio/test_update.py +++ b/tests/components/hassio/test_update.py @@ -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( diff --git a/tests/components/onboarding/test_views.py b/tests/components/onboarding/test_views.py index 3b60178b6ec..45fa654e20f 100644 --- a/tests/components/onboarding/test_views.py +++ b/tests/components/onboarding/test_views.py @@ -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(