Fix unchecked optional access in template, tuya, thermostat, and other components

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
J. Nick Koston
2026-02-27 20:23:46 -10:00
parent 554c395efa
commit e2290367ab
21 changed files with 268 additions and 224 deletions
@@ -9,7 +9,10 @@ static const char *const TAG = "output.lock";
void OutputLock::dump_config() { LOG_LOCK("", "Output Lock", this); }
void OutputLock::control(const lock::LockCall &call) {
auto state = *call.get_state();
auto state_val = call.get_state();
if (!state_val.has_value())
return;
auto state = *state_val;
if (state == lock::LOCK_STATE_LOCKED) {
this->output_->turn_on();
} else if (state == lock::LOCK_STATE_UNLOCKED) {
+4 -4
View File
@@ -41,10 +41,10 @@ void PIDClimate::setup() {
}
}
void PIDClimate::control(const climate::ClimateCall &call) {
if (call.get_mode().has_value())
this->mode = *call.get_mode();
if (call.get_target_temperature().has_value())
this->target_temperature = *call.get_target_temperature();
if (auto val = call.get_mode(); val.has_value())
this->mode = *val;
if (auto val = call.get_target_temperature(); val.has_value())
this->target_temperature = *val;
// If switching to off mode, set output immediately
if (this->mode == climate::CLIMATE_MODE_OFF)
+4 -1
View File
@@ -26,7 +26,10 @@ void PZEM004T::loop() {
// PZEM004T packet size is 7 byte
while (this->available() >= 7) {
auto resp = *this->read_array<7>();
auto resp_opt = this->read_array<7>();
if (!resp_opt.has_value())
break;
auto resp = *resp_opt;
// packet format:
// 0: packet type
// 1-5: data
@@ -257,14 +257,16 @@ void TemplateAlarmControlPanel::bypass_before_arming() {
}
void TemplateAlarmControlPanel::control(const AlarmControlPanelCall &call) {
if (call.get_state()) {
if (call.get_state() == ACP_STATE_ARMED_AWAY) {
auto opt_state = call.get_state();
if (opt_state) {
auto state = *opt_state;
if (state == ACP_STATE_ARMED_AWAY) {
this->arm_(call.get_code(), ACP_STATE_ARMED_AWAY, this->arming_away_time_);
} else if (call.get_state() == ACP_STATE_ARMED_HOME) {
} else if (state == ACP_STATE_ARMED_HOME) {
this->arm_(call.get_code(), ACP_STATE_ARMED_HOME, this->arming_home_time_);
} else if (call.get_state() == ACP_STATE_ARMED_NIGHT) {
} else if (state == ACP_STATE_ARMED_NIGHT) {
this->arm_(call.get_code(), ACP_STATE_ARMED_NIGHT, this->arming_night_time_);
} else if (call.get_state() == ACP_STATE_DISARMED) {
} else if (state == ACP_STATE_DISARMED) {
if (!this->is_code_valid_(call.get_code())) {
ESP_LOGW(TAG, "Not disarming code doesn't match");
return;
@@ -274,13 +276,12 @@ void TemplateAlarmControlPanel::control(const AlarmControlPanelCall &call) {
#ifdef USE_BINARY_SENSOR
this->bypassed_sensor_indicies_.clear();
#endif
} else if (call.get_state() == ACP_STATE_TRIGGERED) {
} else if (state == ACP_STATE_TRIGGERED) {
this->publish_state(ACP_STATE_TRIGGERED);
} else if (call.get_state() == ACP_STATE_PENDING) {
} else if (state == ACP_STATE_PENDING) {
this->publish_state(ACP_STATE_PENDING);
} else {
ESP_LOGE(TAG, "State not yet implemented: %s",
LOG_STR_ARG(alarm_control_panel_state_to_string(*call.get_state())));
ESP_LOGE(TAG, "State not yet implemented: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(state)));
}
}
}
@@ -74,8 +74,8 @@ void TemplateCover::control(const CoverCall &call) {
this->prev_command_trigger_ = &this->toggle_trigger_;
this->publish_state();
}
if (call.get_position().has_value()) {
auto pos = *call.get_position();
if (auto pos_val = call.get_position(); pos_val.has_value()) {
auto pos = *pos_val;
this->stop_prev_trigger_();
if (pos == COVER_OPEN) {
@@ -93,8 +93,8 @@ void TemplateCover::control(const CoverCall &call) {
}
}
if (call.get_tilt().has_value()) {
auto tilt = *call.get_tilt();
if (auto tilt_val = call.get_tilt(); tilt_val.has_value()) {
auto tilt = *tilt_val;
this->tilt_trigger_.trigger(tilt);
if (this->optimistic_) {
@@ -48,46 +48,49 @@ void TemplateDate::update() {
}
void TemplateDate::control(const datetime::DateCall &call) {
bool has_year = call.get_year().has_value();
bool has_month = call.get_month().has_value();
bool has_day = call.get_day().has_value();
auto opt_year = call.get_year();
auto opt_month = call.get_month();
auto opt_day = call.get_day();
bool has_year = opt_year.has_value();
bool has_month = opt_month.has_value();
bool has_day = opt_day.has_value();
ESPTime value = {};
if (has_year)
value.year = *call.get_year();
value.year = *opt_year;
if (has_month)
value.month = *call.get_month();
value.month = *opt_month;
if (has_day)
value.day_of_month = *call.get_day();
value.day_of_month = *opt_day;
this->set_trigger_.trigger(value);
if (this->optimistic_) {
if (has_year)
this->year_ = *call.get_year();
this->year_ = *opt_year;
if (has_month)
this->month_ = *call.get_month();
this->month_ = *opt_month;
if (has_day)
this->day_ = *call.get_day();
this->day_ = *opt_day;
this->publish_state();
}
if (this->restore_value_) {
datetime::DateEntityRestoreState temp = {};
if (has_year) {
temp.year = *call.get_year();
temp.year = *opt_year;
} else {
temp.year = this->year_;
}
if (has_month) {
temp.month = *call.get_month();
temp.month = *opt_month;
} else {
temp.month = this->month_;
}
if (has_day) {
temp.day = *call.get_day();
temp.day = *opt_day;
} else {
temp.day = this->day_;
}
@@ -54,79 +54,85 @@ void TemplateDateTime::update() {
}
void TemplateDateTime::control(const datetime::DateTimeCall &call) {
bool has_year = call.get_year().has_value();
bool has_month = call.get_month().has_value();
bool has_day = call.get_day().has_value();
bool has_hour = call.get_hour().has_value();
bool has_minute = call.get_minute().has_value();
bool has_second = call.get_second().has_value();
auto opt_year = call.get_year();
auto opt_month = call.get_month();
auto opt_day = call.get_day();
auto opt_hour = call.get_hour();
auto opt_minute = call.get_minute();
auto opt_second = call.get_second();
bool has_year = opt_year.has_value();
bool has_month = opt_month.has_value();
bool has_day = opt_day.has_value();
bool has_hour = opt_hour.has_value();
bool has_minute = opt_minute.has_value();
bool has_second = opt_second.has_value();
ESPTime value = {};
if (has_year)
value.year = *call.get_year();
value.year = *opt_year;
if (has_month)
value.month = *call.get_month();
value.month = *opt_month;
if (has_day)
value.day_of_month = *call.get_day();
value.day_of_month = *opt_day;
if (has_hour)
value.hour = *call.get_hour();
value.hour = *opt_hour;
if (has_minute)
value.minute = *call.get_minute();
value.minute = *opt_minute;
if (has_second)
value.second = *call.get_second();
value.second = *opt_second;
this->set_trigger_.trigger(value);
if (this->optimistic_) {
if (has_year)
this->year_ = *call.get_year();
this->year_ = *opt_year;
if (has_month)
this->month_ = *call.get_month();
this->month_ = *opt_month;
if (has_day)
this->day_ = *call.get_day();
this->day_ = *opt_day;
if (has_hour)
this->hour_ = *call.get_hour();
this->hour_ = *opt_hour;
if (has_minute)
this->minute_ = *call.get_minute();
this->minute_ = *opt_minute;
if (has_second)
this->second_ = *call.get_second();
this->second_ = *opt_second;
this->publish_state();
}
if (this->restore_value_) {
datetime::DateTimeEntityRestoreState temp = {};
if (has_year) {
temp.year = *call.get_year();
temp.year = *opt_year;
} else {
temp.year = this->year_;
}
if (has_month) {
temp.month = *call.get_month();
temp.month = *opt_month;
} else {
temp.month = this->month_;
}
if (has_day) {
temp.day = *call.get_day();
temp.day = *opt_day;
} else {
temp.day = this->day_;
}
if (has_hour) {
temp.hour = *call.get_hour();
temp.hour = *opt_hour;
} else {
temp.hour = this->hour_;
}
if (has_minute) {
temp.minute = *call.get_minute();
temp.minute = *opt_minute;
} else {
temp.minute = this->minute_;
}
if (has_second) {
temp.second = *call.get_second();
temp.second = *opt_second;
} else {
temp.second = this->second_;
}
@@ -48,46 +48,49 @@ void TemplateTime::update() {
}
void TemplateTime::control(const datetime::TimeCall &call) {
bool has_hour = call.get_hour().has_value();
bool has_minute = call.get_minute().has_value();
bool has_second = call.get_second().has_value();
auto opt_hour = call.get_hour();
auto opt_minute = call.get_minute();
auto opt_second = call.get_second();
bool has_hour = opt_hour.has_value();
bool has_minute = opt_minute.has_value();
bool has_second = opt_second.has_value();
ESPTime value = {};
if (has_hour)
value.hour = *call.get_hour();
value.hour = *opt_hour;
if (has_minute)
value.minute = *call.get_minute();
value.minute = *opt_minute;
if (has_second)
value.second = *call.get_second();
value.second = *opt_second;
this->set_trigger_.trigger(value);
if (this->optimistic_) {
if (has_hour)
this->hour_ = *call.get_hour();
this->hour_ = *opt_hour;
if (has_minute)
this->minute_ = *call.get_minute();
this->minute_ = *opt_minute;
if (has_second)
this->second_ = *call.get_second();
this->second_ = *opt_second;
this->publish_state();
}
if (this->restore_value_) {
datetime::TimeEntityRestoreState temp = {};
if (has_hour) {
temp.hour = *call.get_hour();
temp.hour = *opt_hour;
} else {
temp.hour = this->hour_;
}
if (has_minute) {
temp.minute = *call.get_minute();
temp.minute = *opt_minute;
} else {
temp.minute = this->minute_;
}
if (has_second) {
temp.second = *call.get_second();
temp.second = *opt_second;
} else {
temp.second = this->second_;
}
@@ -20,14 +20,14 @@ void TemplateFan::setup() {
void TemplateFan::dump_config() { LOG_FAN("", "Template Fan", this); }
void TemplateFan::control(const fan::FanCall &call) {
if (call.get_state().has_value())
this->state = *call.get_state();
if (call.get_speed().has_value() && (this->speed_count_ > 0))
this->speed = *call.get_speed();
if (call.get_oscillating().has_value() && this->has_oscillating_)
this->oscillating = *call.get_oscillating();
if (call.get_direction().has_value() && this->has_direction_)
this->direction = *call.get_direction();
if (auto val = call.get_state(); val.has_value())
this->state = *val;
if (auto val = call.get_speed(); val.has_value() && (this->speed_count_ > 0))
this->speed = *val;
if (auto val = call.get_oscillating(); val.has_value() && this->has_oscillating_)
this->oscillating = *val;
if (auto val = call.get_direction(); val.has_value() && this->has_direction_)
this->direction = *val;
this->apply_preset_mode_(call);
this->publish_state();
@@ -25,7 +25,10 @@ void TemplateLock::control(const lock::LockCall &call) {
this->prev_trigger_->stop_action();
}
auto state = *call.get_state();
auto opt_state = call.get_state();
if (!opt_state.has_value())
return;
auto state = *opt_state;
if (state == LOCK_STATE_LOCKED) {
this->prev_trigger_ = &this->lock_trigger_;
this->lock_trigger_.trigger();
@@ -77,8 +77,8 @@ void TemplateValve::control(const ValveCall &call) {
this->prev_command_trigger_ = &this->toggle_trigger_;
this->publish_state();
}
if (call.get_position().has_value()) {
auto pos = *call.get_position();
if (auto pos_val = call.get_position(); pos_val.has_value()) {
auto pos = *pos_val;
this->stop_prev_trigger_();
if (pos == VALVE_OPEN) {
@@ -101,9 +101,9 @@ water_heater::WaterHeaterCallInternal TemplateWaterHeater::make_call() {
}
void TemplateWaterHeater::control(const water_heater::WaterHeaterCall &call) {
if (call.get_mode().has_value()) {
if (auto val = call.get_mode(); val.has_value()) {
if (this->optimistic_) {
this->mode_ = *call.get_mode();
this->mode_ = *val;
}
}
if (!std::isnan(call.get_target_temperature())) {
@@ -112,14 +112,14 @@ void TemplateWaterHeater::control(const water_heater::WaterHeaterCall &call) {
}
}
if (call.get_away().has_value()) {
if (auto val = call.get_away(); val.has_value()) {
if (this->optimistic_) {
this->set_state_flag_(water_heater::WATER_HEATER_STATE_AWAY, *call.get_away());
this->set_state_flag_(water_heater::WATER_HEATER_STATE_AWAY, *val);
}
}
if (call.get_on().has_value()) {
if (auto val = call.get_on(); val.has_value()) {
if (this->optimistic_) {
this->set_state_flag_(water_heater::WATER_HEATER_STATE_ON, *call.get_on());
this->set_state_flag_(water_heater::WATER_HEATER_STATE_ON, *val);
}
}
@@ -211,12 +211,12 @@ void ThermostatClimate::validate_target_humidity() {
void ThermostatClimate::control(const climate::ClimateCall &call) {
bool target_temperature_high_changed = false;
if (call.get_preset().has_value()) {
if (auto val = call.get_preset(); val.has_value()) {
// setup_complete_ blocks modifying/resetting the temps immediately after boot
if (this->setup_complete_) {
this->change_preset_(call.get_preset().value());
this->change_preset_(*val);
} else {
this->preset = call.get_preset().value();
this->preset = val;
}
}
if (call.has_custom_preset()) {
@@ -229,34 +229,34 @@ void ThermostatClimate::control(const climate::ClimateCall &call) {
}
}
if (call.get_mode().has_value()) {
this->mode = call.get_mode().value();
if (auto val = call.get_mode(); val.has_value()) {
this->mode = *val;
}
if (call.get_fan_mode().has_value()) {
this->fan_mode = call.get_fan_mode().value();
if (auto val = call.get_fan_mode(); val.has_value()) {
this->fan_mode = val;
}
if (call.get_swing_mode().has_value()) {
this->swing_mode = call.get_swing_mode().value();
if (auto val = call.get_swing_mode(); val.has_value()) {
this->swing_mode = *val;
}
if (this->supports_two_points_) {
if (call.get_target_temperature_low().has_value()) {
this->target_temperature_low = call.get_target_temperature_low().value();
if (auto val = call.get_target_temperature_low(); val.has_value()) {
this->target_temperature_low = *val;
}
if (call.get_target_temperature_high().has_value()) {
target_temperature_high_changed = this->target_temperature_high != call.get_target_temperature_high().value();
this->target_temperature_high = call.get_target_temperature_high().value();
if (auto val = call.get_target_temperature_high(); val.has_value()) {
target_temperature_high_changed = this->target_temperature_high != *val;
this->target_temperature_high = *val;
}
// ensure the two set points are valid and adjust one of them if necessary
this->validate_target_temperatures(target_temperature_high_changed ||
(this->prev_mode_ == climate::CLIMATE_MODE_COOL));
} else {
if (call.get_target_temperature().has_value()) {
this->target_temperature = call.get_target_temperature().value();
if (auto val = call.get_target_temperature(); val.has_value()) {
this->target_temperature = *val;
this->validate_target_temperature();
}
}
if (call.get_target_humidity().has_value()) {
this->target_humidity = call.get_target_humidity().value();
if (auto val = call.get_target_humidity(); val.has_value()) {
this->target_humidity = *val;
this->validate_target_humidity();
}
// make any changes happen
@@ -1264,9 +1264,9 @@ bool ThermostatClimate::change_preset_internal_(const ThermostatClimateTargetTem
something_changed = true;
}
if (config.fan_mode_.has_value() && (this->fan_mode != config.fan_mode_.value())) {
if (config.fan_mode_.has_value() && (this->fan_mode != config.fan_mode_)) {
ESP_LOGV(TAG, "Setting fan mode to %s", LOG_STR_ARG(climate::climate_fan_mode_to_string(*config.fan_mode_)));
this->fan_mode = *config.fan_mode_;
this->fan_mode = config.fan_mode_;
something_changed = true;
}
@@ -79,8 +79,8 @@ void TimeBasedCover::control(const CoverCall &call) {
}
}
}
if (call.get_position().has_value()) {
auto pos = *call.get_position();
if (auto pos_val = call.get_position(); pos_val.has_value()) {
auto pos = *pos_val;
if (pos == this->position) {
// already at target
if (this->manual_control_ && (pos == COVER_OPEN || pos == COVER_CLOSED)) {
@@ -66,8 +66,8 @@ void Tormatic::control(const cover::CoverCall &call) {
return;
}
if (call.get_position().has_value()) {
auto pos = call.get_position().value();
if (auto pos_val = call.get_position(); pos_val.has_value()) {
auto pos = *pos_val;
this->control_position_(pos);
return;
}
@@ -7,8 +7,8 @@ namespace tuya {
static const char *const TAG = "tuya.climate";
void TuyaClimate::setup() {
if (this->switch_id_.has_value()) {
this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) {
if (auto switch_id = this->switch_id_; switch_id.has_value()) {
this->parent_->register_listener(*switch_id, [this](const TuyaDatapoint &datapoint) {
ESP_LOGV(TAG, "MCU reported switch is: %s", ONOFF(datapoint.value_bool));
this->mode = climate::CLIMATE_MODE_OFF;
if (datapoint.value_bool) {
@@ -32,16 +32,16 @@ void TuyaClimate::setup() {
this->cooling_state_pin_->setup();
this->cooling_state_ = this->cooling_state_pin_->digital_read();
}
if (this->active_state_id_.has_value()) {
this->parent_->register_listener(*this->active_state_id_, [this](const TuyaDatapoint &datapoint) {
if (auto active_state_id = this->active_state_id_; active_state_id.has_value()) {
this->parent_->register_listener(*active_state_id, [this](const TuyaDatapoint &datapoint) {
ESP_LOGV(TAG, "MCU reported active state is: %u", datapoint.value_enum);
this->active_state_ = datapoint.value_enum;
this->compute_state_();
this->publish_state();
});
}
if (this->target_temperature_id_.has_value()) {
this->parent_->register_listener(*this->target_temperature_id_, [this](const TuyaDatapoint &datapoint) {
if (auto target_temp_id = this->target_temperature_id_; target_temp_id.has_value()) {
this->parent_->register_listener(*target_temp_id, [this](const TuyaDatapoint &datapoint) {
this->manual_temperature_ = datapoint.value_int * this->target_temperature_multiplier_;
if (this->reports_fahrenheit_) {
this->manual_temperature_ = (this->manual_temperature_ - 32) * 5 / 9;
@@ -53,8 +53,8 @@ void TuyaClimate::setup() {
this->publish_state();
});
}
if (this->current_temperature_id_.has_value()) {
this->parent_->register_listener(*this->current_temperature_id_, [this](const TuyaDatapoint &datapoint) {
if (auto current_temp_id = this->current_temperature_id_; current_temp_id.has_value()) {
this->parent_->register_listener(*current_temp_id, [this](const TuyaDatapoint &datapoint) {
this->current_temperature = datapoint.value_int * this->current_temperature_multiplier_;
if (this->reports_fahrenheit_) {
this->current_temperature = (this->current_temperature - 32) * 5 / 9;
@@ -65,8 +65,8 @@ void TuyaClimate::setup() {
this->publish_state();
});
}
if (this->eco_id_.has_value()) {
this->parent_->register_listener(*this->eco_id_, [this](const TuyaDatapoint &datapoint) {
if (auto eco_id = this->eco_id_; eco_id.has_value()) {
this->parent_->register_listener(*eco_id, [this](const TuyaDatapoint &datapoint) {
// Whether data type is BOOL or ENUM, it will still be a 1 or a 0, so the functions below are valid in both cases
this->eco_ = datapoint.value_bool;
this->eco_type_ = datapoint.type;
@@ -76,8 +76,8 @@ void TuyaClimate::setup() {
this->publish_state();
});
}
if (this->sleep_id_.has_value()) {
this->parent_->register_listener(*this->sleep_id_, [this](const TuyaDatapoint &datapoint) {
if (auto sleep_id = this->sleep_id_; sleep_id.has_value()) {
this->parent_->register_listener(*sleep_id, [this](const TuyaDatapoint &datapoint) {
this->sleep_ = datapoint.value_bool;
ESP_LOGV(TAG, "MCU reported sleep is: %s", ONOFF(this->sleep_));
this->compute_preset_();
@@ -85,8 +85,8 @@ void TuyaClimate::setup() {
this->publish_state();
});
}
if (this->swing_vertical_id_.has_value()) {
this->parent_->register_listener(*this->swing_vertical_id_, [this](const TuyaDatapoint &datapoint) {
if (auto swing_vert_id = this->swing_vertical_id_; swing_vert_id.has_value()) {
this->parent_->register_listener(*swing_vert_id, [this](const TuyaDatapoint &datapoint) {
this->swing_vertical_ = datapoint.value_bool;
ESP_LOGV(TAG, "MCU reported vertical swing is: %s", ONOFF(datapoint.value_bool));
this->compute_swingmode_();
@@ -94,8 +94,8 @@ void TuyaClimate::setup() {
});
}
if (this->swing_horizontal_id_.has_value()) {
this->parent_->register_listener(*this->swing_horizontal_id_, [this](const TuyaDatapoint &datapoint) {
if (auto swing_horiz_id = this->swing_horizontal_id_; swing_horiz_id.has_value()) {
this->parent_->register_listener(*swing_horiz_id, [this](const TuyaDatapoint &datapoint) {
this->swing_horizontal_ = datapoint.value_bool;
ESP_LOGV(TAG, "MCU reported horizontal swing is: %s", ONOFF(datapoint.value_bool));
this->compute_swingmode_();
@@ -103,8 +103,8 @@ void TuyaClimate::setup() {
});
}
if (this->fan_speed_id_.has_value()) {
this->parent_->register_listener(*this->fan_speed_id_, [this](const TuyaDatapoint &datapoint) {
if (auto fan_speed_id = this->fan_speed_id_; fan_speed_id.has_value()) {
this->parent_->register_listener(*fan_speed_id, [this](const TuyaDatapoint &datapoint) {
ESP_LOGV(TAG, "MCU reported Fan Speed Mode is: %u", datapoint.value_enum);
this->fan_state_ = datapoint.value_enum;
this->compute_fanmode_();
@@ -139,21 +139,27 @@ void TuyaClimate::loop() {
}
void TuyaClimate::control(const climate::ClimateCall &call) {
if (call.get_mode().has_value()) {
const bool switch_state = *call.get_mode() != climate::CLIMATE_MODE_OFF;
if (auto mode = call.get_mode(); mode.has_value()) {
const bool switch_state = *mode != climate::CLIMATE_MODE_OFF;
ESP_LOGV(TAG, "Setting switch: %s", ONOFF(switch_state));
this->parent_->set_boolean_datapoint_value(*this->switch_id_, switch_state);
const climate::ClimateMode new_mode = *call.get_mode();
if (auto id = this->switch_id_; id.has_value()) {
this->parent_->set_boolean_datapoint_value(*id, switch_state);
}
const climate::ClimateMode new_mode = *mode;
if (this->active_state_id_.has_value()) {
if (auto id = this->active_state_id_; id.has_value()) {
if (new_mode == climate::CLIMATE_MODE_HEAT && this->supports_heat_) {
this->parent_->set_enum_datapoint_value(*this->active_state_id_, *this->active_state_heating_value_);
if (auto val = this->active_state_heating_value_; val.has_value())
this->parent_->set_enum_datapoint_value(*id, *val);
} else if (new_mode == climate::CLIMATE_MODE_COOL && this->supports_cool_) {
this->parent_->set_enum_datapoint_value(*this->active_state_id_, *this->active_state_cooling_value_);
} else if (new_mode == climate::CLIMATE_MODE_DRY && this->active_state_drying_value_.has_value()) {
this->parent_->set_enum_datapoint_value(*this->active_state_id_, *this->active_state_drying_value_);
} else if (new_mode == climate::CLIMATE_MODE_FAN_ONLY && this->active_state_fanonly_value_.has_value()) {
this->parent_->set_enum_datapoint_value(*this->active_state_id_, *this->active_state_fanonly_value_);
if (auto val = this->active_state_cooling_value_; val.has_value())
this->parent_->set_enum_datapoint_value(*id, *val);
} else if (new_mode == climate::CLIMATE_MODE_DRY) {
if (auto val = this->active_state_drying_value_; val.has_value())
this->parent_->set_enum_datapoint_value(*id, *val);
} else if (new_mode == climate::CLIMATE_MODE_FAN_ONLY) {
if (auto val = this->active_state_fanonly_value_; val.has_value())
this->parent_->set_enum_datapoint_value(*id, *val);
}
} else {
ESP_LOGW(TAG, "Active state (mode) datapoint not configured");
@@ -163,31 +169,33 @@ void TuyaClimate::control(const climate::ClimateCall &call) {
control_swing_mode_(call);
control_fan_mode_(call);
if (call.get_target_temperature().has_value()) {
float target_temperature = *call.get_target_temperature();
if (auto target_temp = call.get_target_temperature(); target_temp.has_value()) {
float target_temperature = *target_temp;
if (this->reports_fahrenheit_)
target_temperature = (target_temperature * 9 / 5) + 32;
ESP_LOGV(TAG, "Setting target temperature: %.1f", target_temperature);
this->parent_->set_integer_datapoint_value(*this->target_temperature_id_,
(int) (target_temperature / this->target_temperature_multiplier_));
if (auto id = this->target_temperature_id_; id.has_value()) {
this->parent_->set_integer_datapoint_value(*id,
(int) (target_temperature / this->target_temperature_multiplier_));
}
}
if (call.get_preset().has_value()) {
const climate::ClimatePreset preset = *call.get_preset();
if (this->eco_id_.has_value()) {
if (auto preset_val = call.get_preset(); preset_val.has_value()) {
const climate::ClimatePreset preset = *preset_val;
if (auto id = this->eco_id_; id.has_value()) {
const bool eco = preset == climate::CLIMATE_PRESET_ECO;
ESP_LOGV(TAG, "Setting eco: %s", ONOFF(eco));
if (this->eco_type_ == TuyaDatapointType::ENUM) {
this->parent_->set_enum_datapoint_value(*this->eco_id_, eco);
this->parent_->set_enum_datapoint_value(*id, eco);
} else {
this->parent_->set_boolean_datapoint_value(*this->eco_id_, eco);
this->parent_->set_boolean_datapoint_value(*id, eco);
}
}
if (this->sleep_id_.has_value()) {
if (auto id = this->sleep_id_; id.has_value()) {
const bool sleep = preset == climate::CLIMATE_PRESET_SLEEP;
ESP_LOGV(TAG, "Setting sleep: %s", ONOFF(sleep));
this->parent_->set_boolean_datapoint_value(*this->sleep_id_, sleep);
this->parent_->set_boolean_datapoint_value(*id, sleep);
}
}
}
@@ -196,8 +204,8 @@ void TuyaClimate::control_swing_mode_(const climate::ClimateCall &call) {
bool vertical_swing_changed = false;
bool horizontal_swing_changed = false;
if (call.get_swing_mode().has_value()) {
const auto swing_mode = *call.get_swing_mode();
if (auto swing_mode_val = call.get_swing_mode(); swing_mode_val.has_value()) {
const auto swing_mode = *swing_mode_val;
switch (swing_mode) {
case climate::CLIMATE_SWING_OFF:
@@ -241,14 +249,14 @@ void TuyaClimate::control_swing_mode_(const climate::ClimateCall &call) {
}
}
if (vertical_swing_changed && this->swing_vertical_id_.has_value()) {
if (auto id = this->swing_vertical_id_; vertical_swing_changed && id.has_value()) {
ESP_LOGV(TAG, "Setting vertical swing: %s", ONOFF(swing_vertical_));
this->parent_->set_boolean_datapoint_value(*this->swing_vertical_id_, swing_vertical_);
this->parent_->set_boolean_datapoint_value(*id, swing_vertical_);
}
if (horizontal_swing_changed && this->swing_horizontal_id_.has_value()) {
if (auto id = this->swing_horizontal_id_; horizontal_swing_changed && id.has_value()) {
ESP_LOGV(TAG, "Setting horizontal swing: %s", ONOFF(swing_horizontal_));
this->parent_->set_boolean_datapoint_value(*this->swing_horizontal_id_, swing_horizontal_);
this->parent_->set_boolean_datapoint_value(*id, swing_horizontal_);
}
// Publish the state after updating the swing mode
@@ -256,33 +264,33 @@ void TuyaClimate::control_swing_mode_(const climate::ClimateCall &call) {
}
void TuyaClimate::control_fan_mode_(const climate::ClimateCall &call) {
if (call.get_fan_mode().has_value()) {
climate::ClimateFanMode fan_mode = *call.get_fan_mode();
if (auto fan_mode_val = call.get_fan_mode(); fan_mode_val.has_value()) {
climate::ClimateFanMode fan_mode = *fan_mode_val;
uint8_t tuya_fan_speed;
switch (fan_mode) {
case climate::CLIMATE_FAN_LOW:
tuya_fan_speed = *fan_speed_low_value_;
tuya_fan_speed = this->fan_speed_low_value_.value_or(0);
break;
case climate::CLIMATE_FAN_MEDIUM:
tuya_fan_speed = *fan_speed_medium_value_;
tuya_fan_speed = this->fan_speed_medium_value_.value_or(0);
break;
case climate::CLIMATE_FAN_MIDDLE:
tuya_fan_speed = *fan_speed_middle_value_;
tuya_fan_speed = this->fan_speed_middle_value_.value_or(0);
break;
case climate::CLIMATE_FAN_HIGH:
tuya_fan_speed = *fan_speed_high_value_;
tuya_fan_speed = this->fan_speed_high_value_.value_or(0);
break;
case climate::CLIMATE_FAN_AUTO:
tuya_fan_speed = *fan_speed_auto_value_;
tuya_fan_speed = this->fan_speed_auto_value_.value_or(0);
break;
default:
tuya_fan_speed = 0;
break;
}
if (this->fan_speed_id_.has_value()) {
this->parent_->set_enum_datapoint_value(*this->fan_speed_id_, tuya_fan_speed);
if (auto id = this->fan_speed_id_; id.has_value()) {
this->parent_->set_enum_datapoint_value(*id, tuya_fan_speed);
}
}
}
@@ -337,31 +345,31 @@ climate::ClimateTraits TuyaClimate::traits() {
void TuyaClimate::dump_config() {
LOG_CLIMATE("", "Tuya Climate", this);
if (this->switch_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_);
if (auto id = this->switch_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *id);
}
if (this->active_state_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Active state has datapoint ID %u", *this->active_state_id_);
if (auto id = this->active_state_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Active state has datapoint ID %u", *id);
}
if (this->target_temperature_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Target Temperature has datapoint ID %u", *this->target_temperature_id_);
if (auto id = this->target_temperature_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Target Temperature has datapoint ID %u", *id);
}
if (this->current_temperature_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *this->current_temperature_id_);
if (auto id = this->current_temperature_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Current Temperature has datapoint ID %u", *id);
}
LOG_PIN(" Heating State Pin: ", this->heating_state_pin_);
LOG_PIN(" Cooling State Pin: ", this->cooling_state_pin_);
if (this->eco_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Eco has datapoint ID %u", *this->eco_id_);
if (auto id = this->eco_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Eco has datapoint ID %u", *id);
}
if (this->sleep_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Sleep has datapoint ID %u", *this->sleep_id_);
if (auto id = this->sleep_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Sleep has datapoint ID %u", *id);
}
if (this->swing_vertical_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Swing Vertical has datapoint ID %u", *this->swing_vertical_id_);
if (auto id = this->swing_vertical_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Swing Vertical has datapoint ID %u", *id);
}
if (this->swing_horizontal_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Swing Horizontal has datapoint ID %u", *this->swing_horizontal_id_);
if (auto id = this->swing_horizontal_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Swing Horizontal has datapoint ID %u", *id);
}
}
+9 -6
View File
@@ -39,6 +39,9 @@ void TuyaCover::setup() {
}
});
if (!this->position_id_.has_value()) {
return;
}
uint8_t report_id = *this->position_id_;
if (this->position_report_id_.has_value()) {
// A position report datapoint is configured; listen to that instead.
@@ -60,29 +63,29 @@ void TuyaCover::control(const cover::CoverCall &call) {
if (call.get_stop()) {
if (this->control_id_.has_value()) {
this->parent_->force_set_enum_datapoint_value(*this->control_id_, COMMAND_STOP);
} else {
} else if (this->position_id_.has_value()) {
auto pos = this->position;
pos = this->invert_position_report_ ? pos : 1.0f - pos;
auto position_int = static_cast<uint32_t>(pos * this->value_range_);
position_int = position_int + this->min_value_;
parent_->force_set_integer_datapoint_value(*this->position_id_, position_int);
this->parent_->force_set_integer_datapoint_value(*this->position_id_, position_int);
}
}
if (call.get_position().has_value()) {
auto pos = *call.get_position();
if (auto pos_opt = call.get_position(); pos_opt.has_value()) {
auto pos = *pos_opt;
if (this->control_id_.has_value() && (pos == COVER_OPEN || pos == COVER_CLOSED)) {
if (pos == COVER_OPEN) {
this->parent_->force_set_enum_datapoint_value(*this->control_id_, COMMAND_OPEN);
} else {
this->parent_->force_set_enum_datapoint_value(*this->control_id_, COMMAND_CLOSE);
}
} else {
} else if (this->position_id_.has_value()) {
pos = this->invert_position_report_ ? pos : 1.0f - pos;
auto position_int = static_cast<uint32_t>(pos * this->value_range_);
position_int = position_int + this->min_value_;
parent_->force_set_integer_datapoint_value(*this->position_id_, position_int);
this->parent_->force_set_integer_datapoint_value(*this->position_id_, position_int);
}
}
+40 -32
View File
@@ -7,8 +7,8 @@ namespace tuya {
static const char *const TAG = "tuya.fan";
void TuyaFan::setup() {
if (this->speed_id_.has_value()) {
this->parent_->register_listener(*this->speed_id_, [this](const TuyaDatapoint &datapoint) {
if (auto speed_id = this->speed_id_; speed_id.has_value()) {
this->parent_->register_listener(*speed_id, [this](const TuyaDatapoint &datapoint) {
if (datapoint.type == TuyaDatapointType::ENUM) {
ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum);
if (datapoint.value_enum >= this->speed_count_) {
@@ -25,15 +25,15 @@ void TuyaFan::setup() {
this->speed_type_ = datapoint.type;
});
}
if (this->switch_id_.has_value()) {
this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) {
if (auto switch_id = this->switch_id_; switch_id.has_value()) {
this->parent_->register_listener(*switch_id, [this](const TuyaDatapoint &datapoint) {
ESP_LOGV(TAG, "MCU reported switch is: %s", ONOFF(datapoint.value_bool));
this->state = datapoint.value_bool;
this->publish_state();
});
}
if (this->oscillation_id_.has_value()) {
this->parent_->register_listener(*this->oscillation_id_, [this](const TuyaDatapoint &datapoint) {
if (auto oscillation_id = this->oscillation_id_; oscillation_id.has_value()) {
this->parent_->register_listener(*oscillation_id, [this](const TuyaDatapoint &datapoint) {
// Whether data type is BOOL or ENUM, it will still be a 1 or a 0, so the functions below are valid in both
// scenarios
ESP_LOGV(TAG, "MCU reported oscillation is: %s", ONOFF(datapoint.value_bool));
@@ -43,8 +43,8 @@ void TuyaFan::setup() {
this->oscillation_type_ = datapoint.type;
});
}
if (this->direction_id_.has_value()) {
this->parent_->register_listener(*this->direction_id_, [this](const TuyaDatapoint &datapoint) {
if (auto direction_id = this->direction_id_; direction_id.has_value()) {
this->parent_->register_listener(*direction_id, [this](const TuyaDatapoint &datapoint) {
ESP_LOGD(TAG, "MCU reported reverse direction is: %s", ONOFF(datapoint.value_bool));
this->direction = datapoint.value_bool ? fan::FanDirection::REVERSE : fan::FanDirection::FORWARD;
this->publish_state();
@@ -60,17 +60,17 @@ void TuyaFan::setup() {
void TuyaFan::dump_config() {
LOG_FAN("", "Tuya Fan", this);
if (this->speed_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Speed has datapoint ID %u", *this->speed_id_);
if (auto id = this->speed_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Speed has datapoint ID %u", *id);
}
if (this->switch_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *this->switch_id_);
if (auto id = this->switch_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Switch has datapoint ID %u", *id);
}
if (this->oscillation_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Oscillation has datapoint ID %u", *this->oscillation_id_);
if (auto id = this->oscillation_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Oscillation has datapoint ID %u", *id);
}
if (this->direction_id_.has_value()) {
ESP_LOGCONFIG(TAG, " Direction has datapoint ID %u", *this->direction_id_);
if (auto id = this->direction_id_; id.has_value()) {
ESP_LOGCONFIG(TAG, " Direction has datapoint ID %u", *id);
}
}
@@ -80,25 +80,33 @@ fan::FanTraits TuyaFan::get_traits() {
}
void TuyaFan::control(const fan::FanCall &call) {
if (this->switch_id_.has_value() && call.get_state().has_value()) {
this->parent_->set_boolean_datapoint_value(*this->switch_id_, *call.get_state());
}
if (this->oscillation_id_.has_value() && call.get_oscillating().has_value()) {
if (this->oscillation_type_ == TuyaDatapointType::ENUM) {
this->parent_->set_enum_datapoint_value(*this->oscillation_id_, *call.get_oscillating());
} else if (this->oscillation_type_ == TuyaDatapointType::BOOLEAN) {
this->parent_->set_boolean_datapoint_value(*this->oscillation_id_, *call.get_oscillating());
if (auto switch_id = this->switch_id_; switch_id.has_value()) {
if (auto state = call.get_state(); state.has_value()) {
this->parent_->set_boolean_datapoint_value(*switch_id, *state);
}
}
if (this->direction_id_.has_value() && call.get_direction().has_value()) {
bool enable = *call.get_direction() == fan::FanDirection::REVERSE;
this->parent_->set_enum_datapoint_value(*this->direction_id_, enable);
if (auto osc_id = this->oscillation_id_; osc_id.has_value()) {
if (auto oscillating = call.get_oscillating(); oscillating.has_value()) {
if (this->oscillation_type_ == TuyaDatapointType::ENUM) {
this->parent_->set_enum_datapoint_value(*osc_id, *oscillating);
} else if (this->oscillation_type_ == TuyaDatapointType::BOOLEAN) {
this->parent_->set_boolean_datapoint_value(*osc_id, *oscillating);
}
}
}
if (this->speed_id_.has_value() && call.get_speed().has_value()) {
if (this->speed_type_ == TuyaDatapointType::ENUM) {
this->parent_->set_enum_datapoint_value(*this->speed_id_, *call.get_speed() - 1);
} else if (this->speed_type_ == TuyaDatapointType::INTEGER) {
this->parent_->set_integer_datapoint_value(*this->speed_id_, *call.get_speed());
if (auto dir_id = this->direction_id_; dir_id.has_value()) {
if (auto direction = call.get_direction(); direction.has_value()) {
bool enable = *direction == fan::FanDirection::REVERSE;
this->parent_->set_enum_datapoint_value(*dir_id, enable);
}
}
if (auto spd_id = this->speed_id_; spd_id.has_value()) {
if (auto speed = call.get_speed(); speed.has_value()) {
if (this->speed_type_ == TuyaDatapointType::ENUM) {
this->parent_->set_enum_datapoint_value(*spd_id, *speed - 1);
} else if (this->speed_type_ == TuyaDatapointType::INTEGER) {
this->parent_->set_integer_datapoint_value(*spd_id, *speed);
}
}
}
}
+4 -1
View File
@@ -57,6 +57,9 @@ void TuyaLight::setup() {
return;
}
if (!this->color_type_.has_value())
return;
float red, green, blue;
switch (*this->color_type_) {
case TuyaColorType::RGBHSV:
@@ -185,7 +188,7 @@ void TuyaLight::write_state(light::LightState *state) {
}
}
if (this->color_id_.has_value() && (brightness == 0.0f || !color_interlock_)) {
if (this->color_id_.has_value() && this->color_type_.has_value() && (brightness == 0.0f || !color_interlock_)) {
std::string color_value;
switch (*this->color_type_) {
case TuyaColorType::RGB: {
@@ -42,8 +42,8 @@ climate::ClimateTraits UponorSmatrixClimate::traits() {
}
void UponorSmatrixClimate::control(const climate::ClimateCall &call) {
if (call.get_target_temperature().has_value()) {
uint16_t temp = celsius_to_raw(*call.get_target_temperature());
if (auto val = call.get_target_temperature(); val.has_value()) {
uint16_t temp = celsius_to_raw(*val);
if (this->preset == climate::CLIMATE_PRESET_ECO) {
// During ECO mode, the thermostat automatically substracts the setback value from the setpoint,
// so we need to add it here first
+4 -4
View File
@@ -120,10 +120,10 @@ void YashimaClimate::setup() {
}
void YashimaClimate::control(const climate::ClimateCall &call) {
if (call.get_mode().has_value())
this->mode = *call.get_mode();
if (call.get_target_temperature().has_value())
this->target_temperature = *call.get_target_temperature();
if (auto val = call.get_mode(); val.has_value())
this->mode = *val;
if (auto val = call.get_target_temperature(); val.has_value())
this->target_temperature = *val;
this->transmit_state_();
this->publish_state();