diff --git a/homeassistant/components/number/__init__.py b/homeassistant/components/number/__init__.py index 0012a4b77ff..9c828d1a32f 100644 --- a/homeassistant/components/number/__init__.py +++ b/homeassistant/components/number/__init__.py @@ -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: `m³`, `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/m³` + """ + + NITROGEN_MONOXIDE = "nitrogen_monoxide" + """Amount of NO. + + Unit of measurement: `µg/m³` + """ + + NITROUS_OXIDE = "nitrous_oxide" + """Amount of N2O. + + Unit of measurement: `µg/m³` + """ + + OZONE = "ozone" + """Amount of O3. + + Unit of measurement: `µg/m³` + """ + + PM1 = "pm1" + """Particulate matter <= 0.1 μm. + + Unit of measurement: `µg/m³` + """ + + PM10 = "pm10" + """Particulate matter <= 10 μm. + + Unit of measurement: `µg/m³` + """ + + PM25 = "pm25" + """Particulate matter <= 2.5 μm. + + Unit of measurement: `µg/m³` + """ + + 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/m³` + """ + TEMPERATURE = "temperature" + """Temperature. + + Unit of measurement: `°C`, `°F` + """ + + VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds" + """Amount of VOC. + + Unit of measurement: `µg/m³` + """ + + VOLTAGE = "voltage" + """Voltage. + + Unit of measurement: `V` + """ + + VOLUME = "volume" + """Generic volume. + + Unit of measurement: `VOLUME_*` units + - SI / metric: `mL`, `L`, `m³` + - 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: `m³`, `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)) diff --git a/homeassistant/components/sensor/__init__.py b/homeassistant/components/sensor/__init__.py index 6ba88defc83..0382af7f9f8 100644 --- a/homeassistant/components/sensor/__init__.py +++ b/homeassistant/components/sensor/__init__.py @@ -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. diff --git a/tests/components/number/test_init.py b/tests/components/number/test_init.py index 98b30616952..630506623de 100644 --- a/tests/components/number/test_init.py +++ b/tests/components/number/test_init.py @@ -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 diff --git a/tests/components/sensor/test_init.py b/tests/components/sensor/test_init.py index e168a1c2271..c9c29d6c99a 100644 --- a/tests/components/sensor/test_init.py +++ b/tests/components/sensor/test_init.py @@ -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