mirror of
https://github.com/esphome/esphome.git
synced 2026-06-04 01:18:26 +08:00
Merge branch 'followup/hal-esp32' into followup/hal-esp8266
This commit is contained in:
@@ -78,7 +78,8 @@ class ActionResponse {
|
|||||||
: success_(success), error_message_(error_message) {
|
: success_(success), error_message_(error_message) {
|
||||||
if (data == nullptr || data_len == 0)
|
if (data == nullptr || data_len == 0)
|
||||||
return;
|
return;
|
||||||
this->json_document_ = json::parse_json(data, data_len);
|
JsonDocument tmp = json::parse_json(data, data_len);
|
||||||
|
swap(this->json_document_, tmp);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,7 @@ class AQICalculator : public AbstractAQICalculator {
|
|||||||
uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override {
|
uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override {
|
||||||
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
|
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
|
||||||
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
|
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
|
||||||
|
float aqi = std::max({pm2_5_index, pm10_0_index, 0.0f});
|
||||||
float aqi = std::max(pm2_5_index, pm10_0_index);
|
|
||||||
if (aqi < 0.0f) {
|
|
||||||
aqi = 0.0f;
|
|
||||||
}
|
|
||||||
return static_cast<uint16_t>(std::lround(aqi));
|
return static_cast<uint16_t>(std::lround(aqi));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,7 @@ class CAQICalculator : public AbstractAQICalculator {
|
|||||||
uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override {
|
uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override {
|
||||||
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
|
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
|
||||||
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
|
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
|
||||||
|
float aqi = std::max({pm2_5_index, pm10_0_index, 0.0f});
|
||||||
float aqi = std::max(pm2_5_index, pm10_0_index);
|
|
||||||
if (aqi < 0.0f) {
|
|
||||||
aqi = 0.0f;
|
|
||||||
}
|
|
||||||
return static_cast<uint16_t>(std::lround(aqi));
|
return static_cast<uint16_t>(std::lround(aqi));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ void HonClimate::set_horizontal_airflow(hon_protocol::HorizontalSwingMode direct
|
|||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HonClimate::get_cleaning_status_text() const {
|
const char *HonClimate::get_cleaning_status_text() const {
|
||||||
switch (this->cleaning_status_) {
|
switch (this->cleaning_status_) {
|
||||||
case CleaningState::SELF_CLEAN:
|
case CleaningState::SELF_CLEAN:
|
||||||
return "Self clean";
|
return "Self clean";
|
||||||
@@ -134,29 +134,22 @@ haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(haie
|
|||||||
}
|
}
|
||||||
// All OK
|
// All OK
|
||||||
hon_protocol::DeviceVersionAnswer *answr = (hon_protocol::DeviceVersionAnswer *) data;
|
hon_protocol::DeviceVersionAnswer *answr = (hon_protocol::DeviceVersionAnswer *) data;
|
||||||
char tmp[9];
|
HardwareInfo info{}; // zero-init guarantees null-termination
|
||||||
tmp[8] = 0;
|
strncpy(info.protocol_version_, answr->protocol_version, HARDWARE_INFO_STR_SIZE - 1);
|
||||||
strncpy(tmp, answr->protocol_version, 8);
|
strncpy(info.software_version_, answr->software_version, HARDWARE_INFO_STR_SIZE - 1);
|
||||||
this->hvac_hardware_info_ = HardwareInfo();
|
strncpy(info.hardware_version_, answr->hardware_version, HARDWARE_INFO_STR_SIZE - 1);
|
||||||
this->hvac_hardware_info_.value().protocol_version_ = std::string(tmp);
|
strncpy(info.device_name_, answr->device_name, HARDWARE_INFO_STR_SIZE - 1);
|
||||||
strncpy(tmp, answr->software_version, 8);
|
info.functions_[0] = (answr->functions[1] & 0x01) != 0; // interactive mode support
|
||||||
this->hvac_hardware_info_.value().software_version_ = std::string(tmp);
|
info.functions_[1] = (answr->functions[1] & 0x02) != 0; // controller-device mode support
|
||||||
strncpy(tmp, answr->hardware_version, 8);
|
info.functions_[2] = (answr->functions[1] & 0x04) != 0; // crc support
|
||||||
this->hvac_hardware_info_.value().hardware_version_ = std::string(tmp);
|
info.functions_[3] = (answr->functions[1] & 0x08) != 0; // multiple AC support
|
||||||
strncpy(tmp, answr->device_name, 8);
|
info.functions_[4] = (answr->functions[1] & 0x20) != 0; // roles support
|
||||||
this->hvac_hardware_info_.value().device_name_ = std::string(tmp);
|
this->use_crc_ = info.functions_[2];
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
this->update_sub_text_sensor_(SubTextSensorType::APPLIANCE_NAME, this->hvac_hardware_info_.value().device_name_);
|
this->update_sub_text_sensor_(SubTextSensorType::APPLIANCE_NAME, info.device_name_);
|
||||||
this->update_sub_text_sensor_(SubTextSensorType::PROTOCOL_VERSION,
|
this->update_sub_text_sensor_(SubTextSensorType::PROTOCOL_VERSION, info.protocol_version_);
|
||||||
this->hvac_hardware_info_.value().protocol_version_);
|
|
||||||
#endif
|
#endif
|
||||||
this->hvac_hardware_info_.value().functions_[0] = (answr->functions[1] & 0x01) != 0; // interactive mode support
|
this->hvac_hardware_info_ = info;
|
||||||
this->hvac_hardware_info_.value().functions_[1] =
|
|
||||||
(answr->functions[1] & 0x02) != 0; // controller-device mode support
|
|
||||||
this->hvac_hardware_info_.value().functions_[2] = (answr->functions[1] & 0x04) != 0; // crc support
|
|
||||||
this->hvac_hardware_info_.value().functions_[3] = (answr->functions[1] & 0x08) != 0; // multiple AC support
|
|
||||||
this->hvac_hardware_info_.value().functions_[4] = (answr->functions[1] & 0x20) != 0; // roles support
|
|
||||||
this->use_crc_ = this->hvac_hardware_info_.value().functions_[2];
|
|
||||||
this->set_phase(ProtocolPhases::SENDING_INIT_2);
|
this->set_phase(ProtocolPhases::SENDING_INIT_2);
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
@@ -347,10 +340,9 @@ void HonClimate::dump_config() {
|
|||||||
" Device software version: %s\n"
|
" Device software version: %s\n"
|
||||||
" Device hardware version: %s\n"
|
" Device hardware version: %s\n"
|
||||||
" Device name: %s",
|
" Device name: %s",
|
||||||
this->hvac_hardware_info_.value().protocol_version_.c_str(),
|
this->hvac_hardware_info_.value().protocol_version_,
|
||||||
this->hvac_hardware_info_.value().software_version_.c_str(),
|
this->hvac_hardware_info_.value().software_version_,
|
||||||
this->hvac_hardware_info_.value().hardware_version_.c_str(),
|
this->hvac_hardware_info_.value().hardware_version_, this->hvac_hardware_info_.value().device_name_);
|
||||||
this->hvac_hardware_info_.value().device_name_.c_str());
|
|
||||||
ESP_LOGCONFIG(TAG, " Device features:%s%s%s%s%s",
|
ESP_LOGCONFIG(TAG, " Device features:%s%s%s%s%s",
|
||||||
(this->hvac_hardware_info_.value().functions_[0] ? " interactive" : ""),
|
(this->hvac_hardware_info_.value().functions_[0] ? " interactive" : ""),
|
||||||
(this->hvac_hardware_info_.value().functions_[1] ? " controller-device" : ""),
|
(this->hvac_hardware_info_.value().functions_[1] ? " controller-device" : ""),
|
||||||
@@ -460,7 +452,7 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) {
|
|||||||
if (this->action_request_.has_value()) {
|
if (this->action_request_.has_value()) {
|
||||||
if (this->action_request_.value().message.has_value()) {
|
if (this->action_request_.value().message.has_value()) {
|
||||||
this->send_message_(this->action_request_.value().message.value(), this->use_crc_);
|
this->send_message_(this->action_request_.value().message.value(), this->use_crc_);
|
||||||
this->action_request_.value().message.reset();
|
this->action_request_.value().message.reset(); // NOLINT(bugprone-unchecked-optional-access)
|
||||||
} else {
|
} else {
|
||||||
// Message already sent, reseting request and return to idle
|
// Message already sent, reseting request and return to idle
|
||||||
this->action_request_.reset();
|
this->action_request_.reset();
|
||||||
@@ -796,7 +788,7 @@ void HonClimate::set_sub_text_sensor(SubTextSensorType type, text_sensor::TextSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::string &value) {
|
void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const char *value) {
|
||||||
size_t index = (size_t) type;
|
size_t index = (size_t) type;
|
||||||
if (this->sub_text_sensors_[index] != nullptr)
|
if (this->sub_text_sensors_[index] != nullptr)
|
||||||
this->sub_text_sensors_[index]->publish_state(value);
|
this->sub_text_sensors_[index]->publish_state(value);
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class HonClimate : public HaierClimateBase {
|
|||||||
void set_sub_text_sensor(SubTextSensorType type, text_sensor::TextSensor *sens);
|
void set_sub_text_sensor(SubTextSensorType type, text_sensor::TextSensor *sens);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void update_sub_text_sensor_(SubTextSensorType type, const std::string &value);
|
void update_sub_text_sensor_(SubTextSensorType type, const char *value);
|
||||||
text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
|
text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
@@ -116,7 +116,7 @@ class HonClimate : public HaierClimateBase {
|
|||||||
void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
|
void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
|
||||||
esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
|
esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
|
||||||
void set_horizontal_airflow(hon_protocol::HorizontalSwingMode direction);
|
void set_horizontal_airflow(hon_protocol::HorizontalSwingMode direction);
|
||||||
std::string get_cleaning_status_text() const;
|
const char *get_cleaning_status_text() const;
|
||||||
CleaningState get_cleaning_status() const;
|
CleaningState get_cleaning_status() const;
|
||||||
void start_self_cleaning();
|
void start_self_cleaning();
|
||||||
void start_steri_cleaning();
|
void start_steri_cleaning();
|
||||||
@@ -166,11 +166,12 @@ class HonClimate : public HaierClimateBase {
|
|||||||
void fill_control_messages_queue_();
|
void fill_control_messages_queue_();
|
||||||
void clear_control_messages_queue_();
|
void clear_control_messages_queue_();
|
||||||
|
|
||||||
|
static constexpr size_t HARDWARE_INFO_STR_SIZE = 9;
|
||||||
struct HardwareInfo {
|
struct HardwareInfo {
|
||||||
std::string protocol_version_;
|
char protocol_version_[HARDWARE_INFO_STR_SIZE];
|
||||||
std::string software_version_;
|
char software_version_[HARDWARE_INFO_STR_SIZE];
|
||||||
std::string hardware_version_;
|
char hardware_version_[HARDWARE_INFO_STR_SIZE];
|
||||||
std::string device_name_;
|
char device_name_[HARDWARE_INFO_STR_SIZE];
|
||||||
bool functions_[5];
|
bool functions_[5];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now)
|
|||||||
if (this->action_request_.has_value()) {
|
if (this->action_request_.has_value()) {
|
||||||
if (this->action_request_.value().message.has_value()) {
|
if (this->action_request_.value().message.has_value()) {
|
||||||
this->send_message_(this->action_request_.value().message.value(), this->use_crc_);
|
this->send_message_(this->action_request_.value().message.value(), this->use_crc_);
|
||||||
this->action_request_.value().message.reset();
|
this->action_request_.value().message.reset(); // NOLINT(bugprone-unchecked-optional-access)
|
||||||
} else {
|
} else {
|
||||||
// Message already sent, reseting request and return to idle
|
// Message already sent, reseting request and return to idle
|
||||||
this->action_request_.reset();
|
this->action_request_.reset();
|
||||||
|
|||||||
@@ -139,12 +139,12 @@ void KamstrupKMPComponent::clear_uart_rx_buffer_() {
|
|||||||
|
|
||||||
void KamstrupKMPComponent::read_command_(uint16_t command) {
|
void KamstrupKMPComponent::read_command_(uint16_t command) {
|
||||||
uint8_t buffer[20] = {0};
|
uint8_t buffer[20] = {0};
|
||||||
int buffer_len = 0;
|
size_t buffer_len = 0;
|
||||||
int data;
|
int data;
|
||||||
int timeout = 250; // ms
|
int timeout = 250; // ms
|
||||||
|
|
||||||
// Read the data from the UART
|
// Read the data from the UART
|
||||||
while (timeout > 0 && buffer_len < static_cast<int>(sizeof(buffer))) {
|
while (timeout > 0 && buffer_len < sizeof(buffer)) {
|
||||||
if (this->available()) {
|
if (this->available()) {
|
||||||
data = this->read();
|
data = this->read();
|
||||||
if (data > -1) {
|
if (data > -1) {
|
||||||
@@ -183,7 +183,7 @@ void KamstrupKMPComponent::read_command_(uint16_t command) {
|
|||||||
// Decode
|
// Decode
|
||||||
uint8_t msg[20] = {0};
|
uint8_t msg[20] = {0};
|
||||||
int msg_len = 0;
|
int msg_len = 0;
|
||||||
for (int i = 1; i < buffer_len - 1; i++) {
|
for (size_t i = 1; i < buffer_len - 1; i++) {
|
||||||
if (buffer[i] == 0x1B) {
|
if (buffer[i] == 0x1B) {
|
||||||
msg[msg_len++] = buffer[i + 1] ^ 0xFF;
|
msg[msg_len++] = buffer[i + 1] ^ 0xFF;
|
||||||
i++;
|
i++;
|
||||||
|
|||||||
@@ -31,60 +31,26 @@ template<bool HasTransitionLength, typename... Ts> class ToggleAction : public A
|
|||||||
transition_length_{};
|
transition_length_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unique Empty<Tag> per field so [[no_unique_address]] is guaranteed to coalesce.
|
// All configured fields are baked into a single stateless lambda whose
|
||||||
namespace light_control_detail {
|
// constants live in flash. The action only stores one function pointer
|
||||||
template<int Tag> struct Empty {};
|
// plus one parent pointer, regardless of how many fields the user set.
|
||||||
} // namespace light_control_detail
|
// Trigger args are forwarded to the apply function so user lambdas
|
||||||
|
// (e.g. `brightness: !lambda "return x;"`) keep working.
|
||||||
// X-macro: (type, field_name, bit_index). Order and bit values must match
|
template<typename... Ts> class LightControlAction : public Action<Ts...> {
|
||||||
// the FIELDS table in automation.py.
|
|
||||||
#define LIGHT_CONTROL_FIELDS(X) \
|
|
||||||
X(ColorMode, color_mode, 0) \
|
|
||||||
X(bool, state, 1) \
|
|
||||||
X(uint32_t, transition_length, 2) \
|
|
||||||
X(uint32_t, flash_length, 3) \
|
|
||||||
X(float, brightness, 4) \
|
|
||||||
X(float, color_brightness, 5) \
|
|
||||||
X(float, red, 6) \
|
|
||||||
X(float, green, 7) \
|
|
||||||
X(float, blue, 8) \
|
|
||||||
X(float, white, 9) \
|
|
||||||
X(float, color_temperature, 10) \
|
|
||||||
X(float, cold_white, 11) \
|
|
||||||
X(float, warm_white, 12) \
|
|
||||||
X(uint32_t, effect, 13)
|
|
||||||
|
|
||||||
template<uint16_t Fields, typename... Ts> class LightControlAction : public Action<Ts...> {
|
|
||||||
public:
|
public:
|
||||||
explicit LightControlAction(LightState *parent) : parent_(parent) {}
|
using ApplyFn = void (*)(LightState *, LightCall &, const Ts &...);
|
||||||
|
LightControlAction(LightState *parent, ApplyFn apply) : parent_(parent), apply_(apply) {}
|
||||||
#define LIGHT_FIELD_SETTER_(type, name, idx) \
|
|
||||||
template<typename V> void set_##name(V value) requires((Fields & (1 << (idx))) != 0) { this->name##_ = value; }
|
|
||||||
#define LIGHT_FIELD_APPLY_(type, name, idx) \
|
|
||||||
if constexpr ((Fields & (1 << (idx))) != 0) \
|
|
||||||
call.set_##name(this->name##_.value(x...));
|
|
||||||
#define LIGHT_FIELD_DECL_(type, name, idx) \
|
|
||||||
[[no_unique_address]] std::conditional_t<(Fields & (1 << (idx))) != 0, TemplatableFn<type, Ts...>, \
|
|
||||||
light_control_detail::Empty<(idx)>> \
|
|
||||||
name##_{};
|
|
||||||
|
|
||||||
LIGHT_CONTROL_FIELDS(LIGHT_FIELD_SETTER_)
|
|
||||||
|
|
||||||
void play(const Ts &...x) override {
|
void play(const Ts &...x) override {
|
||||||
auto call = this->parent_->make_call();
|
auto call = this->parent_->make_call();
|
||||||
LIGHT_CONTROL_FIELDS(LIGHT_FIELD_APPLY_)
|
this->apply_(this->parent_, call, x...);
|
||||||
call.perform();
|
call.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
LightState *parent_;
|
LightState *parent_;
|
||||||
LIGHT_CONTROL_FIELDS(LIGHT_FIELD_DECL_)
|
ApplyFn apply_;
|
||||||
|
|
||||||
#undef LIGHT_FIELD_DECL_
|
|
||||||
#undef LIGHT_FIELD_APPLY_
|
|
||||||
#undef LIGHT_FIELD_SETTER_
|
|
||||||
};
|
};
|
||||||
#undef LIGHT_CONTROL_FIELDS
|
|
||||||
|
|
||||||
template<bool HasTransitionLength, typename... Ts> class DimRelativeAction : public Action<Ts...> {
|
template<bool HasTransitionLength, typename... Ts> class DimRelativeAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from .types import (
|
|||||||
AddressableSet,
|
AddressableSet,
|
||||||
ColorMode,
|
ColorMode,
|
||||||
DimRelativeAction,
|
DimRelativeAction,
|
||||||
|
LightCall,
|
||||||
LightControlAction,
|
LightControlAction,
|
||||||
LightIsOffCondition,
|
LightIsOffCondition,
|
||||||
LightIsOnCondition,
|
LightIsOnCondition,
|
||||||
@@ -181,8 +182,8 @@ def _resolve_effect_index(config: ConfigType) -> int:
|
|||||||
async def light_control_to_code(config, action_id, template_arg, args):
|
async def light_control_to_code(config, action_id, template_arg, args):
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
|
||||||
# Order/bits must match LIGHT_CONTROL_FIELDS in automation.h.
|
# All configured fields are folded into a single stateless lambda whose
|
||||||
# EFFECT has special handling below; setter=None skips the generic loop.
|
# constants live in flash; the action stores only a function pointer.
|
||||||
FIELDS = (
|
FIELDS = (
|
||||||
(CONF_COLOR_MODE, "set_color_mode", ColorMode),
|
(CONF_COLOR_MODE, "set_color_mode", ColorMode),
|
||||||
(CONF_STATE, "set_state", cg.bool_),
|
(CONF_STATE, "set_state", cg.bool_),
|
||||||
@@ -197,49 +198,51 @@ async def light_control_to_code(config, action_id, template_arg, args):
|
|||||||
(CONF_COLOR_TEMPERATURE, "set_color_temperature", cg.float_),
|
(CONF_COLOR_TEMPERATURE, "set_color_temperature", cg.float_),
|
||||||
(CONF_COLD_WHITE, "set_cold_white", cg.float_),
|
(CONF_COLD_WHITE, "set_cold_white", cg.float_),
|
||||||
(CONF_WARM_WHITE, "set_warm_white", cg.float_),
|
(CONF_WARM_WHITE, "set_warm_white", cg.float_),
|
||||||
(CONF_EFFECT, None, cg.uint32),
|
|
||||||
)
|
)
|
||||||
# Bitmask is passed as uint16_t in C++ — must stay within 16 bits.
|
|
||||||
assert len(FIELDS) <= 16, "LightControlAction Fields bitmask exceeds uint16_t"
|
|
||||||
|
|
||||||
field_mask = sum(1 << i for i, (k, _, _) in enumerate(FIELDS) if k in config)
|
fwd_args = ", ".join(name for _, name in args)
|
||||||
control_template_arg = cg.TemplateArguments(
|
body_lines: list[str] = []
|
||||||
cg.RawExpression(f"static_cast<uint16_t>({field_mask})"), *template_arg
|
|
||||||
)
|
|
||||||
var = cg.new_Pvariable(action_id, control_template_arg, paren)
|
|
||||||
|
|
||||||
for conf_key, setter, type_ in FIELDS:
|
for conf_key, setter, type_ in FIELDS:
|
||||||
if conf_key in config and setter is not None:
|
if conf_key not in config:
|
||||||
template_ = await cg.templatable(config[conf_key], args, type_)
|
continue
|
||||||
cg.add(getattr(var, setter)(template_))
|
value = config[conf_key]
|
||||||
|
if isinstance(value, Lambda):
|
||||||
|
inner = await cg.process_lambda(value, args, return_type=type_)
|
||||||
|
body_lines.append(f"call.{setter}(({inner})({fwd_args}));")
|
||||||
|
else:
|
||||||
|
body_lines.append(f"call.{setter}({cg.safe_exp(value)});")
|
||||||
|
|
||||||
if CONF_EFFECT in config:
|
if CONF_EFFECT in config:
|
||||||
if isinstance(config[CONF_EFFECT], Lambda):
|
if isinstance(config[CONF_EFFECT], Lambda):
|
||||||
# Lambda returns a string — wrap in a C++ lambda that resolves
|
|
||||||
# the effect name to its uint32_t index at runtime
|
|
||||||
inner_lambda = await cg.process_lambda(
|
inner_lambda = await cg.process_lambda(
|
||||||
config[CONF_EFFECT], args, return_type=cg.std_string
|
config[CONF_EFFECT], args, return_type=cg.std_string
|
||||||
)
|
)
|
||||||
fwd_args = ", ".join(n for _, n in args)
|
body_lines.append(
|
||||||
# capture="" is correct: paren is a global variable name
|
f"{{ auto __effect_s = ({inner_lambda})({fwd_args});\n"
|
||||||
# string-interpolated into the body at codegen time, not a
|
f"call.set_effect(parent->get_effect_index("
|
||||||
# C++ runtime capture.
|
f"__effect_s.c_str(), __effect_s.size())); }}"
|
||||||
wrapper = LambdaExpression(
|
|
||||||
f"auto __effect_s = ({inner_lambda})({fwd_args});\n"
|
|
||||||
f"return {paren}->get_effect_index("
|
|
||||||
f"__effect_s.c_str(), __effect_s.size());",
|
|
||||||
args,
|
|
||||||
capture="",
|
|
||||||
return_type=cg.uint32,
|
|
||||||
)
|
)
|
||||||
cg.add(var.set_effect(wrapper))
|
|
||||||
else:
|
else:
|
||||||
# Static string — resolve effect name to index at codegen time
|
# Cast disambiguates between set_effect(uint32_t) and
|
||||||
template_ = await cg.templatable(
|
# set_effect(optional<uint32_t>) when the literal is an int.
|
||||||
_resolve_effect_index(config), args, cg.uint32
|
body_lines.append(
|
||||||
|
f"call.set_effect(static_cast<uint32_t>({_resolve_effect_index(config)}));"
|
||||||
)
|
)
|
||||||
cg.add(var.set_effect(template_))
|
|
||||||
return var
|
# Match LightControlAction::ApplyFn signature: const Ts &... for trigger args.
|
||||||
|
apply_args = [
|
||||||
|
(LightState.operator("ptr"), "parent"),
|
||||||
|
(LightCall.operator("ref"), "call"),
|
||||||
|
*((t.operator("const").operator("ref"), n) for t, n in args),
|
||||||
|
]
|
||||||
|
apply_lambda = LambdaExpression(
|
||||||
|
["\n".join(body_lines)],
|
||||||
|
apply_args,
|
||||||
|
capture="",
|
||||||
|
return_type=cg.void,
|
||||||
|
)
|
||||||
|
return cg.new_Pvariable(action_id, template_arg, paren, apply_lambda)
|
||||||
|
|
||||||
|
|
||||||
CONF_RELATIVE_BRIGHTNESS = "relative_brightness"
|
CONF_RELATIVE_BRIGHTNESS = "relative_brightness"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ Color = cg.esphome_ns.class_("Color")
|
|||||||
LightColorValues = light_ns.class_("LightColorValues")
|
LightColorValues = light_ns.class_("LightColorValues")
|
||||||
|
|
||||||
LightStateRTCState = light_ns.struct("LightStateRTCState")
|
LightStateRTCState = light_ns.struct("LightStateRTCState")
|
||||||
|
LightCall = light_ns.class_("LightCall")
|
||||||
|
|
||||||
# Color modes
|
# Color modes
|
||||||
ColorMode = light_ns.enum("ColorMode", is_class=True)
|
ColorMode = light_ns.enum("ColorMode", is_class=True)
|
||||||
|
|||||||
@@ -588,6 +588,7 @@ void MixerSpeaker::mix_audio_samples(const int16_t *primary_buffer, audio::Audio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTBEGIN(bugprone-unchecked-optional-access) -- audio_stream_info_ always set before this task is created
|
||||||
void MixerSpeaker::audio_mixer_task(void *params) {
|
void MixerSpeaker::audio_mixer_task(void *params) {
|
||||||
MixerSpeaker *this_mixer = static_cast<MixerSpeaker *>(params);
|
MixerSpeaker *this_mixer = static_cast<MixerSpeaker *>(params);
|
||||||
|
|
||||||
@@ -764,6 +765,7 @@ void MixerSpeaker::audio_mixer_task(void *params) {
|
|||||||
|
|
||||||
vTaskSuspend(nullptr); // Suspend this task indefinitely until the loop method deletes it
|
vTaskSuspend(nullptr); // Suspend this task indefinitely until the loop method deletes it
|
||||||
}
|
}
|
||||||
|
// NOLINTEND(bugprone-unchecked-optional-access)
|
||||||
|
|
||||||
} // namespace esphome::mixer_speaker
|
} // namespace esphome::mixer_speaker
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ class RemoteTransmitterBase : public RemoteComponentBase {
|
|||||||
return TransmitCall(this);
|
return TransmitCall(this);
|
||||||
}
|
}
|
||||||
template<typename Protocol>
|
template<typename Protocol>
|
||||||
void transmit(const typename Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) {
|
void transmit(const Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) {
|
||||||
auto call = this->transmit();
|
auto call = this->transmit();
|
||||||
Protocol().encode(call.get_data(), data);
|
Protocol().encode(call.get_data(), data);
|
||||||
call.set_send_times(send_times);
|
call.set_send_times(send_times);
|
||||||
@@ -250,10 +250,10 @@ template<typename T> class RemoteReceiverBinarySensor : public RemoteReceiverBin
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void set_data(typename T::ProtocolData data) { data_ = data; }
|
void set_data(T::ProtocolData data) { data_ = data; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typename T::ProtocolData data_;
|
T::ProtocolData data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -278,7 +278,7 @@ class RemoteTransmittable {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
template<typename Protocol>
|
template<typename Protocol>
|
||||||
void transmit_(const typename Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) {
|
void transmit_(const Protocol::ProtocolData &data, uint32_t send_times = 1, uint32_t send_wait = 0) {
|
||||||
this->transmitter_->transmit<Protocol>(data, send_times, send_wait);
|
this->transmitter_->transmit<Protocol>(data, send_times, send_wait);
|
||||||
}
|
}
|
||||||
RemoteTransmitterBase *transmitter_;
|
RemoteTransmitterBase *transmitter_;
|
||||||
|
|||||||
@@ -31,13 +31,14 @@ void CronTrigger::check_time_() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (this->last_check_.has_value()) {
|
if (this->last_check_.has_value()) {
|
||||||
if (*this->last_check_ > time && this->last_check_->timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
|
auto &last_check = *this->last_check_;
|
||||||
|
if (last_check > time && last_check.timestamp - time.timestamp > MAX_TIMESTAMP_DRIFT) {
|
||||||
// We went back in time (a lot), probably caused by time synchronization
|
// We went back in time (a lot), probably caused by time synchronization
|
||||||
ESP_LOGW(TAG, "Time has jumped back!");
|
ESP_LOGW(TAG, "Time has jumped back!");
|
||||||
} else if (*this->last_check_ >= time) {
|
} else if (last_check >= time) {
|
||||||
// already handled this one
|
// already handled this one
|
||||||
return;
|
return;
|
||||||
} else if (time > *this->last_check_ && time.timestamp - this->last_check_->timestamp > MAX_TIMESTAMP_DRIFT) {
|
} else if (time > last_check && time.timestamp - last_check.timestamp > MAX_TIMESTAMP_DRIFT) {
|
||||||
// We went ahead in time (a lot), probably caused by time synchronization
|
// We went ahead in time (a lot), probably caused by time synchronization
|
||||||
ESP_LOGW(TAG, "Time has jumped ahead!");
|
ESP_LOGW(TAG, "Time has jumped ahead!");
|
||||||
this->last_check_ = time;
|
this->last_check_ = time;
|
||||||
@@ -45,11 +46,11 @@ void CronTrigger::check_time_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
this->last_check_->increment_second();
|
last_check.increment_second();
|
||||||
if (*this->last_check_ >= time)
|
if (last_check >= time)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (this->matches(*this->last_check_))
|
if (this->matches(last_check))
|
||||||
this->trigger();
|
this->trigger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,12 +282,13 @@ optional<GateStatus> Tormatic::read_gate_status_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto hdr = this->pending_hdr_.value();
|
||||||
|
|
||||||
// Wait for all payload bytes to arrive before processing.
|
// Wait for all payload bytes to arrive before processing.
|
||||||
if (this->available() < this->pending_hdr_->payload_size()) {
|
if (this->available() < hdr.payload_size()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hdr = *this->pending_hdr_;
|
|
||||||
this->pending_hdr_.reset();
|
this->pending_hdr_.reset();
|
||||||
|
|
||||||
switch (hdr.type) {
|
switch (hdr.type) {
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ static Ras2819tSecondPacketCodes get_ras_2819t_second_packet_codes(climate::Clim
|
|||||||
*/
|
*/
|
||||||
static uint8_t get_ras_2819t_temp_code(float temperature) {
|
static uint8_t get_ras_2819t_temp_code(float temperature) {
|
||||||
int temp_index = static_cast<int>(temperature) - 18;
|
int temp_index = static_cast<int>(temperature) - 18;
|
||||||
if (temp_index < 0 || temp_index >= static_cast<int>(sizeof(RAS_2819T_TEMP_CODES))) {
|
if (temp_index < 0 || static_cast<size_t>(temp_index) >= sizeof(RAS_2819T_TEMP_CODES)) {
|
||||||
ESP_LOGW(TAG, "Temperature %.1f°C out of range [18-30°C], defaulting to 24°C", temperature);
|
ESP_LOGW(TAG, "Temperature %.1f°C out of range [18-30°C], defaulting to 24°C", temperature);
|
||||||
return 0x40; // Default to 24°C
|
return 0x40; // Default to 24°C
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ template<typename ValueType, int MaxBits> struct DefaultBitPolicy {
|
|||||||
///
|
///
|
||||||
template<typename ValueType, typename BitPolicy = DefaultBitPolicy<ValueType, 16>> class FiniteSetMask {
|
template<typename ValueType, typename BitPolicy = DefaultBitPolicy<ValueType, 16>> class FiniteSetMask {
|
||||||
public:
|
public:
|
||||||
using bitmask_t = typename BitPolicy::mask_t;
|
using bitmask_t = BitPolicy::mask_t;
|
||||||
|
|
||||||
constexpr FiniteSetMask() = default;
|
constexpr FiniteSetMask() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ TEST(ProtoMacVarint, AllOnes) { verify_mac(0xFFFFFFFFFFFFULL, 7); } // F
|
|||||||
|
|
||||||
// 100 deterministic-random 48-bit MACs to catch regressions across the space.
|
// 100 deterministic-random 48-bit MACs to catch regressions across the space.
|
||||||
TEST(ProtoMacVarint, RandomSample) {
|
TEST(ProtoMacVarint, RandomSample) {
|
||||||
// NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp) -- intentional fixed seed for reproducibility.
|
// NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) -- fixed seed for reproducibility
|
||||||
std::mt19937_64 rng(0xC0FFEE);
|
std::mt19937_64 rng(0xC0FFEE);
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
uint64_t mac = rng() & 0xFFFFFFFFFFFFULL;
|
uint64_t mac = rng() & 0xFFFFFFFFFFFFULL;
|
||||||
|
|||||||
Reference in New Issue
Block a user