Remove option to use mireds for color temperature

This commit is contained in:
jbouwh 2025-04-13 12:37:28 +00:00
parent 10f86948fb
commit cbb9673091
4 changed files with 42 additions and 118 deletions

View File

@ -155,9 +155,7 @@ from .const import (
CONF_KEEPALIVE, CONF_KEEPALIVE,
CONF_LAST_RESET_VALUE_TEMPLATE, CONF_LAST_RESET_VALUE_TEMPLATE,
CONF_MAX_KELVIN, CONF_MAX_KELVIN,
CONF_MAX_MIREDS,
CONF_MIN_KELVIN, CONF_MIN_KELVIN,
CONF_MIN_MIREDS,
CONF_ON_COMMAND_TYPE, CONF_ON_COMMAND_TYPE,
CONF_OPTIONS, CONF_OPTIONS,
CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_AVAILABLE,
@ -236,9 +234,6 @@ ADDON_SETUP_TIMEOUT_ROUNDS = 5
CONF_CLIENT_KEY_PASSWORD = "client_key_password" CONF_CLIENT_KEY_PASSWORD = "client_key_password"
DEFAULT_MAX_MIREDS = 500
DEFAULT_MIN_MIREDS = 154
MQTT_TIMEOUT = 5 MQTT_TIMEOUT = 5
ADVANCED_OPTIONS = "advanced_options" ADVANCED_OPTIONS = "advanced_options"
@ -386,15 +381,6 @@ KELVIN_SELECTOR = NumberSelector(
unit_of_measurement="K", unit_of_measurement="K",
) )
) )
MIRED_SELECTOR = NumberSelector(
NumberSelectorConfig(
mode=NumberSelectorMode.BOX,
min=100,
max=1000,
step="any",
unit_of_measurement="mireds",
)
)
SCALE_SELECTOR = NumberSelector( SCALE_SELECTOR = NumberSelector(
NumberSelectorConfig( NumberSelectorConfig(
mode=NumberSelectorMode.BOX, mode=NumberSelectorMode.BOX,
@ -477,7 +463,8 @@ class PlatformField:
required: bool required: bool
validator: Callable[..., Any] validator: Callable[..., Any]
error: str | None = None error: str | None = None
default: str | int | vol.Undefined = vol.UNDEFINED default: str | int | bool | vol.Undefined = vol.UNDEFINED
is_schema_default: bool = False
exclude_from_reconfig: bool = False exclude_from_reconfig: bool = False
conditions: tuple[dict[str, Any], ...] | None = None conditions: tuple[dict[str, Any], ...] | None = None
custom_filtering: bool = False custom_filtering: bool = False
@ -506,17 +493,11 @@ def unit_of_measurement_selector(user_data: dict[str, Any | None]) -> Selector:
def validate_light_platform_config(user_data: dict[str, Any]) -> dict[str, str]: def validate_light_platform_config(user_data: dict[str, Any]) -> dict[str, str]:
"""Validate MQTT light configuration.""" """Validate MQTT light configuration."""
errors: dict[str, Any] = {} errors: dict[str, Any] = {}
if user_data[CONF_COLOR_TEMP_KELVIN]: if user_data.get(CONF_MIN_KELVIN, DEFAULT_MIN_KELVIN) >= user_data.get(
if user_data.get(CONF_MIN_KELVIN, DEFAULT_MIN_KELVIN) >= user_data.get( CONF_MAX_KELVIN, DEFAULT_MAX_KELVIN
CONF_MAX_KELVIN, DEFAULT_MAX_KELVIN
):
errors[CONF_MAX_KELVIN] = "max_below_min_kelvin"
errors[CONF_MIN_KELVIN] = "max_below_min_kelvin"
elif user_data.get(CONF_MIN_MIREDS, DEFAULT_MIN_MIREDS) >= user_data.get(
CONF_MAX_MIREDS, DEFAULT_MAX_MIREDS
): ):
errors[CONF_MAX_MIREDS] = "max_below_min_mireds" errors[CONF_MAX_KELVIN] = "max_below_min_kelvin"
errors[CONF_MIN_MIREDS] = "max_below_min_mireds" errors[CONF_MIN_KELVIN] = "max_below_min_kelvin"
return errors return errors
@ -580,11 +561,11 @@ PLATFORM_ENTITY_FIELDS = {
exclude_from_reconfig=True, exclude_from_reconfig=True,
), ),
CONF_COLOR_TEMP_KELVIN: PlatformField( CONF_COLOR_TEMP_KELVIN: PlatformField(
selector=BOOLEAN_SELECTOR, selector=Selector(),
required=True, required=True,
validator=bool, validator=bool,
default=True, default=True,
exclude_from_reconfig=True, is_schema_default=True,
), ),
}, },
} }
@ -1113,7 +1094,6 @@ PLATFORM_MQTT_FIELDS = {
required=False, required=False,
validator=cv.positive_int, validator=cv.positive_int,
default=DEFAULT_MAX_KELVIN, default=DEFAULT_MAX_KELVIN,
conditions=({CONF_COLOR_TEMP_KELVIN: True},),
section="advanced_settings", section="advanced_settings",
), ),
CONF_MIN_KELVIN: PlatformField( CONF_MIN_KELVIN: PlatformField(
@ -1121,23 +1101,6 @@ PLATFORM_MQTT_FIELDS = {
required=False, required=False,
validator=cv.positive_int, validator=cv.positive_int,
default=DEFAULT_MIN_KELVIN, default=DEFAULT_MIN_KELVIN,
conditions=({CONF_COLOR_TEMP_KELVIN: True},),
section="advanced_settings",
),
CONF_MAX_MIREDS: PlatformField(
selector=MIRED_SELECTOR,
required=False,
validator=cv.positive_int,
default=DEFAULT_MAX_MIREDS,
conditions=({CONF_COLOR_TEMP_KELVIN: False},),
section="advanced_settings",
),
CONF_MIN_MIREDS: PlatformField(
selector=MIRED_SELECTOR,
required=False,
validator=cv.positive_int,
default=DEFAULT_MIN_MIREDS,
conditions=({CONF_COLOR_TEMP_KELVIN: False},),
section="advanced_settings", section="advanced_settings",
), ),
CONF_FLASH_TIME_SHORT: PlatformField( CONF_FLASH_TIME_SHORT: PlatformField(
@ -1331,7 +1294,9 @@ def data_schema_from_fields(
component_data_with_user_input |= user_input component_data_with_user_input |= user_input
sections: dict[str | None, None] = { sections: dict[str | None, None] = {
field_details.section: None for field_details in data_schema_fields.values() field_details.section: None
for field_details in data_schema_fields.values()
if not field_details.is_schema_default
} }
data_schema: dict[Any, Any] = {} data_schema: dict[Any, Any] = {}
all_data_element_options: set[Any] = set() all_data_element_options: set[Any] = set()
@ -1346,7 +1311,8 @@ def data_schema_from_fields(
if field_details.custom_filtering if field_details.custom_filtering
else field_details.selector else field_details.selector
for field_name, field_details in data_schema_fields.items() for field_name, field_details in data_schema_fields.items()
if field_details.section == schema_section if not field_details.is_schema_default
and field_details.section == schema_section
and (not field_details.exclude_from_reconfig or not reconfig) and (not field_details.exclude_from_reconfig or not reconfig)
and _check_conditions(field_details, component_data_with_user_input) and _check_conditions(field_details, component_data_with_user_input)
} }
@ -1388,6 +1354,18 @@ def data_schema_from_fields(
return vol.Schema(data_schema) return vol.Schema(data_schema)
@callback
def subentry_schema_default_data_from_fields(
data_schema_fields: dict[str, PlatformField],
) -> dict[str, Any]:
"""Generate custom data schema from platform fields or device data."""
return {
key: field.default
for key, field in data_schema_fields.items()
if field.is_schema_default
}
class FlowHandler(ConfigFlow, domain=DOMAIN): class FlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a config flow.""" """Handle a config flow."""
@ -2204,6 +2182,16 @@ class MQTTSubentryFlowHandler(ConfigSubentryFlow):
last_step=False, last_step=False,
) )
@callback
def _async_update_component_data_defaults(self) -> None:
"""Update component data defaults."""
for component_data in self._subentry_data["components"].values():
platform = component_data[CONF_PLATFORM]
subentry_default_data = subentry_schema_default_data_from_fields(
PLATFORM_ENTITY_FIELDS[platform]
)
component_data.update(subentry_default_data)
@callback @callback
def _async_create_subentry( def _async_create_subentry(
self, user_input: dict[str, Any] | None = None self, user_input: dict[str, Any] | None = None
@ -2220,6 +2208,7 @@ class MQTTSubentryFlowHandler(ConfigSubentryFlow):
else: else:
full_entity_name = device_name full_entity_name = device_name
self._async_update_component_data_defaults()
return self.async_create_entry( return self.async_create_entry(
data=self._subentry_data, data=self._subentry_data,
title=self._subentry_data[CONF_DEVICE][CONF_NAME], title=self._subentry_data[CONF_DEVICE][CONF_NAME],
@ -2284,6 +2273,7 @@ class MQTTSubentryFlowHandler(ConfigSubentryFlow):
if len(self._subentry_data["components"]) > 1: if len(self._subentry_data["components"]) > 1:
menu_options.append("delete_entity") menu_options.append("delete_entity")
menu_options.extend(["device", "availability"]) menu_options.extend(["device", "availability"])
self._async_update_component_data_defaults()
if self._subentry_data != self._get_reconfigure_subentry().data: if self._subentry_data != self._get_reconfigure_subentry().data:
menu_options.append("save_changes") menu_options.append("save_changes")
return self.async_show_menu( return self.async_show_menu(

View File

@ -214,7 +214,6 @@
"description": "Please configure specific details for {platform} entity \"{entity}\":", "description": "Please configure specific details for {platform} entity \"{entity}\":",
"data": { "data": {
"device_class": "Device class", "device_class": "Device class",
"color_temp_kelvin": "Color temperature in Kelvin",
"options": "Add option", "options": "Add option",
"schema": "Schema", "schema": "Schema",
"state_class": "State class", "state_class": "State class",
@ -223,7 +222,6 @@
}, },
"data_description": { "data_description": {
"device_class": "The Device class of the {platform} entity. [Learn more.]({url}#device_class)", "device_class": "The Device class of the {platform} entity. [Learn more.]({url}#device_class)",
"color_temp_kelvin": "When set color mode updates will be published in Kelvin and processed state will be assumed in Kelvin. When not set the color temp values are converted to mireds instead.",
"options": "Options for allowed sensor state values. The sensors Device class must be set to Enumeration. The 'Options' setting cannot be used together with State class or Unit of measurement.", "options": "Options for allowed sensor state values. The sensors Device class must be set to Enumeration. The 'Options' setting cannot be used together with State class or Unit of measurement.",
"schema": "The schema to use. [Learn more.]({url}#comparison-of-light-mqtt-schemas)", "schema": "The schema to use. [Learn more.]({url}#comparison-of-light-mqtt-schemas)",
"state_class": "The [State class](https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes) of the sensor. [Learn more.]({url}#state_class)", "state_class": "The [State class](https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes) of the sensor. [Learn more.]({url}#state_class)",
@ -273,10 +271,10 @@
"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.",
"command_off_template": "The [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) for \"off\" state changes. Available variables are: `state` and `transition`.", "command_off_template": "The [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) for \"off\" state changes. Available variables are: `state` and `transition`.",
"command_on_template": "The [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) for \"on\" state changes. Available variables: `state`, `brightness`, `color_temp`, `red`, `green`, `blue`, `hue`, `sat`, `flash`, `transition` and `effect`. Values `red`, `green`, `blue` and `brightness` are provided as integers from range 0-255. Value of `hue` is provided as float from range 0-360. Value of `sat` is provided as float from range 0-100. Value of `color_temp` is provided as integer representing mired or Kelvin units if 'Color temperature in Kelvin' is true.", "command_on_template": "The [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) for \"on\" state changes. Available variables: `state`, `brightness`, `color_temp`, `red`, `green`, `blue`, `hue`, `sat`, `flash`, `transition` and `effect`. Values `red`, `green`, `blue` and `brightness` are provided as integers from range 0-255. Value of `hue` is provided as float from range 0-360. Value of `sat` is provided as float from range 0-100. Value of `color_temp` is provided as integer representing Kelvin units.",
"command_template": "A [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to render the payload to be published at the command topic.", "command_template": "A [template](https://www.home-assistant.io/docs/configuration/templating/#using-command-templates-with-mqtt) to render the payload to be published at the command topic.",
"command_topic": "The publishing topic that will be used to control the {platform} entity. [Learn more.]({url}#command_topic)", "command_topic": "The publishing topic that will be used to control the {platform} entity. [Learn more.]({url}#command_topic)",
"color_temp_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract color temperature from the state payload value. Expected result of the template is an integer. If the color tempemperature in Kelvin setting is true, the expected value is in Kelvin, else mireds are expected.", "color_temp_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract color temperature in Kelvin from the state payload value. Expected result of the template is an integer.",
"green_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract green color from the state payload value. Expected result of the template is an integer from 0-255 range.", "green_template": "[Template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract green color from the state payload value. Expected result of the template is an integer from 0-255 range.",
"last_reset_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the last reset. When Last reset template is set, the State class option must be Total. [Learn more.]({url}#last_reset_value_template)", "last_reset_value_template": "Defines a [template](https://www.home-assistant.io/docs/configuration/templating/#using-value-templates-with-mqtt) to extract the last reset. When Last reset template is set, the State class option must be Total. [Learn more.]({url}#last_reset_value_template)",
"force_update": "Sends update events even if the value hasnt changed. Useful if you want to have meaningful value graphs in history. [Learn more.]({url}#force_update)", "force_update": "Sends update events even if the value hasnt changed. Useful if you want to have meaningful value graphs in history. [Learn more.]({url}#force_update)",
@ -300,18 +298,14 @@
"flash_time_long": "Flash time long", "flash_time_long": "Flash time long",
"flash_time_short": "Flash time short", "flash_time_short": "Flash time short",
"max_kelvin": "Max Kelvin", "max_kelvin": "Max Kelvin",
"min_kelvin": "Min Kelvin", "min_kelvin": "Min Kelvin"
"max_mireds": "Max mireds",
"min_mireds": "Min mireds"
}, },
"data_description": { "data_description": {
"expire_after": "If set, it defines the number of seconds after the sensors state expires, if its not updated. After expiry, the sensors state becomes unavailable. If not set, the sensor's state never expires. [Learn more.]({url}#expire_after)", "expire_after": "If set, it defines the number of seconds after the sensors state expires, if its not updated. After expiry, the sensors state becomes unavailable. If not set, the sensor's state never expires. [Learn more.]({url}#expire_after)",
"flash_time_long": "The duration, in seconds, of a \"long\" flash.", "flash_time_long": "The duration, in seconds, of a \"long\" flash.",
"flash_time_short": "The duration, in seconds, of a \"short\" flash.", "flash_time_short": "The duration, in seconds, of a \"short\" flash.",
"max_kelvin": "The maximum color temperature in Kelvin.", "max_kelvin": "The maximum color temperature in Kelvin.",
"min_kelvin": "The minimum color temperature in Kelvin.", "min_kelvin": "The minimum color temperature in Kelvin."
"max_mireds": "The maximum color temperature in mireds.",
"min_mireds": "The minimum color temperature in mireds."
} }
}, },
"light_brightness_settings": { "light_brightness_settings": {
@ -482,7 +476,6 @@
"invalid_url": "Invalid URL", "invalid_url": "Invalid URL",
"last_reset_not_with_state_class_total": "The last reset value template option should be used with state class 'Total' only", "last_reset_not_with_state_class_total": "The last reset value template option should be used with state class 'Total' only",
"max_below_min_kelvin": "Max Kelvin value should be greater than min Kelvin value", "max_below_min_kelvin": "Max Kelvin value should be greater than min Kelvin value",
"max_below_min_mireds": "Max mireds value should be greater than min mireds value",
"options_not_allowed_with_state_class_or_uom": "The 'Options' setting is not allowed when state class or unit of measurement are used", "options_not_allowed_with_state_class_or_uom": "The 'Options' setting is not allowed when state class or unit of measurement are used",
"options_device_class_enum": "The 'Options' setting must be used with the Enumeration device class. If you continue, the existing options will be reset", "options_device_class_enum": "The 'Options' setting must be used with the Enumeration device class. If you continue, the existing options will be reset",
"options_with_enum_device_class": "Configure options for the enumeration sensor", "options_with_enum_device_class": "Configure options for the enumeration sensor",

View File

@ -155,22 +155,6 @@ MOCK_SUBENTRY_LIGHT_BASIC_KELVIN_COMPONENT = {
"entity_picture": "https://example.com/8131babc5e8d4f44b82e0761d39091a2", "entity_picture": "https://example.com/8131babc5e8d4f44b82e0761d39091a2",
}, },
} }
MOCK_SUBENTRY_LIGHT_BASIC_MIREDS_COMPONENT = {
"8131babc5e8d4f44b82e0761d39091a2": {
"platform": "light",
"name": "Basic light",
"on_command_type": "last",
"optimistic": True,
"payload_off": "OFF",
"payload_on": "ON",
"command_topic": "test-topic",
"schema": "basic",
"state_topic": "test-topic",
"color_temp_kelvin": False,
"state_value_template": "{{ value_json.value }}",
"entity_picture": "https://example.com/8131babc5e8d4f44b82e0761d39091a2",
},
}
MOCK_SUBENTRY_NOTIFY_BAD_SCHEMA = { MOCK_SUBENTRY_NOTIFY_BAD_SCHEMA = {
"b10b531e15244425a74bb0abb1e9d2c6": { "b10b531e15244425a74bb0abb1e9d2c6": {
"platform": "notify", "platform": "notify",
@ -214,10 +198,6 @@ MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}}, "device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_LIGHT_BASIC_KELVIN_COMPONENT, "components": MOCK_SUBENTRY_LIGHT_BASIC_KELVIN_COMPONENT,
} }
MOCK_LIGHT_BASIC_MIREDS_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_LIGHT_BASIC_MIREDS_COMPONENT,
}
MOCK_SENSOR_SUBENTRY_DATA_SINGLE = { MOCK_SENSOR_SUBENTRY_DATA_SINGLE = {
"device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}}, "device": MOCK_SUBENTRY_DEVICE_DATA | {"mqtt_settings": {"qos": 0}},
"components": MOCK_SUBENTRY_SENSOR_COMPONENT, "components": MOCK_SUBENTRY_SENSOR_COMPONENT,

View File

@ -34,7 +34,6 @@ from homeassistant.helpers.service_info.hassio import HassioServiceInfo
from .common import ( from .common import (
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE, MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
MOCK_LIGHT_BASIC_MIREDS_SUBENTRY_DATA_SINGLE,
MOCK_NOTIFY_SUBENTRY_DATA_MULTI, MOCK_NOTIFY_SUBENTRY_DATA_MULTI,
MOCK_NOTIFY_SUBENTRY_DATA_NO_NAME, MOCK_NOTIFY_SUBENTRY_DATA_NO_NAME,
MOCK_NOTIFY_SUBENTRY_DATA_SINGLE, MOCK_NOTIFY_SUBENTRY_DATA_SINGLE,
@ -2768,7 +2767,7 @@ async def test_migrate_of_incompatible_config_entry(
MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE, MOCK_LIGHT_BASIC_KELVIN_SUBENTRY_DATA_SINGLE,
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}}, {"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
{"name": "Basic light"}, {"name": "Basic light"},
{"color_temp_kelvin": True}, {},
{}, {},
{ {
"command_topic": "test-topic", "command_topic": "test-topic",
@ -2801,43 +2800,6 @@ async def test_migrate_of_incompatible_config_entry(
), ),
"Milk notifier Basic light", "Milk notifier Basic light",
), ),
(
MOCK_LIGHT_BASIC_MIREDS_SUBENTRY_DATA_SINGLE,
{"name": "Milk notifier", "mqtt_settings": {"qos": 1}},
{"name": "Basic light"},
{"color_temp_kelvin": False},
{},
{
"command_topic": "test-topic",
"state_topic": "test-topic",
"state_value_template": "{{ value_json.value }}",
"optimistic": True,
},
(
(
{"command_topic": "test-topic#invalid"},
{"command_topic": "invalid_publish_topic"},
),
(
{
"command_topic": "test-topic",
"state_topic": "test-topic#invalid",
},
{"state_topic": "invalid_subscribe_topic"},
),
(
{
"command_topic": "test-topic",
"advanced_settings": {"min_mireds": 200, "max_mireds": 190},
},
{
"max_mireds": "max_below_min_mireds",
"min_mireds": "max_below_min_mireds",
},
),
),
"Milk notifier Basic light",
),
], ],
ids=[ ids=[
"notify_with_entity_name", "notify_with_entity_name",
@ -2846,7 +2808,6 @@ async def test_migrate_of_incompatible_config_entry(
"sensor_total", "sensor_total",
"switch", "switch",
"light_basic_kelvin", "light_basic_kelvin",
"light_basic_mireds",
], ],
) )
async def test_subentry_configflow( async def test_subentry_configflow(