Compare commits

..

17 Commits

Author SHA1 Message Date
Jesse Hills
50b7349fe0 Merge pull request #9234 from esphome/bump-2025.6.2
2025.6.2
2025-06-28 15:47:02 +12:00
Jonathan Swoboda
61b3379f48 [i2c] Disable i2c scan on certain idf versions (#9237) 2025-06-28 13:33:05 +12:00
Samuel Sieb
5010a0f5e7 [mcp23xxx_base] fix pin interrupts (#9244)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-06-28 13:32:57 +12:00
Jesse Hills
948aa13fb9 Bump version to 2025.6.2 2025-06-27 23:16:13 +12:00
scaiper
9e993ac603 [esp32] Change `enable_lwip_mdns_queries default to True` (#9188) 2025-06-27 23:16:12 +12:00
Kevin Ahrendt
9f3f4ead4f [voice_assistant] Support streaming TTS responses and fixes crash for long responses (#9224) 2025-06-27 23:16:12 +12:00
Kevin Ahrendt
068aa0ff1e [speaker] bugfix: continue to block tasks if stop flag is set (#9222) 2025-06-27 23:16:12 +12:00
Kevin Ahrendt
e146c0796a [audio] Bugfix: improve timeout handling (#9221) 2025-06-27 23:16:12 +12:00
Clyde Stubbs
cceab26bfb [lvgl] Fix dangling pointer issue with qrcode (#9190) 2025-06-27 23:16:12 +12:00
Jesse Hills
fa34adbf6c Merge pull request #9185 from esphome/bump-2025.6.1
2025.6.1
2025-06-24 07:27:59 +12:00
Jesse Hills
22e360d479 Bump version to 2025.6.1 2025-06-23 23:32:22 +12:00
myhomeiot
649936200e Restore access to BLEScanResult as get_scan_result (#9148) 2025-06-23 23:32:22 +12:00
rwrozelle
5d6e690c12 Fixes for setup of OpenThread either using TLV or entering Credentials directly (#9157)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-23 23:32:22 +12:00
Jesse Hills
2f2ecadae7 [config validation] Add more ip address / network validators (#9181) 2025-06-23 23:32:22 +12:00
J. Nick Koston
6dfb9eba61 Fix missing BLE GAP events causing RSSI sensor and beacon failures (#9138) 2025-06-23 23:32:22 +12:00
Edward Firmo
24587fe875 [nextion] Fix command spacing double timing and response blocking issues (#9134) 2025-06-23 23:32:22 +12:00
J. Nick Koston
a1aebe6a2c Eliminate memory fragmentation with BLE event pool (#9101) 2025-06-23 23:32:22 +12:00
255 changed files with 2299 additions and 2924 deletions

View File

@@ -49,7 +49,7 @@ jobs:
with:
python-version: "3.10"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1
uses: docker/setup-buildx-action@v3.10.0
- name: Set TAG
run: |

View File

@@ -99,7 +99,7 @@ jobs:
python-version: "3.10"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1
uses: docker/setup-buildx-action@v3.10.0
- name: Log in to docker hub
uses: docker/login-action@v3.4.0
@@ -178,7 +178,7 @@ jobs:
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1
uses: docker/setup-buildx-action@v3.10.0
- name: Log in to docker hub
if: matrix.registry == 'dockerhub'

View File

@@ -4,7 +4,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.12.0
rev: v0.11.10
hooks:
# Run the linter.
- id: ruff
@@ -12,7 +12,7 @@ repos:
# Run the formatter.
- id: ruff-format
- repo: https://github.com/PyCQA/flake8
rev: 7.3.0
rev: 7.2.0
hooks:
- id: flake8
additional_dependencies:

View File

@@ -490,7 +490,7 @@ esphome/components/vbus/* @ssieb
esphome/components/veml3235/* @kbx81
esphome/components/veml7700/* @latonita
esphome/components/version/* @esphome/core
esphome/components/voice_assistant/* @jesserockz
esphome/components/voice_assistant/* @jesserockz @kahrendt
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
esphome/components/watchdog/* @oarcher
esphome/components/waveshare_epaper/* @clydebarrow
@@ -520,7 +520,6 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
esphome/components/xiaomi_xmwsdj04mmc/* @medusalix
esphome/components/xl9535/* @mreditor97
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
esphome/components/xxtea/* @clydebarrow

View File

@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2025.7.0-dev
PROJECT_NUMBER = 2025.6.2
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@@ -22,7 +22,6 @@ from esphome.cpp_generator import ( # noqa: F401
TemplateArguments,
add,
add_build_flag,
add_build_unflag,
add_define,
add_global,
add_library,
@@ -35,7 +34,6 @@ from esphome.cpp_generator import ( # noqa: F401
process_lambda,
progmem_array,
safe_exp,
set_cpp_standard,
statement,
static_const_array,
templatable,

View File

@@ -193,13 +193,14 @@ void AcDimmer::setup() {
setTimer1Callback(&timer_interrupt);
#endif
#ifdef USE_ESP32
// timer frequency of 1mhz
dimmer_timer = timerBegin(1000000);
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr);
// 80 Divider -> 1 count=1µs
dimmer_timer = timerBegin(0, 80, true);
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
// For ESP32, we can't use dynamic interval calculation because the timerX functions
// are not callable from ISR (placed in flash storage).
// Here we just use an interrupt firing every 50 µs.
timerAlarm(dimmer_timer, 50, true, 0);
timerAlarmWrite(dimmer_timer, 50, true);
timerAlarmEnable(dimmer_timer);
#endif
}
void AcDimmer::write_state(float state) {

View File

@@ -17,11 +17,7 @@ void Anova::setup() {
this->current_request_ = 0;
}
void Anova::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE callbacks so loop isn't needed
this->disable_loop();
}
void Anova::loop() {}
void Anova::control(const ClimateCall &call) {
if (call.get_mode().has_value()) {

View File

@@ -61,8 +61,8 @@ void APIConnection::start() {
APIError err = this->helper_->init();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
return;
}
this->client_info_ = helper_->getpeername();
@@ -91,7 +91,7 @@ void APIConnection::loop() {
// when network is disconnected force disconnect immediately
// don't wait for timeout
this->on_fatal_error();
ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->get_client_combined_info().c_str());
ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->client_combined_info_.c_str());
return;
}
if (this->next_close_) {
@@ -104,7 +104,7 @@ void APIConnection::loop() {
APIError err = this->helper_->loop();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
api_error_to_str(err), errno);
return;
}
@@ -118,12 +118,12 @@ void APIConnection::loop() {
} else if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
} else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", this->get_client_combined_info().c_str());
ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
}
return;
} else {
@@ -157,7 +157,7 @@ void APIConnection::loop() {
// Disconnect if not responded within 2.5*keepalive
if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
on_fatal_error();
ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->client_combined_info_.c_str());
}
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
ESP_LOGVV(TAG, "Sending keepalive PING");
@@ -166,7 +166,7 @@ void APIConnection::loop() {
this->next_ping_retry_ = now + ping_retry_interval;
this->ping_retries_++;
std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);",
this->get_client_combined_info().c_str(), this->ping_retries_);
this->client_combined_info_.c_str(), this->ping_retries_);
if (this->ping_retries_ >= max_ping_retries) {
on_fatal_error();
ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str());
@@ -233,7 +233,7 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
// remote initiated disconnect_client
// don't close yet, we still need to send the disconnect response
// close will happen on next loop
ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
ESP_LOGD(TAG, "%s disconnected", this->client_combined_info_.c_str());
this->next_close_ = true;
DisconnectResponse resp;
return resp;
@@ -1544,7 +1544,8 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
HelloResponse APIConnection::hello(const HelloRequest &msg) {
this->client_info_ = msg.client_info;
this->client_peername_ = this->helper_->getpeername();
this->helper_->set_log_info(this->get_client_combined_info());
this->client_combined_info_ = this->client_info_ + " (" + this->client_peername_ + ")";
this->helper_->set_log_info(this->client_combined_info_);
this->client_api_version_major_ = msg.api_version_major;
this->client_api_version_minor_ = msg.api_version_minor;
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
@@ -1566,7 +1567,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
// bool invalid_password = 1;
resp.invalid_password = !correct;
if (correct) {
ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
ESP_LOGD(TAG, "%s connected", this->client_combined_info_.c_str());
this->connection_state_ = ConnectionState::AUTHENTICATED;
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
#ifdef USE_HOMEASSISTANT_TIME
@@ -1672,7 +1673,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
APIError err = this->helper_->loop();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
api_error_to_str(err), errno);
return false;
}
@@ -1694,10 +1695,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type)
if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
}
return false;
}
@@ -1706,11 +1707,11 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type)
}
void APIConnection::on_unauthenticated_access() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s requested access without authentication", this->get_client_combined_info().c_str());
ESP_LOGD(TAG, "%s requested access without authentication", this->client_combined_info_.c_str());
}
void APIConnection::on_no_setup_connection() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s requested access without full connection", this->get_client_combined_info().c_str());
ESP_LOGD(TAG, "%s requested access without full connection", this->client_combined_info_.c_str());
}
void APIConnection::on_fatal_error() {
this->helper_->close();
@@ -1859,10 +1860,10 @@ void APIConnection::process_batch_() {
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset during batch write", this->get_client_combined_info().c_str());
ESP_LOGW(TAG, "%s: Connection reset during batch write", this->client_combined_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
}
}

View File

@@ -275,13 +275,7 @@ class APIConnection : public APIServerConnection {
bool try_to_clear_buffer(bool log_out_of_space);
bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override;
std::string get_client_combined_info() const {
if (this->client_info_ == this->client_peername_) {
// Before Hello message, both are the same (just IP:port)
return this->client_info_;
}
return this->client_info_ + " (" + this->client_peername_ + ")";
}
std::string get_client_combined_info() const { return this->client_combined_info_; }
// Buffer allocator methods for batch processing
ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
@@ -438,45 +432,38 @@ class APIConnection : public APIServerConnection {
// Helper function to get estimated message size for buffer pre-allocation
static uint16_t get_estimated_message_size(uint16_t message_type);
// Pointers first (4 bytes each, naturally aligned)
std::unique_ptr<APIFrameHelper> helper_;
APIServer *parent_;
// 4-byte aligned types
uint32_t last_traffic_;
uint32_t next_ping_retry_{0};
int state_subs_at_ = -1;
// Strings (12 bytes each on 32-bit)
std::string client_info_;
std::string client_peername_;
// 2-byte aligned types
uint16_t client_api_version_major_{0};
uint16_t client_api_version_minor_{0};
// Group all 1-byte types together to minimize padding
enum class ConnectionState : uint8_t {
enum class ConnectionState {
WAITING_FOR_HELLO,
CONNECTED,
AUTHENTICATED,
} connection_state_{ConnectionState::WAITING_FOR_HELLO};
uint8_t log_subscription_{ESPHOME_LOG_LEVEL_NONE};
bool remove_{false};
bool state_subscription_{false};
bool sent_ping_{false};
bool service_call_subscription_{false};
bool next_close_ = false;
uint8_t ping_retries_{0};
// 8 bytes used, no padding needed
// Larger objects at the end
InitialStateIterator initial_state_iterator_;
ListEntitiesIterator list_entities_iterator_;
bool remove_{false};
std::unique_ptr<APIFrameHelper> helper_;
std::string client_info_;
std::string client_peername_;
std::string client_combined_info_;
uint32_t client_api_version_major_{0};
uint32_t client_api_version_minor_{0};
#ifdef USE_ESP32_CAMERA
esp32_camera::CameraImageReader image_reader_;
#endif
bool state_subscription_{false};
int log_subscription_{ESPHOME_LOG_LEVEL_NONE};
uint32_t last_traffic_;
uint32_t next_ping_retry_{0};
uint8_t ping_retries_{0};
bool sent_ping_{false};
bool service_call_subscription_{false};
bool next_close_ = false;
APIServer *parent_;
InitialStateIterator initial_state_iterator_;
ListEntitiesIterator list_entities_iterator_;
int state_subs_at_ = -1;
// Function pointer type for message encoding
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);

View File

@@ -125,6 +125,38 @@ class APIFrameHelper {
const uint8_t *current_data() const { return data.data() + offset; }
};
// Queue of data buffers to be sent
std::deque<SendBuffer> tx_buf_;
// Common state enum for all frame helpers
// Note: Not all states are used by all implementations
// - INITIALIZE: Used by both Noise and Plaintext
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
// - DATA: Used by both Noise and Plaintext
// - CLOSED: Used by both Noise and Plaintext
// - FAILED: Used by both Noise and Plaintext
// - EXPLICIT_REJECT: Only used by Noise protocol
enum class State {
INITIALIZE = 1,
CLIENT_HELLO = 2, // Noise only
SERVER_HELLO = 3, // Noise only
HANDSHAKE = 4, // Noise only
DATA = 5,
CLOSED = 6,
FAILED = 7,
EXPLICIT_REJECT = 8, // Noise only
};
// Current state of the frame helper
State state_{State::INITIALIZE};
// Helper name for logging
std::string info_;
// Socket for communication
socket::Socket *socket_{nullptr};
std::unique_ptr<socket::Socket> socket_owned_;
// Common implementation for writing raw data to socket
APIError write_raw_(const struct iovec *iov, int iovcnt);
@@ -137,41 +169,15 @@ class APIFrameHelper {
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
const std::string &info, StateEnum &state, StateEnum failed_state);
// Pointers first (4 bytes each)
socket::Socket *socket_{nullptr};
std::unique_ptr<socket::Socket> socket_owned_;
// Common state enum for all frame helpers
// Note: Not all states are used by all implementations
// - INITIALIZE: Used by both Noise and Plaintext
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
// - DATA: Used by both Noise and Plaintext
// - CLOSED: Used by both Noise and Plaintext
// - FAILED: Used by both Noise and Plaintext
// - EXPLICIT_REJECT: Only used by Noise protocol
enum class State : uint8_t {
INITIALIZE = 1,
CLIENT_HELLO = 2, // Noise only
SERVER_HELLO = 3, // Noise only
HANDSHAKE = 4, // Noise only
DATA = 5,
CLOSED = 6,
FAILED = 7,
EXPLICIT_REJECT = 8, // Noise only
};
// Containers (size varies, but typically 12+ bytes on 32-bit)
std::deque<SendBuffer> tx_buf_;
std::string info_;
std::vector<struct iovec> reusable_iovs_;
std::vector<uint8_t> rx_buf_;
// Group smaller types together
uint16_t rx_buf_len_ = 0;
State state_{State::INITIALIZE};
uint8_t frame_header_padding_{0};
uint8_t frame_footer_size_{0};
// 5 bytes total, 3 bytes padding
// Reusable IOV array for write_protobuf_packets to avoid repeated allocations
std::vector<struct iovec> reusable_iovs_;
// Receive buffer for reading frame data
std::vector<uint8_t> rx_buf_;
uint16_t rx_buf_len_ = 0;
// Common initialization for both plaintext and noise protocols
APIError init_common_();
@@ -207,28 +213,19 @@ class APINoiseFrameHelper : public APIFrameHelper {
APIError init_handshake_();
APIError check_handshake_finished_();
void send_explicit_handshake_reject_(const std::string &reason);
// Pointers first (4 bytes each)
NoiseHandshakeState *handshake_{nullptr};
NoiseCipherState *send_cipher_{nullptr};
NoiseCipherState *recv_cipher_{nullptr};
// Shared pointer (8 bytes on 32-bit = 4 bytes control block pointer + 4 bytes object pointer)
std::shared_ptr<APINoiseContext> ctx_;
// Vector (12 bytes on 32-bit)
std::vector<uint8_t> prologue_;
// NoiseProtocolId (size depends on implementation)
NoiseProtocolId nid_;
// Group small types together
// Fixed-size header buffer for noise protocol:
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
// Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
uint8_t rx_header_buf_[3];
uint8_t rx_header_buf_len_ = 0;
// 4 bytes total, no padding
std::vector<uint8_t> prologue_;
std::shared_ptr<APINoiseContext> ctx_;
NoiseHandshakeState *handshake_{nullptr};
NoiseCipherState *send_cipher_{nullptr};
NoiseCipherState *recv_cipher_{nullptr};
NoiseProtocolId nid_;
};
#endif // USE_API_NOISE
@@ -255,12 +252,6 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
protected:
APIError try_read_frame_(ParsedFrame *frame);
// Group 2-byte aligned types
uint16_t rx_header_parsed_type_ = 0;
uint16_t rx_header_parsed_len_ = 0;
// Group 1-byte types together
// Fixed-size header buffer for plaintext protocol:
// We now store the indicator byte + the two varints.
// To match noise protocol's maximum message size (UINT16_MAX = 65535), we need:
@@ -272,7 +263,8 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type)
uint8_t rx_header_buf_pos_ = 0;
bool rx_header_parsed_ = false;
// 8 bytes total, no padding needed
uint16_t rx_header_parsed_type_ = 0;
uint16_t rx_header_parsed_len_ = 0;
};
#endif

View File

@@ -797,18 +797,28 @@ void ConnectResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
void DisconnectRequest::encode(ProtoWriteBuffer buffer) const {}
void DisconnectRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); }
#endif
void DisconnectResponse::encode(ProtoWriteBuffer buffer) const {}
void DisconnectResponse::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); }
#endif
void PingRequest::encode(ProtoWriteBuffer buffer) const {}
void PingRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); }
#endif
void PingResponse::encode(ProtoWriteBuffer buffer) const {}
void PingResponse::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); }
#endif
void DeviceInfoRequest::encode(ProtoWriteBuffer buffer) const {}
void DeviceInfoRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); }
#endif
@@ -1029,12 +1039,18 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {}
void ListEntitiesRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); }
#endif
void ListEntitiesDoneResponse::encode(ProtoWriteBuffer buffer) const {}
void ListEntitiesDoneResponse::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); }
#endif
void SubscribeStatesRequest::encode(ProtoWriteBuffer buffer) const {}
void SubscribeStatesRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); }
#endif
@@ -3355,6 +3371,8 @@ void NoiseEncryptionSetKeyResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
void SubscribeHomeassistantServicesRequest::encode(ProtoWriteBuffer buffer) const {}
void SubscribeHomeassistantServicesRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const {
out.append("SubscribeHomeassistantServicesRequest {}");
@@ -3480,6 +3498,8 @@ void HomeassistantServiceResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
void SubscribeHomeAssistantStatesRequest::encode(ProtoWriteBuffer buffer) const {}
void SubscribeHomeAssistantStatesRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
out.append("SubscribeHomeAssistantStatesRequest {}");
@@ -3583,6 +3603,8 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {}
void GetTimeRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); }
#endif
@@ -7477,6 +7499,8 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
void SubscribeBluetoothConnectionsFreeRequest::encode(ProtoWriteBuffer buffer) const {}
void SubscribeBluetoothConnectionsFreeRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeBluetoothConnectionsFreeRequest::dump_to(std::string &out) const {
out.append("SubscribeBluetoothConnectionsFreeRequest {}");
@@ -7760,6 +7784,8 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {}
void UnsubscribeBluetoothLEAdvertisementsRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
@@ -8425,6 +8451,8 @@ void VoiceAssistantWakeWord::dump_to(std::string &out) const {
out.append("}");
}
#endif
void VoiceAssistantConfigurationRequest::encode(ProtoWriteBuffer buffer) const {}
void VoiceAssistantConfigurationRequest::calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const {
out.append("VoiceAssistantConfigurationRequest {}");

View File

@@ -357,6 +357,8 @@ class DisconnectRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "disconnect_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -370,6 +372,8 @@ class DisconnectResponse : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "disconnect_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -383,6 +387,8 @@ class PingRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "ping_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -396,6 +402,8 @@ class PingResponse : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "ping_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -409,6 +417,8 @@ class DeviceInfoRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "device_info_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -458,6 +468,8 @@ class ListEntitiesRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -471,6 +483,8 @@ class ListEntitiesDoneResponse : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_done_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -484,6 +498,8 @@ class SubscribeStatesRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_states_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -995,6 +1011,8 @@ class SubscribeHomeassistantServicesRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_homeassistant_services_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -1043,6 +1061,8 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_home_assistant_states_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -1095,6 +1115,8 @@ class GetTimeRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "get_time_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -2095,6 +2117,8 @@ class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_bluetooth_connections_free_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -2220,6 +2244,8 @@ class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "unsubscribe_bluetooth_le_advertisements_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@@ -2486,6 +2512,8 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_configuration_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif

View File

@@ -620,300 +620,544 @@ void APIServerConnection::on_ping_request(const PingRequest &msg) {
}
}
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
if (this->check_connection_setup_()) {
DeviceInfoResponse ret = this->device_info(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
DeviceInfoResponse ret = this->device_info(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) {
if (this->check_authenticated_()) {
this->list_entities(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->list_entities(msg);
}
void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) {
if (this->check_authenticated_()) {
this->subscribe_states(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_states(msg);
}
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) {
if (this->check_authenticated_()) {
this->subscribe_logs(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_logs(msg);
}
void APIServerConnection::on_subscribe_homeassistant_services_request(
const SubscribeHomeassistantServicesRequest &msg) {
if (this->check_authenticated_()) {
this->subscribe_homeassistant_services(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_homeassistant_services(msg);
}
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
if (this->check_authenticated_()) {
this->subscribe_home_assistant_states(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_home_assistant_states(msg);
}
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
if (this->check_connection_setup_()) {
GetTimeResponse ret = this->get_time(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
GetTimeResponse ret = this->get_time(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) {
if (this->check_authenticated_()) {
this->execute_service(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->execute_service(msg);
}
#ifdef USE_API_NOISE
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
if (this->check_authenticated_()) {
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_BUTTON
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
if (this->check_authenticated_()) {
this->button_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->button_command(msg);
}
#endif
#ifdef USE_ESP32_CAMERA
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) {
if (this->check_authenticated_()) {
this->camera_image(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->camera_image(msg);
}
#endif
#ifdef USE_CLIMATE
void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) {
if (this->check_authenticated_()) {
this->climate_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->climate_command(msg);
}
#endif
#ifdef USE_COVER
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) {
if (this->check_authenticated_()) {
this->cover_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->cover_command(msg);
}
#endif
#ifdef USE_DATETIME_DATE
void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) {
if (this->check_authenticated_()) {
this->date_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->date_command(msg);
}
#endif
#ifdef USE_DATETIME_DATETIME
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
if (this->check_authenticated_()) {
this->datetime_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->datetime_command(msg);
}
#endif
#ifdef USE_FAN
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) {
if (this->check_authenticated_()) {
this->fan_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->fan_command(msg);
}
#endif
#ifdef USE_LIGHT
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) {
if (this->check_authenticated_()) {
this->light_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->light_command(msg);
}
#endif
#ifdef USE_LOCK
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
if (this->check_authenticated_()) {
this->lock_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->lock_command(msg);
}
#endif
#ifdef USE_MEDIA_PLAYER
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
if (this->check_authenticated_()) {
this->media_player_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->media_player_command(msg);
}
#endif
#ifdef USE_NUMBER
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
if (this->check_authenticated_()) {
this->number_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->number_command(msg);
}
#endif
#ifdef USE_SELECT
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
if (this->check_authenticated_()) {
this->select_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->select_command(msg);
}
#endif
#ifdef USE_SIREN
void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) {
if (this->check_authenticated_()) {
this->siren_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->siren_command(msg);
}
#endif
#ifdef USE_SWITCH
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) {
if (this->check_authenticated_()) {
this->switch_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->switch_command(msg);
}
#endif
#ifdef USE_TEXT
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) {
if (this->check_authenticated_()) {
this->text_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->text_command(msg);
}
#endif
#ifdef USE_DATETIME_TIME
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
if (this->check_authenticated_()) {
this->time_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->time_command(msg);
}
#endif
#ifdef USE_UPDATE
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) {
if (this->check_authenticated_()) {
this->update_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->update_command(msg);
}
#endif
#ifdef USE_VALVE
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) {
if (this->check_authenticated_()) {
this->valve_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->valve_command(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
if (this->check_authenticated_()) {
this->subscribe_bluetooth_le_advertisements(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
if (this->check_authenticated_()) {
this->bluetooth_device_request(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_device_request(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
if (this->check_authenticated_()) {
this->bluetooth_gatt_get_services(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_get_services(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
if (this->check_authenticated_()) {
this->bluetooth_gatt_read(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_read(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
if (this->check_authenticated_()) {
this->bluetooth_gatt_write(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_write(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
if (this->check_authenticated_()) {
this->bluetooth_gatt_read_descriptor(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_read_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
if (this->check_authenticated_()) {
this->bluetooth_gatt_write_descriptor(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_write_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
if (this->check_authenticated_()) {
this->bluetooth_gatt_notify(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_notify(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
const SubscribeBluetoothConnectionsFreeRequest &msg) {
if (this->check_authenticated_()) {
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
if (this->check_authenticated_()) {
this->unsubscribe_bluetooth_le_advertisements(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->unsubscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
if (this->check_authenticated_()) {
this->bluetooth_scanner_set_mode(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_scanner_set_mode(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
if (this->check_authenticated_()) {
this->subscribe_voice_assistant(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_voice_assistant(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
if (this->check_authenticated_()) {
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
if (this->check_authenticated_()) {
this->voice_assistant_set_configuration(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->voice_assistant_set_configuration(msg);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
if (this->check_authenticated_()) {
this->alarm_control_panel_command(msg);
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->alarm_control_panel_command(msg);
}
#endif

View File

@@ -47,11 +47,6 @@ void APIServer::setup() {
}
#endif
// Schedule reboot if no clients connect within timeout
if (this->reboot_timeout_ != 0) {
this->schedule_reboot_timeout_();
}
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
if (this->socket_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket");
@@ -111,6 +106,8 @@ void APIServer::setup() {
}
#endif
this->last_connected_ = millis();
#ifdef USE_ESP32_CAMERA
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
esp32_camera::global_esp32_camera->add_image_callback(
@@ -124,16 +121,6 @@ void APIServer::setup() {
#endif
}
void APIServer::schedule_reboot_timeout_() {
this->status_set_warning();
this->set_timeout("api_reboot", this->reboot_timeout_, []() {
if (!global_api_server->is_connected()) {
ESP_LOGE(TAG, "No client connected; rebooting");
App.reboot();
}
});
}
void APIServer::loop() {
// Accept new clients only if the socket exists and has incoming connections
if (this->socket_ && this->socket_->ready()) {
@@ -148,12 +135,6 @@ void APIServer::loop() {
auto *conn = new APIConnection(std::move(sock), this);
this->clients_.emplace_back(conn);
conn->start();
// Clear warning status and cancel reboot when first client connects
if (this->clients_.size() == 1 && this->reboot_timeout_ != 0) {
this->status_clear_warning();
this->cancel_timeout("api_reboot");
}
}
}
@@ -173,12 +154,6 @@ void APIServer::loop() {
std::swap(this->clients_[client_index], this->clients_.back());
}
this->clients_.pop_back();
// Schedule reboot when last client disconnects
if (this->clients_.empty() && this->reboot_timeout_ != 0) {
this->schedule_reboot_timeout_();
}
// Don't increment client_index since we need to process the swapped element
} else {
// Process active client
@@ -188,7 +163,19 @@ void APIServer::loop() {
}
}
// Reboot timeout is now handled by connection/disconnection events
if (this->reboot_timeout_ != 0) {
const uint32_t now = millis();
if (!this->is_connected()) {
if (now - this->last_connected_ > this->reboot_timeout_) {
ESP_LOGE(TAG, "No client connected; rebooting");
App.reboot();
}
this->status_set_warning();
} else {
this->last_connected_ = now;
this->status_clear_warning();
}
}
}
void APIServer::dump_config() {
@@ -240,7 +227,7 @@ bool APIServer::check_password(const std::string &password) const {
void APIServer::handle_disconnect(APIConnection *conn) {}
#ifdef USE_BINARY_SENSOR
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) {
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)

View File

@@ -54,7 +54,7 @@ class APIServer : public Component, public Controller {
void handle_disconnect(APIConnection *conn);
#ifdef USE_BINARY_SENSOR
void on_binary_sensor_update(binary_sensor::BinarySensor *obj) override;
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
#endif
#ifdef USE_COVER
void on_cover_update(cover::Cover *obj) override;
@@ -142,27 +142,19 @@ class APIServer : public Component, public Controller {
}
protected:
void schedule_reboot_timeout_();
// Pointers and pointer-like types first (4 bytes each)
bool shutting_down_ = false;
std::unique_ptr<socket::Socket> socket_ = nullptr;
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
// 4-byte aligned types
uint16_t port_{6053};
uint32_t reboot_timeout_{300000};
uint32_t batch_delay_{100};
// Vectors and strings (12 bytes each on 32-bit)
uint32_t last_connected_{0};
std::vector<std::unique_ptr<APIConnection>> clients_;
std::string password_;
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
std::vector<HomeAssistantStateSubscription> state_subs_;
std::vector<UserServiceDescriptor *> user_services_;
// Group smaller types together
uint16_t port_{6053};
bool shutting_down_ = false;
// 3 bytes used, 1 byte padding
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
#ifdef USE_API_NOISE
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();

View File

@@ -327,11 +327,9 @@ class ProtoWriteBuffer {
class ProtoMessage {
public:
virtual ~ProtoMessage() = default;
// Default implementation for messages with no fields
virtual void encode(ProtoWriteBuffer buffer) const {}
virtual void encode(ProtoWriteBuffer buffer) const = 0;
void decode(const uint8_t *buffer, size_t length);
// Default implementation for messages with no fields
virtual void calculate_size(uint32_t &total_size) const {}
virtual void calculate_size(uint32_t &total_size) const = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
std::string dump() const;
virtual void dump_to(std::string &out) const = 0;
@@ -379,26 +377,6 @@ class ProtoService {
// Send the buffer
return this->send_buffer(buffer, message_type);
}
// Authentication helper methods
bool check_connection_setup_() {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return false;
}
return true;
}
bool check_authenticated_() {
if (!this->check_connection_setup_()) {
return false;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return false;
}
return true;
}
};
} // namespace api

View File

@@ -21,8 +21,8 @@ CONFIG_SCHEMA = cv.All(
@coroutine_with_priority(200.0)
async def to_code(config):
if CORE.is_esp32 or CORE.is_libretiny:
# https://github.com/ESP32Async/AsyncTCP
cg.add_library("ESP32Async/AsyncTCP", "3.4.4")
# https://github.com/esphome/AsyncTCP/blob/master/library.json
cg.add_library("esphome/AsyncTCP-esphome", "2.1.4")
elif CORE.is_esp8266:
# https://github.com/ESP32Async/ESPAsyncTCP
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")
# https://github.com/esphome/ESPAsyncTCP
cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")

View File

@@ -5,6 +5,7 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
#include "esp_crt_bundle.h"
@@ -16,13 +17,13 @@ namespace audio {
static const uint32_t READ_WRITE_TIMEOUT_MS = 20;
static const uint32_t CONNECTION_TIMEOUT_MS = 5000;
// The number of times the http read times out with no data before throwing an error
static const uint32_t ERROR_COUNT_NO_DATA_READ_TIMEOUT = 100;
static const uint8_t MAX_FETCHING_HEADER_ATTEMPTS = 6;
static const size_t HTTP_STREAM_BUFFER_SIZE = 2048;
static const uint8_t MAX_REDIRECTION = 5;
static const uint8_t MAX_REDIRECTIONS = 5;
static const char *const TAG = "audio_reader";
// Some common HTTP status codes - borrowed from http_request component accessed 20241224
enum HttpStatus {
@@ -94,7 +95,7 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
client_config.url = uri.c_str();
client_config.cert_pem = nullptr;
client_config.disable_auto_redirect = false;
client_config.max_redirection_count = 10;
client_config.max_redirection_count = MAX_REDIRECTIONS;
client_config.event_handler = http_event_handler;
client_config.user_data = this;
client_config.buffer_size = HTTP_STREAM_BUFFER_SIZE;
@@ -116,12 +117,29 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
esp_err_t err = esp_http_client_open(this->client_, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open URL");
this->cleanup_connection_();
return err;
}
int64_t header_length = esp_http_client_fetch_headers(this->client_);
uint8_t reattempt_count = 0;
while ((header_length < 0) && (reattempt_count < MAX_FETCHING_HEADER_ATTEMPTS)) {
this->cleanup_connection_();
if (header_length != -ESP_ERR_HTTP_EAGAIN) {
// Serious error, no recovery
return ESP_FAIL;
} else {
// Reconnect from a fresh state to avoid a bug where it never reads the headers even if made available
this->client_ = esp_http_client_init(&client_config);
esp_http_client_open(this->client_, 0);
header_length = esp_http_client_fetch_headers(this->client_);
++reattempt_count;
}
}
if (header_length < 0) {
ESP_LOGE(TAG, "Failed to fetch headers");
this->cleanup_connection_();
return ESP_FAIL;
}
@@ -135,7 +153,7 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
ssize_t redirect_count = 0;
while ((esp_http_client_set_redirection(this->client_) == ESP_OK) && (redirect_count < MAX_REDIRECTION)) {
while ((esp_http_client_set_redirection(this->client_) == ESP_OK) && (redirect_count < MAX_REDIRECTIONS)) {
err = esp_http_client_open(this->client_, 0);
if (err != ESP_OK) {
this->cleanup_connection_();
@@ -267,27 +285,29 @@ AudioReaderState AudioReader::http_read_() {
return AudioReaderState::FINISHED;
}
} else if (this->output_transfer_buffer_->free() > 0) {
size_t bytes_to_read = this->output_transfer_buffer_->free();
int received_len =
esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(), bytes_to_read);
int received_len = esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(),
this->output_transfer_buffer_->free());
if (received_len > 0) {
this->output_transfer_buffer_->increase_buffer_length(received_len);
this->last_data_read_ms_ = millis();
} else if (received_len < 0) {
return AudioReaderState::READING;
} else if (received_len <= 0) {
// HTTP read error
this->cleanup_connection_();
return AudioReaderState::FAILED;
} else {
if (bytes_to_read > 0) {
// Read timed out
if ((millis() - this->last_data_read_ms_) > CONNECTION_TIMEOUT_MS) {
this->cleanup_connection_();
return AudioReaderState::FAILED;
}
delay(READ_WRITE_TIMEOUT_MS);
if (received_len == -1) {
// A true connection error occured, no chance at recovery
this->cleanup_connection_();
return AudioReaderState::FAILED;
}
// Read timed out, manually verify if it has been too long since the last successful read
if ((millis() - this->last_data_read_ms_) > MAX_FETCHING_HEADER_ATTEMPTS * CONNECTION_TIMEOUT_MS) {
ESP_LOGE(TAG, "Timed out");
this->cleanup_connection_();
return AudioReaderState::FAILED;
}
delay(READ_WRITE_TIMEOUT_MS);
}
}

View File

@@ -86,7 +86,7 @@ bool AudioTransferBuffer::reallocate(size_t new_buffer_size) {
bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
this->buffer_size_ = buffer_size;
RAMAllocator<uint8_t> allocator;
RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->buffer_ = allocator.allocate(this->buffer_size_);
if (this->buffer_ == nullptr) {
@@ -101,7 +101,7 @@ bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
void AudioTransferBuffer::deallocate_buffer_() {
if (this->buffer_ != nullptr) {
RAMAllocator<uint8_t> allocator;
RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
allocator.deallocate(this->buffer_, this->buffer_size_);
this->buffer_ = nullptr;
this->data_start_ = nullptr;

View File

@@ -480,11 +480,7 @@ void BedJetHub::set_clock(uint8_t hour, uint8_t minute) {
/* Internal */
void BedJetHub::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE callbacks so loop isn't needed
this->disable_loop();
}
void BedJetHub::loop() {}
void BedJetHub::update() { this->dispatch_status_(); }
void BedJetHub::dump_config() {

View File

@@ -83,11 +83,7 @@ void BedJetClimate::reset_state_() {
this->publish_state();
}
void BedJetClimate::loop() {
// This component is controlled via the parent BedJetHub
// Empty loop not needed, disable to save CPU cycles
this->disable_loop();
}
void BedJetClimate::loop() {}
void BedJetClimate::control(const ClimateCall &call) {
ESP_LOGD(TAG, "Received BedJetClimate::control");

View File

@@ -7,13 +7,11 @@
extern "C" {
#include "rtos_pub.h"
// rtos_pub.h must be included before the rest of the includes
#include "spi.h"
#include "arm_arch.h"
#include "general_dma_pub.h"
#include "gpio_pub.h"
#include "icu_pub.h"
#include "spi.h"
#undef SPI_DAT
#undef SPI_BASE
};
@@ -126,7 +124,7 @@ void BekenSPILEDStripLightOutput::setup() {
size_t buffer_size = this->get_buffer_size_();
size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
RAMAllocator<uint8_t> allocator;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->buf_ = allocator.allocate(buffer_size);
if (this->buf_ == nullptr) {
ESP_LOGE(TAG, "Cannot allocate LED buffer!");

View File

@@ -50,7 +50,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
// turn on (after one-shot sensor automatically powers down)
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Power on failed");
ESP_LOGW(TAG, "Turning on BH1750 failed");
f(NAN);
return;
}
@@ -60,7 +60,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Set measurement time failed");
ESP_LOGW(TAG, "Setting measurement time for BH1750 failed");
active_mtreg_ = 0;
f(NAN);
return;
@@ -88,7 +88,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
return;
}
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Start measurement failed");
ESP_LOGW(TAG, "Starting measurement for BH1750 failed");
f(NAN);
return;
}
@@ -99,7 +99,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
uint16_t raw_value;
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Read data failed");
ESP_LOGW(TAG, "Reading BH1750 data failed");
f(NAN);
return;
}
@@ -156,7 +156,7 @@ void BH1750Sensor::update() {
this->publish_state(NAN);
return;
}
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), val);
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val);
this->status_clear_warning();
this->publish_state(val);
});

View File

@@ -1,10 +1,7 @@
from logging import getLogger
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
from esphome.components.const import CONF_ON_STATE_CHANGE
import esphome.config_validation as cv
from esphome.const import (
CONF_DELAY,
@@ -101,7 +98,6 @@ IS_PLATFORM_COMPONENT = True
CONF_TIME_OFF = "time_off"
CONF_TIME_ON = "time_on"
CONF_TRIGGER_ON_INITIAL_STATE = "trigger_on_initial_state"
DEFAULT_DELAY = "1s"
DEFAULT_TIME_OFF = "100ms"
@@ -131,17 +127,9 @@ MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent")
StateTrigger = binary_sensor_ns.class_(
"StateTrigger", automation.Trigger.template(bool)
)
StateChangeTrigger = binary_sensor_ns.class_(
"StateChangeTrigger",
automation.Trigger.template(cg.optional.template(bool), cg.optional.template(bool)),
)
BinarySensorPublishAction = binary_sensor_ns.class_(
"BinarySensorPublishAction", automation.Action
)
BinarySensorInvalidateAction = binary_sensor_ns.class_(
"BinarySensorInvalidateAction", automation.Action
)
# Condition
BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Condition)
@@ -156,8 +144,6 @@ AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Compon
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component)
_LOGGER = getLogger(__name__)
FILTER_REGISTRY = Registry()
validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
@@ -400,14 +386,6 @@ def validate_click_timing(value):
return value
def validate_publish_initial_state(value):
value = cv.boolean(value)
_LOGGER.warning(
"The 'publish_initial_state' option has been replaced by 'trigger_on_initial_state' and will be removed in a future release"
)
return value
_BINARY_SENSOR_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
.extend(cv.MQTT_COMPONENT_SCHEMA)
@@ -417,12 +395,7 @@ _BINARY_SENSOR_SCHEMA = (
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
mqtt.MQTTBinarySensorComponent
),
cv.Exclusive(
CONF_PUBLISH_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE
): validate_publish_initial_state,
cv.Exclusive(
CONF_TRIGGER_ON_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE
): cv.boolean,
cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
@@ -481,11 +454,6 @@ _BINARY_SENSOR_SCHEMA = (
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateChangeTrigger),
}
),
}
)
)
@@ -525,10 +493,8 @@ async def setup_binary_sensor_core_(var, config):
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class))
trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get(
CONF_PUBLISH_INITIAL_STATE, False
)
cg.add(var.set_trigger_on_initial_state(trigger))
if publish_initial_state := config.get(CONF_PUBLISH_INITIAL_STATE):
cg.add(var.set_publish_initial_state(publish_initial_state))
if inverted := config.get(CONF_INVERTED):
cg.add(var.set_inverted(inverted))
if filters_config := config.get(CONF_FILTERS):
@@ -576,17 +542,6 @@ async def setup_binary_sensor_core_(var, config):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [(bool, "x")], conf)
for conf in config.get(CONF_ON_STATE_CHANGE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(
trigger,
[
(cg.optional.template(bool), "x_previous"),
(cg.optional.template(bool), "x"),
],
conf,
)
if mqtt_id := config.get(CONF_MQTT_ID):
mqtt_ = cg.new_Pvariable(mqtt_id, var)
await mqtt.register_mqtt_component(mqtt_, config)
@@ -636,18 +591,3 @@ async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args)
async def to_code(config):
cg.add_define("USE_BINARY_SENSOR")
cg.add_global(binary_sensor_ns.using)
@automation.register_action(
"binary_sensor.invalidate_state",
BinarySensorInvalidateAction,
cv.maybe_simple_value(
{
cv.Required(CONF_ID): cv.use_id(BinarySensor),
},
key=CONF_ID,
),
)
async def binary_sensor_invalidate_state_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)

View File

@@ -96,7 +96,7 @@ class MultiClickTrigger : public Trigger<>, public Component {
: parent_(parent), timing_(std::move(timing)) {}
void setup() override {
this->last_state_ = this->parent_->get_state_default(false);
this->last_state_ = this->parent_->state;
auto f = std::bind(&MultiClickTrigger::on_state_, this, std::placeholders::_1);
this->parent_->add_on_state_callback(f);
}
@@ -130,14 +130,6 @@ class StateTrigger : public Trigger<bool> {
}
};
class StateChangeTrigger : public Trigger<optional<bool>, optional<bool> > {
public:
explicit StateChangeTrigger(BinarySensor *parent) {
parent->add_full_state_callback(
[this](optional<bool> old_state, optional<bool> state) { this->trigger(old_state, state); });
}
};
template<typename... Ts> class BinarySensorCondition : public Condition<Ts...> {
public:
BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {}
@@ -162,15 +154,5 @@ template<typename... Ts> class BinarySensorPublishAction : public Action<Ts...>
BinarySensor *sensor_;
};
template<typename... Ts> class BinarySensorInvalidateAction : public Action<Ts...> {
public:
explicit BinarySensorInvalidateAction(BinarySensor *sensor) : sensor_(sensor) {}
void play(Ts... x) override { this->sensor_->invalidate_state(); }
protected:
BinarySensor *sensor_;
};
} // namespace binary_sensor
} // namespace esphome

View File

@@ -7,25 +7,42 @@ namespace binary_sensor {
static const char *const TAG = "binary_sensor";
void BinarySensor::publish_state(bool new_state) {
void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) {
this->state_callback_.add(std::move(callback));
}
void BinarySensor::publish_state(bool state) {
if (!this->publish_dedup_.next(state))
return;
if (this->filter_list_ == nullptr) {
this->send_state_internal(new_state);
this->send_state_internal(state, false);
} else {
this->filter_list_->input(new_state);
this->filter_list_->input(state, false);
}
}
void BinarySensor::publish_initial_state(bool new_state) {
this->invalidate_state();
this->publish_state(new_state);
}
void BinarySensor::send_state_internal(bool new_state) {
// copy the new state to the visible property for backwards compatibility, before any callbacks
this->state = new_state;
// Note that set_state_ de-dups and will only trigger callbacks if the state has actually changed
if (this->set_state_(new_state)) {
ESP_LOGD(TAG, "'%s': New state is %s", this->get_name().c_str(), ONOFF(new_state));
void BinarySensor::publish_initial_state(bool state) {
if (!this->publish_dedup_.next(state))
return;
if (this->filter_list_ == nullptr) {
this->send_state_internal(state, true);
} else {
this->filter_list_->input(state, true);
}
}
void BinarySensor::send_state_internal(bool state, bool is_initial) {
if (is_initial) {
ESP_LOGD(TAG, "'%s': Sending initial state %s", this->get_name().c_str(), ONOFF(state));
} else {
ESP_LOGD(TAG, "'%s': Sending state %s", this->get_name().c_str(), ONOFF(state));
}
this->has_state_ = true;
this->state = state;
if (!is_initial || this->publish_initial_state_) {
this->state_callback_.call(state);
}
}
BinarySensor::BinarySensor() : state(false) {}
void BinarySensor::add_filter(Filter *filter) {
filter->parent_ = this;
@@ -43,6 +60,7 @@ void BinarySensor::add_filters(const std::vector<Filter *> &filters) {
this->add_filter(filter);
}
}
bool BinarySensor::has_state() const { return this->has_state_; }
bool BinarySensor::is_status_binary_sensor() const { return false; }
} // namespace binary_sensor

View File

@@ -1,5 +1,6 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/filter.h"
@@ -33,39 +34,52 @@ namespace binary_sensor {
* The sub classes should notify the front-end of new states via the publish_state() method which
* handles inverted inputs for you.
*/
class BinarySensor : public StatefulEntityBase<bool>, public EntityBase_DeviceClass {
class BinarySensor : public EntityBase, public EntityBase_DeviceClass {
public:
explicit BinarySensor(){};
explicit BinarySensor();
/** Add a callback to be notified of state changes.
*
* @param callback The void(bool) callback.
*/
void add_on_state_callback(std::function<void(bool)> &&callback);
/** Publish a new state to the front-end.
*
* @param new_state The new state.
* @param state The new state.
*/
void publish_state(bool new_state);
void publish_state(bool state);
/** Publish the initial state, this will not make the callback manager send callbacks
* and is meant only for the initial state on boot.
*
* @param new_state The new state.
* @param state The new state.
*/
void publish_initial_state(bool new_state);
void publish_initial_state(bool state);
/// The current reported state of the binary sensor.
bool state{false};
void add_filter(Filter *filter);
void add_filters(const std::vector<Filter *> &filters);
void set_publish_initial_state(bool publish_initial_state) { this->publish_initial_state_ = publish_initial_state; }
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
void send_state_internal(bool new_state);
void send_state_internal(bool state, bool is_initial);
/// Return whether this binary sensor has outputted a state.
virtual bool has_state() const;
virtual bool is_status_binary_sensor() const;
// For backward compatibility, provide an accessible property
bool state{};
protected:
CallbackManager<void(bool)> state_callback_{};
Filter *filter_list_{nullptr};
bool has_state_{false};
bool publish_initial_state_{false};
Deduplicator<bool> publish_dedup_;
};
class BinarySensorInitiallyOff : public BinarySensor {

View File

@@ -9,36 +9,37 @@ namespace binary_sensor {
static const char *const TAG = "sensor.filter";
void Filter::output(bool value) {
if (this->next_ == nullptr) {
this->parent_->send_state_internal(value);
} else {
this->next_->input(value);
}
}
void Filter::input(bool value) {
void Filter::output(bool value, bool is_initial) {
if (!this->dedup_.next(value))
return;
auto b = this->new_value(value);
if (this->next_ == nullptr) {
this->parent_->send_state_internal(value, is_initial);
} else {
this->next_->input(value, is_initial);
}
}
void Filter::input(bool value, bool is_initial) {
auto b = this->new_value(value, is_initial);
if (b.has_value()) {
this->output(*b);
this->output(*b, is_initial);
}
}
optional<bool> DelayedOnOffFilter::new_value(bool value) {
optional<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) {
if (value) {
this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); });
this->set_timeout("ON_OFF", this->on_delay_.value(), [this, is_initial]() { this->output(true, is_initial); });
} else {
this->set_timeout("ON_OFF", this->off_delay_.value(), [this]() { this->output(false); });
this->set_timeout("ON_OFF", this->off_delay_.value(), [this, is_initial]() { this->output(false, is_initial); });
}
return {};
}
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> DelayedOnFilter::new_value(bool value) {
optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
if (value) {
this->set_timeout("ON", this->delay_.value(), [this]() { this->output(true); });
this->set_timeout("ON", this->delay_.value(), [this, is_initial]() { this->output(true, is_initial); });
return {};
} else {
this->cancel_timeout("ON");
@@ -48,9 +49,9 @@ optional<bool> DelayedOnFilter::new_value(bool value) {
float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> DelayedOffFilter::new_value(bool value) {
optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
if (!value) {
this->set_timeout("OFF", this->delay_.value(), [this]() { this->output(false); });
this->set_timeout("OFF", this->delay_.value(), [this, is_initial]() { this->output(false, is_initial); });
return {};
} else {
this->cancel_timeout("OFF");
@@ -60,11 +61,11 @@ optional<bool> DelayedOffFilter::new_value(bool value) {
float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> InvertFilter::new_value(bool value) { return !value; }
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
AutorepeatFilter::AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings) : timings_(std::move(timings)) {}
optional<bool> AutorepeatFilter::new_value(bool value) {
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
if (value) {
// Ignore if already running
if (this->active_timing_ != 0)
@@ -100,7 +101,7 @@ void AutorepeatFilter::next_timing_() {
void AutorepeatFilter::next_value_(bool val) {
const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
this->output(val); // This is at least the second one so not initial
this->output(val, false); // This is at least the second one so not initial
this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
}
@@ -108,18 +109,18 @@ float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARD
LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move(f)) {}
optional<bool> LambdaFilter::new_value(bool value) { return this->f_(value); }
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
optional<bool> SettleFilter::new_value(bool value) {
optional<bool> SettleFilter::new_value(bool value, bool is_initial) {
if (!this->steady_) {
this->set_timeout("SETTLE", this->delay_.value(), [this, value]() {
this->set_timeout("SETTLE", this->delay_.value(), [this, value, is_initial]() {
this->steady_ = true;
this->output(value);
this->output(value, is_initial);
});
return {};
} else {
this->steady_ = false;
this->output(value);
this->output(value, is_initial);
this->set_timeout("SETTLE", this->delay_.value(), [this]() { this->steady_ = true; });
return value;
}

View File

@@ -14,11 +14,11 @@ class BinarySensor;
class Filter {
public:
virtual optional<bool> new_value(bool value) = 0;
virtual optional<bool> new_value(bool value, bool is_initial) = 0;
void input(bool value);
void input(bool value, bool is_initial);
void output(bool value);
void output(bool value, bool is_initial);
protected:
friend BinarySensor;
@@ -30,7 +30,7 @@ class Filter {
class DelayedOnOffFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value) override;
optional<bool> new_value(bool value, bool is_initial) override;
float get_setup_priority() const override;
@@ -44,7 +44,7 @@ class DelayedOnOffFilter : public Filter, public Component {
class DelayedOnFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value) override;
optional<bool> new_value(bool value, bool is_initial) override;
float get_setup_priority() const override;
@@ -56,7 +56,7 @@ class DelayedOnFilter : public Filter, public Component {
class DelayedOffFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value) override;
optional<bool> new_value(bool value, bool is_initial) override;
float get_setup_priority() const override;
@@ -68,7 +68,7 @@ class DelayedOffFilter : public Filter, public Component {
class InvertFilter : public Filter {
public:
optional<bool> new_value(bool value) override;
optional<bool> new_value(bool value, bool is_initial) override;
};
struct AutorepeatFilterTiming {
@@ -86,7 +86,7 @@ class AutorepeatFilter : public Filter, public Component {
public:
explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
optional<bool> new_value(bool value) override;
optional<bool> new_value(bool value, bool is_initial) override;
float get_setup_priority() const override;
@@ -102,7 +102,7 @@ class LambdaFilter : public Filter {
public:
explicit LambdaFilter(std::function<optional<bool>(bool)> f);
optional<bool> new_value(bool value) override;
optional<bool> new_value(bool value, bool is_initial) override;
protected:
std::function<optional<bool>(bool)> f_;
@@ -110,7 +110,7 @@ class LambdaFilter : public Filter {
class SettleFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value) override;
optional<bool> new_value(bool value, bool is_initial) override;
float get_setup_priority() const override;

View File

@@ -11,11 +11,7 @@ namespace ble_client {
static const char *const TAG = "ble_rssi_sensor";
void BLEClientRSSISensor::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE GAP callbacks so loop isn't needed
this->disable_loop();
}
void BLEClientRSSISensor::loop() {}
void BLEClientRSSISensor::dump_config() {
LOG_SENSOR("", "BLE Client RSSI Sensor", this);

View File

@@ -11,11 +11,7 @@ namespace ble_client {
static const char *const TAG = "ble_sensor";
void BLESensor::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE callbacks so loop isn't needed
this->disable_loop();
}
void BLESensor::loop() {}
void BLESensor::dump_config() {
LOG_SENSOR("", "BLE Sensor", this);

View File

@@ -14,11 +14,7 @@ static const char *const TAG = "ble_text_sensor";
static const std::string EMPTY = "";
void BLETextSensor::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE callbacks so loop isn't needed
this->disable_loop();
}
void BLETextSensor::loop() {}
void BLETextSensor::dump_config() {
LOG_TEXT_SENSOR("", "BLE Text Sensor", this);

View File

@@ -26,17 +26,10 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase {
protected:
friend class BluetoothProxy;
// Memory optimized layout for 32-bit systems
// Group 1: Pointers (4 bytes each, naturally aligned)
BluetoothProxy *proxy_;
// Group 2: 2-byte types
int16_t send_service_{-2}; // Needs to handle negative values and service count
// Group 3: 1-byte types
bool seen_mtu_or_services_{false};
// 1 byte used, 1 byte padding
int16_t send_service_{-2};
BluetoothProxy *proxy_;
};
} // namespace bluetooth_proxy

View File

@@ -134,17 +134,11 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
// Memory optimized layout for 32-bit systems
// Group 1: Pointers (4 bytes each, naturally aligned)
api::APIConnection *api_connection_{nullptr};
// Group 2: Container types (typically 12 bytes on 32-bit)
std::vector<BluetoothConnection *> connections_{};
// Group 3: 1-byte types grouped together
bool active_;
std::vector<BluetoothConnection *> connections_{};
api::APIConnection *api_connection_{nullptr};
bool raw_advertisements_{false};
// 2 bytes used, 2 bytes padding
};
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -12,8 +12,8 @@ from esphome.const import (
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
ICON_GAS_CYLINDER,
STATE_CLASS_MEASUREMENT,

View File

@@ -1,5 +1,4 @@
import re
from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv

View File

@@ -41,7 +41,6 @@ async def to_code(config):
if CORE.using_arduino:
if CORE.is_esp32:
cg.add_library("ESP32 Async UDP", None)
cg.add_library("DNSServer", None)
cg.add_library("WiFi", None)
if CORE.is_esp8266:

View File

@@ -37,12 +37,7 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
request->redirect("/?save");
}
void CaptivePortal::setup() {
#ifndef USE_ARDUINO
// No DNS server needed for non-Arduino frameworks
this->disable_loop();
#endif
}
void CaptivePortal::setup() {}
void CaptivePortal::start() {
this->base_->init();
if (!this->initialized_) {
@@ -55,8 +50,6 @@ void CaptivePortal::start() {
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
this->dns_server_->start(53, "*", ip);
// Re-enable loop() when DNS server is started
this->enable_loop();
#endif
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
@@ -75,11 +68,7 @@ void CaptivePortal::start() {
void CaptivePortal::handleRequest(AsyncWebServerRequest *req) {
if (req->url() == "/") {
#ifndef USE_ESP8266
auto *response = req->beginResponse(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
#else
auto *response = req->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
#endif
response->addHeader("Content-Encoding", "gzip");
req->send(response);
return;

View File

@@ -21,11 +21,8 @@ class CaptivePortal : public AsyncWebHandler, public Component {
void dump_config() override;
#ifdef USE_ARDUINO
void loop() override {
if (this->dns_server_ != nullptr) {
if (this->dns_server_ != nullptr)
this->dns_server_->processNextRequest();
} else {
this->disable_loop();
}
}
#endif
float get_setup_priority() const override;
@@ -40,7 +37,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
#endif
}
bool canHandle(AsyncWebServerRequest *request) const override {
bool canHandle(AsyncWebServerRequest *request) override {
if (!this->active_)
return false;

View File

@@ -1,10 +1,10 @@
"""CM1106 Sensor component for ESPHome."""
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import sensor, uart
import esphome.config_validation as cv
from esphome.const import (
CONF_CO2,
CONF_ID,

View File

@@ -3,5 +3,4 @@
CODEOWNERS = ["@esphome/core"]
CONF_DRAW_ROUNDING = "draw_rounding"
CONF_ON_STATE_CHANGE = "on_state_change"
CONF_REQUEST_HEADERS = "request_headers"

View File

@@ -11,7 +11,7 @@ namespace display {
static const char *const TAG = "display";
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
RAMAllocator<uint8_t> allocator;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->buffer_ = allocator.allocate(buffer_length);
if (this->buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate buffer for display!");

View File

@@ -132,8 +132,6 @@ def set_core_data(config):
choices = CPU_FREQUENCIES[variant]
if "160MHZ" in choices:
cpu_frequency = "160MHZ"
elif "360MHZ" in choices:
cpu_frequency = "360MHZ"
else:
cpu_frequency = choices[-1]
config[CONF_CPU_FREQUENCY] = cpu_frequency
@@ -291,8 +289,11 @@ def add_extra_build_file(filename: str, path: str) -> bool:
def _format_framework_arduino_version(ver: cv.Version) -> str:
# format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to
# a PIO pioarduino/framework-arduinoespressif32 value
return f"pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/{str(ver)}/esp32-{str(ver)}.zip"
# a PIO platformio/framework-arduinoespressif32 value
# List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32
if ver <= cv.Version(1, 0, 3):
return f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
def _format_framework_espidf_version(
@@ -316,10 +317,12 @@ def _format_framework_espidf_version(
# The default/recommended arduino framework version
# - https://github.com/espressif/arduino-esp32/releases
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 1, 3)
# The platform-espressif32 version to use for arduino frameworks
# - https://github.com/pioarduino/platform-espressif32/releases
ARDUINO_PLATFORM_VERSION = cv.Version(53, 3, 13)
# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5)
# The platformio/espressif32 version to use for arduino frameworks
# - https://github.com/platformio/platform-espressif32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0)
# The default/recommended esp-idf framework version
# - https://github.com/espressif/esp-idf/releases
@@ -362,8 +365,8 @@ SUPPORTED_PIOARDUINO_ESP_IDF_5X = [
def _arduino_check_versions(value):
value = value.copy()
lookups = {
"dev": (cv.Version(3, 1, 3), "https://github.com/espressif/arduino-esp32.git"),
"latest": (cv.Version(3, 1, 3), None),
"dev": (cv.Version(2, 1, 0), "https://github.com/espressif/arduino-esp32.git"),
"latest": (cv.Version(2, 0, 9), None),
"recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None),
}
@@ -385,10 +388,6 @@ def _arduino_check_versions(value):
CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION))
)
if value[CONF_SOURCE].startswith("http"):
# prefix is necessary or platformio will complain with a cryptic error
value[CONF_SOURCE] = f"framework-arduinoespressif32@{value[CONF_SOURCE]}"
if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION:
_LOGGER.warning(
"The selected Arduino framework version is not the recommended one. "
@@ -606,7 +605,7 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False
): cv.boolean,
cv.Optional(
CONF_ENABLE_LWIP_MDNS_QUERIES, default=False
CONF_ENABLE_LWIP_MDNS_QUERIES, default=True
): cv.boolean,
cv.Optional(
CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False
@@ -696,7 +695,6 @@ FINAL_VALIDATE_SCHEMA = cv.Schema(final_validate)
async def to_code(config):
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE])
cg.set_cpp_standard("gnu++17")
cg.add_build_flag("-DUSE_ESP32")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}")
@@ -762,7 +760,7 @@ async def to_code(config):
and not advanced[CONF_ENABLE_LWIP_DHCP_SERVER]
):
add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False)
if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, False):
if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, True):
add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False)
if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False):
add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0)
@@ -830,7 +828,10 @@ async def to_code(config):
cg.add_platformio_option("framework", "arduino")
cg.add_build_flag("-DUSE_ARDUINO")
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
cg.add_platformio_option("platform_packages", [conf[CONF_SOURCE]])
cg.add_platformio_option(
"platform_packages",
[f"platformio/framework-arduinoespressif32@{conf[CONF_SOURCE]}"],
)
if CONF_PARTITIONS in config:
cg.add_platformio_option("board_build.partitions", config[CONF_PARTITIONS])

View File

@@ -22,16 +22,6 @@ void BLEClientBase::setup() {
this->connection_index_ = connection_index++;
}
void BLEClientBase::set_state(espbt::ClientState st) {
ESP_LOGV(TAG, "[%d] [%s] Set state %d", this->connection_index_, this->address_str_.c_str(), (int) st);
ESPBTClient::set_state(st);
if (st == espbt::ClientState::READY_TO_CONNECT) {
// Enable loop when we need to connect
this->enable_loop();
}
}
void BLEClientBase::loop() {
if (!esp32_ble::global_ble->is_active()) {
this->set_state(espbt::ClientState::INIT);
@@ -47,14 +37,9 @@ void BLEClientBase::loop() {
}
// READY_TO_CONNECT means we have discovered the device
// and the scanner has been stopped by the tracker.
else if (this->state_ == espbt::ClientState::READY_TO_CONNECT) {
if (this->state_ == espbt::ClientState::READY_TO_CONNECT) {
this->connect();
}
// If its idle, we can disable the loop as set_state
// will enable it again when we need to connect.
else if (this->state_ == espbt::ClientState::IDLE) {
this->disable_loop();
}
}
float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }

View File

@@ -93,36 +93,21 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
bool check_addr(esp_bd_addr_t &addr) { return memcmp(addr, this->remote_bda_, sizeof(esp_bd_addr_t)) == 0; }
void set_state(espbt::ClientState st) override;
protected:
// Memory optimized layout for 32-bit systems
// Group 1: 8-byte types
uint64_t address_{0};
// Group 2: Container types (grouped for memory optimization)
std::string address_str_{};
std::vector<BLEService *> services_;
// Group 3: 4-byte types
int gattc_if_;
esp_gatt_status_t status_{ESP_GATT_OK};
// Group 4: Arrays (6 bytes)
esp_bd_addr_t remote_bda_;
// Group 5: 2-byte types
uint16_t conn_id_{UNSET_CONN_ID};
uint16_t mtu_{23};
// Group 6: 1-byte types and small enums
esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC};
espbt::ConnectionType connection_type_{espbt::ConnectionType::V1};
uint8_t connection_index_;
uint8_t service_count_{0}; // ESP32 has max handles < 255, typical devices have < 50 services
uint16_t conn_id_{UNSET_CONN_ID};
uint64_t address_{0};
bool auto_connect_{false};
std::string address_str_{};
uint8_t connection_index_;
int16_t service_count_{0};
uint16_t mtu_{23};
bool paired_{false};
// 6 bytes used, 2 bytes padding
espbt::ConnectionType connection_type_{espbt::ConnectionType::V1};
std::vector<BLEService *> services_;
esp_gatt_status_t status_{ESP_GATT_OK};
void log_event_(const char *name);
};

View File

@@ -122,10 +122,10 @@ void ESP32BLETracker::loop() {
// Consumer side: This runs in the main loop thread
if (this->scanner_state_ == ScannerState::RUNNING) {
// Load our own index with relaxed ordering (we're the only writer)
uint8_t read_idx = this->ring_read_index_.load(std::memory_order_relaxed);
size_t read_idx = this->ring_read_index_.load(std::memory_order_relaxed);
// Load producer's index with acquire to see their latest writes
uint8_t write_idx = this->ring_write_index_.load(std::memory_order_acquire);
size_t write_idx = this->ring_write_index_.load(std::memory_order_acquire);
while (read_idx != write_idx) {
// Process one result at a time directly from ring buffer
@@ -409,11 +409,11 @@ void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) {
// IMPORTANT: Only this thread writes to ring_write_index_
// Load our own index with relaxed ordering (we're the only writer)
uint8_t write_idx = this->ring_write_index_.load(std::memory_order_relaxed);
uint8_t next_write_idx = (write_idx + 1) % SCAN_RESULT_BUFFER_SIZE;
size_t write_idx = this->ring_write_index_.load(std::memory_order_relaxed);
size_t next_write_idx = (write_idx + 1) % SCAN_RESULT_BUFFER_SIZE;
// Load consumer's index with acquire to see their latest updates
uint8_t read_idx = this->ring_read_index_.load(std::memory_order_acquire);
size_t read_idx = this->ring_read_index_.load(std::memory_order_acquire);
// Check if buffer is full
if (next_write_idx != read_idx) {
@@ -522,6 +522,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
}
void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
this->scan_result_ = &scan_result;
for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
this->address_[i] = scan_result.bda[i];
this->address_type_ = static_cast<esp_ble_addr_type_t>(scan_result.ble_addr_type);

View File

@@ -85,6 +85,9 @@ class ESPBTDevice {
const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
// Exposed through a function for use in lambdas
const BLEScanResult &get_scan_result() const { return *scan_result_; }
bool resolve_irk(const uint8_t *irk) const;
optional<ESPBLEiBeacon> get_ibeacon() const {
@@ -111,6 +114,7 @@ class ESPBTDevice {
std::vector<ESPBTUUID> service_uuids_{};
std::vector<ServiceData> manufacturer_datas_{};
std::vector<ServiceData> service_datas_{};
const BLEScanResult *scan_result_{nullptr};
};
class ESP32BLETracker;
@@ -129,7 +133,7 @@ class ESPBTDeviceListener {
ESP32BLETracker *parent_{nullptr};
};
enum class ClientState : uint8_t {
enum class ClientState {
// Connection is allocated
INIT,
// Client is disconnecting
@@ -165,7 +169,7 @@ enum class ScannerState {
STOPPED,
};
enum class ConnectionType : uint8_t {
enum class ConnectionType {
// The default connection type, we hold all the services in ram
// for the duration of the connection.
V1,
@@ -193,19 +197,15 @@ class ESPBTClient : public ESPBTDeviceListener {
}
}
ClientState state() const { return state_; }
// Memory optimized layout
uint8_t app_id; // App IDs are small integers assigned sequentially
int app_id;
protected:
// Group 1: 1-byte types
ClientState state_{ClientState::INIT};
// want_disconnect_ is set to true when a disconnect is requested
// while the client is connecting. This is used to disconnect the
// client as soon as we get the connection id (conn_id_) from the
// ESP_GATTC_OPEN_EVT event.
bool want_disconnect_{false};
// 2 bytes used, 2 bytes padding
};
class ESP32BLETracker : public Component,
@@ -266,7 +266,7 @@ class ESP32BLETracker : public Component,
/// Called to set the scanner state. Will also call callbacks to let listeners know when state is changed.
void set_scanner_state_(ScannerState state);
uint8_t app_id_{0};
int app_id_{0};
/// Vector of addresses that have already been printed in print_bt_device_info
std::vector<uint64_t> already_discovered_;
@@ -293,9 +293,9 @@ class ESP32BLETracker : public Component,
// Consumer: ESPHome main loop (loop() method)
// This design ensures zero blocking in the BT callback and prevents scan result loss
BLEScanResult *scan_ring_buffer_;
std::atomic<uint8_t> ring_write_index_{0}; // Written only by BT callback (producer)
std::atomic<uint8_t> ring_read_index_{0}; // Written only by main loop (consumer)
std::atomic<uint16_t> scan_results_dropped_{0}; // Tracks buffer overflow events
std::atomic<size_t> ring_write_index_{0}; // Written only by BT callback (producer)
std::atomic<size_t> ring_read_index_{0}; // Written only by main loop (consumer)
std::atomic<size_t> scan_results_dropped_{0}; // Tracks buffer overflow events
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS};

View File

@@ -1,6 +1,5 @@
from esphome import automation, pins
import esphome.codegen as cg
from esphome.components import i2c
from esphome.components.esp32 import add_idf_component
import esphome.config_validation as cv
from esphome.const import (
@@ -8,7 +7,6 @@ from esphome.const import (
CONF_CONTRAST,
CONF_DATA_PINS,
CONF_FREQUENCY,
CONF_I2C_ID,
CONF_ID,
CONF_PIN,
CONF_RESET_PIN,
@@ -151,104 +149,93 @@ CONF_ON_IMAGE = "on_image"
camera_range_param = cv.int_range(min=-2, max=2)
CONFIG_SCHEMA = cv.All(
cv.ENTITY_BASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(ESP32Camera),
# pin assignment
cv.Required(CONF_DATA_PINS): cv.All(
[pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8)
),
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema(
{
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
cv.frequency, cv.Range(min=8e6, max=20e6)
),
}
),
cv.Optional(CONF_I2C_PINS): cv.Schema(
{
cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number,
cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number,
}
),
cv.Optional(CONF_I2C_ID): cv.Any(
cv.use_id(i2c.InternalI2CBus),
msg="I2C bus must be an internal ESP32 I2C bus",
),
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number,
# image
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
FRAME_SIZES, upper=True
),
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum(
ENUM_SPECIAL_EFFECT, upper=True
),
# exposure
cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum(
ENUM_GAIN_CONTROL_MODE, upper=True
),
cv.Optional(CONF_AEC2, default=False): cv.boolean,
cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
# gains
cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum(
ENUM_GAIN_CONTROL_MODE, upper=True
),
cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30),
cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum(
ENUM_GAIN_CEILING, upper=True
),
# white balance
cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(
ENUM_WB_MODE, upper=True
),
# test pattern
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
# framerates
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
cv.framerate, cv.Range(min=0, min_included=False, max=60)
),
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
cv.framerate, cv.Range(min=0, max=1)
),
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ESP32CameraStreamStartTrigger
),
}
),
cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ESP32CameraStreamStopTrigger
),
}
),
cv.Optional(CONF_ON_IMAGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ESP32CameraImageTrigger
),
}
),
}
).extend(cv.COMPONENT_SCHEMA),
cv.has_exactly_one_key(CONF_I2C_PINS, CONF_I2C_ID),
)
CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(ESP32Camera),
# pin assignment
cv.Required(CONF_DATA_PINS): cv.All(
[pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8)
),
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema(
{
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
cv.frequency, cv.Range(min=8e6, max=20e6)
),
}
),
cv.Required(CONF_I2C_PINS): cv.Schema(
{
cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number,
cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number,
}
),
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number,
# image
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
FRAME_SIZES, upper=True
),
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum(
ENUM_SPECIAL_EFFECT, upper=True
),
# exposure
cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum(
ENUM_GAIN_CONTROL_MODE, upper=True
),
cv.Optional(CONF_AEC2, default=False): cv.boolean,
cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
# gains
cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum(
ENUM_GAIN_CONTROL_MODE, upper=True
),
cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30),
cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum(
ENUM_GAIN_CEILING, upper=True
),
# white balance
cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(ENUM_WB_MODE, upper=True),
# test pattern
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
# framerates
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
cv.framerate, cv.Range(min=0, min_included=False, max=60)
),
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
cv.framerate, cv.Range(min=0, max=1)
),
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ESP32CameraStreamStartTrigger
),
}
),
cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ESP32CameraStreamStopTrigger
),
}
),
cv.Optional(CONF_ON_IMAGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32CameraImageTrigger),
}
),
}
).extend(cv.COMPONENT_SCHEMA)
SETTERS = {
# pin assignment
@@ -293,12 +280,8 @@ async def to_code(config):
extclk = config[CONF_EXTERNAL_CLOCK]
cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY]))
if i2c_id := config.get(CONF_I2C_ID):
i2c_hub = await cg.get_variable(i2c_id)
cg.add(var.set_i2c_id(i2c_hub))
else:
i2c_pins = config[CONF_I2C_PINS]
cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL]))
i2c_pins = config[CONF_I2C_PINS]
cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL]))
cg.add(var.set_max_update_interval(1000 / config[CONF_MAX_FRAMERATE]))
if config[CONF_IDLE_FRAMERATE] == 0:
cg.add(var.set_idle_update_interval(0))

View File

@@ -1,9 +1,9 @@
#ifdef USE_ESP32
#include "esp32_camera.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/application.h"
#include <freertos/task.h>
@@ -16,12 +16,6 @@ static const char *const TAG = "esp32_camera";
void ESP32Camera::setup() {
global_esp32_camera = this;
#ifdef USE_I2C
if (this->i2c_bus_ != nullptr) {
this->config_.sccb_i2c_port = this->i2c_bus_->get_port();
}
#endif
/* initialize time to now */
this->last_update_ = millis();
@@ -252,13 +246,6 @@ void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) {
this->config_.pin_sccb_sda = sda;
this->config_.pin_sccb_scl = scl;
}
#ifdef USE_I2C
void ESP32Camera::set_i2c_id(i2c::InternalI2CBus *i2c_bus) {
this->i2c_bus_ = i2c_bus;
this->config_.pin_sccb_sda = -1;
this->config_.pin_sccb_scl = -1;
}
#endif // USE_I2C
void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; }
void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; }

View File

@@ -2,17 +2,13 @@
#ifdef USE_ESP32
#include <esp_camera.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#ifdef USE_I2C
#include "esphome/components/i2c/i2c_bus.h"
#endif // USE_I2C
#include <esp_camera.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
namespace esphome {
namespace esp32_camera {
@@ -122,9 +118,6 @@ class ESP32Camera : public EntityBase, public Component {
void set_pixel_clock_pin(uint8_t pin);
void set_external_clock(uint8_t pin, uint32_t frequency);
void set_i2c_pins(uint8_t sda, uint8_t scl);
#ifdef USE_I2C
void set_i2c_id(i2c::InternalI2CBus *i2c_bus);
#endif // USE_I2C
void set_reset_pin(uint8_t pin);
void set_power_down_pin(uint8_t pin);
/* -- image */
@@ -217,9 +210,6 @@ class ESP32Camera : public EntityBase, public Component {
uint32_t last_idle_request_{0};
uint32_t last_update_{0};
#ifdef USE_I2C
i2c::InternalI2CBus *i2c_bus_{nullptr};
#endif // USE_I2C
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -3,7 +3,7 @@ import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MODE, CONF_PORT
CODEOWNERS = ["@ayufan"]
DEPENDENCIES = ["esp32_camera", "network"]
DEPENDENCIES = ["esp32_camera"]
MULTI_CONF = True
esp32_camera_web_server_ns = cg.esphome_ns.namespace("esp32_camera_web_server")

View File

@@ -0,0 +1,25 @@
#ifdef USE_ESP32
#include "esp32_hall.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include <driver/adc.h>
namespace esphome {
namespace esp32_hall {
static const char *const TAG = "esp32_hall";
void ESP32HallSensor::update() {
adc1_config_width(ADC_WIDTH_BIT_12);
int value_int = hall_sensor_read();
float value = (value_int / 4095.0f) * 10000.0f;
ESP_LOGD(TAG, "'%s': Got reading %.0f µT", this->name_.c_str(), value);
this->publish_state(value);
}
std::string ESP32HallSensor::unique_id() { return get_mac_address() + "-hall"; }
void ESP32HallSensor::dump_config() { LOG_SENSOR("", "ESP32 Hall Sensor", this); }
} // namespace esp32_hall
} // namespace esphome
#endif

View File

@@ -0,0 +1,23 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#ifdef USE_ESP32
namespace esphome {
namespace esp32_hall {
class ESP32HallSensor : public sensor::Sensor, public PollingComponent {
public:
void dump_config() override;
void update() override;
std::string unique_id() override;
};
} // namespace esp32_hall
} // namespace esphome
#endif

View File

@@ -0,0 +1,24 @@
import esphome.codegen as cg
from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import ICON_MAGNET, STATE_CLASS_MEASUREMENT, UNIT_MICROTESLA
DEPENDENCIES = ["esp32"]
esp32_hall_ns = cg.esphome_ns.namespace("esp32_hall")
ESP32HallSensor = esp32_hall_ns.class_(
"ESP32HallSensor", sensor.Sensor, cg.PollingComponent
)
CONFIG_SCHEMA = sensor.sensor_schema(
ESP32HallSensor,
unit_of_measurement=UNIT_MICROTESLA,
icon=ICON_MAGNET,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
).extend(cv.polling_component_schema("60s"))
async def to_code(config):
var = await sensor.new_sensor(config)
await cg.register_component(var, config)

View File

@@ -168,8 +168,6 @@ void ESP32ImprovComponent::loop() {
case improv::STATE_PROVISIONED: {
this->incoming_data_.clear();
this->set_status_indicator_state_(false);
// Provisioning complete, no further loop execution needed
this->disable_loop();
break;
}
}
@@ -256,7 +254,6 @@ void ESP32ImprovComponent::start() {
ESP_LOGD(TAG, "Setting Improv to start");
this->should_start_ = true;
this->enable_loop();
}
void ESP32ImprovComponent::stop() {

View File

@@ -1,8 +1,48 @@
import esphome.codegen as cg
from esphome.components import esp32
import esphome.config_validation as cv
from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION
from esphome.core import CORE
CODEOWNERS = ["@jesserockz"]
RMT_TX_CHANNELS = {
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32C3: [0, 1],
esp32.const.VARIANT_ESP32C6: [0, 1],
esp32.const.VARIANT_ESP32H2: [0, 1],
}
RMT_RX_CHANNELS = {
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32S3: [4, 5, 6, 7],
esp32.const.VARIANT_ESP32C3: [2, 3],
esp32.const.VARIANT_ESP32C6: [2, 3],
esp32.const.VARIANT_ESP32H2: [2, 3],
}
rmt_channel_t = cg.global_ns.enum("rmt_channel_t")
RMT_CHANNEL_ENUMS = {
0: rmt_channel_t.RMT_CHANNEL_0,
1: rmt_channel_t.RMT_CHANNEL_1,
2: rmt_channel_t.RMT_CHANNEL_2,
3: rmt_channel_t.RMT_CHANNEL_3,
4: rmt_channel_t.RMT_CHANNEL_4,
5: rmt_channel_t.RMT_CHANNEL_5,
6: rmt_channel_t.RMT_CHANNEL_6,
7: rmt_channel_t.RMT_CHANNEL_7,
}
def use_new_rmt_driver():
framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0):
return True
return False
def validate_clock_resolution():
def _validator(value):
@@ -20,3 +60,21 @@ def validate_clock_resolution():
return value
return _validator
def validate_rmt_channel(*, tx: bool):
rmt_channels = RMT_TX_CHANNELS if tx else RMT_RX_CHANNELS
def _validator(value):
cv.only_on_esp32(value)
value = cv.int_(value)
variant = esp32.get_esp32_variant()
if variant not in rmt_channels:
raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.")
if value not in rmt_channels[variant]:
raise cv.Invalid(
f"RMT channel {value} does not support {'transmitting' if tx else 'receiving'} for ESP32 variant {variant}."
)
return cv.enum(RMT_CHANNEL_ENUMS)(value)
return _validator

View File

@@ -42,6 +42,7 @@ void ESP32RMTLEDStripLightOutput::setup() {
return;
}
#if ESP_IDF_VERSION_MAJOR >= 5
RAMAllocator<rmt_symbol_word_t> rmt_allocator(this->use_psram_ ? 0 : RAMAllocator<rmt_symbol_word_t>::ALLOC_INTERNAL);
// 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset
@@ -78,6 +79,36 @@ void ESP32RMTLEDStripLightOutput::setup() {
this->mark_failed();
return;
}
#else
RAMAllocator<rmt_item32_t> rmt_allocator(this->use_psram_ ? 0 : RAMAllocator<rmt_item32_t>::ALLOC_INTERNAL);
// 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset
this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1);
rmt_config_t config;
memset(&config, 0, sizeof(config));
config.channel = this->channel_;
config.rmt_mode = RMT_MODE_TX;
config.gpio_num = gpio_num_t(this->pin_);
config.mem_block_num = 1;
config.clk_div = RMT_CLK_DIV;
config.tx_config.loop_en = false;
config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
config.tx_config.carrier_en = false;
config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
config.tx_config.idle_output_en = true;
if (rmt_config(&config) != ESP_OK) {
ESP_LOGE(TAG, "Cannot initialize RMT!");
this->mark_failed();
return;
}
if (rmt_driver_install(config.channel, 0, 0) != ESP_OK) {
ESP_LOGE(TAG, "Cannot install RMT driver!");
this->mark_failed();
return;
}
#endif
}
void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
@@ -114,7 +145,11 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
ESP_LOGVV(TAG, "Writing RGB values to bus");
#if ESP_IDF_VERSION_MAJOR >= 5
esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000);
#else
esp_err_t error = rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000));
#endif
if (error != ESP_OK) {
ESP_LOGE(TAG, "RMT TX timeout");
this->status_set_warning();
@@ -127,7 +162,11 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
size_t size = 0;
size_t len = 0;
uint8_t *psrc = this->buf_;
#if ESP_IDF_VERSION_MAJOR >= 5
rmt_symbol_word_t *pdest = this->rmt_buf_;
#else
rmt_item32_t *pdest = this->rmt_buf_;
#endif
while (size < buffer_size) {
uint8_t b = *psrc;
for (int i = 0; i < 8; i++) {
@@ -145,11 +184,15 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
len++;
}
#if ESP_IDF_VERSION_MAJOR >= 5
rmt_transmit_config_t config;
memset(&config, 0, sizeof(config));
config.loop_count = 0;
config.flags.eot_level = 0;
error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
#else
error = rmt_write_items(this->channel_, this->rmt_buf_, len, false);
#endif
if (error != ESP_OK) {
ESP_LOGE(TAG, "RMT TX error");
this->status_set_warning();
@@ -208,7 +251,11 @@ void ESP32RMTLEDStripLightOutput::dump_config() {
"ESP32 RMT LED Strip:\n"
" Pin: %u",
this->pin_);
#if ESP_IDF_VERSION_MAJOR >= 5
ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_);
#else
ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_);
#endif
const char *rgb_order;
switch (this->rgb_order_) {
case ORDER_RGB:

View File

@@ -11,7 +11,12 @@
#include <driver/gpio.h>
#include <esp_err.h>
#include <esp_idf_version.h>
#if ESP_IDF_VERSION_MAJOR >= 5
#include <driver/rmt_tx.h>
#else
#include <driver/rmt.h>
#endif
namespace esphome {
namespace esp32_rmt_led_strip {
@@ -56,7 +61,11 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
uint32_t reset_time_high, uint32_t reset_time_low);
void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
#if ESP_IDF_VERSION_MAJOR >= 5
void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; }
#else
void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; }
#endif
void clear_effect_data() override {
for (int i = 0; i < this->size(); i++)
@@ -72,11 +81,17 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
uint8_t *buf_{nullptr};
uint8_t *effect_data_{nullptr};
#if ESP_IDF_VERSION_MAJOR >= 5
rmt_channel_handle_t channel_{nullptr};
rmt_encoder_handle_t encoder_{nullptr};
rmt_symbol_word_t *rmt_buf_{nullptr};
rmt_symbol_word_t bit0_, bit1_, reset_;
uint32_t rmt_symbols_{48};
#else
rmt_item32_t *rmt_buf_{nullptr};
rmt_item32_t bit0_, bit1_, reset_;
rmt_channel_t channel_{RMT_CHANNEL_0};
#endif
uint8_t pin_;
uint16_t num_leds_;

View File

@@ -3,7 +3,7 @@ import logging
from esphome import pins
import esphome.codegen as cg
from esphome.components import esp32, light
from esphome.components import esp32, esp32_rmt, light
import esphome.config_validation as cv
from esphome.const import (
CONF_CHIPSET,
@@ -13,9 +13,11 @@ from esphome.const import (
CONF_OUTPUT_ID,
CONF_PIN,
CONF_RGB_ORDER,
CONF_RMT_CHANNEL,
CONF_RMT_SYMBOLS,
CONF_USE_DMA,
)
from esphome.core import CORE
_LOGGER = logging.getLogger(__name__)
@@ -67,6 +69,53 @@ CONF_RESET_HIGH = "reset_high"
CONF_RESET_LOW = "reset_low"
class OptionalForIDF5(cv.SplitDefault):
@property
def default(self):
if not esp32_rmt.use_new_rmt_driver():
return cv.UNDEFINED
return super().default
@default.setter
def default(self, value):
# Ignore default set from vol.Optional
pass
def only_with_new_rmt_driver(obj):
if not esp32_rmt.use_new_rmt_driver():
raise cv.Invalid(
"This feature is only available for the IDF framework version 5."
)
return obj
def not_with_new_rmt_driver(obj):
if esp32_rmt.use_new_rmt_driver():
raise cv.Invalid(
"This feature is not available for the IDF framework version 5."
)
return obj
def final_validation(config):
if not esp32_rmt.use_new_rmt_driver():
if CONF_RMT_CHANNEL not in config:
if CORE.using_esp_idf:
raise cv.Invalid(
"rmt_channel is a required option for IDF version < 5."
)
raise cv.Invalid(
"rmt_channel is a required option for the Arduino framework."
)
_LOGGER.warning(
"RMT_LED_STRIP support for IDF version < 5 is deprecated and will be removed soon."
)
FINAL_VALIDATE_SCHEMA = final_validation
CONFIG_SCHEMA = cv.All(
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
{
@@ -74,17 +123,20 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
cv.SplitDefault(
cv.Optional(CONF_RMT_CHANNEL): cv.All(
not_with_new_rmt_driver, esp32_rmt.validate_rmt_channel(tx=True)
),
OptionalForIDF5(
CONF_RMT_SYMBOLS,
esp32=192,
esp32_s2=192,
esp32_s3=192,
esp32_p4=192,
esp32_c3=96,
esp32_c5=96,
esp32_c6=96,
esp32_h2=96,
): cv.int_range(min=2),
esp32_idf=192,
esp32_s2_idf=192,
esp32_s3_idf=192,
esp32_p4_idf=192,
esp32_c3_idf=96,
esp32_c5_idf=96,
esp32_c6_idf=96,
esp32_h2_idf=96,
): cv.All(only_with_new_rmt_driver, cv.int_range(min=2)),
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
@@ -93,6 +145,7 @@ CONFIG_SCHEMA = cv.All(
esp32.only_on_variant(
supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4]
),
cv.only_with_esp_idf,
cv.boolean,
),
cv.Optional(CONF_USE_PSRAM, default=True): cv.boolean,
@@ -165,6 +218,15 @@ async def to_code(config):
cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))
cg.add(var.set_use_psram(config[CONF_USE_PSRAM]))
cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS]))
if CONF_USE_DMA in config:
cg.add(var.set_use_dma(config[CONF_USE_DMA]))
if esp32_rmt.use_new_rmt_driver():
cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS]))
if CONF_USE_DMA in config:
cg.add(var.set_use_dma(config[CONF_USE_DMA]))
else:
rmt_channel_t = cg.global_ns.enum("rmt_channel_t")
cg.add(
var.set_rmt_channel(
getattr(rmt_channel_t, f"RMT_CHANNEL_{config[CONF_RMT_CHANNEL]}")
)
)

View File

@@ -183,7 +183,6 @@ async def to_code(config):
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_build_flag("-DUSE_ESP8266")
cg.set_cpp_standard("gnu++17")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_define("ESPHOME_VARIANT", "ESP8266")

View File

@@ -26,19 +26,19 @@ void ESPHomeOTAComponent::setup() {
ota::register_ota_platform(this);
#endif
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
if (this->server_ == nullptr) {
server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
if (server_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket");
this->mark_failed();
return;
}
int enable = 1;
int err = this->server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
int err = server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
// we can still continue
}
err = this->server_->setblocking(false);
err = server_->setblocking(false);
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
this->mark_failed();
@@ -54,14 +54,14 @@ void ESPHomeOTAComponent::setup() {
return;
}
err = this->server_->bind((struct sockaddr *) &server, sizeof(server));
err = server_->bind((struct sockaddr *) &server, sizeof(server));
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
this->mark_failed();
return;
}
err = this->server_->listen(4);
err = server_->listen(4);
if (err != 0) {
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
this->mark_failed();
@@ -82,14 +82,7 @@ void ESPHomeOTAComponent::dump_config() {
#endif
}
void ESPHomeOTAComponent::loop() {
// Skip handle_() call if no client connected and no incoming connections
// This optimization reduces idle loop overhead when OTA is not active
// Note: No need to check server_ for null as the component is marked failed in setup() if server_ creation fails
if (this->client_ != nullptr || this->server_->ready()) {
this->handle_();
}
}
void ESPHomeOTAComponent::loop() { this->handle_(); }
static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
@@ -108,21 +101,23 @@ void ESPHomeOTAComponent::handle_() {
size_t size_acknowledged = 0;
#endif
if (this->client_ == nullptr) {
// We already checked server_->ready() in loop(), so we can accept directly
struct sockaddr_storage source_addr;
socklen_t addr_len = sizeof(source_addr);
this->client_ = this->server_->accept((struct sockaddr *) &source_addr, &addr_len);
if (this->client_ == nullptr)
return;
if (client_ == nullptr) {
// Check if the server socket is ready before accepting
if (this->server_->ready()) {
struct sockaddr_storage source_addr;
socklen_t addr_len = sizeof(source_addr);
client_ = server_->accept((struct sockaddr *) &source_addr, &addr_len);
}
}
if (client_ == nullptr)
return;
int enable = 1;
int err = this->client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
int err = client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
ESP_LOGW(TAG, "Socket could not enable TCP nodelay, errno %d", errno);
this->client_->close();
this->client_ = nullptr;
client_->close();
client_ = nullptr;
return;
}

View File

@@ -106,7 +106,7 @@ void EthernetComponent::setup() {
.post_cb = nullptr,
};
#if ESP_IDF_VERSION_MAJOR >= 5
#if USE_ESP_IDF && (ESP_IDF_VERSION_MAJOR >= 5)
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(host, &devcfg);
#else
spi_device_handle_t spi_handle = nullptr;
@@ -274,9 +274,6 @@ void EthernetComponent::loop() {
ESP_LOGW(TAG, "Connection lost; reconnecting");
this->state_ = EthernetComponentState::CONNECTING;
this->start_connect_();
} else {
// When connected and stable, disable the loop to save CPU cycles
this->disable_loop();
}
break;
}
@@ -400,13 +397,11 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base
case ETHERNET_EVENT_START:
event_name = "ETH started";
global_eth_component->started_ = true;
global_eth_component->enable_loop_soon_any_context();
break;
case ETHERNET_EVENT_STOP:
event_name = "ETH stopped";
global_eth_component->started_ = false;
global_eth_component->connected_ = false;
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
break;
case ETHERNET_EVENT_CONNECTED:
event_name = "ETH connected";
@@ -414,7 +409,6 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base
case ETHERNET_EVENT_DISCONNECTED:
event_name = "ETH disconnected";
global_eth_component->connected_ = false;
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
break;
default:
return;
@@ -431,10 +425,8 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b
global_eth_component->got_ipv4_address_ = true;
#if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT;
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
#else
global_eth_component->connected_ = true;
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
#endif /* USE_NETWORK_IPV6 */
}
@@ -447,10 +439,8 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_
#if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
global_eth_component->connected_ =
global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT);
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
#else
global_eth_component->connected_ = global_eth_component->got_ipv4_address_;
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
#endif
}
#endif /* USE_NETWORK_IPV6 */
@@ -630,7 +620,6 @@ bool EthernetComponent::powerdown() {
}
this->connected_ = false;
this->started_ = false;
// No need to enable_loop() here as this is only called during shutdown/reboot
if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) {
ESP_LOGE(TAG, "Error powering down ethernet PHY");
return false;

View File

@@ -67,10 +67,10 @@ class Font
inline int get_height() { return this->height_; }
inline int get_bpp() { return this->bpp_; }
const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
const std::vector<Glyph, ExternalRAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
protected:
std::vector<Glyph, RAMAllocator<Glyph>> glyphs_;
std::vector<Glyph, ExternalRAMAllocator<Glyph>> glyphs_;
int baseline_;
int height_;
uint8_t bpp_; // bits per pixel

View File

@@ -125,6 +125,6 @@ async def to_code(config):
cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE]))
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
cg.add_library("tonia/HeatpumpIR", "1.0.35")
cg.add_library("tonia/HeatpumpIR", "1.0.32")
if CORE.is_libretiny:
CORE.add_platformio_option("lib_ignore", "IRremoteESP8266")

View File

@@ -41,6 +41,6 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
cg.add_build_flag("-DUSE_HOST")
cg.add_define("USE_ESPHOME_HOST_MAC_ADDRESS", config[CONF_MAC_ADDRESS].parts)
cg.add_build_flag("-std=gnu++17")
cg.add_build_flag("-std=c++17")
cg.add_define("ESPHOME_BOARD", "host")
cg.add_platformio_option("platform", "platformio/native")

View File

@@ -175,7 +175,7 @@ async def to_code(config):
not config.get(CONF_VERIFY_SSL),
)
else:
cg.add_library("NetworkClientSecure", None)
cg.add_library("WiFiClientSecure", None)
cg.add_library("HTTPClient", None)
if CORE.is_esp8266:
cg.add_library("ESP8266HTTPClient", None)

View File

@@ -239,7 +239,7 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
std::string response_body;
if (this->capture_response_.value(x...)) {
RAMAllocator<uint8_t> allocator;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
uint8_t *buf = allocator.allocate(max_length);
if (buf != nullptr) {
size_t read_index = 0;

View File

@@ -6,7 +6,6 @@
#if defined(USE_ESP32) || defined(USE_RP2040)
#include <HTTPClient.h>
#include <WiFiClient.h>
#endif
#ifdef USE_ESP8266
#include <ESP8266HTTPClient.h>

View File

@@ -54,7 +54,7 @@ void HttpRequestUpdate::update_task(void *params) {
UPDATE_RETURN;
}
RAMAllocator<uint8_t> allocator;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
uint8_t *data = allocator.allocate(container->content_length);
if (data == nullptr) {
std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length);

View File

@@ -1,5 +1,8 @@
import logging
from esphome import pins
import esphome.codegen as cg
from esphome.components import esp32
import esphome.config_validation as cv
from esphome.const import (
CONF_ADDRESS,
@@ -12,6 +15,8 @@ from esphome.const import (
CONF_SCL,
CONF_SDA,
CONF_TIMEOUT,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
@@ -19,12 +24,12 @@ from esphome.const import (
from esphome.core import CORE, coroutine_with_priority
import esphome.final_validate as fv
LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@esphome/core"]
i2c_ns = cg.esphome_ns.namespace("i2c")
I2CBus = i2c_ns.class_("I2CBus")
InternalI2CBus = i2c_ns.class_("InternalI2CBus", I2CBus)
ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", InternalI2CBus, cg.Component)
IDFI2CBus = i2c_ns.class_("IDFI2CBus", InternalI2CBus, cg.Component)
ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", I2CBus, cg.Component)
IDFI2CBus = i2c_ns.class_("IDFI2CBus", I2CBus, cg.Component)
I2CDevice = i2c_ns.class_("I2CDevice")
@@ -41,6 +46,32 @@ def _bus_declare_type(value):
raise NotImplementedError
def validate_config(config):
if (
config[CONF_SCAN]
and CORE.is_esp32
and CORE.using_esp_idf
and esp32.get_esp32_variant()
in [
esp32.const.VARIANT_ESP32C5,
esp32.const.VARIANT_ESP32C6,
esp32.const.VARIANT_ESP32P4,
]
):
version: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
if version.major == 5 and (
(version.minor == 3 and version.patch <= 3)
or (version.minor == 4 and version.patch <= 1)
):
LOGGER.warning(
"There is a bug in esp-idf version %s that breaks I2C scan, I2C scan "
"has been disabled, see https://github.com/esphome/issues/issues/7128",
str(version),
)
config[CONF_SCAN] = False
return config
pin_with_input_and_output_support = pins.internal_gpio_pin_number(
{CONF_OUTPUT: True, CONF_INPUT: True}
)
@@ -66,13 +97,13 @@ CONFIG_SCHEMA = cv.All(
}
).extend(cv.COMPONENT_SCHEMA),
cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]),
validate_config,
)
@coroutine_with_priority(1.0)
async def to_code(config):
cg.add_global(i2c_ns.using)
cg.add_define("USE_I2C")
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)

View File

@@ -1,6 +1,6 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <cstddef>
#include <utility>
#include <vector>
@@ -108,12 +108,5 @@ class I2CBus {
bool scan_{false}; ///< Should we scan ? Can be set in the yaml
};
class InternalI2CBus : public I2CBus {
public:
/// @brief Returns the I2C port number.
/// @return the port number of the internal I2C bus
virtual int get_port() const = 0;
};
} // namespace i2c
} // namespace esphome

View File

@@ -1,11 +1,11 @@
#ifdef USE_ARDUINO
#include "i2c_bus_arduino.h"
#include <Arduino.h>
#include <cstring>
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <Arduino.h>
#include <cstring>
namespace esphome {
namespace i2c {
@@ -23,7 +23,6 @@ void ArduinoI2CBus::setup() {
} else {
wire_ = new TwoWire(next_bus_num); // NOLINT(cppcoreguidelines-owning-memory)
}
this->port_ = next_bus_num;
next_bus_num++;
#elif defined(USE_ESP8266)
wire_ = new TwoWire(); // NOLINT(cppcoreguidelines-owning-memory)
@@ -126,7 +125,7 @@ ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt)
size_t to_request = 0;
for (size_t i = 0; i < cnt; i++)
to_request += buffers[i].len;
size_t ret = wire_->requestFrom(address, to_request, true);
size_t ret = wire_->requestFrom((int) address, (int) to_request, 1);
if (ret != to_request) {
ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", to_request, address, ret);
return ERROR_TIMEOUT;

View File

@@ -2,9 +2,9 @@
#ifdef USE_ARDUINO
#include <Wire.h>
#include "esphome/core/component.h"
#include "i2c_bus.h"
#include "esphome/core/component.h"
#include <Wire.h>
namespace esphome {
namespace i2c {
@@ -15,7 +15,7 @@ enum RecoveryCode {
RECOVERY_COMPLETED,
};
class ArduinoI2CBus : public InternalI2CBus, public Component {
class ArduinoI2CBus : public I2CBus, public Component {
public:
void setup() override;
void dump_config() override;
@@ -29,15 +29,12 @@ class ArduinoI2CBus : public InternalI2CBus, public Component {
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
int get_port() const override { return this->port_; }
private:
void recover_();
void set_pins_and_clock_();
RecoveryCode recovery_result_;
protected:
int8_t port_{-1};
TwoWire *wire_;
uint8_t sda_pin_;
uint8_t scl_pin_;

View File

@@ -2,9 +2,9 @@
#ifdef USE_ESP_IDF
#include <driver/i2c.h>
#include "esphome/core/component.h"
#include "i2c_bus.h"
#include "esphome/core/component.h"
#include <driver/i2c.h>
namespace esphome {
namespace i2c {
@@ -15,7 +15,7 @@ enum RecoveryCode {
RECOVERY_COMPLETED,
};
class IDFI2CBus : public InternalI2CBus, public Component {
class IDFI2CBus : public I2CBus, public Component {
public:
void setup() override;
void dump_config() override;
@@ -31,8 +31,6 @@ class IDFI2CBus : public InternalI2CBus, public Component {
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
int get_port() const override { return static_cast<int>(this->port_); }
private:
void recover_();
RecoveryCode recovery_result_;

View File

@@ -9,7 +9,7 @@ namespace i2s_audio {
static const char *const TAG = "i2s_audio";
#if ESP_IDF_VERSION_MAJOR >= 5
#if defined(USE_ESP_IDF) && (ESP_IDF_VERSION_MAJOR >= 5)
static const uint8_t I2S_NUM_MAX = SOC_I2S_NUM; // because IDF 5+ took this away :(
#endif

View File

@@ -114,7 +114,7 @@ async def to_code(config):
cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1))
cg.add(var.set_i2s_comm_fmt_lsb(config[CONF_I2S_COMM_FMT] == "lsb"))
cg.add_library("NetworkClientSecure", None)
cg.add_library("WiFiClientSecure", None)
cg.add_library("HTTPClient", None)
cg.add_library("esphome/ESP32-audioI2S", "2.3.0")
cg.add_library("esphome/ESP32-audioI2S", "2.2.0")
cg.add_build_flag("-DAUDIO_NO_SD_FS")

View File

@@ -484,7 +484,7 @@ bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) {
esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) {
if (this->data_buffer_ == nullptr) {
// Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus
RAMAllocator<uint8_t> allocator;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->data_buffer_ = allocator.allocate(data_buffer_size);
}
@@ -698,7 +698,7 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr
if (this->data_buffer_ != nullptr) {
RAMAllocator<uint8_t> allocator;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
allocator.deallocate(this->data_buffer_, buffer_size);
this->data_buffer_ = nullptr;
}

View File

@@ -129,13 +129,6 @@ void INA219Component::setup() {
}
}
void INA219Component::on_powerdown() {
// Mode = 0 -> power down
if (!this->write_byte_16(INA219_REGISTER_CONFIG, 0)) {
ESP_LOGE(TAG, "powerdown error");
}
}
void INA219Component::dump_config() {
ESP_LOGCONFIG(TAG, "INA219:");
LOG_I2C_DEVICE(this);

View File

@@ -15,7 +15,6 @@ class INA219Component : public PollingComponent, public i2c::I2CDevice {
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void on_powerdown() override;
void set_shunt_resistance_ohm(float shunt_resistance_ohm) { shunt_resistance_ohm_ = shunt_resistance_ohm; }
void set_max_current_a(float max_current_a) { max_current_a_ = max_current_a; }

View File

@@ -57,8 +57,8 @@ void Inkplate6::setup() {
* Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed.
*/
void Inkplate6::initialize_() {
RAMAllocator<uint8_t> allocator;
RAMAllocator<uint32_t> allocator32;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
ExternalRAMAllocator<uint32_t> allocator32(ExternalRAMAllocator<uint32_t>::ALLOW_FAILURE);
uint32_t buffer_size = this->get_buffer_length_();
if (buffer_size == 0)
return;

View File

@@ -3,8 +3,6 @@ from esphome.components import number
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_MOVE_THRESHOLD,
CONF_STILL_THRESHOLD,
CONF_TIMEOUT,
DEVICE_CLASS_DISTANCE,
DEVICE_CLASS_ILLUMINANCE,
@@ -26,6 +24,8 @@ MaxDistanceTimeoutNumber = ld2410_ns.class_("MaxDistanceTimeoutNumber", number.N
CONF_MAX_MOVE_DISTANCE_GATE = "max_move_distance_gate"
CONF_MAX_STILL_DISTANCE_GATE = "max_still_distance_gate"
CONF_LIGHT_THRESHOLD = "light_threshold"
CONF_STILL_THRESHOLD = "still_threshold"
CONF_MOVE_THRESHOLD = "move_threshold"
TIMEOUT_GROUP = "timeout"

View File

@@ -3,7 +3,6 @@ from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_LIGHT,
CONF_MOVING_DISTANCE,
DEVICE_CLASS_DISTANCE,
DEVICE_CLASS_ILLUMINANCE,
ENTITY_CATEGORY_DIAGNOSTIC,
@@ -18,6 +17,7 @@ from esphome.const import (
from . import CONF_LD2410_ID, LD2410Component
DEPENDENCIES = ["ld2410"]
CONF_MOVING_DISTANCE = "moving_distance"
CONF_STILL_DISTANCE = "still_distance"
CONF_MOVING_ENERGY = "moving_energy"
CONF_STILL_ENERGY = "still_energy"

View File

@@ -2,7 +2,6 @@ import esphome.codegen as cg
from esphome.components import switch
import esphome.config_validation as cv
from esphome.const import (
CONF_BLUETOOTH,
DEVICE_CLASS_SWITCH,
ENTITY_CATEGORY_CONFIG,
ICON_BLUETOOTH,
@@ -15,6 +14,7 @@ BluetoothSwitch = ld2410_ns.class_("BluetoothSwitch", switch.Switch)
EngineeringModeSwitch = ld2410_ns.class_("EngineeringModeSwitch", switch.Switch)
CONF_ENGINEERING_MODE = "engineering_mode"
CONF_BLUETOOTH = "bluetooth"
CONFIG_SCHEMA = {
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),

View File

@@ -3,8 +3,6 @@ from esphome.components import number
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_MOVE_THRESHOLD,
CONF_STILL_THRESHOLD,
DEVICE_CLASS_DISTANCE,
ENTITY_CATEGORY_CONFIG,
ICON_MOTION_SENSOR,
@@ -33,6 +31,8 @@ LD2420StillThresholdNumbers = ld2420_ns.class_(
)
CONF_MIN_GATE_DISTANCE = "min_gate_distance"
CONF_MAX_GATE_DISTANCE = "max_gate_distance"
CONF_STILL_THRESHOLD = "still_threshold"
CONF_MOVE_THRESHOLD = "move_threshold"
CONF_GATE_MOVE_SENSITIVITY = "gate_move_sensitivity"
CONF_GATE_STILL_SENSITIVITY = "gate_still_sensitivity"
CONF_GATE_SELECT = "gate_select"

View File

@@ -1,17 +1,13 @@
import esphome.codegen as cg
from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_MOVING_DISTANCE,
DEVICE_CLASS_DISTANCE,
UNIT_CENTIMETER,
)
from esphome.const import CONF_ID, DEVICE_CLASS_DISTANCE, UNIT_CENTIMETER
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
LD2420Sensor = ld2420_ns.class_("LD2420Sensor", sensor.Sensor, cg.Component)
CONF_MOVING_DISTANCE = "moving_distance"
CONF_GATE_ENERGY = "gate_energy"
CONFIG_SCHEMA = cv.All(

View File

@@ -2,7 +2,6 @@ import esphome.codegen as cg
from esphome.components import switch
import esphome.config_validation as cv
from esphome.const import (
CONF_BLUETOOTH,
DEVICE_CLASS_SWITCH,
ENTITY_CATEGORY_CONFIG,
ICON_BLUETOOTH,
@@ -14,6 +13,7 @@ from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
BluetoothSwitch = ld2450_ns.class_("BluetoothSwitch", switch.Switch)
MultiTargetSwitch = ld2450_ns.class_("MultiTargetSwitch", switch.Switch)
CONF_BLUETOOTH = "bluetooth"
CONF_MULTI_TARGET = "multi_target"
CONFIG_SCHEMA = {

View File

@@ -3,16 +3,28 @@
#ifdef USE_ESP32
#ifdef USE_ARDUINO
#include <esp32-hal-ledc.h>
#endif
#include <driver/ledc.h>
#include <cinttypes>
#define CLOCK_FREQUENCY 80e6f
#ifdef USE_ARDUINO
#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
#undef CLOCK_FREQUENCY
// starting with ESP32 Arduino 2.0.2, the 40MHz crystal is used as clock by default if supported
#define CLOCK_FREQUENCY 40e6f
#endif
#else
#ifdef SOC_LEDC_SUPPORT_APB_CLOCK
#define DEFAULT_CLK LEDC_USE_APB_CLK
#else
#define DEFAULT_CLK LEDC_AUTO_CLK
#endif
#endif
static const uint8_t SETUP_ATTEMPT_COUNT_MAX = 5;
@@ -22,6 +34,7 @@ namespace ledc {
static const char *const TAG = "ledc.output";
static const int MAX_RES_BITS = LEDC_TIMER_BIT_MAX - 1;
#ifdef USE_ESP_IDF
#if SOC_LEDC_SUPPORT_HS_MODE
// Only ESP32 has LEDC_HIGH_SPEED_MODE
inline ledc_mode_t get_speed_mode(uint8_t channel) { return channel < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; }
@@ -31,6 +44,7 @@ inline ledc_mode_t get_speed_mode(uint8_t channel) { return channel < 8 ? LEDC_H
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/ledc.html#functionality-overview
inline ledc_mode_t get_speed_mode(uint8_t) { return LEDC_LOW_SPEED_MODE; }
#endif
#endif
float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) {
return static_cast<float>(CLOCK_FREQUENCY) / static_cast<float>(1 << bit_depth);
@@ -54,6 +68,7 @@ optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) {
return {};
}
#ifdef USE_ESP_IDF
esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_channel_t chan_num,
uint8_t channel, uint8_t &bit_depth, float frequency) {
bit_depth = *ledc_bit_depth_for_frequency(frequency);
@@ -83,10 +98,13 @@ esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_n
return init_result;
}
#endif
#ifdef USE_ESP_IDF
constexpr int ledc_angle_to_htop(float angle, uint8_t bit_depth) {
return static_cast<int>(angle * ((1U << bit_depth) - 1) / 360.0f);
}
#endif // USE_ESP_IDF
void LEDCOutput::write_state(float state) {
if (!this->initialized_) {
@@ -102,6 +120,10 @@ void LEDCOutput::write_state(float state) {
const float duty_rounded = roundf(state * max_duty);
auto duty = static_cast<uint32_t>(duty_rounded);
ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_);
#ifdef USE_ARDUINO
ledcWrite(this->channel_, duty);
#endif
#ifdef USE_ESP_IDF
auto speed_mode = get_speed_mode(this->channel_);
auto chan_num = static_cast<ledc_channel_t>(this->channel_ % 8);
int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_);
@@ -113,10 +135,18 @@ void LEDCOutput::write_state(float state) {
ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint);
ledc_update_duty(speed_mode, chan_num);
}
#endif
}
void LEDCOutput::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
#ifdef USE_ARDUINO
this->update_frequency(this->frequency_);
this->turn_off();
// Attach pin after setting default value
ledcAttachPin(this->pin_->get_pin(), this->channel_);
#endif
#ifdef USE_ESP_IDF
auto speed_mode = get_speed_mode(this->channel_);
auto timer_num = static_cast<ledc_timer_t>((this->channel_ % 8) / 2);
auto chan_num = static_cast<ledc_channel_t>(this->channel_ % 8);
@@ -145,6 +175,7 @@ void LEDCOutput::setup() {
ledc_channel_config(&chan_conf);
this->initialized_ = true;
this->status_clear_error();
#endif
}
void LEDCOutput::dump_config() {
@@ -177,7 +208,38 @@ void LEDCOutput::update_frequency(float frequency) {
}
this->bit_depth_ = bit_depth_opt.value_or(8);
this->frequency_ = frequency;
#ifdef USE_ARDUINO
ESP_LOGV(TAG, "Using Arduino API - Trying to define channel, frequency and bit depth");
u_int32_t configured_frequency = 0;
// Configure LEDC channel, frequency and bit depth with fallback
int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX;
while (attempt_count_max > 0 && configured_frequency == 0) {
ESP_LOGV(TAG, "Initializing channel %u with frequency %.1f and bit depth of %u", this->channel_, this->frequency_,
this->bit_depth_);
configured_frequency = ledcSetup(this->channel_, frequency, this->bit_depth_);
if (configured_frequency != 0) {
this->initialized_ = true;
this->status_clear_error();
ESP_LOGV(TAG, "Configured frequency: %u with bit depth: %u", configured_frequency, this->bit_depth_);
} else {
ESP_LOGW(TAG, "Unable to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_,
this->frequency_, this->bit_depth_);
// try again with a lower bit depth
this->bit_depth_--;
}
attempt_count_max--;
}
if (configured_frequency == 0) {
ESP_LOGE(TAG, "Permanently failed to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_,
this->frequency_, this->bit_depth_);
this->status_set_error();
return;
}
#endif // USE_ARDUINO
#ifdef USE_ESP_IDF
if (!this->initialized_) {
ESP_LOGW(TAG, "Not yet initialized");
return;
@@ -197,7 +259,7 @@ void LEDCOutput::update_frequency(float frequency) {
}
this->status_clear_error();
#endif
// re-apply duty
this->write_state(this->duty_);
}

View File

@@ -173,9 +173,9 @@ def _notify_old_style(config):
# The dev and latest branches will be at *least* this version, which is what matters.
ARDUINO_VERSIONS = {
"dev": (cv.Version(1, 9, 1), "https://github.com/libretiny-eu/libretiny.git"),
"latest": (cv.Version(1, 9, 1), "libretiny"),
"recommended": (cv.Version(1, 9, 1), None),
"dev": (cv.Version(1, 7, 0), "https://github.com/libretiny-eu/libretiny.git"),
"latest": (cv.Version(1, 7, 0), "libretiny"),
"recommended": (cv.Version(1, 7, 0), None),
}
@@ -264,7 +264,6 @@ async def component_to_code(config):
# force using arduino framework
cg.add_platformio_option("framework", "arduino")
cg.add_build_flag("-DUSE_ARDUINO")
cg.set_cpp_standard("gnu++17")
# disable library compatibility checks
cg.add_platformio_option("lib_ldf_mode", "off")

View File

@@ -324,10 +324,7 @@ async def to_code(config):
if CORE.using_arduino:
if config[CONF_HARDWARE_UART] == USB_CDC:
cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=1")
if CORE.is_esp32 and get_esp32_variant() in (
VARIANT_ESP32C3,
VARIANT_ESP32C6,
):
if CORE.is_esp32 and get_esp32_variant() == VARIANT_ESP32C3:
cg.add_build_flag("-DARDUINO_USB_MODE=1")
if CORE.using_esp_idf:

View File

@@ -3,7 +3,7 @@ import esphome.config_validation as cv
from esphome.const import CONF_SIZE, CONF_TEXT
from esphome.cpp_generator import MockObjClass
from ..defines import CONF_MAIN, literal
from ..defines import CONF_MAIN
from ..lv_validation import color, color_retmapper, lv_text
from ..lvcode import LocalVariable, lv, lv_expr
from ..schemas import TEXT_SCHEMA
@@ -34,7 +34,7 @@ class QrCodeType(WidgetType):
)
def get_uses(self):
return ("canvas", "img")
return ("canvas", "img", "label")
def obj_creator(self, parent: MockObjClass, config: dict):
dark_color = color_retmapper(config[CONF_DARK_COLOR])
@@ -45,10 +45,8 @@ class QrCodeType(WidgetType):
async def to_code(self, w: Widget, config):
if (value := config.get(CONF_TEXT)) is not None:
value = await lv_text.process(value)
with LocalVariable(
"qr_text", cg.const_char_ptr, value, modifier=""
) as str_obj:
lv.qrcode_update(w.obj, str_obj, literal(f"strlen({str_obj})"))
with LocalVariable("qr_text", cg.std_string, value, modifier="") as str_obj:
lv.qrcode_update(w.obj, str_obj.c_str(), str_obj.size())
qr_code_spec = QrCodeType()

View File

@@ -8,7 +8,7 @@ namespace m5stack_8angle {
static const char *const TAG = "m5stack_8angle.light";
void M5Stack8AngleLightOutput::setup() {
RAMAllocator<uint8_t> allocator;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->buf_ = allocator.allocate(M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED);
if (this->buf_ == nullptr) {
ESP_LOGE(TAG, "Failed to allocate buffer of size %u", M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED);

View File

@@ -50,7 +50,7 @@ MCP23016_PIN_SCHEMA = pins.gpio_base_schema(
cv.int_range(min=0, max=15),
modes=[CONF_INPUT, CONF_OUTPUT],
mode_validator=validate_mode,
invertible=True,
invertable=True,
).extend(
{
cv.Required(CONF_MCP23016): cv.use_id(MCP23016),

View File

@@ -60,7 +60,7 @@ MCP23XXX_PIN_SCHEMA = pins.gpio_base_schema(
cv.int_range(min=0, max=15),
modes=[CONF_INPUT, CONF_OUTPUT, CONF_PULLUP],
mode_validator=validate_mode,
invertible=True,
invertable=True,
).extend(
{
cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase),

View File

@@ -6,7 +6,11 @@ namespace mcp23xxx_base {
float MCP23XXXBase::get_setup_priority() const { return setup_priority::IO; }
void MCP23XXXGPIOPin::setup() { pin_mode(flags_); }
void MCP23XXXGPIOPin::setup() {
pin_mode(flags_);
this->parent_->pin_interrupt_mode(this->pin_, this->interrupt_mode_);
}
void MCP23XXXGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); }
bool MCP23XXXGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; }
void MCP23XXXGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); }

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg
from esphome.components import i2c
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_ID
CODEOWNERS = ["@p1ngb4ck"]

View File

@@ -1,9 +1,8 @@
import esphome.codegen as cg
from esphome.components import output
import esphome.config_validation as cv
from esphome.components import output
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_INITIAL_VALUE
from .. import CONF_MCP4461_ID, Mcp4461Component, mcp4461_ns
from .. import Mcp4461Component, CONF_MCP4461_ID, mcp4461_ns
DEPENDENCIES = ["mcp4461"]

View File

@@ -7,7 +7,7 @@
namespace esphome {
namespace md5 {
#if defined(USE_ARDUINO) && !defined(USE_RP2040) && !defined(USE_ESP32)
#if defined(USE_ARDUINO) && !defined(USE_RP2040)
void MD5Digest::init() {
memset(this->digest_, 0, 16);
MD5Init(&this->ctx_);
@@ -18,7 +18,7 @@ void MD5Digest::add(const uint8_t *data, size_t len) { MD5Update(&this->ctx_, da
void MD5Digest::calculate() { MD5Final(this->digest_, &this->ctx_); }
#endif // USE_ARDUINO && !USE_RP2040
#ifdef USE_ESP32
#ifdef USE_ESP_IDF
void MD5Digest::init() {
memset(this->digest_, 0, 16);
esp_rom_md5_init(&this->ctx_);
@@ -27,7 +27,7 @@ void MD5Digest::init() {
void MD5Digest::add(const uint8_t *data, size_t len) { esp_rom_md5_update(&this->ctx_, data, len); }
void MD5Digest::calculate() { esp_rom_md5_final(this->digest_, &this->ctx_); }
#endif // USE_ESP32
#endif // USE_ESP_IDF
#ifdef USE_RP2040
void MD5Digest::init() {

View File

@@ -3,11 +3,16 @@
#include "esphome/core/defines.h"
#ifdef USE_MD5
#ifdef USE_ESP32
#ifdef USE_ESP_IDF
#include "esp_rom_md5.h"
#define MD5_CTX_TYPE md5_context_t
#endif
#if defined(USE_ARDUINO) && defined(USE_ESP32)
#include "rom/md5_hash.h"
#define MD5_CTX_TYPE MD5Context
#endif
#if defined(USE_ARDUINO) && defined(USE_ESP8266)
#include <md5.h>
#define MD5_CTX_TYPE md5_context_t

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