mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 16:38:16 +01:00
Allow specifying target and current visual steps for climate (#4440)
* Allow specifying target and current visual steps for climate * Fixes * format * format
This commit is contained in:
parent
50fbbf2d3b
commit
0e1d018ce3
11 changed files with 111 additions and 40 deletions
|
@ -829,7 +829,7 @@ message ListEntitiesClimateResponse {
|
|||
repeated ClimateMode supported_modes = 7;
|
||||
float visual_min_temperature = 8;
|
||||
float visual_max_temperature = 9;
|
||||
float visual_temperature_step = 10;
|
||||
float visual_target_temperature_step = 10;
|
||||
// for older peer versions - in new system this
|
||||
// is if CLIMATE_PRESET_AWAY exists is supported_presets
|
||||
bool legacy_supports_away = 11;
|
||||
|
@ -842,6 +842,7 @@ message ListEntitiesClimateResponse {
|
|||
bool disabled_by_default = 18;
|
||||
string icon = 19;
|
||||
EntityCategory entity_category = 20;
|
||||
float visual_current_temperature_step = 21;
|
||||
}
|
||||
message ClimateStateResponse {
|
||||
option (id) = 47;
|
||||
|
|
|
@ -548,7 +548,9 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
|||
|
||||
msg.visual_min_temperature = traits.get_visual_min_temperature();
|
||||
msg.visual_max_temperature = traits.get_visual_max_temperature();
|
||||
msg.visual_temperature_step = traits.get_visual_temperature_step();
|
||||
msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
|
||||
msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
|
||||
|
||||
msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
|
||||
msg.supports_action = traits.get_supports_action();
|
||||
|
||||
|
|
|
@ -3451,7 +3451,11 @@ bool ListEntitiesClimateResponse::decode_32bit(uint32_t field_id, Proto32Bit val
|
|||
return true;
|
||||
}
|
||||
case 10: {
|
||||
this->visual_temperature_step = value.as_float();
|
||||
this->visual_target_temperature_step = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 21: {
|
||||
this->visual_current_temperature_step = value.as_float();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
@ -3470,7 +3474,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||
}
|
||||
buffer.encode_float(8, this->visual_min_temperature);
|
||||
buffer.encode_float(9, this->visual_max_temperature);
|
||||
buffer.encode_float(10, this->visual_temperature_step);
|
||||
buffer.encode_float(10, this->visual_target_temperature_step);
|
||||
buffer.encode_bool(11, this->legacy_supports_away);
|
||||
buffer.encode_bool(12, this->supports_action);
|
||||
for (auto &it : this->supported_fan_modes) {
|
||||
|
@ -3491,6 +3495,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||
buffer.encode_bool(18, this->disabled_by_default);
|
||||
buffer.encode_string(19, this->icon);
|
||||
buffer.encode_enum<enums::EntityCategory>(20, this->entity_category);
|
||||
buffer.encode_float(21, this->visual_current_temperature_step);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
|
@ -3537,8 +3542,8 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
|||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" visual_temperature_step: ");
|
||||
sprintf(buffer, "%g", this->visual_temperature_step);
|
||||
out.append(" visual_target_temperature_step: ");
|
||||
sprintf(buffer, "%g", this->visual_target_temperature_step);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
|
@ -3591,6 +3596,11 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
|||
out.append(" entity_category: ");
|
||||
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" visual_current_temperature_step: ");
|
||||
sprintf(buffer, "%g", this->visual_current_temperature_step);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -915,7 +915,7 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
|||
std::vector<enums::ClimateMode> supported_modes{};
|
||||
float visual_min_temperature{0.0f};
|
||||
float visual_max_temperature{0.0f};
|
||||
float visual_temperature_step{0.0f};
|
||||
float visual_target_temperature_step{0.0f};
|
||||
bool legacy_supports_away{false};
|
||||
bool supports_action{false};
|
||||
std::vector<enums::ClimateFanMode> supported_fan_modes{};
|
||||
|
@ -926,6 +926,7 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
|||
bool disabled_by_default{false};
|
||||
std::string icon{};
|
||||
enums::EntityCategory entity_category{};
|
||||
float visual_current_temperature_step{0.0f};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
|
|
|
@ -104,10 +104,40 @@ CLIMATE_SWING_MODES = {
|
|||
|
||||
validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
|
||||
|
||||
CONF_CURRENT_TEMPERATURE = "current_temperature"
|
||||
|
||||
visual_temperature = cv.float_with_unit(
|
||||
"visual_temperature", "(°C|° C|°|C|° K|° K|K|°F|° F|F)?"
|
||||
)
|
||||
|
||||
|
||||
def single_visual_temperature(value):
|
||||
if isinstance(value, dict):
|
||||
return value
|
||||
|
||||
value = visual_temperature(value)
|
||||
return VISUAL_TEMPERATURE_STEP_SCHEMA(
|
||||
{
|
||||
CONF_TARGET_TEMPERATURE: value,
|
||||
CONF_CURRENT_TEMPERATURE: value,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
# Actions
|
||||
ControlAction = climate_ns.class_("ControlAction", automation.Action)
|
||||
StateTrigger = climate_ns.class_("StateTrigger", automation.Trigger.template())
|
||||
|
||||
VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Any(
|
||||
single_visual_temperature,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_TARGET_TEMPERATURE): visual_temperature,
|
||||
cv.Required(CONF_CURRENT_TEMPERATURE): visual_temperature,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Climate),
|
||||
|
@ -116,9 +146,7 @@ CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).
|
|||
{
|
||||
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
|
||||
cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
|
||||
cv.Optional(CONF_TEMPERATURE_STEP): cv.float_with_unit(
|
||||
"visual_temperature", "(°C|° C|°|C|° K|° K|K|°F|° F|F)?"
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE_STEP): VISUAL_TEMPERATURE_STEP_SCHEMA,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All(
|
||||
|
@ -193,7 +221,12 @@ async def setup_climate_core_(var, config):
|
|||
if CONF_MAX_TEMPERATURE in visual:
|
||||
cg.add(var.set_visual_max_temperature_override(visual[CONF_MAX_TEMPERATURE]))
|
||||
if CONF_TEMPERATURE_STEP in visual:
|
||||
cg.add(var.set_visual_temperature_step_override(visual[CONF_TEMPERATURE_STEP]))
|
||||
cg.add(
|
||||
var.set_visual_temperature_step_override(
|
||||
visual[CONF_TEMPERATURE_STEP][CONF_TARGET_TEMPERATURE],
|
||||
visual[CONF_TEMPERATURE_STEP][CONF_CURRENT_TEMPERATURE],
|
||||
)
|
||||
)
|
||||
|
||||
if CONF_MQTT_ID in config:
|
||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
|
||||
|
|
|
@ -430,9 +430,11 @@ ClimateTraits Climate::get_traits() {
|
|||
if (this->visual_max_temperature_override_.has_value()) {
|
||||
traits.set_visual_max_temperature(*this->visual_max_temperature_override_);
|
||||
}
|
||||
if (this->visual_temperature_step_override_.has_value()) {
|
||||
traits.set_visual_temperature_step(*this->visual_temperature_step_override_);
|
||||
if (this->visual_target_temperature_step_override_.has_value()) {
|
||||
traits.set_visual_target_temperature_step(*this->visual_target_temperature_step_override_);
|
||||
traits.set_visual_current_temperature_step(*this->visual_current_temperature_step_override_);
|
||||
}
|
||||
|
||||
return traits;
|
||||
}
|
||||
|
||||
|
@ -442,8 +444,9 @@ void Climate::set_visual_min_temperature_override(float visual_min_temperature_o
|
|||
void Climate::set_visual_max_temperature_override(float visual_max_temperature_override) {
|
||||
this->visual_max_temperature_override_ = visual_max_temperature_override;
|
||||
}
|
||||
void Climate::set_visual_temperature_step_override(float visual_temperature_step_override) {
|
||||
this->visual_temperature_step_override_ = visual_temperature_step_override;
|
||||
void Climate::set_visual_temperature_step_override(float target, float current) {
|
||||
this->visual_target_temperature_step_override_ = target;
|
||||
this->visual_current_temperature_step_override_ = current;
|
||||
}
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
@ -541,7 +544,9 @@ void Climate::dump_traits_(const char *tag) {
|
|||
ESP_LOGCONFIG(tag, " [x] Visual settings:");
|
||||
ESP_LOGCONFIG(tag, " - Min: %.1f", traits.get_visual_min_temperature());
|
||||
ESP_LOGCONFIG(tag, " - Max: %.1f", traits.get_visual_max_temperature());
|
||||
ESP_LOGCONFIG(tag, " - Step: %.1f", traits.get_visual_temperature_step());
|
||||
ESP_LOGCONFIG(tag, " - Step:");
|
||||
ESP_LOGCONFIG(tag, " Target: %.1f", traits.get_visual_target_temperature_step());
|
||||
ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step());
|
||||
if (traits.get_supports_current_temperature()) {
|
||||
ESP_LOGCONFIG(tag, " [x] Supports current temperature");
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ class Climate : public EntityBase {
|
|||
|
||||
void set_visual_min_temperature_override(float visual_min_temperature_override);
|
||||
void set_visual_max_temperature_override(float visual_max_temperature_override);
|
||||
void set_visual_temperature_step_override(float visual_temperature_step_override);
|
||||
void set_visual_temperature_step_override(float target, float current);
|
||||
|
||||
protected:
|
||||
friend ClimateCall;
|
||||
|
@ -288,7 +288,8 @@ class Climate : public EntityBase {
|
|||
ESPPreferenceObject rtc_;
|
||||
optional<float> visual_min_temperature_override_{};
|
||||
optional<float> visual_max_temperature_override_{};
|
||||
optional<float> visual_temperature_step_override_{};
|
||||
optional<float> visual_target_temperature_step_override_{};
|
||||
optional<float> visual_current_temperature_step_override_{};
|
||||
};
|
||||
|
||||
} // namespace climate
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
namespace esphome {
|
||||
namespace climate {
|
||||
|
||||
int8_t ClimateTraits::get_temperature_accuracy_decimals() const {
|
||||
return step_to_accuracy_decimals(this->visual_temperature_step_);
|
||||
int8_t ClimateTraits::get_target_temperature_accuracy_decimals() const {
|
||||
return step_to_accuracy_decimals(this->visual_target_temperature_step_);
|
||||
}
|
||||
|
||||
int8_t ClimateTraits::get_current_temperature_accuracy_decimals() const {
|
||||
return step_to_accuracy_decimals(this->visual_current_temperature_step_);
|
||||
}
|
||||
|
||||
} // namespace climate
|
||||
|
|
|
@ -147,9 +147,20 @@ class ClimateTraits {
|
|||
void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; }
|
||||
float get_visual_max_temperature() const { return visual_max_temperature_; }
|
||||
void set_visual_max_temperature(float visual_max_temperature) { visual_max_temperature_ = visual_max_temperature; }
|
||||
float get_visual_temperature_step() const { return visual_temperature_step_; }
|
||||
int8_t get_temperature_accuracy_decimals() const;
|
||||
void set_visual_temperature_step(float temperature_step) { visual_temperature_step_ = temperature_step; }
|
||||
float get_visual_target_temperature_step() const { return visual_target_temperature_step_; }
|
||||
float get_visual_current_temperature_step() const { return visual_current_temperature_step_; }
|
||||
void set_visual_target_temperature_step(float temperature_step) {
|
||||
visual_target_temperature_step_ = temperature_step;
|
||||
}
|
||||
void set_visual_current_temperature_step(float temperature_step) {
|
||||
visual_current_temperature_step_ = temperature_step;
|
||||
}
|
||||
void set_visual_temperature_step(float temperature_step) {
|
||||
visual_target_temperature_step_ = temperature_step;
|
||||
visual_current_temperature_step_ = temperature_step;
|
||||
}
|
||||
int8_t get_target_temperature_accuracy_decimals() const;
|
||||
int8_t get_current_temperature_accuracy_decimals() const;
|
||||
|
||||
protected:
|
||||
void set_mode_support_(climate::ClimateMode mode, bool supported) {
|
||||
|
@ -186,7 +197,8 @@ class ClimateTraits {
|
|||
|
||||
float visual_min_temperature_{10};
|
||||
float visual_max_temperature_{30};
|
||||
float visual_temperature_step_{0.1};
|
||||
float visual_target_temperature_step_{0.1};
|
||||
float visual_current_temperature_step_{0.1};
|
||||
};
|
||||
|
||||
} // namespace climate
|
||||
|
|
|
@ -62,7 +62,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
|
|||
// max_temp
|
||||
root[MQTT_MAX_TEMP] = traits.get_visual_max_temperature();
|
||||
// temp_step
|
||||
root["temp_step"] = traits.get_visual_temperature_step();
|
||||
root["temp_step"] = traits.get_visual_target_temperature_step();
|
||||
// temperature units are always coerced to Celsius internally
|
||||
root[MQTT_TEMPERATURE_UNIT] = "C";
|
||||
|
||||
|
@ -281,21 +281,22 @@ bool MQTTClimateComponent::publish_state_() {
|
|||
bool success = true;
|
||||
if (!this->publish(this->get_mode_state_topic(), mode_s))
|
||||
success = false;
|
||||
int8_t accuracy = traits.get_temperature_accuracy_decimals();
|
||||
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
|
||||
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
|
||||
if (traits.get_supports_current_temperature() && !std::isnan(this->device_->current_temperature)) {
|
||||
std::string payload = value_accuracy_to_string(this->device_->current_temperature, accuracy);
|
||||
std::string payload = value_accuracy_to_string(this->device_->current_temperature, current_accuracy);
|
||||
if (!this->publish(this->get_current_temperature_state_topic(), payload))
|
||||
success = false;
|
||||
}
|
||||
if (traits.get_supports_two_point_target_temperature()) {
|
||||
std::string payload = value_accuracy_to_string(this->device_->target_temperature_low, accuracy);
|
||||
std::string payload = value_accuracy_to_string(this->device_->target_temperature_low, target_accuracy);
|
||||
if (!this->publish(this->get_target_temperature_low_state_topic(), payload))
|
||||
success = false;
|
||||
payload = value_accuracy_to_string(this->device_->target_temperature_high, accuracy);
|
||||
payload = value_accuracy_to_string(this->device_->target_temperature_high, target_accuracy);
|
||||
if (!this->publish(this->get_target_temperature_high_state_topic(), payload))
|
||||
success = false;
|
||||
} else {
|
||||
std::string payload = value_accuracy_to_string(this->device_->target_temperature, accuracy);
|
||||
std::string payload = value_accuracy_to_string(this->device_->target_temperature, target_accuracy);
|
||||
if (!this->publish(this->get_target_temperature_state_topic(), payload))
|
||||
success = false;
|
||||
}
|
||||
|
|
|
@ -873,7 +873,8 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
|||
return json::build_json([obj, start_config](JsonObject root) {
|
||||
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
|
||||
const auto traits = obj->get_traits();
|
||||
int8_t accuracy = traits.get_temperature_accuracy_decimals();
|
||||
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
|
||||
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
|
||||
char __buf[16];
|
||||
|
||||
if (start_config == DETAIL_ALL) {
|
||||
|
@ -910,9 +911,9 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
|||
|
||||
bool has_state = false;
|
||||
root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
|
||||
root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), accuracy);
|
||||
root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), accuracy);
|
||||
root["step"] = traits.get_visual_temperature_step();
|
||||
root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy);
|
||||
root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy);
|
||||
root["step"] = traits.get_visual_target_temperature_step();
|
||||
if (traits.get_supports_action()) {
|
||||
root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
|
||||
root["state"] = root["action"];
|
||||
|
@ -935,20 +936,20 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
|||
}
|
||||
if (traits.get_supports_current_temperature()) {
|
||||
if (!std::isnan(obj->current_temperature)) {
|
||||
root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, accuracy);
|
||||
root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, current_accuracy);
|
||||
} else {
|
||||
root["current_temperature"] = "NA";
|
||||
}
|
||||
}
|
||||
if (traits.get_supports_two_point_target_temperature()) {
|
||||
root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, accuracy);
|
||||
root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, accuracy);
|
||||
root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, target_accuracy);
|
||||
root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, target_accuracy);
|
||||
if (!has_state) {
|
||||
root["state"] =
|
||||
value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f, accuracy);
|
||||
root["state"] = value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f,
|
||||
target_accuracy);
|
||||
}
|
||||
} else {
|
||||
root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, accuracy);
|
||||
root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, target_accuracy);
|
||||
if (!has_state)
|
||||
root["state"] = root["target_temperature"];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue