diff --git a/homeassistant/components/hassio/__init__.py b/homeassistant/components/hassio/__init__.py index 4c9529c7840..e367a935ace 100644 --- a/homeassistant/components/hassio/__init__.py +++ b/homeassistant/components/hassio/__init__.py @@ -41,6 +41,7 @@ from homeassistant.helpers.event import async_call_later from homeassistant.helpers.storage import Store from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.loader import bind_hass from homeassistant.util.async_ import create_eager_task from homeassistant.util.dt import now @@ -283,6 +284,7 @@ def hostname_from_addon_slug(addon_slug: str) -> str: @callback +@bind_hass def get_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return generic information from Supervisor. @@ -292,6 +294,7 @@ def get_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback +@bind_hass def get_host_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return generic host information. @@ -301,6 +304,7 @@ def get_host_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback +@bind_hass def get_store(hass: HomeAssistant) -> dict[str, Any] | None: """Return store information. @@ -310,6 +314,7 @@ def get_store(hass: HomeAssistant) -> dict[str, Any] | None: @callback +@bind_hass def get_supervisor_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return Supervisor information. @@ -319,6 +324,7 @@ def get_supervisor_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback +@bind_hass def get_addons_info(hass: HomeAssistant) -> dict[str, dict[str, Any]] | None: """Return Addons info. @@ -328,6 +334,7 @@ def get_addons_info(hass: HomeAssistant) -> dict[str, dict[str, Any]] | None: @callback +@bind_hass def get_addons_stats(hass: HomeAssistant) -> dict[str, Any]: """Return Addons stats. @@ -337,6 +344,7 @@ def get_addons_stats(hass: HomeAssistant) -> dict[str, Any]: @callback +@bind_hass def get_core_stats(hass: HomeAssistant) -> dict[str, Any]: """Return core stats. @@ -346,6 +354,7 @@ def get_core_stats(hass: HomeAssistant) -> dict[str, Any]: @callback +@bind_hass def get_supervisor_stats(hass: HomeAssistant) -> dict[str, Any]: """Return supervisor stats. @@ -355,6 +364,7 @@ def get_supervisor_stats(hass: HomeAssistant) -> dict[str, Any]: @callback +@bind_hass def get_addons_changelogs(hass: HomeAssistant): """Return Addons changelogs. @@ -364,6 +374,7 @@ def get_addons_changelogs(hass: HomeAssistant): @callback +@bind_hass def get_os_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return OS information. @@ -373,6 +384,7 @@ def get_os_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback +@bind_hass def get_core_info(hass: HomeAssistant) -> dict[str, Any] | None: """Return Home Assistant Core information from Supervisor. @@ -382,6 +394,7 @@ def get_core_info(hass: HomeAssistant) -> dict[str, Any] | None: @callback +@bind_hass def get_issues_info(hass: HomeAssistant) -> SupervisorIssues | None: """Return Supervisor issues info. @@ -391,6 +404,7 @@ def get_issues_info(hass: HomeAssistant) -> SupervisorIssues | None: @callback +@bind_hass def is_hassio(hass: HomeAssistant) -> bool: """Return true if Hass.io is loaded. diff --git a/homeassistant/components/hassio/handler.py b/homeassistant/components/hassio/handler.py index 29b70abdd11..9b8e6367647 100644 --- a/homeassistant/components/hassio/handler.py +++ b/homeassistant/components/hassio/handler.py @@ -19,6 +19,7 @@ from homeassistant.components.http import ( ) from homeassistant.const import SERVER_PORT from homeassistant.core import HomeAssistant +from homeassistant.loader import bind_hass from .const import ATTR_DISCOVERY, DOMAIN, X_HASS_SOURCE @@ -62,6 +63,7 @@ def api_data( return _wrapper +@bind_hass async def async_get_addon_info(hass: HomeAssistant, slug: str) -> dict: """Return add-on info. @@ -83,6 +85,7 @@ async def async_get_addon_store_info(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, method="get") +@bind_hass async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> bool: """Update Supervisor diagnostics toggle. @@ -92,6 +95,7 @@ async def async_update_diagnostics(hass: HomeAssistant, diagnostics: bool) -> bo return await hassio.update_diagnostics(diagnostics) +@bind_hass @api_data async def async_install_addon(hass: HomeAssistant, slug: str) -> dict: """Install add-on. @@ -103,6 +107,7 @@ async def async_install_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=None) +@bind_hass @api_data async def async_uninstall_addon(hass: HomeAssistant, slug: str) -> dict: """Uninstall add-on. @@ -114,6 +119,7 @@ async def async_uninstall_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=60) +@bind_hass @api_data async def async_update_addon( hass: HomeAssistant, @@ -133,6 +139,7 @@ async def async_update_addon( ) +@bind_hass @api_data async def async_start_addon(hass: HomeAssistant, slug: str) -> dict: """Start add-on. @@ -144,6 +151,7 @@ async def async_start_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=60) +@bind_hass @api_data async def async_restart_addon(hass: HomeAssistant, slug: str) -> dict: """Restart add-on. @@ -155,6 +163,7 @@ async def async_restart_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=None) +@bind_hass @api_data async def async_stop_addon(hass: HomeAssistant, slug: str) -> dict: """Stop add-on. @@ -166,6 +175,7 @@ async def async_stop_addon(hass: HomeAssistant, slug: str) -> dict: return await hassio.send_command(command, timeout=60) +@bind_hass @api_data async def async_set_addon_options( hass: HomeAssistant, slug: str, options: dict @@ -179,6 +189,7 @@ async def async_set_addon_options( return await hassio.send_command(command, payload=options) +@bind_hass async def async_get_addon_discovery_info(hass: HomeAssistant, slug: str) -> dict | None: """Return discovery data for an add-on.""" hassio: HassIO = hass.data[DOMAIN] @@ -187,6 +198,7 @@ async def async_get_addon_discovery_info(hass: HomeAssistant, slug: str) -> dict return next((addon for addon in discovered_addons if addon["addon"] == slug), None) +@bind_hass @api_data async def async_create_backup( hass: HomeAssistant, payload: dict, partial: bool = False @@ -201,6 +213,7 @@ async def async_create_backup( return await hassio.send_command(command, payload=payload, timeout=None) +@bind_hass @api_data async def async_update_os(hass: HomeAssistant, version: str | None = None) -> dict: """Update Home Assistant Operating System. @@ -216,6 +229,7 @@ async def async_update_os(hass: HomeAssistant, version: str | None = None) -> di ) +@bind_hass @api_data async def async_update_supervisor(hass: HomeAssistant) -> dict: """Update Home Assistant Supervisor. @@ -227,6 +241,7 @@ async def async_update_supervisor(hass: HomeAssistant) -> dict: return await hassio.send_command(command, timeout=None) +@bind_hass @api_data async def async_update_core( hass: HomeAssistant, version: str | None = None, backup: bool = False @@ -244,6 +259,7 @@ async def async_update_core( ) +@bind_hass @_api_bool async def async_apply_suggestion(hass: HomeAssistant, suggestion_uuid: str) -> dict: """Apply a suggestion from supervisor's resolution center. diff --git a/homeassistant/helpers/network.py b/homeassistant/helpers/network.py index 08d6dfe021e..58ca191feb0 100644 --- a/homeassistant/helpers/network.py +++ b/homeassistant/helpers/network.py @@ -40,11 +40,7 @@ def get_supervisor_network_url( hass: HomeAssistant, *, allow_ssl: bool = False ) -> str | None: """Get URL for home assistant within supervisor network.""" - # Local import to avoid circular dependencies - # pylint: disable-next=import-outside-toplevel - from homeassistant.components.hassio import is_hassio - - if hass.config.api is None or not is_hassio(hass): + if hass.config.api is None or not hass.components.hassio.is_hassio(): return None scheme = "http" @@ -129,10 +125,6 @@ def get_url( prefer_cloud: bool = False, ) -> str: """Get a URL to this instance.""" - # Local import to avoid circular dependencies - # pylint: disable-next=import-outside-toplevel - from homeassistant.components.hassio import get_host_info, is_hassio - if require_current_request and http.current_request.get() is None: raise NoURLAvailableError @@ -184,7 +176,8 @@ def get_url( ) known_hostnames = ["localhost"] - if is_hassio(hass) and (host_info := get_host_info(hass)): + if hass.components.hassio.is_hassio(): + host_info = hass.components.hassio.get_host_info() known_hostnames.extend( [host_info["hostname"], f"{host_info['hostname']}.local"] ) diff --git a/homeassistant/helpers/system_info.py b/homeassistant/helpers/system_info.py index d08db3f981a..8af04c11c18 100644 --- a/homeassistant/helpers/system_info.py +++ b/homeassistant/helpers/system_info.py @@ -10,6 +10,7 @@ from typing import Any from homeassistant.const import __version__ as current_version from homeassistant.core import HomeAssistant +from homeassistant.loader import bind_hass from homeassistant.util.package import is_docker_env, is_virtual_env _LOGGER = logging.getLogger(__name__) @@ -26,13 +27,10 @@ def is_official_image() -> bool: cached_get_user = cache(getuser) +@bind_hass async def async_get_system_info(hass: HomeAssistant) -> dict[str, Any]: """Return info about the system.""" - # Local import to avoid circular dependencies - # pylint: disable-next=import-outside-toplevel - from homeassistant.components import hassio - - is_hassio = hassio.is_hassio(hass) + is_hassio = hass.components.hassio.is_hassio() info_object = { "installation_type": "Unknown", @@ -70,11 +68,11 @@ async def async_get_system_info(hass: HomeAssistant) -> dict[str, Any]: # Enrich with Supervisor information if is_hassio: - if not (info := hassio.get_info(hass)): + if not (info := hass.components.hassio.get_info()): _LOGGER.warning("No Home Assistant Supervisor info available") info = {} - host = hassio.get_host_info(hass) or {} + host = hass.components.hassio.get_host_info() or {} info_object["supervisor"] = info.get("supervisor") info_object["host_os"] = host.get("operating_system") info_object["docker_version"] = info.get("docker") diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index a6f94152af0..7d0943b3677 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -15,9 +15,7 @@ from homeassistant.components.hassio import ( DOMAIN, STORAGE_KEY, async_get_addon_store_info, - get_core_info, hostname_from_addon_slug, - is_hassio, ) from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY from homeassistant.components.hassio.handler import HassioAPIError @@ -248,8 +246,8 @@ async def test_setup_api_ping( assert result assert aioclient_mock.call_count == 19 - assert get_core_info(hass)["version_latest"] == "1.0.0" - assert is_hassio(hass) + assert hass.components.hassio.get_core_info()["version_latest"] == "1.0.0" + assert hass.components.hassio.is_hassio() async def test_setup_api_panel( @@ -467,7 +465,7 @@ async def test_warn_when_cannot_connect( result = await async_setup_component(hass, "hassio", {}) assert result - assert is_hassio(hass) + assert hass.components.hassio.is_hassio() assert "Not connected with the supervisor / system too busy!" in caplog.text diff --git a/tests/helpers/test_network.py b/tests/helpers/test_network.py index b697cd4373e..b8fbfc9346b 100644 --- a/tests/helpers/test_network.py +++ b/tests/helpers/test_network.py @@ -32,16 +32,6 @@ def mock_current_request_mock(): yield mock_current_request -@pytest.fixture -def patch_hassio(): - """Patch the hassio component.""" - with patch("homeassistant.components.hassio.is_hassio", return_value=True), patch( - "homeassistant.components.hassio.get_host_info", - return_value={"hostname": "homeassistant"}, - ): - yield - - async def test_get_url_internal(hass: HomeAssistant) -> None: """Test getting an instance URL when the user has set an internal URL.""" assert hass.config.internal_url is None @@ -576,7 +566,7 @@ async def test_get_request_host(hass: HomeAssistant) -> None: async def test_get_current_request_url_with_known_host( - hass: HomeAssistant, current_request, patch_hassio + hass: HomeAssistant, current_request ) -> None: """Test getting current request URL with known hosts addresses.""" hass.config.api = Mock(use_ssl=False, port=8123, local_ip="127.0.0.1") @@ -605,6 +595,10 @@ async def test_get_current_request_url_with_known_host( # Ensure hostname from Supervisor is accepted transparently mock_component(hass, "hassio") + hass.components.hassio.is_hassio = Mock(return_value=True) + hass.components.hassio.get_host_info = Mock( + return_value={"hostname": "homeassistant"} + ) with patch( "homeassistant.helpers.network._get_request_host", @@ -668,8 +662,11 @@ async def test_is_internal_request(hass: HomeAssistant, mock_current_request) -> assert is_internal_request(hass), mock_current_request.return_value.url # Test for matching against HassOS hostname - with patch("homeassistant.components.hassio.is_hassio", return_value=True), patch( - "homeassistant.components.hassio.get_host_info", + with patch.object( + hass.components.hassio, "is_hassio", return_value=True + ), patch.object( + hass.components.hassio, + "get_host_info", return_value={"hostname": "hellohost"}, ): for allowed in ("hellohost", "hellohost.local"):