Compare commits

...

33 Commits

Author SHA1 Message Date
G Johansson aad7b6c0e7 docstring 2026-05-21 20:27:19 +00:00
G Johansson 9634953f69 Fixes 2026-05-21 18:12:24 +00:00
G Johansson 30176c6f68 Remove issue on config entry removal 2026-05-21 17:58:28 +00:00
G Johansson 7c6ff3f172 Fix review comments 2026-05-14 13:58:29 +00:00
G Johansson fbda661516 Restore options flow test 2026-05-13 21:28:46 +00:00
G Johansson 2e1c4e0ef5 Remove from future 2026-05-13 20:55:12 +00:00
G Johansson ee9326dc60 Somewhat stable id 2026-05-13 20:50:31 +00:00
G Johansson d8b23dfaf1 Mods 2026-05-13 20:40:12 +00:00
G Johansson f4bccbb348 fix yaml description 2026-05-13 20:37:50 +00:00
G Johansson 80733a2c45 Handle missing entity 2026-05-13 20:36:14 +00:00
G Johansson 5079709d87 Mods 2026-05-13 20:14:35 +00:00
G Johansson e131b21c5e async_config_entry_title 2026-05-13 19:43:42 +00:00
G Johansson 9fd0f99a44 Fix flow 2026-05-13 19:42:29 +00:00
G Johansson 00e8a6cae6 Fix string 2026-05-13 19:39:02 +00:00
G Johansson 3f690141a5 strings 2026-05-13 19:09:32 +00:00
G Johansson 379c0b1b13 Fixes 2026-05-13 19:01:41 +00:00
G Johansson 46a05e401b mod 2026-05-04 21:30:15 +00:00
G Johansson 787929fbd1 Mods 2026-05-04 20:33:46 +00:00
G Johansson 983df39785 Fixes 2026-05-04 20:01:54 +00:00
G Johansson d037f33023 Fix config flow 2026-05-04 19:50:17 +00:00
G Johansson 6d4f5a4a91 Mods to repair flow 2026-05-04 19:33:45 +00:00
G Johansson 7f1846b34a comments 2026-04-14 20:03:09 +00:00
G Johansson 44b071762d Mods 2026-04-14 20:01:42 +00:00
G Johansson f006283ee8 Mods 2026-04-14 19:58:59 +00:00
G Johansson d25f3c6d2e Delete 2026-04-14 19:46:44 +00:00
G Johansson f821952918 Test 2026-04-14 19:46:03 +00:00
G Johansson 49ce8ed944 Mods 2026-04-14 19:42:59 +00:00
G Johansson d4fca3737d Mods 2026-04-14 19:34:11 +00:00
G Johansson 26d8dfb695 Mods 2026-04-14 16:53:14 +00:00
G Johansson 6e92ba2fc0 Mods 2026-04-14 16:35:17 +00:00
G Johansson 7288d19abf Mods 2026-04-10 14:53:04 +00:00
G Johansson 5bbfe69bbb import 2026-04-10 14:12:23 +00:00
G Johansson 564280cc65 Deprecate min_max and migrate to group sensor 2026-04-08 20:21:06 +00:00
13 changed files with 417 additions and 78 deletions
@@ -47,7 +47,9 @@ from .const import ( # noqa: F401
ATTR_OBJECT_ID,
ATTR_ORDER,
ATTR_REMOVE_ENTITIES,
CONF_GROUP_TYPE,
CONF_HIDE_MEMBERS,
CONF_IGNORE_NON_NUMERIC,
DATA_COMPONENT,
DOMAIN,
GROUP_ORDER,
@@ -24,7 +24,7 @@ from homeassistant.helpers.schema_config_entry_flow import (
from .binary_sensor import CONF_ALL, async_create_preview_binary_sensor
from .button import async_create_preview_button
from .const import CONF_HIDE_MEMBERS, CONF_IGNORE_NON_NUMERIC, DOMAIN
from .const import CONF_GROUP_TYPE, CONF_HIDE_MEMBERS, CONF_IGNORE_NON_NUMERIC, DOMAIN
from .cover import async_create_preview_cover
from .entity import GroupEntity
from .event import async_create_preview_event
@@ -180,7 +180,7 @@ GROUP_TYPES = [
async def choose_options_step(options: dict[str, Any]) -> str:
"""Return next step_id for options flow according to group_type."""
return cast(str, options["group_type"])
return cast(str, options[CONF_GROUP_TYPE])
def set_group_type(
@@ -194,7 +194,7 @@ def set_group_type(
handler: SchemaCommonFlowHandler, user_input: dict[str, Any]
) -> dict[str, Any]:
"""Add group type to user input."""
return {"group_type": group_type, **user_input}
return {CONF_GROUP_TYPE: group_type, **user_input}
return _set_group_type
@@ -430,7 +430,7 @@ def ws_start_preview(
config_entry = hass.config_entries.async_get_entry(config_entry_id)
if not config_entry:
raise HomeAssistantError
group_type = config_entry.options["group_type"]
group_type = config_entry.options[CONF_GROUP_TYPE]
name = config_entry.options["name"]
validated = PREVIEW_OPTIONS_SCHEMA[group_type](msg["user_input"])
entity_registry = er.async_get(hass)
+1
View File
@@ -14,6 +14,7 @@ if TYPE_CHECKING:
CONF_HIDE_MEMBERS = "hide_members"
CONF_IGNORE_NON_NUMERIC = "ignore_non_numeric"
CONF_GROUP_TYPE = "group_type"
DOMAIN = "group"
DATA_COMPONENT: HassKey[EntityComponent[Group]] = HassKey(DOMAIN)
@@ -3,12 +3,30 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
async_delete_issue,
)
from .const import DOMAIN
PLATFORMS = [Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Min/Max from a config entry."""
async_create_issue(
hass,
DOMAIN,
f"migrate_to_group_sensor-{entry.entry_id}",
breaks_in_ha_version="2026.12.0",
is_fixable=True,
is_persistent=False,
severity=IssueSeverity.WARNING,
translation_key="migrate_to_group_sensor",
data={"entry_id": entry.entry_id},
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
@@ -17,3 +35,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Remove a config entry."""
async_delete_issue(hass, DOMAIN, f"migrate_to_group_sensor-{entry.entry_id}")
@@ -11,8 +11,10 @@ from homeassistant.components.input_number import DOMAIN as INPUT_NUMBER_DOMAIN
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import CONF_TYPE
from homeassistant.data_entry_flow import AbortFlow
from homeassistant.helpers import selector
from homeassistant.helpers.schema_config_entry_flow import (
SchemaCommonFlowHandler,
SchemaConfigFlowHandler,
SchemaFlowFormStep,
)
@@ -51,14 +53,16 @@ OPTIONS_SCHEMA = vol.Schema(
}
)
CONFIG_SCHEMA = vol.Schema(
{
vol.Required("name"): selector.TextSelector(),
}
).extend(OPTIONS_SCHEMA.schema)
CONFIG_SCHEMA = vol.Schema({})
async def migrate_to_groups(handler: SchemaCommonFlowHandler) -> vol.Schema:
"""Abort flow as migrate to groups."""
raise AbortFlow("migrated_to_groups")
CONFIG_FLOW = {
"user": SchemaFlowFormStep(CONFIG_SCHEMA),
"user": SchemaFlowFormStep(migrate_to_groups),
}
OPTIONS_FLOW = {
+117
View File
@@ -0,0 +1,117 @@
"""Repairs platform for the Min/Max integration."""
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, cast
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.group import (
CONF_ENTITIES,
CONF_GROUP_TYPE,
CONF_HIDE_MEMBERS,
CONF_IGNORE_NON_NUMERIC,
DOMAIN as GROUP_DOMAIN,
)
from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.config_entries import SOURCE_USER, ConfigEntry, ConfigEntryDisabler
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from .const import CONF_ENTITY_IDS, CONF_ROUND_DIGITS, DOMAIN
class MigrateToGroupSensorFlow(RepairsFlow):
"""Repair flow to migrate Min/Max helper to Group sensor."""
def __init__(self, entry: ConfigEntry) -> None:
"""Create flow."""
self.entry = entry
super().__init__()
async def async_step_init(
self, user_input: dict[str, str] | None = None
) -> data_entry_flow.FlowResult:
"""Handle the first step of a fix flow."""
return await self.async_step_migrate()
async def async_step_migrate(
self, user_input: dict[str, Any] | None = None
) -> data_entry_flow.FlowResult:
"""Handle the migration step of a fix flow."""
entity_reg = er.async_get(self.hass)
old_entity = entity_reg.async_get_entity_id(
SENSOR_DOMAIN, DOMAIN, self.entry.entry_id
)
if not old_entity:
return self.async_abort(reason="entity_not_found")
if user_input is not None:
config = dict(self.entry.options)
config[CONF_ENTITIES] = config.pop(CONF_ENTITY_IDS)
config.pop(CONF_ROUND_DIGITS)
# Set group sensor defaults
config[CONF_HIDE_MEMBERS] = False
config[CONF_IGNORE_NON_NUMERIC] = False
config[CONF_GROUP_TYPE] = SENSOR_DOMAIN
new_config_entry = ConfigEntry(
data={},
discovery_keys=MappingProxyType({}),
domain=GROUP_DOMAIN,
minor_version=1,
options=config,
source=SOURCE_USER,
subentries_data=[],
title=self.entry.title,
unique_id=None,
version=1,
disabled_by=ConfigEntryDisabler.USER,
)
if not await self.hass.config_entries.async_unload(self.entry.entry_id):
return self.async_abort(reason="unload_failed")
await self.hass.config_entries.async_add(new_config_entry)
try:
entity_reg.async_update_entity_platform(
old_entity,
GROUP_DOMAIN,
new_config_entry_id=new_config_entry.entry_id,
new_unique_id=new_config_entry.entry_id,
)
except ValueError:
return self.async_abort(reason="entity_update_failed")
await self.hass.config_entries.async_set_disabled_by(
entry_id=new_config_entry.entry_id, disabled_by=None
)
await self.hass.config_entries.async_remove(self.entry.entry_id)
return self.async_create_entry(data={})
entity_info = entity_reg.async_get(old_entity)
if TYPE_CHECKING:
assert entity_info
title = er.async_get_full_entity_name(self.hass, entity_info)
return self.async_show_form(
step_id="migrate",
data_schema=vol.Schema({}),
description_placeholders={"title": title},
)
async def async_create_fix_flow(
hass: HomeAssistant,
issue_id: str,
data: dict[str, Any] | None,
) -> RepairsFlow:
"""Create flow."""
if data and (entry_id := data.get("entry_id")):
entry_id = cast(str, entry_id)
entry = hass.config_entries.async_get_entry(entry_id)
if TYPE_CHECKING:
assert entry
return MigrateToGroupSensorFlow(entry)
return ConfirmRepairFlow()
@@ -3,12 +3,15 @@
from __future__ import annotations
from datetime import datetime
import hashlib
import json
import logging
import statistics
from typing import Any
import voluptuous as vol
from homeassistant.components.group import CONF_ENTITIES
from homeassistant.components.sensor import (
PLATFORM_SCHEMA as SENSOR_PLATFORM_SCHEMA,
SensorDeviceClass,
@@ -20,6 +23,7 @@ from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_UNIT_OF_MEASUREMENT,
CONF_NAME,
CONF_PLATFORM,
CONF_TYPE,
CONF_UNIQUE_ID,
STATE_UNAVAILABLE,
@@ -34,8 +38,10 @@ from homeassistant.helpers.entity_platform import (
AddEntitiesCallback,
)
from homeassistant.helpers.event import async_track_state_change_event
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
from homeassistant.util import yaml as yaml_util
from . import PLATFORMS
from .const import CONF_ENTITY_IDS, CONF_ROUND_DIGITS, DOMAIN
@@ -53,6 +59,7 @@ ATTR_LAST_ENTITY_ID = "last_entity_id"
ATTR_RANGE = "range"
ATTR_SUM = "sum"
ICON = "mdi:calculator"
SENSOR_TYPES = {
@@ -105,6 +112,36 @@ async def async_setup_entry(
)
async def yaml_deprecation_notice(hass: HomeAssistant, config: ConfigType) -> None:
"""Raise repair issue for YAML configuration deprecation."""
platform_config = config.copy()
platform_config[CONF_ENTITIES] = platform_config.pop(CONF_ENTITY_IDS)
platform_config.pop(CONF_ROUND_DIGITS)
platform_config.pop(CONF_PLATFORM)
if CONF_NAME not in platform_config:
platform_config[CONF_NAME] = f"{platform_config[CONF_TYPE]} sensor".capitalize()
yaml_config = yaml_util.dump(platform_config)
yaml_config = yaml_config.replace("\n", "\n ")
yaml_config = "```yaml\nsensor:\n - platform: group\n " + yaml_config + "\n```"
def make_hash(config: dict[str, Any]) -> str:
d = hashlib.sha1(json.dumps(config, sort_keys=True).encode())
return d.hexdigest()
issue_id = f"yaml_deprecated-{make_hash(platform_config)}"
async_create_issue(
hass,
DOMAIN,
issue_id,
breaks_in_ha_version="2026.12.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
learn_more_url="https://www.home-assistant.io/integrations/group/",
translation_key="yaml_deprecated",
translation_placeholders={"yaml_config": yaml_config},
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
@@ -119,6 +156,7 @@ async def async_setup_platform(
unique_id = config.get(CONF_UNIQUE_ID)
await async_setup_reload_service(hass, DOMAIN, PLATFORMS)
await yaml_deprecation_notice(hass, config)
async_add_entities(
[MinMaxSensor(entity_ids, name, sensor_type, round_digits, unique_id)]
+30 -14
View File
@@ -1,31 +1,47 @@
{
"config": {
"abort": {
"migrated_to_groups": "The Min/Max helper has been migrated to use Group sensors. Please use the Group helper instead."
},
"step": {
"user": {
"data": {
"entity_ids": "Input entities",
"name": "[%key:common::config_flow::data::name%]",
"round_digits": "Precision",
"type": "Statistic characteristic"
},
"data_description": {
"round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean, median or sum."
},
"description": "Create a sensor that calculates a min, max, mean, median or sum from a list of input sensors.",
"description": "Min/Max helper has been deprecated, please use the Group helper instead.",
"title": "[%key:component::min_max::title%]"
}
}
},
"issues": {
"migrate_to_group_sensor": {
"fix_flow": {
"abort": {
"entity_not_found": "Entity could not be found as it has been removed, aborting the repair.",
"entity_update_failed": "Failed to update the entity to a Group sensor, please manually remove the obsolete entity.",
"unload_failed": "Failed to unload the Min/Max helper, please restart repairing the issue."
},
"step": {
"migrate": {
"description": "The Min/Max helper has been deprecated and {title} will be migrated to a Group sensor when you click submit to fix this repair.",
"title": "[%key:component::min_max::issues::migrate_to_group_sensor::title%]"
}
}
},
"title": "Min/Max helper has been deprecated"
},
"yaml_deprecated": {
"description": "The Min/Max helper has been deprecated and you should use Group sensors instead.\n\nReplace your Min/Max YAML configuration with this converted configuration:\n{yaml_config}\n\n**Note:**\n- `precision` is not supported in Group sensor, you need to update your entity options after migrating.\n- Group sensor supports setting `ignore_non_numeric` which defaults to `False` after migrating, change this to `True` to ignore input entities with invalid values.\n\nOnce you have replaced your YAML configuration, restart Home Assistant to use the Group helper instead.\n\nThe Group helper has more configuration possibilities. Refer to the documentation by clicking Learn More.",
"title": "[%key:component::min_max::issues::migrate_to_group_sensor::title%]"
}
},
"options": {
"step": {
"init": {
"data": {
"entity_ids": "[%key:component::min_max::config::step::user::data::entity_ids%]",
"round_digits": "[%key:component::min_max::config::step::user::data::round_digits%]",
"type": "[%key:component::min_max::config::step::user::data::type%]"
"entity_ids": "Input entities",
"round_digits": "Precision",
"type": "Statistic characteristic"
},
"data_description": {
"round_digits": "[%key:component::min_max::config::step::user::data_description::round_digits%]"
"round_digits": "Controls the number of decimal digits in the output when the statistics characteristic is mean, median or sum."
}
}
}
+1 -1
View File
@@ -1932,7 +1932,7 @@ class EntityRegistry(BaseRegistry):
"""
if (
state := self.hass.states.get(entity_id)
) is not None and state.state != STATE_UNKNOWN:
) is not None and state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE):
raise ValueError("Only entities that haven't been loaded can be migrated")
old = self.entities[entity_id]
@@ -0,0 +1,31 @@
# serializer version: 1
# name: test_setup_migrates_to_groups
dict({
'data': dict({
}),
'disabled_by': None,
'discovery_keys': dict({
}),
'domain': 'group',
'minor_version': 1,
'options': dict({
'entities': list([
'sensor.input_one',
'sensor.input_two',
]),
'group_type': 'sensor',
'hide_members': False,
'ignore_non_numeric': False,
'name': 'My min_max',
'type': 'max',
}),
'pref_disable_new_entities': False,
'pref_disable_polling': False,
'source': 'user',
'subentries': list([
]),
'title': 'My min_max',
'unique_id': None,
'version': 1,
})
# ---
+4 -39
View File
@@ -1,7 +1,5 @@
"""Test the Min/Max config flow."""
from unittest.mock import patch
import pytest
from homeassistant import config_entries
@@ -12,47 +10,14 @@ from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry, get_schema_suggested_value
@pytest.mark.parametrize("platform", ["sensor"])
async def test_config_flow(hass: HomeAssistant, platform: str) -> None:
"""Test the config flow."""
input_sensors = ["sensor.input_one", "sensor.input_two"]
async def test_config_flow_aborts(hass: HomeAssistant) -> None:
"""Test the config flow aborts."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] is FlowResultType.FORM
assert result["errors"] is None
with patch(
"homeassistant.components.min_max.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"name": "My min_max", "entity_ids": input_sensors, "type": "max"},
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == "My min_max"
assert result["data"] == {}
assert result["options"] == {
"entity_ids": input_sensors,
"name": "My min_max",
"round_digits": 2.0,
"type": "max",
}
assert len(mock_setup_entry.mock_calls) == 1
config_entry = hass.config_entries.async_entries(DOMAIN)[0]
assert config_entry.data == {}
assert config_entry.options == {
"entity_ids": input_sensors,
"name": "My min_max",
"round_digits": 2.0,
"type": "max",
}
assert config_entry.title == "My min_max"
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "migrated_to_groups"
@pytest.mark.parametrize("platform", ["sensor"])
+117 -13
View File
@@ -1,32 +1,44 @@
"""Test the Min/Max integration."""
import pytest
from freezegun.api import FrozenDateTimeFactory
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.group import DOMAIN as GROUP_DOMAIN
from homeassistant.components.min_max.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, async_fire_time_changed
from tests.components.repairs import process_repair_fix_flow, start_repair_fix_flow
from tests.typing import ClientSessionGenerator, WebSocketGenerator
@pytest.mark.parametrize("platform", ["sensor"])
async def test_setup_and_remove_config_entry(
async def test_setup_migrates_to_groups(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
platform: str,
issue_registry: ir.IssueRegistry,
freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion,
hass_client: ClientSessionGenerator,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test setting up and removing a config entry."""
"""Test migrating to group sensors."""
assert await async_setup_component(hass, "repairs", {})
hass.states.async_set("sensor.input_one", "10")
hass.states.async_set("sensor.input_two", "20")
input_sensors = ["sensor.input_one", "sensor.input_two"]
min_max_entity_id = f"{platform}.my_min_max"
min_max_entity_id = "sensor.my_min_max"
# Setup the config entry
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
entry_id="123",
options={
"entity_ids": input_sensors,
"name": "My min_max",
@@ -40,16 +52,108 @@ async def test_setup_and_remove_config_entry(
await hass.async_block_till_done()
# Check the entity is registered in the entity registry
assert entity_registry.async_get(min_max_entity_id) is not None
entity = entity_registry.async_get(min_max_entity_id)
assert entity is not None
issue = issue_registry.async_get_issue(
DOMAIN, f"migrate_to_group_sensor-{config_entry.entry_id}"
)
assert issue is not None
assert issue.is_fixable is True
assert issue.breaks_in_ha_version == "2026.12.0"
ws_client = await hass_ws_client(hass)
client = await hass_client()
await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
msg = await ws_client.receive_json()
assert msg["success"]
data = await start_repair_fix_flow(
client, DOMAIN, f"migrate_to_group_sensor-{config_entry.entry_id}"
)
flow_id = data["flow_id"]
assert data["description_placeholders"] == {"title": "My min_max"}
assert data["step_id"] == "migrate"
data = await process_repair_fix_flow(client, flow_id, json={})
assert data["type"] == FlowResultType.CREATE_ENTRY
await hass.async_block_till_done()
entity = entity_registry.async_get(min_max_entity_id)
assert entity.config_entry_id is not None
assert entity.config_entry_id != config_entry.entry_id
assert entity.unique_id != config_entry.entry_id
assert entity.platform == GROUP_DOMAIN
# Check the platform is setup correctly
assert len(hass.states.async_all()) == 3
state = hass.states.get(min_max_entity_id)
assert state.state == "20.0"
# Remove the config entry
# Assert min/max config entry is removed
min_max_config_entries = hass.config_entries.async_entries(DOMAIN)
assert len(min_max_config_entries) == 0
config_entry = hass.config_entries.async_entries("group")[0]
assert config_entry.as_dict() == snapshot(
exclude=props("created_at", "entry_id", "modified_at")
)
hass.states.async_set("sensor.input_two", "30")
freezer.tick(60 * 5)
async_fire_time_changed(hass)
await hass.async_block_till_done()
state = hass.states.get(min_max_entity_id)
assert state.state == "30.0"
async def test_issue_is_deleted_on_removal(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
issue_registry: ir.IssueRegistry,
freezer: FrozenDateTimeFactory,
snapshot: SnapshotAssertion,
hass_client: ClientSessionGenerator,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test issue is removed on config entry removal."""
assert await async_setup_component(hass, "repairs", {})
hass.states.async_set("sensor.input_one", "10")
hass.states.async_set("sensor.input_two", "20")
input_sensors = ["sensor.input_one", "sensor.input_two"]
# Setup the config entry
config_entry = MockConfigEntry(
data={},
domain=DOMAIN,
entry_id="123",
options={
"entity_ids": input_sensors,
"name": "My min_max",
"round_digits": 2.0,
"type": "max",
},
title="My min_max",
)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
issue = issue_registry.async_get_issue(
DOMAIN, f"migrate_to_group_sensor-{config_entry.entry_id}"
)
assert issue is not None
assert issue.is_fixable is True
assert issue.breaks_in_ha_version == "2026.12.0"
assert await hass.config_entries.async_remove(config_entry.entry_id)
await hass.async_block_till_done()
# Check the state and entity registry entry are removed
assert hass.states.get(min_max_entity_id) is None
assert entity_registry.async_get(min_max_entity_id) is None
issue = issue_registry.async_get_issue(
DOMAIN, f"migrate_to_group_sensor-{config_entry.entry_id}"
)
assert issue is None
+39 -1
View File
@@ -23,7 +23,7 @@ from homeassistant.const import (
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.setup import async_setup_component
from tests.common import get_fixture_path
@@ -42,6 +42,44 @@ RANGE_4_DIGITS = round(max(VALUES) - min(VALUES), 4)
SUM_VALUE = sum(VALUES)
async def test_deprecation_warning(
hass: HomeAssistant, issue_registry: ir.IssueRegistry
) -> None:
"""Test deprecation issue."""
config = {
"sensor": [
{
"platform": "min_max",
"type": "min",
"entity_ids": ["sensor.test_1", "sensor.test_2", "sensor.test_3"],
},
{
"platform": "min_max",
"type": "min",
"entity_ids": ["sensor.test_1", "sensor.test_2", "sensor.test_3"],
"unique_id": "my_unique_id",
},
]
}
assert await async_setup_component(hass, "sensor", config)
await hass.async_block_till_done()
issue = issue_registry.async_get_issue(
DOMAIN, "yaml_deprecated-ddc87b71acd58a195502396b87387d910c36ff7c"
)
issue2 = issue_registry.async_get_issue(
DOMAIN, "yaml_deprecated-6e9186f09cfb0959d0fe420ef3b01e1b25899b2f"
)
assert issue is not None
assert issue.severity == ir.IssueSeverity.WARNING
assert issue.translation_key == "yaml_deprecated"
assert issue2 is not None
assert issue2.severity == ir.IssueSeverity.WARNING
assert issue2.translation_key == "yaml_deprecated"
async def test_default_name_sensor(hass: HomeAssistant) -> None:
"""Test the min sensor with a default name."""
config = {