Add entity category option to entities set up via an MQTT subentry
parent
91bc56b15c
commit
fd7f693f3b
|
@ -66,6 +66,7 @@ from homeassistant.const import (
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_DISCOVERY,
|
CONF_DISCOVERY,
|
||||||
CONF_EFFECT,
|
CONF_EFFECT,
|
||||||
|
CONF_ENTITY_CATEGORY,
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_OPTIMISTIC,
|
CONF_OPTIMISTIC,
|
||||||
|
@ -84,6 +85,7 @@ from homeassistant.const import (
|
||||||
STATE_CLOSING,
|
STATE_CLOSING,
|
||||||
STATE_OPEN,
|
STATE_OPEN,
|
||||||
STATE_OPENING,
|
STATE_OPENING,
|
||||||
|
EntityCategory,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.data_entry_flow import AbortFlow, SectionConfig, section
|
from homeassistant.data_entry_flow import AbortFlow, SectionConfig, section
|
||||||
|
@ -411,6 +413,14 @@ SUBENTRY_AVAILABILITY_SCHEMA = vol.Schema(
|
||||||
): TEXT_SELECTOR,
|
): TEXT_SELECTOR,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
ENTITY_CATEGORY_SELECTOR = SelectSelector(
|
||||||
|
SelectSelectorConfig(
|
||||||
|
options=[category.value for category in EntityCategory],
|
||||||
|
mode=SelectSelectorMode.DROPDOWN,
|
||||||
|
translation_key=CONF_ENTITY_CATEGORY,
|
||||||
|
sort=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Sensor specific selectors
|
# Sensor specific selectors
|
||||||
SENSOR_DEVICE_CLASS_SELECTOR = SelectSelector(
|
SENSOR_DEVICE_CLASS_SELECTOR = SelectSelector(
|
||||||
|
@ -603,6 +613,18 @@ def validate_fan_platform_config(config: dict[str, Any]) -> dict[str, str]:
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def validate_binary_sensor_platform_config(
|
||||||
|
config: dict[str, Any],
|
||||||
|
) -> dict[str, str]:
|
||||||
|
"""Validate the binary sensor sensor entity_category."""
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
if config.get(CONF_ENTITY_CATEGORY) == EntityCategory.CONFIG:
|
||||||
|
errors[CONF_ENTITY_CATEGORY] = "sensor_entity_category_must_not_be_config"
|
||||||
|
|
||||||
|
return errors
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def validate_sensor_platform_config(
|
def validate_sensor_platform_config(
|
||||||
config: dict[str, Any],
|
config: dict[str, Any],
|
||||||
|
@ -648,6 +670,9 @@ def validate_sensor_platform_config(
|
||||||
):
|
):
|
||||||
errors[CONF_UNIT_OF_MEASUREMENT] = "invalid_uom_for_state_class"
|
errors[CONF_UNIT_OF_MEASUREMENT] = "invalid_uom_for_state_class"
|
||||||
|
|
||||||
|
if config.get(CONF_ENTITY_CATEGORY) == EntityCategory.CONFIG:
|
||||||
|
errors[CONF_ENTITY_CATEGORY] = "sensor_entity_category_must_not_be_config"
|
||||||
|
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
|
|
||||||
|
@ -730,6 +755,11 @@ COMMON_ENTITY_FIELDS: dict[str, PlatformField] = {
|
||||||
exclude_from_reconfig=True,
|
exclude_from_reconfig=True,
|
||||||
default=None,
|
default=None,
|
||||||
),
|
),
|
||||||
|
CONF_ENTITY_CATEGORY: PlatformField(
|
||||||
|
selector=ENTITY_CATEGORY_SELECTOR,
|
||||||
|
required=False,
|
||||||
|
default=None,
|
||||||
|
),
|
||||||
CONF_ENTITY_PICTURE: PlatformField(
|
CONF_ENTITY_PICTURE: PlatformField(
|
||||||
selector=TEXT_SELECTOR, required=False, validator=cv.url, error="invalid_url"
|
selector=TEXT_SELECTOR, required=False, validator=cv.url, error="invalid_url"
|
||||||
),
|
),
|
||||||
|
@ -1855,7 +1885,7 @@ ENTITY_CONFIG_VALIDATOR: dict[
|
||||||
str,
|
str,
|
||||||
Callable[[dict[str, Any]], dict[str, str]] | None,
|
Callable[[dict[str, Any]], dict[str, str]] | None,
|
||||||
] = {
|
] = {
|
||||||
Platform.BINARY_SENSOR.value: None,
|
Platform.BINARY_SENSOR.value: validate_binary_sensor_platform_config,
|
||||||
Platform.BUTTON.value: None,
|
Platform.BUTTON.value: None,
|
||||||
Platform.COVER.value: validate_cover_platform_config,
|
Platform.COVER.value: validate_cover_platform_config,
|
||||||
Platform.FAN.value: validate_fan_platform_config,
|
Platform.FAN.value: validate_fan_platform_config,
|
||||||
|
@ -1995,13 +2025,12 @@ def validate_user_input(
|
||||||
)
|
)
|
||||||
|
|
||||||
if config_validator is not None:
|
if config_validator is not None:
|
||||||
if TYPE_CHECKING:
|
|
||||||
assert component_data is not None
|
|
||||||
|
|
||||||
errors |= config_validator(
|
errors |= config_validator(
|
||||||
calculate_merged_config(
|
merged_user_input
|
||||||
|
if component_data is None
|
||||||
|
else calculate_merged_config(
|
||||||
merged_user_input, data_schema_fields, component_data
|
merged_user_input, data_schema_fields, component_data
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return merged_user_input, errors
|
return merged_user_input, errors
|
||||||
|
@ -2751,8 +2780,16 @@ class MQTTSubentryFlowHandler(ConfigSubentryFlow):
|
||||||
entity_name_label = f" ({name})" if name is not None else ""
|
entity_name_label = f" ({name})" if name is not None else ""
|
||||||
data_schema = data_schema_from_fields(data_schema_fields, reconfig=reconfig)
|
data_schema = data_schema_from_fields(data_schema_fields, reconfig=reconfig)
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
|
platform: str = (
|
||||||
|
user_input[CONF_PLATFORM]
|
||||||
|
if component_data is None
|
||||||
|
else component_data[CONF_PLATFORM]
|
||||||
|
)
|
||||||
merged_user_input, errors = validate_user_input(
|
merged_user_input, errors = validate_user_input(
|
||||||
user_input, data_schema_fields, component_data=component_data
|
user_input,
|
||||||
|
data_schema_fields,
|
||||||
|
component_data=component_data,
|
||||||
|
config_validator=ENTITY_CONFIG_VALIDATOR[platform],
|
||||||
)
|
)
|
||||||
if not errors:
|
if not errors:
|
||||||
if self._component_id is None:
|
if self._component_id is None:
|
||||||
|
|
|
@ -313,6 +313,11 @@ def async_setup_entity_entry_helper(
|
||||||
component_config.pop("platform")
|
component_config.pop("platform")
|
||||||
component_config.update(availability_config)
|
component_config.update(availability_config)
|
||||||
component_config.update(device_mqtt_options)
|
component_config.update(device_mqtt_options)
|
||||||
|
if (
|
||||||
|
CONF_ENTITY_CATEGORY in component_config
|
||||||
|
and component_config[CONF_ENTITY_CATEGORY] is None
|
||||||
|
):
|
||||||
|
component_config.pop(CONF_ENTITY_CATEGORY)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = platform_schema_modern(component_config)
|
config = platform_schema_modern(component_config)
|
||||||
|
|
|
@ -181,11 +181,13 @@
|
||||||
"data": {
|
"data": {
|
||||||
"platform": "Type of entity",
|
"platform": "Type of entity",
|
||||||
"name": "Entity name",
|
"name": "Entity name",
|
||||||
|
"entity_category": "Entity category",
|
||||||
"entity_picture": "Entity picture"
|
"entity_picture": "Entity picture"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"platform": "The type of the entity to configure.",
|
"platform": "The type of the entity to configure.",
|
||||||
"name": "The name of the entity. Leave empty to set it to `None` to [mark it as main feature of the MQTT device](https://www.home-assistant.io/integrations/mqtt/#naming-of-mqtt-entities).",
|
"name": "The name of the entity. Leave empty to set it to `None` to [mark it as main feature of the MQTT device](https://www.home-assistant.io/integrations/mqtt/#naming-of-mqtt-entities).",
|
||||||
|
"entity_category": "The category of the entity to configure. Leave empty to set it to `None` to [mark it as main feature of the MQTT device](https://developers.home-assistant.io/docs/core/entity/#registry-properties). An entity with a category will not be exposed to cloud, Alexa, or Google Assistant components, nor included in indirect service calls to devices or areas.",
|
||||||
"entity_picture": "An URL to a picture to be assigned."
|
"entity_picture": "An URL to a picture to be assigned."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -651,6 +653,7 @@
|
||||||
"options_not_allowed_with_state_class_or_uom": "The 'Options' setting is not allowed when state class or unit of measurement are used",
|
"options_not_allowed_with_state_class_or_uom": "The 'Options' setting is not allowed when state class or unit of measurement are used",
|
||||||
"options_device_class_enum": "The 'Options' setting must be used with the Enumeration device class. If you continue, the existing options will be reset",
|
"options_device_class_enum": "The 'Options' setting must be used with the Enumeration device class. If you continue, the existing options will be reset",
|
||||||
"options_with_enum_device_class": "Configure options for the enumeration sensor",
|
"options_with_enum_device_class": "Configure options for the enumeration sensor",
|
||||||
|
"sensor_entity_category_must_not_be_config": "Sensor entities can not be categorized as configurable",
|
||||||
"uom_required_for_device_class": "The selected device class requires a unit"
|
"uom_required_for_device_class": "The selected device class requires a unit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -887,6 +890,12 @@
|
||||||
"switch": "[%key:component::switch::title%]"
|
"switch": "[%key:component::switch::title%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"entity_category": {
|
||||||
|
"options": {
|
||||||
|
"config": "Config",
|
||||||
|
"diagnostic": "Diagnostic"
|
||||||
|
}
|
||||||
|
},
|
||||||
"light_schema": {
|
"light_schema": {
|
||||||
"options": {
|
"options": {
|
||||||
"basic": "Default schema",
|
"basic": "Default schema",
|
||||||
|
|
|
@ -71,6 +71,7 @@ MOCK_SUBENTRY_BINARY_SENSOR_COMPONENT = {
|
||||||
"platform": "binary_sensor",
|
"platform": "binary_sensor",
|
||||||
"name": "Hatch",
|
"name": "Hatch",
|
||||||
"device_class": "door",
|
"device_class": "door",
|
||||||
|
"entity_category": None,
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
"payload_on": "ON",
|
"payload_on": "ON",
|
||||||
"payload_off": "OFF",
|
"payload_off": "OFF",
|
||||||
|
@ -86,6 +87,7 @@ MOCK_SUBENTRY_BUTTON_COMPONENT = {
|
||||||
"name": "Restart",
|
"name": "Restart",
|
||||||
"device_class": "restart",
|
"device_class": "restart",
|
||||||
"command_topic": "test-topic",
|
"command_topic": "test-topic",
|
||||||
|
"entity_category": None,
|
||||||
"payload_press": "PRESS",
|
"payload_press": "PRESS",
|
||||||
"command_template": "{{ value }}",
|
"command_template": "{{ value }}",
|
||||||
"retain": False,
|
"retain": False,
|
||||||
|
@ -97,6 +99,7 @@ MOCK_SUBENTRY_COVER_COMPONENT = {
|
||||||
"platform": "cover",
|
"platform": "cover",
|
||||||
"name": "Blind",
|
"name": "Blind",
|
||||||
"device_class": "blind",
|
"device_class": "blind",
|
||||||
|
"entity_category": None,
|
||||||
"command_topic": "test-topic",
|
"command_topic": "test-topic",
|
||||||
"payload_stop": None,
|
"payload_stop": None,
|
||||||
"payload_stop_tilt": "STOP",
|
"payload_stop_tilt": "STOP",
|
||||||
|
@ -132,6 +135,7 @@ MOCK_SUBENTRY_FAN_COMPONENT = {
|
||||||
"platform": "fan",
|
"platform": "fan",
|
||||||
"name": "Breezer",
|
"name": "Breezer",
|
||||||
"command_topic": "test-topic",
|
"command_topic": "test-topic",
|
||||||
|
"entity_category": None,
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
"command_template": "{{ value }}",
|
"command_template": "{{ value }}",
|
||||||
"value_template": "{{ value_json.value }}",
|
"value_template": "{{ value_json.value }}",
|
||||||
|
@ -169,6 +173,7 @@ MOCK_SUBENTRY_NOTIFY_COMPONENT1 = {
|
||||||
"363a7ecad6be4a19b939a016ea93e994": {
|
"363a7ecad6be4a19b939a016ea93e994": {
|
||||||
"platform": "notify",
|
"platform": "notify",
|
||||||
"name": "Milkman alert",
|
"name": "Milkman alert",
|
||||||
|
"entity_category": None,
|
||||||
"command_topic": "test-topic",
|
"command_topic": "test-topic",
|
||||||
"command_template": "{{ value }}",
|
"command_template": "{{ value }}",
|
||||||
"entity_picture": "https://example.com/363a7ecad6be4a19b939a016ea93e994",
|
"entity_picture": "https://example.com/363a7ecad6be4a19b939a016ea93e994",
|
||||||
|
@ -179,6 +184,7 @@ MOCK_SUBENTRY_NOTIFY_COMPONENT2 = {
|
||||||
"6494827dac294fa0827c54b02459d309": {
|
"6494827dac294fa0827c54b02459d309": {
|
||||||
"platform": "notify",
|
"platform": "notify",
|
||||||
"name": "The second notifier",
|
"name": "The second notifier",
|
||||||
|
"entity_category": None,
|
||||||
"command_topic": "test-topic2",
|
"command_topic": "test-topic2",
|
||||||
"entity_picture": "https://example.com/6494827dac294fa0827c54b02459d309",
|
"entity_picture": "https://example.com/6494827dac294fa0827c54b02459d309",
|
||||||
},
|
},
|
||||||
|
@ -187,6 +193,7 @@ MOCK_SUBENTRY_NOTIFY_COMPONENT_NO_NAME = {
|
||||||
"5269352dd9534c908d22812ea5d714cd": {
|
"5269352dd9534c908d22812ea5d714cd": {
|
||||||
"platform": "notify",
|
"platform": "notify",
|
||||||
"name": None,
|
"name": None,
|
||||||
|
"entity_category": None,
|
||||||
"command_topic": "test-topic",
|
"command_topic": "test-topic",
|
||||||
"command_template": "{{ value }}",
|
"command_template": "{{ value }}",
|
||||||
"entity_picture": "https://example.com/5269352dd9534c908d22812ea5d714cd",
|
"entity_picture": "https://example.com/5269352dd9534c908d22812ea5d714cd",
|
||||||
|
@ -198,6 +205,7 @@ MOCK_SUBENTRY_SENSOR_COMPONENT = {
|
||||||
"e9261f6feed443e7b7d5f3fbe2a47412": {
|
"e9261f6feed443e7b7d5f3fbe2a47412": {
|
||||||
"platform": "sensor",
|
"platform": "sensor",
|
||||||
"name": "Energy",
|
"name": "Energy",
|
||||||
|
"entity_category": None,
|
||||||
"device_class": "enum",
|
"device_class": "enum",
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
"options": ["low", "medium", "high"],
|
"options": ["low", "medium", "high"],
|
||||||
|
@ -210,6 +218,7 @@ MOCK_SUBENTRY_SENSOR_COMPONENT_STATE_CLASS = {
|
||||||
"a0f85790a95d4889924602effff06b6e": {
|
"a0f85790a95d4889924602effff06b6e": {
|
||||||
"platform": "sensor",
|
"platform": "sensor",
|
||||||
"name": "Energy",
|
"name": "Energy",
|
||||||
|
"entity_category": None,
|
||||||
"state_class": "measurement",
|
"state_class": "measurement",
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
"entity_picture": "https://example.com/a0f85790a95d4889924602effff06b6e",
|
"entity_picture": "https://example.com/a0f85790a95d4889924602effff06b6e",
|
||||||
|
@ -219,6 +228,7 @@ MOCK_SUBENTRY_SENSOR_COMPONENT_LAST_RESET = {
|
||||||
"e9261f6feed443e7b7d5f3fbe2a47412": {
|
"e9261f6feed443e7b7d5f3fbe2a47412": {
|
||||||
"platform": "sensor",
|
"platform": "sensor",
|
||||||
"name": "Energy",
|
"name": "Energy",
|
||||||
|
"entity_category": None,
|
||||||
"state_class": "total",
|
"state_class": "total",
|
||||||
"last_reset_value_template": "{{ value_json.value }}",
|
"last_reset_value_template": "{{ value_json.value }}",
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
|
@ -229,6 +239,7 @@ MOCK_SUBENTRY_SWITCH_COMPONENT = {
|
||||||
"3faf1318016c46c5aea26707eeb6f12e": {
|
"3faf1318016c46c5aea26707eeb6f12e": {
|
||||||
"platform": "switch",
|
"platform": "switch",
|
||||||
"name": "Outlet",
|
"name": "Outlet",
|
||||||
|
"entity_category": None,
|
||||||
"device_class": "outlet",
|
"device_class": "outlet",
|
||||||
"command_topic": "test-topic",
|
"command_topic": "test-topic",
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
|
@ -250,6 +261,7 @@ MOCK_SUBENTRY_LIGHT_BASIC_KELVIN_COMPONENT = {
|
||||||
"payload_off": "OFF",
|
"payload_off": "OFF",
|
||||||
"payload_on": "ON",
|
"payload_on": "ON",
|
||||||
"command_topic": "test-topic",
|
"command_topic": "test-topic",
|
||||||
|
"entity_category": None,
|
||||||
"schema": "basic",
|
"schema": "basic",
|
||||||
"state_topic": "test-topic",
|
"state_topic": "test-topic",
|
||||||
"color_temp_kelvin": True,
|
"color_temp_kelvin": True,
|
||||||
|
|
|
@ -2654,6 +2654,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
"config_subentries_data",
|
"config_subentries_data",
|
||||||
"mock_device_user_input",
|
"mock_device_user_input",
|
||||||
"mock_entity_user_input",
|
"mock_entity_user_input",
|
||||||
|
"mock_entity_failed_user_input",
|
||||||
"mock_entity_details_user_input",
|
"mock_entity_details_user_input",
|
||||||
"mock_entity_details_failed_user_input",
|
"mock_entity_details_failed_user_input",
|
||||||
"mock_mqtt_user_input",
|
"mock_mqtt_user_input",
|
||||||
|
@ -2665,6 +2666,16 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_BINARY_SENSOR_SUBENTRY_DATA_SINGLE,
|
MOCK_BINARY_SENSOR_SUBENTRY_DATA_SINGLE,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 2}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 2}},
|
||||||
{"name": "Hatch"},
|
{"name": "Hatch"},
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
{"entity_category": "config"},
|
||||||
|
{
|
||||||
|
"entity_category": "sensor_entity_category_must_not_be_config"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
{"device_class": "door"},
|
{"device_class": "door"},
|
||||||
(),
|
(),
|
||||||
{
|
{
|
||||||
|
@ -2684,6 +2695,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_BUTTON_SUBENTRY_DATA_SINGLE,
|
MOCK_BUTTON_SUBENTRY_DATA_SINGLE,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 2}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 2}},
|
||||||
{"name": "Restart"},
|
{"name": "Restart"},
|
||||||
|
(),
|
||||||
{"device_class": "restart"},
|
{"device_class": "restart"},
|
||||||
(),
|
(),
|
||||||
{
|
{
|
||||||
|
@ -2704,6 +2716,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_COVER_SUBENTRY_DATA_SINGLE,
|
MOCK_COVER_SUBENTRY_DATA_SINGLE,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
||||||
{"name": "Blind"},
|
{"name": "Blind"},
|
||||||
|
(),
|
||||||
{"device_class": "blind"},
|
{"device_class": "blind"},
|
||||||
(),
|
(),
|
||||||
{
|
{
|
||||||
|
@ -2790,6 +2803,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_FAN_SUBENTRY_DATA_SINGLE,
|
MOCK_FAN_SUBENTRY_DATA_SINGLE,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
||||||
{"name": "Breezer"},
|
{"name": "Breezer"},
|
||||||
|
(),
|
||||||
{
|
{
|
||||||
"fan_feature_speed": True,
|
"fan_feature_speed": True,
|
||||||
"fan_feature_preset_modes": True,
|
"fan_feature_preset_modes": True,
|
||||||
|
@ -2941,6 +2955,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE,
|
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
|
||||||
{"name": "Milkman alert"},
|
{"name": "Milkman alert"},
|
||||||
|
(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
{
|
{
|
||||||
|
@ -2960,6 +2975,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_NOTIFY_SUBENTRY_DATA_NO_NAME,
|
MOCK_NOTIFY_SUBENTRY_DATA_NO_NAME,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
||||||
{},
|
{},
|
||||||
|
(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
{
|
{
|
||||||
|
@ -2979,6 +2995,16 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_SENSOR_SUBENTRY_DATA_SINGLE,
|
MOCK_SENSOR_SUBENTRY_DATA_SINGLE,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
||||||
{"name": "Energy"},
|
{"name": "Energy"},
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
{"entity_category": "config"},
|
||||||
|
{
|
||||||
|
"entity_category": "sensor_entity_category_must_not_be_config"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
{"device_class": "enum", "options": ["low", "medium", "high"]},
|
{"device_class": "enum", "options": ["low", "medium", "high"]},
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
|
@ -3035,6 +3061,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_SENSOR_SUBENTRY_DATA_SINGLE_STATE_CLASS,
|
MOCK_SENSOR_SUBENTRY_DATA_SINGLE_STATE_CLASS,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
||||||
{"name": "Energy"},
|
{"name": "Energy"},
|
||||||
|
(),
|
||||||
{
|
{
|
||||||
"state_class": "measurement",
|
"state_class": "measurement",
|
||||||
},
|
},
|
||||||
|
@ -3057,6 +3084,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS,
|
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
|
||||||
{"name": "Outlet"},
|
{"name": "Outlet"},
|
||||||
|
(),
|
||||||
{"device_class": "outlet"},
|
{"device_class": "outlet"},
|
||||||
(),
|
(),
|
||||||
{
|
{
|
||||||
|
@ -3085,6 +3113,7 @@ async def test_migrate_of_incompatible_config_entry(
|
||||||
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
|
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
|
||||||
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
|
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
|
||||||
{"name": "Basic light"},
|
{"name": "Basic light"},
|
||||||
|
(),
|
||||||
{},
|
{},
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
|
@ -3146,6 +3175,7 @@ async def test_subentry_configflow(
|
||||||
config_subentries_data: dict[str, Any],
|
config_subentries_data: dict[str, Any],
|
||||||
mock_device_user_input: dict[str, Any],
|
mock_device_user_input: dict[str, Any],
|
||||||
mock_entity_user_input: dict[str, Any],
|
mock_entity_user_input: dict[str, Any],
|
||||||
|
mock_entity_failed_user_input: tuple[tuple[dict[str, Any], dict[str, str]],],
|
||||||
mock_entity_details_user_input: dict[str, Any],
|
mock_entity_details_user_input: dict[str, Any],
|
||||||
mock_entity_details_failed_user_input: tuple[
|
mock_entity_details_failed_user_input: tuple[
|
||||||
tuple[dict[str, Any], dict[str, str]],
|
tuple[dict[str, Any], dict[str, str]],
|
||||||
|
@ -3202,6 +3232,16 @@ async def test_subentry_configflow(
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "entity"
|
assert result["step_id"] == "entity"
|
||||||
|
|
||||||
|
# First test platform validators if set of test
|
||||||
|
for failed_user_input, failed_errors in mock_entity_failed_user_input:
|
||||||
|
# Test an invalid entity details user input case
|
||||||
|
result = await hass.config_entries.subentries.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={"platform": component["platform"]} | failed_user_input,
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["errors"] == failed_errors
|
||||||
|
|
||||||
# Try again with valid data
|
# Try again with valid data
|
||||||
result = await hass.config_entries.subentries.async_configure(
|
result = await hass.config_entries.subentries.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
|
@ -3906,9 +3946,26 @@ async def test_subentry_reconfigure_edit_entity_reset_fields(
|
||||||
{
|
{
|
||||||
"command_topic": "test-topic2",
|
"command_topic": "test-topic2",
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
|
(
|
||||||
|
(
|
||||||
|
ConfigSubentryData(
|
||||||
|
data=MOCK_NOTIFY_SUBENTRY_DATA_SINGLE,
|
||||||
|
subentry_type="device",
|
||||||
|
title="Mock subentry",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
{
|
||||||
|
"platform": "notify",
|
||||||
|
"name": "The second notifier",
|
||||||
|
"entity_category": "config",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command_topic": "test-topic2",
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
ids=["notify_notify"],
|
ids=["notify_notify_no_entity_category", "notify_notify_entity_category"],
|
||||||
)
|
)
|
||||||
async def test_subentry_reconfigure_add_entity(
|
async def test_subentry_reconfigure_add_entity(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|
Loading…
Reference in New Issue