Add light as entity platform on MQTT subentries

This commit is contained in:
jbouwh 2025-03-27 13:03:33 +00:00
parent bcd296822d
commit 8b940a9a25
6 changed files with 1083 additions and 108 deletions

View File

@ -27,6 +27,12 @@ import voluptuous as vol
from homeassistant.components.file_upload import process_uploaded_file
from homeassistant.components.hassio import AddonError, AddonManager, AddonState
from homeassistant.components.light import (
DEFAULT_MAX_KELVIN,
DEFAULT_MIN_KELVIN,
VALID_COLOR_MODES,
valid_supported_color_modes,
)
from homeassistant.components.sensor import (
CONF_STATE_CLASS,
DEVICE_CLASS_UNITS,
@ -50,18 +56,23 @@ from homeassistant.const import (
ATTR_MODEL_ID,
ATTR_NAME,
ATTR_SW_VERSION,
CONF_BRIGHTNESS,
CONF_CLIENT_ID,
CONF_DEVICE,
CONF_DEVICE_CLASS,
CONF_DISCOVERY,
CONF_EFFECT,
CONF_HOST,
CONF_NAME,
CONF_OPTIMISTIC,
CONF_PASSWORD,
CONF_PAYLOAD,
CONF_PAYLOAD_OFF,
CONF_PAYLOAD_ON,
CONF_PLATFORM,
CONF_PORT,
CONF_PROTOCOL,
CONF_STATE_TEMPLATE,
CONF_UNIT_OF_MEASUREMENT,
CONF_USERNAME,
CONF_VALUE_TEMPLATE,
@ -102,37 +113,97 @@ from .const import (
CONF_AVAILABILITY_TEMPLATE,
CONF_AVAILABILITY_TOPIC,
CONF_BIRTH_MESSAGE,
CONF_BLUE_TEMPLATE,
CONF_BRIGHTNESS_COMMAND_TEMPLATE,
CONF_BRIGHTNESS_COMMAND_TOPIC,
CONF_BRIGHTNESS_SCALE,
CONF_BRIGHTNESS_STATE_TOPIC,
CONF_BRIGHTNESS_TEMPLATE,
CONF_BRIGHTNESS_VALUE_TEMPLATE,
CONF_BROKER,
CONF_CERTIFICATE,
CONF_CLIENT_CERT,
CONF_CLIENT_KEY,
CONF_COLOR_MODE_STATE_TOPIC,
CONF_COLOR_MODE_VALUE_TEMPLATE,
CONF_COLOR_TEMP_COMMAND_TEMPLATE,
CONF_COLOR_TEMP_COMMAND_TOPIC,
CONF_COLOR_TEMP_KELVIN,
CONF_COLOR_TEMP_STATE_TOPIC,
CONF_COLOR_TEMP_TEMPLATE,
CONF_COLOR_TEMP_VALUE_TEMPLATE,
CONF_COMMAND_OFF_TEMPLATE,
CONF_COMMAND_ON_TEMPLATE,
CONF_COMMAND_TEMPLATE,
CONF_COMMAND_TOPIC,
CONF_DISCOVERY_PREFIX,
CONF_EFFECT_COMMAND_TEMPLATE,
CONF_EFFECT_COMMAND_TOPIC,
CONF_EFFECT_LIST,
CONF_EFFECT_STATE_TOPIC,
CONF_EFFECT_TEMPLATE,
CONF_EFFECT_VALUE_TEMPLATE,
CONF_ENTITY_PICTURE,
CONF_EXPIRE_AFTER,
CONF_FLASH_TIME_LONG,
CONF_FLASH_TIME_SHORT,
CONF_GREEN_TEMPLATE,
CONF_HS_COMMAND_TEMPLATE,
CONF_HS_COMMAND_TOPIC,
CONF_HS_STATE_TOPIC,
CONF_HS_VALUE_TEMPLATE,
CONF_KEEPALIVE,
CONF_LAST_RESET_VALUE_TEMPLATE,
CONF_MAX_KELVIN,
CONF_MAX_MIREDS,
CONF_MIN_KELVIN,
CONF_MIN_MIREDS,
CONF_ON_COMMAND_TYPE,
CONF_OPTIONS,
CONF_PAYLOAD_AVAILABLE,
CONF_PAYLOAD_NOT_AVAILABLE,
CONF_QOS,
CONF_RED_TEMPLATE,
CONF_RETAIN,
CONF_RGB_COMMAND_TEMPLATE,
CONF_RGB_COMMAND_TOPIC,
CONF_RGB_STATE_TOPIC,
CONF_RGB_VALUE_TEMPLATE,
CONF_RGBW_COMMAND_TEMPLATE,
CONF_RGBW_COMMAND_TOPIC,
CONF_RGBW_STATE_TOPIC,
CONF_RGBW_VALUE_TEMPLATE,
CONF_RGBWW_COMMAND_TEMPLATE,
CONF_RGBWW_COMMAND_TOPIC,
CONF_RGBWW_STATE_TOPIC,
CONF_RGBWW_VALUE_TEMPLATE,
CONF_SCHEMA,
CONF_STATE_TOPIC,
CONF_STATE_VALUE_TEMPLATE,
CONF_SUGGESTED_DISPLAY_PRECISION,
CONF_SUPPORTED_COLOR_MODES,
CONF_TLS_INSECURE,
CONF_TRANSPORT,
CONF_WHITE_COMMAND_TOPIC,
CONF_WHITE_SCALE,
CONF_WILL_MESSAGE,
CONF_WS_HEADERS,
CONF_WS_PATH,
CONF_XY_COMMAND_TEMPLATE,
CONF_XY_COMMAND_TOPIC,
CONF_XY_STATE_TOPIC,
CONF_XY_VALUE_TEMPLATE,
CONFIG_ENTRY_MINOR_VERSION,
CONFIG_ENTRY_VERSION,
DEFAULT_BIRTH,
DEFAULT_DISCOVERY,
DEFAULT_ENCODING,
DEFAULT_KEEPALIVE,
DEFAULT_ON_COMMAND_TYPE,
DEFAULT_PAYLOAD_AVAILABLE,
DEFAULT_PAYLOAD_NOT_AVAILABLE,
DEFAULT_PAYLOAD_OFF,
DEFAULT_PAYLOAD_ON,
DEFAULT_PORT,
DEFAULT_PREFIX,
DEFAULT_PROTOCOL,
@ -144,6 +215,7 @@ from .const import (
SUPPORTED_PROTOCOLS,
TRANSPORT_TCP,
TRANSPORT_WEBSOCKETS,
VALUES_ON_COMMAND_TYPE,
Platform,
)
from .models import MqttAvailabilityData, MqttDeviceData, MqttSubentryData
@ -164,6 +236,9 @@ ADDON_SETUP_TIMEOUT_ROUNDS = 5
CONF_CLIENT_KEY_PASSWORD = "client_key_password"
DEFAULT_MAX_MIREDS = 500
DEFAULT_MIN_MIREDS = 154
MQTT_TIMEOUT = 5
ADVANCED_OPTIONS = "advanced_options"
@ -233,7 +308,7 @@ KEY_UPLOAD_SELECTOR = FileSelector(
)
# Subentry selectors
SUBENTRY_PLATFORMS = [Platform.NOTIFY, Platform.SENSOR, Platform.SWITCH]
SUBENTRY_PLATFORMS = [Platform.LIGHT, Platform.NOTIFY, Platform.SENSOR, Platform.SWITCH]
SUBENTRY_PLATFORM_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=[platform.value for platform in SUBENTRY_PLATFORMS],
@ -295,6 +370,63 @@ SWITCH_DEVICE_CLASS_SELECTOR = SelectSelector(
)
)
# Light specific selectors
LIGHT_SCHEMA_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=["basic", "json", "template"],
translation_key="light_schema",
)
)
KELVIN_SELECTOR = NumberSelector(
NumberSelectorConfig(
mode=NumberSelectorMode.BOX,
min=1000,
max=10000,
step="any",
unit_of_measurement="K",
)
)
MIRED_SELECTOR = NumberSelector(
NumberSelectorConfig(
mode=NumberSelectorMode.BOX,
min=100,
max=1000,
step="any",
unit_of_measurement="mireds",
)
)
SCALE_SELECTOR = NumberSelector(
NumberSelectorConfig(
mode=NumberSelectorMode.BOX,
min=1,
max=255,
step=1,
)
)
FLASH_TIME_SELECTOR = NumberSelector(
NumberSelectorConfig(
mode=NumberSelectorMode.BOX,
min=1,
)
)
ON_COMMAND_TYPE_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=VALUES_ON_COMMAND_TYPE,
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_ON_COMMAND_TYPE,
sort=True,
)
)
SUPPORTED_COLOR_MODES_SELECTOR = SelectSelector(
SelectSelectorConfig(
options=[platform.value for platform in VALID_COLOR_MODES],
mode=SelectSelectorMode.DROPDOWN,
translation_key=CONF_SUPPORTED_COLOR_MODES,
multiple=True,
sort=True,
)
)
@callback
def validate_sensor_platform_config(
@ -370,6 +502,24 @@ def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
)
@callback
def validate_light_platform_config(user_data: dict[str, Any]) -> dict[str, str]:
"""Validate MQTT light configuration."""
errors: dict[str, Any] = {}
if user_data[CONF_COLOR_TEMP_KELVIN]:
if user_data.get(CONF_MIN_KELVIN, DEFAULT_MIN_KELVIN) >= user_data.get(
CONF_MAX_KELVIN, DEFAULT_MAX_KELVIN
):
errors[CONF_MAX_KELVIN] = "max_below_min_kelvin"
errors[CONF_MIN_KELVIN] = "max_below_min_kelvin"
elif user_data.get(CONF_MIN_MIREDS, DEFAULT_MIN_MIREDS) >= user_data.get(
CONF_MAX_MIREDS, DEFAULT_MAX_MIREDS
):
errors[CONF_MAX_MIREDS] = "max_below_min_mireds"
errors[CONF_MIN_MIREDS] = "max_below_min_mireds"
return errors
COMMON_ENTITY_FIELDS = {
CONF_PLATFORM: PlatformField(
selector=SUBENTRY_PLATFORM_SELECTOR,
@ -421,6 +571,22 @@ PLATFORM_ENTITY_FIELDS = {
selector=SWITCH_DEVICE_CLASS_SELECTOR, required=False, validator=str
),
},
Platform.LIGHT.value: {
CONF_SCHEMA: PlatformField(
selector=LIGHT_SCHEMA_SELECTOR,
required=True,
validator=str,
default="basic",
exclude_from_reconfig=True,
),
CONF_COLOR_TEMP_KELVIN: PlatformField(
selector=BOOLEAN_SELECTOR,
required=True,
validator=bool,
default=True,
exclude_from_reconfig=True,
),
},
}
PLATFORM_MQTT_FIELDS = {
Platform.NOTIFY.value: {
@ -499,11 +665,504 @@ PLATFORM_MQTT_FIELDS = {
selector=BOOLEAN_SELECTOR, required=False, validator=bool
),
},
Platform.LIGHT.value: {
CONF_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=True,
validator=valid_publish_topic,
error="invalid_publish_topic",
),
CONF_COMMAND_ON_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=True,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
),
CONF_COMMAND_OFF_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=True,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
),
CONF_ON_COMMAND_TYPE: PlatformField(
selector=ON_COMMAND_TYPE_SELECTOR,
required=False,
validator=str,
default=DEFAULT_ON_COMMAND_TYPE,
conditions=({CONF_SCHEMA: "basic"},),
),
CONF_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
),
CONF_STATE_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
),
CONF_STATE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
),
CONF_SUPPORTED_COLOR_MODES: PlatformField(
selector=SUPPORTED_COLOR_MODES_SELECTOR,
required=False,
validator=valid_supported_color_modes,
error="invalid_supported_color_modes",
conditions=({CONF_SCHEMA: "json"},),
),
CONF_OPTIMISTIC: PlatformField(
selector=BOOLEAN_SELECTOR, required=False, validator=bool
),
CONF_RETAIN: PlatformField(
selector=BOOLEAN_SELECTOR,
required=False,
validator=bool,
conditions=({CONF_SCHEMA: "basic"},),
),
CONF_BRIGHTNESS: PlatformField(
selector=BOOLEAN_SELECTOR,
required=False,
validator=bool,
conditions=({CONF_SCHEMA: "json"},),
section="light_brightness_settings",
),
CONF_BRIGHTNESS_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_brightness_settings",
),
CONF_BRIGHTNESS_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_brightness_settings",
),
CONF_BRIGHTNESS_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_brightness_settings",
),
CONF_PAYLOAD_OFF: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=str,
default=DEFAULT_PAYLOAD_OFF,
conditions=({CONF_SCHEMA: "basic"},),
),
CONF_PAYLOAD_ON: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=str,
default=DEFAULT_PAYLOAD_ON,
conditions=({CONF_SCHEMA: "basic"},),
),
CONF_BRIGHTNESS_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_brightness_settings",
),
CONF_BRIGHTNESS_SCALE: PlatformField(
selector=SCALE_SELECTOR,
required=False,
validator=cv.positive_int,
default=255,
conditions=(
{CONF_SCHEMA: "basic"},
{CONF_SCHEMA: "json"},
),
section="light_brightness_settings",
),
CONF_COLOR_MODE_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_color_mode_settings",
),
CONF_COLOR_MODE_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_color_mode_settings",
),
CONF_COLOR_TEMP_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_color_temp_settings",
),
CONF_COLOR_TEMP_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_color_temp_settings",
),
CONF_COLOR_TEMP_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_color_temp_settings",
),
CONF_COLOR_TEMP_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_color_temp_settings",
),
CONF_BRIGHTNESS_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
),
CONF_RED_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
),
CONF_GREEN_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
),
CONF_BLUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
),
CONF_COLOR_TEMP_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
),
CONF_HS_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_hs_settings",
),
CONF_HS_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_hs_settings",
),
CONF_HS_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_hs_settings",
),
CONF_HS_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_hs_settings",
),
CONF_RGB_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgb_settings",
),
CONF_RGB_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgb_settings",
),
CONF_RGB_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgb_settings",
),
CONF_RGB_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgb_settings",
),
CONF_RGBW_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgbw_settings",
),
CONF_RGBW_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgbw_settings",
),
CONF_RGBW_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgbw_settings",
),
CONF_RGBW_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgbw_settings",
),
CONF_RGBWW_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgbww_settings",
),
CONF_RGBWW_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgbww_settings",
),
CONF_RGBWW_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgbww_settings",
),
CONF_RGBWW_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_rgbww_settings",
),
CONF_XY_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_xy_settings",
),
CONF_XY_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_xy_settings",
),
CONF_XY_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_xy_settings",
),
CONF_XY_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_xy_settings",
),
CONF_WHITE_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_white_settings",
),
CONF_WHITE_SCALE: PlatformField(
selector=SCALE_SELECTOR,
required=False,
validator=cv.positive_int,
default=255,
conditions=(
{CONF_SCHEMA: "basic"},
{CONF_SCHEMA: "json"},
),
section="light_white_settings",
),
CONF_EFFECT: PlatformField(
selector=BOOLEAN_SELECTOR,
required=False,
validator=bool,
conditions=({CONF_SCHEMA: "json"},),
section="light_effect_settings",
),
CONF_EFFECT_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_publish_topic,
error="invalid_publish_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_effect_settings",
),
CONF_EFFECT_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_effect_settings",
),
CONF_EFFECT_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
conditions=({CONF_SCHEMA: "basic"},),
section="light_effect_settings",
),
CONF_EFFECT_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "template"},),
section="light_effect_settings",
),
CONF_EFFECT_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=cv.template,
error="invalid_template",
conditions=({CONF_SCHEMA: "basic"},),
section="light_effect_settings",
),
CONF_EFFECT_LIST: PlatformField(
selector=OPTIONS_SELECTOR,
required=False,
validator=cv.ensure_list,
section="light_effect_settings",
),
CONF_MAX_KELVIN: PlatformField(
selector=KELVIN_SELECTOR,
required=False,
validator=cv.positive_int,
default=DEFAULT_MAX_KELVIN,
conditions=({CONF_COLOR_TEMP_KELVIN: True},),
section="advanced_settings",
),
CONF_MIN_KELVIN: PlatformField(
selector=KELVIN_SELECTOR,
required=False,
validator=cv.positive_int,
default=DEFAULT_MIN_KELVIN,
conditions=({CONF_COLOR_TEMP_KELVIN: True},),
section="advanced_settings",
),
CONF_MAX_MIREDS: PlatformField(
selector=MIRED_SELECTOR,
required=False,
validator=cv.positive_int,
default=DEFAULT_MAX_MIREDS,
conditions=({CONF_COLOR_TEMP_KELVIN: False},),
section="advanced_settings",
),
CONF_MIN_MIREDS: PlatformField(
selector=MIRED_SELECTOR,
required=False,
validator=cv.positive_int,
default=DEFAULT_MIN_MIREDS,
conditions=({CONF_COLOR_TEMP_KELVIN: False},),
section="advanced_settings",
),
CONF_FLASH_TIME_SHORT: PlatformField(
selector=FLASH_TIME_SELECTOR,
required=False,
validator=cv.positive_int,
default=2,
conditions=({CONF_SCHEMA: "json"},),
section="advanced_settings",
),
CONF_FLASH_TIME_LONG: PlatformField(
selector=FLASH_TIME_SELECTOR,
required=False,
validator=cv.positive_int,
default=10,
conditions=({CONF_SCHEMA: "json"},),
section="advanced_settings",
),
},
}
ENTITY_CONFIG_VALIDATOR: dict[
str,
Callable[[dict[str, Any]], dict[str, str]] | None,
] = {
Platform.LIGHT.value: validate_light_platform_config,
Platform.NOTIFY.value: None,
Platform.SENSOR.value: validate_sensor_platform_config,
Platform.SWITCH.value: None,
@ -576,7 +1235,7 @@ def validate_field(
return
try:
validator(user_input[field])
except (ValueError, vol.Invalid):
except (ValueError, vol.Error, vol.Invalid):
errors[field] = error
@ -634,7 +1293,7 @@ def validate_user_input(
validator = data_schema_fields[field].validator
try:
validator(value)
except (ValueError, vol.Invalid):
except (ValueError, vol.Error, vol.Invalid):
errors[field] = data_schema_fields[field].error or "invalid_input"
if config_validator is not None:
@ -699,6 +1358,8 @@ def data_schema_from_fields(
if field_details.section == schema_section
and field_details.exclude_from_reconfig
}
if not data_element_options:
continue
if schema_section is None:
data_schema.update(data_schema_element)
continue

View File

@ -194,6 +194,8 @@ DEFAULT_POSITION_OPEN = 100
DEFAULT_RETAIN = False
DEFAULT_WHITE_SCALE = 255
VALUES_ON_COMMAND_TYPE = ["first", "last", "brightness"]
PROTOCOL_31 = "3.1"
PROTOCOL_311 = "3.1.1"
PROTOCOL_5 = "5"

View File

@ -104,6 +104,7 @@ from ..const import (
DEFAULT_PAYLOAD_ON,
DEFAULT_WHITE_SCALE,
PAYLOAD_NONE,
VALUES_ON_COMMAND_TYPE,
)
from ..entity import MqttEntity
from ..models import (
@ -143,8 +144,6 @@ MQTT_LIGHT_ATTRIBUTES_BLOCKED = frozenset(
}
)
VALUES_ON_COMMAND_TYPE = ["first", "last", "brightness"]
COMMAND_TEMPLATE_KEYS = [
CONF_BRIGHTNESS_COMMAND_TEMPLATE,
CONF_COLOR_TEMP_COMMAND_TEMPLATE,

View File

@ -214,15 +214,21 @@
"description": "Please configure specific details for {platform} entity \"{entity}\":",
"data": {
"device_class": "Device class",
"color_temp_kelvin": "Color temperature in Kelvin",
"options": "Add option",
"schema": "Schema",
"state_class": "State class",
"unit_of_measurement": "Unit of measurement",
"options": "Add option"
"suggested_display_precision": "Suggested display precision",
"unit_of_measurement": "Unit of measurement"
},
"data_description": {
"device_class": "The Device class of the {platform} entity. [Learn more.]({url}#device_class)",
"color_temp_kelvin": "When set color mode updates will be published in Kelvin and processed state will be assumed in Kelvin. When not set the color temp values are converted to mireds instead.",
"options": "Options for allowed sensor state values. The sensors Device class must be set to Enumeration. The 'Options' setting cannot be used together with State class or Unit of measurement.",
"schema": "The schema to use. [Learn more.]({url}#comparison-of-light-mqtt-schemas)",
"state_class": "The [State class](https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes) of the sensor. [Learn more.]({url}#state_class)",
"unit_of_measurement": "Defines the unit of measurement of the sensor, if any.",
"options": "Options for allowed sensor state values. The sensors Device class must be set to Enumeration. The 'Options' setting cannot be used together with State class or Unit of measurement."
"suggested_display_precision": "The number of decimals which should be used in the {platform} entity state after rounding. [Learn more.]({url}#suggested_display_precision)",
"unit_of_measurement": "Defines the unit of measurement of the sensor, if any."
},
"sections": {
"advanced_settings": {
@ -240,33 +246,222 @@
"title": "Configure MQTT device \"{mqtt_device}\"",
"description": "Please configure MQTT specific details for {platform} entity \"{entity}\":",
"data": {
"command_topic": "Command topic",
"on_command_type": "ON command type",
"blue_template": "Blue template",
"brightness_template": "Brightness template",
"command_template": "Command template",
"state_topic": "State topic",
"value_template": "Value template",
"last_reset_value_template": "Last reset value template",
"command_topic": "Command topic",
"command_off_template": "Command \"off\" template",
"command_on_template": "Command \"on\" template",
"color_temp_template": "Color temperature template",
"force_update": "Force update",
"green_template": "Green template",
"last_reset_value_template": "Last reset value template",
"optimistic": "Optimistic",
"retain": "Retain"
"payload_off": "Payload off",
"payload_on": "Payload on",
"qos": "QoS",
"red_template": "Red template",
"retain": "Retain",
"state_template": "State template",
"state_topic": "State topic",
"state_value_template": "State value template",
"supported_color_modes": "Supported color modes",
"value_template": "Value template"
},
"data_description": {
"command_topic": "The publishing topic that will be used to control the {platform} entity. [Learn more.]({url}#command_topic)",
"blue_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract blue color from the state payload value. Expected result of the template is an integer from 0-255 range.",
"brightness_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract brightness from the state payload value. Expected result of the template is an integer from 0-255 range.",
"command_off_template": "The [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) for \"off\" state changes. Available variables are: `state` and `transition`.",
"command_on_template": "The [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) for \"on\" state changes. Available variables: `state`, `brightness`, `color_temp`, `red`, `green`, `blue`, `hue`, `sat`, `flash`, `transition` and `effect`. Values `red`, `green`, `blue` and `brightness` are provided as integers from range 0-255. Value of `hue` is provided as float from range 0-360. Value of `sat` is provided as float from range 0-100. Value of `color_temp` is provided as integer representing mired or Kelvin units if 'Color temperature in Kelvin' is true.",
"command_template": "A [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to render the payload to be published at the command topic.",
"state_topic": "The MQTT topic subscribed to receive {platform} state values. [Learn more.]({url}#state_topic)",
"value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the {platform} entity value.",
"command_topic": "The publishing topic that will be used to control the {platform} entity. [Learn more.]({url}#command_topic)",
"color_temp_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract color temperature from the state payload value. Expected result of the template is an integer. If the color tempemperature in Kelvin setting is true, the expected value is in Kelvin, else mireds are expected.",
"green_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract green color from the state payload value. Expected result of the template is an integer from 0-255 range.",
"last_reset_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the last reset. When Last reset template is set, the State class option must be Total. [Learn more.]({url}#last_reset_value_template)",
"force_update": "Sends update events even if the value hasnt changed. Useful if you want to have meaningful value graphs in history. [Learn more.]({url}#force_update)",
"on_command_type": "Defines when on the `payload on` is sent. Using `last` (the default) will send any style (brightness, color, etc) topics first and then a `payload on` to the command_topic. Using `first` will send the `payload on` and then any style topics. Using `brightness` will only send brightness commands instead of the `Payload on` to turn the light on.",
"optimistic": "Flag that defines if the {platform} entity works in optimistic mode. [Learn more.]({url}#optimistic)",
"retain": "Select if values published by the {platform} entity should be retained at the MQTT broker."
"payload_off": "The payload that represents the off state.",
"payload_on": "The payload that represents the on state.",
"qos": "The QoS value a {platform} entity should use.",
"red_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract red color from the state payload value. Expected result of the template is an integer from 0-255 range.",
"retain": "Select if values published by the {platform} entity should be retained at the MQTT broker.",
"state_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract state from the state payload value.",
"state_topic": "The MQTT topic subscribed to receive {platform} state values. [Learn more.]({url}#state_topic)",
"supported_color_modes": "A list of color modes supported by the list. Possible color modes are On/Off, Brightness, Color temperature, HS, XY, RGB, RGBW, RGBWW, WHITE. Note that if onoff or brightness are used, that must be the only value in the list. [Learn more.]({url}#supported_color_modes)",
"value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the {platform} entity value. [Learn more.]({url}#value_template)"
},
"sections": {
"advanced_settings": {
"name": "Advanced settings",
"data": {
"expire_after": "Expire after"
"expire_after": "Expire after",
"flash_time_long": "Flash time long",
"flash_time_short": "Flash time short",
"max_kelvin": "Max Kelvin",
"min_kelvin": "Min Kelvin",
"max_mireds": "Max mireds",
"min_mireds": "Min mireds"
},
"data_description": {
"expire_after": "If set, it defines the number of seconds after the sensors state expires, if its not updated. After expiry, the sensors state becomes unavailable. If not set, the sensor's state never expires. [Learn more.]({url}#expire_after)"
"expire_after": "If set, it defines the number of seconds after the sensors state expires, if its not updated. After expiry, the sensors state becomes unavailable. If not set, the sensor's state never expires. [Learn more.]({url}#expire_after)",
"flash_time_long": "The duration, in seconds, of a \"long\" flash.",
"flash_time_short": "The duration, in seconds, of a \"short\" flash.",
"max_kelvin": "The maximum color temperature in Kelvin.",
"min_kelvin": "The minimum color temperature in Kelvin.",
"max_mireds": "The maximum color temperature in mireds.",
"min_mireds": "The minimum color temperature in mireds."
}
},
"light_brightness_settings": {
"name": "Brightness settings",
"data": {
"brightness": "Brightness",
"brightness_command_template": "Brightness command template",
"brightness_command_topic": "Brightness command topic",
"brightness_scale": "Brightness scale",
"brightness_state_topic": "Brightness state topic",
"brightness_value_template": "Brightness value template"
},
"data_description": {
"brightness": "Flag that defines if light supports brightness when the RGB, RGBW, or RGBWW color mode is supported.",
"brightness_command_template": "A [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to compose the payload to be published at the brightness command topic.",
"brightness_command_topic": "The publishing topic that will be used to control the brigthness. [Learn more.]({url}#brightness_command_topic)",
"brightness_scale": "Defines the maximum brightness value (i.e., 100%) of the maximum brightness.",
"brightness_state_topic": "The MQTT topic subscribed to receive brightness state values. [Learn more.]({url}#brightness_state_topic)",
"brightness_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the brightness value."
}
},
"light_color_mode_settings": {
"name": "Color mode settings",
"data": {
"color_mode_state_topic": "Color mode state topic",
"color_mode_value_template": "Color mode value template"
},
"data_description": {
"color_mode_state_topic": "The MQTT topic subscribed to receive color mode updates. If this is not configured, the color mode will be automatically set according to the last received valid color or color temperature.",
"color_mode_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the color mode value."
}
},
"light_color_temp_settings": {
"name": "Color temperature settings",
"data": {
"color_temp_command_template": "Color temperature command template",
"color_temp_command_topic": "Color temperature command topic",
"color_temp_state_topic": "Color temperature state topic",
"color_temp_value_template": "Color temperature value template"
},
"data_description": {
"color_temp_command_template": "A [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to compose the payload to be published at the color temperature command topic.",
"color_temp_command_topic": "The publishing topic that will be used to control the color temperature. [Learn more.]({url}#color_temp_command_topic)",
"color_temp_state_topic": "The MQTT topic subscribed to receive color temperature state updates. [Learn more.]({url}#color_temp_state_topic)",
"color_temp_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the color temperature value."
}
},
"light_effect_settings": {
"name": "Effect settings",
"data": {
"effect": "Effect",
"effect_command_template": "Effect command template",
"effect_command_topic": "Effect command topic",
"effect_list": "Effect list",
"effect_state_topic": "Effect state topic",
"effect_template": "Effect template",
"effect_value_template": "Effect value template"
},
"data_description": {
"effect": "Flag that defines if the light supports effects.",
"effect_command_template": "A [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to compose the payload to be published at the effect command topic.",
"effect_command_topic": "The publishing topic that will be used to control the light's effect state. [Learn more.]({url}#effect_command_topic)",
"effect_list": "The list of effects the light supports.",
"effect_state_topic": "The MQTT topic subscribed to receive effect state updates. [Learn more.]({url}#effect_state_topic)"
}
},
"light_hs_settings": {
"name": "HS color mode settings",
"data": {
"hs_command_template": "HS command template",
"hs_command_topic": "HS command topic",
"hs_state_topic": "HS state topic",
"hs_value_template": "HS value template"
},
"data_description": {
"hs_command_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to compose message which will be sent to hs_command_topic. Available variables: `hue` and `sat`.",
"hs_command_topic": "The MQTT topic to publish commands to change the lights color state in HS format (Hue Saturation). Range for Hue: 0° .. 360°, Range of Saturation: 0..100. Note: Brightness is sent separately in the brightness command topic. [Learn more.]({url}#hs_command_topic)",
"hs_state_topic": "The MQTT topic subscribed to receive color state updates in HS format. The expected payload is the hue and saturation values separated by commas, for example, `359.5,100.0`. Note: Brightness is received separately in the brightness state topic. [Learn more.]({url}#hs_state_topic)",
"hs_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the HS value."
}
},
"light_rgb_settings": {
"name": "RGB color mode settings",
"data": {
"rgb_command_template": "RGB command template",
"rgb_command_topic": "RGB command topic",
"rgb_state_topic": "RGB state topic",
"rgb_value_template": "RGB value template"
},
"data_description": {
"rgb_command_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to compose message which will be sent to RGB command topic. Available variables: `red`, `green` and `blue`.",
"rgb_command_topic": "The MQTT topic to publish commands to change the lights RGB state. [Learn more.]({url}#rgb_command_topic)",
"rgb_state_topic": "The MQTT topic subscribed to receive RGB state updates. The expected payload is the RGB values separated by commas, for example, `255,0,127`. [Learn more.]({url}rgb_state_topic)",
"rgb_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the RGB value."
}
},
"light_rgbw_settings": {
"name": "RGBW color mode settings",
"data": {
"rgbw_command_template": "RGBW command template",
"rgbw_command_topic": "RGBW command topic",
"rgbw_state_topic": "RGBW state topic",
"rgbw_value_template": "RGBW value template"
},
"data_description": {
"rgbw_command_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to compose message which will be sent to RGBW command topic. Available variables: `red`, `green`, `blue` and `white`.",
"rgbw_command_topic": "The MQTT topic to publish commands to change the lights RGBW state. [Learn more.]({url}#rgbw_command_topic)",
"rgbw_state_topic": "The MQTT topic subscribed to receive RGBW state updates. The expected payload is the RGBW values separated by commas, for example, `255,0,127,64`. [Learn more.]({url}#rgbw_state_topic)",
"rgbw_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the RGBW value."
}
},
"light_rgbww_settings": {
"name": "RGBWW color mode settings",
"data": {
"rgbww_command_template": "RGBWW command template",
"rgbww_command_topic": "RGBWW command topic",
"rgbww_state_topic": "RGBWW state topic",
"rgbww_value_template": "RGBWW value template"
},
"data_description": {
"rgbww_command_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to compose message which will be sent to RGBWW command topic. Available variables: `red`, `green`, `blue`, `cold_white` and `warm_white`.",
"rgbww_command_topic": "The MQTT topic to publish commands to change the lights RGBWW state. [Learn more.]({url}#rgbww_command_topic)",
"rgbww_state_topic": "The MQTT topic subscribed to receive RGBWW state updates. The expected payload is the RGBWW values separated by commas, for example, `255,0,127,64,32`. [Learn more.]({url}#rgbww_state_topic)",
"rgbww_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the RGBWW value."
}
},
"light_white_settings": {
"name": "White color mode settings",
"data": {
"white_command_topic": "White command topic",
"white_scale": "White scale"
},
"data_description": {
"white_command_topic": "The MQTT topic to publish commands to change the light to white mode with a given brightness. [Learn more.]({url}#white_command_topic)",
"white_scale": "Defines the maximum white level (i.e., 100%) of the maximum."
}
},
"light_xy_settings": {
"name": "XY color mode settings",
"data": {
"xy_command_template": "XY command template",
"xy_command_topic": "XY command topic",
"xy_state_topic": "XY state topic",
"xy_value_template": "XY value template"
},
"data_description": {
"xy_command_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to compose message which will be sent to XY command topic. Available variables: `x` and `y`.",
"xy_command_topic": "The MQTT topic to publish commands to change the lights XY state. [Learn more.]({url}#xy_command_topic)",
"xy_state_topic": "The MQTT topic subscribed to receive XY state updates. The expected payload is the X and Y color values separated by commas, for example, `0.675,0.322`. [Learn more.]({url}#xy_state_topic)",
"xy_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the XY value."
}
}
}
@ -282,8 +477,12 @@
"invalid_input": "Invalid value",
"invalid_subscribe_topic": "Invalid subscribe topic",
"invalid_template": "Invalid template",
"invalid_supported_color_modes": "Invalid supported color modes selection",
"invalid_uom": "The unit of measurement \"{unit_of_measurement}\" is not supported by the selected device class, please either remove the device class, select a device class which supports \"{unit_of_measurement}\", or pick a supported unit of measurement from the list",
"invalid_url": "Invalid URL",
"last_reset_not_with_state_class_total": "The last reset value template option should be used with state class 'Total' only",
"max_below_min_kelvin": "Max Kelvin value should be greater than min Kelvin value",
"max_below_min_mireds": "Max mireds value should be greater than min mireds value",
"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_with_enum_device_class": "Configure options for the enumeration sensor",
@ -470,8 +669,23 @@
"switch": "[%key:component::switch::title%]"
}
},
"light_schema": {
"options": {
"basic": "Default schema",
"json": "JSON",
"template": "Template"
}
},
"on_command_type": {
"options": {
"brightness": "Brightness",
"first": "First",
"last": "Last"
}
},
"platform": {
"options": {
"light": "Light",
"notify": "Notify",
"sensor": "Sensor",
"switch": "Switch"
@ -490,6 +704,19 @@
"total": "[%key:component::sensor::entity_component::_::state_attributes::state_class::state::total%]",
"total_increasing": "[%key:component::sensor::entity_component::_::state_attributes::state_class::state::total_increasing%]"
}
},
"supported_color_modes": {
"options": {
"onoff": "On/Off",
"brightness": "Brightness",
"color_temp": "Color temperature",
"hs": "HS",
"xy": "XY",
"rgb": "RGB",
"rgbw": "RGBW",
"rgbww": "RGBWW",
"white": "White"
}
}
},
"services": {

View File

@ -139,15 +139,35 @@ MOCK_SUBENTRY_SWITCH_COMPONENT = {
},
}
# Bogus light component just for code coverage
# Note that light cannot be setup through the UI yet
# The test is for code coverage
MOCK_SUBENTRY_LIGHT_COMPONENT = {
MOCK_SUBENTRY_LIGHT_BASIC_KELVIN_COMPONENT = {
"8131babc5e8d4f44b82e0761d39091a2": {
"platform": "light",
"name": "Test light",
"command_topic": "test-topic4",
"name": "Basic light",
"on_command_type": "last",
"optimistic": True,
"payload_off": "OFF",
"payload_on": "ON",
"command_topic": "test-topic",
"schema": "basic",
"state_topic": "test-topic",
"color_temp_kelvin": True,
"state_value_template": "{{ value_json.value }}",
"entity_picture": "https://example.com/8131babc5e8d4f44b82e0761d39091a2",
},
}
MOCK_SUBENTRY_LIGHT_BASIC_MIREDS_COMPONENT = {
"8131babc5e8d4f44b82e0761d39091a2": {
"platform": "light",
"name": "Basic light",
"on_command_type": "last",
"optimistic": True,
"payload_off": "OFF",
"payload_on": "ON",
"command_topic": "test-topic",
"schema": "basic",
"state_topic": "test-topic",
"color_temp_kelvin": False,
"state_value_template": "{{ value_json.value }}",
"entity_picture": "https://example.com/8131babc5e8d4f44b82e0761d39091a2",
},
}
@ -168,108 +188,61 @@ MOCK_SUBENTRY_AVAILABILITY_DATA = {
}
}
MOCK_NOTIFY_SUBENTRY_DATA_MULTI = {
"device": {
MOCK_SUBENTRY_DEVICE_DATA = {
"name": "Milk notifier",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
},
}
MOCK_NOTIFY_SUBENTRY_DATA_MULTI = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT1 | MOCK_SUBENTRY_NOTIFY_COMPONENT2,
} | MOCK_SUBENTRY_AVAILABILITY_DATA
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE = {
"device": {
"name": "Milk notifier",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
"mqtt_settings": {"qos": 1},
},
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 1}},
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT1,
}
MOCK_NOTIFY_SUBENTRY_DATA_NO_NAME = {
"device": {
"name": "Milk notifier",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
},
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT_NO_NAME,
}
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_LIGHT_BASIC_KELVIN_COMPONENT,
}
MOCK_LIGHT_BASIC_MIREDS_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_LIGHT_BASIC_MIREDS_COMPONENT,
}
MOCK_SENSOR_SUBENTRY_DATA_SINGLE = {
"device": {
"name": "Test sensor",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
},
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_SENSOR_COMPONENT,
}
MOCK_SENSOR_SUBENTRY_DATA_SINGLE_STATE_CLASS = {
"device": {
"name": "Test sensor",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
},
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_SENSOR_COMPONENT_STATE_CLASS,
}
MOCK_SENSOR_SUBENTRY_DATA_SINGLE_LAST_RESET_TEMPLATE = {
"device": {
"name": "Test sensor",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
},
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_SENSOR_COMPONENT_LAST_RESET,
}
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS = {
"device": {
"name": "Test switch",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
},
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_SWITCH_COMPONENT,
}
MOCK_SUBENTRY_DATA_BAD_COMPONENT_SCHEMA = {
"device": {
"name": "Milk notifier",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
},
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_NOTIFY_BAD_SCHEMA,
}
MOCK_SUBENTRY_DATA_SET_MIX = {
"device": {
"name": "Milk notifier",
"sw_version": "1.0",
"hw_version": "2.1 rev a",
"model": "Model XL",
"model_id": "mn002",
"configuration_url": "https://example.com",
},
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_NOTIFY_COMPONENT1
| MOCK_SUBENTRY_NOTIFY_COMPONENT2
| MOCK_SUBENTRY_LIGHT_COMPONENT
| MOCK_SUBENTRY_LIGHT_BASIC_KELVIN_COMPONENT
| MOCK_SUBENTRY_SWITCH_COMPONENT,
} | MOCK_SUBENTRY_AVAILABILITY_DATA
_SENTINEL = object()

View File

@ -33,6 +33,8 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .common import (
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
MOCK_LIGHT_BASIC_MIREDS_SUBENTRY_DATA_SINGLE,
MOCK_NOTIFY_SUBENTRY_DATA_MULTI,
MOCK_NOTIFY_SUBENTRY_DATA_NO_NAME,
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE,
@ -2666,7 +2668,7 @@ async def test_migrate_of_incompatible_config_entry(
),
(
MOCK_SENSOR_SUBENTRY_DATA_SINGLE,
{"name": "Test sensor", "mqtt_settings": {"qos": 0}},
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
{"name": "Energy"},
{"device_class": "enum", "options": ["low", "medium", "high"]},
(
@ -2718,11 +2720,11 @@ async def test_migrate_of_incompatible_config_entry(
{"state_topic": "invalid_subscribe_topic"},
),
),
"Test sensor Energy",
"Milk notifier Energy",
),
(
MOCK_SENSOR_SUBENTRY_DATA_SINGLE_STATE_CLASS,
{"name": "Test sensor", "mqtt_settings": {"qos": 0}},
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
{"name": "Energy"},
{
"state_class": "measurement",
@ -2732,11 +2734,11 @@ async def test_migrate_of_incompatible_config_entry(
"state_topic": "test-topic",
},
(),
"Test sensor Energy",
"Milk notifier Energy",
),
(
MOCK_SWITCH_SUBENTRY_DATA_SINGLE_STATE_CLASS,
{"name": "Test switch", "mqtt_settings": {"qos": 0}},
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
{"name": "Outlet"},
{"device_class": "outlet"},
(),
@ -2760,7 +2762,81 @@ async def test_migrate_of_incompatible_config_entry(
{"state_topic": "invalid_subscribe_topic"},
),
),
"Test switch Outlet",
"Milk notifier Outlet",
),
(
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
{"name": "Basic light"},
{"color_temp_kelvin": True},
{},
{
"command_topic": "test-topic",
"state_topic": "test-topic",
"state_value_template": "{{ value_json.value }}",
"optimistic": True,
},
(
(
{"command_topic": "test-topic#invalid"},
{"command_topic": "invalid_publish_topic"},
),
(
{
"command_topic": "test-topic",
"state_topic": "test-topic#invalid",
},
{"state_topic": "invalid_subscribe_topic"},
),
(
{
"command_topic": "test-topic",
"advanced_settings": {"max_kelvin": 2000, "min_kelvin": 2000},
},
{
"max_kelvin": "max_below_min_kelvin",
"min_kelvin": "max_below_min_kelvin",
},
),
),
"Milk notifier Basic light",
),
(
MOCK_LIGHT_BASIC_MIREDS_SUBENTRY_DATA_SINGLE,
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
{"name": "Basic light"},
{"color_temp_kelvin": False},
{},
{
"command_topic": "test-topic",
"state_topic": "test-topic",
"state_value_template": "{{ value_json.value }}",
"optimistic": True,
},
(
(
{"command_topic": "test-topic#invalid"},
{"command_topic": "invalid_publish_topic"},
),
(
{
"command_topic": "test-topic",
"state_topic": "test-topic#invalid",
},
{"state_topic": "invalid_subscribe_topic"},
),
(
{
"command_topic": "test-topic",
"advanced_settings": {"min_mireds": 200, "max_mireds": 190},
},
{
"max_mireds": "max_below_min_mireds",
"min_mireds": "max_below_min_mireds",
},
),
),
"Milk notifier Basic light",
),
],
ids=[
@ -2769,6 +2845,8 @@ async def test_migrate_of_incompatible_config_entry(
"sensor_options",
"sensor_total",
"switch",
"light_basic_kelvin",
"light_basic_mireds",
],
)
async def test_subentry_configflow(
@ -3169,6 +3247,7 @@ async def test_subentry_reconfigure_edit_entity_multi_entitites(
"user_input_platform_config_validation",
"user_input_platform_config",
"user_input_mqtt",
"component_data",
"removed_options",
),
[
@ -3187,6 +3266,11 @@ async def test_subentry_reconfigure_edit_entity_multi_entitites(
"command_template": "{{ value }}",
"retain": True,
},
{
"command_topic": "test-topic1-updated",
"command_template": "{{ value }}",
"retain": True,
},
{"entity_picture"},
),
(
@ -3223,10 +3307,38 @@ async def test_subentry_reconfigure_edit_entity_multi_entitites(
"state_topic": "test-topic1-updated",
"value_template": "{{ value_json.value }}",
},
{
"state_topic": "test-topic1-updated",
"value_template": "{{ value_json.value }}",
},
{"options", "expire_after", "entity_picture"},
),
(
(
ConfigSubentryData(
data=MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
subentry_type="device",
title="Mock subentry",
),
),
None,
None,
{
"command_topic": "test-topic1-updated",
"state_topic": "test-topic1-updated",
"light_brightness_settings": {
"brightness_command_template": "{{ value_json.value }}"
},
},
{
"command_topic": "test-topic1-updated",
"state_topic": "test-topic1-updated",
"brightness_command_template": "{{ value_json.value }}",
},
{"optimistic", "state_value_template", "entity_picture"},
),
],
ids=["notify", "sensor"],
ids=["notify", "sensor", "light_basic"],
)
async def test_subentry_reconfigure_edit_entity_single_entity(
hass: HomeAssistant,
@ -3239,6 +3351,7 @@ async def test_subentry_reconfigure_edit_entity_single_entity(
| None,
user_input_platform_config: dict[str, Any] | None,
user_input_mqtt: dict[str, Any],
component_data: dict[str, Any],
removed_options: tuple[str, ...],
) -> None:
"""Test the subentry ConfigFlow reconfigure with single entity."""
@ -3343,7 +3456,7 @@ async def test_subentry_reconfigure_edit_entity_single_entity(
assert "entity_picture" not in new_components[component_id]
# Check the second component was updated
for key, value in user_input_mqtt.items():
for key, value in component_data.items():
assert new_components[component_id][key] == value
assert set(component) - set(new_components[component_id]) == removed_options