Merge remote-tracking branch 'upstream/dev' into protect_entity_setters

# Conflicts:
#	esphome/core/entity_base.cpp
#	esphome/core/entity_base.h
#	esphome/core/entity_helpers.py
#	tests/component_tests/sensor/test_sensor.py
#	tests/component_tests/text_sensor/test_text_sensor.py
This commit is contained in:
J. Nick Koston
2026-03-07 07:30:27 -10:00
86 changed files with 1166 additions and 259 deletions
+2 -2
View File
@@ -47,7 +47,7 @@ runs:
- name: Build and push to ghcr by digest
id: build-ghcr
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
@@ -73,7 +73,7 @@ runs:
- name: Build and push to dockerhub by digest
id: build-dockerhub
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
+2 -2
View File
@@ -58,7 +58,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5
uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
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@c793b717bc78562f491db7b0e93a3a178b099162 # v4.32.5
uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
with:
category: "/language:${{matrix.language}}"
+1 -1
View File
@@ -11,7 +11,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.15.3
rev: v0.15.5
hooks:
# Run the linter.
- id: ruff
+3 -3
View File
@@ -121,7 +121,7 @@ void ADE7880::update() {
this->update_sensor_from_s32_register16_(chan->forward_active_energy, AFWATTHR, [&chan](float val) {
return chan->forward_active_energy_total += val / 14400.0f;
});
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, AFWATTHR, [&chan](float val) {
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, ARWATTHR, [&chan](float val) {
return chan->reverse_active_energy_total += val / 14400.0f;
});
}
@@ -137,7 +137,7 @@ void ADE7880::update() {
this->update_sensor_from_s32_register16_(chan->forward_active_energy, BFWATTHR, [&chan](float val) {
return chan->forward_active_energy_total += val / 14400.0f;
});
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, BFWATTHR, [&chan](float val) {
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, BRWATTHR, [&chan](float val) {
return chan->reverse_active_energy_total += val / 14400.0f;
});
}
@@ -153,7 +153,7 @@ void ADE7880::update() {
this->update_sensor_from_s32_register16_(chan->forward_active_energy, CFWATTHR, [&chan](float val) {
return chan->forward_active_energy_total += val / 14400.0f;
});
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, CFWATTHR, [&chan](float val) {
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, CRWATTHR, [&chan](float val) {
return chan->reverse_active_energy_total += val / 14400.0f;
});
}
@@ -85,6 +85,9 @@ constexpr uint16_t CWATTHR = 0xE402;
constexpr uint16_t AFWATTHR = 0xE403;
constexpr uint16_t BFWATTHR = 0xE404;
constexpr uint16_t CFWATTHR = 0xE405;
constexpr uint16_t ARWATTHR = 0xE406;
constexpr uint16_t BRWATTHR = 0xE407;
constexpr uint16_t CRWATTHR = 0xE408;
constexpr uint16_t AFVARHR = 0xE409;
constexpr uint16_t BFVARHR = 0xE40A;
constexpr uint16_t CFVARHR = 0xE40B;
+132
View File
@@ -58,6 +58,7 @@ service APIConnection {
rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
rpc bluetooth_scanner_set_mode(BluetoothScannerSetModeRequest) returns (void) {}
rpc bluetooth_set_connection_params(BluetoothSetConnectionParamsRequest) returns (BluetoothSetConnectionParamsResponse) {}
rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {}
@@ -69,6 +70,12 @@ service APIConnection {
rpc zwave_proxy_request(ZWaveProxyRequest) returns (void) {}
rpc infrared_rf_transmit_raw_timings(InfraredRFTransmitRawTimingsRequest) returns (void) {}
rpc serial_proxy_configure(SerialProxyConfigureRequest) returns (void) {}
rpc serial_proxy_write(SerialProxyWriteRequest) returns (void) {}
rpc serial_proxy_set_modem_pins(SerialProxySetModemPinsRequest) returns (void) {}
rpc serial_proxy_get_modem_pins(SerialProxyGetModemPinsRequest) returns (void) {}
rpc serial_proxy_request(SerialProxyRequest) returns (void) {}
}
@@ -198,6 +205,17 @@ message DeviceInfo {
uint32 area_id = 3;
}
enum SerialProxyPortType {
SERIAL_PROXY_PORT_TYPE_TTL = 0;
SERIAL_PROXY_PORT_TYPE_RS232 = 1;
SERIAL_PROXY_PORT_TYPE_RS485 = 2;
}
message SerialProxyInfo {
string name = 1; // Human-readable port name
SerialProxyPortType port_type = 2; // Port type (RS232, RS485)
}
message DeviceInfoResponse {
option (id) = 10;
option (source) = SOURCE_SERVER;
@@ -260,6 +278,9 @@ message DeviceInfoResponse {
// Indicates if Z-Wave proxy support is available and features supported
uint32 zwave_proxy_feature_flags = 23 [(field_ifdef) = "USE_ZWAVE_PROXY"];
uint32 zwave_home_id = 24 [(field_ifdef) = "USE_ZWAVE_PROXY"];
// Serial proxy instance metadata
repeated SerialProxyInfo serial_proxies = 25 [(field_ifdef) = "USE_SERIAL_PROXY", (fixed_array_size_define) = "SERIAL_PROXY_COUNT"];
}
message ListEntitiesRequest {
@@ -2517,3 +2538,114 @@ message InfraredRFReceiveEvent {
fixed32 key = 2; // Key identifying the receiver instance
repeated sint32 timings = 3 [packed = true, (container_pointer_no_template) = "std::vector<int32_t>"]; // Raw timings in microseconds (zigzag-encoded): alternating mark/space periods
}
// ==================== SERIAL PROXY ====================
enum SerialProxyParity {
SERIAL_PROXY_PARITY_NONE = 0;
SERIAL_PROXY_PARITY_EVEN = 1;
SERIAL_PROXY_PARITY_ODD = 2;
}
// Configure UART parameters for a serial proxy instance
message SerialProxyConfigureRequest {
option (id) = 138;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SERIAL_PROXY";
uint32 instance = 1; // Instance index (0-based)
uint32 baudrate = 2; // Baud rate in bits per second
bool flow_control = 3; // Enable hardware flow control
SerialProxyParity parity = 4; // Parity setting
uint32 stop_bits = 5; // Number of stop bits (1 or 2)
uint32 data_size = 6; // Number of data bits (5-8)
}
// Data received from a serial device, forwarded to clients
message SerialProxyDataReceived {
option (id) = 139;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SERIAL_PROXY";
option (no_delay) = true;
uint32 instance = 1; // Instance index (0-based)
bytes data = 2; // Raw data received from the serial device
}
// Write data to a serial device
message SerialProxyWriteRequest {
option (id) = 140;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SERIAL_PROXY";
option (no_delay) = true;
uint32 instance = 1; // Instance index (0-based)
bytes data = 2; // Raw data to write to the serial device
}
// Set modem control pin states (RTS and DTR)
message SerialProxySetModemPinsRequest {
option (id) = 141;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SERIAL_PROXY";
uint32 instance = 1; // Instance index (0-based)
uint32 line_states = 2; // Bitmask of SerialProxyLineStateFlags
}
// Request current modem control pin states
message SerialProxyGetModemPinsRequest {
option (id) = 142;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SERIAL_PROXY";
uint32 instance = 1; // Instance index (0-based)
}
// Response with current modem control pin states
message SerialProxyGetModemPinsResponse {
option (id) = 143;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SERIAL_PROXY";
uint32 instance = 1; // Instance index (0-based)
uint32 line_states = 2; // Bitmask of SerialProxyLineStateFlags
}
enum SerialProxyRequestType {
SERIAL_PROXY_REQUEST_TYPE_SUBSCRIBE = 0; // Subscribe to receive data from this serial proxy instance
SERIAL_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1; // Unsubscribe from this serial proxy instance
SERIAL_PROXY_REQUEST_TYPE_FLUSH = 2; // Flush the serial port (block until all TX data is sent)
}
// Generic request message for simple serial proxy operations
message SerialProxyRequest {
option (id) = 144;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SERIAL_PROXY";
uint32 instance = 1; // Instance index (0-based)
SerialProxyRequestType type = 2; // Request type
}
// ==================== BLUETOOTH CONNECTION PARAMS ====================
message BluetoothSetConnectionParamsRequest {
option (id) = 145;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
uint32 min_interval = 2; // units of 1.25ms
uint32 max_interval = 3; // units of 1.25ms
uint32 latency = 4;
uint32 timeout = 5; // units of 10ms
}
message BluetoothSetConnectionParamsResponse {
option (id) = 146;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
int32 error = 2;
}
@@ -1188,6 +1188,9 @@ void APIConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScanner
bluetooth_proxy::global_bluetooth_proxy->bluetooth_scanner_set_mode(
msg.mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
}
void APIConnection::on_bluetooth_set_connection_params_request(const BluetoothSetConnectionParamsRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_set_connection_params(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
+1
View File
@@ -148,6 +148,7 @@ class APIConnection final : public APIServerConnectionBase {
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
void on_subscribe_bluetooth_connections_free_request() override;
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
void on_bluetooth_set_connection_params_request(const BluetoothSetConnectionParamsRequest &msg) override;
#endif
#ifdef USE_HOMEASSISTANT_TIME
+175 -14
View File
@@ -71,6 +71,18 @@ uint32_t DeviceInfo::calculate_size() const {
return size;
}
#endif
#ifdef USE_SERIAL_PROXY
void SerialProxyInfo::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->name);
buffer.encode_uint32(2, static_cast<uint32_t>(this->port_type));
}
uint32_t SerialProxyInfo::calculate_size() const {
uint32_t size = 0;
size += ProtoSize::calc_length(1, this->name.size());
size += ProtoSize::calc_uint32(1, static_cast<uint32_t>(this->port_type));
return size;
}
#endif
void DeviceInfoResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(2, this->name);
buffer.encode_string(3, this->mac_address);
@@ -108,16 +120,16 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer &buffer) const {
#endif
#ifdef USE_DEVICES
for (const auto &it : this->devices) {
buffer.encode_message(20, it);
buffer.encode_sub_message(20, it);
}
#endif
#ifdef USE_AREAS
for (const auto &it : this->areas) {
buffer.encode_message(21, it);
buffer.encode_sub_message(21, it);
}
#endif
#ifdef USE_AREAS
buffer.encode_message(22, this->area, false);
buffer.encode_optional_sub_message(22, this->area);
#endif
#ifdef USE_ZWAVE_PROXY
buffer.encode_uint32(23, this->zwave_proxy_feature_flags);
@@ -125,6 +137,11 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer &buffer) const {
#ifdef USE_ZWAVE_PROXY
buffer.encode_uint32(24, this->zwave_home_id);
#endif
#ifdef USE_SERIAL_PROXY
for (const auto &it : this->serial_proxies) {
buffer.encode_message(25, it);
}
#endif
}
uint32_t DeviceInfoResponse::calculate_size() const {
uint32_t size = 0;
@@ -180,6 +197,11 @@ uint32_t DeviceInfoResponse::calculate_size() const {
#endif
#ifdef USE_ZWAVE_PROXY
size += ProtoSize::calc_uint32(2, this->zwave_home_id);
#endif
#ifdef USE_SERIAL_PROXY
for (const auto &it : this->serial_proxies) {
size += ProtoSize::calc_message_force(2, it.calculate_size());
}
#endif
return size;
}
@@ -898,13 +920,13 @@ uint32_t HomeassistantServiceMap::calculate_size() const {
void HomeassistantActionRequest::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->service);
for (auto &it : this->data) {
buffer.encode_message(2, it);
buffer.encode_sub_message(2, it);
}
for (auto &it : this->data_template) {
buffer.encode_message(3, it);
buffer.encode_sub_message(3, it);
}
for (auto &it : this->variables) {
buffer.encode_message(4, it);
buffer.encode_sub_message(4, it);
}
buffer.encode_bool(5, this->is_event);
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
@@ -1104,7 +1126,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_string(1, this->name);
buffer.encode_fixed32(2, this->key);
for (auto &it : this->args) {
buffer.encode_message(3, it);
buffer.encode_sub_message(3, it);
}
buffer.encode_uint32(4, static_cast<uint32_t>(this->supports_response));
}
@@ -2111,7 +2133,7 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(7, static_cast<uint32_t>(this->entity_category));
buffer.encode_bool(8, this->supports_pause);
for (auto &it : this->supported_formats) {
buffer.encode_message(9, it);
buffer.encode_sub_message(9, it);
}
#ifdef USE_DEVICES
buffer.encode_uint32(10, this->device_id);
@@ -2242,7 +2264,7 @@ uint32_t BluetoothLERawAdvertisement::calculate_size() const {
}
void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer &buffer) const {
for (uint16_t i = 0; i < this->advertisements_len; i++) {
buffer.encode_message(1, this->advertisements[i]);
buffer.encode_sub_message(1, this->advertisements[i]);
}
}
uint32_t BluetoothLERawAdvertisementsResponse::calculate_size() const {
@@ -2321,7 +2343,7 @@ void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(2, this->handle);
buffer.encode_uint32(3, this->properties);
for (auto &it : this->descriptors) {
buffer.encode_message(4, it);
buffer.encode_sub_message(4, it);
}
buffer.encode_uint32(5, this->short_uuid);
}
@@ -2348,7 +2370,7 @@ void BluetoothGATTService::encode(ProtoWriteBuffer &buffer) const {
}
buffer.encode_uint32(2, this->handle);
for (auto &it : this->characteristics) {
buffer.encode_message(3, it);
buffer.encode_sub_message(3, it);
}
buffer.encode_uint32(4, this->short_uuid);
}
@@ -2370,7 +2392,7 @@ uint32_t BluetoothGATTService::calculate_size() const {
void BluetoothGATTGetServicesResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
for (auto &it : this->services) {
buffer.encode_message(2, it);
buffer.encode_sub_message(2, it);
}
}
uint32_t BluetoothGATTGetServicesResponse::calculate_size() const {
@@ -2651,7 +2673,7 @@ 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, false);
buffer.encode_optional_sub_message(4, this->audio_settings);
buffer.encode_string(5, this->wake_word_phrase);
}
uint32_t VoiceAssistantRequest::calculate_size() const {
@@ -2884,7 +2906,7 @@ bool VoiceAssistantConfigurationRequest::decode_length(uint32_t field_id, ProtoL
}
void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer &buffer) const {
for (auto &it : this->available_wake_words) {
buffer.encode_message(1, it);
buffer.encode_sub_message(1, it);
}
for (const auto &it : *this->active_wake_words) {
buffer.encode_string(2, it, true);
@@ -3714,5 +3736,144 @@ uint32_t InfraredRFReceiveEvent::calculate_size() const {
return size;
}
#endif
#ifdef USE_SERIAL_PROXY
bool SerialProxyConfigureRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->instance = value.as_uint32();
break;
case 2:
this->baudrate = value.as_uint32();
break;
case 3:
this->flow_control = value.as_bool();
break;
case 4:
this->parity = static_cast<enums::SerialProxyParity>(value.as_uint32());
break;
case 5:
this->stop_bits = value.as_uint32();
break;
case 6:
this->data_size = value.as_uint32();
break;
default:
return false;
}
return true;
}
void SerialProxyDataReceived::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, this->instance);
buffer.encode_bytes(2, this->data_ptr_, this->data_len_);
}
uint32_t SerialProxyDataReceived::calculate_size() const {
uint32_t size = 0;
size += ProtoSize::calc_uint32(1, this->instance);
size += ProtoSize::calc_length(1, this->data_len_);
return size;
}
bool SerialProxyWriteRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->instance = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool SerialProxyWriteRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->data = value.data();
this->data_len = value.size();
break;
}
default:
return false;
}
return true;
}
bool SerialProxySetModemPinsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->instance = value.as_uint32();
break;
case 2:
this->line_states = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool SerialProxyGetModemPinsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->instance = value.as_uint32();
break;
default:
return false;
}
return true;
}
void SerialProxyGetModemPinsResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint32(1, this->instance);
buffer.encode_uint32(2, this->line_states);
}
uint32_t SerialProxyGetModemPinsResponse::calculate_size() const {
uint32_t size = 0;
size += ProtoSize::calc_uint32(1, this->instance);
size += ProtoSize::calc_uint32(1, this->line_states);
return size;
}
bool SerialProxyRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->instance = value.as_uint32();
break;
case 2:
this->type = static_cast<enums::SerialProxyRequestType>(value.as_uint32());
break;
default:
return false;
}
return true;
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool BluetoothSetConnectionParamsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->address = value.as_uint64();
break;
case 2:
this->min_interval = value.as_uint32();
break;
case 3:
this->max_interval = value.as_uint32();
break;
case 4:
this->latency = value.as_uint32();
break;
case 5:
this->timeout = value.as_uint32();
break;
default:
return false;
}
return true;
}
void BluetoothSetConnectionParamsResponse::encode(ProtoWriteBuffer &buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_int32(2, this->error);
}
uint32_t BluetoothSetConnectionParamsResponse::calculate_size() const {
uint32_t size = 0;
size += ProtoSize::calc_uint64(1, this->address);
size += ProtoSize::calc_int32(1, this->error);
return size;
}
#endif
} // namespace esphome::api
+199 -1
View File
@@ -11,6 +11,11 @@ namespace esphome::api {
namespace enums {
enum SerialProxyPortType : uint32_t {
SERIAL_PROXY_PORT_TYPE_TTL = 0,
SERIAL_PROXY_PORT_TYPE_RS232 = 1,
SERIAL_PROXY_PORT_TYPE_RS485 = 2,
};
enum EntityCategory : uint32_t {
ENTITY_CATEGORY_NONE = 0,
ENTITY_CATEGORY_CONFIG = 1,
@@ -317,6 +322,18 @@ enum ZWaveProxyRequestType : uint32_t {
ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE = 2,
};
#endif
#ifdef USE_SERIAL_PROXY
enum SerialProxyParity : uint32_t {
SERIAL_PROXY_PARITY_NONE = 0,
SERIAL_PROXY_PARITY_EVEN = 1,
SERIAL_PROXY_PARITY_ODD = 2,
};
enum SerialProxyRequestType : uint32_t {
SERIAL_PROXY_REQUEST_TYPE_SUBSCRIBE = 0,
SERIAL_PROXY_REQUEST_TYPE_UNSUBSCRIBE = 1,
SERIAL_PROXY_REQUEST_TYPE_FLUSH = 2,
};
#endif
} // namespace enums
@@ -477,10 +494,24 @@ class DeviceInfo final : public ProtoMessage {
protected:
};
#endif
#ifdef USE_SERIAL_PROXY
class SerialProxyInfo final : public ProtoMessage {
public:
StringRef name{};
enums::SerialProxyPortType port_type{};
void encode(ProtoWriteBuffer &buffer) const;
uint32_t calculate_size() const;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
#endif
class DeviceInfoResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 10;
static constexpr uint8_t ESTIMATED_SIZE = 255;
static constexpr uint16_t ESTIMATED_SIZE = 309;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "device_info_response"; }
#endif
@@ -532,6 +563,9 @@ class DeviceInfoResponse final : public ProtoMessage {
#endif
#ifdef USE_ZWAVE_PROXY
uint32_t zwave_home_id{0};
#endif
#ifdef USE_SERIAL_PROXY
std::array<SerialProxyInfo, SERIAL_PROXY_COUNT> serial_proxies{};
#endif
void encode(ProtoWriteBuffer &buffer) const;
uint32_t calculate_size() const;
@@ -3061,5 +3095,169 @@ class InfraredRFReceiveEvent final : public ProtoMessage {
protected:
};
#endif
#ifdef USE_SERIAL_PROXY
class SerialProxyConfigureRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 138;
static constexpr uint8_t ESTIMATED_SIZE = 20;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "serial_proxy_configure_request"; }
#endif
uint32_t instance{0};
uint32_t baudrate{0};
bool flow_control{false};
enums::SerialProxyParity parity{};
uint32_t stop_bits{0};
uint32_t data_size{0};
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SerialProxyDataReceived final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 139;
static constexpr uint8_t ESTIMATED_SIZE = 23;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "serial_proxy_data_received"; }
#endif
uint32_t instance{0};
const uint8_t *data_ptr_{nullptr};
size_t data_len_{0};
void set_data(const uint8_t *data, size_t len) {
this->data_ptr_ = data;
this->data_len_ = len;
}
void encode(ProtoWriteBuffer &buffer) const;
uint32_t calculate_size() const;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class SerialProxyWriteRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 140;
static constexpr uint8_t ESTIMATED_SIZE = 23;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "serial_proxy_write_request"; }
#endif
uint32_t instance{0};
const uint8_t *data{nullptr};
uint16_t data_len{0};
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SerialProxySetModemPinsRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 141;
static constexpr uint8_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "serial_proxy_set_modem_pins_request"; }
#endif
uint32_t instance{0};
uint32_t line_states{0};
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SerialProxyGetModemPinsRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 142;
static constexpr uint8_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "serial_proxy_get_modem_pins_request"; }
#endif
uint32_t instance{0};
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SerialProxyGetModemPinsResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 143;
static constexpr uint8_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "serial_proxy_get_modem_pins_response"; }
#endif
uint32_t instance{0};
uint32_t line_states{0};
void encode(ProtoWriteBuffer &buffer) const;
uint32_t calculate_size() const;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class SerialProxyRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 144;
static constexpr uint8_t ESTIMATED_SIZE = 6;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "serial_proxy_request"; }
#endif
uint32_t instance{0};
enums::SerialProxyRequestType type{};
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
#endif
#ifdef USE_BLUETOOTH_PROXY
class BluetoothSetConnectionParamsRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 145;
static constexpr uint8_t ESTIMATED_SIZE = 20;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "bluetooth_set_connection_params_request"; }
#endif
uint64_t address{0};
uint32_t min_interval{0};
uint32_t max_interval{0};
uint32_t latency{0};
uint32_t timeout{0};
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothSetConnectionParamsResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 146;
static constexpr uint8_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "bluetooth_set_connection_params_response"; }
#endif
uint64_t address{0};
int32_t error{0};
void encode(ProtoWriteBuffer &buffer) const;
uint32_t calculate_size() const;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
#endif
} // namespace esphome::api
+117
View File
@@ -100,6 +100,18 @@ static void dump_bytes_field(DumpBuffer &out, const char *field_name, const uint
out.append(hex_buf).append("\n");
}
template<> const char *proto_enum_to_string<enums::SerialProxyPortType>(enums::SerialProxyPortType value) {
switch (value) {
case enums::SERIAL_PROXY_PORT_TYPE_TTL:
return "SERIAL_PROXY_PORT_TYPE_TTL";
case enums::SERIAL_PROXY_PORT_TYPE_RS232:
return "SERIAL_PROXY_PORT_TYPE_RS232";
case enums::SERIAL_PROXY_PORT_TYPE_RS485:
return "SERIAL_PROXY_PORT_TYPE_RS485";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
switch (value) {
case enums::ENTITY_CATEGORY_NONE:
@@ -752,6 +764,32 @@ template<> const char *proto_enum_to_string<enums::ZWaveProxyRequestType>(enums:
}
}
#endif
#ifdef USE_SERIAL_PROXY
template<> const char *proto_enum_to_string<enums::SerialProxyParity>(enums::SerialProxyParity value) {
switch (value) {
case enums::SERIAL_PROXY_PARITY_NONE:
return "SERIAL_PROXY_PARITY_NONE";
case enums::SERIAL_PROXY_PARITY_EVEN:
return "SERIAL_PROXY_PARITY_EVEN";
case enums::SERIAL_PROXY_PARITY_ODD:
return "SERIAL_PROXY_PARITY_ODD";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::SerialProxyRequestType>(enums::SerialProxyRequestType value) {
switch (value) {
case enums::SERIAL_PROXY_REQUEST_TYPE_SUBSCRIBE:
return "SERIAL_PROXY_REQUEST_TYPE_SUBSCRIBE";
case enums::SERIAL_PROXY_REQUEST_TYPE_UNSUBSCRIBE:
return "SERIAL_PROXY_REQUEST_TYPE_UNSUBSCRIBE";
case enums::SERIAL_PROXY_REQUEST_TYPE_FLUSH:
return "SERIAL_PROXY_REQUEST_TYPE_FLUSH";
default:
return "UNKNOWN";
}
}
#endif
const char *HelloRequest::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "HelloRequest");
@@ -801,6 +839,14 @@ const char *DeviceInfo::dump_to(DumpBuffer &out) const {
return out.c_str();
}
#endif
#ifdef USE_SERIAL_PROXY
const char *SerialProxyInfo::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SerialProxyInfo");
dump_field(out, "name", this->name);
dump_field(out, "port_type", static_cast<enums::SerialProxyPortType>(this->port_type));
return out.c_str();
}
#endif
const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "DeviceInfoResponse");
dump_field(out, "name", this->name);
@@ -861,6 +907,13 @@ const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const {
#endif
#ifdef USE_ZWAVE_PROXY
dump_field(out, "zwave_home_id", this->zwave_home_id);
#endif
#ifdef USE_SERIAL_PROXY
for (const auto &it : this->serial_proxies) {
out.append(" serial_proxies: ");
it.dump_to(out);
out.append("\n");
}
#endif
return out.c_str();
}
@@ -2510,6 +2563,70 @@ const char *InfraredRFReceiveEvent::dump_to(DumpBuffer &out) const {
return out.c_str();
}
#endif
#ifdef USE_SERIAL_PROXY
const char *SerialProxyConfigureRequest::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SerialProxyConfigureRequest");
dump_field(out, "instance", this->instance);
dump_field(out, "baudrate", this->baudrate);
dump_field(out, "flow_control", this->flow_control);
dump_field(out, "parity", static_cast<enums::SerialProxyParity>(this->parity));
dump_field(out, "stop_bits", this->stop_bits);
dump_field(out, "data_size", this->data_size);
return out.c_str();
}
const char *SerialProxyDataReceived::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SerialProxyDataReceived");
dump_field(out, "instance", this->instance);
dump_bytes_field(out, "data", this->data_ptr_, this->data_len_);
return out.c_str();
}
const char *SerialProxyWriteRequest::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SerialProxyWriteRequest");
dump_field(out, "instance", this->instance);
dump_bytes_field(out, "data", this->data, this->data_len);
return out.c_str();
}
const char *SerialProxySetModemPinsRequest::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SerialProxySetModemPinsRequest");
dump_field(out, "instance", this->instance);
dump_field(out, "line_states", this->line_states);
return out.c_str();
}
const char *SerialProxyGetModemPinsRequest::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SerialProxyGetModemPinsRequest");
dump_field(out, "instance", this->instance);
return out.c_str();
}
const char *SerialProxyGetModemPinsResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SerialProxyGetModemPinsResponse");
dump_field(out, "instance", this->instance);
dump_field(out, "line_states", this->line_states);
return out.c_str();
}
const char *SerialProxyRequest::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SerialProxyRequest");
dump_field(out, "instance", this->instance);
dump_field(out, "type", static_cast<enums::SerialProxyRequestType>(this->type));
return out.c_str();
}
#endif
#ifdef USE_BLUETOOTH_PROXY
const char *BluetoothSetConnectionParamsRequest::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "BluetoothSetConnectionParamsRequest");
dump_field(out, "address", this->address);
dump_field(out, "min_interval", this->min_interval);
dump_field(out, "max_interval", this->max_interval);
dump_field(out, "latency", this->latency);
dump_field(out, "timeout", this->timeout);
return out.c_str();
}
const char *BluetoothSetConnectionParamsResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "BluetoothSetConnectionParamsResponse");
dump_field(out, "address", this->address);
dump_field(out, "error", this->error);
return out.c_str();
}
#endif
} // namespace esphome::api
@@ -634,6 +634,72 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
this->on_infrared_rf_transmit_raw_timings_request(msg);
break;
}
#endif
#ifdef USE_SERIAL_PROXY
case SerialProxyConfigureRequest::MESSAGE_TYPE: {
SerialProxyConfigureRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_serial_proxy_configure_request"), msg);
#endif
this->on_serial_proxy_configure_request(msg);
break;
}
#endif
#ifdef USE_SERIAL_PROXY
case SerialProxyWriteRequest::MESSAGE_TYPE: {
SerialProxyWriteRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_serial_proxy_write_request"), msg);
#endif
this->on_serial_proxy_write_request(msg);
break;
}
#endif
#ifdef USE_SERIAL_PROXY
case SerialProxySetModemPinsRequest::MESSAGE_TYPE: {
SerialProxySetModemPinsRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_serial_proxy_set_modem_pins_request"), msg);
#endif
this->on_serial_proxy_set_modem_pins_request(msg);
break;
}
#endif
#ifdef USE_SERIAL_PROXY
case SerialProxyGetModemPinsRequest::MESSAGE_TYPE: {
SerialProxyGetModemPinsRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_serial_proxy_get_modem_pins_request"), msg);
#endif
this->on_serial_proxy_get_modem_pins_request(msg);
break;
}
#endif
#ifdef USE_SERIAL_PROXY
case SerialProxyRequest::MESSAGE_TYPE: {
SerialProxyRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_serial_proxy_request"), msg);
#endif
this->on_serial_proxy_request(msg);
break;
}
#endif
#ifdef USE_BLUETOOTH_PROXY
case BluetoothSetConnectionParamsRequest::MESSAGE_TYPE: {
BluetoothSetConnectionParamsRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_bluetooth_set_connection_params_request"), msg);
#endif
this->on_bluetooth_set_connection_params_request(msg);
break;
}
#endif
default:
break;
+21
View File
@@ -216,6 +216,27 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &value){};
#endif
#ifdef USE_SERIAL_PROXY
virtual void on_serial_proxy_configure_request(const SerialProxyConfigureRequest &value){};
#endif
#ifdef USE_SERIAL_PROXY
virtual void on_serial_proxy_write_request(const SerialProxyWriteRequest &value){};
#endif
#ifdef USE_SERIAL_PROXY
virtual void on_serial_proxy_set_modem_pins_request(const SerialProxySetModemPinsRequest &value){};
#endif
#ifdef USE_SERIAL_PROXY
virtual void on_serial_proxy_get_modem_pins_request(const SerialProxyGetModemPinsRequest &value){};
#endif
#ifdef USE_SERIAL_PROXY
virtual void on_serial_proxy_request(const SerialProxyRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_set_connection_params_request(const BluetoothSetConnectionParamsRequest &value){};
#endif
protected:
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
};
+1 -5
View File
@@ -582,11 +582,7 @@ void APIServer::request_time() {
}
#endif
bool APIServer::is_connected(bool state_subscription_only) const {
if (!state_subscription_only) {
return !this->clients_.empty();
}
bool APIServer::is_connected_with_state_subscription() const {
for (const auto &client : this->clients_) {
if (client->flags_.state_subscription) {
return true;
+6 -2
View File
@@ -185,7 +185,8 @@ class APIServer : public Component,
void send_infrared_rf_receive_event(uint32_t device_id, uint32_t key, const std::vector<int32_t> *timings);
#endif
bool is_connected(bool state_subscription_only = false) const;
bool is_connected() const { return !this->clients_.empty(); }
bool is_connected_with_state_subscription() const;
#ifdef USE_API_HOMEASSISTANT_STATES
struct HomeAssistantStateSubscription {
@@ -323,7 +324,10 @@ template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
TEMPLATABLE_VALUE(bool, state_subscription_only)
public:
bool check(const Ts &...x) override {
return global_api_server->is_connected(this->state_subscription_only_.value(x...));
if (this->state_subscription_only_.value(x...)) {
return global_api_server->is_connected_with_state_subscription();
}
return global_api_server->is_connected();
}
};
+71
View File
@@ -1,5 +1,6 @@
#include "proto.h"
#include <cinttypes>
#include <cstring>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
@@ -87,6 +88,76 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
return count;
}
// Single-pass encode for repeated submessage elements (non-template core).
// Writes field tag, reserves 1 byte for length varint, encodes the submessage body,
// then backpatches the actual length. For the common case (body < 128 bytes), this is
// just a single byte write with no memmove — all current repeated submessage types
// (BLE advertisements at ~47B, GATT descriptors at ~24B, service args, etc.) take
// this fast path.
//
// The memmove fallback for body >= 128 bytes exists only for correctness (e.g., a GATT
// characteristic with many descriptors). It is safe because calculate_size() already
// reserved space for the full multi-byte varint — the shift fills that reserved space:
//
// calculate_size() allocates per element: tag + varint_size(body) + body_size
//
// After encode, before memmove (1 byte reserved, body written):
// [tag][__][body ..... body][??]
// ^ ^-- unused byte (v2 space from calculate_size)
// len_pos
//
// After memmove(body_start+1, body_start, body_size):
// [tag][__][__][body ..... body]
// ^ ^-- body shifted forward, fills v2 space exactly
// len_pos
//
// After writing 2-byte varint at len_pos:
// [tag][v1][v2][body ..... body]
// ^-- pos_ = element end, within buffer
void ProtoWriteBuffer::encode_sub_message(uint32_t field_id, const void *value,
void (*encode_fn)(const void *, ProtoWriteBuffer &)) {
this->encode_field_raw(field_id, 2);
// Reserve 1 byte for length varint (optimistic: submessage < 128 bytes)
uint8_t *len_pos = this->pos_;
this->debug_check_bounds_(1);
this->pos_++;
uint8_t *body_start = this->pos_;
encode_fn(value, *this);
uint32_t body_size = static_cast<uint32_t>(this->pos_ - body_start);
if (body_size < 128) [[likely]] {
// Common case: 1-byte varint, just backpatch
*len_pos = static_cast<uint8_t>(body_size);
return;
}
// Compute extra bytes needed for varint beyond the 1 already reserved
uint8_t extra = ProtoSize::varint(body_size) - 1;
// Shift body forward to make room for the extra varint bytes
this->debug_check_bounds_(extra);
std::memmove(body_start + extra, body_start, body_size);
uint8_t *end = this->pos_ + extra;
// Write the full varint at len_pos
this->pos_ = len_pos;
this->encode_varint_raw(body_size);
this->pos_ = end;
}
// Non-template core for encode_optional_sub_message.
void ProtoWriteBuffer::encode_optional_sub_message(uint32_t field_id, uint32_t nested_size, const void *value,
void (*encode_fn)(const void *, ProtoWriteBuffer &)) {
if (nested_size == 0)
return;
this->encode_field_raw(field_id, 2);
this->encode_varint_raw(nested_size);
#ifdef ESPHOME_DEBUG_API
uint8_t *start = this->pos_;
encode_fn(value, *this);
if (static_cast<uint32_t>(this->pos_ - start) != nested_size)
this->debug_check_encode_size_(field_id, nested_size, this->pos_ - start);
#else
encode_fn(value, *this);
#endif
}
#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()) {
+19 -25
View File
@@ -185,7 +185,7 @@ class ProtoVarInt {
#endif
};
// Forward declarations for decode_to_message, encode_message and encode_packed_sint32
// Forward declarations for decode_to_message and related encoding helpers
class ProtoDecodableMessage;
class ProtoMessage;
class ProtoSize;
@@ -363,12 +363,18 @@ 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);
/// Encode a nested message field (force=true for repeated, false for singular)
/// Templated so concrete message type is preserved for direct encode/calculate_size calls.
template<typename T> void encode_message(uint32_t field_id, const T &value, bool force = true);
// Non-template core for encode_message — all buffer work happens here
void encode_message(uint32_t field_id, uint32_t msg_length_bytes, const void *value,
void (*encode_fn)(const void *, ProtoWriteBuffer &), bool force);
/// Single-pass encode for repeated submessage elements.
/// Thin template wrapper; all buffer work is in the non-template core.
template<typename T> void encode_sub_message(uint32_t field_id, const T &value);
/// Encode an optional singular submessage field — skips if empty.
/// Thin template wrapper; all buffer work is in the non-template core.
template<typename T> void encode_optional_sub_message(uint32_t field_id, const T &value);
// Non-template core for encode_sub_message — backpatch approach.
void encode_sub_message(uint32_t field_id, const void *value, void (*encode_fn)(const void *, ProtoWriteBuffer &));
// Non-template core for encode_optional_sub_message.
void encode_optional_sub_message(uint32_t field_id, uint32_t nested_size, const void *value,
void (*encode_fn)(const void *, ProtoWriteBuffer &));
std::vector<uint8_t> *get_buffer() const { return buffer_; }
protected:
@@ -690,26 +696,14 @@ template<typename T> void proto_encode_msg(const void *msg, ProtoWriteBuffer &bu
static_cast<const T *>(msg)->encode(buf);
}
// Implementation of encode_message - must be after ProtoMessage is defined
template<typename T> inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const T &value, bool force) {
this->encode_message(field_id, value.calculate_size(), &value, &proto_encode_msg<T>, force);
// Thin template wrapper; delegates to non-template core in proto.cpp.
template<typename T> inline void ProtoWriteBuffer::encode_sub_message(uint32_t field_id, const T &value) {
this->encode_sub_message(field_id, &value, &proto_encode_msg<T>);
}
// Non-template core for encode_message
inline void ProtoWriteBuffer::encode_message(uint32_t field_id, uint32_t msg_length_bytes, const void *value,
void (*encode_fn)(const void *, ProtoWriteBuffer &), bool force) {
if (msg_length_bytes == 0 && !force)
return;
this->encode_field_raw(field_id, 2);
this->encode_varint_raw(msg_length_bytes);
#ifdef ESPHOME_DEBUG_API
uint8_t *start = this->pos_;
encode_fn(value, *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
encode_fn(value, *this);
#endif
// Thin template wrapper; delegates to non-template core.
template<typename T> inline void ProtoWriteBuffer::encode_optional_sub_message(uint32_t field_id, const T &value) {
this->encode_optional_sub_message(field_id, value.calculate_size(), &value, &proto_encode_msg<T>);
}
// Implementation of decode_to_message - must be after ProtoDecodableMessage is defined
@@ -78,7 +78,7 @@ static void spi_set_clock(uint32_t max_hz) {
int source_clk = 0;
int spi_clk = 0;
int div = 0;
uint32_t param;
uint32_t param = PWD_SPI_CLK_BIT;
if (max_hz > 4333000) {
if (max_hz > 30000000) {
spi_clk = 30000000;
+3 -1
View File
@@ -10,7 +10,9 @@ static const char *const TAG = "bl0906";
constexpr uint32_t to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
constexpr int32_t to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; }
constexpr int32_t to_int32_t(sbe24_t input) {
return static_cast<int32_t>(encode_uint32((uint8_t) input.h, input.m, input.l, 0)) >> 8;
}
// The SUM byte is (Addr+Data_L+Data_M+Data_H)&0xFF negated;
constexpr uint8_t bl0906_checksum(const uint8_t address, const DataPacket *data) {
@@ -24,6 +24,10 @@ class BluetoothConnection final : public esp32_ble_client::BLEClientBase {
esp_err_t notify_characteristic(uint16_t handle, bool enable);
esp_err_t update_connection_params(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout) {
return this->update_conn_params_(min_interval, max_interval, latency, timeout, "custom");
}
void set_address(uint64_t address) override;
protected:
@@ -3,7 +3,9 @@
#include "esphome/core/log.h"
#include "esphome/core/macros.h"
#include "esphome/core/application.h"
#include <algorithm>
#include <cstring>
#include <limits>
#ifdef USE_ESP32
@@ -361,6 +363,33 @@ void BluetoothProxy::bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest
}
}
void BluetoothProxy::bluetooth_set_connection_params(const api::BluetoothSetConnectionParamsRequest &msg) {
if (this->api_connection_ == nullptr)
return;
auto *connection = this->get_connection_(msg.address, false);
api::BluetoothSetConnectionParamsResponse resp;
resp.address = msg.address;
if (connection == nullptr || !connection->connected()) {
ESP_LOGW(TAG, "[%d] [%s] Cannot set connection params, not connected",
connection ? static_cast<int>(connection->connection_index_) : -1,
connection ? connection->address_str() : "unknown");
resp.error = ESP_GATT_NOT_CONNECTED;
this->api_connection_->send_message(resp);
return;
}
// Protobuf fields are uint32_t to future-proof the API if BLE ever supports wider values;
// clamp to uint16_t since the current BLE spec defines these as 16-bit.
constexpr uint32_t max_val = std::numeric_limits<uint16_t>::max();
resp.error = connection->update_connection_params(static_cast<uint16_t>(std::min(msg.min_interval, max_val)),
static_cast<uint16_t>(std::min(msg.max_interval, max_val)),
static_cast<uint16_t>(std::min(msg.latency, max_val)),
static_cast<uint16_t>(std::min(msg.timeout, max_val)));
this->api_connection_->send_message(resp);
}
void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags) {
if (this->api_connection_ != nullptr) {
ESP_LOGE(TAG, "Only one API subscription is allowed at a time");
@@ -46,6 +46,7 @@ enum BluetoothProxyFeature : uint32_t {
FEATURE_CACHE_CLEARING = 1 << 4,
FEATURE_RAW_ADVERTISEMENTS = 1 << 5,
FEATURE_STATE_AND_MODE = 1 << 6,
FEATURE_CONNECTION_PARAMS_SETTING = 1 << 7,
};
enum BluetoothProxySubscriptionFlag : uint32_t {
@@ -82,6 +83,7 @@ class BluetoothProxy final : public esp32_ble_tracker::ESPBTDeviceListener,
void bluetooth_gatt_write_descriptor(const api::BluetoothGATTWriteDescriptorRequest &msg);
void bluetooth_gatt_send_services(const api::BluetoothGATTGetServicesRequest &msg);
void bluetooth_gatt_notify(const api::BluetoothGATTNotifyRequest &msg);
void bluetooth_set_connection_params(const api::BluetoothSetConnectionParamsRequest &msg);
void subscribe_api_connection(api::APIConnection *api_connection, uint32_t flags);
void unsubscribe_api_connection(api::APIConnection *api_connection);
@@ -130,6 +132,7 @@ class BluetoothProxy final : public esp32_ble_tracker::ESPBTDeviceListener,
flags |= BluetoothProxyFeature::FEATURE_REMOTE_CACHING;
flags |= BluetoothProxyFeature::FEATURE_PAIRING;
flags |= BluetoothProxyFeature::FEATURE_CACHE_CLEARING;
flags |= BluetoothProxyFeature::FEATURE_CONNECTION_PARAMS_SETTING;
}
return flags;
@@ -429,7 +429,7 @@ bool BMP581Component::read_temperature_(float &temperature) {
}
// temperature MSB is in data[2], LSB is in data[1], XLSB in data[0]
int32_t raw_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
int32_t raw_temp = static_cast<int32_t>(encode_uint32(data[2], data[1], data[0], 0)) >> 8;
temperature = (float) (raw_temp / 65536.0); // convert measurement to degrees Celsius (page 22 of datasheet)
return true;
@@ -458,7 +458,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float &
}
// temperature MSB is in data[2], LSB is in data[1], XLSB in data[0]
int32_t raw_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
int32_t raw_temp = static_cast<int32_t>(encode_uint32(data[2], data[1], data[0], 0)) >> 8;
temperature = (float) (raw_temp / 65536.0); // convert measurement to degrees Celsius (page 22 of datasheet)
// pressure MSB is in data[5], LSB is in data[4], XLSB in data[3]
+1 -1
View File
@@ -263,7 +263,7 @@ class ByteBuffer {
void put_uint8(uint8_t value, size_t offset) { this->data_[offset] = value; }
void put_uint16(uint16_t value, size_t offset) { this->put(value, offset); }
void put_uint24(uint32_t value, size_t offset) { this->put(value, offset); }
void put_uint24(uint32_t value, size_t offset) { this->put_uint32_(value, offset, 3); }
void put_uint32(uint32_t value, size_t offset) { this->put(value, offset); }
void put_uint64(uint64_t value, size_t offset) { this->put(value, offset); }
// Signed versions of the put functions
+1 -1
View File
@@ -92,7 +92,7 @@ void CAP1188Component::loop() {
this->read_register(CAP1188_MAIN, &data, 1);
data = data & ~CAP1188_MAIN_INT;
this->write_register(CAP1188_MAIN, &data, 2);
this->write_register(CAP1188_MAIN, &data, 1);
}
for (auto *channel : this->channels_) {
@@ -13,6 +13,7 @@ from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_LN882X,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
PlatformFramework,
)
@@ -53,6 +54,7 @@ CONFIG_SCHEMA = cv.All(
PLATFORM_ESP8266,
PLATFORM_BK72XX,
PLATFORM_LN882X,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
]
),
@@ -103,11 +105,8 @@ async def to_code(config):
if config[CONF_COMPRESSION] == "gzip":
cg.add_define("USE_CAPTIVE_PORTAL_GZIP")
if CORE.using_arduino:
if CORE.is_esp8266:
cg.add_library("DNSServer", None)
if CORE.is_libretiny:
cg.add_library("DNSServer", None)
if CORE.using_arduino and (CORE.is_esp8266 or CORE.is_libretiny or CORE.is_rp2040):
cg.add_library("DNSServer", None)
# Only compile the ESP-IDF DNS server when using ESP-IDF framework
+1 -1
View File
@@ -8,6 +8,7 @@ from esphome.const import (
CONF_AWAY_COMMAND_TOPIC,
CONF_AWAY_STATE_TOPIC,
CONF_CURRENT_HUMIDITY_STATE_TOPIC,
CONF_CURRENT_TEMPERATURE,
CONF_CURRENT_TEMPERATURE_STATE_TOPIC,
CONF_CUSTOM_FAN_MODE,
CONF_CUSTOM_PRESET,
@@ -112,7 +113,6 @@ CLIMATE_SWING_MODES = {
validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
CONF_CURRENT_TEMPERATURE = "current_temperature"
CONF_MIN_HUMIDITY = "min_humidity"
CONF_MAX_HUMIDITY = "max_humidity"
CONF_TARGET_HUMIDITY = "target_humidity"
@@ -236,8 +236,8 @@ void BLEClientBase::log_warning_(const char *message) {
ESP_LOGW(TAG, "[%d] [%s] %s", this->connection_index_, this->address_str_, message);
}
void BLEClientBase::update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency,
uint16_t timeout, const char *param_type) {
esp_err_t BLEClientBase::update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency,
uint16_t timeout, const char *param_type) {
esp_ble_conn_update_params_t conn_params = {{0}};
memcpy(conn_params.bda, this->remote_bda_, sizeof(esp_bd_addr_t));
conn_params.min_int = min_interval;
@@ -249,6 +249,7 @@ void BLEClientBase::update_conn_params_(uint16_t min_interval, uint16_t max_inte
if (err != ESP_OK) {
this->log_gattc_warning_("esp_ble_gap_update_conn_params", err);
}
return err;
}
void BLEClientBase::set_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout,
@@ -129,8 +129,8 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
void log_event_(const char *name);
void log_gattc_lifecycle_event_(const char *name);
void log_gattc_data_event_(const char *name);
void update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout,
const char *param_type);
esp_err_t update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout,
const char *param_type);
void set_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t timeout,
const char *param_type);
void log_gattc_warning_(const char *operation, esp_gatt_status_t status);
@@ -310,8 +310,8 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
(*this->on_write_callback_)(this->value_, param->exec_write.conn_id);
}
}
esp_err_t err =
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, nullptr);
esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->exec_write.conn_id, param->exec_write.trans_id,
ESP_GATT_OK, nullptr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_send_response failed: %d", err);
}
+2 -6
View File
@@ -3,15 +3,11 @@ import logging
from esphome import automation
import esphome.codegen as cg
from esphome.components import climate, logger, uart
from esphome.components.climate import (
CONF_CURRENT_TEMPERATURE,
ClimateMode,
ClimatePreset,
ClimateSwingMode,
)
from esphome.components.climate import ClimateMode, ClimatePreset, ClimateSwingMode
import esphome.config_validation as cv
from esphome.const import (
CONF_BEEPER,
CONF_CURRENT_TEMPERATURE,
CONF_DISPLAY,
CONF_ID,
CONF_LEVEL,
+1 -1
View File
@@ -49,7 +49,7 @@ void HTE501Component::update() {
this->set_timeout(50, [this]() {
uint8_t i2c_response[6];
this->read(i2c_response, 6);
if (i2c_response[2] != crc8(i2c_response, 2, 0xFF, 0x31, true) &&
if (i2c_response[2] != crc8(i2c_response, 2, 0xFF, 0x31, true) ||
i2c_response[5] != crc8(i2c_response + 3, 2, 0xFF, 0x31, true)) {
this->error_code_ = CRC_CHECK_FAILED;
this->status_set_warning();
@@ -50,6 +50,8 @@ CONF_FOLLOW_REDIRECTS = "follow_redirects"
CONF_REDIRECT_LIMIT = "redirect_limit"
CONF_BUFFER_SIZE_RX = "buffer_size_rx"
CONF_BUFFER_SIZE_TX = "buffer_size_tx"
CONF_TLS_BUFFER_SIZE_RX = "tls_buffer_size_rx"
CONF_TLS_BUFFER_SIZE_TX = "tls_buffer_size_tx"
CONF_CA_CERTIFICATE_PATH = "ca_certificate_path"
CONF_MAX_RESPONSE_BUFFER_SIZE = "max_response_buffer_size"
@@ -124,6 +126,12 @@ CONFIG_SCHEMA = cv.All(
cv.SplitDefault(CONF_BUFFER_SIZE_TX, esp32=512): cv.All(
cv.uint16_t, cv.only_on_esp32
),
cv.SplitDefault(CONF_TLS_BUFFER_SIZE_RX, esp8266=512): cv.All(
cv.uint16_t, cv.only_on_esp8266
),
cv.SplitDefault(CONF_TLS_BUFFER_SIZE_TX, esp8266=512): cv.All(
cv.uint16_t, cv.only_on_esp8266
),
cv.Optional(CONF_CA_CERTIFICATE_PATH): cv.All(
cv.file_,
cv.Any(cv.only_on(PLATFORM_HOST), cv.only_on_esp32),
@@ -150,6 +158,8 @@ async def to_code(config):
if CORE.is_esp8266 and not config[CONF_ESP8266_DISABLE_SSL_SUPPORT]:
cg.add_define("USE_HTTP_REQUEST_ESP8266_HTTPS")
cg.add(var.set_tls_buffer_size_rx(config[CONF_TLS_BUFFER_SIZE_RX]))
cg.add(var.set_tls_buffer_size_tx(config[CONF_TLS_BUFFER_SIZE_TX]))
if timeout_ms := config.get(CONF_WATCHDOG_TIMEOUT):
cg.add(var.set_watchdog_timeout(timeout_ms))
@@ -18,8 +18,6 @@ namespace esphome::http_request {
static const char *const TAG = "http_request.arduino";
#ifdef USE_ESP8266
static constexpr int RX_BUFFER_SIZE = 512;
static constexpr int TX_BUFFER_SIZE = 512;
// ESP8266 Arduino core (WiFiClientSecureBearSSL.cpp) returns -1000 on OOM
static constexpr int ESP8266_SSL_ERR_OOM = -1000;
#endif
@@ -58,7 +56,7 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &ur
ESP_LOGV(TAG, "ESP8266 HTTPS connection with WiFiClientSecure");
stream_ptr = std::make_unique<WiFiClientSecure>();
WiFiClientSecure *secure_client = static_cast<WiFiClientSecure *>(stream_ptr.get());
secure_client->setBufferSizes(RX_BUFFER_SIZE, TX_BUFFER_SIZE);
secure_client->setBufferSizes(this->tls_buffer_size_rx_, this->tls_buffer_size_tx_);
secure_client->setInsecure();
} else {
stream_ptr = std::make_unique<WiFiClient>();
@@ -138,8 +136,8 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(const std::string &ur
}
ESP_LOGW(TAG, "SSL failure: %s (Code: %d)", LOG_STR_ARG(error_msg), last_error);
if (last_error == ESP8266_SSL_ERR_OOM) {
ESP_LOGW(TAG, "Heap free: %u bytes, configured buffer sizes: %u bytes", ESP.getFreeHeap(),
static_cast<unsigned int>(RX_BUFFER_SIZE + TX_BUFFER_SIZE));
ESP_LOGW(TAG, "Configured TLS buffer sizes: %u/%u bytes, check max free heap block using the debug component",
(unsigned int) this->tls_buffer_size_rx_, (unsigned int) this->tls_buffer_size_tx_);
}
} else {
ESP_LOGW(TAG, "Connection failure with no error code");
@@ -47,10 +47,20 @@ class HttpContainerArduino : public HttpContainer {
};
class HttpRequestArduino : public HttpRequestComponent {
public:
#ifdef USE_ESP8266
void set_tls_buffer_size_rx(uint16_t size) { this->tls_buffer_size_rx_ = size; }
void set_tls_buffer_size_tx(uint16_t size) { this->tls_buffer_size_tx_ = size; }
#endif
protected:
std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method, const std::string &body,
const std::vector<Header> &request_headers,
const std::vector<std::string> &lower_case_collect_headers) override;
#ifdef USE_ESP8266
uint16_t tls_buffer_size_rx_{512};
uint16_t tls_buffer_size_tx_{512};
#endif
};
} // namespace esphome::http_request
+6 -4
View File
@@ -10,11 +10,13 @@ static const char *const TAG = "iaqcore";
enum IAQCoreErrorCode : uint8_t { ERROR_OK = 0, ERROR_RUNIN = 0x10, ERROR_BUSY = 0x01, ERROR_ERROR = 0x80 };
static constexpr size_t SENSOR_DATA_LENGTH = 9;
struct SensorData {
uint16_t co2;
IAQCoreErrorCode status;
int32_t resistance;
uint16_t co2;
uint16_t tvoc;
IAQCoreErrorCode status;
SensorData(const uint8_t *buffer) {
this->co2 = encode_uint16(buffer[0], buffer[1]);
@@ -33,9 +35,9 @@ void IAQCore::setup() {
}
void IAQCore::update() {
uint8_t buffer[sizeof(SensorData)];
uint8_t buffer[SENSOR_DATA_LENGTH];
if (this->read_register(0xB5, buffer, sizeof(buffer)) != i2c::ERROR_OK) {
if (this->read_register(0xB5, buffer, SENSOR_DATA_LENGTH) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Read failed");
this->status_set_warning();
this->publish_nans_();
@@ -599,11 +599,7 @@ bool INA2XX::read_unsigned_16_(uint8_t reg, uint16_t &out) {
}
int64_t INA2XX::two_complement_(uint64_t value, uint8_t bits) {
if (value > (1ULL << (bits - 1))) {
return (int64_t) (value - (1ULL << bits));
} else {
return (int64_t) value;
}
return (int64_t) (value << (64 - bits)) >> (64 - bits);
}
} // namespace ina2xx_base
} // namespace esphome
+5 -5
View File
@@ -407,7 +407,7 @@ void Inkplate::display1b_() {
break;
}
uint32_t clock = (1 << this->cl_pin_->get_pin());
uint32_t clock = (1UL << this->cl_pin_->get_pin());
uint32_t data_mask = this->get_data_pin_mask_();
ESP_LOGV(TAG, "Display1b start loops (%ums)", millis() - start_time);
@@ -575,7 +575,7 @@ void Inkplate::display3b_() {
break;
}
uint32_t clock = (1 << this->cl_pin_->get_pin());
uint32_t clock = (1UL << this->cl_pin_->get_pin());
uint32_t data_mask = this->get_data_pin_mask_();
uint32_t pos;
uint32_t data;
@@ -646,7 +646,7 @@ bool Inkplate::partial_update_() {
int rep = (this->model_ == INKPLATE_6_V2) ? 6 : 5;
eink_on_();
uint32_t clock = (1 << this->cl_pin_->get_pin());
uint32_t clock = (1UL << this->cl_pin_->get_pin());
uint32_t data_mask = this->get_data_pin_mask_();
for (int k = 0; k < rep; k++) {
vscan_start_();
@@ -704,7 +704,7 @@ void Inkplate::vscan_start_() {
}
void Inkplate::hscan_start_(uint32_t d) {
uint8_t clock = (1 << this->cl_pin_->get_pin());
uint32_t clock = (1UL << this->cl_pin_->get_pin());
this->sph_pin_->digital_write(false);
GPIO.out_w1ts = d | clock;
GPIO.out_w1tc = this->get_data_pin_mask_() | clock;
@@ -751,7 +751,7 @@ void Inkplate::clean_fast_(uint8_t c, uint8_t rep) {
uint32_t send = ((data & 0b00000011) << 4) | (((data & 0b00001100) >> 2) << 18) | (((data & 0b00010000) >> 4) << 23) |
(((data & 0b11100000) >> 5) << 25);
uint32_t clock = (1 << this->cl_pin_->get_pin());
uint32_t clock = (1UL << this->cl_pin_->get_pin());
for (int k = 0; k < rep; k++) {
vscan_start_();
+11 -3
View File
@@ -8,6 +8,7 @@
#include "LwRx.h"
#include <cstring>
#include "esphome/core/helpers.h"
namespace esphome {
namespace lightwaverf {
@@ -185,13 +186,20 @@ bool LwRx::lwrx_getmessage(uint8_t *buf, uint8_t len) {
bool ret = true;
int16_t j = 0; // int
if (this->rx_msgcomplete && len <= RX_MSGLEN) {
// Copy message under interrupt lock to prevent ISR overwriting rx_msg mid-read
uint8_t msg_copy[RX_MSGLEN];
{
InterruptLock lock;
memcpy(msg_copy, this->rx_msg, RX_MSGLEN);
this->rx_msgcomplete = false;
}
for (uint8_t i = 0; ret && i < RX_MSGLEN; i++) {
if (this->rx_translate || (len != RX_MSGLEN)) {
j = this->rx_find_nibble_(this->rx_msg[i]);
j = this->rx_find_nibble_(msg_copy[i]);
if (j < 0)
ret = false;
} else {
j = this->rx_msg[i];
j = msg_copy[i];
}
switch (len) {
case 4:
@@ -199,6 +207,7 @@ bool LwRx::lwrx_getmessage(uint8_t *buf, uint8_t len) {
buf[2] = j;
if (i == 2)
buf[3] = j;
[[fallthrough]];
case 2:
if (i == 3)
buf[0] = j;
@@ -212,7 +221,6 @@ bool LwRx::lwrx_getmessage(uint8_t *buf, uint8_t len) {
break;
}
}
this->rx_msgcomplete = false;
} else {
ret = false;
}
+2 -2
View File
@@ -105,8 +105,8 @@ class LwRx {
uint32_t rx_prev; // time of previous interrupt in microseconds
bool rx_msgcomplete = false; // set high when message available
bool rx_translate = true; // Set false to get raw data
volatile bool rx_msgcomplete = false; // set high when message available
bool rx_translate = true; // Set false to get raw data
uint8_t rx_state = 0;
+2 -1
View File
@@ -192,7 +192,8 @@ void LwTx::lwtx_set_gap_multiplier(uint8_t gap_multiplier) { this->tx_gap_multip
void LwTx::lw_timer_start() {
{
InterruptLock lock;
static LwTx *arg = this; // NOLINT
static LwTx *arg;
arg = this;
timer1_attachInterrupt([] { isr_t_xtimer(arg); });
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
timer1_write(this->espPeriod);
+2 -2
View File
@@ -62,8 +62,8 @@ class LwTx {
uint8_t tx_repeats = 12; // Number of repeats of message sent
uint8_t txon = 1;
uint8_t txoff = 0;
bool tx_msg_active = false; // set true to activate message sending
bool tx_translate = true; // Set false to send raw data
volatile bool tx_msg_active = false; // set true to activate message sending
bool tx_translate = true; // Set false to send raw data
uint8_t tx_buf[TX_MSGLEN]; // the message buffer during reception
uint8_t tx_repeat = 0; // counter for repeats
+10 -11
View File
@@ -146,7 +146,6 @@ void LTRAlsPs501Component::update() {
void LTRAlsPs501Component::loop() {
ErrorCode err = i2c::ERROR_OK;
static uint8_t tries{0};
switch (this->state_) {
case State::DELAYED_SETUP:
@@ -175,20 +174,20 @@ void LTRAlsPs501Component::loop() {
case State::WAITING_FOR_DATA:
if (this->is_als_data_ready_(this->als_readings_) == LtrDataAvail::LTR_DATA_OK) {
tries = 0;
this->tries_ = 0;
ESP_LOGV(TAG, "Reading sensor data assuming gain = %.0fx, time = %d ms",
get_gain_coeff(this->als_readings_.gain), get_itime_ms(this->als_readings_.integration_time));
this->read_sensor_data_(this->als_readings_);
this->apply_lux_calculation_(this->als_readings_);
this->state_ = State::DATA_COLLECTED;
} else if (tries >= MAX_TRIES) {
} else if (this->tries_ >= MAX_TRIES) {
ESP_LOGW(TAG, "Can't get data after several tries. Aborting.");
tries = 0;
this->tries_ = 0;
this->status_set_warning();
this->state_ = State::IDLE;
return;
} else {
tries++;
this->tries_++;
}
break;
@@ -230,21 +229,21 @@ void LTRAlsPs501Component::loop() {
}
void LTRAlsPs501Component::check_and_trigger_ps_() {
static uint32_t last_high_trigger_time{0};
static uint32_t last_low_trigger_time{0};
uint16_t ps_data = this->read_ps_data_();
uint32_t now = millis();
if (ps_data != this->ps_readings_) {
this->ps_readings_ = ps_data;
// Higher values - object is closer to sensor
if (ps_data > this->ps_threshold_high_ && now - last_high_trigger_time >= this->ps_cooldown_time_s_ * 1000) {
last_high_trigger_time = now;
if (ps_data > this->ps_threshold_high_ &&
now - this->last_ps_high_trigger_time_ >= this->ps_cooldown_time_s_ * 1000) {
this->last_ps_high_trigger_time_ = now;
ESP_LOGD(TAG, "Proximity high threshold triggered. Value = %d, Trigger level = %d", ps_data,
this->ps_threshold_high_);
this->on_ps_high_trigger_callback_.call();
} else if (ps_data < this->ps_threshold_low_ && now - last_low_trigger_time >= this->ps_cooldown_time_s_ * 1000) {
last_low_trigger_time = now;
} else if (ps_data < this->ps_threshold_low_ &&
now - this->last_ps_low_trigger_time_ >= this->ps_cooldown_time_s_ * 1000) {
this->last_ps_low_trigger_time_ = now;
ESP_LOGD(TAG, "Proximity low threshold triggered. Value = %d, Trigger level = %d", ps_data,
this->ps_threshold_low_);
this->on_ps_low_trigger_callback_.call();
+3
View File
@@ -74,6 +74,7 @@ class LTRAlsPs501Component : public PollingComponent, public i2c::I2CDevice {
READY_TO_PUBLISH,
KEEP_PUBLISHING
} state_{State::NOT_INITIALIZED};
uint8_t tries_{0};
LtrType ltr_type_{LtrType::LTR_TYPE_ALS_ONLY};
@@ -130,6 +131,8 @@ class LTRAlsPs501Component : public PollingComponent, public i2c::I2CDevice {
PsGain501 ps_gain_{PsGain501::PS_GAIN_1};
uint16_t ps_threshold_high_{0xffff};
uint16_t ps_threshold_low_{0x0000};
uint32_t last_ps_high_trigger_time_{0};
uint32_t last_ps_low_trigger_time_{0};
//
// Sensors for publishing data
+3
View File
@@ -422,6 +422,9 @@ void LvglComponent::write_random_() {
auto row = random_uint32() % this->disp_drv_.ver_res;
row = row / this->draw_rounding * this->draw_rounding;
auto size = ((random_uint32() % 32) / this->draw_rounding + 2) * this->draw_rounding - 1;
// clamp size so the square fits within the draw buffer
if ((size + 1) * (size + 1) > this->draw_buf_.size)
size = static_cast<decltype(size)>(sqrtf(this->draw_buf_.size)) - 1;
lv_area_t area;
area.x1 = col;
area.y1 = row;
+6 -6
View File
@@ -288,9 +288,7 @@ void MipiRgb::draw_pixel_at(int x, int y, Color color) {
if (!this->check_buffer_())
return;
size_t pos = (y * this->width_) + x;
uint8_t hi_byte = static_cast<uint8_t>(color.r & 0xF8) | (color.g >> 5);
uint8_t lo_byte = static_cast<uint8_t>((color.g & 0x1C) << 3) | (color.b >> 3);
uint16_t new_color = hi_byte | (lo_byte << 8); // big endian
uint16_t new_color = convert_big_endian(display::ColorUtil::color_to_565(color));
if (this->buffer_[pos] == new_color)
return;
this->buffer_[pos] = new_color;
@@ -315,10 +313,12 @@ void MipiRgb::fill(Color color) {
}
auto *ptr_16 = reinterpret_cast<uint16_t *>(this->buffer_);
uint8_t hi_byte = static_cast<uint8_t>(color.r & 0xF8) | (color.g >> 5);
uint8_t lo_byte = static_cast<uint8_t>((color.g & 0x1C) << 3) | (color.b >> 3);
uint16_t new_color = lo_byte | (hi_byte << 8); // little endian
uint16_t new_color = convert_big_endian(display::ColorUtil::color_to_565(color));
std::fill_n(ptr_16, this->width_ * this->height_, new_color);
this->x_low_ = 0;
this->y_low_ = 0;
this->x_high_ = this->width_ - 1;
this->y_high_ = this->height_ - 1;
}
int MipiRgb::get_width() {
+1 -5
View File
@@ -364,11 +364,7 @@ void MSA3xxComponent::setup_offset_(float offset_x, float offset_y, float offset
}
int64_t MSA3xxComponent::twos_complement_(uint64_t value, uint8_t bits) {
if (value > (1ULL << (bits - 1))) {
return (int64_t) (value - (1ULL << bits));
} else {
return (int64_t) value;
}
return (int64_t) (value << (64 - bits)) >> (64 - bits);
}
void binary_event_debounce(bool state, bool old_state, uint32_t now, uint32_t &last_ms, Trigger<> &trigger,
@@ -88,7 +88,7 @@ void NextionComponent::update_component_settings(bool force_update) {
this->send_state_to_nextion();
}
if (this->component_flags_.bco_needs_update || (force_update && this->component_flags_.bco2_is_set)) {
if (this->component_flags_.bco_needs_update || (force_update && this->component_flags_.bco_is_set)) {
this->nextion_->set_component_background_color(this->variable_name_.c_str(), this->bco_);
this->component_flags_.bco_needs_update = false;
}
@@ -14,6 +14,11 @@ NdefRecordText::NdefRecordText(const std::vector<uint8_t> &payload) {
uint8_t language_code_length = payload[0] & 0b00111111; // Todo, make use of encoding bit?
if (1 + language_code_length > payload.size()) {
ESP_LOGE(TAG, "Record payload too short for language code");
return;
}
this->language_code_ = std::string(payload.begin() + 1, payload.begin() + 1 + language_code_length);
this->text_ = std::string(payload.begin() + 1 + language_code_length, payload.end());
+15 -7
View File
@@ -35,7 +35,7 @@ uint8_t guess_tag_type(uint8_t uid_length) {
}
}
uint8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data) {
int8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data) {
for (uint8_t i = 0; i < MIFARE_CLASSIC_BLOCK_SIZE; i++) {
if (data[i] == 0x00) {
// Do nothing, skip
@@ -49,17 +49,25 @@ uint8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data) {
}
bool decode_mifare_classic_tlv(std::vector<uint8_t> &data, uint32_t &message_length, uint8_t &message_start_index) {
if (data.size() < MIFARE_CLASSIC_BLOCK_SIZE) {
ESP_LOGE(TAG, "Error, data too short for NDEF detection.");
return false;
}
auto i = get_mifare_classic_ndef_start_index(data);
if (data[i] != 0x03) {
if (i < 0 || data[i] != 0x03) {
ESP_LOGE(TAG, "Error, Can't decode message length.");
return false;
}
if (data[i + 1] == 0xFF) {
message_length = ((0xFF & data[i + 2]) << 8) | (0xFF & data[i + 3]);
message_start_index = i + MIFARE_CLASSIC_LONG_TLV_SIZE;
uint8_t idx = static_cast<uint8_t>(i);
if (idx + 4 <= data.size() && data[idx + 1] == 0xFF) {
message_length = ((0xFF & data[idx + 2]) << 8) | (0xFF & data[idx + 3]);
message_start_index = idx + MIFARE_CLASSIC_LONG_TLV_SIZE;
} else if (idx + 2 <= data.size()) {
message_length = data[idx + 1];
message_start_index = idx + MIFARE_CLASSIC_SHORT_TLV_SIZE;
} else {
message_length = data[i + 1];
message_start_index = i + MIFARE_CLASSIC_SHORT_TLV_SIZE;
ESP_LOGE(TAG, "Error, TLV data too short.");
return false;
}
return true;
}
+1 -1
View File
@@ -72,7 +72,7 @@ ESPDEPRECATED("Use format_bytes_to() with stack buffer instead. Removed in 2026.
std::string format_bytes(std::span<const uint8_t> bytes);
uint8_t guess_tag_type(uint8_t uid_length);
uint8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data);
int8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data);
bool decode_mifare_classic_tlv(std::vector<uint8_t> &data, uint32_t &message_length, uint8_t &message_start_index);
uint32_t get_mifare_classic_buffer_size(uint32_t message_length);
+3
View File
@@ -20,8 +20,10 @@ from esphome.components.zephyr import (
)
from esphome.components.zephyr.const import (
BOOTLOADER_MCUBOOT,
CONF_CDC_ACM,
KEY_BOOTLOADER,
KEY_ZEPHYR,
CdcAcm,
)
import esphome.config_validation as cv
from esphome.const import (
@@ -159,6 +161,7 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_VERSION): cv.string_strict,
}
),
cv.GenerateID(CONF_CDC_ACM): cv.declare_id(CdcAcm),
}
),
set_framework,
+10 -25
View File
@@ -2,42 +2,27 @@
#ifdef USE_NRF52_DFU
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/uart/cdc_acm.h>
#include "esphome/core/log.h"
#include "esphome/components/zephyr/cdc_acm.h"
namespace esphome {
namespace nrf52 {
static const char *const TAG = "dfu";
volatile bool goto_dfu = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
static const uint32_t DFU_DBL_RESET_MAGIC = 0x5A1AD5; // SALADS
#define DEVICE_AND_COMMA(node_id) DEVICE_DT_GET(node_id),
static void cdc_dte_rate_callback(const struct device * /*unused*/, uint32_t rate) {
if (rate == 1200) {
goto_dfu = true;
}
}
void DeviceFirmwareUpdate::setup() {
this->reset_pin_->setup();
const struct device *cdc_dev[] = {DT_FOREACH_STATUS_OKAY(zephyr_cdc_acm_uart, DEVICE_AND_COMMA)};
for (auto &idx : cdc_dev) {
cdc_acm_dte_rate_callback_set(idx, cdc_dte_rate_callback);
}
}
void DeviceFirmwareUpdate::loop() {
if (goto_dfu) {
goto_dfu = false;
volatile uint32_t *dbl_reset_mem = (volatile uint32_t *) 0x20007F7C;
(*dbl_reset_mem) = DFU_DBL_RESET_MAGIC;
this->reset_pin_->digital_write(true);
}
#if defined(CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT)
zephyr::global_cdc_acm->add_on_rate_callback([this](const device *, uint32_t rate) {
if (rate == 1200) {
volatile uint32_t *dbl_reset_mem = (volatile uint32_t *) 0x20007F7C;
(*dbl_reset_mem) = DFU_DBL_RESET_MAGIC;
this->reset_pin_->digital_write(true);
}
});
#endif
}
void DeviceFirmwareUpdate::dump_config() {
-1
View File
@@ -10,7 +10,6 @@ namespace nrf52 {
class DeviceFirmwareUpdate : public Component {
public:
void setup() override;
void loop() override;
void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
void dump_config() override;
+2 -7
View File
@@ -1,7 +1,6 @@
#include "esphome/core/defines.h"
#ifdef USE_OPENTHREAD
#include "openthread.h"
#include "esp_openthread.h"
#include <freertos/portmacro.h>
@@ -51,7 +50,7 @@ void OpenThreadComponent::on_state_changed_(otChangedFlags flags, void *context)
auto *self = static_cast<OpenThreadComponent *>(context);
// This runs on the OpenThread task thread with the OT lock held,
// so we can safely call otThreadGetDeviceRole directly.
otInstance *instance = esp_openthread_get_instance();
otInstance *instance = self->get_openthread_instance_();
otDeviceRole role = otThreadGetDeviceRole(instance);
self->connected_ = role >= OT_DEVICE_ROLE_CHILD;
}
@@ -233,16 +232,12 @@ bool OpenThreadComponent::teardown() {
otSrpClientClearHostAndServices(instance);
otSrpClientBuffersFreeAllServices(instance);
global_openthread_component = nullptr;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
ESP_LOGD(TAG, "Exit main loop ");
int error = esp_openthread_mainloop_exit();
int error = this->openthread_stop_();
if (error != ESP_OK) {
ESP_LOGW(TAG, "Failed attempt to stop main loop %d", error);
this->teardown_complete_ = true;
}
#else
this->teardown_complete_ = true;
#endif
}
return this->teardown_complete_;
}
@@ -44,6 +44,8 @@ class OpenThreadComponent : public Component {
protected:
std::optional<otIp6Address> get_omr_address_(InstanceLock &lock);
static void on_state_changed_(otChangedFlags flags, void *context);
otInstance *get_openthread_instance_();
int openthread_stop_();
std::function<void()> factory_reset_external_callback_;
#if CONFIG_OPENTHREAD_MTD
uint32_t poll_period_{0};
@@ -190,6 +190,8 @@ void OpenThreadComponent::ot_main() {
vTaskDelete(NULL);
}
int OpenThreadComponent::openthread_stop_() { return esp_openthread_mainloop_exit(); }
network::IPAddresses OpenThreadComponent::get_ip_addresses() {
network::IPAddresses addresses;
struct esp_ip6_addr if_ip6s[CONFIG_LWIP_IPV6_NUM_ADDRESSES];
@@ -204,6 +206,9 @@ network::IPAddresses OpenThreadComponent::get_ip_addresses() {
return addresses;
}
// not thread safe, only use in read-only use cases
otInstance *OpenThreadComponent::get_openthread_instance_() { return esp_openthread_get_instance(); }
std::optional<InstanceLock> InstanceLock::try_acquire(int delay) {
if (esp_openthread_lock_acquire(delay)) {
return InstanceLock();
@@ -137,6 +137,8 @@ class PacketDecoder {
return DECODE_EMPTY;
if (this->buffer_[this->position_] != key)
return DECODE_UNMATCHED;
if (this->position_ + 1 + sizeof(T) > this->len_)
return DECODE_ERROR;
this->position_++;
T value = 0;
for (size_t i = 0; i != sizeof(T); ++i) {
+2 -2
View File
@@ -97,7 +97,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
}
bool zc_symmetrical = this->frequency_detector_.is_increase_decrease_symmetrical();
bool amplitude_convergent = this->frequency_detector_.is_increase_decrease_symmetrical();
bool amplitude_convergent = this->amplitude_detector_.is_amplitude_convergent();
if (!zc_symmetrical || !amplitude_convergent) {
// The frequency/amplitude is not fully accurate yet, try to wait
// until the fault clears, or terminate after a while anyway
@@ -362,7 +362,7 @@ bool PIDAutotuner::OscillationAmplitudeDetector::is_amplitude_convergent() const
for (auto v : this->phase_mins)
global_min = std::min(global_min, v);
for (auto v : this->phase_maxs)
global_max = std::min(global_max, v);
global_max = std::max(global_max, v);
float global_amplitude = (global_max - global_min) / 2.0f;
float mean_amplitude = this->get_mean_oscillation_amplitude();
return (mean_amplitude - global_amplitude) / (global_amplitude) < 0.05f;
+1
View File
@@ -647,6 +647,7 @@ void Pipsolar::handle_qpiws_(const char *message) {
case 34:
this->publish_binary_sensor_(enabled, this->warning_high_ac_input_during_bus_soft_start_);
value_warnings_present |= enabled.value_or(false);
break;
case 35:
this->publish_binary_sensor_(enabled, this->warning_battery_equalization_);
value_warnings_present |= enabled.value_or(false);
+2 -2
View File
@@ -49,7 +49,7 @@ bool PN532I2C::read_response(uint8_t command, std::vector<uint8_t> &data) {
return false;
}
if (data[1] != 0x00 && data[2] != 0x00 && data[3] != 0xFF) {
if (data[1] != 0x00 || data[2] != 0x00 || data[3] != 0xFF) {
// invalid packet
ESP_LOGV(TAG, "read data invalid preamble!");
return false;
@@ -95,7 +95,7 @@ uint8_t PN532I2C::read_response_length_() {
return 0;
}
if (data[1] != 0x00 && data[2] != 0x00 && data[3] != 0xFF) {
if (data[1] != 0x00 || data[2] != 0x00 || data[3] != 0xFF) {
// invalid packet
ESP_LOGV(TAG, "read data invalid preamble!");
return 0;
@@ -66,12 +66,11 @@ optional<ParseResult> PVVXMiThermometer::parse_header_(const esp32_ble_tracker::
return {};
}
static uint8_t last_frame_count = 0;
if (last_frame_count == raw[13]) {
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%hhu).", last_frame_count);
if (this->last_frame_count_ == raw[13]) {
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%hhu).", this->last_frame_count_);
return {};
}
last_frame_count = raw[13];
this->last_frame_count_ = raw[13];
return result;
}
@@ -39,6 +39,8 @@ class PVVXMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevic
sensor::Sensor *battery_voltage_{nullptr};
sensor::Sensor *signal_strength_{nullptr};
uint8_t last_frame_count_{0};
optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
bool report_results_(const optional<ParseResult> &result, const char *address);
+1 -1
View File
@@ -222,7 +222,7 @@ bool SCD30Component::force_recalibration_with_reference(uint16_t co2_reference)
}
uint16_t SCD30Component::get_forced_calibration_reference() {
uint16_t forced_calibration_reference;
uint16_t forced_calibration_reference = 0;
// Get current CO2 calibration
if (!this->get_register(SCD30_CMD_FORCED_CALIBRATION, forced_calibration_reference)) {
ESP_LOGE(TAG, "Unable to read forced calibration reference.");
@@ -149,28 +149,25 @@ void MR60FDA2Component::split_frame_(uint8_t buffer) {
switch (this->current_frame_locate_) {
case LOCATE_FRAME_HEADER: // starting buffer
if (buffer == FRAME_HEADER_BUFFER) {
this->current_frame_len_ = 1;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_frame_len_ = 0;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
}
break;
case LOCATE_ID_FRAME1:
this->current_frame_id_ = buffer << 8;
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
break;
case LOCATE_ID_FRAME2:
this->current_frame_id_ += buffer;
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
break;
case LOCATE_LENGTH_FRAME_H:
this->current_data_frame_len_ = buffer << 8;
if (this->current_data_frame_len_ == 0x00) {
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
if (this->current_data_frame_len_ == 0) {
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
} else {
this->current_frame_locate_ = LOCATE_FRAME_HEADER;
@@ -181,15 +178,13 @@ void MR60FDA2Component::split_frame_(uint8_t buffer) {
if (this->current_data_frame_len_ > DATA_BUF_MAX_SIZE) {
this->current_frame_locate_ = LOCATE_FRAME_HEADER;
} else {
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
}
break;
case LOCATE_TYPE_FRAME1:
this->current_frame_type_ = buffer << 8;
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
break;
case LOCATE_TYPE_FRAME2:
@@ -198,8 +193,7 @@ void MR60FDA2Component::split_frame_(uint8_t buffer) {
(this->current_frame_type_ == PEOPLE_EXIST_TYPE_BUFFER) ||
(this->current_frame_type_ == RESULT_INSTALL_HEIGHT) || (this->current_frame_type_ == RESULT_PARAMETERS) ||
(this->current_frame_type_ == RESULT_HEIGHT_THRESHOLD) || (this->current_frame_type_ == RESULT_SENSITIVITY)) {
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
} else {
this->current_frame_locate_ = LOCATE_FRAME_HEADER;
@@ -207,8 +201,7 @@ void MR60FDA2Component::split_frame_(uint8_t buffer) {
break;
case LOCATE_HEAD_CKSUM_FRAME:
if (validate_checksum(this->current_frame_buf_, this->current_frame_len_, buffer)) {
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
} else {
ESP_LOGD(TAG, "HEAD_CKSUM_FRAME ERROR: 0x%02x", buffer);
@@ -223,21 +216,20 @@ void MR60FDA2Component::split_frame_(uint8_t buffer) {
}
break;
case LOCATE_DATA_FRAME:
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_data_buf_[this->current_frame_len_ - LEN_TO_DATA_FRAME] = buffer;
if (this->current_frame_len_ - LEN_TO_HEAD_CKSUM == this->current_data_frame_len_) {
this->current_frame_locate_++;
}
if (this->current_frame_len_ > FRAME_BUF_MAX_SIZE) {
if (this->current_frame_len_ >= FRAME_BUF_MAX_SIZE) {
ESP_LOGD(TAG, "PRACTICE_DATA_FRAME_LEN ERROR: %d", this->current_frame_len_ - LEN_TO_HEAD_CKSUM);
this->current_frame_locate_ = LOCATE_FRAME_HEADER;
break;
}
this->current_data_buf_[this->current_frame_len_ - LEN_TO_DATA_FRAME + 1] = buffer;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
if (this->current_frame_len_ - LEN_TO_HEAD_CKSUM == this->current_data_frame_len_) {
this->current_frame_locate_++;
}
break;
case LOCATE_DATA_CKSUM_FRAME:
if (validate_checksum(this->current_data_buf_, this->current_data_frame_len_, buffer)) {
this->current_frame_len_++;
this->current_frame_buf_[this->current_frame_len_ - 1] = buffer;
this->current_frame_buf_[this->current_frame_len_++] = buffer;
this->current_frame_locate_++;
this->process_frame_();
} else {
+6 -1
View File
@@ -20,7 +20,12 @@ void Sen21231Sensor::dump_config() {
void Sen21231Sensor::read_data_() {
person_sensor_results_t results;
this->read_bytes(PERSON_SENSOR_I2C_ADDRESS, (uint8_t *) &results, sizeof(results));
if (!this->read_bytes(PERSON_SENSOR_I2C_ADDRESS, (uint8_t *) &results, sizeof(results))) {
ESP_LOGW(TAG, "Failed to read data from SEN21231");
this->status_set_warning();
return;
}
this->status_clear_warning();
ESP_LOGD(TAG, "SEN21231: %d faces detected", results.num_faces);
this->publish_state(results.num_faces);
if (results.num_faces == 1) {
@@ -188,8 +188,8 @@ bool ShellyDimmer::upgrade_firmware_() {
break;
}
std::memcpy(buffer, p, BUFFER_SIZE);
p += BUFFER_SIZE;
std::memcpy(buffer, p, len);
p += len;
if (stm32_write_memory(stm32, addr, buffer, len) != STM32_ERR_OK) {
ESP_LOGW(TAG, "Failed to write to STM32 flash memory");
+7 -9
View File
@@ -12,10 +12,9 @@ void SMT100Component::update() {
}
void SMT100Component::loop() {
static char buffer[MAX_LINE_LENGTH];
while (this->available() != 0) {
if (readline_(read(), buffer, MAX_LINE_LENGTH) > 0) {
int counts = (int) strtol((strtok(buffer, ",")), nullptr, 10);
if (this->readline_(this->read(), this->readline_buffer_, MAX_LINE_LENGTH) > 0) {
int counts = (int) strtol((strtok(this->readline_buffer_, ",")), nullptr, 10);
float permittivity = (float) strtod((strtok(nullptr, ",")), nullptr);
float moisture = (float) strtod((strtok(nullptr, ",")), nullptr);
float temperature = (float) strtod((strtok(nullptr, ",")), nullptr);
@@ -56,7 +55,6 @@ void SMT100Component::dump_config() {
}
int SMT100Component::readline_(int readch, char *buffer, int len) {
static int pos = 0;
int rpos;
if (readch > 0) {
@@ -64,13 +62,13 @@ int SMT100Component::readline_(int readch, char *buffer, int len) {
case '\n': // Ignore new-lines
break;
case '\r': // Return on CR
rpos = pos;
pos = 0; // Reset position index ready for next time
rpos = this->readline_pos_;
this->readline_pos_ = 0; // Reset position index ready for next time
return rpos;
default:
if (pos < len - 1) {
buffer[pos++] = readch;
buffer[pos] = 0;
if (this->readline_pos_ < len - 1) {
buffer[this->readline_pos_++] = readch;
buffer[this->readline_pos_] = 0;
}
}
}
+3
View File
@@ -28,6 +28,9 @@ class SMT100Component : public PollingComponent, public uart::UARTDevice {
protected:
int readline_(int readch, char *buffer, int len);
char readline_buffer_[MAX_LINE_LENGTH]{};
int readline_pos_{0};
sensor::Sensor *counts_sensor_{nullptr};
sensor::Sensor *permittivity_sensor_{nullptr};
sensor::Sensor *moisture_sensor_{nullptr};
@@ -52,7 +52,11 @@ template<uint8_t SZ> class TextSaver : public TemplateTextSaverBase {
bool hasdata = this->pref_.load(&temp);
if (hasdata) {
value.assign(temp + 1, (size_t) temp[0]);
size_t len = static_cast<uint8_t>(temp[0]);
if (len > SZ) {
len = SZ;
}
value.assign(temp + 1, len);
}
this->prev_.assign(value);
@@ -4,6 +4,7 @@ from esphome.components import water_heater
import esphome.config_validation as cv
from esphome.const import (
CONF_AWAY,
CONF_CURRENT_TEMPERATURE,
CONF_ID,
CONF_MODE,
CONF_OPTIMISTIC,
@@ -18,7 +19,6 @@ from esphome.types import ConfigType
from .. import template_ns
CONF_CURRENT_TEMPERATURE = "current_temperature"
CONF_IS_ON = "is_on"
TemplateWaterHeater = template_ns.class_(
@@ -124,17 +124,10 @@ void HostUartComponent::setup() {
fcntl(this->file_descriptor_, F_SETFL, 0);
struct termios options;
tcgetattr(this->file_descriptor_, &options);
cfmakeraw(&options);
options.c_cflag &= ~CRTSCTS;
options.c_cflag |= CREAD | CLOCAL;
options.c_lflag &= ~ICANON;
options.c_lflag &= ~ECHO;
options.c_lflag &= ~ECHOE;
options.c_lflag &= ~ECHONL;
options.c_lflag &= ~ISIG;
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
options.c_oflag &= ~OPOST;
options.c_oflag &= ~ONLCR;
options.c_iflag &= ~(IXOFF | IXANY);
// Set data bits
options.c_cflag &= ~CSIZE; // Mask the character size bits
switch (this->data_bits_) {
@@ -110,7 +110,7 @@ void LibreTinyUARTComponent::setup() {
#if LT_HW_UART2
ESP_LOGE(TAG, " TX=%u, RX=%u", PIN_SERIAL2_TX, PIN_SERIAL2_RX);
#endif
this->mark_failed();
this->mark_failed(LOG_STR("SoftwareSerial is not implemented for this chip."));
return;
#endif
}
+3 -1
View File
@@ -445,6 +445,7 @@ void WeikaiChannel::flush() {
}
size_t WeikaiChannel::xfer_fifo_to_buffer_() {
size_t total = 0;
size_t to_transfer;
size_t free;
while ((to_transfer = this->rx_in_fifo_()) && (free = this->receive_buffer_.free())) {
@@ -458,9 +459,10 @@ size_t WeikaiChannel::xfer_fifo_to_buffer_() {
this->reg(0).read_fifo(data, to_transfer);
for (size_t i = 0; i < to_transfer; i++)
this->receive_buffer_.push(data[i]);
total += to_transfer;
}
} // while work to do
return to_transfer;
return total;
}
///
+15 -5
View File
@@ -5,11 +5,13 @@ from typing import TypedDict
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_BOARD, KEY_CORE, KEY_FRAMEWORK_VERSION
from esphome.core import CORE
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.helpers import copy_file_if_changed, write_file_if_changed
from esphome.types import ConfigType
from .const import (
BOOTLOADER_MCUBOOT,
CONF_CDC_ACM,
KEY_BOARD,
KEY_BOOTLOADER,
KEY_EXTRA_BUILD_FILES,
@@ -54,7 +56,7 @@ class ZephyrData(TypedDict):
user: dict[str, list[str]]
def zephyr_set_core_data(config):
def zephyr_set_core_data(config: ConfigType) -> None:
CORE.data[KEY_ZEPHYR] = ZephyrData(
board=config[CONF_BOARD],
bootloader=config[KEY_BOOTLOADER],
@@ -64,7 +66,6 @@ def zephyr_set_core_data(config):
pm_static=[],
user={},
)
return config
def zephyr_data() -> ZephyrData:
@@ -110,7 +111,7 @@ def add_extra_script(stage: str, filename: str, path: Path) -> None:
cg.add_platformio_option("extra_scripts", [key])
def zephyr_to_code(config):
def zephyr_to_code(config: ConfigType) -> None:
cg.add_build_flag("-DUSE_ZEPHYR")
cg.add_define("USE_NATIVE_64BIT_TIME")
cg.set_cpp_standard("gnu++20")
@@ -132,6 +133,15 @@ def zephyr_to_code(config):
Path(__file__).parent / "pre_build.py.script",
)
CORE.add_job(_cdc_acm_to_code, config)
@coroutine_with_priority(CoroPriority.FINAL)
async def _cdc_acm_to_code(config: ConfigType) -> None:
if "CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT" in zephyr_data()[KEY_PRJ_CONF]:
var = cg.new_Pvariable(config[CONF_CDC_ACM])
await cg.register_component(var, {})
def zephyr_setup_preferences():
cg.add(zephyr_ns.setup_preferences())
@@ -151,7 +161,7 @@ def _format_prj_conf_val(value: PrjConfValueType) -> str:
raise ValueError
def zephyr_add_cdc_acm(config, id):
def zephyr_add_cdc_acm(config: ConfigType, id: int) -> None:
framework_ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
if CORE.is_nrf52 and framework_ver >= cv.Version(3, 2, 0):
zephyr_add_prj_conf("CONFIG_USB_DEVICE_STACK_NEXT", False)
+30
View File
@@ -0,0 +1,30 @@
#if defined(CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT)
#include "cdc_acm.h"
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/uart/cdc_acm.h>
#define DEVICE_AND_COMMA(node_id) DEVICE_DT_GET(node_id),
namespace esphome::zephyr {
CdcAcm::CdcAcm() { global_cdc_acm = this; }
void CdcAcm::setup() {
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart)
const struct device *cdc_dev[] = {DT_FOREACH_STATUS_OKAY(zephyr_cdc_acm_uart, DEVICE_AND_COMMA)};
for (auto &idx : cdc_dev) {
// only one global callback can be registered
cdc_acm_dte_rate_callback_set(idx, CdcAcm::cdc_dte_rate_callback_);
}
#endif // DT_HAS_COMPAT_STATUS_OKAY(zephyr_cdc_acm_uart)
}
void CdcAcm::cdc_dte_rate_callback_(const struct device *device, uint32_t rate) {
global_cdc_acm->defer([device, rate]() { global_cdc_acm->rate_callbacks_.call(device, rate); });
}
CdcAcm *global_cdc_acm; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace esphome::zephyr
#endif
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#if defined(CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT)
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include <zephyr/device.h>
namespace esphome::zephyr {
class CdcAcm : public Component {
public:
CdcAcm();
void setup() override;
void add_on_rate_callback(std::function<void(const device *, uint32_t)> &&callback) {
this->rate_callbacks_.add(std::move(callback));
}
protected:
static void cdc_dte_rate_callback_(const device *device, uint32_t rate);
CallbackManager<void(const device *, uint32_t)> rate_callbacks_;
};
extern CdcAcm *global_cdc_acm; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace esphome::zephyr
#endif
+2
View File
@@ -14,3 +14,5 @@ KEY_BOARD: Final = "board"
KEY_USER: Final = "user"
zephyr_ns = cg.esphome_ns.namespace("zephyr")
CdcAcm = zephyr_ns.class_("CdcAcm", cg.Component)
CONF_CDC_ACM = "cdc_acm"
+1
View File
@@ -274,6 +274,7 @@ CONF_CURRENT = "current"
CONF_CURRENT_HUMIDITY_STATE_TOPIC = "current_humidity_state_topic"
CONF_CURRENT_OPERATION = "current_operation"
CONF_CURRENT_RESISTOR = "current_resistor"
CONF_CURRENT_TEMPERATURE = "current_temperature"
CONF_CURRENT_TEMPERATURE_STATE_TOPIC = "current_temperature_state_topic"
CONF_CUSTOM = "custom"
CONF_CUSTOM_FAN_MODE = "custom_fan_mode"
+2 -6
View File
@@ -422,15 +422,11 @@ void Component::status_set_error(const LogString *message) {
store_component_error_message(this, LOG_STR_ARG(message), true);
}
}
void Component::status_clear_warning() {
if ((this->component_state_ & STATUS_LED_WARNING) == 0)
return;
void Component::status_clear_warning_slow_path_() {
this->component_state_ &= ~STATUS_LED_WARNING;
ESP_LOGW(TAG, "%s cleared Warning flag", LOG_STR_ARG(this->get_component_log_str()));
}
void Component::status_clear_error() {
if ((this->component_state_ & STATUS_LED_ERROR) == 0)
return;
void Component::status_clear_error_slow_path_() {
this->component_state_ &= ~STATUS_LED_ERROR;
ESP_LOGE(TAG, "%s cleared Error flag", LOG_STR_ARG(this->get_component_log_str()));
}
+13 -2
View File
@@ -251,9 +251,17 @@ class Component {
void status_set_error(const char *message);
void status_set_error(const LogString *message);
void status_clear_warning();
void status_clear_warning() {
if ((this->component_state_ & STATUS_LED_WARNING) == 0)
return;
this->status_clear_warning_slow_path_();
}
void status_clear_error();
void status_clear_error() {
if ((this->component_state_ & STATUS_LED_ERROR) == 0)
return;
this->status_clear_error_slow_path_();
}
/** Set warning status flag and automatically clear it after a timeout.
*
@@ -505,6 +513,9 @@ class Component {
bool cancel_defer(const char *name); // NOLINT
bool cancel_defer(uint32_t id); // NOLINT
void status_clear_warning_slow_path_();
void status_clear_error_slow_path_();
// Ordered for optimal packing on 32-bit systems
const LogString *component_source_{nullptr};
uint16_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS}; ///< Warn if blocked for this many ms (max 65.5s)
+1 -1
View File
@@ -12,7 +12,7 @@ platformio==6.1.19
esptool==5.2.0
click==8.3.1
esphome-dashboard==20260210.0
aioesphomeapi==44.2.0
aioesphomeapi==44.3.1
zeroconf==0.148.0
puremagic==1.30
ruamel.yaml==0.19.1 # dashboard_import
+1 -1
View File
@@ -1,6 +1,6 @@
pylint==4.0.5
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
ruff==0.15.4 # also change in .pre-commit-config.yaml when updating
ruff==0.15.5 # also change in .pre-commit-config.yaml when updating
pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating
pre-commit
+7 -10
View File
@@ -690,15 +690,12 @@ class MessageType(TypeInfo):
@property
def encode_func(self) -> str:
return "encode_message"
return "encode_optional_sub_message"
@property
def encode_content(self) -> str:
# Singular message fields pass force=false (skip empty messages)
# The default for encode_nested_message is force=true (for repeated fields)
return (
f"buffer.{self.encode_func}({self.number}, this->{self.field_name}, false);"
)
# Singular message fields skip encoding when empty
return f"buffer.{self.encode_func}({self.number}, this->{self.field_name});"
@property
def decode_length(self) -> str:
@@ -1322,9 +1319,9 @@ class FixedArrayRepeatedType(TypeInfo):
"""Helper to generate encode statement for a single element."""
if isinstance(self._ti, EnumType):
return f"buffer.{self._ti.encode_func}({self.number}, static_cast<uint32_t>({element}), true);"
# MessageType.encode_message doesn't have a force parameter
# Repeated message elements use encode_sub_message (force=true is default)
if isinstance(self._ti, MessageType):
return f"buffer.{self._ti.encode_func}({self.number}, {element});"
return f"buffer.encode_sub_message({self.number}, {element});"
return f"buffer.{self._ti.encode_func}({self.number}, {element}, true);"
@property
@@ -1650,9 +1647,9 @@ class RepeatedTypeInfo(TypeInfo):
"""Helper to generate encode call for a single element."""
if isinstance(self._ti, EnumType):
return f"buffer.{self._ti.encode_func}({self.number}, static_cast<uint32_t>({element}), true);"
# MessageType.encode_message doesn't have a force parameter
# Repeated message elements use encode_sub_message (force=true is default)
if isinstance(self._ti, MessageType):
return f"buffer.{self._ti.encode_func}({self.number}, {element});"
return f"buffer.encode_sub_message({self.number}, {element});"
return f"buffer.{self._ti.encode_func}({self.number}, {element}, true);"
@property
@@ -0,0 +1 @@
<<: !include common.yaml
@@ -1,4 +1,6 @@
substitutions:
verify_ssl: "false"
<<: !include common.yaml
http_request:
verify_ssl: false
tls_buffer_size_rx: 16384
tls_buffer_size_tx: 512