diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index c98dbbd270a..ddbced5286d 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -16,6 +16,7 @@ ABBREVIATIONS = { "away_mode_stat_tpl": "away_mode_state_template", "away_mode_stat_t": "away_mode_state_topic", "b_tpl": "blue_template", + "bri_cmd_tpl": "brightness_command_template", "bri_cmd_t": "brightness_command_topic", "bri_scl": "brightness_scale", "bri_stat_t": "brightness_state_topic", @@ -58,6 +59,7 @@ ABBREVIATIONS = { "fanspd_lst": "fan_speed_list", "flsh_tlng": "flash_time_long", "flsh_tsht": "flash_time_short", + "fx_cmd_tpl": "effect_command_template", "fx_cmd_t": "effect_command_topic", "fx_list": "effect_list", "fx_stat_t": "effect_state_topic", diff --git a/homeassistant/components/mqtt/light/schema_basic.py b/homeassistant/components/mqtt/light/schema_basic.py index d5665a1beff..09b23029b0e 100644 --- a/homeassistant/components/mqtt/light/schema_basic.py +++ b/homeassistant/components/mqtt/light/schema_basic.py @@ -68,6 +68,7 @@ from .schema import MQTT_LIGHT_SCHEMA_SCHEMA _LOGGER = logging.getLogger(__name__) +CONF_BRIGHTNESS_COMMAND_TEMPLATE = "brightness_command_template" CONF_BRIGHTNESS_COMMAND_TOPIC = "brightness_command_topic" CONF_BRIGHTNESS_SCALE = "brightness_scale" CONF_BRIGHTNESS_STATE_TOPIC = "brightness_state_topic" @@ -78,6 +79,7 @@ CONF_COLOR_TEMP_COMMAND_TEMPLATE = "color_temp_command_template" CONF_COLOR_TEMP_COMMAND_TOPIC = "color_temp_command_topic" CONF_COLOR_TEMP_STATE_TOPIC = "color_temp_state_topic" CONF_COLOR_TEMP_VALUE_TEMPLATE = "color_temp_value_template" +CONF_EFFECT_COMMAND_TEMPLATE = "effect_command_template" CONF_EFFECT_COMMAND_TOPIC = "effect_command_topic" CONF_EFFECT_LIST = "effect_list" CONF_EFFECT_STATE_TOPIC = "effect_state_topic" @@ -141,7 +143,9 @@ DEFAULT_ON_COMMAND_TYPE = "last" VALUES_ON_COMMAND_TYPE = ["first", "last", "brightness"] COMMAND_TEMPLATE_KEYS = [ + CONF_BRIGHTNESS_COMMAND_TEMPLATE, CONF_COLOR_TEMP_COMMAND_TEMPLATE, + CONF_EFFECT_COMMAND_TEMPLATE, CONF_RGB_COMMAND_TEMPLATE, CONF_RGBW_COMMAND_TEMPLATE, CONF_RGBWW_COMMAND_TEMPLATE, @@ -163,6 +167,7 @@ VALUE_TEMPLATE_KEYS = [ _PLATFORM_SCHEMA_BASE = ( mqtt.MQTT_RW_PLATFORM_SCHEMA.extend( { + vol.Optional(CONF_BRIGHTNESS_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_BRIGHTNESS_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional( CONF_BRIGHTNESS_SCALE, default=DEFAULT_BRIGHTNESS_SCALE @@ -175,6 +180,7 @@ _PLATFORM_SCHEMA_BASE = ( vol.Optional(CONF_COLOR_TEMP_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_COLOR_TEMP_STATE_TOPIC): mqtt.valid_subscribe_topic, vol.Optional(CONF_COLOR_TEMP_VALUE_TEMPLATE): cv.template, + vol.Optional(CONF_EFFECT_COMMAND_TEMPLATE): cv.template, vol.Optional(CONF_EFFECT_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_EFFECT_STATE_TOPIC): mqtt.valid_subscribe_topic, @@ -970,6 +976,8 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): ) # Make sure the brightness is not rounded down to 0 device_brightness = max(device_brightness, 1) + if tpl := self._command_templates[CONF_BRIGHTNESS_COMMAND_TEMPLATE]: + device_brightness = tpl(variables={"value": device_brightness}) await publish(CONF_BRIGHTNESS_COMMAND_TOPIC, device_brightness) should_update |= set_optimistic(ATTR_BRIGHTNESS, kwargs[ATTR_BRIGHTNESS]) elif ( @@ -1038,8 +1046,10 @@ class MqttLight(MqttEntity, LightEntity, RestoreEntity): if ATTR_EFFECT in kwargs and self._topic[CONF_EFFECT_COMMAND_TOPIC] is not None: effect = kwargs[ATTR_EFFECT] if effect in self._config.get(CONF_EFFECT_LIST): + if tpl := self._command_templates[CONF_EFFECT_COMMAND_TEMPLATE]: + effect = tpl(variables={"value": effect}) await publish(CONF_EFFECT_COMMAND_TOPIC, effect) - should_update |= set_optimistic(ATTR_EFFECT, effect) + should_update |= set_optimistic(ATTR_EFFECT, kwargs[ATTR_EFFECT]) if ATTR_WHITE in kwargs and self._topic[CONF_WHITE_COMMAND_TOPIC] is not None: percent_white = float(kwargs[ATTR_WHITE]) / 255 diff --git a/tests/components/mqtt/test_light.py b/tests/components/mqtt/test_light.py index 782ee40f0c8..d6125729191 100644 --- a/tests/components/mqtt/test_light.py +++ b/tests/components/mqtt/test_light.py @@ -152,6 +152,37 @@ light: payload_on: "on" payload_off: "off" +Configuration with brightness command template: + +light: + platform: mqtt + name: "Office Light" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + brightness_state_topic: "office/rgb1/brightness/status" + brightness_command_topic: "office/rgb1/brightness/set" + brightness_command_template: '{ "brightness": "{{ value }}" }' + qos: 0 + payload_on: "on" + payload_off: "off" + +Configuration with effect command template: + +light: + platform: mqtt + name: "Office Light Color Temp" + state_topic: "office/rgb1/light/status" + command_topic: "office/rgb1/light/switch" + effect_state_topic: "office/rgb1/effect/status" + effect_command_topic: "office/rgb1/effect/set" + effect_command_template: '{ "effect": "{{ value }}" }' + effect_list: + - rainbow + - colorloop + qos: 0 + payload_on: "on" + payload_off: "off" + """ import copy from unittest.mock import call, patch @@ -3394,18 +3425,18 @@ async def test_max_mireds(hass, mqtt_mock): "brightness_command_topic", {"color_temp": "200", "brightness": "50"}, 50, - None, - None, - None, + "brightness_command_template", + "value", + b"5", ), ( light.SERVICE_TURN_ON, "effect_command_topic", {"rgb_color": [255, 128, 0], "effect": "color_loop"}, "color_loop", - None, - None, - None, + "effect_command_template", + "value", + b"c", ), ( light.SERVICE_TURN_ON, @@ -3565,3 +3596,81 @@ async def test_encoding_subscribable_topics( attribute_value, init_payload, ) + + +async def test_sending_mqtt_brightness_command_with_template(hass, mqtt_mock): + """Test the sending of Brightness command with template.""" + config = { + light.DOMAIN: { + "platform": "mqtt", + "name": "test", + "command_topic": "test_light_brightness/set", + "brightness_command_topic": "test_light_brightness/brightness/set", + "brightness_command_template": "{{ (1000 / value) | round(0) }}", + "payload_on": "on", + "payload_off": "off", + "qos": 0, + } + } + + assert await async_setup_component(hass, light.DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("light.test") + assert state.state == STATE_OFF + + await common.async_turn_on(hass, "light.test", brightness=100) + + mqtt_mock.async_publish.assert_has_calls( + [ + call("test_light_brightness/set", "on", 0, False), + call("test_light_brightness/brightness/set", "10", 0, False), + ], + any_order=True, + ) + + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes["brightness"] == 100 + + +async def test_sending_mqtt_effect_command_with_template(hass, mqtt_mock): + """Test the sending of Effect command with template.""" + config = { + light.DOMAIN: { + "platform": "mqtt", + "name": "test", + "command_topic": "test_light_brightness/set", + "brightness_command_topic": "test_light_brightness/brightness/set", + "effect_command_topic": "test_light_brightness/effect/set", + "effect_command_template": '{ "effect": "{{ value }}" }', + "effect_list": ["colorloop", "random"], + "payload_on": "on", + "payload_off": "off", + "qos": 0, + } + } + + assert await async_setup_component(hass, light.DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("light.test") + assert state.state == STATE_OFF + + await common.async_turn_on(hass, "light.test", effect="colorloop") + + mqtt_mock.async_publish.assert_has_calls( + [ + call("test_light_brightness/set", "on", 0, False), + call( + "test_light_brightness/effect/set", + '{ "effect": "colorloop" }', + 0, + False, + ), + ], + any_order=True, + ) + state = hass.states.get("light.test") + assert state.state == STATE_ON + assert state.attributes.get("effect") == "colorloop"