Allow to process kelvin as color_temp for mqtt json light (#133955)

pull/133957/head
Jan Bouwhuis 2025-01-09 18:34:42 +01:00 committed by GitHub
parent 07482de4ab
commit cabdae98e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 235 additions and 42 deletions

View File

@ -56,12 +56,15 @@ CONF_SUPPORTED_FEATURES = "supported_features"
CONF_ACTION_TEMPLATE = "action_template"
CONF_ACTION_TOPIC = "action_topic"
CONF_COLOR_TEMP_KELVIN = "color_temp_kelvin"
CONF_CURRENT_HUMIDITY_TEMPLATE = "current_humidity_template"
CONF_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic"
CONF_CURRENT_TEMP_TEMPLATE = "current_temperature_template"
CONF_CURRENT_TEMP_TOPIC = "current_temperature_topic"
CONF_ENABLED_BY_DEFAULT = "enabled_by_default"
CONF_ENTITY_PICTURE = "entity_picture"
CONF_MAX_KELVIN = "max_kelvin"
CONF_MIN_KELVIN = "min_kelvin"
CONF_MODE_COMMAND_TEMPLATE = "mode_command_template"
CONF_MODE_COMMAND_TOPIC = "mode_command_topic"
CONF_MODE_LIST = "modes"

View File

@ -51,7 +51,10 @@ import homeassistant.util.color as color_util
from .. import subscription
from ..config import MQTT_RW_SCHEMA
from ..const import (
CONF_COLOR_TEMP_KELVIN,
CONF_COMMAND_TOPIC,
CONF_MAX_KELVIN,
CONF_MIN_KELVIN,
CONF_STATE_TOPIC,
CONF_STATE_VALUE_TEMPLATE,
PAYLOAD_NONE,
@ -82,7 +85,6 @@ 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_COLOR_TEMP_KELVIN = "color_temp_kelvin"
CONF_EFFECT_COMMAND_TEMPLATE = "effect_command_template"
CONF_EFFECT_COMMAND_TOPIC = "effect_command_topic"
CONF_EFFECT_LIST = "effect_list"
@ -94,8 +96,6 @@ CONF_HS_STATE_TOPIC = "hs_state_topic"
CONF_HS_VALUE_TEMPLATE = "hs_value_template"
CONF_MAX_MIREDS = "max_mireds"
CONF_MIN_MIREDS = "min_mireds"
CONF_MAX_KELVIN = "max_kelvin"
CONF_MIN_KELVIN = "min_kelvin"
CONF_RGB_COMMAND_TEMPLATE = "rgb_command_template"
CONF_RGB_COMMAND_TOPIC = "rgb_command_topic"
CONF_RGB_STATE_TOPIC = "rgb_state_topic"

View File

@ -61,7 +61,10 @@ from homeassistant.util.yaml import dump as yaml_dump
from .. import subscription
from ..config import DEFAULT_QOS, DEFAULT_RETAIN, MQTT_RW_SCHEMA
from ..const import (
CONF_COLOR_TEMP_KELVIN,
CONF_COMMAND_TOPIC,
CONF_MAX_KELVIN,
CONF_MIN_KELVIN,
CONF_QOS,
CONF_RETAIN,
CONF_STATE_TOPIC,
@ -203,6 +206,7 @@ _PLATFORM_SCHEMA_BASE = (
# CONF_COLOR_TEMP was deprecated with HA Core 2024.4 and will be
# removed with HA Core 2025.3
vol.Optional(CONF_COLOR_TEMP, default=DEFAULT_COLOR_TEMP): cv.boolean,
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(
@ -216,6 +220,8 @@ _PLATFORM_SCHEMA_BASE = (
vol.Optional(CONF_HS, default=DEFAULT_HS): cv.boolean,
vol.Optional(CONF_MAX_MIREDS): cv.positive_int,
vol.Optional(CONF_MIN_MIREDS): cv.positive_int,
vol.Optional(CONF_MAX_KELVIN): cv.positive_int,
vol.Optional(CONF_MIN_KELVIN): cv.positive_int,
vol.Optional(CONF_NAME): vol.Any(cv.string, None),
vol.Optional(CONF_QOS, default=DEFAULT_QOS): vol.All(
vol.Coerce(int), vol.In([0, 1, 2])
@ -275,15 +281,16 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
def _setup_from_config(self, config: ConfigType) -> None:
"""(Re)Setup the entity."""
self._color_temp_kelvin = config[CONF_COLOR_TEMP_KELVIN]
self._attr_min_color_temp_kelvin = (
color_util.color_temperature_mired_to_kelvin(max_mireds)
if (max_mireds := config.get(CONF_MAX_MIREDS))
else DEFAULT_MIN_KELVIN
else config.get(CONF_MIN_KELVIN, DEFAULT_MIN_KELVIN)
)
self._attr_max_color_temp_kelvin = (
color_util.color_temperature_mired_to_kelvin(min_mireds)
if (min_mireds := config.get(CONF_MIN_MIREDS))
else DEFAULT_MAX_KELVIN
else config.get(CONF_MAX_KELVIN, DEFAULT_MAX_KELVIN)
)
self._attr_effect_list = config.get(CONF_EFFECT_LIST)
@ -381,7 +388,9 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
try:
if color_mode == ColorMode.COLOR_TEMP:
self._attr_color_temp_kelvin = (
color_util.color_temperature_mired_to_kelvin(
values["color_temp"]
if self._color_temp_kelvin
else color_util.color_temperature_mired_to_kelvin(
values["color_temp"]
)
)
@ -486,7 +495,9 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
self._attr_color_temp_kelvin = None
else:
self._attr_color_temp_kelvin = (
color_util.color_temperature_mired_to_kelvin(
values["color_temp"] # type: ignore[assignment]
if self._color_temp_kelvin
else color_util.color_temperature_mired_to_kelvin(
values["color_temp"] # type: ignore[arg-type]
)
)
@ -709,10 +720,13 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
should_update = True
if ATTR_COLOR_TEMP_KELVIN in kwargs:
message["color_temp"] = color_util.color_temperature_kelvin_to_mired(
message["color_temp"] = (
kwargs[ATTR_COLOR_TEMP_KELVIN]
if self._color_temp_kelvin
else color_util.color_temperature_kelvin_to_mired(
kwargs[ATTR_COLOR_TEMP_KELVIN]
)
)
if self._optimistic:
self._attr_color_mode = ColorMode.COLOR_TEMP
self._attr_color_temp_kelvin = kwargs[ATTR_COLOR_TEMP_KELVIN]

View File

@ -14,7 +14,7 @@ mqtt:
rgb: true
xy: true
Configuration with RGB, brightness, color temp and effect:
Configuration with RGB, brightness, color temp (mireds) and effect:
mqtt:
light:
@ -24,10 +24,11 @@ mqtt:
command_topic: "home/rgb1/set"
brightness: true
color_temp: true
color_temp_kelvin: false
effect: true
rgb: true
Configuration with RGB, brightness and color temp:
Configuration with RGB, brightness and color temp (Kelvin):
mqtt:
light:
@ -38,6 +39,7 @@ mqtt:
brightness: true
rgb: true
color_temp: true
color_temp_kelvin: true
Configuration with RGB, brightness:
@ -399,24 +401,50 @@ async def test_fail_setup_if_color_modes_invalid(
@pytest.mark.parametrize(
"hass_config",
("hass_config", "kelvin", "color_temp_payload_value"),
[
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"command_topic": "test_light/set",
"state_topic": "test_light",
"color_mode": True,
"supported_color_modes": "color_temp",
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"command_topic": "test_light/set",
"state_topic": "test_light",
"color_mode": True,
"color_temp_kelvin": False,
"supported_color_modes": "color_temp",
}
}
}
}
},
5208,
192,
),
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"command_topic": "test_light/set",
"state_topic": "test_light",
"color_mode": True,
"color_temp_kelvin": True,
"supported_color_modes": "color_temp",
}
}
},
5208,
5208,
),
],
ids=["mireds", "kelvin"],
)
async def test_single_color_mode(
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
kelvin: int,
color_temp_payload_value: int,
) -> None:
"""Test setup with single color_mode."""
await mqtt_mock_entry()
@ -424,13 +452,19 @@ async def test_single_color_mode(
assert state.state == STATE_UNKNOWN
await common.async_turn_on(
hass, "light.test", brightness=50, color_temp_kelvin=5208
hass, "light.test", brightness=50, color_temp_kelvin=kelvin
)
payload = {
"state": "ON",
"brightness": 50,
"color_mode": "color_temp",
"color_temp": color_temp_payload_value,
}
async_fire_mqtt_message(
hass,
"test_light",
'{"state": "ON", "brightness": 50, "color_mode": "color_temp", "color_temp": 192}',
json_dumps(payload),
)
color_modes = [light.ColorMode.COLOR_TEMP]
state = hass.states.get("light.test")
@ -788,6 +822,96 @@ async def test_controlling_state_via_topic(
assert light_state.attributes.get("brightness") == 128
@pytest.mark.parametrize(
"hass_config",
[
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"state_topic": "test_light_rgb",
"command_topic": "test_light_rgb/set",
"brightness": True,
"color_temp": True,
"color_temp_kelvin": True,
"effect": True,
"rgb": True,
"xy": True,
"hs": True,
"qos": "0",
}
}
}
],
)
async def test_controlling_state_color_temp_kelvin(
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
) -> None:
"""Test the controlling of the state via topic in Kelvin mode."""
await mqtt_mock_entry()
state = hass.states.get("light.test")
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_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_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
assert state.attributes.get("color_temp_kelvin") is None
assert state.attributes.get("effect") is None
assert state.attributes.get("xy_color") is None
assert state.attributes.get("hs_color") is None
assert not state.attributes.get(ATTR_ASSUMED_STATE)
# Turn on the light
async_fire_mqtt_message(
hass,
"test_light_rgb",
'{"state":"ON",'
'"color":{"r":255,"g":255,"b":255},'
'"brightness":255,'
'"color_temp":155,'
'"effect":"colorloop"}',
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("rgb_color") == (255, 255, 255)
assert state.attributes.get("brightness") == 255
assert state.attributes.get("color_temp_kelvin") is None # rgb color has priority
assert state.attributes.get("effect") == "colorloop"
assert state.attributes.get("xy_color") == (0.323, 0.329)
assert state.attributes.get("hs_color") == (0.0, 0.0)
# Turn on the light
async_fire_mqtt_message(
hass,
"test_light_rgb",
'{"state":"ON",'
'"brightness":255,'
'"color":null,'
'"color_temp":6451,' # Kelvin
'"effect":"colorloop"}',
)
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get("rgb_color") == (
255,
253,
249,
) # temp converted to color
assert state.attributes.get("brightness") == 255
assert state.attributes.get("color_temp_kelvin") == 6451
assert state.attributes.get("effect") == "colorloop"
assert state.attributes.get("xy_color") == (0.328, 0.333) # temp converted to color
assert state.attributes.get("hs_color") == (44.098, 2.43) # temp converted to color
@pytest.mark.parametrize(
"hass_config",
[
@ -2591,30 +2715,82 @@ async def test_entity_debug_info_message(
@pytest.mark.parametrize(
"hass_config",
("hass_config", "min_kelvin", "max_kelvin"),
[
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"command_topic": "test_max_mireds/set",
"color_temp": True,
"max_mireds": 370,
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"command_topic": "test_max_mireds/set",
"supported_color_modes": ["color_temp"],
"max_mireds": 370, # 2702 Kelvin
}
}
}
}
},
2702,
light.DEFAULT_MAX_KELVIN,
),
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"command_topic": "test_max_mireds/set",
"supported_color_modes": ["color_temp"],
"min_mireds": 150, # 6666 Kelvin
}
}
},
light.DEFAULT_MIN_KELVIN,
6666,
),
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"command_topic": "test_max_mireds/set",
"supported_color_modes": ["color_temp"],
"min_kelvin": 2702,
}
}
},
2702,
light.DEFAULT_MAX_KELVIN,
),
(
{
mqtt.DOMAIN: {
light.DOMAIN: {
"schema": "json",
"name": "test",
"command_topic": "test_max_mireds/set",
"supported_color_modes": ["color_temp"],
"max_kelvin": 6666,
}
}
},
light.DEFAULT_MIN_KELVIN,
6666,
),
],
)
async def test_max_mireds(
hass: HomeAssistant, mqtt_mock_entry: MqttMockHAClientGenerator
async def test_min_max_kelvin(
hass: HomeAssistant,
mqtt_mock_entry: MqttMockHAClientGenerator,
min_kelvin: int,
max_kelvin: int,
) -> None:
"""Test setting min_mireds and max_mireds."""
"""Test setting min_color_temp_kelvin and max_color_temp_kelvin."""
await mqtt_mock_entry()
state = hass.states.get("light.test")
assert state.attributes.get("min_mireds") == 153
assert state.attributes.get("max_mireds") == 370
assert state.attributes.get("min_color_temp_kelvin") == min_kelvin
assert state.attributes.get("max_color_temp_kelvin") == max_kelvin
@pytest.mark.parametrize(