mirror of
https://github.com/esphome/esphome.git
synced 2026-09-28 23:40:25 +00:00
[pid] Pid climate add set deadband parameters action (#14730)
Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
co-authored by
J. Nick Koston
J. Nick Koston
parent
f6e97b7ada
commit
cf3552b46e
@@ -3,19 +3,13 @@ import esphome.codegen as cg
|
||||
from esphome.components import climate, output, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_HUMIDITY_SENSOR, CONF_ID, CONF_SENSOR
|
||||
from esphome.core import ID
|
||||
from esphome.core import ID, Lambda
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
pid_ns = cg.esphome_ns.namespace("pid")
|
||||
PIDClimate = pid_ns.class_("PIDClimate", climate.Climate, cg.Component)
|
||||
PIDAutotuneAction = pid_ns.class_("PIDAutotuneAction", automation.Action)
|
||||
PIDResetIntegralTermAction = pid_ns.class_(
|
||||
"PIDResetIntegralTermAction", automation.Action
|
||||
)
|
||||
PIDSetControlParametersAction = pid_ns.class_(
|
||||
"PIDSetControlParametersAction", automation.Action
|
||||
)
|
||||
|
||||
CONF_DEFAULT_TARGET_TEMPERATURE = "default_target_temperature"
|
||||
|
||||
@@ -43,6 +37,24 @@ CONF_KP_MULTIPLIER = "kp_multiplier"
|
||||
CONF_KI_MULTIPLIER = "ki_multiplier"
|
||||
CONF_KD_MULTIPLIER = "kd_multiplier"
|
||||
|
||||
|
||||
def _validate_thresholds(config: ConfigType) -> ConfigType:
|
||||
# Same rule as PIDClimate::set_deadband_thresholds; equal is allowed since 0/0 is the default.
|
||||
if config[CONF_THRESHOLD_LOW] > config[CONF_THRESHOLD_HIGH]:
|
||||
raise cv.Invalid(
|
||||
f"{CONF_THRESHOLD_LOW} must not be greater than {CONF_THRESHOLD_HIGH}"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
def _validate_threshold_action(config: ConfigType) -> ConfigType:
|
||||
threshold_low = config[CONF_THRESHOLD_LOW]
|
||||
threshold_high = config[CONF_THRESHOLD_HIGH]
|
||||
if isinstance(threshold_low, Lambda) or isinstance(threshold_high, Lambda):
|
||||
return config
|
||||
return _validate_thresholds(config)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
climate.climate_schema(PIDClimate).extend(
|
||||
{
|
||||
@@ -51,7 +63,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE): cv.temperature,
|
||||
cv.Optional(CONF_COOL_OUTPUT): cv.use_id(output.FloatOutput),
|
||||
cv.Optional(CONF_HEAT_OUTPUT): cv.use_id(output.FloatOutput),
|
||||
cv.Optional(CONF_DEADBAND_PARAMETERS): cv.Schema(
|
||||
cv.Optional(CONF_DEADBAND_PARAMETERS): cv.All(
|
||||
{
|
||||
cv.Required(CONF_THRESHOLD_HIGH): cv.temperature_delta,
|
||||
cv.Required(CONF_THRESHOLD_LOW): cv.temperature_delta,
|
||||
@@ -61,7 +73,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(
|
||||
CONF_DEADBAND_OUTPUT_AVERAGING_SAMPLES, default=1
|
||||
): cv.positive_not_null_int,
|
||||
}
|
||||
},
|
||||
_validate_thresholds,
|
||||
),
|
||||
cv.Required(CONF_CONTROL_PARAMETERS): cv.Schema(
|
||||
{
|
||||
@@ -134,24 +147,15 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_default_target_temperature(config[CONF_DEFAULT_TARGET_TEMPERATURE]))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"climate.pid.reset_integral_term",
|
||||
PIDResetIntegralTermAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(PIDClimate),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyCall("reset_integral_term()"),
|
||||
)
|
||||
async def pid_reset_integral_term(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
@@ -185,9 +189,8 @@ async def esp8266_set_frequency_to_code(
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"climate.pid.set_control_parameters",
|
||||
PIDSetControlParametersAction,
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(PIDClimate),
|
||||
@@ -196,24 +199,44 @@ async def esp8266_set_frequency_to_code(
|
||||
cv.Optional(CONF_KD, default=0.0): cv.templatable(cv.float_),
|
||||
}
|
||||
),
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_KP, "set_kp", cg.float_),
|
||||
automation.ApplyField(CONF_KI, "set_ki", cg.float_),
|
||||
automation.ApplyField(CONF_KD, "set_kd", cg.float_),
|
||||
)
|
||||
async def set_control_parameters(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
kp_template_ = await cg.templatable(config[CONF_KP], args, cg.float_)
|
||||
cg.add(var.set_kp(kp_template_))
|
||||
|
||||
ki_template_ = await cg.templatable(config[CONF_KI], args, cg.float_)
|
||||
cg.add(var.set_ki(ki_template_))
|
||||
automation.register_apply_action(
|
||||
"climate.pid.set_deadband_control_parameters_multipliers",
|
||||
automation.maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(PIDClimate),
|
||||
# kp_multiplier is required for compatibility with the original action API;
|
||||
# ki_multiplier and kd_multiplier are optional overrides.
|
||||
cv.Required(CONF_KP_MULTIPLIER): cv.templatable(cv.float_),
|
||||
cv.Optional(CONF_KI_MULTIPLIER): cv.templatable(cv.float_),
|
||||
cv.Optional(CONF_KD_MULTIPLIER): cv.templatable(cv.float_),
|
||||
}
|
||||
),
|
||||
automation.ApplyField(CONF_KP_MULTIPLIER, "set_kp_multiplier", cg.float_),
|
||||
automation.ApplyField(CONF_KI_MULTIPLIER, "set_ki_multiplier", cg.float_),
|
||||
automation.ApplyField(CONF_KD_MULTIPLIER, "set_kd_multiplier", cg.float_),
|
||||
)
|
||||
|
||||
kd_template_ = await cg.templatable(config[CONF_KD], args, cg.float_)
|
||||
cg.add(var.set_kd(kd_template_))
|
||||
|
||||
return var
|
||||
automation.register_apply_action(
|
||||
"climate.pid.set_deadband_threshold_parameters",
|
||||
automation.maybe_simple_id(
|
||||
cv.All(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(PIDClimate),
|
||||
cv.Required(CONF_THRESHOLD_HIGH): cv.templatable(cv.temperature_delta),
|
||||
cv.Required(CONF_THRESHOLD_LOW): cv.templatable(cv.temperature_delta),
|
||||
},
|
||||
_validate_threshold_action,
|
||||
)
|
||||
),
|
||||
automation.ApplyCall(
|
||||
"set_deadband_thresholds({}, {})",
|
||||
((CONF_THRESHOLD_LOW, cg.float_), (CONF_THRESHOLD_HIGH, cg.float_)),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -5,6 +5,17 @@ namespace esphome::pid {
|
||||
|
||||
static const char *const TAG = "pid.climate";
|
||||
|
||||
bool PIDClimate::set_deadband_thresholds(float threshold_low, float threshold_high) {
|
||||
if (threshold_low > threshold_high) {
|
||||
ESP_LOGW(TAG, "Deadband threshold low %.2f must not be greater than high %.2f", threshold_low, threshold_high);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->set_threshold_low(threshold_low);
|
||||
this->set_threshold_high(threshold_high);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PIDClimate::setup() {
|
||||
this->sensor_->add_on_state_callback([this](float state) {
|
||||
// only publish if state/current temperature has changed in two digits of precision
|
||||
|
||||
@@ -26,6 +26,7 @@ class PIDClimate final : public climate::Climate, public Component {
|
||||
void set_kd(float kd) { controller_.kd_ = kd; }
|
||||
void set_min_integral(float min_integral) { controller_.min_integral_ = min_integral; }
|
||||
void set_max_integral(float max_integral) { controller_.max_integral_ = max_integral; }
|
||||
bool set_deadband_thresholds(float threshold_low, float threshold_high);
|
||||
void set_output_samples(int in) { controller_.output_samples_ = in; }
|
||||
void set_derivative_samples(int in) {
|
||||
controller_.derivative_samples_ = in;
|
||||
@@ -131,36 +132,4 @@ template<typename... Ts> class PIDAutotuneAction final : public Action<Ts...> {
|
||||
PIDClimate *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class PIDResetIntegralTermAction final : public Action<Ts...> {
|
||||
public:
|
||||
PIDResetIntegralTermAction(PIDClimate *parent) : parent_(parent) {}
|
||||
|
||||
void play(const Ts &...x) { this->parent_->reset_integral_term(); }
|
||||
|
||||
protected:
|
||||
PIDClimate *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class PIDSetControlParametersAction final : public Action<Ts...> {
|
||||
public:
|
||||
PIDSetControlParametersAction(PIDClimate *parent) : parent_(parent) {}
|
||||
|
||||
void play(const Ts &...x) {
|
||||
auto kp = this->kp_.value(x...);
|
||||
auto ki = this->ki_.value(x...);
|
||||
auto kd = this->kd_.value(x...);
|
||||
|
||||
this->parent_->set_kp(kp);
|
||||
this->parent_->set_ki(ki);
|
||||
this->parent_->set_kd(kd);
|
||||
}
|
||||
|
||||
protected:
|
||||
TEMPLATABLE_VALUE(float, kp)
|
||||
TEMPLATABLE_VALUE(float, ki)
|
||||
TEMPLATABLE_VALUE(float, kd)
|
||||
|
||||
PIDClimate *parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::pid
|
||||
|
||||
@@ -2,7 +2,15 @@ import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
from esphome.components.const import CONF_CLIMATE_ID
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_TYPE, ICON_GAUGE, STATE_CLASS_MEASUREMENT, UNIT_PERCENT
|
||||
from esphome.const import (
|
||||
CONF_TYPE,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
ICON_GAUGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_EMPTY,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from ..climate import PIDClimate, pid_ns
|
||||
@@ -11,22 +19,55 @@ PIDClimateSensor = pid_ns.class_("PIDClimateSensor", sensor.Sensor, cg.Component
|
||||
PIDClimateSensorType = pid_ns.enum("PIDClimateSensorType")
|
||||
|
||||
PID_CLIMATE_SENSOR_TYPES = {
|
||||
"RESULT": PIDClimateSensorType.PID_SENSOR_TYPE_RESULT,
|
||||
"ERROR": PIDClimateSensorType.PID_SENSOR_TYPE_ERROR,
|
||||
"PROPORTIONAL": PIDClimateSensorType.PID_SENSOR_TYPE_PROPORTIONAL,
|
||||
"INTEGRAL": PIDClimateSensorType.PID_SENSOR_TYPE_INTEGRAL,
|
||||
"DERIVATIVE": PIDClimateSensorType.PID_SENSOR_TYPE_DERIVATIVE,
|
||||
"HEAT": PIDClimateSensorType.PID_SENSOR_TYPE_HEAT,
|
||||
"COOL": PIDClimateSensorType.PID_SENSOR_TYPE_COOL,
|
||||
"KP": PIDClimateSensorType.PID_SENSOR_TYPE_KP,
|
||||
"KI": PIDClimateSensorType.PID_SENSOR_TYPE_KI,
|
||||
"KD": PIDClimateSensorType.PID_SENSOR_TYPE_KD,
|
||||
"RESULT": (PIDClimateSensorType.PID_SENSOR_TYPE_RESULT, UNIT_PERCENT),
|
||||
"ERROR": (PIDClimateSensorType.PID_SENSOR_TYPE_ERROR, UNIT_PERCENT),
|
||||
"PROPORTIONAL": (PIDClimateSensorType.PID_SENSOR_TYPE_PROPORTIONAL, UNIT_PERCENT),
|
||||
"INTEGRAL": (PIDClimateSensorType.PID_SENSOR_TYPE_INTEGRAL, UNIT_PERCENT),
|
||||
"DERIVATIVE": (PIDClimateSensorType.PID_SENSOR_TYPE_DERIVATIVE, UNIT_PERCENT),
|
||||
"HEAT": (PIDClimateSensorType.PID_SENSOR_TYPE_HEAT, UNIT_PERCENT),
|
||||
"COOL": (PIDClimateSensorType.PID_SENSOR_TYPE_COOL, UNIT_PERCENT),
|
||||
"KP": (PIDClimateSensorType.PID_SENSOR_TYPE_KP, UNIT_PERCENT),
|
||||
"KI": (PIDClimateSensorType.PID_SENSOR_TYPE_KI, UNIT_PERCENT),
|
||||
"KD": (PIDClimateSensorType.PID_SENSOR_TYPE_KD, UNIT_PERCENT),
|
||||
"DEADBAND_THRESHOLD_HIGH": (
|
||||
PIDClimateSensorType.PID_SENSOR_TYPE_DEADBAND_THRESHOLD_HIGH,
|
||||
UNIT_CELSIUS,
|
||||
),
|
||||
"DEADBAND_THRESHOLD_LOW": (
|
||||
PIDClimateSensorType.PID_SENSOR_TYPE_DEADBAND_THRESHOLD_LOW,
|
||||
UNIT_CELSIUS,
|
||||
),
|
||||
"KP_DEADBAND_MULTIPLIER": (
|
||||
PIDClimateSensorType.PID_SENSOR_TYPE_KP_DEADBAND_MULTIPLIER,
|
||||
UNIT_EMPTY,
|
||||
),
|
||||
"KI_DEADBAND_MULTIPLIER": (
|
||||
PIDClimateSensorType.PID_SENSOR_TYPE_KI_DEADBAND_MULTIPLIER,
|
||||
UNIT_EMPTY,
|
||||
),
|
||||
"KD_DEADBAND_MULTIPLIER": (
|
||||
PIDClimateSensorType.PID_SENSOR_TYPE_KD_DEADBAND_MULTIPLIER,
|
||||
UNIT_EMPTY,
|
||||
),
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
PID_CLIMATE_SENSOR_ENUMS = {
|
||||
sensor_type: sensor_config[0]
|
||||
for sensor_type, sensor_config in PID_CLIMATE_SENSOR_TYPES.items()
|
||||
}
|
||||
|
||||
|
||||
def set_default_unit_of_measurement(config: ConfigType) -> ConfigType:
|
||||
sensor_type = config[CONF_TYPE]
|
||||
config.setdefault(
|
||||
CONF_UNIT_OF_MEASUREMENT, PID_CLIMATE_SENSOR_TYPES[sensor_type][1]
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(
|
||||
PIDClimateSensor,
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_GAUGE,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
@@ -34,10 +75,11 @@ CONFIG_SCHEMA = (
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_CLIMATE_ID): cv.use_id(PIDClimate),
|
||||
cv.Required(CONF_TYPE): cv.enum(PID_CLIMATE_SENSOR_TYPES, upper=True),
|
||||
cv.Required(CONF_TYPE): cv.enum(PID_CLIMATE_SENSOR_ENUMS, upper=True),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
set_default_unit_of_measurement,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -14,43 +14,55 @@ void PIDClimateSensor::update_from_parent_() {
|
||||
float value;
|
||||
switch (this->type_) {
|
||||
case PID_SENSOR_TYPE_RESULT:
|
||||
value = this->parent_->get_output_value();
|
||||
value = this->parent_->get_output_value() * 100.0f;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_ERROR:
|
||||
value = this->parent_->get_error_value();
|
||||
value = this->parent_->get_error_value() * 100.0f;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_PROPORTIONAL:
|
||||
value = this->parent_->get_proportional_term();
|
||||
value = this->parent_->get_proportional_term() * 100.0f;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_INTEGRAL:
|
||||
value = this->parent_->get_integral_term();
|
||||
value = this->parent_->get_integral_term() * 100.0f;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_DERIVATIVE:
|
||||
value = this->parent_->get_derivative_term();
|
||||
value = this->parent_->get_derivative_term() * 100.0f;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_HEAT:
|
||||
value = clamp(this->parent_->get_output_value(), 0.0f, 1.0f);
|
||||
value = clamp(this->parent_->get_output_value(), 0.0f, 1.0f) * 100.0f;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_COOL:
|
||||
value = clamp(-this->parent_->get_output_value(), 0.0f, 1.0f);
|
||||
value = clamp(-this->parent_->get_output_value(), 0.0f, 1.0f) * 100.0f;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_KP:
|
||||
value = this->parent_->get_kp();
|
||||
this->publish_state(value);
|
||||
return;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_KI:
|
||||
value = this->parent_->get_ki();
|
||||
this->publish_state(value);
|
||||
return;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_KD:
|
||||
value = this->parent_->get_kd();
|
||||
this->publish_state(value);
|
||||
return;
|
||||
break;
|
||||
case PID_SENSOR_TYPE_DEADBAND_THRESHOLD_HIGH:
|
||||
value = this->parent_->get_threshold_high();
|
||||
break;
|
||||
case PID_SENSOR_TYPE_DEADBAND_THRESHOLD_LOW:
|
||||
value = this->parent_->get_threshold_low();
|
||||
break;
|
||||
case PID_SENSOR_TYPE_KP_DEADBAND_MULTIPLIER:
|
||||
value = this->parent_->get_kp_multiplier();
|
||||
break;
|
||||
case PID_SENSOR_TYPE_KI_DEADBAND_MULTIPLIER:
|
||||
value = this->parent_->get_ki_multiplier();
|
||||
break;
|
||||
case PID_SENSOR_TYPE_KD_DEADBAND_MULTIPLIER:
|
||||
value = this->parent_->get_kd_multiplier();
|
||||
break;
|
||||
default:
|
||||
value = NAN;
|
||||
break;
|
||||
}
|
||||
this->publish_state(value * 100.0f);
|
||||
this->publish_state(value);
|
||||
}
|
||||
void PIDClimateSensor::dump_config() { LOG_SENSOR("", "PID Climate Sensor", this); }
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ enum PIDClimateSensorType {
|
||||
PID_SENSOR_TYPE_KP,
|
||||
PID_SENSOR_TYPE_KI,
|
||||
PID_SENSOR_TYPE_KD,
|
||||
PID_SENSOR_TYPE_DEADBAND_THRESHOLD_HIGH,
|
||||
PID_SENSOR_TYPE_DEADBAND_THRESHOLD_LOW,
|
||||
PID_SENSOR_TYPE_KP_DEADBAND_MULTIPLIER,
|
||||
PID_SENSOR_TYPE_KI_DEADBAND_MULTIPLIER,
|
||||
PID_SENSOR_TYPE_KD_DEADBAND_MULTIPLIER,
|
||||
};
|
||||
|
||||
class PIDClimateSensor final : public sensor::Sensor, public Component {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
manifest.dependencies = manifest.dependencies + ["climate", "output", "sensor"]
|
||||
@@ -12,6 +12,15 @@ esphome:
|
||||
kp: 0.0
|
||||
ki: 0.0
|
||||
kd: 0.0
|
||||
- climate.pid.set_deadband_control_parameters_multipliers:
|
||||
id: pid_climate
|
||||
kp_multiplier: 0.0
|
||||
ki_multiplier: 0.0
|
||||
kd_multiplier: 0.0
|
||||
- climate.pid.set_deadband_threshold_parameters:
|
||||
id: pid_climate
|
||||
threshold_high: 0.5°C
|
||||
threshold_low: !lambda return -0.5f;
|
||||
- climate.pid.reset_integral_term: pid_climate
|
||||
|
||||
output:
|
||||
@@ -30,6 +39,26 @@ sensor:
|
||||
}
|
||||
return 0.0;
|
||||
update_interval: 60s
|
||||
- platform: pid
|
||||
id: pid_kp_multiplier
|
||||
climate_id: pid_climate
|
||||
type: KP_DEADBAND_MULTIPLIER
|
||||
- platform: pid
|
||||
id: pid_ki_multiplier
|
||||
climate_id: pid_climate
|
||||
type: KI_DEADBAND_MULTIPLIER
|
||||
- platform: pid
|
||||
id: pid_kd_multiplier
|
||||
climate_id: pid_climate
|
||||
type: KD_DEADBAND_MULTIPLIER
|
||||
- platform: pid
|
||||
id: pid_threshold_high
|
||||
climate_id: pid_climate
|
||||
type: DEADBAND_THRESHOLD_HIGH
|
||||
- platform: pid
|
||||
id: pid_threshold_low
|
||||
climate_id: pid_climate
|
||||
type: DEADBAND_THRESHOLD_LOW
|
||||
|
||||
climate:
|
||||
- platform: pid
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "esphome/components/pid/pid_climate.h"
|
||||
|
||||
namespace esphome::pid {
|
||||
|
||||
TEST(PIDClimateDeadbandThresholds, InvalidThresholdsDoNotChangeController) {
|
||||
PIDClimate climate;
|
||||
climate.set_threshold_low(-1.0f);
|
||||
climate.set_threshold_high(1.0f);
|
||||
|
||||
EXPECT_FALSE(climate.set_deadband_thresholds(2.0f, 1.0f));
|
||||
|
||||
EXPECT_FLOAT_EQ(climate.get_threshold_low(), -1.0f);
|
||||
EXPECT_FLOAT_EQ(climate.get_threshold_high(), 1.0f);
|
||||
}
|
||||
|
||||
TEST(PIDClimateDeadbandThresholds, ValidThresholdsChangeController) {
|
||||
PIDClimate climate;
|
||||
climate.set_threshold_low(-1.0f);
|
||||
climate.set_threshold_high(1.0f);
|
||||
|
||||
EXPECT_TRUE(climate.set_deadband_thresholds(-2.0f, 0.5f));
|
||||
|
||||
EXPECT_FLOAT_EQ(climate.get_threshold_low(), -2.0f);
|
||||
EXPECT_FLOAT_EQ(climate.get_threshold_high(), 0.5f);
|
||||
}
|
||||
|
||||
TEST(PIDClimateDeadbandThresholds, EqualThresholdsDisableDeadband) {
|
||||
PIDClimate climate;
|
||||
climate.set_threshold_low(-1.0f);
|
||||
climate.set_threshold_high(1.0f);
|
||||
|
||||
EXPECT_TRUE(climate.set_deadband_thresholds(0.0f, 0.0f));
|
||||
|
||||
EXPECT_FLOAT_EQ(climate.get_threshold_low(), 0.0f);
|
||||
EXPECT_FLOAT_EQ(climate.get_threshold_high(), 0.0f);
|
||||
EXPECT_FALSE(climate.in_deadband());
|
||||
}
|
||||
|
||||
} // namespace esphome::pid
|
||||
Reference in New Issue
Block a user