Split deprecated system issue in 2 places (#146453)

This commit is contained in:
Joost Lekkerkerker 2025-06-11 11:35:14 +02:00 committed by GitHub
parent 2afdec4711
commit dd216ac15b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 391 additions and 141 deletions

View File

@ -37,6 +37,7 @@ from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
discovery_flow,
issue_registry as ir,
)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.deprecation import (
@ -51,9 +52,11 @@ from homeassistant.helpers.hassio import (
get_supervisor_ip as _get_supervisor_ip,
is_hassio as _is_hassio,
)
from homeassistant.helpers.issue_registry import IssueSeverity
from homeassistant.helpers.service_info.hassio import (
HassioServiceInfo as _HassioServiceInfo,
)
from homeassistant.helpers.system_info import async_get_system_info
from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass
from homeassistant.util.async_ import create_eager_task
@ -109,7 +112,7 @@ from .coordinator import (
get_core_info, # noqa: F401
get_core_stats, # noqa: F401
get_host_info, # noqa: F401
get_info, # noqa: F401
get_info,
get_issues_info, # noqa: F401
get_os_info,
get_supervisor_info, # noqa: F401
@ -168,6 +171,11 @@ SERVICE_RESTORE_PARTIAL = "restore_partial"
VALID_ADDON_SLUG = vol.Match(re.compile(r"^[-_.A-Za-z0-9]+$"))
DEPRECATION_URL = (
"https://www.home-assistant.io/blog/2025/05/22/"
"deprecating-core-and-supervised-installation-methods-and-32-bit-systems/"
)
def valid_addon(value: Any) -> str:
"""Validate value is a valid addon slug."""
@ -546,6 +554,63 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await coordinator.async_config_entry_first_refresh()
hass.data[ADDONS_COORDINATOR] = coordinator
system_info = await async_get_system_info(hass)
def deprecated_setup_issue() -> None:
os_info = get_os_info(hass)
info = get_info(hass)
if os_info is None or info is None:
return
is_haos = info.get("hassos") is not None
arch = system_info["arch"]
board = os_info.get("board")
supported_board = board in {"rpi3", "rpi4", "tinker", "odroid-xu4", "rpi2"}
if is_haos and arch == "armv7" and supported_board:
issue_id = "deprecated_os_"
if board in {"rpi3", "rpi4"}:
issue_id += "aarch64"
elif board in {"tinker", "odroid-xu4", "rpi2"}:
issue_id += "armv7"
ir.async_create_issue(
hass,
"homeassistant",
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_guide": "https://www.home-assistant.io/installation/",
},
)
deprecated_architecture = False
if arch in {"i386", "armhf"} or (arch == "armv7" and not supported_board):
deprecated_architecture = True
if not is_haos or deprecated_architecture:
issue_id = "deprecated"
if not is_haos:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
ir.async_create_issue(
hass,
"homeassistant",
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_type": "OS" if is_haos else "Supervised",
"arch": arch,
},
)
listener()
listener = coordinator.async_add_listener(deprecated_setup_issue)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True

View File

@ -4,7 +4,7 @@ import asyncio
from collections.abc import Callable, Coroutine
import itertools as it
import logging
from typing import TYPE_CHECKING, Any
from typing import Any
import voluptuous as vol
@ -38,7 +38,6 @@ from homeassistant.helpers import (
restore_state,
)
from homeassistant.helpers.entity_component import async_update_entity
from homeassistant.helpers.importlib import async_import_module
from homeassistant.helpers.issue_registry import IssueSeverity
from homeassistant.helpers.service import (
async_extract_config_entry_ids,
@ -402,46 +401,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
info = await async_get_system_info(hass)
installation_type = info["installation_type"][15:]
deprecated_method = installation_type in {
"Core",
"Supervised",
}
arch = info["arch"]
if arch == "armv7":
if installation_type == "OS":
# Local import to avoid circular dependencies
# We use the import helper because hassio
# may not be loaded yet and we don't want to
# do blocking I/O in the event loop to import it.
if TYPE_CHECKING:
# pylint: disable-next=import-outside-toplevel
from homeassistant.components import hassio
else:
hassio = await async_import_module(
hass, "homeassistant.components.hassio"
)
os_info = hassio.get_os_info(hass)
assert os_info is not None
issue_id = "deprecated_os_"
board = os_info.get("board")
if board in {"rpi3", "rpi4"}:
issue_id += "aarch64"
elif board in {"tinker", "odroid-xu4", "rpi2"}:
issue_id += "armv7"
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_guide": "https://www.home-assistant.io/installation/",
},
)
elif installation_type == "Container":
if installation_type in {"Core", "Container"}:
deprecated_method = installation_type == "Core"
arch = info["arch"]
if arch == "armv7" and installation_type == "Container":
ir.async_create_issue(
hass,
DOMAIN,
@ -452,29 +415,31 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
severity=IssueSeverity.WARNING,
translation_key="deprecated_container_armv7",
)
deprecated_architecture = False
if arch in {"i386", "armhf"} or (arch == "armv7" and deprecated_method):
deprecated_architecture = True
if deprecated_method or deprecated_architecture:
issue_id = "deprecated"
if deprecated_method:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_type": installation_type,
"arch": arch,
},
)
deprecated_architecture = False
if arch in {"i386", "armhf"} or (
arch == "armv7" and installation_type != "Container"
):
deprecated_architecture = True
if deprecated_method or deprecated_architecture:
issue_id = "deprecated"
if deprecated_method:
issue_id += "_method"
if deprecated_architecture:
issue_id += "_architecture"
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
breaks_in_ha_version="2025.12.0",
learn_more_url=DEPRECATION_URL,
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key=issue_id,
translation_placeholders={
"installation_type": installation_type,
"arch": arch,
},
)
return True

View File

@ -8,6 +8,7 @@ from unittest.mock import AsyncMock, patch
from aiohasupervisor import SupervisorError
from aiohasupervisor.models import AddonsStats
from freezegun.api import FrozenDateTimeFactory
import pytest
from voluptuous import Invalid
@ -23,10 +24,13 @@ from homeassistant.components.hassio import (
is_hassio as deprecated_is_hassio,
)
from homeassistant.components.hassio.config import STORAGE_KEY
from homeassistant.components.hassio.const import REQUEST_REFRESH_DELAY
from homeassistant.components.hassio.const import (
HASSIO_UPDATE_INTERVAL,
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
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
@ -1140,3 +1144,285 @@ def test_deprecated_constants(
replacement,
"2025.11",
)
@pytest.mark.parametrize(
("board", "issue_id"),
[
("rpi3", "deprecated_os_aarch64"),
("rpi4", "deprecated_os_aarch64"),
("tinker", "deprecated_os_armv7"),
("odroid-xu4", "deprecated_os_armv7"),
("rpi2", "deprecated_os_armv7"),
],
)
async def test_deprecated_installation_issue_aarch64(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
freezer: FrozenDateTimeFactory,
board: str,
issue_id: str,
) -> None:
"""Test deprecated installation issue."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": "armv7",
},
),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": "armv7",
},
),
patch(
"homeassistant.components.hassio.get_os_info", return_value={"board": board}
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": True}
),
patch("homeassistant.components.hardware.async_setup", return_value=True),
):
assert await async_setup_component(hass, "homeassistant", {})
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
freezer.tick(REQUEST_REFRESH_DELAY)
async_fire_time_changed(hass)
await hass.async_block_till_done()
await hass.services.async_call(
"homeassistant",
"update_entity",
{
"entity_id": [
"update.home_assistant_core_update",
"update.home_assistant_supervisor_update",
]
},
blocking=True,
)
freezer.tick(HASSIO_UPDATE_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert len(issue_registry.issues) == 1
issue = issue_registry.async_get_issue("homeassistant", issue_id)
assert issue.domain == "homeassistant"
assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == {
"installation_guide": "https://www.home-assistant.io/installation/",
}
@pytest.mark.parametrize(
"arch",
[
"i386",
"armhf",
"armv7",
],
)
async def test_deprecated_installation_issue_32bit_method(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
freezer: FrozenDateTimeFactory,
arch: str,
) -> None:
"""Test deprecated architecture issue."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": arch,
},
),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": arch,
},
),
patch(
"homeassistant.components.hassio.get_os_info",
return_value={"board": "rpi3-64"},
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": True}
),
patch("homeassistant.components.hardware.async_setup", return_value=True),
):
assert await async_setup_component(hass, "homeassistant", {})
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
freezer.tick(REQUEST_REFRESH_DELAY)
async_fire_time_changed(hass)
await hass.async_block_till_done()
await hass.services.async_call(
"homeassistant",
"update_entity",
{
"entity_id": [
"update.home_assistant_core_update",
"update.home_assistant_supervisor_update",
]
},
blocking=True,
)
freezer.tick(HASSIO_UPDATE_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert len(issue_registry.issues) == 1
issue = issue_registry.async_get_issue("homeassistant", "deprecated_architecture")
assert issue.domain == "homeassistant"
assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == {"installation_type": "OS", "arch": arch}
@pytest.mark.parametrize(
"arch",
[
"i386",
"armhf",
"armv7",
],
)
async def test_deprecated_installation_issue_32bit_supervised(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
freezer: FrozenDateTimeFactory,
arch: str,
) -> None:
"""Test deprecated architecture issue."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.async_get_system_info",
return_value={
"installation_type": "Home Assistant Supervised",
"arch": arch,
},
),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant Supervised",
"arch": arch,
},
),
patch(
"homeassistant.components.hassio.get_os_info",
return_value={"board": "rpi3-64"},
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": None}
),
patch("homeassistant.components.hardware.async_setup", return_value=True),
):
assert await async_setup_component(hass, "homeassistant", {})
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
freezer.tick(REQUEST_REFRESH_DELAY)
async_fire_time_changed(hass)
await hass.async_block_till_done()
await hass.services.async_call(
"homeassistant",
"update_entity",
{
"entity_id": [
"update.home_assistant_core_update",
"update.home_assistant_supervisor_update",
]
},
blocking=True,
)
freezer.tick(HASSIO_UPDATE_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert len(issue_registry.issues) == 1
issue = issue_registry.async_get_issue(
"homeassistant", "deprecated_method_architecture"
)
assert issue.domain == "homeassistant"
assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == {
"installation_type": "Supervised",
"arch": arch,
}
@pytest.mark.parametrize(
("board", "issue_id"),
[
("rpi5", "deprecated_os_aarch64"),
],
)
async def test_deprecated_installation_issue_supported_board(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
freezer: FrozenDateTimeFactory,
board: str,
issue_id: str,
) -> None:
"""Test no deprecated installation issue for a supported board."""
with (
patch.dict(os.environ, MOCK_ENVIRON),
patch(
"homeassistant.components.hassio.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": "aarch64",
},
),
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": "aarch64",
},
),
patch(
"homeassistant.components.hassio.get_os_info", return_value={"board": board}
),
patch(
"homeassistant.components.hassio.get_info", return_value={"hassos": True}
),
):
assert await async_setup_component(hass, "homeassistant", {})
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
freezer.tick(REQUEST_REFRESH_DELAY)
async_fire_time_changed(hass)
await hass.async_block_till_done()
await hass.services.async_call(
"homeassistant",
"update_entity",
{
"entity_id": [
"update.home_assistant_core_update",
"update.home_assistant_supervisor_update",
]
},
blocking=True,
)
freezer.tick(HASSIO_UPDATE_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert len(issue_registry.issues) == 0

View File

@ -640,13 +640,6 @@ async def test_reload_all(
assert len(jinja) == 1
@pytest.mark.parametrize(
"installation_type",
[
"Home Assistant Core",
"Home Assistant Supervised",
],
)
@pytest.mark.parametrize(
"arch",
[
@ -658,14 +651,13 @@ async def test_reload_all(
async def test_deprecated_installation_issue_32bit_method(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
installation_type: str,
arch: str,
) -> None:
"""Test deprecated installation issue."""
with patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": installation_type,
"installation_type": "Home Assistant Core",
"arch": arch,
},
):
@ -677,18 +669,11 @@ async def test_deprecated_installation_issue_32bit_method(
assert issue.domain == DOMAIN
assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == {
"installation_type": installation_type[15:],
"installation_type": "Core",
"arch": arch,
}
@pytest.mark.parametrize(
"installation_type",
[
"Home Assistant Container",
"Home Assistant OS",
],
)
@pytest.mark.parametrize(
"arch",
[
@ -699,14 +684,13 @@ async def test_deprecated_installation_issue_32bit_method(
async def test_deprecated_installation_issue_32bit(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
installation_type: str,
arch: str,
) -> None:
"""Test deprecated installation issue."""
with patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": installation_type,
"installation_type": "Home Assistant Container",
"arch": arch,
},
):
@ -718,28 +702,19 @@ async def test_deprecated_installation_issue_32bit(
assert issue.domain == DOMAIN
assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == {
"installation_type": installation_type[15:],
"installation_type": "Container",
"arch": arch,
}
@pytest.mark.parametrize(
"installation_type",
[
"Home Assistant Core",
"Home Assistant Supervised",
],
)
async def test_deprecated_installation_issue_method(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
installation_type: str,
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test deprecated installation issue."""
with patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": installation_type,
"installation_type": "Home Assistant Core",
"arch": "generic-x86-64",
},
):
@ -751,52 +726,11 @@ async def test_deprecated_installation_issue_method(
assert issue.domain == DOMAIN
assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == {
"installation_type": installation_type[15:],
"installation_type": "Core",
"arch": "generic-x86-64",
}
@pytest.mark.parametrize(
("board", "issue_id"),
[
("rpi3", "deprecated_os_aarch64"),
("rpi4", "deprecated_os_aarch64"),
("tinker", "deprecated_os_armv7"),
("odroid-xu4", "deprecated_os_armv7"),
("rpi2", "deprecated_os_armv7"),
],
)
async def test_deprecated_installation_issue_aarch64(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,
board: str,
issue_id: str,
) -> None:
"""Test deprecated installation issue."""
with (
patch(
"homeassistant.components.homeassistant.async_get_system_info",
return_value={
"installation_type": "Home Assistant OS",
"arch": "armv7",
},
),
patch(
"homeassistant.components.hassio.get_os_info", return_value={"board": board}
),
):
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert len(issue_registry.issues) == 1
issue = issue_registry.async_get_issue(DOMAIN, issue_id)
assert issue.domain == DOMAIN
assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == {
"installation_guide": "https://www.home-assistant.io/installation/",
}
async def test_deprecated_installation_issue_armv7_container(
hass: HomeAssistant,
issue_registry: ir.IssueRegistry,