mirror of
https://github.com/esphome/esphome.git
synced 2026-10-19 04:32:50 +00:00
Share the entity unit lookup and reject unknown temperature units
This commit is contained in:
@@ -49,16 +49,16 @@ from esphome.const import (
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
CONF_VISUAL,
|
||||
CONF_WEB_SERVER,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
from esphome.core.entity_helpers import (
|
||||
entity_duplicate_validator,
|
||||
entity_temperature_unit,
|
||||
entity_temperature_unit_of_id,
|
||||
queue_entity_register,
|
||||
setup_entity,
|
||||
)
|
||||
from esphome.cpp_generator import MockObj, MockObjClass
|
||||
from esphome.cpp_types import TEMPERATURE_UNITS
|
||||
from esphome.types import ConfigType, SafeExpType
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
@@ -158,30 +158,17 @@ def visual_temperature_step(value: Any) -> ConfigType:
|
||||
return {CONF_TARGET_TEMPERATURE: value, CONF_CURRENT_TEMPERATURE: value}
|
||||
|
||||
|
||||
def temperature_unit_of(config: ConfigType) -> str:
|
||||
"""The unit a climate reports in: ``unit_of_measurement`` when the platform took the option
|
||||
through ``climate_schema``, else Celsius (anova has an unrelated option of that name)."""
|
||||
unit = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
return unit if unit in TEMPERATURE_UNITS else UNIT_CELSIUS
|
||||
|
||||
|
||||
def temperature_unit_of_id(climate_id: ID) -> str:
|
||||
"""The unit of the climate with this id, for actions that take static temperatures."""
|
||||
path = CORE.config.get_path_for_id(climate_id)[:-1]
|
||||
return temperature_unit_of(CORE.config.get_config_for_path(path))
|
||||
|
||||
|
||||
def temperature_in_entity_unit(
|
||||
config: ConfigType, value: tuple[float, str | None]
|
||||
) -> str:
|
||||
"""Render a static action temperature in the unit of the climate the action targets."""
|
||||
unit = temperature_unit_of_id(config[CONF_ID])
|
||||
unit = entity_temperature_unit_of_id(config[CONF_ID])
|
||||
return str(cg.safe_exp(cv.convert_temperature(*value, unit)))
|
||||
|
||||
|
||||
def _visual_temperatures_in_entity_unit(config: ConfigType) -> ConfigType:
|
||||
"""Convert the visual temperatures into the entity's unit."""
|
||||
unit = temperature_unit_of(config)
|
||||
unit = entity_temperature_unit(config)
|
||||
visual = config[CONF_VISUAL]
|
||||
for key in (CONF_MIN_TEMPERATURE, CONF_MAX_TEMPERATURE):
|
||||
if isinstance(value := visual.get(key), tuple):
|
||||
|
||||
@@ -29,6 +29,7 @@ from esphome.const import (
|
||||
CONF_TARGET_TEMPERATURE_LOW,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
)
|
||||
from esphome.core.entity_helpers import entity_temperature_unit
|
||||
from esphome.cpp_types import TEMPERATURE_UNITS
|
||||
from esphome.types import ConfigType
|
||||
|
||||
@@ -200,7 +201,7 @@ def _validate_initial_state(config: ConfigType) -> ConfigType:
|
||||
f"'{CONF_TARGET_HUMIDITY}' requires '{CONF_SUPPORTS_TARGET_HUMIDITY}' to be enabled",
|
||||
path=[CONF_INITIAL_STATE, CONF_TARGET_HUMIDITY],
|
||||
)
|
||||
unit = climate.temperature_unit_of(config)
|
||||
unit = entity_temperature_unit(config)
|
||||
for key in TEMPERATURE_KEYS:
|
||||
if isinstance(value := initial_state.get(key), tuple):
|
||||
initial_state[key] = cv.convert_temperature(*value, unit)
|
||||
|
||||
@@ -15,6 +15,7 @@ from esphome.const import (
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.core.entity_helpers import entity_temperature_unit_of_id
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.cpp_types import TEMPERATURE_UNITS
|
||||
from esphome.types import ConfigType
|
||||
@@ -154,7 +155,7 @@ async def water_heater_template_publish_to_code(
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
# A static temperature is in the unit of the water heater it is published to
|
||||
unit = water_heater.temperature_unit_of_id(config[CONF_ID])
|
||||
unit = entity_temperature_unit_of_id(config[CONF_ID])
|
||||
for key, setter in (
|
||||
(CONF_CURRENT_TEMPERATURE, var.set_current_temperature),
|
||||
(CONF_TARGET_TEMPERATURE, var.set_target_temperature),
|
||||
|
||||
@@ -8,16 +8,15 @@ from esphome.const import (
|
||||
CONF_MIN_TEMPERATURE,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
CONF_VISUAL,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
from esphome.core.entity_helpers import (
|
||||
entity_duplicate_validator,
|
||||
entity_temperature_unit,
|
||||
queue_entity_register,
|
||||
setup_entity,
|
||||
)
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
from esphome.cpp_types import TEMPERATURE_UNITS
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@dhoeben"]
|
||||
@@ -45,22 +44,9 @@ WATER_HEATER_MODES = {
|
||||
validate_water_heater_mode = cv.enum(WATER_HEATER_MODES, upper=True)
|
||||
|
||||
|
||||
def temperature_unit_of(config: ConfigType) -> str:
|
||||
"""The unit a water heater reports in: ``unit_of_measurement`` when the platform took the
|
||||
option through ``water_heater_schema``, else Celsius."""
|
||||
unit = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
return unit if unit in TEMPERATURE_UNITS else UNIT_CELSIUS
|
||||
|
||||
|
||||
def temperature_unit_of_id(water_heater_id: ID) -> str:
|
||||
"""The unit of the water heater with this id, for actions that take static temperatures."""
|
||||
path = CORE.config.get_path_for_id(water_heater_id)[:-1]
|
||||
return temperature_unit_of(CORE.config.get_config_for_path(path))
|
||||
|
||||
|
||||
def _visual_temperatures_in_entity_unit(config: ConfigType) -> ConfigType:
|
||||
"""Convert the visual temperatures into the entity's unit."""
|
||||
unit = temperature_unit_of(config)
|
||||
unit = entity_temperature_unit(config)
|
||||
visual = config[CONF_VISUAL]
|
||||
for key in (CONF_MIN_TEMPERATURE, CONF_MAX_TEMPERATURE):
|
||||
if isinstance(value := visual.get(key), tuple):
|
||||
|
||||
@@ -1472,6 +1472,8 @@ def _temperature_to_celsius(value: float, unit: str, delta: bool) -> float:
|
||||
return value * (5 / 9) if delta else (value - 32) * (5 / 9)
|
||||
if unit == UNIT_KELVIN:
|
||||
return value if delta else value - 273.15
|
||||
if unit != UNIT_CELSIUS:
|
||||
raise ValueError(f"Unknown temperature unit {unit!r}")
|
||||
return value
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from esphome.const import (
|
||||
CONF_INTERNAL,
|
||||
CONF_NAME,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
|
||||
from esphome.core.config import (
|
||||
@@ -24,7 +25,7 @@ from esphome.core.config import (
|
||||
UNIT_OF_MEASUREMENT_MAX_LENGTH,
|
||||
)
|
||||
from esphome.cpp_generator import MockObj, RawStatement, add, get_variable
|
||||
from esphome.cpp_types import App
|
||||
from esphome.cpp_types import TEMPERATURE_UNITS, App
|
||||
import esphome.final_validate as fv
|
||||
from esphome.helpers import (
|
||||
cpp_string_escape,
|
||||
@@ -550,6 +551,19 @@ def inherit_property_from(property_to_inherit, parent_id_property, transform=Non
|
||||
return inherit_property
|
||||
|
||||
|
||||
def entity_temperature_unit(config: ConfigType) -> str:
|
||||
"""The unit a climate or water heater reports in: ``unit_of_measurement`` when the platform took
|
||||
the option through its schema factory, else Celsius (anova has an unrelated option of that name)."""
|
||||
unit = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
return unit if unit in TEMPERATURE_UNITS else UNIT_CELSIUS
|
||||
|
||||
|
||||
def entity_temperature_unit_of_id(entity_id: ID) -> str:
|
||||
"""The unit of the entity with this id, for actions that take static temperatures."""
|
||||
path = CORE.config.get_path_for_id(entity_id)[:-1]
|
||||
return entity_temperature_unit(CORE.config.get_config_for_path(path))
|
||||
|
||||
|
||||
def entity_duplicate_validator(platform: str) -> Callable[[ConfigType], ConfigType]:
|
||||
"""Create a validator function to check for duplicate entity names.
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import pytest
|
||||
|
||||
from esphome.components.climate import (
|
||||
_visual_temperatures_in_entity_unit,
|
||||
temperature_unit_of,
|
||||
visual_temperature_step,
|
||||
)
|
||||
from esphome.const import (
|
||||
@@ -18,6 +17,7 @@ from esphome.const import (
|
||||
UNIT_CELSIUS,
|
||||
UNIT_FAHRENHEIT,
|
||||
)
|
||||
from esphome.core.entity_helpers import entity_temperature_unit
|
||||
|
||||
|
||||
def test_visual_temperature_step_scalar_applies_to_both() -> None:
|
||||
@@ -67,9 +67,9 @@ def test_visual_temperatures_follow_unit_of_measurement() -> None:
|
||||
|
||||
def test_platform_with_its_own_unit_option_keeps_celsius() -> None:
|
||||
# anova validates unit_of_measurement as c|f for the device, not for the entity
|
||||
assert temperature_unit_of({CONF_UNIT_OF_MEASUREMENT: "f"}) == UNIT_CELSIUS
|
||||
assert entity_temperature_unit({CONF_UNIT_OF_MEASUREMENT: "f"}) == UNIT_CELSIUS
|
||||
assert (
|
||||
temperature_unit_of({CONF_UNIT_OF_MEASUREMENT: UNIT_FAHRENHEIT})
|
||||
entity_temperature_unit({CONF_UNIT_OF_MEASUREMENT: UNIT_FAHRENHEIT})
|
||||
== UNIT_FAHRENHEIT
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user