Add transition and flash feature flags for MQTT JSON light (#142692)

This commit is contained in:
Jan Bouwhuis 2025-04-13 21:39:40 +02:00 committed by GitHub
parent 0b02b43b11
commit 7cf63d1985
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 140 additions and 16 deletions

View File

@ -56,6 +56,7 @@ ABBREVIATIONS = {
"ent_pic": "entity_picture",
"evt_typ": "event_types",
"fanspd_lst": "fan_speed_list",
"flsh": "flash",
"flsh_tlng": "flash_time_long",
"flsh_tsht": "flash_time_short",
"fx_cmd_tpl": "effect_command_template",
@ -253,6 +254,7 @@ ABBREVIATIONS = {
"tilt_status_tpl": "tilt_status_template",
"tit": "title",
"t": "topic",
"trns": "transition",
"uniq_id": "unique_id",
"unit_of_meas": "unit_of_measurement",
"url_t": "url_topic",

View File

@ -87,6 +87,7 @@ CONF_EFFECT_TEMPLATE = "effect_template"
CONF_EFFECT_VALUE_TEMPLATE = "effect_value_template"
CONF_ENTITY_PICTURE = "entity_picture"
CONF_EXPIRE_AFTER = "expire_after"
CONF_FLASH = "flash"
CONF_FLASH_TIME_LONG = "flash_time_long"
CONF_FLASH_TIME_SHORT = "flash_time_short"
CONF_GREEN_TEMPLATE = "green_template"
@ -139,6 +140,7 @@ CONF_TEMP_STATE_TOPIC = "temperature_state_topic"
CONF_TEMP_INITIAL = "initial"
CONF_TEMP_MAX = "max_temp"
CONF_TEMP_MIN = "min_temp"
CONF_TRANSITION = "transition"
CONF_XY_COMMAND_TEMPLATE = "xy_command_template"
CONF_XY_COMMAND_TOPIC = "xy_command_topic"
CONF_XY_STATE_TOPIC = "xy_state_topic"

View File

@ -59,6 +59,7 @@ from ..const import (
CONF_COLOR_TEMP_KELVIN,
CONF_COMMAND_TOPIC,
CONF_EFFECT_LIST,
CONF_FLASH,
CONF_FLASH_TIME_LONG,
CONF_FLASH_TIME_SHORT,
CONF_MAX_KELVIN,
@ -69,6 +70,7 @@ from ..const import (
CONF_RETAIN,
CONF_STATE_TOPIC,
CONF_SUPPORTED_COLOR_MODES,
CONF_TRANSITION,
DEFAULT_BRIGHTNESS,
DEFAULT_BRIGHTNESS_SCALE,
DEFAULT_EFFECT,
@ -93,6 +95,9 @@ DOMAIN = "mqtt_json"
DEFAULT_NAME = "MQTT JSON Light"
DEFAULT_FLASH = True
DEFAULT_TRANSITION = True
_PLATFORM_SCHEMA_BASE = (
MQTT_RW_SCHEMA.extend(
{
@ -103,6 +108,7 @@ _PLATFORM_SCHEMA_BASE = (
vol.Optional(CONF_COLOR_TEMP_KELVIN, default=False): cv.boolean,
vol.Optional(CONF_EFFECT, default=DEFAULT_EFFECT): cv.boolean,
vol.Optional(CONF_EFFECT_LIST): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_FLASH, default=DEFAULT_FLASH): cv.boolean,
vol.Optional(
CONF_FLASH_TIME_LONG, default=DEFAULT_FLASH_TIME_LONG
): cv.positive_int,
@ -125,6 +131,7 @@ _PLATFORM_SCHEMA_BASE = (
vol.Unique(),
valid_supported_color_modes,
),
vol.Optional(CONF_TRANSITION, default=DEFAULT_TRANSITION): cv.boolean,
vol.Optional(CONF_WHITE_SCALE, default=DEFAULT_WHITE_SCALE): vol.All(
vol.Coerce(int), vol.Range(min=1)
),
@ -199,12 +206,13 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
for key in (CONF_FLASH_TIME_SHORT, CONF_FLASH_TIME_LONG)
}
self._attr_supported_features = (
LightEntityFeature.TRANSITION | LightEntityFeature.FLASH
)
self._attr_supported_features |= (
config[CONF_EFFECT] and LightEntityFeature.EFFECT
)
self._attr_supported_features |= config[CONF_FLASH] and LightEntityFeature.FLASH
self._attr_supported_features |= (
config[CONF_TRANSITION] and LightEntityFeature.TRANSITION
)
if supported_color_modes := self._config.get(CONF_SUPPORTED_COLOR_MODES):
self._attr_supported_color_modes = supported_color_modes
if self.supported_color_modes and len(self.supported_color_modes) == 1:

View File

@ -330,7 +330,9 @@ async def test_no_color_brightness_color_temp_if_no_topics(
state = hass.states.get("light.test")
assert state.state == STATE_UNKNOWN
expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
expected_features = (
light.LightEntityFeature.FLASH | light.LightEntityFeature.TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get("rgb_color") is None
assert state.attributes.get("brightness") is None
@ -581,6 +583,104 @@ async def test_controlling_state_color_temp_kelvin(
assert state.attributes.get("hs_color") == (44.098, 2.43) # temp converted to color
@pytest.mark.parametrize(
("hass_config", "expected_features"),
[
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
"command_topic": "test_light_rgb/set",
}
}
},
light.LightEntityFeature.FLASH | light.LightEntityFeature.TRANSITION,
),
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
"command_topic": "test_light_rgb/set",
"flash": True,
"transition": True,
}
}
},
light.LightEntityFeature.FLASH | light.LightEntityFeature.TRANSITION,
),
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
"command_topic": "test_light_rgb/set",
"flash": True,
"transition": False,
}
}
},
light.LightEntityFeature.FLASH,
),
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
"command_topic": "test_light_rgb/set",
"flash": False,
"transition": True,
}
}
},
light.LightEntityFeature.TRANSITION,
),
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
"command_topic": "test_light_rgb/set",
"flash": False,
"transition": False,
}
}
},
light.LightEntityFeature(0),
),
],
ids=[
"default",
"explicit_on",
"flash_only",
"transition_only",
"no_flash_not_transition",
],
)
async def test_flash_and_transition_feature_flags(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
expected_features: light.LightEntityFeature,
) -> None:
"""Test for no RGB, brightness, color temp, effector XY."""
await mqtt_mock_entry()
state = hass.states.get("light.test")
assert state.state == STATE_UNKNOWN
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) is expected_features
@pytest.mark.parametrize(
"hass_config",
[
@ -601,9 +701,11 @@ async def test_controlling_state_via_topic(
state = hass.states.get("light.test")
assert state.state == STATE_UNKNOWN
expected_features = (
light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
light.LightEntityFeature.EFFECT
| light.LightEntityFeature.FLASH
| light.LightEntityFeature.TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) is expected_features
assert state.attributes.get("brightness") is None
assert state.attributes.get("color_mode") is None
assert state.attributes.get("color_temp_kelvin") is None
@ -799,9 +901,11 @@ async def test_sending_mqtt_commands_and_optimistic(
state = hass.states.get("light.test")
assert state.state == STATE_ON
expected_features = (
light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
light.LightEntityFeature.EFFECT
| light.LightEntityFeature.FLASH
| light.LightEntityFeature.TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) is expected_features
assert state.attributes.get("brightness") == 95
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("color_temp_kelvin") is None
@ -1457,9 +1561,11 @@ async def test_effect(
state = hass.states.get("light.test")
assert state.state == STATE_UNKNOWN
expected_features = (
light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
light.LightEntityFeature.EFFECT
| light.LightEntityFeature.FLASH
| light.LightEntityFeature.TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) is expected_features
await common.async_turn_on(hass, "light.test")
@ -1523,8 +1629,10 @@ async def test_flash_short_and_long(
state = hass.states.get("light.test")
assert state.state == STATE_UNKNOWN
expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
expected_features = (
light.LightEntityFeature.FLASH | light.LightEntityFeature.TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) is expected_features
await common.async_turn_on(hass, "light.test", flash="short")
@ -1586,8 +1694,10 @@ async def test_transition(
state = hass.states.get("light.test")
assert state.state == STATE_UNKNOWN
expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
expected_features = (
light.LightEntityFeature.FLASH | light.LightEntityFeature.TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) is expected_features
await common.async_turn_on(hass, "light.test", transition=15)
mqtt_mock.async_publish.assert_called_once_with(
@ -1766,8 +1876,10 @@ async def test_invalid_values(
assert state.state == STATE_UNKNOWN
color_modes = [light.ColorMode.COLOR_TEMP, light.ColorMode.HS]
assert state.attributes.get(light.ATTR_SUPPORTED_COLOR_MODES) == color_modes
expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
expected_features = (
light.LightEntityFeature.FLASH | light.LightEntityFeature.TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) is expected_features
assert state.attributes.get("rgb_color") is None
assert state.attributes.get("brightness") is None
assert state.attributes.get("color_temp_kelvin") is None