From 7c448416e1667427491ff55d3389a4c049a08106 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 27 Sep 2022 17:19:34 +0100 Subject: [PATCH] Add speed to SensorDeviceClass (#77953) * Add speed to SensorDeviceClass * Adjust recorder * Adjust tests * Adjust sensor UNIT_CONVERTERS * Add tests * Add websocket tests * Update strings.json --- .../components/recorder/statistics.py | 3 ++ .../components/recorder/websocket_api.py | 2 ++ homeassistant/components/sensor/__init__.py | 6 ++++ .../components/sensor/device_condition.py | 2 ++ .../components/sensor/device_trigger.py | 2 ++ homeassistant/components/sensor/recorder.py | 2 ++ homeassistant/components/sensor/strings.json | 2 ++ .../components/sensor/translations/en.json | 2 ++ .../components/recorder/test_websocket_api.py | 17 +++++++++++ tests/components/sensor/test_init.py | 29 +++++++++++++++++++ tests/components/sensor/test_recorder.py | 8 +++++ 11 files changed, 75 insertions(+) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 6f82626dc37..8d679cbeae6 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -37,6 +37,7 @@ from homeassistant.util.unit_conversion import ( EnergyConverter, PowerConverter, PressureConverter, + SpeedConverter, TemperatureConverter, VolumeConverter, ) @@ -127,6 +128,7 @@ STATISTIC_UNIT_TO_UNIT_CLASS: dict[str | None, str] = { EnergyConverter.NORMALIZED_UNIT: EnergyConverter.UNIT_CLASS, PowerConverter.NORMALIZED_UNIT: PowerConverter.UNIT_CLASS, PressureConverter.NORMALIZED_UNIT: PressureConverter.UNIT_CLASS, + SpeedConverter.NORMALIZED_UNIT: SpeedConverter.UNIT_CLASS, TemperatureConverter.NORMALIZED_UNIT: TemperatureConverter.UNIT_CLASS, VolumeConverter.NORMALIZED_UNIT: VolumeConverter.UNIT_CLASS, } @@ -136,6 +138,7 @@ STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, type[BaseUnitConverter]] = { EnergyConverter.NORMALIZED_UNIT: EnergyConverter, PowerConverter.NORMALIZED_UNIT: PowerConverter, PressureConverter.NORMALIZED_UNIT: PressureConverter, + SpeedConverter.NORMALIZED_UNIT: SpeedConverter, TemperatureConverter.NORMALIZED_UNIT: TemperatureConverter, VolumeConverter.NORMALIZED_UNIT: VolumeConverter, } diff --git a/homeassistant/components/recorder/websocket_api.py b/homeassistant/components/recorder/websocket_api.py index 2b500fb428a..2ede11cf887 100644 --- a/homeassistant/components/recorder/websocket_api.py +++ b/homeassistant/components/recorder/websocket_api.py @@ -25,6 +25,7 @@ from homeassistant.util.unit_conversion import ( EnergyConverter, PowerConverter, PressureConverter, + SpeedConverter, TemperatureConverter, ) @@ -128,6 +129,7 @@ async def ws_handle_get_statistics_during_period( vol.Optional("energy"): vol.In(EnergyConverter.VALID_UNITS), vol.Optional("power"): vol.In(PowerConverter.VALID_UNITS), vol.Optional("pressure"): vol.In(PressureConverter.VALID_UNITS), + vol.Optional("speed"): vol.In(SpeedConverter.VALID_UNITS), vol.Optional("temperature"): vol.In(TemperatureConverter.VALID_UNITS), vol.Optional("volume"): vol.Any(VOLUME_CUBIC_FEET, VOLUME_CUBIC_METERS), } diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 29157d01660..1babc2d0084 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -62,6 +62,7 @@ from homeassistant.util.unit_conversion import ( BaseUnitConverter, DistanceConverter, PressureConverter, + SpeedConverter, TemperatureConverter, ) @@ -166,6 +167,9 @@ class SensorDeviceClass(StrEnum): # signal strength (dB/dBm) SIGNAL_STRENGTH = "signal_strength" + # speed (SPEED_*) + SPEED = "speed" + # Amount of SO2 (µg/m³) SULPHUR_DIOXIDE = "sulphur_dioxide" @@ -215,12 +219,14 @@ STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass] UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = { SensorDeviceClass.DISTANCE: DistanceConverter, SensorDeviceClass.PRESSURE: PressureConverter, + SensorDeviceClass.SPEED: SpeedConverter, SensorDeviceClass.TEMPERATURE: TemperatureConverter, } UNIT_RATIOS: dict[str, dict[str, float]] = { SensorDeviceClass.DISTANCE: DistanceConverter.UNIT_CONVERSION, SensorDeviceClass.PRESSURE: PressureConverter.UNIT_CONVERSION, + SensorDeviceClass.SPEED: SpeedConverter.UNIT_CONVERSION, SensorDeviceClass.TEMPERATURE: { TEMP_CELSIUS: 1.0, TEMP_FAHRENHEIT: 1.8, diff --git a/homeassistant/components/sensor/device_condition.py b/homeassistant/components/sensor/device_condition.py index 6ecce8b1a13..08aeda46ba2 100644 --- a/homeassistant/components/sensor/device_condition.py +++ b/homeassistant/components/sensor/device_condition.py @@ -53,6 +53,7 @@ CONF_IS_PM25 = "is_pm25" CONF_IS_POWER = "is_power" CONF_IS_POWER_FACTOR = "is_power_factor" CONF_IS_PRESSURE = "is_pressure" +CONF_IS_SPEED = "is_speed" CONF_IS_REACTIVE_POWER = "is_reactive_power" CONF_IS_SIGNAL_STRENGTH = "is_signal_strength" CONF_IS_SULPHUR_DIOXIDE = "is_sulphur_dioxide" @@ -86,6 +87,7 @@ ENTITY_CONDITIONS = { SensorDeviceClass.PRESSURE: [{CONF_TYPE: CONF_IS_PRESSURE}], SensorDeviceClass.REACTIVE_POWER: [{CONF_TYPE: CONF_IS_REACTIVE_POWER}], SensorDeviceClass.SIGNAL_STRENGTH: [{CONF_TYPE: CONF_IS_SIGNAL_STRENGTH}], + SensorDeviceClass.SPEED: [{CONF_TYPE: CONF_IS_SPEED}], SensorDeviceClass.SULPHUR_DIOXIDE: [{CONF_TYPE: CONF_IS_SULPHUR_DIOXIDE}], SensorDeviceClass.TEMPERATURE: [{CONF_TYPE: CONF_IS_TEMPERATURE}], SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: [ diff --git a/homeassistant/components/sensor/device_trigger.py b/homeassistant/components/sensor/device_trigger.py index cd009842b97..a1275b202ed 100644 --- a/homeassistant/components/sensor/device_trigger.py +++ b/homeassistant/components/sensor/device_trigger.py @@ -54,6 +54,7 @@ CONF_POWER_FACTOR = "power_factor" CONF_PRESSURE = "pressure" CONF_REACTIVE_POWER = "reactive_power" CONF_SIGNAL_STRENGTH = "signal_strength" +CONF_SPEED = "speed" CONF_SULPHUR_DIOXIDE = "sulphur_dioxide" CONF_TEMPERATURE = "temperature" CONF_VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" @@ -85,6 +86,7 @@ ENTITY_TRIGGERS = { SensorDeviceClass.PRESSURE: [{CONF_TYPE: CONF_PRESSURE}], SensorDeviceClass.REACTIVE_POWER: [{CONF_TYPE: CONF_REACTIVE_POWER}], SensorDeviceClass.SIGNAL_STRENGTH: [{CONF_TYPE: CONF_SIGNAL_STRENGTH}], + SensorDeviceClass.SPEED: [{CONF_TYPE: CONF_SPEED}], SensorDeviceClass.SULPHUR_DIOXIDE: [{CONF_TYPE: CONF_SULPHUR_DIOXIDE}], SensorDeviceClass.TEMPERATURE: [{CONF_TYPE: CONF_TEMPERATURE}], SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: [ diff --git a/homeassistant/components/sensor/recorder.py b/homeassistant/components/sensor/recorder.py index 5196dc562df..d7c8ae38c5c 100644 --- a/homeassistant/components/sensor/recorder.py +++ b/homeassistant/components/sensor/recorder.py @@ -34,6 +34,7 @@ from homeassistant.util.unit_conversion import ( EnergyConverter, PowerConverter, PressureConverter, + SpeedConverter, TemperatureConverter, VolumeConverter, ) @@ -62,6 +63,7 @@ UNIT_CONVERTERS: dict[str, type[BaseUnitConverter]] = { SensorDeviceClass.ENERGY: EnergyConverter, SensorDeviceClass.POWER: PowerConverter, SensorDeviceClass.PRESSURE: PressureConverter, + SensorDeviceClass.SPEED: SpeedConverter, SensorDeviceClass.TEMPERATURE: TemperatureConverter, SensorDeviceClass.GAS: VolumeConverter, } diff --git a/homeassistant/components/sensor/strings.json b/homeassistant/components/sensor/strings.json index d79f1035f62..6a371177321 100644 --- a/homeassistant/components/sensor/strings.json +++ b/homeassistant/components/sensor/strings.json @@ -22,6 +22,7 @@ "is_pressure": "Current {entity_name} pressure", "is_reactive_power": "Current {entity_name} reactive power", "is_signal_strength": "Current {entity_name} signal strength", + "is_speed": "Current {entity_name} speed", "is_sulphur_dioxide": "Current {entity_name} sulphur dioxide concentration level", "is_temperature": "Current {entity_name} temperature", "is_current": "Current {entity_name} current", @@ -53,6 +54,7 @@ "pressure": "{entity_name} pressure changes", "reactive_power": "{entity_name} reactive power changes", "signal_strength": "{entity_name} signal strength changes", + "speed": "{entity_name} speed changes", "sulphur_dioxide": "{entity_name} sulphur dioxide concentration changes", "temperature": "{entity_name} temperature changes", "current": "{entity_name} current changes", diff --git a/homeassistant/components/sensor/translations/en.json b/homeassistant/components/sensor/translations/en.json index 7e2223521eb..0c4af7c1c32 100644 --- a/homeassistant/components/sensor/translations/en.json +++ b/homeassistant/components/sensor/translations/en.json @@ -25,6 +25,7 @@ "is_pressure": "Current {entity_name} pressure", "is_reactive_power": "Current {entity_name} reactive power", "is_signal_strength": "Current {entity_name} signal strength", + "is_speed": "Current {entity_name} speed", "is_sulphur_dioxide": "Current {entity_name} sulphur dioxide concentration level", "is_temperature": "Current {entity_name} temperature", "is_value": "Current {entity_name} value", @@ -56,6 +57,7 @@ "pressure": "{entity_name} pressure changes", "reactive_power": "{entity_name} reactive power changes", "signal_strength": "{entity_name} signal strength changes", + "speed": "{entity_name} speed changes", "sulphur_dioxide": "{entity_name} sulphur dioxide concentration changes", "temperature": "{entity_name} temperature changes", "value": "{entity_name} value changes", diff --git a/tests/components/recorder/test_websocket_api.py b/tests/components/recorder/test_websocket_api.py index 4790889951b..095b5c15d27 100644 --- a/tests/components/recorder/test_websocket_api.py +++ b/tests/components/recorder/test_websocket_api.py @@ -80,6 +80,16 @@ PRESSURE_SENSOR_PA_ATTRIBUTES = { "state_class": "measurement", "unit_of_measurement": "Pa", } +SPEED_SENSOR_KPH_ATTRIBUTES = { + "device_class": "speed", + "state_class": "measurement", + "unit_of_measurement": "km/h", +} +SPEED_SENSOR_MPH_ATTRIBUTES = { + "device_class": "speed", + "state_class": "measurement", + "unit_of_measurement": "mph", +} TEMPERATURE_SENSOR_C_ATTRIBUTES = { "device_class": "temperature", "state_class": "measurement", @@ -159,6 +169,9 @@ async def test_statistics_during_period(hass, hass_ws_client, recorder_mock): (PRESSURE_SENSOR_HPA_ATTRIBUTES, 10, 10, {"pressure": "Pa"}, 1000), (PRESSURE_SENSOR_HPA_ATTRIBUTES, 10, 10, {"pressure": "hPa"}, 10), (PRESSURE_SENSOR_HPA_ATTRIBUTES, 10, 10, {"pressure": "psi"}, 1000 / 6894.757), + (SPEED_SENSOR_KPH_ATTRIBUTES, 10, 10, {"speed": "m/s"}, 2.77778), + (SPEED_SENSOR_KPH_ATTRIBUTES, 10, 10, {"speed": "km/h"}, 10), + (SPEED_SENSOR_KPH_ATTRIBUTES, 10, 10, {"speed": "mph"}, 6.21371), (TEMPERATURE_SENSOR_C_ATTRIBUTES, 10, 10, {"temperature": "°C"}, 10), (TEMPERATURE_SENSOR_C_ATTRIBUTES, 10, 10, {"temperature": "°F"}, 50), (TEMPERATURE_SENSOR_C_ATTRIBUTES, 10, 10, {"temperature": "K"}, 283.15), @@ -564,6 +577,8 @@ async def test_statistics_during_period_bad_end_time( (METRIC_SYSTEM, POWER_SENSOR_KW_ATTRIBUTES, "kW", "W", "power"), (IMPERIAL_SYSTEM, PRESSURE_SENSOR_HPA_ATTRIBUTES, "hPa", "Pa", "pressure"), (METRIC_SYSTEM, PRESSURE_SENSOR_HPA_ATTRIBUTES, "hPa", "Pa", "pressure"), + (IMPERIAL_SYSTEM, SPEED_SENSOR_KPH_ATTRIBUTES, "km/h", "m/s", "speed"), + (METRIC_SYSTEM, SPEED_SENSOR_KPH_ATTRIBUTES, "km/h", "m/s", "speed"), (IMPERIAL_SYSTEM, TEMPERATURE_SENSOR_C_ATTRIBUTES, "°C", "°C", "temperature"), (METRIC_SYSTEM, TEMPERATURE_SENSOR_C_ATTRIBUTES, "°C", "°C", "temperature"), (IMPERIAL_SYSTEM, TEMPERATURE_SENSOR_F_ATTRIBUTES, "°F", "°C", "temperature"), @@ -1357,6 +1372,8 @@ async def test_backup_end_without_start( (METRIC_SYSTEM, POWER_SENSOR_KW_ATTRIBUTES, "W", "power"), (METRIC_SYSTEM, PRESSURE_SENSOR_PA_ATTRIBUTES, "Pa", "pressure"), (METRIC_SYSTEM, PRESSURE_SENSOR_HPA_ATTRIBUTES, "Pa", "pressure"), + (METRIC_SYSTEM, SPEED_SENSOR_KPH_ATTRIBUTES, "m/s", "speed"), + (METRIC_SYSTEM, SPEED_SENSOR_MPH_ATTRIBUTES, "m/s", "speed"), (METRIC_SYSTEM, TEMPERATURE_SENSOR_C_ATTRIBUTES, "°C", "temperature"), (METRIC_SYSTEM, TEMPERATURE_SENSOR_F_ATTRIBUTES, "°C", "temperature"), ], diff --git a/tests/components/sensor/test_init.py b/tests/components/sensor/test_init.py index ad672b56801..47cfc0ef148 100644 --- a/tests/components/sensor/test_init.py +++ b/tests/components/sensor/test_init.py @@ -16,6 +16,10 @@ from homeassistant.const import ( PRESSURE_INHG, PRESSURE_KPA, PRESSURE_MMHG, + SPEED_INCHES_PER_HOUR, + SPEED_KILOMETERS_PER_HOUR, + SPEED_MILES_PER_HOUR, + SPEED_MILLIMETERS_PER_DAY, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT, @@ -520,6 +524,31 @@ async def test_custom_unit( 1000, SensorDeviceClass.PRESSURE, ), + # Speed + ( + SPEED_KILOMETERS_PER_HOUR, + SPEED_MILES_PER_HOUR, + SPEED_MILES_PER_HOUR, + 100, + 62, + SensorDeviceClass.SPEED, + ), + ( + SPEED_MILLIMETERS_PER_DAY, + SPEED_INCHES_PER_HOUR, + SPEED_INCHES_PER_HOUR, + 78, + 0.13, + SensorDeviceClass.SPEED, + ), + ( + SPEED_KILOMETERS_PER_HOUR, + "peer_distance", + SPEED_KILOMETERS_PER_HOUR, + 100, + 100, + SensorDeviceClass.SPEED, + ), ], ) async def test_custom_unit_change( diff --git a/tests/components/sensor/test_recorder.py b/tests/components/sensor/test_recorder.py index 1bb95888e92..ad11206f583 100644 --- a/tests/components/sensor/test_recorder.py +++ b/tests/components/sensor/test_recorder.py @@ -94,6 +94,8 @@ def set_time_zone(): ("pressure", "mbar", "mbar", "Pa", "pressure", 13.050847, -10, 30), ("pressure", "inHg", "inHg", "Pa", "pressure", 13.050847, -10, 30), ("pressure", "psi", "psi", "Pa", "pressure", 13.050847, -10, 30), + ("speed", "m/s", "m/s", "m/s", "speed", 13.050847, -10, 30), + ("speed", "mph", "mph", "m/s", "speed", 13.050847, -10, 30), ("temperature", "°C", "°C", "°C", "temperature", 13.050847, -10, 30), ("temperature", "°F", "°F", "°C", "temperature", 13.050847, -10, 30), ], @@ -1563,6 +1565,8 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog): ("pressure", "mbar", 30), ("pressure", "inHg", 30), ("pressure", "psi", 30), + ("speed", "m/s", 30), + ("speed", "mph", 30), ("temperature", "°C", 30), ("temperature", "°F", 30), ], @@ -1652,6 +1656,8 @@ def test_compile_hourly_statistics_partially_unavailable(hass_recorder, caplog): ("pressure", "mbar", 30), ("pressure", "inHg", 30), ("pressure", "psi", 30), + ("speed", "m/s", 30), + ("speed", "mph", 30), ("temperature", "°C", 30), ("temperature", "°F", 30), ], @@ -1741,6 +1747,8 @@ def test_compile_hourly_statistics_fails(hass_recorder, caplog): ("measurement", "pressure", "mbar", "mbar", "Pa", "pressure", "mean"), ("measurement", "pressure", "inHg", "inHg", "Pa", "pressure", "mean"), ("measurement", "pressure", "psi", "psi", "Pa", "pressure", "mean"), + ("measurement", "speed", "m/s", "m/s", "m/s", "speed", "mean"), + ("measurement", "speed", "mph", "mph", "m/s", "speed", "mean"), ("measurement", "temperature", "°C", "°C", "°C", "temperature", "mean"), ("measurement", "temperature", "°F", "°F", "°C", "temperature", "mean"), ],