forked from home-assistant/core
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8aafc6702 | |||
| a53d010dc2 | |||
| 22b5b67a25 | |||
| e3fe99e130 | |||
| 6f718c2de0 | |||
| 012cea9f37 | |||
| 335621a7a9 | |||
| 1c4c52e46a | |||
| dca353c306 | |||
| a9e39c9354 | |||
| a49b584955 | |||
| 3a116ea4ad | |||
| 4e02f5406e | |||
| e3ae289ee4 | |||
| f0906ac5c0 | |||
| e1739ccd87 | |||
| 83106c9e9d | |||
| 55da0d1d4f | |||
| 6fa4190441 | |||
| 9202b4d746 | |||
| 9f00dfdbbf | |||
| d7086d5c7c | |||
| b3074d272a |
@@ -61,7 +61,7 @@
|
||||
"display_pm_standard": {
|
||||
"name": "Display PM standard",
|
||||
"state": {
|
||||
"ugm3": "µg/m³",
|
||||
"ugm3": "μg/m³",
|
||||
"us_aqi": "US AQI"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -92,7 +92,7 @@ SENSOR_DESCRIPTIONS = {
|
||||
key="radiation_rate",
|
||||
translation_key="radiation_rate",
|
||||
name="Radiation Dose Rate",
|
||||
native_unit_of_measurement="μSv/h",
|
||||
native_unit_of_measurement="μSv/h", # "μ" == "\u03bc"
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=2,
|
||||
scale=0.001,
|
||||
|
||||
@@ -72,7 +72,7 @@ SENSOR_DESCRIPTIONS = {
|
||||
key=str(BTHomeExtendedSensorDeviceClass.CHANNEL),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
# Conductivity (µS/cm)
|
||||
# Conductivity (μS/cm)
|
||||
(
|
||||
BTHomeSensorDeviceClass.CONDUCTIVITY,
|
||||
Units.CONDUCTIVITY,
|
||||
@@ -215,7 +215,7 @@ SENSOR_DESCRIPTIONS = {
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
# PM10 (µg/m3)
|
||||
# PM10 (μg/m3)
|
||||
(
|
||||
BTHomeSensorDeviceClass.PM10,
|
||||
Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
@@ -225,7 +225,7 @@ SENSOR_DESCRIPTIONS = {
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
# PM2.5 (µg/m3)
|
||||
# PM2.5 (μg/m3)
|
||||
(
|
||||
BTHomeSensorDeviceClass.PM25,
|
||||
Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
@@ -318,7 +318,7 @@ SENSOR_DESCRIPTIONS = {
|
||||
key=str(BTHomeSensorDeviceClass.UV_INDEX),
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
# Volatile organic Compounds (VOC) (µg/m3)
|
||||
# Volatile organic Compounds (VOC) (μg/m3)
|
||||
(
|
||||
BTHomeSensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS,
|
||||
Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
|
||||
@@ -35,7 +35,7 @@ from .const import (
|
||||
|
||||
UNIT_PREFIXES = [
|
||||
selector.SelectOptionDict(value="n", label="n (nano)"),
|
||||
selector.SelectOptionDict(value="µ", label="µ (micro)"),
|
||||
selector.SelectOptionDict(value="μ", label="μ (micro)"),
|
||||
selector.SelectOptionDict(value="m", label="m (milli)"),
|
||||
selector.SelectOptionDict(value="k", label="k (kilo)"),
|
||||
selector.SelectOptionDict(value="M", label="M (mega)"),
|
||||
|
||||
@@ -61,7 +61,7 @@ ATTR_SOURCE_ID = "source"
|
||||
UNIT_PREFIXES = {
|
||||
None: 1,
|
||||
"n": 1e-9,
|
||||
"µ": 1e-6,
|
||||
"μ": 1e-6, # "μ" == "\u03bc"
|
||||
"m": 1e-3,
|
||||
"k": 1e3,
|
||||
"M": 1e6,
|
||||
|
||||
@@ -163,7 +163,7 @@ SENSORS: dict[str | None, SensorEntityDescription] = {
|
||||
native_unit_of_measurement=UnitOfSpeed.METERS_PER_SECOND,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
"µg/m³": SensorEntityDescription(
|
||||
"μg/m³": SensorEntityDescription(
|
||||
key="concentration|microgram_per_cubic_meter",
|
||||
translation_key="concentration",
|
||||
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
|
||||
@@ -105,7 +105,7 @@ SENSORS: Final[list[FytaSensorEntityDescription]] = [
|
||||
FytaSensorEntityDescription(
|
||||
key="light",
|
||||
translation_key="light",
|
||||
native_unit_of_measurement="μmol/s⋅m²",
|
||||
native_unit_of_measurement="μmol/s⋅m²", # "μ" == "\u03bc"
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
value_fn=lambda plant: plant.light,
|
||||
),
|
||||
|
||||
@@ -291,7 +291,7 @@ class NitrogenDioxideSensor(AirQualitySensor):
|
||||
class VolatileOrganicCompoundsSensor(AirQualitySensor):
|
||||
"""Generate a VolatileOrganicCompoundsSensor accessory as VOCs sensor.
|
||||
|
||||
Sensor entity must return VOC in µg/m3.
|
||||
Sensor entity must return VOC in μg/m3.
|
||||
"""
|
||||
|
||||
def create_services(self) -> None:
|
||||
|
||||
@@ -480,7 +480,7 @@ def temperature_to_states(temperature: float, unit: str) -> float:
|
||||
|
||||
|
||||
def density_to_air_quality(density: float) -> int:
|
||||
"""Map PM2.5 µg/m3 density to HomeKit AirQuality level."""
|
||||
"""Map PM2.5 μg/m3 density to HomeKit AirQuality level."""
|
||||
if density <= 9: # US AQI 0-50 (HomeKit: Excellent)
|
||||
return 1
|
||||
if density <= 35.4: # US AQI 51-100 (HomeKit: Good)
|
||||
@@ -493,7 +493,7 @@ def density_to_air_quality(density: float) -> int:
|
||||
|
||||
|
||||
def density_to_air_quality_pm10(density: float) -> int:
|
||||
"""Map PM10 µg/m3 density to HomeKit AirQuality level."""
|
||||
"""Map PM10 μg/m3 density to HomeKit AirQuality level."""
|
||||
if density <= 54: # US AQI 0-50 (HomeKit: Excellent)
|
||||
return 1
|
||||
if density <= 154: # US AQI 51-100 (HomeKit: Good)
|
||||
@@ -506,7 +506,7 @@ def density_to_air_quality_pm10(density: float) -> int:
|
||||
|
||||
|
||||
def density_to_air_quality_nitrogen_dioxide(density: float) -> int:
|
||||
"""Map nitrogen dioxide µg/m3 to HomeKit AirQuality level."""
|
||||
"""Map nitrogen dioxide μg/m3 to HomeKit AirQuality level."""
|
||||
if density <= 30:
|
||||
return 1
|
||||
if density <= 60:
|
||||
@@ -519,7 +519,7 @@ def density_to_air_quality_nitrogen_dioxide(density: float) -> int:
|
||||
|
||||
|
||||
def density_to_air_quality_voc(density: float) -> int:
|
||||
"""Map VOCs µg/m3 to HomeKit AirQuality level.
|
||||
"""Map VOCs μg/m3 to HomeKit AirQuality level.
|
||||
|
||||
The VOC mappings use the IAQ guidelines for Europe released by the WHO (World Health Organization).
|
||||
Referenced from Sensirion_Gas_Sensors_SGP3x_TVOC_Concept.pdf
|
||||
|
||||
@@ -16,6 +16,7 @@ from homeassistant.components.number import (
|
||||
NumberMode,
|
||||
RestoreNumber,
|
||||
)
|
||||
from homeassistant.components.sensor.recorder import EQUIVALENT_UNITS
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
@@ -70,6 +71,13 @@ MQTT_NUMBER_ATTRIBUTES_BLOCKED = frozenset(
|
||||
|
||||
def validate_config(config: ConfigType) -> ConfigType:
|
||||
"""Validate that the configuration is valid, throws if it isn't."""
|
||||
if (
|
||||
CONF_UNIT_OF_MEASUREMENT in config
|
||||
and (unit_of_measurement := config[CONF_UNIT_OF_MEASUREMENT])
|
||||
in EQUIVALENT_UNITS
|
||||
):
|
||||
config[CONF_UNIT_OF_MEASUREMENT] = EQUIVALENT_UNITS[unit_of_measurement]
|
||||
|
||||
if config[CONF_MIN] > config[CONF_MAX]:
|
||||
raise vol.Invalid(f"{CONF_MAX} must be >= {CONF_MIN}")
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ from homeassistant.components.sensor import (
|
||||
SensorExtraStoredData,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.components.sensor.recorder import EQUIVALENT_UNITS
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
@@ -129,9 +130,14 @@ def validate_sensor_state_and_device_class_config(config: ConfigType) -> ConfigT
|
||||
f"together with state class '{state_class}'"
|
||||
)
|
||||
|
||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is None or (
|
||||
unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
) is None:
|
||||
if (unit_of_measurement := config.get(CONF_UNIT_OF_MEASUREMENT)) is None:
|
||||
return config
|
||||
|
||||
config[CONF_UNIT_OF_MEASUREMENT] = EQUIVALENT_UNITS.get(
|
||||
unit_of_measurement, unit_of_measurement
|
||||
)
|
||||
|
||||
if (device_class := config.get(CONF_DEVICE_CLASS)) is None:
|
||||
return config
|
||||
|
||||
if (
|
||||
|
||||
@@ -31,6 +31,7 @@ from homeassistant.loader import async_suggest_report_issue
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import ( # noqa: F401
|
||||
AMBIGUOUS_UNITS,
|
||||
ATTR_MAX,
|
||||
ATTR_MIN,
|
||||
ATTR_STEP,
|
||||
@@ -367,6 +368,15 @@ class NumberEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
return self.entity_description.native_unit_of_measurement
|
||||
return None
|
||||
|
||||
@final
|
||||
@property
|
||||
def __native_unit_of_measurement_compat(self) -> str | None:
|
||||
"""Process ambiguous units."""
|
||||
native_unit_of_measurement = self.native_unit_of_measurement
|
||||
return AMBIGUOUS_UNITS.get(
|
||||
native_unit_of_measurement, native_unit_of_measurement
|
||||
)
|
||||
|
||||
@property
|
||||
@final
|
||||
def unit_of_measurement(self) -> str | None:
|
||||
@@ -374,7 +384,7 @@ class NumberEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
if self._number_option_unit_of_measurement:
|
||||
return self._number_option_unit_of_measurement
|
||||
|
||||
native_unit_of_measurement = self.native_unit_of_measurement
|
||||
native_unit_of_measurement = self.__native_unit_of_measurement_compat
|
||||
# device_class is checked after native_unit_of_measurement since most
|
||||
# of the time we can avoid the device_class check
|
||||
if (
|
||||
@@ -441,7 +451,7 @@ class NumberEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
if device_class not in UNIT_CONVERTERS:
|
||||
return value
|
||||
|
||||
native_unit_of_measurement = self.native_unit_of_measurement
|
||||
native_unit_of_measurement = self.__native_unit_of_measurement_compat
|
||||
unit_of_measurement = self.unit_of_measurement
|
||||
if native_unit_of_measurement != unit_of_measurement:
|
||||
if TYPE_CHECKING:
|
||||
@@ -470,7 +480,7 @@ class NumberEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
if value is None or (device_class := self.device_class) not in UNIT_CONVERTERS:
|
||||
return value
|
||||
|
||||
native_unit_of_measurement = self.native_unit_of_measurement
|
||||
native_unit_of_measurement = self.__native_unit_of_measurement_compat
|
||||
unit_of_measurement = self.unit_of_measurement
|
||||
if native_unit_of_measurement != unit_of_measurement:
|
||||
if TYPE_CHECKING:
|
||||
@@ -493,7 +503,7 @@ class NumberEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
(number_options := self.registry_entry.options.get(DOMAIN))
|
||||
and (custom_unit := number_options.get(CONF_UNIT_OF_MEASUREMENT))
|
||||
and (device_class := self.device_class) in UNIT_CONVERTERS
|
||||
and self.native_unit_of_measurement
|
||||
and self.__native_unit_of_measurement_compat
|
||||
in UNIT_CONVERTERS[device_class].VALID_UNITS
|
||||
and custom_unit in UNIT_CONVERTERS[device_class].VALID_UNITS
|
||||
):
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Final
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
@@ -130,7 +131,7 @@ class NumberDeviceClass(StrEnum):
|
||||
CONDUCTIVITY = "conductivity"
|
||||
"""Conductivity.
|
||||
|
||||
Unit of measurement: `S/cm`, `mS/cm`, `µS/cm`
|
||||
Unit of measurement: `S/cm`, `mS/cm`, `μS/cm`
|
||||
"""
|
||||
|
||||
CURRENT = "current"
|
||||
@@ -162,7 +163,7 @@ class NumberDeviceClass(StrEnum):
|
||||
DURATION = "duration"
|
||||
"""Fixed duration.
|
||||
|
||||
Unit of measurement: `d`, `h`, `min`, `s`, `ms`, `µs`
|
||||
Unit of measurement: `d`, `h`, `min`, `s`, `ms`, `μs`
|
||||
"""
|
||||
|
||||
ENERGY = "energy"
|
||||
@@ -240,25 +241,25 @@ class NumberDeviceClass(StrEnum):
|
||||
NITROGEN_DIOXIDE = "nitrogen_dioxide"
|
||||
"""Amount of NO2.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
NITROGEN_MONOXIDE = "nitrogen_monoxide"
|
||||
"""Amount of NO.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
NITROUS_OXIDE = "nitrous_oxide"
|
||||
"""Amount of N2O.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
OZONE = "ozone"
|
||||
"""Amount of O3.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
PH = "ph"
|
||||
@@ -270,19 +271,19 @@ class NumberDeviceClass(StrEnum):
|
||||
PM1 = "pm1"
|
||||
"""Particulate matter <= 1 μm.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
PM10 = "pm10"
|
||||
"""Particulate matter <= 10 μm.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
PM25 = "pm25"
|
||||
"""Particulate matter <= 2.5 μm.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
POWER_FACTOR = "power_factor"
|
||||
@@ -359,7 +360,7 @@ class NumberDeviceClass(StrEnum):
|
||||
SULPHUR_DIOXIDE = "sulphur_dioxide"
|
||||
"""Amount of SO2.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
TEMPERATURE = "temperature"
|
||||
@@ -371,7 +372,7 @@ class NumberDeviceClass(StrEnum):
|
||||
VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds"
|
||||
"""Amount of VOC.
|
||||
|
||||
Unit of measurement: `µg/m³`, `mg/m³`
|
||||
Unit of measurement: `μg/m³`, `mg/m³`
|
||||
"""
|
||||
|
||||
VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts"
|
||||
@@ -383,7 +384,7 @@ class NumberDeviceClass(StrEnum):
|
||||
VOLTAGE = "voltage"
|
||||
"""Voltage.
|
||||
|
||||
Unit of measurement: `V`, `mV`, `µV`, `kV`, `MV`
|
||||
Unit of measurement: `V`, `mV`, `μV`, `kV`, `MV`
|
||||
"""
|
||||
|
||||
VOLUME = "volume"
|
||||
@@ -430,7 +431,7 @@ class NumberDeviceClass(StrEnum):
|
||||
Weight is used instead of mass to fit with every day language.
|
||||
|
||||
Unit of measurement: `MASS_*` units
|
||||
- SI / metric: `µg`, `mg`, `g`, `kg`
|
||||
- SI / metric: `μg`, `mg`, `g`, `kg`
|
||||
- USCS / imperial: `oz`, `lb`
|
||||
"""
|
||||
|
||||
@@ -546,3 +547,14 @@ UNIT_CONVERTERS: dict[NumberDeviceClass, type[BaseUnitConverter]] = {
|
||||
NumberDeviceClass.TEMPERATURE: TemperatureConverter,
|
||||
NumberDeviceClass.VOLUME_FLOW_RATE: VolumeFlowRateConverter,
|
||||
}
|
||||
|
||||
AMBIGUOUS_UNITS: dict[str | None, str] = {
|
||||
"\u00b5Sv/h": "μSv/h", # aranet: radiation rate
|
||||
"\u00b5S/cm": UnitOfConductivity.MICROSIEMENS_PER_CM,
|
||||
"\u00b5V": UnitOfElectricPotential.MICROVOLT,
|
||||
"\u00b5g/ft³": CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT,
|
||||
"\u00b5g/m³": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
"\u00b5mol/s⋅m²": "μmol/s⋅m²", # fyta: light
|
||||
"\u00b5g": UnitOfMass.MICROGRAMS,
|
||||
"\u00b5s": UnitOfTime.MICROSECONDS,
|
||||
}
|
||||
|
||||
@@ -366,6 +366,7 @@ class PrometheusMetrics:
|
||||
|
||||
@staticmethod
|
||||
def _sanitize_metric_name(metric: str) -> str:
|
||||
metric.replace("\u03bc", "\u00b5")
|
||||
return "".join(
|
||||
[c if c in ALLOWED_METRIC_CHARS else f"u{hex(ord(c))}" for c in metric]
|
||||
)
|
||||
@@ -747,6 +748,9 @@ class PrometheusMetrics:
|
||||
PERCENTAGE: "percent",
|
||||
}
|
||||
default = unit.replace("/", "_per_")
|
||||
# Unit conversion for CONCENTRATION_MICROGRAMS_PER_CUBIC_METER "μg/m³"
|
||||
# "μ" == "\u03bc" but the API uses "\u00b5"
|
||||
default = default.replace("\u03bc", "\u00b5")
|
||||
default = default.lower()
|
||||
return units.get(unit, default)
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ def correct_db_schema_precision(
|
||||
)
|
||||
|
||||
precision_columns = _get_precision_column_types(table_object)
|
||||
# Attempt to convert timestamp columns to µs precision
|
||||
# Attempt to convert timestamp columns to μs precision
|
||||
session_maker = instance.get_session
|
||||
engine = instance.engine
|
||||
assert engine is not None, "Engine should be set"
|
||||
|
||||
@@ -34,6 +34,7 @@ from homeassistant.util.enum import try_parse_enum
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import ( # noqa: F401
|
||||
AMBIGUOUS_UNITS,
|
||||
ATTR_LAST_RESET,
|
||||
ATTR_OPTIONS,
|
||||
ATTR_STATE_CLASS,
|
||||
@@ -314,7 +315,7 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
return _numeric_state_expected(
|
||||
try_parse_enum(SensorDeviceClass, self.device_class),
|
||||
self.state_class,
|
||||
self.native_unit_of_measurement,
|
||||
self.__native_unit_of_measurement_compat,
|
||||
self.suggested_display_precision,
|
||||
)
|
||||
|
||||
@@ -366,7 +367,8 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
# Make sure we can convert the units
|
||||
if (
|
||||
(unit_converter := UNIT_CONVERTERS.get(self.device_class)) is None
|
||||
or self.native_unit_of_measurement not in unit_converter.VALID_UNITS
|
||||
or self.__native_unit_of_measurement_compat
|
||||
not in unit_converter.VALID_UNITS
|
||||
or suggested_unit_of_measurement not in unit_converter.VALID_UNITS
|
||||
):
|
||||
if not self._invalid_suggested_unit_of_measurement_reported:
|
||||
@@ -387,7 +389,7 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
if suggested_unit_of_measurement is None:
|
||||
# Fallback to unit suggested by the unit conversion rules from device class
|
||||
suggested_unit_of_measurement = self.hass.config.units.get_converted_unit(
|
||||
self.device_class, self.native_unit_of_measurement
|
||||
self.device_class, self.__native_unit_of_measurement_compat
|
||||
)
|
||||
|
||||
if suggested_unit_of_measurement is None and (
|
||||
@@ -396,7 +398,7 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
# If the device class is not known by the unit system but has a unit converter,
|
||||
# fall back to the unit suggested by the unit converter's unit class.
|
||||
suggested_unit_of_measurement = self.hass.config.units.get_converted_unit(
|
||||
unit_converter.UNIT_CLASS, self.native_unit_of_measurement
|
||||
unit_converter.UNIT_CLASS, self.__native_unit_of_measurement_compat
|
||||
)
|
||||
|
||||
if suggested_unit_of_measurement is None:
|
||||
@@ -468,6 +470,16 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
return self.entity_description.native_unit_of_measurement
|
||||
return None
|
||||
|
||||
@final
|
||||
@property
|
||||
def __native_unit_of_measurement_compat(self) -> str | None:
|
||||
"""Process ambiguous units."""
|
||||
native_unit_of_measurement = self.native_unit_of_measurement
|
||||
return AMBIGUOUS_UNITS.get(
|
||||
native_unit_of_measurement,
|
||||
native_unit_of_measurement,
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def suggested_unit_of_measurement(self) -> str | None:
|
||||
"""Return the unit which should be used for the sensor's state.
|
||||
@@ -503,7 +515,7 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
if self._sensor_option_unit_of_measurement is not UNDEFINED:
|
||||
return self._sensor_option_unit_of_measurement
|
||||
|
||||
native_unit_of_measurement = self.native_unit_of_measurement
|
||||
native_unit_of_measurement = self.__native_unit_of_measurement_compat
|
||||
|
||||
# Second priority, for non registered entities: unit suggested by integration
|
||||
if not self.registry_entry and (
|
||||
@@ -541,7 +553,7 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
@override
|
||||
def state(self) -> Any:
|
||||
"""Return the state of the sensor and perform unit conversions, if needed."""
|
||||
native_unit_of_measurement = self.native_unit_of_measurement
|
||||
native_unit_of_measurement = self.__native_unit_of_measurement_compat
|
||||
unit_of_measurement = self.unit_of_measurement
|
||||
value = self.native_value
|
||||
# For the sake of validation, we can ignore custom device classes
|
||||
@@ -763,7 +775,8 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
return display_precision
|
||||
|
||||
default_unit_of_measurement = (
|
||||
self.suggested_unit_of_measurement or self.native_unit_of_measurement
|
||||
self.suggested_unit_of_measurement
|
||||
or self.__native_unit_of_measurement_compat
|
||||
)
|
||||
if default_unit_of_measurement is None:
|
||||
return display_precision
|
||||
@@ -841,7 +854,7 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
|
||||
(sensor_options := self.registry_entry.options.get(primary_key))
|
||||
and secondary_key in sensor_options
|
||||
and (device_class := self.device_class) in UNIT_CONVERTERS
|
||||
and self.native_unit_of_measurement
|
||||
and self.__native_unit_of_measurement_compat
|
||||
in UNIT_CONVERTERS[device_class].VALID_UNITS
|
||||
and (custom_unit := sensor_options[secondary_key])
|
||||
in UNIT_CONVERTERS[device_class].VALID_UNITS
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Final
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
@@ -158,7 +159,7 @@ class SensorDeviceClass(StrEnum):
|
||||
CONDUCTIVITY = "conductivity"
|
||||
"""Conductivity.
|
||||
|
||||
Unit of measurement: `S/cm`, `mS/cm`, `µS/cm`
|
||||
Unit of measurement: `S/cm`, `mS/cm`, `μS/cm`
|
||||
"""
|
||||
|
||||
CURRENT = "current"
|
||||
@@ -190,7 +191,7 @@ class SensorDeviceClass(StrEnum):
|
||||
DURATION = "duration"
|
||||
"""Fixed duration.
|
||||
|
||||
Unit of measurement: `d`, `h`, `min`, `s`, `ms`, `µs`
|
||||
Unit of measurement: `d`, `h`, `min`, `s`, `ms`, `μs`
|
||||
"""
|
||||
|
||||
ENERGY = "energy"
|
||||
@@ -270,25 +271,25 @@ class SensorDeviceClass(StrEnum):
|
||||
NITROGEN_DIOXIDE = "nitrogen_dioxide"
|
||||
"""Amount of NO2.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
NITROGEN_MONOXIDE = "nitrogen_monoxide"
|
||||
"""Amount of NO.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
NITROUS_OXIDE = "nitrous_oxide"
|
||||
"""Amount of N2O.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
OZONE = "ozone"
|
||||
"""Amount of O3.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
PH = "ph"
|
||||
@@ -300,19 +301,19 @@ class SensorDeviceClass(StrEnum):
|
||||
PM1 = "pm1"
|
||||
"""Particulate matter <= 1 μm.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
PM10 = "pm10"
|
||||
"""Particulate matter <= 10 μm.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
PM25 = "pm25"
|
||||
"""Particulate matter <= 2.5 μm.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
POWER_FACTOR = "power_factor"
|
||||
@@ -390,7 +391,7 @@ class SensorDeviceClass(StrEnum):
|
||||
SULPHUR_DIOXIDE = "sulphur_dioxide"
|
||||
"""Amount of SO2.
|
||||
|
||||
Unit of measurement: `µg/m³`
|
||||
Unit of measurement: `μg/m³`
|
||||
"""
|
||||
|
||||
TEMPERATURE = "temperature"
|
||||
@@ -402,7 +403,7 @@ class SensorDeviceClass(StrEnum):
|
||||
VOLATILE_ORGANIC_COMPOUNDS = "volatile_organic_compounds"
|
||||
"""Amount of VOC.
|
||||
|
||||
Unit of measurement: `µg/m³`, `mg/m³`
|
||||
Unit of measurement: `μg/m³`, `mg/m³`
|
||||
"""
|
||||
|
||||
VOLATILE_ORGANIC_COMPOUNDS_PARTS = "volatile_organic_compounds_parts"
|
||||
@@ -414,7 +415,7 @@ class SensorDeviceClass(StrEnum):
|
||||
VOLTAGE = "voltage"
|
||||
"""Voltage.
|
||||
|
||||
Unit of measurement: `V`, `mV`, `µV`, `kV`, `MV`
|
||||
Unit of measurement: `V`, `mV`, `μV`, `kV`, `MV`
|
||||
"""
|
||||
|
||||
VOLUME = "volume"
|
||||
@@ -461,7 +462,7 @@ class SensorDeviceClass(StrEnum):
|
||||
Weight is used instead of mass to fit with every day language.
|
||||
|
||||
Unit of measurement: `MASS_*` units
|
||||
- SI / metric: `µg`, `mg`, `g`, `kg`
|
||||
- SI / metric: `μg`, `mg`, `g`, `kg`
|
||||
- USCS / imperial: `oz`, `lb`
|
||||
"""
|
||||
|
||||
@@ -770,3 +771,14 @@ DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
|
||||
STATE_CLASS_UNITS: dict[SensorStateClass | str, set[type[StrEnum] | str | None]] = {
|
||||
SensorStateClass.MEASUREMENT_ANGLE: {DEGREE},
|
||||
}
|
||||
|
||||
AMBIGUOUS_UNITS: dict[str | None, str] = {
|
||||
"\u00b5Sv/h": "μSv/h", # aranet: radiation rate
|
||||
"\u00b5S/cm": UnitOfConductivity.MICROSIEMENS_PER_CM,
|
||||
"\u00b5V": UnitOfElectricPotential.MICROVOLT,
|
||||
"\u00b5g/ft³": CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT,
|
||||
"\u00b5g/m³": CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
"\u00b5mol/s⋅m²": "μmol/s⋅m²", # fyta: light
|
||||
"\u00b5g": UnitOfMass.MICROGRAMS,
|
||||
"\u00b5s": UnitOfTime.MICROSECONDS,
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ from homeassistant.util.enum import try_parse_enum
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import (
|
||||
AMBIGUOUS_UNITS,
|
||||
ATTR_LAST_RESET,
|
||||
ATTR_STATE_CLASS,
|
||||
DOMAIN,
|
||||
@@ -79,7 +80,7 @@ EQUIVALENT_UNITS = {
|
||||
"ft3": UnitOfVolume.CUBIC_FEET,
|
||||
"m3": UnitOfVolume.CUBIC_METERS,
|
||||
"ft³/m": UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE,
|
||||
}
|
||||
} | AMBIGUOUS_UNITS
|
||||
|
||||
|
||||
# Keep track of entities for which a warning about decreasing value has been logged
|
||||
|
||||
@@ -197,7 +197,7 @@ SENSOR_TYPES = (
|
||||
attribute=TMRW_ATTR_PRECIPITATION_TYPE,
|
||||
value_map=PrecipitationType,
|
||||
),
|
||||
# Data comes in as ppb, convert to µg/m^3
|
||||
# Data comes in as ppb, convert to μg/m^3
|
||||
# Molecular weight of Ozone is 48
|
||||
TomorrowioSensorEntityDescription(
|
||||
key="ozone",
|
||||
@@ -221,7 +221,7 @@ SENSOR_TYPES = (
|
||||
device_class=SensorDeviceClass.PM10,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
# Data comes in as ppb, convert to µg/m^3
|
||||
# Data comes in as ppb, convert to μg/m^3
|
||||
# Molecular weight of Nitrogen Dioxide is 46.01
|
||||
TomorrowioSensorEntityDescription(
|
||||
key="nitrogen_dioxide",
|
||||
@@ -240,7 +240,7 @@ SENSOR_TYPES = (
|
||||
device_class=SensorDeviceClass.CO,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
# Data comes in as ppb, convert to µg/m^3
|
||||
# Data comes in as ppb, convert to μg/m^3
|
||||
# Molecular weight of Sulphur Dioxide is 64.07
|
||||
TomorrowioSensorEntityDescription(
|
||||
key="sulphur_dioxide",
|
||||
|
||||
@@ -496,7 +496,7 @@ UNITS = (
|
||||
),
|
||||
UnitOfMeasurement(
|
||||
unit=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
aliases={"ug/m3", "µg/m3", "ug/m³"},
|
||||
aliases={"ug/m3", "μg/m3", "\u00b5g/m3", "ug/m³"},
|
||||
device_classes={
|
||||
SensorDeviceClass.NITROGEN_DIOXIDE,
|
||||
SensorDeviceClass.NITROGEN_MONOXIDE,
|
||||
|
||||
@@ -664,7 +664,7 @@ class UnitOfElectricCurrent(StrEnum):
|
||||
class UnitOfElectricPotential(StrEnum):
|
||||
"""Electric potential units."""
|
||||
|
||||
MICROVOLT = "µV"
|
||||
MICROVOLT = "μV" # "μ" == "\u03bc"
|
||||
MILLIVOLT = "mV"
|
||||
VOLT = "V"
|
||||
KILOVOLT = "kV"
|
||||
@@ -693,7 +693,7 @@ class UnitOfTemperature(StrEnum):
|
||||
class UnitOfTime(StrEnum):
|
||||
"""Time units."""
|
||||
|
||||
MICROSECONDS = "μs"
|
||||
MICROSECONDS = "μs" # "μ" == "\u03bc"
|
||||
MILLISECONDS = "ms"
|
||||
SECONDS = "s"
|
||||
MINUTES = "min"
|
||||
@@ -814,7 +814,7 @@ class UnitOfMass(StrEnum):
|
||||
GRAMS = "g"
|
||||
KILOGRAMS = "kg"
|
||||
MILLIGRAMS = "mg"
|
||||
MICROGRAMS = "µg"
|
||||
MICROGRAMS = "μg" # "μ" == "\u03bc"
|
||||
OUNCES = "oz"
|
||||
POUNDS = "lb"
|
||||
STONES = "st"
|
||||
@@ -832,13 +832,13 @@ class UnitOfConductivity(
|
||||
"""Conductivity units."""
|
||||
|
||||
SIEMENS_PER_CM = "S/cm"
|
||||
MICROSIEMENS_PER_CM = "µS/cm"
|
||||
MICROSIEMENS_PER_CM = "μS/cm" # "μ" == "\u03bc"
|
||||
MILLISIEMENS_PER_CM = "mS/cm"
|
||||
|
||||
# Deprecated aliases
|
||||
SIEMENS = "S/cm"
|
||||
"""Deprecated: Please use UnitOfConductivity.SIEMENS_PER_CM"""
|
||||
MICROSIEMENS = "µS/cm"
|
||||
MICROSIEMENS = "μS/cm" # "μ" == "\u03bc"
|
||||
"""Deprecated: Please use UnitOfConductivity.MICROSIEMENS_PER_CM"""
|
||||
MILLISIEMENS = "mS/cm"
|
||||
"""Deprecated: Please use UnitOfConductivity.MILLISIEMENS_PER_CM"""
|
||||
@@ -909,9 +909,9 @@ class UnitOfPrecipitationDepth(StrEnum):
|
||||
|
||||
|
||||
# Concentration units
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "µg/m³"
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "μg/m³" # "μ" == "\u03bc"
|
||||
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: Final = "mg/m³"
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT: Final = "μg/ft³"
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT: Final = "μg/ft³" # "μ" == "\u03bc"
|
||||
CONCENTRATION_PARTS_PER_CUBIC_METER: Final = "p/m³"
|
||||
CONCENTRATION_PARTS_PER_MILLION: Final = "ppm"
|
||||
CONCENTRATION_PARTS_PER_BILLION: Final = "ppb"
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Plugin for checking correct micro unicode card is used."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from astroid import nodes
|
||||
from pylint.checkers import BaseChecker
|
||||
from pylint.lint import PyLinter
|
||||
|
||||
|
||||
class HassEnforceGreekMicroCharChecker(BaseChecker):
|
||||
"""Checker for micro char."""
|
||||
|
||||
name = "hass-enforce-greek-micro-char"
|
||||
priority = -1
|
||||
msgs = {
|
||||
"W7452": (
|
||||
"Constants with a micro unit prefix must encode mu as U+03BC (\u03bc), not as U+00B5 (\u00b5)",
|
||||
"hass-enforce-greek-micro-char",
|
||||
"According to [The Unicode Consortium](https://en.wikipedia.org/wiki/Micro-#Symbol_encoding_in_character_sets), the Greek letter character is preferred.",
|
||||
),
|
||||
}
|
||||
options = ()
|
||||
|
||||
def visit_annassign(self, node: nodes.AnnAssign) -> None:
|
||||
"""Check for micro char const or StrEnum with type annotations."""
|
||||
self._do_micro_check(node.target, node)
|
||||
|
||||
def visit_assign(self, node: nodes.Assign) -> None:
|
||||
"""Check for micro char const without type annotations."""
|
||||
for target in node.targets:
|
||||
self._do_micro_check(target, node)
|
||||
|
||||
def _do_micro_check(
|
||||
self, target: nodes.NodeNG, node: nodes.Assign | nodes.AnnAssign
|
||||
) -> None:
|
||||
"""Check const assignment is not containing ANSI micro char."""
|
||||
|
||||
def _check_const(node_const: nodes.Const | Any) -> bool:
|
||||
if (
|
||||
isinstance(node_const, nodes.Const)
|
||||
and isinstance(node_const.value, str)
|
||||
and "\u00b5" in node_const.value
|
||||
):
|
||||
self.add_message(self.name, node=node)
|
||||
return True
|
||||
return False
|
||||
|
||||
# Check constant assignments
|
||||
if (
|
||||
isinstance(target, nodes.AssignName)
|
||||
and isinstance(node.value, nodes.Const)
|
||||
and _check_const(node.value)
|
||||
):
|
||||
return
|
||||
|
||||
# Check dict with EntityDescription calls
|
||||
if isinstance(target, nodes.AssignName) and isinstance(node.value, nodes.Dict):
|
||||
for _, subnode in node.value.items:
|
||||
if not isinstance(subnode, nodes.Call):
|
||||
continue
|
||||
for keyword in subnode.keywords:
|
||||
if _check_const(keyword.value):
|
||||
return
|
||||
|
||||
|
||||
def register(linter: PyLinter) -> None:
|
||||
"""Register the checker."""
|
||||
linter.register_checker(HassEnforceGreekMicroCharChecker(linter))
|
||||
@@ -163,6 +163,7 @@ load-plugins = [
|
||||
"pylint.extensions.typing",
|
||||
"hass_decorator",
|
||||
"hass_enforce_class_module",
|
||||
"hass_enforce_greek_micro_char",
|
||||
"hass_enforce_sorted_platforms",
|
||||
"hass_enforce_super_call",
|
||||
"hass_enforce_type_hints",
|
||||
|
||||
@@ -622,7 +622,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '84fce612f5b8-pm01',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[indoor][sensor.airgradient_pm1-state]
|
||||
@@ -631,7 +631,7 @@
|
||||
'device_class': 'pm1',
|
||||
'friendly_name': 'Airgradient PM1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airgradient_pm1',
|
||||
@@ -675,7 +675,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '84fce612f5b8-pm10',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[indoor][sensor.airgradient_pm10-state]
|
||||
@@ -684,7 +684,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Airgradient PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airgradient_pm10',
|
||||
@@ -728,7 +728,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '84fce612f5b8-pm02',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[indoor][sensor.airgradient_pm2_5-state]
|
||||
@@ -737,7 +737,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Airgradient PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airgradient_pm2_5',
|
||||
@@ -833,7 +833,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'raw_pm02',
|
||||
'unique_id': '84fce612f5b8-pm02_raw',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[indoor][sensor.airgradient_raw_pm2_5-state]
|
||||
@@ -842,7 +842,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Airgradient Raw PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airgradient_raw_pm2_5',
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'co',
|
||||
'unique_id': '123-456-co',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_carbon_monoxide-state]
|
||||
@@ -47,7 +47,7 @@
|
||||
'limit': 4000,
|
||||
'percent': 4,
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_carbon_monoxide',
|
||||
@@ -207,7 +207,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-456-no2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_nitrogen_dioxide-state]
|
||||
@@ -219,7 +219,7 @@
|
||||
'limit': 25,
|
||||
'percent': 64,
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_nitrogen_dioxide',
|
||||
@@ -266,7 +266,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-456-o3',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_ozone-state]
|
||||
@@ -278,7 +278,7 @@
|
||||
'limit': 100,
|
||||
'percent': 42,
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_ozone',
|
||||
@@ -325,7 +325,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-456-pm1',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_pm1-state]
|
||||
@@ -335,7 +335,7 @@
|
||||
'device_class': 'pm1',
|
||||
'friendly_name': 'Home PM1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_pm1',
|
||||
@@ -382,7 +382,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-456-pm10',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_pm10-state]
|
||||
@@ -394,7 +394,7 @@
|
||||
'limit': 45,
|
||||
'percent': 14,
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_pm10',
|
||||
@@ -441,7 +441,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-456-pm25',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_pm2_5-state]
|
||||
@@ -453,7 +453,7 @@
|
||||
'limit': 15,
|
||||
'percent': 29,
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_pm2_5',
|
||||
@@ -557,7 +557,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-456-so2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_sulphur_dioxide-state]
|
||||
@@ -569,7 +569,7 @@
|
||||
'limit': 40,
|
||||
'percent': 35,
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_sulphur_dioxide',
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'test-serial-number_PM10',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[entry_pm2_5]
|
||||
@@ -181,7 +181,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'test-serial-number_PM25',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[entry_temperature]
|
||||
@@ -314,7 +314,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Test Sensor PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_sensor_pm10',
|
||||
@@ -330,7 +330,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Test Sensor PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_sensor_pm2_5',
|
||||
|
||||
@@ -253,14 +253,14 @@ _LOGGER = logging.getLogger(__name__)
|
||||
{
|
||||
"sensor_entity": "sensor.test_device_18b2_pm10",
|
||||
"friendly_name": "Test Device 18B2 Pm10",
|
||||
"unit_of_measurement": "µg/m³",
|
||||
"unit_of_measurement": "μg/m³",
|
||||
"state_class": "measurement",
|
||||
"expected_state": "7170",
|
||||
},
|
||||
{
|
||||
"sensor_entity": "sensor.test_device_18b2_pm25",
|
||||
"friendly_name": "Test Device 18B2 Pm25",
|
||||
"unit_of_measurement": "µg/m³",
|
||||
"unit_of_measurement": "μg/m³",
|
||||
"state_class": "measurement",
|
||||
"expected_state": "3090",
|
||||
},
|
||||
@@ -296,7 +296,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
"sensor.test_device_18b2_volatile_organic_compounds"
|
||||
),
|
||||
"friendly_name": "Test Device 18B2 Volatile Organic Compounds",
|
||||
"unit_of_measurement": "µg/m³",
|
||||
"unit_of_measurement": "μg/m³",
|
||||
"state_class": "measurement",
|
||||
"expected_state": "307",
|
||||
},
|
||||
@@ -607,14 +607,14 @@ async def test_v1_sensors(
|
||||
{
|
||||
"sensor_entity": "sensor.test_device_18b2_pm10",
|
||||
"friendly_name": "Test Device 18B2 Pm10",
|
||||
"unit_of_measurement": "µg/m³",
|
||||
"unit_of_measurement": "μg/m³",
|
||||
"state_class": "measurement",
|
||||
"expected_state": "7170",
|
||||
},
|
||||
{
|
||||
"sensor_entity": "sensor.test_device_18b2_pm25",
|
||||
"friendly_name": "Test Device 18B2 Pm25",
|
||||
"unit_of_measurement": "µg/m³",
|
||||
"unit_of_measurement": "μg/m³",
|
||||
"state_class": "measurement",
|
||||
"expected_state": "3090",
|
||||
},
|
||||
@@ -650,7 +650,7 @@ async def test_v1_sensors(
|
||||
"sensor.test_device_18b2_volatile_organic_compounds"
|
||||
),
|
||||
"friendly_name": "Test Device 18B2 Volatile Organic Compounds",
|
||||
"unit_of_measurement": "µg/m³",
|
||||
"unit_of_measurement": "μg/m³",
|
||||
"state_class": "measurement",
|
||||
"expected_state": "307",
|
||||
},
|
||||
|
||||
@@ -829,7 +829,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'xx:xx:xx:xx:xx:xx:xx:xx-01-042a-particulate_matter_pm2_5',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[config_entry_options0-sensor_payload14-expected14][sensor.starkvind_airpurifier_pm25-state]
|
||||
@@ -838,7 +838,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'STARKVIND AirPurifier PM25',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.starkvind_airpurifier_pm25',
|
||||
@@ -1377,7 +1377,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00:00:01-02-0113-air_quality_formaldehyde',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[config_entry_options0-sensor_payload2-expected2][sensor.airquality_1_ch2o-state]
|
||||
@@ -1386,7 +1386,7 @@
|
||||
'device_class': 'volatile_organic_compounds',
|
||||
'friendly_name': 'AirQuality 1 CH2O',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airquality_1_ch2o',
|
||||
@@ -1483,7 +1483,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00:00:01-02-0113-air_quality_pm2_5',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[config_entry_options0-sensor_payload2-expected2][sensor.airquality_1_pm25-state]
|
||||
@@ -1492,7 +1492,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'AirQuality 1 PM25',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airquality_1_pm25',
|
||||
@@ -1699,7 +1699,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00:00:01-02-0113-air_quality_formaldehyde',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[config_entry_options0-sensor_payload3-expected3][sensor.airquality_1_ch2o-state]
|
||||
@@ -1708,7 +1708,7 @@
|
||||
'device_class': 'volatile_organic_compounds',
|
||||
'friendly_name': 'AirQuality 1 CH2O',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airquality_1_ch2o',
|
||||
@@ -1805,7 +1805,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00:00:01-02-0113-air_quality_pm2_5',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[config_entry_options0-sensor_payload3-expected3][sensor.airquality_1_pm25-state]
|
||||
@@ -1814,7 +1814,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'AirQuality 1 PM25',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airquality_1_pm25',
|
||||
@@ -1910,7 +1910,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00:00:01-02-0113-air_quality_formaldehyde',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[config_entry_options0-sensor_payload4-expected4][sensor.airquality_1_ch2o-state]
|
||||
@@ -1919,7 +1919,7 @@
|
||||
'device_class': 'volatile_organic_compounds',
|
||||
'friendly_name': 'AirQuality 1 CH2O',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airquality_1_ch2o',
|
||||
@@ -2016,7 +2016,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00:00:01-02-0113-air_quality_pm2_5',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[config_entry_options0-sensor_payload4-expected4][sensor.airquality_1_pm25-state]
|
||||
@@ -2025,7 +2025,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'AirQuality 1 PM25',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.airquality_1_pm25',
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'c6h6',
|
||||
'unique_id': '123-c6h6',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_benzene-state]
|
||||
@@ -112,7 +112,7 @@
|
||||
'attribution': 'Data provided by GIOŚ',
|
||||
'friendly_name': 'Home Benzene',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_benzene',
|
||||
@@ -159,7 +159,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'co',
|
||||
'unique_id': '123-co',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_carbon_monoxide-state]
|
||||
@@ -168,7 +168,7 @@
|
||||
'attribution': 'Data provided by GIOŚ',
|
||||
'friendly_name': 'Home Carbon monoxide',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_carbon_monoxide',
|
||||
@@ -215,7 +215,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-no2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_nitrogen_dioxide-state]
|
||||
@@ -225,7 +225,7 @@
|
||||
'device_class': 'nitrogen_dioxide',
|
||||
'friendly_name': 'Home Nitrogen dioxide',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_nitrogen_dioxide',
|
||||
@@ -339,7 +339,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-o3',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_ozone-state]
|
||||
@@ -349,7 +349,7 @@
|
||||
'device_class': 'ozone',
|
||||
'friendly_name': 'Home Ozone',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_ozone',
|
||||
@@ -463,7 +463,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-pm10',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_pm10-state]
|
||||
@@ -473,7 +473,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Home PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_pm10',
|
||||
@@ -587,7 +587,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-pm25',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_pm2_5-state]
|
||||
@@ -597,7 +597,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Home PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_pm2_5',
|
||||
@@ -711,7 +711,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '123-so2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.home_sulphur_dioxide-state]
|
||||
@@ -721,7 +721,7 @@
|
||||
'device_class': 'sulphur_dioxide',
|
||||
'friendly_name': 'Home Sulphur dioxide',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.home_sulphur_dioxide',
|
||||
|
||||
@@ -183,7 +183,7 @@ async def test_gvh5106(hass: HomeAssistant) -> None:
|
||||
pm25_sensor_attributes = pm25_sensor.attributes
|
||||
assert pm25_sensor.state == "0"
|
||||
assert pm25_sensor_attributes[ATTR_FRIENDLY_NAME] == "H5106 4E05 Pm25"
|
||||
assert pm25_sensor_attributes[ATTR_UNIT_OF_MEASUREMENT] == "µg/m³"
|
||||
assert pm25_sensor_attributes[ATTR_UNIT_OF_MEASUREMENT] == "μg/m³"
|
||||
assert pm25_sensor_attributes[ATTR_STATE_CLASS] == "measurement"
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
|
||||
@@ -365,14 +365,14 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00:00:00:00:00:00_1_2576_2580',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'state': dict({
|
||||
'attributes': dict({
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Airversa AP2 1808 PM2.5 Density',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'entity_id': 'sensor.airversa_ap2_1808_pm2_5_density',
|
||||
'state': '3.0',
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': <PinecilNumber.CALIBRATION_OFFSET: 'calibration_offset'>,
|
||||
'unique_id': 'c0:ff:ee:c0:ff:ee_calibration_offset',
|
||||
'unit_of_measurement': <UnitOfElectricPotential.MICROVOLT: 'µV'>,
|
||||
'unit_of_measurement': <UnitOfElectricPotential.MICROVOLT: 'μV'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_state[number.pinecil_calibration_offset-state]
|
||||
@@ -106,7 +106,7 @@
|
||||
'min': 100,
|
||||
'mode': <NumberMode.BOX: 'box'>,
|
||||
'step': 1,
|
||||
'unit_of_measurement': <UnitOfElectricPotential.MICROVOLT: 'µV'>,
|
||||
'unit_of_measurement': <UnitOfElectricPotential.MICROVOLT: 'μV'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.pinecil_calibration_offset',
|
||||
|
||||
@@ -566,7 +566,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': <PinecilSensor.TIP_VOLTAGE: 'tip_voltage'>,
|
||||
'unique_id': 'c0:ff:ee:c0:ff:ee_tip_voltage',
|
||||
'unit_of_measurement': <UnitOfElectricPotential.MICROVOLT: 'µV'>,
|
||||
'unit_of_measurement': <UnitOfElectricPotential.MICROVOLT: 'μV'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[sensor.pinecil_raw_tip_voltage-state]
|
||||
@@ -575,7 +575,7 @@
|
||||
'device_class': 'voltage',
|
||||
'friendly_name': 'Pinecil Raw tip voltage',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfElectricPotential.MICROVOLT: 'µV'>,
|
||||
'unit_of_measurement': <UnitOfElectricPotential.MICROVOLT: 'μV'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.pinecil_raw_tip_voltage',
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'MW2-2E247F93-B570-46A6-B827-920E9E10F966_pm1',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.test_air_conditioner_pm1-state]
|
||||
@@ -144,7 +144,7 @@
|
||||
'device_class': 'pm1',
|
||||
'friendly_name': 'Test air conditioner PM1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_air_conditioner_pm1',
|
||||
@@ -188,7 +188,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'MW2-2E247F93-B570-46A6-B827-920E9E10F966_pm10',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.test_air_conditioner_pm10-state]
|
||||
@@ -197,7 +197,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Test air conditioner PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_air_conditioner_pm10',
|
||||
@@ -241,7 +241,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'MW2-2E247F93-B570-46A6-B827-920E9E10F966_pm2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.test_air_conditioner_pm2_5-state]
|
||||
@@ -250,7 +250,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Test air conditioner PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_air_conditioner_pm2_5',
|
||||
|
||||
@@ -468,7 +468,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-2-PM1Sensor-1068-0',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[air_purifier][sensor.air_purifier_pm1-state]
|
||||
@@ -477,7 +477,7 @@
|
||||
'device_class': 'pm1',
|
||||
'friendly_name': 'Air Purifier PM1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.air_purifier_pm1',
|
||||
@@ -521,7 +521,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-2-PM10Sensor-1069-0',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[air_purifier][sensor.air_purifier_pm10-state]
|
||||
@@ -530,7 +530,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Air Purifier PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.air_purifier_pm10',
|
||||
@@ -574,7 +574,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-000000000000008F-MatterNodeDevice-2-PM25Sensor-1066-0',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[air_purifier][sensor.air_purifier_pm2_5-state]
|
||||
@@ -583,7 +583,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Air Purifier PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.air_purifier_pm2_5',
|
||||
@@ -1017,7 +1017,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-PM1Sensor-1068-0',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[air_quality_sensor][sensor.lightfi_aq1_air_quality_sensor_pm1-state]
|
||||
@@ -1026,7 +1026,7 @@
|
||||
'device_class': 'pm1',
|
||||
'friendly_name': 'lightfi-aq1-air-quality-sensor PM1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.lightfi_aq1_air_quality_sensor_pm1',
|
||||
@@ -1070,7 +1070,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-PM10Sensor-1069-0',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[air_quality_sensor][sensor.lightfi_aq1_air_quality_sensor_pm10-state]
|
||||
@@ -1079,7 +1079,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'lightfi-aq1-air-quality-sensor PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.lightfi_aq1_air_quality_sensor_pm10',
|
||||
@@ -1123,7 +1123,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '00000000000004D2-0000000000000001-MatterNodeDevice-1-PM25Sensor-1066-0',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[air_quality_sensor][sensor.lightfi_aq1_air_quality_sensor_pm2_5-state]
|
||||
@@ -1132,7 +1132,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'lightfi-aq1-air-quality-sensor PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.lightfi_aq1_air_quality_sensor_pm2_5',
|
||||
|
||||
@@ -26,6 +26,7 @@ from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_MODE,
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
@@ -253,6 +254,62 @@ async def test_native_value_validation(
|
||||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
{
|
||||
mqtt.DOMAIN: {
|
||||
number.DOMAIN: {
|
||||
"name": "test",
|
||||
"command_topic": "test-topic-cmd",
|
||||
"state_topic": "test-topic",
|
||||
"unit_of_measurement": "\u00b5V",
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
)
|
||||
async def test_equivalent_unit_of_measurement(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test device_class with equivalent unit of measurement."""
|
||||
assert await mqtt_mock_entry()
|
||||
async_fire_mqtt_message(hass, "test-topic", "100")
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("number.test")
|
||||
assert state is not None
|
||||
assert state.state == "100"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
is UnitOfElectricPotential.MICROVOLT
|
||||
)
|
||||
|
||||
caplog.clear()
|
||||
|
||||
discovery_payload = {
|
||||
"name": "bla",
|
||||
"command_topic": "test-topic2-cmd",
|
||||
"state_topic": "test-topic2",
|
||||
"unit_of_measurement": "\u00b5V",
|
||||
}
|
||||
# Now discover an invalid sensor
|
||||
async_fire_mqtt_message(
|
||||
hass, "homeassistant/number/bla/config", json.dumps(discovery_payload)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, "test-topic2", "21")
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("number.bla")
|
||||
assert state is not None
|
||||
assert state.state == "21"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
is UnitOfElectricPotential.MICROVOLT
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
|
||||
@@ -15,9 +15,11 @@ import pytest
|
||||
from homeassistant.components import mqtt, sensor
|
||||
from homeassistant.components.mqtt.sensor import MQTT_SENSOR_ATTRIBUTES_BLOCKED
|
||||
from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
EVENT_STATE_CHANGED,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, State, callback
|
||||
@@ -936,6 +938,116 @@ async def test_invalid_unit_of_measurement(
|
||||
assert len(events) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
{
|
||||
mqtt.DOMAIN: {
|
||||
sensor.DOMAIN: {
|
||||
"name": "test",
|
||||
"state_topic": "test-topic",
|
||||
"device_class": "voltage",
|
||||
"unit_of_measurement": "\u00b5V",
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
)
|
||||
async def test_device_class_with_equivalent_unit_of_measurement_received(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test device_class with equivalent unit of measurement."""
|
||||
assert await mqtt_mock_entry()
|
||||
async_fire_mqtt_message(hass, "test-topic", "100")
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test")
|
||||
assert state is not None
|
||||
assert state.state == "100"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
is UnitOfElectricPotential.MICROVOLT
|
||||
)
|
||||
|
||||
caplog.clear()
|
||||
|
||||
discovery_payload = {
|
||||
"name": "bla",
|
||||
"state_topic": "test-topic2",
|
||||
"device_class": "voltage",
|
||||
"unit_of_measurement": "\u00b5V",
|
||||
}
|
||||
# Now discover an invalid sensor
|
||||
async_fire_mqtt_message(
|
||||
hass, "homeassistant/sensor/bla/config", json.dumps(discovery_payload)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, "test-topic2", "21")
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.bla")
|
||||
assert state is not None
|
||||
assert state.state == "21"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
is UnitOfElectricPotential.MICROVOLT
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
{
|
||||
mqtt.DOMAIN: {
|
||||
sensor.DOMAIN: {
|
||||
"name": "test",
|
||||
"state_topic": "test-topic",
|
||||
"unit_of_measurement": "\u00b5V",
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
)
|
||||
async def test_equivalent_unit_of_measurement_received_without_device_class(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test device_class with equivalent unit of measurement."""
|
||||
assert await mqtt_mock_entry()
|
||||
async_fire_mqtt_message(hass, "test-topic", "100")
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.test")
|
||||
assert state is not None
|
||||
assert state.state == "100"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
is UnitOfElectricPotential.MICROVOLT
|
||||
)
|
||||
|
||||
caplog.clear()
|
||||
|
||||
discovery_payload = {
|
||||
"name": "bla",
|
||||
"state_topic": "test-topic2",
|
||||
"unit_of_measurement": "\u00b5V",
|
||||
}
|
||||
# Now discover an invalid sensor
|
||||
async_fire_mqtt_message(
|
||||
hass, "homeassistant/sensor/bla/config", json.dumps(discovery_payload)
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_mqtt_message(hass, "test-topic2", "21")
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.bla")
|
||||
assert state is not None
|
||||
assert state.state == "21"
|
||||
assert (
|
||||
state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
is UnitOfElectricPotential.MICROVOLT
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hass_config",
|
||||
[
|
||||
|
||||
@@ -981,7 +981,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'pmsx003_pm1',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-pms_p0',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_pmsx003_pm1-state]
|
||||
@@ -990,7 +990,7 @@
|
||||
'device_class': 'pm1',
|
||||
'friendly_name': 'Nettigo Air Monitor PMSx003 PM1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_pmsx003_pm1',
|
||||
@@ -1037,7 +1037,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'pmsx003_pm10',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-pms_p1',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_pmsx003_pm10-state]
|
||||
@@ -1046,7 +1046,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Nettigo Air Monitor PMSx003 PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_pmsx003_pm10',
|
||||
@@ -1093,7 +1093,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'pmsx003_pm25',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-pms_p2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_pmsx003_pm2_5-state]
|
||||
@@ -1102,7 +1102,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Nettigo Air Monitor PMSx003 PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_pmsx003_pm2_5',
|
||||
@@ -1261,7 +1261,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sds011_pm10',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-sds011_p1',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_sds011_pm10-state]
|
||||
@@ -1270,7 +1270,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Nettigo Air Monitor SDS011 PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_sds011_pm10',
|
||||
@@ -1317,7 +1317,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sds011_pm25',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-sds011_p2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_sds011_pm2_5-state]
|
||||
@@ -1326,7 +1326,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Nettigo Air Monitor SDS011 PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_sds011_pm2_5',
|
||||
@@ -1653,7 +1653,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sps30_pm1',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-sps30_p0',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_sps30_pm1-state]
|
||||
@@ -1662,7 +1662,7 @@
|
||||
'device_class': 'pm1',
|
||||
'friendly_name': 'Nettigo Air Monitor SPS30 PM1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_sps30_pm1',
|
||||
@@ -1709,7 +1709,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sps30_pm10',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-sps30_p1',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_sps30_pm10-state]
|
||||
@@ -1718,7 +1718,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Nettigo Air Monitor SPS30 PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_sps30_pm10',
|
||||
@@ -1765,7 +1765,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sps30_pm25',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-sps30_p2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_sps30_pm2_5-state]
|
||||
@@ -1774,7 +1774,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Nettigo Air Monitor SPS30 PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_sps30_pm2_5',
|
||||
@@ -1821,7 +1821,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'sps30_pm4',
|
||||
'unique_id': 'aa:bb:cc:dd:ee:ff-sps30_p4',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor[sensor.nettigo_air_monitor_sps30_pm4-state]
|
||||
@@ -1829,7 +1829,7 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Nettigo Air Monitor SPS30 PM4',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.nettigo_air_monitor_sps30_pm4',
|
||||
|
||||
@@ -7,6 +7,7 @@ from unittest.mock import MagicMock, patch
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.number import (
|
||||
AMBIGUOUS_UNITS,
|
||||
ATTR_MAX,
|
||||
ATTR_MIN,
|
||||
ATTR_MODE,
|
||||
@@ -48,6 +49,7 @@ from . import common
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockEntity,
|
||||
MockModule,
|
||||
MockPlatform,
|
||||
async_mock_restore_state_shutdown_restart,
|
||||
@@ -61,6 +63,25 @@ from tests.common import (
|
||||
TEST_DOMAIN = "test"
|
||||
|
||||
|
||||
class MockNumber(MockEntity, NumberEntity):
|
||||
"""Mock NumberEntity class to test unit of measurement."""
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of this sensor."""
|
||||
return self._handle("device_class")
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self):
|
||||
"""Return the native unit_of_measurement of this sensor."""
|
||||
return self._handle("native_unit_of_measurement")
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the native value of this sensor."""
|
||||
return self._handle("native_value")
|
||||
|
||||
|
||||
class MockDefaultNumberEntity(NumberEntity):
|
||||
"""Mock NumberEntity device to use in tests.
|
||||
|
||||
@@ -900,6 +921,33 @@ async def test_translated_unit_with_native_unit_raises(
|
||||
assert entity0.entity_id is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("ambiguous_unit", "normalized_unit"),
|
||||
[
|
||||
(ambiguous_unit, normalized_unit)
|
||||
for ambiguous_unit, normalized_unit in AMBIGUOUS_UNITS.items()
|
||||
],
|
||||
)
|
||||
async def test_ambiguous_unit_of_measurement_compat(
|
||||
hass: HomeAssistant, ambiguous_unit: str, normalized_unit: str
|
||||
) -> None:
|
||||
"""Test ambiguous native_unit_of_measurement values are corrected."""
|
||||
entity0 = MockNumber(
|
||||
name="Test",
|
||||
native_value="0.0",
|
||||
native_unit_of_measurement=ambiguous_unit,
|
||||
)
|
||||
setup_test_component_platform(hass, DOMAIN, [entity0])
|
||||
|
||||
assert await async_setup_component(hass, "number", {"number": {"platform": "test"}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check compatible unit is applied
|
||||
state = hass.states.get(entity0.entity_id)
|
||||
assert state.state == "0.0"
|
||||
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == normalized_unit
|
||||
|
||||
|
||||
def test_device_classes_aligned() -> None:
|
||||
"""Make sure all sensor device classes are also available in NumberDeviceClass."""
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '12.34-56.78-no2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[air_pollution][sensor.openweathermap_nitrogen_dioxide-state]
|
||||
@@ -150,7 +150,7 @@
|
||||
'device_class': 'nitrogen_dioxide',
|
||||
'friendly_name': 'openweathermap Nitrogen dioxide',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.openweathermap_nitrogen_dioxide',
|
||||
@@ -194,7 +194,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '12.34-56.78-no',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[air_pollution][sensor.openweathermap_nitrogen_monoxide-state]
|
||||
@@ -204,7 +204,7 @@
|
||||
'device_class': 'nitrogen_monoxide',
|
||||
'friendly_name': 'openweathermap Nitrogen monoxide',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.openweathermap_nitrogen_monoxide',
|
||||
@@ -248,7 +248,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '12.34-56.78-o3',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[air_pollution][sensor.openweathermap_ozone-state]
|
||||
@@ -258,7 +258,7 @@
|
||||
'device_class': 'ozone',
|
||||
'friendly_name': 'openweathermap Ozone',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.openweathermap_ozone',
|
||||
@@ -302,7 +302,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '12.34-56.78-pm10',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[air_pollution][sensor.openweathermap_pm10-state]
|
||||
@@ -312,7 +312,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'openweathermap PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.openweathermap_pm10',
|
||||
@@ -356,7 +356,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '12.34-56.78-pm2_5',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[air_pollution][sensor.openweathermap_pm2_5-state]
|
||||
@@ -366,7 +366,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'openweathermap PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.openweathermap_pm2_5',
|
||||
@@ -410,7 +410,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '12.34-56.78-so2',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_sensor_states[air_pollution][sensor.openweathermap_sulphur_dioxide-state]
|
||||
@@ -420,7 +420,7 @@
|
||||
'device_class': 'sulphur_dioxide',
|
||||
'friendly_name': 'openweathermap Sulphur dioxide',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.openweathermap_sulphur_dioxide',
|
||||
|
||||
@@ -29,15 +29,15 @@ UNITS_OF_MEASUREMENT = {
|
||||
SensorDeviceClass.HUMIDITY: PERCENTAGE, # % of humidity in the air
|
||||
SensorDeviceClass.ILLUMINANCE: LIGHT_LUX, # current light level lx
|
||||
SensorDeviceClass.MOISTURE: PERCENTAGE, # % of water in a substance
|
||||
SensorDeviceClass.NITROGEN_DIOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of nitrogen dioxide
|
||||
SensorDeviceClass.NITROGEN_MONOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of nitrogen monoxide
|
||||
SensorDeviceClass.NITROUS_OXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of nitrogen oxide
|
||||
SensorDeviceClass.OZONE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of ozone
|
||||
SensorDeviceClass.PM1: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of PM1
|
||||
SensorDeviceClass.PM10: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of PM10
|
||||
SensorDeviceClass.PM25: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of PM2.5
|
||||
SensorDeviceClass.NITROGEN_DIOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of nitrogen dioxide
|
||||
SensorDeviceClass.NITROGEN_MONOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of nitrogen monoxide
|
||||
SensorDeviceClass.NITROUS_OXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of nitrogen oxide
|
||||
SensorDeviceClass.OZONE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of ozone
|
||||
SensorDeviceClass.PM1: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of PM1
|
||||
SensorDeviceClass.PM10: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of PM10
|
||||
SensorDeviceClass.PM25: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of PM2.5
|
||||
SensorDeviceClass.SIGNAL_STRENGTH: SIGNAL_STRENGTH_DECIBELS, # signal strength (dB/dBm)
|
||||
SensorDeviceClass.SULPHUR_DIOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of sulphur dioxide
|
||||
SensorDeviceClass.SULPHUR_DIOXIDE: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of sulphur dioxide
|
||||
SensorDeviceClass.TEMPERATURE: "C", # temperature (C/F)
|
||||
SensorDeviceClass.PRESSURE: UnitOfPressure.HPA, # pressure (hPa/mbar)
|
||||
SensorDeviceClass.POWER: "kW", # power (W/kW)
|
||||
@@ -47,7 +47,7 @@ UNITS_OF_MEASUREMENT = {
|
||||
SensorDeviceClass.POWER_FACTOR: PERCENTAGE, # power factor (no unit, min: -1.0, max: 1.0)
|
||||
SensorDeviceClass.REACTIVE_ENERGY: UnitOfReactiveEnergy.VOLT_AMPERE_REACTIVE_HOUR, # reactive energy (varh)
|
||||
SensorDeviceClass.REACTIVE_POWER: UnitOfReactivePower.VOLT_AMPERE_REACTIVE, # reactive power (var)
|
||||
SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # µg/m³ of vocs
|
||||
SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS: CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, # μg/m³ of vocs
|
||||
SensorDeviceClass.VOLTAGE: "V", # voltage (V)
|
||||
SensorDeviceClass.GAS: UnitOfVolume.CUBIC_METERS, # gas (m³)
|
||||
}
|
||||
|
||||
@@ -159,12 +159,39 @@ async def test_temperature_conversion_wrong_device_class(
|
||||
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check temperature is not converted
|
||||
# Check compatible unit is applied
|
||||
state = hass.states.get(entity0.entity_id)
|
||||
assert state.state == "0.0"
|
||||
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == UnitOfTemperature.FAHRENHEIT
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("ambiguous_unit", "normalized_unit"),
|
||||
[
|
||||
(ambiguous_unit, normalized_unit)
|
||||
for ambiguous_unit, normalized_unit in sensor.AMBIGUOUS_UNITS.items()
|
||||
],
|
||||
)
|
||||
async def test_ambiguous_unit_of_measurement_compat(
|
||||
hass: HomeAssistant, ambiguous_unit: str, normalized_unit: str
|
||||
) -> None:
|
||||
"""Test ambiguous native_unit_of_measurement values are corrected."""
|
||||
entity0 = MockSensor(
|
||||
name="Test",
|
||||
native_value="0.0",
|
||||
native_unit_of_measurement=ambiguous_unit,
|
||||
)
|
||||
setup_test_component_platform(hass, sensor.DOMAIN, [entity0])
|
||||
|
||||
assert await async_setup_component(hass, "sensor", {"sensor": {"platform": "test"}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check temperature is not converted
|
||||
state = hass.states.get(entity0.entity_id)
|
||||
assert state.state == "0.0"
|
||||
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == normalized_unit
|
||||
|
||||
|
||||
@pytest.mark.parametrize("state_class", ["measurement", "total_increasing"])
|
||||
async def test_deprecated_last_reset(
|
||||
hass: HomeAssistant,
|
||||
|
||||
@@ -3755,6 +3755,44 @@ async def test_compile_hourly_statistics_convert_units_1(
|
||||
30,
|
||||
),
|
||||
(None, "m3", "m³", None, "volume", 13.050847, 13.333333, -10, 30),
|
||||
(None, "\u00b5V", "\u03bcV", None, "voltage", 13.050847, 13.333333, -10, 30),
|
||||
(None, "\u00b5Sv/h", "\u03bcSv/h", None, None, 13.050847, 13.333333, -10, 30),
|
||||
(
|
||||
None,
|
||||
"\u00b5S/cm",
|
||||
"\u03bcS/cm",
|
||||
None,
|
||||
"conductivity",
|
||||
13.050847,
|
||||
13.333333,
|
||||
-10,
|
||||
30,
|
||||
),
|
||||
(None, "\u00b5g/ft³", "\u03bcg/ft³", None, None, 13.050847, 13.333333, -10, 30),
|
||||
(
|
||||
None,
|
||||
"\u00b5g/m³",
|
||||
"\u03bcg/m³",
|
||||
None,
|
||||
"concentration",
|
||||
13.050847,
|
||||
13.333333,
|
||||
-10,
|
||||
30,
|
||||
),
|
||||
(
|
||||
None,
|
||||
"\u00b5mol/s⋅m²",
|
||||
"\u03bcmol/s⋅m²",
|
||||
None,
|
||||
None,
|
||||
13.050847,
|
||||
13.333333,
|
||||
-10,
|
||||
30,
|
||||
),
|
||||
(None, "\u00b5g", "\u03bcg", None, "mass", 13.050847, 13.333333, -10, 30),
|
||||
(None, "\u00b5s", "\u03bcs", None, "duration", 13.050847, 13.333333, -10, 30),
|
||||
],
|
||||
)
|
||||
async def test_compile_hourly_statistics_equivalent_units_1(
|
||||
@@ -3884,6 +3922,17 @@ async def test_compile_hourly_statistics_equivalent_units_1(
|
||||
(None, "ft3", "ft³", None, 13.333333, -10, 30),
|
||||
(None, "ft³/m", "ft³/min", None, 13.333333, -10, 30),
|
||||
(None, "m3", "m³", None, 13.333333, -10, 30),
|
||||
(None, "\u00b5V", "\u03bcV", None, 13.333333, -10, 30),
|
||||
(SensorDeviceClass.VOLTAGE, "\u00b5V", "\u03bcV", None, 13.333333, -10, 30),
|
||||
(None, "\u00b5Sv/h", "\u03bcSv/h", None, 13.333333, -10, 30),
|
||||
(None, "\u00b5S/cm", "\u03bcS/cm", None, 13.333333, -10, 30),
|
||||
(None, "\u00b5g/ft³", "\u03bcg/ft³", None, 13.333333, -10, 30),
|
||||
(None, "\u00b5g/m³", "\u03bcg/m³", None, 13.333333, -10, 30),
|
||||
(None, "\u00b5mol/s⋅m²", "\u03bcmol/s⋅m²", None, 13.333333, -10, 30),
|
||||
(None, "\u00b5g", "\u03bcg", None, 13.333333, -10, 30),
|
||||
(SensorDeviceClass.WEIGHT, "\u00b5g", "\u03bcg", None, 13.333333, -10, 30),
|
||||
(None, "\u00b5s", "\u03bcs", None, 13.333333, -10, 30),
|
||||
(SensorDeviceClass.DURATION, "\u00b5s", "\u03bcs", None, 13.333333, -10, 30),
|
||||
],
|
||||
)
|
||||
async def test_compile_hourly_statistics_equivalent_units_2(
|
||||
@@ -5705,6 +5754,14 @@ async def test_validate_statistics_unit_change_no_conversion(
|
||||
(NONE_SENSOR_ATTRIBUTES, "m3", "m³"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "rpm", "RPM"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "RPM", "rpm"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u00b5V", "\u03bcV"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u00b5Sv/h", "\u03bcSv/h"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u00b5S/cm", "\u03bcS/cm"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u00b5g/ft³", "\u03bcg/ft³"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u00b5g/m³", "\u03bcg/m³"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u00b5mol/s⋅m²", "\u03bcmol/s⋅m²"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u00b5g", "\u03bcg"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u00b5s", "\u03bcs"),
|
||||
],
|
||||
)
|
||||
async def test_validate_statistics_unit_change_equivalent_units(
|
||||
@@ -5768,6 +5825,15 @@ async def test_validate_statistics_unit_change_equivalent_units(
|
||||
("attributes", "unit1", "unit2", "supported_unit"),
|
||||
[
|
||||
(NONE_SENSOR_ATTRIBUTES, "m³", "m3", "CCF, L, fl. oz., ft³, gal, mL, m³"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u03bcV", "\u00b5V", "MV, V, kV, mV, \u03bcV"),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u03bcS/cm", "\u00b5S/cm", "S/cm, mS/cm, \u03bcS/cm"),
|
||||
(
|
||||
NONE_SENSOR_ATTRIBUTES,
|
||||
"\u03bcg",
|
||||
"\u00b5g",
|
||||
"g, kg, lb, mg, oz, st, \u03bcg",
|
||||
),
|
||||
(NONE_SENSOR_ATTRIBUTES, "\u03bcs", "\u00b5s", "d, h, min, ms, s, w, \u03bcs"),
|
||||
],
|
||||
)
|
||||
async def test_validate_statistics_unit_change_equivalent_units_2(
|
||||
|
||||
@@ -951,7 +951,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'a3a970ea-e09c-9c04-161b-94c934e21666_main_veryFineDustSensor_veryFineDustLevel_veryFineDustLevel',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_ac_airsensor_01001][sensor.eeomoniteo_peulreoseu_pm1-state]
|
||||
@@ -960,7 +960,7 @@
|
||||
'device_class': 'pm1',
|
||||
'friendly_name': '에어모니터 플러스 PM1',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.eeomoniteo_peulreoseu_pm1',
|
||||
@@ -1004,7 +1004,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'a3a970ea-e09c-9c04-161b-94c934e21666_main_dustSensor_dustLevel_dustLevel',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_ac_airsensor_01001][sensor.eeomoniteo_peulreoseu_pm10-state]
|
||||
@@ -1013,7 +1013,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': '에어모니터 플러스 PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.eeomoniteo_peulreoseu_pm10',
|
||||
@@ -1057,7 +1057,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'a3a970ea-e09c-9c04-161b-94c934e21666_main_dustSensor_fineDustLevel_fineDustLevel',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_ac_airsensor_01001][sensor.eeomoniteo_peulreoseu_pm2_5-state]
|
||||
@@ -1066,7 +1066,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': '에어모니터 플러스 PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.eeomoniteo_peulreoseu_pm2_5',
|
||||
@@ -2820,7 +2820,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'F8042E25-0E53-0000-0000-000000000000_main_dustSensor_dustLevel_dustLevel',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_pm10-state]
|
||||
@@ -2829,7 +2829,7 @@
|
||||
'device_class': 'pm10',
|
||||
'friendly_name': 'Corridor A/C PM10',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.corridor_a_c_pm10',
|
||||
@@ -2873,7 +2873,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'F8042E25-0E53-0000-0000-000000000000_main_dustSensor_fineDustLevel_fineDustLevel',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[da_ac_rac_100001][sensor.corridor_a_c_pm2_5-state]
|
||||
@@ -2882,7 +2882,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Corridor A/C PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.corridor_a_c_pm2_5',
|
||||
|
||||
@@ -361,7 +361,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '400s-purifier-pm25',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
])
|
||||
# ---
|
||||
@@ -399,7 +399,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Air Purifier 400s PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.air_purifier_400s_pm2_5',
|
||||
@@ -547,7 +547,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '600s-purifier-pm25',
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
])
|
||||
# ---
|
||||
@@ -585,7 +585,7 @@
|
||||
'device_class': 'pm25',
|
||||
'friendly_name': 'Air Purifier 600s PM2.5',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': 'µg/m³',
|
||||
'unit_of_measurement': 'μg/m³',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.air_purifier_600s_pm2_5',
|
||||
|
||||
@@ -262,7 +262,7 @@ async def test_xiaomi_hhccjcy01(hass: HomeAssistant) -> None:
|
||||
cond_sensor_attribtes = cond_sensor.attributes
|
||||
assert cond_sensor.state == "599"
|
||||
assert cond_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Plant Sensor 3E7A Conductivity"
|
||||
assert cond_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "µS/cm"
|
||||
assert cond_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "μS/cm"
|
||||
assert cond_sensor_attribtes[ATTR_STATE_CLASS] == "measurement"
|
||||
|
||||
moist_sensor = hass.states.get("sensor.plant_sensor_3e7a_moisture")
|
||||
@@ -351,7 +351,7 @@ async def test_xiaomi_hhccjcy01_not_connectable(hass: HomeAssistant) -> None:
|
||||
cond_sensor_attribtes = cond_sensor.attributes
|
||||
assert cond_sensor.state == "599"
|
||||
assert cond_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Plant Sensor 3E7A Conductivity"
|
||||
assert cond_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "µS/cm"
|
||||
assert cond_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "μS/cm"
|
||||
assert cond_sensor_attribtes[ATTR_STATE_CLASS] == "measurement"
|
||||
|
||||
moist_sensor = hass.states.get("sensor.plant_sensor_3e7a_moisture")
|
||||
@@ -438,7 +438,7 @@ async def test_xiaomi_hhccjcy01_only_some_sources_connectable(
|
||||
cond_sensor_attribtes = cond_sensor.attributes
|
||||
assert cond_sensor.state == "599"
|
||||
assert cond_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Plant Sensor 3E7A Conductivity"
|
||||
assert cond_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "µS/cm"
|
||||
assert cond_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "μS/cm"
|
||||
assert cond_sensor_attribtes[ATTR_STATE_CLASS] == "measurement"
|
||||
|
||||
moist_sensor = hass.states.get("sensor.plant_sensor_3e7a_moisture")
|
||||
@@ -653,7 +653,7 @@ async def test_hhccjcy10_uuid(hass: HomeAssistant) -> None:
|
||||
cond_sensor_attr = cond_sensor.attributes
|
||||
assert cond_sensor.state == "91"
|
||||
assert cond_sensor_attr[ATTR_FRIENDLY_NAME] == "Plant Sensor 5BFC Conductivity"
|
||||
assert cond_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "µS/cm"
|
||||
assert cond_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "μS/cm"
|
||||
assert cond_sensor_attr[ATTR_STATE_CLASS] == "measurement"
|
||||
|
||||
moist_sensor = hass.states.get("sensor.plant_sensor_5bfc_moisture")
|
||||
|
||||
@@ -138,3 +138,24 @@ def decorator_checker_fixture(hass_decorator, linter) -> BaseChecker:
|
||||
type_hint_checker = hass_decorator.HassDecoratorChecker(linter)
|
||||
type_hint_checker.module = "homeassistant.components.pylint_test"
|
||||
return type_hint_checker
|
||||
|
||||
|
||||
@pytest.fixture(name="hass_enforce_greek_micro_char", scope="package")
|
||||
def hass_enforce_greek_micro_checker_fixture() -> ModuleType:
|
||||
"""Fixture to the content for the hass_enforce_greek_micro_char check."""
|
||||
return _load_plugin_from_file(
|
||||
"hass_enforce_greek_micro_char",
|
||||
"pylint/plugins/hass_enforce_greek_micro_char.py",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="enforce_greek_micro_char_checker")
|
||||
def enforce_greek_micro_char_checker_fixture(
|
||||
hass_enforce_greek_micro_char, linter
|
||||
) -> BaseChecker:
|
||||
"""Fixture to provide a hass_enforce_greek_micro_char checker."""
|
||||
enforce_greek_micro_char_checker = (
|
||||
hass_enforce_greek_micro_char.HassEnforceGreekMicroCharChecker(linter)
|
||||
)
|
||||
enforce_greek_micro_char_checker.module = "homeassistant.components.pylint_test"
|
||||
return enforce_greek_micro_char_checker
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
"""Tests for pylint hass_enforce_greek_micro_char plugin."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import astroid
|
||||
from pylint.checkers import BaseChecker
|
||||
from pylint.testutils.unittest_linter import UnittestLinter
|
||||
from pylint.utils.ast_walker import ASTWalker
|
||||
import pytest
|
||||
|
||||
from . import assert_no_messages
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"code",
|
||||
[
|
||||
pytest.param(
|
||||
"""
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "μg/m³" # "μ" == "\u03bc"
|
||||
""",
|
||||
id="good_const_with_annotation",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "\u03bcg/m³" # "μ" == "\u03bc"
|
||||
""",
|
||||
id="good_unicode_const_with_annotation",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER = "μg/m³" # "μ" == "\u03bc"
|
||||
""",
|
||||
id="good_const_without_annotation",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
class UnitOfElectricPotential(StrEnum):
|
||||
\"\"\"Electric potential units.\"\"\"
|
||||
|
||||
MICROVOLT = "μV" # "μ" == "\u03bc"
|
||||
MILLIVOLT = "mV"
|
||||
VOLT = "V"
|
||||
KILOVOLT = "kV"
|
||||
MEGAVOLT = "MV"
|
||||
""",
|
||||
id="good_str_enum",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
SENSOR_DESCRIPTION = {
|
||||
"radiation_rate": AranetSensorEntityDescription(
|
||||
key="radiation_rate",
|
||||
translation_key="radiation_rate",
|
||||
name="Radiation Dose Rate",
|
||||
native_unit_of_measurement="μSv/h", # "μ" == "\u03bc"
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=2,
|
||||
scale=0.001,
|
||||
),
|
||||
}
|
||||
OTHER_DICT = {
|
||||
"value_with_bad_mu_should_pass": "µ"
|
||||
}
|
||||
""",
|
||||
id="good_sensor_description",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_enforce_greek_micro_char(
|
||||
linter: UnittestLinter,
|
||||
enforce_greek_micro_char_checker: BaseChecker,
|
||||
code: str,
|
||||
) -> None:
|
||||
"""Good test cases."""
|
||||
root_node = astroid.parse(code, "homeassistant.components.pylint_test")
|
||||
walker = ASTWalker(linter)
|
||||
walker.add_checker(enforce_greek_micro_char_checker)
|
||||
|
||||
with assert_no_messages(linter):
|
||||
walker.walk(root_node)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"code",
|
||||
[
|
||||
pytest.param(
|
||||
"""
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "µg/m³" # "μ" != "\u03bc"
|
||||
""",
|
||||
id="bad_const_with_annotation",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "\u00b5g/m³" # "μ" != "\u03bc"
|
||||
""",
|
||||
id="bad_unicode_const_with_annotation",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER = "µg/m³" # "μ" != "\u03bc"
|
||||
""",
|
||||
id="bad_const_without_annotation",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
class UnitOfElectricPotential(StrEnum):
|
||||
\"\"\"Electric potential units.\"\"\"
|
||||
|
||||
MICROVOLT = "µV" # "μ" != "\u03bc"
|
||||
MILLIVOLT = "mV"
|
||||
VOLT = "V"
|
||||
KILOVOLT = "kV"
|
||||
MEGAVOLT = "MV"
|
||||
""",
|
||||
id="bad_str_enum",
|
||||
),
|
||||
pytest.param(
|
||||
"""
|
||||
SENSOR_DESCRIPTION = {
|
||||
"radiation_rate": AranetSensorEntityDescription(
|
||||
key="radiation_rate",
|
||||
translation_key="radiation_rate",
|
||||
name="Radiation Dose Rate",
|
||||
native_unit_of_measurement="µSv/h", # "μ" != "\u03bc"
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=2,
|
||||
scale=0.001,
|
||||
),
|
||||
}
|
||||
""",
|
||||
id="bad_sensor_description",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_enforce_greek_micro_char_assign_bad(
|
||||
linter: UnittestLinter,
|
||||
enforce_greek_micro_char_checker: BaseChecker,
|
||||
code: str,
|
||||
) -> None:
|
||||
"""Bad assignment test cases."""
|
||||
root_node = astroid.parse(code, "homeassistant.components.pylint_test")
|
||||
walker = ASTWalker(linter)
|
||||
walker.add_checker(enforce_greek_micro_char_checker)
|
||||
|
||||
walker.walk(root_node)
|
||||
messages = linter.release_messages()
|
||||
assert len(messages) == 1
|
||||
message = next(iter(messages))
|
||||
assert message.msg_id == "hass-enforce-greek-micro-char"
|
||||
+1
-1
@@ -118,7 +118,7 @@ def test_deprecated_unit_of_conductivity_alias() -> None:
|
||||
"""Test UnitOfConductivity deprecation."""
|
||||
|
||||
# Test the deprecated members are aliases
|
||||
assert set(const.UnitOfConductivity) == {"S/cm", "µS/cm", "mS/cm"}
|
||||
assert set(const.UnitOfConductivity) == {"S/cm", "μS/cm", "mS/cm"}
|
||||
|
||||
|
||||
def test_deprecated_unit_of_conductivity_members(
|
||||
|
||||
Reference in New Issue
Block a user