1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-14 05:45:48 +00:00

Compare commits

..

19 Commits

Author SHA1 Message Date
Jesse Hills
c5b2a9e24b Merge pull request #10558 from esphome/bump-2025.8.3
2025.8.3
2025-09-04 22:09:37 +12:00
Jesse Hills
2d3cdf60ba Bump version to 2025.8.3 2025-09-04 09:06:00 +12:00
J. Nick Koston
a29fef166b [api] Fix VERY_VERBOSE logging compilation error with bool arrays (#10539) 2025-09-04 09:06:00 +12:00
Jonathan Swoboda
9fe94f1201 [esp32] Clear IDF environment variables (#10527)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-09-04 09:06:00 +12:00
Anton Viktorov
1b8978a89a [i2c] Fix bug write_register16 (#10547) 2025-09-04 09:06:00 +12:00
Jonathan Swoboda
6f188d1284 [esp32] Rebuild when idf_component.yml changes (#10540) 2025-09-04 09:06:00 +12:00
Clyde Stubbs
a1a336783e [mcp4461] Fix read transaction (#10465)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-09-04 09:06:00 +12:00
Clyde Stubbs
c55bc93f70 [mipi_dsi] Fix config for Guition screen (#10464) 2025-09-04 09:06:00 +12:00
J. Nick Koston
de998f2f39 Fix incorrect entity count due to undefined execution order with globals (#10497) 2025-09-04 09:06:00 +12:00
Oliver Kleinecke
950299e52b Update mcp4461.cpp (#10479) 2025-09-04 09:06:00 +12:00
Jesse Hills
6a20e6f9ad Merge pull request #10485 from esphome/bump-2025.8.2
2025.8.2
2025-08-29 12:38:45 +12:00
Jesse Hills
07875a8b1e Bump version to 2025.8.2 2025-08-29 10:16:19 +12:00
J. Nick Koston
ba4789970c [esphome] Fix OTA watchdog resets by validating all magic bytes before blocking (#10401) 2025-08-29 10:16:19 +12:00
Vinicius Fortuna
015977cfdf [rtttl] Fix RTTTL for speakers (#10381) 2025-08-29 10:16:19 +12:00
J. Nick Koston
e513c0f004 Fix AttributeError when uploading OTA to offline OpenThread devices (#10459) 2025-08-29 10:16:19 +12:00
Clyde Stubbs
a11970aee0 [wifi] Fix retry with hidden networks. (#10445) 2025-08-29 10:16:19 +12:00
Clyde Stubbs
4ab37b069b [i2c] Perform register reads as single transactions (#10389)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-08-29 10:16:08 +12:00
Clyde Stubbs
b6bb6699d1 [mipi_spi] Fix dimensions (#10443) 2025-08-29 10:15:30 +12:00
J. Nick Koston
078eaff9a8 [wifi] Fix reconnection failures after adapter restart by not clearing netif pointers (#10458) 2025-08-29 10:15:30 +12:00
147 changed files with 1025 additions and 2238 deletions

View File

@@ -9,7 +9,7 @@ This document provides essential context for AI models interacting with this pro
## 2. Core Technologies & Stack
* **Languages:** Python (>=3.11), C++ (gnu++20)
* **Languages:** Python (>=3.10), C++ (gnu++20)
* **Frameworks & Runtimes:** PlatformIO, Arduino, ESP-IDF.
* **Build Systems:** PlatformIO is the primary build system. CMake is used as an alternative.
* **Configuration:** YAML.
@@ -38,7 +38,7 @@ This document provides essential context for AI models interacting with this pro
5. **Dashboard** (`esphome/dashboard/`): A web-based interface for device configuration, management, and OTA updates.
* **Platform Support:**
1. **ESP32** (`components/esp32/`): Espressif ESP32 family. Supports multiple variants (Original, C2, C3, C5, C6, H2, P4, S2, S3) with ESP-IDF framework. Arduino framework supports only a subset of the variants (Original, C3, S2, S3).
1. **ESP32** (`components/esp32/`): Espressif ESP32 family. Supports multiple variants (S2, S3, C3, etc.) and both IDF and Arduino frameworks.
2. **ESP8266** (`components/esp8266/`): Espressif ESP8266. Arduino framework only, with memory constraints.
3. **RP2040** (`components/rp2040/`): Raspberry Pi Pico/RP2040. Arduino framework with PIO (Programmable I/O) support.
4. **LibreTiny** (`components/libretiny/`): Realtek and Beken chips. Supports multiple chip families and auto-generated components.
@@ -60,7 +60,7 @@ This document provides essential context for AI models interacting with this pro
├── __init__.py # Component configuration schema and code generation
├── [component].h # C++ header file (if needed)
├── [component].cpp # C++ implementation (if needed)
└── [platform]/ # Platform-specific implementations
└── [platform]/ # Platform-specific implementations
├── __init__.py # Platform-specific configuration
├── [platform].h # Platform C++ header
└── [platform].cpp # Platform C++ implementation
@@ -150,8 +150,7 @@ This document provides essential context for AI models interacting with this pro
* **Configuration Validation:**
* **Common Validators:** `cv.int_`, `cv.float_`, `cv.string`, `cv.boolean`, `cv.int_range(min=0, max=100)`, `cv.positive_int`, `cv.percentage`.
* **Complex Validation:** `cv.All(cv.string, cv.Length(min=1, max=50))`, `cv.Any(cv.int_, cv.string)`.
* **Platform-Specific:** `cv.only_on(["esp32", "esp8266"])`, `esp32.only_on_variant(...)`, `cv.only_on_esp32`, `cv.only_on_esp8266`, `cv.only_on_rp2040`.
* **Framework-Specific:** `cv.only_with_framework(...)`, `cv.only_with_arduino`, `cv.only_with_esp_idf`.
* **Platform-Specific:** `cv.only_on(["esp32", "esp8266"])`, `cv.only_with_arduino`.
* **Schema Extensions:**
```python
CONFIG_SCHEMA = cv.Schema({ ... })

View File

@@ -1 +1 @@
4368db58e8f884aff245996b1e8b644cc0796c0bb2fa706d5740d40b823d3ac9
6af8b429b94191fe8e239fcb3b73f7982d0266cb5b05ffbc81edaeac1bc8c273

View File

@@ -105,9 +105,7 @@ jobs:
// Calculate data from PR files
const changedFiles = prFiles.map(file => file.filename);
const totalAdditions = prFiles.reduce((sum, file) => sum + (file.additions || 0), 0);
const totalDeletions = prFiles.reduce((sum, file) => sum + (file.deletions || 0), 0);
const totalChanges = totalAdditions + totalDeletions;
const totalChanges = prFiles.reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0);
console.log('Current labels:', currentLabels.join(', '));
console.log('Changed files:', changedFiles.length);
@@ -233,21 +231,16 @@ jobs:
// Strategy: PR size detection
async function detectPRSize() {
const labels = new Set();
const testChanges = prFiles
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0);
const nonTestChanges = totalChanges - testChanges;
if (totalChanges <= SMALL_PR_THRESHOLD) {
labels.add('small-pr');
return labels;
}
const testAdditions = prFiles
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.additions || 0), 0);
const testDeletions = prFiles
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.deletions || 0), 0);
const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions);
// Don't add too-big if mega-pr label is already present
if (nonTestChanges > TOO_BIG_THRESHOLD && !isMegaPR) {
labels.add('too-big');
@@ -382,7 +375,7 @@ jobs:
const labels = new Set();
// Check for missing tests
if ((allLabels.has('new-component') || allLabels.has('new-platform') || allLabels.has('new-feature')) && !allLabels.has('has-tests')) {
if ((allLabels.has('new-component') || allLabels.has('new-platform')) && !allLabels.has('has-tests')) {
labels.add('needs-tests');
}
@@ -419,13 +412,10 @@ jobs:
// Too big message
if (finalLabels.includes('too-big')) {
const testAdditions = prFiles
const testChanges = prFiles
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.additions || 0), 0);
const testDeletions = prFiles
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.deletions || 0), 0);
const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions);
.reduce((sum, file) => sum + (file.additions || 0) + (file.deletions || 0), 0);
const nonTestChanges = totalChanges - testChanges;
const tooManyLabels = finalLabels.length > MAX_LABELS;
const tooManyChanges = nonTestChanges > TOO_BIG_THRESHOLD;

View File

@@ -156,7 +156,7 @@ jobs:
. venv/bin/activate
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5.5.0
uses: codecov/codecov-action@v5.4.3
with:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Save Python virtual environment cache

24
.github/workflows/needs-docs.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Needs Docs
on:
pull_request:
types: [labeled, unlabeled]
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- name: Check for needs-docs label
uses: actions/github-script@v7.0.1
with:
script: |
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const needsDocs = labels.find(label => label.name === 'needs-docs');
if (needsDocs) {
core.setFailed('Pull request needs docs');
}

View File

@@ -1,30 +0,0 @@
name: Status check labels
on:
pull_request:
types: [labeled, unlabeled]
jobs:
check:
name: Check ${{ matrix.label }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
label:
- needs-docs
- merge-after-release
steps:
- name: Check for ${{ matrix.label }} label
uses: actions/github-script@v7.0.1
with:
script: |
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const hasLabel = labels.find(label => label.name === '${{ matrix.label }}');
if (hasLabel) {
core.setFailed('Pull request cannot be merged, it is labeled as ${{ matrix.label }}');
}

View File

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

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.9.0-dev
PROJECT_NUMBER = 2025.8.3
# 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

@@ -89,7 +89,7 @@ void AGS10Component::dump_config() {
bool AGS10Component::new_i2c_address(uint8_t newaddress) {
uint8_t rev_newaddress = ~newaddress;
std::array<uint8_t, 5> data{newaddress, rev_newaddress, newaddress, rev_newaddress, 0};
data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
data[4] = calc_crc8_(data, 4);
if (!this->write_bytes(REG_ADDRESS, data)) {
this->error_code_ = COMMUNICATION_FAILED;
this->status_set_warning();
@@ -109,7 +109,7 @@ bool AGS10Component::set_zero_point_with_current_resistance() { return this->set
bool AGS10Component::set_zero_point_with(uint16_t value) {
std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0};
data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
data[4] = calc_crc8_(data, 4);
if (!this->write_bytes(REG_CALIBRATION, data)) {
this->error_code_ = COMMUNICATION_FAILED;
this->status_set_warning();
@@ -184,7 +184,7 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
auto res = *data;
auto crc_byte = res[len];
if (crc_byte != crc8(res.data(), len, 0xFF, 0x31, true)) {
if (crc_byte != calc_crc8_(res, len)) {
this->error_code_ = CRC_CHECK_FAILED;
ESP_LOGE(TAG, "Reading AGS10 version failed: crc error!");
return optional<std::array<uint8_t, N>>();
@@ -192,5 +192,20 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
return data;
}
template<size_t N> uint8_t AGS10Component::calc_crc8_(std::array<uint8_t, N> dat, uint8_t num) {
uint8_t i, byte1, crc = 0xFF;
for (byte1 = 0; byte1 < num; byte1++) {
crc ^= (dat[byte1]);
for (i = 0; i < 8; i++) {
if (crc & 0x80) {
crc = (crc << 1) ^ 0x31;
} else {
crc = (crc << 1);
}
}
}
return crc;
}
} // namespace ags10
} // namespace esphome

View File

@@ -1,9 +1,9 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace ags10 {
@@ -99,6 +99,16 @@ class AGS10Component : public PollingComponent, public i2c::I2CDevice {
* Read, checks and returns data from the sensor.
*/
template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
/**
* Calculates CRC8 value.
*
* CRC8 calculation, initial value: 0xFF, polynomial: 0x31 (x8+ x5+ x4+1)
*
* @param[in] dat the data buffer
* @param num number of bytes in the buffer
*/
template<size_t N> uint8_t calc_crc8_(std::array<uint8_t, N> dat, uint8_t num);
};
template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> {

View File

@@ -18,6 +18,6 @@ CONFIG_SCHEMA = cv.Schema(
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
async def to_code(config):
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await esp32_ble_tracker.register_ble_device(var, config)
yield esp32_ble_tracker.register_ble_device(var, config)

View File

@@ -29,6 +29,22 @@ namespace am2315c {
static const char *const TAG = "am2315c";
uint8_t AM2315C::crc8_(uint8_t *data, uint8_t len) {
uint8_t crc = 0xFF;
while (len--) {
crc ^= *data++;
for (uint8_t i = 0; i < 8; i++) {
if (crc & 0x80) {
crc <<= 1;
crc ^= 0x31;
} else {
crc <<= 1;
}
}
}
return crc;
}
bool AM2315C::reset_register_(uint8_t reg) {
// code based on demo code sent by www.aosong.com
// no further documentation.
@@ -70,7 +86,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
humidity = raw * 9.5367431640625e-5;
raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
temperature = raw * 1.9073486328125e-4 - 50;
return crc8(data, 6, 0xFF, 0x31, true) == data[6];
return this->crc8_(data, 6) == data[6];
}
void AM2315C::setup() {

View File

@@ -21,9 +21,9 @@
// SOFTWARE.
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace am2315c {
@@ -39,6 +39,7 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
protected:
uint8_t crc8_(uint8_t *data, uint8_t len);
bool convert_(uint8_t *data, float &humidity, float &temperature);
bool reset_register_(uint8_t reg);

View File

@@ -289,26 +289,16 @@ uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint8_t mess
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)
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
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);
}
size_t size_before_encode = shared_buf.size();
// Encode directly into buffer
size_t size_before_encode = shared_buf.size();
msg.encode({&shared_buf});
msg.encode(buffer);
// Calculate actual encoded size (not including header that was already added)
size_t actual_payload_size = shared_buf.size() - size_before_encode;
@@ -1626,6 +1616,14 @@ bool APIConnection::schedule_batch_() {
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_() {
// Ensure PacketInfo remains trivially destructible for our placement new approach
static_assert(std::is_trivially_destructible<PacketInfo>::value,
@@ -1733,7 +1731,7 @@ void APIConnection::process_batch_() {
}
remaining_size -= payload_size;
// Calculate where the next message's header padding will start
// Current buffer size + footer space for this message
// Current buffer size + footer space (that prepare_message_buffer will add for this message)
current_offset = shared_buf.size() + footer_size;
}

View File

@@ -44,7 +44,7 @@ static constexpr size_t MAX_PACKETS_PER_BATCH = 64; // ESP32 has 8KB+ stack, HO
static constexpr size_t MAX_PACKETS_PER_BATCH = 32; // ESP8266/RP2040/etc have smaller stacks
#endif
class APIConnection final : public APIServerConnection {
class APIConnection : public APIServerConnection {
public:
friend class APIServer;
friend class ListEntitiesIterator;
@@ -252,21 +252,44 @@ class APIConnection final : public APIServerConnection {
// Get header padding size - used for both reserve and insert
uint8_t header_padding = this->helper_->frame_header_padding();
// Get shared buffer from parent server
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();
// Reserve space for header padding + message + footer
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
shared_buf.reserve(total_size);
shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
// Resize to add header padding so message encoding starts at the correct position
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);
@@ -274,6 +297,10 @@ class APIConnection final : public APIServerConnection {
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:
// Helper function to handle authentication completion
void complete_authentication_();
@@ -301,17 +328,9 @@ class APIConnection final : public APIServerConnection {
APIConnection *conn, uint32_t remaining_size, bool is_single) {
// Set common fields that are shared by all entity types
msg.key = entity->get_object_id_hash();
// Try to use static reference first to avoid allocation
StringRef static_ref = entity->get_object_id_ref_for_api_();
// Store dynamic string outside the if-else to maintain lifetime
std::string object_id;
if (!static_ref.empty()) {
msg.set_object_id(static_ref);
} else {
// Dynamic case - need to allocate
object_id = entity->get_object_id();
msg.set_object_id(StringRef(object_id));
}
// IMPORTANT: get_object_id() may return a temporary std::string
std::string object_id = entity->get_object_id();
msg.set_object_id(StringRef(object_id));
if (entity->has_own_name()) {
msg.set_name(entity->get_name());

View File

@@ -104,9 +104,9 @@ class APIFrameHelper {
// The buffer contains all messages with appropriate padding before each
virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) = 0;
// Get the frame header padding required by this protocol
uint8_t frame_header_padding() const { return frame_header_padding_; }
virtual uint8_t frame_header_padding() = 0;
// Get the frame footer size required by this protocol
uint8_t frame_footer_size() const { return frame_footer_size_; }
virtual uint8_t frame_footer_size() = 0;
// Check if socket has data ready to read
bool is_socket_ready() const { return socket_ != nullptr && socket_->ready(); }

View File

@@ -7,7 +7,7 @@
namespace esphome::api {
class APINoiseFrameHelper final : public APIFrameHelper {
class APINoiseFrameHelper : public APIFrameHelper {
public:
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx,
const ClientInfo *client_info)
@@ -25,6 +25,10 @@ class APINoiseFrameHelper final : public APIFrameHelper {
APIError read_packet(ReadPacketBuffer *buffer) override;
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
// Get the frame header padding required by this protocol
uint8_t frame_header_padding() override { return frame_header_padding_; }
// Get the frame footer size required by this protocol
uint8_t frame_footer_size() override { return frame_footer_size_; }
protected:
APIError state_action_();

View File

@@ -5,7 +5,7 @@
namespace esphome::api {
class APIPlaintextFrameHelper final : public APIFrameHelper {
class APIPlaintextFrameHelper : public APIFrameHelper {
public:
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket, const ClientInfo *client_info)
: APIFrameHelper(std::move(socket), client_info) {
@@ -22,6 +22,9 @@ class APIPlaintextFrameHelper final : public APIFrameHelper {
APIError read_packet(ReadPacketBuffer *buffer) override;
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
uint8_t frame_header_padding() override { return frame_header_padding_; }
// Get the frame footer size required by this protocol
uint8_t frame_footer_size() override { return frame_footer_size_; }
protected:
APIError try_read_frame_(std::vector<uint8_t> *frame);

File diff suppressed because it is too large Load Diff

View File

@@ -1135,7 +1135,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
dump_field(out, "string_", this->string_);
dump_field(out, "int_", this->int_);
for (const auto it : this->bool_array) {
dump_field(out, "bool_array", it, 4);
dump_field(out, "bool_array", static_cast<bool>(it), 4);
}
for (const auto &it : this->int_array) {
dump_field(out, "int_array", it, 4);

View File

@@ -8,70 +8,74 @@ namespace esphome::api {
static const char *const TAG = "api.proto";
void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
const uint8_t *ptr = buffer;
const uint8_t *end = buffer + length;
while (ptr < end) {
uint32_t i = 0;
bool error = false;
while (i < length) {
uint32_t consumed;
// Parse field header
auto res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid field start at offset %ld", (long) (ptr - buffer));
return;
ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i);
break;
}
uint32_t tag = res->as_uint32();
uint32_t field_type = tag & 0b111;
uint32_t field_id = tag >> 3;
ptr += consumed;
uint32_t field_type = (res->as_uint32()) & 0b111;
uint32_t field_id = (res->as_uint32()) >> 3;
i += consumed;
switch (field_type) {
case 0: { // VarInt
res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid VarInt at offset %ld", (long) (ptr - buffer));
return;
ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i);
error = true;
break;
}
if (!this->decode_varint(field_id, *res)) {
ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
}
ptr += consumed;
i += consumed;
break;
}
case 2: { // Length-delimited
res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid Length Delimited at offset %ld", (long) (ptr - buffer));
return;
ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i);
error = true;
break;
}
uint32_t field_length = res->as_uint32();
ptr += consumed;
if (ptr + field_length > end) {
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at offset %ld", (long) (ptr - buffer));
return;
i += consumed;
if (field_length > length - i) {
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i);
error = true;
break;
}
if (!this->decode_length(field_id, ProtoLengthDelimited(ptr, field_length))) {
if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id);
}
ptr += field_length;
i += field_length;
break;
}
case 5: { // 32-bit
if (ptr + 4 > end) {
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer));
return;
if (length - i < 4) {
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %" PRIu32, i);
error = true;
break;
}
uint32_t val = encode_uint32(ptr[3], ptr[2], ptr[1], ptr[0]);
uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
if (!this->decode_32bit(field_id, Proto32Bit(val))) {
ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val);
}
ptr += 4;
i += 4;
break;
}
default:
ESP_LOGV(TAG, "Invalid field type %u at offset %ld", field_type, (long) (ptr - buffer));
return;
ESP_LOGV(TAG, "Invalid field type at %" PRIu32, i);
error = true;
break;
}
if (error) {
break;
}
}
}

View File

@@ -7,19 +7,6 @@ namespace binary_sensor {
static const char *const TAG = "binary_sensor";
// Function implementation of LOG_BINARY_SENSOR macro to reduce code size
void log_binary_sensor(const char *tag, const char *prefix, const char *type, BinarySensor *obj) {
if (obj == nullptr) {
return;
}
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
if (!obj->get_device_class().empty()) {
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str());
}
}
void BinarySensor::publish_state(bool new_state) {
if (this->filter_list_ == nullptr) {
this->send_state_internal(new_state);

View File

@@ -10,10 +10,13 @@ namespace esphome {
namespace binary_sensor {
class BinarySensor;
void log_binary_sensor(const char *tag, const char *prefix, const char *type, BinarySensor *obj);
#define LOG_BINARY_SENSOR(prefix, type, obj) log_binary_sensor(TAG, prefix, LOG_STR_LITERAL(type), obj)
#define LOG_BINARY_SENSOR(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
} \
}
#define SUB_BINARY_SENSOR(name) \
protected: \

View File

@@ -27,7 +27,7 @@ CONFIG_SCHEMA = cv.All(
)
async def to_code(config):
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
@@ -63,6 +63,6 @@ async def to_code(config):
)
cg.add(var.set_char_uuid128(uuid128))
cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE]))
await output.register_output(var, config)
await ble_client.register_ble_node(var, config)
await cg.register_component(var, config)
yield output.register_output(var, config)
yield ble_client.register_ble_node(var, config)
yield cg.register_component(var, config)

View File

@@ -8,7 +8,7 @@ namespace esphome::bluetooth_proxy {
class BluetoothProxy;
class BluetoothConnection final : public esp32_ble_client::BLEClientBase {
class BluetoothConnection : public esp32_ble_client::BLEClientBase {
public:
void dump_config() override;
void loop() override;

View File

@@ -50,7 +50,7 @@ enum BluetoothProxySubscriptionFlag : uint32_t {
SUBSCRIPTION_RAW_ADVERTISEMENTS = 1 << 0,
};
class BluetoothProxy final : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
friend class BluetoothConnection; // Allow connection to update connections_free_response_
public:
BluetoothProxy();

View File

@@ -6,19 +6,6 @@ namespace button {
static const char *const TAG = "button";
// Function implementation of LOG_BUTTON macro to reduce code size
void log_button(const char *tag, const char *prefix, const char *type, Button *obj) {
if (obj == nullptr) {
return;
}
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
if (!obj->get_icon().empty()) {
ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon().c_str());
}
}
void Button::press() {
ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
this->press_action();

View File

@@ -7,10 +7,13 @@
namespace esphome {
namespace button {
class Button;
void log_button(const char *tag, const char *prefix, const char *type, Button *obj);
#define LOG_BUTTON(prefix, type, obj) log_button(TAG, prefix, LOG_STR_LITERAL(type), obj)
#define LOG_BUTTON(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
} \
}
#define SUB_BUTTON(name) \
protected: \

View File

@@ -153,8 +153,8 @@ void CCS811Component::dump_config() {
ESP_LOGCONFIG(TAG, "CCS811");
LOG_I2C_DEVICE(this)
LOG_UPDATE_INTERVAL(this)
LOG_SENSOR(" ", "CO2 Sensor", this->co2_);
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_);
LOG_SENSOR(" ", "CO2 Sensor", this->co2_)
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_)
LOG_TEXT_SENSOR(" ", "Firmware Version Sensor", this->version_)
if (this->baseline_) {
ESP_LOGCONFIG(TAG, " Baseline: %04X", *this->baseline_);

View File

@@ -327,7 +327,7 @@ void Climate::add_on_control_callback(std::function<void(ClimateCall &)> &&callb
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_preference_hash() ^
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash() ^
RESTORE_STATE_VERSION);
ClimateDeviceRestoreState recovered{};
if (!this->rtc_.load(&recovered))

View File

@@ -228,9 +228,9 @@ async def cover_stop_to_code(config, action_id, template_arg, args):
@automation.register_action("cover.toggle", ToggleAction, COVER_ACTION_SCHEMA)
async def cover_toggle_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)
def cover_toggle_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren)
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(

View File

@@ -194,7 +194,7 @@ void Cover::publish_state(bool save) {
}
}
optional<CoverRestoreState> Cover::restore_state_() {
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_preference_hash());
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_object_id_hash());
CoverRestoreState recovered{};
if (!this->rtc_.load(&recovered))
return {};

View File

@@ -41,7 +41,7 @@ void DutyTimeSensor::setup() {
uint32_t seconds = 0;
if (this->restore_) {
this->pref_ = global_preferences->make_preference<uint32_t>(this->get_preference_hash());
this->pref_ = global_preferences->make_preference<uint32_t>(this->get_object_id_hash());
this->pref_.load(&seconds);
}

View File

@@ -40,6 +40,7 @@ from esphome.cpp_generator import RawExpression
import esphome.final_validate as fv
from esphome.helpers import copy_file_if_changed, mkdir_p, write_file_if_changed
from esphome.types import ConfigType
from esphome.writer import clean_cmake_cache
from .boards import BOARDS, STANDARD_BOARDS
from .const import ( # noqa
@@ -840,6 +841,9 @@ async def to_code(config):
if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]:
cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC")
for clean_var in ("IDF_PATH", "IDF_TOOLS_PATH"):
os.environ.pop(clean_var, None)
add_extra_script(
"post",
"post_build.py",
@@ -1079,7 +1083,11 @@ def _write_idf_component_yml():
contents = yaml_util.dump({"dependencies": dependencies})
else:
contents = ""
write_file_if_changed(yml_path, contents)
if write_file_if_changed(yml_path, contents):
dependencies_lock = CORE.relative_build_path("dependencies.lock")
if os.path.isfile(dependencies_lock):
os.remove(dependencies_lock)
clean_cmake_cache()
# Called by writer.py

View File

@@ -8,7 +8,6 @@
#include <cinttypes>
#include <vector>
#include <string>
#include <memory>
namespace esphome {
namespace esp32 {
@@ -157,23 +156,20 @@ class ESP32Preferences : public ESPPreferences {
return failed == 0;
}
bool is_changed(const uint32_t nvs_handle, const NVSData &to_save) {
NVSData stored_data{};
size_t actual_len;
esp_err_t err = nvs_get_blob(nvs_handle, to_save.key.c_str(), nullptr, &actual_len);
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));
return true;
}
// Check size first before allocating memory
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);
stored_data.data.resize(actual_len);
err = nvs_get_blob(nvs_handle, to_save.key.c_str(), stored_data.data.data(), &actual_len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key.c_str(), esp_err_to_name(err));
return true;
}
return memcmp(to_save.data.data(), stored_data.get(), to_save.data.size()) != 0;
return to_save.data != stored_data.data;
}
bool reset() override {

View File

@@ -3,28 +3,11 @@ import re
from esphome import automation
import esphome.codegen as cg
from esphome.components.esp32 import (
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C5,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32S3,
add_idf_sdkconfig_option,
const,
get_esp32_variant,
)
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
import esphome.config_validation as cv
from esphome.const import (
CONF_ENABLE_ON_BOOT,
CONF_ESPHOME,
CONF_ID,
CONF_NAME,
CONF_NAME_ADD_MAC_SUFFIX,
CONF_TX_POWER,
)
from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ESPHOME, CONF_ID, CONF_NAME
from esphome.core import CORE, TimePeriod
from esphome.cpp_types import MockObj
from esphome.core.config import CONF_NAME_ADD_MAC_SUFFIX
import esphome.final_validate as fv
DEPENDENCIES = ["esp32"]
@@ -163,8 +146,7 @@ IO_CAPABILITY = {
esp_power_level_t = cg.global_ns.enum("esp_power_level_t")
# Power level mappings for code generation - ESP32 classic
TX_POWER_LEVELS_ESP32 = {
TX_POWER_LEVELS = {
-12: esp_power_level_t.ESP_PWR_LVL_N12,
-9: esp_power_level_t.ESP_PWR_LVL_N9,
-6: esp_power_level_t.ESP_PWR_LVL_N6,
@@ -175,53 +157,6 @@ TX_POWER_LEVELS_ESP32 = {
9: esp_power_level_t.ESP_PWR_LVL_P9,
}
# Power level mappings for code generation - Extended variants
TX_POWER_LEVELS_EXT = {
-24: esp_power_level_t.ESP_PWR_LVL_N24,
-21: esp_power_level_t.ESP_PWR_LVL_N21,
-18: esp_power_level_t.ESP_PWR_LVL_N18,
-15: esp_power_level_t.ESP_PWR_LVL_N15,
-12: esp_power_level_t.ESP_PWR_LVL_N12,
-9: esp_power_level_t.ESP_PWR_LVL_N9,
-6: esp_power_level_t.ESP_PWR_LVL_N6,
-3: esp_power_level_t.ESP_PWR_LVL_N3,
0: esp_power_level_t.ESP_PWR_LVL_N0,
3: esp_power_level_t.ESP_PWR_LVL_P3,
6: esp_power_level_t.ESP_PWR_LVL_P6,
9: esp_power_level_t.ESP_PWR_LVL_P9,
12: esp_power_level_t.ESP_PWR_LVL_P12,
15: esp_power_level_t.ESP_PWR_LVL_P15,
18: esp_power_level_t.ESP_PWR_LVL_P18,
20: esp_power_level_t.ESP_PWR_LVL_P20,
}
def _get_tx_power_levels() -> dict[str, MockObj]:
variant = get_esp32_variant()
if variant in [
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C5,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32S3,
]:
return TX_POWER_LEVELS_EXT
return TX_POWER_LEVELS_ESP32
def validate_tx_power(value: int) -> int:
value = cv.decibel(value)
power_levels = _get_tx_power_levels()
if value not in power_levels:
raise cv.Invalid(
f"TX power {value}dBm is not valid. "
f"Valid values are: {', '.join(str(v) + 'dBm' for v in sorted(power_levels.keys()))}"
)
# Return just the dBm value, we'll map it to enum in to_code
return value
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(ESP32BLE),
@@ -229,7 +164,6 @@ CONFIG_SCHEMA = cv.Schema(
cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum(
IO_CAPABILITY, lower=True
),
cv.Optional(CONF_TX_POWER): validate_tx_power,
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
cv.Optional(CONF_ADVERTISING, default=False): cv.boolean,
cv.Optional(
@@ -320,9 +254,6 @@ async def to_code(config):
cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME]))
if (name := config.get(CONF_NAME)) is not None:
cg.add(var.set_name(name))
if (tx_power := config.get(CONF_TX_POWER)) is not None:
# The validation already returned the enum value
cg.add(var.set_tx_power(_get_tx_power_levels()[tx_power]))
await cg.register_component(var, config)
if CORE.using_esp_idf:

View File

@@ -212,15 +212,6 @@ bool ESP32BLE::ble_setup_() {
return false;
}
// Set TX power for all BLE operations (advertising, scanning, connections)
err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, this->tx_power_);
if (err != ESP_OK) {
ESP_LOGW(TAG, "esp_ble_tx_power_set failed: %s", esp_err_to_name(err));
// Continue anyway as this is not critical
} else {
ESP_LOGD(TAG, "BLE TX power set to level %d", this->tx_power_);
}
// BLE takes some time to be fully set up, 200ms should be more than enough
delay(200); // NOLINT
@@ -529,106 +520,11 @@ void ESP32BLE::dump_config() {
io_capability_s = "invalid";
break;
}
// Convert TX power level to dBm for display
int tx_power_dbm = 0;
#if defined(CONFIG_IDF_TARGET_ESP32)
// ESP32 classic power levels (0-7)
switch (this->tx_power_) {
case 0:
tx_power_dbm = -12;
break; // ESP_PWR_LVL_N12
case 1:
tx_power_dbm = -9;
break; // ESP_PWR_LVL_N9
case 2:
tx_power_dbm = -6;
break; // ESP_PWR_LVL_N6
case 3:
tx_power_dbm = -3;
break; // ESP_PWR_LVL_N3
case 4:
tx_power_dbm = 0;
break; // ESP_PWR_LVL_N0
case 5:
tx_power_dbm = 3;
break; // ESP_PWR_LVL_P3
case 6:
tx_power_dbm = 6;
break; // ESP_PWR_LVL_P6
case 7:
tx_power_dbm = 9;
break; // ESP_PWR_LVL_P9
default:
tx_power_dbm = 0;
break;
}
#elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || \
defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || \
defined(CONFIG_IDF_TARGET_ESP32S3)
// Extended power levels for C2/C3/C5/C6/H2/S3 (0-15)
switch (this->tx_power_) {
case 0:
tx_power_dbm = -24;
break; // ESP_PWR_LVL_N24
case 1:
tx_power_dbm = -21;
break; // ESP_PWR_LVL_N21
case 2:
tx_power_dbm = -18;
break; // ESP_PWR_LVL_N18
case 3:
tx_power_dbm = -15;
break; // ESP_PWR_LVL_N15
case 4:
tx_power_dbm = -12;
break; // ESP_PWR_LVL_N12
case 5:
tx_power_dbm = -9;
break; // ESP_PWR_LVL_N9
case 6:
tx_power_dbm = -6;
break; // ESP_PWR_LVL_N6
case 7:
tx_power_dbm = -3;
break; // ESP_PWR_LVL_N3
case 8:
tx_power_dbm = 0;
break; // ESP_PWR_LVL_N0
case 9:
tx_power_dbm = 3;
break; // ESP_PWR_LVL_P3
case 10:
tx_power_dbm = 6;
break; // ESP_PWR_LVL_P6
case 11:
tx_power_dbm = 9;
break; // ESP_PWR_LVL_P9
case 12:
tx_power_dbm = 12;
break; // ESP_PWR_LVL_P12
case 13:
tx_power_dbm = 15;
break; // ESP_PWR_LVL_P15
case 14:
tx_power_dbm = 18;
break; // ESP_PWR_LVL_P18
case 15:
tx_power_dbm = 20;
break; // ESP_PWR_LVL_P20
default:
tx_power_dbm = 0;
break;
}
#else
// Unknown variant
tx_power_dbm = 0;
#endif
ESP_LOGCONFIG(TAG,
"BLE:\n"
" MAC address: %s\n"
" IO Capability: %s\n"
" TX Power: %d dBm",
format_mac_address_pretty(mac_address).c_str(), io_capability_s, tx_power_dbm);
" IO Capability: %s",
format_mac_address_pretty(mac_address).c_str(), io_capability_s);
} else {
ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled");
}

View File

@@ -20,7 +20,6 @@
#ifdef USE_ESP32
#include <esp_bt.h>
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#include <esp_gatts_api.h>
@@ -95,7 +94,6 @@ class BLEStatusEventHandler {
class ESP32BLE : public Component {
public:
void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; }
void set_tx_power(esp_power_level_t tx_power) { this->tx_power_ = tx_power; }
void set_advertising_cycle_time(uint32_t advertising_cycle_time) {
this->advertising_cycle_time_ = advertising_cycle_time;
@@ -174,7 +172,6 @@ class ESP32BLE : public Component {
// 1-byte aligned members (grouped together to minimize padding)
BLEComponentState state_{BLE_COMPONENT_STATE_OFF}; // 1 byte (uint8_t enum)
bool enable_on_boot_{}; // 1 byte
esp_power_level_t tx_power_{ESP_PWR_LVL_P9}; // 1 byte (default: +9 dBm)
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -495,11 +495,6 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break;
}
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
this->log_gattc_event_("UNREG_FOR_NOTIFY");
break;
}
default:
// ideally would check all other events for matching conn_id
ESP_LOGD(TAG, "[%d] [%s] Event %d", this->connection_index_, this->address_str_.c_str(), event);

View File

@@ -12,7 +12,7 @@ extern "C" {
#include "preferences.h"
#include <cstring>
#include <memory>
#include <vector>
namespace esphome {
namespace esp8266 {
@@ -67,8 +67,6 @@ static uint32_t get_esp8266_flash_sector() {
}
static uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; }
static inline size_t bytes_to_words(size_t bytes) { return (bytes + 3) / 4; }
template<class It> uint32_t calculate_crc(It first, It last, uint32_t type) {
uint32_t crc = type;
while (first != last) {
@@ -125,36 +123,41 @@ class ESP8266PreferenceBackend : public ESPPreferenceBackend {
size_t length_words = 0;
bool save(const uint8_t *data, size_t len) override {
if (bytes_to_words(len) != length_words) {
if ((len + 3) / 4 != length_words) {
return false;
}
size_t buffer_size = length_words + 1;
std::unique_ptr<uint32_t[]> buffer(new uint32_t[buffer_size]()); // Note the () for zero-initialization
memcpy(buffer.get(), data, len);
buffer[length_words] = calculate_crc(buffer.get(), buffer.get() + length_words, type);
std::vector<uint32_t> buffer;
buffer.resize(length_words + 1);
memcpy(buffer.data(), data, len);
buffer[buffer.size() - 1] = calculate_crc(buffer.begin(), buffer.end() - 1, type);
if (in_flash) {
return save_to_flash(offset, buffer.get(), buffer_size);
return save_to_flash(offset, buffer.data(), buffer.size());
} else {
return save_to_rtc(offset, buffer.data(), buffer.size());
}
return save_to_rtc(offset, buffer.get(), buffer_size);
}
bool load(uint8_t *data, size_t len) override {
if (bytes_to_words(len) != length_words) {
if ((len + 3) / 4 != length_words) {
return false;
}
size_t buffer_size = length_words + 1;
std::unique_ptr<uint32_t[]> buffer(new uint32_t[buffer_size]());
bool ret = in_flash ? load_from_flash(offset, buffer.get(), buffer_size)
: load_from_rtc(offset, buffer.get(), buffer_size);
std::vector<uint32_t> buffer;
buffer.resize(length_words + 1);
bool ret;
if (in_flash) {
ret = load_from_flash(offset, buffer.data(), buffer.size());
} else {
ret = load_from_rtc(offset, buffer.data(), buffer.size());
}
if (!ret)
return false;
uint32_t crc = calculate_crc(buffer.get(), buffer.get() + length_words, type);
if (buffer[length_words] != crc) {
uint32_t crc = calculate_crc(buffer.begin(), buffer.end() - 1, type);
if (buffer[buffer.size() - 1] != crc) {
return false;
}
memcpy(data, buffer.get(), len);
memcpy(data, buffer.data(), len);
return true;
}
};
@@ -175,7 +178,7 @@ class ESP8266Preferences : public ESPPreferences {
}
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
uint32_t length_words = bytes_to_words(length);
uint32_t length_words = (length + 3) / 4;
if (in_flash) {
uint32_t start = current_flash_offset;
uint32_t end = start + length_words + 1;

View File

@@ -100,8 +100,8 @@ void ESPHomeOTAComponent::handle_handshake_() {
/// Handle the initial OTA handshake.
///
/// This method is non-blocking and will return immediately if no data is available.
/// It waits for the first magic byte (0x6C) before proceeding to handle_data_().
/// A 10-second timeout is enforced from initial connection.
/// It reads all 5 magic bytes (0x6C, 0x26, 0xF7, 0x5C, 0x45) non-blocking
/// before proceeding to handle_data_(). A 10-second timeout is enforced from initial connection.
if (this->client_ == nullptr) {
// We already checked server_->ready() in loop(), so we can accept directly
@@ -126,6 +126,7 @@ void ESPHomeOTAComponent::handle_handshake_() {
}
this->log_start_("handshake");
this->client_connect_time_ = App.get_loop_component_start_time();
this->magic_buf_pos_ = 0; // Reset magic buffer position
}
// Check for handshake timeout
@@ -136,34 +137,47 @@ void ESPHomeOTAComponent::handle_handshake_() {
return;
}
// Try to read first byte of magic bytes
uint8_t first_byte;
ssize_t read = this->client_->read(&first_byte, 1);
// Try to read remaining magic bytes
if (this->magic_buf_pos_ < 5) {
// Read as many bytes as available
uint8_t bytes_to_read = 5 - this->magic_buf_pos_;
ssize_t read = this->client_->read(this->magic_buf_ + this->magic_buf_pos_, bytes_to_read);
if (read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
return; // No data yet, try again next loop
}
if (read <= 0) {
// Error or connection closed
if (read == -1) {
this->log_socket_error_("reading first byte");
} else {
ESP_LOGW(TAG, "Remote closed during handshake");
if (read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
return; // No data yet, try again next loop
}
this->cleanup_connection_();
return;
if (read <= 0) {
// Error or connection closed
if (read == -1) {
this->log_socket_error_("reading magic bytes");
} else {
ESP_LOGW(TAG, "Remote closed during handshake");
}
this->cleanup_connection_();
return;
}
this->magic_buf_pos_ += read;
}
// Got first byte, check if it's the magic byte
if (first_byte != 0x6C) {
ESP_LOGW(TAG, "Invalid initial byte: 0x%02X", first_byte);
this->cleanup_connection_();
return;
}
// Check if we have all 5 magic bytes
if (this->magic_buf_pos_ == 5) {
// Validate magic bytes
static const uint8_t MAGIC_BYTES[5] = {0x6C, 0x26, 0xF7, 0x5C, 0x45};
if (memcmp(this->magic_buf_, MAGIC_BYTES, 5) != 0) {
ESP_LOGW(TAG, "Magic bytes mismatch! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", this->magic_buf_[0],
this->magic_buf_[1], this->magic_buf_[2], this->magic_buf_[3], this->magic_buf_[4]);
// Send error response (non-blocking, best effort)
uint8_t error = static_cast<uint8_t>(ota::OTA_RESPONSE_ERROR_MAGIC);
this->client_->write(&error, 1);
this->cleanup_connection_();
return;
}
// First byte is valid, continue with data handling
this->handle_data_();
// All 5 magic bytes are valid, continue with data handling
this->handle_data_();
}
}
void ESPHomeOTAComponent::handle_data_() {
@@ -186,18 +200,6 @@ void ESPHomeOTAComponent::handle_data_() {
size_t size_acknowledged = 0;
#endif
// Read remaining 4 bytes of magic (we already read the first byte 0x6C in handle_handshake_)
if (!this->readall_(buf, 4)) {
this->log_read_error_("magic bytes");
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
// Check remaining magic bytes: 0x26, 0xF7, 0x5C, 0x45
if (buf[0] != 0x26 || buf[1] != 0xF7 || buf[2] != 0x5C || buf[3] != 0x45) {
ESP_LOGW(TAG, "Magic bytes mismatch! 0x6C-0x%02X-0x%02X-0x%02X-0x%02X", buf[0], buf[1], buf[2], buf[3]);
error_code = ota::OTA_RESPONSE_ERROR_MAGIC;
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
// Send OK and version - 2 bytes
buf[0] = ota::OTA_RESPONSE_OK;
buf[1] = USE_OTA_VERSION;
@@ -487,6 +489,7 @@ void ESPHomeOTAComponent::cleanup_connection_() {
this->client_->close();
this->client_ = nullptr;
this->client_connect_time_ = 0;
this->magic_buf_pos_ = 0;
}
void ESPHomeOTAComponent::yield_and_feed_watchdog_() {

View File

@@ -41,11 +41,13 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
std::string password_;
#endif // USE_OTA_PASSWORD
uint16_t port_;
uint32_t client_connect_time_{0};
std::unique_ptr<socket::Socket> server_;
std::unique_ptr<socket::Socket> client_;
uint32_t client_connect_time_{0};
uint16_t port_;
uint8_t magic_buf_[5];
uint8_t magic_buf_pos_{0};
};
} // namespace esphome

View File

@@ -148,8 +148,7 @@ void Fan::publish_state() {
constexpr uint32_t RESTORE_STATE_VERSION = 0x71700ABA;
optional<FanRestoreState> Fan::restore_state_() {
FanRestoreState recovered{};
this->rtc_ =
global_preferences->make_preference<FanRestoreState>(this->get_preference_hash() ^ RESTORE_STATE_VERSION);
this->rtc_ = global_preferences->make_preference<FanRestoreState>(this->get_object_id_hash() ^ RESTORE_STATE_VERSION);
bool restored = this->rtc_.load(&recovered);
switch (this->restore_mode_) {

View File

@@ -58,10 +58,10 @@ void GroveGasMultichannelV2Component::dump_config() {
ESP_LOGCONFIG(TAG, "Grove Multichannel Gas Sensor V2");
LOG_I2C_DEVICE(this)
LOG_UPDATE_INTERVAL(this)
LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_);
LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_);
LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_);
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_)
LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_)
LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_)
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_)
if (this->is_failed()) {
switch (this->error_code_) {

View File

@@ -351,7 +351,7 @@ ClimateTraits HaierClimateBase::traits() { return traits_; }
void HaierClimateBase::initialization() {
constexpr uint32_t restore_settings_version = 0xA77D21EF;
this->base_rtc_ =
global_preferences->make_preference<HaierBaseSettings>(this->get_preference_hash() ^ restore_settings_version);
global_preferences->make_preference<HaierBaseSettings>(this->get_object_id_hash() ^ restore_settings_version);
HaierBaseSettings recovered;
if (!this->base_rtc_.load(&recovered)) {
recovered = {false, true};

View File

@@ -516,7 +516,7 @@ void HonClimate::initialization() {
HaierClimateBase::initialization();
constexpr uint32_t restore_settings_version = 0x57EB59DDUL;
this->hon_rtc_ =
global_preferences->make_preference<HonSettings>(this->get_preference_hash() ^ restore_settings_version);
global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
HonSettings recovered;
if (this->hon_rtc_.load(&recovered)) {
this->settings_ = recovered;

View File

@@ -43,10 +43,10 @@ void HLW8012Component::dump_config() {
" Voltage Divider: %.1f",
this->change_mode_every_, this->current_resistor_ * 1000.0f, this->voltage_divider_);
LOG_UPDATE_INTERVAL(this)
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
LOG_SENSOR(" ", "Current", this->current_sensor_);
LOG_SENSOR(" ", "Power", this->power_sensor_);
LOG_SENSOR(" ", "Energy", this->energy_sensor_);
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_)
LOG_SENSOR(" ", "Current", this->current_sensor_)
LOG_SENSOR(" ", "Power", this->power_sensor_)
LOG_SENSOR(" ", "Energy", this->energy_sensor_)
}
float HLW8012Component::get_setup_priority() const { return setup_priority::DATA; }
void HLW8012Component::update() {

View File

@@ -11,7 +11,7 @@ void HTE501Component::setup() {
uint8_t address[] = {0x70, 0x29};
uint8_t identification[9];
this->write_read(address, sizeof address, identification, sizeof identification);
if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) {
if (identification[8] != calc_crc8_(identification, 0, 7)) {
this->error_code_ = CRC_CHECK_FAILED;
this->mark_failed();
return;
@@ -45,8 +45,7 @@ void HTE501Component::update() {
this->set_timeout(50, [this]() {
uint8_t i2c_response[6];
this->read(i2c_response, 6);
if (i2c_response[2] != crc8(i2c_response, 2, 0xFF, 0x31, true) &&
i2c_response[5] != crc8(i2c_response + 3, 2, 0xFF, 0x31, true)) {
if (i2c_response[2] != calc_crc8_(i2c_response, 0, 1) && i2c_response[5] != calc_crc8_(i2c_response, 3, 4)) {
this->error_code_ = CRC_CHECK_FAILED;
this->status_set_warning();
return;
@@ -67,5 +66,24 @@ void HTE501Component::update() {
this->status_clear_warning();
});
}
unsigned char HTE501Component::calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to) {
unsigned char crc_val = 0xFF;
unsigned char i = 0;
unsigned char j = 0;
for (i = from; i <= to; i++) {
int cur_val = buf[i];
for (j = 0; j < 8; j++) {
if (((crc_val ^ cur_val) & 0x80) != 0) // If MSBs are not equal
{
crc_val = ((crc_val << 1) ^ 0x31);
} else {
crc_val = (crc_val << 1);
}
cur_val = cur_val << 1;
}
}
return crc_val;
}
} // namespace hte501
} // namespace esphome

View File

@@ -1,8 +1,8 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace hte501 {
@@ -19,6 +19,7 @@ class HTE501Component : public PollingComponent, public i2c::I2CDevice {
void update() override;
protected:
unsigned char calc_crc8_(const unsigned char buf[], unsigned char from, unsigned char to);
sensor::Sensor *temperature_sensor_;
sensor::Sensor *humidity_sensor_;

View File

@@ -47,9 +47,9 @@ ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, siz
ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len) const {
std::vector<uint8_t> v(len + 2);
v.push_back(a_register >> 8);
v.push_back(a_register);
v.insert(v.end(), data, data + len);
v[0] = a_register >> 8;
v[1] = a_register;
std::copy(data, data + len, v.begin() + 2);
return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0);
}

View File

@@ -212,7 +212,7 @@ def validate_use_legacy(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):
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]
return value

View File

@@ -92,7 +92,7 @@ CONFIG_SCHEMA = cv.All(
def _final_validate(_):
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

View File

@@ -122,7 +122,7 @@ CONFIG_SCHEMA = cv.All(
def _final_validate(config):
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

View File

@@ -163,7 +163,7 @@ CONFIG_SCHEMA = cv.All(
def _final_validate(config):
if not use_legacy():
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":
raise cv.Invalid(
"I2S standard max format only implemented with legacy i2s driver."

View File

@@ -10,7 +10,7 @@ static const char *const TAG = "integration";
void IntegrationSensor::setup() {
if (this->restore_) {
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
float preference_value = 0;
this->pref_.load(&preference_value);
this->result_ = preference_value;

View File

@@ -1,6 +1,5 @@
#include "lc709203f.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "lc709203f.h"
namespace esphome {
namespace lc709203f {
@@ -190,7 +189,7 @@ uint8_t Lc709203f::get_register_(uint8_t register_to_read, uint16_t *register_va
// Error on the i2c bus
this->status_set_warning(
str_sprintf("Error code %d when reading from register 0x%02X", return_code, register_to_read).c_str());
} else if (crc8(read_buffer, 5, 0x00, 0x07, true) != read_buffer[5]) {
} else if (this->crc8_(read_buffer, 5) != read_buffer[5]) {
// I2C indicated OK, but the CRC of the data does not matcth.
this->status_set_warning(str_sprintf("CRC error reading from register 0x%02X", register_to_read).c_str());
} else {
@@ -221,7 +220,7 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set)
write_buffer[1] = register_to_set;
write_buffer[2] = value_to_set & 0xFF; // Low byte
write_buffer[3] = (value_to_set >> 8) & 0xFF; // High byte
write_buffer[4] = crc8(write_buffer, 4, 0x00, 0x07, true);
write_buffer[4] = this->crc8_(write_buffer, 4);
for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) {
// Note: we don't write the first byte of the write buffer to the device.
@@ -240,6 +239,20 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set)
return return_code;
}
uint8_t Lc709203f::crc8_(uint8_t *byte_buffer, uint8_t length_of_crc) {
uint8_t crc = 0x00;
const uint8_t polynomial(0x07);
for (uint8_t j = length_of_crc; j; --j) {
crc ^= *byte_buffer++;
for (uint8_t i = 8; i; --i) {
crc = (crc & 0x80) ? (crc << 1) ^ polynomial : (crc << 1);
}
}
return crc;
}
void Lc709203f::set_pack_size(uint16_t pack_size) {
static const uint16_t PACK_SIZE_ARRAY[6] = {100, 200, 500, 1000, 2000, 3000};
static const uint16_t APA_ARRAY[6] = {0x08, 0x0B, 0x10, 0x19, 0x2D, 0x36};

View File

@@ -1,8 +1,8 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace lc709203f {
@@ -38,6 +38,7 @@ class Lc709203f : public sensor::Sensor, public PollingComponent, public i2c::I2
private:
uint8_t get_register_(uint8_t register_to_read, uint16_t *register_value);
uint8_t set_register_(uint8_t register_to_set, uint16_t value_to_set);
uint8_t crc8_(uint8_t *byte_buffer, uint8_t length_of_crc);
protected:
sensor::Sensor *voltage_sensor_{nullptr};

View File

@@ -1,4 +1,4 @@
#include "ld2420_text_sensor.h"
#include "text_sensor.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"

View File

@@ -184,7 +184,7 @@ static inline bool validate_header_footer(const uint8_t *header_footer, const ui
void LD2450Component::setup() {
#ifdef USE_NUMBER
if (this->presence_timeout_number_ != nullptr) {
this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_preference_hash());
this->pref_ = global_preferences->make_preference<float>(this->presence_timeout_number_->get_object_id_hash());
this->set_presence_timeout();
}
#endif

View File

@@ -5,7 +5,7 @@
#include "esphome/core/preferences.h"
#include <flashdb.h>
#include <cstring>
#include <memory>
#include <vector>
#include <string>
namespace esphome {
@@ -139,29 +139,21 @@ class LibreTinyPreferences : public ESPPreferences {
}
bool is_changed(const fdb_kvdb_t db, const NVSData &to_save) {
NVSData stored_data{};
struct fdb_kv kv;
fdb_kv_t kvp = fdb_kv_get_obj(db, to_save.key.c_str(), &kv);
if (kvp == nullptr) {
ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", to_save.key.c_str());
return true;
}
// Check size first - if different, data has changed
if (kv.value_len != to_save.data.size()) {
return true;
}
// Allocate buffer on heap to avoid stack allocation for large data
auto stored_data = std::make_unique<uint8_t[]>(kv.value_len);
fdb_blob_make(&blob, stored_data.get(), kv.value_len);
stored_data.data.resize(kv.value_len);
fdb_blob_make(&blob, stored_data.data.data(), kv.value_len);
size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &blob);
if (actual_len != kv.value_len) {
ESP_LOGV(TAG, "fdb_kv_get_blob('%s') len mismatch: %u != %u", to_save.key.c_str(), actual_len, kv.value_len);
return true;
}
// Compare the actual data
return memcmp(to_save.data.data(), stored_data.get(), kv.value_len) != 0;
return to_save.data != stored_data.data;
}
bool reset() override {

View File

@@ -44,13 +44,6 @@ class AddressableLightEffect : public LightEffect {
this->apply(*this->get_addressable_(), current_color);
}
/// Get effect index specifically for addressable effects.
/// Can be used by effects to modify behavior based on their position in the list.
uint32_t get_effect_index() const { return this->get_index(); }
/// Check if this is the currently running addressable effect.
bool is_current_effect() const { return this->is_active() && this->get_addressable_()->is_effect_active(); }
protected:
AddressableLight *get_addressable_() const { return (AddressableLight *) this->state_->get_output(); }
};

View File

@@ -125,10 +125,6 @@ class LambdaLightEffect : public LightEffect {
}
}
/// Get the current effect index for use in lambda functions.
/// This can be useful for lambda effects that need to know their own index.
uint32_t get_current_index() const { return this->get_index(); }
protected:
std::function<void(bool initial_run)> f_;
uint32_t update_interval_;
@@ -147,10 +143,6 @@ class AutomationLightEffect : public LightEffect {
}
Trigger<> *get_trig() const { return trig_; }
/// Get the current effect index for use in automations.
/// Useful for automations that need to know which effect is running.
uint32_t get_current_index() const { return this->get_index(); }
protected:
Trigger<> *trig_{new Trigger<>};
};

View File

@@ -1,36 +0,0 @@
#include "light_effect.h"
#include "light_state.h"
namespace esphome {
namespace light {
uint32_t LightEffect::get_index() const {
if (this->state_ == nullptr) {
return 0;
}
return this->get_index_in_parent_();
}
bool LightEffect::is_active() const {
if (this->state_ == nullptr) {
return false;
}
return this->get_index() != 0 && this->state_->get_current_effect_index() == this->get_index();
}
uint32_t LightEffect::get_index_in_parent_() const {
if (this->state_ == nullptr) {
return 0;
}
const auto &effects = this->state_->get_effects();
for (size_t i = 0; i < effects.size(); i++) {
if (effects[i] == this) {
return i + 1; // Effects are 1-indexed in the API
}
}
return 0; // Not found
}
} // namespace light
} // namespace esphome

View File

@@ -34,23 +34,9 @@ class LightEffect {
this->init();
}
/// Get the index of this effect in the parent light's effect list.
/// Returns 0 if not found or not initialized.
uint32_t get_index() const;
/// Check if this effect is currently active.
bool is_active() const;
/// Get a reference to the parent light state.
/// Returns nullptr if not initialized.
LightState *get_light_state() const { return this->state_; }
protected:
LightState *state_{nullptr};
std::string name_;
/// Internal method to find this effect's index in the parent light's effect list.
uint32_t get_index_in_parent_() const;
};
} // namespace light

View File

@@ -36,11 +36,8 @@ static constexpr const char *get_color_mode_json_str(ColorMode mode) {
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (state.supports_effects()) {
if (state.supports_effects())
root["effect"] = state.get_effect_name();
root["effect_index"] = state.get_current_effect_index();
root["effect_count"] = state.get_effect_count();
}
auto values = state.remote_values;
auto traits = state.get_output()->get_traits();
@@ -163,11 +160,6 @@ void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject
const char *effect = root["effect"];
call.set_effect(effect);
}
if (root["effect_index"].is<uint32_t>()) {
uint32_t effect_index = root["effect_index"];
call.set_effect(effect_index);
}
}
} // namespace light

View File

@@ -41,7 +41,7 @@ void LightState::setup() {
case LIGHT_RESTORE_DEFAULT_ON:
case LIGHT_RESTORE_INVERTED_DEFAULT_OFF:
case LIGHT_RESTORE_INVERTED_DEFAULT_ON:
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_preference_hash());
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
// Attempt to load from preferences, else fall back to default values
if (!this->rtc_.load(&recovered)) {
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_DEFAULT_ON ||
@@ -54,7 +54,7 @@ void LightState::setup() {
break;
case LIGHT_RESTORE_AND_OFF:
case LIGHT_RESTORE_AND_ON:
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_preference_hash());
this->rtc_ = global_preferences->make_preference<LightStateRTCState>(this->get_object_id_hash());
this->rtc_.load(&recovered);
recovered.state = (this->restore_mode_ == LIGHT_RESTORE_AND_ON);
break;

View File

@@ -163,44 +163,6 @@ class LightState : public EntityBase, public Component {
/// Add effects for this light state.
void add_effects(const std::vector<LightEffect *> &effects);
/// Get the total number of effects available for this light.
size_t get_effect_count() const { return this->effects_.size(); }
/// Get the currently active effect index (0 = no effect, 1+ = effect index).
uint32_t get_current_effect_index() const { return this->active_effect_index_; }
/// Get effect index by name. Returns 0 if effect not found.
uint32_t get_effect_index(const std::string &effect_name) const {
if (strcasecmp(effect_name.c_str(), "none") == 0) {
return 0;
}
for (size_t i = 0; i < this->effects_.size(); i++) {
if (strcasecmp(effect_name.c_str(), this->effects_[i]->get_name().c_str()) == 0) {
return i + 1; // Effects are 1-indexed in active_effect_index_
}
}
return 0; // Effect not found
}
/// Get effect by index. Returns nullptr if index is invalid.
LightEffect *get_effect_by_index(uint32_t index) const {
if (index == 0 || index > this->effects_.size()) {
return nullptr;
}
return this->effects_[index - 1]; // Effects are 1-indexed in active_effect_index_
}
/// Get effect name by index. Returns "None" for index 0, empty string for invalid index.
std::string get_effect_name_by_index(uint32_t index) const {
if (index == 0) {
return "None";
}
if (index > this->effects_.size()) {
return ""; // Invalid index
}
return this->effects_[index - 1]->get_name();
}
/// The result of all the current_values_as_* methods have gamma correction applied.
void current_values_as_binary(bool *binary);

View File

@@ -21,7 +21,7 @@ class LVGLNumber : public number::Number, public Component {
void setup() override {
float value = this->value_lambda_();
if (this->restore_) {
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
if (this->pref_.load(&value)) {
this->control_lambda_(value);
}

View File

@@ -20,7 +20,7 @@ class LVGLSelect : public select::Select, public Component {
this->set_options_();
if (this->restore_) {
size_t index;
this->pref_ = global_preferences->make_preference<size_t>(this->get_preference_hash());
this->pref_ = global_preferences->make_preference<size_t>(this->get_object_id_hash());
if (this->pref_.load(&index))
this->widget_->set_selected_index(index, LV_ANIM_OFF);
}

View File

@@ -122,7 +122,7 @@ uint8_t Mcp4461Component::get_status_register_() {
uint8_t addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_STATUS);
uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::READ);
uint16_t buf;
if (!this->read_byte_16(reg, &buf)) {
if (!this->read_16_(reg, &buf)) {
this->error_code_ = MCP4461_STATUS_REGISTER_ERROR;
this->mark_failed();
return 0;
@@ -148,6 +148,20 @@ void Mcp4461Component::read_status_register_to_log() {
((status_register_value >> 3) & 0x01), ((status_register_value >> 2) & 0x01),
((status_register_value >> 1) & 0x01), ((status_register_value >> 0) & 0x01));
}
bool Mcp4461Component::read_16_(uint8_t address, uint16_t *buf) {
// read 16 bits and convert from big endian to host,
// Do this as two separate operations to ensure a stop condition between the write and read
i2c::ErrorCode err = this->write(&address, 1);
if (err != i2c::ERROR_OK) {
return false;
}
err = this->read(reinterpret_cast<uint8_t *>(buf), 2);
if (err != i2c::ERROR_OK) {
return false;
}
*buf = convert_big_endian(*buf);
return true;
}
uint8_t Mcp4461Component::get_wiper_address_(uint8_t wiper) {
uint8_t addr;
@@ -198,14 +212,14 @@ uint16_t Mcp4461Component::get_wiper_level_(Mcp4461WiperIdx wiper) {
uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) {
uint8_t addr = this->get_wiper_address_(wiper_idx);
uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::READ);
if (wiper_idx > 3) {
if (!this->is_eeprom_ready_for_writing_(true)) {
return 0;
}
}
uint16_t buf = 0;
if (!(this->read_byte_16(reg, &buf))) {
if (!(this->read_16_(reg, &buf))) {
this->error_code_ = MCP4461_STATUS_I2C_ERROR;
this->status_set_warning();
ESP_LOGW(TAG, "Error fetching %swiper %u value", (wiper_idx > 3) ? "nonvolatile " : "", wiper_idx);
@@ -392,7 +406,7 @@ uint8_t Mcp4461Component::get_terminal_register_(Mcp4461TerminalIdx terminal_con
: static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1);
reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
uint16_t buf;
if (this->read_byte_16(reg, &buf)) {
if (this->read_16_(reg, &buf)) {
return static_cast<uint8_t>(buf & 0x00ff);
} else {
this->error_code_ = MCP4461_STATUS_I2C_ERROR;
@@ -517,7 +531,7 @@ uint16_t Mcp4461Component::get_eeprom_value(Mcp4461EepromLocation location) {
if (!this->is_eeprom_ready_for_writing_(true)) {
return 0;
}
if (!this->read_byte_16(reg, &buf)) {
if (!this->read_16_(reg, &buf)) {
this->error_code_ = MCP4461_STATUS_I2C_ERROR;
this->status_set_warning();
ESP_LOGW(TAG, "Error fetching EEPROM location value");

View File

@@ -96,6 +96,7 @@ class Mcp4461Component : public Component, public i2c::I2CDevice {
protected:
friend class Mcp4461Wiper;
bool read_16_(uint8_t address, uint16_t *buf);
void update_write_protection_status_();
uint8_t get_wiper_address_(uint8_t wiper);
uint16_t read_wiper_level_(uint8_t wiper);

View File

@@ -24,139 +24,100 @@ static const char *const TAG = "mdns";
void MDNSComponent::compile_records_() {
this->hostname_ = App.get_name();
// Calculate exact capacity needed for services vector
size_t services_count = 0;
this->services_.clear();
#ifdef USE_API
if (api::global_api_server != nullptr) {
services_count++;
}
#endif
#ifdef USE_PROMETHEUS
services_count++;
#endif
#ifdef USE_WEBSERVER
services_count++;
#endif
#ifdef USE_MDNS_EXTRA_SERVICES
services_count += this->services_extra_.size();
#endif
// Reserve for fallback service if needed
if (services_count == 0) {
services_count = 1;
}
this->services_.reserve(services_count);
#ifdef USE_API
if (api::global_api_server != nullptr) {
this->services_.emplace_back();
auto &service = this->services_.back();
MDNSService service{};
service.service_type = "_esphomelib";
service.proto = "_tcp";
service.port = api::global_api_server->get_port();
const std::string &friendly_name = App.get_friendly_name();
bool friendly_name_empty = friendly_name.empty();
// Calculate exact capacity for txt_records
size_t txt_count = 3; // version, mac, board (always present)
if (!friendly_name_empty) {
txt_count++; // friendly_name
if (!App.get_friendly_name().empty()) {
service.txt_records.push_back({"friendly_name", App.get_friendly_name()});
}
#if defined(USE_ESP8266) || defined(USE_ESP32) || defined(USE_RP2040) || defined(USE_LIBRETINY)
txt_count++; // platform
#endif
#if defined(USE_WIFI) || defined(USE_ETHERNET) || defined(USE_OPENTHREAD)
txt_count++; // network
#endif
#ifdef USE_API_NOISE
txt_count++; // api_encryption or api_encryption_supported
#endif
#ifdef ESPHOME_PROJECT_NAME
txt_count += 2; // project_name and project_version
#endif
#ifdef USE_DASHBOARD_IMPORT
txt_count++; // package_import_url
#endif
auto &txt_records = service.txt_records;
txt_records.reserve(txt_count);
if (!friendly_name_empty) {
txt_records.emplace_back(MDNSTXTRecord{"friendly_name", friendly_name});
}
txt_records.emplace_back(MDNSTXTRecord{"version", ESPHOME_VERSION});
txt_records.emplace_back(MDNSTXTRecord{"mac", get_mac_address()});
service.txt_records.push_back({"version", ESPHOME_VERSION});
service.txt_records.push_back({"mac", get_mac_address()});
const char *platform = nullptr;
#ifdef USE_ESP8266
txt_records.emplace_back(MDNSTXTRecord{"platform", "ESP8266"});
#elif defined(USE_ESP32)
txt_records.emplace_back(MDNSTXTRecord{"platform", "ESP32"});
#elif defined(USE_RP2040)
txt_records.emplace_back(MDNSTXTRecord{"platform", "RP2040"});
#elif defined(USE_LIBRETINY)
txt_records.emplace_back(MDNSTXTRecord{"platform", lt_cpu_get_model_name()});
platform = "ESP8266";
#endif
#ifdef USE_ESP32
platform = "ESP32";
#endif
#ifdef USE_RP2040
platform = "RP2040";
#endif
#ifdef USE_LIBRETINY
platform = lt_cpu_get_model_name();
#endif
if (platform != nullptr) {
service.txt_records.push_back({"platform", platform});
}
txt_records.emplace_back(MDNSTXTRecord{"board", ESPHOME_BOARD});
service.txt_records.push_back({"board", ESPHOME_BOARD});
#if defined(USE_WIFI)
txt_records.emplace_back(MDNSTXTRecord{"network", "wifi"});
service.txt_records.push_back({"network", "wifi"});
#elif defined(USE_ETHERNET)
txt_records.emplace_back(MDNSTXTRecord{"network", "ethernet"});
service.txt_records.push_back({"network", "ethernet"});
#elif defined(USE_OPENTHREAD)
txt_records.emplace_back(MDNSTXTRecord{"network", "thread"});
service.txt_records.push_back({"network", "thread"});
#endif
#ifdef USE_API_NOISE
static constexpr const char *NOISE_ENCRYPTION = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
if (api::global_api_server->get_noise_ctx()->has_psk()) {
txt_records.emplace_back(MDNSTXTRecord{"api_encryption", NOISE_ENCRYPTION});
service.txt_records.push_back({"api_encryption", "Noise_NNpsk0_25519_ChaChaPoly_SHA256"});
} else {
txt_records.emplace_back(MDNSTXTRecord{"api_encryption_supported", NOISE_ENCRYPTION});
service.txt_records.push_back({"api_encryption_supported", "Noise_NNpsk0_25519_ChaChaPoly_SHA256"});
}
#endif
#ifdef ESPHOME_PROJECT_NAME
txt_records.emplace_back(MDNSTXTRecord{"project_name", ESPHOME_PROJECT_NAME});
txt_records.emplace_back(MDNSTXTRecord{"project_version", ESPHOME_PROJECT_VERSION});
service.txt_records.push_back({"project_name", ESPHOME_PROJECT_NAME});
service.txt_records.push_back({"project_version", ESPHOME_PROJECT_VERSION});
#endif // ESPHOME_PROJECT_NAME
#ifdef USE_DASHBOARD_IMPORT
txt_records.emplace_back(MDNSTXTRecord{"package_import_url", dashboard_import::get_package_import_url()});
service.txt_records.push_back({"package_import_url", dashboard_import::get_package_import_url()});
#endif
this->services_.push_back(service);
}
#endif // USE_API
#ifdef USE_PROMETHEUS
this->services_.emplace_back();
auto &prom_service = this->services_.back();
prom_service.service_type = "_prometheus-http";
prom_service.proto = "_tcp";
prom_service.port = USE_WEBSERVER_PORT;
{
MDNSService service{};
service.service_type = "_prometheus-http";
service.proto = "_tcp";
service.port = USE_WEBSERVER_PORT;
this->services_.push_back(service);
}
#endif
#ifdef USE_WEBSERVER
this->services_.emplace_back();
auto &web_service = this->services_.back();
web_service.service_type = "_http";
web_service.proto = "_tcp";
web_service.port = USE_WEBSERVER_PORT;
{
MDNSService service{};
service.service_type = "_http";
service.proto = "_tcp";
service.port = USE_WEBSERVER_PORT;
this->services_.push_back(service);
}
#endif
#ifdef USE_MDNS_EXTRA_SERVICES
this->services_.insert(this->services_.end(), this->services_extra_.begin(), this->services_extra_.end());
#endif
#if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_WEBSERVER) && !defined(USE_MDNS_EXTRA_SERVICES)
// Publish "http" service if not using native API or any other services
// This is just to have *some* mDNS service so that .local resolution works
this->services_.emplace_back();
auto &fallback_service = this->services_.back();
fallback_service.service_type = "_http";
fallback_service.proto = "_tcp";
fallback_service.port = USE_WEBSERVER_PORT;
fallback_service.txt_records.emplace_back(MDNSTXTRecord{"version", ESPHOME_VERSION});
#endif
if (this->services_.empty()) {
// Publish "http" service if not using native API
// This is just to have *some* mDNS service so that .local resolution works
MDNSService service{};
service.service_type = "_http";
service.proto = "_tcp";
service.port = USE_WEBSERVER_PORT;
service.txt_records.push_back({"version", ESPHOME_VERSION});
this->services_.push_back(service);
}
}
void MDNSComponent::dump_config() {
@@ -164,7 +125,6 @@ void MDNSComponent::dump_config() {
"mDNS:\n"
" Hostname: %s",
this->hostname_.c_str());
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
ESP_LOGV(TAG, " Services:");
for (const auto &service : this->services_) {
ESP_LOGV(TAG, " - %s, %s, %d", service.service_type.c_str(), service.proto.c_str(),
@@ -174,7 +134,6 @@ void MDNSComponent::dump_config() {
const_cast<TemplatableValue<std::string> &>(record.value).value().c_str());
}
}
#endif
}
std::vector<MDNSService> MDNSComponent::get_services() { return this->services_; }

View File

@@ -201,7 +201,7 @@ def _validate_manifest_version(manifest_data):
else:
raise cv.Invalid("Invalid manifest version")
else:
raise cv.Invalid("Invalid manifest file, missing 'version' key")
raise cv.Invalid("Invalid manifest file, missing 'version' key.")
def _process_http_source(config):
@@ -421,7 +421,7 @@ def _feature_step_size_validate(config):
if features_step_size is None:
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(

View File

@@ -16,7 +16,6 @@ DriverChip(
lane_bit_rate="750Mbps",
swap_xy=cv.UNDEFINED,
color_order="RGB",
reset_pin=27,
initsequence=[
(0x30, 0x00), (0xF7, 0x49, 0x61, 0x02, 0x00), (0x30, 0x01), (0x04, 0x0C), (0x05, 0x00), (0x06, 0x00),
(0x0B, 0x11), (0x17, 0x00), (0x20, 0x04), (0x1F, 0x05), (0x23, 0x00), (0x25, 0x19), (0x28, 0x18), (0x29, 0x04), (0x2A, 0x01),

View File

@@ -255,233 +255,4 @@ DriverChip(
),
)
DriverChip(
"JC3636W518V2",
height=360,
width=360,
offset_height=1,
draw_rounding=1,
cs_pin=10,
reset_pin=47,
invert_colors=True,
color_order=MODE_RGB,
bus_mode=TYPE_QUAD,
data_rate="40MHz",
initsequence=(
(0xF0, 0x28),
(0xF2, 0x28),
(0x73, 0xF0),
(0x7C, 0xD1),
(0x83, 0xE0),
(0x84, 0x61),
(0xF2, 0x82),
(0xF0, 0x00),
(0xF0, 0x01),
(0xF1, 0x01),
(0xB0, 0x56),
(0xB1, 0x4D),
(0xB2, 0x24),
(0xB4, 0x87),
(0xB5, 0x44),
(0xB6, 0x8B),
(0xB7, 0x40),
(0xB8, 0x86),
(0xBA, 0x00),
(0xBB, 0x08),
(0xBC, 0x08),
(0xBD, 0x00),
(0xC0, 0x80),
(0xC1, 0x10),
(0xC2, 0x37),
(0xC3, 0x80),
(0xC4, 0x10),
(0xC5, 0x37),
(0xC6, 0xA9),
(0xC7, 0x41),
(0xC8, 0x01),
(0xC9, 0xA9),
(0xCA, 0x41),
(0xCB, 0x01),
(0xD0, 0x91),
(0xD1, 0x68),
(0xD2, 0x68),
(0xF5, 0x00, 0xA5),
(0xDD, 0x4F),
(0xDE, 0x4F),
(0xF1, 0x10),
(0xF0, 0x00),
(0xF0, 0x02),
(
0xE0,
0xF0,
0x0A,
0x10,
0x09,
0x09,
0x36,
0x35,
0x33,
0x4A,
0x29,
0x15,
0x15,
0x2E,
0x34,
),
(
0xE1,
0xF0,
0x0A,
0x0F,
0x08,
0x08,
0x05,
0x34,
0x33,
0x4A,
0x39,
0x15,
0x15,
0x2D,
0x33,
),
(0xF0, 0x10),
(0xF3, 0x10),
(0xE0, 0x07),
(0xE1, 0x00),
(0xE2, 0x00),
(0xE3, 0x00),
(0xE4, 0xE0),
(0xE5, 0x06),
(0xE6, 0x21),
(0xE7, 0x01),
(0xE8, 0x05),
(0xE9, 0x02),
(0xEA, 0xDA),
(0xEB, 0x00),
(0xEC, 0x00),
(0xED, 0x0F),
(0xEE, 0x00),
(0xEF, 0x00),
(0xF8, 0x00),
(0xF9, 0x00),
(0xFA, 0x00),
(0xFB, 0x00),
(0xFC, 0x00),
(0xFD, 0x00),
(0xFE, 0x00),
(0xFF, 0x00),
(0x60, 0x40),
(0x61, 0x04),
(0x62, 0x00),
(0x63, 0x42),
(0x64, 0xD9),
(0x65, 0x00),
(0x66, 0x00),
(0x67, 0x00),
(0x68, 0x00),
(0x69, 0x00),
(0x6A, 0x00),
(0x6B, 0x00),
(0x70, 0x40),
(0x71, 0x03),
(0x72, 0x00),
(0x73, 0x42),
(0x74, 0xD8),
(0x75, 0x00),
(0x76, 0x00),
(0x77, 0x00),
(0x78, 0x00),
(0x79, 0x00),
(0x7A, 0x00),
(0x7B, 0x00),
(0x80, 0x48),
(0x81, 0x00),
(0x82, 0x06),
(0x83, 0x02),
(0x84, 0xD6),
(0x85, 0x04),
(0x86, 0x00),
(0x87, 0x00),
(0x88, 0x48),
(0x89, 0x00),
(0x8A, 0x08),
(0x8B, 0x02),
(0x8C, 0xD8),
(0x8D, 0x04),
(0x8E, 0x00),
(0x8F, 0x00),
(0x90, 0x48),
(0x91, 0x00),
(0x92, 0x0A),
(0x93, 0x02),
(0x94, 0xDA),
(0x95, 0x04),
(0x96, 0x00),
(0x97, 0x00),
(0x98, 0x48),
(0x99, 0x00),
(0x9A, 0x0C),
(0x9B, 0x02),
(0x9C, 0xDC),
(0x9D, 0x04),
(0x9E, 0x00),
(0x9F, 0x00),
(0xA0, 0x48),
(0xA1, 0x00),
(0xA2, 0x05),
(0xA3, 0x02),
(0xA4, 0xD5),
(0xA5, 0x04),
(0xA6, 0x00),
(0xA7, 0x00),
(0xA8, 0x48),
(0xA9, 0x00),
(0xAA, 0x07),
(0xAB, 0x02),
(0xAC, 0xD7),
(0xAD, 0x04),
(0xAE, 0x00),
(0xAF, 0x00),
(0xB0, 0x48),
(0xB1, 0x00),
(0xB2, 0x09),
(0xB3, 0x02),
(0xB4, 0xD9),
(0xB5, 0x04),
(0xB6, 0x00),
(0xB7, 0x00),
(0xB8, 0x48),
(0xB9, 0x00),
(0xBA, 0x0B),
(0xBB, 0x02),
(0xBC, 0xDB),
(0xBD, 0x04),
(0xBE, 0x00),
(0xBF, 0x00),
(0xC0, 0x10),
(0xC1, 0x47),
(0xC2, 0x56),
(0xC3, 0x65),
(0xC4, 0x74),
(0xC5, 0x88),
(0xC6, 0x99),
(0xC7, 0x01),
(0xC8, 0xBB),
(0xC9, 0xAA),
(0xD0, 0x10),
(0xD1, 0x47),
(0xD2, 0x56),
(0xD3, 0x65),
(0xD4, 0x74),
(0xD5, 0x88),
(0xD6, 0x99),
(0xD7, 0x01),
(0xD8, 0xBB),
(0xD9, 0xAA),
(0xF3, 0x01),
(0xF0, 0x00),
),
)
models = {}

View File

@@ -50,13 +50,28 @@ bool MLX90614Component::write_emissivity_() {
return true;
}
uint8_t MLX90614Component::crc8_pec_(const uint8_t *data, uint8_t len) {
uint8_t crc = 0;
for (uint8_t i = 0; i < len; i++) {
uint8_t in = data[i];
for (uint8_t j = 0; j < 8; j++) {
bool carry = (crc ^ in) & 0x80;
crc <<= 1;
if (carry)
crc ^= 0x07;
in <<= 1;
}
}
return crc;
}
bool MLX90614Component::write_bytes_(uint8_t reg, uint16_t data) {
uint8_t buf[5];
buf[0] = this->address_ << 1;
buf[1] = reg;
buf[2] = data & 0xFF;
buf[3] = data >> 8;
buf[4] = crc8(buf, 4, 0x00, 0x07, true);
buf[4] = this->crc8_pec_(buf, 4);
return this->write_bytes(reg, buf + 2, 3);
}

View File

@@ -22,6 +22,7 @@ class MLX90614Component : public PollingComponent, public i2c::I2CDevice {
protected:
bool write_emissivity_();
uint8_t crc8_pec_(const uint8_t *data, uint8_t len);
bool write_bytes_(uint8_t reg, uint16_t data);
sensor::Sensor *ambient_sensor_{nullptr};

View File

@@ -119,8 +119,8 @@ async def to_code(config: ConfigType) -> None:
cg.add_platformio_option(
"platform_packages",
[
"platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v2.6.1-7.zip",
"platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.17.4-0.zip",
"platformio/framework-zephyr@https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v2.6.1-4.zip",
"platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.16.1-1.zip",
],
)

View File

@@ -11,7 +11,7 @@ void NTC::setup() {
if (this->sensor_->has_state())
this->process_(this->sensor_->state);
}
void NTC::dump_config() { LOG_SENSOR("", "NTC Sensor", this); }
void NTC::dump_config() { LOG_SENSOR("", "NTC Sensor", this) }
float NTC::get_setup_priority() const { return setup_priority::DATA; }
void NTC::process_(float value) {
if (std::isnan(value)) {

View File

@@ -15,7 +15,7 @@ void ValueRangeTrigger::setup() {
float local_min = this->min_.value(0.0);
float local_max = this->max_.value(0.0);
convert hash = {.from = (local_max - local_min)};
uint32_t myhash = hash.to ^ this->parent_->get_preference_hash();
uint32_t myhash = hash.to ^ this->parent_->get_object_id_hash();
this->rtc_ = global_preferences->make_preference<bool>(myhash);
bool initial_state;
if (this->rtc_.load(&initial_state)) {

View File

@@ -6,27 +6,6 @@ namespace number {
static const char *const TAG = "number";
// Function implementation of LOG_NUMBER macro to reduce code size
void log_number(const char *tag, const char *prefix, const char *type, Number *obj) {
if (obj == nullptr) {
return;
}
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
if (!obj->get_icon().empty()) {
ESP_LOGCONFIG(tag, "%s Icon: '%s'", prefix, obj->get_icon().c_str());
}
if (!obj->traits.get_unit_of_measurement().empty()) {
ESP_LOGCONFIG(tag, "%s Unit of Measurement: '%s'", prefix, obj->traits.get_unit_of_measurement().c_str());
}
if (!obj->traits.get_device_class().empty()) {
ESP_LOGCONFIG(tag, "%s Device Class: '%s'", prefix, obj->traits.get_device_class().c_str());
}
}
void Number::publish_state(float state) {
this->set_has_state(true);
this->state = state;

View File

@@ -9,10 +9,19 @@
namespace esphome {
namespace number {
class Number;
void log_number(const char *tag, const char *prefix, const char *type, Number *obj);
#define LOG_NUMBER(prefix, type, obj) log_number(TAG, prefix, LOG_STR_LITERAL(type), obj)
#define LOG_NUMBER(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
} \
if (!(obj)->traits.get_unit_of_measurement().empty()) { \
ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->traits.get_unit_of_measurement().c_str()); \
} \
if (!(obj)->traits.get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->traits.get_device_class().c_str()); \
} \
}
#define SUB_NUMBER(name) \
protected: \

View File

@@ -1,10 +1,10 @@
#pragma once
#include <vector>
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include <vector>
#include "opentherm.h"
@@ -17,21 +17,21 @@
#endif
#ifdef OPENTHERM_USE_SWITCH
#include "esphome/components/opentherm/switch/opentherm_switch.h"
#include "esphome/components/opentherm/switch/switch.h"
#endif
#ifdef OPENTHERM_USE_OUTPUT
#include "esphome/components/opentherm/output/opentherm_output.h"
#include "esphome/components/opentherm/output/output.h"
#endif
#ifdef OPENTHERM_USE_NUMBER
#include "esphome/components/opentherm/number/opentherm_number.h"
#include "esphome/components/opentherm/number/number.h"
#endif
#include <functional>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include "opentherm_macros.h"

View File

@@ -1,4 +1,4 @@
#include "opentherm_number.h"
#include "number.h"
namespace esphome {
namespace opentherm {
@@ -17,7 +17,7 @@ void OpenthermNumber::setup() {
if (!this->restore_value_) {
value = this->initial_value_;
} else {
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
if (!this->pref_.load(&value)) {
if (!std::isnan(this->initial_value_)) {
value = this->initial_value_;

View File

@@ -1,5 +1,5 @@
#include "esphome/core/helpers.h" // for clamp() and lerp()
#include "opentherm_output.h"
#include "output.h"
namespace esphome {
namespace opentherm {

View File

@@ -1,4 +1,4 @@
#include "opentherm_switch.h"
#include "switch.h"
namespace esphome {
namespace opentherm {

View File

@@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.All(
)
async def to_code(config):
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
yield cg.register_component(var, config)
yield uart.register_uart_device(var, config)

View File

@@ -99,9 +99,9 @@ async def to_code(config):
}
),
)
async def output_pipsolar_set_level_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
def output_pipsolar_set_level_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_VALUE], args, float)
template_ = yield cg.templatable(config[CONF_VALUE], args, float)
cg.add(var.set_level(template_))
return var
yield var

View File

@@ -17,7 +17,7 @@ void IRAM_ATTR PulseWidthSensorStore::gpio_intr(PulseWidthSensorStore *arg) {
}
void PulseWidthSensor::dump_config() {
LOG_SENSOR("", "Pulse Width", this);
LOG_SENSOR("", "Pulse Width", this)
LOG_UPDATE_INTERVAL(this)
LOG_PIN(" Pin: ", this->pin_);
}

View File

@@ -18,6 +18,14 @@ 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_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 */
static const uint8_t QMP6988_SLEEP_MODE = 0x00;
static const uint8_t QMP6988_FORCED_MODE = 0x01;
@@ -87,45 +95,64 @@ static const char *iir_filter_to_str(QMP6988IIRFilter filter) {
}
bool QMP6988Component::device_check_() {
if (this->read_register(QMP6988_CHIP_ID_REG, &(qmp6988_data_.chip_id), 1) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Read chip ID (0xD1) failed");
return false;
uint8_t ret = 0;
ret = this->read_register(QMP6988_CHIP_ID_REG, &(qmp6988_data_.chip_id), 1);
if (ret != i2c::ERROR_OK) {
ESP_LOGE(TAG, "%s: read chip ID (0xD1) failed", __func__);
}
ESP_LOGV(TAG, "Read chip ID = 0x%x", qmp6988_data_.chip_id);
ESP_LOGD(TAG, "qmp6988 read chip id = 0x%x", qmp6988_data_.chip_id);
return qmp6988_data_.chip_id == QMP6988_CHIP_ID;
}
bool QMP6988Component::get_calibration_data_() {
uint8_t status = 0;
// BITFIELDS temp_COE;
uint8_t a_data_uint8_tr[QMP6988_CALIBRATION_DATA_LENGTH] = {0};
int len;
for (uint8_t len = 0; len < QMP6988_CALIBRATION_DATA_LENGTH; len += 1) {
if (this->read_register(QMP6988_CALIBRATION_DATA_START + len, &a_data_uint8_tr[len], 1) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Read calibration data (0xA0) error");
for (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 (status != i2c::ERROR_OK) {
ESP_LOGE(TAG, "qmp6988 read calibration data (0xA0) error!");
return false;
}
}
qmp6988_data_.qmp6988_cali.COE_a0 =
(int32_t) encode_uint32(a_data_uint8_tr[18], a_data_uint8_tr[19], (a_data_uint8_tr[24] & 0x0f) << 4, 0);
(QMP6988_S32_t) (((a_data_uint8_tr[18] << SHIFT_LEFT_12_POSITION) |
(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_a1 = (int16_t) encode_uint16(a_data_uint8_tr[20], 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_a1 =
(QMP6988_S16_t) (((a_data_uint8_tr[20]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[21]);
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 =
(int32_t) encode_uint32(a_data_uint8_tr[0], a_data_uint8_tr[1], a_data_uint8_tr[24] & 0xf0, 0);
(QMP6988_S32_t) (((a_data_uint8_tr[0] << SHIFT_LEFT_12_POSITION) | (a_data_uint8_tr[1] << SHIFT_LEFT_4_POSITION) |
((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_bt1 = (int16_t) encode_uint16(a_data_uint8_tr[2], 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_bp1 = (int16_t) encode_uint16(a_data_uint8_tr[6], a_data_uint8_tr[7]);
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_bp2 = (int16_t) encode_uint16(a_data_uint8_tr[10], a_data_uint8_tr[11]);
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_b21 = (int16_t) encode_uint16(a_data_uint8_tr[14], a_data_uint8_tr[15]);
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_bt1 =
(QMP6988_S16_t) (((a_data_uint8_tr[2]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[3]);
qmp6988_data_.qmp6988_cali.COE_bt2 =
(QMP6988_S16_t) (((a_data_uint8_tr[4]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[5]);
qmp6988_data_.qmp6988_cali.COE_bp1 =
(QMP6988_S16_t) (((a_data_uint8_tr[6]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[7]);
qmp6988_data_.qmp6988_cali.COE_b11 =
(QMP6988_S16_t) (((a_data_uint8_tr[8]) << SHIFT_LEFT_8_POSITION) | a_data_uint8_tr[9]);
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, "COE_a0[%d] COE_a1[%d] COE_a2[%d] COE_b00[%d]\r\n", qmp6988_data_.qmp6988_cali.COE_a0,
@@ -139,17 +166,17 @@ bool QMP6988Component::get_calibration_data_() {
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.a1 = 3608L * (int32_t) qmp6988_data_.qmp6988_cali.COE_a1 - 1731677965L; // 31Q23
qmp6988_data_.ik.a2 = 16889L * (int32_t) qmp6988_data_.qmp6988_cali.COE_a2 - 87619360L; // 30Q47
qmp6988_data_.ik.a1 = 3608L * (QMP6988_S32_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.bt1 = 2982L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bt1 + 107370906L; // 28Q15
qmp6988_data_.ik.bt2 = 329854L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bt2 + 108083093L; // 34Q38
qmp6988_data_.ik.bp1 = 19923L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bp1 + 1133836764L; // 31Q20
qmp6988_data_.ik.b11 = 2406L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b11 + 118215883L; // 28Q34
qmp6988_data_.ik.bp2 = 3079L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bp2 - 181579595L; // 29Q43
qmp6988_data_.ik.b12 = 6846L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b12 + 85590281L; // 29Q53
qmp6988_data_.ik.b21 = 13836L * (int64_t) qmp6988_data_.qmp6988_cali.COE_b21 + 79333336L; // 29Q60
qmp6988_data_.ik.bp3 = 2915L * (int64_t) qmp6988_data_.qmp6988_cali.COE_bp3 + 157155561L; // 28Q65
qmp6988_data_.ik.bt1 = 2982L * (QMP6988_S64_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.bp1 = 19923L * (QMP6988_S64_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.bp2 = 3079L * (QMP6988_S64_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.b21 = 13836L * (QMP6988_S64_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
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,
qmp6988_data_.ik.b00);
@@ -161,55 +188,55 @@ bool QMP6988Component::get_calibration_data_() {
return true;
}
int16_t QMP6988Component::get_compensated_temperature_(qmp6988_ik_data_t *ik, int32_t dt) {
int16_t ret;
int64_t wk1, wk2;
QMP6988_S16_t QMP6988Component::get_compensated_temperature_(qmp6988_ik_data_t *ik, QMP6988_S32_t dt) {
QMP6988_S16_t ret;
QMP6988_S64_t wk1, wk2;
// wk1: 60Q4 // bit size
wk1 = ((int64_t) ik->a1 * (int64_t) dt); // 31Q23+24-1=54 (54Q23)
wk2 = ((int64_t) ik->a2 * (int64_t) dt) >> 14; // 30Q47+24-1=53 (39Q33)
wk2 = (wk2 * (int64_t) dt) >> 10; // 39Q33+24-1=62 (52Q23)
wk2 = ((wk1 + wk2) / 32767) >> 19; // 54,52->55Q23 (20Q04)
ret = (int16_t) ((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0
wk1 = ((QMP6988_S64_t) ik->a1 * (QMP6988_S64_t) dt); // 31Q23+24-1=54 (54Q23)
wk2 = ((QMP6988_S64_t) ik->a2 * (QMP6988_S64_t) dt) >> 14; // 30Q47+24-1=53 (39Q33)
wk2 = (wk2 * (QMP6988_S64_t) dt) >> 10; // 39Q33+24-1=62 (52Q23)
wk2 = ((wk1 + wk2) / 32767) >> 19; // 54,52->55Q23 (20Q04)
ret = (QMP6988_S16_t) ((ik->a0 + wk2) >> 4); // 21Q4 -> 17Q0
return ret;
}
int32_t QMP6988Component::get_compensated_pressure_(qmp6988_ik_data_t *ik, int32_t dp, int16_t tx) {
int32_t ret;
int64_t wk1, wk2, wk3;
QMP6988_S32_t QMP6988Component::get_compensated_pressure_(qmp6988_ik_data_t *ik, QMP6988_S32_t dp, QMP6988_S16_t tx) {
QMP6988_S32_t ret;
QMP6988_S64_t wk1, wk2, wk3;
// wk1 = 48Q16 // bit size
wk1 = ((int64_t) ik->bt1 * (int64_t) tx); // 28Q15+16-1=43 (43Q15)
wk2 = ((int64_t) ik->bp1 * (int64_t) dp) >> 5; // 31Q20+24-1=54 (49Q15)
wk1 += wk2; // 43,49->50Q15
wk2 = ((int64_t) ik->bt2 * (int64_t) tx) >> 1; // 34Q38+16-1=49 (48Q37)
wk2 = (wk2 * (int64_t) tx) >> 8; // 48Q37+16-1=63 (55Q29)
wk3 = wk2; // 55Q29
wk2 = ((int64_t) ik->b11 * (int64_t) tx) >> 4; // 28Q34+16-1=43 (39Q30)
wk2 = (wk2 * (int64_t) dp) >> 1; // 39Q30+24-1=62 (61Q29)
wk3 += wk2; // 55,61->62Q29
wk2 = ((int64_t) ik->bp2 * (int64_t) dp) >> 13; // 29Q43+24-1=52 (39Q30)
wk2 = (wk2 * (int64_t) dp) >> 1; // 39Q30+24-1=62 (61Q29)
wk3 += wk2; // 62,61->63Q29
wk1 += wk3 >> 14; // Q29 >> 14 -> Q15
wk2 = ((int64_t) ik->b12 * (int64_t) tx); // 29Q53+16-1=45 (45Q53)
wk2 = (wk2 * (int64_t) tx) >> 22; // 45Q53+16-1=61 (39Q31)
wk2 = (wk2 * (int64_t) dp) >> 1; // 39Q31+24-1=62 (61Q30)
wk3 = wk2; // 61Q30
wk2 = ((int64_t) ik->b21 * (int64_t) tx) >> 6; // 29Q60+16-1=45 (39Q54)
wk2 = (wk2 * (int64_t) dp) >> 23; // 39Q54+24-1=62 (39Q31)
wk2 = (wk2 * (int64_t) dp) >> 1; // 39Q31+24-1=62 (61Q20)
wk3 += wk2; // 61,61->62Q30
wk2 = ((int64_t) ik->bp3 * (int64_t) dp) >> 12; // 28Q65+24-1=51 (39Q53)
wk2 = (wk2 * (int64_t) dp) >> 23; // 39Q53+24-1=62 (39Q30)
wk2 = (wk2 * (int64_t) dp); // 39Q30+24-1=62 (62Q30)
wk3 += wk2; // 62,62->63Q30
wk1 += wk3 >> 15; // Q30 >> 15 = Q15
wk1 = ((QMP6988_S64_t) ik->bt1 * (QMP6988_S64_t) tx); // 28Q15+16-1=43 (43Q15)
wk2 = ((QMP6988_S64_t) ik->bp1 * (QMP6988_S64_t) dp) >> 5; // 31Q20+24-1=54 (49Q15)
wk1 += wk2; // 43,49->50Q15
wk2 = ((QMP6988_S64_t) ik->bt2 * (QMP6988_S64_t) tx) >> 1; // 34Q38+16-1=49 (48Q37)
wk2 = (wk2 * (QMP6988_S64_t) tx) >> 8; // 48Q37+16-1=63 (55Q29)
wk3 = wk2; // 55Q29
wk2 = ((QMP6988_S64_t) ik->b11 * (QMP6988_S64_t) tx) >> 4; // 28Q34+16-1=43 (39Q30)
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 1; // 39Q30+24-1=62 (61Q29)
wk3 += wk2; // 55,61->62Q29
wk2 = ((QMP6988_S64_t) ik->bp2 * (QMP6988_S64_t) dp) >> 13; // 29Q43+24-1=52 (39Q30)
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 1; // 39Q30+24-1=62 (61Q29)
wk3 += wk2; // 62,61->63Q29
wk1 += wk3 >> 14; // Q29 >> 14 -> Q15
wk2 = ((QMP6988_S64_t) ik->b12 * (QMP6988_S64_t) tx); // 29Q53+16-1=45 (45Q53)
wk2 = (wk2 * (QMP6988_S64_t) tx) >> 22; // 45Q53+16-1=61 (39Q31)
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 1; // 39Q31+24-1=62 (61Q30)
wk3 = wk2; // 61Q30
wk2 = ((QMP6988_S64_t) ik->b21 * (QMP6988_S64_t) tx) >> 6; // 29Q60+16-1=45 (39Q54)
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 23; // 39Q54+24-1=62 (39Q31)
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 1; // 39Q31+24-1=62 (61Q20)
wk3 += wk2; // 61,61->62Q30
wk2 = ((QMP6988_S64_t) ik->bp3 * (QMP6988_S64_t) dp) >> 12; // 28Q65+24-1=51 (39Q53)
wk2 = (wk2 * (QMP6988_S64_t) dp) >> 23; // 39Q53+24-1=62 (39Q30)
wk2 = (wk2 * (QMP6988_S64_t) dp); // 39Q30+24-1=62 (62Q30)
wk3 += wk2; // 62,62->63Q30
wk1 += wk3 >> 15; // Q30 >> 15 = Q15
wk1 /= 32767L;
wk1 >>= 11; // Q15 >> 7 = Q4
wk1 += ik->b00; // Q4 + 20Q4
// wk1 >>= 4; // 28Q4 -> 24Q0
ret = (int32_t) wk1;
ret = (QMP6988_S32_t) wk1;
return ret;
}
@@ -247,7 +274,7 @@ void QMP6988Component::set_power_mode_(uint8_t power_mode) {
delay(10);
}
void QMP6988Component::write_filter_(QMP6988IIRFilter filter) {
void QMP6988Component::write_filter_(unsigned char filter) {
uint8_t data;
data = (filter & 0x03);
@@ -255,7 +282,7 @@ void QMP6988Component::write_filter_(QMP6988IIRFilter filter) {
delay(10);
}
void QMP6988Component::write_oversampling_pressure_(QMP6988Oversampling oversampling_p) {
void QMP6988Component::write_oversampling_pressure_(unsigned char oversampling_p) {
uint8_t data;
this->read_register(QMP6988_CTRLMEAS_REG, &data, 1);
@@ -265,7 +292,7 @@ void QMP6988Component::write_oversampling_pressure_(QMP6988Oversampling oversamp
delay(10);
}
void QMP6988Component::write_oversampling_temperature_(QMP6988Oversampling oversampling_t) {
void QMP6988Component::write_oversampling_temperature_(unsigned char oversampling_t) {
uint8_t data;
this->read_register(QMP6988_CTRLMEAS_REG, &data, 1);
@@ -275,6 +302,16 @@ void QMP6988Component::write_oversampling_temperature_(QMP6988Oversampling overs
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) {
float altitude;
altitude = (pow((101325 / pressure), 1 / 5.257) - 1) * (temp + 273.15) / 0.0065;
@@ -283,10 +320,10 @@ void QMP6988Component::calculate_altitude_(float pressure, float temp) {
void QMP6988Component::calculate_pressure_() {
uint8_t err = 0;
uint32_t p_read, t_read;
int32_t p_raw, t_raw;
QMP6988_U32_t p_read, t_read;
QMP6988_S32_t p_raw, t_raw;
uint8_t a_data_uint8_tr[6] = {0};
int32_t t_int, p_int;
QMP6988_S32_t t_int, p_int;
this->qmp6988_data_.temperature = 0;
this->qmp6988_data_.pressure = 0;
@@ -295,11 +332,13 @@ void QMP6988Component::calculate_pressure_() {
ESP_LOGE(TAG, "Error reading raw pressure/temp values");
return;
}
p_read = encode_uint24(a_data_uint8_tr[0], a_data_uint8_tr[1], a_data_uint8_tr[2]);
p_raw = (int32_t) (p_read - SUBTRACTOR);
p_read = (QMP6988_U32_t) ((((QMP6988_U32_t) (a_data_uint8_tr[0])) << SHIFT_LEFT_16_POSITION) |
(((QMP6988_U16_t) (a_data_uint8_tr[1])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[2]));
p_raw = (QMP6988_S32_t) (p_read - SUBTRACTOR);
t_read = encode_uint24(a_data_uint8_tr[3], a_data_uint8_tr[4], a_data_uint8_tr[5]);
t_raw = (int32_t) (t_read - SUBTRACTOR);
t_read = (QMP6988_U32_t) ((((QMP6988_U32_t) (a_data_uint8_tr[3])) << SHIFT_LEFT_16_POSITION) |
(((QMP6988_U16_t) (a_data_uint8_tr[4])) << SHIFT_LEFT_8_POSITION) | (a_data_uint8_tr[5]));
t_raw = (QMP6988_S32_t) (t_read - SUBTRACTOR);
t_int = this->get_compensated_temperature_(&(qmp6988_data_.ik), t_raw);
p_int = this->get_compensated_pressure_(&(qmp6988_data_.ik), p_raw, t_int);
@@ -309,9 +348,10 @@ void QMP6988Component::calculate_pressure_() {
}
void QMP6988Component::setup() {
if (!this->device_check_()) {
this->mark_failed(ESP_LOG_MSG_COMM_FAIL);
return;
bool ret;
ret = this->device_check_();
if (!ret) {
ESP_LOGCONFIG(TAG, "Setup failed - device not found");
}
this->software_reset_();
@@ -325,6 +365,9 @@ void QMP6988Component::setup() {
void QMP6988Component::dump_config() {
ESP_LOGCONFIG(TAG, "QMP6988:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
@@ -334,6 +377,8 @@ void QMP6988Component::dump_config() {
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() {
this->calculate_pressure_();
float pressurehectopascals = this->qmp6988_data_.pressure / 100;

View File

@@ -1,17 +1,24 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
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 */
enum QMP6988Oversampling : uint8_t {
enum QMP6988Oversampling {
QMP6988_OVERSAMPLING_SKIPPED = 0x00,
QMP6988_OVERSAMPLING_1X = 0x01,
QMP6988_OVERSAMPLING_2X = 0x02,
@@ -23,7 +30,7 @@ enum QMP6988Oversampling : uint8_t {
};
/* filter */
enum QMP6988IIRFilter : uint8_t {
enum QMP6988IIRFilter {
QMP6988_IIR_FILTER_OFF = 0x00,
QMP6988_IIR_FILTER_2X = 0x01,
QMP6988_IIR_FILTER_4X = 0x02,
@@ -33,18 +40,18 @@ enum QMP6988IIRFilter : uint8_t {
};
using qmp6988_cali_data_t = struct Qmp6988CaliData {
int32_t COE_a0;
int16_t COE_a1;
int16_t COE_a2;
int32_t COE_b00;
int16_t COE_bt1;
int16_t COE_bt2;
int16_t COE_bp1;
int16_t COE_b11;
int16_t COE_bp2;
int16_t COE_b12;
int16_t COE_b21;
int16_t COE_bp3;
QMP6988_S32_t COE_a0;
QMP6988_S16_t COE_a1;
QMP6988_S16_t COE_a2;
QMP6988_S32_t COE_b00;
QMP6988_S16_t COE_bt1;
QMP6988_S16_t COE_bt2;
QMP6988_S16_t COE_bp1;
QMP6988_S16_t COE_b11;
QMP6988_S16_t COE_bp2;
QMP6988_S16_t COE_b12;
QMP6988_S16_t COE_b21;
QMP6988_S16_t COE_bp3;
};
using qmp6988_fk_data_t = struct Qmp6988FkData {
@@ -53,9 +60,9 @@ using qmp6988_fk_data_t = struct Qmp6988FkData {
};
using qmp6988_ik_data_t = struct Qmp6988IkData {
int32_t a0, b00;
int32_t a1, a2;
int64_t bt1, bt2, bp1, b11, bp2, b12, b21, bp3;
QMP6988_S32_t a0, b00;
QMP6988_S32_t a1, a2;
QMP6988_S64_t bt1, bt2, bp1, b11, bp2, b12, b21, bp3;
};
using qmp6988_data_t = struct Qmp6988Data {
@@ -70,18 +77,17 @@ using qmp6988_data_t = struct Qmp6988Data {
class QMP6988Component : public PollingComponent, public i2c::I2CDevice {
public:
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { this->pressure_sensor_ = pressure_sensor; }
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_iir_filter(QMP6988IIRFilter iirfilter) { this->iir_filter_ = iirfilter; }
void set_temperature_oversampling(QMP6988Oversampling oversampling_t) {
this->temperature_oversampling_ = oversampling_t;
}
void set_pressure_oversampling(QMP6988Oversampling oversampling_p) { this->pressure_oversampling_ = oversampling_p; }
void set_iir_filter(QMP6988IIRFilter iirfilter);
void set_temperature_oversampling(QMP6988Oversampling oversampling_t);
void set_pressure_oversampling(QMP6988Oversampling oversampling_p);
protected:
qmp6988_data_t qmp6988_data_;
@@ -96,14 +102,14 @@ class QMP6988Component : public PollingComponent, public i2c::I2CDevice {
bool get_calibration_data_();
bool device_check_();
void set_power_mode_(uint8_t power_mode);
void write_oversampling_temperature_(QMP6988Oversampling oversampling_t);
void write_oversampling_pressure_(QMP6988Oversampling oversampling_p);
void write_filter_(QMP6988IIRFilter filter);
void write_oversampling_temperature_(unsigned char oversampling_t);
void write_oversampling_pressure_(unsigned char oversampling_p);
void write_filter_(unsigned char filter);
void calculate_pressure_();
void calculate_altitude_(float pressure, float temp);
int32_t get_compensated_pressure_(qmp6988_ik_data_t *ik, int32_t dp, int16_t tx);
int16_t get_compensated_temperature_(qmp6988_ik_data_t *ik, int32_t dt);
QMP6988_S32_t get_compensated_pressure_(qmp6988_ik_data_t *ik, QMP6988_S32_t dp, QMP6988_S16_t tx);
QMP6988_S16_t get_compensated_temperature_(qmp6988_ik_data_t *ik, QMP6988_S32_t dt);
};
} // namespace qmp6988

View File

@@ -18,6 +18,6 @@ CONFIG_SCHEMA = cv.Schema(
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
async def to_code(config):
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await esp32_ble_tracker.register_ble_device(var, config)
yield esp32_ble_tracker.register_ble_device(var, config)

View File

@@ -1782,12 +1782,14 @@ def nexa_dumper(var, config):
@register_action("nexa", NexaAction, NEXA_SCHEMA)
async def nexa_action(var, config, args):
cg.add(var.set_device(await cg.templatable(config[CONF_DEVICE], args, cg.uint32)))
cg.add(var.set_group(await cg.templatable(config[CONF_GROUP], args, cg.uint8)))
cg.add(var.set_state(await cg.templatable(config[CONF_STATE], args, cg.uint8)))
cg.add(var.set_channel(await cg.templatable(config[CONF_CHANNEL], args, cg.uint8)))
cg.add(var.set_level(await cg.templatable(config[CONF_LEVEL], args, cg.uint8)))
def nexa_action(var, config, args):
cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.uint32))))
cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.uint8))))
cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, cg.uint8))))
cg.add(
var.set_channel((yield cg.templatable(config[CONF_CHANNEL], args, cg.uint8)))
)
cg.add(var.set_level((yield cg.templatable(config[CONF_LEVEL], args, cg.uint8))))
# Midea

View File

@@ -132,7 +132,7 @@ void RotaryEncoderSensor::setup() {
int32_t initial_value = 0;
switch (this->restore_mode_) {
case ROTARY_ENCODER_RESTORE_DEFAULT_ZERO:
this->rtc_ = global_preferences->make_preference<int32_t>(this->get_preference_hash());
this->rtc_ = global_preferences->make_preference<int32_t>(this->get_object_id_hash());
if (!this->rtc_.load(&initial_value)) {
initial_value = 0;
}

View File

@@ -15,11 +15,11 @@ namespace safe_mode {
static const char *const TAG = "safe_mode";
void SafeModeComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Safe Mode:");
ESP_LOGCONFIG(TAG,
"Safe Mode:\n"
" Successful after: %" PRIu32 "s\n"
" Invoke after: %u attempts\n"
" Duration: %" PRIu32 "s",
" Boot considered successful after %" PRIu32 " seconds\n"
" Invoke after %u boot attempts\n"
" Remain for %" PRIu32 " seconds",
this->safe_mode_boot_is_good_after_ / 1000, // because milliseconds
this->safe_mode_num_attempts_,
this->safe_mode_enable_time_ / 1000); // because milliseconds
@@ -27,7 +27,7 @@ void SafeModeComponent::dump_config() {
if (this->safe_mode_rtc_value_ > 1 && this->safe_mode_rtc_value_ != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) {
auto remaining_restarts = this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_;
if (remaining_restarts) {
ESP_LOGW(TAG, "Last reset too quick; invoke in %" PRIu32 " restarts", remaining_restarts);
ESP_LOGW(TAG, "Last reset occurred too quickly; will be invoked in %" PRIu32 " restarts", remaining_restarts);
} else {
ESP_LOGW(TAG, "SAFE MODE IS ACTIVE");
}
@@ -72,45 +72,43 @@ bool SafeModeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t en
this->safe_mode_boot_is_good_after_ = boot_is_good_after;
this->safe_mode_num_attempts_ = num_attempts;
this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false);
this->safe_mode_rtc_value_ = this->read_rtc_();
uint32_t rtc_val = this->read_rtc_();
this->safe_mode_rtc_value_ = rtc_val;
bool is_manual_safe_mode = this->safe_mode_rtc_value_ == SafeModeComponent::ENTER_SAFE_MODE_MAGIC;
bool is_manual = rtc_val == SafeModeComponent::ENTER_SAFE_MODE_MAGIC;
if (is_manual) {
ESP_LOGI(TAG, "Manual mode");
if (is_manual_safe_mode) {
ESP_LOGI(TAG, "Safe mode invoked manually");
} else {
ESP_LOGCONFIG(TAG, "Unsuccessful boot attempts: %" PRIu32, rtc_val);
ESP_LOGCONFIG(TAG, "There have been %" PRIu32 " suspected unsuccessful boot attempts", this->safe_mode_rtc_value_);
}
if (rtc_val < num_attempts && !is_manual) {
if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) {
this->clean_rtc();
if (!is_manual_safe_mode) {
ESP_LOGE(TAG, "Boot loop detected. Proceeding");
}
this->status_set_error();
this->set_timeout(enable_time, []() {
ESP_LOGW(TAG, "Safe mode enable time has elapsed -- restarting");
App.reboot();
});
// Delay here to allow power to stabilize before Wi-Fi/Ethernet is initialised
delay(300); // NOLINT
App.setup();
ESP_LOGW(TAG, "SAFE MODE IS ACTIVE");
this->safe_mode_callback_.call();
return true;
} else {
// increment counter
this->write_rtc_(rtc_val + 1);
this->write_rtc_(this->safe_mode_rtc_value_ + 1);
return false;
}
this->clean_rtc();
if (!is_manual) {
ESP_LOGE(TAG, "Boot loop detected");
}
this->status_set_error();
this->set_timeout(enable_time, []() {
ESP_LOGW(TAG, "Timeout, restarting");
App.reboot();
});
// Delay here to allow power to stabilize before Wi-Fi/Ethernet is initialised
delay(300); // NOLINT
App.setup();
ESP_LOGW(TAG, "SAFE MODE IS ACTIVE");
this->safe_mode_callback_.call();
return true;
}
void SafeModeComponent::write_rtc_(uint32_t val) {

View File

@@ -1,5 +1,6 @@
import esphome.codegen as cg
from esphome.components import modbus, sensor
from esphome.components.atm90e32.sensor import CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C
import esphome.config_validation as cv
from esphome.const import (
CONF_ACTIVE_POWER,
@@ -11,10 +12,7 @@ from esphome.const import (
CONF_ID,
CONF_IMPORT_ACTIVE_ENERGY,
CONF_IMPORT_REACTIVE_ENERGY,
CONF_PHASE_A,
CONF_PHASE_ANGLE,
CONF_PHASE_B,
CONF_PHASE_C,
CONF_POWER_FACTOR,
CONF_REACTIVE_POWER,
CONF_TOTAL_POWER,

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