From 78212245ddc0c23ed30307fd5ca5b8331b8f01c9 Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Thu, 22 Jan 2026 11:34:29 +0100 Subject: [PATCH] Add ppb as a valid UOM for sensor/number NO device class (#161379) --- homeassistant/components/number/const.py | 7 +++++-- .../components/recorder/statistics.py | 2 ++ .../components/recorder/websocket_api.py | 4 ++++ homeassistant/components/sensor/const.py | 9 ++++++-- homeassistant/util/unit_conversion.py | 17 +++++++++++++++ tests/components/sensor/test_init.py | 1 - tests/util/test_unit_conversion.py | 21 +++++++++++++++++++ 7 files changed, 56 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/number/const.py b/homeassistant/components/number/const.py index 4400e76191b..225870d8272 100644 --- a/homeassistant/components/number/const.py +++ b/homeassistant/components/number/const.py @@ -253,7 +253,7 @@ class NumberDeviceClass(StrEnum): NITROGEN_MONOXIDE = "nitrogen_monoxide" """Amount of NO. - Unit of measurement: `μg/m³` + Unit of measurement: `ppb` (parts per billion), `μg/m³` """ NITROUS_OXIDE = "nitrous_oxide" @@ -521,7 +521,10 @@ DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = { CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, }, - NumberDeviceClass.NITROGEN_MONOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + NumberDeviceClass.NITROGEN_MONOXIDE: { + CONCENTRATION_PARTS_PER_BILLION, + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + }, NumberDeviceClass.NITROUS_OXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, NumberDeviceClass.OZONE: { CONCENTRATION_PARTS_PER_BILLION, diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 73964eb7e0e..1d061e40eb9 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -60,6 +60,7 @@ from homeassistant.util.unit_conversion import ( MassConverter, MassVolumeConcentrationConverter, NitrogenDioxideConcentrationConverter, + NitrogenMonoxideConcentrationConverter, OzoneConcentrationConverter, PowerConverter, PressureConverter, @@ -228,6 +229,7 @@ _PRIMARY_UNIT_CONVERTERS: list[type[BaseUnitConverter]] = [ _SECONDARY_UNIT_CONVERTERS: list[type[BaseUnitConverter]] = [ CarbonMonoxideConcentrationConverter, NitrogenDioxideConcentrationConverter, + NitrogenMonoxideConcentrationConverter, OzoneConcentrationConverter, SulphurDioxideConcentrationConverter, TemperatureDeltaConverter, diff --git a/homeassistant/components/recorder/websocket_api.py b/homeassistant/components/recorder/websocket_api.py index 0689caaf5bc..4218ed5eb21 100644 --- a/homeassistant/components/recorder/websocket_api.py +++ b/homeassistant/components/recorder/websocket_api.py @@ -34,6 +34,7 @@ from homeassistant.util.unit_conversion import ( MassConverter, MassVolumeConcentrationConverter, NitrogenDioxideConcentrationConverter, + NitrogenMonoxideConcentrationConverter, OzoneConcentrationConverter, PowerConverter, PressureConverter, @@ -94,6 +95,9 @@ UNIT_SCHEMA = vol.Schema( vol.Optional("nitrogen_dioxide"): vol.In( NitrogenDioxideConcentrationConverter.VALID_UNITS ), + vol.Optional("nitrogen_monoxide"): vol.In( + NitrogenMonoxideConcentrationConverter.VALID_UNITS + ), vol.Optional("ozone"): vol.In(OzoneConcentrationConverter.VALID_UNITS), vol.Optional("power"): vol.In(PowerConverter.VALID_UNITS), vol.Optional("pressure"): vol.In(PressureConverter.VALID_UNITS), diff --git a/homeassistant/components/sensor/const.py b/homeassistant/components/sensor/const.py index be6cc4ea3a5..e1109089c27 100644 --- a/homeassistant/components/sensor/const.py +++ b/homeassistant/components/sensor/const.py @@ -64,6 +64,7 @@ from homeassistant.util.unit_conversion import ( MassConverter, MassVolumeConcentrationConverter, NitrogenDioxideConcentrationConverter, + NitrogenMonoxideConcentrationConverter, OzoneConcentrationConverter, PowerConverter, PressureConverter, @@ -291,7 +292,7 @@ class SensorDeviceClass(StrEnum): NITROGEN_MONOXIDE = "nitrogen_monoxide" """Amount of NO. - Unit of measurement: `μg/m³` + Unit of measurement: `ppb` (parts per billion), `μg/m³` """ NITROUS_OXIDE = "nitrous_oxide" @@ -566,6 +567,7 @@ UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] = SensorDeviceClass.ENERGY_STORAGE: EnergyConverter, SensorDeviceClass.GAS: VolumeConverter, SensorDeviceClass.NITROGEN_DIOXIDE: NitrogenDioxideConcentrationConverter, + SensorDeviceClass.NITROGEN_MONOXIDE: NitrogenMonoxideConcentrationConverter, SensorDeviceClass.OZONE: OzoneConcentrationConverter, SensorDeviceClass.POWER: PowerConverter, SensorDeviceClass.POWER_FACTOR: UnitlessRatioConverter, @@ -639,7 +641,10 @@ DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = { CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, }, - SensorDeviceClass.NITROGEN_MONOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, + SensorDeviceClass.NITROGEN_MONOXIDE: { + CONCENTRATION_PARTS_PER_BILLION, + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + }, SensorDeviceClass.NITROUS_OXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, SensorDeviceClass.OZONE: { CONCENTRATION_PARTS_PER_BILLION, diff --git a/homeassistant/util/unit_conversion.py b/homeassistant/util/unit_conversion.py index 5716bf5a885..0f8d3ac474c 100644 --- a/homeassistant/util/unit_conversion.py +++ b/homeassistant/util/unit_conversion.py @@ -104,6 +104,7 @@ _AMBIENT_IDEAL_GAS_MOLAR_VOLUME = ( # m3⋅mol⁻¹ # Molar masses in g⋅mol⁻¹ _CARBON_MONOXIDE_MOLAR_MASS = 28.01 _NITROGEN_DIOXIDE_MOLAR_MASS = 46.0055 +_NITROGEN_MONOXIDE_MOLAR_MASS = 30.0061 _OZONE_MOLAR_MASS = 48.00 _SULPHUR_DIOXIDE_MOLAR_MASS = 64.066 @@ -502,6 +503,22 @@ class NitrogenDioxideConcentrationConverter(BaseUnitConverter): } +class NitrogenMonoxideConcentrationConverter(BaseUnitConverter): + """Convert nitrogen monoxide ratio to mass per volume.""" + + UNIT_CLASS = "nitrogen_monoxide" + _UNIT_CONVERSION: dict[str | None, float] = { + CONCENTRATION_PARTS_PER_BILLION: 1e9, + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: ( + _NITROGEN_MONOXIDE_MOLAR_MASS / _AMBIENT_IDEAL_GAS_MOLAR_VOLUME * 1e6 + ), + } + VALID_UNITS = { + CONCENTRATION_PARTS_PER_BILLION, + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + } + + class OzoneConcentrationConverter(BaseUnitConverter): """Convert ozone ratio to mass per volume.""" diff --git a/tests/components/sensor/test_init.py b/tests/components/sensor/test_init.py index b25eaa0a4d0..9100df66c20 100644 --- a/tests/components/sensor/test_init.py +++ b/tests/components/sensor/test_init.py @@ -3107,7 +3107,6 @@ def test_device_class_converters_are_complete() -> None: SensorDeviceClass.IRRADIANCE, SensorDeviceClass.MOISTURE, SensorDeviceClass.MONETARY, - SensorDeviceClass.NITROGEN_MONOXIDE, SensorDeviceClass.NITROUS_OXIDE, SensorDeviceClass.PH, SensorDeviceClass.PM1, diff --git a/tests/util/test_unit_conversion.py b/tests/util/test_unit_conversion.py index 82a275e1d73..b8355669c22 100644 --- a/tests/util/test_unit_conversion.py +++ b/tests/util/test_unit_conversion.py @@ -57,6 +57,7 @@ from homeassistant.util.unit_conversion import ( MassConverter, MassVolumeConcentrationConverter, NitrogenDioxideConcentrationConverter, + NitrogenMonoxideConcentrationConverter, OzoneConcentrationConverter, PowerConverter, PressureConverter, @@ -107,6 +108,7 @@ _ALL_CONVERTERS: dict[type[BaseUnitConverter], list[str | None]] = { VolumeConverter, VolumeFlowRateConverter, NitrogenDioxideConcentrationConverter, + NitrogenMonoxideConcentrationConverter, SulphurDioxideConcentrationConverter, ) } @@ -169,6 +171,11 @@ _GET_UNIT_RATIO: dict[type[BaseUnitConverter], tuple[str | None, str | None, flo CONCENTRATION_PARTS_PER_BILLION, 1.912503, ), + NitrogenMonoxideConcentrationConverter: ( + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + CONCENTRATION_PARTS_PER_BILLION, + 1.247389, + ), OzoneConcentrationConverter: ( CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_PARTS_PER_BILLION, @@ -407,6 +414,20 @@ _CONVERTED_VALUE: dict[ CONCENTRATION_PARTS_PER_BILLION, ), ], + NitrogenMonoxideConcentrationConverter: [ + ( + 1, + CONCENTRATION_PARTS_PER_BILLION, + 1.247389, + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + ), + ( + 120, + CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, + 96.200906, + CONCENTRATION_PARTS_PER_BILLION, + ), + ], ConductivityConverter: [ ( 5,