mirror of
https://github.com/esphome/esphome.git
synced 2025-08-23 18:49:25 +00:00
Compare commits
30 Commits
2025.8.0b1
...
optimize_e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fb1c2467b9 | ||
![]() |
daf8ec36ab | ||
![]() |
6c5632a0b3 | ||
![]() |
abecc0e8d8 | ||
![]() |
af9ecf3429 | ||
![]() |
5fa84439c2 | ||
![]() |
5d18afcd99 | ||
![]() |
117cffd2b0 | ||
![]() |
8ea1a3ed64 | ||
![]() |
4f29b3c7aa | ||
![]() |
3325592d67 | ||
![]() |
0a3ee7d84e | ||
![]() |
882237120e | ||
![]() |
71efaf097b | ||
![]() |
bd60dbb746 | ||
![]() |
6b5e43ca72 | ||
![]() |
8d61b1e8df | ||
![]() |
9c897993bb | ||
![]() |
93f9475105 | ||
![]() |
95cd224e3e | ||
![]() |
b7afeafda9 | ||
![]() |
7922462bcf | ||
![]() |
46d433775b | ||
![]() |
7c4a54de90 | ||
![]() |
c3f1596498 | ||
![]() |
0d1949a61b | ||
![]() |
6a8722f33e | ||
![]() |
fff66072d4 | ||
![]() |
0dda3faed5 | ||
![]() |
40c0c36179 |
@@ -11,7 +11,7 @@ ci:
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.12.8
|
rev: v0.12.9
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
2
Doxyfile
2
Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = 2025.8.0b1
|
PROJECT_NUMBER = 2025.9.0-dev
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# 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
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
@@ -289,16 +289,26 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
|
|||||||
return 0; // Doesn't fit
|
return 0; // Doesn't fit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate buffer space - pass payload size, allocation functions add header/footer space
|
|
||||||
ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size)
|
|
||||||
: conn->allocate_batch_message_buffer(calculated_size);
|
|
||||||
|
|
||||||
// Get buffer size after allocation (which includes header padding)
|
// Get buffer size after allocation (which includes header padding)
|
||||||
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
|
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
|
||||||
size_t size_before_encode = shared_buf.size();
|
|
||||||
|
if (is_single || conn->flags_.batch_first_message) {
|
||||||
|
// Single message or first batch message
|
||||||
|
conn->prepare_first_message_buffer(shared_buf, header_padding, total_calculated_size);
|
||||||
|
if (conn->flags_.batch_first_message) {
|
||||||
|
conn->flags_.batch_first_message = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Batch message second or later
|
||||||
|
// Add padding for previous message footer + this message header
|
||||||
|
size_t current_size = shared_buf.size();
|
||||||
|
shared_buf.reserve(current_size + total_calculated_size);
|
||||||
|
shared_buf.resize(current_size + footer_size + header_padding);
|
||||||
|
}
|
||||||
|
|
||||||
// Encode directly into buffer
|
// Encode directly into buffer
|
||||||
msg.encode(buffer);
|
size_t size_before_encode = shared_buf.size();
|
||||||
|
msg.encode({&shared_buf});
|
||||||
|
|
||||||
// Calculate actual encoded size (not including header that was already added)
|
// Calculate actual encoded size (not including header that was already added)
|
||||||
size_t actual_payload_size = shared_buf.size() - size_before_encode;
|
size_t actual_payload_size = shared_buf.size() - size_before_encode;
|
||||||
@@ -1620,14 +1630,6 @@ bool APIConnection::schedule_batch_() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); }
|
|
||||||
|
|
||||||
ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
|
|
||||||
ProtoWriteBuffer result = this->prepare_message_buffer(size, this->flags_.batch_first_message);
|
|
||||||
this->flags_.batch_first_message = false;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void APIConnection::process_batch_() {
|
void APIConnection::process_batch_() {
|
||||||
// Ensure PacketInfo remains trivially destructible for our placement new approach
|
// Ensure PacketInfo remains trivially destructible for our placement new approach
|
||||||
static_assert(std::is_trivially_destructible<PacketInfo>::value,
|
static_assert(std::is_trivially_destructible<PacketInfo>::value,
|
||||||
@@ -1735,7 +1737,7 @@ void APIConnection::process_batch_() {
|
|||||||
}
|
}
|
||||||
remaining_size -= payload_size;
|
remaining_size -= payload_size;
|
||||||
// Calculate where the next message's header padding will start
|
// Calculate where the next message's header padding will start
|
||||||
// Current buffer size + footer space (that prepare_message_buffer will add for this message)
|
// Current buffer size + footer space for this message
|
||||||
current_offset = shared_buf.size() + footer_size;
|
current_offset = shared_buf.size() + footer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -252,44 +252,21 @@ class APIConnection : public APIServerConnection {
|
|||||||
|
|
||||||
// Get header padding size - used for both reserve and insert
|
// Get header padding size - used for both reserve and insert
|
||||||
uint8_t header_padding = this->helper_->frame_header_padding();
|
uint8_t header_padding = this->helper_->frame_header_padding();
|
||||||
|
|
||||||
// Get shared buffer from parent server
|
// Get shared buffer from parent server
|
||||||
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||||
|
this->prepare_first_message_buffer(shared_buf, header_padding,
|
||||||
|
reserve_size + header_padding + this->helper_->frame_footer_size());
|
||||||
|
return {&shared_buf};
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t header_padding, size_t total_size) {
|
||||||
shared_buf.clear();
|
shared_buf.clear();
|
||||||
// Reserve space for header padding + message + footer
|
// Reserve space for header padding + message + footer
|
||||||
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
|
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
|
||||||
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
|
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
|
||||||
shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
|
shared_buf.reserve(total_size);
|
||||||
// Resize to add header padding so message encoding starts at the correct position
|
// Resize to add header padding so message encoding starts at the correct position
|
||||||
shared_buf.resize(header_padding);
|
shared_buf.resize(header_padding);
|
||||||
return {&shared_buf};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare buffer for next message in batch
|
|
||||||
ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
|
|
||||||
// Get reference to shared buffer (it maintains state between batch messages)
|
|
||||||
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
|
||||||
|
|
||||||
if (is_first_message) {
|
|
||||||
shared_buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t current_size = shared_buf.size();
|
|
||||||
|
|
||||||
// Calculate padding to add:
|
|
||||||
// - First message: just header padding
|
|
||||||
// - Subsequent messages: footer for previous message + header padding for this message
|
|
||||||
size_t padding_to_add = is_first_message
|
|
||||||
? this->helper_->frame_header_padding()
|
|
||||||
: this->helper_->frame_header_padding() + this->helper_->frame_footer_size();
|
|
||||||
|
|
||||||
// Reserve space for padding + message
|
|
||||||
shared_buf.reserve(current_size + padding_to_add + message_size);
|
|
||||||
|
|
||||||
// Resize to add the padding bytes
|
|
||||||
shared_buf.resize(current_size + padding_to_add);
|
|
||||||
|
|
||||||
return {&shared_buf};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_to_clear_buffer(bool log_out_of_space);
|
bool try_to_clear_buffer(bool log_out_of_space);
|
||||||
@@ -297,10 +274,6 @@ class APIConnection : public APIServerConnection {
|
|||||||
|
|
||||||
std::string get_client_combined_info() const { return this->client_info_.get_combined_info(); }
|
std::string get_client_combined_info() const { return this->client_info_.get_combined_info(); }
|
||||||
|
|
||||||
// Buffer allocator methods for batch processing
|
|
||||||
ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
|
|
||||||
ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Helper function to handle authentication completion
|
// Helper function to handle authentication completion
|
||||||
void complete_authentication_();
|
void complete_authentication_();
|
||||||
|
@@ -235,8 +235,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer
|
|||||||
|
|
||||||
for (const auto &packet : packets) {
|
for (const auto &packet : packets) {
|
||||||
// Calculate varint sizes for header layout
|
// Calculate varint sizes for header layout
|
||||||
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.payload_size));
|
uint8_t size_varint_len = api::ProtoSize::varint(packet.payload_size);
|
||||||
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.message_type));
|
uint8_t type_varint_len = api::ProtoSize::varint(packet.message_type);
|
||||||
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
|
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
|
||||||
|
|
||||||
// Calculate where to start writing the header
|
// Calculate where to start writing the header
|
||||||
@@ -271,9 +271,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer
|
|||||||
buf_start[header_offset] = 0x00; // indicator
|
buf_start[header_offset] = 0x00; // indicator
|
||||||
|
|
||||||
// Encode varints directly into buffer
|
// Encode varints directly into buffer
|
||||||
ProtoVarInt(packet.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
|
encode_varint_unchecked(buf_start + header_offset + 1, packet.payload_size);
|
||||||
ProtoVarInt(packet.message_type)
|
encode_varint_unchecked(buf_start + header_offset + 1 + size_varint_len, packet.message_type);
|
||||||
.encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
|
|
||||||
|
|
||||||
// Add iovec for this packet (header + payload)
|
// Add iovec for this packet (header + payload)
|
||||||
size_t packet_len = static_cast<size_t>(total_header_len + packet.payload_size);
|
size_t packet_len = static_cast<size_t>(total_header_len + packet.payload_size);
|
||||||
|
@@ -124,34 +124,6 @@ class ProtoVarInt {
|
|||||||
// with ZigZag encoding
|
// with ZigZag encoding
|
||||||
return decode_zigzag64(this->value_);
|
return decode_zigzag64(this->value_);
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Encode the varint value to a pre-allocated buffer without bounds checking.
|
|
||||||
*
|
|
||||||
* @param buffer The pre-allocated buffer to write the encoded varint to
|
|
||||||
* @param len The size of the buffer in bytes
|
|
||||||
*
|
|
||||||
* @note The caller is responsible for ensuring the buffer is large enough
|
|
||||||
* to hold the encoded value. Use ProtoSize::varint() to calculate
|
|
||||||
* the exact size needed before calling this method.
|
|
||||||
* @note No bounds checking is performed for performance reasons.
|
|
||||||
*/
|
|
||||||
void encode_to_buffer_unchecked(uint8_t *buffer, size_t len) {
|
|
||||||
uint64_t val = this->value_;
|
|
||||||
if (val <= 0x7F) {
|
|
||||||
buffer[0] = val;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t i = 0;
|
|
||||||
while (val && i < len) {
|
|
||||||
uint8_t temp = val & 0x7F;
|
|
||||||
val >>= 7;
|
|
||||||
if (val) {
|
|
||||||
buffer[i++] = temp | 0x80;
|
|
||||||
} else {
|
|
||||||
buffer[i++] = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void encode(std::vector<uint8_t> &out) {
|
void encode(std::vector<uint8_t> &out) {
|
||||||
uint64_t val = this->value_;
|
uint64_t val = this->value_;
|
||||||
if (val <= 0x7F) {
|
if (val <= 0x7F) {
|
||||||
@@ -330,6 +302,28 @@ class ProtoWriteBuffer {
|
|||||||
std::vector<uint8_t> *buffer_;
|
std::vector<uint8_t> *buffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Encode a uint16_t value as a varint directly to a buffer without bounds checking
|
||||||
|
*
|
||||||
|
* @param buffer The pre-allocated buffer to write the encoded varint to
|
||||||
|
* @param value The uint16_t value to encode (0-65535)
|
||||||
|
*
|
||||||
|
* @note The caller is responsible for ensuring the buffer is large enough (max 3 bytes for uint16_t)
|
||||||
|
* @note No bounds checking is performed for performance reasons
|
||||||
|
*/
|
||||||
|
inline void encode_varint_unchecked(uint8_t *buffer, uint16_t value) {
|
||||||
|
if (value < 128) {
|
||||||
|
buffer[0] = value;
|
||||||
|
} else if (value < 16384) {
|
||||||
|
buffer[0] = (value & 0x7F) | 0x80;
|
||||||
|
buffer[1] = value >> 7;
|
||||||
|
} else {
|
||||||
|
buffer[0] = (value & 0x7F) | 0x80;
|
||||||
|
buffer[1] = ((value >> 7) & 0x7F) | 0x80;
|
||||||
|
buffer[2] = value >> 14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
class ProtoSize;
|
class ProtoSize;
|
||||||
|
|
||||||
@@ -386,6 +380,33 @@ class ProtoSize {
|
|||||||
|
|
||||||
uint32_t get_size() const { return total_size_; }
|
uint32_t get_size() const { return total_size_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the size in bytes needed to encode a uint8_t value as a varint
|
||||||
|
*
|
||||||
|
* @param value The uint8_t value to calculate size for
|
||||||
|
* @return The number of bytes needed to encode the value (1 or 2)
|
||||||
|
*/
|
||||||
|
static constexpr uint8_t varint(uint8_t value) {
|
||||||
|
// For uint8_t (0-255), we need at most 2 bytes
|
||||||
|
return (value < 128) ? 1 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the size in bytes needed to encode a uint16_t value as a varint
|
||||||
|
*
|
||||||
|
* @param value The uint16_t value to calculate size for
|
||||||
|
* @return The number of bytes needed to encode the value (1-3)
|
||||||
|
*/
|
||||||
|
static constexpr uint8_t varint(uint16_t value) {
|
||||||
|
// For uint16_t (0-65535), we need at most 3 bytes
|
||||||
|
if (value < 128)
|
||||||
|
return 1; // 7 bits
|
||||||
|
else if (value < 16384)
|
||||||
|
return 2; // 14 bits
|
||||||
|
else
|
||||||
|
return 3; // 15-16 bits
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
|
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
|
||||||
*
|
*
|
||||||
@@ -395,11 +416,9 @@ class ProtoSize {
|
|||||||
static constexpr uint32_t varint(uint32_t value) {
|
static constexpr uint32_t varint(uint32_t value) {
|
||||||
// Optimized varint size calculation using leading zeros
|
// Optimized varint size calculation using leading zeros
|
||||||
// Each 7 bits requires one byte in the varint encoding
|
// Each 7 bits requires one byte in the varint encoding
|
||||||
if (value < 128)
|
if (value < 128) {
|
||||||
return 1; // 7 bits, common case for small values
|
return 1; // 7 bits, common case for small values
|
||||||
|
} else if (value < 16384) {
|
||||||
// For larger values, count bytes needed based on the position of the highest bit set
|
|
||||||
if (value < 16384) {
|
|
||||||
return 2; // 14 bits
|
return 2; // 14 bits
|
||||||
} else if (value < 2097152) {
|
} else if (value < 2097152) {
|
||||||
return 3; // 21 bits
|
return 3; // 21 bits
|
||||||
@@ -773,7 +792,7 @@ inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessa
|
|||||||
this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
|
this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
|
||||||
|
|
||||||
// Write the length varint directly
|
// Write the length varint directly
|
||||||
ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes);
|
encode_varint_unchecked(this->buffer_->data() + begin, static_cast<uint16_t>(msg_length_bytes));
|
||||||
|
|
||||||
// Now encode the message content - it will append to the buffer
|
// Now encode the message content - it will append to the buffer
|
||||||
value.encode(*this);
|
value.encode(*this);
|
||||||
|
@@ -133,7 +133,7 @@ void BluetoothConnection::loop() {
|
|||||||
|
|
||||||
// Check if we should disable the loop
|
// Check if we should disable the loop
|
||||||
// - For V3_WITH_CACHE: Services are never sent, disable after INIT state
|
// - For V3_WITH_CACHE: Services are never sent, disable after INIT state
|
||||||
// - For other connections: Disable only after service discovery is complete
|
// - For V3_WITHOUT_CACHE: Disable only after service discovery is complete
|
||||||
// (send_service_ == DONE_SENDING_SERVICES, which is only set after services are sent)
|
// (send_service_ == DONE_SENDING_SERVICES, which is only set after services are sent)
|
||||||
if (this->state_ != espbt::ClientState::INIT && (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
|
if (this->state_ != espbt::ClientState::INIT && (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
|
||||||
this->send_service_ == DONE_SENDING_SERVICES)) {
|
this->send_service_ == DONE_SENDING_SERVICES)) {
|
||||||
@@ -160,10 +160,7 @@ void BluetoothConnection::send_service_for_discovery_() {
|
|||||||
if (this->send_service_ >= this->service_count_) {
|
if (this->send_service_ >= this->service_count_) {
|
||||||
this->send_service_ = DONE_SENDING_SERVICES;
|
this->send_service_ = DONE_SENDING_SERVICES;
|
||||||
this->proxy_->send_gatt_services_done(this->address_);
|
this->proxy_->send_gatt_services_done(this->address_);
|
||||||
if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE ||
|
this->release_services();
|
||||||
this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
|
|
||||||
this->release_services();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32 {
|
namespace esp32 {
|
||||||
@@ -156,20 +157,23 @@ class ESP32Preferences : public ESPPreferences {
|
|||||||
return failed == 0;
|
return failed == 0;
|
||||||
}
|
}
|
||||||
bool is_changed(const uint32_t nvs_handle, const NVSData &to_save) {
|
bool is_changed(const uint32_t nvs_handle, const NVSData &to_save) {
|
||||||
NVSData stored_data{};
|
|
||||||
size_t actual_len;
|
size_t actual_len;
|
||||||
esp_err_t err = nvs_get_blob(nvs_handle, to_save.key.c_str(), nullptr, &actual_len);
|
esp_err_t err = nvs_get_blob(nvs_handle, to_save.key.c_str(), nullptr, &actual_len);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", to_save.key.c_str(), esp_err_to_name(err));
|
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", to_save.key.c_str(), esp_err_to_name(err));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
stored_data.data.resize(actual_len);
|
// Check size first before allocating memory
|
||||||
err = nvs_get_blob(nvs_handle, to_save.key.c_str(), stored_data.data.data(), &actual_len);
|
if (actual_len != to_save.data.size()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto stored_data = std::make_unique<uint8_t[]>(actual_len);
|
||||||
|
err = nvs_get_blob(nvs_handle, to_save.key.c_str(), stored_data.get(), &actual_len);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key.c_str(), esp_err_to_name(err));
|
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key.c_str(), esp_err_to_name(err));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return to_save.data != stored_data.data;
|
return memcmp(to_save.data.data(), stored_data.get(), to_save.data.size()) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool reset() override {
|
bool reset() override {
|
||||||
|
@@ -294,6 +294,7 @@ async def to_code(config):
|
|||||||
|
|
||||||
if config[CONF_ADVERTISING]:
|
if config[CONF_ADVERTISING]:
|
||||||
cg.add_define("USE_ESP32_BLE_ADVERTISING")
|
cg.add_define("USE_ESP32_BLE_ADVERTISING")
|
||||||
|
cg.add_define("USE_ESP32_BLE_UUID")
|
||||||
|
|
||||||
|
|
||||||
@automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({}))
|
@automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({}))
|
||||||
|
@@ -3,8 +3,7 @@
|
|||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <cstddef> // for offsetof
|
#include <cstddef> // for offsetof
|
||||||
#include <vector>
|
#include <cstring> // for memcpy
|
||||||
|
|
||||||
#include <esp_gap_ble_api.h>
|
#include <esp_gap_ble_api.h>
|
||||||
#include <esp_gattc_api.h>
|
#include <esp_gattc_api.h>
|
||||||
#include <esp_gatts_api.h>
|
#include <esp_gatts_api.h>
|
||||||
@@ -64,7 +63,7 @@ static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == si
|
|||||||
|
|
||||||
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
|
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
|
||||||
// This class stores each event with minimal memory usage.
|
// This class stores each event with minimal memory usage.
|
||||||
// GAP events (99% of traffic) don't have the vector overhead.
|
// GAP events (99% of traffic) don't have the heap allocation overhead.
|
||||||
// GATTC/GATTS events use heap allocation for their param and data.
|
// GATTC/GATTS events use heap allocation for their param and data.
|
||||||
//
|
//
|
||||||
// Event flow:
|
// Event flow:
|
||||||
@@ -145,16 +144,18 @@ class BLEEvent {
|
|||||||
}
|
}
|
||||||
if (this->type_ == GATTC) {
|
if (this->type_ == GATTC) {
|
||||||
delete this->event_.gattc.gattc_param;
|
delete this->event_.gattc.gattc_param;
|
||||||
delete this->event_.gattc.data;
|
delete[] this->event_.gattc.data;
|
||||||
this->event_.gattc.gattc_param = nullptr;
|
this->event_.gattc.gattc_param = nullptr;
|
||||||
this->event_.gattc.data = nullptr;
|
this->event_.gattc.data = nullptr;
|
||||||
|
this->event_.gattc.data_len = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->type_ == GATTS) {
|
if (this->type_ == GATTS) {
|
||||||
delete this->event_.gatts.gatts_param;
|
delete this->event_.gatts.gatts_param;
|
||||||
delete this->event_.gatts.data;
|
delete[] this->event_.gatts.data;
|
||||||
this->event_.gatts.gatts_param = nullptr;
|
this->event_.gatts.gatts_param = nullptr;
|
||||||
this->event_.gatts.data = nullptr;
|
this->event_.gatts.data = nullptr;
|
||||||
|
this->event_.gatts.data_len = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,17 +210,19 @@ class BLEEvent {
|
|||||||
esp_gattc_cb_event_t gattc_event;
|
esp_gattc_cb_event_t gattc_event;
|
||||||
esp_gatt_if_t gattc_if;
|
esp_gatt_if_t gattc_if;
|
||||||
esp_ble_gattc_cb_param_t *gattc_param; // Heap-allocated
|
esp_ble_gattc_cb_param_t *gattc_param; // Heap-allocated
|
||||||
std::vector<uint8_t> *data; // Heap-allocated
|
uint8_t *data; // Heap-allocated raw buffer (manually managed)
|
||||||
} gattc; // 16 bytes (pointers only)
|
uint16_t data_len; // Track size separately
|
||||||
|
} gattc;
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||||
struct gatts_event {
|
struct gatts_event {
|
||||||
esp_gatts_cb_event_t gatts_event;
|
esp_gatts_cb_event_t gatts_event;
|
||||||
esp_gatt_if_t gatts_if;
|
esp_gatt_if_t gatts_if;
|
||||||
esp_ble_gatts_cb_param_t *gatts_param; // Heap-allocated
|
esp_ble_gatts_cb_param_t *gatts_param; // Heap-allocated
|
||||||
std::vector<uint8_t> *data; // Heap-allocated
|
uint8_t *data; // Heap-allocated raw buffer (manually managed)
|
||||||
} gatts; // 16 bytes (pointers only)
|
uint16_t data_len; // Track size separately
|
||||||
} event_; // 80 bytes
|
} gatts;
|
||||||
|
} event_; // 80 bytes
|
||||||
|
|
||||||
ble_event_t type_;
|
ble_event_t type_;
|
||||||
|
|
||||||
@@ -319,6 +322,7 @@ class BLEEvent {
|
|||||||
if (p == nullptr) {
|
if (p == nullptr) {
|
||||||
this->event_.gattc.gattc_param = nullptr;
|
this->event_.gattc.gattc_param = nullptr;
|
||||||
this->event_.gattc.data = nullptr;
|
this->event_.gattc.data = nullptr;
|
||||||
|
this->event_.gattc.data_len = 0;
|
||||||
return; // Invalid event, but we can't log in header file
|
return; // Invalid event, but we can't log in header file
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,16 +340,29 @@ class BLEEvent {
|
|||||||
// We must copy this data to ensure it remains valid when the event is processed later.
|
// We must copy this data to ensure it remains valid when the event is processed later.
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case ESP_GATTC_NOTIFY_EVT:
|
case ESP_GATTC_NOTIFY_EVT:
|
||||||
this->event_.gattc.data = new std::vector<uint8_t>(p->notify.value, p->notify.value + p->notify.value_len);
|
this->event_.gattc.data_len = p->notify.value_len;
|
||||||
this->event_.gattc.gattc_param->notify.value = this->event_.gattc.data->data();
|
if (p->notify.value_len > 0) {
|
||||||
|
this->event_.gattc.data = new uint8_t[p->notify.value_len];
|
||||||
|
memcpy(this->event_.gattc.data, p->notify.value, p->notify.value_len);
|
||||||
|
} else {
|
||||||
|
this->event_.gattc.data = nullptr;
|
||||||
|
}
|
||||||
|
this->event_.gattc.gattc_param->notify.value = this->event_.gattc.data;
|
||||||
break;
|
break;
|
||||||
case ESP_GATTC_READ_CHAR_EVT:
|
case ESP_GATTC_READ_CHAR_EVT:
|
||||||
case ESP_GATTC_READ_DESCR_EVT:
|
case ESP_GATTC_READ_DESCR_EVT:
|
||||||
this->event_.gattc.data = new std::vector<uint8_t>(p->read.value, p->read.value + p->read.value_len);
|
this->event_.gattc.data_len = p->read.value_len;
|
||||||
this->event_.gattc.gattc_param->read.value = this->event_.gattc.data->data();
|
if (p->read.value_len > 0) {
|
||||||
|
this->event_.gattc.data = new uint8_t[p->read.value_len];
|
||||||
|
memcpy(this->event_.gattc.data, p->read.value, p->read.value_len);
|
||||||
|
} else {
|
||||||
|
this->event_.gattc.data = nullptr;
|
||||||
|
}
|
||||||
|
this->event_.gattc.gattc_param->read.value = this->event_.gattc.data;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this->event_.gattc.data = nullptr;
|
this->event_.gattc.data = nullptr;
|
||||||
|
this->event_.gattc.data_len = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,6 +375,7 @@ class BLEEvent {
|
|||||||
if (p == nullptr) {
|
if (p == nullptr) {
|
||||||
this->event_.gatts.gatts_param = nullptr;
|
this->event_.gatts.gatts_param = nullptr;
|
||||||
this->event_.gatts.data = nullptr;
|
this->event_.gatts.data = nullptr;
|
||||||
|
this->event_.gatts.data_len = 0;
|
||||||
return; // Invalid event, but we can't log in header file
|
return; // Invalid event, but we can't log in header file
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,11 +393,18 @@ class BLEEvent {
|
|||||||
// We must copy this data to ensure it remains valid when the event is processed later.
|
// We must copy this data to ensure it remains valid when the event is processed later.
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case ESP_GATTS_WRITE_EVT:
|
case ESP_GATTS_WRITE_EVT:
|
||||||
this->event_.gatts.data = new std::vector<uint8_t>(p->write.value, p->write.value + p->write.len);
|
this->event_.gatts.data_len = p->write.len;
|
||||||
this->event_.gatts.gatts_param->write.value = this->event_.gatts.data->data();
|
if (p->write.len > 0) {
|
||||||
|
this->event_.gatts.data = new uint8_t[p->write.len];
|
||||||
|
memcpy(this->event_.gatts.data, p->write.value, p->write.len);
|
||||||
|
} else {
|
||||||
|
this->event_.gatts.data = nullptr;
|
||||||
|
}
|
||||||
|
this->event_.gatts.gatts_param->write.value = this->event_.gatts.data;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this->event_.gatts.data = nullptr;
|
this->event_.gatts.data = nullptr;
|
||||||
|
this->event_.gatts.data_len = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -208,11 +208,11 @@ void ESPNowComponent::enable_() {
|
|||||||
esp_wifi_connectionless_module_set_wake_interval(CONFIG_ESPNOW_WAKE_INTERVAL);
|
esp_wifi_connectionless_module_set_wake_interval(CONFIG_ESPNOW_WAKE_INTERVAL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
this->state_ = ESPNOW_STATE_ENABLED;
|
||||||
|
|
||||||
for (auto peer : this->peers_) {
|
for (auto peer : this->peers_) {
|
||||||
this->add_peer(peer.address);
|
this->add_peer(peer.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->state_ = ESPNOW_STATE_ENABLED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESPNowComponent::disable() {
|
void ESPNowComponent::disable() {
|
||||||
@@ -407,7 +407,7 @@ esp_err_t ESPNowComponent::add_peer(const uint8_t *peer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(peer, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
|
if (memcmp(peer, this->own_address_, ESP_NOW_ETH_ALEN) == 0) {
|
||||||
this->mark_failed();
|
this->status_momentary_warning("peer-add-failed");
|
||||||
return ESP_ERR_INVALID_MAC;
|
return ESP_ERR_INVALID_MAC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -212,7 +212,7 @@ def validate_use_legacy(value):
|
|||||||
f"All i2s_audio components must set {CONF_USE_LEGACY} to the same value."
|
f"All i2s_audio components must set {CONF_USE_LEGACY} to the same value."
|
||||||
)
|
)
|
||||||
if (not value[CONF_USE_LEGACY]) and (CORE.using_arduino):
|
if (not value[CONF_USE_LEGACY]) and (CORE.using_arduino):
|
||||||
raise cv.Invalid("Arduino supports only the legacy i2s driver.")
|
raise cv.Invalid("Arduino supports only the legacy i2s driver")
|
||||||
_use_legacy_driver = value[CONF_USE_LEGACY]
|
_use_legacy_driver = value[CONF_USE_LEGACY]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@@ -92,7 +92,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
|
|
||||||
def _final_validate(_):
|
def _final_validate(_):
|
||||||
if not use_legacy():
|
if not use_legacy():
|
||||||
raise cv.Invalid("I2S media player is only compatible with legacy i2s driver.")
|
raise cv.Invalid("I2S media player is only compatible with legacy i2s driver")
|
||||||
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||||
|
@@ -122,7 +122,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
|
|
||||||
def _final_validate(config):
|
def _final_validate(config):
|
||||||
if not use_legacy() and config[CONF_ADC_TYPE] == "internal":
|
if not use_legacy() and config[CONF_ADC_TYPE] == "internal":
|
||||||
raise cv.Invalid("Internal ADC is only compatible with legacy i2s driver.")
|
raise cv.Invalid("Internal ADC is only compatible with legacy i2s driver")
|
||||||
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||||
|
@@ -163,7 +163,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
def _final_validate(config):
|
def _final_validate(config):
|
||||||
if not use_legacy():
|
if not use_legacy():
|
||||||
if config[CONF_DAC_TYPE] == "internal":
|
if config[CONF_DAC_TYPE] == "internal":
|
||||||
raise cv.Invalid("Internal DAC is only compatible with legacy i2s driver.")
|
raise cv.Invalid("Internal DAC is only compatible with legacy i2s driver")
|
||||||
if config[CONF_I2S_COMM_FMT] == "stand_max":
|
if config[CONF_I2S_COMM_FMT] == "stand_max":
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"I2S standard max format only implemented with legacy i2s driver."
|
"I2S standard max format only implemented with legacy i2s driver."
|
||||||
|
@@ -201,7 +201,7 @@ def _validate_manifest_version(manifest_data):
|
|||||||
else:
|
else:
|
||||||
raise cv.Invalid("Invalid manifest version")
|
raise cv.Invalid("Invalid manifest version")
|
||||||
else:
|
else:
|
||||||
raise cv.Invalid("Invalid manifest file, missing 'version' key.")
|
raise cv.Invalid("Invalid manifest file, missing 'version' key")
|
||||||
|
|
||||||
|
|
||||||
def _process_http_source(config):
|
def _process_http_source(config):
|
||||||
@@ -421,7 +421,7 @@ def _feature_step_size_validate(config):
|
|||||||
if features_step_size is None:
|
if features_step_size is None:
|
||||||
features_step_size = model_step_size
|
features_step_size = model_step_size
|
||||||
elif features_step_size != model_step_size:
|
elif features_step_size != model_step_size:
|
||||||
raise cv.Invalid("Cannot load models with different features step sizes.")
|
raise cv.Invalid("Cannot load models with different features step sizes")
|
||||||
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||||
|
@@ -16,6 +16,7 @@ from esphome.components.esp32.const import (
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ADVANCED,
|
CONF_ADVANCED,
|
||||||
|
CONF_DISABLED,
|
||||||
CONF_FRAMEWORK,
|
CONF_FRAMEWORK,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_MODE,
|
CONF_MODE,
|
||||||
@@ -102,6 +103,7 @@ def get_config_schema(config):
|
|||||||
cv.Optional(CONF_MODE, default=modes[0]): cv.one_of(*modes, lower=True),
|
cv.Optional(CONF_MODE, default=modes[0]): cv.one_of(*modes, lower=True),
|
||||||
cv.Optional(CONF_ENABLE_ECC, default=False): cv.boolean,
|
cv.Optional(CONF_ENABLE_ECC, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_SPEED, default=speeds[0]): cv.one_of(*speeds, upper=True),
|
cv.Optional(CONF_SPEED, default=speeds[0]): cv.one_of(*speeds, upper=True),
|
||||||
|
cv.Optional(CONF_DISABLED, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
)(config)
|
)(config)
|
||||||
|
|
||||||
@@ -112,6 +114,8 @@ FINAL_VALIDATE_SCHEMA = validate_psram_mode
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
if config[CONF_DISABLED]:
|
||||||
|
return
|
||||||
if CORE.using_arduino:
|
if CORE.using_arduino:
|
||||||
cg.add_build_flag("-DBOARD_HAS_PSRAM")
|
cg.add_build_flag("-DBOARD_HAS_PSRAM")
|
||||||
if config[CONF_MODE] == TYPE_OCTAL:
|
if config[CONF_MODE] == TYPE_OCTAL:
|
||||||
|
@@ -18,14 +18,6 @@ static const uint8_t QMP6988_TEMPERATURE_MSB_REG = 0xFA; /* Temperature MSB Reg
|
|||||||
static const uint8_t QMP6988_CALIBRATION_DATA_START = 0xA0; /* QMP6988 compensation coefficients */
|
static const uint8_t QMP6988_CALIBRATION_DATA_START = 0xA0; /* QMP6988 compensation coefficients */
|
||||||
static const uint8_t QMP6988_CALIBRATION_DATA_LENGTH = 25;
|
static const uint8_t QMP6988_CALIBRATION_DATA_LENGTH = 25;
|
||||||
|
|
||||||
static const uint8_t SHIFT_RIGHT_4_POSITION = 4;
|
|
||||||
static const uint8_t SHIFT_LEFT_2_POSITION = 2;
|
|
||||||
static const uint8_t SHIFT_LEFT_4_POSITION = 4;
|
|
||||||
static const uint8_t SHIFT_LEFT_5_POSITION = 5;
|
|
||||||
static const uint8_t SHIFT_LEFT_8_POSITION = 8;
|
|
||||||
static const uint8_t SHIFT_LEFT_12_POSITION = 12;
|
|
||||||
static const uint8_t SHIFT_LEFT_16_POSITION = 16;
|
|
||||||
|
|
||||||
/* power mode */
|
/* power mode */
|
||||||
static const uint8_t QMP6988_SLEEP_MODE = 0x00;
|
static const uint8_t QMP6988_SLEEP_MODE = 0x00;
|
||||||
static const uint8_t QMP6988_FORCED_MODE = 0x01;
|
static const uint8_t QMP6988_FORCED_MODE = 0x01;
|
||||||
@@ -95,64 +87,45 @@ static const char *iir_filter_to_str(QMP6988IIRFilter filter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool QMP6988Component::device_check_() {
|
bool QMP6988Component::device_check_() {
|
||||||
uint8_t ret = 0;
|
if (this->read_register(QMP6988_CHIP_ID_REG, &(qmp6988_data_.chip_id), 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGE(TAG, "Read chip ID (0xD1) failed");
|
||||||
ret = this->read_register(QMP6988_CHIP_ID_REG, &(qmp6988_data_.chip_id), 1);
|
return false;
|
||||||
if (ret != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGE(TAG, "%s: read chip ID (0xD1) failed", __func__);
|
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG, "qmp6988 read chip id = 0x%x", qmp6988_data_.chip_id);
|
ESP_LOGV(TAG, "Read chip ID = 0x%x", qmp6988_data_.chip_id);
|
||||||
|
|
||||||
return qmp6988_data_.chip_id == QMP6988_CHIP_ID;
|
return qmp6988_data_.chip_id == QMP6988_CHIP_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QMP6988Component::get_calibration_data_() {
|
bool QMP6988Component::get_calibration_data_() {
|
||||||
uint8_t status = 0;
|
|
||||||
// BITFIELDS temp_COE;
|
// BITFIELDS temp_COE;
|
||||||
uint8_t a_data_uint8_tr[QMP6988_CALIBRATION_DATA_LENGTH] = {0};
|
uint8_t a_data_uint8_tr[QMP6988_CALIBRATION_DATA_LENGTH] = {0};
|
||||||
int len;
|
|
||||||
|
|
||||||
for (len = 0; len < QMP6988_CALIBRATION_DATA_LENGTH; len += 1) {
|
for (uint8_t len = 0; len < QMP6988_CALIBRATION_DATA_LENGTH; len += 1) {
|
||||||
status = this->read_register(QMP6988_CALIBRATION_DATA_START + len, &a_data_uint8_tr[len], 1);
|
if (this->read_register(QMP6988_CALIBRATION_DATA_START + len, &a_data_uint8_tr[len], 1) != i2c::ERROR_OK) {
|
||||||
if (status != i2c::ERROR_OK) {
|
ESP_LOGE(TAG, "Read calibration data (0xA0) error");
|
||||||
ESP_LOGE(TAG, "qmp6988 read calibration data (0xA0) error!");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_a0 =
|
qmp6988_data_.qmp6988_cali.COE_a0 =
|
||||||
(QMP6988_S32_t) (((a_data_uint8_tr[18] << SHIFT_LEFT_12_POSITION) |
|
(int32_t) encode_uint32(a_data_uint8_tr[18], a_data_uint8_tr[19], (a_data_uint8_tr[24] & 0x0f) << 4, 0);
|
||||||
(a_data_uint8_tr[19] << SHIFT_LEFT_4_POSITION) | (a_data_uint8_tr[24] & 0x0f))
|
|
||||||
<< 12);
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_a0 = qmp6988_data_.qmp6988_cali.COE_a0 >> 12;
|
qmp6988_data_.qmp6988_cali.COE_a0 = qmp6988_data_.qmp6988_cali.COE_a0 >> 12;
|
||||||
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_a1 =
|
qmp6988_data_.qmp6988_cali.COE_a1 = (int16_t) encode_uint16(a_data_uint8_tr[20], a_data_uint8_tr[21]);
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[20]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[21]);
|
qmp6988_data_.qmp6988_cali.COE_a2 = (int16_t) encode_uint16(a_data_uint8_tr[22], a_data_uint8_tr[23]);
|
||||||
qmp6988_data_.qmp6988_cali.COE_a2 =
|
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[22]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[23]);
|
|
||||||
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_b00 =
|
qmp6988_data_.qmp6988_cali.COE_b00 =
|
||||||
(QMP6988_S32_t) (((a_data_uint8_tr[0] << SHIFT_LEFT_12_POSITION) | (a_data_uint8_tr[1] << SHIFT_LEFT_4_POSITION) |
|
(int32_t) encode_uint32(a_data_uint8_tr[0], a_data_uint8_tr[1], a_data_uint8_tr[24] & 0xf0, 0);
|
||||||
((a_data_uint8_tr[24] & 0xf0) >> SHIFT_RIGHT_4_POSITION))
|
|
||||||
<< 12);
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_b00 = qmp6988_data_.qmp6988_cali.COE_b00 >> 12;
|
qmp6988_data_.qmp6988_cali.COE_b00 = qmp6988_data_.qmp6988_cali.COE_b00 >> 12;
|
||||||
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_bt1 =
|
qmp6988_data_.qmp6988_cali.COE_bt1 = (int16_t) encode_uint16(a_data_uint8_tr[2], a_data_uint8_tr[3]);
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[2]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[3]);
|
qmp6988_data_.qmp6988_cali.COE_bt2 = (int16_t) encode_uint16(a_data_uint8_tr[4], a_data_uint8_tr[5]);
|
||||||
qmp6988_data_.qmp6988_cali.COE_bt2 =
|
qmp6988_data_.qmp6988_cali.COE_bp1 = (int16_t) encode_uint16(a_data_uint8_tr[6], a_data_uint8_tr[7]);
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[4]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[5]);
|
qmp6988_data_.qmp6988_cali.COE_b11 = (int16_t) encode_uint16(a_data_uint8_tr[8], a_data_uint8_tr[9]);
|
||||||
qmp6988_data_.qmp6988_cali.COE_bp1 =
|
qmp6988_data_.qmp6988_cali.COE_bp2 = (int16_t) encode_uint16(a_data_uint8_tr[10], a_data_uint8_tr[11]);
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[6]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[7]);
|
qmp6988_data_.qmp6988_cali.COE_b12 = (int16_t) encode_uint16(a_data_uint8_tr[12], a_data_uint8_tr[13]);
|
||||||
qmp6988_data_.qmp6988_cali.COE_b11 =
|
qmp6988_data_.qmp6988_cali.COE_b21 = (int16_t) encode_uint16(a_data_uint8_tr[14], a_data_uint8_tr[15]);
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[8]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[9]);
|
qmp6988_data_.qmp6988_cali.COE_bp3 = (int16_t) encode_uint16(a_data_uint8_tr[16], a_data_uint8_tr[17]);
|
||||||
qmp6988_data_.qmp6988_cali.COE_bp2 =
|
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[10]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[11]);
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_b12 =
|
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[12]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[13]);
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_b21 =
|
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[14]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[15]);
|
|
||||||
qmp6988_data_.qmp6988_cali.COE_bp3 =
|
|
||||||
(QMP6988_S16_t) (((a_data_uint8_tr[16]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[17]);
|
|
||||||
|
|
||||||
ESP_LOGV(TAG, "<-----------calibration data-------------->\r\n");
|
ESP_LOGV(TAG, "<-----------calibration data-------------->\r\n");
|
||||||
ESP_LOGV(TAG, "COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_a0,
|
ESP_LOGV(TAG, "COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_a0,
|
||||||
@@ -166,17 +139,17 @@ bool QMP6988Component::get_calibration_data_() {
|
|||||||
qmp6988_data_.ik.a0 = qmp6988_data_.qmp6988_cali.COE_a0; // 20Q4
|
qmp6988_data_.ik.a0 = qmp6988_data_.qmp6988_cali.COE_a0; // 20Q4
|
||||||
qmp6988_data_.ik.b00 = qmp6988_data_.qmp6988_cali.COE_b00; // 20Q4
|
qmp6988_data_.ik.b00 = qmp6988_data_.qmp6988_cali.COE_b00; // 20Q4
|
||||||
|
|
||||||
qmp6988_data_.ik.a1 = 3608L * (QMP6988_S32_t) qmp6988_data_.qmp6988_cali.COE_a1 - 1731677965L; // 31Q23
|
qmp6988_data_.ik.a1 = 3608L * (int32_t) qmp6988_data_.qmp6988_cali.COE_a1 - 1731677965L; // 31Q23
|
||||||
qmp6988_data_.ik.a2 = 16889L * (QMP6988_S32_t) qmp6988_data_.qmp6988_cali.COE_a2 - 87619360L; // 30Q47
|
qmp6988_data_.ik.a2 = 16889L * (int32_t) qmp6988_data_.qmp6988_cali.COE_a2 - 87619360L; // 30Q47
|
||||||
|
|
||||||
qmp6988_data_.ik.bt1 = 2982L * (QMP6988_S64_t) qmp6988_data_.qmp6988_cali.COE_bt1 + 107370906L; // 28Q15
|
qmp6988_data_.ik.bt1 = 2982L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bt1 + 107370906L; // 28Q15
|
||||||
qmp6988_data_.ik.bt2 = 329854L * (QMP6988_S64_t) qmp6988_data_.qmp6988_cali.COE_bt2 + 108083093L; // 34Q38
|
qmp6988_data_.ik.bt2 = 329854L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bt2 + 108083093L; // 34Q38
|
||||||
qmp6988_data_.ik.bp1 = 19923L * (QMP6988_S64_t) qmp6988_data_.qmp6988_cali.COE_bp1 + 1133836764L; // 31Q20
|
qmp6988_data_.ik.bp1 = 19923L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bp1 + 1133836764L; // 31Q20
|
||||||
qmp6988_data_.ik.b11 = 2406L * (QMP6988_S64_t) qmp6988_data_.qmp6988_cali.COE_b11 + 118215883L; // 28Q34
|
qmp6988_data_.ik.b11 = 2406L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b11 + 118215883L; // 28Q34
|
||||||
qmp6988_data_.ik.bp2 = 3079L * (QMP6988_S64_t) qmp6988_data_.qmp6988_cali.COE_bp2 - 181579595L; // 29Q43
|
qmp6988_data_.ik.bp2 = 3079L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bp2 - 181579595L; // 29Q43
|
||||||
qmp6988_data_.ik.b12 = 6846L * (QMP6988_S64_t) qmp6988_data_.qmp6988_cali.COE_b12 + 85590281L; // 29Q53
|
qmp6988_data_.ik.b12 = 6846L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b12 + 85590281L; // 29Q53
|
||||||
qmp6988_data_.ik.b21 = 13836L * (QMP6988_S64_t) qmp6988_data_.qmp6988_cali.COE_b21 + 79333336L; // 29Q60
|
qmp6988_data_.ik.b21 = 13836L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b21 + 79333336L; // 29Q60
|
||||||
qmp6988_data_.ik.bp3 = 2915L * (QMP6988_S64_t) qmp6988_data_.qmp6988_cali.COE_bp3 + 157155561L; // 28Q65
|
qmp6988_data_.ik.bp3 = 2915L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bp3 + 157155561L; // 28Q65
|
||||||
ESP_LOGV(TAG, "<----------- int calibration data -------------->\r\n");
|
ESP_LOGV(TAG, "<----------- int calibration data -------------->\r\n");
|
||||||
ESP_LOGV(TAG, "a0[%d] a1[%d] a2[%d] b00[%d]\r\n", qmp6988_data_.ik.a0, qmp6988_data_.ik.a1, qmp6988_data_.ik.a2,
|
ESP_LOGV(TAG, "a0[%d] a1[%d] a2[%d] b00[%d]\r\n", qmp6988_data_.ik.a0, qmp6988_data_.ik.a1, qmp6988_data_.ik.a2,
|
||||||
qmp6988_data_.ik.b00);
|
qmp6988_data_.ik.b00);
|
||||||
@@ -188,55 +161,55 @@ bool QMP6988Component::get_calibration_data_() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMP6988_S16_t QMP6988Component::get_compensated_temperature_(qmp6988_ik_data_t *ik, QMP6988_S32_t dt) {
|
int16_t QMP6988Component::get_compensated_temperature_(qmp6988_ik_data_t *ik, int32_t dt) {
|
||||||
QMP6988_S16_t ret;
|
int16_t ret;
|
||||||
QMP6988_S64_t wk1, wk2;
|
int64_t wk1, wk2;
|
||||||
|
|
||||||
// wk1: 60Q4 // bit size
|
// wk1: 60Q4 // bit size
|
||||||
wk1 = ((QMP6988_S64_t) ik->a1 * (QMP6988_S64_t) dt); // 31Q23+24-1=54 (54Q23)
|
wk1 = ((int64_t) ik->a1 * (int64_t) dt); // 31Q23+24-1=54 (54Q23)
|
||||||
wk2 = ((QMP6988_S64_t) ik->a2 * (QMP6988_S64_t) dt) >> 14; // 30Q47+24-1=53 (39Q33)
|
wk2 = ((int64_t) ik->a2 * (int64_t) dt) >> 14; // 30Q47+24-1=53 (39Q33)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) dt) >> 10; // 39Q33+24-1=62 (52Q23)
|
wk2 = (wk2 * (int64_t) dt) >> 10; // 39Q33+24-1=62 (52Q23)
|
||||||
wk2 = ((wk1 + wk2) / 32767) >> 19; // 54,52->55Q23 (20Q04)
|
wk2 = ((wk1 + wk2) / 32767) >> 19; // 54,52->55Q23 (20Q04)
|
||||||
ret = (QMP6988_S16_t) ((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0
|
ret = (int16_t) ((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMP6988_S32_t QMP6988Component::get_compensated_pressure_(qmp6988_ik_data_t *ik, QMP6988_S32_t dp, QMP6988_S16_t tx) {
|
int32_t QMP6988Component::get_compensated_pressure_(qmp6988_ik_data_t *ik, int32_t dp, int16_t tx) {
|
||||||
QMP6988_S32_t ret;
|
int32_t ret;
|
||||||
QMP6988_S64_t wk1, wk2, wk3;
|
int64_t wk1, wk2, wk3;
|
||||||
|
|
||||||
// wk1 = 48Q16 // bit size
|
// wk1 = 48Q16 // bit size
|
||||||
wk1 = ((QMP6988_S64_t) ik->bt1 * (QMP6988_S64_t) tx); // 28Q15+16-1=43 (43Q15)
|
wk1 = ((int64_t) ik->bt1 * (int64_t) tx); // 28Q15+16-1=43 (43Q15)
|
||||||
wk2 = ((QMP6988_S64_t) ik->bp1 * (QMP6988_S64_t) dp) >> 5; // 31Q20+24-1=54 (49Q15)
|
wk2 = ((int64_t) ik->bp1 * (int64_t) dp) >> 5; // 31Q20+24-1=54 (49Q15)
|
||||||
wk1 += wk2; // 43,49->50Q15
|
wk1 += wk2; // 43,49->50Q15
|
||||||
wk2 = ((QMP6988_S64_t) ik->bt2 * (QMP6988_S64_t) tx) >> 1; // 34Q38+16-1=49 (48Q37)
|
wk2 = ((int64_t) ik->bt2 * (int64_t) tx) >> 1; // 34Q38+16-1=49 (48Q37)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) tx) >> 8; // 48Q37+16-1=63 (55Q29)
|
wk2 = (wk2 * (int64_t) tx) >> 8; // 48Q37+16-1=63 (55Q29)
|
||||||
wk3 = wk2; // 55Q29
|
wk3 = wk2; // 55Q29
|
||||||
wk2 = ((QMP6988_S64_t) ik->b11 * (QMP6988_S64_t) tx) >> 4; // 28Q34+16-1=43 (39Q30)
|
wk2 = ((int64_t) ik->b11 * (int64_t) tx) >> 4; // 28Q34+16-1=43 (39Q30)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 1; // 39Q30+24-1=62 (61Q29)
|
wk2 = (wk2 * (int64_t) dp) >> 1; // 39Q30+24-1=62 (61Q29)
|
||||||
wk3 += wk2; // 55,61->62Q29
|
wk3 += wk2; // 55,61->62Q29
|
||||||
wk2 = ((QMP6988_S64_t) ik->bp2 * (QMP6988_S64_t) dp) >> 13; // 29Q43+24-1=52 (39Q30)
|
wk2 = ((int64_t) ik->bp2 * (int64_t) dp) >> 13; // 29Q43+24-1=52 (39Q30)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 1; // 39Q30+24-1=62 (61Q29)
|
wk2 = (wk2 * (int64_t) dp) >> 1; // 39Q30+24-1=62 (61Q29)
|
||||||
wk3 += wk2; // 62,61->63Q29
|
wk3 += wk2; // 62,61->63Q29
|
||||||
wk1 += wk3 >> 14; // Q29 >> 14 -> Q15
|
wk1 += wk3 >> 14; // Q29 >> 14 -> Q15
|
||||||
wk2 = ((QMP6988_S64_t) ik->b12 * (QMP6988_S64_t) tx); // 29Q53+16-1=45 (45Q53)
|
wk2 = ((int64_t) ik->b12 * (int64_t) tx); // 29Q53+16-1=45 (45Q53)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) tx) >> 22; // 45Q53+16-1=61 (39Q31)
|
wk2 = (wk2 * (int64_t) tx) >> 22; // 45Q53+16-1=61 (39Q31)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 1; // 39Q31+24-1=62 (61Q30)
|
wk2 = (wk2 * (int64_t) dp) >> 1; // 39Q31+24-1=62 (61Q30)
|
||||||
wk3 = wk2; // 61Q30
|
wk3 = wk2; // 61Q30
|
||||||
wk2 = ((QMP6988_S64_t) ik->b21 * (QMP6988_S64_t) tx) >> 6; // 29Q60+16-1=45 (39Q54)
|
wk2 = ((int64_t) ik->b21 * (int64_t) tx) >> 6; // 29Q60+16-1=45 (39Q54)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 23; // 39Q54+24-1=62 (39Q31)
|
wk2 = (wk2 * (int64_t) dp) >> 23; // 39Q54+24-1=62 (39Q31)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 1; // 39Q31+24-1=62 (61Q20)
|
wk2 = (wk2 * (int64_t) dp) >> 1; // 39Q31+24-1=62 (61Q20)
|
||||||
wk3 += wk2; // 61,61->62Q30
|
wk3 += wk2; // 61,61->62Q30
|
||||||
wk2 = ((QMP6988_S64_t) ik->bp3 * (QMP6988_S64_t) dp) >> 12; // 28Q65+24-1=51 (39Q53)
|
wk2 = ((int64_t) ik->bp3 * (int64_t) dp) >> 12; // 28Q65+24-1=51 (39Q53)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 23; // 39Q53+24-1=62 (39Q30)
|
wk2 = (wk2 * (int64_t) dp) >> 23; // 39Q53+24-1=62 (39Q30)
|
||||||
wk2 = (wk2 * (QMP6988_S64_t) dp); // 39Q30+24-1=62 (62Q30)
|
wk2 = (wk2 * (int64_t) dp); // 39Q30+24-1=62 (62Q30)
|
||||||
wk3 += wk2; // 62,62->63Q30
|
wk3 += wk2; // 62,62->63Q30
|
||||||
wk1 += wk3 >> 15; // Q30 >> 15 = Q15
|
wk1 += wk3 >> 15; // Q30 >> 15 = Q15
|
||||||
wk1 /= 32767L;
|
wk1 /= 32767L;
|
||||||
wk1 >>= 11; // Q15 >> 7 = Q4
|
wk1 >>= 11; // Q15 >> 7 = Q4
|
||||||
wk1 += ik->b00; // Q4 + 20Q4
|
wk1 += ik->b00; // Q4 + 20Q4
|
||||||
// wk1 >>= 4; // 28Q4 -> 24Q0
|
// wk1 >>= 4; // 28Q4 -> 24Q0
|
||||||
ret = (QMP6988_S32_t) wk1;
|
ret = (int32_t) wk1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +247,7 @@ void QMP6988Component::set_power_mode_(uint8_t power_mode) {
|
|||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QMP6988Component::write_filter_(unsigned char filter) {
|
void QMP6988Component::write_filter_(QMP6988IIRFilter filter) {
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
|
|
||||||
data = (filter & 0x03);
|
data = (filter & 0x03);
|
||||||
@@ -282,7 +255,7 @@ void QMP6988Component::write_filter_(unsigned char filter) {
|
|||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QMP6988Component::write_oversampling_pressure_(unsigned char oversampling_p) {
|
void QMP6988Component::write_oversampling_pressure_(QMP6988Oversampling oversampling_p) {
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
|
|
||||||
this->read_register(QMP6988_CTRLMEAS_REG, &data, 1);
|
this->read_register(QMP6988_CTRLMEAS_REG, &data, 1);
|
||||||
@@ -292,7 +265,7 @@ void QMP6988Component::write_oversampling_pressure_(unsigned char oversampling_p
|
|||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QMP6988Component::write_oversampling_temperature_(unsigned char oversampling_t) {
|
void QMP6988Component::write_oversampling_temperature_(QMP6988Oversampling oversampling_t) {
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
|
|
||||||
this->read_register(QMP6988_CTRLMEAS_REG, &data, 1);
|
this->read_register(QMP6988_CTRLMEAS_REG, &data, 1);
|
||||||
@@ -302,16 +275,6 @@ void QMP6988Component::write_oversampling_temperature_(unsigned char oversamplin
|
|||||||
delay(10);
|
delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QMP6988Component::set_temperature_oversampling(QMP6988Oversampling oversampling_t) {
|
|
||||||
this->temperature_oversampling_ = oversampling_t;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QMP6988Component::set_pressure_oversampling(QMP6988Oversampling oversampling_p) {
|
|
||||||
this->pressure_oversampling_ = oversampling_p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QMP6988Component::set_iir_filter(QMP6988IIRFilter iirfilter) { this->iir_filter_ = iirfilter; }
|
|
||||||
|
|
||||||
void QMP6988Component::calculate_altitude_(float pressure, float temp) {
|
void QMP6988Component::calculate_altitude_(float pressure, float temp) {
|
||||||
float altitude;
|
float altitude;
|
||||||
altitude = (pow((101325 / pressure), 1 / 5.257) - 1) * (temp + 273.15) / 0.0065;
|
altitude = (pow((101325 / pressure), 1 / 5.257) - 1) * (temp + 273.15) / 0.0065;
|
||||||
@@ -320,10 +283,10 @@ void QMP6988Component::calculate_altitude_(float pressure, float temp) {
|
|||||||
|
|
||||||
void QMP6988Component::calculate_pressure_() {
|
void QMP6988Component::calculate_pressure_() {
|
||||||
uint8_t err = 0;
|
uint8_t err = 0;
|
||||||
QMP6988_U32_t p_read, t_read;
|
uint32_t p_read, t_read;
|
||||||
QMP6988_S32_t p_raw, t_raw;
|
int32_t p_raw, t_raw;
|
||||||
uint8_t a_data_uint8_tr[6] = {0};
|
uint8_t a_data_uint8_tr[6] = {0};
|
||||||
QMP6988_S32_t t_int, p_int;
|
int32_t t_int, p_int;
|
||||||
this->qmp6988_data_.temperature = 0;
|
this->qmp6988_data_.temperature = 0;
|
||||||
this->qmp6988_data_.pressure = 0;
|
this->qmp6988_data_.pressure = 0;
|
||||||
|
|
||||||
@@ -332,13 +295,11 @@ void QMP6988Component::calculate_pressure_() {
|
|||||||
ESP_LOGE(TAG, "Error reading raw pressure/temp values");
|
ESP_LOGE(TAG, "Error reading raw pressure/temp values");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
p_read = (QMP6988_U32_t) ((((QMP6988_U32_t) (a_data_uint8_tr[0])) << SHIFT_LEFT_16_POSITION) |
|
p_read = encode_uint24(a_data_uint8_tr[0], a_data_uint8_tr[1], a_data_uint8_tr[2]);
|
||||||
(((QMP6988_U16_t) (a_data_uint8_tr[1])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[2]));
|
p_raw = (int32_t) (p_read - SUBTRACTOR);
|
||||||
p_raw = (QMP6988_S32_t) (p_read - SUBTRACTOR);
|
|
||||||
|
|
||||||
t_read = (QMP6988_U32_t) ((((QMP6988_U32_t) (a_data_uint8_tr[3])) << SHIFT_LEFT_16_POSITION) |
|
t_read = encode_uint24(a_data_uint8_tr[3], a_data_uint8_tr[4], a_data_uint8_tr[5]);
|
||||||
(((QMP6988_U16_t) (a_data_uint8_tr[4])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[5]));
|
t_raw = (int32_t) (t_read - SUBTRACTOR);
|
||||||
t_raw = (QMP6988_S32_t) (t_read - SUBTRACTOR);
|
|
||||||
|
|
||||||
t_int = this->get_compensated_temperature_(&(qmp6988_data_.ik), t_raw);
|
t_int = this->get_compensated_temperature_(&(qmp6988_data_.ik), t_raw);
|
||||||
p_int = this->get_compensated_pressure_(&(qmp6988_data_.ik), p_raw, t_int);
|
p_int = this->get_compensated_pressure_(&(qmp6988_data_.ik), p_raw, t_int);
|
||||||
@@ -348,10 +309,9 @@ void QMP6988Component::calculate_pressure_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void QMP6988Component::setup() {
|
void QMP6988Component::setup() {
|
||||||
bool ret;
|
if (!this->device_check_()) {
|
||||||
ret = this->device_check_();
|
this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
|
||||||
if (!ret) {
|
return;
|
||||||
ESP_LOGCONFIG(TAG, "Setup failed - device not found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->software_reset_();
|
this->software_reset_();
|
||||||
@@ -365,9 +325,6 @@ void QMP6988Component::setup() {
|
|||||||
void QMP6988Component::dump_config() {
|
void QMP6988Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "QMP6988:");
|
ESP_LOGCONFIG(TAG, "QMP6988:");
|
||||||
LOG_I2C_DEVICE(this);
|
LOG_I2C_DEVICE(this);
|
||||||
if (this->is_failed()) {
|
|
||||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
|
||||||
}
|
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
|
||||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
@@ -377,8 +334,6 @@ void QMP6988Component::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_));
|
ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_));
|
||||||
}
|
}
|
||||||
|
|
||||||
float QMP6988Component::get_setup_priority() const { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
void QMP6988Component::update() {
|
void QMP6988Component::update() {
|
||||||
this->calculate_pressure_();
|
this->calculate_pressure_();
|
||||||
float pressurehectopascals = this->qmp6988_data_.pressure / 100;
|
float pressurehectopascals = this->qmp6988_data_.pressure / 100;
|
||||||
|
@@ -1,24 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/i2c/i2c.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace qmp6988 {
|
namespace qmp6988 {
|
||||||
|
|
||||||
#define QMP6988_U16_t unsigned short
|
|
||||||
#define QMP6988_S16_t short
|
|
||||||
#define QMP6988_U32_t unsigned int
|
|
||||||
#define QMP6988_S32_t int
|
|
||||||
#define QMP6988_U64_t unsigned long long
|
|
||||||
#define QMP6988_S64_t long long
|
|
||||||
|
|
||||||
/* oversampling */
|
/* oversampling */
|
||||||
enum QMP6988Oversampling {
|
enum QMP6988Oversampling : uint8_t {
|
||||||
QMP6988_OVERSAMPLING_SKIPPED = 0x00,
|
QMP6988_OVERSAMPLING_SKIPPED = 0x00,
|
||||||
QMP6988_OVERSAMPLING_1X = 0x01,
|
QMP6988_OVERSAMPLING_1X = 0x01,
|
||||||
QMP6988_OVERSAMPLING_2X = 0x02,
|
QMP6988_OVERSAMPLING_2X = 0x02,
|
||||||
@@ -30,7 +23,7 @@ enum QMP6988Oversampling {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* filter */
|
/* filter */
|
||||||
enum QMP6988IIRFilter {
|
enum QMP6988IIRFilter : uint8_t {
|
||||||
QMP6988_IIR_FILTER_OFF = 0x00,
|
QMP6988_IIR_FILTER_OFF = 0x00,
|
||||||
QMP6988_IIR_FILTER_2X = 0x01,
|
QMP6988_IIR_FILTER_2X = 0x01,
|
||||||
QMP6988_IIR_FILTER_4X = 0x02,
|
QMP6988_IIR_FILTER_4X = 0x02,
|
||||||
@@ -40,18 +33,18 @@ enum QMP6988IIRFilter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
using qmp6988_cali_data_t = struct Qmp6988CaliData {
|
using qmp6988_cali_data_t = struct Qmp6988CaliData {
|
||||||
QMP6988_S32_t COE_a0;
|
int32_t COE_a0;
|
||||||
QMP6988_S16_t COE_a1;
|
int16_t COE_a1;
|
||||||
QMP6988_S16_t COE_a2;
|
int16_t COE_a2;
|
||||||
QMP6988_S32_t COE_b00;
|
int32_t COE_b00;
|
||||||
QMP6988_S16_t COE_bt1;
|
int16_t COE_bt1;
|
||||||
QMP6988_S16_t COE_bt2;
|
int16_t COE_bt2;
|
||||||
QMP6988_S16_t COE_bp1;
|
int16_t COE_bp1;
|
||||||
QMP6988_S16_t COE_b11;
|
int16_t COE_b11;
|
||||||
QMP6988_S16_t COE_bp2;
|
int16_t COE_bp2;
|
||||||
QMP6988_S16_t COE_b12;
|
int16_t COE_b12;
|
||||||
QMP6988_S16_t COE_b21;
|
int16_t COE_b21;
|
||||||
QMP6988_S16_t COE_bp3;
|
int16_t COE_bp3;
|
||||||
};
|
};
|
||||||
|
|
||||||
using qmp6988_fk_data_t = struct Qmp6988FkData {
|
using qmp6988_fk_data_t = struct Qmp6988FkData {
|
||||||
@@ -60,9 +53,9 @@ using qmp6988_fk_data_t = struct Qmp6988FkData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
using qmp6988_ik_data_t = struct Qmp6988IkData {
|
using qmp6988_ik_data_t = struct Qmp6988IkData {
|
||||||
QMP6988_S32_t a0, b00;
|
int32_t a0, b00;
|
||||||
QMP6988_S32_t a1, a2;
|
int32_t a1, a2;
|
||||||
QMP6988_S64_t bt1, bt2, bp1, b11, bp2, b12, b21, bp3;
|
int64_t bt1, bt2, bp1, b11, bp2, b12, b21, bp3;
|
||||||
};
|
};
|
||||||
|
|
||||||
using qmp6988_data_t = struct Qmp6988Data {
|
using qmp6988_data_t = struct Qmp6988Data {
|
||||||
@@ -77,17 +70,18 @@ using qmp6988_data_t = struct Qmp6988Data {
|
|||||||
|
|
||||||
class QMP6988Component : public PollingComponent, public i2c::I2CDevice {
|
class QMP6988Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
public:
|
public:
|
||||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
|
||||||
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
|
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { this->pressure_sensor_ = pressure_sensor; }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override;
|
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
void set_iir_filter(QMP6988IIRFilter iirfilter);
|
void set_iir_filter(QMP6988IIRFilter iirfilter) { this->iir_filter_ = iirfilter; }
|
||||||
void set_temperature_oversampling(QMP6988Oversampling oversampling_t);
|
void set_temperature_oversampling(QMP6988Oversampling oversampling_t) {
|
||||||
void set_pressure_oversampling(QMP6988Oversampling oversampling_p);
|
this->temperature_oversampling_ = oversampling_t;
|
||||||
|
}
|
||||||
|
void set_pressure_oversampling(QMP6988Oversampling oversampling_p) { this->pressure_oversampling_ = oversampling_p; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
qmp6988_data_t qmp6988_data_;
|
qmp6988_data_t qmp6988_data_;
|
||||||
@@ -102,14 +96,14 @@ class QMP6988Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
bool get_calibration_data_();
|
bool get_calibration_data_();
|
||||||
bool device_check_();
|
bool device_check_();
|
||||||
void set_power_mode_(uint8_t power_mode);
|
void set_power_mode_(uint8_t power_mode);
|
||||||
void write_oversampling_temperature_(unsigned char oversampling_t);
|
void write_oversampling_temperature_(QMP6988Oversampling oversampling_t);
|
||||||
void write_oversampling_pressure_(unsigned char oversampling_p);
|
void write_oversampling_pressure_(QMP6988Oversampling oversampling_p);
|
||||||
void write_filter_(unsigned char filter);
|
void write_filter_(QMP6988IIRFilter filter);
|
||||||
void calculate_pressure_();
|
void calculate_pressure_();
|
||||||
void calculate_altitude_(float pressure, float temp);
|
void calculate_altitude_(float pressure, float temp);
|
||||||
|
|
||||||
QMP6988_S32_t get_compensated_pressure_(qmp6988_ik_data_t *ik, QMP6988_S32_t dp, QMP6988_S16_t tx);
|
int32_t get_compensated_pressure_(qmp6988_ik_data_t *ik, int32_t dp, int16_t tx);
|
||||||
QMP6988_S16_t get_compensated_temperature_(qmp6988_ik_data_t *ik, QMP6988_S32_t dt);
|
int16_t get_compensated_temperature_(qmp6988_ik_data_t *ik, int32_t dt);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace qmp6988
|
} // namespace qmp6988
|
||||||
|
@@ -147,7 +147,7 @@ def _read_audio_file_and_type(file_config):
|
|||||||
elif file_source == TYPE_WEB:
|
elif file_source == TYPE_WEB:
|
||||||
path = _compute_local_file_path(conf_file)
|
path = _compute_local_file_path(conf_file)
|
||||||
else:
|
else:
|
||||||
raise cv.Invalid("Unsupported file source.")
|
raise cv.Invalid("Unsupported file source")
|
||||||
|
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
@@ -219,7 +219,7 @@ def _validate_supported_local_file(config):
|
|||||||
for file_config in config.get(CONF_FILES, []):
|
for file_config in config.get(CONF_FILES, []):
|
||||||
_, media_file_type = _read_audio_file_and_type(file_config)
|
_, media_file_type = _read_audio_file_and_type(file_config)
|
||||||
if str(media_file_type) == str(audio.AUDIO_FILE_TYPE_ENUM["NONE"]):
|
if str(media_file_type) == str(audio.AUDIO_FILE_TYPE_ENUM["NONE"]):
|
||||||
raise cv.Invalid("Unsupported local media file.")
|
raise cv.Invalid("Unsupported local media file")
|
||||||
if not config[CONF_CODEC_SUPPORT_ENABLED] and str(media_file_type) != str(
|
if not config[CONF_CODEC_SUPPORT_ENABLED] and str(media_file_type) != str(
|
||||||
audio.AUDIO_FILE_TYPE_ENUM["WAV"]
|
audio.AUDIO_FILE_TYPE_ENUM["WAV"]
|
||||||
):
|
):
|
||||||
|
@@ -813,7 +813,7 @@ std::string WebServer::cover_state_json_generator(WebServer *web_server, void *s
|
|||||||
return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE);
|
return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE);
|
||||||
}
|
}
|
||||||
std::string WebServer::cover_all_json_generator(WebServer *web_server, void *source) {
|
std::string WebServer::cover_all_json_generator(WebServer *web_server, void *source) {
|
||||||
return web_server->cover_json((cover::Cover *) (source), DETAIL_STATE);
|
return web_server->cover_json((cover::Cover *) (source), DETAIL_ALL);
|
||||||
}
|
}
|
||||||
std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
||||||
return json::build_json([this, obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
|
@@ -375,11 +375,16 @@ async def to_code(config):
|
|||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))
|
cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))
|
||||||
|
|
||||||
|
# Track if any network uses Enterprise authentication
|
||||||
|
has_eap = False
|
||||||
|
|
||||||
def add_sta(ap, network):
|
def add_sta(ap, network):
|
||||||
ip_config = network.get(CONF_MANUAL_IP, config.get(CONF_MANUAL_IP))
|
ip_config = network.get(CONF_MANUAL_IP, config.get(CONF_MANUAL_IP))
|
||||||
cg.add(var.add_sta(wifi_network(network, ap, ip_config)))
|
cg.add(var.add_sta(wifi_network(network, ap, ip_config)))
|
||||||
|
|
||||||
for network in config.get(CONF_NETWORKS, []):
|
for network in config.get(CONF_NETWORKS, []):
|
||||||
|
if CONF_EAP in network:
|
||||||
|
has_eap = True
|
||||||
cg.with_local_variable(network[CONF_ID], WiFiAP(), add_sta, network)
|
cg.with_local_variable(network[CONF_ID], WiFiAP(), add_sta, network)
|
||||||
|
|
||||||
if CONF_AP in config:
|
if CONF_AP in config:
|
||||||
@@ -396,6 +401,10 @@ async def to_code(config):
|
|||||||
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_SOFTAP_SUPPORT", False)
|
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_SOFTAP_SUPPORT", False)
|
||||||
add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False)
|
add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False)
|
||||||
|
|
||||||
|
# Disable Enterprise WiFi support if no EAP is configured
|
||||||
|
if CORE.is_esp32 and CORE.using_esp_idf and not has_eap:
|
||||||
|
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT", False)
|
||||||
|
|
||||||
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
||||||
cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE]))
|
cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE]))
|
||||||
cg.add(var.set_fast_connect(config[CONF_FAST_CONNECT]))
|
cg.add(var.set_fast_connect(config[CONF_FAST_CONNECT]))
|
||||||
|
@@ -393,10 +393,13 @@ def icon(value):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def sub_device_id(value: str | None) -> core.ID:
|
def sub_device_id(value: str | None) -> core.ID | None:
|
||||||
# Lazy import to avoid circular imports
|
# Lazy import to avoid circular imports
|
||||||
from esphome.core.config import Device
|
from esphome.core.config import Device
|
||||||
|
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
|
||||||
return use_id(Device)(value)
|
return use_id(Device)(value)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ from enum import Enum
|
|||||||
|
|
||||||
from esphome.enum import StrEnum
|
from esphome.enum import StrEnum
|
||||||
|
|
||||||
__version__ = "2025.8.0b1"
|
__version__ = "2025.9.0-dev"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
@@ -803,6 +803,10 @@ class EsphomeCore:
|
|||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Library {library} must be instance of Library, not {type(library)}"
|
f"Library {library} must be instance of Library, not {type(library)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not library.name:
|
||||||
|
raise ValueError(f"The library for {library.repository} must have a name")
|
||||||
|
|
||||||
short_name = (
|
short_name = (
|
||||||
library.name if "/" not in library.name else library.name.split("/")[-1]
|
library.name if "/" not in library.name else library.name.split("/")[-1]
|
||||||
)
|
)
|
||||||
|
@@ -475,11 +475,16 @@ bool Application::register_socket_fd(int fd) {
|
|||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#ifndef USE_ESP32
|
||||||
|
// Only check on non-ESP32 platforms
|
||||||
|
// On ESP32 (both Arduino and ESP-IDF), CONFIG_LWIP_MAX_SOCKETS is always <= FD_SETSIZE by design
|
||||||
|
// (LWIP_SOCKET_OFFSET = FD_SETSIZE - CONFIG_LWIP_MAX_SOCKETS per lwipopts.h)
|
||||||
|
// Other platforms may not have this guarantee
|
||||||
if (fd >= FD_SETSIZE) {
|
if (fd >= FD_SETSIZE) {
|
||||||
ESP_LOGE(TAG, "Cannot monitor socket fd %d: exceeds FD_SETSIZE (%d)", fd, FD_SETSIZE);
|
ESP_LOGE(TAG, "fd %d exceeds FD_SETSIZE %d", fd, FD_SETSIZE);
|
||||||
ESP_LOGE(TAG, "Socket will not be monitored for data - may cause performance issues!");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
this->socket_fds_.push_back(fd);
|
this->socket_fds_.push_back(fd);
|
||||||
this->socket_fds_changed_ = true;
|
this->socket_fds_changed_ = true;
|
||||||
|
@@ -77,8 +77,8 @@ async def setup_entity(var: MockObj, config: ConfigType, platform: str) -> None:
|
|||||||
"""
|
"""
|
||||||
# Get device info
|
# Get device info
|
||||||
device_name: str | None = None
|
device_name: str | None = None
|
||||||
if CONF_DEVICE_ID in config:
|
device_id_obj: ID | None
|
||||||
device_id_obj: ID = config[CONF_DEVICE_ID]
|
if device_id_obj := config.get(CONF_DEVICE_ID):
|
||||||
device: MockObj = await get_variable(device_id_obj)
|
device: MockObj = await get_variable(device_id_obj)
|
||||||
add(var.set_device(device))
|
add(var.set_device(device))
|
||||||
# Get device name for object ID calculation
|
# Get device name for object ID calculation
|
||||||
@@ -199,8 +199,8 @@ def entity_duplicate_validator(platform: str) -> Callable[[ConfigType], ConfigTy
|
|||||||
# Get device name if entity is on a sub-device
|
# Get device name if entity is on a sub-device
|
||||||
device_name = None
|
device_name = None
|
||||||
device_id = "" # Empty string for main device
|
device_id = "" # Empty string for main device
|
||||||
if CONF_DEVICE_ID in config:
|
device_id_obj: ID | None
|
||||||
device_id_obj = config[CONF_DEVICE_ID]
|
if device_id_obj := config.get(CONF_DEVICE_ID):
|
||||||
device_name = device_id_obj.id
|
device_name = device_id_obj.id
|
||||||
# Use the device ID string directly for uniqueness
|
# Use the device ID string directly for uniqueness
|
||||||
device_id = device_id_obj.id
|
device_id = device_id_obj.id
|
||||||
|
@@ -80,13 +80,16 @@ def replace_file_content(text, pattern, repl):
|
|||||||
return content_new, count
|
return content_new, count
|
||||||
|
|
||||||
|
|
||||||
def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool:
|
def storage_should_clean(old: StorageJSON | None, new: StorageJSON) -> bool:
|
||||||
if old is None:
|
if old is None:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if old.src_version != new.src_version:
|
if old.src_version != new.src_version:
|
||||||
return True
|
return True
|
||||||
return old.build_path != new.build_path
|
if old.build_path != new.build_path:
|
||||||
|
return True
|
||||||
|
# Check if any components have been removed
|
||||||
|
return bool(old.loaded_integrations - new.loaded_integrations)
|
||||||
|
|
||||||
|
|
||||||
def storage_should_update_cmake_cache(old: StorageJSON, new: StorageJSON) -> bool:
|
def storage_should_update_cmake_cache(old: StorageJSON, new: StorageJSON) -> bool:
|
||||||
@@ -100,7 +103,7 @@ def storage_should_update_cmake_cache(old: StorageJSON, new: StorageJSON) -> boo
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def update_storage_json():
|
def update_storage_json() -> None:
|
||||||
path = storage_path()
|
path = storage_path()
|
||||||
old = StorageJSON.load(path)
|
old = StorageJSON.load(path)
|
||||||
new = StorageJSON.from_esphome_core(CORE, old)
|
new = StorageJSON.from_esphome_core(CORE, old)
|
||||||
@@ -108,7 +111,14 @@ def update_storage_json():
|
|||||||
return
|
return
|
||||||
|
|
||||||
if storage_should_clean(old, new):
|
if storage_should_clean(old, new):
|
||||||
_LOGGER.info("Core config, version changed, cleaning build files...")
|
if old is not None and old.loaded_integrations - new.loaded_integrations:
|
||||||
|
removed = old.loaded_integrations - new.loaded_integrations
|
||||||
|
_LOGGER.info(
|
||||||
|
"Components removed (%s), cleaning build files...",
|
||||||
|
", ".join(sorted(removed)),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_LOGGER.info("Core config or version changed, cleaning build files...")
|
||||||
clean_build()
|
clean_build()
|
||||||
elif storage_should_update_cmake_cache(old, new):
|
elif storage_should_update_cmake_cache(old, new):
|
||||||
_LOGGER.info("Integrations changed, cleaning cmake cache...")
|
_LOGGER.info("Integrations changed, cleaning cmake cache...")
|
||||||
|
@@ -11,8 +11,8 @@ pyserial==3.5
|
|||||||
platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
||||||
esptool==5.0.2
|
esptool==5.0.2
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
esphome-dashboard==20250514.0
|
esphome-dashboard==20250814.0
|
||||||
aioesphomeapi==38.2.1
|
aioesphomeapi==39.0.0
|
||||||
zeroconf==0.147.0
|
zeroconf==0.147.0
|
||||||
puremagic==1.30
|
puremagic==1.30
|
||||||
ruamel.yaml==0.18.14 # dashboard_import
|
ruamel.yaml==0.18.14 # dashboard_import
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
pylint==3.3.8
|
pylint==3.3.8
|
||||||
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
|
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
|
||||||
ruff==0.12.8 # also change in .pre-commit-config.yaml when updating
|
ruff==0.12.9 # also change in .pre-commit-config.yaml when updating
|
||||||
pyupgrade==3.20.0 # also change in .pre-commit-config.yaml when updating
|
pyupgrade==3.20.0 # also change in .pre-commit-config.yaml when updating
|
||||||
pre-commit
|
pre-commit
|
||||||
|
|
||||||
|
@@ -55,6 +55,12 @@ sensor:
|
|||||||
lambda: return 4.0;
|
lambda: return 4.0;
|
||||||
update_interval: 0.1s
|
update_interval: 0.1s
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: Living Room Sensor
|
||||||
|
device_id: ""
|
||||||
|
lambda: return 5.0;
|
||||||
|
update_interval: 0.1s
|
||||||
|
|
||||||
# Switches with the same name on different devices to test device_id lookup
|
# Switches with the same name on different devices to test device_id lookup
|
||||||
switch:
|
switch:
|
||||||
# Switch with no device_id (defaults to 0)
|
# Switch with no device_id (defaults to 0)
|
||||||
@@ -96,3 +102,23 @@ switch:
|
|||||||
- logger.log: "Turning on Test Switch on Motion Detector"
|
- logger.log: "Turning on Test Switch on Motion Detector"
|
||||||
turn_off_action:
|
turn_off_action:
|
||||||
- logger.log: "Turning off Test Switch on Motion Detector"
|
- logger.log: "Turning off Test Switch on Motion Detector"
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: Living Room Blank Switch
|
||||||
|
device_id: ""
|
||||||
|
id: test_switch_blank_living_room
|
||||||
|
optimistic: true
|
||||||
|
turn_on_action:
|
||||||
|
- logger.log: "Turning on Living Room Blank Switch"
|
||||||
|
turn_off_action:
|
||||||
|
- logger.log: "Turning off Living Room Blank Switch"
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: Living Room None Switch
|
||||||
|
device_id:
|
||||||
|
id: test_switch_none_living_room
|
||||||
|
optimistic: true
|
||||||
|
turn_on_action:
|
||||||
|
- logger.log: "Turning on Living Room None Switch"
|
||||||
|
turn_off_action:
|
||||||
|
- logger.log: "Turning off Living Room None Switch"
|
||||||
|
@@ -132,6 +132,7 @@ async def test_areas_and_devices(
|
|||||||
"Temperature Sensor Reading": temp_sensor.device_id,
|
"Temperature Sensor Reading": temp_sensor.device_id,
|
||||||
"Motion Detector Status": motion_detector.device_id,
|
"Motion Detector Status": motion_detector.device_id,
|
||||||
"Smart Switch Power": smart_switch.device_id,
|
"Smart Switch Power": smart_switch.device_id,
|
||||||
|
"Living Room Sensor": 0, # Main device
|
||||||
}
|
}
|
||||||
|
|
||||||
for entity in sensor_entities:
|
for entity in sensor_entities:
|
||||||
@@ -160,6 +161,18 @@ async def test_areas_and_devices(
|
|||||||
"Should have a switch with device_id 0 (main device)"
|
"Should have a switch with device_id 0 (main device)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Verify extra switches with blank and none device_id are correctly available
|
||||||
|
extra_switches = [
|
||||||
|
e for e in switch_entities if e.name.startswith("Living Room")
|
||||||
|
]
|
||||||
|
assert len(extra_switches) == 2, (
|
||||||
|
f"Expected 2 extra switches for Living Room, got {len(extra_switches)}"
|
||||||
|
)
|
||||||
|
extra_switch_device_ids = [e.device_id for e in extra_switches]
|
||||||
|
assert all(d == 0 for d in extra_switch_device_ids), (
|
||||||
|
"All extra switches should have device_id 0 (main device)"
|
||||||
|
)
|
||||||
|
|
||||||
# Wait for initial states to be received for all switches
|
# Wait for initial states to be received for all switches
|
||||||
await asyncio.wait_for(initial_states_future, timeout=2.0)
|
await asyncio.wait_for(initial_states_future, timeout=2.0)
|
||||||
|
|
||||||
|
@@ -689,3 +689,19 @@ def test_entity_duplicate_validator_internal_entities() -> None:
|
|||||||
Invalid, match=r"Duplicate sensor entity with name 'Temperature' found"
|
Invalid, match=r"Duplicate sensor entity with name 'Temperature' found"
|
||||||
):
|
):
|
||||||
validator(config4)
|
validator(config4)
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_or_null_device_id_on_entity() -> None:
|
||||||
|
"""Test that empty or null device IDs are handled correctly."""
|
||||||
|
# Create validator for sensor platform
|
||||||
|
validator = entity_duplicate_validator("sensor")
|
||||||
|
|
||||||
|
# Entity with empty device_id should pass
|
||||||
|
config1 = {CONF_NAME: "Battery", CONF_DEVICE_ID: ""}
|
||||||
|
validated1 = validator(config1)
|
||||||
|
assert validated1 == config1
|
||||||
|
|
||||||
|
# Entity with None device_id should pass
|
||||||
|
config2 = {CONF_NAME: "Temperature", CONF_DEVICE_ID: None}
|
||||||
|
validated2 = validator(config2)
|
||||||
|
assert validated2 == config2
|
||||||
|
220
tests/unit_tests/test_writer.py
Normal file
220
tests/unit_tests/test_writer.py
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
"""Test writer module functionality."""
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from esphome.storage_json import StorageJSON
|
||||||
|
from esphome.writer import storage_should_clean, update_storage_json
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def create_storage() -> Callable[..., StorageJSON]:
|
||||||
|
"""Factory fixture to create StorageJSON instances."""
|
||||||
|
|
||||||
|
def _create(
|
||||||
|
loaded_integrations: list[str] | None = None, **kwargs: Any
|
||||||
|
) -> StorageJSON:
|
||||||
|
return StorageJSON(
|
||||||
|
storage_version=kwargs.get("storage_version", 1),
|
||||||
|
name=kwargs.get("name", "test"),
|
||||||
|
friendly_name=kwargs.get("friendly_name", "Test Device"),
|
||||||
|
comment=kwargs.get("comment"),
|
||||||
|
esphome_version=kwargs.get("esphome_version", "2025.1.0"),
|
||||||
|
src_version=kwargs.get("src_version", 1),
|
||||||
|
address=kwargs.get("address", "test.local"),
|
||||||
|
web_port=kwargs.get("web_port", 80),
|
||||||
|
target_platform=kwargs.get("target_platform", "ESP32"),
|
||||||
|
build_path=kwargs.get("build_path", "/build"),
|
||||||
|
firmware_bin_path=kwargs.get("firmware_bin_path", "/firmware.bin"),
|
||||||
|
loaded_integrations=set(loaded_integrations or []),
|
||||||
|
loaded_platforms=kwargs.get("loaded_platforms", set()),
|
||||||
|
no_mdns=kwargs.get("no_mdns", False),
|
||||||
|
framework=kwargs.get("framework", "arduino"),
|
||||||
|
core_platform=kwargs.get("core_platform", "esp32"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return _create
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_should_clean_when_old_is_none(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test that clean is triggered when old storage is None."""
|
||||||
|
new = create_storage(loaded_integrations=["api", "wifi"])
|
||||||
|
assert storage_should_clean(None, new) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_should_clean_when_src_version_changes(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test that clean is triggered when src_version changes."""
|
||||||
|
old = create_storage(loaded_integrations=["api", "wifi"], src_version=1)
|
||||||
|
new = create_storage(loaded_integrations=["api", "wifi"], src_version=2)
|
||||||
|
assert storage_should_clean(old, new) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_should_clean_when_build_path_changes(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test that clean is triggered when build_path changes."""
|
||||||
|
old = create_storage(loaded_integrations=["api", "wifi"], build_path="/build1")
|
||||||
|
new = create_storage(loaded_integrations=["api", "wifi"], build_path="/build2")
|
||||||
|
assert storage_should_clean(old, new) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_should_clean_when_component_removed(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test that clean is triggered when a component is removed."""
|
||||||
|
old = create_storage(
|
||||||
|
loaded_integrations=["api", "wifi", "bluetooth_proxy", "esp32_ble_tracker"]
|
||||||
|
)
|
||||||
|
new = create_storage(loaded_integrations=["api", "wifi", "esp32_ble_tracker"])
|
||||||
|
assert storage_should_clean(old, new) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_should_clean_when_multiple_components_removed(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test that clean is triggered when multiple components are removed."""
|
||||||
|
old = create_storage(
|
||||||
|
loaded_integrations=["api", "wifi", "ota", "web_server", "logger"]
|
||||||
|
)
|
||||||
|
new = create_storage(loaded_integrations=["api", "wifi", "logger"])
|
||||||
|
assert storage_should_clean(old, new) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_should_not_clean_when_nothing_changes(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test that clean is not triggered when nothing changes."""
|
||||||
|
old = create_storage(loaded_integrations=["api", "wifi", "logger"])
|
||||||
|
new = create_storage(loaded_integrations=["api", "wifi", "logger"])
|
||||||
|
assert storage_should_clean(old, new) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_should_not_clean_when_component_added(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test that clean is not triggered when a component is only added."""
|
||||||
|
old = create_storage(loaded_integrations=["api", "wifi"])
|
||||||
|
new = create_storage(loaded_integrations=["api", "wifi", "ota"])
|
||||||
|
assert storage_should_clean(old, new) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_should_not_clean_when_other_fields_change(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test that clean is not triggered when non-relevant fields change."""
|
||||||
|
old = create_storage(
|
||||||
|
loaded_integrations=["api", "wifi"],
|
||||||
|
friendly_name="Old Name",
|
||||||
|
esphome_version="2024.12.0",
|
||||||
|
)
|
||||||
|
new = create_storage(
|
||||||
|
loaded_integrations=["api", "wifi"],
|
||||||
|
friendly_name="New Name",
|
||||||
|
esphome_version="2025.1.0",
|
||||||
|
)
|
||||||
|
assert storage_should_clean(old, new) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_edge_case_empty_integrations(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test edge case when old has integrations but new has none."""
|
||||||
|
old = create_storage(loaded_integrations=["api", "wifi"])
|
||||||
|
new = create_storage(loaded_integrations=[])
|
||||||
|
assert storage_should_clean(old, new) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_edge_case_from_empty_integrations(
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
) -> None:
|
||||||
|
"""Test edge case when old has no integrations but new has some."""
|
||||||
|
old = create_storage(loaded_integrations=[])
|
||||||
|
new = create_storage(loaded_integrations=["api", "wifi"])
|
||||||
|
assert storage_should_clean(old, new) is False
|
||||||
|
|
||||||
|
|
||||||
|
@patch("esphome.writer.clean_build")
|
||||||
|
@patch("esphome.writer.StorageJSON")
|
||||||
|
@patch("esphome.writer.storage_path")
|
||||||
|
@patch("esphome.writer.CORE")
|
||||||
|
def test_update_storage_json_logging_when_old_is_none(
|
||||||
|
mock_core: MagicMock,
|
||||||
|
mock_storage_path: MagicMock,
|
||||||
|
mock_storage_json_class: MagicMock,
|
||||||
|
mock_clean_build: MagicMock,
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test that update_storage_json doesn't crash when old storage is None.
|
||||||
|
|
||||||
|
This is a regression test for the AttributeError that occurred when
|
||||||
|
old was None and we tried to access old.loaded_integrations.
|
||||||
|
"""
|
||||||
|
# Setup mocks
|
||||||
|
mock_storage_path.return_value = "/test/path"
|
||||||
|
mock_storage_json_class.load.return_value = None # Old storage is None
|
||||||
|
|
||||||
|
new_storage = create_storage(loaded_integrations=["api", "wifi"])
|
||||||
|
new_storage.save = MagicMock() # Mock the save method
|
||||||
|
mock_storage_json_class.from_esphome_core.return_value = new_storage
|
||||||
|
|
||||||
|
# Call the function - should not raise AttributeError
|
||||||
|
with caplog.at_level("INFO"):
|
||||||
|
update_storage_json()
|
||||||
|
|
||||||
|
# Verify clean_build was called
|
||||||
|
mock_clean_build.assert_called_once()
|
||||||
|
|
||||||
|
# Verify the correct log message was used (not the component removal message)
|
||||||
|
assert "Core config or version changed, cleaning build files..." in caplog.text
|
||||||
|
assert "Components removed" not in caplog.text
|
||||||
|
|
||||||
|
# Verify save was called
|
||||||
|
new_storage.save.assert_called_once_with("/test/path")
|
||||||
|
|
||||||
|
|
||||||
|
@patch("esphome.writer.clean_build")
|
||||||
|
@patch("esphome.writer.StorageJSON")
|
||||||
|
@patch("esphome.writer.storage_path")
|
||||||
|
@patch("esphome.writer.CORE")
|
||||||
|
def test_update_storage_json_logging_components_removed(
|
||||||
|
mock_core: MagicMock,
|
||||||
|
mock_storage_path: MagicMock,
|
||||||
|
mock_storage_json_class: MagicMock,
|
||||||
|
mock_clean_build: MagicMock,
|
||||||
|
create_storage: Callable[..., StorageJSON],
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
) -> None:
|
||||||
|
"""Test that update_storage_json logs removed components correctly."""
|
||||||
|
# Setup mocks
|
||||||
|
mock_storage_path.return_value = "/test/path"
|
||||||
|
|
||||||
|
old_storage = create_storage(loaded_integrations=["api", "wifi", "bluetooth_proxy"])
|
||||||
|
new_storage = create_storage(loaded_integrations=["api", "wifi"])
|
||||||
|
new_storage.save = MagicMock() # Mock the save method
|
||||||
|
|
||||||
|
mock_storage_json_class.load.return_value = old_storage
|
||||||
|
mock_storage_json_class.from_esphome_core.return_value = new_storage
|
||||||
|
|
||||||
|
# Call the function
|
||||||
|
with caplog.at_level("INFO"):
|
||||||
|
update_storage_json()
|
||||||
|
|
||||||
|
# Verify clean_build was called
|
||||||
|
mock_clean_build.assert_called_once()
|
||||||
|
|
||||||
|
# Verify the correct log message was used with component names
|
||||||
|
assert (
|
||||||
|
"Components removed (bluetooth_proxy), cleaning build files..." in caplog.text
|
||||||
|
)
|
||||||
|
assert "Core config or version changed" not in caplog.text
|
||||||
|
|
||||||
|
# Verify save was called
|
||||||
|
new_storage.save.assert_called_once_with("/test/path")
|
Reference in New Issue
Block a user