mirror of
https://github.com/esphome/esphome.git
synced 2025-10-08 19:28:42 +00:00
Compare commits
1 Commits
ESPBTUUID_
...
idf_spi_es
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f195ac1afd |
@@ -256,7 +256,6 @@ esphome/components/libretiny_pwm/* @kuba2k2
|
||||
esphome/components/light/* @esphome/core
|
||||
esphome/components/lightwaverf/* @max246
|
||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||
esphome/components/lm75b/* @beormund
|
||||
esphome/components/ln882x/* @lamauny
|
||||
esphome/components/lock/* @esphome/core
|
||||
esphome/components/logger/* @esphome/core
|
||||
|
@@ -640,13 +640,6 @@ def command_vscode(args: ArgsProtocol) -> int | None:
|
||||
|
||||
|
||||
def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
# Set memory analysis options in config
|
||||
if args.analyze_memory:
|
||||
config.setdefault(CONF_ESPHOME, {})["analyze_memory"] = True
|
||||
|
||||
if args.memory_report:
|
||||
config.setdefault(CONF_ESPHOME, {})["memory_report_file"] = args.memory_report
|
||||
|
||||
exit_code = write_cpp(config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
@@ -1053,17 +1046,6 @@ def parse_args(argv):
|
||||
help="Only generate source code, do not compile.",
|
||||
action="store_true",
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--analyze-memory",
|
||||
help="Analyze and display memory usage by component after compilation.",
|
||||
action="store_true",
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--memory-report",
|
||||
help="Save memory analysis report to a file (supports .json or .txt).",
|
||||
type=str,
|
||||
metavar="FILE",
|
||||
)
|
||||
|
||||
parser_upload = subparsers.add_parser(
|
||||
"upload",
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -26,12 +26,12 @@ uint32_t Animation::get_animation_frame_count() const { return this->animation_f
|
||||
int Animation::get_current_frame() const { return this->current_frame_; }
|
||||
void Animation::next_frame() {
|
||||
this->current_frame_++;
|
||||
if (loop_count_ && static_cast<uint32_t>(this->current_frame_) == loop_end_frame_ &&
|
||||
if (loop_count_ && this->current_frame_ == loop_end_frame_ &&
|
||||
(this->loop_current_iteration_ < loop_count_ || loop_count_ < 0)) {
|
||||
this->current_frame_ = loop_start_frame_;
|
||||
this->loop_current_iteration_++;
|
||||
}
|
||||
if (static_cast<uint32_t>(this->current_frame_) >= animation_frame_count_) {
|
||||
if (this->current_frame_ >= animation_frame_count_) {
|
||||
this->loop_current_iteration_ = 1;
|
||||
this->current_frame_ = 0;
|
||||
}
|
||||
|
@@ -18,16 +18,6 @@ namespace esphome::api {
|
||||
// uncomment to log raw packets
|
||||
//#define HELPER_LOG_PACKETS
|
||||
|
||||
// Maximum message size limits to prevent OOM on constrained devices
|
||||
// Voice Assistant is our largest user at 1024 bytes per audio chunk
|
||||
// Using 2048 + 256 bytes overhead = 2304 bytes total to support voice and future needs
|
||||
// ESP8266 has very limited RAM and cannot support voice assistant
|
||||
#ifdef USE_ESP8266
|
||||
static constexpr uint16_t MAX_MESSAGE_SIZE = 512; // Keep small for memory constrained ESP8266
|
||||
#else
|
||||
static constexpr uint16_t MAX_MESSAGE_SIZE = 2304; // Support voice (1024) + headroom for larger messages
|
||||
#endif
|
||||
|
||||
// Forward declaration
|
||||
struct ClientInfo;
|
||||
|
||||
|
@@ -185,13 +185,6 @@ APIError APINoiseFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
||||
return APIError::BAD_HANDSHAKE_PACKET_LEN;
|
||||
}
|
||||
|
||||
// Check against maximum message size to prevent OOM
|
||||
if (msg_size > MAX_MESSAGE_SIZE) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad packet: message size %u exceeds maximum %u", msg_size, MAX_MESSAGE_SIZE);
|
||||
return APIError::BAD_DATA_PACKET;
|
||||
}
|
||||
|
||||
// reserve space for body
|
||||
if (rx_buf_.size() != msg_size) {
|
||||
rx_buf_.resize(msg_size);
|
||||
|
@@ -123,10 +123,10 @@ APIError APIPlaintextFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg_size_varint->as_uint32() > MAX_MESSAGE_SIZE) {
|
||||
if (msg_size_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u", msg_size_varint->as_uint32(),
|
||||
MAX_MESSAGE_SIZE);
|
||||
std::numeric_limits<uint16_t>::max());
|
||||
return APIError::BAD_DATA_PACKET;
|
||||
}
|
||||
rx_header_parsed_len_ = msg_size_varint->as_uint16();
|
||||
|
@@ -57,7 +57,7 @@ const char *audio_file_type_to_string(AudioFileType file_type) {
|
||||
void scale_audio_samples(const int16_t *audio_samples, int16_t *output_buffer, int16_t scale_factor,
|
||||
size_t samples_to_scale) {
|
||||
// Note the assembly dsps_mulc function has audio glitches if the input and output buffers are the same.
|
||||
for (size_t i = 0; i < samples_to_scale; i++) {
|
||||
for (int i = 0; i < samples_to_scale; i++) {
|
||||
int32_t acc = (int32_t) audio_samples[i] * (int32_t) scale_factor;
|
||||
output_buffer[i] = (int16_t) (acc >> 15);
|
||||
}
|
||||
|
@@ -97,10 +97,10 @@ void BL0906::handle_actions_() {
|
||||
return;
|
||||
}
|
||||
ActionCallbackFuncPtr ptr_func = nullptr;
|
||||
for (size_t i = 0; i < this->action_queue_.size(); i++) {
|
||||
for (int i = 0; i < this->action_queue_.size(); i++) {
|
||||
ptr_func = this->action_queue_[i];
|
||||
if (ptr_func) {
|
||||
ESP_LOGI(TAG, "HandleActionCallback[%zu]", i);
|
||||
ESP_LOGI(TAG, "HandleActionCallback[%d]", i);
|
||||
(this->*ptr_func)();
|
||||
}
|
||||
}
|
||||
|
@@ -51,7 +51,7 @@ void BL0942::loop() {
|
||||
if (!avail) {
|
||||
return;
|
||||
}
|
||||
if (static_cast<size_t>(avail) < sizeof(buffer)) {
|
||||
if (avail < sizeof(buffer)) {
|
||||
if (!this->rx_start_) {
|
||||
this->rx_start_ = millis();
|
||||
} else if (millis() > this->rx_start_ + PKT_TIMEOUT_MS) {
|
||||
@@ -148,7 +148,7 @@ void BL0942::setup() {
|
||||
|
||||
this->write_reg_(BL0942_REG_USR_WRPROT, 0);
|
||||
|
||||
if (static_cast<uint32_t>(this->read_reg_(BL0942_REG_MODE)) != mode)
|
||||
if (this->read_reg_(BL0942_REG_MODE) != mode)
|
||||
this->status_set_warning(LOG_STR("BL0942 setup failed!"));
|
||||
|
||||
this->flush();
|
||||
|
@@ -13,7 +13,7 @@ static const uint8_t C_M1106_CMD_SET_CO2_CALIB_RESPONSE[4] = {0x16, 0x01, 0x03,
|
||||
|
||||
uint8_t cm1106_checksum(const uint8_t *response, size_t len) {
|
||||
uint8_t crc = 0;
|
||||
for (size_t i = 0; i < len - 1; i++) {
|
||||
for (int i = 0; i < len - 1; i++) {
|
||||
crc -= response[i];
|
||||
}
|
||||
return crc;
|
||||
|
@@ -26,7 +26,7 @@ void DaikinArcClimate::transmit_query_() {
|
||||
uint8_t remote_header[8] = {0x11, 0xDA, 0x27, 0x00, 0x84, 0x87, 0x20, 0x00};
|
||||
|
||||
// Calculate checksum
|
||||
for (size_t i = 0; i < sizeof(remote_header) - 1; i++) {
|
||||
for (int i = 0; i < sizeof(remote_header) - 1; i++) {
|
||||
remote_header[sizeof(remote_header) - 1] += remote_header[i];
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ void DaikinArcClimate::transmit_state() {
|
||||
remote_state[9] = fan_speed & 0xff;
|
||||
|
||||
// Calculate checksum
|
||||
for (size_t i = 0; i < sizeof(remote_header) - 1; i++) {
|
||||
for (int i = 0; i < sizeof(remote_header) - 1; i++) {
|
||||
remote_header[sizeof(remote_header) - 1] += remote_header[i];
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
bool valid_daikin_frame = false;
|
||||
if (data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
|
||||
valid_daikin_frame = true;
|
||||
size_t bytes_count = data.size() / 2 / 8;
|
||||
int bytes_count = data.size() / 2 / 8;
|
||||
std::unique_ptr<char[]> buf(new char[bytes_count * 3 + 1]);
|
||||
buf[0] = '\0';
|
||||
for (size_t i = 0; i < bytes_count; i++) {
|
||||
@@ -370,7 +370,7 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
if (!valid_daikin_frame) {
|
||||
char sbuf[16 * 10 + 1];
|
||||
sbuf[0] = '\0';
|
||||
for (size_t j = 0; j < static_cast<size_t>(data.size()); j++) {
|
||||
for (size_t j = 0; j < data.size(); j++) {
|
||||
if ((j - 2) % 16 == 0) {
|
||||
if (j > 0) {
|
||||
ESP_LOGD(TAG, "DATA %04x: %s", (j - 16 > 0xffff ? 0 : j - 16), sbuf);
|
||||
@@ -380,26 +380,19 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
char type_ch = ' ';
|
||||
// debug_tolerance = 25%
|
||||
|
||||
if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_MARK)) <= data[j] &&
|
||||
data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_MARK)))
|
||||
if (DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_MARK))
|
||||
type_ch = 'P';
|
||||
if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_SPACE)) <= -data[j] &&
|
||||
-data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_SPACE)))
|
||||
if (DAIKIN_DBG_LOWER(DAIKIN_ARC_PRE_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ARC_PRE_SPACE))
|
||||
type_ch = 'a';
|
||||
if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_HEADER_MARK)) <= data[j] &&
|
||||
data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_HEADER_MARK)))
|
||||
if (DAIKIN_DBG_LOWER(DAIKIN_HEADER_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_HEADER_MARK))
|
||||
type_ch = 'H';
|
||||
if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_HEADER_SPACE)) <= -data[j] &&
|
||||
-data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_HEADER_SPACE)))
|
||||
if (DAIKIN_DBG_LOWER(DAIKIN_HEADER_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_HEADER_SPACE))
|
||||
type_ch = 'h';
|
||||
if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_BIT_MARK)) <= data[j] &&
|
||||
data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_BIT_MARK)))
|
||||
if (DAIKIN_DBG_LOWER(DAIKIN_BIT_MARK) <= data[j] && data[j] <= DAIKIN_DBG_UPPER(DAIKIN_BIT_MARK))
|
||||
type_ch = 'B';
|
||||
if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ONE_SPACE)) <= -data[j] &&
|
||||
-data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ONE_SPACE)))
|
||||
if (DAIKIN_DBG_LOWER(DAIKIN_ONE_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ONE_SPACE))
|
||||
type_ch = '1';
|
||||
if (static_cast<int32_t>(DAIKIN_DBG_LOWER(DAIKIN_ZERO_SPACE)) <= -data[j] &&
|
||||
-data[j] <= static_cast<int32_t>(DAIKIN_DBG_UPPER(DAIKIN_ZERO_SPACE)))
|
||||
if (DAIKIN_DBG_LOWER(DAIKIN_ZERO_SPACE) <= -data[j] && -data[j] <= DAIKIN_DBG_UPPER(DAIKIN_ZERO_SPACE))
|
||||
type_ch = '0';
|
||||
|
||||
if (abs(data[j]) > 100000) {
|
||||
@@ -407,7 +400,7 @@ bool DaikinArcClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
} else {
|
||||
sprintf(sbuf, "%s%-5d[%c] ", sbuf, (int) (round(data[j] / 10.) * 10), type_ch);
|
||||
}
|
||||
if (j + 1 == static_cast<size_t>(data.size())) {
|
||||
if (j == data.size() - 1) {
|
||||
ESP_LOGD(TAG, "DATA %04x: %s", (j - 8 > 0xffff ? 0 : j - 8), sbuf);
|
||||
}
|
||||
}
|
||||
|
@@ -97,12 +97,12 @@ bool ES7210::set_mic_gain(float mic_gain) {
|
||||
}
|
||||
|
||||
bool ES7210::configure_sample_rate_() {
|
||||
uint32_t mclk_fre = this->sample_rate_ * MCLK_DIV_FRE;
|
||||
int mclk_fre = this->sample_rate_ * MCLK_DIV_FRE;
|
||||
int coeff = -1;
|
||||
|
||||
for (size_t i = 0; i < (sizeof(ES7210_COEFFICIENTS) / sizeof(ES7210_COEFFICIENTS[0])); ++i) {
|
||||
for (int i = 0; i < (sizeof(ES7210_COEFFICIENTS) / sizeof(ES7210_COEFFICIENTS[0])); ++i) {
|
||||
if (ES7210_COEFFICIENTS[i].lrclk == this->sample_rate_ && ES7210_COEFFICIENTS[i].mclk == mclk_fre)
|
||||
coeff = static_cast<int>(i);
|
||||
coeff = i;
|
||||
}
|
||||
|
||||
if (coeff >= 0) {
|
||||
|
@@ -68,10 +68,6 @@ void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->advertising_set_manufacturer_data(std::span<const uint8_t>(data));
|
||||
}
|
||||
|
||||
void ESP32BLE::advertising_set_manufacturer_data(std::span<const uint8_t> data) {
|
||||
this->advertising_init_();
|
||||
this->advertising_->set_manufacturer_data(data);
|
||||
this->advertising_start();
|
||||
|
@@ -118,7 +118,6 @@ class ESP32BLE : public Component {
|
||||
void advertising_start();
|
||||
void advertising_set_service_data(const std::vector<uint8_t> &data);
|
||||
void advertising_set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void advertising_set_manufacturer_data(std::span<const uint8_t> data);
|
||||
void advertising_set_appearance(uint16_t appearance) { this->appearance_ = appearance; }
|
||||
void advertising_set_service_data_and_name(std::span<const uint8_t> data, bool include_name);
|
||||
void advertising_add_service_uuid(ESPBTUUID uuid);
|
||||
|
@@ -59,10 +59,6 @@ void BLEAdvertising::set_service_data(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
|
||||
void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
|
||||
this->set_manufacturer_data(std::span<const uint8_t>(data));
|
||||
}
|
||||
|
||||
void BLEAdvertising::set_manufacturer_data(std::span<const uint8_t> data) {
|
||||
delete[] this->advertising_data_.p_manufacturer_data;
|
||||
this->advertising_data_.p_manufacturer_data = nullptr;
|
||||
this->advertising_data_.manufacturer_len = data.size();
|
||||
@@ -156,7 +152,7 @@ void BLEAdvertising::loop() {
|
||||
if (now - this->last_advertisement_time_ > this->advertising_cycle_time_) {
|
||||
this->stop();
|
||||
this->current_adv_index_ += 1;
|
||||
if (static_cast<size_t>(this->current_adv_index_) >= this->raw_advertisements_callbacks_.size()) {
|
||||
if (this->current_adv_index_ >= this->raw_advertisements_callbacks_.size()) {
|
||||
this->current_adv_index_ = -1;
|
||||
}
|
||||
this->start();
|
||||
|
@@ -35,7 +35,6 @@ class BLEAdvertising {
|
||||
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
|
||||
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
|
||||
void set_manufacturer_data(const std::vector<uint8_t> &data);
|
||||
void set_manufacturer_data(std::span<const uint8_t> data);
|
||||
void set_appearance(uint16_t appearance) { this->advertising_data_.appearance = appearance; }
|
||||
void set_service_data(const std::vector<uint8_t> &data);
|
||||
void set_service_data(std::span<const uint8_t> data);
|
||||
|
@@ -42,18 +42,32 @@ ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) {
|
||||
ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
|
||||
ESPBTUUID ret;
|
||||
if (data.length() == 4) {
|
||||
// 16-bit UUID as 4-character hex string
|
||||
auto parsed = parse_hex<uint16_t>(data);
|
||||
if (parsed.has_value()) {
|
||||
ret.uuid_.len = ESP_UUID_LEN_16;
|
||||
ret.uuid_.uuid.uuid16 = parsed.value();
|
||||
ret.uuid_.len = ESP_UUID_LEN_16;
|
||||
ret.uuid_.uuid.uuid16 = 0;
|
||||
for (uint i = 0; i < data.length(); i += 2) {
|
||||
uint8_t msb = data.c_str()[i];
|
||||
uint8_t lsb = data.c_str()[i + 1];
|
||||
uint8_t lsb_shift = i <= 2 ? (2 - i) * 4 : 0;
|
||||
|
||||
if (msb > '9')
|
||||
msb -= 7;
|
||||
if (lsb > '9')
|
||||
lsb -= 7;
|
||||
ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << lsb_shift;
|
||||
}
|
||||
} else if (data.length() == 8) {
|
||||
// 32-bit UUID as 8-character hex string
|
||||
auto parsed = parse_hex<uint32_t>(data);
|
||||
if (parsed.has_value()) {
|
||||
ret.uuid_.len = ESP_UUID_LEN_32;
|
||||
ret.uuid_.uuid.uuid32 = parsed.value();
|
||||
ret.uuid_.len = ESP_UUID_LEN_32;
|
||||
ret.uuid_.uuid.uuid32 = 0;
|
||||
for (uint i = 0; i < data.length(); i += 2) {
|
||||
uint8_t msb = data.c_str()[i];
|
||||
uint8_t lsb = data.c_str()[i + 1];
|
||||
uint8_t lsb_shift = i <= 6 ? (6 - i) * 4 : 0;
|
||||
|
||||
if (msb > '9')
|
||||
msb -= 7;
|
||||
if (lsb > '9')
|
||||
lsb -= 7;
|
||||
ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << lsb_shift;
|
||||
}
|
||||
} else if (data.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be
|
||||
// investigated (lack of time)
|
||||
|
@@ -1,6 +1,5 @@
|
||||
#include "esp32_ble_beacon.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
|
@@ -125,49 +125,69 @@ bool BLECharacteristic::is_created() {
|
||||
if (this->state_ != CREATING_DEPENDENTS)
|
||||
return false;
|
||||
|
||||
bool created = true;
|
||||
for (auto *descriptor : this->descriptors_) {
|
||||
if (!descriptor->is_created())
|
||||
return false;
|
||||
created &= descriptor->is_created();
|
||||
}
|
||||
// All descriptors are created if we reach here
|
||||
this->state_ = CREATED;
|
||||
return true;
|
||||
if (created)
|
||||
this->state_ = CREATED;
|
||||
return this->state_ == CREATED;
|
||||
}
|
||||
|
||||
bool BLECharacteristic::is_failed() {
|
||||
if (this->state_ == FAILED)
|
||||
return true;
|
||||
|
||||
bool failed = false;
|
||||
for (auto *descriptor : this->descriptors_) {
|
||||
if (descriptor->is_failed()) {
|
||||
this->state_ = FAILED;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BLECharacteristic::set_property_bit_(esp_gatt_char_prop_t bit, bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | bit);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~bit);
|
||||
failed |= descriptor->is_failed();
|
||||
}
|
||||
if (failed)
|
||||
this->state_ = FAILED;
|
||||
return this->state_ == FAILED;
|
||||
}
|
||||
|
||||
void BLECharacteristic::set_broadcast_property(bool value) {
|
||||
this->set_property_bit_(ESP_GATT_CHAR_PROP_BIT_BROADCAST, value);
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_BROADCAST);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_indicate_property(bool value) {
|
||||
this->set_property_bit_(ESP_GATT_CHAR_PROP_BIT_INDICATE, value);
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_INDICATE);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_notify_property(bool value) {
|
||||
this->set_property_bit_(ESP_GATT_CHAR_PROP_BIT_NOTIFY, value);
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_NOTIFY);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_read_property(bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_READ);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_write_property(bool value) {
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE);
|
||||
}
|
||||
}
|
||||
void BLECharacteristic::set_read_property(bool value) { this->set_property_bit_(ESP_GATT_CHAR_PROP_BIT_READ, value); }
|
||||
void BLECharacteristic::set_write_property(bool value) { this->set_property_bit_(ESP_GATT_CHAR_PROP_BIT_WRITE, value); }
|
||||
void BLECharacteristic::set_write_no_response_property(bool value) {
|
||||
this->set_property_bit_(ESP_GATT_CHAR_PROP_BIT_WRITE_NR, value);
|
||||
if (value) {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ | ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
} else {
|
||||
this->properties_ = (esp_gatt_char_prop_t) (this->properties_ & ~ESP_GATT_CHAR_PROP_BIT_WRITE_NR);
|
||||
}
|
||||
}
|
||||
|
||||
void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
||||
|
@@ -97,8 +97,6 @@ class BLECharacteristic {
|
||||
void remove_client_from_notify_list_(uint16_t conn_id);
|
||||
ClientNotificationEntry *find_client_in_notify_list_(uint16_t conn_id);
|
||||
|
||||
void set_property_bit_(esp_gatt_char_prop_t bit, bool value);
|
||||
|
||||
std::unique_ptr<std::function<void(std::span<const uint8_t>, uint16_t)>> on_write_callback_;
|
||||
std::unique_ptr<std::function<void(uint16_t)>> on_read_callback_;
|
||||
|
||||
|
@@ -15,10 +15,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_characteristic_on_w
|
||||
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
|
||||
new Trigger<std::vector<uint8_t>, uint16_t>();
|
||||
characteristic->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
|
||||
// Convert span to vector for trigger - copy is necessary because:
|
||||
// 1. Trigger stores the data for use in automation actions that execute later
|
||||
// 2. The span is only valid during this callback (points to temporary BLE stack data)
|
||||
// 3. User lambdas in automations need persistent data they can access asynchronously
|
||||
// Convert span to vector for trigger
|
||||
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
|
||||
});
|
||||
return on_write_trigger;
|
||||
@@ -30,10 +27,7 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_descriptor_on_write
|
||||
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
|
||||
new Trigger<std::vector<uint8_t>, uint16_t>();
|
||||
descriptor->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
|
||||
// Convert span to vector for trigger - copy is necessary because:
|
||||
// 1. Trigger stores the data for use in automation actions that execute later
|
||||
// 2. The span is only valid during this callback (points to temporary BLE stack data)
|
||||
// 3. User lambdas in automations need persistent data they can access asynchronously
|
||||
// Convert span to vector for trigger
|
||||
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
|
||||
});
|
||||
return on_write_trigger;
|
||||
|
@@ -68,7 +68,7 @@ static bool get_bitrate(canbus::CanSpeed bitrate, twai_timing_config_t *t_config
|
||||
|
||||
bool ESP32Can::setup_internal() {
|
||||
static int next_twai_ctrl_num = 0;
|
||||
if (static_cast<unsigned>(next_twai_ctrl_num) >= SOC_TWAI_CONTROLLER_NUM) {
|
||||
if (next_twai_ctrl_num >= SOC_TWAI_CONTROLLER_NUM) {
|
||||
ESP_LOGW(TAG, "Maximum number of esp32_can components created already");
|
||||
this->mark_failed();
|
||||
return false;
|
||||
|
@@ -80,7 +80,7 @@ void FingerprintGrowComponent::setup() {
|
||||
delay(20); // This delay guarantees the sensor will in fact be powered power.
|
||||
|
||||
if (this->check_password_()) {
|
||||
if (this->new_password_ != std::numeric_limits<uint32_t>::max()) {
|
||||
if (this->new_password_ != -1) {
|
||||
if (this->set_password_())
|
||||
return;
|
||||
} else {
|
||||
|
@@ -6,7 +6,6 @@
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
@@ -178,7 +177,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
||||
uint8_t address_[4] = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
uint16_t capacity_ = 64;
|
||||
uint32_t password_ = 0x0;
|
||||
uint32_t new_password_ = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t new_password_ = -1;
|
||||
GPIOPin *sensing_pin_{nullptr};
|
||||
GPIOPin *sensor_power_pin_{nullptr};
|
||||
uint8_t enrollment_image_ = 0;
|
||||
|
@@ -116,7 +116,7 @@ void GraphicalDisplayMenu::draw_menu_internal_(display::Display *display, const
|
||||
int number_items_fit_to_screen = 0;
|
||||
const int max_item_index = this->displayed_item_->items_size() - 1;
|
||||
|
||||
for (size_t i = 0; max_item_index >= 0 && i <= static_cast<size_t>(max_item_index); i++) {
|
||||
for (size_t i = 0; i <= max_item_index; i++) {
|
||||
const auto *item = this->displayed_item_->get_item(i);
|
||||
const bool selected = i == this->cursor_index_;
|
||||
const display::Rect item_dimensions = this->measure_item(display, item, bounds, selected);
|
||||
@@ -174,8 +174,7 @@ void GraphicalDisplayMenu::draw_menu_internal_(display::Display *display, const
|
||||
|
||||
display->filled_rectangle(bounds->x, bounds->y, max_width, total_height, this->background_color_);
|
||||
auto y_offset = bounds->y;
|
||||
for (size_t i = static_cast<size_t>(first_item_index);
|
||||
last_item_index >= 0 && i <= static_cast<size_t>(last_item_index); i++) {
|
||||
for (size_t i = first_item_index; i <= last_item_index; i++) {
|
||||
const auto *item = this->displayed_item_->get_item(i);
|
||||
const bool selected = i == this->cursor_index_;
|
||||
display::Rect dimensions = menu_dimensions[i];
|
||||
|
@@ -107,7 +107,7 @@ void IDFI2CBus::dump_config() {
|
||||
if (s.second) {
|
||||
ESP_LOGCONFIG(TAG, "Found device at address 0x%02X", s.first);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -377,7 +377,7 @@ void I2SAudioSpeaker::speaker_task(void *params) {
|
||||
this_speaker->current_stream_info_.get_bits_per_sample() <= 16) {
|
||||
size_t len = bytes_read / sizeof(int16_t);
|
||||
int16_t *tmp_buf = (int16_t *) new_data;
|
||||
for (size_t i = 0; i < len; i += 2) {
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
int16_t tmp = tmp_buf[i];
|
||||
tmp_buf[i] = tmp_buf[i + 1];
|
||||
tmp_buf[i + 1] = tmp;
|
||||
|
@@ -325,7 +325,7 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons
|
||||
// we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't bother
|
||||
this->write_array(ptr, w * h * 2);
|
||||
} else {
|
||||
for (size_t y = 0; y != static_cast<size_t>(h); y++) {
|
||||
for (size_t y = 0; y != h; y++) {
|
||||
this->write_array(ptr + (y + y_offset) * stride + x_offset, w * 2);
|
||||
}
|
||||
}
|
||||
@@ -349,7 +349,7 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons
|
||||
App.feed_wdt();
|
||||
}
|
||||
// end of line? Skip to the next.
|
||||
if (++pixel == static_cast<size_t>(w)) {
|
||||
if (++pixel == w) {
|
||||
pixel = 0;
|
||||
ptr += (x_pad + x_offset) * 2;
|
||||
}
|
||||
|
@@ -19,19 +19,15 @@ std::string build_json(const json_build_t &f) {
|
||||
|
||||
bool parse_json(const std::string &data, const json_parse_t &f) {
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
JsonDocument doc = parse_json(reinterpret_cast<const uint8_t *>(data.c_str()), data.size());
|
||||
JsonDocument doc = parse_json(data);
|
||||
if (doc.overflowed() || doc.isNull())
|
||||
return false;
|
||||
return f(doc.as<JsonObject>());
|
||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
}
|
||||
|
||||
JsonDocument parse_json(const uint8_t *data, size_t len) {
|
||||
JsonDocument parse_json(const std::string &data) {
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
if (data == nullptr || len == 0) {
|
||||
ESP_LOGE(TAG, "No data to parse");
|
||||
return JsonObject(); // return unbound object
|
||||
}
|
||||
#ifdef USE_PSRAM
|
||||
auto doc_allocator = SpiRamAllocator();
|
||||
JsonDocument json_document(&doc_allocator);
|
||||
@@ -42,7 +38,7 @@ JsonDocument parse_json(const uint8_t *data, size_t len) {
|
||||
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
|
||||
return JsonObject(); // return unbound object
|
||||
}
|
||||
DeserializationError err = deserializeJson(json_document, data, len);
|
||||
DeserializationError err = deserializeJson(json_document, data);
|
||||
|
||||
if (err == DeserializationError::Ok) {
|
||||
return json_document;
|
||||
|
@@ -50,13 +50,8 @@ std::string build_json(const json_build_t &f);
|
||||
|
||||
/// Parse a JSON string and run the provided json parse function if it's valid.
|
||||
bool parse_json(const std::string &data, const json_parse_t &f);
|
||||
|
||||
/// Parse a JSON string and return the root JsonDocument (or an unbound object on error)
|
||||
JsonDocument parse_json(const uint8_t *data, size_t len);
|
||||
/// Parse a JSON string and return the root JsonDocument (or an unbound object on error)
|
||||
inline JsonDocument parse_json(const std::string &data) {
|
||||
return parse_json(reinterpret_cast<const uint8_t *>(data.c_str()), data.size());
|
||||
}
|
||||
JsonDocument parse_json(const std::string &data);
|
||||
|
||||
/// Builder class for creating JSON documents without lambdas
|
||||
class JsonBuilder {
|
||||
|
@@ -22,7 +22,7 @@ void KamstrupKMPComponent::dump_config() {
|
||||
LOG_SENSOR(" ", "Flow", this->flow_sensor_);
|
||||
LOG_SENSOR(" ", "Volume", this->volume_sensor_);
|
||||
|
||||
for (size_t i = 0; i < this->custom_sensors_.size(); i++) {
|
||||
for (int i = 0; i < this->custom_sensors_.size(); i++) {
|
||||
LOG_SENSOR(" ", "Custom Sensor", this->custom_sensors_[i]);
|
||||
ESP_LOGCONFIG(TAG, " Command: 0x%04X", this->custom_commands_[i]);
|
||||
}
|
||||
@@ -268,7 +268,7 @@ void KamstrupKMPComponent::set_sensor_value_(uint16_t command, float value, uint
|
||||
}
|
||||
|
||||
// Custom sensors
|
||||
for (size_t i = 0; i < this->custom_commands_.size(); i++) {
|
||||
for (int i = 0; i < this->custom_commands_.size(); i++) {
|
||||
if (command == this->custom_commands_[i]) {
|
||||
this->custom_sensors_[i]->publish_state(value);
|
||||
}
|
||||
|
@@ -13,8 +13,8 @@ class KeyCollector : public Component {
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
void set_provider(key_provider::KeyProvider *provider);
|
||||
void set_min_length(uint32_t min_length) { this->min_length_ = min_length; };
|
||||
void set_max_length(uint32_t max_length) { this->max_length_ = max_length; };
|
||||
void set_min_length(int min_length) { this->min_length_ = min_length; };
|
||||
void set_max_length(int max_length) { this->max_length_ = max_length; };
|
||||
void set_start_keys(std::string start_keys) { this->start_keys_ = std::move(start_keys); };
|
||||
void set_end_keys(std::string end_keys) { this->end_keys_ = std::move(end_keys); };
|
||||
void set_end_key_required(bool end_key_required) { this->end_key_required_ = end_key_required; };
|
||||
@@ -33,8 +33,8 @@ class KeyCollector : public Component {
|
||||
protected:
|
||||
void key_pressed_(uint8_t key);
|
||||
|
||||
uint32_t min_length_{0};
|
||||
uint32_t max_length_{0};
|
||||
int min_length_{0};
|
||||
int max_length_{0};
|
||||
std::string start_keys_;
|
||||
std::string end_keys_;
|
||||
bool end_key_required_{false};
|
||||
|
@@ -10,15 +10,11 @@ namespace light {
|
||||
static const char *const TAG = "light";
|
||||
|
||||
// Helper functions to reduce code size for logging
|
||||
static void clamp_and_log_if_invalid(const char *name, float &value, const LogString *param_name, float min = 0.0f,
|
||||
float max = 1.0f) {
|
||||
if (value < min || value > max) {
|
||||
ESP_LOGW(TAG, "'%s': %s value %.2f is out of range [%.1f - %.1f]", name, LOG_STR_ARG(param_name), value, min, max);
|
||||
value = clamp(value, min, max);
|
||||
}
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_WARN
|
||||
static void log_validation_warning(const char *name, const LogString *param_name, float val, float min, float max) {
|
||||
ESP_LOGW(TAG, "'%s': %s value %.2f is out of range [%.1f - %.1f]", name, LOG_STR_ARG(param_name), val, min, max);
|
||||
}
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_WARN
|
||||
static void log_feature_not_supported(const char *name, const LogString *feature) {
|
||||
ESP_LOGW(TAG, "'%s': %s not supported", name, LOG_STR_ARG(feature));
|
||||
}
|
||||
@@ -31,6 +27,7 @@ static void log_invalid_parameter(const char *name, const LogString *message) {
|
||||
ESP_LOGW(TAG, "'%s': %s", name, LOG_STR_ARG(message));
|
||||
}
|
||||
#else
|
||||
#define log_validation_warning(name, param_name, val, min, max)
|
||||
#define log_feature_not_supported(name, feature)
|
||||
#define log_color_mode_not_supported(name, feature)
|
||||
#define log_invalid_parameter(name, message)
|
||||
@@ -47,7 +44,7 @@ static void log_invalid_parameter(const char *name, const LogString *message) {
|
||||
} \
|
||||
LightCall &LightCall::set_##name(type name) { \
|
||||
this->name##_ = name; \
|
||||
this->set_flag_(flag); \
|
||||
this->set_flag_(flag, true); \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
@@ -184,16 +181,6 @@ void LightCall::perform() {
|
||||
}
|
||||
}
|
||||
|
||||
void LightCall::log_and_clear_unsupported_(FieldFlags flag, const LogString *feature, bool use_color_mode_log) {
|
||||
auto *name = this->parent_->get_name().c_str();
|
||||
if (use_color_mode_log) {
|
||||
log_color_mode_not_supported(name, feature);
|
||||
} else {
|
||||
log_feature_not_supported(name, feature);
|
||||
}
|
||||
this->clear_flag_(flag);
|
||||
}
|
||||
|
||||
LightColorValues LightCall::validate_() {
|
||||
auto *name = this->parent_->get_name().c_str();
|
||||
auto traits = this->parent_->get_traits();
|
||||
@@ -201,108 +188,141 @@ LightColorValues LightCall::validate_() {
|
||||
// Color mode check
|
||||
if (this->has_color_mode() && !traits.supports_color_mode(this->color_mode_)) {
|
||||
ESP_LOGW(TAG, "'%s' does not support color mode %s", name, LOG_STR_ARG(color_mode_to_human(this->color_mode_)));
|
||||
this->clear_flag_(FLAG_HAS_COLOR_MODE);
|
||||
this->set_flag_(FLAG_HAS_COLOR_MODE, false);
|
||||
}
|
||||
|
||||
// Ensure there is always a color mode set
|
||||
if (!this->has_color_mode()) {
|
||||
this->color_mode_ = this->compute_color_mode_();
|
||||
this->set_flag_(FLAG_HAS_COLOR_MODE);
|
||||
this->set_flag_(FLAG_HAS_COLOR_MODE, true);
|
||||
}
|
||||
auto color_mode = this->color_mode_;
|
||||
|
||||
// Transform calls that use non-native parameters for the current mode.
|
||||
this->transform_parameters_();
|
||||
|
||||
// Business logic adjustments before validation
|
||||
// Brightness exists check
|
||||
if (this->has_brightness() && this->brightness_ > 0.0f && !(color_mode & ColorCapability::BRIGHTNESS)) {
|
||||
log_feature_not_supported(name, LOG_STR("brightness"));
|
||||
this->set_flag_(FLAG_HAS_BRIGHTNESS, false);
|
||||
}
|
||||
|
||||
// Transition length possible check
|
||||
if (this->has_transition_() && this->transition_length_ != 0 && !(color_mode & ColorCapability::BRIGHTNESS)) {
|
||||
log_feature_not_supported(name, LOG_STR("transitions"));
|
||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||
}
|
||||
|
||||
// Color brightness exists check
|
||||
if (this->has_color_brightness() && this->color_brightness_ > 0.0f && !(color_mode & ColorCapability::RGB)) {
|
||||
log_color_mode_not_supported(name, LOG_STR("RGB brightness"));
|
||||
this->set_flag_(FLAG_HAS_COLOR_BRIGHTNESS, false);
|
||||
}
|
||||
|
||||
// RGB exists check
|
||||
if ((this->has_red() && this->red_ > 0.0f) || (this->has_green() && this->green_ > 0.0f) ||
|
||||
(this->has_blue() && this->blue_ > 0.0f)) {
|
||||
if (!(color_mode & ColorCapability::RGB)) {
|
||||
log_color_mode_not_supported(name, LOG_STR("RGB color"));
|
||||
this->set_flag_(FLAG_HAS_RED, false);
|
||||
this->set_flag_(FLAG_HAS_GREEN, false);
|
||||
this->set_flag_(FLAG_HAS_BLUE, false);
|
||||
}
|
||||
}
|
||||
|
||||
// White value exists check
|
||||
if (this->has_white() && this->white_ > 0.0f &&
|
||||
!(color_mode & ColorCapability::WHITE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
||||
log_color_mode_not_supported(name, LOG_STR("white value"));
|
||||
this->set_flag_(FLAG_HAS_WHITE, false);
|
||||
}
|
||||
|
||||
// Color temperature exists check
|
||||
if (this->has_color_temperature() &&
|
||||
!(color_mode & ColorCapability::COLOR_TEMPERATURE || color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
||||
log_color_mode_not_supported(name, LOG_STR("color temperature"));
|
||||
this->set_flag_(FLAG_HAS_COLOR_TEMPERATURE, false);
|
||||
}
|
||||
|
||||
// Cold/warm white value exists check
|
||||
if ((this->has_cold_white() && this->cold_white_ > 0.0f) || (this->has_warm_white() && this->warm_white_ > 0.0f)) {
|
||||
if (!(color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
||||
log_color_mode_not_supported(name, LOG_STR("cold/warm white value"));
|
||||
this->set_flag_(FLAG_HAS_COLD_WHITE, false);
|
||||
this->set_flag_(FLAG_HAS_WARM_WHITE, false);
|
||||
}
|
||||
}
|
||||
|
||||
#define VALIDATE_RANGE_(name_, upper_name, min, max) \
|
||||
if (this->has_##name_()) { \
|
||||
auto val = this->name_##_; \
|
||||
if (val < (min) || val > (max)) { \
|
||||
log_validation_warning(name, LOG_STR(upper_name), val, (min), (max)); \
|
||||
this->name_##_ = clamp(val, (min), (max)); \
|
||||
} \
|
||||
}
|
||||
#define VALIDATE_RANGE(name, upper_name) VALIDATE_RANGE_(name, upper_name, 0.0f, 1.0f)
|
||||
|
||||
// Range checks
|
||||
VALIDATE_RANGE(brightness, "Brightness")
|
||||
VALIDATE_RANGE(color_brightness, "Color brightness")
|
||||
VALIDATE_RANGE(red, "Red")
|
||||
VALIDATE_RANGE(green, "Green")
|
||||
VALIDATE_RANGE(blue, "Blue")
|
||||
VALIDATE_RANGE(white, "White")
|
||||
VALIDATE_RANGE(cold_white, "Cold white")
|
||||
VALIDATE_RANGE(warm_white, "Warm white")
|
||||
VALIDATE_RANGE_(color_temperature, "Color temperature", traits.get_min_mireds(), traits.get_max_mireds())
|
||||
|
||||
// Flag whether an explicit turn off was requested, in which case we'll also stop the effect.
|
||||
bool explicit_turn_off_request = this->has_state() && !this->state_;
|
||||
|
||||
// Turn off when brightness is set to zero, and reset brightness (so that it has nonzero brightness when turned on).
|
||||
if (this->has_brightness() && this->brightness_ == 0.0f) {
|
||||
this->state_ = false;
|
||||
this->set_flag_(FLAG_HAS_STATE);
|
||||
this->set_flag_(FLAG_HAS_STATE, true);
|
||||
this->brightness_ = 1.0f;
|
||||
}
|
||||
|
||||
// Set color brightness to 100% if currently zero and a color is set.
|
||||
if ((this->has_red() || this->has_green() || this->has_blue()) && !this->has_color_brightness() &&
|
||||
this->parent_->remote_values.get_color_brightness() == 0.0f) {
|
||||
this->color_brightness_ = 1.0f;
|
||||
this->set_flag_(FLAG_HAS_COLOR_BRIGHTNESS);
|
||||
if (this->has_red() || this->has_green() || this->has_blue()) {
|
||||
if (!this->has_color_brightness() && this->parent_->remote_values.get_color_brightness() == 0.0f) {
|
||||
this->color_brightness_ = 1.0f;
|
||||
this->set_flag_(FLAG_HAS_COLOR_BRIGHTNESS, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Capability validation
|
||||
if (this->has_brightness() && this->brightness_ > 0.0f && !(color_mode & ColorCapability::BRIGHTNESS))
|
||||
this->log_and_clear_unsupported_(FLAG_HAS_BRIGHTNESS, LOG_STR("brightness"), false);
|
||||
|
||||
// Transition length possible check
|
||||
if (this->has_transition_() && this->transition_length_ != 0 && !(color_mode & ColorCapability::BRIGHTNESS))
|
||||
this->log_and_clear_unsupported_(FLAG_HAS_TRANSITION, LOG_STR("transitions"), false);
|
||||
|
||||
if (this->has_color_brightness() && this->color_brightness_ > 0.0f && !(color_mode & ColorCapability::RGB))
|
||||
this->log_and_clear_unsupported_(FLAG_HAS_COLOR_BRIGHTNESS, LOG_STR("RGB brightness"), true);
|
||||
|
||||
// RGB exists check
|
||||
if (((this->has_red() && this->red_ > 0.0f) || (this->has_green() && this->green_ > 0.0f) ||
|
||||
(this->has_blue() && this->blue_ > 0.0f)) &&
|
||||
!(color_mode & ColorCapability::RGB)) {
|
||||
log_color_mode_not_supported(name, LOG_STR("RGB color"));
|
||||
this->clear_flag_(FLAG_HAS_RED);
|
||||
this->clear_flag_(FLAG_HAS_GREEN);
|
||||
this->clear_flag_(FLAG_HAS_BLUE);
|
||||
}
|
||||
|
||||
// White value exists check
|
||||
if (this->has_white() && this->white_ > 0.0f &&
|
||||
!(color_mode & ColorCapability::WHITE || color_mode & ColorCapability::COLD_WARM_WHITE))
|
||||
this->log_and_clear_unsupported_(FLAG_HAS_WHITE, LOG_STR("white value"), true);
|
||||
|
||||
// Color temperature exists check
|
||||
if (this->has_color_temperature() &&
|
||||
!(color_mode & ColorCapability::COLOR_TEMPERATURE || color_mode & ColorCapability::COLD_WARM_WHITE))
|
||||
this->log_and_clear_unsupported_(FLAG_HAS_COLOR_TEMPERATURE, LOG_STR("color temperature"), true);
|
||||
|
||||
// Cold/warm white value exists check
|
||||
if (((this->has_cold_white() && this->cold_white_ > 0.0f) || (this->has_warm_white() && this->warm_white_ > 0.0f)) &&
|
||||
!(color_mode & ColorCapability::COLD_WARM_WHITE)) {
|
||||
log_color_mode_not_supported(name, LOG_STR("cold/warm white value"));
|
||||
this->clear_flag_(FLAG_HAS_COLD_WHITE);
|
||||
this->clear_flag_(FLAG_HAS_WARM_WHITE);
|
||||
}
|
||||
|
||||
// Create color values and validate+apply ranges in one step to eliminate duplicate checks
|
||||
// Create color values for the light with this call applied.
|
||||
auto v = this->parent_->remote_values;
|
||||
if (this->has_color_mode())
|
||||
v.set_color_mode(this->color_mode_);
|
||||
if (this->has_state())
|
||||
v.set_state(this->state_);
|
||||
|
||||
#define VALIDATE_AND_APPLY(field, setter, name_str, ...) \
|
||||
if (this->has_##field()) { \
|
||||
clamp_and_log_if_invalid(name, this->field##_, LOG_STR(name_str), ##__VA_ARGS__); \
|
||||
v.setter(this->field##_); \
|
||||
}
|
||||
|
||||
VALIDATE_AND_APPLY(brightness, set_brightness, "Brightness")
|
||||
VALIDATE_AND_APPLY(color_brightness, set_color_brightness, "Color brightness")
|
||||
VALIDATE_AND_APPLY(red, set_red, "Red")
|
||||
VALIDATE_AND_APPLY(green, set_green, "Green")
|
||||
VALIDATE_AND_APPLY(blue, set_blue, "Blue")
|
||||
VALIDATE_AND_APPLY(white, set_white, "White")
|
||||
VALIDATE_AND_APPLY(cold_white, set_cold_white, "Cold white")
|
||||
VALIDATE_AND_APPLY(warm_white, set_warm_white, "Warm white")
|
||||
VALIDATE_AND_APPLY(color_temperature, set_color_temperature, "Color temperature", traits.get_min_mireds(),
|
||||
traits.get_max_mireds())
|
||||
|
||||
#undef VALIDATE_AND_APPLY
|
||||
if (this->has_brightness())
|
||||
v.set_brightness(this->brightness_);
|
||||
if (this->has_color_brightness())
|
||||
v.set_color_brightness(this->color_brightness_);
|
||||
if (this->has_red())
|
||||
v.set_red(this->red_);
|
||||
if (this->has_green())
|
||||
v.set_green(this->green_);
|
||||
if (this->has_blue())
|
||||
v.set_blue(this->blue_);
|
||||
if (this->has_white())
|
||||
v.set_white(this->white_);
|
||||
if (this->has_color_temperature())
|
||||
v.set_color_temperature(this->color_temperature_);
|
||||
if (this->has_cold_white())
|
||||
v.set_cold_white(this->cold_white_);
|
||||
if (this->has_warm_white())
|
||||
v.set_warm_white(this->warm_white_);
|
||||
|
||||
v.normalize_color();
|
||||
|
||||
// Flash length check
|
||||
if (this->has_flash_() && this->flash_length_ == 0) {
|
||||
log_invalid_parameter(name, LOG_STR("flash length must be >0"));
|
||||
this->clear_flag_(FLAG_HAS_FLASH);
|
||||
log_invalid_parameter(name, LOG_STR("flash length must be greater than zero"));
|
||||
this->set_flag_(FLAG_HAS_FLASH, false);
|
||||
}
|
||||
|
||||
// validate transition length/flash length/effect not used at the same time
|
||||
@@ -310,40 +330,42 @@ LightColorValues LightCall::validate_() {
|
||||
|
||||
// If effect is already active, remove effect start
|
||||
if (this->has_effect_() && this->effect_ == this->parent_->active_effect_index_) {
|
||||
this->clear_flag_(FLAG_HAS_EFFECT);
|
||||
this->set_flag_(FLAG_HAS_EFFECT, false);
|
||||
}
|
||||
|
||||
// validate effect index
|
||||
if (this->has_effect_() && this->effect_ > this->parent_->effects_.size()) {
|
||||
ESP_LOGW(TAG, "'%s': invalid effect index %" PRIu32, name, this->effect_);
|
||||
this->clear_flag_(FLAG_HAS_EFFECT);
|
||||
this->set_flag_(FLAG_HAS_EFFECT, false);
|
||||
}
|
||||
|
||||
if (this->has_effect_() && (this->has_transition_() || this->has_flash_())) {
|
||||
log_invalid_parameter(name, LOG_STR("effect cannot be used with transition/flash"));
|
||||
this->clear_flag_(FLAG_HAS_TRANSITION);
|
||||
this->clear_flag_(FLAG_HAS_FLASH);
|
||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||
this->set_flag_(FLAG_HAS_FLASH, false);
|
||||
}
|
||||
|
||||
if (this->has_flash_() && this->has_transition_()) {
|
||||
log_invalid_parameter(name, LOG_STR("flash cannot be used with transition"));
|
||||
this->clear_flag_(FLAG_HAS_TRANSITION);
|
||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||
}
|
||||
|
||||
if (!this->has_transition_() && !this->has_flash_() && (!this->has_effect_() || this->effect_ == 0) &&
|
||||
supports_transition) {
|
||||
// nothing specified and light supports transitions, set default transition length
|
||||
this->transition_length_ = this->parent_->default_transition_length_;
|
||||
this->set_flag_(FLAG_HAS_TRANSITION);
|
||||
this->set_flag_(FLAG_HAS_TRANSITION, true);
|
||||
}
|
||||
|
||||
if (this->has_transition_() && this->transition_length_ == 0) {
|
||||
// 0 transition is interpreted as no transition (instant change)
|
||||
this->clear_flag_(FLAG_HAS_TRANSITION);
|
||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||
}
|
||||
|
||||
if (this->has_transition_() && !supports_transition)
|
||||
this->log_and_clear_unsupported_(FLAG_HAS_TRANSITION, LOG_STR("transitions"), false);
|
||||
if (this->has_transition_() && !supports_transition) {
|
||||
log_feature_not_supported(name, LOG_STR("transitions"));
|
||||
this->set_flag_(FLAG_HAS_TRANSITION, false);
|
||||
}
|
||||
|
||||
// If not a flash and turning the light off, then disable the light
|
||||
// Do not use light color values directly, so that effects can set 0% brightness
|
||||
@@ -352,17 +374,17 @@ LightColorValues LightCall::validate_() {
|
||||
if (!this->has_flash_() && !target_state) {
|
||||
if (this->has_effect_()) {
|
||||
log_invalid_parameter(name, LOG_STR("cannot start effect when turning off"));
|
||||
this->clear_flag_(FLAG_HAS_EFFECT);
|
||||
this->set_flag_(FLAG_HAS_EFFECT, false);
|
||||
} else if (this->parent_->active_effect_index_ != 0 && explicit_turn_off_request) {
|
||||
// Auto turn off effect
|
||||
this->effect_ = 0;
|
||||
this->set_flag_(FLAG_HAS_EFFECT);
|
||||
this->set_flag_(FLAG_HAS_EFFECT, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable saving for flashes
|
||||
if (this->has_flash_())
|
||||
this->clear_flag_(FLAG_SAVE);
|
||||
this->set_flag_(FLAG_SAVE, false);
|
||||
|
||||
return v;
|
||||
}
|
||||
@@ -396,12 +418,12 @@ void LightCall::transform_parameters_() {
|
||||
const float gamma = this->parent_->get_gamma_correct();
|
||||
this->cold_white_ = gamma_uncorrect(cw_fraction / max_cw_ww, gamma);
|
||||
this->warm_white_ = gamma_uncorrect(ww_fraction / max_cw_ww, gamma);
|
||||
this->set_flag_(FLAG_HAS_COLD_WHITE);
|
||||
this->set_flag_(FLAG_HAS_WARM_WHITE);
|
||||
this->set_flag_(FLAG_HAS_COLD_WHITE, true);
|
||||
this->set_flag_(FLAG_HAS_WARM_WHITE, true);
|
||||
}
|
||||
if (this->has_white()) {
|
||||
this->brightness_ = this->white_;
|
||||
this->set_flag_(FLAG_HAS_BRIGHTNESS);
|
||||
this->set_flag_(FLAG_HAS_BRIGHTNESS, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,7 +630,7 @@ LightCall &LightCall::set_effect(optional<std::string> effect) {
|
||||
}
|
||||
LightCall &LightCall::set_effect(uint32_t effect_number) {
|
||||
this->effect_ = effect_number;
|
||||
this->set_flag_(FLAG_HAS_EFFECT);
|
||||
this->set_flag_(FLAG_HAS_EFFECT, true);
|
||||
return *this;
|
||||
}
|
||||
LightCall &LightCall::set_effect(optional<uint32_t> effect_number) {
|
||||
|
@@ -4,10 +4,6 @@
|
||||
#include <set>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
// Forward declaration
|
||||
struct LogString;
|
||||
|
||||
namespace light {
|
||||
|
||||
class LightState;
|
||||
@@ -211,14 +207,14 @@ class LightCall {
|
||||
FLAG_SAVE = 1 << 15,
|
||||
};
|
||||
|
||||
inline bool has_transition_() { return (this->flags_ & FLAG_HAS_TRANSITION) != 0; }
|
||||
inline bool has_flash_() { return (this->flags_ & FLAG_HAS_FLASH) != 0; }
|
||||
inline bool has_effect_() { return (this->flags_ & FLAG_HAS_EFFECT) != 0; }
|
||||
inline bool get_publish_() { return (this->flags_ & FLAG_PUBLISH) != 0; }
|
||||
inline bool get_save_() { return (this->flags_ & FLAG_SAVE) != 0; }
|
||||
bool has_transition_() { return (this->flags_ & FLAG_HAS_TRANSITION) != 0; }
|
||||
bool has_flash_() { return (this->flags_ & FLAG_HAS_FLASH) != 0; }
|
||||
bool has_effect_() { return (this->flags_ & FLAG_HAS_EFFECT) != 0; }
|
||||
bool get_publish_() { return (this->flags_ & FLAG_PUBLISH) != 0; }
|
||||
bool get_save_() { return (this->flags_ & FLAG_SAVE) != 0; }
|
||||
|
||||
// Helper to set flag - defaults to true for common case
|
||||
void set_flag_(FieldFlags flag, bool value = true) {
|
||||
// Helper to set flag
|
||||
void set_flag_(FieldFlags flag, bool value) {
|
||||
if (value) {
|
||||
this->flags_ |= flag;
|
||||
} else {
|
||||
@@ -226,12 +222,6 @@ class LightCall {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to clear flag - reduces code size for common case
|
||||
void clear_flag_(FieldFlags flag) { this->flags_ &= ~flag; }
|
||||
|
||||
// Helper to log unsupported feature and clear flag - reduces code duplication
|
||||
void log_and_clear_unsupported_(FieldFlags flag, const LogString *feature, bool use_color_mode_log);
|
||||
|
||||
LightState *parent_;
|
||||
|
||||
// Light state values - use flags_ to check if a value has been set.
|
||||
|
@@ -1,39 +0,0 @@
|
||||
#include "lm75b.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lm75b {
|
||||
|
||||
static const char *const TAG = "lm75b";
|
||||
|
||||
void LM75BComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "LM75B:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Setting up LM75B failed!");
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Temperature", this);
|
||||
}
|
||||
|
||||
void LM75BComponent::update() {
|
||||
// Create a temporary buffer
|
||||
uint8_t buff[2];
|
||||
if (this->read_register(LM75B_REG_TEMPERATURE, buff, 2) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
// Obtain combined 16-bit value
|
||||
int16_t raw_temperature = (buff[0] << 8) | buff[1];
|
||||
// Read the 11-bit raw temperature value
|
||||
raw_temperature >>= 5;
|
||||
// Publish the temperature in °C
|
||||
this->publish_state(raw_temperature * 0.125);
|
||||
if (this->status_has_warning()) {
|
||||
this->status_clear_warning();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lm75b
|
||||
} // namespace esphome
|
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace lm75b {
|
||||
|
||||
static const uint8_t LM75B_REG_TEMPERATURE = 0x00;
|
||||
|
||||
class LM75BComponent : public PollingComponent, public i2c::I2CDevice, public sensor::Sensor {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
};
|
||||
|
||||
} // namespace lm75b
|
||||
} // namespace esphome
|
@@ -1,34 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@beormund"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
lm75b_ns = cg.esphome_ns.namespace("lm75b")
|
||||
LM75BComponent = lm75b_ns.class_(
|
||||
"LM75BComponent", cg.PollingComponent, i2c.I2CDevice, sensor.Sensor
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
LM75BComponent,
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x48))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
@@ -2,7 +2,6 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <limits>
|
||||
|
||||
using esphome::i2c::ErrorCode;
|
||||
|
||||
@@ -29,30 +28,30 @@ bool operator!=(const GainTimePair &lhs, const GainTimePair &rhs) {
|
||||
|
||||
template<typename T, size_t size> T get_next(const T (&array)[size], const T val) {
|
||||
size_t i = 0;
|
||||
size_t idx = std::numeric_limits<size_t>::max();
|
||||
while (idx == std::numeric_limits<size_t>::max() && i < size) {
|
||||
size_t idx = -1;
|
||||
while (idx == -1 && i < size) {
|
||||
if (array[i] == val) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (idx == std::numeric_limits<size_t>::max() || i + 1 >= size)
|
||||
if (idx == -1 || i + 1 >= size)
|
||||
return val;
|
||||
return array[i + 1];
|
||||
}
|
||||
|
||||
template<typename T, size_t size> T get_prev(const T (&array)[size], const T val) {
|
||||
size_t i = size - 1;
|
||||
size_t idx = std::numeric_limits<size_t>::max();
|
||||
while (idx == std::numeric_limits<size_t>::max() && i > 0) {
|
||||
size_t idx = -1;
|
||||
while (idx == -1 && i > 0) {
|
||||
if (array[i] == val) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
if (idx == std::numeric_limits<size_t>::max() || i == 0)
|
||||
if (idx == -1 || i == 0)
|
||||
return val;
|
||||
return array[i - 1];
|
||||
}
|
||||
|
@@ -2,7 +2,6 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <limits>
|
||||
|
||||
using esphome::i2c::ErrorCode;
|
||||
|
||||
@@ -15,30 +14,30 @@ static const uint8_t MAX_TRIES = 5;
|
||||
|
||||
template<typename T, size_t size> T get_next(const T (&array)[size], const T val) {
|
||||
size_t i = 0;
|
||||
size_t idx = std::numeric_limits<size_t>::max();
|
||||
while (idx == std::numeric_limits<size_t>::max() && i < size) {
|
||||
size_t idx = -1;
|
||||
while (idx == -1 && i < size) {
|
||||
if (array[i] == val) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (idx == std::numeric_limits<size_t>::max() || i + 1 >= size)
|
||||
if (idx == -1 || i + 1 >= size)
|
||||
return val;
|
||||
return array[i + 1];
|
||||
}
|
||||
|
||||
template<typename T, size_t size> T get_prev(const T (&array)[size], const T val) {
|
||||
size_t i = size - 1;
|
||||
size_t idx = std::numeric_limits<size_t>::max();
|
||||
while (idx == std::numeric_limits<size_t>::max() && i > 0) {
|
||||
size_t idx = -1;
|
||||
while (idx == -1 && i > 0) {
|
||||
if (array[i] == val) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
if (idx == std::numeric_limits<size_t>::max() || i == 0)
|
||||
if (idx == -1 || i == 0)
|
||||
return val;
|
||||
return array[i - 1];
|
||||
}
|
||||
|
@@ -29,9 +29,9 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component {
|
||||
void set_columns(std::vector<GPIOPin *> pins) { columns_ = std::move(pins); };
|
||||
void set_rows(std::vector<GPIOPin *> pins) { rows_ = std::move(pins); };
|
||||
void set_keys(std::string keys) { keys_ = std::move(keys); };
|
||||
void set_debounce_time(uint32_t debounce_time) { debounce_time_ = debounce_time; };
|
||||
void set_has_diodes(bool has_diodes) { has_diodes_ = has_diodes; };
|
||||
void set_has_pulldowns(bool has_pulldowns) { has_pulldowns_ = has_pulldowns; };
|
||||
void set_debounce_time(int debounce_time) { debounce_time_ = debounce_time; };
|
||||
void set_has_diodes(int has_diodes) { has_diodes_ = has_diodes; };
|
||||
void set_has_pulldowns(int has_pulldowns) { has_pulldowns_ = has_pulldowns; };
|
||||
|
||||
void register_listener(MatrixKeypadListener *listener);
|
||||
void register_key_trigger(MatrixKeyTrigger *trig);
|
||||
@@ -40,7 +40,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component {
|
||||
std::vector<GPIOPin *> rows_;
|
||||
std::vector<GPIOPin *> columns_;
|
||||
std::string keys_;
|
||||
uint32_t debounce_time_ = 0;
|
||||
int debounce_time_ = 0;
|
||||
bool has_diodes_{false};
|
||||
bool has_pulldowns_{false};
|
||||
int pressed_key_ = -1;
|
||||
|
@@ -90,7 +90,7 @@ void MAX7219Component::loop() {
|
||||
}
|
||||
|
||||
if (this->scroll_mode_ == ScrollMode::STOP) {
|
||||
if (static_cast<size_t>(this->stepsleft_ + get_width_internal()) == first_line_size + 1) {
|
||||
if (this->stepsleft_ + get_width_internal() == first_line_size + 1) {
|
||||
if (millis_since_last_scroll < this->scroll_dwell_) {
|
||||
ESP_LOGVV(TAG, "Dwell time at end of string in case of stop at end. Step %d, since last scroll %d, dwell %d.",
|
||||
this->stepsleft_, millis_since_last_scroll, this->scroll_dwell_);
|
||||
|
@@ -56,7 +56,7 @@ void MCP23016::pin_mode(uint8_t pin, gpio::Flags flags) {
|
||||
this->update_reg_(pin, false, iodir);
|
||||
}
|
||||
}
|
||||
float MCP23016::get_setup_priority() const { return setup_priority::IO; }
|
||||
float MCP23016::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
bool MCP23016::read_reg_(uint8_t reg, uint8_t *value) {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
@@ -572,7 +572,7 @@ void MixerSpeaker::audio_mixer_task(void *params) {
|
||||
}
|
||||
} else {
|
||||
// Determine how many frames to mix
|
||||
for (size_t i = 0; i < transfer_buffers_with_data.size(); ++i) {
|
||||
for (int i = 0; i < transfer_buffers_with_data.size(); ++i) {
|
||||
const uint32_t frames_available_in_buffer =
|
||||
speakers_with_data[i]->get_audio_stream_info().bytes_to_frames(transfer_buffers_with_data[i]->available());
|
||||
frames_to_mix = std::min(frames_to_mix, frames_available_in_buffer);
|
||||
@@ -581,7 +581,7 @@ void MixerSpeaker::audio_mixer_task(void *params) {
|
||||
audio::AudioStreamInfo primary_stream_info = speakers_with_data[0]->get_audio_stream_info();
|
||||
|
||||
// Mix two streams together
|
||||
for (size_t i = 1; i < transfer_buffers_with_data.size(); ++i) {
|
||||
for (int i = 1; i < transfer_buffers_with_data.size(); ++i) {
|
||||
mix_audio_samples(primary_buffer, primary_stream_info,
|
||||
reinterpret_cast<int16_t *>(transfer_buffers_with_data[i]->get_buffer_start()),
|
||||
speakers_with_data[i]->get_audio_stream_info(),
|
||||
@@ -596,7 +596,7 @@ void MixerSpeaker::audio_mixer_task(void *params) {
|
||||
}
|
||||
|
||||
// Update source transfer buffer lengths and add new audio durations to the source speaker pending playbacks
|
||||
for (size_t i = 0; i < transfer_buffers_with_data.size(); ++i) {
|
||||
for (int i = 0; i < transfer_buffers_with_data.size(); ++i) {
|
||||
transfer_buffers_with_data[i]->decrease_buffer_length(
|
||||
speakers_with_data[i]->get_audio_stream_info().frames_to_bytes(frames_to_mix));
|
||||
speakers_with_data[i]->pending_playback_frames_ += frames_to_mix;
|
||||
|
@@ -218,7 +218,7 @@ void NAU7802Sensor::dump_config() {
|
||||
|
||||
void NAU7802Sensor::write_value_(uint8_t start_reg, size_t size, int32_t value) {
|
||||
uint8_t data[4];
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
data[i] = 0xFF & (value >> (size - 1 - i) * 8);
|
||||
}
|
||||
this->write_register(start_reg, data, size);
|
||||
@@ -228,7 +228,7 @@ int32_t NAU7802Sensor::read_value_(uint8_t start_reg, size_t size) {
|
||||
uint8_t data[4];
|
||||
this->read_register(start_reg, data, size);
|
||||
int32_t result = 0;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
result |= data[i] << (size - 1 - i) * 8;
|
||||
}
|
||||
// extend sign bit
|
||||
|
@@ -117,8 +117,7 @@ int HOT BmpDecoder::decode(uint8_t *buffer, size_t size) {
|
||||
this->paint_index_++;
|
||||
this->current_index_ += 3;
|
||||
index += 3;
|
||||
size_t last_col = static_cast<size_t>(this->width_) - 1;
|
||||
if (x == last_col && this->padding_bytes_ > 0) {
|
||||
if (x == this->width_ - 1 && this->padding_bytes_ > 0) {
|
||||
index += this->padding_bytes_;
|
||||
this->current_index_ += this->padding_bytes_;
|
||||
}
|
||||
|
@@ -25,10 +25,8 @@ static int draw_callback(JPEGDRAW *jpeg) {
|
||||
// to avoid crashing.
|
||||
App.feed_wdt();
|
||||
size_t position = 0;
|
||||
size_t height = static_cast<size_t>(jpeg->iHeight);
|
||||
size_t width = static_cast<size_t>(jpeg->iWidth);
|
||||
for (size_t y = 0; y < height; y++) {
|
||||
for (size_t x = 0; x < width; x++) {
|
||||
for (size_t y = 0; y < jpeg->iHeight; y++) {
|
||||
for (size_t x = 0; x < jpeg->iWidth; x++) {
|
||||
auto rg = decode_value(jpeg->pPixels[position++]);
|
||||
auto ba = decode_value(jpeg->pPixels[position++]);
|
||||
Color color(rg[1], rg[0], ba[1], ba[0]);
|
||||
|
@@ -104,7 +104,7 @@ float PIDController::weighted_average_(std::deque<float> &list, float new_value,
|
||||
list.push_front(new_value);
|
||||
|
||||
// keep only 'samples' readings, by popping off the back of the list
|
||||
while (samples > 0 && list.size() > static_cast<size_t>(samples))
|
||||
while (list.size() > samples)
|
||||
list.pop_back();
|
||||
|
||||
// calculate and return the average of all values in the list
|
||||
|
@@ -8,7 +8,6 @@ namespace esphome {
|
||||
namespace qmc5883l {
|
||||
|
||||
static const char *const TAG = "qmc5883l";
|
||||
|
||||
static const uint8_t QMC5883L_ADDRESS = 0x0D;
|
||||
|
||||
static const uint8_t QMC5883L_REGISTER_DATA_X_LSB = 0x00;
|
||||
@@ -33,10 +32,6 @@ void QMC5883LComponent::setup() {
|
||||
}
|
||||
delay(10);
|
||||
|
||||
if (this->drdy_pin_) {
|
||||
this->drdy_pin_->setup();
|
||||
}
|
||||
|
||||
uint8_t control_1 = 0;
|
||||
control_1 |= 0b01 << 0; // MODE (Mode) -> 0b00=standby, 0b01=continuous
|
||||
control_1 |= this->datarate_ << 2;
|
||||
@@ -69,7 +64,6 @@ void QMC5883LComponent::setup() {
|
||||
high_freq_.start();
|
||||
}
|
||||
}
|
||||
|
||||
void QMC5883LComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "QMC5883L:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
@@ -83,20 +77,11 @@ void QMC5883LComponent::dump_config() {
|
||||
LOG_SENSOR(" ", "Z Axis", this->z_sensor_);
|
||||
LOG_SENSOR(" ", "Heading", this->heading_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
LOG_PIN(" DRDY Pin: ", this->drdy_pin_);
|
||||
}
|
||||
|
||||
float QMC5883LComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void QMC5883LComponent::update() {
|
||||
i2c::ErrorCode err;
|
||||
uint8_t status = false;
|
||||
|
||||
// If DRDY pin is configured and the data is not ready return.
|
||||
if (this->drdy_pin_ && !this->drdy_pin_->digital_read()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Status byte gets cleared when data is read, so we have to read this first.
|
||||
// If status and two axes are desired, it's possible to save one byte of traffic by enabling
|
||||
// ROL_PNT in setup and reading 7 bytes starting at the status register.
|
||||
|
@@ -3,7 +3,6 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace qmc5883l {
|
||||
@@ -34,7 +33,6 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||
float get_setup_priority() const override;
|
||||
void update() override;
|
||||
|
||||
void set_drdy_pin(GPIOPin *pin) { drdy_pin_ = pin; }
|
||||
void set_datarate(QMC5883LDatarate datarate) { datarate_ = datarate; }
|
||||
void set_range(QMC5883LRange range) { range_ = range; }
|
||||
void set_oversampling(QMC5883LOversampling oversampling) { oversampling_ = oversampling; }
|
||||
@@ -53,7 +51,6 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
|
||||
sensor::Sensor *z_sensor_{nullptr};
|
||||
sensor::Sensor *heading_sensor_{nullptr};
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
GPIOPin *drdy_pin_{nullptr};
|
||||
enum ErrorCode {
|
||||
NONE = 0,
|
||||
COMMUNICATION_FAILED,
|
||||
|
@@ -1,12 +1,8 @@
|
||||
import logging
|
||||
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_DATA_RATE,
|
||||
CONF_FIELD_STRENGTH_X,
|
||||
CONF_FIELD_STRENGTH_Y,
|
||||
CONF_FIELD_STRENGTH_Z,
|
||||
@@ -25,10 +21,6 @@ from esphome.const import (
|
||||
UNIT_MICROTESLA,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_DRDY_PIN = "drdy_pin"
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
qmc5883l_ns = cg.esphome_ns.namespace("qmc5883l")
|
||||
@@ -60,18 +52,6 @@ QMC5883LOversamplings = {
|
||||
}
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
if (
|
||||
config[CONF_UPDATE_INTERVAL].total_milliseconds < 15
|
||||
and CONF_DRDY_PIN not in config
|
||||
):
|
||||
_LOGGER.warning(
|
||||
"[qmc5883l] 'update_interval' is less than 15ms and 'drdy_pin' is "
|
||||
"not configured, this may result in I2C errors"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
def validate_enum(enum_values, units=None, int=True):
|
||||
_units = []
|
||||
if units is not None:
|
||||
@@ -108,7 +88,7 @@ temperature_schema = sensor.sensor_schema(
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(QMC5883LComponent),
|
||||
@@ -124,25 +104,29 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
|
||||
cv.Optional(CONF_HEADING): heading_schema,
|
||||
cv.Optional(CONF_TEMPERATURE): temperature_schema,
|
||||
cv.Optional(CONF_DRDY_PIN): pins.gpio_input_pin_schema,
|
||||
cv.Optional(CONF_DATA_RATE, default="200hz"): validate_enum(
|
||||
QMC5883LDatarates, units=["hz", "Hz"]
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x0D)),
|
||||
validate_config,
|
||||
.extend(i2c.i2c_device_schema(0x0D))
|
||||
)
|
||||
|
||||
|
||||
def auto_data_rate(config):
|
||||
interval_sec = config[CONF_UPDATE_INTERVAL].total_milliseconds / 1000
|
||||
interval_hz = 1.0 / interval_sec
|
||||
for datarate in sorted(QMC5883LDatarates.keys()):
|
||||
if float(datarate) >= interval_hz:
|
||||
return QMC5883LDatarates[datarate]
|
||||
return QMC5883LDatarates[200]
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_oversampling(config[CONF_OVERSAMPLING]))
|
||||
cg.add(var.set_datarate(config[CONF_DATA_RATE]))
|
||||
cg.add(var.set_datarate(auto_data_rate(config)))
|
||||
cg.add(var.set_range(config[CONF_RANGE]))
|
||||
if CONF_FIELD_STRENGTH_X in config:
|
||||
sens = await sensor.new_sensor(config[CONF_FIELD_STRENGTH_X])
|
||||
@@ -159,6 +143,3 @@ async def to_code(config):
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
if CONF_DRDY_PIN in config:
|
||||
pin = await cg.gpio_pin_expression(config[CONF_DRDY_PIN])
|
||||
cg.add(var.set_drdy_pin(pin))
|
||||
|
@@ -10,8 +10,8 @@ constexpr uint32_t BIT_MARK_US = 580; // 70us seems like a safe time delta for
|
||||
constexpr uint32_t BIT_ONE_SPACE_US = 1640;
|
||||
constexpr uint32_t BIT_ZERO_SPACE_US = 545;
|
||||
constexpr uint64_t HEADER = 0b011001001100010uL; // 15 bits
|
||||
constexpr size_t HEADER_SIZE = 15;
|
||||
constexpr size_t CODE_SIZE = 17;
|
||||
constexpr uint64_t HEADER_SIZE = 15;
|
||||
constexpr uint64_t CODE_SIZE = 17;
|
||||
|
||||
void GoboxProtocol::dump_timings_(const RawTimings &timings) const {
|
||||
ESP_LOGD(TAG, "Gobox: size=%u", timings.size());
|
||||
@@ -39,7 +39,7 @@ void GoboxProtocol::encode(RemoteTransmitData *dst, const GoboxData &data) {
|
||||
}
|
||||
|
||||
optional<GoboxData> GoboxProtocol::decode(RemoteReceiveData src) {
|
||||
if (static_cast<size_t>(src.size()) < ((HEADER_SIZE + CODE_SIZE) * 2 + 1)) {
|
||||
if (src.size() < ((HEADER_SIZE + CODE_SIZE) * 2 + 1)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@@ -268,10 +268,10 @@ def validate_spi_config(config):
|
||||
|
||||
# Given an SPI index, convert to a string that represents the C++ object for it.
|
||||
def get_spi_interface(index):
|
||||
if CORE.using_esp_idf:
|
||||
platform = get_target_platform()
|
||||
if platform == PLATFORM_ESP32:
|
||||
return ["SPI2_HOST", "SPI3_HOST"][index]
|
||||
# Arduino code follows
|
||||
platform = get_target_platform()
|
||||
if platform == PLATFORM_RP2040:
|
||||
return ["&SPI", "&SPI1"][index]
|
||||
if index == 0:
|
||||
@@ -306,7 +306,7 @@ def spi_mode_schema(mode):
|
||||
if mode == TYPE_SINGLE:
|
||||
return SPI_SINGLE_SCHEMA
|
||||
pin_count = 4 if mode == TYPE_QUAD else 8
|
||||
onlys = [cv.only_on([PLATFORM_ESP32]), cv.only_with_esp_idf]
|
||||
onlys = [cv.only_on([PLATFORM_ESP32])]
|
||||
if pin_count == 8:
|
||||
onlys.append(
|
||||
only_on_variant(
|
||||
@@ -352,7 +352,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
async def to_code(configs):
|
||||
cg.add_define("USE_SPI")
|
||||
cg.add_global(spi_ns.using)
|
||||
if CORE.using_arduino:
|
||||
if CORE.using_arduino and get_target_platform() != PLATFORM_ESP32:
|
||||
cg.add_library("SPI", None)
|
||||
for spi in configs:
|
||||
var = cg.new_Pvariable(spi[CONF_ID])
|
||||
@@ -394,7 +394,9 @@ def spi_device_schema(
|
||||
cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum(
|
||||
SPI_MODE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_RELEASE_DEVICE): cv.All(cv.boolean, cv.only_with_esp_idf),
|
||||
cv.Optional(CONF_RELEASE_DEVICE): cv.All(
|
||||
cv.boolean, cv.only_on([PLATFORM_ESP32])
|
||||
),
|
||||
}
|
||||
if cs_pin_required:
|
||||
schema[cv.Required(CONF_CS_PIN)] = pins.gpio_output_pin_schema
|
||||
@@ -443,13 +445,15 @@ def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso:
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"spi_arduino.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP8266_ARDUINO,
|
||||
PlatformFramework.RP2040_ARDUINO,
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
},
|
||||
"spi_esp_idf.cpp": {PlatformFramework.ESP32_IDF},
|
||||
"spi_esp_idf.cpp": {
|
||||
PlatformFramework.ESP32_IDF,
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
@@ -19,13 +19,13 @@ using SPIInterface = SPIClass *;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "driver/spi_master.h"
|
||||
|
||||
using SPIInterface = spi_host_device_t;
|
||||
|
||||
#endif // USE_ESP_IDF
|
||||
#endif // USE_ESP32
|
||||
|
||||
#ifdef USE_ZEPHYR
|
||||
// TODO supprse clang-tidy. Remove after SPI driver for nrf52 is added.
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace esphome {
|
||||
namespace spi {
|
||||
#ifdef USE_ARDUINO
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
|
||||
static const char *const TAG = "spi-esp-arduino";
|
||||
class SPIDelegateHw : public SPIDelegate {
|
||||
@@ -73,9 +73,6 @@ class SPIBusHw : public SPIBus {
|
||||
channel->pins(Utility::get_pin_no(clk), Utility::get_pin_no(sdi), Utility::get_pin_no(sdo), -1);
|
||||
channel->begin();
|
||||
#endif // USE_ESP8266
|
||||
#ifdef USE_ESP32
|
||||
channel->begin(Utility::get_pin_no(clk), Utility::get_pin_no(sdi), Utility::get_pin_no(sdo), -1);
|
||||
#endif
|
||||
#ifdef USE_RP2040
|
||||
if (Utility::get_pin_no(sdi) != -1)
|
||||
channel->setRX(Utility::get_pin_no(sdi));
|
||||
|
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace spi {
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#ifdef USE_ESP32
|
||||
static const char *const TAG = "spi-esp-idf";
|
||||
static const size_t MAX_TRANSFER_SIZE = 4092; // dictated by ESP-IDF API.
|
||||
|
||||
|
@@ -50,10 +50,8 @@ void HOT I2CST7567::write_display_data() {
|
||||
|
||||
static const size_t BLOCK_SIZE = 64;
|
||||
for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x += BLOCK_SIZE) {
|
||||
size_t remaining = static_cast<size_t>(this->get_width_internal()) - x;
|
||||
size_t chunk = remaining > BLOCK_SIZE ? BLOCK_SIZE : remaining;
|
||||
this->write_register(esphome::st7567_base::ST7567_SET_START_LINE, &buffer_[y * this->get_width_internal() + x],
|
||||
chunk);
|
||||
this->get_width_internal() - x > BLOCK_SIZE ? BLOCK_SIZE : this->get_width_internal() - x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -176,9 +176,8 @@ void ST7789V::write_display_data() {
|
||||
if (this->eightbitcolor_) {
|
||||
uint8_t temp_buffer[TEMP_BUFFER_SIZE];
|
||||
size_t temp_index = 0;
|
||||
size_t width = static_cast<size_t>(this->get_width_internal());
|
||||
for (size_t line = 0; line < this->get_buffer_length_(); line += width) {
|
||||
for (size_t index = 0; index < width; ++index) {
|
||||
for (int line = 0; line < this->get_buffer_length_(); line = line + this->get_width_internal()) {
|
||||
for (int index = 0; index < this->get_width_internal(); ++index) {
|
||||
auto color = display::ColorUtil::color_to_565(
|
||||
display::ColorUtil::to_color(this->buffer_[index + line], display::ColorOrder::COLOR_ORDER_RGB,
|
||||
display::ColorBitness::COLOR_BITNESS_332, true));
|
||||
|
@@ -151,7 +151,7 @@ void StatsdComponent::send_(std::string *out) {
|
||||
|
||||
int n_bytes = this->sock_->sendto(out->c_str(), out->length(), 0, reinterpret_cast<sockaddr *>(&this->destination_),
|
||||
sizeof(this->destination_));
|
||||
if (n_bytes != static_cast<int>(out->length())) {
|
||||
if (n_bytes != out->length()) {
|
||||
ESP_LOGE(TAG, "Failed to send UDP packed (%d of %d)", n_bytes, out->length());
|
||||
}
|
||||
#endif
|
||||
|
@@ -703,15 +703,6 @@ class EsphomeCore:
|
||||
def relative_piolibdeps_path(self, *path: str | Path) -> Path:
|
||||
return self.relative_build_path(".piolibdeps", *path)
|
||||
|
||||
@property
|
||||
def platformio_cache_dir(self) -> str:
|
||||
"""Get the PlatformIO cache directory path."""
|
||||
# Check if running in Docker/HA addon with custom cache dir
|
||||
if (cache_dir := os.environ.get("PLATFORMIO_CACHE_DIR")) and cache_dir.strip():
|
||||
return cache_dir
|
||||
# Default PlatformIO cache location
|
||||
return os.path.expanduser("~/.platformio/.cache")
|
||||
|
||||
@property
|
||||
def firmware_bin(self) -> Path:
|
||||
if self.is_libretiny:
|
||||
|
@@ -390,8 +390,10 @@ int8_t step_to_accuracy_decimals(float step) {
|
||||
return str.length() - dot_pos - 1;
|
||||
}
|
||||
|
||||
// Store BASE64 characters as array - automatically placed in flash/ROM on embedded platforms
|
||||
static const char BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
// Use C-style string constant to store in ROM instead of RAM (saves 24 bytes)
|
||||
static constexpr const char *BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
// Helper function to find the index of a base64 character in the lookup table.
|
||||
// Returns the character's position (0-63) if found, or 0 if not found.
|
||||
@@ -401,8 +403,8 @@ static const char BASE64_CHARS[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr
|
||||
// stops processing at the first invalid character due to the is_base64() check in its
|
||||
// while loop condition, making this edge case harmless in practice.
|
||||
static inline uint8_t base64_find_char(char c) {
|
||||
const void *ptr = memchr(BASE64_CHARS, c, sizeof(BASE64_CHARS));
|
||||
return ptr ? (static_cast<const char *>(ptr) - BASE64_CHARS) : 0;
|
||||
const char *pos = strchr(BASE64_CHARS, c);
|
||||
return pos ? (pos - BASE64_CHARS) : 0;
|
||||
}
|
||||
|
||||
static inline bool is_base64(char c) { return (isalnum(c) || (c == '+') || (c == '/')); }
|
||||
|
@@ -143,9 +143,6 @@ template<typename T, size_t N> class StaticVector {
|
||||
size_t size() const { return count_; }
|
||||
bool empty() const { return count_ == 0; }
|
||||
|
||||
// Direct access to size counter for efficient in-place construction
|
||||
size_t &count() { return count_; }
|
||||
|
||||
T &operator[](size_t i) { return data_[i]; }
|
||||
const T &operator[](size_t i) const { return data_[i]; }
|
||||
|
||||
|
@@ -95,9 +95,10 @@ class Scheduler {
|
||||
} name_;
|
||||
uint32_t interval;
|
||||
// Split time to handle millis() rollover. The scheduler combines the 32-bit millis()
|
||||
// with a 16-bit rollover counter to create a 48-bit time space (stored as 64-bit
|
||||
// for compatibility). With 49.7 days per 32-bit rollover, the 16-bit counter
|
||||
// supports 49.7 days × 65536 = ~8900 years. This ensures correct scheduling
|
||||
// with a 16-bit rollover counter to create a 48-bit time space (using 32+16 bits).
|
||||
// This is intentionally limited to 48 bits, not stored as a full 64-bit value.
|
||||
// With 49.7 days per 32-bit rollover, the 16-bit counter supports
|
||||
// 49.7 days × 65536 = ~8900 years. This ensures correct scheduling
|
||||
// even when devices run for months. Split into two fields for better memory
|
||||
// alignment on 32-bit systems.
|
||||
uint32_t next_execution_low_; // Lower 32 bits of execution time (millis value)
|
||||
|
@@ -5,7 +5,6 @@ import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
from esphome.const import CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, KEY_CORE
|
||||
from esphome.core import CORE, EsphomeError
|
||||
@@ -112,16 +111,7 @@ def run_compile(config, verbose):
|
||||
args = []
|
||||
if CONF_COMPILE_PROCESS_LIMIT in config[CONF_ESPHOME]:
|
||||
args += [f"-j{config[CONF_ESPHOME][CONF_COMPILE_PROCESS_LIMIT]}"]
|
||||
result = run_platformio_cli_run(config, verbose, *args)
|
||||
|
||||
# Run memory analysis if enabled
|
||||
if config.get(CONF_ESPHOME, {}).get("analyze_memory", False):
|
||||
try:
|
||||
analyze_memory_usage(config)
|
||||
except Exception as e:
|
||||
_LOGGER.warning("Failed to analyze memory usage: %s", e)
|
||||
|
||||
return result
|
||||
return run_platformio_cli_run(config, verbose, *args)
|
||||
|
||||
|
||||
def _run_idedata(config):
|
||||
@@ -350,93 +340,3 @@ class IDEData:
|
||||
return f"{self.cc_path[:-7]}addr2line.exe"
|
||||
|
||||
return f"{self.cc_path[:-3]}addr2line"
|
||||
|
||||
@property
|
||||
def objdump_path(self) -> str:
|
||||
# replace gcc at end with objdump
|
||||
|
||||
# Windows
|
||||
if self.cc_path.endswith(".exe"):
|
||||
return f"{self.cc_path[:-7]}objdump.exe"
|
||||
|
||||
return f"{self.cc_path[:-3]}objdump"
|
||||
|
||||
@property
|
||||
def readelf_path(self) -> str:
|
||||
# replace gcc at end with readelf
|
||||
|
||||
# Windows
|
||||
if self.cc_path.endswith(".exe"):
|
||||
return f"{self.cc_path[:-7]}readelf.exe"
|
||||
|
||||
return f"{self.cc_path[:-3]}readelf"
|
||||
|
||||
|
||||
def analyze_memory_usage(config: dict[str, Any]) -> None:
|
||||
"""Analyze memory usage by component after compilation."""
|
||||
# Lazy import to avoid overhead when not needed
|
||||
from esphome.analyze_memory import MemoryAnalyzer
|
||||
|
||||
idedata = get_idedata(config)
|
||||
|
||||
# Get paths to tools
|
||||
elf_path = idedata.firmware_elf_path
|
||||
objdump_path = idedata.objdump_path
|
||||
readelf_path = idedata.readelf_path
|
||||
|
||||
# Debug logging
|
||||
_LOGGER.debug("ELF path from idedata: %s", elf_path)
|
||||
|
||||
# Check if file exists
|
||||
if not Path(elf_path).exists():
|
||||
# Try alternate path
|
||||
alt_path = Path(CORE.relative_build_path(".pioenvs", CORE.name, "firmware.elf"))
|
||||
if alt_path.exists():
|
||||
elf_path = str(alt_path)
|
||||
_LOGGER.debug("Using alternate ELF path: %s", elf_path)
|
||||
else:
|
||||
_LOGGER.warning("ELF file not found at %s or %s", elf_path, alt_path)
|
||||
return
|
||||
|
||||
# Extract external components from config
|
||||
external_components = set()
|
||||
|
||||
# Get the list of built-in ESPHome components
|
||||
from esphome.analyze_memory import get_esphome_components
|
||||
|
||||
builtin_components = get_esphome_components()
|
||||
|
||||
# Special non-component keys that appear in configs
|
||||
NON_COMPONENT_KEYS = {
|
||||
CONF_ESPHOME,
|
||||
"substitutions",
|
||||
"packages",
|
||||
"globals",
|
||||
"<<",
|
||||
}
|
||||
|
||||
# Check all top-level keys in config
|
||||
for key in config:
|
||||
if key not in builtin_components and key not in NON_COMPONENT_KEYS:
|
||||
# This is an external component
|
||||
external_components.add(key)
|
||||
|
||||
_LOGGER.debug("Detected external components: %s", external_components)
|
||||
|
||||
# Create analyzer and run analysis
|
||||
analyzer = MemoryAnalyzer(elf_path, objdump_path, readelf_path, external_components)
|
||||
analyzer.analyze()
|
||||
|
||||
# Generate and print report
|
||||
report = analyzer.generate_report()
|
||||
_LOGGER.info("\n%s", report)
|
||||
|
||||
# Optionally save to file
|
||||
if config.get(CONF_ESPHOME, {}).get("memory_report_file"):
|
||||
report_file = Path(config[CONF_ESPHOME]["memory_report_file"])
|
||||
if report_file.suffix == ".json":
|
||||
report_file.write_text(analyzer.to_json())
|
||||
_LOGGER.info("Memory report saved to %s", report_file)
|
||||
else:
|
||||
report_file.write_text(report)
|
||||
_LOGGER.info("Memory report saved to %s", report_file)
|
||||
|
@@ -1,9 +0,0 @@
|
||||
i2c:
|
||||
- id: i2c_lm75b
|
||||
scl: ${scl_pin}
|
||||
sda: ${sda_pin}
|
||||
|
||||
sensor:
|
||||
- platform: lm75b
|
||||
name: LM75B Temperature
|
||||
update_interval: 30s
|
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO15
|
||||
sda_pin: GPIO13
|
||||
|
||||
<<: !include common.yaml
|
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
|
||||
<<: !include common.yaml
|
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
|
||||
<<: !include common.yaml
|
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO15
|
||||
sda_pin: GPIO13
|
||||
|
||||
<<: !include common.yaml
|
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
|
||||
<<: !include common.yaml
|
@@ -1,5 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
|
||||
<<: !include common.yaml
|
@@ -17,7 +17,5 @@ sensor:
|
||||
temperature:
|
||||
name: QMC5883L Temperature
|
||||
range: 800uT
|
||||
data_rate: 200Hz
|
||||
oversampling: 256x
|
||||
update_interval: 15s
|
||||
drdy_pin: ${drdy_pin}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO16
|
||||
sda_pin: GPIO17
|
||||
drdy_pin: GPIO18
|
||||
|
||||
<<: !include common.yaml
|
||||
|
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
drdy_pin: GPIO6
|
||||
|
||||
<<: !include common.yaml
|
||||
|
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
drdy_pin: GPIO6
|
||||
|
||||
<<: !include common.yaml
|
||||
|
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO16
|
||||
sda_pin: GPIO17
|
||||
drdy_pin: GPIO18
|
||||
|
||||
<<: !include common.yaml
|
||||
|
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
drdy_pin: GPIO2
|
||||
|
||||
<<: !include common.yaml
|
||||
|
@@ -1,6 +1,5 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO5
|
||||
sda_pin: GPIO4
|
||||
drdy_pin: GPIO2
|
||||
|
||||
<<: !include common.yaml
|
||||
|
@@ -1,6 +1,16 @@
|
||||
substitutions:
|
||||
clk_pin: GPIO6
|
||||
mosi_pin: GPIO7
|
||||
miso_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
||||
spi:
|
||||
- id: quad_spi
|
||||
type: quad
|
||||
interface: hardware
|
||||
clk_pin:
|
||||
number: 6
|
||||
data_pins:
|
||||
- 7
|
||||
- 2
|
||||
- 10
|
||||
- allow_other_uses: true
|
||||
number: 3
|
||||
- id: spi_id_2
|
||||
interface: any
|
||||
clk_pin: 4
|
||||
mosi_pin: 5
|
||||
|
@@ -1,13 +1,37 @@
|
||||
spi:
|
||||
- id: three_spi
|
||||
- id: quad_spi
|
||||
type: quad
|
||||
interface: spi3
|
||||
clk_pin:
|
||||
number: 47
|
||||
mosi_pin:
|
||||
number: 40
|
||||
- id: hw_spi
|
||||
data_pins:
|
||||
- allow_other_uses: true
|
||||
number: 40
|
||||
- allow_other_uses: true
|
||||
number: 41
|
||||
- allow_other_uses: true
|
||||
number: 42
|
||||
- allow_other_uses: true
|
||||
number: 43
|
||||
- id: octal_spi
|
||||
type: octal
|
||||
interface: hardware
|
||||
clk_pin:
|
||||
number: 0
|
||||
miso_pin:
|
||||
number: 41
|
||||
data_pins:
|
||||
- 36
|
||||
- 37
|
||||
- 38
|
||||
- 39
|
||||
- allow_other_uses: true
|
||||
number: 40
|
||||
- allow_other_uses: true
|
||||
number: 41
|
||||
- allow_other_uses: true
|
||||
number: 42
|
||||
- allow_other_uses: true
|
||||
number: 43
|
||||
- id: spi_id_3
|
||||
interface: any
|
||||
clk_pin: 8
|
||||
mosi_pin: 9
|
||||
|
@@ -15,7 +15,7 @@ async def test_oversized_payload_plaintext(
|
||||
run_compiled: RunCompiledFunction,
|
||||
api_client_connected_with_disconnect: APIClientConnectedWithDisconnectFactory,
|
||||
) -> None:
|
||||
"""Test that oversized payloads (>2304 bytes) from client cause disconnection without crashing."""
|
||||
"""Test that oversized payloads (>100KiB) from client cause disconnection without crashing."""
|
||||
process_exited = False
|
||||
helper_log_found = False
|
||||
|
||||
@@ -39,8 +39,8 @@ async def test_oversized_payload_plaintext(
|
||||
assert device_info is not None
|
||||
assert device_info.name == "oversized-plaintext"
|
||||
|
||||
# Create an oversized payload (>2304 bytes which is our new limit)
|
||||
oversized_data = b"X" * 3000 # ~3KiB, exceeds the 2304 byte limit
|
||||
# Create an oversized payload (>100KiB)
|
||||
oversized_data = b"X" * (100 * 1024 + 1) # 100KiB + 1 byte
|
||||
|
||||
# Access the internal connection to send raw data
|
||||
frame_helper = client._connection._frame_helper
|
||||
@@ -132,24 +132,22 @@ async def test_oversized_payload_noise(
|
||||
run_compiled: RunCompiledFunction,
|
||||
api_client_connected_with_disconnect: APIClientConnectedWithDisconnectFactory,
|
||||
) -> None:
|
||||
"""Test that oversized payloads from client cause disconnection without crashing with noise encryption."""
|
||||
"""Test that oversized payloads (>100KiB) from client cause disconnection without crashing with noise encryption."""
|
||||
noise_key = "N4Yle5YirwZhPiHHsdZLdOA73ndj/84veVaLhTvxCuU="
|
||||
process_exited = False
|
||||
helper_log_found = False
|
||||
cipherstate_failed = False
|
||||
|
||||
def check_logs(line: str) -> None:
|
||||
nonlocal process_exited, helper_log_found
|
||||
nonlocal process_exited, cipherstate_failed
|
||||
# Check for signs that the process exited/crashed
|
||||
if "Segmentation fault" in line or "core dumped" in line:
|
||||
process_exited = True
|
||||
# Check for HELPER_LOG message about message size exceeding maximum
|
||||
# With our new protection, oversized messages are rejected at frame level
|
||||
# Check for the expected warning about decryption failure
|
||||
if (
|
||||
"[VV]" in line
|
||||
and "Bad packet: message size" in line
|
||||
and "exceeds maximum" in line
|
||||
"[W][api.connection" in line
|
||||
and "Reading failed CIPHERSTATE_DECRYPT_FAILED" in line
|
||||
):
|
||||
helper_log_found = True
|
||||
cipherstate_failed = True
|
||||
|
||||
async with run_compiled(yaml_config, line_callback=check_logs):
|
||||
async with api_client_connected_with_disconnect(noise_psk=noise_key) as (
|
||||
@@ -161,8 +159,8 @@ async def test_oversized_payload_noise(
|
||||
assert device_info is not None
|
||||
assert device_info.name == "oversized-noise"
|
||||
|
||||
# Create an oversized payload (>2304 bytes which is our new limit)
|
||||
oversized_data = b"Y" * 3000 # ~3KiB, exceeds the 2304 byte limit
|
||||
# Create an oversized payload (>100KiB)
|
||||
oversized_data = b"Y" * (100 * 1024 + 1) # 100KiB + 1 byte
|
||||
|
||||
# Access the internal connection to send raw data
|
||||
frame_helper = client._connection._frame_helper
|
||||
@@ -177,9 +175,9 @@ async def test_oversized_payload_noise(
|
||||
|
||||
# After disconnection, verify process didn't crash
|
||||
assert not process_exited, "ESPHome process should not crash"
|
||||
# Verify we saw the expected HELPER_LOG message
|
||||
assert helper_log_found, (
|
||||
"Expected to see HELPER_LOG about message size exceeding maximum"
|
||||
# Verify we saw the expected warning message
|
||||
assert cipherstate_failed, (
|
||||
"Expected to see warning about CIPHERSTATE_DECRYPT_FAILED"
|
||||
)
|
||||
|
||||
# Try to reconnect to verify the process is still running
|
||||
|
@@ -661,45 +661,3 @@ class TestEsphomeCore:
|
||||
os.environ.pop("ESPHOME_IS_HA_ADDON", None)
|
||||
os.environ.pop("ESPHOME_DATA_DIR", None)
|
||||
assert target.data_dir == Path(expected_default)
|
||||
|
||||
def test_platformio_cache_dir_with_env_var(self):
|
||||
"""Test platformio_cache_dir when PLATFORMIO_CACHE_DIR env var is set."""
|
||||
target = core.EsphomeCore()
|
||||
test_cache_dir = "/custom/cache/dir"
|
||||
|
||||
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": test_cache_dir}):
|
||||
assert target.platformio_cache_dir == test_cache_dir
|
||||
|
||||
def test_platformio_cache_dir_without_env_var(self):
|
||||
"""Test platformio_cache_dir defaults to ~/.platformio/.cache."""
|
||||
target = core.EsphomeCore()
|
||||
|
||||
with patch.dict(os.environ, {}, clear=True):
|
||||
# Ensure env var is not set
|
||||
os.environ.pop("PLATFORMIO_CACHE_DIR", None)
|
||||
expected = os.path.expanduser("~/.platformio/.cache")
|
||||
assert target.platformio_cache_dir == expected
|
||||
|
||||
def test_platformio_cache_dir_empty_env_var(self):
|
||||
"""Test platformio_cache_dir with empty env var falls back to default."""
|
||||
target = core.EsphomeCore()
|
||||
|
||||
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": ""}):
|
||||
expected = os.path.expanduser("~/.platformio/.cache")
|
||||
assert target.platformio_cache_dir == expected
|
||||
|
||||
def test_platformio_cache_dir_whitespace_env_var(self):
|
||||
"""Test platformio_cache_dir with whitespace-only env var falls back to default."""
|
||||
target = core.EsphomeCore()
|
||||
|
||||
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": " "}):
|
||||
expected = os.path.expanduser("~/.platformio/.cache")
|
||||
assert target.platformio_cache_dir == expected
|
||||
|
||||
def test_platformio_cache_dir_docker_addon_path(self):
|
||||
"""Test platformio_cache_dir in Docker/HA addon environment."""
|
||||
target = core.EsphomeCore()
|
||||
addon_cache = "/data/cache/platformio"
|
||||
|
||||
with patch.dict(os.environ, {"PLATFORMIO_CACHE_DIR": addon_cache}):
|
||||
assert target.platformio_cache_dir == addon_cache
|
||||
|
@@ -355,7 +355,6 @@ def test_clean_build(
|
||||
mock_core.relative_pioenvs_path.return_value = pioenvs_dir
|
||||
mock_core.relative_piolibdeps_path.return_value = piolibdeps_dir
|
||||
mock_core.relative_build_path.return_value = dependencies_lock
|
||||
mock_core.platformio_cache_dir = str(platformio_cache_dir)
|
||||
|
||||
# Verify all exist before
|
||||
assert pioenvs_dir.exists()
|
||||
|
Reference in New Issue
Block a user