From 970f7cc059de512fcb4ea36e9ab6d919c2b220f8 Mon Sep 17 00:00:00 2001 From: jbouwh Date: Wed, 26 Mar 2025 20:52:00 +0000 Subject: [PATCH] Allow to set translation_domain for SelectSelector and create helpers for sensor/switch device_class and sensor state_class --- homeassistant/components/mqtt/config_flow.py | 34 +++------ homeassistant/components/mqtt/strings.json | 73 -------------------- homeassistant/components/sensor/helpers.py | 35 +++++++++- homeassistant/components/sensor/strings.json | 69 ++++++++++++++++++ homeassistant/components/switch/helpers.py | 25 +++++++ homeassistant/components/switch/strings.json | 8 +++ homeassistant/helpers/selector.py | 2 + tests/components/sensor/test_helpers.py | 30 +++++++- tests/components/switch/test_helpers.py | 16 +++++ tests/helpers/test_selector.py | 1 + 10 files changed, 193 insertions(+), 100 deletions(-) create mode 100644 homeassistant/components/switch/helpers.py create mode 100644 tests/components/switch/test_helpers.py diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 83592c4c23d..13f4d8f3c6c 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -31,7 +31,10 @@ from homeassistant.components.sensor import ( CONF_STATE_CLASS, DEVICE_CLASS_UNITS, SensorDeviceClass, - SensorStateClass, +) +from homeassistant.components.sensor.helpers import ( + create_sensor_device_class_select_selector, + create_sensor_state_class_select_selector, ) from homeassistant.components.switch import SwitchDeviceClass from homeassistant.config_entries import ( @@ -257,27 +260,8 @@ SUBENTRY_AVAILABILITY_SCHEMA = vol.Schema( ) # Sensor specific selectors -SENSOR_DEVICE_CLASS_SELECTOR = SelectSelector( - SelectSelectorConfig( - options=[device_class.value for device_class in SensorDeviceClass], - mode=SelectSelectorMode.DROPDOWN, - translation_key="device_class_sensor", - sort=True, - ) -) -SENSOR_STATE_CLASS_SELECTOR = SelectSelector( - SelectSelectorConfig( - options=[device_class.value for device_class in SensorStateClass], - mode=SelectSelectorMode.DROPDOWN, - translation_key=CONF_STATE_CLASS, - ) -) OPTIONS_SELECTOR = SelectSelector( - SelectSelectorConfig( - options=[], - custom_value=True, - multiple=True, - ) + SelectSelectorConfig(options=[], custom_value=True, multiple=True) ) SUGGESTED_DISPLAY_PRECISION_SELECTOR = NumberSelector( NumberSelectorConfig(mode=NumberSelectorMode.BOX, min=0, max=9) @@ -392,10 +376,14 @@ PLATFORM_ENTITY_FIELDS = { Platform.NOTIFY.value: {}, Platform.SENSOR.value: { CONF_DEVICE_CLASS: PlatformField( - selector=SENSOR_DEVICE_CLASS_SELECTOR, required=False, validator=str + selector=create_sensor_device_class_select_selector(), + required=False, + validator=str, ), CONF_STATE_CLASS: PlatformField( - selector=SENSOR_STATE_CLASS_SELECTOR, required=False, validator=str + selector=create_sensor_state_class_select_selector(), + required=False, + validator=str, ), CONF_UNIT_OF_MEASUREMENT: PlatformField( selector=unit_of_measurement_selector, diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index cedf120def1..455f729156e 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -404,72 +404,6 @@ } }, "selector": { - "device_class_sensor": { - "options": { - "apparent_power": "[%key:component::sensor::entity_component::apparent_power::name%]", - "area": "[%key:component::sensor::entity_component::area::name%]", - "aqi": "[%key:component::sensor::entity_component::aqi::name%]", - "atmospheric_pressure": "[%key:component::sensor::entity_component::atmospheric_pressure::name%]", - "battery": "[%key:component::sensor::entity_component::battery::name%]", - "blood_glucose_concentration": "[%key:component::sensor::entity_component::blood_glucose_concentration::name%]", - "carbon_dioxide": "[%key:component::sensor::entity_component::carbon_dioxide::name%]", - "carbon_monoxide": "[%key:component::sensor::entity_component::carbon_monoxide::name%]", - "conductivity": "[%key:component::sensor::entity_component::conductivity::name%]", - "current": "[%key:component::sensor::entity_component::current::name%]", - "data_rate": "[%key:component::sensor::entity_component::data_rate::name%]", - "data_size": "[%key:component::sensor::entity_component::data_size::name%]", - "date": "[%key:component::sensor::entity_component::date::name%]", - "distance": "[%key:component::sensor::entity_component::distance::name%]", - "duration": "[%key:component::sensor::entity_component::duration::name%]", - "energy": "[%key:component::sensor::entity_component::energy::name%]", - "energy_distance": "[%key:component::sensor::entity_component::energy_distance::name%]", - "energy_storage": "[%key:component::sensor::entity_component::energy_storage::name%]", - "enum": "Enumeration", - "frequency": "[%key:component::sensor::entity_component::frequency::name%]", - "gas": "[%key:component::sensor::entity_component::gas::name%]", - "humidity": "[%key:component::sensor::entity_component::humidity::name%]", - "illuminance": "[%key:component::sensor::entity_component::illuminance::name%]", - "irradiance": "[%key:component::sensor::entity_component::irradiance::name%]", - "moisture": "[%key:component::sensor::entity_component::moisture::name%]", - "monetary": "[%key:component::sensor::entity_component::monetary::name%]", - "nitrogen_dioxide": "[%key:component::sensor::entity_component::nitrogen_dioxide::name%]", - "nitrogen_monoxide": "[%key:component::sensor::entity_component::nitrogen_monoxide::name%]", - "nitrous_oxide": "[%key:component::sensor::entity_component::nitrous_oxide::name%]", - "ozone": "[%key:component::sensor::entity_component::ozone::name%]", - "ph": "[%key:component::sensor::entity_component::ph::name%]", - "pm1": "[%key:component::sensor::entity_component::pm1::name%]", - "pm10": "[%key:component::sensor::entity_component::pm10::name%]", - "pm25": "[%key:component::sensor::entity_component::pm25::name%]", - "power": "[%key:component::sensor::entity_component::power::name%]", - "power_factor": "[%key:component::sensor::entity_component::power_factor::name%]", - "precipitation": "[%key:component::sensor::entity_component::precipitation::name%]", - "precipitation_intensity": "[%key:component::sensor::entity_component::precipitation_intensity::name%]", - "pressure": "[%key:component::sensor::entity_component::pressure::name%]", - "reactive_power": "[%key:component::sensor::entity_component::reactive_power::name%]", - "signal_strength": "[%key:component::sensor::entity_component::signal_strength::name%]", - "sound_pressure": "[%key:component::sensor::entity_component::sound_pressure::name%]", - "speed": "[%key:component::sensor::entity_component::speed::name%]", - "sulphur_dioxide": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]", - "temperature": "[%key:component::sensor::entity_component::temperature::name%]", - "timestamp": "[%key:component::sensor::entity_component::timestamp::name%]", - "volatile_organic_compounds": "[%key:component::sensor::entity_component::volatile_organic_compounds::name%]", - "volatile_organic_compounds_parts": "[%key:component::sensor::entity_component::volatile_organic_compounds_parts::name%]", - "voltage": "[%key:component::sensor::entity_component::voltage::name%]", - "volume": "[%key:component::sensor::entity_component::volume::name%]", - "volume_flow_rate": "[%key:component::sensor::entity_component::volume_flow_rate::name%]", - "volume_storage": "[%key:component::sensor::entity_component::volume_storage::name%]", - "water": "[%key:component::sensor::entity_component::water::name%]", - "weight": "[%key:component::sensor::entity_component::weight::name%]", - "wind_direction": "[%key:component::sensor::entity_component::wind_direction::name%]", - "wind_speed": "[%key:component::sensor::entity_component::wind_speed::name%]" - } - }, - "device_class_switch": { - "options": { - "outlet": "[%key:component::switch::entity_component::outlet::name%]", - "switch": "[%key:component::switch::title%]" - } - }, "platform": { "options": { "notify": "Notify", @@ -483,13 +417,6 @@ "auto": "Auto", "custom": "Custom" } - }, - "state_class": { - "options": { - "measurement": "[%key:component::sensor::entity_component::_::state_attributes::state_class::state::measurement%]", - "total": "[%key:component::sensor::entity_component::_::state_attributes::state_class::state::total%]", - "total_increasing": "[%key:component::sensor::entity_component::_::state_attributes::state_class::state::total_increasing%]" - } } }, "services": { diff --git a/homeassistant/components/sensor/helpers.py b/homeassistant/components/sensor/helpers.py index 12a5dcefdf8..efc8b29d4a4 100644 --- a/homeassistant/components/sensor/helpers.py +++ b/homeassistant/components/sensor/helpers.py @@ -6,9 +6,14 @@ from datetime import date, datetime import logging from homeassistant.core import callback +from homeassistant.helpers.selector import ( + SelectSelector, + SelectSelectorConfig, + SelectSelectorMode, +) from homeassistant.util import dt as dt_util -from . import SensorDeviceClass +from . import DOMAIN, SensorDeviceClass, SensorStateClass _LOGGER = logging.getLogger(__name__) @@ -37,3 +42,31 @@ def async_parse_date_datetime( _LOGGER.warning("%s rendered invalid date %s", entity_id, value) return None + + +@callback +def create_sensor_device_class_select_selector() -> SelectSelector: + """Create sensor device class select selector.""" + return SelectSelector( + SelectSelectorConfig( + options=[device_class.value for device_class in SensorDeviceClass], + mode=SelectSelectorMode.DROPDOWN, + translation_key="device_class", + translation_domain=DOMAIN, + sort=True, + ) + ) + + +@callback +def create_sensor_state_class_select_selector() -> SelectSelector: + """Create sensor state class select selector.""" + return SelectSelector( + SelectSelectorConfig( + options=[device_class.value for device_class in SensorStateClass], + mode=SelectSelectorMode.DROPDOWN, + translation_key="state_class", + translation_domain=DOMAIN, + sort=True, + ) + ) diff --git a/homeassistant/components/sensor/strings.json b/homeassistant/components/sensor/strings.json index 123c30da72e..8860a9c0f07 100644 --- a/homeassistant/components/sensor/strings.json +++ b/homeassistant/components/sensor/strings.json @@ -321,5 +321,74 @@ "title": "The unit of {statistic_id} has changed", "description": "" } + }, + "selector": { + "device_class": { + "options": { + "apparent_power": "[%key:component::sensor::entity_component::apparent_power::name%]", + "area": "[%key:component::sensor::entity_component::area::name%]", + "aqi": "[%key:component::sensor::entity_component::aqi::name%]", + "atmospheric_pressure": "[%key:component::sensor::entity_component::atmospheric_pressure::name%]", + "battery": "[%key:component::sensor::entity_component::battery::name%]", + "blood_glucose_concentration": "[%key:component::sensor::entity_component::blood_glucose_concentration::name%]", + "carbon_dioxide": "[%key:component::sensor::entity_component::carbon_dioxide::name%]", + "carbon_monoxide": "[%key:component::sensor::entity_component::carbon_monoxide::name%]", + "conductivity": "[%key:component::sensor::entity_component::conductivity::name%]", + "current": "[%key:component::sensor::entity_component::current::name%]", + "data_rate": "[%key:component::sensor::entity_component::data_rate::name%]", + "data_size": "[%key:component::sensor::entity_component::data_size::name%]", + "date": "[%key:component::sensor::entity_component::date::name%]", + "distance": "[%key:component::sensor::entity_component::distance::name%]", + "duration": "[%key:component::sensor::entity_component::duration::name%]", + "energy": "[%key:component::sensor::entity_component::energy::name%]", + "energy_distance": "[%key:component::sensor::entity_component::energy_distance::name%]", + "energy_storage": "[%key:component::sensor::entity_component::energy_storage::name%]", + "enum": "Enumeration", + "frequency": "[%key:component::sensor::entity_component::frequency::name%]", + "gas": "[%key:component::sensor::entity_component::gas::name%]", + "humidity": "[%key:component::sensor::entity_component::humidity::name%]", + "illuminance": "[%key:component::sensor::entity_component::illuminance::name%]", + "irradiance": "[%key:component::sensor::entity_component::irradiance::name%]", + "moisture": "[%key:component::sensor::entity_component::moisture::name%]", + "monetary": "[%key:component::sensor::entity_component::monetary::name%]", + "nitrogen_dioxide": "[%key:component::sensor::entity_component::nitrogen_dioxide::name%]", + "nitrogen_monoxide": "[%key:component::sensor::entity_component::nitrogen_monoxide::name%]", + "nitrous_oxide": "[%key:component::sensor::entity_component::nitrous_oxide::name%]", + "ozone": "[%key:component::sensor::entity_component::ozone::name%]", + "ph": "[%key:component::sensor::entity_component::ph::name%]", + "pm1": "[%key:component::sensor::entity_component::pm1::name%]", + "pm10": "[%key:component::sensor::entity_component::pm10::name%]", + "pm25": "[%key:component::sensor::entity_component::pm25::name%]", + "power": "[%key:component::sensor::entity_component::power::name%]", + "power_factor": "[%key:component::sensor::entity_component::power_factor::name%]", + "precipitation": "[%key:component::sensor::entity_component::precipitation::name%]", + "precipitation_intensity": "[%key:component::sensor::entity_component::precipitation_intensity::name%]", + "pressure": "[%key:component::sensor::entity_component::pressure::name%]", + "reactive_power": "[%key:component::sensor::entity_component::reactive_power::name%]", + "signal_strength": "[%key:component::sensor::entity_component::signal_strength::name%]", + "sound_pressure": "[%key:component::sensor::entity_component::sound_pressure::name%]", + "speed": "[%key:component::sensor::entity_component::speed::name%]", + "sulphur_dioxide": "[%key:component::sensor::entity_component::sulphur_dioxide::name%]", + "temperature": "[%key:component::sensor::entity_component::temperature::name%]", + "timestamp": "[%key:component::sensor::entity_component::timestamp::name%]", + "volatile_organic_compounds": "[%key:component::sensor::entity_component::volatile_organic_compounds::name%]", + "volatile_organic_compounds_parts": "[%key:component::sensor::entity_component::volatile_organic_compounds::name%]", + "voltage": "[%key:component::sensor::entity_component::voltage::name%]", + "volume": "[%key:component::sensor::entity_component::volume::name%]", + "volume_flow_rate": "[%key:component::sensor::entity_component::volume_flow_rate::name%]", + "volume_storage": "[%key:component::sensor::entity_component::volume_storage::name%]", + "water": "[%key:component::sensor::entity_component::water::name%]", + "weight": "[%key:component::sensor::entity_component::weight::name%]", + "wind_direction": "[%key:component::sensor::entity_component::wind_direction::name%]", + "wind_speed": "[%key:component::sensor::entity_component::wind_speed::name%]" + } + }, + "state_class": { + "options": { + "measurement": "[%key:component::sensor::entity_component::_::state_attributes::state_class::state::measurement%]", + "total": "[%key:component::sensor::entity_component::_::state_attributes::state_class::state::total%]", + "total_increasing": "[%key:component::sensor::entity_component::_::state_attributes::state_class::state::total_increasing%]" + } + } } } diff --git a/homeassistant/components/switch/helpers.py b/homeassistant/components/switch/helpers.py new file mode 100644 index 00000000000..872b9480cee --- /dev/null +++ b/homeassistant/components/switch/helpers.py @@ -0,0 +1,25 @@ +"""Helpers for switch entities.""" + +from homeassistant.core import callback +from homeassistant.helpers.selector import ( + SelectSelector, + SelectSelectorConfig, + SelectSelectorMode, +) + +from . import DOMAIN, SwitchDeviceClass + + +@callback +def create_switch_device_class_select_selector() -> SelectSelector: + """Create sensor device class select selector.""" + + return SelectSelector( + SelectSelectorConfig( + options=[device_class.value for device_class in SwitchDeviceClass], + mode=SelectSelectorMode.DROPDOWN, + translation_key="device_class", + translation_domain=DOMAIN, + sort=True, + ) + ) diff --git a/homeassistant/components/switch/strings.json b/homeassistant/components/switch/strings.json index b73cf8f849d..e2eac8fd89a 100644 --- a/homeassistant/components/switch/strings.json +++ b/homeassistant/components/switch/strings.json @@ -52,5 +52,13 @@ "name": "[%key:common::action::toggle%]", "description": "Toggles a switch on/off." } + }, + "selector": { + "device_class": { + "options": { + "outlet": "[%key:component::switch::entity_component::outlet::name%]", + "switch": "[%key:component::switch::title%]" + } + } } } diff --git a/homeassistant/helpers/selector.py b/homeassistant/helpers/selector.py index f2c76d1d019..dcae52b3d02 100644 --- a/homeassistant/helpers/selector.py +++ b/homeassistant/helpers/selector.py @@ -1150,6 +1150,7 @@ class SelectSelectorConfig(TypedDict, total=False): custom_value: bool mode: SelectSelectorMode translation_key: str + translation_domain: str sort: bool @@ -1168,6 +1169,7 @@ class SelectSelector(Selector[SelectSelectorConfig]): vol.Coerce(SelectSelectorMode), lambda val: val.value ), vol.Optional("translation_key"): cv.string, + vol.Optional("translation_domain"): cv.string, vol.Optional("sort", default=False): cv.boolean, } ) diff --git a/tests/components/sensor/test_helpers.py b/tests/components/sensor/test_helpers.py index e197579fa66..b9b8070fff8 100644 --- a/tests/components/sensor/test_helpers.py +++ b/tests/components/sensor/test_helpers.py @@ -1,9 +1,13 @@ -"""The test for sensor helpers.""" +"""Tests for sensor helpers.""" import pytest -from homeassistant.components.sensor import SensorDeviceClass -from homeassistant.components.sensor.helpers import async_parse_date_datetime +from homeassistant.components.sensor import DOMAIN, SensorDeviceClass, SensorStateClass +from homeassistant.components.sensor.helpers import ( + async_parse_date_datetime, + create_sensor_device_class_select_selector, + create_sensor_state_class_select_selector, +) def test_async_parse_datetime(caplog: pytest.LogCaptureFixture) -> None: @@ -39,3 +43,23 @@ def test_async_parse_datetime(caplog: pytest.LogCaptureFixture) -> None: # Invalid date assert async_parse_date_datetime("December 12th", entity_id, device_class) is None assert "sensor.timestamp rendered invalid date December 12th" in caplog.text + + +def test_create_sensor_device_class_select_selector() -> None: + "Test Create sensor state class select selector helper." + selector = create_sensor_device_class_select_selector() + assert selector.config["options"] == list(SensorDeviceClass) + assert selector.config["translation_domain"] == DOMAIN + assert selector.config["translation_key"] == "device_class" + assert selector.config["sort"] + assert not selector.config["custom_value"] + + +def test_create_sensor_state_class_select_selector() -> None: + "Test Create sensor state class select selector helper." + selector = create_sensor_state_class_select_selector() + assert selector.config["options"] == list(SensorStateClass) + assert selector.config["translation_domain"] == DOMAIN + assert selector.config["translation_key"] == "state_class" + assert selector.config["sort"] + assert not selector.config["custom_value"] diff --git a/tests/components/switch/test_helpers.py b/tests/components/switch/test_helpers.py new file mode 100644 index 00000000000..242d233fac6 --- /dev/null +++ b/tests/components/switch/test_helpers.py @@ -0,0 +1,16 @@ +"""Tests for switch helpers.""" + +from homeassistant.components.switch import DOMAIN, SwitchDeviceClass +from homeassistant.components.switch.helpers import ( + create_switch_device_class_select_selector, +) + + +def test_create_switch_device_class_select_selector() -> None: + "Test Create sensor state class select selector helper." + selector = create_switch_device_class_select_selector() + assert selector.config["options"] == list(SwitchDeviceClass) + assert selector.config["translation_domain"] == DOMAIN + assert selector.config["translation_key"] == "device_class" + assert selector.config["sort"] + assert not selector.config["custom_value"] diff --git a/tests/helpers/test_selector.py b/tests/helpers/test_selector.py index 3ddbecaf48d..a11d5b671ff 100644 --- a/tests/helpers/test_selector.py +++ b/tests/helpers/test_selector.py @@ -638,6 +638,7 @@ def test_text_selector_schema(schema, valid_selections, invalid_selections) -> N { "options": ["red", "green", "blue"], "translation_key": "color", + "translation_domain": "homeassistant", }, ("red", "green", "blue"), ("cat", 0, None, ["red"]),