Merge branch 'dev' into esp32_touch_new_driver

This commit is contained in:
Jonathan Swoboda
2026-02-21 08:51:51 -05:00
committed by GitHub
134 changed files with 2278 additions and 1155 deletions

View File

@@ -1 +1 @@
ce05c28e9dc0b12c4f6e7454986ffea5123ac974a949da841be698c535f2083e
5eb1e5852765114ad06533220d3160b6c23f5ccefc4de41828699de5dfff5ad6

View File

@@ -58,7 +58,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -86,6 +86,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4
with:
category: "/language:${{matrix.language}}"

View File

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

View File

@@ -347,9 +347,7 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
#endif
// Calculate size
ProtoSize size_calc;
msg.calculate_size(size_calc);
uint32_t calculated_size = size_calc.get_size();
uint32_t calculated_size = msg.calculated_size();
// Cache frame sizes to avoid repeated virtual calls
const uint8_t header_padding = conn->helper_->frame_header_padding();
@@ -377,19 +375,14 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
shared_buf.resize(current_size + footer_size + header_padding);
}
// Encode directly into buffer
size_t size_before_encode = shared_buf.size();
msg.encode({&shared_buf});
// Pre-resize buffer to include payload, then encode through raw pointer
size_t write_start = shared_buf.size();
shared_buf.resize(write_start + calculated_size);
ProtoWriteBuffer buffer{&shared_buf, write_start};
msg.encode(buffer);
// Calculate actual encoded size (not including header that was already added)
size_t actual_payload_size = shared_buf.size() - size_before_encode;
// Return actual total size (header + actual payload + footer)
size_t actual_total_size = header_padding + actual_payload_size + footer_size;
// Verify that calculate_size() returned the correct value
assert(calculated_size == actual_payload_size);
return static_cast<uint16_t>(actual_total_size);
// Return total size (header + payload + footer)
return static_cast<uint16_t>(header_padding + calculated_size + footer_size);
}
#ifdef USE_BINARY_SENSOR
@@ -1534,6 +1527,12 @@ bool APIConnection::send_hello_response_(const HelloRequest &msg) {
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu16 ".%" PRIu16, this->helper_->get_client_name(),
this->helper_->get_peername_to(peername), this->client_api_version_major_, this->client_api_version_minor_);
// TODO: Remove before 2026.8.0 (one version after get_object_id backward compat removal)
if (!this->client_supports_api_version(1, 14)) {
ESP_LOGW(TAG, "'%s' using outdated API %" PRIu16 ".%" PRIu16 ", update to 1.14+", this->helper_->get_client_name(),
this->client_api_version_major_, this->client_api_version_minor_);
}
HelloResponse resp;
resp.api_version_major = 1;
resp.api_version_minor = 14;
@@ -1848,12 +1847,14 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
return false;
}
bool APIConnection::send_message_impl(const ProtoMessage &msg, uint8_t message_type) {
ProtoSize size;
msg.calculate_size(size);
uint32_t payload_size = msg.calculated_size();
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
this->prepare_first_message_buffer(shared_buf, size.get_size());
msg.encode({&shared_buf});
return this->send_buffer({&shared_buf}, message_type);
this->prepare_first_message_buffer(shared_buf, payload_size);
size_t write_start = shared_buf.size();
shared_buf.resize(write_start + payload_size);
ProtoWriteBuffer buffer{&shared_buf, write_start};
msg.encode(buffer);
return this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type);
}
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
const bool is_log_message = (message_type == SubscribeLogsResponse::MESSAGE_TYPE);

View File

@@ -19,7 +19,7 @@ namespace esphome::api {
static const char *const TAG = "api.noise";
#ifdef USE_ESP8266
static const char PROLOGUE_INIT[] PROGMEM = "NoiseAPIInit";
static constexpr char PROLOGUE_INIT[] PROGMEM = "NoiseAPIInit";
#else
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
#endif

View File

@@ -31,7 +31,7 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value)
}
return true;
}
void HelloResponse::encode(ProtoWriteBuffer buffer) const {
void HelloResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, this->api_version_major);
buffer.encode_uint32(2, this->api_version_minor);
buffer.encode_string(3, this->server_info);
@@ -44,7 +44,7 @@ void HelloResponse::calculate_size(ProtoSize &size) const {
size.add_length(1, this->name.size());
}
#ifdef USE_AREAS
void AreaInfo::encode(ProtoWriteBuffer buffer) const {
void AreaInfo::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, this->area_id);
buffer.encode_string(2, this->name);
}
@@ -54,7 +54,7 @@ void AreaInfo::calculate_size(ProtoSize &size) const {
}
#endif
#ifdef USE_DEVICES
void DeviceInfo::encode(ProtoWriteBuffer buffer) const {
void DeviceInfo::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, this->device_id);
buffer.encode_string(2, this->name);
buffer.encode_uint32(3, this->area_id);
@@ -65,7 +65,7 @@ void DeviceInfo::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->area_id);
}
#endif
void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
void DeviceInfoResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(2, this->name);
buffer.encode_string(3, this->mac_address);
buffer.encode_string(4, this->esphome_version);
@@ -111,7 +111,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
}
#endif
#ifdef USE_AREAS
buffer.encode_message(22, this->area);
buffer.encode_message(22, this->area, false);
#endif
#ifdef USE_ZWAVE_PROXY
buffer.encode_uint32(23, this->zwave_proxy_feature_flags);
@@ -176,7 +176,7 @@ void DeviceInfoResponse::calculate_size(ProtoSize &size) const {
#endif
}
#ifdef USE_BINARY_SENSOR
void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -206,7 +206,7 @@ void ListEntitiesBinarySensorResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
void BinarySensorStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state);
buffer.encode_bool(3, this->missing_state);
@@ -224,7 +224,7 @@ void BinarySensorStateResponse::calculate_size(ProtoSize &size) const {
}
#endif
#ifdef USE_COVER
void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesCoverResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -260,7 +260,7 @@ void ListEntitiesCoverResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
void CoverStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_float(3, this->position);
buffer.encode_float(4, this->tilt);
@@ -317,7 +317,7 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_FAN
void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesFanResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -359,7 +359,7 @@ void ListEntitiesFanResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
void FanStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state);
buffer.encode_bool(3, this->oscillating);
@@ -443,7 +443,7 @@ bool FanCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_LIGHT
void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesLightResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -489,7 +489,7 @@ void ListEntitiesLightResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(2, this->device_id);
#endif
}
void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
void LightStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state);
buffer.encode_float(3, this->brightness);
@@ -635,7 +635,7 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_SENSOR
void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesSensorResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -671,7 +671,7 @@ void ListEntitiesSensorResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
void SensorStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_float(2, this->state);
buffer.encode_bool(3, this->missing_state);
@@ -689,7 +689,7 @@ void SensorStateResponse::calculate_size(ProtoSize &size) const {
}
#endif
#ifdef USE_SWITCH
void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -719,7 +719,7 @@ void ListEntitiesSwitchResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const {
void SwitchStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state);
#ifdef USE_DEVICES
@@ -760,7 +760,7 @@ bool SwitchCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_TEXT_SENSOR
void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -788,7 +788,7 @@ void ListEntitiesTextSensorResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
void TextSensorStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_string(2, this->state);
buffer.encode_bool(3, this->missing_state);
@@ -818,7 +818,7 @@ bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
}
return true;
}
void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
void SubscribeLogsResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, static_cast<uint32_t>(this->level));
buffer.encode_bytes(3, this->message_ptr_, this->message_len_);
}
@@ -839,11 +839,11 @@ bool NoiseEncryptionSetKeyRequest::decode_length(uint32_t field_id, ProtoLengthD
}
return true;
}
void NoiseEncryptionSetKeyResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); }
void NoiseEncryptionSetKeyResponse::encode(ProtoWriteBuffer &buffer) const { buffer.encode_bool(1, this->success); }
void NoiseEncryptionSetKeyResponse::calculate_size(ProtoSize &size) const { size.add_bool(1, this->success); }
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
void HomeassistantServiceMap::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->key);
buffer.encode_string(2, this->value);
}
@@ -851,7 +851,7 @@ void HomeassistantServiceMap::calculate_size(ProtoSize &size) const {
size.add_length(1, this->key.size());
size.add_length(1, this->value.size());
}
void HomeassistantActionRequest::encode(ProtoWriteBuffer buffer) const {
void HomeassistantActionRequest::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->service);
for (auto &it : this->data) {
buffer.encode_message(2, it);
@@ -924,7 +924,7 @@ bool HomeassistantActionResponse::decode_length(uint32_t field_id, ProtoLengthDe
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->entity_id);
buffer.encode_string(2, this->attribute);
buffer.encode_bool(3, this->once);
@@ -976,7 +976,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
return true;
}
#ifdef USE_API_USER_DEFINED_ACTIONS
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->name);
buffer.encode_uint32(2, static_cast<uint32_t>(this->type));
}
@@ -984,7 +984,7 @@ void ListEntitiesServicesArgument::calculate_size(ProtoSize &size) const {
size.add_length(1, this->name.size());
size.add_uint32(1, static_cast<uint32_t>(this->type));
}
void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesServicesResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->name);
buffer.encode_fixed32(2, this->key);
for (auto &it : this->args) {
@@ -1103,7 +1103,7 @@ void ExecuteServiceRequest::decode(const uint8_t *buffer, size_t length) {
}
#endif
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
void ExecuteServiceResponse::encode(ProtoWriteBuffer buffer) const {
void ExecuteServiceResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, this->call_id);
buffer.encode_bool(2, this->success);
buffer.encode_string(3, this->error_message);
@@ -1121,7 +1121,7 @@ void ExecuteServiceResponse::calculate_size(ProtoSize &size) const {
}
#endif
#ifdef USE_CAMERA
void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesCameraResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1147,7 +1147,7 @@ void ListEntitiesCameraResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
void CameraImageResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bytes(2, this->data_ptr_, this->data_len_);
buffer.encode_bool(3, this->done);
@@ -1178,7 +1178,7 @@ bool CameraImageRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
}
#endif
#ifdef USE_CLIMATE
void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesClimateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1276,7 +1276,7 @@ void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const {
#endif
size.add_uint32(2, this->feature_flags);
}
void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
void ClimateStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_uint32(2, static_cast<uint32_t>(this->mode));
buffer.encode_float(3, this->current_temperature);
@@ -1407,7 +1407,7 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_WATER_HEATER
void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesWaterHeaterResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1449,7 +1449,7 @@ void ListEntitiesWaterHeaterResponse::calculate_size(ProtoSize &size) const {
}
size.add_uint32(1, this->supported_features);
}
void WaterHeaterStateResponse::encode(ProtoWriteBuffer buffer) const {
void WaterHeaterStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_float(2, this->current_temperature);
buffer.encode_float(3, this->target_temperature);
@@ -1515,7 +1515,7 @@ bool WaterHeaterCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value
}
#endif
#ifdef USE_NUMBER
void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesNumberResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1553,7 +1553,7 @@ void ListEntitiesNumberResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
void NumberStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_float(2, this->state);
buffer.encode_bool(3, this->missing_state);
@@ -1596,7 +1596,7 @@ bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_SELECT
void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesSelectResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1630,7 +1630,7 @@ void ListEntitiesSelectResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void SelectStateResponse::encode(ProtoWriteBuffer buffer) const {
void SelectStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_string(2, this->state);
buffer.encode_bool(3, this->missing_state);
@@ -1681,7 +1681,7 @@ bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_SIREN
void ListEntitiesSirenResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesSirenResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1719,7 +1719,7 @@ void ListEntitiesSirenResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void SirenStateResponse::encode(ProtoWriteBuffer buffer) const {
void SirenStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->state);
#ifdef USE_DEVICES
@@ -1789,7 +1789,7 @@ bool SirenCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_LOCK
void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesLockResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1823,7 +1823,7 @@ void ListEntitiesLockResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void LockStateResponse::encode(ProtoWriteBuffer buffer) const {
void LockStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_uint32(2, static_cast<uint32_t>(this->state));
#ifdef USE_DEVICES
@@ -1878,7 +1878,7 @@ bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_BUTTON
void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesButtonResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1930,7 +1930,7 @@ bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_MEDIA_PLAYER
void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const {
void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->format);
buffer.encode_uint32(2, this->sample_rate);
buffer.encode_uint32(3, this->num_channels);
@@ -1944,7 +1944,7 @@ void MediaPlayerSupportedFormat::calculate_size(ProtoSize &size) const {
size.add_uint32(1, static_cast<uint32_t>(this->purpose));
size.add_uint32(1, this->sample_bytes);
}
void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -1978,7 +1978,7 @@ void ListEntitiesMediaPlayerResponse::calculate_size(ProtoSize &size) const {
#endif
size.add_uint32(1, this->feature_flags);
}
void MediaPlayerStateResponse::encode(ProtoWriteBuffer buffer) const {
void MediaPlayerStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_uint32(2, static_cast<uint32_t>(this->state));
buffer.encode_float(3, this->volume);
@@ -2062,7 +2062,7 @@ bool SubscribeBluetoothLEAdvertisementsRequest::decode_varint(uint32_t field_id,
}
return true;
}
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const {
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_sint32(2, this->rssi);
buffer.encode_uint32(3, this->address_type);
@@ -2074,7 +2074,7 @@ void BluetoothLERawAdvertisement::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->address_type);
size.add_length(1, this->data_len);
}
void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer &buffer) const {
for (uint16_t i = 0; i < this->advertisements_len; i++) {
buffer.encode_message(1, this->advertisements[i]);
}
@@ -2103,7 +2103,7 @@ bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
}
return true;
}
void BluetoothDeviceConnectionResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothDeviceConnectionResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_bool(2, this->connected);
buffer.encode_uint32(3, this->mtu);
@@ -2125,7 +2125,7 @@ bool BluetoothGATTGetServicesRequest::decode_varint(uint32_t field_id, ProtoVarI
}
return true;
}
void BluetoothGATTDescriptor::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTDescriptor::encode(ProtoWriteBuffer &buffer) const {
if (this->uuid[0] != 0 || this->uuid[1] != 0) {
buffer.encode_uint64(1, this->uuid[0], true);
buffer.encode_uint64(1, this->uuid[1], true);
@@ -2141,7 +2141,7 @@ void BluetoothGATTDescriptor::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->handle);
size.add_uint32(1, this->short_uuid);
}
void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer &buffer) const {
if (this->uuid[0] != 0 || this->uuid[1] != 0) {
buffer.encode_uint64(1, this->uuid[0], true);
buffer.encode_uint64(1, this->uuid[1], true);
@@ -2163,7 +2163,7 @@ void BluetoothGATTCharacteristic::calculate_size(ProtoSize &size) const {
size.add_repeated_message(1, this->descriptors);
size.add_uint32(1, this->short_uuid);
}
void BluetoothGATTService::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTService::encode(ProtoWriteBuffer &buffer) const {
if (this->uuid[0] != 0 || this->uuid[1] != 0) {
buffer.encode_uint64(1, this->uuid[0], true);
buffer.encode_uint64(1, this->uuid[1], true);
@@ -2183,7 +2183,7 @@ void BluetoothGATTService::calculate_size(ProtoSize &size) const {
size.add_repeated_message(1, this->characteristics);
size.add_uint32(1, this->short_uuid);
}
void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
for (auto &it : this->services) {
buffer.encode_message(2, it);
@@ -2193,7 +2193,7 @@ void BluetoothGATTGetServicesResponse::calculate_size(ProtoSize &size) const {
size.add_uint64(1, this->address);
size.add_repeated_message(1, this->services);
}
void BluetoothGATTGetServicesDoneResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTGetServicesDoneResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
}
void BluetoothGATTGetServicesDoneResponse::calculate_size(ProtoSize &size) const { size.add_uint64(1, this->address); }
@@ -2210,7 +2210,7 @@ bool BluetoothGATTReadRequest::decode_varint(uint32_t field_id, ProtoVarInt valu
}
return true;
}
void BluetoothGATTReadResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTReadResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_uint32(2, this->handle);
buffer.encode_bytes(3, this->data_ptr_, this->data_len_);
@@ -2302,7 +2302,7 @@ bool BluetoothGATTNotifyRequest::decode_varint(uint32_t field_id, ProtoVarInt va
}
return true;
}
void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_uint32(2, this->handle);
buffer.encode_bytes(3, this->data_ptr_, this->data_len_);
@@ -2312,7 +2312,7 @@ void BluetoothGATTNotifyDataResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->handle);
size.add_length(1, this->data_len_);
}
void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, this->free);
buffer.encode_uint32(2, this->limit);
for (const auto &it : this->allocated) {
@@ -2330,7 +2330,7 @@ void BluetoothConnectionsFreeResponse::calculate_size(ProtoSize &size) const {
}
}
}
void BluetoothGATTErrorResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTErrorResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_uint32(2, this->handle);
buffer.encode_int32(3, this->error);
@@ -2340,7 +2340,7 @@ void BluetoothGATTErrorResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->handle);
size.add_int32(1, this->error);
}
void BluetoothGATTWriteResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTWriteResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_uint32(2, this->handle);
}
@@ -2348,7 +2348,7 @@ void BluetoothGATTWriteResponse::calculate_size(ProtoSize &size) const {
size.add_uint64(1, this->address);
size.add_uint32(1, this->handle);
}
void BluetoothGATTNotifyResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothGATTNotifyResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_uint32(2, this->handle);
}
@@ -2356,7 +2356,7 @@ void BluetoothGATTNotifyResponse::calculate_size(ProtoSize &size) const {
size.add_uint64(1, this->address);
size.add_uint32(1, this->handle);
}
void BluetoothDevicePairingResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothDevicePairingResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_bool(2, this->paired);
buffer.encode_int32(3, this->error);
@@ -2366,7 +2366,7 @@ void BluetoothDevicePairingResponse::calculate_size(ProtoSize &size) const {
size.add_bool(1, this->paired);
size.add_int32(1, this->error);
}
void BluetoothDeviceUnpairingResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothDeviceUnpairingResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_bool(2, this->success);
buffer.encode_int32(3, this->error);
@@ -2376,7 +2376,7 @@ void BluetoothDeviceUnpairingResponse::calculate_size(ProtoSize &size) const {
size.add_bool(1, this->success);
size.add_int32(1, this->error);
}
void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_bool(2, this->success);
buffer.encode_int32(3, this->error);
@@ -2386,7 +2386,7 @@ void BluetoothDeviceClearCacheResponse::calculate_size(ProtoSize &size) const {
size.add_bool(1, this->success);
size.add_int32(1, this->error);
}
void BluetoothScannerStateResponse::encode(ProtoWriteBuffer buffer) const {
void BluetoothScannerStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, static_cast<uint32_t>(this->state));
buffer.encode_uint32(2, static_cast<uint32_t>(this->mode));
buffer.encode_uint32(3, static_cast<uint32_t>(this->configured_mode));
@@ -2421,7 +2421,7 @@ bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarIn
}
return true;
}
void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer buffer) const {
void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, this->noise_suppression_level);
buffer.encode_uint32(2, this->auto_gain);
buffer.encode_float(3, this->volume_multiplier);
@@ -2431,11 +2431,11 @@ void VoiceAssistantAudioSettings::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->auto_gain);
size.add_float(1, this->volume_multiplier);
}
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
void VoiceAssistantRequest::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_bool(1, this->start);
buffer.encode_string(2, this->conversation_id);
buffer.encode_uint32(3, this->flags);
buffer.encode_message(4, this->audio_settings);
buffer.encode_message(4, this->audio_settings, false);
buffer.encode_string(5, this->wake_word_phrase);
}
void VoiceAssistantRequest::calculate_size(ProtoSize &size) const {
@@ -2516,7 +2516,7 @@ bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited
}
return true;
}
void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const {
void VoiceAssistantAudio::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_bytes(1, this->data, this->data_len);
buffer.encode_bool(2, this->end);
}
@@ -2587,9 +2587,9 @@ bool VoiceAssistantAnnounceRequest::decode_length(uint32_t field_id, ProtoLength
}
return true;
}
void VoiceAssistantAnnounceFinished::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); }
void VoiceAssistantAnnounceFinished::encode(ProtoWriteBuffer &buffer) const { buffer.encode_bool(1, this->success); }
void VoiceAssistantAnnounceFinished::calculate_size(ProtoSize &size) const { size.add_bool(1, this->success); }
void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const {
void VoiceAssistantWakeWord::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->id);
buffer.encode_string(2, this->wake_word);
for (auto &it : this->trained_languages) {
@@ -2656,7 +2656,7 @@ bool VoiceAssistantConfigurationRequest::decode_length(uint32_t field_id, ProtoL
}
return true;
}
void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const {
void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer &buffer) const {
for (auto &it : this->available_wake_words) {
buffer.encode_message(1, it);
}
@@ -2686,7 +2686,7 @@ bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengt
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -2718,7 +2718,7 @@ void ListEntitiesAlarmControlPanelResponse::calculate_size(ProtoSize &size) cons
size.add_uint32(1, this->device_id);
#endif
}
void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer buffer) const {
void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_uint32(2, static_cast<uint32_t>(this->state));
#ifdef USE_DEVICES
@@ -2770,7 +2770,7 @@ bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit
}
#endif
#ifdef USE_TEXT
void ListEntitiesTextResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesTextResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -2804,7 +2804,7 @@ void ListEntitiesTextResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void TextStateResponse::encode(ProtoWriteBuffer buffer) const {
void TextStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_string(2, this->state);
buffer.encode_bool(3, this->missing_state);
@@ -2855,7 +2855,7 @@ bool TextCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_DATETIME_DATE
void ListEntitiesDateResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesDateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -2881,7 +2881,7 @@ void ListEntitiesDateResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void DateStateResponse::encode(ProtoWriteBuffer buffer) const {
void DateStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->missing_state);
buffer.encode_uint32(3, this->year);
@@ -2934,7 +2934,7 @@ bool DateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_DATETIME_TIME
void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesTimeResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -2960,7 +2960,7 @@ void ListEntitiesTimeResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void TimeStateResponse::encode(ProtoWriteBuffer buffer) const {
void TimeStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->missing_state);
buffer.encode_uint32(3, this->hour);
@@ -3013,7 +3013,7 @@ bool TimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_EVENT
void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesEventResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -3049,7 +3049,7 @@ void ListEntitiesEventResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void EventResponse::encode(ProtoWriteBuffer buffer) const {
void EventResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_string(2, this->event_type);
#ifdef USE_DEVICES
@@ -3065,7 +3065,7 @@ void EventResponse::calculate_size(ProtoSize &size) const {
}
#endif
#ifdef USE_VALVE
void ListEntitiesValveResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesValveResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -3099,7 +3099,7 @@ void ListEntitiesValveResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void ValveStateResponse::encode(ProtoWriteBuffer buffer) const {
void ValveStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_float(2, this->position);
buffer.encode_uint32(3, static_cast<uint32_t>(this->current_operation));
@@ -3148,7 +3148,7 @@ bool ValveCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_DATETIME_DATETIME
void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -3174,7 +3174,7 @@ void ListEntitiesDateTimeResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void DateTimeStateResponse::encode(ProtoWriteBuffer buffer) const {
void DateTimeStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->missing_state);
buffer.encode_fixed32(3, this->epoch_seconds);
@@ -3217,7 +3217,7 @@ bool DateTimeCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_UPDATE
void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -3245,7 +3245,7 @@ void ListEntitiesUpdateResponse::calculate_size(ProtoSize &size) const {
size.add_uint32(1, this->device_id);
#endif
}
void UpdateStateResponse::encode(ProtoWriteBuffer buffer) const {
void UpdateStateResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bool(2, this->missing_state);
buffer.encode_bool(3, this->in_progress);
@@ -3314,7 +3314,7 @@ bool ZWaveProxyFrame::decode_length(uint32_t field_id, ProtoLengthDelimited valu
}
return true;
}
void ZWaveProxyFrame::encode(ProtoWriteBuffer buffer) const { buffer.encode_bytes(1, this->data, this->data_len); }
void ZWaveProxyFrame::encode(ProtoWriteBuffer &buffer) const { buffer.encode_bytes(1, this->data, this->data_len); }
void ZWaveProxyFrame::calculate_size(ProtoSize &size) const { size.add_length(1, this->data_len); }
bool ZWaveProxyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
@@ -3338,7 +3338,7 @@ bool ZWaveProxyRequest::decode_length(uint32_t field_id, ProtoLengthDelimited va
}
return true;
}
void ZWaveProxyRequest::encode(ProtoWriteBuffer buffer) const {
void ZWaveProxyRequest::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, static_cast<uint32_t>(this->type));
buffer.encode_bytes(2, this->data, this->data_len);
}
@@ -3348,7 +3348,7 @@ void ZWaveProxyRequest::calculate_size(ProtoSize &size) const {
}
#endif
#ifdef USE_INFRARED
void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer buffer) const {
void ListEntitiesInfraredResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
@@ -3419,7 +3419,7 @@ bool InfraredRFTransmitRawTimingsRequest::decode_32bit(uint32_t field_id, Proto3
}
return true;
}
void InfraredRFReceiveEvent::encode(ProtoWriteBuffer buffer) const {
void InfraredRFReceiveEvent::encode(ProtoWriteBuffer &buffer) const {
#ifdef USE_DEVICES
buffer.encode_uint32(1, this->device_id);
#endif

View File

@@ -382,7 +382,7 @@ class HelloResponse final : public ProtoMessage {
uint32_t api_version_minor{0};
StringRef server_info{};
StringRef name{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -447,7 +447,7 @@ class AreaInfo final : public ProtoMessage {
public:
uint32_t area_id{0};
StringRef name{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -462,7 +462,7 @@ class DeviceInfo final : public ProtoMessage {
uint32_t device_id{0};
StringRef name{};
uint32_t area_id{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -527,7 +527,7 @@ class DeviceInfoResponse final : public ProtoMessage {
#ifdef USE_ZWAVE_PROXY
uint32_t zwave_home_id{0};
#endif
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -558,7 +558,7 @@ class ListEntitiesBinarySensorResponse final : public InfoResponseProtoMessage {
#endif
StringRef device_class{};
bool is_status_binary_sensor{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -575,7 +575,7 @@ class BinarySensorStateResponse final : public StateResponseProtoMessage {
#endif
bool state{false};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -597,7 +597,7 @@ class ListEntitiesCoverResponse final : public InfoResponseProtoMessage {
bool supports_tilt{false};
StringRef device_class{};
bool supports_stop{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -615,7 +615,7 @@ class CoverStateResponse final : public StateResponseProtoMessage {
float position{0.0f};
float tilt{0.0f};
enums::CoverOperation current_operation{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -657,7 +657,7 @@ class ListEntitiesFanResponse final : public InfoResponseProtoMessage {
bool supports_direction{false};
int32_t supported_speed_count{0};
const std::vector<const char *> *supported_preset_modes{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -677,7 +677,7 @@ class FanStateResponse final : public StateResponseProtoMessage {
enums::FanDirection direction{};
int32_t speed_level{0};
StringRef preset_mode{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -724,7 +724,7 @@ class ListEntitiesLightResponse final : public InfoResponseProtoMessage {
float min_mireds{0.0f};
float max_mireds{0.0f};
const FixedVector<const char *> *effects{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -751,7 +751,7 @@ class LightStateResponse final : public StateResponseProtoMessage {
float cold_white{0.0f};
float warm_white{0.0f};
StringRef effect{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -815,7 +815,7 @@ class ListEntitiesSensorResponse final : public InfoResponseProtoMessage {
bool force_update{false};
StringRef device_class{};
enums::SensorStateClass state_class{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -832,7 +832,7 @@ class SensorStateResponse final : public StateResponseProtoMessage {
#endif
float state{0.0f};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -851,7 +851,7 @@ class ListEntitiesSwitchResponse final : public InfoResponseProtoMessage {
#endif
bool assumed_state{false};
StringRef device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -867,7 +867,7 @@ class SwitchStateResponse final : public StateResponseProtoMessage {
const char *message_name() const override { return "switch_state_response"; }
#endif
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -901,7 +901,7 @@ class ListEntitiesTextSensorResponse final : public InfoResponseProtoMessage {
const char *message_name() const override { return "list_entities_text_sensor_response"; }
#endif
StringRef device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -918,7 +918,7 @@ class TextSensorStateResponse final : public StateResponseProtoMessage {
#endif
StringRef state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -957,7 +957,7 @@ class SubscribeLogsResponse final : public ProtoMessage {
this->message_ptr_ = data;
this->message_len_ = len;
}
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -990,7 +990,7 @@ class NoiseEncryptionSetKeyResponse final : public ProtoMessage {
const char *message_name() const override { return "noise_encryption_set_key_response"; }
#endif
bool success{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1004,7 +1004,7 @@ class HomeassistantServiceMap final : public ProtoMessage {
public:
StringRef key{};
StringRef value{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1033,7 +1033,7 @@ class HomeassistantActionRequest final : public ProtoMessage {
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
StringRef response_template{};
#endif
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1077,7 +1077,7 @@ class SubscribeHomeAssistantStateResponse final : public ProtoMessage {
StringRef entity_id{};
StringRef attribute{};
bool once{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1138,7 +1138,7 @@ class ListEntitiesServicesArgument final : public ProtoMessage {
public:
StringRef name{};
enums::ServiceArgType type{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1157,7 +1157,7 @@ class ListEntitiesServicesResponse final : public ProtoMessage {
uint32_t key{0};
FixedVector<ListEntitiesServicesArgument> args{};
enums::SupportsResponseType supports_response{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1227,7 +1227,7 @@ class ExecuteServiceResponse final : public ProtoMessage {
const uint8_t *response_data{nullptr};
uint16_t response_data_len{0};
#endif
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1244,7 +1244,7 @@ class ListEntitiesCameraResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "list_entities_camera_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1266,7 +1266,7 @@ class CameraImageResponse final : public StateResponseProtoMessage {
this->data_len_ = len;
}
bool done{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1317,7 +1317,7 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage {
float visual_min_humidity{0.0f};
float visual_max_humidity{0.0f};
uint32_t feature_flags{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1345,7 +1345,7 @@ class ClimateStateResponse final : public StateResponseProtoMessage {
StringRef custom_preset{};
float current_humidity{0.0f};
float target_humidity{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1403,7 +1403,7 @@ class ListEntitiesWaterHeaterResponse final : public InfoResponseProtoMessage {
float target_temperature_step{0.0f};
const water_heater::WaterHeaterModeMask *supported_modes{};
uint32_t supported_features{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1424,7 +1424,7 @@ class WaterHeaterStateResponse final : public StateResponseProtoMessage {
uint32_t state{0};
float target_temperature_low{0.0f};
float target_temperature_high{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1468,7 +1468,7 @@ class ListEntitiesNumberResponse final : public InfoResponseProtoMessage {
StringRef unit_of_measurement{};
enums::NumberMode mode{};
StringRef device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1485,7 +1485,7 @@ class NumberStateResponse final : public StateResponseProtoMessage {
#endif
float state{0.0f};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1519,7 +1519,7 @@ class ListEntitiesSelectResponse final : public InfoResponseProtoMessage {
const char *message_name() const override { return "list_entities_select_response"; }
#endif
const FixedVector<const char *> *options{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1536,7 +1536,7 @@ class SelectStateResponse final : public StateResponseProtoMessage {
#endif
StringRef state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1573,7 +1573,7 @@ class ListEntitiesSirenResponse final : public InfoResponseProtoMessage {
const FixedVector<const char *> *tones{};
bool supports_duration{false};
bool supports_volume{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1589,7 +1589,7 @@ class SirenStateResponse final : public StateResponseProtoMessage {
const char *message_name() const override { return "siren_state_response"; }
#endif
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1634,7 +1634,7 @@ class ListEntitiesLockResponse final : public InfoResponseProtoMessage {
bool supports_open{false};
bool requires_code{false};
StringRef code_format{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1650,7 +1650,7 @@ class LockStateResponse final : public StateResponseProtoMessage {
const char *message_name() const override { return "lock_state_response"; }
#endif
enums::LockState state{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1687,7 +1687,7 @@ class ListEntitiesButtonResponse final : public InfoResponseProtoMessage {
const char *message_name() const override { return "list_entities_button_response"; }
#endif
StringRef device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1719,7 +1719,7 @@ class MediaPlayerSupportedFormat final : public ProtoMessage {
uint32_t num_channels{0};
enums::MediaPlayerFormatPurpose purpose{};
uint32_t sample_bytes{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1737,7 +1737,7 @@ class ListEntitiesMediaPlayerResponse final : public InfoResponseProtoMessage {
bool supports_pause{false};
std::vector<MediaPlayerSupportedFormat> supported_formats{};
uint32_t feature_flags{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1755,7 +1755,7 @@ class MediaPlayerStateResponse final : public StateResponseProtoMessage {
enums::MediaPlayerState state{};
float volume{0.0f};
bool muted{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1811,7 +1811,7 @@ class BluetoothLERawAdvertisement final : public ProtoMessage {
uint32_t address_type{0};
uint8_t data[62]{};
uint8_t data_len{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1828,7 +1828,7 @@ class BluetoothLERawAdvertisementsResponse final : public ProtoMessage {
#endif
std::array<BluetoothLERawAdvertisement, BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE> advertisements{};
uint16_t advertisements_len{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1865,7 +1865,7 @@ class BluetoothDeviceConnectionResponse final : public ProtoMessage {
bool connected{false};
uint32_t mtu{0};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1893,7 +1893,7 @@ class BluetoothGATTDescriptor final : public ProtoMessage {
std::array<uint64_t, 2> uuid{};
uint32_t handle{0};
uint32_t short_uuid{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1908,7 +1908,7 @@ class BluetoothGATTCharacteristic final : public ProtoMessage {
uint32_t properties{0};
FixedVector<BluetoothGATTDescriptor> descriptors{};
uint32_t short_uuid{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1922,7 +1922,7 @@ class BluetoothGATTService final : public ProtoMessage {
uint32_t handle{0};
FixedVector<BluetoothGATTCharacteristic> characteristics{};
uint32_t short_uuid{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1939,7 +1939,7 @@ class BluetoothGATTGetServicesResponse final : public ProtoMessage {
#endif
uint64_t address{0};
std::vector<BluetoothGATTService> services{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1955,7 +1955,7 @@ class BluetoothGATTGetServicesDoneResponse final : public ProtoMessage {
const char *message_name() const override { return "bluetooth_gatt_get_services_done_response"; }
#endif
uint64_t address{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -1994,7 +1994,7 @@ class BluetoothGATTReadResponse final : public ProtoMessage {
this->data_ptr_ = data;
this->data_len_ = len;
}
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2089,7 +2089,7 @@ class BluetoothGATTNotifyDataResponse final : public ProtoMessage {
this->data_ptr_ = data;
this->data_len_ = len;
}
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2107,7 +2107,7 @@ class BluetoothConnectionsFreeResponse final : public ProtoMessage {
uint32_t free{0};
uint32_t limit{0};
std::array<uint64_t, BLUETOOTH_PROXY_MAX_CONNECTIONS> allocated{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2125,7 +2125,7 @@ class BluetoothGATTErrorResponse final : public ProtoMessage {
uint64_t address{0};
uint32_t handle{0};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2142,7 +2142,7 @@ class BluetoothGATTWriteResponse final : public ProtoMessage {
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2159,7 +2159,7 @@ class BluetoothGATTNotifyResponse final : public ProtoMessage {
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2177,7 +2177,7 @@ class BluetoothDevicePairingResponse final : public ProtoMessage {
uint64_t address{0};
bool paired{false};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2195,7 +2195,7 @@ class BluetoothDeviceUnpairingResponse final : public ProtoMessage {
uint64_t address{0};
bool success{false};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2213,7 +2213,7 @@ class BluetoothDeviceClearCacheResponse final : public ProtoMessage {
uint64_t address{0};
bool success{false};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2231,7 +2231,7 @@ class BluetoothScannerStateResponse final : public ProtoMessage {
enums::BluetoothScannerState state{};
enums::BluetoothScannerMode mode{};
enums::BluetoothScannerMode configured_mode{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2277,7 +2277,7 @@ class VoiceAssistantAudioSettings final : public ProtoMessage {
uint32_t noise_suppression_level{0};
uint32_t auto_gain{0};
float volume_multiplier{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2297,7 +2297,7 @@ class VoiceAssistantRequest final : public ProtoMessage {
uint32_t flags{0};
VoiceAssistantAudioSettings audio_settings{};
StringRef wake_word_phrase{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2359,7 +2359,7 @@ class VoiceAssistantAudio final : public ProtoDecodableMessage {
const uint8_t *data{nullptr};
uint16_t data_len{0};
bool end{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2417,7 +2417,7 @@ class VoiceAssistantAnnounceFinished final : public ProtoMessage {
const char *message_name() const override { return "voice_assistant_announce_finished"; }
#endif
bool success{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2430,7 +2430,7 @@ class VoiceAssistantWakeWord final : public ProtoMessage {
StringRef id{};
StringRef wake_word{};
std::vector<std::string> trained_languages{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2480,7 +2480,7 @@ class VoiceAssistantConfigurationResponse final : public ProtoMessage {
std::vector<VoiceAssistantWakeWord> available_wake_words{};
const std::vector<std::string> *active_wake_words{};
uint32_t max_active_wake_words{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2515,7 +2515,7 @@ class ListEntitiesAlarmControlPanelResponse final : public InfoResponseProtoMess
uint32_t supported_features{0};
bool requires_code{false};
bool requires_code_to_arm{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2531,7 +2531,7 @@ class AlarmControlPanelStateResponse final : public StateResponseProtoMessage {
const char *message_name() const override { return "alarm_control_panel_state_response"; }
#endif
enums::AlarmControlPanelState state{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2570,7 +2570,7 @@ class ListEntitiesTextResponse final : public InfoResponseProtoMessage {
uint32_t max_length{0};
StringRef pattern{};
enums::TextMode mode{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2587,7 +2587,7 @@ class TextStateResponse final : public StateResponseProtoMessage {
#endif
StringRef state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2621,7 +2621,7 @@ class ListEntitiesDateResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "list_entities_date_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2640,7 +2640,7 @@ class DateStateResponse final : public StateResponseProtoMessage {
uint32_t year{0};
uint32_t month{0};
uint32_t day{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2675,7 +2675,7 @@ class ListEntitiesTimeResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "list_entities_time_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2694,7 +2694,7 @@ class TimeStateResponse final : public StateResponseProtoMessage {
uint32_t hour{0};
uint32_t minute{0};
uint32_t second{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2731,7 +2731,7 @@ class ListEntitiesEventResponse final : public InfoResponseProtoMessage {
#endif
StringRef device_class{};
const FixedVector<const char *> *event_types{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2747,7 +2747,7 @@ class EventResponse final : public StateResponseProtoMessage {
const char *message_name() const override { return "event_response"; }
#endif
StringRef event_type{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2768,7 +2768,7 @@ class ListEntitiesValveResponse final : public InfoResponseProtoMessage {
bool assumed_state{false};
bool supports_position{false};
bool supports_stop{false};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2785,7 +2785,7 @@ class ValveStateResponse final : public StateResponseProtoMessage {
#endif
float position{0.0f};
enums::ValveOperation current_operation{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2820,7 +2820,7 @@ class ListEntitiesDateTimeResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "list_entities_date_time_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2837,7 +2837,7 @@ class DateTimeStateResponse final : public StateResponseProtoMessage {
#endif
bool missing_state{false};
uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2871,7 +2871,7 @@ class ListEntitiesUpdateResponse final : public InfoResponseProtoMessage {
const char *message_name() const override { return "list_entities_update_response"; }
#endif
StringRef device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2895,7 +2895,7 @@ class UpdateStateResponse final : public StateResponseProtoMessage {
StringRef title{};
StringRef release_summary{};
StringRef release_url{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2930,7 +2930,7 @@ class ZWaveProxyFrame final : public ProtoDecodableMessage {
#endif
const uint8_t *data{nullptr};
uint16_t data_len{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2949,7 +2949,7 @@ class ZWaveProxyRequest final : public ProtoDecodableMessage {
enums::ZWaveProxyRequestType type{};
const uint8_t *data{nullptr};
uint16_t data_len{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -2969,7 +2969,7 @@ class ListEntitiesInfraredResponse final : public InfoResponseProtoMessage {
const char *message_name() const override { return "list_entities_infrared_response"; }
#endif
uint32_t capabilities{0};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
@@ -3016,7 +3016,7 @@ class InfraredRFReceiveEvent final : public ProtoMessage {
#endif
uint32_t key{0};
const std::vector<int32_t> *timings{};
void encode(ProtoWriteBuffer buffer) const override;
void encode(ProtoWriteBuffer &buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;

View File

@@ -30,6 +30,12 @@ APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-c
APIServer::APIServer() { global_api_server = this; }
void APIServer::socket_failed_(const LogString *msg) {
ESP_LOGW(TAG, "Socket %s: errno %d", LOG_STR_ARG(msg), errno);
this->destroy_socket_();
this->mark_failed();
}
void APIServer::setup() {
ControllerRegistry::register_controller(this);
@@ -48,22 +54,20 @@ void APIServer::setup() {
#endif
#endif
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0).release(); // monitored for incoming connections
if (this->socket_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket");
this->mark_failed();
this->socket_failed_(LOG_STR("creation"));
return;
}
int enable = 1;
int err = this->socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
ESP_LOGW(TAG, "Socket reuseaddr: errno %d", errno);
// we can still continue
}
err = this->socket_->setblocking(false);
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
this->mark_failed();
this->socket_failed_(LOG_STR("nonblocking"));
return;
}
@@ -71,28 +75,28 @@ void APIServer::setup() {
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
if (sl == 0) {
ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
this->mark_failed();
this->socket_failed_(LOG_STR("set sockaddr"));
return;
}
err = this->socket_->bind((struct sockaddr *) &server, sl);
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
this->mark_failed();
this->socket_failed_(LOG_STR("bind"));
return;
}
err = this->socket_->listen(this->listen_backlog_);
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
this->mark_failed();
this->socket_failed_(LOG_STR("listen"));
return;
}
#ifdef USE_LOGGER
if (logger::global_logger != nullptr) {
logger::global_logger->add_log_listener(this);
logger::global_logger->add_log_callback(
this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
static_cast<APIServer *>(self)->on_log(level, tag, message, message_len);
});
}
#endif
@@ -619,10 +623,7 @@ void APIServer::on_shutdown() {
this->shutting_down_ = true;
// Close the listening socket to prevent new connections
if (this->socket_) {
this->socket_->close();
this->socket_ = nullptr;
}
this->destroy_socket_();
// Change batch delay to 5ms for quick flushing during shutdown
this->batch_delay_ = 5;

View File

@@ -37,10 +37,6 @@ struct SavedNoisePsk {
class APIServer : public Component,
public Controller
#ifdef USE_LOGGER
,
public logger::LogListener
#endif
#ifdef USE_CAMERA
,
public camera::CameraListener
@@ -56,7 +52,7 @@ class APIServer : public Component,
void on_shutdown() override;
bool teardown() override;
#ifdef USE_LOGGER
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override;
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len);
#endif
#ifdef USE_CAMERA
void on_camera_image(const std::shared_ptr<camera::CameraImage> &image) override;
@@ -253,8 +249,15 @@ class APIServer : public Component,
void add_state_subscription_(std::string entity_id, optional<std::string> attribute,
std::function<void(const std::string &)> f, bool once);
#endif // USE_API_HOMEASSISTANT_STATES
// No explicit close() needed — listen sockets have no active connections on
// failure/shutdown. Destructor handles fd cleanup (close or abort per platform).
inline void destroy_socket_() {
delete this->socket_;
this->socket_ = nullptr;
}
void socket_failed_(const LogString *msg);
// Pointers and pointer-like types first (4 bytes each)
std::unique_ptr<socket::Socket> socket_ = nullptr;
socket::Socket *socket_{nullptr};
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
Trigger<std::string, std::string> client_connected_trigger_;
#endif

View File

@@ -70,6 +70,21 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
return count;
}
#ifdef ESPHOME_DEBUG_API
void ProtoWriteBuffer::debug_check_bounds_(size_t bytes, const char *caller) {
if (this->pos_ + bytes > this->buffer_->data() + this->buffer_->size()) {
ESP_LOGE(TAG, "ProtoWriteBuffer bounds check failed in %s: bytes=%zu offset=%td buf_size=%zu", caller, bytes,
this->pos_ - this->buffer_->data(), this->buffer_->size());
abort();
}
}
void ProtoWriteBuffer::debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual) {
ESP_LOGE(TAG, "encode_message: size mismatch for field %" PRIu32 ": calculated=%" PRIu32 " actual=%td", field_id,
expected, actual);
abort();
}
#endif
void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
const uint8_t *ptr = buffer;
const uint8_t *end = buffer + length;

View File

@@ -217,21 +217,26 @@ class Proto32Bit {
class ProtoWriteBuffer {
public:
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
void write(uint8_t value) { this->buffer_->push_back(value); }
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer), pos_(buffer->data() + buffer->size()) {}
ProtoWriteBuffer(std::vector<uint8_t> *buffer, size_t write_pos)
: buffer_(buffer), pos_(buffer->data() + write_pos) {}
void encode_varint_raw(uint32_t value) {
while (value > 0x7F) {
this->buffer_->push_back(static_cast<uint8_t>(value | 0x80));
this->debug_check_bounds_(1);
*this->pos_++ = static_cast<uint8_t>(value | 0x80);
value >>= 7;
}
this->buffer_->push_back(static_cast<uint8_t>(value));
this->debug_check_bounds_(1);
*this->pos_++ = static_cast<uint8_t>(value);
}
void encode_varint_raw_64(uint64_t value) {
while (value > 0x7F) {
this->buffer_->push_back(static_cast<uint8_t>(value | 0x80));
this->debug_check_bounds_(1);
*this->pos_++ = static_cast<uint8_t>(value | 0x80);
value >>= 7;
}
this->buffer_->push_back(static_cast<uint8_t>(value));
this->debug_check_bounds_(1);
*this->pos_++ = static_cast<uint8_t>(value);
}
/**
* Encode a field key (tag/wire type combination).
@@ -245,23 +250,18 @@ class ProtoWriteBuffer {
*
* Following https://protobuf.dev/programming-guides/encoding/#structure
*/
void encode_field_raw(uint32_t field_id, uint32_t type) {
uint32_t val = (field_id << 3) | (type & WIRE_TYPE_MASK);
this->encode_varint_raw(val);
}
void encode_field_raw(uint32_t field_id, uint32_t type) { this->encode_varint_raw((field_id << 3) | type); }
void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
if (len == 0 && !force)
return;
this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
this->encode_varint_raw(len);
// Using resize + memcpy instead of insert provides significant performance improvement:
// ~10-11x faster for 16-32 byte strings, ~3x faster for 64-byte strings
// as it avoids iterator checks and potential element moves that insert performs
size_t old_size = this->buffer_->size();
this->buffer_->resize(old_size + len);
std::memcpy(this->buffer_->data() + old_size, string, len);
// Direct memcpy into pre-sized buffer — avoids push_back() per-byte capacity checks
// and vector::insert() iterator overhead. ~10-11x faster for 16-32 byte strings.
this->debug_check_bounds_(len);
std::memcpy(this->pos_, string, len);
this->pos_ += len;
}
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
this->encode_string(field_id, value.data(), value.size(), force);
@@ -288,17 +288,26 @@ class ProtoWriteBuffer {
if (!value && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - bool
this->buffer_->push_back(value ? 0x01 : 0x00);
this->debug_check_bounds_(1);
*this->pos_++ = value ? 0x01 : 0x00;
}
void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
// noinline: 51 call sites; inlining causes net code growth vs a single out-of-line copy
__attribute__((noinline)) void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
if (value == 0 && !force)
return;
this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
this->write((value >> 0) & 0xFF);
this->write((value >> 8) & 0xFF);
this->write((value >> 16) & 0xFF);
this->write((value >> 24) & 0xFF);
this->debug_check_bounds_(4);
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
// Protobuf fixed32 is little-endian, so direct copy works
std::memcpy(this->pos_, &value, 4);
this->pos_ += 4;
#else
*this->pos_++ = (value >> 0) & 0xFF;
*this->pos_++ = (value >> 8) & 0xFF;
*this->pos_++ = (value >> 16) & 0xFF;
*this->pos_++ = (value >> 24) & 0xFF;
#endif
}
// NOTE: Wire type 1 (64-bit fixed: double, fixed64, sfixed64) is intentionally
// not supported to reduce overhead on embedded systems. All ESPHome devices are
@@ -334,11 +343,20 @@ class ProtoWriteBuffer {
}
/// Encode a packed repeated sint32 field (zero-copy from vector)
void encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values);
void encode_message(uint32_t field_id, const ProtoMessage &value);
/// Encode a nested message field (force=true for repeated, false for singular)
void encode_message(uint32_t field_id, const ProtoMessage &value, bool force = true);
std::vector<uint8_t> *get_buffer() const { return buffer_; }
protected:
#ifdef ESPHOME_DEBUG_API
void debug_check_bounds_(size_t bytes, const char *caller = __builtin_FUNCTION());
void debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual);
#else
void debug_check_bounds_([[maybe_unused]] size_t bytes) {}
#endif
std::vector<uint8_t> *buffer_;
uint8_t *pos_;
};
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -416,9 +434,11 @@ class ProtoMessage {
public:
virtual ~ProtoMessage() = default;
// Default implementation for messages with no fields
virtual void encode(ProtoWriteBuffer buffer) const {}
virtual void encode(ProtoWriteBuffer &buffer) const {}
// Default implementation for messages with no fields
virtual void calculate_size(ProtoSize &size) const {}
// Convenience: calculate and return size directly (defined after ProtoSize)
uint32_t calculated_size() const;
#ifdef HAS_PROTO_MESSAGE_DUMP
virtual const char *dump_to(DumpBuffer &out) const = 0;
virtual const char *message_name() const { return "unknown"; }
@@ -877,6 +897,14 @@ class ProtoSize {
}
};
// Implementation of methods that depend on ProtoSize being fully defined
inline uint32_t ProtoMessage::calculated_size() const {
ProtoSize size;
this->calculate_size(size);
return size.get_size();
}
// Implementation of encode_packed_sint32 - must be after ProtoSize is defined
inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std::vector<int32_t> &values) {
if (values.empty())
@@ -897,30 +925,30 @@ inline void ProtoWriteBuffer::encode_packed_sint32(uint32_t field_id, const std:
}
// Implementation of encode_message - must be after ProtoMessage is defined
inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value) {
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value, bool force) {
// Calculate the message size first
ProtoSize msg_size;
value.calculate_size(msg_size);
uint32_t msg_length_bytes = msg_size.get_size();
// Calculate how many bytes the length varint needs
uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes);
// Skip empty singular messages (matches add_message_field which skips when nested_size == 0)
// Repeated messages (force=true) are always encoded since an empty item is meaningful
if (msg_length_bytes == 0 && !force)
return;
// Reserve exact space for the length varint
size_t begin = this->buffer_->size();
this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
// Write the length varint directly
encode_varint_to_buffer(msg_length_bytes, this->buffer_->data() + begin);
// Now encode the message content - it will append to the buffer
value.encode(*this);
// Write the length varint directly through pos_
this->encode_varint_raw(msg_length_bytes);
// Encode nested message - pos_ advances directly through the reference
#ifdef ESPHOME_DEBUG_API
// Verify that the encoded size matches what we calculated
assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
uint8_t *start = this->pos_;
value.encode(*this);
if (static_cast<uint32_t>(this->pos_ - start) != msg_length_bytes)
this->debug_check_encode_size_(field_id, msg_length_bytes, this->pos_ - start);
#else
value.encode(*this);
#endif
}

View File

@@ -264,9 +264,9 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
// Build and send JSON response
json::JsonBuilder builder;
this->json_builder_(x..., builder.root());
std::string json_str = builder.serialize();
auto json_buf = builder.serialize();
this->parent_->send_action_response(call_id, success, StringRef(error_message),
reinterpret_cast<const uint8_t *>(json_str.data()), json_str.size());
reinterpret_cast<const uint8_t *>(json_buf.data()), json_buf.size());
return;
}
#endif

View File

@@ -15,8 +15,8 @@ static const uint32_t READ_WRITE_TIMEOUT_MS = 20; // Timeout for transferring a
static const uint32_t MAX_POTENTIALLY_FAILED_COUNT = 10;
AudioDecoder::AudioDecoder(size_t input_buffer_size, size_t output_buffer_size) {
this->input_transfer_buffer_ = AudioSourceTransferBuffer::create(input_buffer_size);
AudioDecoder::AudioDecoder(size_t input_buffer_size, size_t output_buffer_size)
: input_buffer_size_(input_buffer_size) {
this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(output_buffer_size);
}
@@ -29,11 +29,20 @@ AudioDecoder::~AudioDecoder() {
}
esp_err_t AudioDecoder::add_source(std::weak_ptr<RingBuffer> &input_ring_buffer) {
if (this->input_transfer_buffer_ != nullptr) {
this->input_transfer_buffer_->set_source(input_ring_buffer);
return ESP_OK;
auto source = AudioSourceTransferBuffer::create(this->input_buffer_size_);
if (source == nullptr) {
return ESP_ERR_NO_MEM;
}
return ESP_ERR_NO_MEM;
source->set_source(input_ring_buffer);
this->input_buffer_ = std::move(source);
return ESP_OK;
}
esp_err_t AudioDecoder::add_source(const uint8_t *data_pointer, size_t length) {
auto source = make_unique<ConstAudioSourceBuffer>();
source->set_data(data_pointer, length);
this->input_buffer_ = std::move(source);
return ESP_OK;
}
esp_err_t AudioDecoder::add_sink(std::weak_ptr<RingBuffer> &output_ring_buffer) {
@@ -54,8 +63,16 @@ esp_err_t AudioDecoder::add_sink(speaker::Speaker *speaker) {
}
#endif
esp_err_t AudioDecoder::add_sink(AudioSinkCallback *callback) {
if (this->output_transfer_buffer_ != nullptr) {
this->output_transfer_buffer_->set_sink(callback);
return ESP_OK;
}
return ESP_ERR_NO_MEM;
}
esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
if ((this->input_transfer_buffer_ == nullptr) || (this->output_transfer_buffer_ == nullptr)) {
if (this->output_transfer_buffer_ == nullptr) {
return ESP_ERR_NO_MEM;
}
@@ -68,6 +85,10 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
#ifdef USE_AUDIO_FLAC_SUPPORT
case AudioFileType::FLAC:
this->flac_decoder_ = make_unique<esp_audio_libs::flac::FLACDecoder>();
// CRC check slows down decoding by 15-20% on an ESP32-S3. FLAC sources in ESPHome are either from an http source
// or built into the firmware, so the data integrity is already verified by the time it gets to the decoder,
// making the CRC check unnecessary.
this->flac_decoder_->set_crc_check_enabled(false);
this->free_buffer_required_ =
this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header
break;
@@ -112,6 +133,10 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
}
AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
if (this->input_buffer_ == nullptr) {
return AudioDecoderState::FAILED;
}
if (stop_gracefully) {
if (this->output_transfer_buffer_->available() == 0) {
if (this->end_of_file_) {
@@ -119,7 +144,7 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
return AudioDecoderState::FINISHED;
}
if (!this->input_transfer_buffer_->has_buffered_data()) {
if (!this->input_buffer_->has_buffered_data()) {
// If all the internal buffers are empty, the decoding is done
return AudioDecoderState::FINISHED;
}
@@ -170,10 +195,10 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
// Only shift data on the first loop iteration to avoid unnecessary, slow moves
// If the decoder buffers internally, then never shift
size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(
pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), first_loop_iteration && !this->decoder_buffers_internally_);
size_t bytes_read = this->input_buffer_->fill(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS),
first_loop_iteration && !this->decoder_buffers_internally_);
if (!first_loop_iteration && (this->input_transfer_buffer_->available() < bytes_processed)) {
if (!first_loop_iteration && (this->input_buffer_->available() < bytes_processed)) {
// Less data is available than what was processed in last iteration, so don't attempt to decode.
// This attempts to avoid the decoder from consistently trying to decode an incomplete frame. The transfer buffer
// will shift the remaining data to the start and copy more from the source the next time the decode function is
@@ -181,19 +206,21 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
break;
}
bytes_available_before_processing = this->input_transfer_buffer_->available();
bytes_available_before_processing = this->input_buffer_->available();
if ((this->potentially_failed_count_ > 0) && (bytes_read == 0)) {
// Failed to decode in last attempt and there is no new data
if ((this->input_transfer_buffer_->free() == 0) && first_loop_iteration) {
// The input buffer is full. Since it previously failed on the exact same data, we can never recover
if ((this->input_buffer_->free() == 0) && first_loop_iteration) {
// The input buffer is full (or read-only, e.g. const flash source). Since it previously failed on the exact
// same data, we can never recover. For const sources this is correct: the entire file is already available, so
// a decode failure is genuine, not a transient out-of-data condition.
state = FileDecoderState::FAILED;
} else {
// Attempt to get more data next time
state = FileDecoderState::IDLE;
}
} else if (this->input_transfer_buffer_->available() == 0) {
} else if (this->input_buffer_->available() == 0) {
// No data to decode, attempt to get more data next time
state = FileDecoderState::IDLE;
} else {
@@ -224,7 +251,7 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
}
first_loop_iteration = false;
bytes_processed = bytes_available_before_processing - this->input_transfer_buffer_->available();
bytes_processed = bytes_available_before_processing - this->input_buffer_->available();
if (state == FileDecoderState::POTENTIALLY_FAILED) {
++this->potentially_failed_count_;
@@ -243,8 +270,7 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
FileDecoderState AudioDecoder::decode_flac_() {
if (!this->audio_stream_info_.has_value()) {
// Header hasn't been read
auto result = this->flac_decoder_->read_header(this->input_transfer_buffer_->get_buffer_start(),
this->input_transfer_buffer_->available());
auto result = this->flac_decoder_->read_header(this->input_buffer_->data(), this->input_buffer_->available());
if (result > esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
// Serrious error reading FLAC header, there is no recovery
@@ -252,7 +278,7 @@ FileDecoderState AudioDecoder::decode_flac_() {
}
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
this->input_buffer_->consume(bytes_consumed);
if (result == esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
return FileDecoderState::MORE_TO_PROCESS;
@@ -273,8 +299,7 @@ FileDecoderState AudioDecoder::decode_flac_() {
}
uint32_t output_samples = 0;
auto result = this->flac_decoder_->decode_frame(this->input_transfer_buffer_->get_buffer_start(),
this->input_transfer_buffer_->available(),
auto result = this->flac_decoder_->decode_frame(this->input_buffer_->data(), this->input_buffer_->available(),
this->output_transfer_buffer_->get_buffer_end(), &output_samples);
if (result == esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) {
@@ -283,7 +308,7 @@ FileDecoderState AudioDecoder::decode_flac_() {
}
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
this->input_buffer_->consume(bytes_consumed);
if (result > esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) {
// Corrupted frame, don't retry with current buffer content, wait for new sync
@@ -305,26 +330,25 @@ FileDecoderState AudioDecoder::decode_flac_() {
#ifdef USE_AUDIO_MP3_SUPPORT
FileDecoderState AudioDecoder::decode_mp3_() {
// Look for the next sync word
int buffer_length = (int) this->input_transfer_buffer_->available();
int32_t offset =
esp_audio_libs::helix_decoder::MP3FindSyncWord(this->input_transfer_buffer_->get_buffer_start(), buffer_length);
int buffer_length = (int) this->input_buffer_->available();
int32_t offset = esp_audio_libs::helix_decoder::MP3FindSyncWord(this->input_buffer_->data(), buffer_length);
if (offset < 0) {
// New data may have the sync word
this->input_transfer_buffer_->decrease_buffer_length(buffer_length);
this->input_buffer_->consume(buffer_length);
return FileDecoderState::POTENTIALLY_FAILED;
}
// Advance read pointer to match the offset for the syncword
this->input_transfer_buffer_->decrease_buffer_length(offset);
const uint8_t *buffer_start = this->input_transfer_buffer_->get_buffer_start();
this->input_buffer_->consume(offset);
const uint8_t *buffer_start = this->input_buffer_->data();
buffer_length = (int) this->input_transfer_buffer_->available();
buffer_length = (int) this->input_buffer_->available();
int err = esp_audio_libs::helix_decoder::MP3Decode(this->mp3_decoder_, &buffer_start, &buffer_length,
(int16_t *) this->output_transfer_buffer_->get_buffer_end(), 0);
size_t consumed = this->input_transfer_buffer_->available() - buffer_length;
this->input_transfer_buffer_->decrease_buffer_length(consumed);
size_t consumed = this->input_buffer_->available() - buffer_length;
this->input_buffer_->consume(consumed);
if (err) {
switch (err) {
@@ -363,9 +387,8 @@ FileDecoderState AudioDecoder::decode_opus_() {
size_t bytes_consumed, samples_decoded;
micro_opus::OggOpusResult result = this->opus_decoder_->decode(
this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available(),
this->output_transfer_buffer_->get_buffer_end(), this->output_transfer_buffer_->free(), bytes_consumed,
samples_decoded);
this->input_buffer_->data(), this->input_buffer_->available(), this->output_transfer_buffer_->get_buffer_end(),
this->output_transfer_buffer_->free(), bytes_consumed, samples_decoded);
if (result == micro_opus::OGG_OPUS_OK) {
if (!processed_header && this->opus_decoder_->is_initialized()) {
@@ -379,7 +402,7 @@ FileDecoderState AudioDecoder::decode_opus_() {
this->output_transfer_buffer_->increase_buffer_length(
this->audio_stream_info_.value().frames_to_bytes(samples_decoded));
}
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
this->input_buffer_->consume(bytes_consumed);
} else if (result == micro_opus::OGG_OPUS_OUTPUT_BUFFER_TOO_SMALL) {
// Reallocate to decode the packet on the next call
this->free_buffer_required_ = this->opus_decoder_->get_required_output_buffer_size();
@@ -399,11 +422,11 @@ FileDecoderState AudioDecoder::decode_wav_() {
if (!this->audio_stream_info_.has_value()) {
// Header hasn't been processed
esp_audio_libs::wav_decoder::WAVDecoderResult result = this->wav_decoder_->decode_header(
this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available());
esp_audio_libs::wav_decoder::WAVDecoderResult result =
this->wav_decoder_->decode_header(this->input_buffer_->data(), this->input_buffer_->available());
if (result == esp_audio_libs::wav_decoder::WAV_DECODER_SUCCESS_IN_DATA) {
this->input_transfer_buffer_->decrease_buffer_length(this->wav_decoder_->bytes_processed());
this->input_buffer_->consume(this->wav_decoder_->bytes_processed());
this->audio_stream_info_ = audio::AudioStreamInfo(
this->wav_decoder_->bits_per_sample(), this->wav_decoder_->num_channels(), this->wav_decoder_->sample_rate());
@@ -419,7 +442,7 @@ FileDecoderState AudioDecoder::decode_wav_() {
}
} else {
if (!this->wav_has_known_end_ || (this->wav_bytes_left_ > 0)) {
size_t bytes_to_copy = this->input_transfer_buffer_->available();
size_t bytes_to_copy = this->input_buffer_->available();
if (this->wav_has_known_end_) {
bytes_to_copy = std::min(bytes_to_copy, this->wav_bytes_left_);
@@ -428,9 +451,8 @@ FileDecoderState AudioDecoder::decode_wav_() {
bytes_to_copy = std::min(bytes_to_copy, this->output_transfer_buffer_->free());
if (bytes_to_copy > 0) {
std::memcpy(this->output_transfer_buffer_->get_buffer_end(), this->input_transfer_buffer_->get_buffer_start(),
bytes_to_copy);
this->input_transfer_buffer_->decrease_buffer_length(bytes_to_copy);
std::memcpy(this->output_transfer_buffer_->get_buffer_end(), this->input_buffer_->data(), bytes_to_copy);
this->input_buffer_->consume(bytes_to_copy);
this->output_transfer_buffer_->increase_buffer_length(bytes_to_copy);
if (this->wav_has_known_end_) {
this->wav_bytes_left_ -= bytes_to_copy;

View File

@@ -50,12 +50,12 @@ enum class FileDecoderState : uint8_t {
class AudioDecoder {
/*
* @brief Class that facilitates decoding an audio file.
* The audio file is read from a ring buffer source, decoded, and sent to an audio sink (ring buffer or speaker
* component).
* The audio file is read from a source (ring buffer or const data pointer), decoded, and sent to an audio sink
* (ring buffer, speaker component, or callback).
* Supports wav, flac, mp3, and ogg opus formats.
*/
public:
/// @brief Allocates the input and output transfer buffers
/// @brief Allocates the output transfer buffer and stores the input buffer size for later use by add_source()
/// @param input_buffer_size Size of the input transfer buffer in bytes.
/// @param output_buffer_size Size of the output transfer buffer in bytes.
AudioDecoder(size_t input_buffer_size, size_t output_buffer_size);
@@ -80,6 +80,17 @@ class AudioDecoder {
esp_err_t add_sink(speaker::Speaker *speaker);
#endif
/// @brief Adds a const data pointer as the source for raw file data. Does not allocate a transfer buffer.
/// @param data_pointer Pointer to the const audio data (e.g., stored in flash memory)
/// @param length Size of the data in bytes
/// @return ESP_OK
esp_err_t add_source(const uint8_t *data_pointer, size_t length);
/// @brief Adds a callback as the sink for decoded audio.
/// @param callback Pointer to the AudioSinkCallback implementation
/// @return ESP_OK if successful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated
esp_err_t add_sink(AudioSinkCallback *callback);
/// @brief Sets up decoding the file
/// @param audio_file_type AudioFileType of the file
/// @return ESP_OK if successful, ESP_ERR_NO_MEM if the transfer buffers fail to allocate, or ESP_ERR_NOT_SUPPORTED if
@@ -120,25 +131,26 @@ class AudioDecoder {
#endif
FileDecoderState decode_wav_();
std::unique_ptr<AudioSourceTransferBuffer> input_transfer_buffer_;
std::unique_ptr<AudioReadableBuffer> input_buffer_;
std::unique_ptr<AudioSinkTransferBuffer> output_transfer_buffer_;
AudioFileType audio_file_type_{AudioFileType::NONE};
optional<AudioStreamInfo> audio_stream_info_{};
size_t input_buffer_size_{0};
size_t free_buffer_required_{0};
size_t wav_bytes_left_{0};
uint32_t potentially_failed_count_{0};
uint32_t accumulated_frames_written_{0};
uint32_t playback_ms_{0};
bool end_of_file_{false};
bool wav_has_known_end_{false};
bool decoder_buffers_internally_{false};
bool pause_output_{false};
uint32_t accumulated_frames_written_{0};
uint32_t playback_ms_{0};
};
} // namespace audio
} // namespace esphome

View File

@@ -142,7 +142,7 @@ size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_
this->data_start_ = this->buffer_;
}
size_t bytes_to_read = this->free();
size_t bytes_to_read = AudioTransferBuffer::free();
size_t bytes_read = 0;
if (bytes_to_read > 0) {
if (this->ring_buffer_.use_count() > 0) {
@@ -193,6 +193,21 @@ bool AudioSinkTransferBuffer::has_buffered_data() const {
return (this->available() > 0);
}
size_t AudioSourceTransferBuffer::free() const { return AudioTransferBuffer::free(); }
bool AudioSourceTransferBuffer::has_buffered_data() const { return AudioTransferBuffer::has_buffered_data(); }
void ConstAudioSourceBuffer::set_data(const uint8_t *data, size_t length) {
this->data_start_ = data;
this->length_ = length;
}
void ConstAudioSourceBuffer::consume(size_t bytes) {
bytes = std::min(bytes, this->length_);
this->length_ -= bytes;
this->data_start_ += bytes;
}
} // namespace audio
} // namespace esphome

View File

@@ -32,7 +32,7 @@ class AudioTransferBuffer {
/// @brief Destructor that deallocates the transfer buffer
~AudioTransferBuffer();
/// @brief Returns a pointer to the start of the transfer buffer where available() bytes of exisiting data can be read
/// @brief Returns a pointer to the start of the transfer buffer where available() bytes of existing data can be read
uint8_t *get_buffer_start() const { return this->data_start_; }
/// @brief Returns a pointer to the end of the transfer buffer where free() bytes of new data can be written
@@ -129,10 +129,41 @@ class AudioSinkTransferBuffer : public AudioTransferBuffer {
AudioSinkCallback *sink_callback_{nullptr};
};
class AudioSourceTransferBuffer : public AudioTransferBuffer {
/// @brief Abstract interface for reading audio data from a buffer.
/// Provides a common read interface for both mutable transfer buffers and read-only const buffers.
class AudioReadableBuffer {
public:
virtual ~AudioReadableBuffer() = default;
/// @brief Returns a pointer to the start of readable data
virtual const uint8_t *data() const = 0;
/// @brief Returns the number of bytes available to read
virtual size_t available() const = 0;
/// @brief Returns the number of free bytes available to write. Defaults to 0 for read-only buffers.
virtual size_t free() const { return 0; }
/// @brief Advances past consumed data
/// @param bytes Number of bytes consumed
virtual void consume(size_t bytes) = 0;
/// @brief Tests if there is any buffered data
virtual bool has_buffered_data() const = 0;
/// @brief Refills the buffer from its source. No-op by default for read-only buffers.
/// @param ticks_to_wait FreeRTOS ticks to block while waiting for data
/// @param pre_shift If true, shifts existing data to the start of the buffer before reading
/// @return Number of bytes read
virtual size_t fill(TickType_t ticks_to_wait, bool pre_shift) { return 0; }
size_t fill(TickType_t ticks_to_wait) { return this->fill(ticks_to_wait, true); }
};
class AudioSourceTransferBuffer : public AudioTransferBuffer, public AudioReadableBuffer {
/*
* @brief A class that implements a transfer buffer for audio sources.
* Supports reading audio data from a ring buffer into the transfer buffer for processing.
* Implements AudioReadableBuffer for use by consumers that only need read access.
*/
public:
/// @brief Creates a new source transfer buffer.
@@ -140,7 +171,7 @@ class AudioSourceTransferBuffer : public AudioTransferBuffer {
/// @return unique_ptr if successfully allocated, nullptr otherwise
static std::unique_ptr<AudioSourceTransferBuffer> create(size_t buffer_size);
/// @brief Reads any available data from the sink into the transfer buffer.
/// @brief Reads any available data from the source into the transfer buffer.
/// @param ticks_to_wait FreeRTOS ticks to block while waiting for the source to have enough data
/// @param pre_shift If true, any unwritten data is moved to the start of the buffer before transferring from the
/// source. Defaults to true.
@@ -150,6 +181,36 @@ class AudioSourceTransferBuffer : public AudioTransferBuffer {
/// @brief Adds a ring buffer as the transfer buffer's source.
/// @param ring_buffer weak_ptr to the allocated ring buffer
void set_source(const std::weak_ptr<RingBuffer> &ring_buffer) { this->ring_buffer_ = ring_buffer.lock(); };
// AudioReadableBuffer interface
const uint8_t *data() const override { return this->data_start_; }
size_t available() const override { return this->buffer_length_; }
size_t free() const override;
void consume(size_t bytes) override { this->decrease_buffer_length(bytes); }
bool has_buffered_data() const override;
size_t fill(TickType_t ticks_to_wait, bool pre_shift) override {
return this->transfer_data_from_source(ticks_to_wait, pre_shift);
}
};
/// @brief A lightweight read-only audio buffer for const data sources (e.g., flash memory).
/// Does not allocate memory or transfer data from external sources.
class ConstAudioSourceBuffer : public AudioReadableBuffer {
public:
/// @brief Sets the data pointer and length for the buffer
/// @param data Pointer to the const audio data
/// @param length Size of the data in bytes
void set_data(const uint8_t *data, size_t length);
// AudioReadableBuffer interface
const uint8_t *data() const override { return this->data_start_; }
size_t available() const override { return this->length_; }
void consume(size_t bytes) override;
bool has_buffered_data() const override { return this->length_ > 0; }
protected:
const uint8_t *data_start_{nullptr};
size_t length_{0};
};
} // namespace audio

View File

@@ -87,7 +87,10 @@ void BLENUS::setup() {
global_ble_nus = this;
#ifdef USE_LOGGER
if (logger::global_logger != nullptr && this->expose_log_) {
logger::global_logger->add_log_listener(this);
logger::global_logger->add_log_callback(
this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
static_cast<BLENUS *>(self)->on_log(level, tag, message, message_len);
});
}
#endif
}

View File

@@ -10,12 +10,7 @@
namespace esphome::ble_nus {
class BLENUS : public Component
#ifdef USE_LOGGER
,
public logger::LogListener
#endif
{
class BLENUS : public Component {
enum TxStatus {
TX_DISABLED,
TX_ENABLED,
@@ -29,7 +24,7 @@ class BLENUS : public Component
size_t write_array(const uint8_t *data, size_t len);
void set_expose_log(bool expose_log) { this->expose_log_ = expose_log; }
#ifdef USE_LOGGER
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override;
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len);
#endif
protected:

View File

@@ -22,11 +22,11 @@ static const uint8_t BME680_REGISTER_CHIPID = 0xD0;
static const uint8_t BME680_REGISTER_FIELD0 = 0x1D;
const float BME680_GAS_LOOKUP_TABLE_1[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8,
0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0};
constexpr float BME680_GAS_LOOKUP_TABLE_1[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8,
0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0};
const float BME680_GAS_LOOKUP_TABLE_2[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8,
-0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
constexpr float BME680_GAS_LOOKUP_TABLE_2[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8,
-0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
[[maybe_unused]] static const char *oversampling_to_str(BME680Oversampling oversampling) {
switch (oversampling) {

View File

@@ -6,7 +6,7 @@
namespace esphome::captive_portal {
#ifdef USE_CAPTIVE_PORTAL_GZIP
const uint8_t INDEX_GZ[] PROGMEM = {
constexpr uint8_t INDEX_GZ[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x95, 0x16, 0x6b, 0x8f, 0xdb, 0x36, 0xf2, 0x7b, 0x7e,
0x05, 0x8f, 0x49, 0xbb, 0x52, 0xb3, 0x7a, 0x7a, 0xed, 0x6c, 0x24, 0x51, 0x45, 0x9a, 0xbb, 0xa2, 0x05, 0x9a, 0x36,
0xc0, 0x6e, 0x73, 0x1f, 0x82, 0x00, 0x4b, 0x53, 0x23, 0x8b, 0x31, 0x45, 0xea, 0x48, 0xca, 0x8f, 0x18, 0xbe, 0xdf,
@@ -86,7 +86,7 @@ const uint8_t INDEX_GZ[] PROGMEM = {
0xfc, 0xda, 0xd1, 0xf8, 0xe9, 0xa3, 0xe1, 0xa6, 0xfb, 0x1f, 0x53, 0x58, 0x46, 0xb2, 0xf9, 0x0a, 0x00, 0x00};
#else // Brotli (default, smaller)
const uint8_t INDEX_BR[] PROGMEM = {
constexpr uint8_t INDEX_BR[] PROGMEM = {
0x1b, 0xf8, 0x0a, 0x00, 0x64, 0x5a, 0xd3, 0xfa, 0xe7, 0xf3, 0x62, 0xd8, 0x06, 0x1b, 0xe9, 0x6a, 0x8a, 0x81, 0x2b,
0xb5, 0x49, 0x14, 0x37, 0xdc, 0x9e, 0x1a, 0xcb, 0x56, 0x87, 0xfb, 0xff, 0xf7, 0x73, 0x75, 0x12, 0x0a, 0xd6, 0x48,
0x84, 0xc6, 0x21, 0xa4, 0x6d, 0xb5, 0x71, 0xef, 0x13, 0xbe, 0x4e, 0x54, 0xf1, 0x64, 0x8f, 0x3f, 0xcc, 0x9a, 0x78,

View File

@@ -53,7 +53,7 @@ void DNSServer::start(const network::IPAddress &ip) {
#endif
// Create loop-monitored UDP socket
this->socket_ = socket::socket_ip_loop_monitored(SOCK_DGRAM, IPPROTO_UDP);
this->socket_ = socket::socket_ip_loop_monitored(SOCK_DGRAM, IPPROTO_UDP).release();
if (this->socket_ == nullptr) {
ESP_LOGE(TAG, "Socket create failed");
return;
@@ -70,17 +70,14 @@ void DNSServer::start(const network::IPAddress &ip) {
int err = this->socket_->bind((struct sockaddr *) &server_addr, addr_len);
if (err != 0) {
ESP_LOGE(TAG, "Bind failed: %d", errno);
this->socket_ = nullptr;
this->destroy_socket_();
return;
}
ESP_LOGV(TAG, "Bound to port %d", DNS_PORT);
}
void DNSServer::stop() {
if (this->socket_ != nullptr) {
this->socket_->close();
this->socket_ = nullptr;
}
this->destroy_socket_();
ESP_LOGV(TAG, "Stopped");
}

View File

@@ -1,7 +1,6 @@
#pragma once
#ifdef USE_ESP32
#include <memory>
#include "esphome/core/helpers.h"
#include "esphome/components/network/ip_address.h"
#include "esphome/components/socket/socket.h"
@@ -15,9 +14,15 @@ class DNSServer {
void process_next_request();
protected:
// No explicit close() needed — listen sockets have no active connections on
// failure/shutdown. Destructor handles fd cleanup (close or abort per platform).
inline void destroy_socket_() {
delete this->socket_;
this->socket_ = nullptr;
}
static constexpr size_t DNS_BUFFER_SIZE = 192;
std::unique_ptr<socket::Socket> socket_{nullptr};
socket::Socket *socket_{nullptr};
network::IPAddress server_ip_;
uint8_t buffer_[DNS_BUFFER_SIZE];
};

View File

@@ -9,6 +9,7 @@ from esphome.const import (
CONF_DATA,
CONF_FREQUENCY,
CONF_ID,
CONF_VALUE,
CONF_WAIT_TIME,
)
from esphome.core import ID
@@ -333,3 +334,94 @@ async def send_packet_action_to_code(config, action_id, template_arg, args):
arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data))
cg.add(var.set_data_static(arr, len(data)))
return var
# Setter action definitions: (setter_name, validator, template_type, enum_map)
_SETTER_ACTIONS = [
(
"set_frequency",
cv.All(cv.frequency, cv.float_range(min=300.0e6, max=928.0e6)),
float,
None,
),
("set_output_power", cv.float_range(min=-30.0, max=11.0), float, None),
("set_modulation_type", cv.enum(MODULATION, upper=False), Modulation, MODULATION),
("set_symbol_rate", cv.float_range(min=600, max=500000), float, None),
(
"set_rx_attenuation",
cv.enum(RX_ATTENUATION, upper=False),
RxAttenuation,
RX_ATTENUATION,
),
("set_dc_blocking_filter", cv.boolean, bool, None),
("set_manchester", cv.boolean, bool, None),
(
"set_filter_bandwidth",
cv.All(cv.frequency, cv.float_range(min=58000, max=812000)),
float,
None,
),
(
"set_fsk_deviation",
cv.All(cv.frequency, cv.float_range(min=1500, max=381000)),
float,
None,
),
("set_msk_deviation", cv.int_range(min=1, max=8), cg.uint8, None),
("set_channel", cv.uint8_t, cg.uint8, None),
(
"set_channel_spacing",
cv.All(cv.frequency, cv.float_range(min=25000, max=405000)),
float,
None,
),
(
"set_if_frequency",
cv.All(cv.frequency, cv.float_range(min=25000, max=788000)),
float,
None,
),
]
def _register_setter_actions():
for setter_name, validator, templ_type, enum_map in _SETTER_ACTIONS:
class_name = (
"".join(word.capitalize() for word in setter_name.split("_")) + "Action"
)
action_cls = ns.class_(
class_name, automation.Action, cg.Parented.template(CC1101Component)
)
schema = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(CC1101Component),
cv.Required(CONF_VALUE): cv.templatable(validator),
},
key=CONF_VALUE,
)
async def _setter_action_to_code(
config,
action_id,
template_arg,
args,
_setter=setter_name,
_type=templ_type,
_map=enum_map,
):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
data = config[CONF_VALUE]
if cg.is_template(data):
templ_ = await cg.templatable(data, args, _type)
cg.add(getattr(var, _setter)(templ_))
else:
cg.add(getattr(var, _setter)(_map[data] if _map else data))
return var
automation.register_action(f"cc1101.{setter_name}", action_cls, schema)(
_setter_action_to_code
)
_register_setter_actions()

View File

@@ -161,4 +161,82 @@ template<typename... Ts> class SendPacketAction : public Action<Ts...>, public P
size_t data_static_len_{0};
};
template<typename... Ts> class SetSymbolRateAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, symbol_rate)
void play(const Ts &...x) override { this->parent_->set_symbol_rate(this->symbol_rate_.value(x...)); }
};
template<typename... Ts> class SetFrequencyAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, frequency)
void play(const Ts &...x) override { this->parent_->set_frequency(this->frequency_.value(x...)); }
};
template<typename... Ts> class SetOutputPowerAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, output_power)
void play(const Ts &...x) override { this->parent_->set_output_power(this->output_power_.value(x...)); }
};
template<typename... Ts> class SetModulationTypeAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(Modulation, modulation_type)
void play(const Ts &...x) override { this->parent_->set_modulation_type(this->modulation_type_.value(x...)); }
};
template<typename... Ts> class SetRxAttenuationAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(RxAttenuation, rx_attenuation)
void play(const Ts &...x) override { this->parent_->set_rx_attenuation(this->rx_attenuation_.value(x...)); }
};
template<typename... Ts> class SetDcBlockingFilterAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(bool, dc_blocking_filter)
void play(const Ts &...x) override { this->parent_->set_dc_blocking_filter(this->dc_blocking_filter_.value(x...)); }
};
template<typename... Ts> class SetManchesterAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(bool, manchester)
void play(const Ts &...x) override { this->parent_->set_manchester(this->manchester_.value(x...)); }
};
template<typename... Ts> class SetFilterBandwidthAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, filter_bandwidth)
void play(const Ts &...x) override { this->parent_->set_filter_bandwidth(this->filter_bandwidth_.value(x...)); }
};
template<typename... Ts> class SetFskDeviationAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, fsk_deviation)
void play(const Ts &...x) override { this->parent_->set_fsk_deviation(this->fsk_deviation_.value(x...)); }
};
template<typename... Ts> class SetMskDeviationAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(uint8_t, msk_deviation)
void play(const Ts &...x) override { this->parent_->set_msk_deviation(this->msk_deviation_.value(x...)); }
};
template<typename... Ts> class SetChannelAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(uint8_t, channel)
void play(const Ts &...x) override { this->parent_->set_channel(this->channel_.value(x...)); }
};
template<typename... Ts> class SetChannelSpacingAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, channel_spacing)
void play(const Ts &...x) override { this->parent_->set_channel_spacing(this->channel_spacing_.value(x...)); }
};
template<typename... Ts> class SetIfFrequencyAction : public Action<Ts...>, public Parented<CC1101Component> {
public:
TEMPLATABLE_VALUE(float, if_frequency)
void play(const Ts &...x) override { this->parent_->set_if_frequency(this->if_frequency_.value(x...)); }
};
} // namespace esphome::cc1101

View File

@@ -15,29 +15,29 @@ static const char *const TAG = "cse7761";
* https://github.com/arendst/Tasmota/blob/development/tasmota/xnrg_19_cse7761.ino
\*********************************************************************************************/
static const int CSE7761_UREF = 42563; // RmsUc
static const int CSE7761_IREF = 52241; // RmsIAC
static const int CSE7761_PREF = 44513; // PowerPAC
static constexpr int CSE7761_UREF = 42563; // RmsUc
static constexpr int CSE7761_IREF = 52241; // RmsIAC
static constexpr int CSE7761_PREF = 44513; // PowerPAC
static const uint8_t CSE7761_REG_SYSCON = 0x00; // (2) System Control Register (0x0A04)
static const uint8_t CSE7761_REG_EMUCON = 0x01; // (2) Metering control register (0x0000)
static const uint8_t CSE7761_REG_EMUCON2 = 0x13; // (2) Metering control register 2 (0x0001)
static const uint8_t CSE7761_REG_PULSE1SEL = 0x1D; // (2) Pin function output select register (0x3210)
static constexpr uint8_t CSE7761_REG_SYSCON = 0x00; // (2) System Control Register (0x0A04)
static constexpr uint8_t CSE7761_REG_EMUCON = 0x01; // (2) Metering control register (0x0000)
static constexpr uint8_t CSE7761_REG_EMUCON2 = 0x13; // (2) Metering control register 2 (0x0001)
static constexpr uint8_t CSE7761_REG_PULSE1SEL = 0x1D; // (2) Pin function output select register (0x3210)
static const uint8_t CSE7761_REG_RMSIA = 0x24; // (3) The effective value of channel A current (0x000000)
static const uint8_t CSE7761_REG_RMSIB = 0x25; // (3) The effective value of channel B current (0x000000)
static const uint8_t CSE7761_REG_RMSU = 0x26; // (3) Voltage RMS (0x000000)
static const uint8_t CSE7761_REG_POWERPA = 0x2C; // (4) Channel A active power, update rate 27.2Hz (0x00000000)
static const uint8_t CSE7761_REG_POWERPB = 0x2D; // (4) Channel B active power, update rate 27.2Hz (0x00000000)
static const uint8_t CSE7761_REG_SYSSTATUS = 0x43; // (1) System status register
static constexpr uint8_t CSE7761_REG_RMSIA = 0x24; // (3) The effective value of channel A current (0x000000)
static constexpr uint8_t CSE7761_REG_RMSIB = 0x25; // (3) The effective value of channel B current (0x000000)
static constexpr uint8_t CSE7761_REG_RMSU = 0x26; // (3) Voltage RMS (0x000000)
static constexpr uint8_t CSE7761_REG_POWERPA = 0x2C; // (4) Channel A active power, update rate 27.2Hz (0x00000000)
static constexpr uint8_t CSE7761_REG_POWERPB = 0x2D; // (4) Channel B active power, update rate 27.2Hz (0x00000000)
static constexpr uint8_t CSE7761_REG_SYSSTATUS = 0x43; // (1) System status register
static const uint8_t CSE7761_REG_COEFFCHKSUM = 0x6F; // (2) Coefficient checksum
static const uint8_t CSE7761_REG_RMSIAC = 0x70; // (2) Channel A effective current conversion coefficient
static constexpr uint8_t CSE7761_REG_COEFFCHKSUM = 0x6F; // (2) Coefficient checksum
static constexpr uint8_t CSE7761_REG_RMSIAC = 0x70; // (2) Channel A effective current conversion coefficient
static const uint8_t CSE7761_SPECIAL_COMMAND = 0xEA; // Start special command
static const uint8_t CSE7761_CMD_RESET = 0x96; // Reset command, after receiving the command, the chip resets
static const uint8_t CSE7761_CMD_CLOSE_WRITE = 0xDC; // Close write operation
static const uint8_t CSE7761_CMD_ENABLE_WRITE = 0xE5; // Enable write operation
static constexpr uint8_t CSE7761_SPECIAL_COMMAND = 0xEA; // Start special command
static constexpr uint8_t CSE7761_CMD_RESET = 0x96; // Reset command, after receiving the command, the chip resets
static constexpr uint8_t CSE7761_CMD_CLOSE_WRITE = 0xDC; // Close write operation
static constexpr uint8_t CSE7761_CMD_ENABLE_WRITE = 0xE5; // Enable write operation
enum CSE7761 { RMS_IAC, RMS_IBC, RMS_UC, POWER_PAC, POWER_PBC, POWER_SC, ENERGY_AC, ENERGY_BC };

View File

@@ -2,6 +2,7 @@
#ifdef USE_ZEPHYR
#include <climits>
#include "esphome/core/log.h"
#include <esphome/components/zephyr/reset_reason.h>
#include <zephyr/drivers/hwinfo.h>
#include <hal/nrf_power.h>
#include <cstdint>
@@ -15,16 +16,6 @@ static const char *const TAG = "debug";
constexpr std::uintptr_t MBR_PARAM_PAGE_ADDR = 0xFFC;
constexpr std::uintptr_t MBR_BOOTLOADER_ADDR = 0xFF8;
static size_t append_reset_reason(char *buf, size_t size, size_t pos, bool set, const char *reason) {
if (!set) {
return pos;
}
if (pos > 0) {
pos = buf_append_printf(buf, size, pos, ", ");
}
return buf_append_printf(buf, size, pos, "%s", reason);
}
static inline uint32_t read_mem_u32(uintptr_t addr) {
return *reinterpret_cast<volatile uint32_t *>(addr); // NOLINT(performance-no-int-to-ptr)
}
@@ -57,39 +48,7 @@ static inline uint32_t sd_version_get() {
}
const char *DebugComponent::get_reset_reason_(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
char *buf = buffer.data();
const size_t size = RESET_REASON_BUFFER_SIZE;
uint32_t cause;
auto ret = hwinfo_get_reset_cause(&cause);
if (ret) {
ESP_LOGE(TAG, "Unable to get reset cause: %d", ret);
buf[0] = '\0';
return buf;
}
size_t pos = 0;
pos = append_reset_reason(buf, size, pos, cause & RESET_PIN, "External pin");
pos = append_reset_reason(buf, size, pos, cause & RESET_SOFTWARE, "Software reset");
pos = append_reset_reason(buf, size, pos, cause & RESET_BROWNOUT, "Brownout (drop in voltage)");
pos = append_reset_reason(buf, size, pos, cause & RESET_POR, "Power-on reset (POR)");
pos = append_reset_reason(buf, size, pos, cause & RESET_WATCHDOG, "Watchdog timer expiration");
pos = append_reset_reason(buf, size, pos, cause & RESET_DEBUG, "Debug event");
pos = append_reset_reason(buf, size, pos, cause & RESET_SECURITY, "Security violation");
pos = append_reset_reason(buf, size, pos, cause & RESET_LOW_POWER_WAKE, "Waking up from low power mode");
pos = append_reset_reason(buf, size, pos, cause & RESET_CPU_LOCKUP, "CPU lock-up detected");
pos = append_reset_reason(buf, size, pos, cause & RESET_PARITY, "Parity error");
pos = append_reset_reason(buf, size, pos, cause & RESET_PLL, "PLL error");
pos = append_reset_reason(buf, size, pos, cause & RESET_CLOCK, "Clock error");
pos = append_reset_reason(buf, size, pos, cause & RESET_HARDWARE, "Hardware reset");
pos = append_reset_reason(buf, size, pos, cause & RESET_USER, "User reset");
pos = append_reset_reason(buf, size, pos, cause & RESET_TEMPERATURE, "Temperature reset");
// Ensure null termination if nothing was written
if (pos == 0) {
buf[0] = '\0';
}
const char *buf = zephyr::get_reset_reason(buffer);
ESP_LOGD(TAG, "Reset Reason: %s", buf);
return buf;
}

View File

@@ -810,9 +810,9 @@ bool Display::clamp_y_(int y, int h, int &min_y, int &max_y) {
return min_y < max_y;
}
const uint8_t TESTCARD_FONT[3][8] PROGMEM = {{0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00}, // 'R'
{0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00}, // 'G'
{0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00}}; // 'B'
constexpr uint8_t TESTCARD_FONT[3][8] PROGMEM = {{0x41, 0x7F, 0x7F, 0x09, 0x19, 0x7F, 0x66, 0x00}, // 'R'
{0x1C, 0x3E, 0x63, 0x41, 0x51, 0x73, 0x72, 0x00}, // 'G'
{0x41, 0x7F, 0x7F, 0x49, 0x49, 0x7F, 0x36, 0x00}}; // 'B'
void Display::test_card() {
int w = get_width(), h = get_height(), image_w, image_h;

View File

@@ -14,12 +14,17 @@ static const int PORT = 5568;
E131Component::E131Component() {}
E131Component::~E131Component() {
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
if (this->socket_) {
this->socket_->close();
}
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
this->udp_.stop();
#endif
}
void E131Component::setup() {
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
this->socket_ = socket::socket_ip(SOCK_DGRAM, IPPROTO_IP);
int enable = 1;
@@ -50,6 +55,13 @@ void E131Component::setup() {
this->mark_failed();
return;
}
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
if (!this->udp_.begin(PORT)) {
ESP_LOGW(TAG, "Cannot bind E1.31 to port %d.", PORT);
this->mark_failed();
return;
}
#endif
join_igmp_groups_();
}
@@ -58,19 +70,20 @@ void E131Component::loop() {
E131Packet packet;
int universe = 0;
uint8_t buf[1460];
ssize_t len;
ssize_t len = this->socket_->read(buf, sizeof(buf));
if (len == -1) {
return;
}
// Drain all queued packets so multi-universe frames are applied
// atomically before the light writes. Without this, each universe
// packet would trigger a separate full-strip write causing tearing.
while ((len = this->read_(buf, sizeof(buf))) > 0) {
if (!this->packet_(buf, (size_t) len, universe, packet)) {
ESP_LOGV(TAG, "Invalid packet received of size %d.", (int) len);
continue;
}
if (!this->packet_(buf, (size_t) len, universe, packet)) {
ESP_LOGV(TAG, "Invalid packet received of size %zd.", len);
return;
}
if (!this->process_(universe, packet)) {
ESP_LOGV(TAG, "Ignored packet for %d universe of size %d.", universe, packet.count);
if (!this->process_(universe, packet)) {
ESP_LOGV(TAG, "Ignored packet for %d universe of size %d.", universe, packet.count);
}
}
}

View File

@@ -1,11 +1,14 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_NETWORK
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
#include "esphome/components/socket/socket.h"
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
#include <WiFiUdp.h>
#endif
#include "esphome/core/component.h"
#include <cinttypes>
#include <map>
#include <memory>
#include <vector>
@@ -23,6 +26,11 @@ struct E131Packet {
uint8_t values[E131_MAX_PROPERTY_VALUES_COUNT];
};
struct UniverseConsumer {
uint16_t universe;
uint16_t consumers;
};
class E131Component : public esphome::Component {
public:
E131Component();
@@ -38,16 +46,30 @@ class E131Component : public esphome::Component {
void set_method(E131ListenMethod listen_method) { this->listen_method_ = listen_method; }
protected:
inline ssize_t read_(uint8_t *buf, size_t len) {
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
return this->socket_->read(buf, len);
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
if (!this->udp_.parsePacket())
return -1;
return this->udp_.read(buf, len);
#endif
}
bool packet_(const uint8_t *data, size_t len, int &universe, E131Packet &packet);
bool process_(int universe, const E131Packet &packet);
bool join_igmp_groups_();
UniverseConsumer *find_universe_(int universe);
void join_(int universe);
void leave_(int universe);
E131ListenMethod listen_method_{E131_MULTICAST};
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
std::unique_ptr<socket::Socket> socket_;
#elif defined(USE_SOCKET_IMPL_LWIP_TCP)
WiFiUDP udp_;
#endif
std::vector<E131AddressableLightEffect *> light_effects_;
std::map<int, int> universe_consumers_;
std::vector<UniverseConsumer> universe_consumers_;
};
} // namespace e131

View File

@@ -60,17 +60,19 @@ union E131RawPacket {
const size_t E131_MIN_PACKET_SIZE = reinterpret_cast<size_t>(&((E131RawPacket *) nullptr)->property_values[1]);
bool E131Component::join_igmp_groups_() {
if (listen_method_ != E131_MULTICAST)
if (this->listen_method_ != E131_MULTICAST)
return false;
#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS)
if (this->socket_ == nullptr)
return false;
#endif
for (auto universe : universe_consumers_) {
if (!universe.second)
for (auto &entry : this->universe_consumers_) {
if (!entry.consumers)
continue;
ip4_addr_t multicast_addr =
network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff));
network::IPAddress(239, 255, ((entry.universe >> 8) & 0xff), ((entry.universe >> 0) & 0xff));
err_t err;
{
@@ -79,34 +81,47 @@ bool E131Component::join_igmp_groups_() {
}
if (err) {
ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", universe.first);
ESP_LOGW(TAG, "IGMP join for %d universe of E1.31 failed. Multicast might not work.", entry.universe);
}
}
return true;
}
UniverseConsumer *E131Component::find_universe_(int universe) {
for (auto &entry : this->universe_consumers_) {
if (entry.universe == universe)
return &entry;
}
return nullptr;
}
void E131Component::join_(int universe) {
// store only latest received packet for the given universe
auto consumers = ++universe_consumers_[universe];
if (consumers > 1) {
return; // we already joined before
auto *consumer = this->find_universe_(universe);
if (consumer != nullptr) {
if (consumer->consumers++ > 0) {
return; // we already joined before
}
} else {
this->universe_consumers_.push_back({static_cast<uint16_t>(universe), 1});
}
if (join_igmp_groups_()) {
if (this->join_igmp_groups_()) {
ESP_LOGD(TAG, "Joined %d universe for E1.31.", universe);
}
}
void E131Component::leave_(int universe) {
auto consumers = --universe_consumers_[universe];
auto *consumer = this->find_universe_(universe);
if (consumer == nullptr)
return;
if (consumers > 0) {
if (--consumer->consumers > 0) {
return; // we have other consumers of the given universe
}
if (listen_method_ == E131_MULTICAST) {
if (this->listen_method_ == E131_MULTICAST) {
ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff));
LwIPLock lock;

View File

@@ -25,7 +25,6 @@ from esphome.const import (
CONF_PLATFORM_VERSION,
CONF_PLATFORMIO_OPTIONS,
CONF_REF,
CONF_REFRESH,
CONF_SAFE_MODE,
CONF_SOURCE,
CONF_TYPE,
@@ -41,7 +40,7 @@ from esphome.const import (
ThreadModel,
__version__,
)
from esphome.core import CORE, HexInt, TimePeriod
from esphome.core import CORE, HexInt
from esphome.coroutine import CoroPriority, coroutine_with_priority
import esphome.final_validate as fv
from esphome.helpers import copy_file_if_changed, rmtree, write_file_if_changed
@@ -88,6 +87,7 @@ IS_TARGET_PLATFORM = True
CONF_ASSERTION_LEVEL = "assertion_level"
CONF_COMPILER_OPTIMIZATION = "compiler_optimization"
CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES = "enable_idf_experimental_features"
CONF_ENGINEERING_SAMPLE = "engineering_sample"
CONF_INCLUDE_BUILTIN_IDF_COMPONENTS = "include_builtin_idf_components"
CONF_ENABLE_LWIP_ASSERT = "enable_lwip_assert"
CONF_ENABLE_OTA_ROLLBACK = "enable_ota_rollback"
@@ -499,49 +499,24 @@ def add_idf_component(
repo: str | None = None,
ref: str | None = None,
path: str | None = None,
refresh: TimePeriod | None = None,
components: list[str] | None = None,
submodules: list[str] | None = None,
):
"""Add an esp-idf component to the project."""
if not repo and not ref and not path:
raise ValueError("Requires at least one of repo, ref or path")
if refresh or submodules or components:
_LOGGER.warning(
"The refresh, components and submodules parameters in add_idf_component() are "
"deprecated and will be removed in ESPHome 2026.1. If you are seeing this, report "
"an issue to the external_component author and ask them to update it."
)
components_registry = CORE.data[KEY_ESP32][KEY_COMPONENTS]
if components:
for comp in components:
existing = components_registry.get(comp)
if existing and existing.get(KEY_REF) != ref:
_LOGGER.warning(
"IDF component %s version conflict %s replaced by %s",
comp,
existing.get(KEY_REF),
ref,
)
components_registry[comp] = {
KEY_REPO: repo,
KEY_REF: ref,
KEY_PATH: f"{path}/{comp}" if path else comp,
}
else:
existing = components_registry.get(name)
if existing and existing.get(KEY_REF) != ref:
_LOGGER.warning(
"IDF component %s version conflict %s replaced by %s",
name,
existing.get(KEY_REF),
ref,
)
components_registry[name] = {
KEY_REPO: repo,
KEY_REF: ref,
KEY_PATH: path,
}
existing = components_registry.get(name)
if existing and existing.get(KEY_REF) != ref:
_LOGGER.warning(
"IDF component %s version conflict %s replaced by %s",
name,
existing.get(KEY_REF),
ref,
)
components_registry[name] = {
KEY_REPO: repo,
KEY_REF: ref,
KEY_PATH: path,
}
def exclude_builtin_idf_component(name: str) -> None:
@@ -613,16 +588,22 @@ def _format_framework_arduino_version(ver: cv.Version) -> str:
return f"{ARDUINO_FRAMEWORK_PKG}@https://github.com/espressif/arduino-esp32/releases/download/{ver}/{filename}"
def _format_framework_espidf_version(ver: cv.Version, release: str) -> str:
def _format_framework_espidf_version(
ver: cv.Version, release: str | None = None
) -> str:
# format the given espidf (https://github.com/pioarduino/esp-idf/releases) version to
# a PIO platformio/framework-espidf value
if ver == cv.Version(5, 4, 3) or ver >= cv.Version(5, 5, 1):
ext = "tar.xz"
else:
ext = "zip"
# Build version string with dot-separated extra (e.g., "5.5.3.1" not "5.5.3-1")
ver_str = f"{ver.major}.{ver.minor}.{ver.patch}"
if ver.extra:
ver_str += f".{ver.extra}"
if release:
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.{ext}"
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.{ext}"
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{ver_str}.{release}/esp-idf-v{ver_str}.{ext}"
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{ver_str}/esp-idf-v{ver_str}.{ext}"
def _is_framework_url(source: str) -> bool:
@@ -669,7 +650,7 @@ ARDUINO_PLATFORM_VERSION_LOOKUP = {
# These versions correspond to pioarduino/esp-idf releases
# See: https://github.com/pioarduino/esp-idf/releases
ARDUINO_IDF_VERSION_LOOKUP = {
cv.Version(3, 3, 7): cv.Version(5, 5, 2),
cv.Version(3, 3, 7): cv.Version(5, 5, 3, "1"),
cv.Version(3, 3, 6): cv.Version(5, 5, 2),
cv.Version(3, 3, 5): cv.Version(5, 5, 2),
cv.Version(3, 3, 4): cv.Version(5, 5, 1),
@@ -688,11 +669,13 @@ ARDUINO_IDF_VERSION_LOOKUP = {
# The default/recommended esp-idf framework version
# - https://github.com/espressif/esp-idf/releases
ESP_IDF_FRAMEWORK_VERSION_LOOKUP = {
"recommended": cv.Version(5, 5, 2),
"latest": cv.Version(5, 5, 2),
"dev": cv.Version(5, 5, 2),
"recommended": cv.Version(5, 5, 3, "1"),
"latest": cv.Version(5, 5, 3, "1"),
"dev": cv.Version(5, 5, 3, "1"),
}
ESP_IDF_PLATFORM_VERSION_LOOKUP = {
cv.Version(5, 5, 3, "1"): cv.Version(55, 3, 37),
cv.Version(5, 5, 3): cv.Version(55, 3, 37),
cv.Version(5, 5, 2): cv.Version(55, 3, 37),
cv.Version(5, 5, 1): cv.Version(55, 3, 31, "2"),
cv.Version(5, 5, 0): cv.Version(55, 3, 31, "2"),
@@ -755,7 +738,7 @@ def _check_versions(config):
platform_lookup = ESP_IDF_PLATFORM_VERSION_LOOKUP.get(version)
value[CONF_SOURCE] = value.get(
CONF_SOURCE,
_format_framework_espidf_version(version, value.get(CONF_RELEASE, None)),
_format_framework_espidf_version(version, value.get(CONF_RELEASE)),
)
if _is_framework_url(value[CONF_SOURCE]):
value[CONF_SOURCE] = f"pioarduino/framework-espidf@{value[CONF_SOURCE]}"
@@ -803,6 +786,15 @@ def _detect_variant(value):
# variant has already been validated against the known set
value = value.copy()
value[CONF_BOARD] = STANDARD_BOARDS[variant]
if variant == VARIANT_ESP32P4:
engineering_sample = value.get(CONF_ENGINEERING_SAMPLE)
if engineering_sample is None:
_LOGGER.warning(
"No board specified for ESP32-P4. Defaulting to production silicon (rev3). "
"If you have an early engineering sample (pre-rev3), set 'engineering_sample: true'."
)
elif engineering_sample:
value[CONF_BOARD] = "esp32-p4-evboard"
elif board in BOARDS:
variant = variant or BOARDS[board][KEY_VARIANT]
if variant != BOARDS[board][KEY_VARIANT]:
@@ -866,6 +858,30 @@ def final_validate(config):
path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_MINIMUM_CHIP_REVISION],
)
)
if (
config[CONF_VARIANT] != VARIANT_ESP32P4
and config.get(CONF_ENGINEERING_SAMPLE) is not None
):
errs.append(
cv.Invalid(
f"'{CONF_ENGINEERING_SAMPLE}' is only supported on {VARIANT_ESP32P4}",
path=[CONF_ENGINEERING_SAMPLE],
)
)
if (
config[CONF_VARIANT] == VARIANT_ESP32P4
and config.get(CONF_ENGINEERING_SAMPLE) is not None
):
board_is_es = BOARDS.get(config[CONF_BOARD], {}).get(
"engineering_sample", False
)
if config[CONF_ENGINEERING_SAMPLE] != board_is_es:
errs.append(
cv.Invalid(
f"'{CONF_ENGINEERING_SAMPLE}' does not match board '{config[CONF_BOARD]}'",
path=[CONF_ENGINEERING_SAMPLE],
)
)
if advanced[CONF_EXECUTE_FROM_PSRAM]:
if config[CONF_VARIANT] != VARIANT_ESP32S3:
errs.append(
@@ -1037,16 +1053,6 @@ def _parse_idf_component(value: str) -> ConfigType:
)
def _validate_idf_component(config: ConfigType) -> ConfigType:
"""Validate IDF component config and warn about deprecated options."""
if CONF_REFRESH in config:
_LOGGER.warning(
"The 'refresh' option for IDF components is deprecated and has no effect. "
"It will be removed in ESPHome 2026.1. Please remove it from your configuration."
)
return config
FRAMEWORK_ESP_IDF = "esp-idf"
FRAMEWORK_ARDUINO = "arduino"
FRAMEWORK_SCHEMA = cv.Schema(
@@ -1135,13 +1141,9 @@ FRAMEWORK_SCHEMA = cv.Schema(
cv.Optional(CONF_SOURCE): cv.git_ref,
cv.Optional(CONF_REF): cv.string,
cv.Optional(CONF_PATH): cv.string,
cv.Optional(CONF_REFRESH): cv.All(
cv.string, cv.source_refresh
),
}
),
),
_validate_idf_component,
)
),
}
@@ -1229,6 +1231,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_CPU_FREQUENCY): cv.one_of(
*FULL_CPU_FREQUENCIES, upper=True
),
cv.Optional(CONF_ENGINEERING_SAMPLE): cv.boolean,
cv.Optional(CONF_FLASH_SIZE, default="4MB"): cv.one_of(
*FLASH_SIZES, upper=True
),
@@ -1467,7 +1470,7 @@ async def to_code(config):
if (idf_ver := ARDUINO_IDF_VERSION_LOOKUP.get(framework_ver)) is not None:
cg.add_platformio_option(
"platform_packages",
[_format_framework_espidf_version(idf_ver, None)],
[_format_framework_espidf_version(idf_ver)],
)
# Use stub package to skip downloading precompiled libs
stubs_dir = CORE.relative_build_path("arduino_libs_stub")
@@ -1511,6 +1514,16 @@ async def to_code(config):
f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True
)
# ESP32-P4: ESP-IDF 5.5.3 changed the default of ESP32P4_SELECTS_REV_LESS_V3
# from y to n. PlatformIO uses sections.ld.in (for rev <3) or
# sections.rev3.ld.in (for rev >=3) based on board definition.
# Set the sdkconfig option to match the board's chip revision.
if variant == VARIANT_ESP32P4:
is_eng_sample = BOARDS.get(config[CONF_BOARD], {}).get(
"engineering_sample", False
)
add_idf_sdkconfig_option("CONFIG_ESP32P4_SELECTS_REV_LESS_V3", is_eng_sample)
# Set minimum chip revision for ESP32 variant
# Setting this to 3.0 or higher reduces flash size by excluding workaround code,
# and for PSRAM users saves significant IRAM by keeping C library functions in ROM.

View File

@@ -20,7 +20,7 @@ STANDARD_BOARDS = {
VARIANT_ESP32C6: "esp32-c6-devkitm-1",
VARIANT_ESP32C61: "esp32-c61-devkitc1-n8r2",
VARIANT_ESP32H2: "esp32-h2-devkitm-1",
VARIANT_ESP32P4: "esp32-p4-evboard",
VARIANT_ESP32P4: "esp32-p4_r3-evboard",
VARIANT_ESP32S2: "esp32-s2-kaluga-1",
VARIANT_ESP32S3: "esp32-s3-devkitc-1",
}
@@ -1713,10 +1713,12 @@ BOARDS = {
"esp32-p4": {
"name": "Espressif ESP32-P4 ES (pre rev.300) generic",
"variant": VARIANT_ESP32P4,
"engineering_sample": True,
},
"esp32-p4-evboard": {
"name": "Espressif ESP32-P4 Function EV Board (ES pre rev.300)",
"variant": VARIANT_ESP32P4,
"engineering_sample": True,
},
"esp32-p4_r3": {
"name": "Espressif ESP32-P4 rev.300 generic",
@@ -2141,6 +2143,7 @@ BOARDS = {
"m5stack-tab5-p4": {
"name": "M5STACK Tab5 esp32-p4 Board (ES pre rev.300)",
"variant": VARIANT_ESP32P4,
"engineering_sample": True,
},
"m5stack-timer-cam": {
"name": "M5Stack Timer CAM",

View File

@@ -9,6 +9,7 @@ from esphome import automation
import esphome.codegen as cg
from esphome.components import socket
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
from esphome.components.esp32.const import VARIANT_ESP32C2
import esphome.config_validation as cv
from esphome.const import (
CONF_ENABLE_ON_BOOT,
@@ -387,6 +388,15 @@ def final_validation(config):
f"Name '{name}' is too long, maximum length is {max_length} characters"
)
# ESP32-C2 has very limited RAM (~272KB). Without releasing BLE IRAM,
# esp_bt_controller_init fails with ESP_ERR_NO_MEM.
# CONFIG_BT_RELEASE_IRAM changes the memory layout so IRAM and DRAM share
# space more flexibly, giving the BT controller enough contiguous memory.
# This requires CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT to be disabled.
if get_esp32_variant() == VARIANT_ESP32C2:
add_idf_sdkconfig_option("CONFIG_BT_RELEASE_IRAM", True)
add_idf_sdkconfig_option("CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT", False)
# Set GATT Client/Server sdkconfig options based on which components are loaded
full_config = fv.full_config.get()
@@ -403,16 +413,16 @@ def final_validation(config):
add_idf_sdkconfig_option("CONFIG_ESP_HOSTED_ENABLE_BT_BLUEDROID", True)
add_idf_sdkconfig_option("CONFIG_ESP_HOSTED_BLUEDROID_HCI_VHCI", True)
# Check if BLE Server is needed
has_ble_server = "esp32_ble_server" in full_config
# Check if BLE Client is needed (via esp32_ble_tracker or esp32_ble_client)
has_ble_client = (
"esp32_ble_tracker" in full_config or "esp32_ble_client" in full_config
)
# Check if BLE Server is needed
has_ble_server = "esp32_ble_server" in full_config
# ESP-IDF BLE stack requires GATT Server to be enabled when GATT Client is enabled
# This is an internal dependency in the Bluedroid stack (tested ESP-IDF 5.4.2-5.5.1)
# This is an internal dependency in the Bluedroid stack
# See: https://github.com/espressif/esp-idf/issues/17724
add_idf_sdkconfig_option("CONFIG_BT_GATTS_ENABLE", has_ble_server or has_ble_client)
add_idf_sdkconfig_option("CONFIG_BT_GATTC_ENABLE", has_ble_client)

View File

@@ -202,11 +202,11 @@ async def add_pin_initial_states_array():
cg.add_global(
cg.RawExpression(
f"const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_MODE[16] PROGMEM = {{{initial_modes_s}}}"
f"constexpr uint8_t ESPHOME_ESP8266_GPIO_INITIAL_MODE[16] PROGMEM = {{{initial_modes_s}}}"
)
)
cg.add_global(
cg.RawExpression(
f"const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[16] PROGMEM = {{{initial_levels_s}}}"
f"constexpr uint8_t ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[16] PROGMEM = {{{initial_levels_s}}}"
)
)

View File

@@ -28,10 +28,9 @@ static constexpr uint32_t OTA_SOCKET_TIMEOUT_HANDSHAKE = 20000; // milliseconds
static constexpr uint32_t OTA_SOCKET_TIMEOUT_DATA = 90000; // milliseconds for data transfer
void ESPHomeOTAComponent::setup() {
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0).release(); // monitored for incoming connections
if (this->server_ == nullptr) {
this->log_socket_error_(LOG_STR("creation"));
this->mark_failed();
this->server_failed_(LOG_STR("creation"));
return;
}
int enable = 1;
@@ -42,8 +41,7 @@ void ESPHomeOTAComponent::setup() {
}
err = this->server_->setblocking(false);
if (err != 0) {
this->log_socket_error_(LOG_STR("non-blocking"));
this->mark_failed();
this->server_failed_(LOG_STR("nonblocking"));
return;
}
@@ -51,22 +49,19 @@ void ESPHomeOTAComponent::setup() {
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
if (sl == 0) {
this->log_socket_error_(LOG_STR("set sockaddr"));
this->mark_failed();
this->server_failed_(LOG_STR("set sockaddr"));
return;
}
err = this->server_->bind((struct sockaddr *) &server, sizeof(server));
if (err != 0) {
this->log_socket_error_(LOG_STR("bind"));
this->mark_failed();
this->server_failed_(LOG_STR("bind"));
return;
}
err = this->server_->listen(1); // Only one client at a time
if (err != 0) {
this->log_socket_error_(LOG_STR("listen"));
this->mark_failed();
this->server_failed_(LOG_STR("listen"));
return;
}
}
@@ -455,6 +450,15 @@ void ESPHomeOTAComponent::log_remote_closed_(const LogString *during) {
ESP_LOGW(TAG, "Remote closed at %s", LOG_STR_ARG(during));
}
void ESPHomeOTAComponent::server_failed_(const LogString *msg) {
this->log_socket_error_(msg);
// No explicit close() needed — listen sockets have no active connections on
// failure/shutdown. Destructor handles fd cleanup (close or abort per platform).
delete this->server_;
this->server_ = nullptr;
this->mark_failed();
}
bool ESPHomeOTAComponent::handle_read_error_(ssize_t read, const LogString *desc) {
if (read == -1 && this->would_block_(errno)) {
return false; // No data yet, try again next loop

View File

@@ -66,6 +66,7 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
this->handshake_buf_pos_ = 0; // Reset buffer position for next state
}
void server_failed_(const LogString *msg);
void log_socket_error_(const LogString *msg);
void log_read_error_(const LogString *what);
void log_start_(const LogString *phase);
@@ -83,7 +84,7 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
std::unique_ptr<uint8_t[]> auth_buf_;
#endif // USE_OTA_PASSWORD
std::unique_ptr<socket::Socket> server_;
socket::Socket *server_{nullptr};
std::unique_ptr<socket::Socket> client_;
std::unique_ptr<ota::OTABackend> backend_;

View File

@@ -218,12 +218,19 @@ def _validate(config):
)
elif config[CONF_TYPE] != "OPENETH":
if CONF_CLK_MODE in config:
mode, pin = CLK_MODES_DEPRECATED[config[CONF_CLK_MODE]]
LOGGER.warning(
"[ethernet] The 'clk_mode' option is deprecated and will be removed in ESPHome 2026.1. "
"Please update your configuration to use 'clk' instead."
"[ethernet] The 'clk_mode' option is deprecated. "
"Please replace 'clk_mode: %s' with:\n"
" clk:\n"
" mode: %s\n"
" pin: %s\n"
"Removal scheduled for 2026.7.0.",
config[CONF_CLK_MODE],
mode,
pin,
)
mode = CLK_MODES_DEPRECATED[config[CONF_CLK_MODE]]
config[CONF_CLK] = CLK_SCHEMA({CONF_MODE: mode[0], CONF_PIN: mode[1]})
config[CONF_CLK] = CLK_SCHEMA({CONF_MODE: mode, CONF_PIN: pin})
del config[CONF_CLK_MODE]
elif CONF_CLK not in config:
raise cv.Invalid("'clk' is a required option for [ethernet].")

View File

@@ -267,37 +267,6 @@ class I2CDevice {
bool write_byte_16(uint8_t a_register, uint16_t data) const { return write_bytes_16(a_register, &data, 1); }
// Deprecated functions
ESPDEPRECATED("The stop argument is no longer used. This will be removed from ESPHome 2026.3.0", "2025.9.0")
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop) {
return this->read_register(a_register, data, len);
}
ESPDEPRECATED("The stop argument is no longer used. This will be removed from ESPHome 2026.3.0", "2025.9.0")
ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop) {
return this->read_register16(a_register, data, len);
}
ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be "
"removed from ESPHome 2026.3.0",
"2025.9.0")
ErrorCode write(const uint8_t *data, size_t len, bool stop) const { return this->write(data, len); }
ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be "
"removed from ESPHome 2026.3.0",
"2025.9.0")
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop) const {
return this->write_register(a_register, data, len);
}
ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be "
"removed from ESPHome 2026.3.0",
"2025.9.0")
ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop) const {
return this->write_register16(a_register, data, len);
}
protected:
uint8_t address_{0x00}; ///< store the address of the device on the bus
I2CBus *bus_{nullptr}; ///< pointer to I2CBus instance

View File

@@ -1,8 +1,6 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <utility>
#include <vector>
@@ -24,18 +22,6 @@ enum ErrorCode {
ERROR_CRC = 7, ///< bytes received with a CRC error
};
/// @brief the ReadBuffer structure stores a pointer to a read buffer and its length
struct ReadBuffer {
uint8_t *data; ///< pointer to the read buffer
size_t len; ///< length of the buffer
};
/// @brief the WriteBuffer structure stores a pointer to a write buffer and its length
struct WriteBuffer {
const uint8_t *data; ///< pointer to the write buffer
size_t len; ///< length of the buffer
};
/// @brief This Class provides the methods to read and write bytes from an I2CBus.
/// @note The I2CBus virtual class follows a *Factory design pattern* that provides all the interfaces methods required
/// by clients while deferring the actual implementation of these methods to a subclasses. I2C-bus specification and
@@ -68,50 +54,6 @@ class I2CBus {
return this->write_readv(address, buffer, len, nullptr, 0);
}
ESPDEPRECATED("This method is deprecated and will be removed in ESPHome 2026.3.0. Use write_readv() instead.",
"2025.9.0")
ErrorCode readv(uint8_t address, ReadBuffer *read_buffers, size_t count) {
size_t total_len = 0;
for (size_t i = 0; i != count; i++) {
total_len += read_buffers[i].len;
}
SmallBufferWithHeapFallback<128> buffer_alloc(total_len); // Most I2C reads are small
uint8_t *buffer = buffer_alloc.get();
auto err = this->write_readv(address, nullptr, 0, buffer, total_len);
if (err != ERROR_OK)
return err;
size_t pos = 0;
for (size_t i = 0; i != count; i++) {
if (read_buffers[i].len != 0) {
std::memcpy(read_buffers[i].data, buffer + pos, read_buffers[i].len);
pos += read_buffers[i].len;
}
}
return ERROR_OK;
}
ESPDEPRECATED("This method is deprecated and will be removed in ESPHome 2026.3.0. Use write_readv() instead.",
"2025.9.0")
ErrorCode writev(uint8_t address, const WriteBuffer *write_buffers, size_t count, bool stop = true) {
size_t total_len = 0;
for (size_t i = 0; i != count; i++) {
total_len += write_buffers[i].len;
}
SmallBufferWithHeapFallback<128> buffer_alloc(total_len); // Most I2C writes are small
uint8_t *buffer = buffer_alloc.get();
size_t pos = 0;
for (size_t i = 0; i != count; i++) {
std::memcpy(buffer + pos, write_buffers[i].data, write_buffers[i].len);
pos += write_buffers[i].len;
}
return this->write_readv(address, buffer, total_len, nullptr, 0);
}
protected:
/// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair
/// that contains the address and the corresponding bool presence flag.

View File

@@ -7,7 +7,7 @@ namespace esphome {
namespace ili9xxx {
// clang-format off
static const uint8_t PROGMEM INITCMD_M5STACK[] = {
static constexpr uint8_t PROGMEM INITCMD_M5STACK[] = {
0xEF, 3, 0x03, 0x80, 0x02,
0xCF, 3, 0x00, 0xC1, 0x30,
0xED, 4, 0x64, 0x03, 0x12, 0x81,
@@ -37,7 +37,7 @@ static const uint8_t PROGMEM INITCMD_M5STACK[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_M5CORE[] = {
static constexpr uint8_t PROGMEM INITCMD_M5CORE[] = {
ILI9XXX_SETEXTC, 3, 0xFF,0x93,0x42, // Turn on the external command
ILI9XXX_PWCTR1 , 2, 0x12, 0x12,
ILI9XXX_PWCTR2 , 1, 0x03,
@@ -56,7 +56,7 @@ static const uint8_t PROGMEM INITCMD_M5CORE[] = {
static const uint8_t PROGMEM INITCMD_ILI9341[] = {
static constexpr uint8_t PROGMEM INITCMD_ILI9341[] = {
0xEF, 3, 0x03, 0x80, 0x02,
0xCF, 3, 0x00, 0xC1, 0x30,
0xED, 4, 0x64, 0x03, 0x12, 0x81,
@@ -86,7 +86,7 @@ static const uint8_t PROGMEM INITCMD_ILI9341[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_ILI9481[] = {
static constexpr uint8_t PROGMEM INITCMD_ILI9481[] = {
ILI9XXX_SLPOUT , 0x80, // Exit sleep mode
ILI9XXX_PWSET , 3, 0x07, 0x41, 0x1D,
ILI9XXX_VMCTR , 3, 0x00, 0x1C, 0x1F,
@@ -105,7 +105,7 @@ static const uint8_t PROGMEM INITCMD_ILI9481[] = {
0x00 // end
};
static const uint8_t PROGMEM INITCMD_ILI9481_18[] = {
static constexpr uint8_t PROGMEM INITCMD_ILI9481_18[] = {
ILI9XXX_SLPOUT , 0x80, // Exit sleep mode
ILI9XXX_PWSET , 3, 0x07, 0x41, 0x1D,
ILI9XXX_VMCTR , 3, 0x00, 0x1C, 0x1F,
@@ -124,7 +124,7 @@ static const uint8_t PROGMEM INITCMD_ILI9481_18[] = {
0x00 // end
};
static const uint8_t PROGMEM INITCMD_ILI9486[] = {
static constexpr uint8_t PROGMEM INITCMD_ILI9486[] = {
ILI9XXX_SLPOUT, 0x80,
ILI9XXX_PIXFMT, 1, 0x55,
ILI9XXX_PWCTR3, 1, 0x44,
@@ -173,7 +173,7 @@ static const uint8_t INITCMD_WAVESHARE_RES_3_5[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_ILI9488_A[] = {
static constexpr uint8_t PROGMEM INITCMD_ILI9488_A[] = {
ILI9XXX_GMCTRP1,15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F,
ILI9XXX_GMCTRN1,15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F,
@@ -206,7 +206,7 @@ static const uint8_t PROGMEM INITCMD_ILI9488_A[] = {
0x00 // end
};
static const uint8_t PROGMEM INITCMD_ST7796[] = {
static constexpr uint8_t PROGMEM INITCMD_ST7796[] = {
// This ST7796S initilization routine was copied from https://github.com/prenticedavid/Adafruit_ST7796S_kbv/blob/master/Adafruit_ST7796S_kbv.cpp
ILI9XXX_SWRESET, 0x80, // Soft reset, then delay 150 ms
ILI9XXX_CSCON, 1, 0xC3, // ?? Unlock Manufacturer
@@ -226,7 +226,7 @@ static const uint8_t PROGMEM INITCMD_ST7796[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_S3BOX[] = {
static constexpr uint8_t PROGMEM INITCMD_S3BOX[] = {
0xEF, 3, 0x03, 0x80, 0x02,
0xCF, 3, 0x00, 0xC1, 0x30,
0xED, 4, 0x64, 0x03, 0x12, 0x81,
@@ -256,7 +256,7 @@ static const uint8_t PROGMEM INITCMD_S3BOX[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_S3BOXLITE[] = {
static constexpr uint8_t PROGMEM INITCMD_S3BOXLITE[] = {
0xEF, 3, 0x03, 0x80, 0x02,
0xCF, 3, 0x00, 0xC1, 0x30,
0xED, 4, 0x64, 0x03, 0x12, 0x81,
@@ -286,7 +286,7 @@ static const uint8_t PROGMEM INITCMD_S3BOXLITE[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_ST7789V[] = {
static constexpr uint8_t PROGMEM INITCMD_ST7789V[] = {
ILI9XXX_SLPOUT , 0x80, // Exit Sleep
ILI9XXX_DISPON , 0x80, // Display on
ILI9XXX_MADCTL , 1, 0x08, // Memory Access Control, BGR
@@ -313,7 +313,7 @@ static const uint8_t PROGMEM INITCMD_ST7789V[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_GC9A01A[] = {
static constexpr uint8_t PROGMEM INITCMD_GC9A01A[] = {
0xEF, 0,
0xEB, 1, 0x14, // ?
0xFE, 0,
@@ -367,7 +367,7 @@ static const uint8_t PROGMEM INITCMD_GC9A01A[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_GC9D01N[] = {
static constexpr uint8_t PROGMEM INITCMD_GC9D01N[] = {
// Enable Inter_command
0xFE, 0, // Inter Register Enable 1 (FEh)
0xEF, 0, // Inter Register Enable 2 (EFh)
@@ -426,7 +426,7 @@ static const uint8_t PROGMEM INITCMD_GC9D01N[] = {
0x00 // End of list
};
static const uint8_t PROGMEM INITCMD_ST7735[] = {
static constexpr uint8_t PROGMEM INITCMD_ST7735[] = {
ILI9XXX_SWRESET, 0, // Soft reset, then delay 10ms
ILI9XXX_DELAY(10),
ILI9XXX_SLPOUT , 0, // Exit Sleep, delay

View File

@@ -15,7 +15,7 @@ static const char *const TAG = "json";
static SpiRamAllocator global_json_allocator;
#endif
std::string build_json(const json_build_t &f) {
SerializationBuffer<> build_json(const json_build_t &f) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonBuilder builder;
JsonObject root = builder.root();
@@ -66,14 +66,83 @@ JsonDocument parse_json(const uint8_t *data, size_t len) {
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
std::string JsonBuilder::serialize() {
SerializationBuffer<> JsonBuilder::serialize() {
// ===========================================================================================
// CRITICAL: NRVO (Named Return Value Optimization) - DO NOT REFACTOR WITHOUT UNDERSTANDING
// ===========================================================================================
//
// This function is carefully structured to enable NRVO. The compiler constructs `result`
// directly in the caller's stack frame, eliminating the move constructor call entirely.
//
// WITHOUT NRVO: Each return would trigger SerializationBuffer's move constructor, which
// must memcpy up to 512 bytes of stack buffer content. This happens on EVERY JSON
// serialization (sensor updates, web server responses, MQTT publishes, etc.).
//
// WITH NRVO: Zero memcpy, zero move constructor overhead. The buffer lives directly
// where the caller needs it.
//
// Requirements for NRVO to work:
// 1. Single named variable (`result`) returned from ALL paths
// 2. All paths must return the SAME variable (not different variables)
// 3. No std::move() on the return statement
//
// If you must modify this function:
// - Keep a single `result` variable declared at the top
// - All code paths must return `result` (not a different variable)
// - Verify NRVO still works by checking the disassembly for move constructor calls
// - Test: objdump -d -C firmware.elf | grep "SerializationBuffer.*SerializationBuffer"
// Should show only destructor, NOT move constructor
//
// Try stack buffer first. 640 bytes covers 99.9% of JSON payloads (sensors ~200B,
// lights ~170B, climate ~500-700B). Only entities with 40+ options exceed this.
//
// IMPORTANT: ArduinoJson's serializeJson() with a bounded buffer returns the actual
// bytes written (truncated count), NOT the would-be size like snprintf(). When the
// payload exceeds the buffer, the return value equals the buffer capacity. The heap
// fallback doubles the buffer size until the payload fits. This avoids instantiating
// measureJson()'s DummyWriter templates (~736 bytes flash) at the cost of temporarily
// over-allocating heap (at most 2x) for the rare payloads that exceed 640 bytes.
//
// ===========================================================================================
constexpr size_t buf_size = SerializationBuffer<>::BUFFER_SIZE;
SerializationBuffer<> result(buf_size - 1); // Max content size (reserve 1 for null)
if (doc_.overflowed()) {
ESP_LOGE(TAG, "JSON document overflow");
return "{}";
auto *buf = result.data_writable_();
buf[0] = '{';
buf[1] = '}';
buf[2] = '\0';
result.set_size_(2);
return result;
}
std::string output;
serializeJson(doc_, output);
return output;
size_t size = serializeJson(doc_, result.data_writable_(), buf_size);
if (size < buf_size) {
// Fits in stack buffer - update size to actual length
result.set_size_(size);
return result;
}
// Payload exceeded stack buffer. Double the buffer and retry until it fits.
// In practice, one iteration (1024 bytes) covers all known entity types.
// Payloads exceeding 1024 bytes are not known to exist in real configurations.
// Cap at 5120 as a safety limit to prevent runaway allocation.
constexpr size_t max_heap_size = 5120;
size_t heap_size = buf_size * 2;
while (heap_size <= max_heap_size) {
result.reallocate_heap_(heap_size - 1);
size = serializeJson(doc_, result.data_writable_(), heap_size);
if (size < heap_size) {
result.set_size_(size);
return result;
}
heap_size *= 2;
}
// Payload exceeds 5120 bytes - return truncated result
ESP_LOGW(TAG, "JSON payload too large, truncated to %zu bytes", size);
result.set_size_(size);
return result;
}
} // namespace json

View File

@@ -1,5 +1,7 @@
#pragma once
#include <cstring>
#include <string>
#include <vector>
#include "esphome/core/defines.h"
@@ -14,6 +16,108 @@
namespace esphome {
namespace json {
/// Buffer for JSON serialization that uses stack allocation for small payloads.
/// Template parameter STACK_SIZE specifies the stack buffer size (default 512 bytes).
/// Supports move semantics for efficient return-by-value.
template<size_t STACK_SIZE = 640> class SerializationBuffer {
public:
static constexpr size_t BUFFER_SIZE = STACK_SIZE; ///< Stack buffer size for this instantiation
/// Construct with known size (typically from measureJson)
explicit SerializationBuffer(size_t size) : size_(size) {
if (size + 1 <= STACK_SIZE) {
buffer_ = stack_buffer_;
} else {
heap_buffer_ = new char[size + 1];
buffer_ = heap_buffer_;
}
buffer_[0] = '\0';
}
~SerializationBuffer() { delete[] heap_buffer_; }
// Move constructor - works with same template instantiation
SerializationBuffer(SerializationBuffer &&other) noexcept : heap_buffer_(other.heap_buffer_), size_(other.size_) {
if (other.buffer_ == other.stack_buffer_) {
// Stack buffer - must copy content
std::memcpy(stack_buffer_, other.stack_buffer_, size_ + 1);
buffer_ = stack_buffer_;
} else {
// Heap buffer - steal ownership
buffer_ = heap_buffer_;
other.heap_buffer_ = nullptr;
}
// Leave moved-from object in valid empty state
other.stack_buffer_[0] = '\0';
other.buffer_ = other.stack_buffer_;
other.size_ = 0;
}
// Move assignment
SerializationBuffer &operator=(SerializationBuffer &&other) noexcept {
if (this != &other) {
delete[] heap_buffer_;
heap_buffer_ = other.heap_buffer_;
size_ = other.size_;
if (other.buffer_ == other.stack_buffer_) {
std::memcpy(stack_buffer_, other.stack_buffer_, size_ + 1);
buffer_ = stack_buffer_;
} else {
buffer_ = heap_buffer_;
other.heap_buffer_ = nullptr;
}
// Leave moved-from object in valid empty state
other.stack_buffer_[0] = '\0';
other.buffer_ = other.stack_buffer_;
other.size_ = 0;
}
return *this;
}
// Delete copy operations
SerializationBuffer(const SerializationBuffer &) = delete;
SerializationBuffer &operator=(const SerializationBuffer &) = delete;
/// Get null-terminated C string
const char *c_str() const { return buffer_; }
/// Get data pointer
const char *data() const { return buffer_; }
/// Get string length (excluding null terminator)
size_t size() const { return size_; }
/// Implicit conversion to std::string for backward compatibility
/// WARNING: This allocates a new std::string on the heap. Prefer using
/// c_str() or data()/size() directly when possible to avoid allocation.
operator std::string() const { return std::string(buffer_, size_); } // NOLINT(google-explicit-constructor)
private:
friend class JsonBuilder; ///< Allows JsonBuilder::serialize() to call private methods
/// Get writable buffer (for serialization)
char *data_writable_() { return buffer_; }
/// Set actual size after serialization (must not exceed allocated size)
/// Also ensures null termination for c_str() safety
void set_size_(size_t size) {
size_ = size;
buffer_[size] = '\0';
}
/// Reallocate to heap buffer with new size (for when stack buffer is too small)
/// This invalidates any previous buffer content. Used by JsonBuilder::serialize().
void reallocate_heap_(size_t size) {
delete[] heap_buffer_;
heap_buffer_ = new char[size + 1];
buffer_ = heap_buffer_;
size_ = size;
buffer_[0] = '\0';
}
char stack_buffer_[STACK_SIZE];
char *heap_buffer_{nullptr};
char *buffer_;
size_t size_;
};
#ifdef USE_PSRAM
// Build an allocator for the JSON Library using the RAMAllocator class
// This is only compiled when PSRAM is enabled
@@ -47,7 +151,8 @@ using json_parse_t = std::function<bool(JsonObject)>;
using json_build_t = std::function<void(JsonObject)>;
/// Build a JSON string with the provided json build function.
std::string build_json(const json_build_t &f);
/// Returns SerializationBuffer for stack-first allocation; implicitly converts to std::string.
SerializationBuffer<> build_json(const json_build_t &f);
/// Parse a JSON string and run the provided json parse function if it's valid.
bool parse_json(const std::string &data, const json_parse_t &f);
@@ -72,7 +177,9 @@ class JsonBuilder {
return root_;
}
std::string serialize();
/// Serialize the JSON document to a SerializationBuffer (stack-first allocation)
/// Uses 512-byte stack buffer by default, falls back to heap for larger JSON
SerializationBuffer<> serialize();
private:
#ifdef USE_PSRAM

View File

@@ -608,8 +608,9 @@ void LD2410Component::readline_(int readch) {
// We should never get here, but just in case...
ESP_LOGW(TAG, "Max command length exceeded; ignoring");
this->buffer_pos_ = 0;
return;
}
if (this->buffer_pos_ < 4) {
if (this->buffer_pos_ < HEADER_FOOTER_SIZE) {
return; // Not enough data to process yet
}
if (ld2410::validate_header_footer(DATA_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {

View File

@@ -33,8 +33,10 @@ namespace esphome::ld2410 {
using namespace ld24xx;
static constexpr uint8_t MAX_LINE_LENGTH = 46; // Max characters for serial buffer
static constexpr uint8_t TOTAL_GATES = 9; // Total number of gates supported by the LD2410
// Engineering data frame is 45 bytes; +1 for null terminator, +4 so that a frame footer always
// lands inside the buffer during footer-based resynchronization after losing sync.
static constexpr uint8_t MAX_LINE_LENGTH = 50;
static constexpr uint8_t TOTAL_GATES = 9; // Total number of gates supported by the LD2410
class LD2410Component : public Component, public uart::UARTDevice {
#ifdef USE_BINARY_SENSOR

View File

@@ -21,7 +21,9 @@
namespace esphome::ld2420 {
static constexpr uint8_t CALIBRATE_SAMPLES = 64;
static constexpr uint8_t MAX_LINE_LENGTH = 46; // Max characters for serial buffer
// Energy frame is 45 bytes; +1 for null terminator, +4 so that a frame footer always lands
// inside the buffer during footer-based resynchronization after losing sync.
static constexpr uint8_t MAX_LINE_LENGTH = 50;
static constexpr uint8_t TOTAL_GATES = 16;
enum OpMode : uint8_t {

View File

@@ -776,8 +776,9 @@ void LD2450Component::readline_(int readch) {
// We should never get here, but just in case...
ESP_LOGW(TAG, "Max command length exceeded; ignoring");
this->buffer_pos_ = 0;
return;
}
if (this->buffer_pos_ < 4) {
if (this->buffer_pos_ < HEADER_FOOTER_SIZE) {
return; // Not enough data to process yet
}
if (this->buffer_data_[this->buffer_pos_ - 2] == DATA_FRAME_FOOTER[0] &&

View File

@@ -1,5 +1,6 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/defines.h"
#include "esphome/core/component.h"
#ifdef USE_SENSOR
@@ -37,9 +38,11 @@ using namespace ld24xx;
// Constants
static constexpr uint8_t DEFAULT_PRESENCE_TIMEOUT = 5; // Timeout to reset presense status 5 sec.
static constexpr uint8_t MAX_LINE_LENGTH = 41; // Max characters for serial buffer
static constexpr uint8_t MAX_TARGETS = 3; // Max 3 Targets in LD2450
static constexpr uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450
// Zone query response is 40 bytes; +1 for null terminator, +4 so that a frame footer always
// lands inside the buffer during footer-based resynchronization after losing sync.
static constexpr uint8_t MAX_LINE_LENGTH = 45;
static constexpr uint8_t MAX_TARGETS = 3; // Max 3 Targets in LD2450
static constexpr uint8_t MAX_ZONES = 3; // Max 3 Zones in LD2450
enum Direction : uint8_t {
DIRECTION_APPROACHING = 0,

View File

@@ -101,6 +101,8 @@ CONF_INITIAL_LEVEL = "initial_level"
CONF_LOGGER_ID = "logger_id"
CONF_RUNTIME_TAG_LEVELS = "runtime_tag_levels"
CONF_TASK_LOG_BUFFER_SIZE = "task_log_buffer_size"
CONF_WAIT_FOR_CDC = "wait_for_cdc"
CONF_EARLY_MESSAGE = "early_message"
UART_SELECTION_ESP32 = {
VARIANT_ESP32: [UART0, UART1, UART2],
@@ -208,6 +210,12 @@ def validate_initial_no_higher_than_global(config):
return config
def validate_wait_for_cdc(config):
if config.get(CONF_WAIT_FOR_CDC) and config.get(CONF_HARDWARE_UART) != USB_CDC:
raise cv.Invalid("wait_for_cdc requires hardware_uart: USB_CDC")
return config
Logger = logger_ns.class_("Logger", cg.Component)
LoggerMessageTrigger = logger_ns.class_(
"LoggerMessageTrigger",
@@ -300,10 +308,18 @@ CONFIG_SCHEMA = cv.All(
cv.SplitDefault(
CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH, esp8266=True
): cv.All(cv.only_on_esp8266, cv.boolean),
cv.SplitDefault(CONF_WAIT_FOR_CDC, nrf52=False): cv.All(
cv.only_on(PLATFORM_NRF52),
cv.boolean,
),
cv.SplitDefault(CONF_EARLY_MESSAGE, nrf52=False): cv.All(
cv.only_on(PLATFORM_NRF52), cv.boolean
),
}
).extend(cv.COMPONENT_SCHEMA),
validate_local_no_higher_than_global,
validate_initial_no_higher_than_global,
validate_wait_for_cdc,
)
@@ -425,13 +441,21 @@ async def to_code(config):
except cv.Invalid:
pass
if config.get(CONF_WAIT_FOR_CDC):
cg.add_define("USE_LOGGER_WAIT_FOR_CDC")
if config.get(CONF_EARLY_MESSAGE):
cg.add_define("USE_LOGGER_EARLY_MESSAGE")
if CORE.is_nrf52:
# esphome implement own fatal error handler which save PC/LR before reset
zephyr_add_prj_conf("RESET_ON_FATAL_ERROR", False)
zephyr_add_prj_conf("THREAD_LOCAL_STORAGE", True)
if config[CONF_HARDWARE_UART] == UART0:
zephyr_add_overlay("""&uart0 { status = "okay";};""")
if config[CONF_HARDWARE_UART] == UART1:
zephyr_add_overlay("""&uart1 { status = "okay";};""")
if config[CONF_HARDWARE_UART] == USB_CDC:
cg.add_define("USE_LOGGER_UART_SELECTION_USB_CDC")
zephyr_add_prj_conf("UART_LINE_CTRL", True)
zephyr_add_cdc_acm(config, 0)

View File

@@ -170,19 +170,19 @@ void Logger::init_log_buffer(size_t total_buffer_size) {
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - allocated once, never freed
this->log_buffer_ = new logger::TaskLogBuffer(total_buffer_size);
// Zephyr needs loop working to check when CDC port is open
#if !(defined(USE_ZEPHYR) || defined(USE_LOGGER_USB_CDC))
// Start with loop disabled when using task buffer (unless using USB CDC on ESP32)
#if !(defined(USE_ZEPHYR) && defined(USE_LOGGER_UART_SELECTION_USB_CDC))
// Start with loop disabled when using task buffer
// The loop will be enabled automatically when messages arrive
// Zephyr with USB CDC needs loop active to poll port readiness via cdc_loop_()
this->disable_loop_when_buffer_empty_();
#endif
}
#endif
#if defined(USE_ESPHOME_TASK_LOG_BUFFER) || (defined(USE_ZEPHYR) && defined(USE_LOGGER_USB_CDC))
#if defined(USE_ESPHOME_TASK_LOG_BUFFER) || (defined(USE_ZEPHYR) && defined(USE_LOGGER_UART_SELECTION_USB_CDC))
void Logger::loop() {
this->process_messages_();
#if defined(USE_ZEPHYR) && defined(USE_LOGGER_USB_CDC)
#if defined(USE_ZEPHYR) && defined(USE_LOGGER_UART_SELECTION_USB_CDC)
this->cdc_loop_();
#endif
}
@@ -204,8 +204,7 @@ void Logger::process_messages_() {
this->write_log_buffer_to_console_(buf);
}
}
// Zephyr needs loop working to check when CDC port is open
#if !(defined(USE_ZEPHYR) || defined(USE_LOGGER_USB_CDC))
#if !(defined(USE_ZEPHYR) && defined(USE_LOGGER_UART_SELECTION_USB_CDC))
else {
// No messages to process, disable loop if appropriate
// This reduces overhead when there's no async logging activity
@@ -261,6 +260,9 @@ void Logger::dump_config() {
ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first, LOG_STR_ARG(get_log_level_str(it.second)));
}
#endif
#ifdef USE_ZEPHYR
dump_crash_();
#endif
}
void Logger::set_log_level(uint8_t level) {

View File

@@ -40,26 +40,25 @@ struct device;
namespace esphome::logger {
/** Interface for receiving log messages without std::function overhead.
/** Lightweight callback for receiving log messages without virtual dispatch overhead.
*
* Components can implement this interface instead of using lambdas with std::function
* to reduce flash usage from std::function type erasure machinery.
* Replaces the former LogListener virtual interface to eliminate per-implementer
* vtable sub-tables and thunk code (~39 bytes saved per class that used LogListener).
*
* Usage:
* class MyComponent : public Component, public LogListener {
* public:
* void setup() override {
* if (logger::global_logger != nullptr)
* logger::global_logger->add_log_listener(this);
* }
* void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override {
* // Handle log message
* }
* };
* // In your component's setup():
* if (logger::global_logger != nullptr)
* logger::global_logger->add_log_callback(
* this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
* static_cast<MyComponent *>(self)->on_log(level, tag, message, message_len);
* });
*/
class LogListener {
public:
virtual void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) = 0;
struct LogCallback {
void *instance;
void (*fn)(void *, uint8_t, const char *, const char *, size_t);
void invoke(uint8_t level, const char *tag, const char *message, size_t message_len) const {
this->fn(this->instance, level, tag, message, message_len);
}
};
#ifdef USE_LOGGER_LEVEL_LISTENERS
@@ -148,7 +147,7 @@ class Logger : public Component {
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
void init_log_buffer(size_t total_buffer_size);
#endif
#if defined(USE_ESPHOME_TASK_LOG_BUFFER) || (defined(USE_ZEPHYR) && defined(USE_LOGGER_USB_CDC))
#if defined(USE_ESPHOME_TASK_LOG_BUFFER) || (defined(USE_ZEPHYR) && defined(USE_LOGGER_UART_SELECTION_USB_CDC))
void loop() override;
#endif
/// Manually set the baud rate for serial, set to 0 to disable.
@@ -187,11 +186,13 @@ class Logger : public Component {
inline uint8_t level_for(const char *tag);
#ifdef USE_LOG_LISTENERS
/// Register a log listener to receive log messages
void add_log_listener(LogListener *listener) { this->log_listeners_.push_back(listener); }
/// Register a log callback to receive log messages
void add_log_callback(void *instance, void (*fn)(void *, uint8_t, const char *, const char *, size_t)) {
this->log_callbacks_.push_back(LogCallback{instance, fn});
}
#else
/// No-op when log listeners are disabled
void add_log_listener(LogListener *listener) {}
void add_log_callback(void *instance, void (*fn)(void *, uint8_t, const char *, const char *, size_t)) {}
#endif
#ifdef USE_LOGGER_LEVEL_LISTENERS
@@ -228,7 +229,7 @@ class Logger : public Component {
void log_vprintf_non_main_thread_(uint8_t level, const char *tag, int line, const char *format, va_list args,
const char *thread_name);
#endif
#if defined(USE_ZEPHYR) && defined(USE_LOGGER_USB_CDC)
#if defined(USE_ZEPHYR) && defined(USE_LOGGER_UART_SELECTION_USB_CDC)
void cdc_loop_();
#endif
void process_messages_();
@@ -253,11 +254,11 @@ class Logger : public Component {
}
#endif
// Helper to notify log listeners
// Helper to notify log callbacks
inline void HOT notify_listeners_(uint8_t level, const char *tag, const LogBuffer &buf) {
#ifdef USE_LOG_LISTENERS
for (auto *listener : this->log_listeners_)
listener->on_log(level, tag, buf.data, buf.pos);
for (auto &cb : this->log_callbacks_)
cb.invoke(level, tag, buf.data, buf.pos);
#endif
}
@@ -316,6 +317,7 @@ class Logger : public Component {
Stream *hw_serial_{nullptr};
#endif
#if defined(USE_ZEPHYR)
void dump_crash_();
const device *uart_dev_{nullptr};
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
@@ -341,8 +343,8 @@ class Logger : public Component {
std::map<const char *, uint8_t, CStrCompare> log_levels_{};
#endif
#ifdef USE_LOG_LISTENERS
StaticVector<LogListener *, ESPHOME_LOG_MAX_LISTENERS>
log_listeners_; // Log message listeners (API, MQTT, syslog, etc.)
StaticVector<LogCallback, ESPHOME_LOG_MAX_LISTENERS>
log_callbacks_; // Log message callbacks (API, MQTT, syslog, etc.)
#endif
#ifdef USE_LOGGER_LEVEL_LISTENERS
std::vector<LoggerLevelListener *> level_listeners_; // Log level change listeners
@@ -463,9 +465,9 @@ class Logger : public Component {
inline RecursionGuard make_non_main_task_guard_() { return RecursionGuard(non_main_task_recursion_guard_); }
#endif
// Zephyr needs loop working to check when CDC port is open
#if defined(USE_ESPHOME_TASK_LOG_BUFFER) && !(defined(USE_ZEPHYR) || defined(USE_LOGGER_USB_CDC))
// Disable loop when task buffer is empty (with USB CDC check on ESP32)
#if defined(USE_ESPHOME_TASK_LOG_BUFFER) && !(defined(USE_ZEPHYR) && defined(USE_LOGGER_UART_SELECTION_USB_CDC))
// Disable loop when task buffer is empty
// Zephyr with USB CDC needs loop active to poll port readiness via cdc_loop_()
inline void disable_loop_when_buffer_empty_() {
// Thread safety note: This is safe even if another task calls enable_loop_soon_any_context()
// concurrently. If that happens between our check and disable_loop(), the enable request
@@ -478,15 +480,16 @@ class Logger : public Component {
};
extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
class LoggerMessageTrigger : public Trigger<uint8_t, const char *, const char *>, public LogListener {
class LoggerMessageTrigger : public Trigger<uint8_t, const char *, const char *> {
public:
explicit LoggerMessageTrigger(Logger *parent, uint8_t level) : level_(level) { parent->add_log_listener(this); }
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override {
(void) message_len;
if (level <= this->level_) {
this->trigger(level, tag, message);
}
explicit LoggerMessageTrigger(Logger *parent, uint8_t level) : level_(level) {
parent->add_log_callback(this,
[](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
auto *trigger = static_cast<LoggerMessageTrigger *>(self);
if (level <= trigger->level_) {
trigger->trigger(level, tag, message);
}
});
}
protected:

View File

@@ -77,9 +77,10 @@ void init_uart(uart_port_t uart_num, uint32_t baud_rate, int tx_buffer_size) {
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
uart_config.source_clk = UART_SCLK_DEFAULT;
uart_param_config(uart_num, &uart_config);
const int uart_buffer_size = tx_buffer_size;
// Install UART driver using an event queue here
uart_driver_install(uart_num, uart_buffer_size, uart_buffer_size, 10, nullptr, 0);
// The logger only writes to UART, never reads, so use the minimum RX buffer.
// ESP-IDF requires rx_buffer_size > UART_HW_FIFO_LEN (128 bytes).
const int min_rx_buffer_size = UART_HW_FIFO_LEN(uart_num) + 1;
uart_driver_install(uart_num, min_rx_buffer_size, tx_buffer_size, 0, nullptr, 0);
}
void Logger::pre_setup() {

View File

@@ -8,12 +8,33 @@
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/printk.h>
#include <zephyr/usb/usb_device.h>
#ifdef USE_LOGGER_EARLY_MESSAGE
#include <esphome/components/zephyr/reset_reason.h>
#endif
namespace esphome::zephyr_coredump {
__attribute__((weak)) void print_coredump() {}
} // namespace esphome::zephyr_coredump
namespace esphome::logger {
static const uint32_t CRASH_MAGIC = 0xDEADBEEF;
__attribute__((section(".noinit"))) struct {
uint32_t magic;
uint32_t reason;
uint32_t pc;
uint32_t lr;
#if defined(CONFIG_THREAD_NAME)
char thread[CONFIG_THREAD_MAX_NAME_LEN];
#endif
} crash_buf; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
static const char *const TAG = "logger";
#ifdef USE_LOGGER_USB_CDC
#ifdef USE_LOGGER_UART_SELECTION_USB_CDC
void Logger::cdc_loop_() {
if (this->uart_ != UART_SELECTION_USB_CDC || this->uart_dev_ == nullptr) {
return;
@@ -57,10 +78,26 @@ void Logger::pre_setup() {
ESP_LOGE(TAG, "%s is not ready.", LOG_STR_ARG(get_uart_selection_()));
} else {
this->uart_dev_ = uart_dev;
#if defined(USE_LOGGER_WAIT_FOR_CDC) && defined(USE_LOGGER_UART_SELECTION_USB_CDC)
uint32_t dtr = 0;
uint32_t count = (10 * 100); // wait 10 sec for USB CDC to have early logs
while (dtr == 0 && count-- != 0) {
uart_line_ctrl_get(this->uart_dev_, UART_LINE_CTRL_DTR, &dtr);
delay(10);
arch_feed_wdt();
}
#endif
}
}
global_logger = this;
ESP_LOGI(TAG, "Log initialized");
#ifdef USE_LOGGER_EARLY_MESSAGE
char reason_buffer[zephyr::RESET_REASON_BUFFER_SIZE];
const char *reset_reason = zephyr::get_reset_reason(std::span<char, zephyr::RESET_REASON_BUFFER_SIZE>(reason_buffer));
ESP_LOGI(TAG, "Reset reason: %s", reset_reason);
dump_crash_();
zephyr_coredump::print_coredump();
#endif
}
void HOT Logger::write_msg_(const char *msg, uint16_t len) {
@@ -93,6 +130,66 @@ const LogString *Logger::get_uart_selection_() {
}
}
static const uint8_t REASON_BUF_SIZE = 32;
static const char *reason_to_str(unsigned int reason, char *buf) {
switch (reason) {
case K_ERR_CPU_EXCEPTION:
return "CPU exception";
case K_ERR_SPURIOUS_IRQ:
return "Unhandled interrupt";
case K_ERR_STACK_CHK_FAIL:
return "Stack overflow";
case K_ERR_KERNEL_OOPS:
return "Kernel oops";
case K_ERR_KERNEL_PANIC:
return "Kernel panic";
default:
snprintf(buf, REASON_BUF_SIZE, "Unknown error (%u)", reason);
return buf;
}
}
void Logger::dump_crash_() {
ESP_LOGD(TAG, "Crash buffer address %p", &crash_buf);
if (crash_buf.magic == CRASH_MAGIC) {
char reason_buf[REASON_BUF_SIZE];
ESP_LOGE(TAG, "Last crash:");
ESP_LOGE(TAG, "Reason=%s PC=0x%08x LR=0x%08x", reason_to_str(crash_buf.reason, reason_buf), crash_buf.pc,
crash_buf.lr);
#if defined(CONFIG_THREAD_NAME)
ESP_LOGE(TAG, "Thread: %s", crash_buf.thread);
#endif
}
}
void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) {
crash_buf.magic = CRASH_MAGIC;
crash_buf.reason = reason;
if (esf) {
crash_buf.pc = esf->basic.pc;
crash_buf.lr = esf->basic.lr;
}
#if defined(CONFIG_THREAD_NAME)
auto thread = k_current_get();
const char *name = k_thread_name_get(thread);
if (name) {
strncpy(crash_buf.thread, name, sizeof(crash_buf.thread) - 1);
crash_buf.thread[sizeof(crash_buf.thread) - 1] = '\0';
} else {
crash_buf.thread[0] = '\0';
}
#endif
arch_restart();
}
} // namespace esphome::logger
extern "C" {
void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf) {
esphome::logger::k_sys_fatal_error_handler(reason, esf);
}
}
#endif

View File

@@ -15,7 +15,7 @@ static const uint8_t MAX7219_REGISTER_SHUTDOWN = 0x0C;
static const uint8_t MAX7219_REGISTER_TEST = 0x0F;
static const uint8_t MAX7219_UNKNOWN_CHAR = 0b11111111;
const uint8_t MAX7219_ASCII_TO_RAW[95] PROGMEM = {
constexpr uint8_t MAX7219_ASCII_TO_RAW[95] PROGMEM = {
0b00000000, // ' ', ord 0x20
0b10110000, // '!', ord 0x21
0b00100010, // '"', ord 0x22

View File

@@ -133,12 +133,12 @@ MAX7219_ON_ACTION_SCHEMA = automation.maybe_simple_id(
@automation.register_action(
"max7129digit.invert_off", DisplayInvertAction, MAX7219_OFF_ACTION_SCHEMA
"max7219digit.invert_off", DisplayInvertAction, MAX7219_OFF_ACTION_SCHEMA
)
@automation.register_action(
"max7129digit.invert_on", DisplayInvertAction, MAX7219_ON_ACTION_SCHEMA
"max7219digit.invert_on", DisplayInvertAction, MAX7219_ON_ACTION_SCHEMA
)
async def max7129digit_invert_to_code(config, action_id, template_arg, args):
async def max7219digit_invert_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
cg.add(var.set_state(config[CONF_STATE]))
@@ -146,12 +146,12 @@ async def max7129digit_invert_to_code(config, action_id, template_arg, args):
@automation.register_action(
"max7129digit.turn_off", DisplayVisibilityAction, MAX7219_OFF_ACTION_SCHEMA
"max7219digit.turn_off", DisplayVisibilityAction, MAX7219_OFF_ACTION_SCHEMA
)
@automation.register_action(
"max7129digit.turn_on", DisplayVisibilityAction, MAX7219_ON_ACTION_SCHEMA
"max7219digit.turn_on", DisplayVisibilityAction, MAX7219_ON_ACTION_SCHEMA
)
async def max7129digit_visible_to_code(config, action_id, template_arg, args):
async def max7219digit_visible_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
cg.add(var.set_state(config[CONF_STATE]))
@@ -159,12 +159,12 @@ async def max7129digit_visible_to_code(config, action_id, template_arg, args):
@automation.register_action(
"max7129digit.reverse_off", DisplayReverseAction, MAX7219_OFF_ACTION_SCHEMA
"max7219digit.reverse_off", DisplayReverseAction, MAX7219_OFF_ACTION_SCHEMA
)
@automation.register_action(
"max7129digit.reverse_on", DisplayReverseAction, MAX7219_ON_ACTION_SCHEMA
"max7219digit.reverse_on", DisplayReverseAction, MAX7219_ON_ACTION_SCHEMA
)
async def max7129digit_reverse_to_code(config, action_id, template_arg, args):
async def max7219digit_reverse_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
cg.add(var.set_state(config[CONF_STATE]))
@@ -183,9 +183,9 @@ MAX7219_INTENSITY_SCHEMA = cv.maybe_simple_value(
@automation.register_action(
"max7129digit.intensity", DisplayIntensityAction, MAX7219_INTENSITY_SCHEMA
"max7219digit.intensity", DisplayIntensityAction, MAX7219_INTENSITY_SCHEMA
)
async def max7129digit_intensity_to_code(config, action_id, template_arg, args):
async def max7219digit_intensity_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
template_ = await cg.templatable(config[CONF_INTENSITY], args, cg.uint8)

View File

@@ -7,7 +7,7 @@ namespace max7219digit {
// bit patterns for the CP437 font
const uint8_t MAX7219_DOT_MATRIX_FONT[256][8] PROGMEM = {
constexpr uint8_t MAX7219_DOT_MATRIX_FONT[256][8] PROGMEM = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 0x00
{0x7E, 0x81, 0x95, 0xB1, 0xB1, 0x95, 0x81, 0x7E}, // 0x01
{0x7E, 0xFF, 0xEB, 0xCF, 0xCF, 0xEB, 0xFF, 0x7E}, // 0x02

View File

@@ -87,38 +87,24 @@ COLOR_DEPTHS = {
def model_schema(config):
model = MODELS[config[CONF_MODEL].upper()]
model.defaults[CONF_SWAP_XY] = cv.UNDEFINED
transform = cv.Schema(
{
cv.Required(CONF_MIRROR_X): cv.boolean,
cv.Required(CONF_MIRROR_Y): cv.boolean,
cv.Optional(CONF_SWAP_XY): cv.invalid(
"Axis swapping not supported by DSI displays"
),
}
)
if model.get_default(CONF_SWAP_XY) != cv.UNDEFINED:
transform = transform.extend(
{
cv.Optional(CONF_SWAP_XY): cv.invalid(
"Axis swapping not supported by this model"
)
}
)
else:
transform = transform.extend(
{
cv.Required(CONF_SWAP_XY): cv.boolean,
}
)
# CUSTOM model will need to provide a custom init sequence
iseqconf = (
cv.Required(CONF_INIT_SEQUENCE)
if model.initsequence is None
else cv.Optional(CONF_INIT_SEQUENCE)
)
swap_xy = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY, False)
# Dimensions are optional if the model has a default width and the swap_xy transform is not overridden
cv_dimensions = (
cv.Optional if model.get_default(CONF_WIDTH) and not swap_xy else cv.Required
)
# Dimensions are optional if the model has a default width
cv_dimensions = cv.Optional if model.get_default(CONF_WIDTH) else cv.Required
pixel_modes = (PIXEL_MODE_16BIT, PIXEL_MODE_24BIT, "16", "24")
schema = display.FULL_DISPLAY_SCHEMA.extend(
{

View File

@@ -64,7 +64,10 @@ void MQTTClientComponent::setup() {
});
#ifdef USE_LOGGER
if (this->is_log_message_enabled() && logger::global_logger != nullptr) {
logger::global_logger->add_log_listener(this);
logger::global_logger->add_log_callback(
this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
static_cast<MQTTClientComponent *>(self)->on_log(level, tag, message, message_len);
});
}
#endif
@@ -540,8 +543,8 @@ bool MQTTClientComponent::publish(const char *topic, const char *payload, size_t
}
bool MQTTClientComponent::publish_json(const char *topic, const json::json_build_t &f, uint8_t qos, bool retain) {
std::string message = json::build_json(f);
return this->publish(topic, message.c_str(), message.length(), qos, retain);
auto message = json::build_json(f);
return this->publish(topic, message.c_str(), message.size(), qos, retain);
}
void MQTTClientComponent::enable() {

View File

@@ -99,12 +99,7 @@ enum MQTTClientState {
class MQTTComponent;
class MQTTClientComponent : public Component
#ifdef USE_LOGGER
,
public logger::LogListener
#endif
{
class MQTTClientComponent : public Component {
public:
MQTTClientComponent();
@@ -252,7 +247,7 @@ class MQTTClientComponent : public Component
float get_setup_priority() const override;
#ifdef USE_LOGGER
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override;
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len);
#endif
void on_message(const std::string &topic, const std::string &payload);

View File

@@ -243,6 +243,7 @@
X(MQTT_STATE_VALUE_TEMPLATE, "stat_val_tpl", "state_value_template") \
X(MQTT_STEP, "step", "step") \
X(MQTT_SUBTYPE, "stype", "subtype") \
X(MQTT_SUGGESTED_DISPLAY_PRECISION, "sug_dsp_prc", "suggested_display_precision") \
X(MQTT_SUPPORTED_COLOR_MODES, "sup_clrm", "supported_color_modes") \
X(MQTT_SUPPORTED_FEATURES, "sup_feat", "supported_features") \
X(MQTT_SWING_MODE_COMMAND_TEMPLATE, "swing_mode_cmd_tpl", "swing_mode_command_template") \

View File

@@ -49,6 +49,10 @@ void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
root[MQTT_DEVICE_CLASS] = device_class;
}
if (this->sensor_->has_accuracy_decimals()) {
root[MQTT_SUGGESTED_DISPLAY_PRECISION] = this->sensor_->get_accuracy_decimals();
}
const auto unit_of_measurement = this->sensor_->get_unit_of_measurement_ref();
if (!unit_of_measurement.empty()) {
root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement;

View File

@@ -266,6 +266,7 @@ async def to_code(config: ConfigType) -> None:
};
"""
)
zephyr_add_prj_conf("REBOOT", True)
@coroutine_with_priority(CoroPriority.DIAGNOSTICS)

View File

@@ -1,4 +1,3 @@
import logging
from typing import Any
from esphome import automation, pins
@@ -24,7 +23,6 @@ CONF_CH2_ACTIVE = "ch2_active"
CONF_SUMMER_MODE_ACTIVE = "summer_mode_active"
CONF_DHW_BLOCK = "dhw_block"
CONF_SYNC_MODE = "sync_mode"
CONF_OPENTHERM_VERSION = "opentherm_version" # Deprecated, will be removed
CONF_BEFORE_SEND = "before_send"
CONF_BEFORE_PROCESS_RESPONSE = "before_process_response"
@@ -38,8 +36,6 @@ BeforeProcessResponseTrigger = generate.opentherm_ns.class_(
automation.Trigger.template(generate.OpenthermData.operator("ref")),
)
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
@@ -54,7 +50,6 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_SUMMER_MODE_ACTIVE, False): cv.boolean,
cv.Optional(CONF_DHW_BLOCK, False): cv.boolean,
cv.Optional(CONF_SYNC_MODE, False): cv.boolean,
cv.Optional(CONF_OPENTHERM_VERSION): cv.positive_float, # Deprecated
cv.Optional(CONF_BEFORE_SEND): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BeforeSendTrigger),
@@ -123,11 +118,6 @@ async def to_code(config: dict[str, Any]) -> None:
cg.add(getattr(var, f"set_{key}_{const.SETTING}")(value))
settings.append(key)
else:
if key == CONF_OPENTHERM_VERSION:
_LOGGER.warning(
"opentherm_version is deprecated and will be removed in esphome 2025.2.0\n"
"Please change to 'opentherm_version_controller'."
)
cg.add(getattr(var, f"set_{key}")(value))
if len(input_sensors) > 0:

View File

@@ -117,7 +117,7 @@ bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) {
}
if (this->filter_us != 0) {
uint32_t max_glitch_ns = PCNT_LL_MAX_GLITCH_WIDTH * 1000000u / (uint32_t) esp_clk_apb_freq();
uint32_t max_glitch_ns = PCNT_LL_MAX_GLITCH_WIDTH * 1000u / ((uint32_t) esp_clk_apb_freq() / 1000000u);
pcnt_glitch_filter_config_t filter_config = {
.max_glitch_ns = std::min(this->filter_us * 1000u, max_glitch_ns),
};

View File

@@ -8,10 +8,10 @@
#if defined(USE_ESP32)
#include <soc/soc_caps.h>
#ifdef SOC_PCNT_SUPPORTED
#if defined(SOC_PCNT_SUPPORTED) && __has_include(<driver/pulse_cnt.h>)
#include <driver/pulse_cnt.h>
#define HAS_PCNT
#endif // SOC_PCNT_SUPPORTED
#endif // defined(SOC_PCNT_SUPPORTED) && __has_include(<driver/pulse_cnt.h>)
#endif // USE_ESP32
namespace esphome {

View File

@@ -11,6 +11,7 @@
#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
#include <esp_ota_ops.h>
#include <esp_system.h>
#endif
namespace esphome::safe_mode {
@@ -54,6 +55,10 @@ void SafeModeComponent::dump_config() {
"OTA rollback detected! Rolled back from partition '%s'\n"
"The device reset before the boot was marked successful",
last_invalid->label);
if (esp_reset_reason() == ESP_RST_BROWNOUT) {
ESP_LOGW(TAG, "Last reset was due to brownout - check your power supply!\n"
"See https://esphome.io/guides/faq.html#brownout-detector-was-triggered");
}
}
#endif
}
@@ -99,7 +104,7 @@ bool SafeModeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t en
this->safe_mode_enable_time_ = enable_time;
this->safe_mode_boot_is_good_after_ = boot_is_good_after;
this->safe_mode_num_attempts_ = num_attempts;
this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false);
this->rtc_ = global_preferences->make_preference<uint32_t>(RTC_KEY, false);
#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
// Check partition state to detect if bootloader supports rollback

View File

@@ -11,6 +11,9 @@
namespace esphome::safe_mode {
/// RTC key for storing boot loop counter - used by safe_mode and preferences backends
constexpr uint32_t RTC_KEY = 233825507UL;
/// SafeModeComponent provides a safe way to recover from repeated boot failures
class SafeModeComponent : public Component {
public:

View File

@@ -48,6 +48,8 @@ class Sensor : public EntityBase, public EntityBase_DeviceClass, public EntityBa
int8_t get_accuracy_decimals();
/// Manually set the accuracy in decimals.
void set_accuracy_decimals(int8_t accuracy_decimals);
/// Check if the accuracy in decimals has been manually set.
bool has_accuracy_decimals() const { return this->sensor_flags_.has_accuracy_override; }
/// Get the state class, using the manual override if set.
StateClass get_state_class();

View File

@@ -680,6 +680,11 @@ class LWIPRawListenImpl final : public LWIPRawImpl {
};
std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
if (type != SOCK_STREAM) {
ESP_LOGE(TAG, "UDP sockets not supported on this platform, use WiFiUDP");
errno = EPROTOTYPE;
return nullptr;
}
auto *pcb = tcp_new();
if (pcb == nullptr)
return nullptr;

View File

@@ -59,8 +59,14 @@ size_t format_sockaddr_to(const struct sockaddr *addr_ptr, socklen_t len, std::s
#if USE_NETWORK_IPV6
else if (addr_ptr->sa_family == AF_INET6 && len >= sizeof(sockaddr_in6)) {
const auto *addr = reinterpret_cast<const struct sockaddr_in6 *>(addr_ptr);
#ifndef USE_SOCKET_IMPL_LWIP_TCP
// Format IPv4-mapped IPv6 addresses as regular IPv4 (not supported on ESP8266 raw TCP)
#ifdef USE_HOST
// Format IPv4-mapped IPv6 addresses as regular IPv4 (POSIX layout, no LWIP union)
if (IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr) &&
esphome_inet_ntop4(&addr->sin6_addr.s6_addr[12], buf.data(), buf.size()) != nullptr) {
return strlen(buf.data());
}
#elif !defined(USE_SOCKET_IMPL_LWIP_TCP)
// Format IPv4-mapped IPv6 addresses as regular IPv4 (LWIP layout)
if (addr->sin6_addr.un.u32_addr[0] == 0 && addr->sin6_addr.un.u32_addr[1] == 0 &&
addr->sin6_addr.un.u32_addr[2] == htonl(0xFFFF) &&
esphome_inet_ntop4(&addr->sin6_addr.un.u32_addr[3], buf.data(), buf.size()) != nullptr) {

View File

@@ -49,7 +49,7 @@ struct ModelDimensions {
uint8_t width;
uint8_t height;
};
static const ModelDimensions MODEL_DIMS[] PROGMEM = {
static constexpr ModelDimensions MODEL_DIMS[] PROGMEM = {
{128, 32}, // SSD1306_MODEL_128_32
{128, 64}, // SSD1306_MODEL_128_64
{96, 16}, // SSD1306_MODEL_96_16

View File

@@ -68,7 +68,7 @@ static const uint8_t ST7735_GMCTRP1 = 0xE0;
static const uint8_t ST7735_GMCTRN1 = 0xE1;
// clang-format off
static const uint8_t PROGMEM
static constexpr uint8_t PROGMEM
BCMD[] = { // Init commands for 7735B screens
18, // 18 commands in list:
ST77XX_SWRESET, ST_CMD_DELAY, // 1: Software reset, no args, w/delay

View File

@@ -18,7 +18,12 @@ constexpr int LOG_LEVEL_TO_SYSLOG_SEVERITY[] = {
7 // VERY_VERBOSE
};
void Syslog::setup() { logger::global_logger->add_log_listener(this); }
void Syslog::setup() {
logger::global_logger->add_log_callback(
this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
static_cast<Syslog *>(self)->on_log(level, tag, message, message_len);
});
}
void Syslog::on_log(uint8_t level, const char *tag, const char *message, size_t message_len) {
this->log_(level, tag, message, message_len);

View File

@@ -2,17 +2,16 @@
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/components/logger/logger.h"
#include "esphome/components/udp/udp_component.h"
#include "esphome/components/time/real_time_clock.h"
#ifdef USE_NETWORK
namespace esphome::syslog {
class Syslog : public Component, public Parented<udp::UDPComponent>, public logger::LogListener {
class Syslog : public Component, public Parented<udp::UDPComponent> {
public:
Syslog(int level, time::RealTimeClock *time) : log_level_(level), time_(time) {}
void setup() override;
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override;
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len);
void set_strip(bool strip) { this->strip_ = strip; }
void set_facility(int facility) { this->facility_ = facility; }

View File

@@ -23,7 +23,7 @@ enum Tm1621Device { TM1621_USER, TM1621_POWR316D, TM1621_THR316D };
const uint8_t TM1621_COMMANDS[] = {TM1621_SYS_EN, TM1621_LCD_ON, TM1621_BIAS, TM1621_TIMER_DIS,
TM1621_WDT_DIS, TM1621_TONE_OFF, TM1621_IRQ_DIS};
const char TM1621_KCHAR[] PROGMEM = {"0|1|2|3|4|5|6|7|8|9|-| "};
constexpr char TM1621_KCHAR[] PROGMEM = {"0|1|2|3|4|5|6|7|8|9|-| "};
// 0 1 2 3 4 5 6 7 8 9 - off
const uint8_t TM1621_DIGIT_ROW[2][12] = {{0x5F, 0x50, 0x3D, 0x79, 0x72, 0x6B, 0x6F, 0x51, 0x7F, 0x7B, 0x20, 0x00},
{0xF5, 0x05, 0xB6, 0x97, 0x47, 0xD3, 0xF3, 0x85, 0xF7, 0xD7, 0x02, 0x00}};

View File

@@ -27,7 +27,7 @@ const uint8_t TM1637_DATA_FIXED_ADDR = 0x04; //!< Fixed address
// ---
// D X
// XABCDEFG
const uint8_t TM1637_ASCII_TO_RAW[] PROGMEM = {
constexpr uint8_t TM1637_ASCII_TO_RAW[] PROGMEM = {
0b00000000, // ' ', ord 0x20
0b10110000, // '!', ord 0x21
0b00100010, // '"', ord 0x22

View File

@@ -4,7 +4,7 @@ namespace esphome {
namespace tm1638 {
namespace TM1638Translation {
const unsigned char SEVEN_SEG[] PROGMEM = {
constexpr unsigned char SEVEN_SEG[] PROGMEM = {
0x00, /* (space) */
0x86, /* ! */
0x22, /* " */

View File

@@ -19,6 +19,13 @@ namespace esphome::uart {
static const char *const TAG = "uart.idf";
/// Check if a pin number matches one of the default UART0 GPIO pins.
/// These pins may have residual state from the boot console that requires
/// explicit reset before UART reconfiguration (ESP-IDF issue #17459).
static constexpr bool is_default_uart0_pin(int8_t pin_num) {
return pin_num == U0TXD_GPIO_NUM || pin_num == U0RXD_GPIO_NUM;
}
uart_config_t IDFUARTComponent::get_config_() {
uart_parity_t parity = UART_PARITY_DISABLE;
if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
@@ -150,20 +157,26 @@ void IDFUARTComponent::load_settings(bool dump_config) {
// Commit 9ed617fb17 removed gpio_func_sel() calls from uart_set_pin(), which breaks
// UART on default UART0 pins that may have residual state from boot console.
// Reset these pins before configuring UART to ensure they're in a clean state.
if (tx == U0TXD_GPIO_NUM || tx == U0RXD_GPIO_NUM) {
if (is_default_uart0_pin(tx)) {
gpio_reset_pin(static_cast<gpio_num_t>(tx));
}
if (rx == U0TXD_GPIO_NUM || rx == U0RXD_GPIO_NUM) {
if (is_default_uart0_pin(rx)) {
gpio_reset_pin(static_cast<gpio_num_t>(rx));
}
// Setup pins after reset to preserve open drain/pullup/pulldown flags
// Setup pins after reset to configure GPIO direction and pull resistors.
// For UART0 default pins, setup() must always be called because gpio_reset_pin()
// above sets GPIO_MODE_DISABLE which disables the input buffer. Without setup(),
// uart_set_pin() on ESP-IDF 5.4.2+ does not re-enable the input buffer for
// IOMUX-connected pins, so the RX pin cannot receive data (see issue #10132).
// For other pins, only call setup() if pull or open-drain flags are set to avoid
// disturbing the default pin state which breaks some external components (#11823).
auto setup_pin_if_needed = [](InternalGPIOPin *pin) {
if (!pin) {
return;
}
const auto mask = gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN;
if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
if (is_default_uart0_pin(pin->get_pin()) || (pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
pin->setup();
}
};

View File

@@ -1,30 +1,16 @@
#include "uptime_seconds_sensor.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
namespace esphome {
namespace uptime {
namespace esphome::uptime {
static const char *const TAG = "uptime.sensor";
void UptimeSecondsSensor::update() {
const uint32_t ms = millis();
const uint64_t ms_mask = (1ULL << 32) - 1ULL;
const uint32_t last_ms = this->uptime_ & ms_mask;
if (ms < last_ms) {
this->uptime_ += ms_mask + 1ULL;
ESP_LOGD(TAG, "Detected roll-over \xf0\x9f\xa6\x84");
}
this->uptime_ &= ~ms_mask;
this->uptime_ |= ms;
// Do separate second and milliseconds conversion to avoid floating point division errors
// Probably some IEEE standard already guarantees this division can be done without loss
// of precision in a single division, but let's do it like this to be sure.
const uint64_t seconds_int = this->uptime_ / 1000ULL;
const float seconds = float(seconds_int) + (this->uptime_ % 1000ULL) / 1000.0f;
const uint64_t uptime = App.scheduler.millis_64();
const uint64_t seconds_int = uptime / 1000ULL;
const float seconds = float(seconds_int) + (uptime % 1000ULL) / 1000.0f;
this->publish_state(seconds);
}
float UptimeSecondsSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
@@ -33,5 +19,4 @@ void UptimeSecondsSensor::dump_config() {
ESP_LOGCONFIG(TAG, " Type: Seconds");
}
} // namespace uptime
} // namespace esphome
} // namespace esphome::uptime

View File

@@ -3,8 +3,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
namespace esphome {
namespace uptime {
namespace esphome::uptime {
class UptimeSecondsSensor : public sensor::Sensor, public PollingComponent {
public:
@@ -12,10 +11,6 @@ class UptimeSecondsSensor : public sensor::Sensor, public PollingComponent {
void dump_config() override;
float get_setup_priority() const override;
protected:
uint64_t uptime_{0};
};
} // namespace uptime
} // namespace esphome
} // namespace esphome::uptime

View File

@@ -6,8 +6,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace uptime {
namespace esphome::uptime {
static const char *const TAG = "uptime.sensor";
@@ -33,7 +32,6 @@ void UptimeTimestampSensor::dump_config() {
ESP_LOGCONFIG(TAG, " Type: Timestamp");
}
} // namespace uptime
} // namespace esphome
} // namespace esphome::uptime
#endif // USE_TIME

View File

@@ -8,8 +8,7 @@
#include "esphome/components/time/real_time_clock.h"
#include "esphome/core/component.h"
namespace esphome {
namespace uptime {
namespace esphome::uptime {
class UptimeTimestampSensor : public sensor::Sensor, public Component {
public:
@@ -24,7 +23,6 @@ class UptimeTimestampSensor : public sensor::Sensor, public Component {
time::RealTimeClock *time_;
};
} // namespace uptime
} // namespace esphome
} // namespace esphome::uptime
#endif // USE_TIME

View File

@@ -1,11 +1,10 @@
#include "uptime_text_sensor.h"
#include "esphome/core/hal.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace uptime {
namespace esphome::uptime {
static const char *const TAG = "uptime.sensor";
@@ -17,22 +16,10 @@ static void append_unit(char *buf, size_t buf_size, size_t &pos, const char *sep
pos = buf_append_printf(buf, buf_size, pos, "%u%s", value, label);
}
void UptimeTextSensor::setup() {
this->last_ms_ = millis();
if (this->last_ms_ < 60 * 1000)
this->last_ms_ = 0;
this->update();
}
void UptimeTextSensor::setup() { this->update(); }
void UptimeTextSensor::update() {
auto now = millis();
// get whole seconds since last update. Note that even if the millis count has overflowed between updates,
// the difference will still be correct due to the way twos-complement arithmetic works.
uint32_t delta = now - this->last_ms_;
this->last_ms_ = now - delta % 1000; // save remainder for next update
delta /= 1000;
this->uptime_ += delta;
uint32_t uptime = this->uptime_;
uint32_t uptime = static_cast<uint32_t>(App.scheduler.millis_64() / 1000);
unsigned interval = this->get_update_interval() / 1000;
// Calculate all time units
@@ -89,5 +76,4 @@ void UptimeTextSensor::update() {
float UptimeTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; }
void UptimeTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Uptime Text Sensor", this); }
} // namespace uptime
} // namespace esphome
} // namespace esphome::uptime

View File

@@ -5,8 +5,7 @@
#include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/core/component.h"
namespace esphome {
namespace uptime {
namespace esphome::uptime {
class UptimeTextSensor : public text_sensor::TextSensor, public PollingComponent {
public:
@@ -35,9 +34,6 @@ class UptimeTextSensor : public text_sensor::TextSensor, public PollingComponent
const char *seconds_text_;
const char *separator_;
bool expand_{};
uint32_t uptime_{0}; // uptime in seconds, will overflow after 136 years
uint32_t last_ms_{0};
};
} // namespace uptime
} // namespace esphome
} // namespace esphome::uptime

View File

@@ -73,12 +73,12 @@ static constexpr UBaseType_t USB_TASK_PRIORITY = 5; // Higher priority than mai
// used to report a transfer status
struct TransferStatus {
bool success;
uint16_t error_code;
uint8_t *data;
size_t data_len;
uint8_t endpoint;
void *user_data;
uint16_t error_code;
uint8_t endpoint;
bool success;
};
using transfer_cb_t = std::function<void(const TransferStatus &)>;
@@ -127,7 +127,7 @@ class USBClient : public Component {
friend class USBHost;
public:
USBClient(uint16_t vid, uint16_t pid) : vid_(vid), pid_(pid), trq_in_use_(0) {}
USBClient(uint16_t vid, uint16_t pid) : trq_in_use_(0), vid_(vid), pid_(pid) {}
void setup() override;
void loop() override;
// setup must happen after the host bus has been setup
@@ -148,6 +148,10 @@ class USBClient : public Component {
EventPool<UsbEvent, USB_EVENT_QUEUE_SIZE> event_pool;
protected:
// Process USB events from the queue. Returns true if any work was done.
// Subclasses should call this instead of USBClient::loop() to combine
// with their own work check for a single disable_loop() decision.
bool process_usb_events_();
void handle_open_state_();
TransferRequest *get_trq_(); // Lock-free allocation using atomic bitmask (multi-consumer safe)
virtual void disconnect();
@@ -161,20 +165,19 @@ class USBClient : public Component {
static void usb_task_fn(void *arg);
[[noreturn]] void usb_task_loop() const;
// Members ordered to minimize struct padding on 32-bit platforms
TransferRequest requests_[MAX_REQUESTS]{};
TaskHandle_t usb_task_handle_{nullptr};
usb_host_client_handle_t handle_{};
usb_device_handle_t device_handle_{};
int device_addr_{-1};
int state_{USB_CLIENT_INIT};
uint16_t vid_{};
uint16_t pid_{};
// Lock-free pool management using atomic bitmask (no dynamic allocation)
// Bit i = 1: requests_[i] is in use, Bit i = 0: requests_[i] is available
// Supports multiple concurrent consumers and producers (both threads can allocate/deallocate)
// Bitmask type automatically selected: uint16_t for <= 16 slots, uint32_t for 17-32 slots
std::atomic<trq_bitmask_t> trq_in_use_;
TransferRequest requests_[MAX_REQUESTS]{};
uint16_t vid_{};
uint16_t pid_{};
};
class USBHost : public Component {
public:

View File

@@ -197,6 +197,9 @@ static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *
// Push to lock-free queue (always succeeds since pool size == queue size)
client->event_queue.push(event);
// Re-enable component loop to process the queued event
client->enable_loop_soon_any_context();
// Wake main loop immediately to process USB event instead of waiting for select() timeout
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
App.wake_loop_threadsafe();
@@ -243,10 +246,13 @@ void USBClient::usb_task_loop() const {
}
}
void USBClient::loop() {
bool USBClient::process_usb_events_() {
bool had_work = false;
// Process any events from the USB task
UsbEvent *event;
while ((event = this->event_queue.pop()) != nullptr) {
had_work = true;
switch (event->type) {
case EVENT_DEVICE_NEW:
this->on_opened(event->data.device_new.address);
@@ -266,8 +272,17 @@ void USBClient::loop() {
}
if (this->state_ == USB_CLIENT_OPEN) {
had_work = true;
this->handle_open_state_();
}
return had_work;
}
void USBClient::loop() {
if (!this->process_usb_events_()) {
this->disable_loop();
}
}
void USBClient::handle_open_state_() {

View File

@@ -172,11 +172,12 @@ bool USBUartChannel::read_array(uint8_t *data, size_t len) {
}
void USBUartComponent::setup() { USBClient::setup(); }
void USBUartComponent::loop() {
USBClient::loop();
bool had_work = this->process_usb_events_();
// Process USB data from the lock-free queue
UsbDataChunk *chunk;
while ((chunk = this->usb_data_queue_.pop()) != nullptr) {
had_work = true;
auto *channel = chunk->channel;
#ifdef USE_UART_DEBUGGER
@@ -198,6 +199,11 @@ void USBUartComponent::loop() {
if (dropped > 0) {
ESP_LOGW(TAG, "Dropped %u USB data chunks due to buffer overflow", dropped);
}
// Disable loop when idle. Callbacks re-enable via enable_loop_soon_any_context().
if (!had_work) {
this->disable_loop();
}
}
void USBUartComponent::dump_config() {
USBClient::dump_config();
@@ -264,6 +270,9 @@ void USBUartComponent::start_input(USBUartChannel *channel) {
// Push always succeeds because pool size == queue size
this->usb_data_queue_.push(chunk);
// Re-enable component loop to process the queued data
this->enable_loop_soon_any_context();
// Wake main loop immediately to process USB data instead of waiting for select() timeout
#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
App.wake_loop_threadsafe();

View File

@@ -280,10 +280,8 @@ def add_resource_as_progmem(
content_encoded = gzip.compress(content_encoded)
content_encoded_size = len(content_encoded)
bytes_as_int = ", ".join(str(x) for x in content_encoded)
uint8_t = f"const uint8_t ESPHOME_WEBSERVER_{resource_name}[{content_encoded_size}] PROGMEM = {{{bytes_as_int}}}"
size_t = (
f"const size_t ESPHOME_WEBSERVER_{resource_name}_SIZE = {content_encoded_size}"
)
uint8_t = f"constexpr uint8_t ESPHOME_WEBSERVER_{resource_name}[{content_encoded_size}] PROGMEM = {{{bytes_as_int}}}"
size_t = f"constexpr size_t ESPHOME_WEBSERVER_{resource_name}_SIZE = {content_encoded_size}"
cg.add_global(cg.RawExpression(uint8_t))
cg.add_global(cg.RawExpression(size_t))

View File

@@ -9,7 +9,7 @@
namespace esphome::web_server {
#ifdef USE_WEBSERVER_GZIP
const uint8_t INDEX_GZ[] PROGMEM = {
constexpr uint8_t INDEX_GZ[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xed, 0x7d, 0xd9, 0x72, 0xdb, 0x48, 0xb6, 0xe0, 0xf3,
0xd4, 0x57, 0x40, 0x28, 0xb5, 0x8c, 0x2c, 0x26, 0xc1, 0x45, 0x92, 0x2d, 0x83, 0x4a, 0xb2, 0x65, 0xd9, 0xd5, 0x76,
0x97, 0xb7, 0xb6, 0xec, 0xda, 0x58, 0x6c, 0x09, 0x02, 0x92, 0x44, 0x96, 0x41, 0x80, 0x05, 0x24, 0xb5, 0x14, 0x89,
@@ -697,7 +697,7 @@ const uint8_t INDEX_GZ[] PROGMEM = {
0x56, 0x78, 0xff, 0xff, 0x01, 0xa2, 0x89, 0x8c, 0x0d, 0xc4, 0x97, 0x00, 0x00};
#else // Brotli (default, smaller)
const uint8_t INDEX_BR[] PROGMEM = {
constexpr uint8_t INDEX_BR[] PROGMEM = {
0x1b, 0xc3, 0x97, 0x11, 0x55, 0xb5, 0x65, 0x2c, 0x8a, 0x8a, 0x55, 0x0b, 0xd0, 0xba, 0x80, 0x1b, 0x32, 0xb0, 0x81,
0x4f, 0x27, 0x63, 0xf1, 0x7e, 0x88, 0xe3, 0xd8, 0x52, 0x84, 0x55, 0xe8, 0x35, 0x5b, 0x2b, 0x82, 0xe1, 0xed, 0x1f,
0xfd, 0xde, 0x63, 0x38, 0x3a, 0x71, 0x78, 0xb0, 0x42, 0x17, 0x15, 0x54, 0x23, 0xe1, 0xaa, 0x28, 0x11, 0x94, 0x23,

View File

@@ -9,7 +9,7 @@
namespace esphome::web_server {
#ifdef USE_WEBSERVER_GZIP
const uint8_t INDEX_GZ[] PROGMEM = {
constexpr uint8_t INDEX_GZ[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcc, 0xbd, 0x7b, 0x7f, 0x1a, 0xb9, 0xb2, 0x28, 0xfa,
0xf7, 0x3d, 0x9f, 0xc2, 0xee, 0x9d, 0xf1, 0xb4, 0x8c, 0x68, 0x03, 0x36, 0x8e, 0xd3, 0x58, 0xe6, 0xe4, 0x39, 0xc9,
0x3c, 0x92, 0x4c, 0x9c, 0x64, 0x26, 0xc3, 0xb0, 0x33, 0xa2, 0x11, 0xa0, 0xa4, 0x91, 0x98, 0x96, 0x88, 0xed, 0x01,
@@ -4104,7 +4104,7 @@ const uint8_t INDEX_GZ[] PROGMEM = {
0x37, 0x7a, 0x03, 0x00};
#else // Brotli (default, smaller)
const uint8_t INDEX_BR[] PROGMEM = {
constexpr uint8_t INDEX_BR[] PROGMEM = {
0x5b, 0x36, 0x7a, 0x53, 0xc2, 0x36, 0x06, 0x5a, 0x1f, 0xd4, 0x4e, 0x00, 0xb3, 0xd6, 0xea, 0xff, 0x0a, 0xab, 0x51,
0x94, 0xb1, 0xe6, 0xb0, 0x2e, 0x61, 0xbb, 0x1a, 0x70, 0x3b, 0xd8, 0x06, 0xfd, 0x7d, 0x2f, 0x1a, 0x00, 0x55, 0x35,
0xe3, 0xa8, 0x1c, 0x62, 0xca, 0xd3, 0xb4, 0x00, 0xdb, 0x5e, 0x43, 0xa7, 0x14, 0x08, 0xa4, 0x51, 0x99, 0x96, 0xb6,

View File

@@ -214,7 +214,7 @@ DeferredUpdateEventSource::deq_push_back_with_dedup_(void *source, message_gener
void DeferredUpdateEventSource::process_deferred_queue_() {
while (!deferred_queue_.empty()) {
DeferredEvent &de = deferred_queue_.front();
std::string message = de.message_generator_(web_server_, de.source_);
auto message = de.message_generator_(web_server_, de.source_);
if (this->send(message.c_str(), "state") != DISCARDED) {
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
deferred_queue_.erase(deferred_queue_.begin());
@@ -271,7 +271,7 @@ void DeferredUpdateEventSource::deferrable_send_state(void *source, const char *
// deferred queue still not empty which means downstream event queue full, no point trying to send first
deq_push_back_with_dedup_(source, message_generator);
} else {
std::string message = message_generator(web_server_, source);
auto message = message_generator(web_server_, source);
if (this->send(message.c_str(), "state") == DISCARDED) {
deq_push_back_with_dedup_(source, message_generator);
} else {
@@ -325,7 +325,7 @@ void DeferredUpdateEventSourceList::on_client_connect_(DeferredUpdateEventSource
ws->defer([ws, source]() {
// Configure reconnect timeout and send config
// this should always go through since the AsyncEventSourceClient event queue is empty on connect
std::string message = ws->get_config_json();
auto message = ws->get_config_json();
source->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
#ifdef USE_WEBSERVER_SORTING
@@ -334,10 +334,10 @@ void DeferredUpdateEventSourceList::on_client_connect_(DeferredUpdateEventSource
JsonObject root = builder.root();
root[ESPHOME_F("name")] = group.second.name;
root[ESPHOME_F("sorting_weight")] = group.second.weight;
message = builder.serialize();
auto group_msg = builder.serialize();
// up to 31 groups should be able to be queued initially without defer
source->try_send_nodefer(message.c_str(), "sorting_group");
source->try_send_nodefer(group_msg.c_str(), "sorting_group");
}
#endif
@@ -370,11 +370,11 @@ void WebServer::set_css_include(const char *css_include) { this->css_include_ =
void WebServer::set_js_include(const char *js_include) { this->js_include_ = js_include; }
#endif
std::string WebServer::get_config_json() {
json::SerializationBuffer<> WebServer::get_config_json() {
json::JsonBuilder builder;
JsonObject root = builder.root();
root[ESPHOME_F("title")] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name();
root[ESPHOME_F("title")] = App.get_friendly_name().empty() ? App.get_name().c_str() : App.get_friendly_name().c_str();
char comment_buffer[ESPHOME_COMMENT_SIZE];
App.get_comment_string(comment_buffer);
root[ESPHOME_F("comment")] = comment_buffer;
@@ -395,7 +395,10 @@ void WebServer::setup() {
#ifdef USE_LOGGER
if (logger::global_logger != nullptr && this->expose_log_) {
logger::global_logger->add_log_listener(this);
logger::global_logger->add_log_callback(
this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
static_cast<WebServer *>(self)->on_log(level, tag, message, message_len);
});
}
#endif
@@ -510,21 +513,27 @@ static void set_json_id(JsonObject &root, EntityBase *obj, const char *prefix, J
size_t device_len = device_name ? strlen(device_name) : 0;
#endif
// Build id into stack buffer - ArduinoJson copies the string
// Format: {prefix}/{device?}/{name}
// Single stack buffer for both id formats - ArduinoJson copies the string before we overwrite
// Buffer sizes use constants from entity_base.h validated in core/config.py
// Note: Device name uses ESPHOME_FRIENDLY_NAME_MAX_LEN (sub-device max 120), not ESPHOME_DEVICE_NAME_MAX_LEN
// (hostname)
// Without USE_DEVICES: legacy id ({prefix}-{object_id}) is the largest format
// With USE_DEVICES: name_id ({prefix}/{device}/{name}) is the largest format
static constexpr size_t LEGACY_ID_SIZE = ESPHOME_DOMAIN_MAX_LEN + 1 + OBJECT_ID_MAX_LEN;
#ifdef USE_DEVICES
static constexpr size_t ID_BUF_SIZE =
ESPHOME_DOMAIN_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1;
std::max(ESPHOME_DOMAIN_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1,
LEGACY_ID_SIZE);
#else
static constexpr size_t ID_BUF_SIZE = ESPHOME_DOMAIN_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1;
static constexpr size_t ID_BUF_SIZE =
std::max(ESPHOME_DOMAIN_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1, LEGACY_ID_SIZE);
#endif
char id_buf[ID_BUF_SIZE];
char *p = id_buf;
memcpy(p, prefix, prefix_len);
p += prefix_len;
memcpy(id_buf, prefix, prefix_len); // NOLINT(bugprone-not-null-terminated-result)
// name_id: new format {prefix}/{device?}/{name} - frontend should prefer this
// Remove in 2026.8.0 when id switches to new format permanently
char *p = id_buf + prefix_len;
*p++ = '/';
#ifdef USE_DEVICES
if (device_name) {
@@ -535,31 +544,25 @@ static void set_json_id(JsonObject &root, EntityBase *obj, const char *prefix, J
#endif
memcpy(p, name.c_str(), name_len);
p[name_len] = '\0';
// name_id: new format {prefix}/{device?}/{name} - frontend should prefer this
// Remove in 2026.8.0 when id switches to new format permanently
root[ESPHOME_F("name_id")] = id_buf;
// id: old format {prefix}-{object_id} for backward compatibility
// Will switch to new format in 2026.8.0
char legacy_buf[ESPHOME_DOMAIN_MAX_LEN + 1 + OBJECT_ID_MAX_LEN];
char *lp = legacy_buf;
memcpy(lp, prefix, prefix_len);
lp += prefix_len;
*lp++ = '-';
obj->write_object_id_to(lp, sizeof(legacy_buf) - (lp - legacy_buf));
root[ESPHOME_F("id")] = legacy_buf;
// Will switch to new format in 2026.8.0 - reuses prefix already in id_buf
id_buf[prefix_len] = '-';
obj->write_object_id_to(id_buf + prefix_len + 1, ID_BUF_SIZE - prefix_len - 1);
root[ESPHOME_F("id")] = id_buf;
if (start_config == DETAIL_ALL) {
root[ESPHOME_F("domain")] = prefix;
root[ESPHOME_F("name")] = name;
// Use .c_str() to avoid instantiating set<StringRef> template (saves ~24B)
root[ESPHOME_F("name")] = name.c_str();
#ifdef USE_DEVICES
if (device_name) {
root[ESPHOME_F("device")] = device_name;
}
#endif
#ifdef USE_ENTITY_ICON
root[ESPHOME_F("icon")] = obj->get_icon_ref();
root[ESPHOME_F("icon")] = obj->get_icon_ref().c_str();
#endif
root[ESPHOME_F("entity_category")] = obj->get_entity_category();
bool is_disabled = obj->is_disabled_by_default();
@@ -603,20 +606,20 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM
// Note: request->method() is always HTTP_GET here (canHandle ensures this)
if (entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->sensor_json_(obj, obj->state, detail);
auto data = this->sensor_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
}
request->send(404);
}
std::string WebServer::sensor_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::sensor_state_json_generator(WebServer *web_server, void *source) {
return web_server->sensor_json_((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_STATE);
}
std::string WebServer::sensor_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::sensor_all_json_generator(WebServer *web_server, void *source) {
return web_server->sensor_json_((sensor::Sensor *) (source), ((sensor::Sensor *) (source))->state, DETAIL_ALL);
}
std::string WebServer::sensor_json_(sensor::Sensor *obj, float value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::sensor_json_(sensor::Sensor *obj, float value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -629,7 +632,7 @@ std::string WebServer::sensor_json_(sensor::Sensor *obj, float value, JsonDetail
if (start_config == DETAIL_ALL) {
this->add_sorting_info_(root, obj);
if (!uom_ref.empty())
root[ESPHOME_F("uom")] = uom_ref;
root[ESPHOME_F("uom")] = uom_ref.c_str();
}
return builder.serialize();
@@ -650,23 +653,23 @@ void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const
// Note: request->method() is always HTTP_GET here (canHandle ensures this)
if (entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->text_sensor_json_(obj, obj->state, detail);
auto data = this->text_sensor_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
}
request->send(404);
}
std::string WebServer::text_sensor_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::text_sensor_state_json_generator(WebServer *web_server, void *source) {
return web_server->text_sensor_json_((text_sensor::TextSensor *) (source),
((text_sensor::TextSensor *) (source))->state, DETAIL_STATE);
}
std::string WebServer::text_sensor_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::text_sensor_all_json_generator(WebServer *web_server, void *source) {
return web_server->text_sensor_json_((text_sensor::TextSensor *) (source),
((text_sensor::TextSensor *) (source))->state, DETAIL_ALL);
}
std::string WebServer::text_sensor_json_(text_sensor::TextSensor *obj, const std::string &value,
JsonDetail start_config) {
json::SerializationBuffer<> WebServer::text_sensor_json_(text_sensor::TextSensor *obj, const std::string &value,
JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -711,7 +714,7 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->switch_json_(obj, obj->state, detail);
auto data = this->switch_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -736,13 +739,13 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM
}
request->send(404);
}
std::string WebServer::switch_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::switch_state_json_generator(WebServer *web_server, void *source) {
return web_server->switch_json_((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_STATE);
}
std::string WebServer::switch_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::switch_all_json_generator(WebServer *web_server, void *source) {
return web_server->switch_json_((switch_::Switch *) (source), ((switch_::Switch *) (source))->state, DETAIL_ALL);
}
std::string WebServer::switch_json_(switch_::Switch *obj, bool value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::switch_json_(switch_::Switch *obj, bool value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -764,7 +767,7 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
continue;
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->button_json_(obj, detail);
auto data = this->button_json_(obj, detail);
request->send(200, "application/json", data.c_str());
} else if (match.method_equals(ESPHOME_F("press"))) {
DEFER_ACTION(obj, obj->press());
@@ -777,10 +780,10 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM
}
request->send(404);
}
std::string WebServer::button_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::button_all_json_generator(WebServer *web_server, void *source) {
return web_server->button_json_((button::Button *) (source), DETAIL_ALL);
}
std::string WebServer::button_json_(button::Button *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::button_json_(button::Button *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -807,22 +810,23 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con
// Note: request->method() is always HTTP_GET here (canHandle ensures this)
if (entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->binary_sensor_json_(obj, obj->state, detail);
auto data = this->binary_sensor_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
}
request->send(404);
}
std::string WebServer::binary_sensor_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::binary_sensor_state_json_generator(WebServer *web_server, void *source) {
return web_server->binary_sensor_json_((binary_sensor::BinarySensor *) (source),
((binary_sensor::BinarySensor *) (source))->state, DETAIL_STATE);
}
std::string WebServer::binary_sensor_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::binary_sensor_all_json_generator(WebServer *web_server, void *source) {
return web_server->binary_sensor_json_((binary_sensor::BinarySensor *) (source),
((binary_sensor::BinarySensor *) (source))->state, DETAIL_ALL);
}
std::string WebServer::binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value,
JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -849,7 +853,7 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->fan_json_(obj, detail);
auto data = this->fan_json_(obj, detail);
request->send(200, "application/json", data.c_str());
} else if (match.method_equals(ESPHOME_F("toggle"))) {
DEFER_ACTION(obj, obj->toggle().perform());
@@ -890,13 +894,13 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
}
request->send(404);
}
std::string WebServer::fan_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::fan_state_json_generator(WebServer *web_server, void *source) {
return web_server->fan_json_((fan::Fan *) (source), DETAIL_STATE);
}
std::string WebServer::fan_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::fan_all_json_generator(WebServer *web_server, void *source) {
return web_server->fan_json_((fan::Fan *) (source), DETAIL_ALL);
}
std::string WebServer::fan_json_(fan::Fan *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::fan_json_(fan::Fan *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -930,7 +934,7 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->light_json_(obj, detail);
auto data = this->light_json_(obj, detail);
request->send(200, "application/json", data.c_str());
} else if (match.method_equals(ESPHOME_F("toggle"))) {
DEFER_ACTION(obj, obj->toggle().perform());
@@ -969,13 +973,13 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa
}
request->send(404);
}
std::string WebServer::light_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::light_state_json_generator(WebServer *web_server, void *source) {
return web_server->light_json_((light::LightState *) (source), DETAIL_STATE);
}
std::string WebServer::light_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::light_all_json_generator(WebServer *web_server, void *source) {
return web_server->light_json_((light::LightState *) (source), DETAIL_ALL);
}
std::string WebServer::light_json_(light::LightState *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::light_json_(light::LightState *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1009,7 +1013,7 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->cover_json_(obj, detail);
auto data = this->cover_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1057,13 +1061,13 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
}
request->send(404);
}
std::string WebServer::cover_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::cover_state_json_generator(WebServer *web_server, void *source) {
return web_server->cover_json_((cover::Cover *) (source), DETAIL_STATE);
}
std::string WebServer::cover_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::cover_all_json_generator(WebServer *web_server, void *source) {
return web_server->cover_json_((cover::Cover *) (source), DETAIL_ALL);
}
std::string WebServer::cover_json_(cover::Cover *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::cover_json_(cover::Cover *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1098,7 +1102,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->number_json_(obj, obj->state, detail);
auto data = this->number_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1117,13 +1121,13 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
request->send(404);
}
std::string WebServer::number_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::number_state_json_generator(WebServer *web_server, void *source) {
return web_server->number_json_((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_STATE);
}
std::string WebServer::number_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::number_all_json_generator(WebServer *web_server, void *source) {
return web_server->number_json_((number::Number *) (source), ((number::Number *) (source))->state, DETAIL_ALL);
}
std::string WebServer::number_json_(number::Number *obj, float value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::number_json_(number::Number *obj, float value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1144,7 +1148,7 @@ std::string WebServer::number_json_(number::Number *obj, float value, JsonDetail
root[ESPHOME_F("step")] = (value_accuracy_to_buf(val_buf, obj->traits.get_step(), accuracy), val_buf);
root[ESPHOME_F("mode")] = (int) obj->traits.get_mode();
if (!uom_ref.empty())
root[ESPHOME_F("uom")] = uom_ref;
root[ESPHOME_F("uom")] = uom_ref.c_str();
this->add_sorting_info_(root, obj);
}
@@ -1165,7 +1169,7 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
continue;
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->date_json_(obj, detail);
auto data = this->date_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1191,13 +1195,13 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
request->send(404);
}
std::string WebServer::date_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::date_state_json_generator(WebServer *web_server, void *source) {
return web_server->date_json_((datetime::DateEntity *) (source), DETAIL_STATE);
}
std::string WebServer::date_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::date_all_json_generator(WebServer *web_server, void *source) {
return web_server->date_json_((datetime::DateEntity *) (source), DETAIL_ALL);
}
std::string WebServer::date_json_(datetime::DateEntity *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::date_json_(datetime::DateEntity *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1226,7 +1230,7 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
continue;
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->time_json_(obj, detail);
auto data = this->time_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1251,13 +1255,13 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
}
request->send(404);
}
std::string WebServer::time_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::time_state_json_generator(WebServer *web_server, void *source) {
return web_server->time_json_((datetime::TimeEntity *) (source), DETAIL_STATE);
}
std::string WebServer::time_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::time_all_json_generator(WebServer *web_server, void *source) {
return web_server->time_json_((datetime::TimeEntity *) (source), DETAIL_ALL);
}
std::string WebServer::time_json_(datetime::TimeEntity *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::time_json_(datetime::TimeEntity *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1286,7 +1290,7 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
continue;
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->datetime_json_(obj, detail);
auto data = this->datetime_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1311,13 +1315,13 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
}
request->send(404);
}
std::string WebServer::datetime_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::datetime_state_json_generator(WebServer *web_server, void *source) {
return web_server->datetime_json_((datetime::DateTimeEntity *) (source), DETAIL_STATE);
}
std::string WebServer::datetime_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::datetime_all_json_generator(WebServer *web_server, void *source) {
return web_server->datetime_json_((datetime::DateTimeEntity *) (source), DETAIL_ALL);
}
std::string WebServer::datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1348,7 +1352,7 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->text_json_(obj, obj->state, detail);
auto data = this->text_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1367,13 +1371,13 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat
request->send(404);
}
std::string WebServer::text_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::text_state_json_generator(WebServer *web_server, void *source) {
return web_server->text_json_((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_STATE);
}
std::string WebServer::text_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::text_all_json_generator(WebServer *web_server, void *source) {
return web_server->text_json_((text::Text *) (source), ((text::Text *) (source))->state, DETAIL_ALL);
}
std::string WebServer::text_json_(text::Text *obj, const std::string &value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::text_json_(text::Text *obj, const std::string &value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1405,7 +1409,7 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), detail);
auto data = this->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1424,15 +1428,15 @@ void WebServer::handle_select_request(AsyncWebServerRequest *request, const UrlM
}
request->send(404);
}
std::string WebServer::select_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::select_state_json_generator(WebServer *web_server, void *source) {
auto *obj = (select::Select *) (source);
return web_server->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), DETAIL_STATE);
}
std::string WebServer::select_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::select_all_json_generator(WebServer *web_server, void *source) {
auto *obj = (select::Select *) (source);
return web_server->select_json_(obj, obj->has_state() ? obj->current_option() : StringRef(), DETAIL_ALL);
}
std::string WebServer::select_json_(select::Select *obj, StringRef value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::select_json_(select::Select *obj, StringRef value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1464,7 +1468,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->climate_json_(obj, detail);
auto data = this->climate_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1497,15 +1501,15 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
}
request->send(404);
}
std::string WebServer::climate_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::climate_state_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->climate_json_((climate::Climate *) (source), DETAIL_STATE);
}
std::string WebServer::climate_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::climate_all_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->climate_json_((climate::Climate *) (source), DETAIL_ALL);
}
std::string WebServer::climate_json_(climate::Climate *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::climate_json_(climate::Climate *obj, JsonDetail start_config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1638,7 +1642,7 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->lock_json_(obj, obj->state, detail);
auto data = this->lock_json_(obj, obj->state, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1663,13 +1667,13 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat
}
request->send(404);
}
std::string WebServer::lock_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::lock_state_json_generator(WebServer *web_server, void *source) {
return web_server->lock_json_((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_STATE);
}
std::string WebServer::lock_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::lock_all_json_generator(WebServer *web_server, void *source) {
return web_server->lock_json_((lock::Lock *) (source), ((lock::Lock *) (source))->state, DETAIL_ALL);
}
std::string WebServer::lock_json_(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::lock_json_(lock::Lock *obj, lock::LockState value, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1697,7 +1701,7 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->valve_json_(obj, detail);
auto data = this->valve_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1743,13 +1747,13 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
}
request->send(404);
}
std::string WebServer::valve_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::valve_state_json_generator(WebServer *web_server, void *source) {
return web_server->valve_json_((valve::Valve *) (source), DETAIL_STATE);
}
std::string WebServer::valve_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::valve_all_json_generator(WebServer *web_server, void *source) {
return web_server->valve_json_((valve::Valve *) (source), DETAIL_ALL);
}
std::string WebServer::valve_json_(valve::Valve *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::valve_json_(valve::Valve *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1782,7 +1786,7 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->alarm_control_panel_json_(obj, obj->get_state(), detail);
auto data = this->alarm_control_panel_json_(obj, obj->get_state(), detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1822,19 +1826,19 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques
}
request->send(404);
}
std::string WebServer::alarm_control_panel_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::alarm_control_panel_state_json_generator(WebServer *web_server, void *source) {
return web_server->alarm_control_panel_json_((alarm_control_panel::AlarmControlPanel *) (source),
((alarm_control_panel::AlarmControlPanel *) (source))->get_state(),
DETAIL_STATE);
}
std::string WebServer::alarm_control_panel_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::alarm_control_panel_all_json_generator(WebServer *web_server, void *source) {
return web_server->alarm_control_panel_json_((alarm_control_panel::AlarmControlPanel *) (source),
((alarm_control_panel::AlarmControlPanel *) (source))->get_state(),
DETAIL_ALL);
}
std::string WebServer::alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value,
JsonDetail start_config) {
json::SerializationBuffer<> WebServer::alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value,
JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -1863,7 +1867,7 @@ void WebServer::handle_water_heater_request(AsyncWebServerRequest *request, cons
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->water_heater_json_(obj, detail);
auto data = this->water_heater_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -1899,14 +1903,14 @@ void WebServer::handle_water_heater_request(AsyncWebServerRequest *request, cons
request->send(404);
}
std::string WebServer::water_heater_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::water_heater_state_json_generator(WebServer *web_server, void *source) {
return web_server->water_heater_json_(static_cast<water_heater::WaterHeater *>(source), DETAIL_STATE);
}
std::string WebServer::water_heater_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::water_heater_all_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->water_heater_json_(static_cast<water_heater::WaterHeater *>(source), DETAIL_ALL);
}
std::string WebServer::water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
char buf[PSTR_LOCAL_SIZE];
@@ -1968,7 +1972,7 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->infrared_json_(obj, detail);
auto data = this->infrared_json_(obj, detail);
request->send(200, ESPHOME_F("application/json"), data.c_str());
return;
}
@@ -2028,12 +2032,12 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur
request->send(404);
}
std::string WebServer::infrared_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::infrared_all_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->infrared_json_(static_cast<infrared::Infrared *>(source), DETAIL_ALL);
}
std::string WebServer::infrared_json_(infrared::Infrared *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::infrared_json_(infrared::Infrared *obj, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -2068,7 +2072,7 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa
// Note: request->method() is always HTTP_GET here (canHandle ensures this)
if (entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->event_json_(obj, StringRef(), detail);
auto data = this->event_json_(obj, StringRef(), detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -2078,16 +2082,16 @@ void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMa
static StringRef get_event_type(event::Event *event) { return event ? event->get_last_event_type() : StringRef(); }
std::string WebServer::event_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::event_state_json_generator(WebServer *web_server, void *source) {
auto *event = static_cast<event::Event *>(source);
return web_server->event_json_(event, get_event_type(event), DETAIL_STATE);
}
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
std::string WebServer::event_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::event_all_json_generator(WebServer *web_server, void *source) {
auto *event = static_cast<event::Event *>(source);
return web_server->event_json_(event, get_event_type(event), DETAIL_ALL);
}
std::string WebServer::event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config) {
json::JsonBuilder builder;
JsonObject root = builder.root();
@@ -2121,7 +2125,7 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM
if (request->method() == HTTP_GET && entity_match.action_is_empty) {
auto detail = get_request_detail(request);
std::string data = this->update_json_(obj, detail);
auto data = this->update_json_(obj, detail);
request->send(200, "application/json", data.c_str());
return;
}
@@ -2137,15 +2141,15 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM
}
request->send(404);
}
std::string WebServer::update_state_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::update_state_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->update_json_((update::UpdateEntity *) (source), DETAIL_STATE);
}
std::string WebServer::update_all_json_generator(WebServer *web_server, void *source) {
json::SerializationBuffer<> WebServer::update_all_json_generator(WebServer *web_server, void *source) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return web_server->update_json_((update::UpdateEntity *) (source), DETAIL_STATE);
}
std::string WebServer::update_json_(update::UpdateEntity *obj, JsonDetail start_config) {
json::SerializationBuffer<> WebServer::update_json_(update::UpdateEntity *obj, JsonDetail start_config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
json::JsonBuilder builder;
JsonObject root = builder.root();

View File

@@ -2,6 +2,7 @@
#include "list_entities.h"
#include "esphome/components/json/json_util.h"
#include "esphome/components/web_server_base/web_server_base.h"
#ifdef USE_WEBSERVER
#include "esphome/core/component.h"
@@ -103,7 +104,7 @@ enum JsonDetail { DETAIL_ALL, DETAIL_STATE };
can be forgotten.
*/
#if !defined(USE_ESP32) && defined(USE_ARDUINO)
using message_generator_t = std::string(WebServer *, void *);
using message_generator_t = json::SerializationBuffer<>(WebServer *, void *);
class DeferredUpdateEventSourceList;
class DeferredUpdateEventSource : public AsyncEventSource {
@@ -186,14 +187,7 @@ class DeferredUpdateEventSourceList : public std::list<DeferredUpdateEventSource
* under the '/light/...', '/sensor/...', ... URLs. A full documentation for this API
* can be found under https://esphome.io/web-api/.
*/
class WebServer : public Controller,
public Component,
public AsyncWebHandler
#ifdef USE_LOGGER
,
public logger::LogListener
#endif
{
class WebServer : public Controller, public Component, public AsyncWebHandler {
#if !defined(USE_ESP32) && defined(USE_ARDUINO)
friend class DeferredUpdateEventSourceList;
#endif
@@ -254,7 +248,7 @@ class WebServer : public Controller,
void dump_config() override;
#ifdef USE_LOGGER
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) override;
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len);
#endif
/// MQTT setup priority.
@@ -264,7 +258,7 @@ class WebServer : public Controller,
void handle_index_request(AsyncWebServerRequest *request);
/// Return the webserver configuration as JSON.
std::string get_config_json();
json::SerializationBuffer<> get_config_json();
#ifdef USE_WEBSERVER_CSS_INCLUDE
/// Handle included css request under '/0.css'.
@@ -286,8 +280,8 @@ class WebServer : public Controller,
/// Handle a sensor request under '/sensor/<id>'.
void handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string sensor_state_json_generator(WebServer *web_server, void *source);
static std::string sensor_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> sensor_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> sensor_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_SWITCH
@@ -296,8 +290,8 @@ class WebServer : public Controller,
/// Handle a switch request under '/switch/<id>/</turn_on/turn_off/toggle>'.
void handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string switch_state_json_generator(WebServer *web_server, void *source);
static std::string switch_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> switch_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> switch_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_BUTTON
@@ -305,7 +299,7 @@ class WebServer : public Controller,
void handle_button_request(AsyncWebServerRequest *request, const UrlMatch &match);
// Buttons are stateless, so there is no button_state_json_generator
static std::string button_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> button_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_BINARY_SENSOR
@@ -314,8 +308,8 @@ class WebServer : public Controller,
/// Handle a binary sensor request under '/binary_sensor/<id>'.
void handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string binary_sensor_state_json_generator(WebServer *web_server, void *source);
static std::string binary_sensor_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> binary_sensor_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> binary_sensor_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_FAN
@@ -324,8 +318,8 @@ class WebServer : public Controller,
/// Handle a fan request under '/fan/<id>/</turn_on/turn_off/toggle>'.
void handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string fan_state_json_generator(WebServer *web_server, void *source);
static std::string fan_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> fan_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> fan_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_LIGHT
@@ -334,8 +328,8 @@ class WebServer : public Controller,
/// Handle a light request under '/light/<id>/</turn_on/turn_off/toggle>'.
void handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string light_state_json_generator(WebServer *web_server, void *source);
static std::string light_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> light_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> light_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_TEXT_SENSOR
@@ -344,8 +338,8 @@ class WebServer : public Controller,
/// Handle a text sensor request under '/text_sensor/<id>'.
void handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string text_sensor_state_json_generator(WebServer *web_server, void *source);
static std::string text_sensor_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> text_sensor_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> text_sensor_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_COVER
@@ -354,8 +348,8 @@ class WebServer : public Controller,
/// Handle a cover request under '/cover/<id>/<open/close/stop/set>'.
void handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string cover_state_json_generator(WebServer *web_server, void *source);
static std::string cover_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> cover_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> cover_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_NUMBER
@@ -363,8 +357,8 @@ class WebServer : public Controller,
/// Handle a number request under '/number/<id>'.
void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string number_state_json_generator(WebServer *web_server, void *source);
static std::string number_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> number_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> number_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_DATETIME_DATE
@@ -372,8 +366,8 @@ class WebServer : public Controller,
/// Handle a date request under '/date/<id>'.
void handle_date_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string date_state_json_generator(WebServer *web_server, void *source);
static std::string date_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> date_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> date_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_DATETIME_TIME
@@ -381,8 +375,8 @@ class WebServer : public Controller,
/// Handle a time request under '/time/<id>'.
void handle_time_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string time_state_json_generator(WebServer *web_server, void *source);
static std::string time_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> time_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> time_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_DATETIME_DATETIME
@@ -390,8 +384,8 @@ class WebServer : public Controller,
/// Handle a datetime request under '/datetime/<id>'.
void handle_datetime_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string datetime_state_json_generator(WebServer *web_server, void *source);
static std::string datetime_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> datetime_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> datetime_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_TEXT
@@ -399,8 +393,8 @@ class WebServer : public Controller,
/// Handle a text input request under '/text/<id>'.
void handle_text_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string text_state_json_generator(WebServer *web_server, void *source);
static std::string text_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> text_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> text_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_SELECT
@@ -408,8 +402,8 @@ class WebServer : public Controller,
/// Handle a select request under '/select/<id>'.
void handle_select_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string select_state_json_generator(WebServer *web_server, void *source);
static std::string select_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> select_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> select_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_CLIMATE
@@ -417,8 +411,8 @@ class WebServer : public Controller,
/// Handle a climate request under '/climate/<id>'.
void handle_climate_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string climate_state_json_generator(WebServer *web_server, void *source);
static std::string climate_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> climate_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> climate_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_LOCK
@@ -427,8 +421,8 @@ class WebServer : public Controller,
/// Handle a lock request under '/lock/<id>/</lock/unlock/open>'.
void handle_lock_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string lock_state_json_generator(WebServer *web_server, void *source);
static std::string lock_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> lock_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> lock_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_VALVE
@@ -437,8 +431,8 @@ class WebServer : public Controller,
/// Handle a valve request under '/valve/<id>/<open/close/stop/set>'.
void handle_valve_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string valve_state_json_generator(WebServer *web_server, void *source);
static std::string valve_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> valve_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> valve_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
@@ -447,8 +441,8 @@ class WebServer : public Controller,
/// Handle a alarm_control_panel request under '/alarm_control_panel/<id>'.
void handle_alarm_control_panel_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string alarm_control_panel_state_json_generator(WebServer *web_server, void *source);
static std::string alarm_control_panel_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> alarm_control_panel_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> alarm_control_panel_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_WATER_HEATER
@@ -457,22 +451,22 @@ class WebServer : public Controller,
/// Handle a water_heater request under '/water_heater/<id>/<mode/set>'.
void handle_water_heater_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string water_heater_state_json_generator(WebServer *web_server, void *source);
static std::string water_heater_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> water_heater_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> water_heater_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_INFRARED
/// Handle an infrared request under '/infrared/<id>/transmit'.
void handle_infrared_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string infrared_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> infrared_all_json_generator(WebServer *web_server, void *source);
#endif
#ifdef USE_EVENT
void on_event(event::Event *obj) override;
static std::string event_state_json_generator(WebServer *web_server, void *source);
static std::string event_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> event_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> event_all_json_generator(WebServer *web_server, void *source);
/// Handle a event request under '/event<id>'.
void handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match);
@@ -484,8 +478,8 @@ class WebServer : public Controller,
/// Handle a update request under '/update/<id>'.
void handle_update_request(AsyncWebServerRequest *request, const UrlMatch &match);
static std::string update_state_json_generator(WebServer *web_server, void *source);
static std::string update_all_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> update_state_json_generator(WebServer *web_server, void *source);
static json::SerializationBuffer<> update_all_json_generator(WebServer *web_server, void *source);
#endif
/// Override the web handler's canHandle method.
@@ -593,71 +587,74 @@ class WebServer : public Controller,
private:
#ifdef USE_SENSOR
std::string sensor_json_(sensor::Sensor *obj, float value, JsonDetail start_config);
json::SerializationBuffer<> sensor_json_(sensor::Sensor *obj, float value, JsonDetail start_config);
#endif
#ifdef USE_SWITCH
std::string switch_json_(switch_::Switch *obj, bool value, JsonDetail start_config);
json::SerializationBuffer<> switch_json_(switch_::Switch *obj, bool value, JsonDetail start_config);
#endif
#ifdef USE_BUTTON
std::string button_json_(button::Button *obj, JsonDetail start_config);
json::SerializationBuffer<> button_json_(button::Button *obj, JsonDetail start_config);
#endif
#ifdef USE_BINARY_SENSOR
std::string binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value, JsonDetail start_config);
json::SerializationBuffer<> binary_sensor_json_(binary_sensor::BinarySensor *obj, bool value,
JsonDetail start_config);
#endif
#ifdef USE_FAN
std::string fan_json_(fan::Fan *obj, JsonDetail start_config);
json::SerializationBuffer<> fan_json_(fan::Fan *obj, JsonDetail start_config);
#endif
#ifdef USE_LIGHT
std::string light_json_(light::LightState *obj, JsonDetail start_config);
json::SerializationBuffer<> light_json_(light::LightState *obj, JsonDetail start_config);
#endif
#ifdef USE_TEXT_SENSOR
std::string text_sensor_json_(text_sensor::TextSensor *obj, const std::string &value, JsonDetail start_config);
json::SerializationBuffer<> text_sensor_json_(text_sensor::TextSensor *obj, const std::string &value,
JsonDetail start_config);
#endif
#ifdef USE_COVER
std::string cover_json_(cover::Cover *obj, JsonDetail start_config);
json::SerializationBuffer<> cover_json_(cover::Cover *obj, JsonDetail start_config);
#endif
#ifdef USE_NUMBER
std::string number_json_(number::Number *obj, float value, JsonDetail start_config);
json::SerializationBuffer<> number_json_(number::Number *obj, float value, JsonDetail start_config);
#endif
#ifdef USE_DATETIME_DATE
std::string date_json_(datetime::DateEntity *obj, JsonDetail start_config);
json::SerializationBuffer<> date_json_(datetime::DateEntity *obj, JsonDetail start_config);
#endif
#ifdef USE_DATETIME_TIME
std::string time_json_(datetime::TimeEntity *obj, JsonDetail start_config);
json::SerializationBuffer<> time_json_(datetime::TimeEntity *obj, JsonDetail start_config);
#endif
#ifdef USE_DATETIME_DATETIME
std::string datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_config);
json::SerializationBuffer<> datetime_json_(datetime::DateTimeEntity *obj, JsonDetail start_config);
#endif
#ifdef USE_TEXT
std::string text_json_(text::Text *obj, const std::string &value, JsonDetail start_config);
json::SerializationBuffer<> text_json_(text::Text *obj, const std::string &value, JsonDetail start_config);
#endif
#ifdef USE_SELECT
std::string select_json_(select::Select *obj, StringRef value, JsonDetail start_config);
json::SerializationBuffer<> select_json_(select::Select *obj, StringRef value, JsonDetail start_config);
#endif
#ifdef USE_CLIMATE
std::string climate_json_(climate::Climate *obj, JsonDetail start_config);
json::SerializationBuffer<> climate_json_(climate::Climate *obj, JsonDetail start_config);
#endif
#ifdef USE_LOCK
std::string lock_json_(lock::Lock *obj, lock::LockState value, JsonDetail start_config);
json::SerializationBuffer<> lock_json_(lock::Lock *obj, lock::LockState value, JsonDetail start_config);
#endif
#ifdef USE_VALVE
std::string valve_json_(valve::Valve *obj, JsonDetail start_config);
json::SerializationBuffer<> valve_json_(valve::Valve *obj, JsonDetail start_config);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
std::string alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value, JsonDetail start_config);
json::SerializationBuffer<> alarm_control_panel_json_(alarm_control_panel::AlarmControlPanel *obj,
alarm_control_panel::AlarmControlPanelState value,
JsonDetail start_config);
#endif
#ifdef USE_EVENT
std::string event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config);
json::SerializationBuffer<> event_json_(event::Event *obj, StringRef event_type, JsonDetail start_config);
#endif
#ifdef USE_WATER_HEATER
std::string water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config);
json::SerializationBuffer<> water_heater_json_(water_heater::WaterHeater *obj, JsonDetail start_config);
#endif
#ifdef USE_INFRARED
std::string infrared_json_(infrared::Infrared *obj, JsonDetail start_config);
json::SerializationBuffer<> infrared_json_(infrared::Infrared *obj, JsonDetail start_config);
#endif
#ifdef USE_UPDATE
std::string update_json_(update::UpdateEntity *obj, JsonDetail start_config);
json::SerializationBuffer<> update_json_(update::UpdateEntity *obj, JsonDetail start_config);
#endif
};

View File

@@ -563,7 +563,7 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *
// Configure reconnect timeout and send config
// this should always go through since the tcp send buffer is empty on connect
std::string message = ws->get_config_json();
auto message = ws->get_config_json();
this->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
#ifdef USE_WEBSERVER_SORTING
@@ -617,7 +617,7 @@ void AsyncEventSourceResponse::deq_push_back_with_dedup_(void *source, message_g
void AsyncEventSourceResponse::process_deferred_queue_() {
while (!deferred_queue_.empty()) {
DeferredEvent &de = deferred_queue_.front();
std::string message = de.message_generator_(web_server_, de.source_);
auto message = de.message_generator_(web_server_, de.source_);
if (this->try_send_nodefer(message.c_str(), "state")) {
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
deferred_queue_.erase(deferred_queue_.begin());
@@ -854,7 +854,7 @@ void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *e
// trying to send first
deq_push_back_with_dedup_(source, message_generator);
} else {
std::string message = message_generator(web_server_, source);
auto message = message_generator(web_server_, source);
if (!this->try_send_nodefer(message.c_str(), "state")) {
deq_push_back_with_dedup_(source, message_generator);
}

View File

@@ -16,6 +16,7 @@
#include <vector>
#ifdef USE_WEBSERVER
#include "esphome/components/json/json_util.h"
#include "esphome/components/web_server/list_entities.h"
#endif
@@ -250,7 +251,7 @@ class AsyncWebHandler {
class AsyncEventSource;
class AsyncEventSourceResponse;
using message_generator_t = std::string(esphome::web_server::WebServer *, void *);
using message_generator_t = json::SerializationBuffer<>(esphome::web_server::WebServer *, void *);
/*
This class holds a pointer to the source component that wants to publish a state event, and a pointer to a function

View File

@@ -1,10 +1,16 @@
import logging
import math
from esphome import automation
from esphome.automation import Condition
import esphome.codegen as cg
from esphome.components.const import CONF_USE_PSRAM
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
from esphome.components.esp32 import (
add_idf_sdkconfig_option,
const,
get_esp32_variant,
only_on_variant,
)
from esphome.components.network import (
has_high_performance_networking,
ip_address_literal,
@@ -63,6 +69,7 @@ _LOGGER = logging.getLogger(__name__)
NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2, const.VARIANT_ESP32P4]
CONF_SAVE = "save"
CONF_BAND_MODE = "band_mode"
CONF_MIN_AUTH_MODE = "min_auth_mode"
CONF_POST_CONNECT_ROAMING = "post_connect_roaming"
@@ -89,6 +96,13 @@ WIFI_POWER_SAVE_MODES = {
"HIGH": WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH,
}
WiFiBandMode = cg.global_ns.enum("wifi_band_mode_t")
WIFI_BAND_MODES = {
"AUTO": WiFiBandMode.WIFI_BAND_MODE_AUTO,
"2.4GHZ": WiFiBandMode.WIFI_BAND_MODE_2G_ONLY,
"5GHZ": WiFiBandMode.WIFI_BAND_MODE_5G_ONLY,
}
WifiMinAuthMode = wifi_ns.enum("WifiMinAuthMode")
WIFI_MIN_AUTH_MODES = {
"WPA": WifiMinAuthMode.WIFI_MIN_AUTH_MODE_WPA,
@@ -352,6 +366,11 @@ CONFIG_SCHEMA = cv.All(
cv.SplitDefault(CONF_ENABLE_RRM, esp32=False): cv.All(
cv.boolean, cv.only_on_esp32
),
cv.Optional(CONF_BAND_MODE): cv.All(
cv.enum(WIFI_BAND_MODES, upper=True),
cv.only_on_esp32,
only_on_variant(supported=[const.VARIANT_ESP32C5]),
),
cv.Optional(CONF_PASSIVE_SCAN, default=False): cv.boolean,
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
cv.Optional(CONF_POST_CONNECT_ROAMING, default=True): cv.boolean,
@@ -493,6 +512,13 @@ async def to_code(config):
cg.add(var.set_passive_scan(True))
if CONF_OUTPUT_POWER in config:
cg.add(var.set_output_power(config[CONF_OUTPUT_POWER]))
if CORE.is_esp32:
# Set PHY max TX power to match output_power so calibration also uses
# reduced power. This prevents brownout during PHY init on marginal
# power supplies, which is critical for OTA updates with rollback enabled.
# Kconfig range is 10-20, ESPHome allows 8.5-20.5
phy_tx_power = max(10, min(20, math.ceil(config[CONF_OUTPUT_POWER])))
add_idf_sdkconfig_option("CONFIG_ESP_PHY_MAX_WIFI_TX_POWER", phy_tx_power)
# enable_on_boot defaults to true in C++ - only set if false
if not config[CONF_ENABLE_ON_BOOT]:
cg.add(var.set_enable_on_boot(False))
@@ -519,6 +545,8 @@ async def to_code(config):
cg.add(var.set_btm(config[CONF_ENABLE_BTM]))
if config[CONF_ENABLE_RRM]:
cg.add(var.set_rrm(config[CONF_ENABLE_RRM]))
if CONF_BAND_MODE in config:
cg.add(var.set_band_mode(config[CONF_BAND_MODE]))
if config.get(CONF_USE_PSRAM):
add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)

View File

@@ -1469,6 +1469,22 @@ void WiFiComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Disabled");
return;
}
#if defined(USE_ESP32) && defined(SOC_WIFI_SUPPORT_5G)
const char *band_mode_s;
switch (this->band_mode_) {
case WIFI_BAND_MODE_2G_ONLY:
band_mode_s = "2.4GHz";
break;
case WIFI_BAND_MODE_5G_ONLY:
band_mode_s = "5GHz";
break;
case WIFI_BAND_MODE_AUTO:
default:
band_mode_s = "Auto";
break;
}
ESP_LOGCONFIG(TAG, " Band Mode: %s", band_mode_s);
#endif
if (this->is_connected()) {
this->print_connect_params_();
}

View File

@@ -45,6 +45,10 @@ extern "C" {
#include <WiFi.h>
#endif
#if defined(USE_ESP32) && defined(SOC_WIFI_SUPPORT_5G)
#include <esp_wifi_types.h>
#endif
#if defined(USE_ESP32) && defined(USE_WIFI_RUNTIME_POWER_SAVE)
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
@@ -435,6 +439,9 @@ class WiFiComponent : public Component {
void set_power_save_mode(WiFiPowerSaveMode power_save);
void set_min_auth_mode(WifiMinAuthMode min_auth_mode) { min_auth_mode_ = min_auth_mode; }
void set_output_power(float output_power) { output_power_ = output_power; }
#if defined(USE_ESP32) && defined(SOC_WIFI_SUPPORT_5G)
void set_band_mode(wifi_band_mode_t band_mode) { this->band_mode_ = band_mode; }
#endif
void set_passive_scan(bool passive);
@@ -652,6 +659,9 @@ class WiFiComponent : public Component {
bool wifi_sta_pre_setup_();
bool wifi_apply_output_power_(float output_power);
bool wifi_apply_power_save_();
#if defined(USE_ESP32) && defined(SOC_WIFI_SUPPORT_5G)
bool wifi_apply_band_mode_();
#endif
bool wifi_sta_ip_config_(const optional<ManualIP> &manual_ip);
bool wifi_apply_hostname_();
bool wifi_sta_connect_(const WiFiAP &ap);
@@ -774,6 +784,9 @@ class WiFiComponent : public Component {
// 1-byte enums and integers
WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF};
WiFiPowerSaveMode power_save_{WIFI_POWER_SAVE_NONE};
#if defined(USE_ESP32) && defined(SOC_WIFI_SUPPORT_5G)
wifi_band_mode_t band_mode_{WIFI_BAND_MODE_AUTO};
#endif
WifiMinAuthMode min_auth_mode_{WIFI_MIN_AUTH_MODE_WPA2};
WiFiRetryPhase retry_phase_{WiFiRetryPhase::INITIAL_CONNECT};
uint8_t num_retried_{0};

View File

@@ -292,6 +292,10 @@ bool WiFiComponent::wifi_apply_power_save_() {
return success;
}
#ifdef SOC_WIFI_SUPPORT_5G
bool WiFiComponent::wifi_apply_band_mode_() { return esp_wifi_set_band_mode(this->band_mode_) == ESP_OK; }
#endif
bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
// enable STA
if (!this->wifi_mode_(true, {}))
@@ -726,6 +730,9 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
s_sta_started = true;
// re-apply power save mode
wifi_apply_power_save_();
#ifdef SOC_WIFI_SUPPORT_5G
wifi_apply_band_mode_();
#endif
} else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_STOP) {
ESP_LOGV(TAG, "STA stop");

View File

@@ -3,8 +3,7 @@
#ifdef USE_ZEPHYR
#include "esphome/core/hal.h"
#include <zephyr/device.h>
namespace esphome {
namespace zephyr {
namespace esphome::zephyr {
class ZephyrGPIOPin : public InternalGPIOPin {
public:
@@ -39,7 +38,6 @@ class ZephyrGPIOPin : public InternalGPIOPin {
bool value_{false};
};
} // namespace zephyr
} // namespace esphome
} // namespace esphome::zephyr
#endif // USE_ZEPHYR

View File

@@ -2,12 +2,10 @@
#ifdef USE_ZEPHYR
namespace esphome {
namespace zephyr {
namespace esphome::zephyr {
void setup_preferences();
} // namespace zephyr
} // namespace esphome
}
#endif

View File

@@ -0,0 +1,63 @@
#include "reset_reason.h"
#if defined(USE_ZEPHYR) && (defined(USE_LOGGER_EARLY_MESSAGE) || defined(USE_DEBUG))
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include <zephyr/drivers/hwinfo.h>
namespace esphome::zephyr {
static const char *const TAG = "zephyr";
static size_t append_reset_reason(char *buf, size_t size, size_t pos, bool set, const char *reason) {
if (!set) {
return pos;
}
if (pos > 0) {
pos = buf_append_printf(buf, size, pos, ", ");
}
return buf_append_printf(buf, size, pos, "%s", reason);
}
const char *get_reset_reason(std::span<char, RESET_REASON_BUFFER_SIZE> buffer) {
char *buf = buffer.data();
const size_t size = RESET_REASON_BUFFER_SIZE;
uint32_t cause;
auto ret = hwinfo_get_reset_cause(&cause);
if (ret) {
ESP_LOGE(TAG, "Unable to get reset cause: %d", ret);
buf[0] = '\0';
return buf;
}
size_t pos = 0;
if (cause == 0) {
pos = append_reset_reason(buf, size, pos, true, "None");
} else {
pos = append_reset_reason(buf, size, pos, cause & RESET_PIN, "External pin");
pos = append_reset_reason(buf, size, pos, cause & RESET_SOFTWARE, "Software reset");
pos = append_reset_reason(buf, size, pos, cause & RESET_BROWNOUT, "Brownout (drop in voltage)");
pos = append_reset_reason(buf, size, pos, cause & RESET_POR, "Power-on reset (POR)");
pos = append_reset_reason(buf, size, pos, cause & RESET_WATCHDOG, "Watchdog timer expiration");
pos = append_reset_reason(buf, size, pos, cause & RESET_DEBUG, "Debug event");
pos = append_reset_reason(buf, size, pos, cause & RESET_SECURITY, "Security violation");
pos = append_reset_reason(buf, size, pos, cause & RESET_LOW_POWER_WAKE, "Waking up from low power mode");
pos = append_reset_reason(buf, size, pos, cause & RESET_CPU_LOCKUP, "CPU lock-up detected");
pos = append_reset_reason(buf, size, pos, cause & RESET_PARITY, "Parity error");
pos = append_reset_reason(buf, size, pos, cause & RESET_PLL, "PLL error");
pos = append_reset_reason(buf, size, pos, cause & RESET_CLOCK, "Clock error");
pos = append_reset_reason(buf, size, pos, cause & RESET_HARDWARE, "Hardware reset");
pos = append_reset_reason(buf, size, pos, cause & RESET_USER, "User reset");
pos = append_reset_reason(buf, size, pos, cause & RESET_TEMPERATURE, "Temperature reset");
}
// Ensure null termination if nothing was written
if (pos == 0) {
buf[0] = '\0';
}
return buf;
}
} // namespace esphome::zephyr
#endif

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