diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 62105459b89..7fb4455d546 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -150,6 +150,7 @@ from .const import ( CONF_ACTION_TOPIC, CONF_AVAILABILITY_TEMPLATE, CONF_AVAILABILITY_TOPIC, + CONF_AVAILABLE_TONES, CONF_BIRTH_MESSAGE, CONF_BLUE_TEMPLATE, CONF_BRIGHTNESS_COMMAND_TEMPLATE, @@ -307,6 +308,8 @@ from .const import ( CONF_STATE_VALUE_TEMPLATE, CONF_STEP, CONF_SUGGESTED_DISPLAY_PRECISION, + CONF_SUPPORT_DURATION, + CONF_SUPPORT_VOLUME_SET, CONF_SUPPORTED_COLOR_MODES, CONF_SUPPORTED_FEATURES, CONF_SWING_HORIZONTAL_MODE_COMMAND_TEMPLATE, @@ -460,6 +463,7 @@ SUBENTRY_PLATFORMS = [ Platform.NUMBER, Platform.SELECT, Platform.SENSOR, + Platform.SIREN, Platform.SWITCH, ] @@ -1163,6 +1167,7 @@ ENTITY_CONFIG_VALIDATOR: dict[ Platform.NOTIFY.value: None, Platform.NUMBER.value: validate_number_platform_config, Platform.SELECT: None, + Platform.SIREN: None, Platform.SENSOR.value: validate_sensor_platform_config, Platform.SWITCH.value: None, } @@ -1419,6 +1424,7 @@ PLATFORM_ENTITY_FIELDS: dict[str, dict[str, PlatformField]] = { default=None, ), }, + Platform.SIREN: {}, Platform.SWITCH.value: { CONF_DEVICE_CLASS: PlatformField( selector=SWITCH_DEVICE_CLASS_SELECTOR, required=False @@ -3181,6 +3187,71 @@ PLATFORM_MQTT_FIELDS: dict[str, dict[str, PlatformField]] = { 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: { CONF_COMMAND_TOPIC: PlatformField( selector=TEXT_SELECTOR, diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index c77264b91d6..80fbc905946 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -30,6 +30,7 @@ CONF_AVAILABILITY = "availability" CONF_AVAILABILITY_MODE = "availability_mode" CONF_AVAILABILITY_TEMPLATE = "availability_template" CONF_AVAILABILITY_TOPIC = "availability_topic" +CONF_AVAILABLE_TONES = "available_tones" CONF_BROKER = "broker" CONF_BIRTH_MESSAGE = "birth_message" CONF_CODE_ARM_REQUIRED = "code_arm_required" @@ -200,6 +201,8 @@ CONF_STATE_UNLOCKED = "state_unlocked" CONF_STATE_UNLOCKING = "state_unlocking" CONF_STEP = "step" 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_SWING_HORIZONTAL_MODE_COMMAND_TEMPLATE = "swing_horizontal_mode_command_template" CONF_SWING_HORIZONTAL_MODE_COMMAND_TOPIC = "swing_horizontal_mode_command_topic" diff --git a/homeassistant/components/mqtt/siren.py b/homeassistant/components/mqtt/siren.py index 48ab4676dea..b4102b054a5 100644 --- a/homeassistant/components/mqtt/siren.py +++ b/homeassistant/components/mqtt/siren.py @@ -39,10 +39,18 @@ from homeassistant.util.json import JSON_DECODE_EXCEPTIONS, json_loads_object from . import subscription from .config import MQTT_RW_SCHEMA from .const import ( + CONF_AVAILABLE_TONES, + CONF_COMMAND_OFF_TEMPLATE, CONF_COMMAND_TEMPLATE, CONF_COMMAND_TOPIC, + CONF_STATE_OFF, + CONF_STATE_ON, CONF_STATE_TOPIC, CONF_STATE_VALUE_TEMPLATE, + CONF_SUPPORT_DURATION, + CONF_SUPPORT_VOLUME_SET, + DEFAULT_PAYLOAD_OFF, + DEFAULT_PAYLOAD_ON, PAYLOAD_EMPTY_JSON, PAYLOAD_NONE, ) @@ -58,18 +66,9 @@ from .schemas import MQTT_ENTITY_COMMON_SCHEMA PARALLEL_UPDATES = 0 DEFAULT_NAME = "MQTT Siren" -DEFAULT_PAYLOAD_ON = "ON" -DEFAULT_PAYLOAD_OFF = "OFF" 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" PLATFORM_SCHEMA_MODERN = MQTT_RW_SCHEMA.extend( diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index 438d64c48df..a6bd64cb873 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -318,6 +318,7 @@ "title": "Configure MQTT device \"{mqtt_device}\"", "description": "Please configure MQTT specific details for {platform} entity \"{entity}\":", "data": { + "available_tones": "Available Tones", "blue_template": "Blue template", "brightness_template": "Brightness template", "code": "Alarm code", @@ -360,12 +361,15 @@ "state_topic": "State topic", "state_value_template": "State value template", "step": "Step", + "support_duration": "Duration support", + "support_volume_set": "Set volume support", "supported_color_modes": "Supported color modes", "url_template": "URL template", "url_topic": "URL topic", "value_template": "Value template" }, "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.", "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)", @@ -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_topic": "The MQTT topic subscribed to receive {platform} state values. [Learn more.]({url}#state_topic)", "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)", "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)", @@ -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)" } }, + "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": { "name": "Target temperature settings", "data": { @@ -1338,6 +1353,7 @@ "number": "[%key:component::number::title%]", "select": "[%key:component::select::title%]", "sensor": "[%key:component::sensor::title%]", + "siren": "[%key:component::siren::title%]", "switch": "[%key:component::switch::title%]" } }, diff --git a/tests/components/mqtt/common.py b/tests/components/mqtt/common.py index 0451ef25d14..5b02453e44e 100644 --- a/tests/components/mqtt/common.py +++ b/tests/components/mqtt/common.py @@ -565,6 +565,25 @@ MOCK_SUBENTRY_SENSOR_COMPONENT_LAST_RESET = { "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 = { "3faf1318016c46c5aea26707eeb6f12e": { "platform": "switch", @@ -698,6 +717,10 @@ MOCK_SENSOR_SUBENTRY_DATA_LAST_RESET_TEMPLATE = { "device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}}, "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 = { "device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}}, "components": MOCK_SUBENTRY_SWITCH_COMPONENT, diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index 6185e2ffae1..03d669c2d38 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -60,6 +60,7 @@ from .common import ( MOCK_SENSOR_SUBENTRY_DATA, MOCK_SENSOR_SUBENTRY_DATA_LAST_RESET_TEMPLATE, MOCK_SENSOR_SUBENTRY_DATA_STATE_CLASS, + MOCK_SIREN_SUBENTRY_DATA, MOCK_SWITCH_SUBENTRY_DATA, ) @@ -3655,6 +3656,41 @@ async def test_migrate_of_incompatible_config_entry( "Milk notifier Energy", 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( MOCK_SWITCH_SUBENTRY_DATA, {"name": "Milk notifier", "mqtt_settings": {"qos": 0}},