mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 23:57:06 +00:00
Add new device class for absolute humidity (#148567)
This commit is contained in:
parent
21b1122f83
commit
50047f0a4e
@ -8,6 +8,7 @@ from typing import Final
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
@ -78,6 +79,11 @@ class NumberDeviceClass(StrEnum):
|
||||
"""Device class for numbers."""
|
||||
|
||||
# NumberDeviceClass should be aligned with SensorDeviceClass
|
||||
ABSOLUTE_HUMIDITY = "absolute_humidity"
|
||||
"""Absolute humidity.
|
||||
|
||||
Unit of measurement: `g/m³`, `mg/m³`
|
||||
"""
|
||||
|
||||
APPARENT_POWER = "apparent_power"
|
||||
"""Apparent power.
|
||||
@ -452,6 +458,10 @@ class NumberDeviceClass(StrEnum):
|
||||
|
||||
DEVICE_CLASSES_SCHEMA: Final = vol.All(vol.Lower, vol.Coerce(NumberDeviceClass))
|
||||
DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
|
||||
NumberDeviceClass.ABSOLUTE_HUMIDITY: {
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
},
|
||||
NumberDeviceClass.APPARENT_POWER: set(UnitOfApparentPower),
|
||||
NumberDeviceClass.AQI: {None},
|
||||
NumberDeviceClass.AREA: set(UnitOfArea),
|
||||
|
@ -3,6 +3,9 @@
|
||||
"_": {
|
||||
"default": "mdi:ray-vertex"
|
||||
},
|
||||
"absolute_humidity": {
|
||||
"default": "mdi:water-opacity"
|
||||
},
|
||||
"apparent_power": {
|
||||
"default": "mdi:flash"
|
||||
},
|
||||
|
@ -31,6 +31,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"absolute_humidity": {
|
||||
"name": "[%key:component::sensor::entity_component::absolute_humidity::name%]"
|
||||
},
|
||||
"apparent_power": {
|
||||
"name": "[%key:component::sensor::entity_component::apparent_power::name%]"
|
||||
},
|
||||
|
@ -8,6 +8,7 @@ from typing import Final
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
@ -107,6 +108,12 @@ class SensorDeviceClass(StrEnum):
|
||||
"""
|
||||
|
||||
# Numerical device classes, these should be aligned with NumberDeviceClass
|
||||
ABSOLUTE_HUMIDITY = "absolute_humidity"
|
||||
"""Absolute humidity.
|
||||
|
||||
Unit of measurement: `g/m³`, `mg/m³`
|
||||
"""
|
||||
|
||||
APPARENT_POWER = "apparent_power"
|
||||
"""Apparent power.
|
||||
|
||||
@ -521,6 +528,7 @@ STATE_CLASSES_SCHEMA: Final = vol.All(vol.Lower, vol.Coerce(SensorStateClass))
|
||||
STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass]
|
||||
|
||||
UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] = {
|
||||
SensorDeviceClass.ABSOLUTE_HUMIDITY: MassVolumeConcentrationConverter,
|
||||
SensorDeviceClass.AREA: AreaConverter,
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: PressureConverter,
|
||||
SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION: BloodGlucoseConcentrationConverter,
|
||||
@ -554,6 +562,10 @@ UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] =
|
||||
}
|
||||
|
||||
DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = {
|
||||
SensorDeviceClass.ABSOLUTE_HUMIDITY: {
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
},
|
||||
SensorDeviceClass.APPARENT_POWER: set(UnitOfApparentPower),
|
||||
SensorDeviceClass.AQI: {None},
|
||||
SensorDeviceClass.AREA: set(UnitOfArea),
|
||||
@ -651,6 +663,7 @@ DEFAULT_PRECISION_LIMIT = 2
|
||||
# have 0 decimals, that one should be used and not mW, even though mW also should have
|
||||
# 0 decimals. Otherwise the smaller units will have more decimals than expected.
|
||||
UNITS_PRECISION = {
|
||||
SensorDeviceClass.ABSOLUTE_HUMIDITY: (CONCENTRATION_GRAMS_PER_CUBIC_METER, 1),
|
||||
SensorDeviceClass.APPARENT_POWER: (UnitOfApparentPower.VOLT_AMPERE, 0),
|
||||
SensorDeviceClass.AREA: (UnitOfArea.SQUARE_CENTIMETERS, 0),
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: (UnitOfPressure.PA, 0),
|
||||
@ -691,6 +704,7 @@ UNITS_PRECISION = {
|
||||
}
|
||||
|
||||
DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
|
||||
SensorDeviceClass.ABSOLUTE_HUMIDITY: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.APPARENT_POWER: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.AQI: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.AREA: set(SensorStateClass),
|
||||
|
@ -33,6 +33,7 @@ from . import ATTR_STATE_CLASS, DOMAIN, SensorDeviceClass
|
||||
|
||||
DEVICE_CLASS_NONE = "none"
|
||||
|
||||
CONF_IS_ABSOLUTE_HUMIDITY = "is_absolute_humidity"
|
||||
CONF_IS_APPARENT_POWER = "is_apparent_power"
|
||||
CONF_IS_AQI = "is_aqi"
|
||||
CONF_IS_AREA = "is_area"
|
||||
@ -88,6 +89,7 @@ CONF_IS_WIND_DIRECTION = "is_wind_direction"
|
||||
CONF_IS_WIND_SPEED = "is_wind_speed"
|
||||
|
||||
ENTITY_CONDITIONS = {
|
||||
SensorDeviceClass.ABSOLUTE_HUMIDITY: [{CONF_TYPE: CONF_IS_ABSOLUTE_HUMIDITY}],
|
||||
SensorDeviceClass.APPARENT_POWER: [{CONF_TYPE: CONF_IS_APPARENT_POWER}],
|
||||
SensorDeviceClass.AQI: [{CONF_TYPE: CONF_IS_AQI}],
|
||||
SensorDeviceClass.AREA: [{CONF_TYPE: CONF_IS_AREA}],
|
||||
@ -159,6 +161,7 @@ CONDITION_SCHEMA = vol.All(
|
||||
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
|
||||
vol.Required(CONF_TYPE): vol.In(
|
||||
[
|
||||
CONF_IS_ABSOLUTE_HUMIDITY,
|
||||
CONF_IS_APPARENT_POWER,
|
||||
CONF_IS_AQI,
|
||||
CONF_IS_AREA,
|
||||
|
@ -32,6 +32,7 @@ from . import ATTR_STATE_CLASS, DOMAIN, SensorDeviceClass
|
||||
|
||||
DEVICE_CLASS_NONE = "none"
|
||||
|
||||
CONF_ABSOLUTE_HUMIDITY = "absolute_humidity"
|
||||
CONF_APPARENT_POWER = "apparent_power"
|
||||
CONF_AQI = "aqi"
|
||||
CONF_AREA = "area"
|
||||
@ -87,6 +88,7 @@ CONF_WIND_DIRECTION = "wind_direction"
|
||||
CONF_WIND_SPEED = "wind_speed"
|
||||
|
||||
ENTITY_TRIGGERS = {
|
||||
SensorDeviceClass.ABSOLUTE_HUMIDITY: [{CONF_TYPE: CONF_ABSOLUTE_HUMIDITY}],
|
||||
SensorDeviceClass.APPARENT_POWER: [{CONF_TYPE: CONF_APPARENT_POWER}],
|
||||
SensorDeviceClass.AQI: [{CONF_TYPE: CONF_AQI}],
|
||||
SensorDeviceClass.AREA: [{CONF_TYPE: CONF_AREA}],
|
||||
@ -159,6 +161,7 @@ TRIGGER_SCHEMA = vol.All(
|
||||
vol.Required(CONF_ENTITY_ID): cv.entity_id_or_uuid,
|
||||
vol.Required(CONF_TYPE): vol.In(
|
||||
[
|
||||
CONF_ABSOLUTE_HUMIDITY,
|
||||
CONF_APPARENT_POWER,
|
||||
CONF_AQI,
|
||||
CONF_AREA,
|
||||
|
@ -3,6 +3,9 @@
|
||||
"_": {
|
||||
"default": "mdi:eye"
|
||||
},
|
||||
"absolute_humidity": {
|
||||
"default": "mdi:water-opacity"
|
||||
},
|
||||
"apparent_power": {
|
||||
"default": "mdi:flash"
|
||||
},
|
||||
|
@ -2,6 +2,7 @@
|
||||
"title": "Sensor",
|
||||
"device_automation": {
|
||||
"condition_type": {
|
||||
"is_absolute_humidity": "Current {entity_name} absolute humidity",
|
||||
"is_apparent_power": "Current {entity_name} apparent power",
|
||||
"is_aqi": "Current {entity_name} air quality index",
|
||||
"is_area": "Current {entity_name} area",
|
||||
@ -57,6 +58,7 @@
|
||||
"is_wind_speed": "Current {entity_name} wind speed"
|
||||
},
|
||||
"trigger_type": {
|
||||
"absolute_humidity": "{entity_name} absolute humidity changes",
|
||||
"apparent_power": "{entity_name} apparent power changes",
|
||||
"aqi": "{entity_name} air quality index changes",
|
||||
"area": "{entity_name} area changes",
|
||||
@ -148,6 +150,9 @@
|
||||
"duration": {
|
||||
"name": "Duration"
|
||||
},
|
||||
"absolute_humidity": {
|
||||
"name": "Absolute humidity"
|
||||
},
|
||||
"apparent_power": {
|
||||
"name": "Apparent power"
|
||||
},
|
||||
|
@ -910,6 +910,7 @@ class UnitOfPrecipitationDepth(StrEnum):
|
||||
|
||||
|
||||
# Concentration units
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER: Final = "g/m³"
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "µg/m³"
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: Final = "mg/m³"
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT: Final = "μg/ft³"
|
||||
|
@ -7,6 +7,7 @@ from functools import lru_cache
|
||||
from math import floor, log10
|
||||
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
@ -693,12 +694,14 @@ class MassVolumeConcentrationConverter(BaseUnitConverter):
|
||||
|
||||
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,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: 1000000.0, # 1000 µg/m³ = 1 mg/m³
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: 1000.0, # 1000 mg/m³ = 1 g/m³
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER: 1.0,
|
||||
}
|
||||
VALID_UNITS = {
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@ from homeassistant.components.sensor import (
|
||||
)
|
||||
from homeassistant.components.sensor.const import DEVICE_CLASS_STATE_CLASSES
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
DEGREE,
|
||||
@ -44,6 +45,7 @@ from homeassistant.const import (
|
||||
from tests.common import MockEntity
|
||||
|
||||
UNITS_OF_MEASUREMENT = {
|
||||
SensorDeviceClass.ABSOLUTE_HUMIDITY: CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
SensorDeviceClass.APPARENT_POWER: UnitOfApparentPower.VOLT_AMPERE,
|
||||
SensorDeviceClass.AQI: None,
|
||||
SensorDeviceClass.AREA: UnitOfArea.SQUARE_METERS,
|
||||
|
@ -125,7 +125,7 @@ async def test_get_conditions(
|
||||
conditions = await async_get_device_automations(
|
||||
hass, DeviceAutomationType.CONDITION, device_entry.id
|
||||
)
|
||||
assert len(conditions) == 54
|
||||
assert len(conditions) == 55
|
||||
assert conditions == unordered(expected_conditions)
|
||||
|
||||
|
||||
|
@ -126,7 +126,7 @@ async def test_get_triggers(
|
||||
triggers = await async_get_device_automations(
|
||||
hass, DeviceAutomationType.TRIGGER, device_entry.id
|
||||
)
|
||||
assert len(triggers) == 54
|
||||
assert len(triggers) == 55
|
||||
assert triggers == unordered(expected_triggers)
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@ from itertools import chain
|
||||
import pytest
|
||||
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
@ -762,6 +763,13 @@ _CONVERTED_VALUE: dict[
|
||||
2000,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
),
|
||||
# 3 g/m³ = 3000 mg/m³
|
||||
(
|
||||
3,
|
||||
CONCENTRATION_GRAMS_PER_CUBIC_METER,
|
||||
3000,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
),
|
||||
],
|
||||
VolumeConverter: [
|
||||
(5, UnitOfVolume.LITERS, 1.32086, UnitOfVolume.GALLONS),
|
||||
|
Loading…
x
Reference in New Issue
Block a user