Revert "Climate bang bang enhancements (#1061)" (#1106)

This reverts commit 0082c5b459.
This commit is contained in:
Guillermo Ruffino 2020-07-01 14:08:55 -03:00 committed by GitHub
parent 31067530a5
commit f479eae714
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 123 additions and 945 deletions

View file

@ -9,393 +9,136 @@ static const char *TAG = "bang_bang.climate";
void BangBangClimate::setup() { void BangBangClimate::setup() {
this->sensor_->add_on_state_callback([this](float state) { this->sensor_->add_on_state_callback([this](float state) {
this->current_temperature = state; this->current_temperature = state;
// required action may have changed, recompute, refresh // control may have changed, recompute
this->switch_to_action_(compute_action_()); this->compute_state_();
// current temperature and possibly action changed, so publish the new state // current temperature changed, publish state
this->publish_state(); this->publish_state();
}); });
this->current_temperature = this->sensor_->state; this->current_temperature = this->sensor_->state;
// restore all climate data, if possible // restore set points
auto restore = this->restore_state_(); auto restore = this->restore_state_();
if (restore.has_value()) { if (restore.has_value()) {
restore->to_call(this).perform(); restore->to_call(this).perform();
} else { } else {
// restore from defaults, change_away handles temps for us // restore from defaults, change_away handles those for us
this->mode = climate::CLIMATE_MODE_AUTO; this->mode = climate::CLIMATE_MODE_AUTO;
this->change_away_(false); this->change_away_(false);
} }
// refresh the climate action based on the restored settings
this->switch_to_action_(compute_action_());
this->setup_complete_ = true;
this->publish_state();
}
void BangBangClimate::refresh() {
this->switch_to_mode_(this->mode);
this->switch_to_action_(compute_action_());
this->switch_to_fan_mode_(this->fan_mode);
this->switch_to_swing_mode_(this->swing_mode);
this->publish_state();
} }
void BangBangClimate::control(const climate::ClimateCall &call) { void BangBangClimate::control(const climate::ClimateCall &call) {
if (call.get_mode().has_value()) if (call.get_mode().has_value())
this->mode = *call.get_mode(); this->mode = *call.get_mode();
if (call.get_fan_mode().has_value())
this->fan_mode = *call.get_fan_mode();
if (call.get_swing_mode().has_value())
this->swing_mode = *call.get_swing_mode();
if (call.get_target_temperature().has_value())
this->target_temperature = *call.get_target_temperature();
if (call.get_target_temperature_low().has_value()) if (call.get_target_temperature_low().has_value())
this->target_temperature_low = *call.get_target_temperature_low(); this->target_temperature_low = *call.get_target_temperature_low();
if (call.get_target_temperature_high().has_value()) if (call.get_target_temperature_high().has_value())
this->target_temperature_high = *call.get_target_temperature_high(); this->target_temperature_high = *call.get_target_temperature_high();
if (call.get_away().has_value()) { if (call.get_away().has_value())
// setup_complete_ blocks modifying/resetting the temps immediately after boot this->change_away_(*call.get_away());
if (this->setup_complete_) {
this->change_away_(*call.get_away()); this->compute_state_();
} else { this->publish_state();
this->away = *call.get_away();
}
}
// set point validation
if (this->supports_two_points_) {
if (this->target_temperature_low < this->get_traits().get_visual_min_temperature())
this->target_temperature_low = this->get_traits().get_visual_min_temperature();
if (this->target_temperature_high > this->get_traits().get_visual_max_temperature())
this->target_temperature_high = this->get_traits().get_visual_max_temperature();
if (this->target_temperature_high < this->target_temperature_low)
this->target_temperature_high = this->target_temperature_low;
} else {
if (this->target_temperature < this->get_traits().get_visual_min_temperature())
this->target_temperature = this->get_traits().get_visual_min_temperature();
if (this->target_temperature > this->get_traits().get_visual_max_temperature())
this->target_temperature = this->get_traits().get_visual_max_temperature();
}
// make any changes happen
refresh();
} }
climate::ClimateTraits BangBangClimate::traits() { climate::ClimateTraits BangBangClimate::traits() {
auto traits = climate::ClimateTraits(); auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true); traits.set_supports_current_temperature(true);
traits.set_supports_auto_mode(this->supports_auto_); traits.set_supports_auto_mode(true);
traits.set_supports_cool_mode(this->supports_cool_); traits.set_supports_cool_mode(this->supports_cool_);
traits.set_supports_dry_mode(this->supports_dry_);
traits.set_supports_fan_only_mode(this->supports_fan_only_);
traits.set_supports_heat_mode(this->supports_heat_); traits.set_supports_heat_mode(this->supports_heat_);
traits.set_supports_fan_mode_on(this->supports_fan_mode_on_); traits.set_supports_two_point_target_temperature(true);
traits.set_supports_fan_mode_off(this->supports_fan_mode_off_);
traits.set_supports_fan_mode_auto(this->supports_fan_mode_auto_);
traits.set_supports_fan_mode_low(this->supports_fan_mode_low_);
traits.set_supports_fan_mode_medium(this->supports_fan_mode_medium_);
traits.set_supports_fan_mode_high(this->supports_fan_mode_high_);
traits.set_supports_fan_mode_middle(this->supports_fan_mode_middle_);
traits.set_supports_fan_mode_focus(this->supports_fan_mode_focus_);
traits.set_supports_fan_mode_diffuse(this->supports_fan_mode_diffuse_);
traits.set_supports_swing_mode_both(this->supports_swing_mode_both_);
traits.set_supports_swing_mode_horizontal(this->supports_swing_mode_horizontal_);
traits.set_supports_swing_mode_off(this->supports_swing_mode_off_);
traits.set_supports_swing_mode_vertical(this->supports_swing_mode_vertical_);
traits.set_supports_two_point_target_temperature(this->supports_two_points_);
traits.set_supports_away(this->supports_away_); traits.set_supports_away(this->supports_away_);
traits.set_supports_action(true); traits.set_supports_action(true);
return traits; return traits;
} }
climate::ClimateAction BangBangClimate::compute_action_() { void BangBangClimate::compute_state_() {
climate::ClimateAction target_action = this->action; if (this->mode != climate::CLIMATE_MODE_AUTO) {
if (this->supports_two_points_) { // in non-auto mode, switch directly to appropriate action
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || // - HEAT mode -> HEATING action
isnan(this->target_temperature_high) || isnan(this->hysteresis_)) // - COOL mode -> COOLING action
// if any control parameters are nan, go to OFF action (not IDLE!) // - OFF mode -> OFF action (not IDLE!)
return climate::CLIMATE_ACTION_OFF; this->switch_to_action_(static_cast<climate::ClimateAction>(this->mode));
return;
}
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
// if any control parameters are nan, go to OFF action (not IDLE!)
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
return;
}
const bool too_cold = this->current_temperature < this->target_temperature_low;
const bool too_hot = this->current_temperature > this->target_temperature_high;
if (((this->action == climate::CLIMATE_ACTION_FAN) && (this->mode != climate::CLIMATE_MODE_FAN_ONLY)) || climate::ClimateAction target_action;
((this->action == climate::CLIMATE_ACTION_DRYING) && (this->mode != climate::CLIMATE_MODE_DRY))) { if (too_cold) {
// too cold -> enable heating if possible, else idle
if (this->supports_heat_)
target_action = climate::CLIMATE_ACTION_HEATING;
else
target_action = climate::CLIMATE_ACTION_IDLE;
} else if (too_hot) {
// too hot -> enable cooling if possible, else idle
if (this->supports_cool_)
target_action = climate::CLIMATE_ACTION_COOLING;
else
target_action = climate::CLIMATE_ACTION_IDLE; target_action = climate::CLIMATE_ACTION_IDLE;
}
switch (this->mode) {
case climate::CLIMATE_MODE_FAN_ONLY:
if (this->supports_fan_only_) {
if (this->current_temperature > this->target_temperature_high + this->hysteresis_)
target_action = climate::CLIMATE_ACTION_FAN;
else if (this->current_temperature < this->target_temperature_high - this->hysteresis_)
if (this->action == climate::CLIMATE_ACTION_FAN)
target_action = climate::CLIMATE_ACTION_IDLE;
}
break;
case climate::CLIMATE_MODE_DRY:
target_action = climate::CLIMATE_ACTION_DRYING;
break;
case climate::CLIMATE_MODE_OFF:
target_action = climate::CLIMATE_ACTION_OFF;
break;
case climate::CLIMATE_MODE_AUTO:
case climate::CLIMATE_MODE_COOL:
case climate::CLIMATE_MODE_HEAT:
if (this->supports_cool_) {
if (this->current_temperature > this->target_temperature_high + this->hysteresis_)
target_action = climate::CLIMATE_ACTION_COOLING;
else if (this->current_temperature < this->target_temperature_high - this->hysteresis_)
if (this->action == climate::CLIMATE_ACTION_COOLING)
target_action = climate::CLIMATE_ACTION_IDLE;
}
if (this->supports_heat_) {
if (this->current_temperature < this->target_temperature_low - this->hysteresis_)
target_action = climate::CLIMATE_ACTION_HEATING;
else if (this->current_temperature > this->target_temperature_low + this->hysteresis_)
if (this->action == climate::CLIMATE_ACTION_HEATING)
target_action = climate::CLIMATE_ACTION_IDLE;
}
break;
default:
break;
}
} else { } else {
if (isnan(this->current_temperature) || isnan(this->target_temperature) || isnan(this->hysteresis_)) // neither too hot nor too cold -> in range
// if any control parameters are nan, go to OFF action (not IDLE!) if (this->supports_cool_ && this->supports_heat_) {
return climate::CLIMATE_ACTION_OFF; // if supports both ends, go to idle action
if (((this->action == climate::CLIMATE_ACTION_FAN) && (this->mode != climate::CLIMATE_MODE_FAN_ONLY)) ||
((this->action == climate::CLIMATE_ACTION_DRYING) && (this->mode != climate::CLIMATE_MODE_DRY))) {
target_action = climate::CLIMATE_ACTION_IDLE; target_action = climate::CLIMATE_ACTION_IDLE;
} } else {
// else use current mode and don't change (hysteresis)
switch (this->mode) { target_action = this->action;
case climate::CLIMATE_MODE_FAN_ONLY:
if (this->supports_fan_only_) {
if (this->current_temperature > this->target_temperature + this->hysteresis_)
target_action = climate::CLIMATE_ACTION_FAN;
else if (this->current_temperature < this->target_temperature - this->hysteresis_)
if (this->action == climate::CLIMATE_ACTION_FAN)
target_action = climate::CLIMATE_ACTION_IDLE;
}
break;
case climate::CLIMATE_MODE_DRY:
target_action = climate::CLIMATE_ACTION_DRYING;
break;
case climate::CLIMATE_MODE_OFF:
target_action = climate::CLIMATE_ACTION_OFF;
break;
case climate::CLIMATE_MODE_COOL:
if (this->supports_cool_) {
if (this->current_temperature > this->target_temperature + this->hysteresis_)
target_action = climate::CLIMATE_ACTION_COOLING;
else if (this->current_temperature < this->target_temperature - this->hysteresis_)
if (this->action == climate::CLIMATE_ACTION_COOLING)
target_action = climate::CLIMATE_ACTION_IDLE;
}
case climate::CLIMATE_MODE_HEAT:
if (this->supports_heat_) {
if (this->current_temperature < this->target_temperature - this->hysteresis_)
target_action = climate::CLIMATE_ACTION_HEATING;
else if (this->current_temperature > this->target_temperature + this->hysteresis_)
if (this->action == climate::CLIMATE_ACTION_HEATING)
target_action = climate::CLIMATE_ACTION_IDLE;
}
break;
default:
break;
} }
} }
// do not switch to an action that isn't enabled per the active climate mode
if ((this->mode == climate::CLIMATE_MODE_COOL) && (target_action == climate::CLIMATE_ACTION_HEATING))
target_action = climate::CLIMATE_ACTION_IDLE;
if ((this->mode == climate::CLIMATE_MODE_HEAT) && (target_action == climate::CLIMATE_ACTION_COOLING))
target_action = climate::CLIMATE_ACTION_IDLE;
return target_action; this->switch_to_action_(target_action);
} }
void BangBangClimate::switch_to_action_(climate::ClimateAction action) { void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
// setup_complete_ helps us ensure an action is called immediately after boot if (action == this->action)
if ((action == this->action) && this->setup_complete_)
// already in target mode // already in target mode
return; return;
if (((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) || if ((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) ||
(action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) && (action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) {
this->setup_complete_) {
// switching from OFF to IDLE or vice-versa // switching from OFF to IDLE or vice-versa
// these only have visual difference. OFF means user manually disabled, // these only have visual difference. OFF means user manually disabled,
// IDLE means it's in auto mode but value is in target range. // IDLE means it's in auto mode but value is in target range.
this->action = action; this->action = action;
this->publish_state();
return; return;
} }
if (this->prev_action_trigger_ != nullptr) { if (this->prev_trigger_ != nullptr) {
this->prev_action_trigger_->stop(); this->prev_trigger_->stop();
this->prev_action_trigger_ = nullptr; this->prev_trigger_ = nullptr;
} }
Trigger<> *trig = this->idle_action_trigger_; Trigger<> *trig;
switch (action) { switch (action) {
case climate::CLIMATE_ACTION_OFF: case climate::CLIMATE_ACTION_OFF:
case climate::CLIMATE_ACTION_IDLE: case climate::CLIMATE_ACTION_IDLE:
// trig = this->idle_action_trigger_; trig = this->idle_trigger_;
break; break;
case climate::CLIMATE_ACTION_COOLING: case climate::CLIMATE_ACTION_COOLING:
trig = this->cool_action_trigger_; trig = this->cool_trigger_;
break; break;
case climate::CLIMATE_ACTION_HEATING: case climate::CLIMATE_ACTION_HEATING:
trig = this->heat_action_trigger_; trig = this->heat_trigger_;
break;
case climate::CLIMATE_ACTION_FAN:
trig = this->fan_only_action_trigger_;
break;
case climate::CLIMATE_ACTION_DRYING:
trig = this->dry_action_trigger_;
break; break;
default: default:
// we cannot report an invalid mode back to HA (even if it asked for one) trig = nullptr;
// and must assume some valid value
action = climate::CLIMATE_ACTION_OFF;
// trig = this->idle_action_trigger_;
} }
assert(trig != nullptr); assert(trig != nullptr);
trig->trigger(); trig->trigger();
this->action = action; this->action = action;
this->prev_action_trigger_ = trig; this->prev_trigger_ = trig;
} this->publish_state();
void BangBangClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode) {
// setup_complete_ helps us ensure an action is called immediately after boot
if ((fan_mode == this->prev_fan_mode_) && this->setup_complete_)
// already in target mode
return;
if (this->prev_fan_mode_trigger_ != nullptr) {
this->prev_fan_mode_trigger_->stop();
this->prev_fan_mode_trigger_ = nullptr;
}
Trigger<> *trig = this->fan_mode_auto_trigger_;
switch (fan_mode) {
case climate::CLIMATE_FAN_ON:
trig = this->fan_mode_on_trigger_;
break;
case climate::CLIMATE_FAN_OFF:
trig = this->fan_mode_off_trigger_;
break;
case climate::CLIMATE_FAN_AUTO:
// trig = this->fan_mode_auto_trigger_;
break;
case climate::CLIMATE_FAN_LOW:
trig = this->fan_mode_low_trigger_;
break;
case climate::CLIMATE_FAN_MEDIUM:
trig = this->fan_mode_medium_trigger_;
break;
case climate::CLIMATE_FAN_HIGH:
trig = this->fan_mode_high_trigger_;
break;
case climate::CLIMATE_FAN_MIDDLE:
trig = this->fan_mode_middle_trigger_;
break;
case climate::CLIMATE_FAN_FOCUS:
trig = this->fan_mode_focus_trigger_;
break;
case climate::CLIMATE_FAN_DIFFUSE:
trig = this->fan_mode_diffuse_trigger_;
break;
default:
// we cannot report an invalid mode back to HA (even if it asked for one)
// and must assume some valid value
fan_mode = climate::CLIMATE_FAN_AUTO;
// trig = this->fan_mode_auto_trigger_;
}
assert(trig != nullptr);
trig->trigger();
this->fan_mode = fan_mode;
this->prev_fan_mode_ = fan_mode;
this->prev_fan_mode_trigger_ = trig;
}
void BangBangClimate::switch_to_mode_(climate::ClimateMode mode) {
// setup_complete_ helps us ensure an action is called immediately after boot
if ((mode == this->prev_mode_) && this->setup_complete_)
// already in target mode
return;
if (this->prev_mode_trigger_ != nullptr) {
this->prev_mode_trigger_->stop();
this->prev_mode_trigger_ = nullptr;
}
Trigger<> *trig = this->auto_mode_trigger_;
switch (mode) {
case climate::CLIMATE_MODE_OFF:
trig = this->off_mode_trigger_;
break;
case climate::CLIMATE_MODE_AUTO:
// trig = this->auto_mode_trigger_;
break;
case climate::CLIMATE_MODE_COOL:
trig = this->cool_mode_trigger_;
break;
case climate::CLIMATE_MODE_HEAT:
trig = this->heat_mode_trigger_;
break;
case climate::CLIMATE_MODE_FAN_ONLY:
trig = this->fan_only_mode_trigger_;
break;
case climate::CLIMATE_MODE_DRY:
trig = this->dry_mode_trigger_;
break;
default:
// we cannot report an invalid mode back to HA (even if it asked for one)
// and must assume some valid value
mode = climate::CLIMATE_MODE_AUTO;
// trig = this->auto_mode_trigger_;
}
assert(trig != nullptr);
trig->trigger();
this->mode = mode;
this->prev_mode_ = mode;
this->prev_mode_trigger_ = trig;
}
void BangBangClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mode) {
// setup_complete_ helps us ensure an action is called immediately after boot
if ((swing_mode == this->prev_swing_mode_) && this->setup_complete_)
// already in target mode
return;
if (this->prev_swing_mode_trigger_ != nullptr) {
this->prev_swing_mode_trigger_->stop();
this->prev_swing_mode_trigger_ = nullptr;
}
Trigger<> *trig = this->swing_mode_off_trigger_;
switch (swing_mode) {
case climate::CLIMATE_SWING_BOTH:
trig = this->swing_mode_both_trigger_;
break;
case climate::CLIMATE_SWING_HORIZONTAL:
trig = this->swing_mode_horizontal_trigger_;
break;
case climate::CLIMATE_SWING_OFF:
// trig = this->swing_mode_off_trigger_;
break;
case climate::CLIMATE_SWING_VERTICAL:
trig = this->swing_mode_vertical_trigger_;
break;
default:
// we cannot report an invalid mode back to HA (even if it asked for one)
// and must assume some valid value
swing_mode = climate::CLIMATE_SWING_OFF;
// trig = this->swing_mode_off_trigger_;
}
assert(trig != nullptr);
trig->trigger();
this->swing_mode = swing_mode;
this->prev_swing_mode_ = swing_mode;
this->prev_swing_mode_trigger_ = trig;
} }
void BangBangClimate::change_away_(bool away) { void BangBangClimate::change_away_(bool away) {
if (!away) { if (!away) {
if (this->supports_two_points_) { this->target_temperature_low = this->normal_config_.default_temperature_low;
this->target_temperature_low = this->normal_config_.default_temperature_low; this->target_temperature_high = this->normal_config_.default_temperature_high;
this->target_temperature_high = this->normal_config_.default_temperature_high;
} else
this->target_temperature = this->normal_config_.default_temperature;
} else { } else {
if (this->supports_two_points_) { this->target_temperature_low = this->away_config_.default_temperature_low;
this->target_temperature_low = this->away_config_.default_temperature_low; this->target_temperature_high = this->away_config_.default_temperature_high;
this->target_temperature_high = this->away_config_.default_temperature_high;
} else
this->target_temperature = this->away_config_.default_temperature;
} }
this->away = away; this->away = away;
} }
@ -407,142 +150,23 @@ void BangBangClimate::set_away_config(const BangBangClimateTargetTempConfig &awa
this->away_config_ = away_config; this->away_config_ = away_config;
} }
BangBangClimate::BangBangClimate() BangBangClimate::BangBangClimate()
: cool_action_trigger_(new Trigger<>()), : idle_trigger_(new Trigger<>()), cool_trigger_(new Trigger<>()), heat_trigger_(new Trigger<>()) {}
cool_mode_trigger_(new Trigger<>()),
dry_action_trigger_(new Trigger<>()),
dry_mode_trigger_(new Trigger<>()),
heat_action_trigger_(new Trigger<>()),
heat_mode_trigger_(new Trigger<>()),
auto_mode_trigger_(new Trigger<>()),
idle_action_trigger_(new Trigger<>()),
off_mode_trigger_(new Trigger<>()),
fan_only_action_trigger_(new Trigger<>()),
fan_only_mode_trigger_(new Trigger<>()),
fan_mode_on_trigger_(new Trigger<>()),
fan_mode_off_trigger_(new Trigger<>()),
fan_mode_auto_trigger_(new Trigger<>()),
fan_mode_low_trigger_(new Trigger<>()),
fan_mode_medium_trigger_(new Trigger<>()),
fan_mode_high_trigger_(new Trigger<>()),
fan_mode_middle_trigger_(new Trigger<>()),
fan_mode_focus_trigger_(new Trigger<>()),
fan_mode_diffuse_trigger_(new Trigger<>()),
swing_mode_both_trigger_(new Trigger<>()),
swing_mode_off_trigger_(new Trigger<>()),
swing_mode_horizontal_trigger_(new Trigger<>()),
swing_mode_vertical_trigger_(new Trigger<>()) {}
void BangBangClimate::set_hysteresis(float hysteresis) { this->hysteresis_ = hysteresis; }
void BangBangClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; } void BangBangClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }
void BangBangClimate::set_supports_auto(bool supports_auto) { this->supports_auto_ = supports_auto; } Trigger<> *BangBangClimate::get_idle_trigger() const { return this->idle_trigger_; }
Trigger<> *BangBangClimate::get_cool_trigger() const { return this->cool_trigger_; }
void BangBangClimate::set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; } void BangBangClimate::set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; }
void BangBangClimate::set_supports_dry(bool supports_dry) { this->supports_dry_ = supports_dry; } Trigger<> *BangBangClimate::get_heat_trigger() const { return this->heat_trigger_; }
void BangBangClimate::set_supports_fan_only(bool supports_fan_only) { this->supports_fan_only_ = supports_fan_only; }
void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }
void BangBangClimate::set_supports_fan_mode_on(bool supports_fan_mode_on) {
this->supports_fan_mode_on_ = supports_fan_mode_on;
}
void BangBangClimate::set_supports_fan_mode_off(bool supports_fan_mode_off) {
this->supports_fan_mode_off_ = supports_fan_mode_off;
}
void BangBangClimate::set_supports_fan_mode_auto(bool supports_fan_mode_auto) {
this->supports_fan_mode_auto_ = supports_fan_mode_auto;
}
void BangBangClimate::set_supports_fan_mode_low(bool supports_fan_mode_low) {
this->supports_fan_mode_low_ = supports_fan_mode_low;
}
void BangBangClimate::set_supports_fan_mode_medium(bool supports_fan_mode_medium) {
this->supports_fan_mode_medium_ = supports_fan_mode_medium;
}
void BangBangClimate::set_supports_fan_mode_high(bool supports_fan_mode_high) {
this->supports_fan_mode_high_ = supports_fan_mode_high;
}
void BangBangClimate::set_supports_fan_mode_middle(bool supports_fan_mode_middle) {
this->supports_fan_mode_middle_ = supports_fan_mode_middle;
}
void BangBangClimate::set_supports_fan_mode_focus(bool supports_fan_mode_focus) {
this->supports_fan_mode_focus_ = supports_fan_mode_focus;
}
void BangBangClimate::set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse) {
this->supports_fan_mode_diffuse_ = supports_fan_mode_diffuse;
}
void BangBangClimate::set_supports_swing_mode_both(bool supports_swing_mode_both) {
this->supports_swing_mode_both_ = supports_swing_mode_both;
}
void BangBangClimate::set_supports_swing_mode_off(bool supports_swing_mode_off) {
this->supports_swing_mode_off_ = supports_swing_mode_off;
}
void BangBangClimate::set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal) {
this->supports_swing_mode_horizontal_ = supports_swing_mode_horizontal;
}
void BangBangClimate::set_supports_swing_mode_vertical(bool supports_swing_mode_vertical) {
this->supports_swing_mode_vertical_ = supports_swing_mode_vertical;
}
void BangBangClimate::set_supports_two_points(bool supports_two_points) {
this->supports_two_points_ = supports_two_points;
}
Trigger<> *BangBangClimate::get_cool_action_trigger() const { return this->cool_action_trigger_; }
Trigger<> *BangBangClimate::get_dry_action_trigger() const { return this->dry_action_trigger_; }
Trigger<> *BangBangClimate::get_fan_only_action_trigger() const { return this->fan_only_action_trigger_; }
Trigger<> *BangBangClimate::get_heat_action_trigger() const { return this->heat_action_trigger_; }
Trigger<> *BangBangClimate::get_idle_action_trigger() const { return this->idle_action_trigger_; }
Trigger<> *BangBangClimate::get_auto_mode_trigger() const { return this->auto_mode_trigger_; }
Trigger<> *BangBangClimate::get_cool_mode_trigger() const { return this->cool_mode_trigger_; }
Trigger<> *BangBangClimate::get_dry_mode_trigger() const { return this->dry_mode_trigger_; }
Trigger<> *BangBangClimate::get_fan_only_mode_trigger() const { return this->fan_only_mode_trigger_; }
Trigger<> *BangBangClimate::get_heat_mode_trigger() const { return this->heat_mode_trigger_; }
Trigger<> *BangBangClimate::get_off_mode_trigger() const { return this->off_mode_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_on_trigger() const { return this->fan_mode_on_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_off_trigger() const { return this->fan_mode_off_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_auto_trigger() const { return this->fan_mode_auto_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_low_trigger() const { return this->fan_mode_low_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_medium_trigger() const { return this->fan_mode_medium_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_high_trigger() const { return this->fan_mode_high_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_middle_trigger() const { return this->fan_mode_middle_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_focus_trigger() const { return this->fan_mode_focus_trigger_; }
Trigger<> *BangBangClimate::get_fan_mode_diffuse_trigger() const { return this->fan_mode_diffuse_trigger_; }
Trigger<> *BangBangClimate::get_swing_mode_both_trigger() const { return this->swing_mode_both_trigger_; }
Trigger<> *BangBangClimate::get_swing_mode_off_trigger() const { return this->swing_mode_off_trigger_; }
Trigger<> *BangBangClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; }
Trigger<> *BangBangClimate::get_swing_mode_vertical_trigger() const { return this->swing_mode_vertical_trigger_; }
void BangBangClimate::dump_config() { void BangBangClimate::dump_config() {
LOG_CLIMATE("", "Bang Bang Climate", this); LOG_CLIMATE("", "Bang Bang Climate", this);
if (this->supports_heat_)
ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C", this->normal_config_.default_temperature_low);
if ((this->supports_cool_) || (this->supports_fan_only_))
ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C", this->normal_config_.default_temperature_high);
ESP_LOGCONFIG(TAG, " Hysteresis: %.1f°C", this->hysteresis_);
ESP_LOGCONFIG(TAG, " Supports AUTO: %s", YESNO(this->supports_auto_));
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
ESP_LOGCONFIG(TAG, " Supports DRY: %s", YESNO(this->supports_dry_));
ESP_LOGCONFIG(TAG, " Supports FAN_ONLY: %s", YESNO(this->supports_fan_only_));
ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_)); ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE ON: %s", YESNO(this->supports_fan_mode_on_)); ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE OFF: %s", YESNO(this->supports_fan_mode_off_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE AUTO: %s", YESNO(this->supports_fan_mode_auto_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE LOW: %s", YESNO(this->supports_fan_mode_low_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE MEDIUM: %s", YESNO(this->supports_fan_mode_medium_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE HIGH: %s", YESNO(this->supports_fan_mode_high_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE MIDDLE: %s", YESNO(this->supports_fan_mode_middle_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE FOCUS: %s", YESNO(this->supports_fan_mode_focus_));
ESP_LOGCONFIG(TAG, " Supports FAN MODE DIFFUSE: %s", YESNO(this->supports_fan_mode_diffuse_));
ESP_LOGCONFIG(TAG, " Supports SWING MODE BOTH: %s", YESNO(this->supports_swing_mode_both_));
ESP_LOGCONFIG(TAG, " Supports SWING MODE OFF: %s", YESNO(this->supports_swing_mode_off_));
ESP_LOGCONFIG(TAG, " Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_));
ESP_LOGCONFIG(TAG, " Supports SWING MODE VERTICAL: %s", YESNO(this->supports_swing_mode_vertical_));
ESP_LOGCONFIG(TAG, " Supports TWO SET POINTS: %s", YESNO(this->supports_two_points_));
ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_)); ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_));
if (this->supports_away_) { ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.1f°C", this->normal_config_.default_temperature_low);
if (this->supports_heat_) ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.1f°C", this->normal_config_.default_temperature_high);
ESP_LOGCONFIG(TAG, " Away Default Target Temperature Low: %.1f°C", this->away_config_.default_temperature_low);
if ((this->supports_cool_) || (this->supports_fan_only_))
ESP_LOGCONFIG(TAG, " Away Default Target Temperature High: %.1f°C",
this->away_config_.default_temperature_high);
}
} }
BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default; BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default;
BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig(float default_temperature)
: default_temperature(default_temperature) {}
BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig(float default_temperature_low, BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig(float default_temperature_low,
float default_temperature_high) float default_temperature_high)
: default_temperature_low(default_temperature_low), default_temperature_high(default_temperature_high) {} : default_temperature_low(default_temperature_low), default_temperature_high(default_temperature_high) {}

View file

@ -11,13 +11,10 @@ namespace bang_bang {
struct BangBangClimateTargetTempConfig { struct BangBangClimateTargetTempConfig {
public: public:
BangBangClimateTargetTempConfig(); BangBangClimateTargetTempConfig();
BangBangClimateTargetTempConfig(float default_temperature);
BangBangClimateTargetTempConfig(float default_temperature_low, float default_temperature_high); BangBangClimateTargetTempConfig(float default_temperature_low, float default_temperature_high);
float default_temperature{NAN};
float default_temperature_low{NAN}; float default_temperature_low{NAN};
float default_temperature_high{NAN}; float default_temperature_high{NAN};
float hysteresis{NAN};
}; };
class BangBangClimate : public climate::Climate, public Component { class BangBangClimate : public climate::Climate, public Component {
@ -26,243 +23,62 @@ class BangBangClimate : public climate::Climate, public Component {
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
void set_hysteresis(float hysteresis);
void set_sensor(sensor::Sensor *sensor); void set_sensor(sensor::Sensor *sensor);
void set_supports_auto(bool supports_auto); Trigger<> *get_idle_trigger() const;
Trigger<> *get_cool_trigger() const;
void set_supports_cool(bool supports_cool); void set_supports_cool(bool supports_cool);
void set_supports_dry(bool supports_dry); Trigger<> *get_heat_trigger() const;
void set_supports_fan_only(bool supports_fan_only);
void set_supports_heat(bool supports_heat); void set_supports_heat(bool supports_heat);
void set_supports_fan_mode_on(bool supports_fan_mode_on);
void set_supports_fan_mode_off(bool supports_fan_mode_off);
void set_supports_fan_mode_auto(bool supports_fan_mode_auto);
void set_supports_fan_mode_low(bool supports_fan_mode_low);
void set_supports_fan_mode_medium(bool supports_fan_mode_medium);
void set_supports_fan_mode_high(bool supports_fan_mode_high);
void set_supports_fan_mode_middle(bool supports_fan_mode_middle);
void set_supports_fan_mode_focus(bool supports_fan_mode_focus);
void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse);
void set_supports_swing_mode_both(bool supports_swing_mode_both);
void set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal);
void set_supports_swing_mode_off(bool supports_swing_mode_off);
void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
void set_supports_two_points(bool supports_two_points);
void set_normal_config(const BangBangClimateTargetTempConfig &normal_config); void set_normal_config(const BangBangClimateTargetTempConfig &normal_config);
void set_away_config(const BangBangClimateTargetTempConfig &away_config); void set_away_config(const BangBangClimateTargetTempConfig &away_config);
Trigger<> *get_cool_action_trigger() const;
Trigger<> *get_dry_action_trigger() const;
Trigger<> *get_fan_only_action_trigger() const;
Trigger<> *get_heat_action_trigger() const;
Trigger<> *get_idle_action_trigger() const;
Trigger<> *get_auto_mode_trigger() const;
Trigger<> *get_cool_mode_trigger() const;
Trigger<> *get_dry_mode_trigger() const;
Trigger<> *get_fan_only_mode_trigger() const;
Trigger<> *get_heat_mode_trigger() const;
Trigger<> *get_off_mode_trigger() const;
Trigger<> *get_fan_mode_on_trigger() const;
Trigger<> *get_fan_mode_off_trigger() const;
Trigger<> *get_fan_mode_auto_trigger() const;
Trigger<> *get_fan_mode_low_trigger() const;
Trigger<> *get_fan_mode_medium_trigger() const;
Trigger<> *get_fan_mode_high_trigger() const;
Trigger<> *get_fan_mode_middle_trigger() const;
Trigger<> *get_fan_mode_focus_trigger() const;
Trigger<> *get_fan_mode_diffuse_trigger() const;
Trigger<> *get_swing_mode_both_trigger() const;
Trigger<> *get_swing_mode_horizontal_trigger() const;
Trigger<> *get_swing_mode_off_trigger() const;
Trigger<> *get_swing_mode_vertical_trigger() const;
/// Call triggers based on updated climate states (modes/actions)
void refresh();
protected: protected:
/// Override control to change settings of the climate device. /// Override control to change settings of the climate device.
void control(const climate::ClimateCall &call) override; void control(const climate::ClimateCall &call) override;
/// Change the away setting, will reset target temperatures to defaults. /// Change the away setting, will reset target temperatures to defaults.
void change_away_(bool away); void change_away_(bool away);
/// Return the traits of this controller. /// Return the traits of this controller.
climate::ClimateTraits traits() override; climate::ClimateTraits traits() override;
/// Re-compute the required action of this climate controller. /// Re-compute the state of this climate controller.
climate::ClimateAction compute_action_(); void compute_state_();
/// Switch the climate device to the given climate action.
void switch_to_action_(climate::ClimateAction action);
/// Switch the climate device to the given climate fan mode.
void switch_to_fan_mode_(climate::ClimateFanMode fan_mode);
/// Switch the climate device to the given climate mode. /// Switch the climate device to the given climate mode.
void switch_to_mode_(climate::ClimateMode mode); void switch_to_action_(climate::ClimateAction action);
/// Switch the climate device to the given climate swing mode.
void switch_to_swing_mode_(climate::ClimateSwingMode swing_mode);
/// The sensor used for getting the current temperature /// The sensor used for getting the current temperature
sensor::Sensor *sensor_{nullptr}; sensor::Sensor *sensor_{nullptr};
/** The trigger to call when the controller should switch to idle mode.
/// Whether the controller supports auto/cooling/drying/fanning/heating. *
/// * In idle mode, the controller is assumed to have both heating and cooling disabled.
/// A false value for any given attribute means that the controller has no such action */
/// (for example a thermostat, where only heating and not-heating is possible). Trigger<> *idle_trigger_;
bool supports_auto_{false}; /** The trigger to call when the controller should switch to cooling mode.
*/
Trigger<> *cool_trigger_;
/** Whether the controller supports cooling.
*
* A false value for this attribute means that the controller has no cooling action
* (for example a thermostat, where only heating and not-heating is possible).
*/
bool supports_cool_{false}; bool supports_cool_{false};
bool supports_dry_{false}; /** The trigger to call when the controller should switch to heating mode.
bool supports_fan_only_{false}; *
* A null value for this attribute means that the controller has no heating action
* For example window blinds, where only cooling (blinds closed) and not-cooling
* (blinds open) is possible.
*/
Trigger<> *heat_trigger_{nullptr};
bool supports_heat_{false}; bool supports_heat_{false};
/** A reference to the trigger that was previously active.
*
* This is so that the previous trigger can be stopped before enabling a new one.
*/
Trigger<> *prev_trigger_{nullptr};
/// Whether the controller supports turning on or off just the fan.
///
/// A false value for either attribute means that the controller has no fan on/off action
/// (for example a thermostat, where independent control of the fan is not possible).
bool supports_fan_mode_on_{false};
bool supports_fan_mode_off_{false};
/// Whether the controller supports fan auto mode.
///
/// A false value for this attribute means that the controller has no fan-auto action
/// (for example a thermostat, where independent control of the fan is not possible).
bool supports_fan_mode_auto_{false};
/// Whether the controller supports various fan speeds and/or positions.
///
/// A false value for any given attribute means that the controller has no such fan action.
bool supports_fan_mode_low_{false};
bool supports_fan_mode_medium_{false};
bool supports_fan_mode_high_{false};
bool supports_fan_mode_middle_{false};
bool supports_fan_mode_focus_{false};
bool supports_fan_mode_diffuse_{false};
/// Whether the controller supports various swing modes.
///
/// A false value for any given attribute means that the controller has no such swing mode.
bool supports_swing_mode_both_{false};
bool supports_swing_mode_off_{false};
bool supports_swing_mode_horizontal_{false};
bool supports_swing_mode_vertical_{false};
/// Whether the controller supports two set points
///
/// A false value means that the controller has no such support.
bool supports_two_points_{false};
/// Whether the controller supports an "away" mode
///
/// A false value means that the controller has no such mode.
bool supports_away_{false};
/// The trigger to call when the controller should switch to cooling action/mode.
///
/// A null value for this attribute means that the controller has no cooling action
/// For example electric heat, where only heating (power on) and not-heating
/// (power off) is possible.
Trigger<> *cool_action_trigger_{nullptr};
Trigger<> *cool_mode_trigger_{nullptr};
/// The trigger to call when the controller should switch to dry (dehumidification) mode.
///
/// In dry mode, the controller is assumed to have both heating and cooling disabled,
/// although the system may use its cooling mechanism to achieve drying.
Trigger<> *dry_action_trigger_{nullptr};
Trigger<> *dry_mode_trigger_{nullptr};
/// The trigger to call when the controller should switch to heating action/mode.
///
/// A null value for this attribute means that the controller has no heating action
/// For example window blinds, where only cooling (blinds closed) and not-cooling
/// (blinds open) is possible.
Trigger<> *heat_action_trigger_{nullptr};
Trigger<> *heat_mode_trigger_{nullptr};
/// The trigger to call when the controller should switch to auto mode.
///
/// In auto mode, the controller will enable heating/cooling as necessary and switch
/// to idle when the temperature is within the thresholds/set points.
Trigger<> *auto_mode_trigger_{nullptr};
/// The trigger to call when the controller should switch to idle action/off mode.
///
/// In these actions/modes, the controller is assumed to have both heating and cooling disabled.
Trigger<> *idle_action_trigger_{nullptr};
Trigger<> *off_mode_trigger_{nullptr};
/// The trigger to call when the controller should switch to fan-only action/mode.
///
/// In fan-only mode, the controller is assumed to have both heating and cooling disabled.
/// The system should activate the fan only.
Trigger<> *fan_only_action_trigger_{nullptr};
Trigger<> *fan_only_mode_trigger_{nullptr};
/// The trigger to call when the controller should switch on the fan.
Trigger<> *fan_mode_on_trigger_{nullptr};
/// The trigger to call when the controller should switch off the fan.
Trigger<> *fan_mode_off_trigger_{nullptr};
/// The trigger to call when the controller should switch the fan to "auto" mode.
Trigger<> *fan_mode_auto_trigger_{nullptr};
/// The trigger to call when the controller should switch the fan to "low" speed.
Trigger<> *fan_mode_low_trigger_{nullptr};
/// The trigger to call when the controller should switch the fan to "medium" speed.
Trigger<> *fan_mode_medium_trigger_{nullptr};
/// The trigger to call when the controller should switch the fan to "high" speed.
Trigger<> *fan_mode_high_trigger_{nullptr};
/// The trigger to call when the controller should switch the fan to "middle" position.
Trigger<> *fan_mode_middle_trigger_{nullptr};
/// The trigger to call when the controller should switch the fan to "focus" position.
Trigger<> *fan_mode_focus_trigger_{nullptr};
/// The trigger to call when the controller should switch the fan to "diffuse" position.
Trigger<> *fan_mode_diffuse_trigger_{nullptr};
/// The trigger to call when the controller should switch the swing mode to "both".
Trigger<> *swing_mode_both_trigger_{nullptr};
/// The trigger to call when the controller should switch the swing mode to "off".
Trigger<> *swing_mode_off_trigger_{nullptr};
/// The trigger to call when the controller should switch the swing mode to "horizontal".
Trigger<> *swing_mode_horizontal_trigger_{nullptr};
/// The trigger to call when the controller should switch the swing mode to "vertical".
Trigger<> *swing_mode_vertical_trigger_{nullptr};
/// A reference to the trigger that was previously active.
///
/// This is so that the previous trigger can be stopped before enabling a new one
/// for each climate category (mode, action, fan_mode, swing_mode).
Trigger<> *prev_action_trigger_{nullptr};
Trigger<> *prev_fan_mode_trigger_{nullptr};
Trigger<> *prev_mode_trigger_{nullptr};
Trigger<> *prev_swing_mode_trigger_{nullptr};
/// Store previously-known states
///
/// These are used to determine when a trigger/action needs to be called
climate::ClimateFanMode prev_fan_mode_{climate::CLIMATE_FAN_ON};
climate::ClimateMode prev_mode_{climate::CLIMATE_MODE_OFF};
climate::ClimateSwingMode prev_swing_mode_{climate::CLIMATE_SWING_OFF};
/// Temperature data for normal/home and away modes
BangBangClimateTargetTempConfig normal_config_{}; BangBangClimateTargetTempConfig normal_config_{};
bool supports_away_{false};
BangBangClimateTargetTempConfig away_config_{}; BangBangClimateTargetTempConfig away_config_{};
/// Hysteresis value used for computing climate actions
float hysteresis_{0};
/// setup_complete_ blocks modifying/resetting the temps immediately after boot
bool setup_complete_{false};
}; };
} // namespace bang_bang } // namespace bang_bang

View file

@ -2,107 +2,27 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.components import climate, sensor from esphome.components import climate, sensor
from esphome.const import CONF_AUTO_MODE, CONF_AWAY_CONFIG, CONF_COOL_ACTION, CONF_COOL_MODE, \ from esphome.const import CONF_AWAY_CONFIG, CONF_COOL_ACTION, \
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_DRY_ACTION, \ CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION, \
CONF_DRY_MODE, CONF_FAN_MODE_ON_ACTION, CONF_FAN_MODE_OFF_ACTION, CONF_FAN_MODE_AUTO_ACTION, \ CONF_ID, CONF_IDLE_ACTION, CONF_SENSOR
CONF_FAN_MODE_LOW_ACTION, CONF_FAN_MODE_MEDIUM_ACTION, CONF_FAN_MODE_HIGH_ACTION, \
CONF_FAN_MODE_MIDDLE_ACTION, CONF_FAN_MODE_FOCUS_ACTION, CONF_FAN_MODE_DIFFUSE_ACTION, \
CONF_FAN_ONLY_ACTION, CONF_FAN_ONLY_MODE, CONF_HEAT_ACTION, CONF_HEAT_MODE, CONF_HYSTERESIS, \
CONF_ID, CONF_IDLE_ACTION, CONF_OFF_MODE, CONF_SENSOR, CONF_SWING_BOTH_ACTION, \
CONF_SWING_HORIZONTAL_ACTION, CONF_SWING_OFF_ACTION, CONF_SWING_VERTICAL_ACTION
bang_bang_ns = cg.esphome_ns.namespace('bang_bang') bang_bang_ns = cg.esphome_ns.namespace('bang_bang')
BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.Climate, cg.Component) BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.Climate, cg.Component)
BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig') BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig')
def validate_bangbang(config):
# verify corresponding climate action action exists for any defined climate mode action
if CONF_COOL_MODE in config and CONF_COOL_ACTION not in config:
raise cv.Invalid("{} must be defined to use {}".format(CONF_COOL_ACTION, CONF_COOL_MODE))
if CONF_DRY_MODE in config and CONF_DRY_ACTION not in config:
raise cv.Invalid("{} must be defined to use {}".format(CONF_DRY_ACTION, CONF_DRY_MODE))
if CONF_FAN_ONLY_MODE in config and CONF_FAN_ONLY_ACTION not in config:
raise cv.Invalid("{} must be defined to use {}".format(CONF_FAN_ONLY_ACTION,
CONF_FAN_ONLY_MODE))
if CONF_HEAT_MODE in config and CONF_HEAT_ACTION not in config:
raise cv.Invalid("{} must be defined to use {}".format(CONF_HEAT_ACTION, CONF_HEAT_MODE))
# verify corresponding default target temperature exists when a given climate action exists
if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH not in config and (CONF_COOL_ACTION in config
or CONF_FAN_ONLY_ACTION in config):
raise cv.Invalid("{} must be defined when using {} or {}".format(
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION, CONF_FAN_ONLY_ACTION))
if CONF_DEFAULT_TARGET_TEMPERATURE_LOW not in config and CONF_HEAT_ACTION in config:
raise cv.Invalid("{} must be defined when using {}".format(
CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION))
# if a given climate action is NOT defined, it should not have a default target temperature
if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in config and (CONF_COOL_ACTION not in config
and CONF_FAN_ONLY_ACTION not in config):
raise cv.Invalid("{} is defined with no {}".format(
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION))
if CONF_DEFAULT_TARGET_TEMPERATURE_LOW in config and CONF_HEAT_ACTION not in config:
raise cv.Invalid("{} is defined with no {}".format(
CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION))
if CONF_AWAY_CONFIG in config:
away = config[CONF_AWAY_CONFIG]
# verify corresponding default target temperature exists when a given climate action exists
if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH not in away and (CONF_COOL_ACTION in config or
CONF_FAN_ONLY_ACTION in config):
raise cv.Invalid("{} must be defined in away configuration when using {} or {}".format(
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION, CONF_FAN_ONLY_ACTION))
if CONF_DEFAULT_TARGET_TEMPERATURE_LOW not in away and CONF_HEAT_ACTION in config:
raise cv.Invalid("{} must be defined in away configuration when using {}".format(
CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION))
# if a given climate action is NOT defined, it should not have a default target temperature
if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in away and (CONF_COOL_ACTION not in config and
CONF_FAN_ONLY_ACTION not in config):
raise cv.Invalid("{} is defined in away configuration with no {} or {}".format(
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION, CONF_FAN_ONLY_ACTION))
if CONF_DEFAULT_TARGET_TEMPERATURE_LOW in away and CONF_HEAT_ACTION not in config:
raise cv.Invalid("{} is defined in away configuration with no {}".format(
CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION))
return config
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(BangBangClimate), cv.GenerateID(): cv.declare_id(BangBangClimate),
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True), cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_DRY_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_ONLY_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True), cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_AUTO_MODE): automation.validate_automation(single=True),
cv.Optional(CONF_COOL_MODE): automation.validate_automation(single=True),
cv.Optional(CONF_DRY_MODE): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_ONLY_MODE): automation.validate_automation(single=True),
cv.Optional(CONF_HEAT_MODE): automation.validate_automation(single=True),
cv.Optional(CONF_OFF_MODE): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_ON_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_OFF_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_AUTO_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_LOW_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_MEDIUM_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_HIGH_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_MIDDLE_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_FOCUS_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_FAN_MODE_DIFFUSE_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_SWING_BOTH_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_SWING_HORIZONTAL_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_SWING_OFF_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_SWING_VERTICAL_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
cv.Optional(CONF_HYSTERESIS, default=0.5): cv.temperature,
cv.Optional(CONF_AWAY_CONFIG): cv.Schema({ cv.Optional(CONF_AWAY_CONFIG): cv.Schema({
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
}), }),
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_DRY_ACTION, }).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION))
CONF_FAN_ONLY_ACTION, CONF_HEAT_ACTION),
validate_bangbang)
def to_code(config): def to_code(config):
@ -110,145 +30,28 @@ def to_code(config):
yield cg.register_component(var, config) yield cg.register_component(var, config)
yield climate.register_climate(var, config) yield climate.register_climate(var, config)
auto_mode_available = CONF_HEAT_ACTION in config and CONF_COOL_ACTION in config
two_points_available = CONF_HEAT_ACTION in config and (CONF_COOL_ACTION in config or
CONF_FAN_ONLY_ACTION in config)
sens = yield cg.get_variable(config[CONF_SENSOR]) sens = yield cg.get_variable(config[CONF_SENSOR])
cg.add(var.set_sensor(sens)) cg.add(var.set_sensor(sens))
cg.add(var.set_hysteresis(config[CONF_HYSTERESIS]))
if two_points_available is True: normal_config = BangBangClimateTargetTempConfig(
cg.add(var.set_supports_two_points(True)) config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
normal_config = BangBangClimateTargetTempConfig( config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH]
config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], )
config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH]
)
elif CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in config:
cg.add(var.set_supports_two_points(False))
normal_config = BangBangClimateTargetTempConfig(
config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH]
)
elif CONF_DEFAULT_TARGET_TEMPERATURE_LOW in config:
cg.add(var.set_supports_two_points(False))
normal_config = BangBangClimateTargetTempConfig(
config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]
)
cg.add(var.set_normal_config(normal_config)) cg.add(var.set_normal_config(normal_config))
yield automation.build_automation(var.get_idle_action_trigger(), [], yield automation.build_automation(var.get_idle_trigger(), [], config[CONF_IDLE_ACTION])
config[CONF_IDLE_ACTION])
if auto_mode_available is True:
cg.add(var.set_supports_auto(True))
else:
cg.add(var.set_supports_auto(False))
if CONF_COOL_ACTION in config: if CONF_COOL_ACTION in config:
yield automation.build_automation(var.get_cool_action_trigger(), [], yield automation.build_automation(var.get_cool_trigger(), [], config[CONF_COOL_ACTION])
config[CONF_COOL_ACTION])
cg.add(var.set_supports_cool(True)) cg.add(var.set_supports_cool(True))
if CONF_DRY_ACTION in config:
yield automation.build_automation(var.get_dry_action_trigger(), [],
config[CONF_DRY_ACTION])
cg.add(var.set_supports_dry(True))
if CONF_FAN_ONLY_ACTION in config:
yield automation.build_automation(var.get_fan_only_action_trigger(), [],
config[CONF_FAN_ONLY_ACTION])
cg.add(var.set_supports_fan_only(True))
if CONF_HEAT_ACTION in config: if CONF_HEAT_ACTION in config:
yield automation.build_automation(var.get_heat_action_trigger(), [], yield automation.build_automation(var.get_heat_trigger(), [], config[CONF_HEAT_ACTION])
config[CONF_HEAT_ACTION])
cg.add(var.set_supports_heat(True)) cg.add(var.set_supports_heat(True))
if CONF_AUTO_MODE in config:
yield automation.build_automation(var.get_auto_mode_trigger(), [],
config[CONF_AUTO_MODE])
if CONF_COOL_MODE in config:
yield automation.build_automation(var.get_cool_mode_trigger(), [],
config[CONF_COOL_MODE])
cg.add(var.set_supports_cool(True))
if CONF_DRY_MODE in config:
yield automation.build_automation(var.get_dry_mode_trigger(), [],
config[CONF_DRY_MODE])
cg.add(var.set_supports_dry(True))
if CONF_FAN_ONLY_MODE in config:
yield automation.build_automation(var.get_fan_only_mode_trigger(), [],
config[CONF_FAN_ONLY_MODE])
cg.add(var.set_supports_fan_only(True))
if CONF_HEAT_MODE in config:
yield automation.build_automation(var.get_heat_mode_trigger(), [],
config[CONF_HEAT_MODE])
cg.add(var.set_supports_heat(True))
if CONF_OFF_MODE in config:
yield automation.build_automation(var.get_off_mode_trigger(), [],
config[CONF_OFF_MODE])
if CONF_FAN_MODE_ON_ACTION in config:
yield automation.build_automation(var.get_fan_mode_on_trigger(), [],
config[CONF_FAN_MODE_ON_ACTION])
cg.add(var.set_supports_fan_mode_on(True))
if CONF_FAN_MODE_OFF_ACTION in config:
yield automation.build_automation(var.get_fan_mode_off_trigger(), [],
config[CONF_FAN_MODE_OFF_ACTION])
cg.add(var.set_supports_fan_mode_off(True))
if CONF_FAN_MODE_AUTO_ACTION in config:
yield automation.build_automation(var.get_fan_mode_auto_trigger(), [],
config[CONF_FAN_MODE_AUTO_ACTION])
cg.add(var.set_supports_fan_mode_auto(True))
if CONF_FAN_MODE_LOW_ACTION in config:
yield automation.build_automation(var.get_fan_mode_low_trigger(), [],
config[CONF_FAN_MODE_LOW_ACTION])
cg.add(var.set_supports_fan_mode_low(True))
if CONF_FAN_MODE_MEDIUM_ACTION in config:
yield automation.build_automation(var.get_fan_mode_medium_trigger(), [],
config[CONF_FAN_MODE_MEDIUM_ACTION])
cg.add(var.set_supports_fan_mode_medium(True))
if CONF_FAN_MODE_HIGH_ACTION in config:
yield automation.build_automation(var.get_fan_mode_high_trigger(), [],
config[CONF_FAN_MODE_HIGH_ACTION])
cg.add(var.set_supports_fan_mode_high(True))
if CONF_FAN_MODE_MIDDLE_ACTION in config:
yield automation.build_automation(var.get_fan_mode_middle_trigger(), [],
config[CONF_FAN_MODE_MIDDLE_ACTION])
cg.add(var.set_supports_fan_mode_middle(True))
if CONF_FAN_MODE_FOCUS_ACTION in config:
yield automation.build_automation(var.get_fan_mode_focus_trigger(), [],
config[CONF_FAN_MODE_FOCUS_ACTION])
cg.add(var.set_supports_fan_mode_focus(True))
if CONF_FAN_MODE_DIFFUSE_ACTION in config:
yield automation.build_automation(var.get_fan_mode_diffuse_trigger(), [],
config[CONF_FAN_MODE_DIFFUSE_ACTION])
cg.add(var.set_supports_fan_mode_diffuse(True))
if CONF_SWING_BOTH_ACTION in config:
yield automation.build_automation(var.get_swing_mode_both_trigger(), [],
config[CONF_SWING_BOTH_ACTION])
cg.add(var.set_supports_swing_mode_both(True))
if CONF_SWING_HORIZONTAL_ACTION in config:
yield automation.build_automation(var.get_swing_mode_horizontal_trigger(), [],
config[CONF_SWING_HORIZONTAL_ACTION])
cg.add(var.set_supports_swing_mode_horizontal(True))
if CONF_SWING_OFF_ACTION in config:
yield automation.build_automation(var.get_swing_mode_off_trigger(), [],
config[CONF_SWING_OFF_ACTION])
cg.add(var.set_supports_swing_mode_off(True))
if CONF_SWING_VERTICAL_ACTION in config:
yield automation.build_automation(var.get_swing_mode_vertical_trigger(), [],
config[CONF_SWING_VERTICAL_ACTION])
cg.add(var.set_supports_swing_mode_vertical(True))
if CONF_AWAY_CONFIG in config: if CONF_AWAY_CONFIG in config:
away = config[CONF_AWAY_CONFIG] away = config[CONF_AWAY_CONFIG]
away_config = BangBangClimateTargetTempConfig(
if two_points_available is True: away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
away_config = BangBangClimateTargetTempConfig( away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH]
away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], )
away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH]
)
elif CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in away:
away_config = BangBangClimateTargetTempConfig(
away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH]
)
elif CONF_DEFAULT_TARGET_TEMPERATURE_LOW in away:
away_config = BangBangClimateTargetTempConfig(
away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]
)
cg.add(var.set_away_config(away_config)) cg.add(var.set_away_config(away_config))

View file

@ -44,7 +44,6 @@ CONF_ASSUMED_STATE = 'assumed_state'
CONF_AT = 'at' CONF_AT = 'at'
CONF_ATTENUATION = 'attenuation' CONF_ATTENUATION = 'attenuation'
CONF_AUTH = 'auth' CONF_AUTH = 'auth'
CONF_AUTO_MODE = 'auto_mode'
CONF_AUTOMATION_ID = 'automation_id' CONF_AUTOMATION_ID = 'automation_id'
CONF_AVAILABILITY = 'availability' CONF_AVAILABILITY = 'availability'
CONF_AWAY = 'away' CONF_AWAY = 'away'
@ -103,7 +102,6 @@ CONF_CONDITION = 'condition'
CONF_CONDITION_ID = 'condition_id' CONF_CONDITION_ID = 'condition_id'
CONF_CONDUCTIVITY = 'conductivity' CONF_CONDUCTIVITY = 'conductivity'
CONF_COOL_ACTION = 'cool_action' CONF_COOL_ACTION = 'cool_action'
CONF_COOL_MODE = 'cool_mode'
CONF_COUNT_MODE = 'count_mode' CONF_COUNT_MODE = 'count_mode'
CONF_CRON = 'cron' CONF_CRON = 'cron'
CONF_CS_PIN = 'cs_pin' CONF_CS_PIN = 'cs_pin'
@ -142,8 +140,6 @@ CONF_DIV_RATIO = 'div_ratio'
CONF_DNS1 = 'dns1' CONF_DNS1 = 'dns1'
CONF_DNS2 = 'dns2' CONF_DNS2 = 'dns2'
CONF_DOMAIN = 'domain' CONF_DOMAIN = 'domain'
CONF_DRY_ACTION = 'dry_action'
CONF_DRY_MODE = 'dry_mode'
CONF_DUMP = 'dump' CONF_DUMP = 'dump'
CONF_DURATION = 'duration' CONF_DURATION = 'duration'
CONF_ECHO_PIN = 'echo_pin' CONF_ECHO_PIN = 'echo_pin'
@ -163,17 +159,6 @@ CONF_EXTERNAL_VCC = 'external_vcc'
CONF_FALLING_EDGE = 'falling_edge' CONF_FALLING_EDGE = 'falling_edge'
CONF_FAMILY = 'family' CONF_FAMILY = 'family'
CONF_FAN_MODE = 'fan_mode' CONF_FAN_MODE = 'fan_mode'
CONF_FAN_MODE_AUTO_ACTION = 'fan_mode_auto_action'
CONF_FAN_MODE_DIFFUSE_ACTION = 'fan_mode_diffuse_action'
CONF_FAN_MODE_FOCUS_ACTION = 'fan_mode_focus_action'
CONF_FAN_MODE_HIGH_ACTION = 'fan_mode_high_action'
CONF_FAN_MODE_LOW_ACTION = 'fan_mode_low_action'
CONF_FAN_MODE_MEDIUM_ACTION = 'fan_mode_medium_action'
CONF_FAN_MODE_MIDDLE_ACTION = 'fan_mode_middle_action'
CONF_FAN_MODE_OFF_ACTION = 'fan_mode_off_action'
CONF_FAN_MODE_ON_ACTION = 'fan_mode_on_action'
CONF_FAN_ONLY_ACTION = 'fan_only_action'
CONF_FAN_ONLY_MODE = 'fan_only_mode'
CONF_FAST_CONNECT = 'fast_connect' CONF_FAST_CONNECT = 'fast_connect'
CONF_FILE = 'file' CONF_FILE = 'file'
CONF_FILTER = 'filter' CONF_FILTER = 'filter'
@ -198,7 +183,6 @@ CONF_GROUP = 'group'
CONF_HARDWARE_UART = 'hardware_uart' CONF_HARDWARE_UART = 'hardware_uart'
CONF_HEARTBEAT = 'heartbeat' CONF_HEARTBEAT = 'heartbeat'
CONF_HEAT_ACTION = 'heat_action' CONF_HEAT_ACTION = 'heat_action'
CONF_HEAT_MODE = 'heat_mode'
CONF_HEATER = 'heater' CONF_HEATER = 'heater'
CONF_HIDDEN = 'hidden' CONF_HIDDEN = 'hidden'
CONF_HIGH = 'high' CONF_HIGH = 'high'
@ -206,7 +190,6 @@ CONF_HIGH_VOLTAGE_REFERENCE = 'high_voltage_reference'
CONF_HOUR = 'hour' CONF_HOUR = 'hour'
CONF_HOURS = 'hours' CONF_HOURS = 'hours'
CONF_HUMIDITY = 'humidity' CONF_HUMIDITY = 'humidity'
CONF_HYSTERESIS = "hysteresis"
CONF_I2C = 'i2c' CONF_I2C = 'i2c'
CONF_I2C_ID = 'i2c_id' CONF_I2C_ID = 'i2c_id'
CONF_ICON = 'icon' CONF_ICON = 'icon'
@ -302,7 +285,6 @@ CONF_NUM_CHANNELS = 'num_channels'
CONF_NUM_CHIPS = 'num_chips' CONF_NUM_CHIPS = 'num_chips'
CONF_NUM_LEDS = 'num_leds' CONF_NUM_LEDS = 'num_leds'
CONF_NUMBER = 'number' CONF_NUMBER = 'number'
CONF_OFF_MODE = 'off_mode'
CONF_OFFSET = 'offset' CONF_OFFSET = 'offset'
CONF_ON = 'on' CONF_ON = 'on'
CONF_ON_BLE_ADVERTISE = 'on_ble_advertise' CONF_ON_BLE_ADVERTISE = 'on_ble_advertise'
@ -467,11 +449,7 @@ CONF_STOP_ACTION = 'stop_action'
CONF_SUBNET = 'subnet' CONF_SUBNET = 'subnet'
CONF_SUPPORTS_COOL = 'supports_cool' CONF_SUPPORTS_COOL = 'supports_cool'
CONF_SUPPORTS_HEAT = 'supports_heat' CONF_SUPPORTS_HEAT = 'supports_heat'
CONF_SWING_BOTH_ACTION = 'swing_both_action'
CONF_SWING_HORIZONTAL_ACTION = 'swing_horizontal_action'
CONF_SWING_MODE = 'swing_mode' CONF_SWING_MODE = 'swing_mode'
CONF_SWING_OFF_ACTION = 'swing_off_action'
CONF_SWING_VERTICAL_ACTION = 'swing_vertical_action'
CONF_SWITCHES = 'switches' CONF_SWITCHES = 'switches'
CONF_SYNC = 'sync' CONF_SYNC = 'sync'
CONF_TABLET = 'tablet' CONF_TABLET = 'tablet'

View file

@ -621,49 +621,6 @@ climate:
- switch.turn_on: gpio_switch2 - switch.turn_on: gpio_switch2
heat_action: heat_action:
- switch.turn_on: gpio_switch1 - switch.turn_on: gpio_switch1
dry_action:
- switch.turn_on: gpio_switch2
fan_only_action:
- switch.turn_on: gpio_switch1
auto_mode:
- switch.turn_on: gpio_switch2
off_mode:
- switch.turn_on: gpio_switch1
heat_mode:
- switch.turn_on: gpio_switch2
cool_mode:
- switch.turn_on: gpio_switch1
dry_mode:
- switch.turn_on: gpio_switch2
fan_only_mode:
- switch.turn_on: gpio_switch1
fan_mode_auto_action:
- switch.turn_on: gpio_switch2
fan_mode_on_action:
- switch.turn_on: gpio_switch1
fan_mode_off_action:
- switch.turn_on: gpio_switch2
fan_mode_low_action:
- switch.turn_on: gpio_switch1
fan_mode_medium_action:
- switch.turn_on: gpio_switch2
fan_mode_high_action:
- switch.turn_on: gpio_switch1
fan_mode_middle_action:
- switch.turn_on: gpio_switch2
fan_mode_focus_action:
- switch.turn_on: gpio_switch1
fan_mode_diffuse_action:
- switch.turn_on: gpio_switch2
swing_off_action:
- switch.turn_on: gpio_switch1
swing_horizontal_action:
- switch.turn_on: gpio_switch2
swing_vertical_action:
- switch.turn_on: gpio_switch1
swing_both_action:
- switch.turn_on: gpio_switch2
hysteresis: 0.2
away_config: away_config:
default_target_temperature_low: 16°C default_target_temperature_low: 16°C
default_target_temperature_high: 20°C default_target_temperature_high: 20°C