1 """Unit system helper class and methods."""
3 from __future__
import annotations
5 from numbers
import Number
6 from typing
import TYPE_CHECKING, Final
8 import voluptuous
as vol
11 ACCUMULATED_PRECIPITATION,
17 UNIT_NOT_RECOGNIZED_TEMPLATE,
23 UnitOfPrecipitationDepth,
31 from .unit_conversion
import (
43 _CONF_UNIT_SYSTEM_IMPERIAL: Final =
"imperial"
44 _CONF_UNIT_SYSTEM_METRIC: Final =
"metric"
45 _CONF_UNIT_SYSTEM_US_CUSTOMARY: Final =
"us_customary"
47 AREA_UNITS = AreaConverter.VALID_UNITS
49 LENGTH_UNITS = DistanceConverter.VALID_UNITS
51 MASS_UNITS: set[str] = {
58 PRESSURE_UNITS = PressureConverter.VALID_UNITS
60 VOLUME_UNITS = VolumeConverter.VALID_UNITS
62 WIND_SPEED_UNITS = SpeedConverter.VALID_UNITS
64 TEMPERATURE_UNITS: set[str] = {UnitOfTemperature.FAHRENHEIT, UnitOfTemperature.CELSIUS}
66 _VALID_BY_TYPE: dict[str, set[str] | set[str |
None]] = {
68 ACCUMULATED_PRECIPITATION: LENGTH_UNITS,
69 WIND_SPEED: WIND_SPEED_UNITS,
70 TEMPERATURE: TEMPERATURE_UNITS,
73 PRESSURE: PRESSURE_UNITS,
79 """Check if the unit is valid for it's type."""
80 if units := _VALID_BY_TYPE.get(unit_type):
86 """A container for units of measure."""
92 accumulated_precipitation: UnitOfPrecipitationDepth,
94 conversions: dict[tuple[SensorDeviceClass | str |
None, str |
None], str],
97 pressure: UnitOfPressure,
98 temperature: UnitOfTemperature,
100 wind_speed: UnitOfSpeed,
102 """Initialize the unit system object."""
103 errors: str =
", ".join(
104 UNIT_NOT_RECOGNIZED_TEMPLATE.format(unit, unit_type)
105 for unit, unit_type
in (
106 (accumulated_precipitation, ACCUMULATED_PRECIPITATION),
108 (temperature, TEMPERATURE),
110 (wind_speed, WIND_SPEED),
113 (pressure, PRESSURE),
119 raise ValueError(errors)
132 def temperature(self, temperature: float, from_unit: str) -> float:
133 """Convert the given temperature to this unit system."""
134 if not isinstance(temperature, Number):
135 raise TypeError(f
"{temperature!s} is not a numeric value.")
137 return TemperatureConverter.convert(
141 def length(self, length: float |
None, from_unit: str) -> float:
142 """Convert the given length to this unit system."""
143 if not isinstance(length, Number):
144 raise TypeError(f
"{length!s} is not a numeric value.")
147 return DistanceConverter.convert(
152 """Convert the given length to this unit system."""
153 if not isinstance(precip, Number):
154 raise TypeError(f
"{precip!s} is not a numeric value.")
157 return DistanceConverter.convert(
161 def area(self, area: float |
None, from_unit: str) -> float:
162 """Convert the given area to this unit system."""
163 if not isinstance(area, Number):
164 raise TypeError(f
"{area!s} is not a numeric value.")
167 return AreaConverter.convert(
171 def pressure(self, pressure: float |
None, from_unit: str) -> float:
172 """Convert the given pressure to this unit system."""
173 if not isinstance(pressure, Number):
174 raise TypeError(f
"{pressure!s} is not a numeric value.")
177 return PressureConverter.convert(
181 def wind_speed(self, wind_speed: float |
None, from_unit: str) -> float:
182 """Convert the given wind_speed to this unit system."""
183 if not isinstance(wind_speed, Number):
184 raise TypeError(f
"{wind_speed!s} is not a numeric value.")
187 return SpeedConverter.convert(
191 def volume(self, volume: float |
None, from_unit: str) -> float:
192 """Convert the given volume to this unit system."""
193 if not isinstance(volume, Number):
194 raise TypeError(f
"{volume!s} is not a numeric value.")
197 return VolumeConverter.convert(
202 """Convert the unit system to a dictionary."""
216 device_class: SensorDeviceClass | str |
None,
217 original_unit: str |
None,
219 """Return converted unit given a device class or an original unit."""
220 return self.
_conversions_conversions.
get((device_class, original_unit))
224 """Get unit system based on key."""
225 if key == _CONF_UNIT_SYSTEM_US_CUSTOMARY:
226 return US_CUSTOMARY_SYSTEM
227 if key == _CONF_UNIT_SYSTEM_METRIC:
229 raise ValueError(f
"`{key}` is not a valid unit system key")
233 """Convert deprecated unit system."""
235 if value == _CONF_UNIT_SYSTEM_IMPERIAL:
237 return _CONF_UNIT_SYSTEM_US_CUSTOMARY
241 validate_unit_system = vol.All(
243 _deprecated_unit_system,
244 vol.Any(_CONF_UNIT_SYSTEM_METRIC, _CONF_UNIT_SYSTEM_US_CUSTOMARY),
248 _CONF_UNIT_SYSTEM_METRIC,
249 accumulated_precipitation=UnitOfPrecipitationDepth.MILLIMETERS,
253 (
"atmospheric_pressure", unit): UnitOfPressure.HPA
254 for unit
in UnitOfPressure
255 if unit != UnitOfPressure.HPA
258 (
"area", UnitOfArea.SQUARE_INCHES): UnitOfArea.SQUARE_CENTIMETERS,
259 (
"area", UnitOfArea.SQUARE_FEET): UnitOfArea.SQUARE_METERS,
260 (
"area", UnitOfArea.SQUARE_MILES): UnitOfArea.SQUARE_KILOMETERS,
261 (
"area", UnitOfArea.SQUARE_YARDS): UnitOfArea.SQUARE_METERS,
262 (
"area", UnitOfArea.ACRES): UnitOfArea.HECTARES,
264 (
"distance", UnitOfLength.FEET): UnitOfLength.METERS,
265 (
"distance", UnitOfLength.INCHES): UnitOfLength.MILLIMETERS,
266 (
"distance", UnitOfLength.MILES): UnitOfLength.KILOMETERS,
267 (
"distance", UnitOfLength.NAUTICAL_MILES): UnitOfLength.KILOMETERS,
268 (
"distance", UnitOfLength.YARDS): UnitOfLength.METERS,
270 (
"gas", UnitOfVolume.CENTUM_CUBIC_FEET): UnitOfVolume.CUBIC_METERS,
271 (
"gas", UnitOfVolume.CUBIC_FEET): UnitOfVolume.CUBIC_METERS,
273 (
"precipitation", UnitOfLength.INCHES): UnitOfLength.MILLIMETERS,
276 "precipitation_intensity",
277 UnitOfVolumetricFlux.INCHES_PER_DAY,
278 ): UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
280 "precipitation_intensity",
281 UnitOfVolumetricFlux.INCHES_PER_HOUR,
282 ): UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
284 (
"pressure", UnitOfPressure.PSI): UnitOfPressure.KPA,
285 (
"pressure", UnitOfPressure.INHG): UnitOfPressure.HPA,
287 (
"speed", UnitOfSpeed.FEET_PER_SECOND): UnitOfSpeed.KILOMETERS_PER_HOUR,
288 (
"speed", UnitOfSpeed.INCHES_PER_SECOND): UnitOfSpeed.MILLIMETERS_PER_SECOND,
289 (
"speed", UnitOfSpeed.MILES_PER_HOUR): UnitOfSpeed.KILOMETERS_PER_HOUR,
292 UnitOfVolumetricFlux.INCHES_PER_DAY,
293 ): UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
296 UnitOfVolumetricFlux.INCHES_PER_HOUR,
297 ): UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
299 (
"volume", UnitOfVolume.CENTUM_CUBIC_FEET): UnitOfVolume.CUBIC_METERS,
300 (
"volume", UnitOfVolume.CUBIC_FEET): UnitOfVolume.CUBIC_METERS,
301 (
"volume", UnitOfVolume.FLUID_OUNCES): UnitOfVolume.MILLILITERS,
302 (
"volume", UnitOfVolume.GALLONS): UnitOfVolume.LITERS,
304 (
"water", UnitOfVolume.CENTUM_CUBIC_FEET): UnitOfVolume.CUBIC_METERS,
305 (
"water", UnitOfVolume.CUBIC_FEET): UnitOfVolume.CUBIC_METERS,
306 (
"water", UnitOfVolume.GALLONS): UnitOfVolume.LITERS,
309 (
"wind_speed", unit): UnitOfSpeed.KILOMETERS_PER_HOUR
310 for unit
in UnitOfSpeed
311 if unit
not in (UnitOfSpeed.KILOMETERS_PER_HOUR, UnitOfSpeed.KNOTS)
314 area=UnitOfArea.SQUARE_METERS,
315 length=UnitOfLength.KILOMETERS,
316 mass=UnitOfMass.GRAMS,
317 pressure=UnitOfPressure.PA,
318 temperature=UnitOfTemperature.CELSIUS,
319 volume=UnitOfVolume.LITERS,
320 wind_speed=UnitOfSpeed.METERS_PER_SECOND,
324 _CONF_UNIT_SYSTEM_US_CUSTOMARY,
325 accumulated_precipitation=UnitOfPrecipitationDepth.INCHES,
329 (
"atmospheric_pressure", unit): UnitOfPressure.INHG
330 for unit
in UnitOfPressure
331 if unit != UnitOfPressure.INHG
334 (
"area", UnitOfArea.SQUARE_METERS): UnitOfArea.SQUARE_FEET,
335 (
"area", UnitOfArea.SQUARE_CENTIMETERS): UnitOfArea.SQUARE_INCHES,
336 (
"area", UnitOfArea.SQUARE_MILLIMETERS): UnitOfArea.SQUARE_INCHES,
337 (
"area", UnitOfArea.SQUARE_KILOMETERS): UnitOfArea.SQUARE_MILES,
338 (
"area", UnitOfArea.HECTARES): UnitOfArea.ACRES,
340 (
"distance", UnitOfLength.CENTIMETERS): UnitOfLength.INCHES,
341 (
"distance", UnitOfLength.KILOMETERS): UnitOfLength.MILES,
342 (
"distance", UnitOfLength.METERS): UnitOfLength.FEET,
343 (
"distance", UnitOfLength.MILLIMETERS): UnitOfLength.INCHES,
345 (
"gas", UnitOfVolume.CUBIC_METERS): UnitOfVolume.CUBIC_FEET,
347 (
"precipitation", UnitOfLength.CENTIMETERS): UnitOfLength.INCHES,
348 (
"precipitation", UnitOfLength.MILLIMETERS): UnitOfLength.INCHES,
351 "precipitation_intensity",
352 UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
353 ): UnitOfVolumetricFlux.INCHES_PER_DAY,
355 "precipitation_intensity",
356 UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
357 ): UnitOfVolumetricFlux.INCHES_PER_HOUR,
359 (
"pressure", UnitOfPressure.MBAR): UnitOfPressure.PSI,
360 (
"pressure", UnitOfPressure.CBAR): UnitOfPressure.PSI,
361 (
"pressure", UnitOfPressure.BAR): UnitOfPressure.PSI,
362 (
"pressure", UnitOfPressure.PA): UnitOfPressure.PSI,
363 (
"pressure", UnitOfPressure.HPA): UnitOfPressure.PSI,
364 (
"pressure", UnitOfPressure.KPA): UnitOfPressure.PSI,
365 (
"pressure", UnitOfPressure.MMHG): UnitOfPressure.INHG,
367 (
"speed", UnitOfSpeed.METERS_PER_SECOND): UnitOfSpeed.MILES_PER_HOUR,
368 (
"speed", UnitOfSpeed.MILLIMETERS_PER_SECOND): UnitOfSpeed.INCHES_PER_SECOND,
369 (
"speed", UnitOfSpeed.KILOMETERS_PER_HOUR): UnitOfSpeed.MILES_PER_HOUR,
372 UnitOfVolumetricFlux.MILLIMETERS_PER_DAY,
373 ): UnitOfVolumetricFlux.INCHES_PER_DAY,
376 UnitOfVolumetricFlux.MILLIMETERS_PER_HOUR,
377 ): UnitOfVolumetricFlux.INCHES_PER_HOUR,
379 (
"volume", UnitOfVolume.CUBIC_METERS): UnitOfVolume.CUBIC_FEET,
380 (
"volume", UnitOfVolume.LITERS): UnitOfVolume.GALLONS,
381 (
"volume", UnitOfVolume.MILLILITERS): UnitOfVolume.FLUID_OUNCES,
383 (
"water", UnitOfVolume.CUBIC_METERS): UnitOfVolume.CUBIC_FEET,
384 (
"water", UnitOfVolume.LITERS): UnitOfVolume.GALLONS,
387 (
"wind_speed", unit): UnitOfSpeed.MILES_PER_HOUR
388 for unit
in UnitOfSpeed
389 if unit
not in (UnitOfSpeed.KNOTS, UnitOfSpeed.MILES_PER_HOUR)
392 area=UnitOfArea.SQUARE_FEET,
393 length=UnitOfLength.MILES,
394 mass=UnitOfMass.POUNDS,
395 pressure=UnitOfPressure.PSI,
396 temperature=UnitOfTemperature.FAHRENHEIT,
397 volume=UnitOfVolume.GALLONS,
398 wind_speed=UnitOfSpeed.MILES_PER_HOUR,
401 IMPERIAL_SYSTEM = US_CUSTOMARY_SYSTEM
402 """IMPERIAL_SYSTEM is deprecated. Please use US_CUSTOMARY_SYSTEM instead."""
float volume(self, float|None volume, str from_unit)
float temperature(self, float temperature, str from_unit)
accumulated_precipitation_unit
float accumulated_precipitation(self, float|None precip, str from_unit)
float length(self, float|None length, str from_unit)
float pressure(self, float|None pressure, str from_unit)
float wind_speed(self, float|None wind_speed, str from_unit)
None __init__(self, str name, *UnitOfPrecipitationDepth accumulated_precipitation, UnitOfArea area, dict[tuple[SensorDeviceClass|str|None, str|None], str] conversions, UnitOfLength length, UnitOfMass mass, UnitOfPressure pressure, UnitOfTemperature temperature, UnitOfVolume volume, UnitOfSpeed wind_speed)
str|None get_converted_unit(self, SensorDeviceClass|str|None device_class, str|None original_unit)
float area(self, float|None area, str from_unit)
dict[str, str] as_dict(self)
web.Response get(self, web.Request request, str config_key)
UnitSystem get_unit_system(str key)
str _deprecated_unit_system(str value)
bool _is_valid_unit(str unit, str unit_type)