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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions aioesphomeapi/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,13 @@ message CameraImageRequest {
bool stream = 2;
}

// ==================== TEMPERATURE UNIT ====================
enum TemperatureUnit {
TEMPERATURE_UNIT_CELSIUS = 0;
TEMPERATURE_UNIT_FAHRENHEIT = 1;
TEMPERATURE_UNIT_KELVIN = 2;
}

// ==================== CLIMATE ====================
enum ClimateMode {
CLIMATE_MODE_OFF = 0;
Expand Down Expand Up @@ -1110,6 +1117,7 @@ message ListEntitiesClimateResponse {
float visual_max_humidity = 25;
uint32 device_id = 26 [(field_ifdef) = "USE_DEVICES"];
uint32 feature_flags = 27;
Comment thread
bdraco marked this conversation as resolved.
TemperatureUnit temperature_unit = 28;
}
message ClimateStateResponse {
option (id) = 47;
Expand Down Expand Up @@ -1203,6 +1211,7 @@ message ListEntitiesWaterHeaterResponse {
repeated WaterHeaterMode supported_modes = 11 [(container_pointer_no_template) = "water_heater::WaterHeaterModeMask"];
// Bitmask of WaterHeaterFeature flags
uint32 supported_features = 12;
TemperatureUnit temperature_unit = 13;
}

message WaterHeaterStateResponse {
Expand Down
610 changes: 306 additions & 304 deletions aioesphomeapi/api_pb2.py

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion aioesphomeapi/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,12 @@ class ClimatePreset(APIIntEnum):
ACTIVITY = 7


class TemperatureUnit(APIIntEnum):
CELSIUS = 0
FAHRENHEIT = 1
KELVIN = 2


@_frozen_dataclass_decorator
class ClimateInfo(EntityInfo):
feature_flags: int = 0
Expand All @@ -712,6 +718,10 @@ class ClimateInfo(EntityInfo):
visual_current_temperature_step: float = converter_field(
default=0.0, converter=fix_float_single_double_conversion
)
temperature_unit: TemperatureUnit | None = converter_field(
default=TemperatureUnit.CELSIUS,
converter=TemperatureUnit.convert,
)
legacy_supports_away: bool = False
supports_action: bool = False
supported_fan_modes: list[ClimateFanMode] = converter_field(
Expand Down Expand Up @@ -1178,7 +1188,10 @@ class WaterHeaterInfo(EntityInfo):
target_temperature_step: float = converter_field(
default=0.0, converter=fix_float_single_double_conversion
)

temperature_unit: TemperatureUnit | None = converter_field(
default=TemperatureUnit.CELSIUS,
converter=TemperatureUnit.convert,
)
supported_modes: list[WaterHeaterMode] = converter_field(
default_factory=list, converter=WaterHeaterMode.convert_list
)
Expand Down
55 changes: 55 additions & 0 deletions tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
SupportsResponseType,
SwitchInfo,
SwitchState,
TemperatureUnit,
TextInfo,
TextSensorInfo,
TextSensorState,
Expand Down Expand Up @@ -2227,3 +2228,57 @@ def test_serial_proxy_request_response_conversion() -> None:
assert model2.type == SerialProxyRequestType.FLUSH
assert model2.status == SerialProxyStatus.TIMEOUT
assert model2.error_message == "timeout"


def test_climate_info_missing_temperature_unit_defaults_to_celsius() -> None:
pb = ListEntitiesClimateResponse()
info = ClimateInfo.from_pb(pb)
assert info.temperature_unit == TemperatureUnit.CELSIUS


def test_water_heater_info_missing_temperature_unit_defaults_to_celsius() -> None:
pb = ListEntitiesWaterHeaterResponse()
info = WaterHeaterInfo.from_pb(pb)
assert info.temperature_unit == TemperatureUnit.CELSIUS


@pytest.mark.parametrize(
"unit",
[
TemperatureUnit.CELSIUS,
TemperatureUnit.FAHRENHEIT,
TemperatureUnit.KELVIN,
],
)
def test_climate_info_temperature_unit_roundtrip(unit: TemperatureUnit) -> None:
pb = ListEntitiesClimateResponse(temperature_unit=unit)
info = ClimateInfo.from_pb(pb)
assert info.temperature_unit is unit
info2 = ClimateInfo.from_dict(info.to_dict())
assert info2.temperature_unit is unit


@pytest.mark.parametrize(
"unit",
[
TemperatureUnit.CELSIUS,
TemperatureUnit.FAHRENHEIT,
TemperatureUnit.KELVIN,
],
)
def test_water_heater_info_temperature_unit_roundtrip(unit: TemperatureUnit) -> None:
pb = ListEntitiesWaterHeaterResponse(temperature_unit=unit)
info = WaterHeaterInfo.from_pb(pb)
assert info.temperature_unit is unit
info2 = WaterHeaterInfo.from_dict(info.to_dict())
assert info2.temperature_unit is unit


def test_climate_info_unknown_temperature_unit_converts_to_none() -> None:
info = ClimateInfo(temperature_unit=999)
assert info.temperature_unit is None


def test_water_heater_info_unknown_temperature_unit_converts_to_none() -> None:
info = WaterHeaterInfo(temperature_unit=999)
assert info.temperature_unit is None
Loading