Merge pull request #16488 from esphome/bump-2026.5.0b2
CI for docker images / Build docker containers (docker, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (docker, ubuntu-24.04-arm) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04-arm) (push) Has been cancelled
CI / Create common environment (push) Has been cancelled
CI / Check pylint (push) Has been cancelled
CI / Run script/ci-custom (push) Has been cancelled
CI / Check import esphome.__main__ time (push) Has been cancelled
CI / Test downstream esphome/device-builder (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.11) (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.14) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.13) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.14) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.11) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.14) (push) Has been cancelled
CI / Determine which jobs to run (push) Has been cancelled
CI / Run integration tests (${{ matrix.bucket.name }}) (push) Has been cancelled
CI / Run C++ unit tests (push) Has been cancelled
CI / Run CodSpeed benchmarks (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF (push) Has been cancelled
CI / Run script/clang-tidy for ESP8266 (push) Has been cancelled
CI / Run script/clang-tidy for ZEPHYR (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Has been cancelled
CI / Test components batch (${{ matrix.components }}) (push) Has been cancelled
CI / Test components with native ESP-IDF (push) Has been cancelled
CI / pre-commit.ci lite (push) Has been cancelled
CI / Build target branch for memory impact (push) Has been cancelled
CI / Build PR branch for memory impact (push) Has been cancelled
CI / Comment memory impact (push) Has been cancelled
CI / CI Status (push) Has been cancelled

2026.5.0b2
This commit is contained in:
Jesse Hills
2026-05-18 15:28:53 +12:00
committed by GitHub
62 changed files with 483 additions and 167 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 2026.5.0b1 PROJECT_NUMBER = 2026.5.0b2
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a
+7 -3
View File
@@ -13,12 +13,16 @@ RUN git config --system --add safe.directory "*" \
&& git config --system advice.detachedHead false && git config --system advice.detachedHead false
# Install build tools for Python packages that require compilation # Install build tools for Python packages that require compilation
# (e.g., ruamel.yaml.clibz used by ESP-IDF's idf-component-manager) # (e.g., ruamel.yaml.clib used by ESP-IDF's idf-component-manager).
# Also install libusb-1.0 at runtime so the ESP-IDF tools installer can
# validate openocd-esp32 (it dynamically links libusb-1.0.so.0); without
# it idf_tools.py rejects the openocd install with exit 127 and aborts
# the whole framework setup.
RUN if command -v apk > /dev/null; then \ RUN if command -v apk > /dev/null; then \
apk add --no-cache build-base; \ apk add --no-cache build-base libusb; \
else \ else \
apt-get update \ apt-get update \
&& apt-get install -y --no-install-recommends build-essential \ && apt-get install -y --no-install-recommends build-essential libusb-1.0-0 \
&& rm -rf /var/lib/apt/lists/*; \ && rm -rf /var/lib/apt/lists/*; \
fi fi
+8
View File
@@ -50,6 +50,7 @@ from esphome.const import (
CONF_TOPIC, CONF_TOPIC,
CONF_USERNAME, CONF_USERNAME,
CONF_WEB_SERVER, CONF_WEB_SERVER,
CONF_WIFI,
ENV_NOGITIGNORE, ENV_NOGITIGNORE,
KEY_CORE, KEY_CORE,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
@@ -733,6 +734,13 @@ def write_cpp_file() -> int:
def compile_program(args: ArgsProtocol, config: ConfigType) -> int: def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
# Keep this gate here, NOT in config validation: device-builder needs
# `esphome config` to keep succeeding with placeholders so onboarding can run.
if CONF_WIFI in config:
from esphome.components.wifi import check_placeholder_credentials
check_placeholder_credentials(config)
# NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py # NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py
# If you change this format, update the regex in that script as well # If you change this format, update the regex in that script as well
_LOGGER.info("Compiling app... Build path: %s", CORE.build_path) _LOGGER.info("Compiling app... Build path: %s", CORE.build_path)
+8 -2
View File
@@ -3,7 +3,8 @@
import json import json
from pathlib import Path from pathlib import Path
from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32 import get_esp32_variant, idf_version
import esphome.config_validation as cv
from esphome.core import CORE from esphome.core import CORE
from esphome.helpers import mkdir_p, write_file_if_changed from esphome.helpers import mkdir_p, write_file_if_changed
from esphome.writer import update_storage_json from esphome.writer import update_storage_json
@@ -61,6 +62,11 @@ def get_project_cmakelists(minimal: bool = False) -> str:
variant = get_esp32_variant() variant = get_esp32_variant()
idf_target = variant.lower().replace("-", "") idf_target = variant.lower().replace("-", "")
# esp_idf_size 2.x (bundled with IDF >=6.0) made NG the default and
# removed the --ng flag; on 1.x (IDF 5.5) --ng is required to get
# --format=raw because the legacy mode doesn't support it.
size_ng_flag = "--ng" if idf_version() < cv.Version(6, 0, 0) else ""
# Project-wide compile options: -D defines and -W warning flags (skip # Project-wide compile options: -D defines and -W warning flags (skip
# -Wl, linker flags — those go on the src component via # -Wl, linker flags — those go on the src component via
# target_link_options below). Emitted via idf_build_set_property so the # target_link_options below). Emitted via idf_build_set_property so the
@@ -146,7 +152,7 @@ project({CORE.name})
# Emit raw JSON size data for ESPHome to read post-build. # Emit raw JSON size data for ESPHome to read post-build.
add_custom_command( add_custom_command(
TARGET ${{CMAKE_PROJECT_NAME}}.elf POST_BUILD TARGET ${{CMAKE_PROJECT_NAME}}.elf POST_BUILD
COMMAND ${{PYTHON}} -m esp_idf_size --ng --format=raw COMMAND ${{PYTHON}} -m esp_idf_size {size_ng_flag} --format=raw
-o ${{CMAKE_BINARY_DIR}}/esp_idf_size.json -o ${{CMAKE_BINARY_DIR}}/esp_idf_size.json
${{CMAKE_PROJECT_NAME}}.map ${{CMAKE_PROJECT_NAME}}.map
WORKING_DIRECTORY ${{CMAKE_BINARY_DIR}} WORKING_DIRECTORY ${{CMAKE_BINARY_DIR}}
+1 -1
View File
@@ -395,7 +395,7 @@ async def to_code(config):
) )
if data.mp3_support: if data.mp3_support:
cg.add_define("USE_AUDIO_MP3_SUPPORT") cg.add_define("USE_AUDIO_MP3_SUPPORT")
add_idf_component(name="esphome/micro-mp3", ref="0.2.0") add_idf_component(name="esphome/micro-mp3", ref="0.2.1")
_emit_memory_pair( _emit_memory_pair(
data.mp3.buffer_memory, data.mp3.buffer_memory,
"CONFIG_MP3_DECODER_PREFER_PSRAM", "CONFIG_MP3_DECODER_PREFER_PSRAM",
@@ -161,7 +161,7 @@ void BME680BSECComponent::dump_config() {
" IAQ Mode: %s\n" " IAQ Mode: %s\n"
" Supply Voltage: %sV\n" " Supply Voltage: %sV\n"
" Sample Rate: %s\n" " Sample Rate: %s\n"
" State Save Interval: %ims", " State Save Interval: %" PRIu32 "ms",
this->temperature_offset_, this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile", this->temperature_offset_, this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile",
this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8", this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8",
BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_), this->state_save_interval_ms_); BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_), this->state_save_interval_ms_);
@@ -461,7 +461,7 @@ int8_t BME680BSECComponent::write_bytes_wrapper(uint8_t devid, uint8_t a_registe
} }
void BME680BSECComponent::delay_ms(uint32_t period) { void BME680BSECComponent::delay_ms(uint32_t period) {
ESP_LOGV(TAG, "Delaying for %ums", period); ESP_LOGV(TAG, "Delaying for %" PRIu32 "ms", period);
delay(period); delay(period);
} }
+16 -3
View File
@@ -1767,9 +1767,11 @@ async def to_code(config):
else: else:
cg.add_build_flag("-Wno-error=format") cg.add_build_flag("-Wno-error=format")
cg.add_build_flag("-Wno-error=maybe-uninitialized") cg.add_build_flag("-Wno-error=maybe-uninitialized")
cg.add_build_flag("-Wno-error=missing-field-initializers") cg.add_build_flag("-Wno-error=overloaded-virtual")
cg.add_build_flag("-Wno-error=reorder") cg.add_build_flag("-Wno-error=reorder")
cg.add_build_flag("-Wno-error=volatile") cg.add_build_flag("-Wno-error=volatile")
# -Wno- (not -Wno-error=): suppress entirely, too noisy on C++ aggregates
cg.add_build_flag("-Wno-missing-field-initializers")
cg.set_cpp_standard("gnu++20") cg.set_cpp_standard("gnu++20")
cg.add_build_flag("-DUSE_ESP32") cg.add_build_flag("-DUSE_ESP32")
@@ -2464,8 +2466,14 @@ def _write_sdkconfig():
) )
want_opts = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] want_opts = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS]
# Include the resolved framework version as a Kconfig comment so a
# version switch that happens to leave the option set unchanged still
# bumps this file's content -- which is what has_outdated_files()
# uses to decide whether to reconfigure.
framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
contents = ( contents = (
"\n".join( f"# ESPHOME_IDF_VERSION={framework_version}\n"
+ "\n".join(
f"{name}={_format_sdkconfig_val(value)}" f"{name}={_format_sdkconfig_val(value)}"
for name, value in sorted(want_opts.items()) for name, value in sorted(want_opts.items())
) )
@@ -2509,7 +2517,12 @@ def _write_idf_component_yml():
stubs_dir = CORE.relative_build_path("component_stubs") stubs_dir = CORE.relative_build_path("component_stubs")
stubs_dir.mkdir(exist_ok=True) stubs_dir.mkdir(exist_ok=True)
for component_name in components_to_stub: # Sort so the dict insertion order (and thus the generated
# src/idf_component.yml) is deterministic across runs; otherwise
# the manifest content shuffles every build, write_file_if_changed
# always writes, and ninja keeps triggering CMake re-runs on
# otherwise-cached rebuilds.
for component_name in sorted(components_to_stub):
# Create stub directory with minimal CMakeLists.txt # Create stub directory with minimal CMakeLists.txt
stub_path = stubs_dir / _idf_component_stub_name(component_name) stub_path = stubs_dir / _idf_component_stub_name(component_name)
stub_path.mkdir(exist_ok=True) stub_path.mkdir(exist_ok=True)
+1 -1
View File
@@ -249,7 +249,7 @@ async def to_code(config):
esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="1.5.1") esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="1.5.1")
esp32.add_idf_component(name="espressif/wifi_remote_over_eppp", ref="0.3.2") esp32.add_idf_component(name="espressif/wifi_remote_over_eppp", ref="0.3.2")
esp32.add_idf_component(name="espressif/eppp_link", ref="1.1.5") esp32.add_idf_component(name="espressif/eppp_link", ref="1.1.5")
esp32.add_idf_component(name="espressif/esp_hosted", ref="2.12.6") esp32.add_idf_component(name="espressif/esp_hosted", ref="2.12.7")
else: else:
esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="0.13.0") esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="0.13.0")
esp32.add_idf_component(name="espressif/eppp_link", ref="0.2.0") esp32.add_idf_component(name="espressif/eppp_link", ref="0.2.0")
@@ -92,7 +92,7 @@ void Esp32HostedUpdate::setup() {
if (esp_hosted_get_coprocessor_fwversion(&ver_info) == ESP_OK) { if (esp_hosted_get_coprocessor_fwversion(&ver_info) == ESP_OK) {
// 16 bytes: "255.255.255" (11 chars) + null + safety margin // 16 bytes: "255.255.255" (11 chars) + null + safety margin
char buf[16]; char buf[16];
snprintf(buf, sizeof(buf), "%d.%d.%d", ver_info.major1, ver_info.minor1, ver_info.patch1); snprintf(buf, sizeof(buf), "%" PRIu32 ".%" PRIu32 ".%" PRIu32, ver_info.major1, ver_info.minor1, ver_info.patch1);
this->update_info_.current_version = buf; this->update_info_.current_version = buf;
} else { } else {
this->update_info_.current_version = "unknown"; this->update_info_.current_version = "unknown";
@@ -120,8 +120,8 @@ void Esp32HostedUpdate::setup() {
this->state_ = update::UPDATE_STATE_NO_UPDATE; this->state_ = update::UPDATE_STATE_NO_UPDATE;
} }
} else { } else {
ESP_LOGW(TAG, "Invalid app description magic word: 0x%08x (expected 0x%08x)", app_desc->magic_word, ESP_LOGW(TAG, "Invalid app description magic word: 0x%08" PRIx32 " (expected 0x%08" PRIx32 ")",
ESP_APP_DESC_MAGIC_WORD); app_desc->magic_word, static_cast<uint32_t>(ESP_APP_DESC_MAGIC_WORD));
this->state_ = update::UPDATE_STATE_NO_UPDATE; this->state_ = update::UPDATE_STATE_NO_UPDATE;
} }
} else { } else {
@@ -108,8 +108,8 @@ void ESPHomeOTAComponent::dump_config() {
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
" Partition access allowed\n" " Partition access allowed\n"
" Running app:\n" " Running app:\n"
" Partition address: 0x%X\n" " Partition address: 0x%" PRIX32 "\n"
" Used size: %zu bytes (0x%X)", " Used size: %zu bytes (0x%zX)",
this->running_app_offset_, this->running_app_size_, this->running_app_size_); this->running_app_offset_, this->running_app_size_, this->running_app_size_);
#ifdef USE_ESP32 #ifdef USE_ESP32
@@ -378,7 +378,7 @@ void ESPHomeOTAComponent::handle_data_() {
} }
ota_size = (static_cast<size_t>(buf[0]) << 24) | (static_cast<size_t>(buf[1]) << 16) | ota_size = (static_cast<size_t>(buf[0]) << 24) | (static_cast<size_t>(buf[1]) << 16) |
(static_cast<size_t>(buf[2]) << 8) | buf[3]; (static_cast<size_t>(buf[2]) << 8) | buf[3];
ESP_LOGV(TAG, "Size is %u bytes", ota_size); ESP_LOGV(TAG, "Size is %zu bytes", ota_size);
#ifndef USE_OTA_PARTITIONS #ifndef USE_OTA_PARTITIONS
if (ota_type != ota::OTA_TYPE_UPDATE_APP) { if (ota_type != ota::OTA_TYPE_UPDATE_APP) {
@@ -749,7 +749,7 @@ bool ESPHomeOTAComponent::handle_auth_send_() {
this->auth_buf_[0] = this->auth_type_; this->auth_buf_[0] = this->auth_type_;
hasher.get_hex(buf); hasher.get_hex(buf);
ESP_LOGV(TAG, "Auth: Nonce is %.*s", hex_size, buf); ESP_LOGV(TAG, "Auth: Nonce is %.*s", (int) hex_size, buf);
} }
// Try to write auth_type + nonce // Try to write auth_type + nonce
@@ -809,13 +809,13 @@ bool ESPHomeOTAComponent::handle_auth_read_() {
hasher.add(nonce, hex_size * 2); // Add both nonce and cnonce (contiguous in buffer) hasher.add(nonce, hex_size * 2); // Add both nonce and cnonce (contiguous in buffer)
hasher.calculate(); hasher.calculate();
ESP_LOGV(TAG, "Auth: CNonce is %.*s", hex_size, cnonce); ESP_LOGV(TAG, "Auth: CNonce is %.*s", (int) hex_size, cnonce);
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char computed_hash[SHA256_HEX_SIZE + 1]; // Buffer for hex-encoded hash (max expected length + null terminator) char computed_hash[SHA256_HEX_SIZE + 1]; // Buffer for hex-encoded hash (max expected length + null terminator)
hasher.get_hex(computed_hash); hasher.get_hex(computed_hash);
ESP_LOGV(TAG, "Auth: Result is %.*s", hex_size, computed_hash); ESP_LOGV(TAG, "Auth: Result is %.*s", (int) hex_size, computed_hash);
#endif #endif
ESP_LOGV(TAG, "Auth: Response is %.*s", hex_size, response); ESP_LOGV(TAG, "Auth: Response is %.*s", (int) hex_size, response);
// Compare response // Compare response
bool matches = hasher.equals_hex(response); bool matches = hasher.equals_hex(response);
@@ -19,7 +19,7 @@ void FastLEDLightOutput::dump_config() {
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
"FastLED light:\n" "FastLED light:\n"
" Num LEDs: %u\n" " Num LEDs: %u\n"
" Max refresh rate: %u", " Max refresh rate: %" PRIu32,
this->num_leds_, this->max_refresh_rate_.value_or(0)); this->num_leds_, this->max_refresh_rate_.value_or(0));
} }
void FastLEDLightOutput::write_state(light::LightState *state) { void FastLEDLightOutput::write_state(light::LightState *state) {
@@ -206,6 +206,7 @@ uint8_t FingerprintGrowComponent::save_fingerprint_() {
break; break;
case ENROLL_MISMATCH: case ENROLL_MISMATCH:
ESP_LOGE(TAG, "Scans do not match"); ESP_LOGE(TAG, "Scans do not match");
[[fallthrough]];
default: default:
return this->data_[0]; return this->data_[0];
} }
@@ -15,6 +15,16 @@ void FT5x06Touchscreen::setup() {
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
} }
// reading the chip registers to get max x/y does not seem to work.
if (this->display_ != nullptr) {
if (this->x_raw_max_ == this->x_raw_min_) {
this->x_raw_max_ = this->display_->get_native_width();
}
if (this->y_raw_max_ == this->y_raw_min_) {
this->y_raw_max_ = this->display_->get_native_height();
}
}
// wait 200ms after reset. // wait 200ms after reset.
this->set_timeout(200, [this] { this->continue_setup_(); }); this->set_timeout(200, [this] { this->continue_setup_(); });
} }
@@ -39,15 +49,6 @@ void FT5x06Touchscreen::continue_setup_() {
this->mark_failed(); this->mark_failed();
return; return;
} }
// reading the chip registers to get max x/y does not seem to work.
if (this->display_ != nullptr) {
if (this->x_raw_max_ == this->x_raw_min_) {
this->x_raw_max_ = this->display_->get_native_width();
}
if (this->y_raw_max_ == this->y_raw_min_) {
this->y_raw_max_ = this->display_->get_native_height();
}
}
} }
void FT5x06Touchscreen::update_touches() { void FT5x06Touchscreen::update_touches() {
@@ -71,7 +72,7 @@ void FT5x06Touchscreen::update_touches() {
uint16_t x = encode_uint16(data[i][0] & 0x0F, data[i][1]); uint16_t x = encode_uint16(data[i][0] & 0x0F, data[i][1]);
uint16_t y = encode_uint16(data[i][2] & 0xF, data[i][3]); uint16_t y = encode_uint16(data[i][2] & 0xF, data[i][3]);
ESP_LOGD(TAG, "Read %X status, id: %d, pos %d/%d", status, id, x, y); ESP_LOGV(TAG, "Read %X status, id: %d, pos %d/%d", status, id, x, y);
if (status == 0 || status == 2) { if (status == 0 || status == 2) {
this->add_raw_touch_position_(id, x, y); this->add_raw_touch_position_(id, x, y);
} }
+1 -1
View File
@@ -22,7 +22,7 @@ static constexpr uint8_t MEAS_CONF_HUM = 0x04; // Bits 2:1 = 10: humidity only
void HDC2080Component::setup() { void HDC2080Component::setup() {
const uint8_t data = 0x00; // automatic measurement mode disabled, heater off const uint8_t data = 0x00; // automatic measurement mode disabled, heater off
if (this->write_register(REG_RESET_DRDY_INT_CONF, &data, 1) != i2c::ERROR_OK) { if (this->write_register(REG_RESET_DRDY_INT_CONF, &data, 1) != i2c::ERROR_OK) {
this->mark_failed(ESP_LOG_MSG_COMM_FAIL); this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
return; return;
} }
} }
-1
View File
@@ -125,7 +125,6 @@ async def to_code(config):
cg.add(var.set_vertical_default(config[CONF_VERTICAL_DEFAULT])) cg.add(var.set_vertical_default(config[CONF_VERTICAL_DEFAULT]))
cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE])) cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE]))
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE])) cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
cg.add_build_flag("-Wno-error=overloaded-virtual")
cg.add_library("tonia/HeatpumpIR", "1.0.41") cg.add_library("tonia/HeatpumpIR", "1.0.41")
if CORE.is_libretiny or CORE.is_esp32: if CORE.is_libretiny or CORE.is_esp32:
+1 -1
View File
@@ -319,7 +319,7 @@ void Inkplate::fill(Color color) {
memset(this->partial_buffer_, fill, this->get_buffer_length_()); memset(this->partial_buffer_, fill, this->get_buffer_length_());
} }
ESP_LOGV(TAG, "Fill finished (%ums)", millis() - start_time); ESP_LOGV(TAG, "Fill finished (%" PRIu32 "ms)", millis() - start_time);
} }
void Inkplate::display() { void Inkplate::display() {
+7 -7
View File
@@ -506,13 +506,13 @@ async def _late_logger_init(config: ConfigType) -> None:
def validate_printf(value): def validate_printf(value):
# https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python # https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
cfmt = r""" cfmt = r"""
( # start of capture group 1 ( # start of capture group 1
% # literal "%" % # literal "%"
(?:[-+0 #]{0,5}) # optional flags (?:[-+0 #]{0,5}) # optional flags
(?:\d+|\*)? # width (?:\d+|\*)? # width
(?:\.(?:\d+|\*))? # precision (?:\.(?:\d+|\*))? # precision
(?:h|l|ll|w|I|I32|I64)? # size (?:hh|h|ll|l|j|z|t|L|w|I|I32|I64)? # size
[cCdiouxXeEfgGaAnpsSZ] # type [cCdiouxXeEfgGaAnpsSZ] # type
) )
""" # noqa """ # noqa
matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.VERBOSE) matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.VERBOSE)
+5
View File
@@ -55,6 +55,7 @@ from .automation import layers_to_code, lvgl_update
from .defines import ( from .defines import (
CONF_ALIGN_TO_LAMBDA_ID, CONF_ALIGN_TO_LAMBDA_ID,
LOGGER, LOGGER,
add_lv_use,
get_focused_widgets, get_focused_widgets,
get_lv_images_used, get_lv_images_used,
get_refreshed_widgets, get_refreshed_widgets,
@@ -71,6 +72,7 @@ from .keypads import KEYPADS_CONFIG, keypads_to_code
from .lv_validation import lv_bool from .lv_validation import lv_bool
from .lvcode import LvContext, LvglComponent, lv_event_t_ptr, lvgl_static from .lvcode import LvContext, LvglComponent, lv_event_t_ptr, lvgl_static
from .schemas import ( from .schemas import (
BASE_PROPS,
DISP_BG_SCHEMA, DISP_BG_SCHEMA,
FULL_STYLE_SCHEMA, FULL_STYLE_SCHEMA,
STYLE_REMAP, STYLE_REMAP,
@@ -100,6 +102,7 @@ from .widgets import (
get_screen_active, get_screen_active,
set_obj_properties, set_obj_properties,
) )
from .widgets.img import CONF_IMAGE
# Import only what we actually use directly in this file # Import only what we actually use directly in this file
from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code
@@ -433,6 +436,8 @@ async def to_code(configs):
# This must be done after all widgets are created # This must be done after all widgets are created
styles_used = df.get_styles_used() styles_used = df.get_styles_used()
if any(BASE_PROPS.get(x) is lvalid.lv_image for x in styles_used):
add_lv_use(CONF_IMAGE)
for use in df.get_lv_uses(): for use in df.get_lv_uses():
df.add_define(f"LV_USE_{use.upper()}") df.add_define(f"LV_USE_{use.upper()}")
cg.add_define(f"USE_LVGL_{use.upper()}") cg.add_define(f"USE_LVGL_{use.upper()}")
+14 -14
View File
@@ -9,13 +9,13 @@ CONF_IF_NAN = "if_nan"
# noqa # noqa
f_regex = re.compile( f_regex = re.compile(
r""" r"""
( # start of capture group 1 ( # start of capture group 1
% # literal "%" % # literal "%"
[-+0 #]{0,5} # optional flags [-+0 #]{0,5} # optional flags
(?:\d+|\*)? # width (?:\d+|\*)? # width
(?:\.(?:\d+|\*))? # precision (?:\.(?:\d+|\*))? # precision
(?:h|l|ll|w|I|I32|I64)? # size (?:hh|h|ll|l|j|z|t|L|w|I|I32|I64)? # size
f # type f # type
) )
""", """,
flags=re.VERBOSE, flags=re.VERBOSE,
@@ -23,13 +23,13 @@ f_regex = re.compile(
# noqa # noqa
c_regex = re.compile( c_regex = re.compile(
r""" r"""
( # start of capture group 1 ( # start of capture group 1
% # literal "%" % # literal "%"
[-+0 #]{0,5} # optional flags [-+0 #]{0,5} # optional flags
(?:\d+|\*)? # width (?:\d+|\*)? # width
(?:\.(?:\d+|\*))? # precision (?:\.(?:\d+|\*))? # precision
(?:h|l|ll|w|I|I32|I64)? # size (?:hh|h|ll|l|j|z|t|L|w|I|I32|I64)? # size
[cCdiouxXeEfgGaAnpsSZ] # type [cCdiouxXeEfgGaAnpsSZ] # type
) )
""", """,
flags=re.VERBOSE, flags=re.VERBOSE,
+6 -4
View File
@@ -74,11 +74,11 @@ inline void lv_style_set_text_font(lv_style_t *style, const font::Font *font) {
lv_style_set_text_font(style, font->get_lv_font()); lv_style_set_text_font(style, font->get_lv_font());
} }
#endif #endif
#if defined(USE_LVGL_IMAGE) && defined(USE_IMAGE)
#if LV_USE_IMAGE #ifdef USE_IMAGE
#ifdef USE_LVGL_IMAGE
// Shortcut / overload, so that the source of an image widget can easily be updated from within a lambda. // Shortcut / overload, so that the source of an image widget can easily be updated from within a lambda.
inline void lv_image_set_src(lv_obj_t *obj, image::Image *image) { ::lv_image_set_src(obj, image->get_lv_image_dsc()); } inline void lv_image_set_src(lv_obj_t *obj, image::Image *image) { ::lv_image_set_src(obj, image->get_lv_image_dsc()); }
#endif // LV_USE_IMAGE
inline void lv_obj_set_style_bitmap_mask_src(lv_obj_t *obj, image::Image *image, lv_style_selector_t selector) { inline void lv_obj_set_style_bitmap_mask_src(lv_obj_t *obj, image::Image *image, lv_style_selector_t selector) {
::lv_obj_set_style_bitmap_mask_src(obj, image->get_lv_image_dsc(), selector); ::lv_obj_set_style_bitmap_mask_src(obj, image->get_lv_image_dsc(), selector);
@@ -93,7 +93,8 @@ inline void lv_style_set_bg_image_src(lv_style_t *style, image::Image *image) {
inline void lv_style_set_bitmap_mask_src(lv_style_t *style, image::Image *image) { inline void lv_style_set_bitmap_mask_src(lv_style_t *style, image::Image *image) {
::lv_style_set_bitmap_mask_src(style, image->get_lv_image_dsc()); ::lv_style_set_bitmap_mask_src(style, image->get_lv_image_dsc());
} }
#endif // USE_LVGL_IMAGE #endif
#ifdef USE_LVGL_ANIMIMG #ifdef USE_LVGL_ANIMIMG
inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) { inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images) {
auto *dsc = static_cast<std::vector<lv_image_dsc_t *> *>(lv_obj_get_user_data(img)); auto *dsc = static_cast<std::vector<lv_image_dsc_t *> *>(lv_obj_get_user_data(img));
@@ -109,6 +110,7 @@ inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images
lv_animimg_set_src(img, (const void **) dsc->data(), dsc->size()); lv_animimg_set_src(img, (const void **) dsc->data(), dsc->size());
} }
#endif // USE_LVGL_ANIMIMG #endif // USE_LVGL_ANIMIMG
#endif // USE_IMAGE
#ifdef USE_LVGL_METER #ifdef USE_LVGL_METER
int16_t lv_get_needle_angle_for_value(lv_obj_t *obj, int32_t value); int16_t lv_get_needle_angle_for_value(lv_obj_t *obj, int32_t value);
+2
View File
@@ -9,6 +9,7 @@ from .defines import (
CONF_THEME, CONF_THEME,
LValidator, LValidator,
add_lv_use, add_lv_use,
get_styles_used,
get_theme_widget_map, get_theme_widget_map,
literal, literal,
) )
@@ -25,6 +26,7 @@ def has_style_props(config) -> bool:
async def style_set(svar, style): async def style_set(svar, style):
for prop, validator in ALL_STYLES.items(): for prop, validator in ALL_STYLES.items():
if (value := style.get(prop)) is not None: if (value := style.get(prop)) is not None:
get_styles_used().add(prop)
if isinstance(validator, LValidator): if isinstance(validator, LValidator):
value = await validator.process(value) value = await validator.process(value)
if isinstance(value, list): if isinstance(value, list):
+2 -2
View File
@@ -130,8 +130,8 @@ ClimateTraits AirConditioner::traits() {
void AirConditioner::dump_config() { void AirConditioner::dump_config() {
ESP_LOGCONFIG(Constants::TAG, ESP_LOGCONFIG(Constants::TAG,
"MideaDongle:\n" "MideaDongle:\n"
" [x] Period: %dms\n" " [x] Period: %" PRIu32 "ms\n"
" [x] Response timeout: %dms\n" " [x] Response timeout: %" PRIu32 "ms\n"
" [x] Request attempts: %d", " [x] Request attempts: %d",
this->base_.getPeriod(), this->base_.getTimeout(), this->base_.getNumAttempts()); this->base_.getPeriod(), this->base_.getTimeout(), this->base_.getNumAttempts());
#ifdef USE_REMOTE_TRANSMITTER #ifdef USE_REMOTE_TRANSMITTER
@@ -210,7 +210,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
ESP_LOGE(TAG, "Cannot resolve running app partition at address 0x%" PRIX32, running_app_offset); ESP_LOGE(TAG, "Cannot resolve running app partition at address 0x%" PRIX32, running_app_offset);
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE; return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
} }
ESP_LOGD(TAG, "Copying running app from 0x%X to 0x%X (size: 0x%X)", running_app_part->address, ESP_LOGD(TAG, "Copying running app from 0x%" PRIX32 " to 0x%" PRIX32 " (size: 0x%zX)", running_app_part->address,
plan.copy_dest_part->address, running_app_size); plan.copy_dest_part->address, running_app_size);
err = esp_partition_copy(plan.copy_dest_part, 0, running_app_part, 0, running_app_size); err = esp_partition_copy(plan.copy_dest_part, 0, running_app_part, 0, running_app_size);
if (err != ESP_OK) { if (err != ESP_OK) {
@@ -261,7 +261,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
ESP_LOGE(TAG, "Selected app partition not found after partition table update"); ESP_LOGE(TAG, "Selected app partition not found after partition table update");
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE; return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
} }
ESP_LOGD(TAG, "Setting next boot partition to 0x%X", new_boot_partition->address); ESP_LOGD(TAG, "Setting next boot partition to 0x%" PRIX32, new_boot_partition->address);
err = esp_ota_set_boot_partition(new_boot_partition); err = esp_ota_set_boot_partition(new_boot_partition);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (err=0x%X)", err); ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (err=0x%X)", err);
@@ -150,7 +150,7 @@ void IRAM_ATTR PulseMeterSensor::edge_intr(PulseMeterSensor *sensor) {
edge_state.last_sent_edge_us_ = now; edge_state.last_sent_edge_us_ = now;
state.last_detected_edge_us_ = now; state.last_detected_edge_us_ = now;
state.last_rising_edge_us_ = now; state.last_rising_edge_us_ = now;
state.count_++; // NOLINT(clang-diagnostic-deprecated-volatile) state.count_ += 1;
} }
// This ISR is bound to rising edges, so the pin is high // This ISR is bound to rising edges, so the pin is high
@@ -173,7 +173,7 @@ void IRAM_ATTR PulseMeterSensor::pulse_intr(PulseMeterSensor *sensor) {
} else if (length && !pulse_state.latched_ && sensor->last_pin_val_) { // Long enough high edge } else if (length && !pulse_state.latched_ && sensor->last_pin_val_) { // Long enough high edge
pulse_state.latched_ = true; pulse_state.latched_ = true;
state.last_detected_edge_us_ = pulse_state.last_intr_; state.last_detected_edge_us_ = pulse_state.last_intr_;
state.count_++; // NOLINT(clang-diagnostic-deprecated-volatile) state.count_ += 1;
} }
// Due to order of operations this includes // Due to order of operations this includes
@@ -78,10 +78,10 @@ void RemoteReceiverComponent::setup() {
void RemoteReceiverComponent::dump_config() { void RemoteReceiverComponent::dump_config() {
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
"Remote Receiver:\n" "Remote Receiver:\n"
" Buffer Size: %u\n" " Buffer Size: %" PRIu32 "\n"
" Tolerance: %u%s\n" " Tolerance: %" PRIu32 "%s\n"
" Filter out pulses shorter than: %u us\n" " Filter out pulses shorter than: %" PRIu32 " us\n"
" Signal is done after %u us of no changes", " Signal is done after %" PRIu32 " us of no changes",
this->buffer_size_, this->tolerance_, this->buffer_size_, this->tolerance_,
(this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_, (this->tolerance_mode_ == remote_base::TOLERANCE_MODE_TIME) ? " us" : "%", this->filter_us_,
this->idle_us_); this->idle_us_);
+2 -2
View File
@@ -153,7 +153,7 @@ bool SendspinHub::save_last_server_hash(uint32_t hash) {
LastPlayedServerPref pref{.server_id_hash = hash}; LastPlayedServerPref pref{.server_id_hash = hash};
bool ok = this->last_played_server_pref_.save(&pref); bool ok = this->last_played_server_pref_.save(&pref);
if (ok) { if (ok) {
ESP_LOGD(TAG, "Persisted last played server hash: 0x%08X", hash); ESP_LOGD(TAG, "Persisted last played server hash: 0x%08" PRIX32, hash);
} else { } else {
ESP_LOGW(TAG, "Failed to persist last played server hash"); ESP_LOGW(TAG, "Failed to persist last played server hash");
} }
@@ -164,7 +164,7 @@ bool SendspinHub::save_last_server_hash(uint32_t hash) {
std::optional<uint32_t> SendspinHub::load_last_server_hash() { std::optional<uint32_t> SendspinHub::load_last_server_hash() {
LastPlayedServerPref pref{}; LastPlayedServerPref pref{};
if (this->last_played_server_pref_.load(&pref)) { if (this->last_played_server_pref_.load(&pref)) {
ESP_LOGI(TAG, "Loaded last played server hash: 0x%08X", pref.server_id_hash); ESP_LOGI(TAG, "Loaded last played server hash: 0x%08" PRIX32, pref.server_id_hash);
return pref.server_id_hash; return pref.server_id_hash;
} }
return std::nullopt; return std::nullopt;
+1 -1
View File
@@ -126,7 +126,7 @@ void Sim800LComponent::parse_cmd_(std::string message) {
break; break;
} }
// Else fall thru ... [[fallthrough]];
} }
case STATE_CHECK_SMS: case STATE_CHECK_SMS:
send_cmd_("AT+CMGL=\"ALL\""); send_cmd_("AT+CMGL=\"ALL\"");
+30 -28
View File
@@ -11,7 +11,7 @@ namespace esphome::sound_level {
static const char *const TAG = "sound_level"; static const char *const TAG = "sound_level";
static const uint32_t AUDIO_BUFFER_DURATION_MS = 30; static const uint32_t MAX_FILL_DURATION_MS = 30;
static const uint32_t RING_BUFFER_DURATION_MS = 120; static const uint32_t RING_BUFFER_DURATION_MS = 120;
// Square INT16_MIN since INT16_MIN^2 > INT16_MAX^2 // Square INT16_MIN since INT16_MIN^2 > INT16_MAX^2
@@ -30,8 +30,7 @@ void SoundLevelComponent::dump_config() {
void SoundLevelComponent::setup() { void SoundLevelComponent::setup() {
this->microphone_source_->add_data_callback([this](const std::vector<uint8_t> &data) { this->microphone_source_->add_data_callback([this](const std::vector<uint8_t> &data) {
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_.lock(); std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
if (this->ring_buffer_.use_count() == 2) { if (temp_ring_buffer != nullptr) {
// ``audio_buffer_`` and ``temp_ring_buffer`` share ownership of a ring buffer, so its safe/useful to write
temp_ring_buffer->write((void *) data.data(), data.size()); temp_ring_buffer->write((void *) data.data(), data.size());
} }
}); });
@@ -81,10 +80,11 @@ void SoundLevelComponent::loop() {
return; return;
} }
// Copy data from ring buffer into the transfer buffer - don't block to avoid slowing the main loop // Expose a chunk of the ring buffer's internal storage - don't block to avoid slowing the main loop.
this->audio_buffer_->transfer_data_from_source(0); // pre_shift is ignored by RingBufferAudioSource (no intermediate transfer buffer to compact).
this->audio_source_->fill(0, false);
if (this->audio_buffer_->available() == 0) { if (this->audio_source_->available() == 0) {
// No new audio available for processing // No new audio available for processing
return; return;
} }
@@ -92,11 +92,11 @@ void SoundLevelComponent::loop() {
const uint32_t samples_in_window = const uint32_t samples_in_window =
this->microphone_source_->get_audio_stream_info().ms_to_samples(this->measurement_duration_ms_); this->microphone_source_->get_audio_stream_info().ms_to_samples(this->measurement_duration_ms_);
const uint32_t samples_available_to_process = const uint32_t samples_available_to_process =
this->microphone_source_->get_audio_stream_info().bytes_to_samples(this->audio_buffer_->available()); this->microphone_source_->get_audio_stream_info().bytes_to_samples(this->audio_source_->available());
const uint32_t samples_to_process = std::min(samples_in_window - this->sample_count_, samples_available_to_process); const uint32_t samples_to_process = std::min(samples_in_window - this->sample_count_, samples_available_to_process);
// MicrophoneSource always provides int16 samples due to Python codegen settings // MicrophoneSource always provides int16 samples due to Python codegen settings
const int16_t *audio_data = reinterpret_cast<const int16_t *>(this->audio_buffer_->get_buffer_start()); const int16_t *audio_data = reinterpret_cast<const int16_t *>(this->audio_source_->data());
// Process all the new audio samples // Process all the new audio samples
for (uint32_t i = 0; i < samples_to_process; ++i) { for (uint32_t i = 0; i < samples_to_process; ++i) {
@@ -115,9 +115,8 @@ void SoundLevelComponent::loop() {
++this->sample_count_; ++this->sample_count_;
} }
// Remove the processed samples from ``audio_buffer_`` // Remove the processed samples from ``audio_source_``
this->audio_buffer_->decrease_buffer_length( this->audio_source_->consume(this->microphone_source_->get_audio_stream_info().samples_to_bytes(samples_to_process));
this->microphone_source_->get_audio_stream_info().samples_to_bytes(samples_to_process));
if (this->sample_count_ == samples_in_window) { if (this->sample_count_ == samples_in_window) {
// Processed enough samples for the measurement window, compute and publish the sensor values // Processed enough samples for the measurement window, compute and publish the sensor values
@@ -158,36 +157,39 @@ void SoundLevelComponent::stop() {
} }
bool SoundLevelComponent::start_() { bool SoundLevelComponent::start_() {
if (this->audio_buffer_ != nullptr) { if (this->audio_source_ != nullptr) {
return true; return true;
} }
// Allocate a transfer buffer const auto &stream_info = this->microphone_source_->get_audio_stream_info();
this->audio_buffer_ = audio::AudioSourceTransferBuffer::create( const size_t bytes_per_frame = stream_info.frames_to_bytes(1);
this->microphone_source_->get_audio_stream_info().ms_to_bytes(AUDIO_BUFFER_DURATION_MS));
if (this->audio_buffer_ == nullptr) { // Allocate a ring buffer for the microphone callback to write into. Round the size down to a multiple
this->status_momentary_error("transfer_buffer", 15000); // of bytes_per_frame so the wrap boundary stays frame-aligned and avoids unnecessary single-frame splices.
this->ring_buffer_.reset(); // Reset pointer to any previous ring buffer allocation
const size_t ring_buffer_size =
(stream_info.ms_to_bytes(RING_BUFFER_DURATION_MS) / bytes_per_frame) * bytes_per_frame;
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = ring_buffer::RingBuffer::create(ring_buffer_size);
if (temp_ring_buffer == nullptr) {
this->status_momentary_error("ring_buffer", 15000);
return false; return false;
} }
// Allocates a new ring buffer, adds it as a source for the transfer buffer, and points ring_buffer_ to it // Zero-copy source that reads directly from the ring buffer's internal storage. Frame-aligned reads
this->ring_buffer_.reset(); // Reset pointer to any previous ring buffer allocation // ensure multi-channel frames are never split across the ring buffer's wrap boundary.
std::shared_ptr<ring_buffer::RingBuffer> temp_ring_buffer = ring_buffer::RingBuffer::create( this->audio_source_ = audio::RingBufferAudioSource::create(
this->microphone_source_->get_audio_stream_info().ms_to_bytes(RING_BUFFER_DURATION_MS)); temp_ring_buffer, stream_info.ms_to_bytes(MAX_FILL_DURATION_MS), static_cast<uint8_t>(bytes_per_frame));
if (temp_ring_buffer.use_count() == 0) { if (this->audio_source_ == nullptr) {
this->status_momentary_error("ring_buffer", 15000); this->status_momentary_error("audio_source", 15000);
this->stop_();
return false; return false;
} else {
this->ring_buffer_ = temp_ring_buffer;
this->audio_buffer_->set_source(temp_ring_buffer);
} }
this->ring_buffer_ = temp_ring_buffer;
this->status_clear_error(); this->status_clear_error();
return true; return true;
} }
void SoundLevelComponent::stop_() { this->audio_buffer_.reset(); } void SoundLevelComponent::stop_() { this->audio_source_.reset(); }
} // namespace esphome::sound_level } // namespace esphome::sound_level
+5 -4
View File
@@ -36,11 +36,12 @@ class SoundLevelComponent : public Component {
void stop(); void stop();
protected: protected:
/// @brief Internal start command that, if necessary, allocates ``audio_buffer_`` and a ring buffer which /// @brief Internal start command that, if necessary, allocates a ring buffer and a zero-copy
/// ``audio_buffer_`` owns and ``ring_buffer_`` points to. Returns true if allocations were successful. /// ``RingBufferAudioSource`` that reads directly from it. ``ring_buffer_`` weakly references the
/// ring buffer owned by ``audio_source_``. Returns true if allocations were successful.
bool start_(); bool start_();
/// @brief Internal stop command the deallocates ``audio_buffer_`` (which automatically deallocates its ring buffer) /// @brief Internal stop command that deallocates ``audio_source_`` (which releases its ring buffer)
void stop_(); void stop_();
microphone::MicrophoneSource *microphone_source_{nullptr}; microphone::MicrophoneSource *microphone_source_{nullptr};
@@ -48,7 +49,7 @@ class SoundLevelComponent : public Component {
sensor::Sensor *peak_sensor_{nullptr}; sensor::Sensor *peak_sensor_{nullptr};
sensor::Sensor *rms_sensor_{nullptr}; sensor::Sensor *rms_sensor_{nullptr};
std::unique_ptr<audio::AudioSourceTransferBuffer> audio_buffer_; std::unique_ptr<audio::RingBufferAudioSource> audio_source_;
std::weak_ptr<ring_buffer::RingBuffer> ring_buffer_; std::weak_ptr<ring_buffer::RingBuffer> ring_buffer_;
int32_t squared_peak_{0}; int32_t squared_peak_{0};
+32 -1
View File
@@ -1,3 +1,4 @@
from esphome import final_validate as fv
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import esp32 from esphome.components import esp32
from esphome.components.esp32 import ( from esphome.components.esp32 import (
@@ -8,7 +9,7 @@ from esphome.components.esp32 import (
add_idf_sdkconfig_option, add_idf_sdkconfig_option,
) )
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_HARDWARE_UART, CONF_ID
CODEOWNERS = ["@kbx81"] CODEOWNERS = ["@kbx81"]
CONFLICTS_WITH = ["usb_host"] CONFLICTS_WITH = ["usb_host"]
@@ -20,6 +21,13 @@ CONF_USB_PRODUCT_STR = "usb_product_str"
CONF_USB_SERIAL_STR = "usb_serial_str" CONF_USB_SERIAL_STR = "usb_serial_str"
CONF_USB_VENDOR_ID = "usb_vendor_id" CONF_USB_VENDOR_ID = "usb_vendor_id"
# Components that provide a USB device class (CDC, HID, MSC, ...) on top of
# tinyusb. Configuring `tinyusb:` without any of these triggers a 5s hang in
# esp_tinyusb's driver install (descriptors_set fails with no class and no
# user-provided full_speed_config), which trips the task watchdog before
# loop() ever runs.
_USB_CLASS_COMPONENTS = ("usb_cdc_acm",)
tinyusb_ns = cg.esphome_ns.namespace("tinyusb") tinyusb_ns = cg.esphome_ns.namespace("tinyusb")
TinyUSB = tinyusb_ns.class_("TinyUSB", cg.Component) TinyUSB = tinyusb_ns.class_("TinyUSB", cg.Component)
@@ -41,6 +49,29 @@ CONFIG_SCHEMA = cv.All(
) )
def _final_validate(config):
full_config = fv.full_config.get()
if not any(name in full_config for name in _USB_CLASS_COMPONENTS):
raise cv.Invalid(
"The 'tinyusb' component requires at least one USB class component"
)
# tinyusb owns the USB OTG peripheral. The logger's USB_CDC backend routes
# the ROM console through that same peripheral, so the two cannot coexist.
# (USB_SERIAL_JTAG is a separate peripheral and is fine alongside tinyusb.)
logger_config = full_config.get("logger")
if logger_config and logger_config.get(CONF_HARDWARE_UART) == "USB_CDC":
raise cv.Invalid(
"'tinyusb' cannot be used with 'logger.hardware_uart: USB_CDC' "
"because both share the USB OTG peripheral. Set "
"'logger.hardware_uart' to a hardware UART (e.g. UART0), or to "
"USB_SERIAL_JTAG on variants that support it (ESP32-S3, ESP32-P4)"
)
return config
FINAL_VALIDATE_SCHEMA = _final_validate
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config) await cg.register_component(var, config)
@@ -26,6 +26,21 @@ void TinyUSB::setup() {
.string_count = SIZE, .string_count = SIZE,
}; };
// Defense-in-depth: esp_tinyusb's tinyusb_descriptors_set() fails with
// ESP_ERR_INVALID_ARG when no configuration descriptor is provided and
// no class that has a built-in default (CDC/MSC/NCM) is compiled in. In
// that case the internal task exits without notifying us, and
// tinyusb_driver_install() blocks 5s on the notify-take -- long enough
// to trip the task watchdog. Bail early so the rest of the device can
// still boot.
#if !(CFG_TUD_CDC > 0 || CFG_TUD_MSC > 0 || CFG_TUD_NCM > 0)
if (this->tusb_cfg_.descriptor.full_speed_config == nullptr) {
ESP_LOGE(TAG, "No USB class configured");
this->mark_failed();
return;
}
#endif
esp_err_t result = tinyusb_driver_install(&this->tusb_cfg_); esp_err_t result = tinyusb_driver_install(&this->tusb_cfg_);
if (result != ESP_OK) { if (result != ESP_OK) {
ESP_LOGE(TAG, "tinyusb_driver_install failed: %s", esp_err_to_name(result)); ESP_LOGE(TAG, "tinyusb_driver_install failed: %s", esp_err_to_name(result));
@@ -72,7 +72,7 @@ void TotalDailyEnergy::schedule_midnight_reset_() {
timeout_seconds = seconds_until_midnight + 1; timeout_seconds = seconds_until_midnight + 1;
} }
ESP_LOGD(TAG, "Scheduling midnight check in %us", timeout_seconds); ESP_LOGD(TAG, "Scheduling midnight check in %" PRIu32 "s", timeout_seconds);
this->set_timeout(TIMEOUT_ID_MIDNIGHT, timeout_seconds * MILLIS_PER_SECOND, this->set_timeout(TIMEOUT_ID_MIDNIGHT, timeout_seconds * MILLIS_PER_SECOND,
[this]() { this->schedule_midnight_reset_(); }); [this]() { this->schedule_midnight_reset_(); });
} }
+2
View File
@@ -684,8 +684,10 @@ void Tuya::set_numeric_datapoint_value_(uint8_t datapoint_id, TuyaDatapointType
case 4: case 4:
data.push_back(value >> 24); data.push_back(value >> 24);
data.push_back(value >> 16); data.push_back(value >> 16);
[[fallthrough]];
case 2: case 2:
data.push_back(value >> 8); data.push_back(value >> 8);
[[fallthrough]];
case 1: case 1:
data.push_back(value >> 0); data.push_back(value >> 0);
break; break;
+3 -3
View File
@@ -135,7 +135,7 @@ void Tx20Component::decode_and_publish_() {
} }
if (tx20_se == tx20_sb) { if (tx20_se == tx20_sb) {
tx20_wind_direction = tx20_se; tx20_wind_direction = tx20_se;
if (tx20_wind_direction >= 0 && tx20_wind_direction < 16) { if (tx20_wind_direction < 16) {
wind_cardinal_direction_ = DIRECTIONS[tx20_wind_direction]; wind_cardinal_direction_ = DIRECTIONS[tx20_wind_direction];
} }
ESP_LOGV(TAG, "WindDirection %d", tx20_wind_direction); ESP_LOGV(TAG, "WindDirection %d", tx20_wind_direction);
@@ -164,7 +164,7 @@ void IRAM_ATTR Tx20ComponentStore::gpio_intr(Tx20ComponentStore *arg) {
} }
arg->buffer[arg->buffer_index] = 1; arg->buffer[arg->buffer_index] = 1;
arg->start_time = now; arg->start_time = now;
arg->buffer_index++; // NOLINT(clang-diagnostic-deprecated-volatile) arg->buffer_index += 1;
return; return;
} }
const uint32_t delay = now - arg->start_time; const uint32_t delay = now - arg->start_time;
@@ -195,7 +195,7 @@ void IRAM_ATTR Tx20ComponentStore::gpio_intr(Tx20ComponentStore *arg) {
} }
arg->spent_time += delay; arg->spent_time += delay;
arg->start_time = now; arg->start_time = now;
arg->buffer_index++; // NOLINT(clang-diagnostic-deprecated-volatile) arg->buffer_index += 1;
} }
void IRAM_ATTR Tx20ComponentStore::reset() { void IRAM_ATTR Tx20ComponentStore::reset() {
tx20_available = false; tx20_available = false;
+1 -1
View File
@@ -135,7 +135,7 @@ class USBUartChannel : public uart::UARTComponent, public Parented<USBUartCompon
// Computed as ceil(buffer_size / 64) + 1 in Python codegen; defaults to 5 (256 / 64 + 1). // Computed as ceil(buffer_size / 64) + 1 in Python codegen; defaults to 5 (256 / 64 + 1).
static constexpr uint8_t USB_OUTPUT_CHUNK_COUNT = USB_UART_OUTPUT_CHUNK_COUNT; static constexpr uint8_t USB_OUTPUT_CHUNK_COUNT = USB_UART_OUTPUT_CHUNK_COUNT;
USBUartChannel(uint8_t index, uint16_t buffer_size) : index_(index), input_buffer_(RingBuffer(buffer_size)) {} USBUartChannel(uint8_t index, uint16_t buffer_size) : input_buffer_(RingBuffer(buffer_size)), index_(index) {}
void write_array(const uint8_t *data, size_t len) override; void write_array(const uint8_t *data, size_t len) override;
bool peek_byte(uint8_t *data) override; bool peek_byte(uint8_t *data) override;
bool read_array(uint8_t *data, size_t len) override; bool read_array(uint8_t *data, size_t len) override;
+1 -1
View File
@@ -611,7 +611,7 @@ static void set_json_icon_state_value(JsonObject &root, EntityBase *obj, const c
} }
// Helper to get request detail parameter // Helper to get request detail parameter
static JsonDetail get_request_detail(AsyncWebServerRequest *request) { [[maybe_unused]] static JsonDetail get_request_detail(AsyncWebServerRequest *request) {
return request->arg(ESPHOME_F("detail")) == "all" ? DETAIL_ALL : DETAIL_STATE; return request->arg(ESPHOME_F("detail")) == "all" ? DETAIL_ALL : DETAIL_STATE;
} }
@@ -66,7 +66,7 @@ namespace {
* - HTTPD_SOCK_ERR_TIMEOUT if the send buffer is full (EAGAIN/EWOULDBLOCK). * - HTTPD_SOCK_ERR_TIMEOUT if the send buffer is full (EAGAIN/EWOULDBLOCK).
* - HTTPD_SOCK_ERR_FAIL for other errors. * - HTTPD_SOCK_ERR_FAIL for other errors.
*/ */
int nonblocking_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags) { [[maybe_unused]] int nonblocking_send(httpd_handle_t hd, int sockfd, const char *buf, size_t buf_len, int flags) {
if (buf == nullptr) { if (buf == nullptr) {
return HTTPD_SOCK_ERR_INVALID; return HTTPD_SOCK_ERR_INVALID;
} }
+2 -2
View File
@@ -11,7 +11,7 @@ static const char *const KEYS = "0123456789*#";
void IRAM_ATTR HOT WiegandStore::d0_gpio_intr(WiegandStore *arg) { void IRAM_ATTR HOT WiegandStore::d0_gpio_intr(WiegandStore *arg) {
if (arg->d0.digital_read()) if (arg->d0.digital_read())
return; return;
arg->count++; // NOLINT(clang-diagnostic-deprecated-volatile) arg->count += 1;
arg->value <<= 1; arg->value <<= 1;
arg->last_bit_time = millis(); arg->last_bit_time = millis();
arg->done = false; arg->done = false;
@@ -20,7 +20,7 @@ void IRAM_ATTR HOT WiegandStore::d0_gpio_intr(WiegandStore *arg) {
void IRAM_ATTR HOT WiegandStore::d1_gpio_intr(WiegandStore *arg) { void IRAM_ATTR HOT WiegandStore::d1_gpio_intr(WiegandStore *arg) {
if (arg->d1.digital_read()) if (arg->d1.digital_read())
return; return;
arg->count++; // NOLINT(clang-diagnostic-deprecated-volatile) arg->count += 1;
arg->value = (arg->value << 1) | 1; arg->value = (arg->value << 1) | 1;
arg->last_bit_time = millis(); arg->last_bit_time = millis();
arg->done = false; arg->done = false;
+51 -1
View File
@@ -54,10 +54,18 @@ from esphome.const import (
CONF_TTLS_PHASE_2, CONF_TTLS_PHASE_2,
CONF_USE_ADDRESS, CONF_USE_ADDRESS,
CONF_USERNAME, CONF_USERNAME,
CONF_WIFI,
PLACEHOLDER_WIFI_SSID,
Platform, Platform,
PlatformFramework, PlatformFramework,
) )
from esphome.core import CORE, CoroPriority, HexInt, coroutine_with_priority from esphome.core import (
CORE,
CoroPriority,
EsphomeError,
HexInt,
coroutine_with_priority,
)
import esphome.final_validate as fv import esphome.final_validate as fv
from esphome.types import ConfigType from esphome.types import ConfigType
@@ -903,3 +911,45 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
"wifi_component_pico_w.cpp": {PlatformFramework.RP2040_ARDUINO}, "wifi_component_pico_w.cpp": {PlatformFramework.RP2040_ARDUINO},
} }
) )
def _placeholder_wifi_credentials(config: ConfigType) -> list[str]:
"""Return human-readable locations where the dashboard's placeholder wifi
values still appear. Empty list means no placeholders were found.
"""
placeholders: list[str] = []
wifi_conf = config.get(CONF_WIFI)
if not wifi_conf:
return placeholders
for idx, network in enumerate(wifi_conf.get(CONF_NETWORKS, [])):
ssid = network.get(CONF_SSID)
if isinstance(ssid, str) and ssid == PLACEHOLDER_WIFI_SSID:
placeholders.append(f"wifi.networks[{idx}].ssid")
ap_conf = wifi_conf.get(CONF_AP)
if ap_conf:
ap_ssid = ap_conf.get(CONF_SSID)
if isinstance(ap_ssid, str) and ap_ssid == PLACEHOLDER_WIFI_SSID:
placeholders.append("wifi.ap.ssid")
return placeholders
def check_placeholder_credentials(config: ConfigType) -> None:
"""Raise EsphomeError if any wifi credential is the dashboard placeholder.
Call only at compile time. NEVER from CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA,
or any path reached by `esphome config`; device-builder relies on
validation passing with the placeholders still in place.
"""
locations = _placeholder_wifi_credentials(config)
if not locations:
return
formatted = ", ".join(locations)
raise EsphomeError(
f"wifi configuration still contains the dashboard placeholder value "
f"'{PLACEHOLDER_WIFI_SSID}' at: {formatted}. "
f"Open secrets.yaml and replace 'wifi_ssid' (and 'wifi_password') "
f"with your real wifi credentials before flashing."
)
+10 -1
View File
@@ -4,7 +4,7 @@ from enum import Enum
from esphome.enum import StrEnum from esphome.enum import StrEnum
__version__ = "2026.5.0b1" __version__ = "2026.5.0b2"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = ( VALID_SUBSTITUTIONS_CHARACTERS = (
@@ -1415,3 +1415,12 @@ ENTITY_CATEGORY_DIAGNOSTIC = "diagnostic"
# The corresponding constant exists in c++ # The corresponding constant exists in c++
# when update_interval is set to never, it becomes SCHEDULER_DONT_RUN milliseconds # when update_interval is set to never, it becomes SCHEDULER_DONT_RUN milliseconds
SCHEDULER_DONT_RUN = 4294967295 SCHEDULER_DONT_RUN = 4294967295
# Sentinel values written by the esphome-device-builder dashboard into
# secrets.yaml on first boot so that !secret wifi_ssid / !secret wifi_password
# references resolve cleanly through validation before the user has finished
# the onboarding wizard. Compilation refuses if these reach the binary so that
# a user who dismisses onboarding can't accidentally flash a device that will
# never associate with their wifi.
PLACEHOLDER_WIFI_SSID = "REPLACE_WITH_YOUR_WIFI_NETWORK"
PLACEHOLDER_WIFI_PASSWORD = "REPLACE_WITH_YOUR_WIFI_PASSWORD" # noqa: S105
+1 -1
View File
@@ -69,7 +69,7 @@ ESPHOME_IDF_DEFAULT_FEATURES = _str_to_lst_of_str(
ESPHOME_IDF_FRAMEWORK_MIRRORS = _str_to_lst_of_str( ESPHOME_IDF_FRAMEWORK_MIRRORS = _str_to_lst_of_str(
os.environ.get( os.environ.get(
"ESPHOME_IDF_FRAMEWORK_MIRRORS", "ESPHOME_IDF_FRAMEWORK_MIRRORS",
"https://github.com/espressif/esp-idf/releases/download/v{VERSION}/esp-idf-v{VERSION}.zip;https://github.com/espressif/esp-idf/releases/download/v{MAJOR}.{MINOR}/esp-idf-v{MAJOR}.{MINOR}.zip", "https://github.com/esphome-libs/esp-idf/releases/download/v{VERSION}/esp-idf-v{VERSION}.tar.xz;https://github.com/esphome-libs/esp-idf/releases/download/v{MAJOR}.{MINOR}/esp-idf-v{MAJOR}.{MINOR}.tar.xz",
) )
) )
+30 -23
View File
@@ -191,17 +191,38 @@ def run_reconfigure() -> int:
def has_outdated_files(): def has_outdated_files():
"""Check if the build configuration is stale. """Check if the build configuration is stale.
Returns True if required build files are missing or if configuration inputs Returns True if required build files are missing or if ESPHome's
are newer than the generated CMake/Ninja build artifacts. resolved build inputs are newer than CMakeCache.txt:
- ``sdkconfig.<name>.esphomeinternal`` -- the canonical "what state
did ESPHome resolve the YAML to" snapshot. Any change in build
flags, enabled components, framework version, or target ends up
rewriting it (we embed a ``# ESPHOME_IDF_VERSION=`` comment line
for the version case where the option set would otherwise be
identical).
- ``src/idf_component.yml`` -- the project manifest. Managed
component additions/removals (e.g. via ``add_idf_component``) can
happen without any sdkconfig impact, and ``_write_idf_component_yml``
already deletes ``dependencies.lock`` on a change but that signal
gets lost as soon as the lock is missing.
We deliberately don't watch:
- The top-level/src ``CMakeLists.txt`` -- ESPHome owns those, and
ninja already tracks them as configure-time deps. Including them
causes a perpetual reconfigure loop because CMake doesn't restamp
``CMakeCache.txt`` when only ``idf_build_set_property`` values
change between configures.
- ``$IDF_PATH`` and CMake's ``build/config/`` -- both have mtime
semantics that fire after the wrong configure (or not at all in
common cases like in-place IDF version replacement). The sdkconfig
and manifest hashes subsume the meaningful signal.
""" """
cmakecache_txt_path = CORE.relative_build_path("build/CMakeCache.txt") cmakecache_txt_path = CORE.relative_build_path("build/CMakeCache.txt")
cmakelists_txt_build_path = CORE.relative_build_path("CMakeLists.txt")
cmakelists_txt_src_path = CORE.relative_src_path("CMakeLists.txt")
build_config_path = CORE.relative_build_path("build/config") build_config_path = CORE.relative_build_path("build/config")
sdkconfig_internal_path = CORE.relative_build_path( sdkconfig_internal_path = CORE.relative_build_path(
f"sdkconfig.{CORE.name}.esphomeinternal" f"sdkconfig.{CORE.name}.esphomeinternal"
) )
idf_component_yml_path = CORE.relative_build_path("src/idf_component.yml")
dependency_lock_path = CORE.relative_build_path("dependencies.lock") dependency_lock_path = CORE.relative_build_path("dependencies.lock")
build_ninja_path = CORE.relative_build_path("build/build.ninja") build_ninja_path = CORE.relative_build_path("build/build.ninja")
@@ -219,14 +240,8 @@ def has_outdated_files():
cmakecache_txt_mtime = os.path.getmtime(cmakecache_txt_path) cmakecache_txt_mtime = os.path.getmtime(cmakecache_txt_path)
return any( return any(
os.path.getmtime(f) > cmakecache_txt_mtime os.path.getmtime(f) > cmakecache_txt_mtime
for f in [ for f in [sdkconfig_internal_path, idf_component_yml_path]
_get_idf_path(), if f.exists()
cmakelists_txt_build_path,
cmakelists_txt_src_path,
sdkconfig_internal_path,
build_config_path,
]
if f and os.path.exists(f)
) )
@@ -302,21 +317,13 @@ def run_compile(config, verbose: bool) -> int:
return rc return rc
_LOGGER.info("Regenerating CMakeLists.txt with discovered components...") _LOGGER.info("Regenerating CMakeLists.txt with discovered components...")
write_project(minimal=False) write_project(minimal=False)
# The post-discovery rewrite leaves CMakeLists newer than
# CMakeCache.txt. CMake won't re-touch CMakeCache.txt on a
# configure that only changes idf_build_set_property values
# (those aren't cache variables), so has_outdated_files() would
# return True on every subsequent build, perpetually retriggering
# the two-pass. Touch CMakeCache.txt now so its mtime stays past
# the rewritten CMakeLists.
cmakecache = CORE.relative_build_path("build/CMakeCache.txt")
if cmakecache.is_file():
os.utime(cmakecache)
if CORE.testing_mode: if CORE.testing_mode:
# Reconfigure again so cmake is up to date with the full # Reconfigure again so cmake is up to date with the full
# component list before the build's idf.py invocation runs -- # component list before the build's idf.py invocation runs --
# idf.py build would otherwise re-run cmake and regenerate # idf.py build would otherwise re-run cmake and regenerate
# memory.ld, wiping the DRAM/IRAM patches applied below. # memory.ld, wiping the DRAM/IRAM patches applied below.
# Outside testing mode ninja's own configure-time dep on
# CMakeLists.txt handles the re-run as part of the build step.
rc = run_reconfigure() rc = run_reconfigure()
if rc != 0: if rc != 0:
_LOGGER.error("Reconfigure with discovered components failed") _LOGGER.error("Reconfigure with discovered components failed")
+2 -2
View File
@@ -10,7 +10,7 @@ dependencies:
esphome/micro-flac: esphome/micro-flac:
version: 0.2.0 version: 0.2.0
esphome/micro-mp3: esphome/micro-mp3:
version: 0.2.0 version: 0.2.1
esphome/micro-opus: esphome/micro-opus:
version: 0.4.1 version: 0.4.1
esphome/micro-wav: esphome/micro-wav:
@@ -36,7 +36,7 @@ dependencies:
rules: rules:
- if: "target in [esp32h2, esp32p4]" - if: "target in [esp32h2, esp32p4]"
espressif/esp_hosted: espressif/esp_hosted:
version: 2.12.6 version: 2.12.7
rules: rules:
- if: "target in [esp32h2, esp32p4]" - if: "target in [esp32h2, esp32p4]"
zorxx/multipart-parser: zorxx/multipart-parser:
+12 -1
View File
@@ -273,10 +273,21 @@ class StorageJSON:
""" """
CORE.name = self.name CORE.name = self.name
CORE.build_path = self.build_path CORE.build_path = self.build_path
target_platform = self.core_platform or self.target_platform.lower()
CORE.data[KEY_CORE] = { CORE.data[KEY_CORE] = {
KEY_TARGET_PLATFORM: self.core_platform or self.target_platform.lower(), KEY_TARGET_PLATFORM: target_platform,
KEY_TARGET_FRAMEWORK: self.framework, KEY_TARGET_FRAMEWORK: self.framework,
} }
# The compile pipeline populates CORE.data[KEY_ESP32] when esp32's
# validator runs; on the cache fast path that validator is skipped,
# so populate the variant upload_using_esptool reads via
# esp32.get_esp32_variant(). target_platform on disk is the variant
# (e.g. "ESP32S3"); core_platform is the family (e.g. "esp32").
if target_platform == const.PLATFORM_ESP32:
from esphome.components.esp32.const import KEY_ESP32
from esphome.const import KEY_VARIANT
CORE.data[KEY_ESP32] = {KEY_VARIANT: self.target_platform}
def __eq__(self, o) -> bool: def __eq__(self, o) -> bool:
return isinstance(o, StorageJSON) and self.as_dict() == o.as_dict() return isinstance(o, StorageJSON) and self.as_dict() == o.as_dict()
+2 -2
View File
@@ -12,8 +12,8 @@ platformio==6.1.19
esptool==5.2.0 esptool==5.2.0
click==8.3.3 click==8.3.3
esphome-dashboard==20260425.0 esphome-dashboard==20260425.0
aioesphomeapi==45.0.0 aioesphomeapi==45.0.3
zeroconf==0.148.0 zeroconf==0.149.3
puremagic==1.30 puremagic==1.30
ruamel.yaml==0.19.1 # dashboard_import ruamel.yaml==0.19.1 # dashboard_import
ruamel.yaml.clib==0.2.15 # dashboard_import ruamel.yaml.clib==0.2.15 # dashboard_import
@@ -29,12 +29,12 @@ esp32_ble_tracker:
- service_uuid: ABCD - service_uuid: ABCD
then: then:
- lambda: !lambda |- - lambda: !lambda |-
ESP_LOGD("main", "Length of service data is %i", x.size()); ESP_LOGD("main", "Length of service data is %zu", x.size());
on_ble_manufacturer_data_advertise: on_ble_manufacturer_data_advertise:
- manufacturer_id: ABCD - manufacturer_id: ABCD
then: then:
- lambda: !lambda |- - lambda: !lambda |-
ESP_LOGD("main", "Length of manufacturer data is %i", x.size()); ESP_LOGD("main", "Length of manufacturer data is %zu", x.size());
on_scan_end: on_scan_end:
- then: - then:
- lambda: |- - lambda: |-
+1 -1
View File
@@ -123,7 +123,7 @@ select:
- lambda: |- - lambda: |-
id(uart_bus).flush(); id(uart_bus).flush();
uint32_t new_baud_rate = stoi(x); uint32_t new_baud_rate = stoi(x);
ESP_LOGD("change_baud_rate", "Changing baud rate from %i to %i",id(uart_bus).get_baud_rate(), new_baud_rate); ESP_LOGD("change_baud_rate", "Changing baud rate from %" PRIu32 " to %" PRIu32, id(uart_bus).get_baud_rate(), new_baud_rate);
if (id(uart_bus).get_baud_rate() != new_baud_rate) { if (id(uart_bus).get_baud_rate() != new_baud_rate) {
id(uart_bus).set_baud_rate(new_baud_rate); id(uart_bus).set_baud_rate(new_baud_rate);
#if defined(USE_ESP8266) || defined(USE_ESP32) #if defined(USE_ESP8266) || defined(USE_ESP32)
+3 -3
View File
@@ -660,13 +660,13 @@ lvgl:
on_release: on_release:
logger.log: logger.log:
format: Button released at %d/%d format: Button released at %d/%d
args: [point.x, point.y] args: ['(int) point.x', '(int) point.y']
on_long_press_repeat: on_long_press_repeat:
logger.log: Button clicked logger.log: Button clicked
on_pressing: on_pressing:
logger.log: logger.log:
format: Button pressing at %d/%d format: Button pressing at %d/%d
args: [point.x, point.y] args: ['(int) point.x', '(int) point.y']
on_press_lost: on_press_lost:
logger.log: Button press lost logger.log: Button press lost
on_single_click: on_single_click:
@@ -944,7 +944,7 @@ lvgl:
on_release: on_release:
logger.log: logger.log:
format: Slider released at %d/%d with value %.0f format: Slider released at %d/%d with value %.0f
args: [point.x, point.y, x] args: ['(int) point.x', '(int) point.y', x]
- button: - button:
styles: spin_button styles: spin_button
id: spin_up id: spin_up
+1 -1
View File
@@ -21,7 +21,7 @@ modbus_server:
read_lambda: |- read_lambda: |-
return 31; return 31;
write_lambda: |- write_lambda: |-
printf("address=%d, value=%d", x); printf("address=%d, value=%" PRId32 "\n", (int) address, x);
return true; return true;
- id: modbus_server4 - id: modbus_server4
modbus_id: mod_bus2 modbus_id: mod_bus2
+1 -1
View File
@@ -64,7 +64,7 @@ mqtt:
topic: some/topic topic: some/topic
payload: Good-bye payload: Good-bye
- lambda: |- - lambda: |-
ESP_LOGD("MQTT", "Disconnect reason %d", reason); ESP_LOGD("MQTT", "Disconnect reason %d", (int) reason);
publish_nan_as_none: false publish_nan_as_none: false
binary_sensor: binary_sensor:

Some files were not shown because too many files have changed in this diff Show More