1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-20 08:46:01 +00:00

Merge branch 'dev' into light_loop

This commit is contained in:
J. Nick Koston
2025-11-18 23:02:48 -06:00
committed by GitHub
201 changed files with 2250 additions and 1123 deletions

View File

@@ -22,7 +22,7 @@ jobs:
if: github.event.action != 'labeled' || github.event.sender.type != 'Bot' if: github.event.action != 'labeled' || github.event.sender.type != 'Bot'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Generate a token - name: Generate a token
id: generate-token id: generate-token

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Set up Python - name: Set up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Set up Python - name: Set up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0

View File

@@ -43,7 +43,7 @@ jobs:
- "docker" - "docker"
# - "lint" # - "lint"
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Set up Python - name: Set up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:

View File

@@ -49,7 +49,7 @@ jobs:
- name: Check out code from base repository - name: Check out code from base repository
if: steps.pr.outputs.skip != 'true' if: steps.pr.outputs.skip != 'true'
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with: with:
# Always check out from the base repository (esphome/esphome), never from forks # Always check out from the base repository (esphome/esphome), never from forks
# Use the PR's target branch to ensure we run trusted code from the main repo # Use the PR's target branch to ensure we run trusted code from the main repo

View File

@@ -36,7 +36,7 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }} cache-key: ${{ steps.cache-key.outputs.key }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Generate cache-key - name: Generate cache-key
id: cache-key id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
@@ -70,7 +70,7 @@ jobs:
if: needs.determine-jobs.outputs.python-linters == 'true' if: needs.determine-jobs.outputs.python-linters == 'true'
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -91,7 +91,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -132,7 +132,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Restore Python - name: Restore Python
id: restore-python id: restore-python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
@@ -183,7 +183,7 @@ jobs:
component-test-batches: ${{ steps.determine.outputs.component-test-batches }} component-test-batches: ${{ steps.determine.outputs.component-test-batches }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with: with:
# Fetch enough history to find the merge base # Fetch enough history to find the merge base
fetch-depth: 2 fetch-depth: 2
@@ -237,7 +237,7 @@ jobs:
if: needs.determine-jobs.outputs.integration-tests == 'true' if: needs.determine-jobs.outputs.integration-tests == 'true'
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Set up Python 3.13 - name: Set up Python 3.13
id: python id: python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
@@ -273,7 +273,7 @@ jobs:
if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]') if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]')
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
@@ -321,7 +321,7 @@ jobs:
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with: with:
# Need history for HEAD~1 to work for checking changed files # Need history for HEAD~1 to work for checking changed files
fetch-depth: 2 fetch-depth: 2
@@ -400,7 +400,7 @@ jobs:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with: with:
# Need history for HEAD~1 to work for checking changed files # Need history for HEAD~1 to work for checking changed files
fetch-depth: 2 fetch-depth: 2
@@ -489,7 +489,7 @@ jobs:
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with: with:
# Need history for HEAD~1 to work for checking changed files # Need history for HEAD~1 to work for checking changed files
fetch-depth: 2 fetch-depth: 2
@@ -577,7 +577,7 @@ jobs:
version: 1.0 version: 1.0
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -662,7 +662,7 @@ jobs:
if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release')
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -688,7 +688,7 @@ jobs:
skip: ${{ steps.check-script.outputs.skip }} skip: ${{ steps.check-script.outputs.skip }}
steps: steps:
- name: Check out target branch - name: Check out target branch
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with: with:
ref: ${{ github.base_ref }} ref: ${{ github.base_ref }}
@@ -840,7 +840,7 @@ jobs:
flash_usage: ${{ steps.extract.outputs.flash_usage }} flash_usage: ${{ steps.extract.outputs.flash_usage }}
steps: steps:
- name: Check out PR branch - name: Check out PR branch
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -908,7 +908,7 @@ jobs:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:

View File

@@ -54,11 +54,11 @@ jobs:
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 uses: github/codeql-action/init@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }} build-mode: ${{ matrix.build-mode }}
@@ -86,6 +86,6 @@ jobs:
exit 1 exit 1
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 uses: github/codeql-action/analyze@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
with: with:
category: "/language:${{matrix.language}}" category: "/language:${{matrix.language}}"

View File

@@ -20,7 +20,7 @@ jobs:
branch_build: ${{ steps.tag.outputs.branch_build }} branch_build: ${{ steps.tag.outputs.branch_build }}
deploy_env: ${{ steps.tag.outputs.deploy_env }} deploy_env: ${{ steps.tag.outputs.deploy_env }}
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Get tag - name: Get tag
id: tag id: tag
# yamllint disable rule:line-length # yamllint disable rule:line-length
@@ -60,7 +60,7 @@ jobs:
contents: read contents: read
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Set up Python - name: Set up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:
@@ -92,7 +92,7 @@ jobs:
os: "ubuntu-24.04-arm" os: "ubuntu-24.04-arm"
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Set up Python - name: Set up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with: with:
@@ -168,7 +168,7 @@ jobs:
- ghcr - ghcr
- dockerhub - dockerhub
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Download digests - name: Download digests
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0

View File

@@ -13,10 +13,10 @@ jobs:
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
- name: Checkout Home Assistant - name: Checkout Home Assistant
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with: with:
repository: home-assistant/core repository: home-assistant/core
path: lib/home-assistant path: lib/home-assistant

View File

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

View File

@@ -15,6 +15,11 @@ from . import (
class MemoryAnalyzerCLI(MemoryAnalyzer): class MemoryAnalyzerCLI(MemoryAnalyzer):
"""Memory analyzer with CLI-specific report generation.""" """Memory analyzer with CLI-specific report generation."""
# Symbol size threshold for detailed analysis
SYMBOL_SIZE_THRESHOLD: int = (
100 # Show symbols larger than this in detailed analysis
)
# Column width constants # Column width constants
COL_COMPONENT: int = 29 COL_COMPONENT: int = 29
COL_FLASH_TEXT: int = 14 COL_FLASH_TEXT: int = 14
@@ -191,14 +196,21 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
f"{len(symbols):>{self.COL_CORE_COUNT}} | {percentage:>{self.COL_CORE_PERCENT - 1}.1f}%" f"{len(symbols):>{self.COL_CORE_COUNT}} | {percentage:>{self.COL_CORE_PERCENT - 1}.1f}%"
) )
# Top 15 largest core symbols # All core symbols above threshold
lines.append("") lines.append("")
lines.append(f"Top 15 Largest {_COMPONENT_CORE} Symbols:")
sorted_core_symbols = sorted( sorted_core_symbols = sorted(
self._esphome_core_symbols, key=lambda x: x[2], reverse=True self._esphome_core_symbols, key=lambda x: x[2], reverse=True
) )
large_core_symbols = [
(symbol, demangled, size)
for symbol, demangled, size in sorted_core_symbols
if size > self.SYMBOL_SIZE_THRESHOLD
]
for i, (symbol, demangled, size) in enumerate(sorted_core_symbols[:15]): lines.append(
f"{_COMPONENT_CORE} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B ({len(large_core_symbols)} symbols):"
)
for i, (symbol, demangled, size) in enumerate(large_core_symbols):
lines.append(f"{i + 1}. {demangled} ({size:,} B)") lines.append(f"{i + 1}. {demangled} ({size:,} B)")
lines.append("=" * self.TABLE_WIDTH) lines.append("=" * self.TABLE_WIDTH)
@@ -268,13 +280,15 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append(f"Total size: {comp_mem.flash_total:,} B") lines.append(f"Total size: {comp_mem.flash_total:,} B")
lines.append("") lines.append("")
# Show all symbols > 100 bytes for better visibility # Show all symbols above threshold for better visibility
large_symbols = [ large_symbols = [
(sym, dem, size) for sym, dem, size in sorted_symbols if size > 100 (sym, dem, size)
for sym, dem, size in sorted_symbols
if size > self.SYMBOL_SIZE_THRESHOLD
] ]
lines.append( lines.append(
f"{comp_name} Symbols > 100 B ({len(large_symbols)} symbols):" f"{comp_name} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B ({len(large_symbols)} symbols):"
) )
for i, (symbol, demangled, size) in enumerate(large_symbols): for i, (symbol, demangled, size) in enumerate(large_symbols):
lines.append(f"{i + 1}. {demangled} ({size:,} B)") lines.append(f"{i + 1}. {demangled} ({size:,} B)")

View File

@@ -1,12 +1,11 @@
#include "automation.h" #include "automation.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome::binary_sensor {
namespace binary_sensor {
static const char *const TAG = "binary_sensor.automation"; static const char *const TAG = "binary_sensor.automation";
void binary_sensor::MultiClickTrigger::on_state_(bool state) { void MultiClickTrigger::on_state_(bool state) {
// Handle duplicate events // Handle duplicate events
if (state == this->last_state_) { if (state == this->last_state_) {
return; return;
@@ -67,7 +66,7 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) {
*this->at_index_ = *this->at_index_ + 1; *this->at_index_ = *this->at_index_ + 1;
} }
void binary_sensor::MultiClickTrigger::schedule_cooldown_() { void MultiClickTrigger::schedule_cooldown_() {
ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_); ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_);
this->is_in_cooldown_ = true; this->is_in_cooldown_ = true;
this->set_timeout("cooldown", this->invalid_cooldown_, [this]() { this->set_timeout("cooldown", this->invalid_cooldown_, [this]() {
@@ -79,7 +78,7 @@ void binary_sensor::MultiClickTrigger::schedule_cooldown_() {
this->cancel_timeout("is_valid"); this->cancel_timeout("is_valid");
this->cancel_timeout("is_not_valid"); this->cancel_timeout("is_not_valid");
} }
void binary_sensor::MultiClickTrigger::schedule_is_valid_(uint32_t min_length) { void MultiClickTrigger::schedule_is_valid_(uint32_t min_length) {
if (min_length == 0) { if (min_length == 0) {
this->is_valid_ = true; this->is_valid_ = true;
return; return;
@@ -90,19 +89,19 @@ void binary_sensor::MultiClickTrigger::schedule_is_valid_(uint32_t min_length) {
this->is_valid_ = true; this->is_valid_ = true;
}); });
} }
void binary_sensor::MultiClickTrigger::schedule_is_not_valid_(uint32_t max_length) { void MultiClickTrigger::schedule_is_not_valid_(uint32_t max_length) {
this->set_timeout("is_not_valid", max_length, [this]() { this->set_timeout("is_not_valid", max_length, [this]() {
ESP_LOGV(TAG, "Multi Click: You waited too long to %s.", this->parent_->state ? "RELEASE" : "PRESS"); ESP_LOGV(TAG, "Multi Click: You waited too long to %s.", this->parent_->state ? "RELEASE" : "PRESS");
this->is_valid_ = false; this->is_valid_ = false;
this->schedule_cooldown_(); this->schedule_cooldown_();
}); });
} }
void binary_sensor::MultiClickTrigger::cancel() { void MultiClickTrigger::cancel() {
ESP_LOGV(TAG, "Multi Click: Sequence explicitly cancelled."); ESP_LOGV(TAG, "Multi Click: Sequence explicitly cancelled.");
this->is_valid_ = false; this->is_valid_ = false;
this->schedule_cooldown_(); this->schedule_cooldown_();
} }
void binary_sensor::MultiClickTrigger::trigger_() { void MultiClickTrigger::trigger_() {
ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!"); ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!");
this->at_index_.reset(); this->at_index_.reset();
this->cancel_timeout("trigger"); this->cancel_timeout("trigger");
@@ -118,5 +117,4 @@ bool match_interval(uint32_t min_length, uint32_t max_length, uint32_t length) {
return length >= min_length && length <= max_length; return length >= min_length && length <= max_length;
} }
} }
} // namespace binary_sensor } // namespace esphome::binary_sensor
} // namespace esphome

View File

@@ -9,8 +9,7 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/binary_sensor.h" #include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome { namespace esphome::binary_sensor {
namespace binary_sensor {
struct MultiClickTriggerEvent { struct MultiClickTriggerEvent {
bool state; bool state;
@@ -172,5 +171,4 @@ template<typename... Ts> class BinarySensorInvalidateAction : public Action<Ts..
BinarySensor *sensor_; BinarySensor *sensor_;
}; };
} // namespace binary_sensor } // namespace esphome::binary_sensor
} // namespace esphome

View File

@@ -3,9 +3,7 @@
#include "esphome/core/controller_registry.h" #include "esphome/core/controller_registry.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome::binary_sensor {
namespace binary_sensor {
static const char *const TAG = "binary_sensor"; static const char *const TAG = "binary_sensor";
@@ -63,6 +61,4 @@ void BinarySensor::add_filters(std::initializer_list<Filter *> filters) {
} }
bool BinarySensor::is_status_binary_sensor() const { return false; } bool BinarySensor::is_status_binary_sensor() const { return false; }
} // namespace binary_sensor } // namespace esphome::binary_sensor
} // namespace esphome

View File

@@ -6,9 +6,7 @@
#include <initializer_list> #include <initializer_list>
namespace esphome { namespace esphome::binary_sensor {
namespace binary_sensor {
class BinarySensor; class BinarySensor;
void log_binary_sensor(const char *tag, const char *prefix, const char *type, BinarySensor *obj); void log_binary_sensor(const char *tag, const char *prefix, const char *type, BinarySensor *obj);
@@ -70,5 +68,4 @@ class BinarySensorInitiallyOff : public BinarySensor {
bool has_state() const override { return true; } bool has_state() const override { return true; }
}; };
} // namespace binary_sensor } // namespace esphome::binary_sensor
} // namespace esphome

View File

@@ -2,9 +2,7 @@
#include "binary_sensor.h" #include "binary_sensor.h"
namespace esphome { namespace esphome::binary_sensor {
namespace binary_sensor {
static const char *const TAG = "sensor.filter"; static const char *const TAG = "sensor.filter";
@@ -132,6 +130,4 @@ optional<bool> SettleFilter::new_value(bool value) {
float SettleFilter::get_setup_priority() const { return setup_priority::HARDWARE; } float SettleFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
} // namespace binary_sensor } // namespace esphome::binary_sensor
} // namespace esphome

View File

@@ -4,9 +4,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
namespace esphome { namespace esphome::binary_sensor {
namespace binary_sensor {
class BinarySensor; class BinarySensor;
@@ -139,6 +137,4 @@ class SettleFilter : public Filter, public Component {
bool steady_{true}; bool steady_{true};
}; };
} // namespace binary_sensor } // namespace esphome::binary_sensor
} // namespace esphome

View File

@@ -70,6 +70,9 @@ void BME68xBSEC2Component::dump_config() {
if (this->is_failed()) { if (this->is_failed()) {
ESP_LOGE(TAG, "Communication failed (BSEC2 status: %d, BME68X status: %d)", this->bsec_status_, ESP_LOGE(TAG, "Communication failed (BSEC2 status: %d, BME68X status: %d)", this->bsec_status_,
this->bme68x_status_); this->bme68x_status_);
if (this->bsec_status_ == BSEC_I_SU_SUBSCRIBEDOUTPUTGATES) {
ESP_LOGE(TAG, "No sensors, add at least one sensor to the config");
}
} }
if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) { if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) {

View File

@@ -72,6 +72,16 @@ def _final_validate(config: ConfigType) -> ConfigType:
"Add 'ap:' to your WiFi configuration to enable the captive portal." "Add 'ap:' to your WiFi configuration to enable the captive portal."
) )
# Register socket needs for DNS server and additional HTTP connections
# - 1 UDP socket for DNS server
# - 3 additional TCP sockets for captive portal detection probes + configuration requests
# OS captive portal detection makes multiple probe requests that stay in TIME_WAIT.
# Need headroom for actual user configuration requests.
# LRU purging will reclaim idle sockets to prevent exhaustion from repeated attempts.
from esphome.components import socket
socket.consume_sockets(4, "captive_portal")(config)
return config return config

View File

@@ -13,14 +13,16 @@ static const char *const TAG = "captive_portal";
void CaptivePortal::handle_config(AsyncWebServerRequest *request) { void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
AsyncResponseStream *stream = request->beginResponseStream(ESPHOME_F("application/json")); AsyncResponseStream *stream = request->beginResponseStream(ESPHOME_F("application/json"));
stream->addHeader(ESPHOME_F("cache-control"), ESPHOME_F("public, max-age=0, must-revalidate")); stream->addHeader(ESPHOME_F("cache-control"), ESPHOME_F("public, max-age=0, must-revalidate"));
char mac_s[18];
const char *mac_str = get_mac_address_pretty_into_buffer(mac_s);
#ifdef USE_ESP8266 #ifdef USE_ESP8266
stream->print(ESPHOME_F("{\"mac\":\"")); stream->print(ESPHOME_F("{\"mac\":\""));
stream->print(get_mac_address_pretty().c_str()); stream->print(mac_str);
stream->print(ESPHOME_F("\",\"name\":\"")); stream->print(ESPHOME_F("\",\"name\":\""));
stream->print(App.get_name().c_str()); stream->print(App.get_name().c_str());
stream->print(ESPHOME_F("\",\"aps\":[{}")); stream->print(ESPHOME_F("\",\"aps\":[{}"));
#else #else
stream->printf(R"({"mac":"%s","name":"%s","aps":[{})", get_mac_address_pretty().c_str(), App.get_name().c_str()); stream->printf(R"({"mac":"%s","name":"%s","aps":[{})", mac_str, App.get_name().c_str());
#endif #endif
for (auto &scan : wifi::global_wifi_component->get_scan_result()) { for (auto &scan : wifi::global_wifi_component->get_scan_result()) {
@@ -50,8 +52,8 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
ESP_LOGI(TAG, "Requested WiFi Settings Change:"); ESP_LOGI(TAG, "Requested WiFi Settings Change:");
ESP_LOGI(TAG, " SSID='%s'", ssid.c_str()); ESP_LOGI(TAG, " SSID='%s'", ssid.c_str());
ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str()); ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str());
wifi::global_wifi_component->save_wifi_sta(ssid, psk); // Defer save to main loop thread to avoid NVS operations from HTTP thread
wifi::global_wifi_component->start_scanning(); this->defer([ssid, psk]() { wifi::global_wifi_component->save_wifi_sta(ssid, psk); });
request->redirect(ESPHOME_F("/?save")); request->redirect(ESPHOME_F("/?save"));
} }
@@ -63,6 +65,12 @@ void CaptivePortal::start() {
this->base_->init(); this->base_->init();
if (!this->initialized_) { if (!this->initialized_) {
this->base_->add_handler(this); this->base_->add_handler(this);
#ifdef USE_ESP32
// Enable LRU socket purging to handle captive portal detection probe bursts
// OS captive portal detection makes many simultaneous HTTP requests which can
// exhaust sockets. LRU purging automatically closes oldest idle connections.
this->base_->get_server()->set_lru_purge_enable(true);
#endif
} }
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();

View File

@@ -40,6 +40,10 @@ class CaptivePortal : public AsyncWebHandler, public Component {
void end() { void end() {
this->active_ = false; this->active_ = false;
this->disable_loop(); // Stop processing DNS requests this->disable_loop(); // Stop processing DNS requests
#ifdef USE_ESP32
// Disable LRU socket purging now that captive portal is done
this->base_->get_server()->set_lru_purge_enable(false);
#endif
this->base_->deinit(); this->base_->deinit();
if (this->dns_server_ != nullptr) { if (this->dns_server_ != nullptr) {
this->dns_server_->stop(); this->dns_server_->stop();

View File

@@ -4,8 +4,7 @@
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "cover.h" #include "cover.h"
namespace esphome { namespace esphome::cover {
namespace cover {
template<typename... Ts> class OpenAction : public Action<Ts...> { template<typename... Ts> class OpenAction : public Action<Ts...> {
public: public:
@@ -131,5 +130,4 @@ class CoverClosedTrigger : public Trigger<> {
} }
}; };
} // namespace cover } // namespace esphome::cover
} // namespace esphome

View File

@@ -6,8 +6,7 @@
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome::cover {
namespace cover {
static const char *const TAG = "cover"; static const char *const TAG = "cover";
@@ -212,5 +211,4 @@ void CoverRestoreState::apply(Cover *cover) {
cover->publish_state(); cover->publish_state();
} }
} // namespace cover } // namespace esphome::cover
} // namespace esphome

View File

@@ -7,8 +7,7 @@
#include "cover_traits.h" #include "cover_traits.h"
namespace esphome { namespace esphome::cover {
namespace cover {
const extern float COVER_OPEN; const extern float COVER_OPEN;
const extern float COVER_CLOSED; const extern float COVER_CLOSED;
@@ -157,5 +156,4 @@ class Cover : public EntityBase, public EntityBase_DeviceClass {
ESPPreferenceObject rtc_; ESPPreferenceObject rtc_;
}; };
} // namespace cover } // namespace esphome::cover
} // namespace esphome

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
namespace esphome { namespace esphome::cover {
namespace cover {
class CoverTraits { class CoverTraits {
public: public:
@@ -26,5 +25,4 @@ class CoverTraits {
bool supports_stop_{false}; bool supports_stop_{false};
}; };
} // namespace cover } // namespace esphome::cover
} // namespace esphome

View File

@@ -3,10 +3,10 @@
namespace esphome { namespace esphome {
namespace dashboard_import { namespace dashboard_import {
static std::string g_package_import_url; // NOLINT static const char *g_package_import_url = ""; // NOLINT
const std::string &get_package_import_url() { return g_package_import_url; } const char *get_package_import_url() { return g_package_import_url; }
void set_package_import_url(std::string url) { g_package_import_url = std::move(url); } void set_package_import_url(const char *url) { g_package_import_url = url; }
} // namespace dashboard_import } // namespace dashboard_import
} // namespace esphome } // namespace esphome

View File

@@ -1,12 +1,10 @@
#pragma once #pragma once
#include <string>
namespace esphome { namespace esphome {
namespace dashboard_import { namespace dashboard_import {
const std::string &get_package_import_url(); const char *get_package_import_url();
void set_package_import_url(std::string url); void set_package_import_url(const char *url);
} // namespace dashboard_import } // namespace dashboard_import
} // namespace esphome } // namespace esphome

View File

@@ -381,8 +381,9 @@ PLATFORM_VERSION_LOOKUP = {
} }
def _check_versions(value): def _check_versions(config):
value = value.copy() config = config.copy()
value = config[CONF_FRAMEWORK]
if value[CONF_VERSION] in PLATFORM_VERSION_LOOKUP: if value[CONF_VERSION] in PLATFORM_VERSION_LOOKUP:
if CONF_SOURCE in value or CONF_PLATFORM_VERSION in value: if CONF_SOURCE in value or CONF_PLATFORM_VERSION in value:
@@ -447,7 +448,7 @@ def _check_versions(value):
"If there are connectivity or build issues please remove the manual version." "If there are connectivity or build issues please remove the manual version."
) )
return value return config
def _parse_platform_version(value): def _parse_platform_version(value):
@@ -497,6 +498,8 @@ def final_validate(config):
from esphome.components.psram import DOMAIN as PSRAM_DOMAIN from esphome.components.psram import DOMAIN as PSRAM_DOMAIN
errs = [] errs = []
conf_fw = config[CONF_FRAMEWORK]
advanced = conf_fw[CONF_ADVANCED]
full_config = fv.full_config.get() full_config = fv.full_config.get()
if pio_options := full_config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS): if pio_options := full_config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS):
pio_flash_size_key = "board_upload.flash_size" pio_flash_size_key = "board_upload.flash_size"
@@ -513,22 +516,14 @@ def final_validate(config):
f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only" f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only"
) )
) )
if ( if config[CONF_VARIANT] != VARIANT_ESP32 and advanced[CONF_IGNORE_EFUSE_MAC_CRC]:
config[CONF_VARIANT] != VARIANT_ESP32
and CONF_ADVANCED in (conf_fw := config[CONF_FRAMEWORK])
and CONF_IGNORE_EFUSE_MAC_CRC in conf_fw[CONF_ADVANCED]
):
errs.append( errs.append(
cv.Invalid( cv.Invalid(
f"'{CONF_IGNORE_EFUSE_MAC_CRC}' is not supported on {config[CONF_VARIANT]}", f"'{CONF_IGNORE_EFUSE_MAC_CRC}' is not supported on {config[CONF_VARIANT]}",
path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_IGNORE_EFUSE_MAC_CRC], path=[CONF_FRAMEWORK, CONF_ADVANCED, CONF_IGNORE_EFUSE_MAC_CRC],
) )
) )
if ( if advanced[CONF_EXECUTE_FROM_PSRAM]:
config.get(CONF_FRAMEWORK, {})
.get(CONF_ADVANCED, {})
.get(CONF_EXECUTE_FROM_PSRAM)
):
if config[CONF_VARIANT] != VARIANT_ESP32S3: if config[CONF_VARIANT] != VARIANT_ESP32S3:
errs.append( errs.append(
cv.Invalid( cv.Invalid(
@@ -544,6 +539,17 @@ def final_validate(config):
) )
) )
if (
config[CONF_FLASH_SIZE] == "32MB"
and "ota" in full_config
and not advanced[CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES]
):
errs.append(
cv.Invalid(
f"OTA with 32MB flash requires '{CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES}' to be set in the '{CONF_ADVANCED}' section of the esp32 configuration",
path=[CONF_FLASH_SIZE],
)
)
if errs: if errs:
raise cv.MultipleInvalid(errs) raise cv.MultipleInvalid(errs)
@@ -598,89 +604,74 @@ def _validate_idf_component(config: ConfigType) -> ConfigType:
FRAMEWORK_ESP_IDF = "esp-idf" FRAMEWORK_ESP_IDF = "esp-idf"
FRAMEWORK_ARDUINO = "arduino" FRAMEWORK_ARDUINO = "arduino"
FRAMEWORK_SCHEMA = cv.All( FRAMEWORK_SCHEMA = cv.Schema(
cv.Schema( {
{ cv.Optional(CONF_TYPE): cv.one_of(FRAMEWORK_ESP_IDF, FRAMEWORK_ARDUINO),
cv.Optional(CONF_TYPE, default=FRAMEWORK_ARDUINO): cv.one_of( cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict,
FRAMEWORK_ESP_IDF, FRAMEWORK_ARDUINO cv.Optional(CONF_RELEASE): cv.string_strict,
), cv.Optional(CONF_SOURCE): cv.string_strict,
cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version,
cv.Optional(CONF_RELEASE): cv.string_strict, cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): {
cv.Optional(CONF_SOURCE): cv.string_strict, cv.string_strict: cv.string_strict
cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, },
cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): { cv.Optional(CONF_LOG_LEVEL, default="ERROR"): cv.one_of(
cv.string_strict: cv.string_strict *LOG_LEVELS_IDF, upper=True
}, ),
cv.Optional(CONF_LOG_LEVEL, default="ERROR"): cv.one_of( cv.Optional(CONF_ADVANCED, default={}): cv.Schema(
*LOG_LEVELS_IDF, upper=True {
), cv.Optional(CONF_ASSERTION_LEVEL): cv.one_of(
cv.Optional(CONF_ADVANCED, default={}): cv.Schema( *ASSERTION_LEVELS, upper=True
{ ),
cv.Optional(CONF_ASSERTION_LEVEL): cv.one_of( cv.Optional(CONF_COMPILER_OPTIMIZATION, default="SIZE"): cv.one_of(
*ASSERTION_LEVELS, upper=True *COMPILER_OPTIMIZATIONS, upper=True
), ),
cv.Optional(CONF_COMPILER_OPTIMIZATION, default="SIZE"): cv.one_of( cv.Optional(
*COMPILER_OPTIMIZATIONS, upper=True CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES, default=False
), ): cv.boolean,
cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean, cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean,
cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean, cv.Optional(CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False): cv.boolean,
cv.Optional( cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean,
CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False # DHCP server is needed for WiFi AP mode. When WiFi component is used,
): cv.boolean, # it will handle disabling DHCP server when AP is not configured.
cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, # Default to false (disabled) when WiFi is not used.
# DHCP server is needed for WiFi AP mode. When WiFi component is used, cv.OnlyWithout(
# it will handle disabling DHCP server when AP is not configured. CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False
# Default to false (disabled) when WiFi is not used. ): cv.boolean,
cv.OnlyWithout( cv.Optional(CONF_ENABLE_LWIP_MDNS_QUERIES, default=True): cv.boolean,
CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False cv.Optional(
): cv.boolean, CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False
cv.Optional( ): cv.boolean,
CONF_ENABLE_LWIP_MDNS_QUERIES, default=True cv.Optional(
): cv.boolean, CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING, default=True
cv.Optional( ): cv.boolean,
CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False cv.Optional(
): cv.boolean, CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, default=True
cv.Optional( ): cv.boolean,
CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING, default=True cv.Optional(CONF_DISABLE_LIBC_LOCKS_IN_IRAM, default=True): cv.boolean,
): cv.boolean, cv.Optional(CONF_DISABLE_VFS_SUPPORT_TERMIOS, default=True): cv.boolean,
cv.Optional( cv.Optional(CONF_DISABLE_VFS_SUPPORT_SELECT, default=True): cv.boolean,
CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, default=True cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean,
): cv.boolean, cv.Optional(CONF_EXECUTE_FROM_PSRAM, default=False): cv.boolean,
cv.Optional( cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range(
CONF_DISABLE_LIBC_LOCKS_IN_IRAM, default=True min=8192, max=32768
): cv.boolean, ),
cv.Optional( }
CONF_DISABLE_VFS_SUPPORT_TERMIOS, default=True ),
): cv.boolean, cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list(
cv.Optional( cv.All(
CONF_DISABLE_VFS_SUPPORT_SELECT, default=True cv.Schema(
): cv.boolean, {
cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean, cv.Required(CONF_NAME): cv.string_strict,
cv.Optional(CONF_EXECUTE_FROM_PSRAM): cv.boolean, cv.Optional(CONF_SOURCE): cv.git_ref,
cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range( cv.Optional(CONF_REF): cv.string,
min=8192, max=32768 cv.Optional(CONF_PATH): cv.string,
), cv.Optional(CONF_REFRESH): cv.All(cv.string, cv.source_refresh),
} }
), ),
cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( _validate_idf_component,
cv.All( )
cv.Schema( ),
{ }
cv.Required(CONF_NAME): cv.string_strict,
cv.Optional(CONF_SOURCE): cv.git_ref,
cv.Optional(CONF_REF): cv.string,
cv.Optional(CONF_PATH): cv.string,
cv.Optional(CONF_REFRESH): cv.All(
cv.string, cv.source_refresh
),
}
),
_validate_idf_component,
)
),
}
),
_check_versions,
) )
@@ -743,11 +734,11 @@ def _show_framework_migration_message(name: str, variant: str) -> None:
def _set_default_framework(config): def _set_default_framework(config):
config = config.copy()
if CONF_FRAMEWORK not in config: if CONF_FRAMEWORK not in config:
config = config.copy()
variant = config[CONF_VARIANT]
config[CONF_FRAMEWORK] = FRAMEWORK_SCHEMA({}) config[CONF_FRAMEWORK] = FRAMEWORK_SCHEMA({})
if CONF_TYPE not in config[CONF_FRAMEWORK]:
variant = config[CONF_VARIANT]
if variant in ARDUINO_ALLOWED_VARIANTS: if variant in ARDUINO_ALLOWED_VARIANTS:
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO
_show_framework_migration_message( _show_framework_migration_message(
@@ -787,6 +778,7 @@ CONFIG_SCHEMA = cv.All(
), ),
_detect_variant, _detect_variant,
_set_default_framework, _set_default_framework,
_check_versions,
set_core_data, set_core_data,
cv.has_at_least_one_key(CONF_BOARD, CONF_VARIANT), cv.has_at_least_one_key(CONF_BOARD, CONF_VARIANT),
) )
@@ -805,9 +797,7 @@ def _configure_lwip_max_sockets(conf: dict) -> None:
from esphome.components.socket import KEY_SOCKET_CONSUMERS from esphome.components.socket import KEY_SOCKET_CONSUMERS
# Check if user manually specified CONFIG_LWIP_MAX_SOCKETS # Check if user manually specified CONFIG_LWIP_MAX_SOCKETS
user_max_sockets = conf.get(CONF_SDKCONFIG_OPTIONS, {}).get( user_max_sockets = conf[CONF_SDKCONFIG_OPTIONS].get("CONFIG_LWIP_MAX_SOCKETS")
"CONFIG_LWIP_MAX_SOCKETS"
)
socket_consumers: dict[str, int] = CORE.data.get(KEY_SOCKET_CONSUMERS, {}) socket_consumers: dict[str, int] = CORE.data.get(KEY_SOCKET_CONSUMERS, {})
total_sockets = sum(socket_consumers.values()) total_sockets = sum(socket_consumers.values())
@@ -941,6 +931,12 @@ async def to_code(config):
add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True) add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True)
add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True) add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True)
# ESP32-S2 Arduino: Disable USB Serial on boot to avoid TinyUSB dependency
if get_esp32_variant() == VARIANT_ESP32S2:
cg.add_build_unflag("-DARDUINO_USB_CDC_ON_BOOT=1")
cg.add_build_unflag("-DARDUINO_USB_CDC_ON_BOOT=0")
cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=0")
cg.add_build_flag("-Wno-nonnull-compare") cg.add_build_flag("-Wno-nonnull-compare")
add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True) add_idf_sdkconfig_option(f"CONFIG_IDF_TARGET_{variant}", True)
@@ -977,23 +973,18 @@ async def to_code(config):
# WiFi component handles its own optimization when AP mode is not used # WiFi component handles its own optimization when AP mode is not used
# When using Arduino with Ethernet, DHCP server functions must be available # When using Arduino with Ethernet, DHCP server functions must be available
# for the Network library to compile, even if not actively used # for the Network library to compile, even if not actively used
if ( if advanced.get(CONF_ENABLE_LWIP_DHCP_SERVER) is False and not (
CONF_ENABLE_LWIP_DHCP_SERVER in advanced conf[CONF_TYPE] == FRAMEWORK_ARDUINO and "ethernet" in CORE.loaded_integrations
and not advanced[CONF_ENABLE_LWIP_DHCP_SERVER]
and not (
conf[CONF_TYPE] == FRAMEWORK_ARDUINO
and "ethernet" in CORE.loaded_integrations
)
): ):
add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False) add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False)
if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, True): if not advanced[CONF_ENABLE_LWIP_MDNS_QUERIES]:
add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False) add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False)
if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False): if not advanced[CONF_ENABLE_LWIP_BRIDGE_INTERFACE]:
add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0) add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0)
_configure_lwip_max_sockets(conf) _configure_lwip_max_sockets(conf)
if advanced.get(CONF_EXECUTE_FROM_PSRAM, False): if advanced[CONF_EXECUTE_FROM_PSRAM]:
add_idf_sdkconfig_option("CONFIG_SPIRAM_FETCH_INSTRUCTIONS", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_FETCH_INSTRUCTIONS", True)
add_idf_sdkconfig_option("CONFIG_SPIRAM_RODATA", True) add_idf_sdkconfig_option("CONFIG_SPIRAM_RODATA", True)
@@ -1004,23 +995,22 @@ async def to_code(config):
# - select() on 4 sockets: ~190μs (Arduino/core locking) vs ~235μs (ESP-IDF default) # - select() on 4 sockets: ~190μs (Arduino/core locking) vs ~235μs (ESP-IDF default)
# - Up to 200% slower under load when all operations queue through tcpip_thread # - Up to 200% slower under load when all operations queue through tcpip_thread
# Enabling this makes ESP-IDF socket performance match Arduino framework. # Enabling this makes ESP-IDF socket performance match Arduino framework.
if advanced.get(CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING, True): if advanced[CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING]:
add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_CORE_LOCKING", True) add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_CORE_LOCKING", True)
if advanced.get(CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY, True): if advanced[CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY]:
add_idf_sdkconfig_option("CONFIG_LWIP_CHECK_THREAD_SAFETY", True) add_idf_sdkconfig_option("CONFIG_LWIP_CHECK_THREAD_SAFETY", True)
# Disable placing libc locks in IRAM to save RAM # Disable placing libc locks in IRAM to save RAM
# This is safe for ESPHome since no IRAM ISRs (interrupts that run while cache is disabled) # This is safe for ESPHome since no IRAM ISRs (interrupts that run while cache is disabled)
# use libc lock APIs. Saves approximately 1.3KB (1,356 bytes) of IRAM. # use libc lock APIs. Saves approximately 1.3KB (1,356 bytes) of IRAM.
if advanced.get(CONF_DISABLE_LIBC_LOCKS_IN_IRAM, True): if advanced[CONF_DISABLE_LIBC_LOCKS_IN_IRAM]:
add_idf_sdkconfig_option("CONFIG_LIBC_LOCKS_PLACE_IN_IRAM", False) add_idf_sdkconfig_option("CONFIG_LIBC_LOCKS_PLACE_IN_IRAM", False)
# Disable VFS support for termios (terminal I/O functions) # Disable VFS support for termios (terminal I/O functions)
# ESPHome doesn't use termios functions on ESP32 (only used in host UART driver). # ESPHome doesn't use termios functions on ESP32 (only used in host UART driver).
# Saves approximately 1.8KB of flash when disabled (default). # Saves approximately 1.8KB of flash when disabled (default).
add_idf_sdkconfig_option( add_idf_sdkconfig_option(
"CONFIG_VFS_SUPPORT_TERMIOS", "CONFIG_VFS_SUPPORT_TERMIOS", not advanced[CONF_DISABLE_VFS_SUPPORT_TERMIOS]
not advanced.get(CONF_DISABLE_VFS_SUPPORT_TERMIOS, True),
) )
# Disable VFS support for select() with file descriptors # Disable VFS support for select() with file descriptors
@@ -1034,8 +1024,7 @@ async def to_code(config):
else: else:
# No component needs it - allow user to control (default: disabled) # No component needs it - allow user to control (default: disabled)
add_idf_sdkconfig_option( add_idf_sdkconfig_option(
"CONFIG_VFS_SUPPORT_SELECT", "CONFIG_VFS_SUPPORT_SELECT", not advanced[CONF_DISABLE_VFS_SUPPORT_SELECT]
not advanced.get(CONF_DISABLE_VFS_SUPPORT_SELECT, True),
) )
# Disable VFS support for directory functions (opendir, readdir, mkdir, etc.) # Disable VFS support for directory functions (opendir, readdir, mkdir, etc.)
@@ -1048,8 +1037,7 @@ async def to_code(config):
else: else:
# No component needs it - allow user to control (default: disabled) # No component needs it - allow user to control (default: disabled)
add_idf_sdkconfig_option( add_idf_sdkconfig_option(
"CONFIG_VFS_SUPPORT_DIR", "CONFIG_VFS_SUPPORT_DIR", not advanced[CONF_DISABLE_VFS_SUPPORT_DIR]
not advanced.get(CONF_DISABLE_VFS_SUPPORT_DIR, True),
) )
cg.add_platformio_option("board_build.partitions", "partitions.csv") cg.add_platformio_option("board_build.partitions", "partitions.csv")
@@ -1063,7 +1051,7 @@ async def to_code(config):
add_idf_sdkconfig_option(flag, assertion_level == key) add_idf_sdkconfig_option(flag, assertion_level == key)
add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False) add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False)
compiler_optimization = advanced.get(CONF_COMPILER_OPTIMIZATION) compiler_optimization = advanced[CONF_COMPILER_OPTIMIZATION]
for key, flag in COMPILER_OPTIMIZATIONS.items(): for key, flag in COMPILER_OPTIMIZATIONS.items():
add_idf_sdkconfig_option(flag, compiler_optimization == key) add_idf_sdkconfig_option(flag, compiler_optimization == key)
@@ -1072,18 +1060,20 @@ async def to_code(config):
conf[CONF_ADVANCED][CONF_ENABLE_LWIP_ASSERT], conf[CONF_ADVANCED][CONF_ENABLE_LWIP_ASSERT],
) )
if advanced.get(CONF_IGNORE_EFUSE_MAC_CRC): if advanced[CONF_IGNORE_EFUSE_MAC_CRC]:
add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True) add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True)
add_idf_sdkconfig_option("CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False) add_idf_sdkconfig_option("CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False)
if advanced.get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): if advanced[CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES]:
_LOGGER.warning( _LOGGER.warning(
"Using experimental features in ESP-IDF may result in unexpected failures." "Using experimental features in ESP-IDF may result in unexpected failures."
) )
add_idf_sdkconfig_option("CONFIG_IDF_EXPERIMENTAL_FEATURES", True) add_idf_sdkconfig_option("CONFIG_IDF_EXPERIMENTAL_FEATURES", True)
if config[CONF_FLASH_SIZE] == "32MB":
add_idf_sdkconfig_option(
"CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH", True
)
cg.add_define( cg.add_define("ESPHOME_LOOP_TASK_STACK_SIZE", advanced[CONF_LOOP_TASK_STACK_SIZE])
"ESPHOME_LOOP_TASK_STACK_SIZE", advanced.get(CONF_LOOP_TASK_STACK_SIZE)
)
cg.add_define( cg.add_define(
"USE_ESP_IDF_VERSION_CODE", "USE_ESP_IDF_VERSION_CODE",

View File

@@ -20,6 +20,10 @@ CONF_ON_STOP = "on_stop"
CONF_STATUS_INDICATOR = "status_indicator" CONF_STATUS_INDICATOR = "status_indicator"
CONF_WIFI_TIMEOUT = "wifi_timeout" CONF_WIFI_TIMEOUT = "wifi_timeout"
# Default WiFi timeout - aligned with WiFi component ap_timeout
# Allows sufficient time to try all BSSIDs before starting provisioning mode
DEFAULT_WIFI_TIMEOUT = "90s"
improv_ns = cg.esphome_ns.namespace("improv") improv_ns = cg.esphome_ns.namespace("improv")
Error = improv_ns.enum("Error") Error = improv_ns.enum("Error")
@@ -59,7 +63,7 @@ CONFIG_SCHEMA = (
CONF_AUTHORIZED_DURATION, default="1min" CONF_AUTHORIZED_DURATION, default="1min"
): cv.positive_time_period_milliseconds, ): cv.positive_time_period_milliseconds,
cv.Optional( cv.Optional(
CONF_WIFI_TIMEOUT, default="1min" CONF_WIFI_TIMEOUT, default=DEFAULT_WIFI_TIMEOUT
): cv.positive_time_period_milliseconds, ): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ON_PROVISIONED): automation.validate_automation( cv.Optional(CONF_ON_PROVISIONED): automation.validate_automation(
{ {

View File

@@ -127,6 +127,7 @@ void ESP32ImprovComponent::loop() {
// Set initial state based on whether we have an authorizer // Set initial state based on whether we have an authorizer
this->set_state_(this->get_initial_state_(), false); this->set_state_(this->get_initial_state_(), false);
this->set_error_(improv::ERROR_NONE); this->set_error_(improv::ERROR_NONE);
this->should_start_ = false; // Clear flag after starting
ESP_LOGD(TAG, "Service started!"); ESP_LOGD(TAG, "Service started!");
} }
} }

View File

@@ -45,6 +45,7 @@ class ESP32ImprovComponent : public Component, public improv_base::ImprovBase {
void start(); void start();
void stop(); void stop();
bool is_active() const { return this->state_ != improv::STATE_STOPPED; } bool is_active() const { return this->state_ != improv::STATE_STOPPED; }
bool should_start() const { return this->should_start_; }
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK #ifdef USE_ESP32_IMPROV_STATE_CALLBACK
void add_on_state_callback(std::function<void(improv::State, improv::Error)> &&callback) { void add_on_state_callback(std::function<void(improv::State, improv::Error)> &&callback) {

View File

@@ -36,7 +36,6 @@ from esphome.const import (
CONF_WEIGHT, CONF_WEIGHT,
) )
from esphome.core import CORE, HexInt from esphome.core import CORE, HexInt
from esphome.helpers import cpp_string_escape
from esphome.types import ConfigType from esphome.types import ConfigType
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -50,7 +49,6 @@ font_ns = cg.esphome_ns.namespace("font")
Font = font_ns.class_("Font") Font = font_ns.class_("Font")
Glyph = font_ns.class_("Glyph") Glyph = font_ns.class_("Glyph")
GlyphData = font_ns.struct("GlyphData")
CONF_BPP = "bpp" CONF_BPP = "bpp"
CONF_EXTRAS = "extras" CONF_EXTRAS = "extras"
@@ -463,7 +461,7 @@ FONT_SCHEMA = cv.Schema(
) )
), ),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
cv.GenerateID(CONF_RAW_GLYPH_ID): cv.declare_id(GlyphData), cv.GenerateID(CONF_RAW_GLYPH_ID): cv.declare_id(Glyph),
}, },
) )
@@ -488,6 +486,8 @@ class GlyphInfo:
def glyph_to_glyphinfo(glyph, font, size, bpp): def glyph_to_glyphinfo(glyph, font, size, bpp):
# Convert to 32 bit unicode codepoint
glyph = ord(glyph)
scale = 256 // (1 << bpp) scale = 256 // (1 << bpp)
if not font.is_scalable: if not font.is_scalable:
sizes = [pt_to_px(x.size) for x in font.available_sizes] sizes = [pt_to_px(x.size) for x in font.available_sizes]
@@ -583,22 +583,15 @@ async def to_code(config):
# Create the glyph table that points to data in the above array. # Create the glyph table that points to data in the above array.
glyph_initializer = [ glyph_initializer = [
cg.StructInitializer( [
GlyphData, x.glyph,
( prog_arr + (y - len(x.bitmap_data)),
"a_char", x.advance,
cg.RawExpression(f"(const uint8_t *){cpp_string_escape(x.glyph)}"), x.offset_x,
), x.offset_y,
( x.width,
"data", x.height,
cg.RawExpression(f"{str(prog_arr)} + {str(y - len(x.bitmap_data))}"), ]
),
("advance", x.advance),
("offset_x", x.offset_x),
("offset_y", x.offset_y),
("width", x.width),
("height", x.height),
)
for (x, y) in zip( for (x, y) in zip(
glyph_args, list(accumulate([len(x.bitmap_data) for x in glyph_args])) glyph_args, list(accumulate([len(x.bitmap_data) for x in glyph_args]))
) )

View File

@@ -6,133 +6,245 @@
namespace esphome { namespace esphome {
namespace font { namespace font {
static const char *const TAG = "font"; static const char *const TAG = "font";
const uint8_t *Glyph::get_char() const { return this->glyph_data_->a_char; } #ifdef USE_LVGL_FONT
// Compare the char at the string position with this char. const uint8_t *Font::get_glyph_bitmap(const lv_font_t *font, uint32_t unicode_letter) {
// Return true if this char is less than or equal the other. auto *fe = (Font *) font->dsc;
bool Glyph::compare_to(const uint8_t *str) const { const auto *gd = fe->get_glyph_data_(unicode_letter);
// 1 -> this->char_ if (gd == nullptr) {
// 2 -> str return nullptr;
for (uint32_t i = 0;; i++) {
if (this->glyph_data_->a_char[i] == '\0')
return true;
if (str[i] == '\0')
return false;
if (this->glyph_data_->a_char[i] > str[i])
return false;
if (this->glyph_data_->a_char[i] < str[i])
return true;
} }
// this should not happen return gd->data;
return false;
}
int Glyph::match_length(const uint8_t *str) const {
for (uint32_t i = 0;; i++) {
if (this->glyph_data_->a_char[i] == '\0')
return i;
if (str[i] != this->glyph_data_->a_char[i])
return 0;
}
// this should not happen
return 0;
}
void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
*x1 = this->glyph_data_->offset_x;
*y1 = this->glyph_data_->offset_y;
*width = this->glyph_data_->width;
*height = this->glyph_data_->height;
} }
Font::Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight, bool Font::get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next) {
auto *fe = (Font *) font->dsc;
const auto *gd = fe->get_glyph_data_(unicode_letter);
if (gd == nullptr) {
return false;
}
dsc->adv_w = gd->advance;
dsc->ofs_x = gd->offset_x;
dsc->ofs_y = fe->height_ - gd->height - gd->offset_y - fe->lv_font_.base_line;
dsc->box_w = gd->width;
dsc->box_h = gd->height;
dsc->is_placeholder = 0;
dsc->bpp = fe->get_bpp();
return true;
}
const Glyph *Font::get_glyph_data_(uint32_t unicode_letter) {
if (unicode_letter == this->last_letter_ && this->last_letter_ != 0)
return this->last_data_;
auto *glyph = this->find_glyph(unicode_letter);
if (glyph == nullptr) {
return nullptr;
}
this->last_data_ = glyph;
this->last_letter_ = unicode_letter;
return glyph;
}
#endif
/**
* Attempt to extract a 32 bit Unicode codepoint from a UTF-8 string.
* If successful, return the codepoint and set the length to the number of bytes read.
* If the end of the string has been reached and a valid codepoint has not been found, return 0 and set the length to
* 0.
*
* @param utf8_str The input string
* @param length Pointer to length storage
* @return The extracted code point
*/
static uint32_t extract_unicode_codepoint(const char *utf8_str, size_t *length) {
// Safely cast to uint8_t* for correct bitwise operations on bytes
const uint8_t *current = reinterpret_cast<const uint8_t *>(utf8_str);
uint32_t code_point = 0;
uint8_t c1 = *current++;
// check for end of string
if (c1 == 0) {
*length = 0;
return 0;
}
// --- 1-Byte Sequence: 0xxxxxxx (ASCII) ---
if (c1 < 0x80) {
// Valid ASCII byte.
code_point = c1;
// Optimization: No need to check for continuation bytes.
}
// --- 2-Byte Sequence: 110xxxxx 10xxxxxx ---
else if ((c1 & 0xE0) == 0xC0) {
uint8_t c2 = *current++;
// Error Check 1: Check if c2 is a valid continuation byte (10xxxxxx)
if ((c2 & 0xC0) != 0x80) {
*length = 0;
return 0;
}
code_point = (c1 & 0x1F) << 6;
code_point |= (c2 & 0x3F);
// Error Check 2: Overlong check (2-byte must be > 0x7F)
if (code_point <= 0x7F) {
*length = 0;
return 0;
}
}
// --- 3-Byte Sequence: 1110xxxx 10xxxxxx 10xxxxxx ---
else if ((c1 & 0xF0) == 0xE0) {
uint8_t c2 = *current++;
uint8_t c3 = *current++;
// Error Check 1: Check continuation bytes
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
*length = 0;
return 0;
}
code_point = (c1 & 0x0F) << 12;
code_point |= (c2 & 0x3F) << 6;
code_point |= (c3 & 0x3F);
// Error Check 2: Overlong check (3-byte must be > 0x7FF)
// Also check for surrogates (0xD800-0xDFFF)
if (code_point <= 0x7FF || (code_point >= 0xD800 && code_point <= 0xDFFF)) {
*length = 0;
return 0;
}
}
// --- 4-Byte Sequence: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx ---
else if ((c1 & 0xF8) == 0xF0) {
uint8_t c2 = *current++;
uint8_t c3 = *current++;
uint8_t c4 = *current++;
// Error Check 1: Check continuation bytes
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80) || ((c4 & 0xC0) != 0x80)) {
*length = 0;
return 0;
}
code_point = (c1 & 0x07) << 18;
code_point |= (c2 & 0x3F) << 12;
code_point |= (c3 & 0x3F) << 6;
code_point |= (c4 & 0x3F);
// Error Check 2: Overlong check (4-byte must be > 0xFFFF)
// Also check for valid Unicode range (must be <= 0x10FFFF)
if (code_point <= 0xFFFF || code_point > 0x10FFFF) {
*length = 0;
return 0;
}
}
// --- Invalid leading byte (e.g., 10xxxxxx or 11111xxx) ---
else {
*length = 0;
return 0;
}
*length = current - reinterpret_cast<const uint8_t *>(utf8_str);
return code_point;
}
Font::Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
uint8_t bpp) uint8_t bpp)
: baseline_(baseline), : glyphs_(ConstVector(data, data_nr)),
baseline_(baseline),
height_(height), height_(height),
descender_(descender), descender_(descender),
linegap_(height - baseline - descender), linegap_(height - baseline - descender),
xheight_(xheight), xheight_(xheight),
capheight_(capheight), capheight_(capheight),
bpp_(bpp) { bpp_(bpp) {
glyphs_.reserve(data_nr); #ifdef USE_LVGL_FONT
for (int i = 0; i < data_nr; ++i) this->lv_font_.dsc = this;
glyphs_.emplace_back(&data[i]); this->lv_font_.line_height = this->get_height();
this->lv_font_.base_line = this->lv_font_.line_height - this->get_baseline();
this->lv_font_.get_glyph_dsc = get_glyph_dsc_cb;
this->lv_font_.get_glyph_bitmap = get_glyph_bitmap;
this->lv_font_.subpx = LV_FONT_SUBPX_NONE;
this->lv_font_.underline_position = -1;
this->lv_font_.underline_thickness = 1;
#endif
} }
int Font::match_next_glyph(const uint8_t *str, int *match_length) {
const Glyph *Font::find_glyph(uint32_t codepoint) const {
int lo = 0; int lo = 0;
int hi = this->glyphs_.size() - 1; int hi = this->glyphs_.size() - 1;
while (lo != hi) { while (lo != hi) {
int mid = (lo + hi + 1) / 2; int mid = (lo + hi + 1) / 2;
if (this->glyphs_[mid].compare_to(str)) { if (this->glyphs_[mid].is_less_or_equal(codepoint)) {
lo = mid; lo = mid;
} else { } else {
hi = mid - 1; hi = mid - 1;
} }
} }
*match_length = this->glyphs_[lo].match_length(str); auto *result = &this->glyphs_[lo];
if (*match_length <= 0) if (result->code_point == codepoint)
return -1; return result;
return lo; return nullptr;
} }
#ifdef USE_DISPLAY #ifdef USE_DISPLAY
void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) { void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) {
*baseline = this->baseline_; *baseline = this->baseline_;
*height = this->height_; *height = this->height_;
int i = 0;
int min_x = 0; int min_x = 0;
bool has_char = false; bool has_char = false;
int x = 0; int x = 0;
while (str[i] != '\0') { for (;;) {
int match_length; size_t length;
int glyph_n = this->match_next_glyph((const uint8_t *) str + i, &match_length); auto code_point = extract_unicode_codepoint(str, &length);
if (glyph_n < 0) { if (length == 0)
break;
str += length;
auto *glyph = this->find_glyph(code_point);
if (glyph == nullptr) {
// Unknown char, skip // Unknown char, skip
if (!this->get_glyphs().empty()) if (!this->glyphs_.empty())
x += this->get_glyphs()[0].glyph_data_->advance; x += this->glyphs_[0].advance;
i++;
continue; continue;
} }
const Glyph &glyph = this->glyphs_[glyph_n];
if (!has_char) { if (!has_char) {
min_x = glyph.glyph_data_->offset_x; min_x = glyph->offset_x;
} else { } else {
min_x = std::min(min_x, x + glyph.glyph_data_->offset_x); min_x = std::min(min_x, x + glyph->offset_x);
} }
x += glyph.glyph_data_->advance; x += glyph->advance;
i += match_length;
has_char = true; has_char = true;
} }
*x_offset = min_x; *x_offset = min_x;
*width = x - min_x; *width = x - min_x;
} }
void Font::print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) { void Font::print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) {
int i = 0;
int x_at = x_start; int x_at = x_start;
int scan_x1, scan_y1, scan_width, scan_height; for (;;) {
while (text[i] != '\0') { size_t length;
int match_length; auto code_point = extract_unicode_codepoint(text, &length);
int glyph_n = this->match_next_glyph((const uint8_t *) text + i, &match_length); if (length == 0)
if (glyph_n < 0) { break;
text += length;
auto *glyph = this->find_glyph(code_point);
if (glyph == nullptr) {
// Unknown char, skip // Unknown char, skip
ESP_LOGW(TAG, "Encountered character without representation in font: '%c'", text[i]); ESP_LOGW(TAG, "Codepoint 0x%08" PRIx32 " not found in font", code_point);
if (!this->get_glyphs().empty()) { if (!this->glyphs_.empty()) {
uint8_t glyph_width = this->get_glyphs()[0].glyph_data_->advance; uint8_t glyph_width = this->glyphs_[0].advance;
display->filled_rectangle(x_at, y_start, glyph_width, this->height_, color); display->rectangle(x_at, y_start, glyph_width, this->height_, color);
x_at += glyph_width; x_at += glyph_width;
} }
i++;
continue; continue;
} }
const Glyph &glyph = this->get_glyphs()[glyph_n]; const uint8_t *data = glyph->data;
glyph.scan_area(&scan_x1, &scan_y1, &scan_width, &scan_height); const int max_x = x_at + glyph->offset_x + glyph->width;
const int max_y = y_start + glyph->offset_y + glyph->height;
const uint8_t *data = glyph.glyph_data_->data;
const int max_x = x_at + scan_x1 + scan_width;
const int max_y = y_start + scan_y1 + scan_height;
uint8_t bitmask = 0; uint8_t bitmask = 0;
uint8_t pixel_data = 0; uint8_t pixel_data = 0;
@@ -145,10 +257,10 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
auto b_g = (float) background.g; auto b_g = (float) background.g;
auto b_b = (float) background.b; auto b_b = (float) background.b;
auto b_w = (float) background.w; auto b_w = (float) background.w;
for (int glyph_y = y_start + scan_y1; glyph_y != max_y; glyph_y++) { for (int glyph_y = y_start + glyph->offset_y; glyph_y != max_y; glyph_y++) {
for (int glyph_x = x_at + scan_x1; glyph_x != max_x; glyph_x++) { for (int glyph_x = x_at + glyph->offset_x; glyph_x != max_x; glyph_x++) {
uint8_t pixel = 0; uint8_t pixel = 0;
for (int bit_num = 0; bit_num != this->bpp_; bit_num++) { for (uint8_t bit_num = 0; bit_num != this->bpp_; bit_num++) {
if (bitmask == 0) { if (bitmask == 0) {
pixel_data = progmem_read_byte(data++); pixel_data = progmem_read_byte(data++);
bitmask = 0x80; bitmask = 0x80;
@@ -168,12 +280,9 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo
} }
} }
} }
x_at += glyph.glyph_data_->advance; x_at += glyph->advance;
i += match_length;
} }
} }
#endif #endif
} // namespace font } // namespace font
} // namespace esphome } // namespace esphome

View File

@@ -6,14 +6,30 @@
#ifdef USE_DISPLAY #ifdef USE_DISPLAY
#include "esphome/components/display/display.h" #include "esphome/components/display/display.h"
#endif #endif
#ifdef USE_LVGL_FONT
#include <lvgl.h>
#endif
namespace esphome { namespace esphome {
namespace font { namespace font {
class Font; class Font;
struct GlyphData { class Glyph {
const uint8_t *a_char; public:
constexpr Glyph(uint32_t code_point, const uint8_t *data, int advance, int offset_x, int offset_y, int width,
int height)
: code_point(code_point),
data(data),
advance(advance),
offset_x(offset_x),
offset_y(offset_y),
width(width),
height(height) {}
bool is_less_or_equal(uint32_t other) const { return this->code_point <= other; }
const uint32_t code_point;
const uint8_t *data; const uint8_t *data;
int advance; int advance;
int offset_x; int offset_x;
@@ -22,26 +38,6 @@ struct GlyphData {
int height; int height;
}; };
class Glyph {
public:
Glyph(const GlyphData *data) : glyph_data_(data) {}
const uint8_t *get_char() const;
bool compare_to(const uint8_t *str) const;
int match_length(const uint8_t *str) const;
void scan_area(int *x1, int *y1, int *width, int *height) const;
const GlyphData *get_glyph_data() const { return this->glyph_data_; }
protected:
friend Font;
const GlyphData *glyph_data_;
};
class Font class Font
#ifdef USE_DISPLAY #ifdef USE_DISPLAY
: public display::BaseFont : public display::BaseFont
@@ -50,8 +46,8 @@ class Font
public: public:
/** Construct the font with the given glyphs. /** Construct the font with the given glyphs.
* *
* @param data A vector of glyphs, must be sorted lexicographically. * @param data A list of glyphs, must be sorted lexicographically.
* @param data_nr The number of glyphs in data. * @param data_nr The number of glyphs
* @param baseline The y-offset from the top of the text to the baseline. * @param baseline The y-offset from the top of the text to the baseline.
* @param height The y-offset from the top of the text to the bottom. * @param height The y-offset from the top of the text to the bottom.
* @param descender The y-offset from the baseline to the lowest stroke in the font (e.g. from letters like g or p). * @param descender The y-offset from the baseline to the lowest stroke in the font (e.g. from letters like g or p).
@@ -59,10 +55,10 @@ class Font
* @param capheight The height of capital letters, usually measured at the "X" glyph. * @param capheight The height of capital letters, usually measured at the "X" glyph.
* @param bpp The bits per pixel used for this font. Used to read data out of the glyph bitmaps. * @param bpp The bits per pixel used for this font. Used to read data out of the glyph bitmaps.
*/ */
Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight, Font(const Glyph *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
uint8_t bpp = 1); uint8_t bpp = 1);
int match_next_glyph(const uint8_t *str, int *match_length); const Glyph *find_glyph(uint32_t codepoint) const;
#ifdef USE_DISPLAY #ifdef USE_DISPLAY
void print(int x_start, int y_start, display::Display *display, Color color, const char *text, void print(int x_start, int y_start, display::Display *display, Color color, const char *text,
@@ -77,11 +73,14 @@ class Font
inline int get_xheight() { return this->xheight_; } inline int get_xheight() { return this->xheight_; }
inline int get_capheight() { return this->capheight_; } inline int get_capheight() { return this->capheight_; }
inline int get_bpp() { return this->bpp_; } inline int get_bpp() { return this->bpp_; }
#ifdef USE_LVGL_FONT
const lv_font_t *get_lv_font() const { return &this->lv_font_; }
#endif
const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; } const ConstVector<Glyph> &get_glyphs() const { return glyphs_; }
protected: protected:
std::vector<Glyph, RAMAllocator<Glyph>> glyphs_; ConstVector<Glyph> glyphs_;
int baseline_; int baseline_;
int height_; int height_;
int descender_; int descender_;
@@ -89,6 +88,14 @@ class Font
int xheight_; int xheight_;
int capheight_; int capheight_;
uint8_t bpp_; // bits per pixel uint8_t bpp_; // bits per pixel
#ifdef USE_LVGL_FONT
lv_font_t lv_font_{};
static const uint8_t *get_glyph_bitmap(const lv_font_t *font, uint32_t unicode_letter);
static bool get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next);
const Glyph *get_glyph_data_(uint32_t unicode_letter);
uint32_t last_letter_{};
const Glyph *last_data_{};
#endif
}; };
} // namespace font } // namespace font

View File

@@ -4,8 +4,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "ld2410.h" #include "ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
template<typename... Ts> class BluetoothPasswordSetAction : public Action<Ts...> { template<typename... Ts> class BluetoothPasswordSetAction : public Action<Ts...> {
public: public:
@@ -18,5 +17,4 @@ template<typename... Ts> class BluetoothPasswordSetAction : public Action<Ts...>
LD2410Component *ld2410_comp_; LD2410Component *ld2410_comp_;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,9 +1,7 @@
#include "factory_reset_button.h" #include "factory_reset_button.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void FactoryResetButton::press_action() { this->parent_->factory_reset(); } void FactoryResetButton::press_action() { this->parent_->factory_reset(); }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class FactoryResetButton : public button::Button, public Parented<LD2410Component> { class FactoryResetButton : public button::Button, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class FactoryResetButton : public button::Button, public Parented<LD2410Componen
void press_action() override; void press_action() override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,9 +1,7 @@
#include "query_button.h" #include "query_button.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void QueryButton::press_action() { this->parent_->read_all_info(); } void QueryButton::press_action() { this->parent_->read_all_info(); }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class QueryButton : public button::Button, public Parented<LD2410Component> { class QueryButton : public button::Button, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class QueryButton : public button::Button, public Parented<LD2410Component> {
void press_action() override; void press_action() override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,9 +1,7 @@
#include "restart_button.h" #include "restart_button.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void RestartButton::press_action() { this->parent_->restart_and_read_all_info(); } void RestartButton::press_action() { this->parent_->restart_and_read_all_info(); }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class RestartButton : public button::Button, public Parented<LD2410Component> { class RestartButton : public button::Button, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class RestartButton : public button::Button, public Parented<LD2410Component> {
void press_action() override; void press_action() override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -9,12 +9,9 @@
#include "esphome/core/application.h" #include "esphome/core/application.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
static const char *const TAG = "ld2410"; static const char *const TAG = "ld2410";
static const char *const UNKNOWN_MAC = "unknown";
static const char *const VERSION_FMT = "%u.%02X.%02X%02X%02X%02X";
enum BaudRate : uint8_t { enum BaudRate : uint8_t {
BAUD_RATE_9600 = 1, BAUD_RATE_9600 = 1,
@@ -181,15 +178,15 @@ static inline bool validate_header_footer(const uint8_t *header_footer, const ui
} }
void LD2410Component::dump_config() { void LD2410Component::dump_config() {
std::string mac_str = char mac_s[18];
mac_address_is_valid(this->mac_address_) ? format_mac_address_pretty(this->mac_address_) : UNKNOWN_MAC; char version_s[20];
std::string version = str_sprintf(VERSION_FMT, this->version_[1], this->version_[0], this->version_[5], const char *mac_str = ld24xx::format_mac_str(this->mac_address_, mac_s);
this->version_[4], this->version_[3], this->version_[2]); ld24xx::format_version_str(this->version_, version_s);
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
"LD2410:\n" "LD2410:\n"
" Firmware version: %s\n" " Firmware version: %s\n"
" MAC address: %s", " MAC address: %s",
version.c_str(), mac_str.c_str()); version_s, mac_str);
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
ESP_LOGCONFIG(TAG, "Binary Sensors:"); ESP_LOGCONFIG(TAG, "Binary Sensors:");
LOG_BINARY_SENSOR(" ", "Target", this->target_binary_sensor_); LOG_BINARY_SENSOR(" ", "Target", this->target_binary_sensor_);
@@ -448,12 +445,12 @@ bool LD2410Component::handle_ack_data_() {
case CMD_QUERY_VERSION: { case CMD_QUERY_VERSION: {
std::memcpy(this->version_, &this->buffer_data_[12], sizeof(this->version_)); std::memcpy(this->version_, &this->buffer_data_[12], sizeof(this->version_));
std::string version = str_sprintf(VERSION_FMT, this->version_[1], this->version_[0], this->version_[5], char version_s[20];
this->version_[4], this->version_[3], this->version_[2]); ld24xx::format_version_str(this->version_, version_s);
ESP_LOGV(TAG, "Firmware version: %s", version.c_str()); ESP_LOGV(TAG, "Firmware version: %s", version_s);
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->version_text_sensor_ != nullptr) { if (this->version_text_sensor_ != nullptr) {
this->version_text_sensor_->publish_state(version); this->version_text_sensor_->publish_state(version_s);
} }
#endif #endif
break; break;
@@ -506,9 +503,9 @@ bool LD2410Component::handle_ack_data_() {
std::memcpy(this->mac_address_, &this->buffer_data_[10], sizeof(this->mac_address_)); std::memcpy(this->mac_address_, &this->buffer_data_[10], sizeof(this->mac_address_));
} }
std::string mac_str = char mac_s[18];
mac_address_is_valid(this->mac_address_) ? format_mac_address_pretty(this->mac_address_) : UNKNOWN_MAC; const char *mac_str = ld24xx::format_mac_str(this->mac_address_, mac_s);
ESP_LOGV(TAG, "MAC address: %s", mac_str.c_str()); ESP_LOGV(TAG, "MAC address: %s", mac_str);
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->mac_text_sensor_ != nullptr) { if (this->mac_text_sensor_ != nullptr) {
this->mac_text_sensor_->publish_state(mac_str); this->mac_text_sensor_->publish_state(mac_str);
@@ -784,5 +781,4 @@ void LD2410Component::set_gate_still_sensor(uint8_t gate, sensor::Sensor *s) {
} }
#endif #endif
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -29,8 +29,7 @@
#include <array> #include <array>
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
using namespace ld24xx; using namespace ld24xx;
@@ -133,5 +132,4 @@ class LD2410Component : public Component, public uart::UARTDevice {
#endif #endif
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,7 +1,6 @@
#include "gate_threshold_number.h" #include "gate_threshold_number.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
GateThresholdNumber::GateThresholdNumber(uint8_t gate) : gate_(gate) {} GateThresholdNumber::GateThresholdNumber(uint8_t gate) : gate_(gate) {}
@@ -10,5 +9,4 @@ void GateThresholdNumber::control(float value) {
this->parent_->set_gate_threshold(this->gate_); this->parent_->set_gate_threshold(this->gate_);
} }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/number/number.h" #include "esphome/components/number/number.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class GateThresholdNumber : public number::Number, public Parented<LD2410Component> { class GateThresholdNumber : public number::Number, public Parented<LD2410Component> {
public: public:
@@ -15,5 +14,4 @@ class GateThresholdNumber : public number::Number, public Parented<LD2410Compone
void control(float value) override; void control(float value) override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "light_threshold_number.h" #include "light_threshold_number.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void LightThresholdNumber::control(float value) { void LightThresholdNumber::control(float value) {
this->publish_state(value); this->publish_state(value);
this->parent_->set_light_out_control(); this->parent_->set_light_out_control();
} }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/number/number.h" #include "esphome/components/number/number.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class LightThresholdNumber : public number::Number, public Parented<LD2410Component> { class LightThresholdNumber : public number::Number, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class LightThresholdNumber : public number::Number, public Parented<LD2410Compon
void control(float value) override; void control(float value) override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "max_distance_timeout_number.h" #include "max_distance_timeout_number.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void MaxDistanceTimeoutNumber::control(float value) { void MaxDistanceTimeoutNumber::control(float value) {
this->publish_state(value); this->publish_state(value);
this->parent_->set_max_distances_timeout(); this->parent_->set_max_distances_timeout();
} }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/number/number.h" #include "esphome/components/number/number.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class MaxDistanceTimeoutNumber : public number::Number, public Parented<LD2410Component> { class MaxDistanceTimeoutNumber : public number::Number, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class MaxDistanceTimeoutNumber : public number::Number, public Parented<LD2410Co
void control(float value) override; void control(float value) override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "baud_rate_select.h" #include "baud_rate_select.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void BaudRateSelect::control(size_t index) { void BaudRateSelect::control(size_t index) {
this->publish_state(index); this->publish_state(index);
this->parent_->set_baud_rate(this->option_at(index)); this->parent_->set_baud_rate(this->option_at(index));
} }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/select/select.h" #include "esphome/components/select/select.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class BaudRateSelect : public select::Select, public Parented<LD2410Component> { class BaudRateSelect : public select::Select, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class BaudRateSelect : public select::Select, public Parented<LD2410Component> {
void control(size_t index) override; void control(size_t index) override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "distance_resolution_select.h" #include "distance_resolution_select.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void DistanceResolutionSelect::control(size_t index) { void DistanceResolutionSelect::control(size_t index) {
this->publish_state(index); this->publish_state(index);
this->parent_->set_distance_resolution(this->option_at(index)); this->parent_->set_distance_resolution(this->option_at(index));
} }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/select/select.h" #include "esphome/components/select/select.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class DistanceResolutionSelect : public select::Select, public Parented<LD2410Component> { class DistanceResolutionSelect : public select::Select, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class DistanceResolutionSelect : public select::Select, public Parented<LD2410Co
void control(size_t index) override; void control(size_t index) override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "light_out_control_select.h" #include "light_out_control_select.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void LightOutControlSelect::control(size_t index) { void LightOutControlSelect::control(size_t index) {
this->publish_state(index); this->publish_state(index);
this->parent_->set_light_out_control(); this->parent_->set_light_out_control();
} }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/select/select.h" #include "esphome/components/select/select.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class LightOutControlSelect : public select::Select, public Parented<LD2410Component> { class LightOutControlSelect : public select::Select, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class LightOutControlSelect : public select::Select, public Parented<LD2410Compo
void control(size_t index) override; void control(size_t index) override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -31,35 +31,83 @@ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component), cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema( cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema(
device_class=DEVICE_CLASS_DISTANCE, device_class=DEVICE_CLASS_DISTANCE,
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_SIGNAL, icon=ICON_SIGNAL,
unit_of_measurement=UNIT_CENTIMETER, unit_of_measurement=UNIT_CENTIMETER,
), ),
cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema( cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema(
device_class=DEVICE_CLASS_DISTANCE, device_class=DEVICE_CLASS_DISTANCE,
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_SIGNAL, icon=ICON_SIGNAL,
unit_of_measurement=UNIT_CENTIMETER, unit_of_measurement=UNIT_CENTIMETER,
), ),
cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema( cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema(
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_MOTION_SENSOR, icon=ICON_MOTION_SENSOR,
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,
), ),
cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema(
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_FLASH, icon=ICON_FLASH,
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,
), ),
cv.Optional(CONF_LIGHT): sensor.sensor_schema( cv.Optional(CONF_LIGHT): sensor.sensor_schema(
device_class=DEVICE_CLASS_ILLUMINANCE, device_class=DEVICE_CLASS_ILLUMINANCE,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_LIGHTBULB, icon=ICON_LIGHTBULB,
), ),
cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema( cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema(
device_class=DEVICE_CLASS_DISTANCE, device_class=DEVICE_CLASS_DISTANCE,
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_SIGNAL, icon=ICON_SIGNAL,
unit_of_measurement=UNIT_CENTIMETER, unit_of_measurement=UNIT_CENTIMETER,
), ),
@@ -73,7 +121,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
cv.Optional(CONF_MOVE_ENERGY): sensor.sensor_schema( cv.Optional(CONF_MOVE_ENERGY): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
filters=[ filters=[
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} {
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
], ],
icon=ICON_MOTION_SENSOR, icon=ICON_MOTION_SENSOR,
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,
@@ -81,7 +135,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
filters=[ filters=[
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} {
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
], ],
icon=ICON_FLASH, icon=ICON_FLASH,
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,

View File

@@ -1,12 +1,10 @@
#include "bluetooth_switch.h" #include "bluetooth_switch.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void BluetoothSwitch::write_state(bool state) { void BluetoothSwitch::write_state(bool state) {
this->publish_state(state); this->publish_state(state);
this->parent_->set_bluetooth(state); this->parent_->set_bluetooth(state);
} }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/switch/switch.h" #include "esphome/components/switch/switch.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class BluetoothSwitch : public switch_::Switch, public Parented<LD2410Component> { class BluetoothSwitch : public switch_::Switch, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class BluetoothSwitch : public switch_::Switch, public Parented<LD2410Component>
void write_state(bool state) override; void write_state(bool state) override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "engineering_mode_switch.h" #include "engineering_mode_switch.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
void EngineeringModeSwitch::write_state(bool state) { void EngineeringModeSwitch::write_state(bool state) {
this->publish_state(state); this->publish_state(state);
this->parent_->set_engineering_mode(state); this->parent_->set_engineering_mode(state);
} }
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/switch/switch.h" #include "esphome/components/switch/switch.h"
#include "../ld2410.h" #include "../ld2410.h"
namespace esphome { namespace esphome::ld2410 {
namespace ld2410 {
class EngineeringModeSwitch : public switch_::Switch, public Parented<LD2410Component> { class EngineeringModeSwitch : public switch_::Switch, public Parented<LD2410Component> {
public: public:
@@ -14,5 +13,4 @@ class EngineeringModeSwitch : public switch_::Switch, public Parented<LD2410Comp
void write_state(bool state) override; void write_state(bool state) override;
}; };
} // namespace ld2410 } // namespace esphome::ld2410
} // namespace esphome

View File

@@ -1,9 +1,7 @@
#include "factory_reset_button.h" #include "factory_reset_button.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void FactoryResetButton::press_action() { this->parent_->factory_reset(); } void FactoryResetButton::press_action() { this->parent_->factory_reset(); }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class FactoryResetButton : public button::Button, public Parented<LD2412Component> { class FactoryResetButton : public button::Button, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class FactoryResetButton : public button::Button, public Parented<LD2412Componen
void press_action() override; void press_action() override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,9 +1,7 @@
#include "query_button.h" #include "query_button.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void QueryButton::press_action() { this->parent_->read_all_info(); } void QueryButton::press_action() { this->parent_->read_all_info(); }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class QueryButton : public button::Button, public Parented<LD2412Component> { class QueryButton : public button::Button, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class QueryButton : public button::Button, public Parented<LD2412Component> {
void press_action() override; void press_action() override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,9 +1,7 @@
#include "restart_button.h" #include "restart_button.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void RestartButton::press_action() { this->parent_->restart_and_read_all_info(); } void RestartButton::press_action() { this->parent_->restart_and_read_all_info(); }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class RestartButton : public button::Button, public Parented<LD2412Component> { class RestartButton : public button::Button, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class RestartButton : public button::Button, public Parented<LD2412Component> {
void press_action() override; void press_action() override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -2,10 +2,8 @@
#include "restart_button.h" #include "restart_button.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void StartDynamicBackgroundCorrectionButton::press_action() { this->parent_->start_dynamic_background_correction(); } void StartDynamicBackgroundCorrectionButton::press_action() { this->parent_->start_dynamic_background_correction(); }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class StartDynamicBackgroundCorrectionButton : public button::Button, public Parented<LD2412Component> { class StartDynamicBackgroundCorrectionButton : public button::Button, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class StartDynamicBackgroundCorrectionButton : public button::Button, public Par
void press_action() override; void press_action() override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -10,12 +10,9 @@
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
static const char *const TAG = "ld2412"; static const char *const TAG = "ld2412";
static const char *const UNKNOWN_MAC = "unknown";
static const char *const VERSION_FMT = "%u.%02X.%02X%02X%02X%02X";
enum BaudRate : uint8_t { enum BaudRate : uint8_t {
BAUD_RATE_9600 = 1, BAUD_RATE_9600 = 1,
@@ -200,15 +197,15 @@ static inline bool validate_header_footer(const uint8_t *header_footer, const ui
} }
void LD2412Component::dump_config() { void LD2412Component::dump_config() {
std::string mac_str = char mac_s[18];
mac_address_is_valid(this->mac_address_) ? format_mac_address_pretty(this->mac_address_) : UNKNOWN_MAC; char version_s[20];
std::string version = str_sprintf(VERSION_FMT, this->version_[1], this->version_[0], this->version_[5], const char *mac_str = ld24xx::format_mac_str(this->mac_address_, mac_s);
this->version_[4], this->version_[3], this->version_[2]); ld24xx::format_version_str(this->version_, version_s);
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
"LD2412:\n" "LD2412:\n"
" Firmware version: %s\n" " Firmware version: %s\n"
" MAC address: %s", " MAC address: %s",
version.c_str(), mac_str.c_str()); version_s, mac_str);
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
ESP_LOGCONFIG(TAG, "Binary Sensors:"); ESP_LOGCONFIG(TAG, "Binary Sensors:");
LOG_BINARY_SENSOR(" ", "DynamicBackgroundCorrectionStatus", LOG_BINARY_SENSOR(" ", "DynamicBackgroundCorrectionStatus",
@@ -492,12 +489,12 @@ bool LD2412Component::handle_ack_data_() {
case CMD_QUERY_VERSION: { case CMD_QUERY_VERSION: {
std::memcpy(this->version_, &this->buffer_data_[12], sizeof(this->version_)); std::memcpy(this->version_, &this->buffer_data_[12], sizeof(this->version_));
std::string version = str_sprintf(VERSION_FMT, this->version_[1], this->version_[0], this->version_[5], char version_s[20];
this->version_[4], this->version_[3], this->version_[2]); ld24xx::format_version_str(this->version_, version_s);
ESP_LOGV(TAG, "Firmware version: %s", version.c_str()); ESP_LOGV(TAG, "Firmware version: %s", version_s);
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->version_text_sensor_ != nullptr) { if (this->version_text_sensor_ != nullptr) {
this->version_text_sensor_->publish_state(version); this->version_text_sensor_->publish_state(version_s);
} }
#endif #endif
break; break;
@@ -544,9 +541,9 @@ bool LD2412Component::handle_ack_data_() {
std::memcpy(this->mac_address_, &this->buffer_data_[10], sizeof(this->mac_address_)); std::memcpy(this->mac_address_, &this->buffer_data_[10], sizeof(this->mac_address_));
} }
std::string mac_str = char mac_s[18];
mac_address_is_valid(this->mac_address_) ? format_mac_address_pretty(this->mac_address_) : UNKNOWN_MAC; const char *mac_str = ld24xx::format_mac_str(this->mac_address_, mac_s);
ESP_LOGV(TAG, "MAC address: %s", mac_str.c_str()); ESP_LOGV(TAG, "MAC address: %s", mac_str);
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->mac_text_sensor_ != nullptr) { if (this->mac_text_sensor_ != nullptr) {
this->mac_text_sensor_->publish_state(mac_str); this->mac_text_sensor_->publish_state(mac_str);
@@ -857,5 +854,4 @@ void LD2412Component::set_gate_still_sensor(uint8_t gate, sensor::Sensor *s) {
} }
#endif #endif
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -29,8 +29,7 @@
#include <array> #include <array>
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
using namespace ld24xx; using namespace ld24xx;
@@ -137,5 +136,4 @@ class LD2412Component : public Component, public uart::UARTDevice {
#endif #endif
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,7 +1,6 @@
#include "gate_threshold_number.h" #include "gate_threshold_number.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
GateThresholdNumber::GateThresholdNumber(uint8_t gate) : gate_(gate) {} GateThresholdNumber::GateThresholdNumber(uint8_t gate) : gate_(gate) {}
@@ -10,5 +9,4 @@ void GateThresholdNumber::control(float value) {
this->parent_->set_gate_threshold(); this->parent_->set_gate_threshold();
} }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/number/number.h" #include "esphome/components/number/number.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class GateThresholdNumber : public number::Number, public Parented<LD2412Component> { class GateThresholdNumber : public number::Number, public Parented<LD2412Component> {
public: public:
@@ -15,5 +14,4 @@ class GateThresholdNumber : public number::Number, public Parented<LD2412Compone
void control(float value) override; void control(float value) override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "light_threshold_number.h" #include "light_threshold_number.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void LightThresholdNumber::control(float value) { void LightThresholdNumber::control(float value) {
this->publish_state(value); this->publish_state(value);
this->parent_->set_light_out_control(); this->parent_->set_light_out_control();
} }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/number/number.h" #include "esphome/components/number/number.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class LightThresholdNumber : public number::Number, public Parented<LD2412Component> { class LightThresholdNumber : public number::Number, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class LightThresholdNumber : public number::Number, public Parented<LD2412Compon
void control(float value) override; void control(float value) override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "max_distance_timeout_number.h" #include "max_distance_timeout_number.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void MaxDistanceTimeoutNumber::control(float value) { void MaxDistanceTimeoutNumber::control(float value) {
this->publish_state(value); this->publish_state(value);
this->parent_->set_basic_config(); this->parent_->set_basic_config();
} }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/number/number.h" #include "esphome/components/number/number.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class MaxDistanceTimeoutNumber : public number::Number, public Parented<LD2412Component> { class MaxDistanceTimeoutNumber : public number::Number, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class MaxDistanceTimeoutNumber : public number::Number, public Parented<LD2412Co
void control(float value) override; void control(float value) override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "baud_rate_select.h" #include "baud_rate_select.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void BaudRateSelect::control(size_t index) { void BaudRateSelect::control(size_t index) {
this->publish_state(index); this->publish_state(index);
this->parent_->set_baud_rate(this->option_at(index)); this->parent_->set_baud_rate(this->option_at(index));
} }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/select/select.h" #include "esphome/components/select/select.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class BaudRateSelect : public select::Select, public Parented<LD2412Component> { class BaudRateSelect : public select::Select, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class BaudRateSelect : public select::Select, public Parented<LD2412Component> {
void control(size_t index) override; void control(size_t index) override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "distance_resolution_select.h" #include "distance_resolution_select.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void DistanceResolutionSelect::control(size_t index) { void DistanceResolutionSelect::control(size_t index) {
this->publish_state(index); this->publish_state(index);
this->parent_->set_distance_resolution(this->option_at(index)); this->parent_->set_distance_resolution(this->option_at(index));
} }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/select/select.h" #include "esphome/components/select/select.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class DistanceResolutionSelect : public select::Select, public Parented<LD2412Component> { class DistanceResolutionSelect : public select::Select, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class DistanceResolutionSelect : public select::Select, public Parented<LD2412Co
void control(size_t index) override; void control(size_t index) override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "light_out_control_select.h" #include "light_out_control_select.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void LightOutControlSelect::control(size_t index) { void LightOutControlSelect::control(size_t index) {
this->publish_state(index); this->publish_state(index);
this->parent_->set_light_out_control(); this->parent_->set_light_out_control();
} }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/select/select.h" #include "esphome/components/select/select.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class LightOutControlSelect : public select::Select, public Parented<LD2412Component> { class LightOutControlSelect : public select::Select, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class LightOutControlSelect : public select::Select, public Parented<LD2412Compo
void control(size_t index) override; void control(size_t index) override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -31,36 +31,84 @@ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component), cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component),
cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema( cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema(
device_class=DEVICE_CLASS_DISTANCE, device_class=DEVICE_CLASS_DISTANCE,
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_SIGNAL, icon=ICON_SIGNAL,
unit_of_measurement=UNIT_CENTIMETER, unit_of_measurement=UNIT_CENTIMETER,
), ),
cv.Optional(CONF_LIGHT): sensor.sensor_schema( cv.Optional(CONF_LIGHT): sensor.sensor_schema(
device_class=DEVICE_CLASS_ILLUMINANCE, device_class=DEVICE_CLASS_ILLUMINANCE,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_LIGHTBULB, icon=ICON_LIGHTBULB,
unit_of_measurement=UNIT_EMPTY, # No standard unit for this light sensor unit_of_measurement=UNIT_EMPTY, # No standard unit for this light sensor
), ),
cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema( cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema(
device_class=DEVICE_CLASS_DISTANCE, device_class=DEVICE_CLASS_DISTANCE,
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_SIGNAL, icon=ICON_SIGNAL,
unit_of_measurement=UNIT_CENTIMETER, unit_of_measurement=UNIT_CENTIMETER,
), ),
cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema( cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema(
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_MOTION_SENSOR, icon=ICON_MOTION_SENSOR,
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,
), ),
cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema( cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema(
device_class=DEVICE_CLASS_DISTANCE, device_class=DEVICE_CLASS_DISTANCE,
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_SIGNAL, icon=ICON_SIGNAL,
unit_of_measurement=UNIT_CENTIMETER, unit_of_measurement=UNIT_CENTIMETER,
), ),
cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema(
filters=[{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)}], filters=[
{
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
],
icon=ICON_FLASH, icon=ICON_FLASH,
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,
), ),
@@ -74,7 +122,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
cv.Optional(CONF_MOVE_ENERGY): sensor.sensor_schema( cv.Optional(CONF_MOVE_ENERGY): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
filters=[ filters=[
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} {
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
], ],
icon=ICON_MOTION_SENSOR, icon=ICON_MOTION_SENSOR,
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,
@@ -82,7 +136,13 @@ CONFIG_SCHEMA = CONFIG_SCHEMA.extend(
cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema( cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
filters=[ filters=[
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)} {
"timeout": {
"timeout": cv.TimePeriod(milliseconds=1000),
"value": "last",
}
},
{"throttle_with_priority": cv.TimePeriod(milliseconds=1000)},
], ],
icon=ICON_FLASH, icon=ICON_FLASH,
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,

View File

@@ -1,12 +1,10 @@
#include "bluetooth_switch.h" #include "bluetooth_switch.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void BluetoothSwitch::write_state(bool state) { void BluetoothSwitch::write_state(bool state) {
this->publish_state(state); this->publish_state(state);
this->parent_->set_bluetooth(state); this->parent_->set_bluetooth(state);
} }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/switch/switch.h" #include "esphome/components/switch/switch.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class BluetoothSwitch : public switch_::Switch, public Parented<LD2412Component> { class BluetoothSwitch : public switch_::Switch, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class BluetoothSwitch : public switch_::Switch, public Parented<LD2412Component>
void write_state(bool state) override; void write_state(bool state) override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -1,12 +1,10 @@
#include "engineering_mode_switch.h" #include "engineering_mode_switch.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
void EngineeringModeSwitch::write_state(bool state) { void EngineeringModeSwitch::write_state(bool state) {
this->publish_state(state); this->publish_state(state);
this->parent_->set_engineering_mode(state); this->parent_->set_engineering_mode(state);
} }
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/switch/switch.h" #include "esphome/components/switch/switch.h"
#include "../ld2412.h" #include "../ld2412.h"
namespace esphome { namespace esphome::ld2412 {
namespace ld2412 {
class EngineeringModeSwitch : public switch_::Switch, public Parented<LD2412Component> { class EngineeringModeSwitch : public switch_::Switch, public Parented<LD2412Component> {
public: public:
@@ -14,5 +13,4 @@ class EngineeringModeSwitch : public switch_::Switch, public Parented<LD2412Comp
void write_state(bool state) override; void write_state(bool state) override;
}; };
} // namespace ld2412 } // namespace esphome::ld2412
} // namespace esphome

View File

@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
static const char *const TAG = "ld2420.binary_sensor"; static const char *const TAG = "ld2420.binary_sensor";
@@ -12,5 +11,4 @@ void LD2420BinarySensor::dump_config() {
LOG_BINARY_SENSOR(" ", "Presence", this->presence_bsensor_); LOG_BINARY_SENSOR(" ", "Presence", this->presence_bsensor_);
} }
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "../ld2420.h" #include "../ld2420.h"
#include "esphome/components/binary_sensor/binary_sensor.h" #include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
class LD2420BinarySensor : public LD2420Listener, public Component, binary_sensor::BinarySensor { class LD2420BinarySensor : public LD2420Listener, public Component, binary_sensor::BinarySensor {
public: public:
@@ -21,5 +20,4 @@ class LD2420BinarySensor : public LD2420Listener, public Component, binary_senso
binary_sensor::BinarySensor *presence_bsensor_{nullptr}; binary_sensor::BinarySensor *presence_bsensor_{nullptr};
}; };
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -4,13 +4,11 @@
static const char *const TAG = "ld2420.button"; static const char *const TAG = "ld2420.button";
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
void LD2420ApplyConfigButton::press_action() { this->parent_->apply_config_action(); } void LD2420ApplyConfigButton::press_action() { this->parent_->apply_config_action(); }
void LD2420RevertConfigButton::press_action() { this->parent_->revert_config_action(); } void LD2420RevertConfigButton::press_action() { this->parent_->revert_config_action(); }
void LD2420RestartModuleButton::press_action() { this->parent_->restart_module_action(); } void LD2420RestartModuleButton::press_action() { this->parent_->restart_module_action(); }
void LD2420FactoryResetButton::press_action() { this->parent_->factory_reset_action(); } void LD2420FactoryResetButton::press_action() { this->parent_->factory_reset_action(); }
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#include "../ld2420.h" #include "../ld2420.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
class LD2420ApplyConfigButton : public button::Button, public Parented<LD2420Component> { class LD2420ApplyConfigButton : public button::Button, public Parented<LD2420Component> {
public: public:
@@ -38,5 +37,4 @@ class LD2420FactoryResetButton : public button::Button, public Parented<LD2420Co
void press_action() override; void press_action() override;
}; };
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -58,8 +58,7 @@ Gate 0 high thresh = 10 00 uint16_t 0x0010, Threshold value = 60 EA 00 00 uint32
Gate 0 low thresh = 20 00 uint16_t 0x0020, Threshold value = 60 EA 00 00 uint32_t 0x0000EA60 Gate 0 low thresh = 20 00 uint16_t 0x0020, Threshold value = 60 EA 00 00 uint32_t 0x0000EA60
*/ */
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
static const char *const TAG = "ld2420"; static const char *const TAG = "ld2420";
@@ -880,5 +879,4 @@ void LD2420Component::refresh_gate_config_numbers() {
#endif #endif
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -17,8 +17,7 @@
#include "esphome/components/button/button.h" #include "esphome/components/button/button.h"
#endif #endif
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
static const uint8_t CALIBRATE_SAMPLES = 64; static const uint8_t CALIBRATE_SAMPLES = 64;
static const uint8_t MAX_LINE_LENGTH = 46; // Max characters for serial buffer static const uint8_t MAX_LINE_LENGTH = 46; // Max characters for serial buffer
@@ -193,5 +192,4 @@ class LD2420Component : public Component, public uart::UARTDevice {
std::vector<LD2420Listener *> listeners_{}; std::vector<LD2420Listener *> listeners_{};
}; };
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -4,8 +4,7 @@
static const char *const TAG = "ld2420.number"; static const char *const TAG = "ld2420.number";
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
void LD2420TimeoutNumber::control(float timeout) { void LD2420TimeoutNumber::control(float timeout) {
this->publish_state(timeout); this->publish_state(timeout);
@@ -69,5 +68,4 @@ void LD2420StillThresholdNumbers::control(float still_threshold) {
} }
} }
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "esphome/components/number/number.h" #include "esphome/components/number/number.h"
#include "../ld2420.h" #include "../ld2420.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
class LD2420TimeoutNumber : public number::Number, public Parented<LD2420Component> { class LD2420TimeoutNumber : public number::Number, public Parented<LD2420Component> {
public: public:
@@ -74,5 +73,4 @@ class LD2420MoveThresholdNumbers : public number::Number, public Parented<LD2420
void control(float move_threshold) override; void control(float move_threshold) override;
}; };
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
static const char *const TAG = "ld2420.select"; static const char *const TAG = "ld2420.select";
@@ -12,5 +11,4 @@ void LD2420Select::control(size_t index) {
this->parent_->set_operating_mode(this->option_at(index)); this->parent_->set_operating_mode(this->option_at(index));
} }
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "../ld2420.h" #include "../ld2420.h"
#include "esphome/components/select/select.h" #include "esphome/components/select/select.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
class LD2420Select : public Component, public select::Select, public Parented<LD2420Component> { class LD2420Select : public Component, public select::Select, public Parented<LD2420Component> {
public: public:
@@ -14,5 +13,4 @@ class LD2420Select : public Component, public select::Select, public Parented<LD
void control(size_t index) override; void control(size_t index) override;
}; };
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
static const char *const TAG = "ld2420.sensor"; static const char *const TAG = "ld2420.sensor";
@@ -12,5 +11,4 @@ void LD2420Sensor::dump_config() {
LOG_SENSOR(" ", "Distance", this->distance_sensor_); LOG_SENSOR(" ", "Distance", this->distance_sensor_);
} }
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -3,8 +3,7 @@
#include "../ld2420.h" #include "../ld2420.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
class LD2420Sensor : public LD2420Listener, public Component, sensor::Sensor { class LD2420Sensor : public LD2420Listener, public Component, sensor::Sensor {
public: public:
@@ -30,5 +29,4 @@ class LD2420Sensor : public LD2420Listener, public Component, sensor::Sensor {
std::vector<sensor::Sensor *> energy_sensors_ = std::vector<sensor::Sensor *>(TOTAL_GATES); std::vector<sensor::Sensor *> energy_sensors_ = std::vector<sensor::Sensor *>(TOTAL_GATES);
}; };
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

View File

@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
namespace esphome { namespace esphome::ld2420 {
namespace ld2420 {
static const char *const TAG = "ld2420.text_sensor"; static const char *const TAG = "ld2420.text_sensor";
@@ -12,5 +11,4 @@ void LD2420TextSensor::dump_config() {
LOG_TEXT_SENSOR(" ", "Firmware", this->fw_version_text_sensor_); LOG_TEXT_SENSOR(" ", "Firmware", this->fw_version_text_sensor_);
} }
} // namespace ld2420 } // namespace esphome::ld2420
} // namespace esphome

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