mirror of
https://github.com/home-assistant/core.git
synced 2026-09-24 07:25:52 -05:00
Validate KNX entity store data before setting up UI entities (#180067)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bbe4de36a1
commit
60d579f0ca
@@ -68,9 +68,7 @@ async def async_setup_entry(
|
||||
KnxYamlBinarySensor(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(
|
||||
Platform.BINARY_SENSOR
|
||||
):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.BINARY_SENSOR):
|
||||
entities.extend(
|
||||
KnxUiBinarySensor(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -49,7 +49,7 @@ async def async_setup_entry(
|
||||
KnxYamlButton(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.BUTTON):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.BUTTON):
|
||||
entities.extend(
|
||||
KnxUiButton(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -103,7 +103,7 @@ async def async_setup_entry(
|
||||
KnxYamlClimate(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.CLIMATE):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.CLIMATE):
|
||||
entities.extend(
|
||||
KnxUiClimate(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -124,6 +124,7 @@ SERVICE_KNX_EXPOSURE_REGISTER: Final = "exposure_register"
|
||||
SERVICE_KNX_READ: Final = "read"
|
||||
|
||||
REPAIR_ISSUE_DATA_SECURE_GROUP_KEY: Final = "data_secure_group_key_issue"
|
||||
REPAIR_ISSUE_ENTITY_VALIDATION_ERROR: Final = "entity_validation_error"
|
||||
REPAIR_ISSUE_TELEGRAM_BACKEND_ERROR: Final = "telegram_backend_error"
|
||||
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ async def async_setup_entry(
|
||||
KnxYamlCover(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.COVER):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.COVER):
|
||||
entities.extend(
|
||||
KnxUiCover(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -59,7 +59,7 @@ async def async_setup_entry(
|
||||
KnxYamlDate(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.DATE):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.DATE):
|
||||
entities.extend(
|
||||
KnxUiDate(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -60,7 +60,7 @@ async def async_setup_entry(
|
||||
KnxYamlDateTime(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.DATETIME):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.DATETIME):
|
||||
entities.extend(
|
||||
KnxUiDateTime(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -14,7 +14,6 @@ from homeassistant.const import (
|
||||
CONF_ID,
|
||||
CONF_NAME,
|
||||
CONF_UNIQUE_ID,
|
||||
EntityCategory,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
@@ -267,7 +266,6 @@ class KnxUiEntity(_KnxEntityBase):
|
||||
|
||||
self._attr_name = entity_config[CONF_NAME]
|
||||
self._attr_unique_id = unique_id
|
||||
if entity_category := entity_config.get(CONF_ENTITY_CATEGORY):
|
||||
self._attr_entity_category = EntityCategory(entity_category)
|
||||
if device_info := entity_config.get(CONF_DEVICE_INFO):
|
||||
self._attr_entity_category = entity_config[CONF_ENTITY_CATEGORY]
|
||||
if device_info := entity_config[CONF_DEVICE_INFO]:
|
||||
self._attr_device_info = DeviceInfo(identifiers={(DOMAIN, device_info)})
|
||||
|
||||
@@ -122,7 +122,7 @@ async def async_setup_entry(
|
||||
KnxYamlFan(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.FAN):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.FAN):
|
||||
entities.extend(
|
||||
KnxUiFan(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -83,7 +83,7 @@ async def async_setup_entry(
|
||||
KnxYamlLight(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.LIGHT):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.LIGHT):
|
||||
entities.extend(
|
||||
KnxUiLight(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -49,7 +49,7 @@ async def async_setup_entry(
|
||||
KnxYamlNotify(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.NOTIFY):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.NOTIFY):
|
||||
entities.extend(
|
||||
KnxUiNotify(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -68,7 +68,7 @@ async def async_setup_entry(
|
||||
KnxYamlNumber(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.NUMBER):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.NUMBER):
|
||||
entities.extend(
|
||||
KnxUiNumber(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from collections.abc import Callable
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, Final
|
||||
|
||||
import voluptuous as vol
|
||||
@@ -9,6 +10,7 @@ from xknx.exceptions.exception import InvalidSecureConfiguration
|
||||
from xknx.telegram import GroupAddress, IndividualAddress, Telegram
|
||||
|
||||
from homeassistant.components.repairs import RepairsFlow, RepairsFlowResult
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import issue_registry as ir, selector
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
@@ -21,12 +23,15 @@ from .const import (
|
||||
CONF_KNX_KNXKEY_PASSWORD,
|
||||
DOMAIN,
|
||||
REPAIR_ISSUE_DATA_SECURE_GROUP_KEY,
|
||||
REPAIR_ISSUE_ENTITY_VALIDATION_ERROR,
|
||||
REPAIR_ISSUE_TELEGRAM_BACKEND_ERROR,
|
||||
SIGNAL_KNX_DATA_SECURE_ISSUE_TELEGRAM,
|
||||
KNXConfigEntryData,
|
||||
)
|
||||
from .storage.keyring import DEFAULT_KNX_KEYRING_FILENAME, save_uploaded_knxkeys_file
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_KEYRING_FILE: Final = "knxkeys_file"
|
||||
|
||||
|
||||
@@ -43,6 +48,35 @@ async def async_create_fix_flow(
|
||||
raise ValueError(f"unknown repair {issue_id}")
|
||||
|
||||
|
||||
###########################
|
||||
# Entity store schema issue
|
||||
###########################
|
||||
|
||||
|
||||
@callback
|
||||
def async_create_entity_validation_issue(
|
||||
hass: HomeAssistant, platform: Platform, unique_ids: list[str]
|
||||
) -> None:
|
||||
"""Create a repair issue for invalid entity configurations in the config store."""
|
||||
_LOGGER.error(
|
||||
"Invalid KNX %s configuration in storage. These entities were not set up: %s",
|
||||
platform,
|
||||
", ".join(unique_ids),
|
||||
)
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
f"{REPAIR_ISSUE_ENTITY_VALIDATION_ERROR}_{platform}",
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key=REPAIR_ISSUE_ENTITY_VALIDATION_ERROR,
|
||||
translation_placeholders={
|
||||
"platform": platform,
|
||||
"entities": "\n".join(f"- {unique_id}" for unique_id in unique_ids),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
######################
|
||||
# DataSecure key issue
|
||||
######################
|
||||
|
||||
@@ -51,7 +51,7 @@ async def async_setup_entry(
|
||||
KnxYamlScene(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.SCENE):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.SCENE):
|
||||
entities.extend(
|
||||
KnxUiScene(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -71,7 +71,7 @@ async def async_setup_entry(
|
||||
KnxYamlSelect(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.SELECT):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.SELECT):
|
||||
entities.extend(
|
||||
KnxUiSelect(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -156,7 +156,7 @@ async def async_setup_entry(
|
||||
KnxYamlSensor(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.SENSOR):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.SENSOR):
|
||||
entities.extend(
|
||||
KnxUiSensor(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -12,8 +12,13 @@ from homeassistant.helpers.storage import Store
|
||||
from homeassistant.util.ulid import ulid_now
|
||||
|
||||
from ..const import DOMAIN, KNX_MODULE_KEY
|
||||
from ..repairs import async_create_entity_validation_issue
|
||||
from . import migration
|
||||
from .const import CONF_DATA
|
||||
from .entity_store_validation import (
|
||||
EntityStoreValidationException,
|
||||
validate_entity_data,
|
||||
)
|
||||
from .expose_controller import KNXExposeStoreConfigModel, KNXExposeStoreModel
|
||||
from .time_server import KNXTimeServerStoreModel
|
||||
|
||||
@@ -118,6 +123,28 @@ class KNXConfigStore:
|
||||
"""Add platform controller."""
|
||||
self._platform_controllers[platform] = controller
|
||||
|
||||
@callback
|
||||
def get_entity_configs(self, platform: Platform) -> KNXPlatformStoreModel:
|
||||
"""Return validated entity configurations for a platform.
|
||||
|
||||
Invalid configurations are reported as a repair issue and stay in
|
||||
`self.data` so they aren't dropped from storage.
|
||||
"""
|
||||
validated: KNXPlatformStoreModel = {}
|
||||
invalid: list[str] = []
|
||||
for unique_id, config in self.data["entities"].get(platform, {}).items():
|
||||
try:
|
||||
result = validate_entity_data(
|
||||
{CONF_PLATFORM: platform, CONF_DATA: config}
|
||||
)
|
||||
except EntityStoreValidationException:
|
||||
invalid.append(unique_id)
|
||||
else:
|
||||
validated[unique_id] = result[CONF_DATA]
|
||||
if invalid:
|
||||
async_create_entity_validation_issue(self.hass, platform, invalid)
|
||||
return validated
|
||||
|
||||
async def create_entity(
|
||||
self, platform: Platform, data: dict[str, Any]
|
||||
) -> str | None:
|
||||
|
||||
@@ -408,19 +408,21 @@ LIGHT_KNX_SCHEMA = AllSerializeFirst(
|
||||
probatio.Optional(CONF_GA_COLOR_TEMP): GASelector(
|
||||
write_required=True, dpt=ColorTempModes
|
||||
),
|
||||
probatio.Required(
|
||||
CONF_COLOR_TEMP_MIN, default=2700
|
||||
): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=1, max=10000, step=1, unit_of_measurement="K"
|
||||
)
|
||||
probatio.Required(CONF_COLOR_TEMP_MIN, default=2700): AllSerializeFirst(
|
||||
selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=1, max=10000, step=1, unit_of_measurement="K"
|
||||
)
|
||||
),
|
||||
probatio.Coerce(int),
|
||||
),
|
||||
probatio.Required(
|
||||
CONF_COLOR_TEMP_MAX, default=6000
|
||||
): selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=1, max=10000, step=1, unit_of_measurement="K"
|
||||
)
|
||||
probatio.Required(CONF_COLOR_TEMP_MAX, default=6000): AllSerializeFirst(
|
||||
selector.NumberSelector(
|
||||
selector.NumberSelectorConfig(
|
||||
min=1, max=10000, step=1, unit_of_measurement="K"
|
||||
)
|
||||
),
|
||||
probatio.Coerce(int),
|
||||
),
|
||||
probatio.Optional(CONF_COLOR): GroupSelect(
|
||||
GroupSelectOption(
|
||||
|
||||
@@ -1321,6 +1321,10 @@
|
||||
},
|
||||
"title": "KNX Data Secure telegrams can't be decrypted"
|
||||
},
|
||||
"entity_validation_error": {
|
||||
"description": "The stored configuration of the following KNX {platform} entities is invalid, so they were not set up:\n\n{entities}\n\nCorrect or delete them. Check the logs for details.",
|
||||
"title": "Invalid KNX entity configuration"
|
||||
},
|
||||
"telegram_storage_error": {
|
||||
"description": "The configured KNX telegram storage backend failed to initialize. As a result, KNX telegrams are currently not being stored. Check the logs for details on the error and ensure your database is accessible.",
|
||||
"title": "KNX telegram storage error"
|
||||
|
||||
@@ -65,7 +65,7 @@ async def async_setup_entry(
|
||||
KnxYamlSwitch(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.SWITCH):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.SWITCH):
|
||||
entities.extend(
|
||||
KnxUiSwitch(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -66,7 +66,7 @@ async def async_setup_entry(
|
||||
KnxYamlText(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.TEXT):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.TEXT):
|
||||
entities.extend(
|
||||
KnxUiText(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -59,7 +59,7 @@ async def async_setup_entry(
|
||||
KnxYamlTime(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.TIME):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.TIME):
|
||||
entities.extend(
|
||||
KnxUiTime(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -72,7 +72,7 @@ async def async_setup_entry(
|
||||
KnxYamlWeather(knx_module, entity_config)
|
||||
for entity_config in yaml_platform_config
|
||||
)
|
||||
if ui_config := knx_module.config_store.data["entities"].get(Platform.WEATHER):
|
||||
if ui_config := knx_module.config_store.get_entity_configs(Platform.WEATHER):
|
||||
entities.extend(
|
||||
KnxUiWeather(knx_module, unique_id, config)
|
||||
for unique_id, config in ui_config.items()
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"passive": []
|
||||
},
|
||||
"respond_to_read": false,
|
||||
"sync_state": false
|
||||
"sync_state": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"passive": []
|
||||
},
|
||||
"respond_to_read": false,
|
||||
"sync_state": false
|
||||
"sync_state": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"version": 2,
|
||||
"minor_version": 4,
|
||||
"key": "knx/config_store.json",
|
||||
"data": {
|
||||
"entities": {
|
||||
"switch": {
|
||||
"knx_es_01JWDFHP1ZG6NT62BX6ENR3MG7": {
|
||||
"entity": {
|
||||
"name": "valid",
|
||||
"device_info": null,
|
||||
"entity_category": "config"
|
||||
},
|
||||
"knx": {
|
||||
"ga_switch": {
|
||||
"write": "1/1/45",
|
||||
"state": "1/0/45",
|
||||
"passive": []
|
||||
},
|
||||
"invert": false,
|
||||
"sync_state": true,
|
||||
"respond_to_read": false
|
||||
}
|
||||
},
|
||||
"knx_es_01JWDFKBG3PYPPRQDJZ3N3PMCB": {
|
||||
"entity": {
|
||||
"name": "invalid group address",
|
||||
"device_info": null,
|
||||
"entity_category": null
|
||||
},
|
||||
"knx": {
|
||||
"ga_switch": {
|
||||
"write": "not a group address",
|
||||
"state": null,
|
||||
"passive": []
|
||||
},
|
||||
"invert": false,
|
||||
"sync_state": true,
|
||||
"respond_to_read": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"light": {
|
||||
"knx_es_01J85ZKTFHSZNG4X9DYBE592TF": {
|
||||
"entity": {
|
||||
"name": "missing defaults",
|
||||
"device_info": null,
|
||||
"entity_category": null
|
||||
},
|
||||
"knx": {
|
||||
"ga_switch": {
|
||||
"write": "1/1/21",
|
||||
"state": "1/0/21",
|
||||
"passive": []
|
||||
},
|
||||
"sync_state": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expose": {},
|
||||
"time_server": {}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
"passive": []
|
||||
},
|
||||
"respond_to_read": false,
|
||||
"sync_state": false
|
||||
"sync_state": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,17 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.knx.const import (
|
||||
DOMAIN,
|
||||
KNX_MODULE_KEY,
|
||||
REPAIR_ISSUE_ENTITY_VALIDATION_ERROR,
|
||||
)
|
||||
from homeassistant.components.knx.storage.config_store import (
|
||||
STORAGE_KEY as KNX_CONFIG_STORAGE_KEY,
|
||||
)
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.const import EntityCategory, Platform
|
||||
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 . import KnxEntityGenerator
|
||||
from .conftest import KNXTestKit
|
||||
@@ -605,6 +610,86 @@ async def test_delete_expose_error(
|
||||
)
|
||||
|
||||
|
||||
##################
|
||||
# STORE VALIDATION
|
||||
##################
|
||||
|
||||
VALID_SWITCH_UID = "knx_es_01JWDFHP1ZG6NT62BX6ENR3MG7"
|
||||
INVALID_SWITCH_UID = "knx_es_01JWDFKBG3PYPPRQDJZ3N3PMCB"
|
||||
LIGHT_UID = "knx_es_01J85ZKTFHSZNG4X9DYBE592TF"
|
||||
|
||||
|
||||
async def test_load_skips_invalid_entity_config(
|
||||
hass: HomeAssistant,
|
||||
knx: KNXTestKit,
|
||||
entity_registry: er.EntityRegistry,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test an invalid stored config is skipped without failing its platform."""
|
||||
await knx.setup_integration(
|
||||
config_store_fixture="config_store_invalid.json", state_updater=False
|
||||
)
|
||||
assert entity_registry.async_get_entity_id(
|
||||
Platform.SWITCH, DOMAIN, VALID_SWITCH_UID
|
||||
)
|
||||
assert (
|
||||
entity_registry.async_get_entity_id(Platform.SWITCH, DOMAIN, INVALID_SWITCH_UID)
|
||||
is None
|
||||
)
|
||||
|
||||
issue = issue_registry.async_get_issue(
|
||||
DOMAIN, f"{REPAIR_ISSUE_ENTITY_VALIDATION_ERROR}_{Platform.SWITCH}"
|
||||
)
|
||||
assert issue is not None
|
||||
assert issue.severity is ir.IssueSeverity.ERROR
|
||||
assert issue.translation_placeholders == {
|
||||
"platform": Platform.SWITCH,
|
||||
"entities": f"- {INVALID_SWITCH_UID}",
|
||||
}
|
||||
|
||||
|
||||
async def test_load_applies_schema_defaults_and_coercion(
|
||||
hass: HomeAssistant,
|
||||
knx: KNXTestKit,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test stored configs are normalized on load.
|
||||
|
||||
The light in the fixture predates `color_temp_min` / `color_temp_max`, which
|
||||
`KnxUiLight.__init__` reads by direct key access, and the switch stores
|
||||
`entity_category` as a plain string.
|
||||
"""
|
||||
await knx.setup_integration(
|
||||
config_store_fixture="config_store_invalid.json", state_updater=False
|
||||
)
|
||||
assert hass.states.get("light.missing_defaults") is not None
|
||||
config_store = hass.data[KNX_MODULE_KEY].config_store
|
||||
light_config = config_store.get_entity_configs(Platform.LIGHT)[LIGHT_UID][DOMAIN]
|
||||
assert light_config["color_temp_min"] == 2700
|
||||
assert light_config["color_temp_max"] == 6000
|
||||
|
||||
switch_id = entity_registry.async_get_entity_id(
|
||||
Platform.SWITCH, DOMAIN, VALID_SWITCH_UID
|
||||
)
|
||||
assert entity_registry.async_get(switch_id).entity_category is EntityCategory.CONFIG
|
||||
|
||||
|
||||
async def test_load_valid_store_creates_no_issue(
|
||||
hass: HomeAssistant,
|
||||
knx: KNXTestKit,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test a valid store doesn't raise a repair issue."""
|
||||
await knx.setup_integration(
|
||||
config_store_fixture="config_store_light_switch.json", state_updater=False
|
||||
)
|
||||
assert not [
|
||||
issue
|
||||
for issue in issue_registry.issues.values()
|
||||
if issue.issue_id.startswith(REPAIR_ISSUE_ENTITY_VALIDATION_ERROR)
|
||||
]
|
||||
|
||||
|
||||
###########
|
||||
# MIGRATION
|
||||
###########
|
||||
|
||||
@@ -281,7 +281,7 @@ async def test_number_ui_load(knx: KNXTestKit) -> None:
|
||||
)
|
||||
knx.assert_state(
|
||||
"number.test_options",
|
||||
"3000",
|
||||
"3000.0", # `min`, `max` and `step` are floats after validation
|
||||
unit_of_measurement="kW",
|
||||
device_class="power",
|
||||
min=3000,
|
||||
|
||||
Reference in New Issue
Block a user