diff --git a/homeassistant/components/climate/__init__.py b/homeassistant/components/climate/__init__.py index cc26bcc9bcc..0b80402b3ec 100644 --- a/homeassistant/components/climate/__init__.py +++ b/homeassistant/components/climate/__init__.py @@ -18,6 +18,7 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, TEMP_CELSIUS, + UnitTemperatureT, ) from homeassistant.core import HomeAssistant, ServiceCall import homeassistant.helpers.config_validation as cv @@ -195,7 +196,7 @@ class ClimateEntity(Entity): _attr_target_temperature_low: float | None _attr_target_temperature_step: float | None = None _attr_target_temperature: float | None = None - _attr_temperature_unit: str + _attr_temperature_unit: UnitTemperatureT @property def state(self) -> str: @@ -303,7 +304,7 @@ class ClimateEntity(Entity): return data @property - def temperature_unit(self) -> str: + def temperature_unit(self) -> UnitTemperatureT: """Return the unit of measurement used by the platform.""" return self._attr_temperature_unit diff --git a/homeassistant/components/climate/device_trigger.py b/homeassistant/components/climate/device_trigger.py index 1b5127d7d4a..db8eeedd54c 100644 --- a/homeassistant/components/climate/device_trigger.py +++ b/homeassistant/components/climate/device_trigger.py @@ -19,6 +19,7 @@ from homeassistant.const import ( CONF_PLATFORM, CONF_TYPE, PERCENTAGE, + UnitT, ) from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.helpers import config_validation as cv, entity_registry @@ -172,6 +173,7 @@ async def async_get_trigger_capabilities(hass: HomeAssistant, config): ) } + unit_of_measurement: UnitT if trigger_type == "current_temperature_changed": unit_of_measurement = hass.config.units.temperature_unit else: diff --git a/homeassistant/components/devolo_home_control/climate.py b/homeassistant/components/devolo_home_control/climate.py index 6b890544da5..cad439eb284 100644 --- a/homeassistant/components/devolo_home_control/climate.py +++ b/homeassistant/components/devolo_home_control/climate.py @@ -11,7 +11,7 @@ from homeassistant.components.climate import ( ClimateEntity, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS +from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS, UnitTemperatureT from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -105,7 +105,7 @@ class DevoloClimateDeviceEntity(DevoloMultiLevelSwitchDeviceEntity, ClimateEntit return SUPPORT_TARGET_TEMPERATURE @property - def temperature_unit(self) -> str: + def temperature_unit(self) -> UnitTemperatureT: """Return the supported unit of temperature.""" return TEMP_CELSIUS diff --git a/homeassistant/components/esphome/climate.py b/homeassistant/components/esphome/climate.py index 218f0fb319b..016d197e8d9 100644 --- a/homeassistant/components/esphome/climate.py +++ b/homeassistant/components/esphome/climate.py @@ -65,6 +65,7 @@ from homeassistant.const import ( PRECISION_TENTHS, PRECISION_WHOLE, TEMP_CELSIUS, + UnitTemperatureT, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -166,7 +167,7 @@ class EsphomeClimateEntity(EsphomeEntity[ClimateInfo, ClimateState], ClimateEnti return PRECISION_TENTHS @property - def temperature_unit(self) -> str: + def temperature_unit(self) -> UnitTemperatureT: """Return the unit of measurement used by the platform.""" return TEMP_CELSIUS diff --git a/homeassistant/components/fritzbox/climate.py b/homeassistant/components/fritzbox/climate.py index c50e0d4f270..7f3ee76b48e 100644 --- a/homeassistant/components/fritzbox/climate.py +++ b/homeassistant/components/fritzbox/climate.py @@ -23,6 +23,7 @@ from homeassistant.const import ( ATTR_UNIT_OF_MEASUREMENT, PRECISION_HALVES, TEMP_CELSIUS, + UnitTemperatureT, ) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -97,7 +98,7 @@ class FritzboxThermostat(FritzBoxEntity, ClimateEntity): return self.device.present # type: ignore [no-any-return] @property - def temperature_unit(self) -> str: + def temperature_unit(self) -> UnitTemperatureT: """Return the unit of measurement that is used.""" return TEMP_CELSIUS diff --git a/homeassistant/components/lcn/climate.py b/homeassistant/components/lcn/climate.py index 0a595076a8d..e453f26c25c 100644 --- a/homeassistant/components/lcn/climate.py +++ b/homeassistant/components/lcn/climate.py @@ -18,6 +18,9 @@ from homeassistant.const import ( CONF_ENTITIES, CONF_SOURCE, CONF_UNIT_OF_MEASUREMENT, + TEMP_CELSIUS, + TEMP_FAHRENHEIT, + UnitTemperatureT, ) from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, HomeAssistantType @@ -107,9 +110,12 @@ class LcnClimate(LcnEntity, ClimateEntity): return const.SUPPORT_TARGET_TEMPERATURE @property - def temperature_unit(self) -> str: + def temperature_unit(self) -> UnitTemperatureT: """Return the unit of measurement.""" - return cast(str, self.unit.value) + # Config schema only allows for: TEMP_CELSIUS and TEMP_FAHRENHEIT + if self.unit == pypck.lcn_defs.VarUnit.FAHRENHEIT: + return TEMP_FAHRENHEIT + return TEMP_CELSIUS @property def current_temperature(self) -> float | None: diff --git a/homeassistant/components/mysensors/climate.py b/homeassistant/components/mysensors/climate.py index 5dd52673581..5afb4a803d2 100644 --- a/homeassistant/components/mysensors/climate.py +++ b/homeassistant/components/mysensors/climate.py @@ -19,7 +19,12 @@ from homeassistant.components.climate.const import ( ) from homeassistant.components.mysensors.const import MYSENSORS_DISCOVERY, DiscoveryInfo from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.const import ( + ATTR_TEMPERATURE, + TEMP_CELSIUS, + TEMP_FAHRENHEIT, + UnitTemperatureT, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity_platform import AddEntitiesCallback @@ -91,7 +96,7 @@ class MySensorsHVAC(mysensors.device.MySensorsEntity, ClimateEntity): return features @property - def temperature_unit(self) -> str: + def temperature_unit(self) -> UnitTemperatureT: """Return the unit of measurement.""" return TEMP_CELSIUS if self.hass.config.units.is_metric else TEMP_FAHRENHEIT diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index 1621e87cfab..62da8cc8c80 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -53,6 +53,7 @@ from homeassistant.const import ( PRECISION_TENTHS, TEMP_CELSIUS, TEMP_FAHRENHEIT, + UnitTemperatureT, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -248,7 +249,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity): return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), []) # type: ignore @property - def temperature_unit(self) -> str: + def temperature_unit(self) -> UnitTemperatureT: """Return the unit of measurement used by the platform.""" if ( self._unit_value diff --git a/homeassistant/const.py b/homeassistant/const.py index 35b946fc6ab..3f07196d570 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" from __future__ import annotations -from typing import Final +from typing import Final, NewType MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 8 @@ -393,166 +393,212 @@ ATTR_DEVICE_CLASS: Final = "device_class" # Temperature attribute ATTR_TEMPERATURE: Final = "temperature" + # #### UNITS OF MEASUREMENT #### +UnitT = NewType("UnitT", str) + # Power units -POWER_WATT: Final = "W" -POWER_KILO_WATT: Final = "kW" +UnitPowerT = NewType("UnitPowerT", UnitT) +POWER_WATT: Final[UnitPowerT] = UnitPowerT(UnitT("W")) +POWER_KILO_WATT: Final[UnitPowerT] = UnitPowerT(UnitT("kW")) # Voltage units -VOLT: Final = "V" +VOLT: Final[UnitT] = UnitT("V") # Energy units -ENERGY_WATT_HOUR: Final = "Wh" -ENERGY_KILO_WATT_HOUR: Final = "kWh" +UnitEnergyT = NewType("UnitEnergyT", UnitT) +ENERGY_WATT_HOUR: Final[UnitEnergyT] = UnitEnergyT(UnitT("Wh")) +ENERGY_KILO_WATT_HOUR: Final[UnitEnergyT] = UnitEnergyT(UnitT("kWh")) # Electrical units -ELECTRICAL_CURRENT_AMPERE: Final = "A" -ELECTRICAL_VOLT_AMPERE: Final = "VA" +ELECTRICAL_CURRENT_AMPERE: Final[UnitT] = UnitT("A") +ELECTRICAL_VOLT_AMPERE: Final[UnitT] = UnitT("VA") # Degree units -DEGREE: Final = "°" +DEGREE: Final[UnitT] = UnitT("°") # Currency units -CURRENCY_EURO: Final = "€" -CURRENCY_DOLLAR: Final = "$" -CURRENCY_CENT: Final = "¢" +UnitCurrencyT = NewType("UnitCurrencyT", UnitT) +CURRENCY_EURO: Final[UnitCurrencyT] = UnitCurrencyT(UnitT("€")) +CURRENCY_DOLLAR: Final[UnitCurrencyT] = UnitCurrencyT(UnitT("$")) +CURRENCY_CENT: Final[UnitCurrencyT] = UnitCurrencyT(UnitT("¢")) # Temperature units -TEMP_CELSIUS: Final = "°C" -TEMP_FAHRENHEIT: Final = "°F" -TEMP_KELVIN: Final = "K" +UnitTemperatureT = NewType("UnitTemperatureT", UnitT) +TEMP_CELSIUS: Final[UnitTemperatureT] = UnitTemperatureT(UnitT("°C")) +TEMP_FAHRENHEIT: Final[UnitTemperatureT] = UnitTemperatureT(UnitT("°F")) +TEMP_KELVIN: Final[UnitTemperatureT] = UnitTemperatureT(UnitT("K")) # Time units -TIME_MICROSECONDS: Final = "μs" -TIME_MILLISECONDS: Final = "ms" -TIME_SECONDS: Final = "s" -TIME_MINUTES: Final = "min" -TIME_HOURS: Final = "h" -TIME_DAYS: Final = "d" -TIME_WEEKS: Final = "w" -TIME_MONTHS: Final = "m" -TIME_YEARS: Final = "y" +UnitTimeT = NewType("UnitTimeT", UnitT) +TIME_MICROSECONDS: Final[UnitTimeT] = UnitTimeT(UnitT("μs")) +TIME_MILLISECONDS: Final[UnitTimeT] = UnitTimeT(UnitT("ms")) +TIME_SECONDS: Final[UnitTimeT] = UnitTimeT(UnitT("s")) +TIME_MINUTES: Final[UnitTimeT] = UnitTimeT(UnitT("min")) +TIME_HOURS: Final[UnitTimeT] = UnitTimeT(UnitT("h")) +TIME_DAYS: Final[UnitTimeT] = UnitTimeT(UnitT("d")) +TIME_WEEKS: Final[UnitTimeT] = UnitTimeT(UnitT("w")) +TIME_MONTHS: Final[UnitTimeT] = UnitTimeT(UnitT("m")) +TIME_YEARS: Final[UnitTimeT] = UnitTimeT(UnitT("y")) # Length units -LENGTH_MILLIMETERS: Final = "mm" -LENGTH_CENTIMETERS: Final = "cm" -LENGTH_METERS: Final = "m" -LENGTH_KILOMETERS: Final = "km" +UnitLengthT = NewType("UnitLengthT", UnitT) +LENGTH_MILLIMETERS: Final[UnitLengthT] = UnitLengthT(UnitT("mm")) +LENGTH_CENTIMETERS: Final[UnitLengthT] = UnitLengthT(UnitT("cm")) +LENGTH_METERS: Final[UnitLengthT] = UnitLengthT(UnitT("m")) +LENGTH_KILOMETERS: Final[UnitLengthT] = UnitLengthT(UnitT("km")) -LENGTH_INCHES: Final = "in" -LENGTH_FEET: Final = "ft" -LENGTH_YARD: Final = "yd" -LENGTH_MILES: Final = "mi" +LENGTH_INCHES: Final[UnitLengthT] = UnitLengthT(UnitT("in")) +LENGTH_FEET: Final[UnitLengthT] = UnitLengthT(UnitT("ft")) +LENGTH_YARD: Final[UnitLengthT] = UnitLengthT(UnitT("yd")) +LENGTH_MILES: Final[UnitLengthT] = UnitLengthT(UnitT("mi")) # Frequency units -FREQUENCY_HERTZ: Final = "Hz" -FREQUENCY_GIGAHERTZ: Final = "GHz" +UnitFrequencyT = NewType("UnitFrequencyT", UnitT) +FREQUENCY_HERTZ: Final[UnitFrequencyT] = UnitFrequencyT(UnitT("Hz")) +FREQUENCY_GIGAHERTZ: Final[UnitFrequencyT] = UnitFrequencyT(UnitT("GHz")) # Pressure units -PRESSURE_PA: Final = "Pa" -PRESSURE_HPA: Final = "hPa" -PRESSURE_BAR: Final = "bar" -PRESSURE_MBAR: Final = "mbar" -PRESSURE_INHG: Final = "inHg" -PRESSURE_PSI: Final = "psi" +UnitPressureT = NewType("UnitPressureT", UnitT) +PRESSURE_PA: Final[UnitPressureT] = UnitPressureT(UnitT("Pa")) +PRESSURE_HPA: Final[UnitPressureT] = UnitPressureT(UnitT("hPa")) +PRESSURE_BAR: Final[UnitPressureT] = UnitPressureT(UnitT("bar")) +PRESSURE_MBAR: Final[UnitPressureT] = UnitPressureT(UnitT("mbar")) +PRESSURE_INHG: Final[UnitPressureT] = UnitPressureT(UnitT("inHg")) +PRESSURE_PSI: Final[UnitPressureT] = UnitPressureT(UnitT("psi")) # Volume units -VOLUME_LITERS: Final = "L" -VOLUME_MILLILITERS: Final = "mL" -VOLUME_CUBIC_METERS: Final = "m³" -VOLUME_CUBIC_FEET: Final = "ft³" +UnitVolumeT = NewType("UnitVolumeT", UnitT) +VOLUME_LITERS: Final[UnitVolumeT] = UnitVolumeT(UnitT("L")) +VOLUME_MILLILITERS: Final[UnitVolumeT] = UnitVolumeT(UnitT("mL")) +VOLUME_CUBIC_METERS: Final[UnitVolumeT] = UnitVolumeT(UnitT("m³")) +VOLUME_CUBIC_FEET: Final[UnitVolumeT] = UnitVolumeT(UnitT("ft³")) -VOLUME_GALLONS: Final = "gal" -VOLUME_FLUID_OUNCE: Final = "fl. oz." +VOLUME_GALLONS: Final[UnitVolumeT] = UnitVolumeT(UnitT("gal")) +VOLUME_FLUID_OUNCE: Final[UnitVolumeT] = UnitVolumeT(UnitT("fl. oz.")) # Volume Flow Rate units -VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR: Final = "m³/h" -VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE: Final = "ft³/m" +UnitVolumeFlowT = NewType("UnitVolumeFlowT", UnitT) +VOLUME_FLOW_RATE_CUBIC_METERS_PER_HOUR: Final[UnitVolumeFlowT] = UnitVolumeFlowT( + UnitT("m³/h") +) +VOLUME_FLOW_RATE_CUBIC_FEET_PER_MINUTE: Final[UnitVolumeFlowT] = UnitVolumeFlowT( + UnitT("ft³/m") +) # Area units -AREA_SQUARE_METERS: Final = "m²" +UnitAreaT = NewType("UnitAreaT", UnitT) +AREA_SQUARE_METERS: Final[UnitAreaT] = UnitAreaT(UnitT("m²")) # Mass units -MASS_GRAMS: Final = "g" -MASS_KILOGRAMS: Final = "kg" -MASS_MILLIGRAMS: Final = "mg" -MASS_MICROGRAMS: Final = "µg" +UnitMassT = NewType("UnitMassT", UnitT) +MASS_GRAMS: Final[UnitMassT] = UnitMassT(UnitT("g")) +MASS_KILOGRAMS: Final[UnitMassT] = UnitMassT(UnitT("kg")) +MASS_MILLIGRAMS: Final[UnitMassT] = UnitMassT(UnitT("mg")) +MASS_MICROGRAMS: Final[UnitMassT] = UnitMassT(UnitT("µg")) -MASS_OUNCES: Final = "oz" -MASS_POUNDS: Final = "lb" +MASS_OUNCES: Final[UnitMassT] = UnitMassT(UnitT("oz")) +MASS_POUNDS: Final[UnitMassT] = UnitMassT(UnitT("lb")) # Conductivity units -CONDUCTIVITY: Final = "µS/cm" +CONDUCTIVITY: Final[UnitT] = UnitT("µS/cm") # Light units -LIGHT_LUX: Final = "lx" +LIGHT_LUX: Final[UnitT] = UnitT("lx") # UV Index units -UV_INDEX: Final = "UV index" +UV_INDEX: Final[UnitT] = UnitT("UV index") # Percentage units -PERCENTAGE: Final = "%" +PERCENTAGE: Final[UnitT] = UnitT("%") # Irradiation units -IRRADIATION_WATTS_PER_SQUARE_METER: Final = "W/m²" -IRRADIATION_BTUS_PER_HOUR_SQUARE_FOOT: Final = "BTU/(h×ft²)" +UnitIrradiationT = NewType("UnitIrradiationT", UnitT) +IRRADIATION_WATTS_PER_SQUARE_METER: Final[UnitIrradiationT] = UnitIrradiationT( + UnitT("W/m²") +) +IRRADIATION_BTUS_PER_HOUR_SQUARE_FOOT: Final[UnitIrradiationT] = UnitIrradiationT( + UnitT("BTU/(h×ft²)") +) # Precipitation units -PRECIPITATION_MILLIMETERS_PER_HOUR: Final = "mm/h" +PRECIPITATION_MILLIMETERS_PER_HOUR: Final[UnitT] = UnitT("mm/h") # Concentration units -CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final = "µg/m³" -CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: Final = "mg/m³" -CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT: Final = "μg/ft³" -CONCENTRATION_PARTS_PER_CUBIC_METER: Final = "p/m³" -CONCENTRATION_PARTS_PER_MILLION: Final = "ppm" -CONCENTRATION_PARTS_PER_BILLION: Final = "ppb" +UnitConcentrationT = NewType("UnitConcentrationT", UnitT) +CONCENTRATION_MICROGRAMS_PER_CUBIC_METER: Final[ + UnitConcentrationT +] = UnitConcentrationT(UnitT("µg/m³")) +CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER: Final[ + UnitConcentrationT +] = UnitConcentrationT(UnitT("mg/m³")) +CONCENTRATION_MICROGRAMS_PER_CUBIC_FOOT: Final[UnitConcentrationT] = UnitConcentrationT( + UnitT("μg/ft³") +) +CONCENTRATION_PARTS_PER_CUBIC_METER: Final[UnitConcentrationT] = UnitConcentrationT( + UnitT("p/m³") +) +CONCENTRATION_PARTS_PER_MILLION: Final[UnitConcentrationT] = UnitConcentrationT( + UnitT("ppm") +) +CONCENTRATION_PARTS_PER_BILLION: Final[UnitConcentrationT] = UnitConcentrationT( + UnitT("ppb") +) # Speed units -SPEED_MILLIMETERS_PER_DAY: Final = "mm/d" -SPEED_INCHES_PER_DAY: Final = "in/d" -SPEED_METERS_PER_SECOND: Final = "m/s" -SPEED_INCHES_PER_HOUR: Final = "in/h" -SPEED_KILOMETERS_PER_HOUR: Final = "km/h" -SPEED_MILES_PER_HOUR: Final = "mph" +UnitSpeedT = NewType("UnitSpeedT", UnitT) +SPEED_MILLIMETERS_PER_DAY: Final[UnitSpeedT] = UnitSpeedT(UnitT("mm/d")) +SPEED_INCHES_PER_DAY: Final[UnitSpeedT] = UnitSpeedT(UnitT("in/d")) +SPEED_METERS_PER_SECOND: Final[UnitSpeedT] = UnitSpeedT(UnitT("m/s")) +SPEED_INCHES_PER_HOUR: Final[UnitSpeedT] = UnitSpeedT(UnitT("in/h")) +SPEED_KILOMETERS_PER_HOUR: Final[UnitSpeedT] = UnitSpeedT(UnitT("km/h")) +SPEED_MILES_PER_HOUR: Final[UnitSpeedT] = UnitSpeedT(UnitT("mph")) # Signal_strength units -SIGNAL_STRENGTH_DECIBELS: Final = "dB" -SIGNAL_STRENGTH_DECIBELS_MILLIWATT: Final = "dBm" +UnitSignalStrengthT = NewType("UnitSignalStrengthT", UnitT) +SIGNAL_STRENGTH_DECIBELS: Final[UnitSignalStrengthT] = UnitSignalStrengthT(UnitT("dB")) +SIGNAL_STRENGTH_DECIBELS_MILLIWATT: Final[UnitSignalStrengthT] = UnitSignalStrengthT( + UnitT("dBm") +) # Data units -DATA_BITS: Final = "bit" -DATA_KILOBITS: Final = "kbit" -DATA_MEGABITS: Final = "Mbit" -DATA_GIGABITS: Final = "Gbit" -DATA_BYTES: Final = "B" -DATA_KILOBYTES: Final = "kB" -DATA_MEGABYTES: Final = "MB" -DATA_GIGABYTES: Final = "GB" -DATA_TERABYTES: Final = "TB" -DATA_PETABYTES: Final = "PB" -DATA_EXABYTES: Final = "EB" -DATA_ZETTABYTES: Final = "ZB" -DATA_YOTTABYTES: Final = "YB" -DATA_KIBIBYTES: Final = "KiB" -DATA_MEBIBYTES: Final = "MiB" -DATA_GIBIBYTES: Final = "GiB" -DATA_TEBIBYTES: Final = "TiB" -DATA_PEBIBYTES: Final = "PiB" -DATA_EXBIBYTES: Final = "EiB" -DATA_ZEBIBYTES: Final = "ZiB" -DATA_YOBIBYTES: Final = "YiB" -DATA_RATE_BITS_PER_SECOND: Final = "bit/s" -DATA_RATE_KILOBITS_PER_SECOND: Final = "kbit/s" -DATA_RATE_MEGABITS_PER_SECOND: Final = "Mbit/s" -DATA_RATE_GIGABITS_PER_SECOND: Final = "Gbit/s" -DATA_RATE_BYTES_PER_SECOND: Final = "B/s" -DATA_RATE_KILOBYTES_PER_SECOND: Final = "kB/s" -DATA_RATE_MEGABYTES_PER_SECOND: Final = "MB/s" -DATA_RATE_GIGABYTES_PER_SECOND: Final = "GB/s" -DATA_RATE_KIBIBYTES_PER_SECOND: Final = "KiB/s" -DATA_RATE_MEBIBYTES_PER_SECOND: Final = "MiB/s" -DATA_RATE_GIBIBYTES_PER_SECOND: Final = "GiB/s" +UnitDataT = NewType("UnitDataT", UnitT) +DATA_BITS: Final[UnitDataT] = UnitDataT(UnitT("bit")) +DATA_KILOBITS: Final[UnitDataT] = UnitDataT(UnitT("kbit")) +DATA_MEGABITS: Final[UnitDataT] = UnitDataT(UnitT("Mbit")) +DATA_GIGABITS: Final[UnitDataT] = UnitDataT(UnitT("Gbit")) +DATA_BYTES: Final[UnitDataT] = UnitDataT(UnitT("B")) +DATA_KILOBYTES: Final[UnitDataT] = UnitDataT(UnitT("kB")) +DATA_MEGABYTES: Final[UnitDataT] = UnitDataT(UnitT("MB")) +DATA_GIGABYTES: Final[UnitDataT] = UnitDataT(UnitT("GB")) +DATA_TERABYTES: Final[UnitDataT] = UnitDataT(UnitT("TB")) +DATA_PETABYTES: Final[UnitDataT] = UnitDataT(UnitT("PB")) +DATA_EXABYTES: Final[UnitDataT] = UnitDataT(UnitT("EB")) +DATA_ZETTABYTES: Final[UnitDataT] = UnitDataT(UnitT("ZB")) +DATA_YOTTABYTES: Final[UnitDataT] = UnitDataT(UnitT("YB")) +DATA_KIBIBYTES: Final[UnitDataT] = UnitDataT(UnitT("KiB")) +DATA_MEBIBYTES: Final[UnitDataT] = UnitDataT(UnitT("MiB")) +DATA_GIBIBYTES: Final[UnitDataT] = UnitDataT(UnitT("GiB")) +DATA_TEBIBYTES: Final[UnitDataT] = UnitDataT(UnitT("TiB")) +DATA_PEBIBYTES: Final[UnitDataT] = UnitDataT(UnitT("PiB")) +DATA_EXBIBYTES: Final[UnitDataT] = UnitDataT(UnitT("EiB")) +DATA_ZEBIBYTES: Final[UnitDataT] = UnitDataT(UnitT("ZiB")) +DATA_YOBIBYTES: Final[UnitDataT] = UnitDataT(UnitT("YiB")) + +# Data_rate units +UnitDataRateT = NewType("UnitDataRateT", UnitT) +DATA_RATE_BITS_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("bit/s")) +DATA_RATE_KILOBITS_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("kbit/s")) +DATA_RATE_MEGABITS_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("Mbit/s")) +DATA_RATE_GIGABITS_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("Gbit/s")) +DATA_RATE_BYTES_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("B/s")) +DATA_RATE_KILOBYTES_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("kB/s")) +DATA_RATE_MEGABYTES_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("MB/s")) +DATA_RATE_GIGABYTES_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("GB/s")) +DATA_RATE_KIBIBYTES_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("KiB/s")) +DATA_RATE_MEBIBYTES_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("MiB/s")) +DATA_RATE_GIBIBYTES_PER_SECOND: Final[UnitDataRateT] = UnitDataRateT(UnitT("GiB/s")) + # #### SERVICES #### SERVICE_HOMEASSISTANT_STOP: Final = "stop" @@ -653,13 +699,14 @@ RESTART_EXIT_CODE: Final = 100 UNIT_NOT_RECOGNIZED_TEMPLATE: Final = "{} is not a recognized {} unit." -LENGTH: Final = "length" -MASS: Final = "mass" -PRESSURE: Final = "pressure" -VOLUME: Final = "volume" -TEMPERATURE: Final = "temperature" -SPEED_MS: Final = "speed_ms" -ILLUMINANCE: Final = "illuminance" +UnitTypeT = NewType("UnitTypeT", str) +LENGTH: Final[UnitTypeT] = UnitTypeT("length") +MASS: Final[UnitTypeT] = UnitTypeT("mass") +PRESSURE: Final[UnitTypeT] = UnitTypeT("pressure") +VOLUME: Final[UnitTypeT] = UnitTypeT("volume") +TEMPERATURE: Final[UnitTypeT] = UnitTypeT("temperature") +SPEED_MS: Final[UnitTypeT] = UnitTypeT("speed_ms") +ILLUMINANCE: Final[UnitTypeT] = UnitTypeT("illuminance") WEEKDAYS: Final[list[str]] = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] diff --git a/homeassistant/helpers/temperature.py b/homeassistant/helpers/temperature.py index e0f089e93b9..2693fc13fc2 100644 --- a/homeassistant/helpers/temperature.py +++ b/homeassistant/helpers/temperature.py @@ -3,13 +3,16 @@ from __future__ import annotations from numbers import Number -from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS +from homeassistant.const import PRECISION_HALVES, PRECISION_TENTHS, UnitTemperatureT from homeassistant.core import HomeAssistant from homeassistant.util.temperature import convert as convert_temperature def display_temp( - hass: HomeAssistant, temperature: float | None, unit: str, precision: float + hass: HomeAssistant, + temperature: float | None, + unit: UnitTemperatureT, + precision: float, ) -> float | None: """Convert temperature into preferred units/precision for display.""" temperature_unit = unit diff --git a/homeassistant/util/distance.py b/homeassistant/util/distance.py index 592c7c3145e..921e2941760 100644 --- a/homeassistant/util/distance.py +++ b/homeassistant/util/distance.py @@ -15,9 +15,10 @@ from homeassistant.const import ( LENGTH_MILLIMETERS, LENGTH_YARD, UNIT_NOT_RECOGNIZED_TEMPLATE, + UnitLengthT, ) -VALID_UNITS = [ +VALID_UNITS: tuple[UnitLengthT, ...] = ( LENGTH_KILOMETERS, LENGTH_MILES, LENGTH_FEET, @@ -26,9 +27,9 @@ VALID_UNITS = [ LENGTH_MILLIMETERS, LENGTH_INCHES, LENGTH_YARD, -] +) -TO_METERS: dict[str, Callable[[float], float]] = { +TO_METERS: dict[UnitLengthT, Callable[[float], float]] = { LENGTH_METERS: lambda meters: meters, LENGTH_MILES: lambda miles: miles * 1609.344, LENGTH_YARD: lambda yards: yards * 0.9144, @@ -39,7 +40,7 @@ TO_METERS: dict[str, Callable[[float], float]] = { LENGTH_MILLIMETERS: lambda millimeters: millimeters * 0.001, } -METERS_TO: dict[str, Callable[[float], float]] = { +METERS_TO: dict[UnitLengthT, Callable[[float], float]] = { LENGTH_METERS: lambda meters: meters, LENGTH_MILES: lambda meters: meters * 0.000621371, LENGTH_YARD: lambda meters: meters * 1.09361, @@ -51,7 +52,7 @@ METERS_TO: dict[str, Callable[[float], float]] = { } -def convert(value: float, unit_1: str, unit_2: str) -> float: +def convert(value: float, unit_1: UnitLengthT, unit_2: UnitLengthT) -> float: """Convert one unit of measurement to another.""" if unit_1 not in VALID_UNITS: raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit_1, LENGTH)) diff --git a/homeassistant/util/pressure.py b/homeassistant/util/pressure.py index 24ad3242921..9939b02e141 100644 --- a/homeassistant/util/pressure.py +++ b/homeassistant/util/pressure.py @@ -1,4 +1,6 @@ """Pressure util functions.""" +from __future__ import annotations + from numbers import Number from homeassistant.const import ( @@ -9,11 +11,18 @@ from homeassistant.const import ( PRESSURE_PA, PRESSURE_PSI, UNIT_NOT_RECOGNIZED_TEMPLATE, + UnitPressureT, ) -VALID_UNITS = [PRESSURE_PA, PRESSURE_HPA, PRESSURE_MBAR, PRESSURE_INHG, PRESSURE_PSI] +VALID_UNITS: tuple[UnitPressureT, ...] = ( + PRESSURE_PA, + PRESSURE_HPA, + PRESSURE_MBAR, + PRESSURE_INHG, + PRESSURE_PSI, +) -UNIT_CONVERSION = { +UNIT_CONVERSION: dict[UnitPressureT, float] = { PRESSURE_PA: 1, PRESSURE_HPA: 1 / 100, PRESSURE_MBAR: 1 / 100, @@ -22,7 +31,7 @@ UNIT_CONVERSION = { } -def convert(value: float, unit_1: str, unit_2: str) -> float: +def convert(value: float, unit_1: UnitPressureT, unit_2: UnitPressureT) -> float: """Convert one unit of measurement to another.""" if unit_1 not in VALID_UNITS: raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit_1, PRESSURE)) diff --git a/homeassistant/util/temperature.py b/homeassistant/util/temperature.py index bc3cb4c1017..015f33383d1 100644 --- a/homeassistant/util/temperature.py +++ b/homeassistant/util/temperature.py @@ -5,6 +5,7 @@ from homeassistant.const import ( TEMP_KELVIN, TEMPERATURE, UNIT_NOT_RECOGNIZED_TEMPLATE, + UnitTemperatureT, ) @@ -37,7 +38,10 @@ def celsius_to_kelvin(celsius: float, interval: bool = False) -> float: def convert( - temperature: float, from_unit: str, to_unit: str, interval: bool = False + temperature: float, + from_unit: UnitTemperatureT, + to_unit: UnitTemperatureT, + interval: bool = False, ) -> float: """Convert a temperature from one unit to another.""" if from_unit not in (TEMP_CELSIUS, TEMP_FAHRENHEIT, TEMP_KELVIN): diff --git a/homeassistant/util/unit_system.py b/homeassistant/util/unit_system.py index b5c8c38425a..e49c1895180 100644 --- a/homeassistant/util/unit_system.py +++ b/homeassistant/util/unit_system.py @@ -24,6 +24,13 @@ from homeassistant.const import ( VOLUME, VOLUME_GALLONS, VOLUME_LITERS, + UnitLengthT, + UnitMassT, + UnitPressureT, + UnitT, + UnitTemperatureT, + UnitTypeT, + UnitVolumeT, ) from homeassistant.util import ( distance as distance_util, @@ -36,17 +43,23 @@ from homeassistant.util import ( LENGTH_UNITS = distance_util.VALID_UNITS -MASS_UNITS = [MASS_POUNDS, MASS_OUNCES, MASS_KILOGRAMS, MASS_GRAMS] +MASS_UNITS: tuple[UnitMassT, ...] = ( + MASS_POUNDS, + MASS_OUNCES, + MASS_KILOGRAMS, + MASS_GRAMS, +) PRESSURE_UNITS = pressure_util.VALID_UNITS VOLUME_UNITS = volume_util.VALID_UNITS -TEMPERATURE_UNITS = [TEMP_FAHRENHEIT, TEMP_CELSIUS] +TEMPERATURE_UNITS: tuple[UnitTemperatureT, ...] = (TEMP_FAHRENHEIT, TEMP_CELSIUS) -def is_valid_unit(unit: str, unit_type: str) -> bool: +def is_valid_unit(unit: UnitT, unit_type: UnitTypeT) -> bool: """Check if the unit is valid for it's type.""" + units: tuple[UnitT, ...] if unit_type == LENGTH: units = LENGTH_UNITS elif unit_type == TEMPERATURE: @@ -69,11 +82,11 @@ class UnitSystem: def __init__( self, name: str, - temperature: str, - length: str, - volume: str, - mass: str, - pressure: str, + temperature: UnitTemperatureT, + length: UnitLengthT, + volume: UnitVolumeT, + mass: UnitMassT, + pressure: UnitPressureT, ) -> None: """Initialize the unit system object.""" errors: str = ", ".join( @@ -103,14 +116,14 @@ class UnitSystem: """Determine if this is the metric unit system.""" return self.name == CONF_UNIT_SYSTEM_METRIC - def temperature(self, temperature: float, from_unit: str) -> float: + def temperature(self, temperature: float, from_unit: UnitTemperatureT) -> float: """Convert the given temperature to this unit system.""" if not isinstance(temperature, Number): raise TypeError(f"{temperature!s} is not a numeric value.") return temperature_util.convert(temperature, from_unit, self.temperature_unit) - def length(self, length: float | None, from_unit: str) -> float: + def length(self, length: float | None, from_unit: UnitLengthT) -> float: """Convert the given length to this unit system.""" if not isinstance(length, Number): raise TypeError(f"{length!s} is not a numeric value.") @@ -120,7 +133,7 @@ class UnitSystem: length, from_unit, self.length_unit ) - def pressure(self, pressure: float | None, from_unit: str) -> float: + def pressure(self, pressure: float | None, from_unit: UnitPressureT) -> float: """Convert the given pressure to this unit system.""" if not isinstance(pressure, Number): raise TypeError(f"{pressure!s} is not a numeric value.") @@ -130,7 +143,7 @@ class UnitSystem: pressure, from_unit, self.pressure_unit ) - def volume(self, volume: float | None, from_unit: str) -> float: + def volume(self, volume: float | None, from_unit: UnitVolumeT) -> float: """Convert the given volume to this unit system.""" if not isinstance(volume, Number): raise TypeError(f"{volume!s} is not a numeric value.") @@ -138,7 +151,7 @@ class UnitSystem: # type ignore: https://github.com/python/mypy/issues/7207 return volume_util.convert(volume, from_unit, self.volume_unit) # type: ignore - def as_dict(self) -> dict[str, str]: + def as_dict(self) -> dict[str, UnitT]: """Convert the unit system to a dictionary.""" return { LENGTH: self.length_unit, diff --git a/homeassistant/util/volume.py b/homeassistant/util/volume.py index 5d94f848cd8..ddf2be5a3bc 100644 --- a/homeassistant/util/volume.py +++ b/homeassistant/util/volume.py @@ -1,4 +1,6 @@ """Volume conversion util functions.""" +from __future__ import annotations + from numbers import Number from homeassistant.const import ( @@ -8,9 +10,15 @@ from homeassistant.const import ( VOLUME_GALLONS, VOLUME_LITERS, VOLUME_MILLILITERS, + UnitVolumeT, ) -VALID_UNITS = [VOLUME_LITERS, VOLUME_MILLILITERS, VOLUME_GALLONS, VOLUME_FLUID_OUNCE] +VALID_UNITS: tuple[UnitVolumeT, ...] = ( + VOLUME_LITERS, + VOLUME_MILLILITERS, + VOLUME_GALLONS, + VOLUME_FLUID_OUNCE, +) def __liter_to_gallon(liter: float) -> float: @@ -23,7 +31,7 @@ def __gallon_to_liter(gallon: float) -> float: return gallon * 3.785 -def convert(volume: float, from_unit: str, to_unit: str) -> float: +def convert(volume: float, from_unit: UnitVolumeT, to_unit: UnitVolumeT) -> float: """Convert a temperature from one unit to another.""" if from_unit not in VALID_UNITS: raise ValueError(UNIT_NOT_RECOGNIZED_TEMPLATE.format(from_unit, VOLUME))