mirror of
https://github.com/esphome/esphome.git
synced 2025-11-01 15:41:52 +00:00
Merge branch 'integration' into memory_api
This commit is contained in:
@@ -1 +1 @@
|
|||||||
ab49c22900dd39c004623e450a1076b111d6741f31967a637ab6e0e3dd2e753e
|
049d60eed541730efaa4c0dc5d337b4287bf29b6daa350b5dfc1f23915f1c52f
|
||||||
|
|||||||
@@ -19,13 +19,14 @@ namespace esphome::api {
|
|||||||
//#define HELPER_LOG_PACKETS
|
//#define HELPER_LOG_PACKETS
|
||||||
|
|
||||||
// Maximum message size limits to prevent OOM on constrained devices
|
// Maximum message size limits to prevent OOM on constrained devices
|
||||||
// Voice Assistant is our largest user at 1024 bytes per audio chunk
|
// Handshake messages are limited to a small size for security
|
||||||
// Using 2048 + 256 bytes overhead = 2304 bytes total to support voice and future needs
|
static constexpr uint16_t MAX_HANDSHAKE_SIZE = 128;
|
||||||
// ESP8266 has very limited RAM and cannot support voice assistant
|
|
||||||
|
// Data message limits vary by platform based on available memory
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
static constexpr uint16_t MAX_MESSAGE_SIZE = 512; // Keep small for memory constrained ESP8266
|
static constexpr uint16_t MAX_MESSAGE_SIZE = 8192; // 8 KiB for ESP8266
|
||||||
#else
|
#else
|
||||||
static constexpr uint16_t MAX_MESSAGE_SIZE = 2304; // Support voice (1024) + headroom for larger messages
|
static constexpr uint16_t MAX_MESSAGE_SIZE = 32768; // 32 KiB for ESP32 and other platforms
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
|
|||||||
@@ -173,18 +173,12 @@ APIError APINoiseFrameHelper::try_read_frame_() {
|
|||||||
// read body
|
// read body
|
||||||
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
|
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
|
||||||
|
|
||||||
if (state_ != State::DATA && msg_size > 128) {
|
// Check against size limits to prevent OOM: MAX_HANDSHAKE_SIZE for handshake, MAX_MESSAGE_SIZE for data
|
||||||
// for handshake message only permit up to 128 bytes
|
uint16_t limit = (state_ == State::DATA) ? MAX_MESSAGE_SIZE : MAX_HANDSHAKE_SIZE;
|
||||||
|
if (msg_size > limit) {
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Bad packet len for handshake: %d", msg_size);
|
HELPER_LOG("Bad packet: message size %u exceeds maximum %u", msg_size, limit);
|
||||||
return APIError::BAD_HANDSHAKE_PACKET_LEN;
|
return (state_ == State::DATA) ? APIError::BAD_DATA_PACKET : APIError::BAD_HANDSHAKE_PACKET_LEN;
|
||||||
}
|
|
||||||
|
|
||||||
// Check against maximum message size to prevent OOM
|
|
||||||
if (msg_size > MAX_MESSAGE_SIZE) {
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Bad packet: message size %u exceeds maximum %u", msg_size, MAX_MESSAGE_SIZE);
|
|
||||||
return APIError::BAD_DATA_PACKET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve space for body
|
// Reserve space for body
|
||||||
|
|||||||
@@ -165,4 +165,4 @@ def final_validate_audio_schema(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_library("esphome/esp-audio-libs", "1.1.4")
|
cg.add_library("esphome/esp-audio-libs", "2.0.1")
|
||||||
|
|||||||
@@ -229,18 +229,18 @@ FileDecoderState AudioDecoder::decode_flac_() {
|
|||||||
auto result = this->flac_decoder_->read_header(this->input_transfer_buffer_->get_buffer_start(),
|
auto result = this->flac_decoder_->read_header(this->input_transfer_buffer_->get_buffer_start(),
|
||||||
this->input_transfer_buffer_->available());
|
this->input_transfer_buffer_->available());
|
||||||
|
|
||||||
if (result == esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
|
if (result > esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
|
||||||
return FileDecoderState::POTENTIALLY_FAILED;
|
// Serrious error reading FLAC header, there is no recovery
|
||||||
}
|
|
||||||
|
|
||||||
if (result != esp_audio_libs::flac::FLAC_DECODER_SUCCESS) {
|
|
||||||
// Couldn't read FLAC header
|
|
||||||
return FileDecoderState::FAILED;
|
return FileDecoderState::FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
|
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
|
||||||
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
|
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
|
||||||
|
|
||||||
|
if (result == esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
|
||||||
|
return FileDecoderState::MORE_TO_PROCESS;
|
||||||
|
}
|
||||||
|
|
||||||
// Reallocate the output transfer buffer to the smallest necessary size
|
// Reallocate the output transfer buffer to the smallest necessary size
|
||||||
this->free_buffer_required_ = flac_decoder_->get_output_buffer_size_bytes();
|
this->free_buffer_required_ = flac_decoder_->get_output_buffer_size_bytes();
|
||||||
if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) {
|
if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) {
|
||||||
@@ -256,9 +256,9 @@ FileDecoderState AudioDecoder::decode_flac_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t output_samples = 0;
|
uint32_t output_samples = 0;
|
||||||
auto result = this->flac_decoder_->decode_frame(
|
auto result = this->flac_decoder_->decode_frame(this->input_transfer_buffer_->get_buffer_start(),
|
||||||
this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available(),
|
this->input_transfer_buffer_->available(),
|
||||||
reinterpret_cast<int16_t *>(this->output_transfer_buffer_->get_buffer_end()), &output_samples);
|
this->output_transfer_buffer_->get_buffer_end(), &output_samples);
|
||||||
|
|
||||||
if (result == esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) {
|
if (result == esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) {
|
||||||
// Not an issue, just needs more data that we'll get next time.
|
// Not an issue, just needs more data that we'll get next time.
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
def mdns_txt_record(key: str, value: str):
|
def mdns_txt_record(key: str, value: str):
|
||||||
return cg.StructInitializer(
|
return cg.StructInitializer(
|
||||||
MDNSTXTRecord,
|
MDNSTXTRecord,
|
||||||
("key", key),
|
("key", cg.RawExpression(f"MDNS_STR({cg.safe_exp(key)})")),
|
||||||
("value", value),
|
("value", value),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -71,8 +71,8 @@ def mdns_service(
|
|||||||
):
|
):
|
||||||
return cg.StructInitializer(
|
return cg.StructInitializer(
|
||||||
MDNSService,
|
MDNSService,
|
||||||
("service_type", service),
|
("service_type", cg.RawExpression(f"MDNS_STR({cg.safe_exp(service)})")),
|
||||||
("proto", proto),
|
("proto", cg.RawExpression(f"MDNS_STR({cg.safe_exp(proto)})")),
|
||||||
("port", port),
|
("port", port),
|
||||||
("txt_records", txt_records),
|
("txt_records", txt_records),
|
||||||
)
|
)
|
||||||
@@ -114,7 +114,7 @@ async def to_code(config):
|
|||||||
txt = [
|
txt = [
|
||||||
cg.StructInitializer(
|
cg.StructInitializer(
|
||||||
MDNSTXTRecord,
|
MDNSTXTRecord,
|
||||||
("key", txt_key),
|
("key", cg.RawExpression(f"MDNS_STR({cg.safe_exp(txt_key)})")),
|
||||||
("value", await cg.templatable(txt_value, [], cg.std_string)),
|
("value", await cg.templatable(txt_value, [], cg.std_string)),
|
||||||
)
|
)
|
||||||
for txt_key, txt_value in service[CONF_TXT].items()
|
for txt_key, txt_value in service[CONF_TXT].items()
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
// Macro to define strings in PROGMEM on ESP8266, regular memory on other platforms
|
// Macro to define strings in PROGMEM on ESP8266, regular memory on other platforms
|
||||||
#define MDNS_STATIC_CONST_CHAR(name, value) static const char name[] PROGMEM = value
|
#define MDNS_STATIC_CONST_CHAR(name, value) static const char name[] PROGMEM = value
|
||||||
#define MDNS_STR(name) (reinterpret_cast<const MDNSString *>(name))
|
|
||||||
// Helper to convert PROGMEM string to std::string for TemplatableValue
|
// Helper to convert PROGMEM string to std::string for TemplatableValue
|
||||||
// Only define this function if we have services that will use it
|
// Only define this function if we have services that will use it
|
||||||
#if defined(USE_API) || defined(USE_PROMETHEUS) || defined(USE_WEBSERVER) || defined(USE_MDNS_EXTRA_SERVICES)
|
#if defined(USE_API) || defined(USE_PROMETHEUS) || defined(USE_WEBSERVER) || defined(USE_MDNS_EXTRA_SERVICES)
|
||||||
@@ -24,7 +23,6 @@ static std::string mdns_str_value(PGM_P str) {
|
|||||||
#else
|
#else
|
||||||
// On non-ESP8266 platforms, use regular const char*
|
// On non-ESP8266 platforms, use regular const char*
|
||||||
#define MDNS_STATIC_CONST_CHAR(name, value) static constexpr const char name[] = value
|
#define MDNS_STATIC_CONST_CHAR(name, value) static constexpr const char name[] = value
|
||||||
#define MDNS_STR(name) (reinterpret_cast<const MDNSString *>(name))
|
|
||||||
#define MDNS_STR_VALUE(name) std::string(name)
|
#define MDNS_STR_VALUE(name) std::string(name)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ namespace mdns {
|
|||||||
// Helper struct that identifies strings that may be stored in flash storage (similar to LogString)
|
// Helper struct that identifies strings that may be stored in flash storage (similar to LogString)
|
||||||
struct MDNSString;
|
struct MDNSString;
|
||||||
|
|
||||||
|
// Macro to cast string literals to MDNSString* (works on all platforms)
|
||||||
|
#define MDNS_STR(name) (reinterpret_cast<const esphome::mdns::MDNSString *>(name))
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#include <pgmspace.h>
|
#include <pgmspace.h>
|
||||||
#define MDNS_STR_ARG(s) ((PGM_P) (s))
|
#define MDNS_STR_ARG(s) ((PGM_P) (s))
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ lib_deps =
|
|||||||
makuna/NeoPixelBus@2.8.0 ; neopixelbus
|
makuna/NeoPixelBus@2.8.0 ; neopixelbus
|
||||||
esphome/ESP32-audioI2S@2.3.0 ; i2s_audio
|
esphome/ESP32-audioI2S@2.3.0 ; i2s_audio
|
||||||
droscy/esp_wireguard@0.4.2 ; wireguard
|
droscy/esp_wireguard@0.4.2 ; wireguard
|
||||||
esphome/esp-audio-libs@1.1.4 ; audio
|
esphome/esp-audio-libs@2.0.1 ; audio
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:arduino.build_flags}
|
${common:arduino.build_flags}
|
||||||
@@ -170,7 +170,7 @@ lib_deps =
|
|||||||
${common:idf.lib_deps}
|
${common:idf.lib_deps}
|
||||||
droscy/esp_wireguard@0.4.2 ; wireguard
|
droscy/esp_wireguard@0.4.2 ; wireguard
|
||||||
kahrendt/ESPMicroSpeechFeatures@1.1.0 ; micro_wake_word
|
kahrendt/ESPMicroSpeechFeatures@1.1.0 ; micro_wake_word
|
||||||
esphome/esp-audio-libs@1.1.4 ; audio
|
esphome/esp-audio-libs@2.0.1 ; audio
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:idf.build_flags}
|
${common:idf.build_flags}
|
||||||
-Wno-nonnull-compare
|
-Wno-nonnull-compare
|
||||||
|
|||||||
@@ -161,8 +161,8 @@ async def test_oversized_payload_noise(
|
|||||||
assert device_info is not None
|
assert device_info is not None
|
||||||
assert device_info.name == "oversized-noise"
|
assert device_info.name == "oversized-noise"
|
||||||
|
|
||||||
# Create an oversized payload (>2304 bytes which is our new limit)
|
# Create an oversized payload (>32Kbytes which is our new limit)
|
||||||
oversized_data = b"Y" * 3000 # ~3KiB, exceeds the 2304 byte limit
|
oversized_data = b"Y" * 32769 # ~32KiB, exceeds the 32 Kbyte limit
|
||||||
|
|
||||||
# Access the internal connection to send raw data
|
# Access the internal connection to send raw data
|
||||||
frame_helper = client._connection._frame_helper
|
frame_helper = client._connection._frame_helper
|
||||||
|
|||||||
Reference in New Issue
Block a user