Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@v5.0.0
- name: Dependency review
uses: actions/dependency-review-action@v4.7.1
uses: actions/dependency-review-action@v4.7.2
with:
license-check: false # We use our own license audit checks

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/airgradient/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"display_pm_standard": {
"name": "Display PM standard",
"state": {
"ugm3": "µg/m³",
"ugm3": "μg/m³",
"us_aqi": "US AQI"
}
},
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/bluetooth/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"quality_scale": "internal",
"requirements": [
"bleak==1.0.1",
"bleak-retry-connector==4.0.1",
"bleak-retry-connector==4.0.2",
"bluetooth-adapters==2.0.0",
"bluetooth-auto-recovery==1.5.2",
"bluetooth-data-tools==1.28.2",
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/bthome/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
key=str(BTHomeExtendedSensorDeviceClass.CHANNEL),
state_class=SensorStateClass.MEASUREMENT,
),
# Conductivity (µS/cm)
# Conductivity (μS/cm)
(
BTHomeSensorDeviceClass.CONDUCTIVITY,
Units.CONDUCTIVITY,
Expand Down Expand Up @@ -215,7 +215,7 @@
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
# PM10 (µg/m3)
# PM10 (μg/m3)
(
BTHomeSensorDeviceClass.PM10,
Units.CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
Expand All @@ -225,7 +225,7 @@
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,
Expand Down Expand Up @@ -318,7 +318,7 @@
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,
Expand Down
12 changes: 12 additions & 0 deletions homeassistant/components/derivative/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
config_entry, version=1, minor_version=3
)

if config_entry.minor_version < 4:
# Ensure we use the correct units
new_options = {**config_entry.options}

if new_options.get("unit_prefix") == "\u00b5":
# Ensure we use the preferred coding of μ
new_options["unit_prefix"] = "\u03bc"

hass.config_entries.async_update_entry(
config_entry, options=new_options, version=1, minor_version=4
)

_LOGGER.debug(
"Migration to configuration version %s.%s successful",
config_entry.version,
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/derivative/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

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)"),
Expand Down Expand Up @@ -142,7 +142,7 @@ class ConfigFlowHandler(SchemaConfigFlowHandler, domain=DOMAIN):
options_flow = OPTIONS_FLOW

VERSION = 1
MINOR_VERSION = 3
MINOR_VERSION = 4

def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
"""Return config entry title."""
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/derivative/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
UNIT_PREFIXES = {
None: 1,
"n": 1e-9,
"µ": 1e-6,
"μ": 1e-6,
"m": 1e-3,
"k": 1e3,
"M": 1e6,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/emoncms/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
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,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/homekit/type_sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ def async_update_state(self, new_state: State) -> None:
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:
Expand Down
8 changes: 4 additions & 4 deletions homeassistant/components/homekit/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,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)
Expand All @@ -507,7 +507,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)
Expand All @@ -520,7 +520,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:
Expand All @@ -533,7 +533,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
Expand Down
7 changes: 7 additions & 0 deletions homeassistant/components/mqtt/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
NumberMode,
RestoreNumber,
)
from homeassistant.components.sensor import AMBIGUOUS_UNITS
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_CLASS,
Expand Down Expand Up @@ -70,6 +71,12 @@

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 AMBIGUOUS_UNITS
):
config[CONF_UNIT_OF_MEASUREMENT] = AMBIGUOUS_UNITS[unit_of_measurement]

if config[CONF_MIN] > config[CONF_MAX]:
raise vol.Invalid(f"{CONF_MAX} must be >= {CONF_MIN}")

Expand Down
12 changes: 9 additions & 3 deletions homeassistant/components/mqtt/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from homeassistant.components import sensor
from homeassistant.components.sensor import (
AMBIGUOUS_UNITS,
CONF_STATE_CLASS,
DEVICE_CLASS_UNITS,
DEVICE_CLASSES_SCHEMA,
Expand Down Expand Up @@ -133,9 +134,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

unit_of_measurement = config[CONF_UNIT_OF_MEASUREMENT] = AMBIGUOUS_UNITS.get(
unit_of_measurement, unit_of_measurement
)

if (device_class := config.get(CONF_DEVICE_CLASS)) is None:
return config

if (
Expand Down
18 changes: 14 additions & 4 deletions homeassistant/components/number/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from homeassistant.util.hass_dict import HassKey

from .const import ( # noqa: F401
AMBIGUOUS_UNITS,
ATTR_MAX,
ATTR_MIN,
ATTR_STEP,
Expand Down Expand Up @@ -368,14 +369,23 @@ def native_unit_of_measurement(self) -> str | None:
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:
"""Return the unit of measurement of the entity, after unit conversion."""
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 (
Expand Down Expand Up @@ -444,7 +454,7 @@ def _convert_to_state_value(
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:
Expand Down Expand Up @@ -473,7 +483,7 @@ def convert_to_native_value(self, value: float) -> float:
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:
Expand All @@ -496,7 +506,7 @@ def async_registry_entry_updated(self) -> None:
(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
):
Expand Down
Loading
Loading