Compare commits

..

2 Commits

Author SHA1 Message Date
J. Nick Koston
f580fef9d4 Update esphome/components/daikin_arc/daikin_arc.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-16 11:17:43 -10:00
J. Nick Koston
355697e377 [daikin_arc] Fix undefined behavior in sprintf calls 2026-01-16 11:13:51 -10:00
10 changed files with 59 additions and 130 deletions

View File

@@ -11,7 +11,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.13
rev: v0.14.11
hooks:
# Run the linter.
- id: ruff

View File

@@ -18,31 +18,31 @@ AnovaPacket *AnovaCodec::clean_packet_() {
AnovaPacket *AnovaCodec::get_read_device_status_request() {
this->current_query_ = READ_DEVICE_STATUS;
strncpy((char *) this->packet_.data, CMD_READ_DEVICE_STATUS, sizeof(this->packet_.data));
sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_target_temp_request() {
this->current_query_ = READ_TARGET_TEMPERATURE;
strncpy((char *) this->packet_.data, CMD_READ_TARGET_TEMP, sizeof(this->packet_.data));
sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_current_temp_request() {
this->current_query_ = READ_CURRENT_TEMPERATURE;
strncpy((char *) this->packet_.data, CMD_READ_CURRENT_TEMP, sizeof(this->packet_.data));
sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_unit_request() {
this->current_query_ = READ_UNIT;
strncpy((char *) this->packet_.data, CMD_READ_UNIT, sizeof(this->packet_.data));
sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_data_request() {
this->current_query_ = READ_DATA;
strncpy((char *) this->packet_.data, CMD_READ_DATA, sizeof(this->packet_.data));
sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA);
return this->clean_packet_();
}
@@ -50,25 +50,25 @@ AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
this->current_query_ = SET_TARGET_TEMPERATURE;
if (this->fahrenheit_)
temperature = ctof(temperature);
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), CMD_SET_TARGET_TEMP, temperature);
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_set_unit_request(char unit) {
this->current_query_ = SET_UNIT;
snprintf((char *) this->packet_.data, sizeof(this->packet_.data), CMD_SET_TEMP_UNIT, unit);
sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_start_request() {
this->current_query_ = START;
strncpy((char *) this->packet_.data, CMD_START, sizeof(this->packet_.data));
sprintf((char *) this->packet_.data, CMD_START);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_stop_request() {
this->current_query_ = STOP;
strncpy((char *) this->packet_.data, CMD_STOP, sizeof(this->packet_.data));
sprintf((char *) this->packet_.data, CMD_STOP);
return this->clean_packet_();
}

View File

@@ -258,8 +258,9 @@ bool DaikinArcClimate::parse_state_frame_(const uint8_t frame[]) {
}
char buf[DAIKIN_STATE_FRAME_SIZE * 3 + 1] = {0};
size_t pos = 0;
for (size_t i = 0; i < DAIKIN_STATE_FRAME_SIZE; i++) {
sprintf(buf, "%s%02x ", buf, frame[i]);
pos = buf_append_printf(buf, sizeof(buf), pos, "%02x ", frame[i]);
}
ESP_LOGD(TAG, "FRAME %s", buf);
@@ -349,8 +350,9 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
if (data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
valid_daikin_frame = true;
size_t bytes_count = data.size() / 2 / 8;
std::unique_ptr<char[]> buf(new char[bytes_count * 3 + 1]);
buf[0] = '\0';
size_t buf_size = bytes_count * 3 + 1;
std::unique_ptr<char[]> buf(new char[buf_size]()); // value-initialize (zero-fill)
size_t buf_pos = 0;
for (size_t i = 0; i < bytes_count; i++) {
uint8_t byte = 0;
for (int8_t bit = 0; bit < 8; bit++) {
@@ -361,19 +363,19 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
break;
}
}
sprintf(buf.get(), "%s%02x ", buf.get(), byte);
buf_pos = buf_append_printf(buf.get(), buf_size, buf_pos, "%02x ", byte);
}
ESP_LOGD(TAG, "WHOLE FRAME %s size: %d", buf.get(), data.size());
}
if (!valid_daikin_frame) {
char sbuf[16 * 10 + 1];
sbuf[0] = '\0';
char sbuf[16 * 10 + 1] = {0};
size_t sbuf_pos = 0;
for (size_t j = 0; j < static_cast<size_t>(data.size()); j++) {
if ((j - 2) % 16 == 0) {
if (j > 0) {
ESP_LOGD(TAG, "DATA %04x: %s", (j - 16 > 0xffff ? 0 : j - 16), sbuf);
}
sbuf[0] = '\0';
sbuf_pos = 0;
}
char type_ch = ' ';
// debug_tolerance = 25%
@@ -401,9 +403,10 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
type_ch = '0';
if (abs(data[j]) > 100000) {
sprintf(sbuf, "%s%-5d[%c] ", sbuf, data[j] > 0 ? 99999 : -99999, type_ch);
sbuf_pos = buf_append_printf(sbuf, sizeof(sbuf), sbuf_pos, "%-5d[%c] ", data[j] > 0 ? 99999 : -99999, type_ch);
} else {
sprintf(sbuf, "%s%-5d[%c] ", sbuf, (int) (round(data[j] / 10.) * 10), type_ch);
sbuf_pos =
buf_append_printf(sbuf, sizeof(sbuf), sbuf_pos, "%-5d[%c] ", (int) (round(data[j] / 10.) * 10), type_ch);
}
if (j + 1 == static_cast<size_t>(data.size())) {
ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);

View File

@@ -85,8 +85,8 @@ optional<AEHAData> AEHAProtocol::decode(RemoteReceiveData src) {
std::string AEHAProtocol::format_data_(const std::vector<uint8_t> &data) {
std::string out;
for (uint8_t byte : data) {
char buf[8]; // "0x%02X," = 5 chars + null + margin
snprintf(buf, sizeof(buf), "0x%02X,", byte);
char buf[6];
sprintf(buf, "0x%02X,", byte);
out += buf;
}
out.pop_back();

View File

@@ -1,5 +1,4 @@
#include "raw_protocol.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
@@ -9,30 +8,36 @@ static const char *const TAG = "remote.raw";
bool RawDumper::dump(RemoteReceiveData src) {
char buffer[256];
size_t pos = buf_append_printf(buffer, sizeof(buffer), 0, "Received Raw: ");
uint32_t buffer_offset = 0;
buffer_offset += sprintf(buffer, "Received Raw: ");
for (int32_t i = 0; i < src.size() - 1; i++) {
const int32_t value = src[i];
size_t prev_pos = pos;
const uint32_t remaining_length = sizeof(buffer) - buffer_offset;
int written;
if (i + 1 < src.size() - 1) {
pos = buf_append_printf(buffer, sizeof(buffer), pos, "%" PRId32 ", ", value);
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32 ", ", value);
} else {
pos = buf_append_printf(buffer, sizeof(buffer), pos, "%" PRId32, value);
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32, value);
}
if (pos >= sizeof(buffer) - 1) {
// buffer full, flush and continue
buffer[prev_pos] = '\0';
if (written < 0 || written >= int(remaining_length)) {
// write failed, flush...
buffer[buffer_offset] = '\0';
ESP_LOGI(TAG, "%s", buffer);
buffer_offset = 0;
written = sprintf(buffer, " ");
if (i + 1 < src.size() - 1) {
pos = buf_append_printf(buffer, sizeof(buffer), 0, " %" PRId32 ", ", value);
written += sprintf(buffer + written, "%" PRId32 ", ", value);
} else {
pos = buf_append_printf(buffer, sizeof(buffer), 0, " %" PRId32, value);
written += sprintf(buffer + written, "%" PRId32, value);
}
}
buffer_offset += written;
}
if (pos != 0) {
if (buffer_offset != 0) {
ESP_LOGI(TAG, "%s", buffer);
}
return true;

View File

@@ -1,5 +1,4 @@
#include "remote_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cinttypes>
@@ -170,31 +169,36 @@ void RemoteTransmitterBase::send_(uint32_t send_times, uint32_t send_wait) {
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
const auto &vec = this->temp_.get_data();
char buffer[256];
size_t pos = buf_append_printf(buffer, sizeof(buffer), 0,
"Sending times=%" PRIu32 " wait=%" PRIu32 "ms: ", send_times, send_wait);
uint32_t buffer_offset = 0;
buffer_offset += sprintf(buffer, "Sending times=%" PRIu32 " wait=%" PRIu32 "ms: ", send_times, send_wait);
for (size_t i = 0; i < vec.size(); i++) {
const int32_t value = vec[i];
size_t prev_pos = pos;
const uint32_t remaining_length = sizeof(buffer) - buffer_offset;
int written;
if (i + 1 < vec.size()) {
pos = buf_append_printf(buffer, sizeof(buffer), pos, "%" PRId32 ", ", value);
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32 ", ", value);
} else {
pos = buf_append_printf(buffer, sizeof(buffer), pos, "%" PRId32, value);
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32, value);
}
if (pos >= sizeof(buffer) - 1) {
// buffer full, flush and continue
buffer[prev_pos] = '\0';
if (written < 0 || written >= int(remaining_length)) {
// write failed, flush...
buffer[buffer_offset] = '\0';
ESP_LOGVV(TAG, "%s", buffer);
buffer_offset = 0;
written = sprintf(buffer, " ");
if (i + 1 < vec.size()) {
pos = buf_append_printf(buffer, sizeof(buffer), 0, " %" PRId32 ", ", value);
written += sprintf(buffer + written, "%" PRId32 ", ", value);
} else {
pos = buf_append_printf(buffer, sizeof(buffer), 0, " %" PRId32, value);
written += sprintf(buffer + written, "%" PRId32, value);
}
}
buffer_offset += written;
}
if (pos != 0) {
if (buffer_offset != 0) {
ESP_LOGVV(TAG, "%s", buffer);
}
#endif

View File

@@ -8,20 +8,17 @@ from esphome.const import (
CONF_ICON,
CONF_ID,
CONF_INDEX,
CONF_LAMBDA,
CONF_MODE,
CONF_MQTT_ID,
CONF_ON_VALUE,
CONF_OPERATION,
CONF_OPTION,
CONF_OPTIONS,
CONF_TRIGGER_ID,
CONF_WEB_SERVER,
)
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass, TemplateArguments
from esphome.cpp_types import global_ns
from esphome.cpp_generator import MockObjClass
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
@@ -41,9 +38,6 @@ SelectSetAction = select_ns.class_("SelectSetAction", automation.Action)
SelectSetIndexAction = select_ns.class_("SelectSetIndexAction", automation.Action)
SelectOperationAction = select_ns.class_("SelectOperationAction", automation.Action)
# Conditions
SelectIsCondition = select_ns.class_("SelectIsCondition", automation.Condition)
# Enums
SelectOperation = select_ns.enum("SelectOperation")
SELECT_OPERATION_OPTIONS = {
@@ -171,41 +165,6 @@ async def select_set_index_to_code(config, action_id, template_arg, args):
return var
@automation.register_condition(
"select.is",
SelectIsCondition,
OPERATION_BASE_SCHEMA.extend(
{
cv.Optional(CONF_OPTIONS): cv.All(
cv.ensure_list(cv.string_strict), cv.Length(min=1)
),
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
}
).add_extra(cv.has_exactly_one_key(CONF_OPTIONS, CONF_LAMBDA)),
)
async def select_is_to_code(config, condition_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
if options := config.get(CONF_OPTIONS):
# List of constant options
# Create a constexpr and pass that with a template length
arr_id = ID(
f"{condition_id}_data",
is_declaration=True,
type=global_ns.namespace("constexpr char * const"),
)
arg = cg.static_const_array(arr_id, cg.ArrayInitializer(*options))
template_arg = TemplateArguments(len(options), *template_arg)
else:
# Lambda
arg = await cg.process_lambda(
config[CONF_LAMBDA],
[(global_ns.namespace("StringRef &").operator("const"), "current")] + args,
return_type=cg.bool_,
)
template_arg = TemplateArguments(0, *template_arg)
return cg.new_Pvariable(condition_id, template_arg, paren, arg)
@automation.register_action(
"select.operation",
SelectOperationAction,

View File

@@ -66,34 +66,4 @@ template<typename... Ts> class SelectOperationAction : public Action<Ts...> {
Select *select_;
};
template<size_t N, typename... Ts> class SelectIsCondition : public Condition<Ts...> {
public:
SelectIsCondition(Select *parent, const char *const *option_list) : parent_(parent), option_list_(option_list) {}
bool check(const Ts &...x) override {
auto current = this->parent_->current_option();
for (size_t i = 0; i != N; i++) {
if (current == this->option_list_[i]) {
return true;
}
}
return false;
}
protected:
Select *parent_;
const char *const *option_list_;
};
template<typename... Ts> class SelectIsCondition<0, Ts...> : public Condition<Ts...> {
public:
SelectIsCondition(Select *parent, std::function<bool(const StringRef &, const Ts &...)> &&f)
: parent_(parent), f_(f) {}
bool check(const Ts &...x) override { return this->f_(this->parent_->current_option(), x...); }
protected:
Select *parent_;
std::function<bool(const StringRef &, const Ts &...)> f_;
};
} // namespace esphome::select

View File

@@ -1,6 +1,6 @@
pylint==4.0.4
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
ruff==0.14.13 # also change in .pre-commit-config.yaml when updating
ruff==0.14.12 # also change in .pre-commit-config.yaml when updating
pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating
pre-commit

View File

@@ -53,17 +53,6 @@ binary_sensor:
// Garage Door is closed.
return false;
}
- platform: template
id: select_binary_sensor
name: Select is one or two
condition:
any:
- select.is:
id: template_select
options: [one, two]
- select.is:
id: template_select
lambda: return current == id(template_text).state;
- platform: template
id: other_binary_sensor
name: "Garage Door Closed"
@@ -331,7 +320,6 @@ valve:
text:
- platform: template
id: template_text
name: "Template text"
optimistic: true
min_length: 0