mirror of
https://github.com/esphome/esphome.git
synced 2026-09-22 18:05:00 +08:00
Merge remote-tracking branch 'origin/dev' into jesserockz-2026-584
# Conflicts: # esphome/components/api/__init__.py # platformio.ini
This commit is contained in:
@@ -70,6 +70,7 @@ async function isStackedPr(github, context) {
|
||||
async function detectMergeBranch(github, context) {
|
||||
const labels = new Set();
|
||||
const baseRef = context.payload.pull_request.base.ref;
|
||||
const defaultBranch = context.payload.repository.default_branch;
|
||||
|
||||
if (baseRef === 'release') {
|
||||
labels.add('merging-to-release');
|
||||
@@ -78,7 +79,7 @@ async function detectMergeBranch(github, context) {
|
||||
} else if (await isStackedPr(github, context)) {
|
||||
// GitHub manages the merge order for a stack, so these are not blocked.
|
||||
labels.add('stacked-pr');
|
||||
} else if (baseRef !== 'dev') {
|
||||
} else if (baseRef !== defaultBranch) {
|
||||
// A chain built by hand: it must not merge until its base branch does.
|
||||
labels.add('chained-pr');
|
||||
}
|
||||
|
||||
@@ -43,14 +43,14 @@ const WITHOUT_SCHEMA = 'CODEOWNERS = ["@esphome/core"]';
|
||||
|
||||
// Builds a fresh context for detectMergeBranch tests instead of mutating the
|
||||
// shared CONTEXT fixture above (which other describe blocks rely on).
|
||||
function makeMergeContext(baseRef, { stack } = {}) {
|
||||
function makeMergeContext(baseRef, { stack, defaultBranch = 'dev' } = {}) {
|
||||
const pull_request = { number: 1, base: { ref: baseRef } };
|
||||
if (stack !== undefined) {
|
||||
pull_request.stack = stack;
|
||||
}
|
||||
return {
|
||||
repo: { owner: 'esphome', repo: 'esphome' },
|
||||
payload: { pull_request }
|
||||
payload: { pull_request, repository: { default_branch: defaultBranch } }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -136,6 +136,21 @@ describe('detectMergeBranch', () => {
|
||||
assert.deepEqual(Array.from(labels).sort(), ['chained-pr']);
|
||||
assert.equal(state.calls, 1);
|
||||
});
|
||||
|
||||
it('base ref matches default branch adds no labels', async () => {
|
||||
const { github } = makeStackGithub({ stack: null });
|
||||
const context = makeMergeContext('other', { defaultBranch: 'other' });
|
||||
const labels = await detectMergeBranch(github, context);
|
||||
assert.deepEqual(Array.from(labels).sort(), []);
|
||||
});
|
||||
|
||||
it('base ref dev when the default branch is main adds chained-pr', async () => {
|
||||
const { github } = makeStackGithub({ stack: null });
|
||||
const context = makeMergeContext('dev', { defaultBranch: 'main' });
|
||||
const labels = await detectMergeBranch(github, context);
|
||||
assert.deepEqual(Array.from(labels).sort(), ['chained-pr']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -179,6 +179,7 @@ jobs:
|
||||
. venv/bin/activate
|
||||
script/ci-custom.py
|
||||
script/build_codeowners.py --check
|
||||
script/build_alias_registry.py --check
|
||||
script/build_language_schema.py --check
|
||||
script/generate-esp32-boards.py --check
|
||||
script/generate-rp2-boards.py --check
|
||||
@@ -444,8 +445,12 @@ jobs:
|
||||
- common
|
||||
- determine-jobs
|
||||
if: >-
|
||||
(github.event_name == 'push' && github.ref_name == 'dev') ||
|
||||
(github.event_name == 'pull_request' && needs.determine-jobs.outputs.benchmarks == 'true')
|
||||
github.repository == 'esphome/esphome' && (
|
||||
(github.event_name == 'push' && github.ref_name == 'dev') ||
|
||||
(github.event_name == 'pull_request' && needs.determine-jobs.outputs.benchmarks == 'true')
|
||||
)
|
||||
# CodSpeed benchmarks require a CodSpeed account linked to the repository to run
|
||||
# (https://codspeed.io) -- disabled on forks that aren't esphome/esphome itself.
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ RUN \
|
||||
-r /requirements.txt
|
||||
|
||||
# Install the ESPHome Device Builder dashboard.
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.9.6
|
||||
RUN uv pip install --no-cache-dir esphome-device-builder==1.11.0
|
||||
|
||||
RUN \
|
||||
platformio settings set enable_telemetry No \
|
||||
|
||||
+14
-14
@@ -2732,7 +2732,8 @@ def run_esphome(argv):
|
||||
conf_path.name,
|
||||
)
|
||||
|
||||
if config is None:
|
||||
cache_missed = config is None
|
||||
if cache_missed:
|
||||
from esphome.config import read_config
|
||||
|
||||
config = read_config(
|
||||
@@ -2741,26 +2742,25 @@ def run_esphome(argv):
|
||||
# Snapshot only needed by `esphome config --no-defaults`.
|
||||
snapshot_user_config=getattr(args, "no_defaults", False),
|
||||
)
|
||||
# Refresh the cache so the next upload/logs hits the fast path
|
||||
# instead of re-running read_config. Skip when the storage
|
||||
# sidecar is absent (no compile has run): the cache would
|
||||
# never be loaded back, so writing secrets to disk is wasted.
|
||||
if cache_eligible and config is not None:
|
||||
from esphome.compiled_config import save_compiled_config
|
||||
from esphome.storage_json import ext_storage_path
|
||||
|
||||
if ext_storage_path(conf_path.name).exists():
|
||||
save_compiled_config(config)
|
||||
if config is None:
|
||||
return 2
|
||||
if config is None:
|
||||
return 2
|
||||
CORE.config = config
|
||||
|
||||
# Fallback for platforms whose validators didn't set the toolchain
|
||||
# (only the esp32 component reads esp32.framework.toolchain). All
|
||||
# other platforms only support PlatformIO today.
|
||||
# other platforms only support PlatformIO today. Must run before the
|
||||
# cache refresh below so its sidecar records the same toolchain a
|
||||
# compile would.
|
||||
if CORE.toolchain is None:
|
||||
CORE.toolchain = Toolchain.PLATFORMIO
|
||||
|
||||
# Refresh the cache so the next upload/logs hits the fast path
|
||||
# instead of re-running read_config.
|
||||
if cache_eligible and cache_missed:
|
||||
from esphome.compiled_config import save_compiled_config_and_sidecar
|
||||
|
||||
save_compiled_config_and_sidecar(config)
|
||||
|
||||
if args.command not in POST_CONFIG_ACTIONS:
|
||||
safe_print(f"Unknown command {args.command}")
|
||||
return 1
|
||||
|
||||
@@ -18,9 +18,9 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from esphome.const import __version__ as ESPHOME_VERSION
|
||||
from esphome.core import CORE, Lambda
|
||||
from esphome.core import CORE, EsphomeError, Lambda
|
||||
from esphome.helpers import write_file
|
||||
from esphome.storage_json import StorageJSON, ext_storage_path
|
||||
from esphome.storage_json import StorageJSON, ext_storage_path, storage_path
|
||||
from esphome.types import ConfigType
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -65,7 +65,71 @@ def save_compiled_config(config: ConfigType) -> None:
|
||||
# non-basic dict key), so every upload/logs pays the slow path.
|
||||
_LOGGER.warning("Cannot cache the validated config: %s", err)
|
||||
except Exception as err: # noqa: BLE001 # pylint: disable=broad-except
|
||||
_LOGGER.debug("Skipping compiled config cache write: %s", err)
|
||||
# Likely persistent (permissions, full disk): every upload/logs
|
||||
# pays the slow path until it clears, so surface it.
|
||||
_LOGGER.warning("Skipping compiled config cache write: %s", err)
|
||||
|
||||
|
||||
def save_compiled_config_and_sidecar(config: ConfigType) -> None:
|
||||
"""Refresh the cache from the upload/logs fallback (CORE.config must be set).
|
||||
|
||||
The cache is only written when a complete sidecar is on disk:
|
||||
load_compiled_config can't use it otherwise, and it holds resolved
|
||||
secrets.
|
||||
"""
|
||||
if _refresh_sidecar():
|
||||
save_compiled_config(config)
|
||||
|
||||
|
||||
def _refresh_sidecar() -> bool:
|
||||
"""Ensure a complete sidecar is on disk; True when one is.
|
||||
|
||||
Writes one (without claiming a build) when missing or wizard-only.
|
||||
Failures are non-fatal; the next upload/logs pays the slow path again.
|
||||
"""
|
||||
try:
|
||||
path = storage_path()
|
||||
try:
|
||||
old = StorageJSON.load_strict(path)
|
||||
except Exception as err: # noqa: BLE001 # pylint: disable=broad-except
|
||||
# Present but unreadable: it may hold a real build's metadata,
|
||||
# and a fresh rewrite would also stop the next compile from
|
||||
# cleaning a possibly incoherent build tree.
|
||||
_LOGGER.warning(
|
||||
"Not caching: storage sidecar %s is unreadable (%s)", path, err
|
||||
)
|
||||
return False
|
||||
if old is not None and old.can_apply_to_core():
|
||||
# Compile-written; nothing to refresh.
|
||||
return True
|
||||
if CORE.build_path is not None and CORE.build_path.exists():
|
||||
# An unvalidated build tree: its absent or mismatched sidecar
|
||||
# is what makes the next compile wipe it, so don't vouch for
|
||||
# a build this run never saw.
|
||||
_LOGGER.warning(
|
||||
"Not caching: build tree %s has no matching sidecar; "
|
||||
"'esphome compile' will settle it",
|
||||
CORE.build_path,
|
||||
)
|
||||
return False
|
||||
new = StorageJSON.from_esphome_core(CORE, old, claim_build=False)
|
||||
if not new.can_apply_to_core():
|
||||
_LOGGER.warning("Not caching: rebuilt storage sidecar is still incomplete")
|
||||
return False
|
||||
new.save(path)
|
||||
return True
|
||||
except (OSError, EsphomeError) as err:
|
||||
# write_file wraps OSError into EsphomeError. Persistent
|
||||
# (unwritable storage dir), so surface that every upload/logs
|
||||
# pays the slow path.
|
||||
_LOGGER.warning("Could not refresh the storage sidecar: %s", err)
|
||||
except Exception: # noqa: BLE001 # pylint: disable=broad-except
|
||||
# A structural bug; keep the traceback so it isn't mistaken
|
||||
# for the I/O failure above.
|
||||
_LOGGER.warning(
|
||||
"Unexpected error refreshing the storage sidecar", exc_info=True
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def load_compiled_config(conf_path: Path) -> ConfigType | None:
|
||||
@@ -98,11 +162,8 @@ def load_compiled_config(conf_path: Path) -> ConfigType | None:
|
||||
return None
|
||||
|
||||
storage = StorageJSON.load(ext_storage_path(conf_path.name))
|
||||
if storage is None:
|
||||
return None
|
||||
# apply_to_core assumes a real compile wrote the sidecar; wizard-only
|
||||
# sidecars leave both of these unset and can't drive upload/logs.
|
||||
if not storage.core_platform and not storage.target_platform:
|
||||
if storage is None or not storage.can_apply_to_core():
|
||||
_LOGGER.debug("Ignoring compiled config cache: sidecar missing or incomplete")
|
||||
return None
|
||||
storage.apply_to_core()
|
||||
return config
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
"""Component alias registry.
|
||||
|
||||
Generated by script/build_alias_registry.py - do not edit manually.
|
||||
See the component-alias section of esphome/loader.py.
|
||||
"""
|
||||
|
||||
# alias -> (canonical component, removal version or None)
|
||||
COMPONENT_ALIASES: dict[str, tuple[str, str | None]] = {
|
||||
"rp2040": ("rp2", "2027.7.0"),
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.common";
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
const LogString *sampling_mode_to_str(SamplingMode mode) {
|
||||
switch (mode) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.esp32";
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
adc_oneshot_unit_handle_t ADCSensor::shared_adc_handles[2] = {nullptr, nullptr};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ ADC_MODE(ADC_VCC)
|
||||
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.esp8266";
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
void ADCSensor::setup() {
|
||||
#ifndef USE_ADC_SENSOR_VCC
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.libretiny";
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
void ADCSensor::setup() {
|
||||
#ifndef USE_ADC_SENSOR_VCC
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.rp2";
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
// The on-die temperature sensor sits on the last ADC channel: input 4 on RP2040
|
||||
// and RP2350A, but input 8 on RP2350B, which has eight external channels rather
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.zephyr";
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
void ADCSensor::setup() {
|
||||
if (!adc_is_ready_dt(this->channel_)) {
|
||||
|
||||
@@ -45,7 +45,7 @@ CODEOWNERS = ["@esphome/core"]
|
||||
|
||||
# Keep in sync with platformio.ini and esphome/idf_component.yml.
|
||||
# LIBSODIUM_VERSION must match the version noise-c pins in its idf_component.yml.
|
||||
NOISE_C_VERSION = "0.1.15"
|
||||
NOISE_C_VERSION = "0.1.18"
|
||||
LIBSODIUM_VERSION = "1.10021.2"
|
||||
|
||||
|
||||
|
||||
@@ -160,11 +160,6 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
|
||||
#else
|
||||
#error "No frame helper defined"
|
||||
#endif
|
||||
#ifdef USE_CAMERA
|
||||
if (camera::Camera::instance() != nullptr) {
|
||||
this->image_reader_ = std::unique_ptr<camera::CameraImageReader>{camera::Camera::instance()->create_image_reader()};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void APIConnection::start() {
|
||||
@@ -448,7 +443,7 @@ void APIConnection::on_disconnect_response() {
|
||||
uint16_t APIConnection::fill_and_encode_entity_state(EntityBase *entity, StateResponseProtoMessage &msg,
|
||||
CalculateSizeFn size_fn, MessageEncodeFn encode_fn,
|
||||
APIConnection *conn, uint32_t remaining_size) {
|
||||
msg.key = entity->get_entity_key();
|
||||
msg.key = entity->get_object_id_hash();
|
||||
#ifdef USE_DEVICES
|
||||
msg.device_id = entity->get_device_id();
|
||||
#endif
|
||||
@@ -459,7 +454,7 @@ uint16_t APIConnection::fill_and_encode_entity_info(EntityBase *entity, InfoResp
|
||||
CalculateSizeFn size_fn, MessageEncodeFn encode_fn,
|
||||
APIConnection *conn, uint32_t remaining_size) {
|
||||
// Set common fields that are shared by all entity types
|
||||
msg.key = entity->get_entity_key();
|
||||
msg.key = entity->get_object_id_hash();
|
||||
|
||||
if (entity->has_own_name()) {
|
||||
msg.name = entity->get_name();
|
||||
@@ -1140,6 +1135,7 @@ void APIConnection::try_send_camera_image_() {
|
||||
if (!this->image_reader_)
|
||||
return;
|
||||
|
||||
const auto *cam = camera::Camera::instance();
|
||||
// Send as many chunks as possible without blocking
|
||||
while (this->image_reader_->available()) {
|
||||
if (!this->helper_->can_write_without_blocking())
|
||||
@@ -1149,11 +1145,11 @@ void APIConnection::try_send_camera_image_() {
|
||||
bool done = this->image_reader_->available() == to_send;
|
||||
|
||||
CameraImageResponse msg;
|
||||
msg.key = camera::Camera::instance()->get_entity_key();
|
||||
msg.key = cam->get_object_id_hash();
|
||||
msg.set_data(this->image_reader_->peek_data_buffer(), to_send);
|
||||
msg.done = done;
|
||||
#ifdef USE_DEVICES
|
||||
msg.device_id = camera::Camera::instance()->get_device_id();
|
||||
msg.device_id = cam->get_device_id();
|
||||
#endif
|
||||
|
||||
if (!this->send_message(msg)) {
|
||||
@@ -1169,15 +1165,19 @@ void APIConnection::try_send_camera_image_() {
|
||||
void APIConnection::set_camera_state(std::shared_ptr<camera::CameraImage> image) {
|
||||
if (!this->flags_.state_subscription)
|
||||
return;
|
||||
if (!this->image_reader_)
|
||||
if (this->image_reader_ && this->image_reader_->available())
|
||||
return;
|
||||
if (this->image_reader_->available())
|
||||
if (!image->was_requested_by(esphome::camera::API_REQUESTER) && !image->was_requested_by(esphome::camera::IDLE))
|
||||
return;
|
||||
if (image->was_requested_by(esphome::camera::API_REQUESTER) || image->was_requested_by(esphome::camera::IDLE)) {
|
||||
this->image_reader_->set_image(std::move(image));
|
||||
// Try to send immediately to reduce latency
|
||||
this->try_send_camera_image_();
|
||||
if (!this->image_reader_) {
|
||||
// Created on the first image this connection will send, so connections
|
||||
// that never receive one never pay for a reader. Only a registered
|
||||
// camera's listener can reach this, so instance() is non-null here.
|
||||
this->image_reader_ = std::unique_ptr<camera::CameraImageReader>{camera::Camera::instance()->create_image_reader()};
|
||||
}
|
||||
this->image_reader_->set_image(std::move(image));
|
||||
// Try to send immediately to reduce latency
|
||||
this->try_send_camera_image_();
|
||||
}
|
||||
uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size) {
|
||||
auto *camera = static_cast<camera::Camera *>(entity);
|
||||
|
||||
@@ -591,18 +591,21 @@ APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
|
||||
*/
|
||||
APIError APINoiseFrameHelper::init_handshake_() {
|
||||
int err;
|
||||
memset(&nid_, 0, sizeof(nid_));
|
||||
// const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
|
||||
// err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
|
||||
nid_.pattern_id = NOISE_PATTERN_NN;
|
||||
nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
|
||||
nid_.dh_id = NOISE_DH_CURVE25519;
|
||||
nid_.prefix_id = NOISE_PREFIX_STANDARD;
|
||||
nid_.hybrid_id = NOISE_DH_NONE;
|
||||
nid_.hash_id = NOISE_HASH_SHA256;
|
||||
nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
|
||||
// Noise_NNpsk0_25519_ChaChaPoly_SHA256, built on the stack:
|
||||
// noise_handshakestate_new_by_id copies it, so a member would waste
|
||||
// 104 bytes per connection, and a static const would sit in RAM on
|
||||
// ESP8266 (.rodata is DRAM there).
|
||||
const NoiseProtocolId nid = {
|
||||
.prefix_id = NOISE_PREFIX_STANDARD,
|
||||
.pattern_id = NOISE_PATTERN_NN,
|
||||
.modifier_ids = {NOISE_MODIFIER_PSK0},
|
||||
.dh_id = NOISE_DH_CURVE25519,
|
||||
.cipher_id = NOISE_CIPHER_CHACHAPOLY,
|
||||
.hash_id = NOISE_HASH_SHA256,
|
||||
.hybrid_id = NOISE_DH_NONE,
|
||||
};
|
||||
|
||||
err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
|
||||
err = noise_handshakestate_new_by_id(&handshake_, &nid, NOISE_ROLE_RESPONDER);
|
||||
APIError aerr =
|
||||
handle_noise_error_(err, LOG_STR("noise_handshakestate_new_by_id"), APIError::HANDSHAKESTATE_SETUP_FAILED);
|
||||
if (aerr != APIError::OK)
|
||||
|
||||
@@ -63,9 +63,6 @@ class APINoiseFrameHelper final : public APIFrameHelper {
|
||||
// Buffer for noise handshake prologue (released after handshake)
|
||||
APIBuffer prologue_;
|
||||
|
||||
// NoiseProtocolId (size depends on implementation)
|
||||
NoiseProtocolId nid_;
|
||||
|
||||
// Group small types together
|
||||
// Fixed-size header buffer for noise protocol:
|
||||
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
|
||||
|
||||
@@ -5,11 +5,11 @@ bring-up and the controller BLE address. Consumers (bk72xx_ble_tracker) build
|
||||
on this component and contain no SDK calls of their own.
|
||||
|
||||
Supported SoCs (BLE 5.x): BK7231N/BK7236 (BLE 5.1), BK7238/BK7252N/BK7253
|
||||
(BLE 5.2), and any future BLE-5.x SoC. Capability is detected at compile time,
|
||||
not by a chip list: the C++ guards on `__has_include("ble_api.h")` — the Beken
|
||||
BLE 5.x public API header, which the LibreTiny beken-72xx builder ships only
|
||||
for BLE-5.x SoCs. BK7231T/BK7251/BK7271 (BLE 4.2) and BK7231Q (no BLE) fail
|
||||
with a clear #error.
|
||||
(BLE 5.2), and any future BLE-5.x SoC. Known non-5.x families are rejected in
|
||||
to_code; unknown families are capability-checked at compile time via
|
||||
`__has_include("app_ble.h")`, a header only on the BLE 5.x include path
|
||||
(ble_api.h ships for every SoC, so it cannot be the probe). A non-5.x build
|
||||
fails with a clear #error.
|
||||
|
||||
No framework patch is needed: the LibreTiny beken-72xx builder already compiles
|
||||
and links the BLE 5.x stack (CFG_SUPPORT_BLE=1 + CFG_BLE_VERSION=BLE_VERSION_5_x;
|
||||
@@ -21,9 +21,16 @@ import logging
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import libretiny
|
||||
from esphome.components.libretiny.const import FAMILY_BK7231N, FAMILY_BK7238
|
||||
from esphome.components.libretiny.const import (
|
||||
FAMILY_BK7231N,
|
||||
FAMILY_BK7231Q,
|
||||
FAMILY_BK7231T,
|
||||
FAMILY_BK7238,
|
||||
FAMILY_BK7251,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID
|
||||
from esphome.core import EsphomeError
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DEPENDENCIES = ["bk72xx"]
|
||||
@@ -50,7 +57,32 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
request_scan_listener_slot = cg.slot_counter("BK72XX_BLE_SCAN_LISTENER_COUNT")
|
||||
|
||||
|
||||
def _unsupported_family_message(family: str) -> str | None:
|
||||
if family in (FAMILY_BK7231T, FAMILY_BK7251):
|
||||
return (
|
||||
f"bk72xx_ble does not support {family}: this SoC has the Beken BLE 4.2 "
|
||||
"stack; a BLE 5.x SoC such as BK7231N or BK7238 is required"
|
||||
)
|
||||
if family == FAMILY_BK7231Q:
|
||||
return "bk72xx_ble does not support BK7231Q: this SoC has no BLE"
|
||||
return None
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
# Warn only: a hard error here would break the validate-only CI fixtures,
|
||||
# which run on a BLE 4.2 board. The hard error is raised at codegen.
|
||||
if msg := _unsupported_family_message(libretiny.get_libretiny_family()):
|
||||
_LOGGER.warning("%s (this configuration cannot compile)", msg)
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
if msg := _unsupported_family_message(libretiny.get_libretiny_family()):
|
||||
raise EsphomeError(msg)
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#ifdef USE_BK72XX_BLE
|
||||
|
||||
// Same SDK gate as bk72xx_ble.cpp (which carries the explanatory #error).
|
||||
#if !defined(CLANG_TIDY) && __has_include("ble_api.h")
|
||||
#if !defined(CLANG_TIDY) && __has_include("ble_api.h") && __has_include("app_ble.h")
|
||||
|
||||
extern "C" {
|
||||
#include "app_ble.h" // app_ble_env, app_ble_run, app_ble_reset, actv_state_t,
|
||||
@@ -115,5 +115,5 @@ BdkOpResult bdk_scan_release(uint8_t activity_idx, bool created, int *err_out) {
|
||||
|
||||
} // namespace esphome::bk72xx_ble
|
||||
|
||||
#endif // !CLANG_TIDY && ble_api.h
|
||||
#endif // !CLANG_TIDY && ble_api.h && app_ble.h
|
||||
#endif // USE_BK72XX_BLE
|
||||
|
||||
@@ -34,22 +34,26 @@
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SDK-capability gate (not a chip allowlist).
|
||||
// This component drives the Beken BLE *5.x* controller via its public API,
|
||||
// `ble_api.h`, which the LibreTiny beken-72xx builder ships only for the
|
||||
// BLE-5.x SoCs (it selects the `ble_pub` 5.x stack from CFG_BLE_VERSION; the
|
||||
// 4.2 SoCs build a different, older API with no ble_api.h). Gate on the header
|
||||
// itself so any BLE-5.x Beken chip — present or future — is supported without a
|
||||
// hard-coded list, and a non-5.x build fails here with a clear message instead
|
||||
// of a cryptic "ble_api.h: No such file or directory".
|
||||
// This component drives the Beken BLE *5.x* controller. `ble_api.h` cannot be
|
||||
// the probe: it ships for every SoC (driver/include) and merely switches on
|
||||
// CFG_BLE_VERSION internally. `app_ble.h` is on the include path only when the
|
||||
// LibreTiny beken-72xx builder selects a 5.x stack, so gating on it supports
|
||||
// any BLE-5.x chip — present or future — without a hard-coded list, and a
|
||||
// non-5.x build fails here with a clear message instead of a cryptic
|
||||
// "app_ble.h: No such file or directory".
|
||||
// ---------------------------------------------------------------------------
|
||||
#if defined(CLANG_TIDY)
|
||||
// The clang-tidy environment does not carry the full Beken BDK BLE 5.x API
|
||||
// (its ble_api.h variant lacks parts of the 5.x surface), so there is nothing
|
||||
// accurate to analyze the SDK calls against — skip the file under analysis.
|
||||
#define BK72XX_BLE_NO_SDK
|
||||
#elif !__has_include("ble_api.h")
|
||||
#elif !__has_include("ble_api.h") || !__has_include("app_ble.h")
|
||||
// Also skip the SDK body: #error does not stop the preprocessor, and on a 4.2
|
||||
// SoC ble_api.h exists, so without the guard the 5.x symbols would fail one by
|
||||
// one and bury this message.
|
||||
#define BK72XX_BLE_NO_SDK
|
||||
#error \
|
||||
"bk72xx_ble requires a BLE 5.x Beken SDK (ble_api.h). Supported SoCs: BK7231N/BK7236 (BLE 5.1) and BK7238/BK7252N/BK7253 (BLE 5.2). BK7231T/BK7251/BK7271 (BLE 4.2) and BK7231Q (no BLE) are not supported."
|
||||
"bk72xx_ble requires a BLE 5.x Beken SDK (app_ble.h). Supported SoCs: BK7231N/BK7236 (BLE 5.1) and BK7238/BK7252N/BK7253 (BLE 5.2). BK7231T/BK7251/BK7271 (BLE 4.2) and BK7231Q (no BLE) are not supported."
|
||||
#endif
|
||||
|
||||
#ifndef BK72XX_BLE_NO_SDK
|
||||
|
||||
@@ -37,7 +37,7 @@ from esphome.const import (
|
||||
CONF_INTERVAL,
|
||||
KEY_TARGET_PLATFORM,
|
||||
)
|
||||
from esphome.core import CORE, ID, KEY_CORE
|
||||
from esphome.core import CORE, ID, KEY_CORE, TimePeriod
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@Bl00d-B0b"]
|
||||
@@ -243,19 +243,27 @@ def validate_scan_parameters(config: ConfigType) -> ConfigType:
|
||||
return config
|
||||
|
||||
|
||||
# The historical scan window default shared by the trackers that do not pin
|
||||
# their own; also the fallback for esp32's conditional default.
|
||||
DEFAULT_SCAN_WINDOW = "30ms"
|
||||
|
||||
|
||||
def scan_parameters_schema(
|
||||
interval_default: str,
|
||||
*,
|
||||
window_default: str = "30ms",
|
||||
window_default: str | Callable[[], TimePeriod] = DEFAULT_SCAN_WINDOW,
|
||||
) -> cv.All:
|
||||
"""Build the scan_parameters value schema shared by all BLE trackers.
|
||||
|
||||
interval_default and window_default are per chip (e.g. esp32 320/30 ms,
|
||||
bk72xx/rp2 100/30 ms — the reference scan rates of the respective stacks;
|
||||
LN882H's SDK recommends 100/50 ms). The `active` option (default on) is
|
||||
unconditional: active scanning is part of the tracker contract — every
|
||||
current proxy client assumes it, so a passive-only tracker must not share
|
||||
this schema.
|
||||
LN882H's SDK recommends 100/50 ms). window_default may also be a zero-arg
|
||||
callable evaluated per validation when the user omits the key (esp32 uses
|
||||
this to record that the window was defaulted, so a later validation step
|
||||
can adjust it once sibling keys are resolved). The `active` option
|
||||
(default on) is unconditional: active scanning is part of the tracker
|
||||
contract — every current proxy client assumes it, so a passive-only
|
||||
tracker must not share this schema.
|
||||
"""
|
||||
schema = {
|
||||
cv.Optional(CONF_DURATION, default="5min"): cv.positive_time_period_seconds,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace esphome::bluetooth_connection {
|
||||
|
||||
static const char *const TAG = "bluetooth_connection.bluedroid";
|
||||
static const char *const TAG = "bluetooth_connection";
|
||||
|
||||
using ble_device_base::FAST_CONN_TIMEOUT;
|
||||
using ble_device_base::FAST_MAX_CONN_INTERVAL;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace esphome::bluetooth_connection {
|
||||
|
||||
static const char *const TAG = "bluetooth_connection.rp2";
|
||||
static const char *const TAG = "bluetooth_connection";
|
||||
|
||||
using ble_device_base::ESPBTUUID;
|
||||
using ble_device_base::GATT_ERR_NOT_CONNECTED;
|
||||
|
||||
@@ -103,7 +103,8 @@ struct CameraImageSpec {
|
||||
/** Abstract camera base class. Collaborates with API.
|
||||
* 1) API server starts and registers as a listener (add_listener)
|
||||
* to receive new images from the camera.
|
||||
* 2) New API client connects and creates a new image reader (create_image_reader).
|
||||
* 2) API connection creates an image reader (create_image_reader) when it receives
|
||||
* the first image it will send.
|
||||
* 3) API connection receives protobuf CameraImageRequest and calls request_image.
|
||||
* 3.a) API connection receives protobuf CameraImageRequest and calls start_stream.
|
||||
* 4) Camera implementation provides JPEG data in the CameraImage and notifies listeners.
|
||||
|
||||
@@ -22,6 +22,7 @@ CONF_GYROSCOPE_ODR = "gyroscope_odr"
|
||||
CONF_GYROSCOPE_RANGE = "gyroscope_range"
|
||||
CONF_IAQ = "iaq"
|
||||
CONF_IGNORE_NOT_FOUND = "ignore_not_found"
|
||||
CONF_LABEL = "label"
|
||||
CONF_LIBRETINY = "libretiny"
|
||||
CONF_LOOP = "loop"
|
||||
CONF_NOX_INDEX = "nox_index"
|
||||
@@ -35,6 +36,7 @@ CONF_REQUEST_HEADERS = "request_headers"
|
||||
CONF_ROWS = "rows"
|
||||
CONF_SCAN_PARAMETERS = "scan_parameters"
|
||||
CONF_SHA256 = "sha256"
|
||||
CONF_SLOT = "slot"
|
||||
CONF_STATE_SAVE_INTERVAL = "state_save_interval"
|
||||
CONF_STOP_BITS = "stop_bits"
|
||||
CONF_TARGET_COUNT = "target_count"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace esphome::deep_sleep {
|
||||
|
||||
static const char *const TAG = "deep_sleep.bk72xx";
|
||||
static const char *const TAG = "deep_sleep";
|
||||
|
||||
#ifdef USE_DEEP_SLEEP_ON_WAKE
|
||||
WakeupCause get_wakeup_cause() {
|
||||
|
||||
@@ -3,6 +3,7 @@ import re
|
||||
from esphome import automation, core
|
||||
from esphome.automation import maybe_simple_id
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.const import CONF_LABEL
|
||||
from esphome.components.number import Number
|
||||
from esphome.components.select import Select
|
||||
from esphome.components.switch import Switch
|
||||
@@ -30,7 +31,6 @@ display_menu_base_ns = cg.esphome_ns.namespace("display_menu_base")
|
||||
|
||||
CONF_ROTARY = "rotary"
|
||||
CONF_JOYSTICK = "joystick"
|
||||
CONF_LABEL = "label"
|
||||
CONF_MENU = "menu"
|
||||
CONF_BACK = "back"
|
||||
CONF_SELECT = "select"
|
||||
|
||||
@@ -570,6 +570,9 @@ def get_download_types(storage_json):
|
||||
the shape stable so the download panel
|
||||
doesn't have to special-case per-platform schemas.
|
||||
"""
|
||||
# No recorded firmware path means nothing was built; no downloads.
|
||||
if storage_json.firmware_bin_path is None:
|
||||
return []
|
||||
return [
|
||||
{
|
||||
"title": "Factory format (Previously Modern)",
|
||||
|
||||
@@ -360,17 +360,6 @@ static bool has_fault_addr() {
|
||||
return s_raw_crash_data.exception == PANIC_EXCEPTION_FAULT && !s_raw_crash_data.pseudo_excause;
|
||||
}
|
||||
|
||||
// Append both cores' backtrace addresses to buf; returns the new position.
|
||||
static int append_all_backtraces(char *buf, int size, int pos) {
|
||||
pos = append_addrs_to_hint(buf, size, pos, s_raw_crash_data.backtrace, s_raw_crash_data.backtrace_count,
|
||||
s_raw_crash_data.reg_frame_count);
|
||||
#if SOC_CPU_CORES_NUM > 1
|
||||
pos = append_addrs_to_hint(buf, size, pos, s_raw_crash_data.other_backtrace, s_raw_crash_data.other_backtrace_count,
|
||||
s_raw_crash_data.other_reg_frame_count);
|
||||
#endif
|
||||
return pos;
|
||||
}
|
||||
|
||||
// The record was captured by a different firmware build (it survives soft
|
||||
// resets, including the OTA reboot), so symbolizing its addresses against the
|
||||
// current ELF would produce misleading symbols. Print them with lowercase
|
||||
@@ -443,11 +432,23 @@ void crash_handler_log() {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Build addr2line hint with all captured addresses for easy copy-paste
|
||||
// Build addr2line hints for easy copy-paste. One line per core: the two
|
||||
// backtraces are separate stacks, and a combined list decodes as one
|
||||
// impossible call chain (and can overflow the buffer, dropping addresses).
|
||||
static const char *const ADDR2LINE_CMD = "addr2line -pfiaC -e firmware.elf";
|
||||
char hint[256];
|
||||
int pos = snprintf(hint, sizeof(hint), "Use: addr2line -pfiaC -e firmware.elf 0x%08" PRIX32, s_raw_crash_data.pc);
|
||||
append_all_backtraces(hint, sizeof(hint), pos);
|
||||
int pos = snprintf(hint, sizeof(hint), "Use: %s 0x%08" PRIX32, ADDR2LINE_CMD, s_raw_crash_data.pc);
|
||||
append_addrs_to_hint(hint, sizeof(hint), pos, s_raw_crash_data.backtrace, s_raw_crash_data.backtrace_count,
|
||||
s_raw_crash_data.reg_frame_count);
|
||||
ESP_LOGE(TAG, "%s", hint);
|
||||
#if SOC_CPU_CORES_NUM > 1
|
||||
if (s_raw_crash_data.other_backtrace_count > 0) {
|
||||
pos = snprintf(hint, sizeof(hint), "Other core: %s", ADDR2LINE_CMD);
|
||||
append_addrs_to_hint(hint, sizeof(hint), pos, s_raw_crash_data.other_backtrace,
|
||||
s_raw_crash_data.other_backtrace_count, s_raw_crash_data.other_reg_frame_count);
|
||||
ESP_LOGE(TAG, "%s", hint);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace esphome::esp32
|
||||
|
||||
@@ -648,6 +648,8 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa
|
||||
case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
|
||||
case ESP_GAP_BLE_PHY_UPDATE_COMPLETE_EVT: // BLE 5.0 PHY update complete
|
||||
case ESP_GAP_BLE_CHANNEL_SELECT_ALGORITHM_EVT: // BLE 5.0 channel selection algorithm
|
||||
case ESP_GAP_BLE_LOCAL_IR_EVT: // Local identity root key generated at security init
|
||||
case ESP_GAP_BLE_LOCAL_ER_EVT: // Local encryption root key generated at security init
|
||||
return;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from esphome import automation
|
||||
@@ -8,6 +10,7 @@ from esphome.components import ble_device_base, esp32_ble, ota
|
||||
from esphome.components.const import CONF_ON_SCAN_END, CONF_SCAN_PARAMETERS, CONF_WINDOW
|
||||
from esphome.components.esp32 import (
|
||||
add_idf_sdkconfig_option,
|
||||
idf_version,
|
||||
request_bluetooth,
|
||||
request_software_coexistence,
|
||||
)
|
||||
@@ -35,10 +38,12 @@ from esphome.const import (
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
from esphome.core import CORE, CoroPriority, TimePeriod, coroutine_with_priority
|
||||
from esphome.enum import StrEnum
|
||||
from esphome.types import ConfigType
|
||||
|
||||
DOMAIN = "esp32_ble_tracker"
|
||||
|
||||
AUTO_LOAD = ["ble_device_base", "esp32_ble"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
CODEOWNERS = ["@bdraco"]
|
||||
@@ -125,10 +130,71 @@ def validate_max_connections_deprecated(config: ConfigType) -> ConfigType:
|
||||
return config
|
||||
|
||||
|
||||
# ESP-IDF 5.5.5 fixed a coexistence bug on the ESP32 where BLE scans ran far
|
||||
# longer than the configured window (espressif/esp-idf#18931). Before the fix,
|
||||
# the default 30 ms window in a 320 ms interval effectively scanned at a much
|
||||
# higher duty cycle than requested; with the fix, that same default only
|
||||
# listens 9.4 % of the time and misses most advertisements when wifi shares
|
||||
# the radio. Espressif recommends setting the window equal to the interval in
|
||||
# that case: the coexistence arbiter still shares the radio with wifi, and
|
||||
# BLE uses the airtime wifi does not claim.
|
||||
IDF_SCAN_WINDOW_FIX_VERSION = cv.Version(5, 5, 5)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TrackerData:
|
||||
"""Per-run validation state, namespaced under DOMAIN in CORE.data."""
|
||||
|
||||
scan_window_defaulted: bool = False
|
||||
|
||||
|
||||
def _get_data() -> TrackerData:
|
||||
if DOMAIN not in CORE.data:
|
||||
CORE.data[DOMAIN] = TrackerData()
|
||||
return CORE.data[DOMAIN]
|
||||
|
||||
|
||||
def _scan_window_default() -> TimePeriod:
|
||||
"""Schema default for the scan window.
|
||||
|
||||
Records that the user did not set a window, so _raise_defaulted_scan_window
|
||||
can tell a defaulted 30 ms from an explicit one; the raise itself must wait
|
||||
for the outer schema because it depends on software_coexistence, a sibling
|
||||
key not yet resolved here.
|
||||
"""
|
||||
_get_data().scan_window_defaulted = True
|
||||
return cv.positive_time_period(ble_device_base.DEFAULT_SCAN_WINDOW)
|
||||
|
||||
|
||||
def _raise_defaulted_scan_window(config: ConfigType) -> ConfigType:
|
||||
"""Raise a defaulted scan window to the interval where that is safe.
|
||||
|
||||
Only when the coexistence arbiter is compiled in (software_coexistence,
|
||||
present iff wifi is configured and not disabled by the user) and the IDF
|
||||
honors the window strictly (>= 5.5.5); without the arbiter a full-duty
|
||||
scan would starve wifi outright, and a user-set window is never touched.
|
||||
Raising to the interval cannot invalidate the already-validated
|
||||
parameters, so no re-validation is needed.
|
||||
"""
|
||||
if (
|
||||
_get_data().scan_window_defaulted
|
||||
and config.get(CONF_SOFTWARE_COEXISTENCE)
|
||||
and idf_version() >= IDF_SCAN_WINDOW_FIX_VERSION
|
||||
):
|
||||
params = config[CONF_SCAN_PARAMETERS]
|
||||
# Copy so the config dump shows a plain value instead of a YAML
|
||||
# anchor/alias pair pointing at the interval.
|
||||
params[CONF_WINDOW] = copy.copy(params[CONF_INTERVAL])
|
||||
return config
|
||||
|
||||
|
||||
# 320 ms is the ESP-IDF reference scan interval; the shared schema also
|
||||
# tightens validation to the controller's 2.5 ms .. 10240 ms range and rejects
|
||||
# window/interval pairs that collapse to the same 0.625 ms unit count.
|
||||
SCAN_PARAMETERS_SCHEMA = ble_device_base.scan_parameters_schema("320ms")
|
||||
# The window default is conditional (see _scan_window_default above).
|
||||
SCAN_PARAMETERS_SCHEMA = ble_device_base.scan_parameters_schema(
|
||||
"320ms", window_default=_scan_window_default
|
||||
)
|
||||
|
||||
# Codegen helpers are owned by ble_device_base; kept under the historical names
|
||||
# here for the components that import them from this module.
|
||||
@@ -183,6 +249,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
validate_max_connections_deprecated,
|
||||
_raise_defaulted_scan_window,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ from pathlib import Path
|
||||
|
||||
from esphome import pins
|
||||
from esphome.components import esp32
|
||||
from esphome.components.const import CONF_USE_PSRAM
|
||||
from esphome.components.const import CONF_SLOT, CONF_USE_PSRAM
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_CLK_PIN,
|
||||
@@ -16,8 +16,10 @@ from esphome.const import (
|
||||
CONF_VARIANT,
|
||||
)
|
||||
from esphome.cpp_generator import add_define
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@swoboda1337"]
|
||||
DEPENDENCIES = ["esp32"]
|
||||
# esp32_ble raises the task watchdog around the remote BT controller bring-up
|
||||
AUTO_LOAD = ["watchdog"]
|
||||
|
||||
@@ -33,7 +35,6 @@ CONF_DATA_READY_PIN = "data_ready_pin"
|
||||
CONF_HANDSHAKE_ACTIVE_HIGH = "handshake_active_high"
|
||||
CONF_HANDSHAKE_PIN = "handshake_pin"
|
||||
CONF_SDIO_FREQUENCY = "sdio_frequency"
|
||||
CONF_SLOT = "slot"
|
||||
CONF_SPI_MODE = "spi_mode"
|
||||
|
||||
# Shared fields for both transport modes
|
||||
@@ -125,6 +126,22 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||
)
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
# The esp_hosted releases compatible with older ESP-IDF versions crash at
|
||||
# boot with a heap double free in the SDIO RX path (fixed in esp_hosted
|
||||
# 2.11.0, which requires ESP-IDF 5.3), so reject them at validation time.
|
||||
if (idf_ver := esp32.idf_version()) < cv.Version(5, 3, 0):
|
||||
raise cv.Invalid(
|
||||
f"esp32_hosted requires ESP-IDF 5.3 or newer, got {idf_ver}. "
|
||||
"Remove the framework version from your configuration to use the "
|
||||
"recommended version, or pin a version at or above 5.3."
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
def _configure_sdio(config):
|
||||
slot = config[CONF_SLOT]
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
@@ -252,18 +269,14 @@ async def to_code(config):
|
||||
if config[CONF_USE_PSRAM]:
|
||||
esp32.add_idf_sdkconfig_option("CONFIG_ESP_HOSTED_MEMPOOL_PREFER_SPIRAM", True)
|
||||
|
||||
# Library versions
|
||||
# Library versions; this component set requires ESP-IDF 5.3 or newer,
|
||||
# which is enforced at validation time.
|
||||
idf_ver = esp32.idf_version()
|
||||
os.environ["ESP_IDF_VERSION"] = f"{idf_ver.major}.{idf_ver.minor}"
|
||||
if idf_ver >= cv.Version(5, 5, 0):
|
||||
esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="1.6.3")
|
||||
esp32.add_idf_component(name="espressif/wifi_remote_over_eppp", ref="0.3.3")
|
||||
esp32.add_idf_component(name="espressif/eppp_link", ref="1.1.5")
|
||||
esp32.add_idf_component(name="espressif/esp_hosted", ref="2.12.12")
|
||||
else:
|
||||
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/esp_hosted", ref="2.0.11")
|
||||
esp32.add_idf_component(name="espressif/esp_wifi_remote", ref="1.6.3")
|
||||
esp32.add_idf_component(name="espressif/wifi_remote_over_eppp", ref="0.3.3")
|
||||
esp32.add_idf_component(name="espressif/eppp_link", ref="1.1.5")
|
||||
esp32.add_idf_component(name="espressif/esp_hosted", ref="2.12.12")
|
||||
esp32.add_extra_script(
|
||||
"post",
|
||||
"esp32_hosted.py",
|
||||
|
||||
@@ -113,6 +113,9 @@ def get_download_types(storage_json):
|
||||
the shape stable so the download panel
|
||||
doesn't have to special-case per-platform schemas.
|
||||
"""
|
||||
# No recorded firmware path means nothing was built; no downloads.
|
||||
if storage_json.firmware_bin_path is None:
|
||||
return []
|
||||
return [
|
||||
{
|
||||
"title": "Standard format",
|
||||
|
||||
@@ -355,7 +355,7 @@ def _validate(config):
|
||||
" clk:\n"
|
||||
" mode: %s\n"
|
||||
" pin: %s\n"
|
||||
"Removal scheduled for 2026.9.0.",
|
||||
"Removal scheduled for 2026.11.0.",
|
||||
config[CONF_CLK_MODE],
|
||||
mode,
|
||||
pin,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
namespace esphome::http_request {
|
||||
|
||||
static const char *const TAG = "http_request.arduino";
|
||||
static const char *const TAG = "http_request";
|
||||
#ifdef USE_ESP8266
|
||||
// ESP8266 Arduino core (WiFiClientSecureBearSSL.cpp) returns -1000 on OOM
|
||||
static constexpr int ESP8266_SSL_ERR_OOM = -1000;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace esphome::http_request {
|
||||
|
||||
static const char *const TAG = "http_request.host";
|
||||
static const char *const TAG = "http_request";
|
||||
|
||||
std::shared_ptr<HttpContainer> HttpRequestHost::perform(const std::string &url, const std::string &method,
|
||||
const std::string &body,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
namespace esphome::http_request {
|
||||
|
||||
static const char *const TAG = "http_request.idf";
|
||||
static const char *const TAG = "http_request";
|
||||
static constexpr uint32_t ERROR_DURATION_MS = 1000;
|
||||
|
||||
void HttpRequestIDF::dump_config() {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace esphome::i2c {
|
||||
|
||||
static const char *const TAG = "i2c.arduino";
|
||||
static const char *const TAG = "i2c";
|
||||
|
||||
// Maximum bytes to log in hex format (truncates larger transfers)
|
||||
static constexpr size_t I2C_MAX_LOG_BYTES = 32;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace esphome::i2c {
|
||||
|
||||
static const char *const TAG = "i2c.idf";
|
||||
static const char *const TAG = "i2c";
|
||||
|
||||
// Maximum bytes to log in hex format (truncates larger transfers)
|
||||
static constexpr size_t I2C_MAX_LOG_BYTES = 32;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
namespace esphome::i2c {
|
||||
|
||||
static const char *const TAG = "i2c.host";
|
||||
static const char *const TAG = "i2c";
|
||||
|
||||
HostI2CBus::~HostI2CBus() {
|
||||
if (this->file_descriptor_ != -1) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace esphome::i2c {
|
||||
|
||||
static const char *const TAG = "i2c.zephyr";
|
||||
static const char *const TAG = "i2c";
|
||||
|
||||
static const char *get_speed(uint32_t dev_config) {
|
||||
switch (I2C_SPEED_GET(dev_config)) {
|
||||
|
||||
@@ -154,8 +154,12 @@ bool Infrared::on_receive(remote_base::RemoteReceiveData data) {
|
||||
// Forward received IR data to API server
|
||||
#if defined(USE_API) && defined(USE_IR_RF)
|
||||
if (api::global_api_server != nullptr) {
|
||||
api::global_api_server->send_infrared_rf_receive_event(this->get_device_id_or_zero(), this->get_entity_key(),
|
||||
&data.get_raw_data());
|
||||
#ifdef USE_DEVICES
|
||||
uint32_t device_id = this->get_device_id();
|
||||
#else
|
||||
uint32_t device_id = 0;
|
||||
#endif
|
||||
api::global_api_server->send_infrared_rf_receive_event(device_id, this->get_object_id_hash(), &data.get_raw_data());
|
||||
}
|
||||
#endif
|
||||
return false; // Don't consume the event, allow other listeners to process it
|
||||
|
||||
@@ -9,7 +9,7 @@ uint32_t temp_single_get_current_temperature(uint32_t *temp_value);
|
||||
|
||||
namespace esphome::internal_temperature {
|
||||
|
||||
static const char *const TAG = "internal_temperature.bk72xx";
|
||||
static const char *const TAG = "internal_temperature";
|
||||
|
||||
void InternalTemperatureSensor::update() {
|
||||
float temperature = NAN;
|
||||
|
||||
@@ -16,7 +16,7 @@ uint8_t temprature_sens_read();
|
||||
|
||||
namespace esphome::internal_temperature {
|
||||
|
||||
static const char *const TAG = "internal_temperature.esp32";
|
||||
static const char *const TAG = "internal_temperature";
|
||||
|
||||
void InternalTemperatureSensor::update() {
|
||||
float temperature = NAN;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
namespace esphome::internal_temperature {
|
||||
|
||||
static const char *const TAG = "internal_temperature.rp2";
|
||||
static const char *const TAG = "internal_temperature";
|
||||
|
||||
// The on-die temperature sensor sits on the last ADC channel: input 4 on RP2040
|
||||
// and RP2350A, but input 8 on RP2350B, which has eight external channels rather
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace esphome::internal_temperature {
|
||||
|
||||
static const char *const TAG = "internal_temperature.zephyr";
|
||||
static const char *const TAG = "internal_temperature";
|
||||
|
||||
static const struct device *const DIE_TEMPERATURE_SENSOR = DEVICE_DT_GET_ONE(nordic_nrf_temp);
|
||||
|
||||
|
||||
@@ -184,8 +184,6 @@ static int32_t get_firmware_int(const char *version_string) {
|
||||
return result;
|
||||
}
|
||||
|
||||
float LD2420Component::get_setup_priority() const { return setup_priority::BUS; }
|
||||
|
||||
void LD2420Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"LD2420:\n"
|
||||
|
||||
@@ -105,7 +105,6 @@ class LD2420Component final : public Component, public uart::UARTDevice {
|
||||
void apply_config_action();
|
||||
void factory_reset_action();
|
||||
void revert_config_action();
|
||||
float get_setup_priority() const override;
|
||||
int send_cmd_from_array(CmdFrameT cmd_frame);
|
||||
void report_gate_data();
|
||||
void handle_cmd_error(uint16_t error);
|
||||
|
||||
@@ -182,6 +182,9 @@ def get_download_types(storage_json: StorageJSON = None):
|
||||
the shape stable so the download panel
|
||||
doesn't have to special-case per-platform schemas.
|
||||
"""
|
||||
# No recorded firmware path means nothing was built; no downloads.
|
||||
if storage_json.firmware_bin_path is None:
|
||||
return []
|
||||
types = [
|
||||
{
|
||||
"title": "UF2 package (recommended)",
|
||||
|
||||
@@ -444,7 +444,7 @@ LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_r
|
||||
lv_indev_set_type(this->drv_, LV_INDEV_TYPE_POINTER);
|
||||
lv_indev_set_disp(this->drv_, parent->get_disp());
|
||||
lv_indev_set_long_press_time(this->drv_, long_press_time);
|
||||
// long press repeat time TBD
|
||||
lv_indev_set_long_press_repeat_time(this->drv_, long_press_repeat_time);
|
||||
lv_indev_set_user_data(this->drv_, this);
|
||||
lv_indev_set_read_cb(this->drv_, [](lv_indev_t *d, lv_indev_data_t *data) {
|
||||
auto *l = static_cast<LVTouchListener *>(lv_indev_get_user_data(d));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user