mirror of
https://github.com/esphome/esphome.git
synced 2025-11-16 14:55:50 +00:00
Merge branch 'integration' into memory_api
This commit is contained in:
@@ -945,6 +945,10 @@ async def to_code(config):
|
|||||||
cg.add(var.set_humidity_hysteresis(config[CONF_HUMIDITY_HYSTERESIS]))
|
cg.add(var.set_humidity_hysteresis(config[CONF_HUMIDITY_HYSTERESIS]))
|
||||||
|
|
||||||
if CONF_PRESET in config:
|
if CONF_PRESET in config:
|
||||||
|
# Separate standard and custom presets, and build preset config variables
|
||||||
|
standard_presets: list[tuple[cg.MockObj, cg.MockObj]] = []
|
||||||
|
custom_presets: list[tuple[str, cg.MockObj]] = []
|
||||||
|
|
||||||
for preset_config in config[CONF_PRESET]:
|
for preset_config in config[CONF_PRESET]:
|
||||||
name = preset_config[CONF_NAME]
|
name = preset_config[CONF_NAME]
|
||||||
standard_preset = None
|
standard_preset = None
|
||||||
@@ -987,9 +991,39 @@ async def to_code(config):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if standard_preset is not None:
|
if standard_preset is not None:
|
||||||
cg.add(var.set_preset_config(standard_preset, preset_target_variable))
|
standard_presets.append((standard_preset, preset_target_variable))
|
||||||
else:
|
else:
|
||||||
cg.add(var.set_custom_preset_config(name, preset_target_variable))
|
custom_presets.append((name, preset_target_variable))
|
||||||
|
|
||||||
|
# Build initializer list for standard presets
|
||||||
|
if standard_presets:
|
||||||
|
cg.add(
|
||||||
|
var.set_preset_config(
|
||||||
|
[
|
||||||
|
cg.StructInitializer(
|
||||||
|
thermostat_ns.struct("ThermostatPresetEntry"),
|
||||||
|
("preset", preset),
|
||||||
|
("config", preset_var),
|
||||||
|
)
|
||||||
|
for preset, preset_var in standard_presets
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build initializer list for custom presets
|
||||||
|
if custom_presets:
|
||||||
|
cg.add(
|
||||||
|
var.set_custom_preset_config(
|
||||||
|
[
|
||||||
|
cg.StructInitializer(
|
||||||
|
thermostat_ns.struct("ThermostatCustomPresetEntry"),
|
||||||
|
("name", cg.RawExpression(f'"{name}"')),
|
||||||
|
("config", preset_var),
|
||||||
|
)
|
||||||
|
for name, preset_var in custom_presets
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if CONF_DEFAULT_PRESET in config:
|
if CONF_DEFAULT_PRESET in config:
|
||||||
default_preset_name = config[CONF_DEFAULT_PRESET]
|
default_preset_name = config[CONF_DEFAULT_PRESET]
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ void ThermostatClimate::setup() {
|
|||||||
if (use_default_preset) {
|
if (use_default_preset) {
|
||||||
if (this->default_preset_ != climate::ClimatePreset::CLIMATE_PRESET_NONE) {
|
if (this->default_preset_ != climate::ClimatePreset::CLIMATE_PRESET_NONE) {
|
||||||
this->change_preset_(this->default_preset_);
|
this->change_preset_(this->default_preset_);
|
||||||
} else if (!this->default_custom_preset_.empty()) {
|
} else if (this->default_custom_preset_ != nullptr) {
|
||||||
this->change_custom_preset_(this->default_custom_preset_.c_str());
|
this->change_custom_preset_(this->default_custom_preset_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,16 +319,16 @@ climate::ClimateTraits ThermostatClimate::traits() {
|
|||||||
if (this->supports_swing_mode_vertical_)
|
if (this->supports_swing_mode_vertical_)
|
||||||
traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
|
traits.add_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
|
||||||
|
|
||||||
for (auto &it : this->preset_config_) {
|
for (const auto &entry : this->preset_config_) {
|
||||||
traits.add_supported_preset(it.first);
|
traits.add_supported_preset(entry.preset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract custom preset names from the custom_preset_config_ map
|
// Extract custom preset names from the custom_preset_config_ vector
|
||||||
if (!this->custom_preset_config_.empty()) {
|
if (!this->custom_preset_config_.empty()) {
|
||||||
std::vector<const char *> custom_preset_names;
|
std::vector<const char *> custom_preset_names;
|
||||||
custom_preset_names.reserve(this->custom_preset_config_.size());
|
custom_preset_names.reserve(this->custom_preset_config_.size());
|
||||||
for (const auto &it : this->custom_preset_config_) {
|
for (const auto &entry : this->custom_preset_config_) {
|
||||||
custom_preset_names.push_back(it.first.c_str());
|
custom_preset_names.push_back(entry.name);
|
||||||
}
|
}
|
||||||
traits.set_supported_custom_presets(custom_preset_names);
|
traits.set_supported_custom_presets(custom_preset_names);
|
||||||
}
|
}
|
||||||
@@ -1154,12 +1154,18 @@ void ThermostatClimate::dump_preset_config_(const char *preset_name, const Therm
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ThermostatClimate::change_preset_(climate::ClimatePreset preset) {
|
void ThermostatClimate::change_preset_(climate::ClimatePreset preset) {
|
||||||
auto config = this->preset_config_.find(preset);
|
// Linear search through preset configurations
|
||||||
|
const ThermostatClimateTargetTempConfig *config = nullptr;
|
||||||
|
for (const auto &entry : this->preset_config_) {
|
||||||
|
if (entry.preset == preset) {
|
||||||
|
config = &entry.config;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config != this->preset_config_.end()) {
|
if (config != nullptr) {
|
||||||
ESP_LOGV(TAG, "Preset %s requested", LOG_STR_ARG(climate::climate_preset_to_string(preset)));
|
ESP_LOGV(TAG, "Preset %s requested", LOG_STR_ARG(climate::climate_preset_to_string(preset)));
|
||||||
if (this->change_preset_internal_(config->second) || (!this->preset.has_value()) ||
|
if (this->change_preset_internal_(*config) || (!this->preset.has_value()) || this->preset.value() != preset) {
|
||||||
this->preset.value() != preset) {
|
|
||||||
// Fire any preset changed trigger if defined
|
// Fire any preset changed trigger if defined
|
||||||
Trigger<> *trig = this->preset_change_trigger_;
|
Trigger<> *trig = this->preset_change_trigger_;
|
||||||
this->set_preset_(preset);
|
this->set_preset_(preset);
|
||||||
@@ -1178,11 +1184,18 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ThermostatClimate::change_custom_preset_(const char *custom_preset) {
|
void ThermostatClimate::change_custom_preset_(const char *custom_preset) {
|
||||||
auto config = this->custom_preset_config_.find(custom_preset);
|
// Linear search through custom preset configurations
|
||||||
|
const ThermostatClimateTargetTempConfig *config = nullptr;
|
||||||
|
for (const auto &entry : this->custom_preset_config_) {
|
||||||
|
if (strcmp(entry.name, custom_preset) == 0) {
|
||||||
|
config = &entry.config;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config != this->custom_preset_config_.end()) {
|
if (config != nullptr) {
|
||||||
ESP_LOGV(TAG, "Custom preset %s requested", custom_preset);
|
ESP_LOGV(TAG, "Custom preset %s requested", custom_preset);
|
||||||
if (this->change_preset_internal_(config->second) || !this->has_custom_preset() ||
|
if (this->change_preset_internal_(*config) || !this->has_custom_preset() ||
|
||||||
strcmp(this->get_custom_preset(), custom_preset) != 0) {
|
strcmp(this->get_custom_preset(), custom_preset) != 0) {
|
||||||
// Fire any preset changed trigger if defined
|
// Fire any preset changed trigger if defined
|
||||||
Trigger<> *trig = this->preset_change_trigger_;
|
Trigger<> *trig = this->preset_change_trigger_;
|
||||||
@@ -1247,14 +1260,12 @@ bool ThermostatClimate::change_preset_internal_(const ThermostatClimateTargetTem
|
|||||||
return something_changed;
|
return something_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThermostatClimate::set_preset_config(climate::ClimatePreset preset,
|
void ThermostatClimate::set_preset_config(std::initializer_list<PresetEntry> presets) {
|
||||||
const ThermostatClimateTargetTempConfig &config) {
|
this->preset_config_ = presets;
|
||||||
this->preset_config_[preset] = config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThermostatClimate::set_custom_preset_config(const std::string &name,
|
void ThermostatClimate::set_custom_preset_config(std::initializer_list<CustomPresetEntry> presets) {
|
||||||
const ThermostatClimateTargetTempConfig &config) {
|
this->custom_preset_config_ = presets;
|
||||||
this->custom_preset_config_[name] = config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThermostatClimate::ThermostatClimate()
|
ThermostatClimate::ThermostatClimate()
|
||||||
@@ -1293,8 +1304,16 @@ ThermostatClimate::ThermostatClimate()
|
|||||||
humidity_control_humidify_action_trigger_(new Trigger<>()),
|
humidity_control_humidify_action_trigger_(new Trigger<>()),
|
||||||
humidity_control_off_action_trigger_(new Trigger<>()) {}
|
humidity_control_off_action_trigger_(new Trigger<>()) {}
|
||||||
|
|
||||||
void ThermostatClimate::set_default_preset(const std::string &custom_preset) {
|
void ThermostatClimate::set_default_preset(const char *custom_preset) {
|
||||||
this->default_custom_preset_ = custom_preset;
|
// Find the preset in custom_preset_config_ and store pointer from there
|
||||||
|
for (const auto &entry : this->custom_preset_config_) {
|
||||||
|
if (strcmp(entry.name, custom_preset) == 0) {
|
||||||
|
this->default_custom_preset_ = entry.name;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If not found, it will be caught during validation
|
||||||
|
this->default_custom_preset_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThermostatClimate::set_default_preset(climate::ClimatePreset preset) { this->default_preset_ = preset; }
|
void ThermostatClimate::set_default_preset(climate::ClimatePreset preset) { this->default_preset_ = preset; }
|
||||||
@@ -1605,19 +1624,22 @@ void ThermostatClimate::dump_config() {
|
|||||||
|
|
||||||
if (!this->preset_config_.empty()) {
|
if (!this->preset_config_.empty()) {
|
||||||
ESP_LOGCONFIG(TAG, " Supported PRESETS:");
|
ESP_LOGCONFIG(TAG, " Supported PRESETS:");
|
||||||
for (auto &it : this->preset_config_) {
|
for (const auto &entry : this->preset_config_) {
|
||||||
const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(it.first));
|
const auto *preset_name = LOG_STR_ARG(climate::climate_preset_to_string(entry.preset));
|
||||||
ESP_LOGCONFIG(TAG, " %s:%s", preset_name, it.first == this->default_preset_ ? " (default)" : "");
|
ESP_LOGCONFIG(TAG, " %s:%s", preset_name, entry.preset == this->default_preset_ ? " (default)" : "");
|
||||||
this->dump_preset_config_(preset_name, it.second);
|
this->dump_preset_config_(preset_name, entry.config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->custom_preset_config_.empty()) {
|
if (!this->custom_preset_config_.empty()) {
|
||||||
ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS:");
|
ESP_LOGCONFIG(TAG, " Supported CUSTOM PRESETS:");
|
||||||
for (auto &it : this->custom_preset_config_) {
|
for (const auto &entry : this->custom_preset_config_) {
|
||||||
const auto *preset_name = it.first.c_str();
|
const auto *preset_name = entry.name;
|
||||||
ESP_LOGCONFIG(TAG, " %s:%s", preset_name, it.first == this->default_custom_preset_ ? " (default)" : "");
|
ESP_LOGCONFIG(TAG, " %s:%s", preset_name,
|
||||||
this->dump_preset_config_(preset_name, it.second);
|
(this->default_custom_preset_ != nullptr && strcmp(entry.name, this->default_custom_preset_) == 0)
|
||||||
|
? " (default)"
|
||||||
|
: "");
|
||||||
|
this->dump_preset_config_(preset_name, entry.config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/components/climate/climate.h"
|
#include "esphome/components/climate/climate.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace thermostat {
|
namespace thermostat {
|
||||||
@@ -72,14 +72,30 @@ struct ThermostatClimateTargetTempConfig {
|
|||||||
optional<climate::ClimateMode> mode_{};
|
optional<climate::ClimateMode> mode_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Entry for standard preset lookup
|
||||||
|
struct ThermostatPresetEntry {
|
||||||
|
climate::ClimatePreset preset;
|
||||||
|
ThermostatClimateTargetTempConfig config;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Entry for custom preset lookup
|
||||||
|
struct ThermostatCustomPresetEntry {
|
||||||
|
const char *name;
|
||||||
|
ThermostatClimateTargetTempConfig config;
|
||||||
|
};
|
||||||
|
|
||||||
class ThermostatClimate : public climate::Climate, public Component {
|
class ThermostatClimate : public climate::Climate, public Component {
|
||||||
|
public:
|
||||||
|
using PresetEntry = ThermostatPresetEntry;
|
||||||
|
using CustomPresetEntry = ThermostatCustomPresetEntry;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ThermostatClimate();
|
ThermostatClimate();
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
void set_default_preset(const std::string &custom_preset);
|
void set_default_preset(const char *custom_preset);
|
||||||
void set_default_preset(climate::ClimatePreset preset);
|
void set_default_preset(climate::ClimatePreset preset);
|
||||||
void set_on_boot_restore_from(OnBootRestoreFrom on_boot_restore_from);
|
void set_on_boot_restore_from(OnBootRestoreFrom on_boot_restore_from);
|
||||||
void set_set_point_minimum_differential(float differential);
|
void set_set_point_minimum_differential(float differential);
|
||||||
@@ -131,8 +147,8 @@ class ThermostatClimate : public climate::Climate, public Component {
|
|||||||
void set_supports_humidification(bool supports_humidification);
|
void set_supports_humidification(bool supports_humidification);
|
||||||
void set_supports_two_points(bool supports_two_points);
|
void set_supports_two_points(bool supports_two_points);
|
||||||
|
|
||||||
void set_preset_config(climate::ClimatePreset preset, const ThermostatClimateTargetTempConfig &config);
|
void set_preset_config(std::initializer_list<PresetEntry> presets);
|
||||||
void set_custom_preset_config(const std::string &name, const ThermostatClimateTargetTempConfig &config);
|
void set_custom_preset_config(std::initializer_list<CustomPresetEntry> presets);
|
||||||
|
|
||||||
Trigger<> *get_cool_action_trigger() const;
|
Trigger<> *get_cool_action_trigger() const;
|
||||||
Trigger<> *get_supplemental_cool_action_trigger() const;
|
Trigger<> *get_supplemental_cool_action_trigger() const;
|
||||||
@@ -516,9 +532,6 @@ class ThermostatClimate : public climate::Climate, public Component {
|
|||||||
Trigger<> *prev_swing_mode_trigger_{nullptr};
|
Trigger<> *prev_swing_mode_trigger_{nullptr};
|
||||||
Trigger<> *prev_humidity_control_trigger_{nullptr};
|
Trigger<> *prev_humidity_control_trigger_{nullptr};
|
||||||
|
|
||||||
/// Default custom preset to use on start up
|
|
||||||
std::string default_custom_preset_{};
|
|
||||||
|
|
||||||
/// Climate action timers
|
/// Climate action timers
|
||||||
std::array<ThermostatClimateTimer, THERMOSTAT_TIMER_COUNT> timer_{
|
std::array<ThermostatClimateTimer, THERMOSTAT_TIMER_COUNT> timer_{
|
||||||
ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)),
|
ThermostatClimateTimer(false, 0, 0, std::bind(&ThermostatClimate::cooling_max_run_time_timer_callback_, this)),
|
||||||
@@ -534,9 +547,11 @@ class ThermostatClimate : public climate::Climate, public Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc)
|
/// The set of standard preset configurations this thermostat supports (Eg. AWAY, ECO, etc)
|
||||||
std::map<climate::ClimatePreset, ThermostatClimateTargetTempConfig> preset_config_{};
|
FixedVector<PresetEntry> preset_config_{};
|
||||||
/// The set of custom preset configurations this thermostat supports (eg. "My Custom Preset")
|
/// The set of custom preset configurations this thermostat supports (eg. "My Custom Preset")
|
||||||
std::map<std::string, ThermostatClimateTargetTempConfig> custom_preset_config_{};
|
FixedVector<CustomPresetEntry> custom_preset_config_{};
|
||||||
|
/// Default custom preset to use on start up (pointer to entry in custom_preset_config_)
|
||||||
|
const char *default_custom_preset_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace thermostat
|
} // namespace thermostat
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ climate:
|
|||||||
id: test_thermostat
|
id: test_thermostat
|
||||||
name: Test Thermostat Custom Modes
|
name: Test Thermostat Custom Modes
|
||||||
sensor: thermostat_sensor
|
sensor: thermostat_sensor
|
||||||
|
default_preset: "Eco Plus"
|
||||||
preset:
|
preset:
|
||||||
- name: Away
|
- name: Away
|
||||||
default_target_temperature_low: 16°C
|
default_target_temperature_low: 16°C
|
||||||
|
|||||||
@@ -2,9 +2,13 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from aioesphomeapi import ClimateInfo, ClimatePreset
|
import asyncio
|
||||||
|
|
||||||
|
import aioesphomeapi
|
||||||
|
from aioesphomeapi import ClimateInfo, ClimatePreset, EntityState
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from .state_utils import InitialStateHelper
|
||||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||||
|
|
||||||
|
|
||||||
@@ -14,15 +18,50 @@ async def test_climate_custom_fan_modes_and_presets(
|
|||||||
run_compiled: RunCompiledFunction,
|
run_compiled: RunCompiledFunction,
|
||||||
api_client_connected: APIClientConnectedFactory,
|
api_client_connected: APIClientConnectedFactory,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test that custom presets are properly exposed via API."""
|
"""Test that custom presets are properly exposed and can be changed."""
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
async with run_compiled(yaml_config), api_client_connected() as client:
|
async with run_compiled(yaml_config), api_client_connected() as client:
|
||||||
# Get entities and services
|
states: dict[int, EntityState] = {}
|
||||||
|
super_saver_future: asyncio.Future[EntityState] = loop.create_future()
|
||||||
|
vacation_future: asyncio.Future[EntityState] = loop.create_future()
|
||||||
|
|
||||||
|
def on_state(state: EntityState) -> None:
|
||||||
|
states[state.key] = state
|
||||||
|
if isinstance(state, aioesphomeapi.ClimateState):
|
||||||
|
# Wait for Super Saver preset
|
||||||
|
if (
|
||||||
|
state.custom_preset == "Super Saver"
|
||||||
|
and state.target_temperature_low == 20.0
|
||||||
|
and state.target_temperature_high == 24.0
|
||||||
|
and not super_saver_future.done()
|
||||||
|
):
|
||||||
|
super_saver_future.set_result(state)
|
||||||
|
# Wait for Vacation Mode preset
|
||||||
|
elif (
|
||||||
|
state.custom_preset == "Vacation Mode"
|
||||||
|
and state.target_temperature_low == 15.0
|
||||||
|
and state.target_temperature_high == 18.0
|
||||||
|
and not vacation_future.done()
|
||||||
|
):
|
||||||
|
vacation_future.set_result(state)
|
||||||
|
|
||||||
|
# Get entities and set up state synchronization
|
||||||
entities, services = await client.list_entities_services()
|
entities, services = await client.list_entities_services()
|
||||||
|
initial_state_helper = InitialStateHelper(entities)
|
||||||
climate_infos = [e for e in entities if isinstance(e, ClimateInfo)]
|
climate_infos = [e for e in entities if isinstance(e, ClimateInfo)]
|
||||||
assert len(climate_infos) == 1, "Expected exactly 1 climate entity"
|
assert len(climate_infos) == 1, "Expected exactly 1 climate entity"
|
||||||
|
|
||||||
test_climate = climate_infos[0]
|
test_climate = climate_infos[0]
|
||||||
|
|
||||||
|
# Subscribe with the wrapper that filters initial states
|
||||||
|
client.subscribe_states(initial_state_helper.on_state_wrapper(on_state))
|
||||||
|
|
||||||
|
# Wait for all initial states to be broadcast
|
||||||
|
try:
|
||||||
|
await initial_state_helper.wait_for_initial_states()
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Timeout waiting for initial states")
|
||||||
|
|
||||||
# Verify enum presets are exposed (from preset: config map)
|
# Verify enum presets are exposed (from preset: config map)
|
||||||
assert ClimatePreset.AWAY in test_climate.supported_presets, (
|
assert ClimatePreset.AWAY in test_climate.supported_presets, (
|
||||||
"Expected AWAY in enum presets"
|
"Expected AWAY in enum presets"
|
||||||
@@ -40,3 +79,43 @@ async def test_climate_custom_fan_modes_and_presets(
|
|||||||
assert "Vacation Mode" in custom_presets, (
|
assert "Vacation Mode" in custom_presets, (
|
||||||
"Expected 'Vacation Mode' in custom presets"
|
"Expected 'Vacation Mode' in custom presets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get initial state and verify default preset
|
||||||
|
initial_state = initial_state_helper.initial_states.get(test_climate.key)
|
||||||
|
assert initial_state is not None, "Climate initial state not found"
|
||||||
|
assert isinstance(initial_state, aioesphomeapi.ClimateState)
|
||||||
|
assert initial_state.custom_preset == "Eco Plus", (
|
||||||
|
f"Expected default preset 'Eco Plus', got '{initial_state.custom_preset}'"
|
||||||
|
)
|
||||||
|
assert initial_state.target_temperature_low == 18.0, (
|
||||||
|
f"Expected low temp 18.0, got {initial_state.target_temperature_low}"
|
||||||
|
)
|
||||||
|
assert initial_state.target_temperature_high == 22.0, (
|
||||||
|
f"Expected high temp 22.0, got {initial_state.target_temperature_high}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test changing to "Super Saver" custom preset
|
||||||
|
client.climate_command(test_climate.key, custom_preset="Super Saver")
|
||||||
|
|
||||||
|
try:
|
||||||
|
super_saver_state = await asyncio.wait_for(super_saver_future, timeout=5.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Super Saver preset change not received within 5 seconds")
|
||||||
|
|
||||||
|
assert isinstance(super_saver_state, aioesphomeapi.ClimateState)
|
||||||
|
assert super_saver_state.custom_preset == "Super Saver"
|
||||||
|
assert super_saver_state.target_temperature_low == 20.0
|
||||||
|
assert super_saver_state.target_temperature_high == 24.0
|
||||||
|
|
||||||
|
# Test changing to "Vacation Mode" custom preset
|
||||||
|
client.climate_command(test_climate.key, custom_preset="Vacation Mode")
|
||||||
|
|
||||||
|
try:
|
||||||
|
vacation_state = await asyncio.wait_for(vacation_future, timeout=5.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Vacation Mode preset change not received within 5 seconds")
|
||||||
|
|
||||||
|
assert isinstance(vacation_state, aioesphomeapi.ClimateState)
|
||||||
|
assert vacation_state.custom_preset == "Vacation Mode"
|
||||||
|
assert vacation_state.target_temperature_low == 15.0
|
||||||
|
assert vacation_state.target_temperature_high == 18.0
|
||||||
|
|||||||
Reference in New Issue
Block a user