Add subentry support for MQTT siren device (#154220)

This commit is contained in:
Jan Bouwhuis
2025-10-14 16:45:48 +02:00
committed by GitHub
parent 28405e2b04
commit 8b6fb05ee4
6 changed files with 157 additions and 9 deletions

View File

@@ -150,6 +150,7 @@ from .const import (
CONF_ACTION_TOPIC, CONF_ACTION_TOPIC,
CONF_AVAILABILITY_TEMPLATE, CONF_AVAILABILITY_TEMPLATE,
CONF_AVAILABILITY_TOPIC, CONF_AVAILABILITY_TOPIC,
CONF_AVAILABLE_TONES,
CONF_BIRTH_MESSAGE, CONF_BIRTH_MESSAGE,
CONF_BLUE_TEMPLATE, CONF_BLUE_TEMPLATE,
CONF_BRIGHTNESS_COMMAND_TEMPLATE, CONF_BRIGHTNESS_COMMAND_TEMPLATE,
@@ -307,6 +308,8 @@ from .const import (
CONF_STATE_VALUE_TEMPLATE, CONF_STATE_VALUE_TEMPLATE,
CONF_STEP, CONF_STEP,
CONF_SUGGESTED_DISPLAY_PRECISION, CONF_SUGGESTED_DISPLAY_PRECISION,
CONF_SUPPORT_DURATION,
CONF_SUPPORT_VOLUME_SET,
CONF_SUPPORTED_COLOR_MODES, CONF_SUPPORTED_COLOR_MODES,
CONF_SUPPORTED_FEATURES, CONF_SUPPORTED_FEATURES,
CONF_SWING_HORIZONTAL_MODE_COMMAND_TEMPLATE, CONF_SWING_HORIZONTAL_MODE_COMMAND_TEMPLATE,
@@ -460,6 +463,7 @@ SUBENTRY_PLATFORMS = [
Platform.NUMBER, Platform.NUMBER,
Platform.SELECT, Platform.SELECT,
Platform.SENSOR, Platform.SENSOR,
Platform.SIREN,
Platform.SWITCH, Platform.SWITCH,
] ]
@@ -1163,6 +1167,7 @@ ENTITY_CONFIG_VALIDATOR: dict[
Platform.NOTIFY.value: None, Platform.NOTIFY.value: None,
Platform.NUMBER.value: validate_number_platform_config, Platform.NUMBER.value: validate_number_platform_config,
Platform.SELECT: None, Platform.SELECT: None,
Platform.SIREN: None,
Platform.SENSOR.value: validate_sensor_platform_config, Platform.SENSOR.value: validate_sensor_platform_config,
Platform.SWITCH.value: None, Platform.SWITCH.value: None,
} }
@@ -1419,6 +1424,7 @@ PLATFORM_ENTITY_FIELDS: dict[str, dict[str, PlatformField]] = {
default=None, default=None,
), ),
}, },
Platform.SIREN: {},
Platform.SWITCH.value: { Platform.SWITCH.value: {
CONF_DEVICE_CLASS: PlatformField( CONF_DEVICE_CLASS: PlatformField(
selector=SWITCH_DEVICE_CLASS_SELECTOR, required=False selector=SWITCH_DEVICE_CLASS_SELECTOR, required=False
@@ -3181,6 +3187,71 @@ PLATFORM_MQTT_FIELDS: dict[str, dict[str, PlatformField]] = {
section="advanced_settings", section="advanced_settings",
), ),
}, },
Platform.SIREN: {
CONF_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=True,
validator=valid_publish_topic,
error="invalid_publish_topic",
),
CONF_COMMAND_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=validate(cv.template),
error="invalid_template",
),
CONF_STATE_TOPIC: PlatformField(
selector=TEXT_SELECTOR,
required=False,
validator=valid_subscribe_topic,
error="invalid_subscribe_topic",
),
CONF_VALUE_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=validate(cv.template),
error="invalid_template",
),
CONF_PAYLOAD_OFF: PlatformField(
selector=TEXT_SELECTOR,
required=False,
default=DEFAULT_PAYLOAD_OFF,
),
CONF_PAYLOAD_ON: PlatformField(
selector=TEXT_SELECTOR,
required=False,
default=DEFAULT_PAYLOAD_ON,
),
CONF_STATE_OFF: PlatformField(
selector=TEXT_SELECTOR,
required=False,
),
CONF_STATE_ON: PlatformField(
selector=TEXT_SELECTOR,
required=False,
),
CONF_AVAILABLE_TONES: PlatformField(
selector=OPTIONS_SELECTOR,
required=False,
),
CONF_SUPPORT_DURATION: PlatformField(
selector=BOOLEAN_SELECTOR,
required=False,
),
CONF_SUPPORT_VOLUME_SET: PlatformField(
selector=BOOLEAN_SELECTOR,
required=False,
),
CONF_RETAIN: PlatformField(selector=BOOLEAN_SELECTOR, required=False),
CONF_OPTIMISTIC: PlatformField(selector=BOOLEAN_SELECTOR, required=False),
CONF_COMMAND_OFF_TEMPLATE: PlatformField(
selector=TEMPLATE_SELECTOR,
required=False,
validator=validate(cv.template),
error="invalid_template",
section="siren_advanced_settings",
),
},
Platform.SWITCH.value: { Platform.SWITCH.value: {
CONF_COMMAND_TOPIC: PlatformField( CONF_COMMAND_TOPIC: PlatformField(
selector=TEXT_SELECTOR, selector=TEXT_SELECTOR,

View File

@@ -30,6 +30,7 @@ CONF_AVAILABILITY = "availability"
CONF_AVAILABILITY_MODE = "availability_mode" CONF_AVAILABILITY_MODE = "availability_mode"
CONF_AVAILABILITY_TEMPLATE = "availability_template" CONF_AVAILABILITY_TEMPLATE = "availability_template"
CONF_AVAILABILITY_TOPIC = "availability_topic" CONF_AVAILABILITY_TOPIC = "availability_topic"
CONF_AVAILABLE_TONES = "available_tones"
CONF_BROKER = "broker" CONF_BROKER = "broker"
CONF_BIRTH_MESSAGE = "birth_message" CONF_BIRTH_MESSAGE = "birth_message"
CONF_CODE_ARM_REQUIRED = "code_arm_required" CONF_CODE_ARM_REQUIRED = "code_arm_required"
@@ -200,6 +201,8 @@ CONF_STATE_UNLOCKED = "state_unlocked"
CONF_STATE_UNLOCKING = "state_unlocking" CONF_STATE_UNLOCKING = "state_unlocking"
CONF_STEP = "step" CONF_STEP = "step"
CONF_SUGGESTED_DISPLAY_PRECISION = "suggested_display_precision" CONF_SUGGESTED_DISPLAY_PRECISION = "suggested_display_precision"
CONF_SUPPORT_DURATION = "support_duration"
CONF_SUPPORT_VOLUME_SET = "support_volume_set"
CONF_SUPPORTED_COLOR_MODES = "supported_color_modes" CONF_SUPPORTED_COLOR_MODES = "supported_color_modes"
CONF_SWING_HORIZONTAL_MODE_COMMAND_TEMPLATE = "swing_horizontal_mode_command_template" CONF_SWING_HORIZONTAL_MODE_COMMAND_TEMPLATE = "swing_horizontal_mode_command_template"
CONF_SWING_HORIZONTAL_MODE_COMMAND_TOPIC = "swing_horizontal_mode_command_topic" CONF_SWING_HORIZONTAL_MODE_COMMAND_TOPIC = "swing_horizontal_mode_command_topic"

View File

@@ -39,10 +39,18 @@ from homeassistant.util.json import JSON_DECODE_EXCEPTIONS, json_loads_object
from . import subscription from . import subscription
from .config import MQTT_RW_SCHEMA from .config import MQTT_RW_SCHEMA
from .const import ( from .const import (
CONF_AVAILABLE_TONES,
CONF_COMMAND_OFF_TEMPLATE,
CONF_COMMAND_TEMPLATE, CONF_COMMAND_TEMPLATE,
CONF_COMMAND_TOPIC, CONF_COMMAND_TOPIC,
CONF_STATE_OFF,
CONF_STATE_ON,
CONF_STATE_TOPIC, CONF_STATE_TOPIC,
CONF_STATE_VALUE_TEMPLATE, CONF_STATE_VALUE_TEMPLATE,
CONF_SUPPORT_DURATION,
CONF_SUPPORT_VOLUME_SET,
DEFAULT_PAYLOAD_OFF,
DEFAULT_PAYLOAD_ON,
PAYLOAD_EMPTY_JSON, PAYLOAD_EMPTY_JSON,
PAYLOAD_NONE, PAYLOAD_NONE,
) )
@@ -58,18 +66,9 @@ from .schemas import MQTT_ENTITY_COMMON_SCHEMA
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0
DEFAULT_NAME = "MQTT Siren" DEFAULT_NAME = "MQTT Siren"
DEFAULT_PAYLOAD_ON = "ON"
DEFAULT_PAYLOAD_OFF = "OFF"
ENTITY_ID_FORMAT = siren.DOMAIN + ".{}" ENTITY_ID_FORMAT = siren.DOMAIN + ".{}"
CONF_AVAILABLE_TONES = "available_tones"
CONF_COMMAND_OFF_TEMPLATE = "command_off_template"
CONF_STATE_ON = "state_on"
CONF_STATE_OFF = "state_off"
CONF_SUPPORT_DURATION = "support_duration"
CONF_SUPPORT_VOLUME_SET = "support_volume_set"
STATE = "state" STATE = "state"
PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend(

View File

@@ -318,6 +318,7 @@
"title": "Configure MQTT device \"{mqtt_device}\"", "title": "Configure MQTT device \"{mqtt_device}\"",
"description": "Please configure MQTT specific details for {platform} entity \"{entity}\":", "description": "Please configure MQTT specific details for {platform} entity \"{entity}\":",
"data": { "data": {
"available_tones": "Available Tones",
"blue_template": "Blue template", "blue_template": "Blue template",
"brightness_template": "Brightness template", "brightness_template": "Brightness template",
"code": "Alarm code", "code": "Alarm code",
@@ -360,12 +361,15 @@
"state_topic": "State topic", "state_topic": "State topic",
"state_value_template": "State value template", "state_value_template": "State value template",
"step": "Step", "step": "Step",
"support_duration": "Duration support",
"support_volume_set": "Set volume support",
"supported_color_modes": "Supported color modes", "supported_color_modes": "Supported color modes",
"url_template": "URL template", "url_template": "URL template",
"url_topic": "URL topic", "url_topic": "URL topic",
"value_template": "Value template" "value_template": "Value template"
}, },
"data_description": { "data_description": {
"available_tones": "The siren supports tones. The `tone` variable will become available for use in the \"Command template\" setting. [Learn more.]({url}#available_tones)",
"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.", "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.", "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.",
"code": "Specifies a code to enable or disable the alarm in the frontend. Note that this blocks sending MQTT message commands to the remote device if the code validation fails. [Learn more.]({url}#code)", "code": "Specifies a code to enable or disable the alarm in the frontend. Note that this blocks sending MQTT message commands to the remote device if the code validation fails. [Learn more.]({url}#code)",
@@ -407,6 +411,8 @@
"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_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)", "state_topic": "The MQTT topic subscribed to receive {platform} state values. [Learn more.]({url}#state_topic)",
"step": "Step value. Smallest value 0.001.", "step": "Step value. Smallest value 0.001.",
"support_duration": "The siren supports setting a duration in second. The `duration` variable will become available for use in the \"Command template\" setting. [Learn more.]({url}#support_duration)",
"support_volume_set": "The siren supports setting a volume. The `tone` variable will become available for use in the \"Command template\" setting. [Learn more.]({url}#support_volume_set)",
"supported_color_modes": "A list of color modes supported by the light. Possible color modes are On/Off, Brightness, Color temperature, HS, XY, RGB, RGBW, RGBWW, White. Note that if On/Off or Brightness are used, that must be the only value in the list. [Learn more.]({url}#supported_color_modes)", "supported_color_modes": "A list of color modes supported by the light. Possible color modes are On/Off, Brightness, Color temperature, HS, XY, RGB, RGBW, RGBWW, White. Note that if On/Off or Brightness are used, that must be the only value in the list. [Learn more.]({url}#supported_color_modes)",
"url_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract an URL from the received URL topic payload value. [Learn more.]({url}#url_template)", "url_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract an URL from the received URL topic payload value. [Learn more.]({url}#url_template)",
"url_topic": "The MQTT topic subscribed to receive messages containing the image URL. [Learn more.]({url}#url_topic)", "url_topic": "The MQTT topic subscribed to receive messages containing the image URL. [Learn more.]({url}#url_topic)",
@@ -910,6 +916,15 @@
"target_humidity_state_topic": "The MQTT topic to subscribe for changes of the target humidity. [Learn more.]({url}#humidity_state_topic)" "target_humidity_state_topic": "The MQTT topic to subscribe for changes of the target humidity. [Learn more.]({url}#humidity_state_topic)"
} }
}, },
"siren_advanced_settings": {
"name": "Advanced siren settings",
"data": {
"command_off_template": "Command \"off\" template"
},
"data_description": {
"command_off_template": "The [template]({templating_url}#using-command-templates-with-mqtt) for \"off\" state changes. By default the \"[Command template]({url}#command_template)\" will be used. [Learn more.]({url}#command_off_template)"
}
},
"target_temperature_settings": { "target_temperature_settings": {
"name": "Target temperature settings", "name": "Target temperature settings",
"data": { "data": {
@@ -1338,6 +1353,7 @@
"number": "[%key:component::number::title%]", "number": "[%key:component::number::title%]",
"select": "[%key:component::select::title%]", "select": "[%key:component::select::title%]",
"sensor": "[%key:component::sensor::title%]", "sensor": "[%key:component::sensor::title%]",
"siren": "[%key:component::siren::title%]",
"switch": "[%key:component::switch::title%]" "switch": "[%key:component::switch::title%]"
} }
}, },

View File

@@ -565,6 +565,25 @@ MOCK_SUBENTRY_SENSOR_COMPONENT_LAST_RESET = {
"entity_picture": "https://example.com/e9261f6feed443e7b7d5f3fbe2a47412", "entity_picture": "https://example.com/e9261f6feed443e7b7d5f3fbe2a47412",
}, },
} }
MOCK_SUBENTRY_SIREN_COMPONENT = {
"3faf1318023c46c5aea26707eeb6f12e": {
"platform": "siren",
"name": "Siren",
"entity_category": None,
"command_topic": "test-topic",
"state_topic": "test-topic",
"command_template": "{{ value }}",
"command_off_template": "{{ value }}",
"value_template": "{{ value_json.value }}",
"payload_off": "OFF",
"payload_on": "ON",
"available_tones": ["Happy hour", "Cooling alarm"],
"support_volume_set": True,
"support_duration": True,
"entity_picture": "https://example.com/3faf1318023c46c5aea26707eeb6f12e",
"optimistic": True,
},
}
MOCK_SUBENTRY_SWITCH_COMPONENT = { MOCK_SUBENTRY_SWITCH_COMPONENT = {
"3faf1318016c46c5aea26707eeb6f12e": { "3faf1318016c46c5aea26707eeb6f12e": {
"platform": "switch", "platform": "switch",
@@ -698,6 +717,10 @@ MOCK_SENSOR_SUBENTRY_DATA_LAST_RESET_TEMPLATE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}}, "device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_SENSOR_COMPONENT_LAST_RESET, "components": MOCK_SUBENTRY_SENSOR_COMPONENT_LAST_RESET,
} }
MOCK_SIREN_SUBENTRY_DATA = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_SIREN_COMPONENT,
}
MOCK_SWITCH_SUBENTRY_DATA = { MOCK_SWITCH_SUBENTRY_DATA = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}}, "device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_SWITCH_COMPONENT, "components": MOCK_SUBENTRY_SWITCH_COMPONENT,

View File

@@ -60,6 +60,7 @@ from .common import (
MOCK_SENSOR_SUBENTRY_DATA, MOCK_SENSOR_SUBENTRY_DATA,
MOCK_SENSOR_SUBENTRY_DATA_LAST_RESET_TEMPLATE, MOCK_SENSOR_SUBENTRY_DATA_LAST_RESET_TEMPLATE,
MOCK_SENSOR_SUBENTRY_DATA_STATE_CLASS, MOCK_SENSOR_SUBENTRY_DATA_STATE_CLASS,
MOCK_SIREN_SUBENTRY_DATA,
MOCK_SWITCH_SUBENTRY_DATA, MOCK_SWITCH_SUBENTRY_DATA,
) )
@@ -3655,6 +3656,41 @@ async def test_migrate_of_incompatible_config_entry(
"Milk notifier Energy", "Milk notifier Energy",
id="sensor_total", id="sensor_total",
), ),
pytest.param(
MOCK_SIREN_SUBENTRY_DATA,
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}},
{"name": "Siren"},
{},
(),
{
"command_topic": "test-topic",
"command_template": "{{ value }}",
"state_topic": "test-topic",
"value_template": "{{ value_json.value }}",
"optimistic": True,
"available_tones": ["Happy hour", "Cooling alarm"],
"support_duration": True,
"support_volume_set": True,
"siren_advanced_settings": {
"command_off_template": "{{ value }}",
},
},
(
(
{"command_topic": "test-topic#invalid"},
{"command_topic": "invalid_publish_topic"},
),
(
{
"command_topic": "test-topic",
"state_topic": "test-topic#invalid",
},
{"state_topic": "invalid_subscribe_topic"},
),
),
"Milk notifier Siren",
id="siren",
),
pytest.param( pytest.param(
MOCK_SWITCH_SUBENTRY_DATA, MOCK_SWITCH_SUBENTRY_DATA,
{"name": "Milk notifier", "mqtt_settings": {"qos": 0}}, {"name": "Milk notifier", "mqtt_settings": {"qos": 0}},