Add concentration conversion support for mg/m³ (#145325)

This commit is contained in:
Franck Nijhof 2025-05-23 15:24:18 +02:00 committed by GitHub
parent 0c9b1b5c58
commit bca4793c69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 59 additions and 4 deletions

View File

@ -9,6 +9,7 @@ import voluptuous as vol
from homeassistant.const import ( from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_PARTS_PER_MILLION,
DEGREE, DEGREE,
@ -370,7 +371,7 @@ class NumberDeviceClass(StrEnum):
VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds"
"""Amount of VOC. """Amount of VOC.
Unit of measurement: `µg/` Unit of measurement: `µg/`, `mg/`
""" """
VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts" VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts"
@ -517,7 +518,8 @@ DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
NumberDeviceClass.SULPHUR_DIOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, NumberDeviceClass.SULPHUR_DIOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
NumberDeviceClass.TEMPERATURE: set(UnitOfTemperature), NumberDeviceClass.TEMPERATURE: set(UnitOfTemperature),
NumberDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: { NumberDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: {
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
}, },
NumberDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS: { NumberDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS: {
CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_BILLION,

View File

@ -55,6 +55,7 @@ from homeassistant.util.unit_conversion import (
EnergyDistanceConverter, EnergyDistanceConverter,
InformationConverter, InformationConverter,
MassConverter, MassConverter,
MassVolumeConcentrationConverter,
PowerConverter, PowerConverter,
PressureConverter, PressureConverter,
ReactiveEnergyConverter, ReactiveEnergyConverter,
@ -197,6 +198,9 @@ STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, type[BaseUnitConverter]] = {
BloodGlucoseConcentrationConverter.VALID_UNITS, BloodGlucoseConcentrationConverter.VALID_UNITS,
BloodGlucoseConcentrationConverter, BloodGlucoseConcentrationConverter,
), ),
**dict.fromkeys(
MassVolumeConcentrationConverter.VALID_UNITS, MassVolumeConcentrationConverter
),
**dict.fromkeys(ConductivityConverter.VALID_UNITS, ConductivityConverter), **dict.fromkeys(ConductivityConverter.VALID_UNITS, ConductivityConverter),
**dict.fromkeys(DataRateConverter.VALID_UNITS, DataRateConverter), **dict.fromkeys(DataRateConverter.VALID_UNITS, DataRateConverter),
**dict.fromkeys(DistanceConverter.VALID_UNITS, DistanceConverter), **dict.fromkeys(DistanceConverter.VALID_UNITS, DistanceConverter),

View File

@ -28,6 +28,7 @@ from homeassistant.util.unit_conversion import (
EnergyDistanceConverter, EnergyDistanceConverter,
InformationConverter, InformationConverter,
MassConverter, MassConverter,
MassVolumeConcentrationConverter,
PowerConverter, PowerConverter,
PressureConverter, PressureConverter,
ReactiveEnergyConverter, ReactiveEnergyConverter,
@ -62,6 +63,9 @@ UNIT_SCHEMA = vol.Schema(
vol.Optional("blood_glucose_concentration"): vol.In( vol.Optional("blood_glucose_concentration"): vol.In(
BloodGlucoseConcentrationConverter.VALID_UNITS BloodGlucoseConcentrationConverter.VALID_UNITS
), ),
vol.Optional("concentration"): vol.In(
MassVolumeConcentrationConverter.VALID_UNITS
),
vol.Optional("conductivity"): vol.In(ConductivityConverter.VALID_UNITS), vol.Optional("conductivity"): vol.In(ConductivityConverter.VALID_UNITS),
vol.Optional("data_rate"): vol.In(DataRateConverter.VALID_UNITS), vol.Optional("data_rate"): vol.In(DataRateConverter.VALID_UNITS),
vol.Optional("distance"): vol.In(DistanceConverter.VALID_UNITS), vol.Optional("distance"): vol.In(DistanceConverter.VALID_UNITS),

View File

@ -9,6 +9,7 @@ import voluptuous as vol
from homeassistant.const import ( from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_PARTS_PER_MILLION,
DEGREE, DEGREE,
@ -57,6 +58,7 @@ from homeassistant.util.unit_conversion import (
EnergyDistanceConverter, EnergyDistanceConverter,
InformationConverter, InformationConverter,
MassConverter, MassConverter,
MassVolumeConcentrationConverter,
PowerConverter, PowerConverter,
PressureConverter, PressureConverter,
ReactiveEnergyConverter, ReactiveEnergyConverter,
@ -400,7 +402,7 @@ class SensorDeviceClass(StrEnum):
VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds"
"""Amount of VOC. """Amount of VOC.
Unit of measurement: `µg/` Unit of measurement: `µg/`, `mg/`
""" """
VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts" VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts"
@ -540,6 +542,7 @@ UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] =
SensorDeviceClass.REACTIVE_ENERGY: ReactiveEnergyConverter, SensorDeviceClass.REACTIVE_ENERGY: ReactiveEnergyConverter,
SensorDeviceClass.SPEED: SpeedConverter, SensorDeviceClass.SPEED: SpeedConverter,
SensorDeviceClass.TEMPERATURE: TemperatureConverter, SensorDeviceClass.TEMPERATURE: TemperatureConverter,
SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: MassVolumeConcentrationConverter,
SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS: UnitlessRatioConverter, SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS: UnitlessRatioConverter,
SensorDeviceClass.VOLTAGE: ElectricPotentialConverter, SensorDeviceClass.VOLTAGE: ElectricPotentialConverter,
SensorDeviceClass.VOLUME: VolumeConverter, SensorDeviceClass.VOLUME: VolumeConverter,
@ -617,7 +620,8 @@ DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = {
SensorDeviceClass.SULPHUR_DIOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, SensorDeviceClass.SULPHUR_DIOXIDE: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
SensorDeviceClass.TEMPERATURE: set(UnitOfTemperature), SensorDeviceClass.TEMPERATURE: set(UnitOfTemperature),
SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: { SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: {
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
}, },
SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS: { SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS_PARTS: {
CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_BILLION,

View File

@ -7,6 +7,8 @@ from functools import lru_cache
from math import floor, log10 from math import floor, log10
from homeassistant.const import ( from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE, PERCENTAGE,
@ -686,6 +688,20 @@ class UnitlessRatioConverter(BaseUnitConverter):
} }
class MassVolumeConcentrationConverter(BaseUnitConverter):
"""Utility to convert mass volume concentration values."""
UNIT_CLASS = "concentration"
_UNIT_CONVERSION: dict[str | None, float] = {
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: 1000.0, # 1000 µg/m³ = 1 mg/m³
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: 1.0,
}
VALID_UNITS = {
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
}
class VolumeConverter(BaseUnitConverter): class VolumeConverter(BaseUnitConverter):
"""Utility to convert volume values.""" """Utility to convert volume values."""

View File

@ -8,6 +8,8 @@ from itertools import chain
import pytest import pytest
from homeassistant.const import ( from homeassistant.const import (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_PARTS_PER_MILLION,
PERCENTAGE, PERCENTAGE,
@ -48,6 +50,7 @@ from homeassistant.util.unit_conversion import (
EnergyDistanceConverter, EnergyDistanceConverter,
InformationConverter, InformationConverter,
MassConverter, MassConverter,
MassVolumeConcentrationConverter,
PowerConverter, PowerConverter,
PressureConverter, PressureConverter,
ReactiveEnergyConverter, ReactiveEnergyConverter,
@ -69,6 +72,7 @@ _ALL_CONVERTERS: dict[type[BaseUnitConverter], list[str | None]] = {
for converter in ( for converter in (
AreaConverter, AreaConverter,
BloodGlucoseConcentrationConverter, BloodGlucoseConcentrationConverter,
MassVolumeConcentrationConverter,
ConductivityConverter, ConductivityConverter,
DataRateConverter, DataRateConverter,
DistanceConverter, DistanceConverter,
@ -128,6 +132,11 @@ _GET_UNIT_RATIO: dict[type[BaseUnitConverter], tuple[str | None, str | None, flo
), ),
InformationConverter: (UnitOfInformation.BITS, UnitOfInformation.BYTES, 8), InformationConverter: (UnitOfInformation.BITS, UnitOfInformation.BYTES, 8),
MassConverter: (UnitOfMass.STONES, UnitOfMass.KILOGRAMS, 0.157473), MassConverter: (UnitOfMass.STONES, UnitOfMass.KILOGRAMS, 0.157473),
MassVolumeConcentrationConverter: (
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
1000,
),
PowerConverter: (UnitOfPower.WATT, UnitOfPower.KILO_WATT, 1000), PowerConverter: (UnitOfPower.WATT, UnitOfPower.KILO_WATT, 1000),
PressureConverter: (UnitOfPressure.HPA, UnitOfPressure.INHG, 33.86389), PressureConverter: (UnitOfPressure.HPA, UnitOfPressure.INHG, 33.86389),
ReactiveEnergyConverter: ( ReactiveEnergyConverter: (
@ -738,6 +747,22 @@ _CONVERTED_VALUE: dict[
(5, None, 5000000, CONCENTRATION_PARTS_PER_MILLION), (5, None, 5000000, CONCENTRATION_PARTS_PER_MILLION),
(5, PERCENTAGE, 0.05, None), (5, PERCENTAGE, 0.05, None),
], ],
MassVolumeConcentrationConverter: [
# 1000 µg/m³ = 1 mg/m³
(
1000,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
1,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
),
# 2 mg/m³ = 2000 µg/m³
(
2,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
2000,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
),
],
VolumeConverter: [ VolumeConverter: [
(5, UnitOfVolume.LITERS, 1.32086, UnitOfVolume.GALLONS), (5, UnitOfVolume.LITERS, 1.32086, UnitOfVolume.GALLONS),
(5, UnitOfVolume.GALLONS, 18.92706, UnitOfVolume.LITERS), (5, UnitOfVolume.GALLONS, 18.92706, UnitOfVolume.LITERS),