Add PM4 (particulates < 4μm) sensor and number device classes (#112867)

Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
Simon Roberts
2025-09-20 00:12:09 +10:00
committed by GitHub
parent 286763b998
commit ec148e0459
9 changed files with 28 additions and 2 deletions

View File

@@ -291,6 +291,12 @@ class NumberDeviceClass(StrEnum):
Unit of measurement: `μg/m³` Unit of measurement: `μg/m³`
""" """
PM4 = "pm4"
"""Particulate matter <= 4 μm.
Unit of measurement: `μg/m³`
"""
POWER_FACTOR = "power_factor" POWER_FACTOR = "power_factor"
"""Power factor. """Power factor.
@@ -510,6 +516,7 @@ DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
NumberDeviceClass.PM1: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, NumberDeviceClass.PM1: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
NumberDeviceClass.PM10: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, NumberDeviceClass.PM10: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
NumberDeviceClass.PM25: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, NumberDeviceClass.PM25: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
NumberDeviceClass.PM4: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
NumberDeviceClass.POWER_FACTOR: {PERCENTAGE, None}, NumberDeviceClass.POWER_FACTOR: {PERCENTAGE, None},
NumberDeviceClass.POWER: { NumberDeviceClass.POWER: {
UnitOfPower.MILLIWATT, UnitOfPower.MILLIWATT,

View File

@@ -326,6 +326,12 @@ class SensorDeviceClass(StrEnum):
Unit of measurement: `μg/m³` Unit of measurement: `μg/m³`
""" """
PM4 = "pm4"
"""Particulate matter <= 4 μm.
Unit of measurement: `μg/m³`
"""
POWER_FACTOR = "power_factor" POWER_FACTOR = "power_factor"
"""Power factor. """Power factor.
@@ -621,6 +627,7 @@ DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = {
SensorDeviceClass.PM1: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, SensorDeviceClass.PM1: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
SensorDeviceClass.PM10: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, SensorDeviceClass.PM10: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
SensorDeviceClass.PM25: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER}, SensorDeviceClass.PM25: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
SensorDeviceClass.PM4: {CONCENTRATION_MICROGRAMS_PER_CUBIC_METER},
SensorDeviceClass.POWER_FACTOR: {PERCENTAGE, None}, SensorDeviceClass.POWER_FACTOR: {PERCENTAGE, None},
SensorDeviceClass.POWER: { SensorDeviceClass.POWER: {
UnitOfPower.MILLIWATT, UnitOfPower.MILLIWATT,
@@ -755,6 +762,7 @@ DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
SensorDeviceClass.PM1: {SensorStateClass.MEASUREMENT}, SensorDeviceClass.PM1: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.PM10: {SensorStateClass.MEASUREMENT}, SensorDeviceClass.PM10: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.PM25: {SensorStateClass.MEASUREMENT}, SensorDeviceClass.PM25: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.PM4: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.POWER_FACTOR: {SensorStateClass.MEASUREMENT}, SensorDeviceClass.POWER_FACTOR: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.POWER: {SensorStateClass.MEASUREMENT}, SensorDeviceClass.POWER: {SensorStateClass.MEASUREMENT},
SensorDeviceClass.PRECIPITATION: set(SensorStateClass), SensorDeviceClass.PRECIPITATION: set(SensorStateClass),

View File

@@ -65,6 +65,7 @@ CONF_IS_PH = "is_ph"
CONF_IS_PM1 = "is_pm1" CONF_IS_PM1 = "is_pm1"
CONF_IS_PM10 = "is_pm10" CONF_IS_PM10 = "is_pm10"
CONF_IS_PM25 = "is_pm25" CONF_IS_PM25 = "is_pm25"
CONF_IS_PM4 = "is_pm4"
CONF_IS_POWER = "is_power" CONF_IS_POWER = "is_power"
CONF_IS_POWER_FACTOR = "is_power_factor" CONF_IS_POWER_FACTOR = "is_power_factor"
CONF_IS_PRECIPITATION = "is_precipitation" CONF_IS_PRECIPITATION = "is_precipitation"
@@ -126,6 +127,7 @@ ENTITY_CONDITIONS = {
SensorDeviceClass.PM1: [{CONF_TYPE: CONF_IS_PM1}], SensorDeviceClass.PM1: [{CONF_TYPE: CONF_IS_PM1}],
SensorDeviceClass.PM10: [{CONF_TYPE: CONF_IS_PM10}], SensorDeviceClass.PM10: [{CONF_TYPE: CONF_IS_PM10}],
SensorDeviceClass.PM25: [{CONF_TYPE: CONF_IS_PM25}], SensorDeviceClass.PM25: [{CONF_TYPE: CONF_IS_PM25}],
SensorDeviceClass.PM4: [{CONF_TYPE: CONF_IS_PM4}],
SensorDeviceClass.PRECIPITATION: [{CONF_TYPE: CONF_IS_PRECIPITATION}], SensorDeviceClass.PRECIPITATION: [{CONF_TYPE: CONF_IS_PRECIPITATION}],
SensorDeviceClass.PRECIPITATION_INTENSITY: [ SensorDeviceClass.PRECIPITATION_INTENSITY: [
{CONF_TYPE: CONF_IS_PRECIPITATION_INTENSITY} {CONF_TYPE: CONF_IS_PRECIPITATION_INTENSITY}
@@ -195,6 +197,7 @@ CONDITION_SCHEMA = vol.All(
CONF_IS_PM1, CONF_IS_PM1,
CONF_IS_PM10, CONF_IS_PM10,
CONF_IS_PM25, CONF_IS_PM25,
CONF_IS_PM4,
CONF_IS_PRECIPITATION, CONF_IS_PRECIPITATION,
CONF_IS_PRECIPITATION_INTENSITY, CONF_IS_PRECIPITATION_INTENSITY,
CONF_IS_PRESSURE, CONF_IS_PRESSURE,

View File

@@ -64,6 +64,7 @@ CONF_PH = "ph"
CONF_PM1 = "pm1" CONF_PM1 = "pm1"
CONF_PM10 = "pm10" CONF_PM10 = "pm10"
CONF_PM25 = "pm25" CONF_PM25 = "pm25"
CONF_PM4 = "pm4"
CONF_POWER = "power" CONF_POWER = "power"
CONF_POWER_FACTOR = "power_factor" CONF_POWER_FACTOR = "power_factor"
CONF_PRECIPITATION = "precipitation" CONF_PRECIPITATION = "precipitation"
@@ -123,6 +124,7 @@ ENTITY_TRIGGERS = {
SensorDeviceClass.PM1: [{CONF_TYPE: CONF_PM1}], SensorDeviceClass.PM1: [{CONF_TYPE: CONF_PM1}],
SensorDeviceClass.PM10: [{CONF_TYPE: CONF_PM10}], SensorDeviceClass.PM10: [{CONF_TYPE: CONF_PM10}],
SensorDeviceClass.PM25: [{CONF_TYPE: CONF_PM25}], SensorDeviceClass.PM25: [{CONF_TYPE: CONF_PM25}],
SensorDeviceClass.PM4: [{CONF_TYPE: CONF_PM4}],
SensorDeviceClass.POWER: [{CONF_TYPE: CONF_POWER}], SensorDeviceClass.POWER: [{CONF_TYPE: CONF_POWER}],
SensorDeviceClass.POWER_FACTOR: [{CONF_TYPE: CONF_POWER_FACTOR}], SensorDeviceClass.POWER_FACTOR: [{CONF_TYPE: CONF_POWER_FACTOR}],
SensorDeviceClass.PRECIPITATION: [{CONF_TYPE: CONF_PRECIPITATION}], SensorDeviceClass.PRECIPITATION: [{CONF_TYPE: CONF_PRECIPITATION}],
@@ -193,6 +195,7 @@ TRIGGER_SCHEMA = vol.All(
CONF_PM1, CONF_PM1,
CONF_PM10, CONF_PM10,
CONF_PM25, CONF_PM25,
CONF_PM4,
CONF_POWER, CONF_POWER,
CONF_POWER_FACTOR, CONF_POWER_FACTOR,
CONF_PRECIPITATION, CONF_PRECIPITATION,

View File

@@ -34,6 +34,7 @@
"is_pm1": "Current {entity_name} PM1 concentration level", "is_pm1": "Current {entity_name} PM1 concentration level",
"is_pm10": "Current {entity_name} PM10 concentration level", "is_pm10": "Current {entity_name} PM10 concentration level",
"is_pm25": "Current {entity_name} PM2.5 concentration level", "is_pm25": "Current {entity_name} PM2.5 concentration level",
"is_pm4": "Current {entity_name} PM4 concentration level",
"is_power": "Current {entity_name} power", "is_power": "Current {entity_name} power",
"is_power_factor": "Current {entity_name} power factor", "is_power_factor": "Current {entity_name} power factor",
"is_precipitation": "Current {entity_name} precipitation", "is_precipitation": "Current {entity_name} precipitation",
@@ -90,6 +91,7 @@
"pm1": "{entity_name} PM1 concentration changes", "pm1": "{entity_name} PM1 concentration changes",
"pm10": "{entity_name} PM10 concentration changes", "pm10": "{entity_name} PM10 concentration changes",
"pm25": "{entity_name} PM2.5 concentration changes", "pm25": "{entity_name} PM2.5 concentration changes",
"pm4": "{entity_name} PM4 concentration changes",
"power": "{entity_name} power changes", "power": "{entity_name} power changes",
"power_factor": "{entity_name} power factor changes", "power_factor": "{entity_name} power factor changes",
"precipitation": "{entity_name} precipitation changes", "precipitation": "{entity_name} precipitation changes",

View File

@@ -80,6 +80,7 @@ UNITS_OF_MEASUREMENT = {
SensorDeviceClass.PM10: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, SensorDeviceClass.PM10: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
SensorDeviceClass.PM1: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, SensorDeviceClass.PM1: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
SensorDeviceClass.PM25: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, SensorDeviceClass.PM25: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
SensorDeviceClass.PM4: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
SensorDeviceClass.POWER: UnitOfPower.KILO_WATT, SensorDeviceClass.POWER: UnitOfPower.KILO_WATT,
SensorDeviceClass.POWER_FACTOR: PERCENTAGE, SensorDeviceClass.POWER_FACTOR: PERCENTAGE,
SensorDeviceClass.PRECIPITATION: UnitOfPrecipitationDepth.MILLIMETERS, SensorDeviceClass.PRECIPITATION: UnitOfPrecipitationDepth.MILLIMETERS,

View File

@@ -125,7 +125,7 @@ async def test_get_conditions(
conditions = await async_get_device_automations( conditions = await async_get_device_automations(
hass, DeviceAutomationType.CONDITION, device_entry.id hass, DeviceAutomationType.CONDITION, device_entry.id
) )
assert len(conditions) == 55 assert len(conditions) == 56
assert conditions == unordered(expected_conditions) assert conditions == unordered(expected_conditions)

View File

@@ -126,7 +126,7 @@ async def test_get_triggers(
triggers = await async_get_device_automations( triggers = await async_get_device_automations(
hass, DeviceAutomationType.TRIGGER, device_entry.id hass, DeviceAutomationType.TRIGGER, device_entry.id
) )
assert len(triggers) == 55 assert len(triggers) == 56
assert triggers == unordered(expected_triggers) assert triggers == unordered(expected_triggers)

View File

@@ -2158,6 +2158,7 @@ async def test_non_numeric_device_class_with_unit_of_measurement(
SensorDeviceClass.PM1, SensorDeviceClass.PM1,
SensorDeviceClass.PM10, SensorDeviceClass.PM10,
SensorDeviceClass.PM25, SensorDeviceClass.PM25,
SensorDeviceClass.PM4,
SensorDeviceClass.POWER_FACTOR, SensorDeviceClass.POWER_FACTOR,
SensorDeviceClass.POWER, SensorDeviceClass.POWER,
SensorDeviceClass.PRECIPITATION_INTENSITY, SensorDeviceClass.PRECIPITATION_INTENSITY,
@@ -3024,6 +3025,7 @@ def test_device_class_converters_are_complete() -> None:
SensorDeviceClass.PM1, SensorDeviceClass.PM1,
SensorDeviceClass.PM10, SensorDeviceClass.PM10,
SensorDeviceClass.PM25, SensorDeviceClass.PM25,
SensorDeviceClass.PM4,
SensorDeviceClass.SIGNAL_STRENGTH, SensorDeviceClass.SIGNAL_STRENGTH,
SensorDeviceClass.SOUND_PRESSURE, SensorDeviceClass.SOUND_PRESSURE,
SensorDeviceClass.SULPHUR_DIOXIDE, SensorDeviceClass.SULPHUR_DIOXIDE,