Align number and sensor device classes (#81909)

* Align number and sensor device classes

* Add tests

* Tweak tests
This commit is contained in:
Erik Montnemery 2022-11-17 14:00:28 +01:00 committed by GitHub
parent 18e30e7c06
commit b6586d5c34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 293 additions and 23 deletions

View file

@ -55,8 +55,249 @@ _LOGGER = logging.getLogger(__name__)
class NumberDeviceClass(StrEnum):
"""Device class for numbers."""
# temperature (C/F)
# NumberDeviceClass should be aligned with SensorDeviceClass
APPARENT_POWER = "apparent_power"
"""Apparent power.
Unit of measurement: `VA`
"""
AQI = "aqi"
"""Air Quality Index.
Unit of measurement: `None`
"""
BATTERY = "battery"
"""Percentage of battery that is left.
Unit of measurement: `%`
"""
CO = "carbon_monoxide"
"""Carbon Monoxide gas concentration.
Unit of measurement: `ppm` (parts per million)
"""
CO2 = "carbon_dioxide"
"""Carbon Dioxide gas concentration.
Unit of measurement: `ppm` (parts per million)
"""
CURRENT = "current"
"""Current.
Unit of measurement: `A`
"""
DISTANCE = "distance"
"""Generic distance.
Unit of measurement: `LENGTH_*` units
- SI /metric: `mm`, `cm`, `m`, `km`
- USCS / imperial: `in`, `ft`, `yd`, `mi`
"""
ENERGY = "energy"
"""Energy.
Unit of measurement: `Wh`, `kWh`, `MWh`, `GJ`
"""
FREQUENCY = "frequency"
"""Frequency.
Unit of measurement: `Hz`, `kHz`, `MHz`, `GHz`
"""
GAS = "gas"
"""Gas.
Unit of measurement: ``, `ft³`
"""
HUMIDITY = "humidity"
"""Relative humidity.
Unit of measurement: `%`
"""
ILLUMINANCE = "illuminance"
"""Illuminance.
Unit of measurement: `lx`, `lm`
"""
MOISTURE = "moisture"
"""Moisture.
Unit of measurement: `%`
"""
MONETARY = "monetary"
"""Amount of money.
Unit of measurement: ISO4217 currency code
See https://en.wikipedia.org/wiki/ISO_4217#Active_codes for active codes
"""
NITROGEN_DIOXIDE = "nitrogen_dioxide"
"""Amount of NO2.
Unit of measurement: `µg/`
"""
NITROGEN_MONOXIDE = "nitrogen_monoxide"
"""Amount of NO.
Unit of measurement: `µg/`
"""
NITROUS_OXIDE = "nitrous_oxide"
"""Amount of N2O.
Unit of measurement: `µg/`
"""
OZONE = "ozone"
"""Amount of O3.
Unit of measurement: `µg/`
"""
PM1 = "pm1"
"""Particulate matter <= 0.1 μm.
Unit of measurement: `µg/`
"""
PM10 = "pm10"
"""Particulate matter <= 10 μm.
Unit of measurement: `µg/`
"""
PM25 = "pm25"
"""Particulate matter <= 2.5 μm.
Unit of measurement: `µg/`
"""
POWER_FACTOR = "power_factor"
"""Power factor.
Unit of measurement: `%`
"""
POWER = "power"
"""Power.
Unit of measurement: `W`, `kW`
"""
PRECIPITATION_INTENSITY = "precipitation_intensity"
"""Precipitation intensity.
Unit of measurement: UnitOfVolumetricFlux
- SI /metric: `mm/d`, `mm/h`
- USCS / imperial: `in/d`, `in/h`
"""
PRESSURE = "pressure"
"""Pressure.
Unit of measurement:
- `mbar`, `cbar`, `bar`
- `Pa`, `hPa`, `kPa`
- `inHg`
- `psi`
"""
REACTIVE_POWER = "reactive_power"
"""Reactive power.
Unit of measurement: `var`
"""
SIGNAL_STRENGTH = "signal_strength"
"""Signal strength.
Unit of measurement: `dB`, `dBm`
"""
SPEED = "speed"
"""Generic speed.
Unit of measurement: `SPEED_*` units or `UnitOfVolumetricFlux`
- SI /metric: `mm/d`, `mm/h`, `m/s`, `km/h`
- USCS / imperial: `in/d`, `in/h`, `ft/s`, `mph`
- Nautical: `kn`
"""
SULPHUR_DIOXIDE = "sulphur_dioxide"
"""Amount of SO2.
Unit of measurement: `µg/`
"""
TEMPERATURE = "temperature"
"""Temperature.
Unit of measurement: `°C`, `°F`
"""
VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds"
"""Amount of VOC.
Unit of measurement: `µg/`
"""
VOLTAGE = "voltage"
"""Voltage.
Unit of measurement: `V`
"""
VOLUME = "volume"
"""Generic volume.
Unit of measurement: `VOLUME_*` units
- SI / metric: `mL`, `L`, ``
- USCS / imperial: `fl. oz.`, `ft³`, `gal` (warning: volumes expressed in
USCS/imperial units are currently assumed to be US volumes)
"""
WATER = "water"
"""Water.
Unit of measurement:
- SI / metric: ``, `L`
- USCS / imperial: `ft³`, `gal` (warning: volumes expressed in
USCS/imperial units are currently assumed to be US volumes)
"""
WEIGHT = "weight"
"""Generic weight, represents a measurement of an object's mass.
Weight is used instead of mass to fit with every day language.
Unit of measurement: `MASS_*` units
- SI / metric: `µg`, `mg`, `g`, `kg`
- USCS / imperial: `oz`, `lb`
"""
WIND_SPEED = "wind_speed"
"""Wind speed.
Unit of measurement: `SPEED_*` units
- SI /metric: `m/s`, `km/h`
- USCS / imperial: `ft/s`, `mph`
- Nautical: `kn`
"""
DEVICE_CLASSES_SCHEMA: Final = vol.All(vol.Lower, vol.Coerce(NumberDeviceClass))

View file

@ -88,6 +88,30 @@ SCAN_INTERVAL: Final = timedelta(seconds=30)
class SensorDeviceClass(StrEnum):
"""Device class for sensors."""
# Non-numerical device classes
DATE = "date"
"""Date.
Unit of measurement: `None`
ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601
"""
DURATION = "duration"
"""Fixed duration.
Unit of measurement: `d`, `h`, `min`, `s`
"""
TIMESTAMP = "timestamp"
"""Timestamp.
Unit of measurement: `None`
ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601
"""
# Numerical device classes, these should be aligned with NumberDeviceClass
APPARENT_POWER = "apparent_power"
"""Apparent power.
@ -124,14 +148,6 @@ class SensorDeviceClass(StrEnum):
Unit of measurement: `A`
"""
DATE = "date"
"""Date.
Unit of measurement: `None`
ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601
"""
DISTANCE = "distance"
"""Generic distance.
@ -140,12 +156,6 @@ class SensorDeviceClass(StrEnum):
- USCS / imperial: `in`, `ft`, `yd`, `mi`
"""
DURATION = "duration"
"""Fixed duration.
Unit of measurement: `d`, `h`, `min`, `s`
"""
ENERGY = "energy"
"""Energy.
@ -295,14 +305,6 @@ class SensorDeviceClass(StrEnum):
Unit of measurement: `°C`, `°F`
"""
TIMESTAMP = "timestamp"
"""Timestamp.
Unit of measurement: `None`
ISO8601 format: https://en.wikipedia.org/wiki/ISO_8601
"""
VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds"
"""Amount of VOC.

View file

@ -14,6 +14,7 @@ from homeassistant.components.number import (
NumberEntity,
NumberEntityDescription,
)
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_UNIT_OF_MEASUREMENT,
@ -848,3 +849,20 @@ async def test_custom_unit_change(
state = hass.states.get(entity0.entity_id)
assert float(state.state) == pytest.approx(float(default_value))
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == default_unit
def test_device_classes_aligned():
"""Make sure all sensor device classes are also available in NumberDeviceClass."""
non_numeric_device_classes = {
SensorDeviceClass.DATE,
SensorDeviceClass.DURATION,
SensorDeviceClass.TIMESTAMP,
}
for device_class in SensorDeviceClass:
if device_class in non_numeric_device_classes:
continue
assert hasattr(NumberDeviceClass, device_class.name)
assert getattr(NumberDeviceClass, device_class.name).value == device_class.value

View file

@ -5,6 +5,7 @@ from decimal import Decimal
import pytest
from pytest import approx
from homeassistant.components.number import NumberDeviceClass
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import (
ATTR_UNIT_OF_MEASUREMENT,
@ -927,3 +928,11 @@ async def test_unit_conversion_priority_legacy_conversion_removed(
state = hass.states.get(entity0.entity_id)
assert float(state.state) == approx(float(original_value))
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == original_unit
def test_device_classes_aligned():
"""Make sure all number device classes are also available in SensorDeviceClass."""
for device_class in NumberDeviceClass:
assert hasattr(SensorDeviceClass, device_class.name)
assert getattr(SensorDeviceClass, device_class.name).value == device_class.value