mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 01:37:08 +00:00
Area units and conversion between metric and US (#123563)
* area conversions * start work on tests * add number device class * update unit conversions to utilise distance constants * add area unit * update test unit system * update device condition and trigger * update statistic unit converters * further tests work WIP * update test unit system * add missing string translations * fix websocket tests * add deprecated notice * add more missing strings and missing initialisation of unit system * adjust icon and remove strings from scrape and random * Fix acre to meters conversion Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Tidy up valid units Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * fix ordering of area * update order alphabetically * fix broken test * update test_init * Update homeassistant/const.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * remove deprecated unit and fix alphabetical order * change deprecation and add tests, change to millimeter conversion for inches * fix order * re-order defs alphabetically * add measurement as well * update icons * fix up Deprecation of area square meters * Update core integrations to UnitOfArea * update test recorder tests * unit system tests in alphabetical * update snapshot * rebuild * revert alphabetization of functions * other revert of alphabetical order --------- Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
This commit is contained in:
parent
9add3a6c9b
commit
d8549409f7
@ -26,11 +26,11 @@ from homeassistant.components.sensor import (
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
ATTR_BATTERY_LEVEL,
|
||||
CONF_DESCRIPTION,
|
||||
PERCENTAGE,
|
||||
EntityCategory,
|
||||
UnitOfArea,
|
||||
UnitOfTime,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -67,7 +67,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
|
||||
capability_fn=lambda caps: caps.stats.clean,
|
||||
value_fn=lambda e: e.area,
|
||||
translation_key="stats_area",
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
),
|
||||
EcovacsSensorEntityDescription[StatsEvent](
|
||||
key="stats_time",
|
||||
@ -84,7 +84,7 @@ ENTITY_DESCRIPTIONS: tuple[EcovacsSensorEntityDescription, ...] = (
|
||||
value_fn=lambda e: e.area,
|
||||
key="total_stats_area",
|
||||
translation_key="total_stats_area",
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
),
|
||||
EcovacsSensorEntityDescription[TotalStatsEvent](
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.const import (
|
||||
SIGNAL_STRENGTH_DECIBELS,
|
||||
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||
UnitOfApparentPower,
|
||||
UnitOfArea,
|
||||
UnitOfBloodGlucoseConcentration,
|
||||
UnitOfConductivity,
|
||||
UnitOfDataRate,
|
||||
@ -98,6 +99,12 @@ class NumberDeviceClass(StrEnum):
|
||||
Unit of measurement: `None`
|
||||
"""
|
||||
|
||||
AREA = "area"
|
||||
"""Area
|
||||
|
||||
Unit of measurement: `UnitOfArea` units
|
||||
"""
|
||||
|
||||
ATMOSPHERIC_PRESSURE = "atmospheric_pressure"
|
||||
"""Atmospheric pressure.
|
||||
|
||||
@ -434,6 +441,7 @@ DEVICE_CLASSES_SCHEMA: Final = vol.All(vol.Lower, vol.Coerce(NumberDeviceClass))
|
||||
DEVICE_CLASS_UNITS: dict[NumberDeviceClass, set[type[StrEnum] | str | None]] = {
|
||||
NumberDeviceClass.APPARENT_POWER: set(UnitOfApparentPower),
|
||||
NumberDeviceClass.AQI: {None},
|
||||
NumberDeviceClass.AREA: set(UnitOfArea),
|
||||
NumberDeviceClass.ATMOSPHERIC_PRESSURE: set(UnitOfPressure),
|
||||
NumberDeviceClass.BATTERY: {PERCENTAGE},
|
||||
NumberDeviceClass.BLOOD_GLUCOSE_CONCENTRATION: set(UnitOfBloodGlucoseConcentration),
|
||||
|
@ -9,6 +9,9 @@
|
||||
"aqi": {
|
||||
"default": "mdi:air-filter"
|
||||
},
|
||||
"area": {
|
||||
"default": "mdi:texture-box"
|
||||
},
|
||||
"atmospheric_pressure": {
|
||||
"default": "mdi:thermometer-lines"
|
||||
},
|
||||
|
@ -37,6 +37,9 @@
|
||||
"aqi": {
|
||||
"name": "[%key:component::sensor::entity_component::aqi::name%]"
|
||||
},
|
||||
"area": {
|
||||
"name": "[%key:component::sensor::entity_component::area::name%]"
|
||||
},
|
||||
"atmospheric_pressure": {
|
||||
"name": "[%key:component::sensor::entity_component::atmospheric_pressure::name%]"
|
||||
},
|
||||
|
@ -27,6 +27,7 @@ from homeassistant.helpers.singleton import singleton
|
||||
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.unit_conversion import (
|
||||
AreaConverter,
|
||||
BaseUnitConverter,
|
||||
BloodGlucoseConcentrationConverter,
|
||||
ConductivityConverter,
|
||||
@ -129,6 +130,7 @@ QUERY_STATISTICS_SUMMARY_SUM = (
|
||||
|
||||
|
||||
STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, type[BaseUnitConverter]] = {
|
||||
**{unit: AreaConverter for unit in AreaConverter.VALID_UNITS},
|
||||
**{
|
||||
unit: BloodGlucoseConcentrationConverter
|
||||
for unit in BloodGlucoseConcentrationConverter.VALID_UNITS
|
||||
|
@ -16,6 +16,7 @@ from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.json import json_bytes
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.unit_conversion import (
|
||||
AreaConverter,
|
||||
BloodGlucoseConcentrationConverter,
|
||||
ConductivityConverter,
|
||||
DataRateConverter,
|
||||
@ -55,6 +56,7 @@ UPDATE_STATISTICS_METADATA_TIME_OUT = 10
|
||||
|
||||
UNIT_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional("area"): vol.In(AreaConverter.VALID_UNITS),
|
||||
vol.Optional("blood_glucose_concentration"): vol.In(
|
||||
BloodGlucoseConcentrationConverter.VALID_UNITS
|
||||
),
|
||||
|
@ -9,7 +9,7 @@ from pyrituals import Diffuser
|
||||
|
||||
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import AREA_SQUARE_METERS, EntityCategory
|
||||
from homeassistant.const import EntityCategory, UnitOfArea
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
@ -30,7 +30,7 @@ ENTITY_DESCRIPTIONS = (
|
||||
RitualsSelectEntityDescription(
|
||||
key="room_size_square_meter",
|
||||
translation_key="room_size_square_meter",
|
||||
unit_of_measurement=AREA_SQUARE_METERS,
|
||||
unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
options=["15", "30", "60", "100"],
|
||||
current_fn=lambda diffuser: str(diffuser.room_size_square_meter),
|
||||
|
@ -25,12 +25,7 @@ from homeassistant.components.sensor import (
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
PERCENTAGE,
|
||||
EntityCategory,
|
||||
UnitOfTime,
|
||||
)
|
||||
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfArea, UnitOfTime
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
@ -131,14 +126,14 @@ SENSOR_DESCRIPTIONS = [
|
||||
translation_key="cleaning_area",
|
||||
value_fn=lambda data: data.status.square_meter_clean_area,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
),
|
||||
RoborockSensorDescription(
|
||||
key="total_cleaning_area",
|
||||
translation_key="total_cleaning_area",
|
||||
value_fn=lambda data: data.clean_summary.square_meter_clean_area,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
),
|
||||
RoborockSensorDescription(
|
||||
key="vacuum_error",
|
||||
|
@ -8,10 +8,10 @@ from homeassistant.components.sensor import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
PERCENTAGE,
|
||||
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||
EntityCategory,
|
||||
UnitOfArea,
|
||||
UnitOfLength,
|
||||
UnitOfTime,
|
||||
)
|
||||
@ -61,7 +61,7 @@ SENSORS: list[SensorEntityDescription] = [
|
||||
key="total_area_cleaned",
|
||||
translation_key="total_area_cleaned",
|
||||
state_class=SensorStateClass.TOTAL,
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
|
@ -12,12 +12,7 @@ from homeassistant.components.sensor import (
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
PERCENTAGE,
|
||||
EntityCategory,
|
||||
UnitOfTime,
|
||||
)
|
||||
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfArea, UnitOfTime
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
@ -108,7 +103,7 @@ SENSORS: list[RoombaSensorEntityDescription] = [
|
||||
RoombaSensorEntityDescription(
|
||||
key="total_cleaned_area",
|
||||
translation_key="total_cleaned_area",
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
value_fn=lambda self: (
|
||||
None if (sqft := self.run_stats.get("sqft")) is None else sqft * 9.29
|
||||
|
@ -17,6 +17,7 @@ from homeassistant.const import (
|
||||
SIGNAL_STRENGTH_DECIBELS,
|
||||
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
|
||||
UnitOfApparentPower,
|
||||
UnitOfArea,
|
||||
UnitOfBloodGlucoseConcentration,
|
||||
UnitOfConductivity,
|
||||
UnitOfDataRate,
|
||||
@ -47,6 +48,7 @@ from homeassistant.helpers.deprecation import (
|
||||
dir_with_deprecated_constants,
|
||||
)
|
||||
from homeassistant.util.unit_conversion import (
|
||||
AreaConverter,
|
||||
BaseUnitConverter,
|
||||
BloodGlucoseConcentrationConverter,
|
||||
ConductivityConverter,
|
||||
@ -117,6 +119,12 @@ class SensorDeviceClass(StrEnum):
|
||||
Unit of measurement: `None`
|
||||
"""
|
||||
|
||||
AREA = "area"
|
||||
"""Area
|
||||
|
||||
Unit of measurement: `UnitOfArea` units
|
||||
"""
|
||||
|
||||
ATMOSPHERIC_PRESSURE = "atmospheric_pressure"
|
||||
"""Atmospheric pressure.
|
||||
|
||||
@ -500,6 +508,7 @@ _DEPRECATED_STATE_CLASS_TOTAL_INCREASING: Final = DeprecatedConstantEnum(
|
||||
STATE_CLASSES: Final[list[str]] = [cls.value for cls in SensorStateClass]
|
||||
|
||||
UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] = {
|
||||
SensorDeviceClass.AREA: AreaConverter,
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: PressureConverter,
|
||||
SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION: BloodGlucoseConcentrationConverter,
|
||||
SensorDeviceClass.CONDUCTIVITY: ConductivityConverter,
|
||||
@ -531,6 +540,7 @@ UNIT_CONVERTERS: dict[SensorDeviceClass | str | None, type[BaseUnitConverter]] =
|
||||
DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = {
|
||||
SensorDeviceClass.APPARENT_POWER: set(UnitOfApparentPower),
|
||||
SensorDeviceClass.AQI: {None},
|
||||
SensorDeviceClass.AREA: set(UnitOfArea),
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: set(UnitOfPressure),
|
||||
SensorDeviceClass.BATTERY: {PERCENTAGE},
|
||||
SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION: set(UnitOfBloodGlucoseConcentration),
|
||||
@ -607,6 +617,7 @@ DEVICE_CLASS_UNITS: dict[SensorDeviceClass, set[type[StrEnum] | str | None]] = {
|
||||
DEVICE_CLASS_STATE_CLASSES: dict[SensorDeviceClass, set[SensorStateClass]] = {
|
||||
SensorDeviceClass.APPARENT_POWER: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.AQI: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.AREA: set(SensorStateClass),
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.BATTERY: {SensorStateClass.MEASUREMENT},
|
||||
SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION: {SensorStateClass.MEASUREMENT},
|
||||
|
@ -35,6 +35,7 @@ DEVICE_CLASS_NONE = "none"
|
||||
|
||||
CONF_IS_APPARENT_POWER = "is_apparent_power"
|
||||
CONF_IS_AQI = "is_aqi"
|
||||
CONF_IS_AREA = "is_area"
|
||||
CONF_IS_ATMOSPHERIC_PRESSURE = "is_atmospheric_pressure"
|
||||
CONF_IS_BATTERY_LEVEL = "is_battery_level"
|
||||
CONF_IS_BLOOD_GLUCOSE_CONCENTRATION = "is_blood_glucose_concentration"
|
||||
@ -86,6 +87,7 @@ CONF_IS_WIND_SPEED = "is_wind_speed"
|
||||
ENTITY_CONDITIONS = {
|
||||
SensorDeviceClass.APPARENT_POWER: [{CONF_TYPE: CONF_IS_APPARENT_POWER}],
|
||||
SensorDeviceClass.AQI: [{CONF_TYPE: CONF_IS_AQI}],
|
||||
SensorDeviceClass.AREA: [{CONF_TYPE: CONF_IS_AREA}],
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: [{CONF_TYPE: CONF_IS_ATMOSPHERIC_PRESSURE}],
|
||||
SensorDeviceClass.BATTERY: [{CONF_TYPE: CONF_IS_BATTERY_LEVEL}],
|
||||
SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION: [
|
||||
@ -153,6 +155,7 @@ CONDITION_SCHEMA = vol.All(
|
||||
[
|
||||
CONF_IS_APPARENT_POWER,
|
||||
CONF_IS_AQI,
|
||||
CONF_IS_AREA,
|
||||
CONF_IS_ATMOSPHERIC_PRESSURE,
|
||||
CONF_IS_BATTERY_LEVEL,
|
||||
CONF_IS_BLOOD_GLUCOSE_CONCENTRATION,
|
||||
|
@ -34,6 +34,7 @@ DEVICE_CLASS_NONE = "none"
|
||||
|
||||
CONF_APPARENT_POWER = "apparent_power"
|
||||
CONF_AQI = "aqi"
|
||||
CONF_AREA = "area"
|
||||
CONF_ATMOSPHERIC_PRESSURE = "atmospheric_pressure"
|
||||
CONF_BATTERY_LEVEL = "battery_level"
|
||||
CONF_BLOOD_GLUCOSE_CONCENTRATION = "blood_glucose_concentration"
|
||||
@ -85,6 +86,7 @@ CONF_WIND_SPEED = "wind_speed"
|
||||
ENTITY_TRIGGERS = {
|
||||
SensorDeviceClass.APPARENT_POWER: [{CONF_TYPE: CONF_APPARENT_POWER}],
|
||||
SensorDeviceClass.AQI: [{CONF_TYPE: CONF_AQI}],
|
||||
SensorDeviceClass.AREA: [{CONF_TYPE: CONF_AREA}],
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: [{CONF_TYPE: CONF_ATMOSPHERIC_PRESSURE}],
|
||||
SensorDeviceClass.BATTERY: [{CONF_TYPE: CONF_BATTERY_LEVEL}],
|
||||
SensorDeviceClass.BLOOD_GLUCOSE_CONCENTRATION: [
|
||||
@ -153,6 +155,7 @@ TRIGGER_SCHEMA = vol.All(
|
||||
[
|
||||
CONF_APPARENT_POWER,
|
||||
CONF_AQI,
|
||||
CONF_AREA,
|
||||
CONF_ATMOSPHERIC_PRESSURE,
|
||||
CONF_BATTERY_LEVEL,
|
||||
CONF_BLOOD_GLUCOSE_CONCENTRATION,
|
||||
|
@ -9,6 +9,9 @@
|
||||
"aqi": {
|
||||
"default": "mdi:air-filter"
|
||||
},
|
||||
"area": {
|
||||
"default": "mdi:texture-box"
|
||||
},
|
||||
"atmospheric_pressure": {
|
||||
"default": "mdi:thermometer-lines"
|
||||
},
|
||||
|
@ -4,6 +4,7 @@
|
||||
"condition_type": {
|
||||
"is_apparent_power": "Current {entity_name} apparent power",
|
||||
"is_aqi": "Current {entity_name} air quality index",
|
||||
"is_area": "Current {entity_name} area",
|
||||
"is_atmospheric_pressure": "Current {entity_name} atmospheric pressure",
|
||||
"is_battery_level": "Current {entity_name} battery level",
|
||||
"is_blood_glucose_concentration": "Current {entity_name} blood glucose concentration",
|
||||
@ -55,6 +56,7 @@
|
||||
"trigger_type": {
|
||||
"apparent_power": "{entity_name} apparent power changes",
|
||||
"aqi": "{entity_name} air quality index changes",
|
||||
"area": "{entity_name} area changes",
|
||||
"atmospheric_pressure": "{entity_name} atmospheric pressure changes",
|
||||
"battery_level": "{entity_name} battery level changes",
|
||||
"blood_glucose_concentration": "{entity_name} blood glucose concentration changes",
|
||||
@ -145,6 +147,9 @@
|
||||
"aqi": {
|
||||
"name": "Air quality index"
|
||||
},
|
||||
"area": {
|
||||
"name": "Area"
|
||||
},
|
||||
"atmospheric_pressure": {
|
||||
"name": "Atmospheric pressure"
|
||||
},
|
||||
|
@ -15,11 +15,11 @@ from homeassistant.components.sensor import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
LIGHT_LUX,
|
||||
PERCENTAGE,
|
||||
EntityCategory,
|
||||
UnitOfArea,
|
||||
UnitOfElectricPotential,
|
||||
UnitOfEnergy,
|
||||
UnitOfMass,
|
||||
@ -95,7 +95,7 @@ CAPABILITY_TO_SENSORS: dict[str, list[Map]] = {
|
||||
Map(
|
||||
Attribute.bmi_measurement,
|
||||
"Body Mass Index",
|
||||
f"{UnitOfMass.KILOGRAMS}/{AREA_SQUARE_METERS}",
|
||||
f"{UnitOfMass.KILOGRAMS}/{UnitOfArea.SQUARE_METERS}",
|
||||
None,
|
||||
SensorStateClass.MEASUREMENT,
|
||||
None,
|
||||
|
@ -24,7 +24,6 @@ from homeassistant.components.sensor import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
ATTR_BATTERY_LEVEL,
|
||||
ATTR_TEMPERATURE,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
@ -37,6 +36,7 @@ from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
REVOLUTIONS_PER_MINUTE,
|
||||
EntityCategory,
|
||||
UnitOfArea,
|
||||
UnitOfPower,
|
||||
UnitOfPressure,
|
||||
UnitOfTemperature,
|
||||
@ -622,7 +622,7 @@ VACUUM_SENSORS = {
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
f"last_clean_{ATTR_LAST_CLEAN_AREA}": XiaomiMiioSensorDescription(
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
icon="mdi:texture-box",
|
||||
key=ATTR_LAST_CLEAN_AREA,
|
||||
parent_key=VacuumCoordinatorDataAttributes.last_clean_details,
|
||||
@ -639,7 +639,7 @@ VACUUM_SENSORS = {
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
f"current_{ATTR_LAST_CLEAN_AREA}": XiaomiMiioSensorDescription(
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
icon="mdi:texture-box",
|
||||
key=ATTR_STATUS_CLEAN_AREA,
|
||||
parent_key=VacuumCoordinatorDataAttributes.status,
|
||||
@ -657,7 +657,7 @@ VACUUM_SENSORS = {
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
),
|
||||
f"clean_history_{ATTR_CLEAN_HISTORY_TOTAL_AREA}": XiaomiMiioSensorDescription(
|
||||
native_unit_of_measurement=AREA_SQUARE_METERS,
|
||||
native_unit_of_measurement=UnitOfArea.SQUARE_METERS,
|
||||
icon="mdi:texture-box",
|
||||
key=ATTR_CLEAN_HISTORY_TOTAL_AREA,
|
||||
parent_key=VacuumCoordinatorDataAttributes.clean_history_status,
|
||||
|
@ -1179,8 +1179,27 @@ _DEPRECATED_VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE: Final = DeprecatedConstantEn
|
||||
)
|
||||
"""Deprecated: please use UnitOfVolumeFlowRate.CUBIC_FEET_PER_MINUTE"""
|
||||
|
||||
# Area units
|
||||
AREA_SQUARE_METERS: Final = "m²"
|
||||
|
||||
class UnitOfArea(StrEnum):
|
||||
"""Area units."""
|
||||
|
||||
SQUARE_METERS = "m²"
|
||||
SQUARE_CENTIMETERS = "cm²"
|
||||
SQUARE_KILOMETERS = "km²"
|
||||
SQUARE_MILLIMETERS = "mm²"
|
||||
SQUARE_INCHES = "in²"
|
||||
SQUARE_FEET = "ft²"
|
||||
SQUARE_YARDS = "yd²"
|
||||
SQUARE_MILES = "mi²"
|
||||
ACRES = "ac"
|
||||
HECTARES = "ha"
|
||||
|
||||
|
||||
_DEPRECATED_AREA_SQUARE_METERS: Final = DeprecatedConstantEnum(
|
||||
UnitOfArea.SQUARE_METERS,
|
||||
"2025.12",
|
||||
)
|
||||
"""Deprecated: please use UnitOfArea.SQUARE_METERS"""
|
||||
|
||||
|
||||
# Mass units
|
||||
@ -1704,6 +1723,7 @@ RESTART_EXIT_CODE: Final = 100
|
||||
UNIT_NOT_RECOGNIZED_TEMPLATE: Final = "{} is not a recognized {} unit."
|
||||
|
||||
LENGTH: Final = "length"
|
||||
AREA: Final = "area"
|
||||
MASS: Final = "mass"
|
||||
PRESSURE: Final = "pressure"
|
||||
VOLUME: Final = "volume"
|
||||
|
@ -10,6 +10,7 @@ from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
PERCENTAGE,
|
||||
UNIT_NOT_RECOGNIZED_TEMPLATE,
|
||||
UnitOfArea,
|
||||
UnitOfBloodGlucoseConcentration,
|
||||
UnitOfConductivity,
|
||||
UnitOfDataRate,
|
||||
@ -42,6 +43,19 @@ _MILE_TO_M = _YARD_TO_M * 1760 # 1760 yard = 1 mile (1609.344 m)
|
||||
|
||||
_NAUTICAL_MILE_TO_M = 1852 # 1 nautical mile = 1852 m
|
||||
|
||||
# Area constants to square meters
|
||||
_CM2_TO_M2 = _CM_TO_M**2 # 1 cm² = 0.0001 m²
|
||||
_MM2_TO_M2 = _MM_TO_M**2 # 1 mm² = 0.000001 m²
|
||||
_KM2_TO_M2 = _KM_TO_M**2 # 1 km² = 1,000,000 m²
|
||||
|
||||
_IN2_TO_M2 = _IN_TO_M**2 # 1 in² = 0.00064516 m²
|
||||
_FT2_TO_M2 = _FOOT_TO_M**2 # 1 ft² = 0.092903 m²
|
||||
_YD2_TO_M2 = _YARD_TO_M**2 # 1 yd² = 0.836127 m²
|
||||
_MI2_TO_M2 = _MILE_TO_M**2 # 1 mi² = 2,590,000 m²
|
||||
|
||||
_ACRE_TO_M2 = 66 * 660 * _FT2_TO_M2 # 1 acre = 4,046.86 m²
|
||||
_HECTARE_TO_M2 = 100 * 100 # 1 hectare = 10,000 m²
|
||||
|
||||
# Duration conversion constants
|
||||
_MIN_TO_SEC = 60 # 1 min = 60 seconds
|
||||
_HRS_TO_MINUTES = 60 # 1 hr = 60 minutes
|
||||
@ -146,6 +160,25 @@ class DataRateConverter(BaseUnitConverter):
|
||||
VALID_UNITS = set(UnitOfDataRate)
|
||||
|
||||
|
||||
class AreaConverter(BaseUnitConverter):
|
||||
"""Utility to convert area values."""
|
||||
|
||||
UNIT_CLASS = "area"
|
||||
_UNIT_CONVERSION: dict[str | None, float] = {
|
||||
UnitOfArea.SQUARE_METERS: 1,
|
||||
UnitOfArea.SQUARE_CENTIMETERS: 1 / _CM2_TO_M2,
|
||||
UnitOfArea.SQUARE_MILLIMETERS: 1 / _MM2_TO_M2,
|
||||
UnitOfArea.SQUARE_KILOMETERS: 1 / _KM2_TO_M2,
|
||||
UnitOfArea.SQUARE_INCHES: 1 / _IN2_TO_M2,
|
||||
UnitOfArea.SQUARE_FEET: 1 / _FT2_TO_M2,
|
||||
UnitOfArea.SQUARE_YARDS: 1 / _YD2_TO_M2,
|
||||
UnitOfArea.SQUARE_MILES: 1 / _MI2_TO_M2,
|
||||
UnitOfArea.ACRES: 1 / _ACRE_TO_M2,
|
||||
UnitOfArea.HECTARES: 1 / _HECTARE_TO_M2,
|
||||
}
|
||||
VALID_UNITS = set(UnitOfArea)
|
||||
|
||||
|
||||
class DistanceConverter(BaseUnitConverter):
|
||||
"""Utility to convert distance values."""
|
||||
|
||||
|
@ -9,6 +9,7 @@ import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
ACCUMULATED_PRECIPITATION,
|
||||
AREA,
|
||||
LENGTH,
|
||||
MASS,
|
||||
PRESSURE,
|
||||
@ -16,6 +17,7 @@ from homeassistant.const import (
|
||||
UNIT_NOT_RECOGNIZED_TEMPLATE,
|
||||
VOLUME,
|
||||
WIND_SPEED,
|
||||
UnitOfArea,
|
||||
UnitOfLength,
|
||||
UnitOfMass,
|
||||
UnitOfPrecipitationDepth,
|
||||
@ -27,6 +29,7 @@ from homeassistant.const import (
|
||||
)
|
||||
|
||||
from .unit_conversion import (
|
||||
AreaConverter,
|
||||
DistanceConverter,
|
||||
PressureConverter,
|
||||
SpeedConverter,
|
||||
@ -41,6 +44,8 @@ _CONF_UNIT_SYSTEM_IMPERIAL: Final = "imperial"
|
||||
_CONF_UNIT_SYSTEM_METRIC: Final = "metric"
|
||||
_CONF_UNIT_SYSTEM_US_CUSTOMARY: Final = "us_customary"
|
||||
|
||||
AREA_UNITS = AreaConverter.VALID_UNITS
|
||||
|
||||
LENGTH_UNITS = DistanceConverter.VALID_UNITS
|
||||
|
||||
MASS_UNITS: set[str] = {
|
||||
@ -66,6 +71,7 @@ _VALID_BY_TYPE: dict[str, set[str] | set[str | None]] = {
|
||||
MASS: MASS_UNITS,
|
||||
VOLUME: VOLUME_UNITS,
|
||||
PRESSURE: PRESSURE_UNITS,
|
||||
AREA: AREA_UNITS,
|
||||
}
|
||||
|
||||
|
||||
@ -84,6 +90,7 @@ class UnitSystem:
|
||||
name: str,
|
||||
*,
|
||||
accumulated_precipitation: UnitOfPrecipitationDepth,
|
||||
area: UnitOfArea,
|
||||
conversions: dict[tuple[SensorDeviceClass | str | None, str | None], str],
|
||||
length: UnitOfLength,
|
||||
mass: UnitOfMass,
|
||||
@ -97,6 +104,7 @@ class UnitSystem:
|
||||
UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit, unit_type)
|
||||
for unit, unit_type in (
|
||||
(accumulated_precipitation, ACCUMULATED_PRECIPITATION),
|
||||
(area, AREA),
|
||||
(temperature, TEMPERATURE),
|
||||
(length, LENGTH),
|
||||
(wind_speed, WIND_SPEED),
|
||||
@ -112,10 +120,11 @@ class UnitSystem:
|
||||
|
||||
self._name = name
|
||||
self.accumulated_precipitation_unit = accumulated_precipitation
|
||||
self.temperature_unit = temperature
|
||||
self.area_unit = area
|
||||
self.length_unit = length
|
||||
self.mass_unit = mass
|
||||
self.pressure_unit = pressure
|
||||
self.temperature_unit = temperature
|
||||
self.volume_unit = volume
|
||||
self.wind_speed_unit = wind_speed
|
||||
self._conversions = conversions
|
||||
@ -149,6 +158,16 @@ class UnitSystem:
|
||||
precip, from_unit, self.accumulated_precipitation_unit
|
||||
)
|
||||
|
||||
def area(self, area: float | None, from_unit: str) -> float:
|
||||
"""Convert the given area to this unit system."""
|
||||
if not isinstance(area, Number):
|
||||
raise TypeError(f"{area!s} is not a numeric value.")
|
||||
|
||||
# type ignore: https://github.com/python/mypy/issues/7207
|
||||
return AreaConverter.convert( # type: ignore[unreachable]
|
||||
area, from_unit, self.area_unit
|
||||
)
|
||||
|
||||
def pressure(self, pressure: float | None, from_unit: str) -> float:
|
||||
"""Convert the given pressure to this unit system."""
|
||||
if not isinstance(pressure, Number):
|
||||
@ -184,6 +203,7 @@ class UnitSystem:
|
||||
return {
|
||||
LENGTH: self.length_unit,
|
||||
ACCUMULATED_PRECIPITATION: self.accumulated_precipitation_unit,
|
||||
AREA: self.area_unit,
|
||||
MASS: self.mass_unit,
|
||||
PRESSURE: self.pressure_unit,
|
||||
TEMPERATURE: self.temperature_unit,
|
||||
@ -234,6 +254,12 @@ METRIC_SYSTEM = UnitSystem(
|
||||
for unit in UnitOfPressure
|
||||
if unit != UnitOfPressure.HPA
|
||||
},
|
||||
# Convert non-metric area
|
||||
("area", UnitOfArea.SQUARE_INCHES): UnitOfArea.SQUARE_CENTIMETERS,
|
||||
("area", UnitOfArea.SQUARE_FEET): UnitOfArea.SQUARE_METERS,
|
||||
("area", UnitOfArea.SQUARE_MILES): UnitOfArea.SQUARE_KILOMETERS,
|
||||
("area", UnitOfArea.SQUARE_YARDS): UnitOfArea.SQUARE_METERS,
|
||||
("area", UnitOfArea.ACRES): UnitOfArea.HECTARES,
|
||||
# Convert non-metric distances
|
||||
("distance", UnitOfLength.FEET): UnitOfLength.METERS,
|
||||
("distance", UnitOfLength.INCHES): UnitOfLength.MILLIMETERS,
|
||||
@ -285,6 +311,7 @@ METRIC_SYSTEM = UnitSystem(
|
||||
if unit not in (UnitOfSpeed.KILOMETERS_PER_HOUR, UnitOfSpeed.KNOTS)
|
||||
},
|
||||
},
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
length=UnitOfLength.KILOMETERS,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
pressure=UnitOfPressure.PA,
|
||||
@ -303,6 +330,12 @@ US_CUSTOMARY_SYSTEM = UnitSystem(
|
||||
for unit in UnitOfPressure
|
||||
if unit != UnitOfPressure.INHG
|
||||
},
|
||||
# Convert non-USCS areas
|
||||
("area", UnitOfArea.SQUARE_METERS): UnitOfArea.SQUARE_FEET,
|
||||
("area", UnitOfArea.SQUARE_CENTIMETERS): UnitOfArea.SQUARE_INCHES,
|
||||
("area", UnitOfArea.SQUARE_MILLIMETERS): UnitOfArea.SQUARE_INCHES,
|
||||
("area", UnitOfArea.SQUARE_KILOMETERS): UnitOfArea.SQUARE_MILES,
|
||||
("area", UnitOfArea.HECTARES): UnitOfArea.ACRES,
|
||||
# Convert non-USCS distances
|
||||
("distance", UnitOfLength.CENTIMETERS): UnitOfLength.INCHES,
|
||||
("distance", UnitOfLength.KILOMETERS): UnitOfLength.MILES,
|
||||
@ -356,6 +389,7 @@ US_CUSTOMARY_SYSTEM = UnitSystem(
|
||||
if unit not in (UnitOfSpeed.KNOTS, UnitOfSpeed.MILES_PER_HOUR)
|
||||
},
|
||||
},
|
||||
area=UnitOfArea.SQUARE_FEET,
|
||||
length=UnitOfLength.MILES,
|
||||
mass=UnitOfMass.POUNDS,
|
||||
pressure=UnitOfPressure.PSI,
|
||||
|
@ -177,14 +177,14 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'stats_area',
|
||||
'unique_id': '8516fbb1-17f1-4194-0000000_stats_area',
|
||||
'unit_of_measurement': 'm²',
|
||||
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[5xu9h3][sensor.goat_g1_area_cleaned:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Goat G1 Area cleaned',
|
||||
'unit_of_measurement': 'm²',
|
||||
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.goat_g1_area_cleaned',
|
||||
@ -512,7 +512,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'total_stats_area',
|
||||
'unique_id': '8516fbb1-17f1-4194-0000000_total_stats_area',
|
||||
'unit_of_measurement': 'm²',
|
||||
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[5xu9h3][sensor.goat_g1_total_area_cleaned:state]
|
||||
@ -520,7 +520,7 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Goat G1 Total area cleaned',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': 'm²',
|
||||
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.goat_g1_total_area_cleaned',
|
||||
@ -755,14 +755,14 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'stats_area',
|
||||
'unique_id': 'E1234567890000000001_stats_area',
|
||||
'unit_of_measurement': 'm²',
|
||||
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[yna5x1][sensor.ozmo_950_area_cleaned:state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Ozmo 950 Area cleaned',
|
||||
'unit_of_measurement': 'm²',
|
||||
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.ozmo_950_area_cleaned',
|
||||
@ -1137,7 +1137,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'total_stats_area',
|
||||
'unique_id': 'E1234567890000000001_total_stats_area',
|
||||
'unit_of_measurement': 'm²',
|
||||
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sensors[yna5x1][sensor.ozmo_950_total_area_cleaned:state]
|
||||
@ -1145,7 +1145,7 @@
|
||||
'attributes': ReadOnlyDict({
|
||||
'friendly_name': 'Ozmo 950 Total area cleaned',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': 'm²',
|
||||
'unit_of_measurement': <UnitOfArea.SQUARE_METERS: 'm²'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.ozmo_950_total_area_cleaned',
|
||||
|
@ -51,6 +51,16 @@ async def mock_recorder_before_hass(
|
||||
"""Set up recorder."""
|
||||
|
||||
|
||||
AREA_SENSOR_FT_ATTRIBUTES = {
|
||||
"device_class": "area",
|
||||
"state_class": "measurement",
|
||||
"unit_of_measurement": "ft²",
|
||||
}
|
||||
AREA_SENSOR_M_ATTRIBUTES = {
|
||||
"device_class": "area",
|
||||
"state_class": "measurement",
|
||||
"unit_of_measurement": "m²",
|
||||
}
|
||||
DISTANCE_SENSOR_FT_ATTRIBUTES = {
|
||||
"device_class": "distance",
|
||||
"state_class": "measurement",
|
||||
@ -1247,6 +1257,9 @@ async def test_statistic_during_period_calendar(
|
||||
@pytest.mark.parametrize(
|
||||
("attributes", "state", "value", "custom_units", "converted_value"),
|
||||
[
|
||||
(AREA_SENSOR_M_ATTRIBUTES, 10, 10, {"area": "cm²"}, 100000),
|
||||
(AREA_SENSOR_M_ATTRIBUTES, 10, 10, {"area": "m²"}, 10),
|
||||
(AREA_SENSOR_M_ATTRIBUTES, 10, 10, {"area": "ft²"}, 107.639),
|
||||
(DISTANCE_SENSOR_M_ATTRIBUTES, 10, 10, {"distance": "cm"}, 1000),
|
||||
(DISTANCE_SENSOR_M_ATTRIBUTES, 10, 10, {"distance": "m"}, 10),
|
||||
(DISTANCE_SENSOR_M_ATTRIBUTES, 10, 10, {"distance": "in"}, 10 / 0.0254),
|
||||
@ -1434,6 +1447,7 @@ async def test_sum_statistics_during_period_unit_conversion(
|
||||
"custom_units",
|
||||
[
|
||||
{"distance": "L"},
|
||||
{"area": "L"},
|
||||
{"energy": "W"},
|
||||
{"power": "Pa"},
|
||||
{"pressure": "K"},
|
||||
@ -1678,6 +1692,8 @@ async def test_statistics_during_period_empty_statistic_ids(
|
||||
@pytest.mark.parametrize(
|
||||
("units", "attributes", "display_unit", "statistics_unit", "unit_class"),
|
||||
[
|
||||
(US_CUSTOMARY_SYSTEM, AREA_SENSOR_M_ATTRIBUTES, "m²", "m²", "area"),
|
||||
(METRIC_SYSTEM, AREA_SENSOR_M_ATTRIBUTES, "m²", "m²", "area"),
|
||||
(US_CUSTOMARY_SYSTEM, DISTANCE_SENSOR_M_ATTRIBUTES, "m", "m", "distance"),
|
||||
(METRIC_SYSTEM, DISTANCE_SENSOR_M_ATTRIBUTES, "m", "m", "distance"),
|
||||
(
|
||||
@ -1852,6 +1868,13 @@ async def test_list_statistic_ids(
|
||||
@pytest.mark.parametrize(
|
||||
("attributes", "attributes2", "display_unit", "statistics_unit", "unit_class"),
|
||||
[
|
||||
(
|
||||
AREA_SENSOR_M_ATTRIBUTES,
|
||||
AREA_SENSOR_FT_ATTRIBUTES,
|
||||
"ft²",
|
||||
"m²",
|
||||
"area",
|
||||
),
|
||||
(
|
||||
DISTANCE_SENSOR_M_ATTRIBUTES,
|
||||
DISTANCE_SENSOR_FT_ATTRIBUTES,
|
||||
|
@ -9,10 +9,10 @@ from homeassistant.components.select import (
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_SELECT_OPTION,
|
||||
EntityCategory,
|
||||
UnitOfArea,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
@ -38,7 +38,7 @@ async def test_select_entity(
|
||||
entry = entity_registry.async_get("select.genie_room_size")
|
||||
assert entry
|
||||
assert entry.unique_id == f"{diffuser.hublot}-room_size_square_meter"
|
||||
assert entry.unit_of_measurement == AREA_SQUARE_METERS
|
||||
assert entry.unit_of_measurement == UnitOfArea.SQUARE_METERS
|
||||
assert entry.entity_category == EntityCategory.CONFIG
|
||||
|
||||
|
||||
|
@ -30,6 +30,7 @@ from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
STATE_UNKNOWN,
|
||||
EntityCategory,
|
||||
UnitOfArea,
|
||||
UnitOfDataRate,
|
||||
UnitOfEnergy,
|
||||
UnitOfLength,
|
||||
@ -651,6 +652,34 @@ async def test_custom_unit(
|
||||
"device_class",
|
||||
),
|
||||
[
|
||||
# Area
|
||||
(
|
||||
UnitOfArea.SQUARE_KILOMETERS,
|
||||
UnitOfArea.SQUARE_MILES,
|
||||
UnitOfArea.SQUARE_MILES,
|
||||
1000,
|
||||
"1000",
|
||||
"386",
|
||||
SensorDeviceClass.AREA,
|
||||
),
|
||||
(
|
||||
UnitOfArea.SQUARE_CENTIMETERS,
|
||||
UnitOfArea.SQUARE_INCHES,
|
||||
UnitOfArea.SQUARE_INCHES,
|
||||
7.24,
|
||||
"7.24",
|
||||
"1.12",
|
||||
SensorDeviceClass.AREA,
|
||||
),
|
||||
(
|
||||
UnitOfArea.SQUARE_KILOMETERS,
|
||||
"peer_distance",
|
||||
UnitOfArea.SQUARE_KILOMETERS,
|
||||
1000,
|
||||
"1000",
|
||||
"1000",
|
||||
SensorDeviceClass.AREA,
|
||||
),
|
||||
# Distance
|
||||
(
|
||||
UnitOfLength.KILOMETERS,
|
||||
@ -1834,6 +1863,7 @@ async def test_non_numeric_device_class_with_unit_of_measurement(
|
||||
[
|
||||
SensorDeviceClass.APPARENT_POWER,
|
||||
SensorDeviceClass.AQI,
|
||||
SensorDeviceClass.AREA,
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
||||
SensorDeviceClass.BATTERY,
|
||||
SensorDeviceClass.CO,
|
||||
|
@ -227,6 +227,8 @@ async def assert_validation_result(
|
||||
),
|
||||
[
|
||||
(None, "%", "%", "%", "unitless", 13.050847, -10, 30),
|
||||
("area", "m²", "m²", "m²", "area", 13.050847, -10, 30),
|
||||
("area", "mi²", "mi²", "mi²", "area", 13.050847, -10, 30),
|
||||
("battery", "%", "%", "%", "unitless", 13.050847, -10, 30),
|
||||
("battery", None, None, None, "unitless", 13.050847, -10, 30),
|
||||
("distance", "m", "m", "m", "distance", 13.050847, -10, 30),
|
||||
@ -914,6 +916,8 @@ async def test_compile_hourly_statistics_wrong_unit(
|
||||
"factor",
|
||||
),
|
||||
[
|
||||
(US_CUSTOMARY_SYSTEM, "area", "m²", "m²", "m²", "area", 1),
|
||||
(US_CUSTOMARY_SYSTEM, "area", "mi²", "mi²", "mi²", "area", 1),
|
||||
(US_CUSTOMARY_SYSTEM, "distance", "m", "m", "m", "distance", 1),
|
||||
(US_CUSTOMARY_SYSTEM, "distance", "mi", "mi", "mi", "distance", 1),
|
||||
(US_CUSTOMARY_SYSTEM, "energy", "kWh", "kWh", "kWh", "energy", 1),
|
||||
@ -926,6 +930,8 @@ async def test_compile_hourly_statistics_wrong_unit(
|
||||
(US_CUSTOMARY_SYSTEM, "volume", "ft³", "ft³", "ft³", "volume", 1),
|
||||
(US_CUSTOMARY_SYSTEM, "weight", "g", "g", "g", "mass", 1),
|
||||
(US_CUSTOMARY_SYSTEM, "weight", "oz", "oz", "oz", "mass", 1),
|
||||
(METRIC_SYSTEM, "area", "m²", "m²", "m²", "area", 1),
|
||||
(METRIC_SYSTEM, "area", "mi²", "mi²", "mi²", "area", 1),
|
||||
(METRIC_SYSTEM, "distance", "m", "m", "m", "distance", 1),
|
||||
(METRIC_SYSTEM, "distance", "mi", "mi", "mi", "distance", 1),
|
||||
(METRIC_SYSTEM, "energy", "kWh", "kWh", "kWh", "energy", 1),
|
||||
@ -2228,6 +2234,8 @@ async def test_compile_hourly_energy_statistics_multiple(
|
||||
[
|
||||
("battery", "%", 30),
|
||||
("battery", None, 30),
|
||||
("area", "m²", 30),
|
||||
("area", "mi²", 30),
|
||||
("distance", "m", 30),
|
||||
("distance", "mi", 30),
|
||||
("humidity", "%", 30),
|
||||
@ -2336,6 +2344,8 @@ async def test_compile_hourly_statistics_partially_unavailable(
|
||||
[
|
||||
("battery", "%", 30),
|
||||
("battery", None, 30),
|
||||
("area", "m²", 30),
|
||||
("area", "mi²", 30),
|
||||
("distance", "m", 30),
|
||||
("distance", "mi", 30),
|
||||
("humidity", "%", 30),
|
||||
@ -2438,6 +2448,10 @@ async def test_compile_hourly_statistics_fails(
|
||||
"statistic_type",
|
||||
),
|
||||
[
|
||||
("measurement", "area", "m²", "m²", "m²", "area", "mean"),
|
||||
("measurement", "area", "mi²", "mi²", "mi²", "area", "mean"),
|
||||
("total", "area", "m²", "m²", "m²", "area", "sum"),
|
||||
("total", "area", "mi²", "mi²", "mi²", "area", "sum"),
|
||||
("measurement", "battery", "%", "%", "%", "unitless", "mean"),
|
||||
("measurement", "battery", None, None, None, "unitless", "mean"),
|
||||
("measurement", "distance", "m", "m", "m", "distance", "mean"),
|
||||
|
@ -24,6 +24,7 @@ from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
STATE_ON,
|
||||
STATE_UNAVAILABLE,
|
||||
UnitOfArea,
|
||||
UnitOfLength,
|
||||
UnitOfMass,
|
||||
UnitOfPrecipitationDepth,
|
||||
@ -61,6 +62,7 @@ def _set_up_units(hass: HomeAssistant) -> None:
|
||||
hass.config.units = UnitSystem(
|
||||
"custom",
|
||||
accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
conversions={},
|
||||
length=UnitOfLength.METERS,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
|
@ -177,18 +177,24 @@ def test_deprecated_constants(
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("replacement", "constant_name"),
|
||||
("replacement", "constant_name", "breaks_in_version"),
|
||||
[
|
||||
(const.UnitOfLength.YARDS, "LENGTH_YARD"),
|
||||
(const.UnitOfSoundPressure.DECIBEL, "SOUND_PRESSURE_DB"),
|
||||
(const.UnitOfSoundPressure.WEIGHTED_DECIBEL_A, "SOUND_PRESSURE_WEIGHTED_DBA"),
|
||||
(const.UnitOfVolume.FLUID_OUNCES, "VOLUME_FLUID_OUNCE"),
|
||||
(const.UnitOfLength.YARDS, "LENGTH_YARD", "2025.1"),
|
||||
(const.UnitOfSoundPressure.DECIBEL, "SOUND_PRESSURE_DB", "2025.1"),
|
||||
(
|
||||
const.UnitOfSoundPressure.WEIGHTED_DECIBEL_A,
|
||||
"SOUND_PRESSURE_WEIGHTED_DBA",
|
||||
"2025.1",
|
||||
),
|
||||
(const.UnitOfVolume.FLUID_OUNCES, "VOLUME_FLUID_OUNCE", "2025.1"),
|
||||
(const.UnitOfArea.SQUARE_METERS, "AREA_SQUARE_METERS", "2025.12"),
|
||||
],
|
||||
)
|
||||
def test_deprecated_constant_name_changes(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
replacement: Enum,
|
||||
constant_name: str,
|
||||
breaks_in_version: str,
|
||||
) -> None:
|
||||
"""Test deprecated constants, where the name is not the same as the enum value."""
|
||||
import_and_test_deprecated_constant(
|
||||
@ -197,7 +203,7 @@ def test_deprecated_constant_name_changes(
|
||||
constant_name,
|
||||
f"{replacement.__class__.__name__}.{replacement.name}",
|
||||
replacement,
|
||||
"2025.1",
|
||||
breaks_in_version,
|
||||
)
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@ from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
PERCENTAGE,
|
||||
UnitOfArea,
|
||||
UnitOfBloodGlucoseConcentration,
|
||||
UnitOfConductivity,
|
||||
UnitOfDataRate,
|
||||
@ -32,6 +33,7 @@ from homeassistant.const import (
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.util import unit_conversion
|
||||
from homeassistant.util.unit_conversion import (
|
||||
AreaConverter,
|
||||
BaseUnitConverter,
|
||||
BloodGlucoseConcentrationConverter,
|
||||
ConductivityConverter,
|
||||
@ -61,6 +63,7 @@ INVALID_SYMBOL = "bob"
|
||||
_ALL_CONVERTERS: dict[type[BaseUnitConverter], list[str | None]] = {
|
||||
converter: sorted(converter.VALID_UNITS, key=lambda x: (x is None, x))
|
||||
for converter in (
|
||||
AreaConverter,
|
||||
BloodGlucoseConcentrationConverter,
|
||||
ConductivityConverter,
|
||||
DataRateConverter,
|
||||
@ -83,6 +86,7 @@ _ALL_CONVERTERS: dict[type[BaseUnitConverter], list[str | None]] = {
|
||||
|
||||
# Dict containing all converters with a corresponding unit ratio.
|
||||
_GET_UNIT_RATIO: dict[type[BaseUnitConverter], tuple[str | None, str | None, float]] = {
|
||||
AreaConverter: (UnitOfArea.SQUARE_KILOMETERS, UnitOfArea.SQUARE_METERS, 0.000001),
|
||||
BloodGlucoseConcentrationConverter: (
|
||||
UnitOfBloodGlucoseConcentration.MILLIGRAMS_PER_DECILITER,
|
||||
UnitOfBloodGlucoseConcentration.MILLIMOLE_PER_LITER,
|
||||
@ -138,6 +142,62 @@ _GET_UNIT_RATIO: dict[type[BaseUnitConverter], tuple[str | None, str | None, flo
|
||||
_CONVERTED_VALUE: dict[
|
||||
type[BaseUnitConverter], list[tuple[float, str | None, float, str | None]]
|
||||
] = {
|
||||
AreaConverter: [
|
||||
# Square Meters to other units
|
||||
(5, UnitOfArea.SQUARE_METERS, 50000, UnitOfArea.SQUARE_CENTIMETERS),
|
||||
(5, UnitOfArea.SQUARE_METERS, 5000000, UnitOfArea.SQUARE_MILLIMETERS),
|
||||
(5, UnitOfArea.SQUARE_METERS, 0.000005, UnitOfArea.SQUARE_KILOMETERS),
|
||||
(5, UnitOfArea.SQUARE_METERS, 7750.015500031001, UnitOfArea.SQUARE_INCHES),
|
||||
(5, UnitOfArea.SQUARE_METERS, 53.81955, UnitOfArea.SQUARE_FEET),
|
||||
(5, UnitOfArea.SQUARE_METERS, 5.979950231505403, UnitOfArea.SQUARE_YARDS),
|
||||
(5, UnitOfArea.SQUARE_METERS, 1.9305107927122295e-06, UnitOfArea.SQUARE_MILES),
|
||||
(5, UnitOfArea.SQUARE_METERS, 0.0012355269073358272, UnitOfArea.ACRES),
|
||||
(5, UnitOfArea.SQUARE_METERS, 0.0005, UnitOfArea.HECTARES),
|
||||
# Square Kilometers to other units
|
||||
(1, UnitOfArea.SQUARE_KILOMETERS, 1000000, UnitOfArea.SQUARE_METERS),
|
||||
(1, UnitOfArea.SQUARE_KILOMETERS, 1e10, UnitOfArea.SQUARE_CENTIMETERS),
|
||||
(1, UnitOfArea.SQUARE_KILOMETERS, 1e12, UnitOfArea.SQUARE_MILLIMETERS),
|
||||
(5, UnitOfArea.SQUARE_KILOMETERS, 1.9305107927122296, UnitOfArea.SQUARE_MILES),
|
||||
(5, UnitOfArea.SQUARE_KILOMETERS, 1235.5269073358272, UnitOfArea.ACRES),
|
||||
(5, UnitOfArea.SQUARE_KILOMETERS, 500, UnitOfArea.HECTARES),
|
||||
# Acres to other units
|
||||
(5, UnitOfArea.ACRES, 20234.3, UnitOfArea.SQUARE_METERS),
|
||||
(5, UnitOfArea.ACRES, 202342821.11999995, UnitOfArea.SQUARE_CENTIMETERS),
|
||||
(5, UnitOfArea.ACRES, 20234282111.999992, UnitOfArea.SQUARE_MILLIMETERS),
|
||||
(5, UnitOfArea.ACRES, 0.0202343, UnitOfArea.SQUARE_KILOMETERS),
|
||||
(5, UnitOfArea.ACRES, 217800, UnitOfArea.SQUARE_FEET),
|
||||
(5, UnitOfArea.ACRES, 24200.0, UnitOfArea.SQUARE_YARDS),
|
||||
(5, UnitOfArea.ACRES, 0.0078125, UnitOfArea.SQUARE_MILES),
|
||||
(5, UnitOfArea.ACRES, 2.02343, UnitOfArea.HECTARES),
|
||||
# Hectares to other units
|
||||
(5, UnitOfArea.HECTARES, 50000, UnitOfArea.SQUARE_METERS),
|
||||
(5, UnitOfArea.HECTARES, 500000000, UnitOfArea.SQUARE_CENTIMETERS),
|
||||
(5, UnitOfArea.HECTARES, 50000000000.0, UnitOfArea.SQUARE_MILLIMETERS),
|
||||
(5, UnitOfArea.HECTARES, 0.019305107927122298, UnitOfArea.SQUARE_MILES),
|
||||
(5, UnitOfArea.HECTARES, 538195.5, UnitOfArea.SQUARE_FEET),
|
||||
(5, UnitOfArea.HECTARES, 59799.50231505403, UnitOfArea.SQUARE_YARDS),
|
||||
(5, UnitOfArea.HECTARES, 12.355269073358272, UnitOfArea.ACRES),
|
||||
# Square Miles to other units
|
||||
(5, UnitOfArea.SQUARE_MILES, 12949940.551679997, UnitOfArea.SQUARE_METERS),
|
||||
(5, UnitOfArea.SQUARE_MILES, 129499405516.79997, UnitOfArea.SQUARE_CENTIMETERS),
|
||||
(5, UnitOfArea.SQUARE_MILES, 12949940551679.996, UnitOfArea.SQUARE_MILLIMETERS),
|
||||
(5, UnitOfArea.SQUARE_MILES, 1294.9940551679997, UnitOfArea.HECTARES),
|
||||
(5, UnitOfArea.SQUARE_MILES, 3200, UnitOfArea.ACRES),
|
||||
# Square Yards to other units
|
||||
(5, UnitOfArea.SQUARE_YARDS, 4.1806367999999985, UnitOfArea.SQUARE_METERS),
|
||||
(5, UnitOfArea.SQUARE_YARDS, 41806.4, UnitOfArea.SQUARE_CENTIMETERS),
|
||||
(5, UnitOfArea.SQUARE_YARDS, 4180636.7999999984, UnitOfArea.SQUARE_MILLIMETERS),
|
||||
(
|
||||
5,
|
||||
UnitOfArea.SQUARE_YARDS,
|
||||
4.180636799999998e-06,
|
||||
UnitOfArea.SQUARE_KILOMETERS,
|
||||
),
|
||||
(5, UnitOfArea.SQUARE_YARDS, 45.0, UnitOfArea.SQUARE_FEET),
|
||||
(5, UnitOfArea.SQUARE_YARDS, 6479.999999999998, UnitOfArea.SQUARE_INCHES),
|
||||
(5, UnitOfArea.SQUARE_YARDS, 1.6141528925619832e-06, UnitOfArea.SQUARE_MILES),
|
||||
(5, UnitOfArea.SQUARE_YARDS, 0.0010330578512396695, UnitOfArea.ACRES),
|
||||
],
|
||||
BloodGlucoseConcentrationConverter: [
|
||||
(
|
||||
90,
|
||||
|
@ -7,12 +7,14 @@ import pytest
|
||||
from homeassistant.components.sensor import DEVICE_CLASS_UNITS, SensorDeviceClass
|
||||
from homeassistant.const import (
|
||||
ACCUMULATED_PRECIPITATION,
|
||||
AREA,
|
||||
LENGTH,
|
||||
MASS,
|
||||
PRESSURE,
|
||||
TEMPERATURE,
|
||||
VOLUME,
|
||||
WIND_SPEED,
|
||||
UnitOfArea,
|
||||
UnitOfLength,
|
||||
UnitOfMass,
|
||||
UnitOfPrecipitationDepth,
|
||||
@ -44,6 +46,7 @@ def test_invalid_units() -> None:
|
||||
UnitSystem(
|
||||
SYSTEM_NAME,
|
||||
accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
conversions={},
|
||||
length=UnitOfLength.METERS,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
@ -57,6 +60,7 @@ def test_invalid_units() -> None:
|
||||
UnitSystem(
|
||||
SYSTEM_NAME,
|
||||
accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
conversions={},
|
||||
length=INVALID_UNIT,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
@ -70,6 +74,7 @@ def test_invalid_units() -> None:
|
||||
UnitSystem(
|
||||
SYSTEM_NAME,
|
||||
accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
conversions={},
|
||||
length=UnitOfLength.METERS,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
@ -83,6 +88,7 @@ def test_invalid_units() -> None:
|
||||
UnitSystem(
|
||||
SYSTEM_NAME,
|
||||
accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
conversions={},
|
||||
length=UnitOfLength.METERS,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
@ -96,6 +102,7 @@ def test_invalid_units() -> None:
|
||||
UnitSystem(
|
||||
SYSTEM_NAME,
|
||||
accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
conversions={},
|
||||
length=UnitOfLength.METERS,
|
||||
mass=INVALID_UNIT,
|
||||
@ -109,6 +116,7 @@ def test_invalid_units() -> None:
|
||||
UnitSystem(
|
||||
SYSTEM_NAME,
|
||||
accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
conversions={},
|
||||
length=UnitOfLength.METERS,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
@ -122,6 +130,21 @@ def test_invalid_units() -> None:
|
||||
UnitSystem(
|
||||
SYSTEM_NAME,
|
||||
accumulated_precipitation=INVALID_UNIT,
|
||||
area=UnitOfArea.SQUARE_METERS,
|
||||
conversions={},
|
||||
length=UnitOfLength.METERS,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
pressure=UnitOfPressure.PA,
|
||||
temperature=UnitOfTemperature.CELSIUS,
|
||||
volume=UnitOfVolume.LITERS,
|
||||
wind_speed=UnitOfSpeed.METERS_PER_SECOND,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
UnitSystem(
|
||||
SYSTEM_NAME,
|
||||
accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
|
||||
area=INVALID_UNIT,
|
||||
conversions={},
|
||||
length=UnitOfLength.METERS,
|
||||
mass=UnitOfMass.GRAMS,
|
||||
@ -146,6 +169,8 @@ def test_invalid_value() -> None:
|
||||
METRIC_SYSTEM.pressure("50Pa", UnitOfPressure.PA)
|
||||
with pytest.raises(TypeError):
|
||||
METRIC_SYSTEM.accumulated_precipitation("50mm", UnitOfLength.MILLIMETERS)
|
||||
with pytest.raises(TypeError):
|
||||
METRIC_SYSTEM.area("2m²", UnitOfArea.SQUARE_METERS)
|
||||
|
||||
|
||||
def test_as_dict() -> None:
|
||||
@ -158,6 +183,7 @@ def test_as_dict() -> None:
|
||||
MASS: UnitOfMass.GRAMS,
|
||||
PRESSURE: UnitOfPressure.PA,
|
||||
ACCUMULATED_PRECIPITATION: UnitOfLength.MILLIMETERS,
|
||||
AREA: UnitOfArea.SQUARE_METERS,
|
||||
}
|
||||
|
||||
assert expected == METRIC_SYSTEM.as_dict()
|
||||
@ -303,6 +329,29 @@ def test_accumulated_precipitation_to_imperial() -> None:
|
||||
) == pytest.approx(10, abs=1e-4)
|
||||
|
||||
|
||||
def test_area_same_unit() -> None:
|
||||
"""Test no conversion happens if to unit is same as from unit."""
|
||||
assert METRIC_SYSTEM.area(5, METRIC_SYSTEM.area_unit) == 5
|
||||
|
||||
|
||||
def test_area_unknown_unit() -> None:
|
||||
"""Test no conversion happens if unknown unit."""
|
||||
with pytest.raises(HomeAssistantError, match="is not a recognized .* unit"):
|
||||
METRIC_SYSTEM.area(5, "abc")
|
||||
|
||||
|
||||
def test_area_to_metric() -> None:
|
||||
"""Test area conversion to metric system."""
|
||||
assert METRIC_SYSTEM.area(25, METRIC_SYSTEM.area_unit) == 25
|
||||
assert round(METRIC_SYSTEM.area(10, IMPERIAL_SYSTEM.area_unit), 1) == 0.9
|
||||
|
||||
|
||||
def test_area_to_imperial() -> None:
|
||||
"""Test area conversion to imperial system."""
|
||||
assert IMPERIAL_SYSTEM.area(77, IMPERIAL_SYSTEM.area_unit) == 77
|
||||
assert IMPERIAL_SYSTEM.area(25, METRIC_SYSTEM.area_unit) == 269.09776041774313
|
||||
|
||||
|
||||
def test_properties() -> None:
|
||||
"""Test the unit properties are returned as expected."""
|
||||
assert METRIC_SYSTEM.length_unit == UnitOfLength.KILOMETERS
|
||||
@ -312,6 +361,7 @@ def test_properties() -> None:
|
||||
assert METRIC_SYSTEM.volume_unit == UnitOfVolume.LITERS
|
||||
assert METRIC_SYSTEM.pressure_unit == UnitOfPressure.PA
|
||||
assert METRIC_SYSTEM.accumulated_precipitation_unit == UnitOfLength.MILLIMETERS
|
||||
assert METRIC_SYSTEM.area_unit == UnitOfArea.SQUARE_METERS
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -338,6 +388,18 @@ def test_get_unit_system_invalid(key: str) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
("device_class", "original_unit", "state_unit"),
|
||||
[
|
||||
# Test area conversion
|
||||
(SensorDeviceClass.AREA, UnitOfArea.SQUARE_FEET, UnitOfArea.SQUARE_METERS),
|
||||
(
|
||||
SensorDeviceClass.AREA,
|
||||
UnitOfArea.SQUARE_INCHES,
|
||||
UnitOfArea.SQUARE_CENTIMETERS,
|
||||
),
|
||||
(SensorDeviceClass.AREA, UnitOfArea.SQUARE_MILES, UnitOfArea.SQUARE_KILOMETERS),
|
||||
(SensorDeviceClass.AREA, UnitOfArea.SQUARE_YARDS, UnitOfArea.SQUARE_METERS),
|
||||
(SensorDeviceClass.AREA, UnitOfArea.ACRES, UnitOfArea.HECTARES),
|
||||
(SensorDeviceClass.AREA, UnitOfArea.SQUARE_KILOMETERS, None),
|
||||
(SensorDeviceClass.AREA, "very_long", None),
|
||||
# Test atmospheric pressure
|
||||
(
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
||||
@ -495,6 +557,13 @@ def test_get_metric_converted_unit_(
|
||||
|
||||
|
||||
UNCONVERTED_UNITS_METRIC_SYSTEM = {
|
||||
SensorDeviceClass.AREA: (
|
||||
UnitOfArea.SQUARE_MILLIMETERS,
|
||||
UnitOfArea.SQUARE_CENTIMETERS,
|
||||
UnitOfArea.SQUARE_METERS,
|
||||
UnitOfArea.SQUARE_KILOMETERS,
|
||||
UnitOfArea.HECTARES,
|
||||
),
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: (UnitOfPressure.HPA,),
|
||||
SensorDeviceClass.DISTANCE: (
|
||||
UnitOfLength.CENTIMETERS,
|
||||
@ -544,6 +613,7 @@ UNCONVERTED_UNITS_METRIC_SYSTEM = {
|
||||
@pytest.mark.parametrize(
|
||||
"device_class",
|
||||
[
|
||||
SensorDeviceClass.AREA,
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
||||
SensorDeviceClass.DISTANCE,
|
||||
SensorDeviceClass.GAS,
|
||||
@ -572,6 +642,21 @@ def test_metric_converted_units(device_class: SensorDeviceClass) -> None:
|
||||
@pytest.mark.parametrize(
|
||||
("device_class", "original_unit", "state_unit"),
|
||||
[
|
||||
# Test area conversion
|
||||
(
|
||||
SensorDeviceClass.AREA,
|
||||
UnitOfArea.SQUARE_MILLIMETERS,
|
||||
UnitOfArea.SQUARE_INCHES,
|
||||
),
|
||||
(
|
||||
SensorDeviceClass.AREA,
|
||||
UnitOfArea.SQUARE_CENTIMETERS,
|
||||
UnitOfArea.SQUARE_INCHES,
|
||||
),
|
||||
(SensorDeviceClass.AREA, UnitOfArea.SQUARE_METERS, UnitOfArea.SQUARE_FEET),
|
||||
(SensorDeviceClass.AREA, UnitOfArea.SQUARE_KILOMETERS, UnitOfArea.SQUARE_MILES),
|
||||
(SensorDeviceClass.AREA, UnitOfArea.HECTARES, UnitOfArea.ACRES),
|
||||
(SensorDeviceClass.AREA, "very_area", None),
|
||||
# Test atmospheric pressure
|
||||
(
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE,
|
||||
@ -721,6 +806,13 @@ def test_get_us_converted_unit(
|
||||
|
||||
|
||||
UNCONVERTED_UNITS_US_SYSTEM = {
|
||||
SensorDeviceClass.AREA: (
|
||||
UnitOfArea.SQUARE_FEET,
|
||||
UnitOfArea.SQUARE_INCHES,
|
||||
UnitOfArea.SQUARE_MILES,
|
||||
UnitOfArea.SQUARE_YARDS,
|
||||
UnitOfArea.ACRES,
|
||||
),
|
||||
SensorDeviceClass.ATMOSPHERIC_PRESSURE: (UnitOfPressure.INHG,),
|
||||
SensorDeviceClass.DISTANCE: (
|
||||
UnitOfLength.FEET,
|
||||
|
Loading…
x
Reference in New Issue
Block a user