Remove hassio from ALLOWED_USED_COMPONENTS and move some functions to helper (#127228)

* Remove hassio from ALLOWED_USED_COMPONENTS

* Move HassioServiceInfo to helpers.service_info

* Deprecate moved functions

* Add note about deprecation

* Fix tests

* Implement suggestion

* Typo

* Update pyproject.toml

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>

---------

Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
This commit is contained in:
Robert Resch 2024-10-30 12:43:41 +01:00 committed by GitHub
parent 8151403bf6
commit 380974eed4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 259 additions and 108 deletions

View file

@ -7,7 +7,6 @@ from typing import Any
from adguardhome import AdGuardHome, AdGuardHomeConnectionError
import voluptuous as vol
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import (
CONF_HOST,
@ -18,6 +17,7 @@ from homeassistant.const import (
CONF_VERIFY_SSL,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .const import DOMAIN

View file

@ -29,6 +29,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.entity_registry as er
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.storage import Store
from homeassistant.helpers.system_info import async_get_system_info
from homeassistant.loader import (
@ -136,7 +137,7 @@ class Analytics:
@property
def supervisor(self) -> bool:
"""Return bool if a supervisor is present."""
return hassio.is_hassio(self.hass)
return is_hassio(self.hass)
async def load(self) -> None:
"""Load preferences."""

View file

@ -1,7 +1,7 @@
{
"domain": "analytics",
"name": "Analytics",
"after_dependencies": ["energy", "recorder"],
"after_dependencies": ["energy", "hassio", "recorder"],
"codeowners": ["@home-assistant/core", "@ludeeus"],
"dependencies": ["api", "websocket_api"],
"documentation": "https://www.home-assistant.io/integrations/analytics",

View file

@ -1,8 +1,8 @@
"""The Backup integration."""
from homeassistant.components.hassio import is_hassio
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.typing import ConfigType
from .const import DATA_MANAGER, DOMAIN, LOGGER

View file

@ -20,7 +20,6 @@ from pydeconz.utils import (
import voluptuous as vol
from homeassistant.components import ssdp
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import (
SOURCE_HASSIO,
ConfigEntry,
@ -31,6 +30,7 @@ from homeassistant.config_entries import (
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .const import (
CONF_ALLOW_CLIP_SENSOR,

View file

@ -21,7 +21,6 @@ import aiohttp
import voluptuous as vol
from homeassistant.components import dhcp, zeroconf
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import (
SOURCE_REAUTH,
ConfigEntry,
@ -32,6 +31,7 @@ from homeassistant.config_entries import (
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
from homeassistant.core import callback
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
from homeassistant.util.json import json_loads_object

View file

@ -2,10 +2,11 @@
from __future__ import annotations
from homeassistant.components.hassio import get_os_info, is_hassio
from homeassistant.components.hassio import get_os_info
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.hassio import is_hassio
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

View file

@ -5,6 +5,7 @@ from __future__ import annotations
import asyncio
from contextlib import suppress
from datetime import datetime
from functools import partial
import logging
import os
import re
@ -38,8 +39,22 @@ from homeassistant.helpers import (
discovery_flow,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.deprecation import (
DeprecatedConstant,
all_with_deprecated_constants,
check_if_deprecated_constant,
deprecated_function,
dir_with_deprecated_constants,
)
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.hassio import (
get_supervisor_ip as _get_supervisor_ip,
is_hassio as _is_hassio,
)
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.service_info.hassio import (
HassioServiceInfo as _HassioServiceInfo,
)
from homeassistant.helpers.storage import Store
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
@ -97,7 +112,7 @@ from .coordinator import (
get_supervisor_info, # noqa: F401
get_supervisor_stats, # noqa: F401
)
from .discovery import HassioServiceInfo, async_setup_discovery_view # noqa: F401
from .discovery import async_setup_discovery_view # noqa: F401
from .handler import ( # noqa: F401
HassIO,
HassioAPIError,
@ -117,6 +132,14 @@ from .websocket_api import async_load_websocket_api
_LOGGER = logging.getLogger(__name__)
get_supervisor_ip = deprecated_function(
"homeassistant.helpers.hassio.get_supervisor_ip", breaks_in_ha_version="2025.11"
)(_get_supervisor_ip)
_DEPRECATED_HassioServiceInfo = DeprecatedConstant(
_HassioServiceInfo,
"homeassistant.helpers.service_info.hassio.HassioServiceInfo",
"2025.11",
)
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
@ -272,21 +295,16 @@ def hostname_from_addon_slug(addon_slug: str) -> str:
@callback
@deprecated_function(
"homeassistant.helpers.hassio.is_hassio", breaks_in_ha_version="2025.11"
)
@bind_hass
def is_hassio(hass: HomeAssistant) -> bool:
"""Return true if Hass.io is loaded.
Async friendly.
"""
return DOMAIN in hass.config.components
@callback
def get_supervisor_ip() -> str | None:
"""Return the supervisor ip address."""
if "SUPERVISOR" not in os.environ:
return None
return os.environ["SUPERVISOR"].partition(":")[0]
return _is_hassio(hass)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: C901
@ -551,3 +569,11 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.pop(ADDONS_COORDINATOR, None)
return unload_ok
# These can be removed if no deprecated constant are in this module anymore
__getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
__dir__ = partial(
dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
)
__all__ = all_with_deprecated_constants(globals())

View file

@ -3,7 +3,6 @@
from __future__ import annotations
import asyncio
from dataclasses import dataclass
import logging
from typing import Any
@ -16,9 +15,9 @@ from homeassistant import config_entries
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import ATTR_SERVICE, EVENT_HOMEASSISTANT_START
from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.data_entry_flow import BaseServiceInfo
from homeassistant.helpers import discovery_flow
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .const import ATTR_ADDON, ATTR_UUID, DOMAIN
from .handler import HassIO, get_supervisor_client
@ -26,16 +25,6 @@ from .handler import HassIO, get_supervisor_client
_LOGGER = logging.getLogger(__name__)
@dataclass(slots=True)
class HassioServiceInfo(BaseServiceInfo):
"""Prepared info from hassio entries."""
config: dict[str, Any]
name: str
slug: str
uuid: str
@callback
def async_setup_discovery_view(hass: HomeAssistant, hassio: HassIO) -> None:
"""Discovery setup."""

View file

@ -5,10 +5,11 @@ import logging
from awesomeversion import AwesomeVersion, AwesomeVersionStrategy
from homeassistant.components.hassio import get_supervisor_info, is_hassio
from homeassistant.components.hassio import get_supervisor_info
from homeassistant.const import __version__
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import DOMAIN, REQUEST_TIMEOUT, UPDATE_INTERVAL

View file

@ -1,6 +1,7 @@
{
"domain": "homeassistant_alerts",
"name": "Home Assistant Alerts",
"after_dependencies": ["hassio"],
"codeowners": ["@home-assistant/core"],
"config_flow": false,
"documentation": "https://www.home-assistant.io/integrations/homeassistant_alerts",

View file

@ -2,10 +2,11 @@
from __future__ import annotations
from homeassistant.components.hassio import get_os_info, is_hassio
from homeassistant.components.hassio import get_os_info
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.hassio import is_hassio
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

View file

@ -13,7 +13,6 @@ from homeassistant.components.hassio import (
HassioAPIError,
async_get_green_settings,
async_set_green_settings,
is_hassio,
)
from homeassistant.config_entries import (
ConfigEntry,
@ -23,6 +22,7 @@ from homeassistant.config_entries import (
)
from homeassistant.core import callback
from homeassistant.helpers import selector
from homeassistant.helpers.hassio import is_hassio
from .const import DOMAIN

View file

@ -14,7 +14,6 @@ from homeassistant.components.hassio import (
AddonInfo,
AddonManager,
AddonState,
is_hassio,
)
from homeassistant.components.zha.repairs.wrong_silabs_firmware import (
probe_silabs_firmware_type,
@ -29,6 +28,7 @@ from homeassistant.config_entries import (
)
from homeassistant.core import callback
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers.hassio import is_hassio
from . import silabs_multiprotocol_addon
from .const import ZHA_DOMAIN

View file

@ -1,7 +1,7 @@
{
"domain": "homeassistant_hardware",
"name": "Home Assistant Hardware",
"after_dependencies": ["zha"],
"after_dependencies": ["hassio", "zha"],
"codeowners": ["@home-assistant/core"],
"documentation": "https://www.home-assistant.io/integrations/homeassistant_hardware",
"integration_type": "system"

View file

@ -17,7 +17,6 @@ from homeassistant.components.hassio import (
AddonManager,
AddonState,
hostname_from_addon_slug,
is_hassio,
)
from homeassistant.config_entries import (
ConfigEntry,
@ -28,6 +27,7 @@ from homeassistant.config_entries import (
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.integration_platform import (
async_process_integration_platforms,
)

View file

@ -9,9 +9,10 @@ from typing import cast
from universal_silabs_flasher.const import ApplicationType
from homeassistant.components.hassio import AddonError, AddonState, is_hassio
from homeassistant.components.hassio import AddonError, AddonState
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.singleton import singleton
from .const import (

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import logging
from homeassistant.components.hassio import get_os_info, is_hassio
from homeassistant.components.hassio import get_os_info
from homeassistant.components.homeassistant_hardware.silabs_multiprotocol_addon import (
check_multi_pan_addon,
)
@ -16,6 +16,7 @@ from homeassistant.config_entries import SOURCE_HARDWARE, ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
from homeassistant.helpers import discovery_flow
from homeassistant.helpers.hassio import is_hassio
from .const import FIRMWARE, RADIO_DEVICE, ZHA_HW_DISCOVERY_DATA

View file

@ -27,6 +27,7 @@ from homeassistant.config import load_yaml_config_file
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.hassio import get_supervisor_ip, is_hassio
from homeassistant.util import dt as dt_util, yaml
from .const import KEY_HASS
@ -149,12 +150,8 @@ async def process_wrong_login(request: Request) -> None:
request.app[KEY_FAILED_LOGIN_ATTEMPTS][remote_addr] += 1
# Supervisor IP should never be banned
if "hassio" in hass.config.components:
# pylint: disable-next=import-outside-toplevel
from homeassistant.components import hassio
if hassio.get_supervisor_ip() == str(remote_addr):
return
if is_hassio(hass) and str(remote_addr) == get_supervisor_ip():
return
if (
request.app[KEY_FAILED_LOGIN_ATTEMPTS][remote_addr]

View file

@ -14,8 +14,6 @@ from homeassistant.components.hassio import (
AddonInfo,
AddonManager,
AddonState,
HassioServiceInfo,
is_hassio,
)
from homeassistant.components.onboarding import async_is_onboarded
from homeassistant.components.zeroconf import ZeroconfServiceInfo
@ -25,6 +23,8 @@ from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .addon import get_addon_manager
from .const import (

View file

@ -1,6 +1,7 @@
{
"domain": "matter",
"name": "Matter (BETA)",
"after_dependencies": ["hassio"],
"codeowners": ["@home-assistant/matter"],
"config_flow": true,
"dependencies": ["websocket_api"],

View file

@ -12,7 +12,6 @@ from motioneye_client.client import (
)
import voluptuous as vol
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import (
SOURCE_REAUTH,
ConfigEntry,
@ -24,6 +23,7 @@ from homeassistant.const import CONF_URL, CONF_WEBHOOK_ID
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.helpers.typing import VolDictType
from . import create_motioneye_client

View file

@ -16,13 +16,7 @@ from cryptography.x509 import load_pem_x509_certificate
import voluptuous as vol
from homeassistant.components.file_upload import process_uploaded_file
from homeassistant.components.hassio import (
AddonError,
AddonManager,
AddonState,
HassioServiceInfo,
is_hassio,
)
from homeassistant.components.hassio import AddonError, AddonManager, AddonState
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
@ -42,6 +36,7 @@ from homeassistant.const import (
from homeassistant.core import callback
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.selector import (
BooleanSelector,
@ -58,6 +53,7 @@ from homeassistant.helpers.selector import (
TextSelectorConfig,
TextSelectorType,
)
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.util.json import JSON_DECODE_EXCEPTIONS, json_loads
from .addon import get_addon_manager

View file

@ -1,6 +1,7 @@
{
"domain": "mqtt",
"name": "MQTT",
"after_dependencies": ["hassio"],
"codeowners": ["@emontnemery", "@jbouwh", "@bdraco"],
"config_flow": true,
"dependencies": ["file_upload", "http"],

View file

@ -20,6 +20,7 @@ from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.components.http.view import HomeAssistantView
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import area_registry as ar
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.system_info import async_get_system_info
from homeassistant.helpers.translation import async_get_translations
from homeassistant.setup import async_setup_component
@ -216,7 +217,7 @@ class CoreConfigOnboardingView(_BaseOnboardingView):
from homeassistant.components import hassio
if (
hassio.is_hassio(hass)
is_hassio(hass)
and (core_info := hassio.get_core_info(hass))
and "raspberrypi" in core_info["machine"]
):

View file

@ -13,7 +13,7 @@ from python_otbr_api.tlv_parser import MeshcopTLVType
import voluptuous as vol
import yarl
from homeassistant.components.hassio import AddonError, AddonManager, HassioServiceInfo
from homeassistant.components.hassio import AddonError, AddonManager
from homeassistant.components.homeassistant_yellow import hardware as yellow_hardware
from homeassistant.components.thread import async_get_preferred_dataset
from homeassistant.config_entries import SOURCE_HASSIO, ConfigFlow, ConfigFlowResult
@ -21,6 +21,7 @@ from homeassistant.const import CONF_URL
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .const import DEFAULT_CHANNEL, DOMAIN
from .util import (

View file

@ -2,10 +2,11 @@
from __future__ import annotations
from homeassistant.components.hassio import get_os_info, is_hassio
from homeassistant.components.hassio import get_os_info
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.hassio import is_hassio
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

View file

@ -9,7 +9,6 @@ from urllib.parse import urlparse
import rtsp_to_webrtc
import voluptuous as vol
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
@ -19,6 +18,7 @@ from homeassistant.config_entries import (
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.core import callback
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from . import CONF_STUN_SERVER, DATA_SERVER_URL, DOMAIN

View file

@ -10,11 +10,11 @@ from aiovlc.client import Client
from aiovlc.exceptions import AuthError, ConnectError
import voluptuous as vol
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .const import DEFAULT_PORT, DOMAIN

View file

@ -8,9 +8,10 @@ from urllib.parse import urlparse
import voluptuous as vol
from homeassistant.components import hassio, zeroconf
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .const import DOMAIN
from .data import WyomingService
@ -30,7 +31,7 @@ class WyomingConfigFlow(ConfigFlow, domain=DOMAIN):
VERSION = 1
_hassio_discovery: hassio.HassioServiceInfo
_hassio_discovery: HassioServiceInfo
_service: WyomingService | None = None
_name: str | None = None
@ -61,7 +62,7 @@ class WyomingConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_abort(reason="no_services")
async def async_step_hassio(
self, discovery_info: hassio.HassioServiceInfo
self, discovery_info: HassioServiceInfo
) -> ConfigFlowResult:
"""Handle Supervisor add-on discovery."""
_LOGGER.debug("Supervisor discovery info: %s", discovery_info)

View file

@ -1,7 +1,7 @@
{
"domain": "zha",
"name": "Zigbee Home Automation",
"after_dependencies": ["onboarding", "usb"],
"after_dependencies": ["hassio", "onboarding", "usb"],
"codeowners": ["@dmulcahey", "@adminiuga", "@puddly", "@TheJulianJES"],
"config_flow": true,
"dependencies": ["file_upload"],

View file

@ -18,8 +18,6 @@ from homeassistant.components.hassio import (
AddonInfo,
AddonManager,
AddonState,
HassioServiceInfo,
is_hassio,
)
from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.config_entries import (
@ -39,6 +37,8 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import AbortFlow, FlowManager
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.helpers.typing import VolDictType
from . import disconnect_client

View file

@ -1,6 +1,7 @@
{
"domain": "zwave_js",
"name": "Z-Wave",
"after_dependencies": ["hassio"],
"codeowners": ["@home-assistant/z-wave"],
"config_flow": true,
"dependencies": ["http", "repairs", "usb", "websocket_api"],

View file

@ -78,10 +78,10 @@ from .util.enum import try_parse_enum
if TYPE_CHECKING:
from .components.bluetooth import BluetoothServiceInfoBleak
from .components.dhcp import DhcpServiceInfo
from .components.hassio import HassioServiceInfo
from .components.ssdp import SsdpServiceInfo
from .components.usb import UsbServiceInfo
from .components.zeroconf import ZeroconfServiceInfo
from .helpers.service_info.hassio import HassioServiceInfo
from .helpers.service_info.mqtt import MqttServiceInfo

View file

@ -0,0 +1,22 @@
"""Hass.io helper."""
import os
from homeassistant.core import HomeAssistant, callback
@callback
def is_hassio(hass: HomeAssistant) -> bool:
"""Return true if Hass.io is loaded.
Async friendly.
"""
return "hassio" in hass.config.components
@callback
def get_supervisor_ip() -> str | None:
"""Return the supervisor ip address."""
if "SUPERVISOR" not in os.environ:
return None
return os.environ["SUPERVISOR"].partition(":")[0]

View file

@ -16,6 +16,8 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.loader import bind_hass
from homeassistant.util.network import is_ip_address, is_loopback, normalize_url
from .hassio import is_hassio
TYPE_URL_INTERNAL = "internal_url"
TYPE_URL_EXTERNAL = "external_url"
SUPERVISOR_NETWORK_HOST = "homeassistant"
@ -42,10 +44,6 @@ 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):
return None
@ -180,20 +178,21 @@ def get_url(
and request_host is not None
and hass.config.api is not None
):
# Local import to avoid circular dependencies
# pylint: disable-next=import-outside-toplevel
from homeassistant.components.hassio import get_host_info, is_hassio
scheme = "https" if hass.config.api.use_ssl else "http"
current_url = yarl.URL.build(
scheme=scheme, host=request_host, port=hass.config.api.port
)
known_hostnames = ["localhost"]
if is_hassio(hass) and (host_info := get_host_info(hass)):
known_hostnames.extend(
[host_info["hostname"], f"{host_info['hostname']}.local"]
)
if is_hassio(hass):
# Local import to avoid circular dependencies
# pylint: disable-next=import-outside-toplevel
from homeassistant.components.hassio import get_host_info
if host_info := get_host_info(hass):
known_hostnames.extend(
[host_info["hostname"], f"{host_info['hostname']}.local"]
)
if (
(

View file

@ -0,0 +1,16 @@
"""Hassio Discovery data."""
from dataclasses import dataclass
from typing import Any
from homeassistant.data_entry_flow import BaseServiceInfo
@dataclass(slots=True)
class HassioServiceInfo(BaseServiceInfo):
"""Prepared info from hassio entries."""
config: dict[str, Any]
name: str
slug: str
uuid: str

View file

@ -14,6 +14,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.loader import bind_hass
from homeassistant.util.package import is_docker_env, is_virtual_env
from .hassio import is_hassio
from .importlib import async_import_module
from .singleton import singleton
@ -52,13 +53,13 @@ async def async_get_system_info(hass: HomeAssistant) -> dict[str, Any]:
else:
hassio = await async_import_module(hass, "homeassistant.components.hassio")
is_hassio = hassio.is_hassio(hass)
is_hassio_ = is_hassio(hass)
info_object = {
"installation_type": "Unknown",
"version": current_version,
"dev": "dev" in current_version,
"hassio": is_hassio,
"hassio": is_hassio_,
"virtualenv": is_virtual_env(),
"python_version": platform.python_version(),
"docker": False,
@ -89,7 +90,7 @@ async def async_get_system_info(hass: HomeAssistant) -> dict[str, Any]:
info_object["installation_type"] = "Home Assistant Core"
# Enrich with Supervisor information
if is_hassio:
if is_hassio_:
if not (info := hassio.get_info(hass)):
_LOGGER.warning("No Home Assistant Supervisor info available")
info = {}

View file

@ -25,7 +25,8 @@ requires-python = ">=3.12.0"
dependencies = [
"aiodns==3.2.0",
# Integrations may depend on hassio integration without listing it to
# change behavior based on presence of supervisor
# change behavior based on presence of supervisor. Deprecated with #127228
# Lib can be removed with 2025.11
"aiohasupervisor==0.2.0",
"aiohttp==3.10.10",
"aiohttp_cors==0.7.0",

View file

@ -44,6 +44,15 @@ class ImportCollector(ast.NodeVisitor):
assert self._cur_fil_dir
self.referenced[self._cur_fil_dir].add(reference_domain)
def visit_If(self, node: ast.If) -> None:
"""Visit If node."""
if isinstance(node.test, ast.Name) and node.test.id == "TYPE_CHECKING":
# Ignore TYPE_CHECKING block
return
# Have it visit other kids
self.generic_visit(node)
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
"""Visit ImportFrom node."""
if node.module is None:
@ -115,7 +124,6 @@ ALLOWED_USED_COMPONENTS = {
"device_automation",
"frontend",
"group",
"hassio",
"homeassistant",
"input_boolean",
"input_button",

View file

@ -4,7 +4,6 @@ import aiohttp
from homeassistant import config_entries
from homeassistant.components.adguard.const import DOMAIN
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import (
CONF_HOST,
@ -17,6 +16,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from tests.common import MockConfigEntry
from tests.test_util.aiohttp import AiohttpClientMocker

View file

@ -76,7 +76,7 @@ async def test_no_send(
"""Test send when no preferences are defined."""
analytics = Analytics(hass)
with patch(
"homeassistant.components.hassio.is_hassio",
"homeassistant.components.analytics.analytics.is_hassio",
side_effect=Mock(return_value=False),
):
assert not analytics.preferences[ATTR_BASE]
@ -97,7 +97,7 @@ async def test_load_with_supervisor_diagnostics(hass: HomeAssistant) -> None:
side_effect=Mock(return_value={"diagnostics": True}),
),
patch(
"homeassistant.components.hassio.is_hassio",
"homeassistant.components.analytics.analytics.is_hassio",
side_effect=Mock(return_value=True),
),
):
@ -118,7 +118,7 @@ async def test_load_with_supervisor_without_diagnostics(hass: HomeAssistant) ->
side_effect=Mock(return_value={"diagnostics": False}),
),
patch(
"homeassistant.components.hassio.is_hassio",
"homeassistant.components.analytics.analytics.is_hassio",
side_effect=Mock(return_value=True),
),
):
@ -219,8 +219,12 @@ async def test_send_base_with_supervisor(
side_effect=Mock(return_value={}),
),
patch(
"homeassistant.components.hassio.is_hassio",
"homeassistant.components.analytics.analytics.is_hassio",
side_effect=Mock(return_value=True),
) as is_hassio_mock,
patch(
"homeassistant.helpers.system_info.is_hassio",
new=is_hassio_mock,
),
):
await analytics.load()
@ -314,8 +318,12 @@ async def test_send_usage_with_supervisor(
side_effect=Mock(return_value={}),
),
patch(
"homeassistant.components.hassio.is_hassio",
"homeassistant.components.analytics.analytics.is_hassio",
side_effect=Mock(return_value=True),
) as is_hassio_mock,
patch(
"homeassistant.helpers.system_info.is_hassio",
new=is_hassio_mock,
),
):
await analytics.send_analytics()
@ -529,8 +537,12 @@ async def test_send_statistics_with_supervisor(
side_effect=Mock(return_value={}),
),
patch(
"homeassistant.components.hassio.is_hassio",
"homeassistant.components.analytics.analytics.is_hassio",
side_effect=Mock(return_value=True),
) as is_hassio_mock,
patch(
"homeassistant.helpers.system_info.is_hassio",
new=is_hassio_mock,
),
):
await analytics.send_analytics()

View file

@ -20,12 +20,12 @@ from homeassistant.components.deconz.const import (
DOMAIN as DECONZ_DOMAIN,
HASSIO_CONFIGURATION_URL,
)
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.ssdp import ATTR_UPNP_MANUFACTURER_URL, ATTR_UPNP_SERIAL
from homeassistant.config_entries import SOURCE_HASSIO, SOURCE_SSDP, SOURCE_USER
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT, CONTENT_TYPE_JSON
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .conftest import API_KEY, BRIDGE_ID

View file

@ -27,10 +27,10 @@ from homeassistant.components.esphome.const import (
DEFAULT_NEW_CONFIG_ALLOW_ALLOW_SERVICE_CALLS,
DOMAIN,
)
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
from . import VALID_NOISE_PSK

View file

@ -10,12 +10,12 @@ from aiohttp.test_utils import TestClient
import pytest
from homeassistant import config_entries
from homeassistant.components.hassio.discovery import HassioServiceInfo
from homeassistant.components.hassio.handler import HassioAPIError
from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STARTED
from homeassistant.core import HomeAssistant
from homeassistant.helpers.discovery_flow import DiscoveryKey
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.setup import async_setup_component
from tests.common import (

View file

@ -1,6 +1,7 @@
"""The tests for the hassio component."""
from datetime import timedelta
import logging
import os
from typing import Any
from unittest.mock import AsyncMock, patch
@ -11,24 +12,31 @@ import pytest
from voluptuous import Invalid
from homeassistant.auth.const import GROUP_ID_ADMIN
from homeassistant.components import frontend
from homeassistant.components import frontend, hassio
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.hassio import (
ADDONS_COORDINATOR,
DOMAIN,
STORAGE_KEY,
get_core_info,
get_supervisor_ip,
hostname_from_addon_slug,
is_hassio,
is_hassio as deprecated_is_hassio,
)
from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, issue_registry as ir
from homeassistant.helpers.hassio import is_hassio
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
from tests.common import MockConfigEntry, async_fire_time_changed
from tests.common import (
MockConfigEntry,
async_fire_time_changed,
import_and_test_deprecated_constant,
)
from tests.test_util.aiohttp import AiohttpClientMocker
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
@ -1085,3 +1093,62 @@ def test_hostname_from_addon_slug() -> None:
hostname_from_addon_slug("core_silabs_multiprotocol")
== "core-silabs-multiprotocol"
)
def test_deprecated_function_is_hassio(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test calling deprecated_is_hassio function will create log entry."""
deprecated_is_hassio(hass)
assert caplog.record_tuples == [
(
"homeassistant.components.hassio",
logging.WARNING,
"is_hassio is a deprecated function which will be removed in HA Core 2025.11. Use homeassistant.helpers.hassio.is_hassio instead",
)
]
def test_deprecated_function_get_supervisor_ip(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test calling get_supervisor_ip function will create log entry."""
get_supervisor_ip()
assert caplog.record_tuples == [
(
"homeassistant.helpers.hassio",
logging.WARNING,
"get_supervisor_ip is a deprecated function which will be removed in HA Core 2025.11. Use homeassistant.helpers.hassio.get_supervisor_ip instead",
)
]
@pytest.mark.parametrize(
("constant_name", "replacement_name", "replacement"),
[
(
"HassioServiceInfo",
"homeassistant.helpers.service_info.hassio.HassioServiceInfo",
HassioServiceInfo,
),
],
)
def test_deprecated_constants(
caplog: pytest.LogCaptureFixture,
constant_name: str,
replacement_name: str,
replacement: Any,
) -> None:
"""Test deprecated automation constants."""
import_and_test_deprecated_constant(
caplog,
hassio,
constant_name,
replacement_name,
replacement,
"2025.11",
)

View file

@ -13,11 +13,11 @@ from matter_server.client.exceptions import CannotConnect, InvalidServerVersion
import pytest
from homeassistant import config_entries
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.matter.const import ADDON_SLUG, DOMAIN
from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from tests.common import MockConfigEntry

View file

@ -9,7 +9,6 @@ from motioneye_client.client import (
)
from homeassistant import config_entries
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.motioneye.const import (
CONF_ADMIN_PASSWORD,
CONF_ADMIN_USERNAME,
@ -23,6 +22,7 @@ from homeassistant.components.motioneye.const import (
from homeassistant.const import CONF_URL, CONF_WEBHOOK_ID
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from . import TEST_URL, create_mock_motioneye_client, create_mock_motioneye_config_entry

View file

@ -15,7 +15,7 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import mqtt
from homeassistant.components.hassio import AddonError, HassioServiceInfo
from homeassistant.components.hassio import AddonError
from homeassistant.components.mqtt.config_flow import PWD_NOT_CHANGED
from homeassistant.const import (
CONF_CLIENT_ID,
@ -26,6 +26,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from tests.common import MockConfigEntry
from tests.typing import MqttMockHAClientGenerator, MqttMockPahoClient

View file

@ -9,22 +9,23 @@ import aiohttp
import pytest
import python_otbr_api
from homeassistant.components import hassio, otbr
from homeassistant.components import otbr
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from . import DATASET_CH15, DATASET_CH16, TEST_BORDER_AGENT_ID, TEST_BORDER_AGENT_ID_2
from tests.common import MockConfigEntry, MockModule, mock_integration
from tests.test_util.aiohttp import AiohttpClientMocker
HASSIO_DATA = hassio.HassioServiceInfo(
HASSIO_DATA = HassioServiceInfo(
config={"host": "core-silabs-multiprotocol", "port": 8081},
name="Silicon Labs Multiprotocol",
slug="otbr",
uuid="12345",
)
HASSIO_DATA_2 = hassio.HassioServiceInfo(
HASSIO_DATA_2 = HassioServiceInfo(
config={"host": "core-silabs-multiprotocol_2", "port": 8082},
name="Silicon Labs Multiprotocol",
slug="other_addon",

View file

@ -7,11 +7,11 @@ from unittest.mock import patch
import rtsp_to_webrtc
from homeassistant import config_entries
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.rtsp_to_webrtc import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .conftest import ComponentSetup

View file

@ -9,10 +9,10 @@ from aiovlc.exceptions import AuthError, ConnectError
import pytest
from homeassistant import config_entries
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.vlc_telnet.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from tests.common import MockConfigEntry

View file

@ -8,11 +8,11 @@ from syrupy.assertion import SnapshotAssertion
from wyoming.info import Info
from homeassistant import config_entries
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.wyoming.const import DOMAIN
from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from . import EMPTY_INFO, SATELLITE_INFO, STT_INFO, TTS_INFO

View file

@ -17,12 +17,12 @@ from zwave_js_server.version import VersionInfo
from homeassistant import config_entries
from homeassistant.components import usb
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.components.zeroconf import ZeroconfServiceInfo
from homeassistant.components.zwave_js.config_flow import SERVER_VERSION_TIMEOUT, TITLE
from homeassistant.components.zwave_js.const import ADDON_SLUG, DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from tests.common import MockConfigEntry

View file

@ -727,7 +727,7 @@ async def test_get_current_request_url_with_known_host(
@patch(
"homeassistant.components.hassio.is_hassio",
"homeassistant.helpers.network.is_hassio",
Mock(return_value={"hostname": "homeassistant"}),
)
@patch(

View file

@ -16,7 +16,6 @@ from syrupy.assertion import SnapshotAssertion
from homeassistant import config_entries, data_entry_flow, loader
from homeassistant.components import dhcp
from homeassistant.components.hassio import HassioServiceInfo
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
EVENT_COMPONENT_LOADED,
@ -40,6 +39,7 @@ from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.helpers.discovery_flow import DiscoveryKey
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.json import json_dumps
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.setup import async_set_domains_to_be_loaded, async_setup_component

View file

@ -585,7 +585,8 @@ async def test_discovery_requirements_mqtt(hass: HomeAssistant) -> None:
) as mock_process:
await async_get_integration_with_requirements(hass, "mqtt_comp")
assert len(mock_process.mock_calls) == 1
assert len(mock_process.mock_calls) == 2
# one for mqtt and one for hassio
assert mock_process.mock_calls[0][1][1] == mqtt.requirements