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

This commit is contained in:
J. Nick Koston
2026-05-12 12:52:15 -05:00
1808 changed files with 12525 additions and 8089 deletions
-2
View File
@@ -96,7 +96,6 @@ Checks: >-
-modernize-avoid-variadic-functions,
-modernize-avoid-c-arrays,
-modernize-avoid-c-style-cast,
-modernize-concat-nested-namespaces,
-modernize-macro-to-enum,
-modernize-return-braced-init-list,
-modernize-type-traits,
@@ -133,7 +132,6 @@ Checks: >-
-readability-redundant-inline-specifier,
-readability-redundant-member-init,
-readability-redundant-parentheses,
-readability-redundant-string-init,
-readability-redundant-typename,
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
+1 -1
View File
@@ -1 +1 @@
0c7f309d70eca8e3efd510092ddb23c530f3934c49371717efa124b788d761f8
96c95feaa60831da5f43e3c6a7c7a3a237e17c5d12995a730dbc3884c8dcd11c
+1 -1
View File
@@ -115,4 +115,4 @@ examples/
Dockerfile
.git/
tests/
.*
.?*
+74 -31
View File
@@ -1,4 +1,3 @@
const fs = require('fs');
const { DOCS_PR_PATTERNS } = require('./constants');
const {
COMPONENT_REGEX,
@@ -9,6 +8,31 @@ const {
} = require('../detect-tags');
const { loadCodeowners, getEffectiveOwners } = require('../codeowners');
// Top-level `CONFIG_SCHEMA = ...` (assignment) or `CONFIG_SCHEMA: ConfigType = ...` (annotation).
// Ruff/Black enforce exactly one space around `=` and no space before `:`,
// so we can match strictly: `CONFIG_SCHEMA ` or `CONFIG_SCHEMA:`.
const CONFIG_SCHEMA_REGEX = /^CONFIG_SCHEMA[ :]/m;
// Fetch a file's contents from the PR head SHA via the GitHub API.
// The auto-label workflow runs on `pull_request_target`, which checks out the
// base branch — files added by the PR don't exist in the workspace, so we have
// to fetch them from the head SHA. Returns null if the file can't be fetched.
async function fetchPrFileContent(github, context, path) {
try {
const { owner, repo } = context.repo;
const { data } = await github.rest.repos.getContent({
owner,
repo,
path,
ref: context.payload.pull_request.head.sha,
});
return Buffer.from(data.content, 'base64').toString('utf8');
} catch (error) {
console.log(`Failed to fetch ${path} from PR head:`, error.message);
return null;
}
}
// Strategy: Merge branch detection
async function detectMergeBranch(context) {
const labels = new Set();
@@ -45,52 +69,64 @@ async function detectComponentPlatforms(changedFiles, apiData) {
}
// Strategy: New component detection
async function detectNewComponents(prFiles) {
async function detectNewComponents(github, context, prFiles) {
const labels = new Set();
let hasYamlLoadable = false;
const addedFiles = prFiles.filter(file => file.status === 'added').map(file => file.filename);
for (const file of addedFiles) {
const componentMatch = file.match(/^esphome\/components\/([^\/]+)\/__init__\.py$/);
if (componentMatch) {
try {
const content = fs.readFileSync(file, 'utf8');
if (content.includes('IS_TARGET_PLATFORM = True')) {
labels.add('new-target-platform');
}
} catch (error) {
console.log(`Failed to read content of ${file}:`, error.message);
}
labels.add('new-component');
if (!componentMatch) continue;
labels.add('new-component');
const content = await fetchPrFileContent(github, context, file);
if (content === null) {
// Safe default: assume YAML-loadable so needs-docs behaviour is unchanged on fetch failure
hasYamlLoadable = true;
continue;
}
if (content.includes('IS_TARGET_PLATFORM = True')) {
labels.add('new-target-platform');
}
if (CONFIG_SCHEMA_REGEX.test(content)) {
hasYamlLoadable = true;
}
}
return labels;
return { labels, hasYamlLoadable };
}
// Strategy: New platform detection
async function detectNewPlatforms(prFiles, apiData) {
async function detectNewPlatforms(github, context, prFiles, apiData) {
const labels = new Set();
let hasYamlLoadable = false;
const addedFiles = prFiles.filter(file => file.status === 'added').map(file => file.filename);
for (const file of addedFiles) {
const platformFileMatch = file.match(/^esphome\/components\/([^\/]+)\/([^\/]+)\.py$/);
if (platformFileMatch) {
const [, component, platform] = platformFileMatch;
if (apiData.platformComponents.includes(platform)) {
labels.add('new-platform');
}
}
const platformPathPatterns = [
/^esphome\/components\/([^\/]+)\/([^\/]+)\.py$/,
/^esphome\/components\/([^\/]+)\/([^\/]+)\/__init__\.py$/,
];
const platformDirMatch = file.match(/^esphome\/components\/([^\/]+)\/([^\/]+)\/__init__\.py$/);
if (platformDirMatch) {
const [, component, platform] = platformDirMatch;
if (apiData.platformComponents.includes(platform)) {
labels.add('new-platform');
for (const file of addedFiles) {
for (const re of platformPathPatterns) {
const match = file.match(re);
if (!match) continue;
const platform = match[2];
if (!apiData.platformComponents.includes(platform)) break;
labels.add('new-platform');
const content = await fetchPrFileContent(github, context, file);
if (content === null) {
// Safe default: assume YAML-loadable so needs-docs behaviour is unchanged on fetch failure
hasYamlLoadable = true;
} else if (CONFIG_SCHEMA_REGEX.test(content)) {
hasYamlLoadable = true;
}
break;
}
}
return labels;
return { labels, hasYamlLoadable };
}
// Strategy: Core files detection
@@ -300,7 +336,7 @@ function detectMaintainerAccess(context) {
}
// Strategy: Requirements detection
async function detectRequirements(allLabels, prFiles, context) {
async function detectRequirements(allLabels, prFiles, context, hasYamlLoadable) {
const labels = new Set();
// Check for missing tests
@@ -308,8 +344,15 @@ async function detectRequirements(allLabels, prFiles, context) {
labels.add('needs-tests');
}
// Check for missing docs
if (allLabels.has('new-component') || allLabels.has('new-platform') || allLabels.has('new-feature')) {
// Check for missing docs.
// `new-feature` (PR-body checkbox) always counts. `new-component` / `new-platform`
// only count when at least one newly added file defines a top-level CONFIG_SCHEMA,
// i.e. the new component/platform is actually loadable from YAML.
const docsEligible =
allLabels.has('new-feature') ||
((allLabels.has('new-component') || allLabels.has('new-platform')) && hasYamlLoadable);
if (docsEligible) {
const prBody = context.payload.pull_request.body || '';
const hasDocsLink = DOCS_PR_PATTERNS.some(pattern => pattern.test(prBody));
+12 -5
View File
@@ -106,8 +106,8 @@ module.exports = async ({ github, context }) => {
const [
branchLabels,
componentLabels,
newComponentLabels,
newPlatformLabels,
newComponentResult,
newPlatformResult,
coreLabels,
sizeLabels,
dashboardLabels,
@@ -120,8 +120,8 @@ module.exports = async ({ github, context }) => {
] = await Promise.all([
detectMergeBranch(context),
detectComponentPlatforms(changedFiles, apiData),
detectNewComponents(prFiles),
detectNewPlatforms(prFiles, apiData),
detectNewComponents(github, context, prFiles),
detectNewPlatforms(github, context, prFiles, apiData),
detectCoreChanges(changedFiles),
detectPRSize(prFiles, totalAdditions, totalDeletions, totalChanges, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD),
detectDashboardChanges(changedFiles),
@@ -133,6 +133,13 @@ module.exports = async ({ github, context }) => {
detectMaintainerAccess(context)
]);
// Extract new-component / new-platform results
const newComponentLabels = newComponentResult.labels;
const newPlatformLabels = newPlatformResult.labels;
// Eligible for needs-docs only if any newly added component or platform file
// defines a top-level CONFIG_SCHEMA (i.e. is actually loadable from YAML).
const hasYamlLoadable = newComponentResult.hasYamlLoadable || newPlatformResult.hasYamlLoadable;
// Extract deprecated component info
const deprecatedLabels = deprecatedResult.labels;
const deprecatedInfo = deprecatedResult.deprecatedInfo;
@@ -154,7 +161,7 @@ module.exports = async ({ github, context }) => {
]);
// Detect requirements based on all other labels
const requirementLabels = await detectRequirements(allLabels, prFiles, context);
const requirementLabels = await detectRequirements(allLabels, prFiles, context, hasYamlLoadable);
for (const label of requirementLabels) {
allLabels.add(label);
}
+7 -2
View File
@@ -6,9 +6,10 @@ on:
pull_request_target:
types: [labeled, opened, reopened, synchronize, edited]
# All PR/label/review writes are performed with the App token minted below,
# so the workflow's GITHUB_TOKEN only needs read access for checkout.
permissions:
pull-requests: write
contents: read
contents: read # actions/checkout reads the workflow source
env:
SMALL_PR_THRESHOLD: 30
@@ -31,6 +32,10 @@ jobs:
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
# Scope the minted App token to the minimum needed by auto-label-pr/*.js.
permission-contents: read # repos.getContent for CODEOWNERS and file lookups in detectors.js
permission-issues: write # listLabelsOnIssue, addLabels, removeLabel, list/createComment
permission-pull-requests: write # pulls.listFiles, list/create/update/dismissReview
- name: Auto Label PR
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+2 -2
View File
@@ -12,8 +12,8 @@ on:
- ".github/workflows/ci-api-proto.yml"
permissions:
contents: read
pull-requests: write
contents: read # actions/checkout for the PR head
pull-requests: write # pulls.createReview / listReviews / dismissReview when generated proto files are stale
jobs:
check:
+2 -2
View File
@@ -12,8 +12,8 @@ on:
- ".github/workflows/ci-clang-tidy-hash.yml"
permissions:
contents: read
pull-requests: write
contents: read # actions/checkout for the PR head
pull-requests: write # pulls.createReview / listReviews / dismissReview when the clang-tidy hash is out of date
jobs:
verify-hash:
+1 -2
View File
@@ -22,8 +22,7 @@ on:
- "script/platformio_install_deps.py"
permissions:
contents: read
packages: read
contents: read # actions/checkout only; the build does not push images
concurrency:
# yamllint disable-line rule:line-length
@@ -7,9 +7,9 @@ on:
types: [completed]
permissions:
contents: read
pull-requests: write
actions: read
contents: read # actions/checkout of the base repo at the PR's target branch
pull-requests: write # gh api to look up the PR by head SHA and post/update the memory-impact comment
actions: read # gh run download for the memory-analysis artifacts produced by the CI workflow run
jobs:
memory-impact-comment:
+89 -3
View File
@@ -17,7 +17,7 @@ on:
merge_group:
permissions:
contents: read
contents: read # actions/checkout for all jobs; individual jobs add their own scopes when they need to write
env:
DEFAULT_PYTHON: "3.11"
@@ -783,6 +783,91 @@ jobs:
# Run compilation with grouping and isolation
python3 script/test_build_components.py -e compile -c "$components_csv" -f --isolate "$directly_changed_csv"
test-native-idf:
name: Test components with native ESP-IDF
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request'
env:
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
TEST_COMPONENTS: esp32,api,heatpumpir,bme280_i2c,bh1750,aht10,esp32_ble,esp32_ble_beacon,esp32_ble_client,esp32_ble_server,esp32_ble_tracker,ble_client,ble_presence,ble_rssi,ble_scanner
steps:
- name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESPHome
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
- name: Run native ESP-IDF compile test
run: |
. venv/bin/activate
# Check if /mnt has more free space than / before bind mounting
# Extract available space in KB for comparison
root_avail=$(df -k / | awk 'NR==2 {print $4}')
mnt_avail=$(df -k /mnt 2>/dev/null | awk 'NR==2 {print $4}')
echo "Available space: / has ${root_avail}KB, /mnt has ${mnt_avail}KB"
# Only use /mnt if it has more space than /
if [ -n "$mnt_avail" ] && [ "$mnt_avail" -gt "$root_avail" ]; then
echo "Using /mnt for build files (more space available)"
# Bind mount PlatformIO directory to /mnt (tools, packages, build cache all go there)
sudo mkdir -p /mnt/esphome-idf
sudo chown $USER:$USER /mnt/esphome-idf
mkdir -p ~/.esphome-idf
sudo mount --bind /mnt/esphome-idf ~/.esphome-idf
# Bind mount test build directory to /mnt
sudo mkdir -p /mnt/test_build_components_build
sudo chown $USER:$USER /mnt/test_build_components_build
mkdir -p tests/test_build_components/build
sudo mount --bind /mnt/test_build_components_build tests/test_build_components/build
else
echo "Using / for build files (more space available than /mnt or /mnt unavailable)"
fi
echo "Testing components: $TEST_COMPONENTS"
echo ""
# Show disk space before validation (after bind mounts setup)
echo "Disk space before config validation:"
df -h
echo ""
# Run config validation (auto-grouped by test_build_components.py)
python3 script/test_build_components.py -e config -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
echo ""
echo "Config validation passed! Starting compilation..."
echo ""
# Show disk space before compilation
echo "Disk space before compilation:"
df -h
echo ""
# Run compilation (auto-grouped by test_build_components.py)
python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
- name: Save ESPHome cache
if: github.ref == 'refs/heads/dev'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
pre-commit-ci-lite:
name: pre-commit.ci lite
runs-on: ubuntu-latest
@@ -1062,8 +1147,8 @@ jobs:
- memory-impact-pr-branch
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && fromJSON(needs.determine-jobs.outputs.memory_impact).should_run == 'true' && needs.memory-impact-target-branch.outputs.skip != 'true'
permissions:
contents: read
pull-requests: write
contents: read # actions/checkout to load the comment-posting script
pull-requests: write # ci_memory_impact_comment.py posts/updates the memory-impact comment on the PR
env:
GH_TOKEN: ${{ github.token }}
steps:
@@ -1114,6 +1199,7 @@ jobs:
- determine-jobs
- device-builder
- test-build-components-split
- test-native-idf
- pre-commit-ci-lite
- memory-impact-target-branch
- memory-impact-pr-branch
@@ -6,8 +6,8 @@ on:
types: [opened, reopened]
permissions:
pull-requests: write
issues: write
pull-requests: write # pulls.update to close the PR opened from a fork's default branch
issues: write # issues.createComment to explain to the contributor why the PR was closed
jobs:
close:
@@ -15,9 +15,9 @@ on:
- beta
permissions:
issues: write
pull-requests: read
contents: read
issues: write # issues.addLabels / removeLabel to manage the 'code-owner-approved' label on the PR
pull-requests: read # listReviews to determine whether a codeowner has approved
contents: read # actions/checkout to read CODEOWNERS and the shared codeowners.js helper
jobs:
codeowner-approved:
+14 -2
View File
@@ -17,9 +17,10 @@ on:
- release
- beta
# PR/review writes (requestReviewers, issues.createComment) are performed with the App token minted below,
# so the workflow's GITHUB_TOKEN only needs read access for checkout.
permissions:
pull-requests: write
contents: read
contents: read # actions/checkout to read CODEOWNERS and the shared codeowners.js helper
jobs:
request-codeowner-reviews:
@@ -32,9 +33,20 @@ jobs:
with:
ref: ${{ github.event.pull_request.base.sha }}
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
# Scope the minted App token to the minimum needed by the github-script step below.
permission-pull-requests: write # pulls.listFiles, pulls.get, pulls.listReviews, pulls.requestReviewers
permission-issues: write # issues.listComments and issues.createComment (PR comments use the issues API)
- name: Request reviews from component codeowners
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const { loadCodeowners, getEffectiveOwners } = require('./.github/scripts/codeowners.js');
+9 -11
View File
@@ -16,6 +16,9 @@ on:
schedule:
- cron: "30 18 * * 4"
# Deny by default; the analyze job opts in to exactly what it needs.
permissions: {}
jobs:
analyze:
name: Analyze (${{ matrix.language }})
@@ -26,15 +29,10 @@ jobs:
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
security-events: write # upload CodeQL SARIF results to the Code Scanning API
packages: read # fetch internal or private CodeQL query packs
actions: read # required by codeql-action when run from a private repo
contents: read # actions/checkout to scan the repository
strategy:
fail-fast: false
@@ -58,7 +56,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -86,6 +84,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v4.35.3
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with:
category: "/language:${{matrix.language}}"
+14 -5
View File
@@ -4,20 +4,29 @@ on:
pull_request_target:
types: [opened, synchronize]
permissions:
contents: read # Needed to fetch PR details
issues: write # Needed to create and update comments (PR comments are managed via the issues REST API)
pull-requests: write # also needed?
# All API calls (pulls.listFiles + issues.{list,create,update}Comment) are performed with
# the App token minted below, so the workflow's GITHUB_TOKEN does not need any scopes.
permissions: {}
jobs:
external-comment:
name: External component comment
runs-on: ubuntu-latest
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
# pulls.listFiles + issues.{list,create,update}Comment on PRs. For PR resources
# the issues.*Comment APIs require the pull-requests scope, not issues.
permission-pull-requests: write
- name: Add external component comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: ${{ steps.generate-token.outputs.token }}
script: |
// Generate external component usage instructions
function generateExternalComponentInstructions(prNumber, componentNames, owner, repo) {
+2 -2
View File
@@ -9,8 +9,8 @@ on:
types: [labeled]
permissions:
issues: write
contents: read
issues: write # issues.createComment to mention component codeowners on the newly labelled issue
contents: read # repos.getContent to fetch CODEOWNERS from the default branch
jobs:
notify-codeowners:
+6
View File
@@ -6,6 +6,12 @@ on:
- cron: "30 0 * * *" # Run daily at 00:30 UTC
workflow_dispatch:
# Deny by default; the lock job opts in to exactly what the reusable workflow needs.
permissions: {}
jobs:
lock:
permissions:
issues: write # issues.lock on closed issues
pull-requests: write # issues.lock on closed pull requests
uses: esphome/workflows/.github/workflows/lock.yml@025a1e6255610c498ed590403b7e510b69e474df # 2026.4.1
+2 -2
View File
@@ -8,8 +8,8 @@ on:
- beta
permissions:
contents: read
pull-requests: read
contents: read # actions/checkout to load detect-tags.js
pull-requests: read # pulls.listFiles to map changed files to component/core/dashboard/ci tags
jobs:
check:
+10 -7
View File
@@ -9,7 +9,7 @@ on:
- cron: "0 2 * * *"
permissions:
contents: read
contents: read # actions/checkout for all jobs; deploy jobs add their own scopes when they need to write
jobs:
init:
@@ -57,8 +57,8 @@ jobs:
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
contents: read # actions/checkout to build the sdist/wheel
id-token: write # OIDC token for PyPI Trusted Publishing (pypa/gh-action-pypi-publish)
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Python
@@ -78,8 +78,8 @@ jobs:
name: Build ESPHome ${{ matrix.platform.arch }}
if: github.repository == 'esphome/esphome'
permissions:
contents: read
packages: write
contents: read # actions/checkout to load Dockerfile and build context
packages: write # docker/login-action + build-push-action push image digests to ghcr.io
runs-on: ${{ matrix.platform.os }}
needs: [init]
strategy:
@@ -152,8 +152,8 @@ jobs:
- deploy-docker
if: github.repository == 'esphome/esphome'
permissions:
contents: read
packages: write
contents: read # actions/checkout to load Dockerfile and build context
packages: write # docker/login-action + build-push-action push image digests to ghcr.io
strategy:
fail-fast: false
matrix:
@@ -227,6 +227,7 @@ jobs:
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
owner: esphome
repositories: home-assistant-addon
permission-actions: write # actions.createWorkflowDispatch on the target repo (only API call made with this token)
- name: Trigger Workflow
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@@ -262,6 +263,7 @@ jobs:
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
owner: esphome
repositories: esphome-schema
permission-actions: write # actions.createWorkflowDispatch on the target repo (only API call made with this token)
- name: Trigger Workflow
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@@ -293,6 +295,7 @@ jobs:
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
owner: esphome
repositories: version-notifier
permission-actions: write # actions.createWorkflowDispatch on the target repo (only API call made with this token)
- name: Trigger Workflow
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+2 -2
View File
@@ -7,8 +7,8 @@ on:
workflow_dispatch:
permissions:
issues: write
pull-requests: write
issues: write # actions/stale labels, comments on, and closes stale issues
pull-requests: write # actions/stale labels, comments on, and closes stale pull requests
concurrency:
group: lock
@@ -4,6 +4,9 @@ on:
pull_request:
types: [opened, reopened, labeled, unlabeled, synchronize]
permissions:
pull-requests: read # issues.listLabelsOnIssue to detect blocking labels (needs-docs, merge-after-release, chained-pr)
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
+16 -1
View File
@@ -6,12 +6,27 @@ on:
schedule:
- cron: "45 6 * * *"
# Repo writes (branch push, PR open) happen via the App token minted below,
# so the workflow's GITHUB_TOKEN does not need any write scopes.
permissions:
contents: read # actions/checkout for this repo and home-assistant/core
jobs:
sync:
name: Sync Device Classes
runs-on: ubuntu-latest
if: github.repository == 'esphome/esphome'
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
# Scope the minted App token to the minimum needed by peter-evans/create-pull-request.
permission-contents: write # git.createCommit + refs.create/update to push the sync/device-classes branch
permission-pull-requests: write # pulls.create / pulls.update to open or refresh the sync PR
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -50,4 +65,4 @@ jobs:
delete-branch: true
title: "Synchronise Device Classes from Home Assistant"
body-path: .github/PULL_REQUEST_TEMPLATE.md
token: ${{ secrets.DEVICE_CLASS_SYNC_TOKEN }}
token: ${{ steps.generate-token.outputs.token }}
+1
View File
@@ -416,6 +416,7 @@ esphome/components/resampler/speaker/* @kahrendt
esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
esphome/components/ring_buffer/* @kahrendt
esphome/components/rp2040/* @jesserockz
esphome/components/rp2040_ble/* @bdraco
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
+218 -103
View File
@@ -52,18 +52,19 @@ from esphome.const import (
CONF_WEB_SERVER,
ENV_NOGITIGNORE,
KEY_CORE,
KEY_NATIVE_IDF,
KEY_TARGET_PLATFORM,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
SECRETS_FILES,
Toolchain,
)
from esphome.core import CORE, EsphomeError, coroutine
from esphome.enum import StrEnum
from esphome.helpers import get_bool_env, indent, is_ip_address
from esphome.log import AnsiFore, color, setup_log
from esphome.types import ConfigType
from esphome.upload_targets import PortType, get_port_type
from esphome.util import (
PICOTOOL_PACKAGE,
FlashImage,
@@ -155,7 +156,6 @@ class ArgsProtocol(Protocol):
configuration: str
name: str
upload_speed: str | None
native_idf: bool
def choose_prompt(options, purpose: str = None):
@@ -195,14 +195,6 @@ class Purpose(StrEnum):
LOGGING = "logging"
class PortType(StrEnum):
SERIAL = "SERIAL"
NETWORK = "NETWORK"
MQTT = "MQTT"
MQTTIP = "MQTTIP"
BOOTSEL = "BOOTSEL"
# Magic MQTT port types that require special handling
_MQTT_PORT_TYPES = frozenset({PortType.MQTT, PortType.MQTTIP})
@@ -598,27 +590,6 @@ def _resolve_network_devices(
return network_devices
def get_port_type(port: str) -> PortType:
"""Determine the type of port/device identifier.
Returns:
PortType.SERIAL for serial ports (/dev/ttyUSB0, COM1, etc.)
PortType.BOOTSEL for RP2040 BOOTSEL upload via picotool
PortType.MQTT for MQTT logging
PortType.MQTTIP for MQTT IP lookup
PortType.NETWORK for IP addresses, hostnames, or mDNS names
"""
if port == "BOOTSEL":
return PortType.BOOTSEL
if port.startswith("/") or port.startswith("COM"):
return PortType.SERIAL
if port == "MQTT":
return PortType.MQTT
if port == "MQTTIP":
return PortType.MQTTIP
return PortType.NETWORK
def run_miniterm(config: ConfigType, port: str, args) -> int:
from aioesphomeapi import LogParser
import serial
@@ -721,17 +692,14 @@ def _wrap_to_code(name, comp, yaml_util):
return wrapped
def write_cpp(config: ConfigType, native_idf: bool = False) -> int:
def write_cpp(config: ConfigType) -> int:
from esphome import writer
if not get_bool_env(ENV_NOGITIGNORE):
writer.write_gitignore()
# Store native_idf flag so esp32 component can check it
CORE.data[KEY_NATIVE_IDF] = native_idf
generate_cpp_contents(config)
return write_cpp_file(native_idf=native_idf)
return write_cpp_file()
def generate_cpp_contents(config: ConfigType) -> None:
@@ -747,13 +715,13 @@ def generate_cpp_contents(config: ConfigType) -> None:
CORE.flush_tasks()
def write_cpp_file(native_idf: bool = False) -> int:
def write_cpp_file() -> int:
from esphome import writer
code_s = indent(CORE.cpp_main_section)
writer.write_cpp(code_s)
if native_idf and CORE.is_esp32 and CORE.target_framework == "esp-idf":
if CORE.using_toolchain_esp_idf:
from esphome.build_gen import espidf
espidf.write_project()
@@ -766,30 +734,29 @@ def write_cpp_file(native_idf: bool = False) -> int:
def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
native_idf = getattr(args, "native_idf", False)
# NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py
# If you change this format, update the regex in that script as well
_LOGGER.info("Compiling app... Build path: %s", CORE.build_path)
if native_idf and CORE.is_esp32 and CORE.target_framework == "esp-idf":
from esphome import espidf_api
if CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
rc = espidf_api.run_compile(config, CORE.verbose)
rc = toolchain.run_compile(config, CORE.verbose)
if rc != 0:
return rc
# Create factory.bin and ota.bin
espidf_api.create_factory_bin()
espidf_api.create_ota_bin()
# Create factory.bin, ota.bin, and firmware.elf copy
toolchain.create_factory_bin()
toolchain.create_ota_bin()
toolchain.create_elf_copy()
else:
from esphome import platformio_api
from esphome.platformio import toolchain
rc = platformio_api.run_compile(config, CORE.verbose)
rc = toolchain.run_compile(config, CORE.verbose)
if rc != 0:
return rc
idedata = platformio_api.get_idedata(config)
idedata = toolchain.get_idedata(config)
if idedata is None:
return 1
@@ -884,10 +851,16 @@ def upload_using_esptool(
if file is not None:
flash_images = [FlashImage(path=file, offset="0x0")]
else:
from esphome import platformio_api
elif CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
idedata = platformio_api.get_idedata(config)
flash_images = [
FlashImage(path=toolchain.get_factory_firmware_path(), offset="0x0")
]
else:
from esphome.platformio import toolchain
idedata = toolchain.get_idedata(config)
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
flash_images = [
@@ -960,13 +933,13 @@ def upload_using_esptool(
def upload_using_platformio(config: ConfigType, port: str) -> int:
from esphome import platformio_api
from esphome.platformio import toolchain
# RP2040 platform-raspberrypi build recipe expects firmware.bin.signed for
# the upload target, but 'nobuild' skips the build phase that creates it.
# Create it here so the upload doesn't fail.
if CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040:
idedata = platformio_api.get_idedata(config)
idedata = toolchain.get_idedata(config)
build_dir = Path(idedata.firmware_elf_path).parent
firmware_bin = build_dir / "firmware.bin"
signed_bin = build_dir / "firmware.bin.signed"
@@ -976,15 +949,15 @@ def upload_using_platformio(config: ConfigType, port: str) -> int:
upload_args = ["-t", "upload", "-t", "nobuild"]
if port is not None:
upload_args += ["--upload-port", port]
return platformio_api.run_platformio_cli_run(config, CORE.verbose, *upload_args)
return toolchain.run_platformio_cli_run(config, CORE.verbose, *upload_args)
def _find_picotool() -> Path | None:
"""Find the picotool binary from PlatformIO packages."""
from esphome import platformio_api
from esphome.platformio import toolchain
try:
idedata = platformio_api.get_idedata(CORE.config)
idedata = toolchain.get_idedata(CORE.config)
except Exception: # noqa: BLE001 # pylint: disable=broad-except
return None
return get_picotool_path(idedata.cc_path)
@@ -997,9 +970,9 @@ def upload_using_picotool(config: ConfigType) -> int:
the mass storage copy approach that causes "disk not ejected properly"
warnings on macOS.
"""
from esphome import platformio_api
from esphome.platformio import toolchain
idedata = platformio_api.get_idedata(config)
idedata = toolchain.get_idedata(config)
firmware_elf = Path(idedata.firmware_elf_path)
if not firmware_elf.is_file():
@@ -1126,11 +1099,18 @@ def upload_program(
# MQTT and MQTTIP are also OTA paths; MQTTIP gets resolved to a real IP later by
# _resolve_network_devices(). Only SERIAL and BOOTSEL are non-OTA upload paths.
if port_type in (PortType.SERIAL, PortType.BOOTSEL) and getattr(
args, "partition_table", False
is_partition_table = getattr(args, "partition_table", False)
is_bootloader = getattr(args, "bootloader", False)
if is_partition_table and is_bootloader:
raise EsphomeError(
"The options --partition-table and --bootloader can't be used together."
)
option_string = "--partition-table" if is_partition_table else "--bootloader"
if port_type in (PortType.SERIAL, PortType.BOOTSEL) and (
is_partition_table or is_bootloader
):
raise EsphomeError(
"The option --partition-table can only be used for Over The Air updates."
f"The option {option_string} can only be used for Over The Air updates."
)
if port_type == PortType.BOOTSEL:
@@ -1159,9 +1139,9 @@ def upload_program(
network_devices = _resolve_network_devices(devices, config, args)
if chosen_platform == CONF_WEB_SERVER:
if getattr(args, "partition_table", False):
if is_partition_table or is_bootloader:
raise EsphomeError(
"--partition-table is only supported with the esphome OTA platform; "
f"{option_string} is only supported with the esphome OTA platform; "
"the web_server OTA path can only update the firmware image."
)
binary = CORE.firmware_bin
@@ -1229,25 +1209,34 @@ def _upload_via_native_api(
remote_port = int(ota_conf[CONF_PORT])
password = ota_conf.get(CONF_PASSWORD)
def check_partition_access(option_string: str) -> None:
if not ota_conf.get("allow_partition_access"):
raise EsphomeError(
f"The option {option_string} requires 'allow_partition_access: true' on the "
"esphome OTA platform in the device's YAML configuration. Add it, recompile, "
f"flash a build with the option enabled, and then retry {option_string}."
)
binary = CORE.firmware_bin
ota_type = espota2.OTA_TYPE_UPDATE_APP
if getattr(args, "partition_table", False):
# Fail fast if the resolved ESPHome OTA config does not enable allow_partition_access.
# The device-side handshake also rejects this with "Device only supports app updates",
# but checking here surfaces the misconfiguration before opening a network connection.
if not ota_conf.get("allow_partition_access"):
raise EsphomeError(
"The option --partition-table requires 'allow_partition_access: true' on the "
"esphome OTA platform in the device's YAML configuration. Add it, recompile, "
"flash a build with the option enabled, and then retry --partition-table."
)
check_partition_access("--partition-table")
binary = CORE.partition_table_bin
ota_type = espota2.OTA_TYPE_UPDATE_PARTITION_TABLE
elif getattr(args, "bootloader", False):
check_partition_access("--bootloader")
binary = CORE.bootloader_bin
ota_type = espota2.OTA_TYPE_UPDATE_BOOTLOADER
if getattr(args, "file", None) is not None:
binary = Path(args.file)
if ota_type == espota2.OTA_TYPE_UPDATE_PARTITION_TABLE:
_validate_partition_table_binary(binary)
if ota_type == espota2.OTA_TYPE_UPDATE_BOOTLOADER:
_validate_bootloader_binary(binary)
return espota2.run_ota(network_devices, remote_port, password, binary, ota_type)
@@ -1282,6 +1271,7 @@ def _upload_via_web_server(
_PARTITION_TABLE_MAX_LEN = 0xC00
_ESP_PARTITION_MAGIC = 0x50AA
_ESP_PARTITION_MAGIC_MD5 = 0xEBEB
_ESP_IMAGE_HEADER_MAGIC = 0xE9
def _validate_partition_table_binary(binary: Path) -> None:
@@ -1327,6 +1317,28 @@ def _validate_partition_table_binary(binary: Path) -> None:
)
def _validate_bootloader_binary(binary: Path) -> None:
"""Validate that ``binary`` looks like an ESP32 bootloader image."""
try:
data = binary.read_bytes()
except OSError as err:
raise EsphomeError(f"Cannot read bootloader file '{binary}': {err}") from err
if not data:
raise EsphomeError(
f"Bootloader file '{binary}' is empty. "
"This file does not look like an ESP32 bootloader."
)
first_magic = data[0]
if first_magic != _ESP_IMAGE_HEADER_MAGIC:
raise EsphomeError(
f"Bootloader file '{binary}' does not start with the expected "
f"image header magic 0x{_ESP_IMAGE_HEADER_MAGIC:02X} (got 0x{first_magic:02X}). "
"This file does not look like an ESP32 bootloader."
)
def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int | None:
try:
module = importlib.import_module("esphome.components." + CORE.target_platform)
@@ -1409,8 +1421,7 @@ def command_vscode(args: ArgsProtocol) -> int | None:
def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
native_idf = getattr(args, "native_idf", False)
exit_code = write_cpp(config, native_idf=native_idf)
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
if args.only_generate:
@@ -1420,9 +1431,14 @@ def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
if exit_code != 0:
return exit_code
if CORE.is_host:
from esphome.platformio_api import get_idedata
if CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
program_path = str(get_idedata(config).firmware_elf_path)
program_path = str(toolchain.get_elf_path())
else:
from esphome.platformio.toolchain import get_idedata
program_path = str(get_idedata(config).firmware_elf_path)
_LOGGER.info("Successfully compiled program to path '%s'", program_path)
else:
_LOGGER.info("Successfully compiled program.")
@@ -1465,8 +1481,7 @@ def command_logs(args: ArgsProtocol, config: ConfigType) -> int | None:
def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
native_idf = getattr(args, "native_idf", False)
exit_code = write_cpp(config, native_idf=native_idf)
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
exit_code = compile_program(args, config)
@@ -1474,9 +1489,14 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
return exit_code
_LOGGER.info("Successfully compiled program.")
if CORE.is_host:
from esphome.platformio_api import get_idedata
if CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
program_path = str(get_idedata(config).firmware_elf_path)
program_path = str(toolchain.get_elf_path())
else:
from esphome.platformio.toolchain import get_idedata
program_path = str(get_idedata(config).firmware_elf_path)
_LOGGER.info("Running program from path '%s'", program_path)
return run_external_process(program_path)
@@ -1667,12 +1687,19 @@ def command_update_all(args: ArgsProtocol) -> int | None:
def command_idedata(args: ArgsProtocol, config: ConfigType) -> int:
import json
from esphome import platformio_api
if not CORE.using_toolchain_platformio:
_LOGGER.error(
"The idedata command is not compatible with %s toolchain",
CORE.toolchain.value,
)
return 1
from esphome.platformio import toolchain
logging.disable(logging.INFO)
logging.disable(logging.WARNING)
idedata = platformio_api.get_idedata(config)
idedata = toolchain.get_idedata(config)
if idedata is None:
return 1
@@ -1686,7 +1713,6 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
This command compiles the configuration and performs memory analysis.
Compilation is fast if sources haven't changed (just relinking).
"""
from esphome import platformio_api
from esphome.analyze_memory.cli import MemoryAnalyzerCLI
from esphome.analyze_memory.ram_strings import RamStringsAnalyzer
@@ -1700,12 +1726,25 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
_LOGGER.info("Successfully compiled program.")
# Get idedata for analysis
idedata = platformio_api.get_idedata(config)
if idedata is None:
_LOGGER.error("Failed to get IDE data for memory analysis")
return 1
idedata = None
if CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
firmware_elf = Path(idedata.firmware_elf_path)
objdump_path = str(toolchain.get_objdump_path())
readelf_path = str(toolchain.get_readelf_path())
firmware_elf = toolchain.get_elf_path()
else:
from esphome.platformio import toolchain
idedata = toolchain.get_idedata(config)
if idedata is None:
_LOGGER.error("Failed to get IDE data for memory analysis")
return 1
objdump_path = idedata.objdump_path
readelf_path = idedata.readelf_path
firmware_elf = Path(idedata.firmware_elf_path)
# Extract external components from config
external_components = detect_external_components(config)
@@ -1715,8 +1754,8 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
_LOGGER.info("Analyzing memory usage...")
analyzer = MemoryAnalyzerCLI(
str(firmware_elf),
idedata.objdump_path,
idedata.readelf_path,
objdump_path,
readelf_path,
external_components,
idedata=idedata,
)
@@ -1732,7 +1771,7 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
try:
ram_analyzer = RamStringsAnalyzer(
str(firmware_elf),
objdump_path=idedata.objdump_path,
objdump_path=objdump_path,
platform=CORE.target_platform,
)
ram_analyzer.analyze()
@@ -1775,11 +1814,32 @@ def command_rename(args: ArgsProtocol, config: ConfigType) -> int | None:
old_name = yaml[CONF_ESPHOME][CONF_NAME]
match = re.match(r"^\$\{?([a-zA-Z0-9_]+)\}?$", old_name)
if match is None:
new_raw = re.sub(
rf"name:\s+[\"']?{old_name}[\"']?",
f'name: "{new_name}"',
raw_contents,
# Only swap the ``name:`` line that sits directly under the
# top-level ``esphome:`` block. A naked ``re.sub`` would
# also clobber any other ``name:`` line whose value happens
# to match (e.g. a sensor / output / wifi entry sharing the
# device's hostname), silently rewriting unrelated user
# configuration. The pattern anchors:
# - at the start of the line so ``friendly_name:``,
# ``device_name:`` etc. don't match the trailing ``name:``
# substring; and
# - at the end of the value (lookahead for whitespace +
# comment + EOL) so ``old_name`` doesn't match as a
# prefix of a longer value (``kitchen`` vs ``kitchen2``).
name_pattern = re.compile(
rf"^(\s*)name:\s+[\"']?{re.escape(old_name)}[\"']?(?=\s*(?:#|$))"
)
out_lines: list[str] = []
in_esphome_block = False
for line in raw_contents.splitlines(keepends=True):
if line and not line[0].isspace() and line.strip():
in_esphome_block = line.lstrip().startswith("esphome:")
out_lines.append(line)
continue
if in_esphome_block:
line = name_pattern.sub(rf'\1name: "{new_name}"', line, count=1)
out_lines.append(line)
new_raw = "".join(out_lines)
else:
old_name = yaml[CONF_SUBSTITUTIONS][match.group(1)]
if (
@@ -1802,7 +1862,40 @@ def command_rename(args: ArgsProtocol, config: ConfigType) -> int | None:
flags=re.MULTILINE,
)
# ``new_name == old_name`` (after substitution resolution) is
# a no-op rewrite that would still queue a pointless re-flash.
# Catch it before the path-equality check below — covers the
# case where the config filename doesn't match the device name
# (e.g. ``weird-file.yaml`` whose ``esphome.name`` is
# ``kitchen``; running ``esphome rename weird-file.yaml kitchen``
# would otherwise just re-flash the same hostname).
if new_name == old_name:
print(
color(
AnsiFore.BOLD_RED,
f"'{new_name}' is already the device's name.",
)
)
return 1
new_path: Path = CORE.config_dir / (new_name + ".yaml")
if new_path.resolve() == CORE.config_path.resolve():
print(
color(
AnsiFore.BOLD_RED,
f"'{new_name}' is already the device's name.",
)
)
return 1
if new_path.exists():
print(
color(
AnsiFore.BOLD_RED,
f"Cannot rename: {new_path} already exists. "
"Refusing to overwrite an existing configuration.",
)
)
return 1
print(
f"Updating {color(AnsiFore.CYAN, str(CORE.config_path))} to {color(AnsiFore.CYAN, str(new_path))}"
)
@@ -1923,6 +2016,17 @@ def parse_args(argv):
action="store_true",
default=False,
)
options_parser.add_argument(
"--toolchain",
type=Toolchain,
default=None,
choices=list(Toolchain),
metavar="{" + ",".join(t.value for t in Toolchain) + "}",
help=(
"Select toolchain for compiling. Overrides '<platform>.toolchain' in YAML. "
f"Default: {Toolchain.PLATFORMIO.value}."
),
)
parser = argparse.ArgumentParser(
description=f"ESPHome {const.__version__}", parents=[options_parser]
@@ -1967,11 +2071,6 @@ def parse_args(argv):
help="Only generate source code, do not compile.",
action="store_true",
)
parser_compile.add_argument(
"--native-idf",
help="Build with native ESP-IDF instead of PlatformIO (ESP32 esp-idf framework only).",
action="store_true",
)
parser_upload = subparsers.add_parser(
"upload",
@@ -2010,6 +2109,11 @@ def parse_args(argv):
help="Upload as partition table (OTA).",
action="store_true",
)
parser_upload.add_argument(
"--bootloader",
help="Upload as bootloader (OTA).",
action="store_true",
)
parser_logs = subparsers.add_parser(
"logs",
@@ -2067,6 +2171,13 @@ def parse_args(argv):
parser_run.add_argument(
"--no-logs", help="Disable starting logs.", action="store_true"
)
parser_run.add_argument(
"--no-states",
action="store_true",
help="Do not show entity state changes in log output.",
)
parser_run.add_argument(
"--reset",
"-r",
@@ -2074,11 +2185,6 @@ def parse_args(argv):
help="Reset the device before starting serial logs.",
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
)
parser_run.add_argument(
"--native-idf",
help="Build with native ESP-IDF instead of PlatformIO (ESP32 esp-idf framework only).",
action="store_true",
)
parser_run.add_argument(
"--ota-platform",
choices=[CONF_ESPHOME, CONF_WEB_SERVER],
@@ -2301,6 +2407,9 @@ def run_esphome(argv):
CORE.config_path = conf_path
CORE.dashboard = args.dashboard
if args.toolchain is not None:
# CLI toolchain wins over esp32.toolchain in YAML.
CORE.toolchain = args.toolchain
# Commands that don't need fresh external components: logs just connects
# to the device, and clean is about to delete the build directory.
@@ -2313,6 +2422,12 @@ def run_esphome(argv):
return 2
CORE.config = config
# Fallback for platforms whose validators didn't set the toolchain
# (only the esp32 component reads esp32.framework.toolchain). All
# other platforms only support PlatformIO today.
if CORE.toolchain is None:
CORE.toolchain = Toolchain.PLATFORMIO
if args.command not in POST_CONFIG_ACTIONS:
safe_print(f"Unknown command {args.command}")
return 1
+1 -1
View File
@@ -24,7 +24,7 @@ from .helpers import (
from .toolchain import find_tool, resolve_tool_path, run_tool
if TYPE_CHECKING:
from esphome.platformio_api import IDEData
from esphome.platformio.toolchain import IDEData
_LOGGER = logging.getLogger(__name__)
+1 -1
View File
@@ -739,7 +739,7 @@ def main():
# Load build directory
from pathlib import Path
from esphome.platformio_api import IDEData
from esphome.platformio.toolchain import IDEData
build_path = Path(build_dir)
+23 -1
View File
@@ -6,6 +6,7 @@ from pathlib import Path
from esphome.components.esp32 import get_esp32_variant
from esphome.core import CORE
from esphome.helpers import mkdir_p, write_file_if_changed
from esphome.writer import update_storage_json
def get_available_components() -> list[str] | None:
@@ -54,7 +55,7 @@ def get_project_cmakelists() -> str:
idf_target = variant.lower().replace("-", "")
# Extract compile definitions from build flags (-DXXX -> XXX)
compile_defs = [flag for flag in CORE.build_flags if flag.startswith("-D")]
compile_defs = [flag for flag in sorted(CORE.build_flags) if flag.startswith("-D")]
extra_compile_options = "\n".join(
f'idf_build_set_property(COMPILE_OPTIONS "{compile_def}" APPEND)'
for compile_def in compile_defs
@@ -64,6 +65,22 @@ def get_project_cmakelists() -> str:
# Auto-generated by ESPHome
cmake_minimum_required(VERSION 3.16)
# On Windows, Ninja can fail with:
# "CreateProcess: The parameter is incorrect (is the command line too long?)"
# when compiler/linker command lines exceed the OS length limit.
#
# The following settings force CMake/Ninja to use *response files* (@file.rsp)
# to pass long lists of includes, objects, and other arguments indirectly,
# avoiding command-line length limits and fixing the build failure.
#
# This is especially useful for large ESP-IDF / ESPHome projects with many
# source files or include directories.
set(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1)
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1)
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1)
set(IDF_TARGET {idf_target})
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
@@ -124,6 +141,11 @@ target_link_options(${{COMPONENT_LIB}} PUBLIC
def write_project(minimal: bool = False) -> None:
"""Write ESP-IDF project files."""
# Refresh <data_dir>/storage/<name>.yaml.json so the dashboard's
# /info and /downloads endpoints can locate the build (they 404
# otherwise). This mirrors the PlatformIO build-gen path's call
# in build_gen/platformio.py:write_ini().
update_storage_json()
mkdir_p(CORE.build_path)
mkdir_p(CORE.relative_src_path())
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace a01nyub {
namespace esphome::a01nyub {
static const char *const TAG = "a01nyub.sensor";
@@ -42,5 +41,4 @@ void A01nyubComponent::check_buffer_() {
void A01nyubComponent::dump_config() { LOG_SENSOR("", "A01nyub Sensor", this); }
} // namespace a01nyub
} // namespace esphome
} // namespace esphome::a01nyub
+2 -4
View File
@@ -6,8 +6,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace a01nyub {
namespace esphome::a01nyub {
class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
@@ -23,5 +22,4 @@ class A01nyubComponent : public sensor::Sensor, public Component, public uart::U
std::vector<uint8_t> buffer_;
};
} // namespace a01nyub
} // namespace esphome
} // namespace esphome::a01nyub
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace a02yyuw {
namespace esphome::a02yyuw {
static const char *const TAG = "a02yyuw.sensor";
@@ -41,5 +40,4 @@ void A02yyuwComponent::check_buffer_() {
void A02yyuwComponent::dump_config() { LOG_SENSOR("", "A02yyuw Sensor", this); }
} // namespace a02yyuw
} // namespace esphome
} // namespace esphome::a02yyuw
+2 -4
View File
@@ -6,8 +6,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace a02yyuw {
namespace esphome::a02yyuw {
class A02yyuwComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
@@ -23,5 +22,4 @@ class A02yyuwComponent : public sensor::Sensor, public Component, public uart::U
std::vector<uint8_t> buffer_;
};
} // namespace a02yyuw
} // namespace esphome
} // namespace esphome::a02yyuw
+2 -4
View File
@@ -1,8 +1,7 @@
#include "a4988.h"
#include "esphome/core/log.h"
namespace esphome {
namespace a4988 {
namespace esphome::a4988 {
static const char *const TAG = "a4988.stepper";
@@ -51,5 +50,4 @@ void A4988::loop() {
this->step_pin_->digital_write(false);
}
} // namespace a4988
} // namespace esphome
} // namespace esphome::a4988
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/hal.h"
#include "esphome/components/stepper/stepper.h"
namespace esphome {
namespace a4988 {
namespace esphome::a4988 {
class A4988 : public stepper::Stepper, public Component {
public:
@@ -25,5 +24,4 @@ class A4988 : public stepper::Stepper, public Component {
HighFrequencyLoopRequester high_freq_;
};
} // namespace a4988
} // namespace esphome
} // namespace esphome::a4988
@@ -1,8 +1,7 @@
#include "adalight_light_effect.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adalight {
namespace esphome::adalight {
static const char *const TAG = "adalight_light_effect";
@@ -138,5 +137,4 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL
return CONSUMED;
}
} // namespace adalight
} // namespace esphome
} // namespace esphome::adalight
@@ -6,8 +6,7 @@
#include <vector>
namespace esphome {
namespace adalight {
namespace esphome::adalight {
class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice {
public:
@@ -35,5 +34,4 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
std::vector<uint8_t> frame_;
};
} // namespace adalight
} // namespace esphome
} // namespace esphome::adalight
+2 -4
View File
@@ -17,8 +17,7 @@
#include <zephyr/drivers/adc.h>
#endif
namespace esphome {
namespace adc {
namespace esphome::adc {
#ifdef USE_ESP32
// clang-format off
@@ -162,5 +161,4 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#endif
};
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
+2 -4
View File
@@ -1,8 +1,7 @@
#include "adc_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.common";
@@ -79,5 +78,4 @@ void ADCSensor::set_sample_count(uint8_t sample_count) {
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.esp32";
@@ -364,7 +363,6 @@ float ADCSensor::sample_autorange_() {
return final_result;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif // USE_ESP32
@@ -11,8 +11,7 @@ ADC_MODE(ADC_VCC)
#include <Arduino.h>
#endif // USE_ADC_SENSOR_VCC
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.esp8266";
@@ -55,7 +54,6 @@ float ADCSensor::sample() {
return aggr.aggregate() / 1024.0f;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif // USE_ESP8266
@@ -3,8 +3,7 @@
#include "adc_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.libretiny";
@@ -48,7 +47,6 @@ float ADCSensor::sample() {
return aggr.aggregate() / 1000.0f;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif // USE_LIBRETINY
+2 -4
View File
@@ -15,8 +15,7 @@
#define PICO_VSYS_PIN 29 // NOLINT(cppcoreguidelines-macro-usage)
#endif
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.rp2040";
@@ -98,7 +97,6 @@ float ADCSensor::sample() {
return aggr.aggregate() * 3.3f / 4096.0f * coeff;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif // USE_RP2040
+2 -4
View File
@@ -5,8 +5,7 @@
#include "hal/nrf_saadc.h"
namespace esphome {
namespace adc {
namespace esphome::adc {
static const char *const TAG = "adc.zephyr";
@@ -202,6 +201,5 @@ float ADCSensor::sample() {
return val_mv / 1000.0f;
}
} // namespace adc
} // namespace esphome
} // namespace esphome::adc
#endif
+2 -4
View File
@@ -1,8 +1,7 @@
#include "adc128s102.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc128s102 {
namespace esphome::adc128s102 {
static const char *const TAG = "adc128s102";
@@ -28,5 +27,4 @@ uint16_t ADC128S102::read_data(uint8_t channel) {
return digital_value;
}
} // namespace adc128s102
} // namespace esphome
} // namespace esphome::adc128s102
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/hal.h"
#include "esphome/components/spi/spi.h"
namespace esphome {
namespace adc128s102 {
namespace esphome::adc128s102 {
class ADC128S102 : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
@@ -19,5 +18,4 @@ class ADC128S102 : public Component,
uint16_t read_data(uint8_t channel);
};
} // namespace adc128s102
} // namespace esphome
} // namespace esphome::adc128s102
@@ -2,8 +2,7 @@
#include "esphome/core/log.h"
namespace esphome {
namespace adc128s102 {
namespace esphome::adc128s102 {
static const char *const TAG = "adc128s102.sensor";
@@ -18,5 +17,4 @@ void ADC128S102Sensor::dump_config() {
float ADC128S102Sensor::sample() { return this->parent_->read_data(this->channel_); }
void ADC128S102Sensor::update() { this->publish_state(this->sample()); }
} // namespace adc128s102
} // namespace esphome
} // namespace esphome::adc128s102
@@ -7,8 +7,7 @@
#include "../adc128s102.h"
namespace esphome {
namespace adc128s102 {
namespace esphome::adc128s102 {
class ADC128S102Sensor : public PollingComponent,
public Parented<ADC128S102>,
@@ -24,5 +23,4 @@ class ADC128S102Sensor : public PollingComponent,
protected:
uint8_t channel_;
};
} // namespace adc128s102
} // namespace esphome
} // namespace esphome::adc128s102
@@ -1,8 +1,7 @@
#include "addressable_light_display.h"
#include "esphome/core/log.h"
namespace esphome {
namespace addressable_light {
namespace esphome::addressable_light {
static const char *const TAG = "addressable_light.display";
@@ -66,5 +65,4 @@ void HOT AddressableLightDisplay::draw_absolute_pixel_internal(int x, int y, Col
this->addressable_light_buffer_[y * this->get_width_internal() + x] = color;
}
}
} // namespace addressable_light
} // namespace esphome
} // namespace esphome::addressable_light
@@ -7,8 +7,7 @@
#include <vector>
namespace esphome {
namespace addressable_light {
namespace esphome::addressable_light {
class AddressableLightDisplay : public display::DisplayBuffer {
public:
@@ -61,5 +60,4 @@ class AddressableLightDisplay : public display::DisplayBuffer {
optional<uint32_t> last_effect_index_;
optional<std::function<int(int, int)>> pixel_mapper_f_;
};
} // namespace addressable_light
} // namespace esphome
} // namespace esphome::addressable_light
+2 -4
View File
@@ -13,8 +13,7 @@
#include <cinttypes>
namespace esphome {
namespace ade7880 {
namespace esphome::ade7880 {
static const char *const TAG = "ade7880";
@@ -313,5 +312,4 @@ void ADE7880::reset_device_() {
this->store_.reset_pending = true;
}
} // namespace ade7880
} // namespace esphome
} // namespace esphome::ade7880
+2 -4
View File
@@ -16,8 +16,7 @@
#include "ade7880_registers.h"
namespace esphome {
namespace ade7880 {
namespace esphome::ade7880 {
struct NeutralChannel {
void set_current(sensor::Sensor *sens) { this->current = sens; }
@@ -125,5 +124,4 @@ class ADE7880 : public i2c::I2CDevice, public PollingComponent {
void write_u32_register16_(uint16_t a_register, uint32_t value);
};
} // namespace ade7880
} // namespace esphome
} // namespace esphome::ade7880
+2 -4
View File
@@ -9,8 +9,7 @@
#include "ade7880.h"
namespace esphome {
namespace ade7880 {
namespace esphome::ade7880 {
// adapted from https://stackoverflow.com/a/55912127/1886371
template<size_t Bits, typename T> inline T sign_extend(const T &v) noexcept {
@@ -97,5 +96,4 @@ void ADE7880::write_u32_register16_(uint16_t a_register, uint32_t value) {
this->write_register16(a_register, reinterpret_cast<uint8_t *>(&out), sizeof(out));
}
} // namespace ade7880
} // namespace esphome
} // namespace esphome::ade7880
@@ -4,8 +4,7 @@
// Source: https://www.analog.com/media/en/technical-documentation/application-notes/AN-1127.pdf
namespace esphome {
namespace ade7880 {
namespace esphome::ade7880 {
// DSP Data Memory RAM registers
constexpr uint16_t AIGAIN = 0x4380;
@@ -242,5 +241,4 @@ constexpr uint8_t DSPWP_SET_RO = (1 << 7);
// DSPWP_SEL Register Bits
constexpr uint8_t DSPWP_SEL_SET = 0xAD;
} // namespace ade7880
} // namespace esphome
} // namespace esphome::ade7880
@@ -3,8 +3,7 @@
#include <cinttypes>
namespace esphome {
namespace ade7953_base {
namespace esphome::ade7953_base {
static const char *const TAG = "ade7953";
@@ -160,5 +159,4 @@ void ADE7953::update() {
ADE_PUBLISH(frequency, 223750.0f, 1 + val_16);
}
} // namespace ade7953_base
} // namespace esphome
} // namespace esphome::ade7953_base
@@ -6,8 +6,7 @@
#include <vector>
namespace esphome {
namespace ade7953_base {
namespace esphome::ade7953_base {
static constexpr uint8_t PGA_V_8 =
0x007; // PGA_V, (R/W) Default: 0x00, Unsigned, Voltage channel gain configuration (Bits[2:0])
@@ -131,5 +130,4 @@ class ADE7953 : public PollingComponent, public sensor::Sensor {
virtual bool ade_read_32(uint16_t reg, uint32_t *value) = 0;
};
} // namespace ade7953_base
} // namespace esphome
} // namespace esphome::ade7953_base
@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ade7953_i2c {
namespace esphome::ade7953_i2c {
static const char *const TAG = "ade7953";
@@ -76,5 +75,4 @@ bool AdE7953I2c::ade_read_32(uint16_t reg, uint32_t *value) {
return false;
}
} // namespace ade7953_i2c
} // namespace esphome
} // namespace esphome::ade7953_i2c
+2 -4
View File
@@ -8,8 +8,7 @@
#include <vector>
namespace esphome {
namespace ade7953_i2c {
namespace esphome::ade7953_i2c {
class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
public:
@@ -24,5 +23,4 @@ class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
bool ade_read_32(uint16_t reg, uint32_t *value) override;
};
} // namespace ade7953_i2c
} // namespace esphome
} // namespace esphome::ade7953_i2c
@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ade7953_spi {
namespace esphome::ade7953_spi {
static const char *const TAG = "ade7953";
@@ -83,5 +82,4 @@ bool AdE7953Spi::ade_read_32(uint16_t reg, uint32_t *value) {
return false;
}
} // namespace ade7953_spi
} // namespace esphome
} // namespace esphome::ade7953_spi
+2 -4
View File
@@ -8,8 +8,7 @@
#include <vector>
namespace esphome {
namespace ade7953_spi {
namespace esphome::ade7953_spi {
class AdE7953Spi : public ade7953_base::ADE7953,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
@@ -28,5 +27,4 @@ class AdE7953Spi : public ade7953_base::ADE7953,
bool ade_read_32(uint16_t reg, uint32_t *value) override;
};
} // namespace ade7953_spi
} // namespace esphome
} // namespace esphome::ade7953_spi
+2 -4
View File
@@ -2,8 +2,7 @@
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ads1115 {
namespace esphome::ads1115 {
static const char *const TAG = "ads1115";
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
@@ -208,5 +207,4 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
return millivolts / 1e3f;
}
} // namespace ads1115
} // namespace esphome
} // namespace esphome::ads1115
+2 -4
View File
@@ -5,8 +5,7 @@
#include <vector>
namespace esphome {
namespace ads1115 {
namespace esphome::ads1115 {
enum ADS1115Multiplexer {
ADS1115_MULTIPLEXER_P0_N1 = 0b000,
@@ -60,5 +59,4 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
bool continuous_mode_;
};
} // namespace ads1115
} // namespace esphome
} // namespace esphome::ads1115
@@ -2,8 +2,7 @@
#include "esphome/core/log.h"
namespace esphome {
namespace ads1115 {
namespace esphome::ads1115 {
static const char *const TAG = "ads1115.sensor";
@@ -29,5 +28,4 @@ void ADS1115Sensor::dump_config() {
this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
}
} // namespace ads1115
} // namespace esphome
} // namespace esphome::ads1115
@@ -8,8 +8,7 @@
#include "../ads1115.h"
namespace esphome {
namespace ads1115 {
namespace esphome::ads1115 {
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor : public sensor::Sensor,
@@ -33,5 +32,4 @@ class ADS1115Sensor : public sensor::Sensor,
ADS1115Samplerate samplerate_;
};
} // namespace ads1115
} // namespace esphome
} // namespace esphome::ads1115
+2 -4
View File
@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ads1118 {
namespace esphome::ads1118 {
static const char *const TAG = "ads1118";
static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111;
@@ -122,5 +121,4 @@ float ADS1118::request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain g
}
}
} // namespace ads1118
} // namespace esphome
} // namespace esphome::ads1118
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace ads1118 {
namespace esphome::ads1118 {
enum ADS1118Multiplexer {
ADS1118_MULTIPLEXER_P0_N1 = 0b000,
@@ -41,5 +40,4 @@ class ADS1118 : public Component,
uint16_t config_{0};
};
} // namespace ads1118
} // namespace esphome
} // namespace esphome::ads1118
@@ -2,8 +2,7 @@
#include "esphome/core/log.h"
namespace esphome {
namespace ads1118 {
namespace esphome::ads1118 {
static const char *const TAG = "ads1118.sensor";
@@ -27,5 +26,4 @@ void ADS1118Sensor::update() {
}
}
} // namespace ads1118
} // namespace esphome
} // namespace esphome::ads1118
@@ -8,8 +8,7 @@
#include "../ads1118.h"
namespace esphome {
namespace ads1118 {
namespace esphome::ads1118 {
class ADS1118Sensor : public PollingComponent,
public sensor::Sensor,
@@ -32,5 +31,4 @@ class ADS1118Sensor : public PollingComponent,
bool temperature_mode_;
};
} // namespace ads1118
} // namespace esphome
} // namespace esphome::ads1118
+2 -4
View File
@@ -3,8 +3,7 @@
#include <cinttypes>
namespace esphome {
namespace ags10 {
namespace esphome::ags10 {
static const char *const TAG = "ags10";
// Data acquisition.
@@ -192,5 +191,4 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
return data;
}
} // namespace ags10
} // namespace esphome
} // namespace esphome::ags10
+2 -4
View File
@@ -5,8 +5,7 @@
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
namespace esphome {
namespace ags10 {
namespace esphome::ags10 {
class AGS10Component : public PollingComponent, public i2c::I2CDevice {
public:
@@ -136,5 +135,4 @@ template<typename... Ts> class AGS10SetZeroPointAction : public Action<Ts...>, p
}
}
};
} // namespace ags10
} // namespace esphome
} // namespace esphome::ags10
+2 -4
View File
@@ -17,8 +17,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace aht10 {
namespace esphome::aht10 {
static const char *const TAG = "aht10";
static const uint8_t AHT10_INITIALIZE_CMD[] = {0xE1, 0x08, 0x00};
@@ -160,5 +159,4 @@ void AHT10Component::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
} // namespace aht10
} // namespace esphome
} // namespace esphome::aht10
+2 -4
View File
@@ -6,8 +6,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace aht10 {
namespace esphome::aht10 {
enum AHT10Variant { AHT10, AHT20 };
@@ -31,5 +30,4 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
uint32_t start_time_{};
};
} // namespace aht10
} // namespace esphome
} // namespace esphome::aht10
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace aic3204 {
namespace esphome::aic3204 {
static const char *const TAG = "aic3204";
@@ -167,5 +166,4 @@ bool AIC3204::write_volume_() {
return true;
}
} // namespace aic3204
} // namespace esphome
} // namespace esphome::aic3204
+2 -4
View File
@@ -6,8 +6,7 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace aic3204 {
namespace esphome::aic3204 {
// TLV320AIC3204 Register Addresses
// Page 0
@@ -83,5 +82,4 @@ class AIC3204 : public audio_dac::AudioDac, public Component, public i2c::I2CDev
float volume_{0};
};
} // namespace aic3204
} // namespace esphome
} // namespace esphome::aic3204
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/core/component.h"
#include "aic3204.h"
namespace esphome {
namespace aic3204 {
namespace esphome::aic3204 {
template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
public:
@@ -19,5 +18,4 @@ template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
AIC3204 *aic3204_;
};
} // namespace aic3204
} // namespace esphome
} // namespace esphome::aic3204
@@ -4,8 +4,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace airthings_ble {
namespace esphome::airthings_ble {
static const char *const TAG = "airthings_ble";
@@ -29,7 +28,6 @@ bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &devic
return false;
}
} // namespace airthings_ble
} // namespace esphome
} // namespace esphome::airthings_ble
#endif
@@ -5,15 +5,13 @@
#include "esphome/core/component.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
namespace esphome {
namespace airthings_ble {
namespace esphome::airthings_ble {
class AirthingsListener : public esp32_ble_tracker::ESPBTDeviceListener {
public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
};
} // namespace airthings_ble
} // namespace esphome
} // namespace esphome::airthings_ble
#endif
@@ -6,8 +6,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_base {
namespace esphome::airthings_wave_base {
static const char *const TAG = "airthings_wave_base";
@@ -211,7 +210,6 @@ void AirthingsWaveBase::set_response_timeout_() {
});
}
} // namespace airthings_wave_base
} // namespace esphome
} // namespace esphome::airthings_wave_base
#endif // USE_ESP32
@@ -14,8 +14,7 @@
#include "esphome/core/component.h"
#include "esphome/core/log.h"
namespace esphome {
namespace airthings_wave_base {
namespace esphome::airthings_wave_base {
namespace espbt = esphome::esp32_ble_tracker;
@@ -84,7 +83,6 @@ class AirthingsWaveBase : public PollingComponent, public ble_client::BLEClientN
};
};
} // namespace airthings_wave_base
} // namespace esphome
} // namespace esphome::airthings_wave_base
#endif // USE_ESP32
@@ -2,8 +2,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_mini {
namespace esphome::airthings_wave_mini {
static const char *const TAG = "airthings_wave_mini";
@@ -49,7 +48,6 @@ AirthingsWaveMini::AirthingsWaveMini() {
espbt::ESPBTUUID::from_raw(ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID);
}
} // namespace airthings_wave_mini
} // namespace esphome
} // namespace esphome::airthings_wave_mini
#endif // USE_ESP32
@@ -4,8 +4,7 @@
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
namespace esphome {
namespace airthings_wave_mini {
namespace esphome::airthings_wave_mini {
namespace espbt = esphome::esp32_ble_tracker;
@@ -34,7 +33,6 @@ class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase {
};
};
} // namespace airthings_wave_mini
} // namespace esphome
} // namespace esphome::airthings_wave_mini
#endif // USE_ESP32
@@ -2,8 +2,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_plus {
namespace esphome::airthings_wave_plus {
static const char *const TAG = "airthings_wave_plus";
@@ -98,7 +97,6 @@ void AirthingsWavePlus::setup() {
espbt::ESPBTUUID::from_raw(access_control_point_characteristic_uuid);
}
} // namespace airthings_wave_plus
} // namespace esphome
} // namespace esphome::airthings_wave_plus
#endif // USE_ESP32
@@ -4,8 +4,7 @@
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
namespace esphome {
namespace airthings_wave_plus {
namespace esphome::airthings_wave_plus {
namespace espbt = esphome::esp32_ble_tracker;
@@ -58,7 +57,6 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
};
};
} // namespace airthings_wave_plus
} // namespace esphome
} // namespace esphome::airthings_wave_plus
#endif // USE_ESP32
+2 -4
View File
@@ -5,8 +5,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace alpha3 {
namespace esphome::alpha3 {
static const char *const TAG = "alpha3";
@@ -185,7 +184,6 @@ void Alpha3::update() {
delay(25); // need to wait between requests
}
}
} // namespace alpha3
} // namespace esphome
} // namespace esphome::alpha3
#endif
+2 -4
View File
@@ -9,8 +9,7 @@
#include <esp_gattc_api.h>
namespace esphome {
namespace alpha3 {
namespace esphome::alpha3 {
namespace espbt = esphome::esp32_ble_tracker;
@@ -64,7 +63,6 @@ class Alpha3 : public esphome::ble_client::BLEClientNode, public PollingComponen
void send_request_(uint8_t *request, size_t len);
bool is_current_response_type_(const uint8_t *response_type);
};
} // namespace alpha3
} // namespace esphome
} // namespace esphome::alpha3
#endif
+2 -4
View File
@@ -24,8 +24,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace am2315c {
namespace esphome::am2315c {
static const char *const TAG = "am2315c";
@@ -176,5 +175,4 @@ void AM2315C::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
} // namespace am2315c
} // namespace esphome
} // namespace esphome::am2315c
+2 -4
View File
@@ -25,8 +25,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
namespace esphome {
namespace am2315c {
namespace esphome::am2315c {
class AM2315C : public PollingComponent, public i2c::I2CDevice {
public:
@@ -45,5 +44,4 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace am2315c
} // namespace esphome
} // namespace esphome::am2315c
+2 -4
View File
@@ -8,8 +8,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace am2320 {
namespace esphome::am2320 {
static const char *const TAG = "am2320";
@@ -86,5 +85,4 @@ bool AM2320Component::read_data_(uint8_t *data) {
return true;
}
} // namespace am2320
} // namespace esphome
} // namespace esphome::am2320
+2 -4
View File
@@ -4,8 +4,7 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace am2320 {
namespace esphome::am2320 {
class AM2320Component : public PollingComponent, public i2c::I2CDevice {
public:
@@ -24,5 +23,4 @@ class AM2320Component : public PollingComponent, public i2c::I2CDevice {
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace am2320
} // namespace esphome
} // namespace esphome::am2320
+2 -4
View File
@@ -2,8 +2,7 @@
#include "esphome/core/helpers.h"
#include <cstring>
namespace esphome {
namespace am43 {
namespace esphome::am43 {
const uint8_t START_PACKET[5] = {0x00, 0xff, 0x00, 0x00, 0x9a};
@@ -134,5 +133,4 @@ void Am43Decoder::decode(const uint8_t *data, uint16_t length) {
}
};
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
+2 -4
View File
@@ -3,8 +3,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace am43 {
namespace esphome::am43 {
static const uint16_t AM43_SERVICE_UUID = 0xFE50;
static const uint16_t AM43_CHARACTERISTIC_UUID = 0xFE51;
@@ -74,5 +73,4 @@ class Am43Decoder {
bool has_pin_response_;
};
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
+2 -4
View File
@@ -3,8 +3,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace am43 {
namespace esphome::am43 {
static const char *const TAG = "am43_cover";
@@ -154,7 +153,6 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
}
}
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
#endif
+2 -4
View File
@@ -10,8 +10,7 @@
#include <esp_gattc_api.h>
namespace esphome {
namespace am43 {
namespace esphome::am43 {
namespace espbt = esphome::esp32_ble_tracker;
@@ -38,7 +37,6 @@ class Am43Component : public cover::Cover, public esphome::ble_client::BLEClient
float position_;
};
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
#endif
@@ -4,8 +4,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace am43 {
namespace esphome::am43 {
static const char *const TAG = "am43";
@@ -111,7 +110,6 @@ void Am43::update() {
}
}
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
#endif
+2 -4
View File
@@ -10,8 +10,7 @@
#include <esp_gattc_api.h>
namespace esphome {
namespace am43 {
namespace esphome::am43 {
namespace espbt = esphome::esp32_ble_tracker;
@@ -38,7 +37,6 @@ class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent
uint32_t last_battery_update_;
};
} // namespace am43
} // namespace esphome
} // namespace esphome::am43
#endif
@@ -1,8 +1,7 @@
#include "analog_threshold_binary_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace analog_threshold {
namespace esphome::analog_threshold {
static const char *const TAG = "analog_threshold.binary_sensor";
@@ -43,5 +42,4 @@ void AnalogThresholdBinarySensor::dump_config() {
this->upper_threshold_.value(), this->lower_threshold_.value());
}
} // namespace analog_threshold
} // namespace esphome
} // namespace esphome::analog_threshold
@@ -5,8 +5,7 @@
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace analog_threshold {
namespace esphome::analog_threshold {
class AnalogThresholdBinarySensor : public Component, public binary_sensor::BinarySensor {
public:
@@ -24,5 +23,4 @@ class AnalogThresholdBinarySensor : public Component, public binary_sensor::Bina
bool raw_state_{false}; // Pre-filter state for hysteresis logic
};
} // namespace analog_threshold
} // namespace esphome
} // namespace esphome::analog_threshold
+2 -4
View File
@@ -2,8 +2,7 @@
#include "esphome/core/hal.h"
namespace esphome {
namespace animation {
namespace esphome::animation {
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
image::ImageType type, image::Transparency transparent)
@@ -71,5 +70,4 @@ void Animation::update_data_start_() {
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
}
} // namespace animation
} // namespace esphome
} // namespace esphome::animation
+2 -4
View File
@@ -3,8 +3,7 @@
#include "esphome/core/automation.h"
namespace esphome {
namespace animation {
namespace esphome::animation {
class Animation : public image::Image {
public:
@@ -64,5 +63,4 @@ template<typename... Ts> class AnimationSetFrameAction : public Action<Ts...> {
Animation *parent_;
};
} // namespace animation
} // namespace esphome
} // namespace esphome::animation
+2 -4
View File
@@ -3,8 +3,7 @@
#ifdef USE_ESP32
namespace esphome {
namespace anova {
namespace esphome::anova {
static const char *const TAG = "anova";
@@ -160,7 +159,6 @@ void Anova::update() {
}
}
} // namespace anova
} // namespace esphome
} // namespace esphome::anova
#endif
+2 -4
View File
@@ -10,8 +10,7 @@
#include <esp_gattc_api.h>
namespace esphome {
namespace anova {
namespace esphome::anova {
namespace espbt = esphome::esp32_ble_tracker;
@@ -45,7 +44,6 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
bool fahrenheit_;
};
} // namespace anova
} // namespace esphome
} // namespace esphome::anova
#endif

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