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, config_validation as cv,
device_registry as dr, device_registry as dr,
discovery_flow, discovery_flow,
issue_registry as ir,
) )
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.deprecation import ( from homeassistant.helpers.deprecation import (
@ -51,9 +52,11 @@ from homeassistant.helpers.hassio import (
get_supervisor_ip as _get_supervisor_ip, get_supervisor_ip as _get_supervisor_ip,
is_hassio as _is_hassio, is_hassio as _is_hassio,
) )
from homeassistant.helpers.issue_registry import IssueSeverity
from homeassistant.helpers.service_info.hassio import ( from homeassistant.helpers.service_info.hassio import (
HassioServiceInfo as _HassioServiceInfo, HassioServiceInfo as _HassioServiceInfo,
) )
from homeassistant.helpers.system_info import async_get_system_info
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
from homeassistant.util.async_ import create_eager_task from homeassistant.util.async_ import create_eager_task
@ -109,7 +112,7 @@ from .coordinator import (
get_core_info, # noqa: F401 get_core_info, # noqa: F401
get_core_stats, # noqa: F401 get_core_stats, # noqa: F401
get_host_info, # noqa: F401 get_host_info, # noqa: F401
get_info, # noqa: F401 get_info,
get_issues_info, # noqa: F401 get_issues_info, # noqa: F401
get_os_info, get_os_info,
get_supervisor_info, # noqa: F401 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]+$")) 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: def valid_addon(value: Any) -> str:
"""Validate value is a valid addon slug.""" """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() await coordinator.async_config_entry_first_refresh()
hass.data[ADDONS_COORDINATOR] = coordinator 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) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True

View File

@ -4,7 +4,7 @@ import asyncio
from collections.abc import Callable, Coroutine from collections.abc import Callable, Coroutine
import itertools as it import itertools as it
import logging import logging
from typing import TYPE_CHECKING, Any from typing import Any
import voluptuous as vol import voluptuous as vol
@ -38,7 +38,6 @@ from homeassistant.helpers import (
restore_state, restore_state,
) )
from homeassistant.helpers.entity_component import async_update_entity 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.issue_registry import IssueSeverity
from homeassistant.helpers.service import ( from homeassistant.helpers.service import (
async_extract_config_entry_ids, 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) info = await async_get_system_info(hass)
installation_type = info["installation_type"][15:] installation_type = info["installation_type"][15:]
deprecated_method = installation_type in { if installation_type in {"Core", "Container"}:
"Core", deprecated_method = installation_type == "Core"
"Supervised", arch = info["arch"]
} if arch == "armv7" and installation_type == "Container":
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":
ir.async_create_issue( ir.async_create_issue(
hass, hass,
DOMAIN, DOMAIN,
@ -452,29 +415,31 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa:
severity=IssueSeverity.WARNING, severity=IssueSeverity.WARNING,
translation_key="deprecated_container_armv7", translation_key="deprecated_container_armv7",
) )
deprecated_architecture = False deprecated_architecture = False
if arch in {"i386", "armhf"} or (arch == "armv7" and deprecated_method): if arch in {"i386", "armhf"} or (
deprecated_architecture = True arch == "armv7" and installation_type != "Container"
if deprecated_method or deprecated_architecture: ):
issue_id = "deprecated" deprecated_architecture = True
if deprecated_method: if deprecated_method or deprecated_architecture:
issue_id += "_method" issue_id = "deprecated"
if deprecated_architecture: if deprecated_method:
issue_id += "_architecture" issue_id += "_method"
ir.async_create_issue( if deprecated_architecture:
hass, issue_id += "_architecture"
DOMAIN, ir.async_create_issue(
issue_id, hass,
breaks_in_ha_version="2025.12.0", DOMAIN,
learn_more_url=DEPRECATION_URL, issue_id,
is_fixable=False, breaks_in_ha_version="2025.12.0",
severity=IssueSeverity.WARNING, learn_more_url=DEPRECATION_URL,
translation_key=issue_id, is_fixable=False,
translation_placeholders={ severity=IssueSeverity.WARNING,
"installation_type": installation_type, translation_key=issue_id,
"arch": arch, translation_placeholders={
}, "installation_type": installation_type,
) "arch": arch,
},
)
return True return True

View File

@ -8,6 +8,7 @@ from unittest.mock import AsyncMock, patch
from aiohasupervisor import SupervisorError from aiohasupervisor import SupervisorError
from aiohasupervisor.models import AddonsStats from aiohasupervisor.models import AddonsStats
from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
from voluptuous import Invalid from voluptuous import Invalid
@ -23,10 +24,13 @@ from homeassistant.components.hassio import (
is_hassio as deprecated_is_hassio, is_hassio as deprecated_is_hassio,
) )
from homeassistant.components.hassio.config import STORAGE_KEY 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.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.core import HomeAssistant 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.hassio import is_hassio
from homeassistant.helpers.service_info.hassio import HassioServiceInfo from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
@ -1140,3 +1144,285 @@ def test_deprecated_constants(
replacement, replacement,
"2025.11", "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 assert len(jinja) == 1
@pytest.mark.parametrize(
"installation_type",
[
"Home Assistant Core",
"Home Assistant Supervised",
],
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"arch", "arch",
[ [
@ -658,14 +651,13 @@ async def test_reload_all(
async def test_deprecated_installation_issue_32bit_method( async def test_deprecated_installation_issue_32bit_method(
hass: HomeAssistant, hass: HomeAssistant,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
installation_type: str,
arch: str, arch: str,
) -> None: ) -> None:
"""Test deprecated installation issue.""" """Test deprecated installation issue."""
with patch( with patch(
"homeassistant.components.homeassistant.async_get_system_info", "homeassistant.components.homeassistant.async_get_system_info",
return_value={ return_value={
"installation_type": installation_type, "installation_type": "Home Assistant Core",
"arch": arch, "arch": arch,
}, },
): ):
@ -677,18 +669,11 @@ async def test_deprecated_installation_issue_32bit_method(
assert issue.domain == DOMAIN assert issue.domain == DOMAIN
assert issue.severity == ir.IssueSeverity.WARNING assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == { assert issue.translation_placeholders == {
"installation_type": installation_type[15:], "installation_type": "Core",
"arch": arch, "arch": arch,
} }
@pytest.mark.parametrize(
"installation_type",
[
"Home Assistant Container",
"Home Assistant OS",
],
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"arch", "arch",
[ [
@ -699,14 +684,13 @@ async def test_deprecated_installation_issue_32bit_method(
async def test_deprecated_installation_issue_32bit( async def test_deprecated_installation_issue_32bit(
hass: HomeAssistant, hass: HomeAssistant,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
installation_type: str,
arch: str, arch: str,
) -> None: ) -> None:
"""Test deprecated installation issue.""" """Test deprecated installation issue."""
with patch( with patch(
"homeassistant.components.homeassistant.async_get_system_info", "homeassistant.components.homeassistant.async_get_system_info",
return_value={ return_value={
"installation_type": installation_type, "installation_type": "Home Assistant Container",
"arch": arch, "arch": arch,
}, },
): ):
@ -718,28 +702,19 @@ async def test_deprecated_installation_issue_32bit(
assert issue.domain == DOMAIN assert issue.domain == DOMAIN
assert issue.severity == ir.IssueSeverity.WARNING assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == { assert issue.translation_placeholders == {
"installation_type": installation_type[15:], "installation_type": "Container",
"arch": arch, "arch": arch,
} }
@pytest.mark.parametrize(
"installation_type",
[
"Home Assistant Core",
"Home Assistant Supervised",
],
)
async def test_deprecated_installation_issue_method( async def test_deprecated_installation_issue_method(
hass: HomeAssistant, hass: HomeAssistant, issue_registry: ir.IssueRegistry
issue_registry: ir.IssueRegistry,
installation_type: str,
) -> None: ) -> None:
"""Test deprecated installation issue.""" """Test deprecated installation issue."""
with patch( with patch(
"homeassistant.components.homeassistant.async_get_system_info", "homeassistant.components.homeassistant.async_get_system_info",
return_value={ return_value={
"installation_type": installation_type, "installation_type": "Home Assistant Core",
"arch": "generic-x86-64", "arch": "generic-x86-64",
}, },
): ):
@ -751,52 +726,11 @@ async def test_deprecated_installation_issue_method(
assert issue.domain == DOMAIN assert issue.domain == DOMAIN
assert issue.severity == ir.IssueSeverity.WARNING assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_placeholders == { assert issue.translation_placeholders == {
"installation_type": installation_type[15:], "installation_type": "Core",
"arch": "generic-x86-64", "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( async def test_deprecated_installation_issue_armv7_container(
hass: HomeAssistant, hass: HomeAssistant,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,