diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index f13db03ca4c..6a383e28ff1 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -41,7 +41,8 @@ CONFIG_SCHEMA = vol.Schema( ) -DATA_HOMEASSISTANT_VERSION = "hassio_hass_version" +DATA_INFO = "hassio_info" +DATA_HOST_INFO = "hassio_host_info" HASSIO_UPDATE_INTERVAL = timedelta(minutes=55) SERVICE_ADDON_START = "addon_start" @@ -130,7 +131,29 @@ def get_homeassistant_version(hass): Async friendly. """ - return hass.data.get(DATA_HOMEASSISTANT_VERSION) + if DATA_INFO not in hass.data: + return None + return hass.data[DATA_INFO].get("homeassistant") + + +@callback +@bind_hass +def get_info(hass): + """Return generic information from Supervisor. + + Async friendly. + """ + return hass.data.get(DATA_INFO) + + +@callback +@bind_hass +def get_host_info(hass): + """Return generic host information. + + Async friendly. + """ + return hass.data.get(DATA_HOST_INFO) @callback @@ -247,20 +270,20 @@ async def async_setup(hass, config): DOMAIN, service, async_service_handler, schema=settings[1] ) - async def update_homeassistant_version(now): - """Update last available Home Assistant version.""" + async def update_info_data(now): + """Update last available supervisor information.""" try: - data = await hassio.get_homeassistant_info() - hass.data[DATA_HOMEASSISTANT_VERSION] = data["last_version"] + hass.data[DATA_INFO] = await hassio.get_info() + hass.data[DATA_HOST_INFO] = await hassio.get_host_info() except HassioAPIError as err: _LOGGER.warning("Can't read last version: %s", err) hass.helpers.event.async_track_point_in_utc_time( - update_homeassistant_version, utcnow() + HASSIO_UPDATE_INTERVAL + update_info_data, utcnow() + HASSIO_UPDATE_INTERVAL ) # Fetch last version - await update_homeassistant_version(None) + await update_info_data(None) async def async_handle_core_service(call): """Service handler for handling core services.""" diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index d929f2d3e82..9d3df7e8aec 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -68,12 +68,20 @@ class HassIO: return self.send_command("/supervisor/ping", method="get", timeout=15) @_api_data - def get_homeassistant_info(self): - """Return data for Home Assistant. + def get_info(self): + """Return generic Supervisor information. This method return a coroutine. """ - return self.send_command("/homeassistant/info", method="get") + return self.send_command("/info", method="get") + + @_api_data + def get_host_info(self): + """Return data for Host. + + This method return a coroutine. + """ + return self.send_command("/host/info", method="get") @_api_data def get_addon_info(self, addon): diff --git a/homeassistant/helpers/system_info.py b/homeassistant/helpers/system_info.py index 26ef3929a3f..293a6e7bcef 100644 --- a/homeassistant/helpers/system_info.py +++ b/homeassistant/helpers/system_info.py @@ -14,6 +14,7 @@ from .typing import HomeAssistantType async def async_get_system_info(hass: HomeAssistantType) -> Dict: """Return info about the system.""" info_object = { + "installation_type": "Unknown", "version": current_version, "dev": "dev" in current_version, "hassio": hass.components.hassio.is_hassio(), @@ -33,4 +34,26 @@ async def async_get_system_info(hass: HomeAssistantType) -> Dict: elif platform.system() == "Linux": info_object["docker"] = os.path.isfile("/.dockerenv") + # Determine installation type on current data + if info_object["docker"]: + info_object["installation_type"] = "Home Assistant Core on Docker" + elif is_virtual_env(): + info_object[ + "installation_type" + ] = "Home Assistant Core in a Python Virtual Environment" + + # Enrich with Supervisor information + if hass.components.hassio.is_hassio(): + info = hass.components.hassio.get_info() + host = hass.components.hassio.get_host_info() + + info_object["supervisor"] = info.get("supervisor") + info_object["host_os"] = host.get("operating_system") + info_object["chassis"] = host.get("chassis") + + if info.get("hassos") is not None: + info_object["installation_type"] = "Home Assistant" + else: + info_object["installation_type"] = "Home Assistant Supervised" + return info_object diff --git a/tests/components/hassio/conftest.py b/tests/components/hassio/conftest.py index abcf62915b6..5768d192c8a 100644 --- a/tests/components/hassio/conftest.py +++ b/tests/components/hassio/conftest.py @@ -19,7 +19,7 @@ def hassio_env(): "homeassistant.components.hassio.HassIO.is_connected", return_value={"result": "ok", "data": {}}, ), patch.dict(os.environ, {"HASSIO_TOKEN": "123456"}), patch( - "homeassistant.components.hassio.HassIO.get_homeassistant_info", + "homeassistant.components.hassio.HassIO.get_info", Mock(side_effect=HassioAPIError()), ): yield @@ -35,8 +35,7 @@ def hassio_stubs(hassio_env, hass, hass_client, aioclient_mock): "homeassistant.components.hassio.HassIO.update_hass_timezone", return_value={"result": "ok"}, ), patch( - "homeassistant.components.hassio.HassIO.get_homeassistant_info", - side_effect=HassioAPIError(), + "homeassistant.components.hassio.HassIO.get_info", side_effect=HassioAPIError(), ): hass.state = CoreState.starting hass.loop.run_until_complete(async_setup_component(hass, "hassio", {})) diff --git a/tests/components/hassio/test_discovery.py b/tests/components/hassio/test_discovery.py index fd4fa26f813..845c60c2f85 100644 --- a/tests/components/hassio/test_discovery.py +++ b/tests/components/hassio/test_discovery.py @@ -91,7 +91,7 @@ async def test_hassio_discovery_startup_done(hass, aioclient_mock, hassio_client "homeassistant.components.hassio.HassIO.update_hass_api", return_value={"result": "ok"}, ), patch( - "homeassistant.components.hassio.HassIO.get_homeassistant_info", + "homeassistant.components.hassio.HassIO.get_info", Mock(side_effect=HassioAPIError()), ), patch( "homeassistant.components.mqtt.config_flow.FlowHandler.async_step_hassio", diff --git a/tests/components/hassio/test_handler.py b/tests/components/hassio/test_handler.py index 0b1bbd5237c..67fcfb75d5f 100644 --- a/tests/components/hassio/test_handler.py +++ b/tests/components/hassio/test_handler.py @@ -30,26 +30,64 @@ async def test_api_ping_exeption(hassio_handler, aioclient_mock): assert aioclient_mock.call_count == 1 -async def test_api_homeassistant_info(hassio_handler, aioclient_mock): - """Test setup with API Home Assistant info.""" +async def test_api_info(hassio_handler, aioclient_mock): + """Test setup with API generic info.""" aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", - json={"result": "ok", "data": {"last_version": "10.0"}}, + "http://127.0.0.1/info", + json={ + "result": "ok", + "data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None}, + }, ) - data = await hassio_handler.get_homeassistant_info() + data = await hassio_handler.get_info() assert aioclient_mock.call_count == 1 - assert data["last_version"] == "10.0" + assert data["hassos"] is None + assert data["homeassistant"] == "0.110.0" + assert data["supervisor"] == "222" -async def test_api_homeassistant_info_error(hassio_handler, aioclient_mock): +async def test_api_info_error(hassio_handler, aioclient_mock): """Test setup with API Home Assistant info error.""" aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", json={"result": "error", "message": None} + "http://127.0.0.1/info", json={"result": "error", "message": None} ) with pytest.raises(HassioAPIError): - await hassio_handler.get_homeassistant_info() + await hassio_handler.get_info() + + assert aioclient_mock.call_count == 1 + + +async def test_api_host_info(hassio_handler, aioclient_mock): + """Test setup with API Host info.""" + aioclient_mock.get( + "http://127.0.0.1/host/info", + json={ + "result": "ok", + "data": { + "chassis": "vm", + "operating_system": "Debian GNU/Linux 10 (buster)", + "kernel": "4.19.0-6-amd64", + }, + }, + ) + + data = await hassio_handler.get_host_info() + assert aioclient_mock.call_count == 1 + assert data["chassis"] == "vm" + assert data["kernel"] == "4.19.0-6-amd64" + assert data["operating_system"] == "Debian GNU/Linux 10 (buster)" + + +async def test_api_host_info_error(hassio_handler, aioclient_mock): + """Test setup with API Home Assistant info error.""" + aioclient_mock.get( + "http://127.0.0.1/host/info", json={"result": "error", "message": None} + ) + + with pytest.raises(HassioAPIError): + await hassio_handler.get_host_info() assert aioclient_mock.call_count == 1 diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 13bae001448..c3110b6599c 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -20,8 +20,25 @@ def mock_all(aioclient_mock): aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"}) aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"}) aioclient_mock.get( - "http://127.0.0.1/homeassistant/info", - json={"result": "ok", "data": {"last_version": "10.0"}}, + "http://127.0.0.1/info", + json={ + "result": "ok", + "data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None}, + }, + ) + aioclient_mock.get( + "http://127.0.0.1/host/info", + json={ + "result": "ok", + "data": { + "result": "ok", + "data": { + "chassis": "vm", + "operating_system": "Debian GNU/Linux 10 (buster)", + "kernel": "4.19.0-6-amd64", + }, + }, + }, ) aioclient_mock.get( "http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}} @@ -34,8 +51,8 @@ async def test_setup_api_ping(hass, aioclient_mock): result = await async_setup_component(hass, "hassio", {}) assert result - assert aioclient_mock.call_count == 5 - assert hass.components.hassio.get_homeassistant_version() == "10.0" + assert aioclient_mock.call_count == 6 + assert hass.components.hassio.get_homeassistant_version() == "0.110.0" assert hass.components.hassio.is_hassio() @@ -73,7 +90,7 @@ async def test_setup_api_push_api_data(hass, aioclient_mock): ) assert result - assert aioclient_mock.call_count == 5 + assert aioclient_mock.call_count == 6 assert not aioclient_mock.mock_calls[1][2]["ssl"] assert aioclient_mock.mock_calls[1][2]["port"] == 9999 assert aioclient_mock.mock_calls[1][2]["watchdog"] @@ -89,7 +106,7 @@ async def test_setup_api_push_api_data_server_host(hass, aioclient_mock): ) assert result - assert aioclient_mock.call_count == 5 + assert aioclient_mock.call_count == 6 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"] @@ -101,7 +118,7 @@ async def test_setup_api_push_api_data_default(hass, aioclient_mock, hass_storag result = await async_setup_component(hass, "hassio", {"http": {}, "hassio": {}}) assert result - assert aioclient_mock.call_count == 5 + assert aioclient_mock.call_count == 6 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"] @@ -148,7 +165,7 @@ async def test_setup_api_existing_hassio_user(hass, aioclient_mock, hass_storage result = await async_setup_component(hass, "hassio", {"http": {}, "hassio": {}}) assert result - assert aioclient_mock.call_count == 5 + assert aioclient_mock.call_count == 6 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 @@ -162,7 +179,7 @@ async def test_setup_core_push_timezone(hass, aioclient_mock): result = await async_setup_component(hass, "hassio", {"hassio": {}}) assert result - assert aioclient_mock.call_count == 5 + assert aioclient_mock.call_count == 6 assert aioclient_mock.mock_calls[2][2]["timezone"] == "testzone" await hass.config.async_update(time_zone="America/New_York") @@ -178,7 +195,7 @@ async def test_setup_hassio_no_additional_data(hass, aioclient_mock): result = await async_setup_component(hass, "hassio", {"hassio": {}}) assert result - assert aioclient_mock.call_count == 5 + assert aioclient_mock.call_count == 6 assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" diff --git a/tests/components/updater/test_init.py b/tests/components/updater/test_init.py index 604a71f08b4..203a4df8355 100644 --- a/tests/components/updater/test_init.py +++ b/tests/components/updater/test_init.py @@ -154,7 +154,12 @@ async def test_new_version_shows_entity_after_hour_hassio( """Test if binary sensor gets updated if new version is available / Hass.io.""" mock_get_uuid.return_value = MOCK_HUUID mock_component(hass, "hassio") - hass.data["hassio_hass_version"] = "999.0" + hass.data["hassio_info"] = {"hassos": None, "homeassistant": "999.0"} + hass.data["hassio_host"] = { + "supervisor": "222", + "chassis": "vm", + "operating_system": "HassOS 4.6", + } assert await async_setup_component(hass, updater.DOMAIN, {updater.DOMAIN: {}})