mirror of
https://github.com/esphome/esphome.git
synced 2025-09-29 16:42:19 +01:00
Merge branch 'integration' into memory_api
This commit is contained in:
4
.github/actions/build-image/action.yaml
vendored
4
.github/actions/build-image/action.yaml
vendored
@@ -47,7 +47,7 @@ runs:
|
|||||||
|
|
||||||
- name: Build and push to ghcr by digest
|
- name: Build and push to ghcr by digest
|
||||||
id: build-ghcr
|
id: build-ghcr
|
||||||
uses: docker/build-push-action@v6.18.0
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
env:
|
env:
|
||||||
DOCKER_BUILD_SUMMARY: false
|
DOCKER_BUILD_SUMMARY: false
|
||||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||||
@@ -73,7 +73,7 @@ runs:
|
|||||||
|
|
||||||
- name: Build and push to dockerhub by digest
|
- name: Build and push to dockerhub by digest
|
||||||
id: build-dockerhub
|
id: build-dockerhub
|
||||||
uses: docker/build-push-action@v6.18.0
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
env:
|
env:
|
||||||
DOCKER_BUILD_SUMMARY: false
|
DOCKER_BUILD_SUMMARY: false
|
||||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||||
|
4
.github/actions/restore-python/action.yml
vendored
4
.github/actions/restore-python/action.yml
vendored
@@ -17,12 +17,12 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Set up Python ${{ inputs.python-version }}
|
- name: Set up Python ${{ inputs.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ inputs.python-version }}
|
python-version: ${{ inputs.python-version }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache/restore@v4.3.0
|
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
|
6
.github/workflows/auto-label-pr.yml
vendored
6
.github/workflows/auto-label-pr.yml
vendored
@@ -22,17 +22,17 @@ 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@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@v2
|
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.ESPHOME_GITHUB_APP_ID }}
|
app-id: ${{ secrets.ESPHOME_GITHUB_APP_ID }}
|
||||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- name: Auto Label PR
|
- name: Auto Label PR
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ steps.generate-token.outputs.token }}
|
github-token: ${{ steps.generate-token.outputs.token }}
|
||||||
script: |
|
script: |
|
||||||
|
10
.github/workflows/ci-api-proto.yml
vendored
10
.github/workflows/ci-api-proto.yml
vendored
@@ -21,9 +21,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
- if: failure()
|
- if: failure()
|
||||||
name: Review PR
|
name: Review PR
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
await github.rest.pulls.createReview({
|
await github.rest.pulls.createReview({
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
run: git diff
|
run: git diff
|
||||||
- if: failure()
|
- if: failure()
|
||||||
name: Archive artifacts
|
name: Archive artifacts
|
||||||
uses: actions/upload-artifact@v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: generated-proto-files
|
name: generated-proto-files
|
||||||
path: |
|
path: |
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
esphome/components/api/api_pb2_service.*
|
esphome/components/api/api_pb2_service.*
|
||||||
- if: success()
|
- if: success()
|
||||||
name: Dismiss review
|
name: Dismiss review
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let reviews = await github.rest.pulls.listReviews({
|
let reviews = await github.rest.pulls.listReviews({
|
||||||
|
8
.github/workflows/ci-clang-tidy-hash.yml
vendored
8
.github/workflows/ci-clang-tidy-hash.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
|
|
||||||
- if: failure()
|
- if: failure()
|
||||||
name: Request changes
|
name: Request changes
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
await github.rest.pulls.createReview({
|
await github.rest.pulls.createReview({
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
|
|
||||||
- if: success()
|
- if: success()
|
||||||
name: Dismiss review
|
name: Dismiss review
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
let reviews = await github.rest.pulls.listReviews({
|
let reviews = await github.rest.pulls.listReviews({
|
||||||
|
6
.github/workflows/ci-docker.yml
vendored
6
.github/workflows/ci-docker.yml
vendored
@@ -43,13 +43,13 @@ jobs:
|
|||||||
- "docker"
|
- "docker"
|
||||||
# - "lint"
|
# - "lint"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.11.1
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Set TAG
|
- name: Set TAG
|
||||||
run: |
|
run: |
|
||||||
|
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@@ -36,18 +36,18 @@ 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@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- 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
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache@v4.3.0
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
@@ -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@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- 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@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
@@ -137,7 +137,7 @@ jobs:
|
|||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
id: restore-python
|
id: restore-python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
@@ -157,12 +157,12 @@ jobs:
|
|||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/
|
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v5.5.1
|
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
- name: Save Python virtual environment cache
|
- name: Save Python virtual environment cache
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: actions/cache/save@v4.3.0
|
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
||||||
@@ -180,7 +180,7 @@ jobs:
|
|||||||
component-test-count: ${{ steps.determine.outputs.component-test-count }}
|
component-test-count: ${{ steps.determine.outputs.component-test-count }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
# Fetch enough history to find the merge base
|
# Fetch enough history to find the merge base
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
@@ -215,15 +215,15 @@ 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@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Set up Python 3.13
|
- name: Set up Python 3.13
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.13"
|
python-version: "3.13"
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache@v4.3.0
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
||||||
@@ -288,7 +288,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
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
|
||||||
@@ -301,14 +301,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: actions/cache@v4.3.0
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref != 'refs/heads/dev'
|
if: github.ref != 'refs/heads/dev'
|
||||||
uses: actions/cache/restore@v4.3.0
|
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||||
@@ -375,7 +375,7 @@ jobs:
|
|||||||
sudo apt-get install libsdl2-dev
|
sudo apt-get install libsdl2-dev
|
||||||
|
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
@@ -401,7 +401,7 @@ jobs:
|
|||||||
matrix: ${{ steps.split.outputs.components }}
|
matrix: ${{ steps.split.outputs.components }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Split components into 20 groups
|
- name: Split components into 20 groups
|
||||||
id: split
|
id: split
|
||||||
run: |
|
run: |
|
||||||
@@ -431,7 +431,7 @@ jobs:
|
|||||||
sudo apt-get install libsdl2-dev
|
sudo apt-get install libsdl2-dev
|
||||||
|
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
@@ -460,16 +460,16 @@ jobs:
|
|||||||
if: github.event_name == 'pull_request' && github.base_ref != 'beta' && github.base_ref != 'release'
|
if: github.event_name == 'pull_request' && github.base_ref != 'beta' && github.base_ref != 'release'
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Restore Python
|
- name: Restore Python
|
||||||
uses: ./.github/actions/restore-python
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
- uses: pre-commit/action@v3.0.1
|
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
|
||||||
env:
|
env:
|
||||||
SKIP: pylint,clang-tidy-hash
|
SKIP: pylint,clang-tidy-hash
|
||||||
- uses: pre-commit-ci/lite-action@v1.1.0
|
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
ci-status:
|
ci-status:
|
||||||
|
@@ -25,7 +25,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Request reviews from component codeowners
|
- name: Request reviews from component codeowners
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const owner = context.repo.owner;
|
const owner = context.repo.owner;
|
||||||
|
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -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@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||||
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@v3
|
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||||
with:
|
with:
|
||||||
category: "/language:${{matrix.language}}"
|
category: "/language:${{matrix.language}}"
|
||||||
|
2
.github/workflows/external-component-bot.yml
vendored
2
.github/workflows/external-component-bot.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Add external component comment
|
- name: Add external component comment
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
2
.github/workflows/issue-codeowner-notify.yml
vendored
2
.github/workflows/issue-codeowner-notify.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Notify codeowners for component issues
|
- name: Notify codeowners for component issues
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const owner = context.repo.owner;
|
const owner = context.repo.owner;
|
||||||
|
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
@@ -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@v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Get tag
|
- name: Get tag
|
||||||
id: tag
|
id: tag
|
||||||
# yamllint disable rule:line-length
|
# yamllint disable rule:line-length
|
||||||
@@ -60,9 +60,9 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
pip3 install build
|
pip3 install build
|
||||||
python3 -m build
|
python3 -m build
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: pypa/gh-action-pypi-publish@v1.13.0
|
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||||
with:
|
with:
|
||||||
skip-existing: true
|
skip-existing: true
|
||||||
|
|
||||||
@@ -92,22 +92,22 @@ jobs:
|
|||||||
os: "ubuntu-24.04-arm"
|
os: "ubuntu-24.04-arm"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.11.1
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v3.5.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
uses: docker/login-action@v3.5.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -138,7 +138,7 @@ jobs:
|
|||||||
# version: ${{ needs.init.outputs.tag }}
|
# version: ${{ needs.init.outputs.tag }}
|
||||||
|
|
||||||
- name: Upload digests
|
- name: Upload digests
|
||||||
uses: actions/upload-artifact@v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: digests-${{ matrix.platform.arch }}
|
name: digests-${{ matrix.platform.arch }}
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
@@ -168,27 +168,27 @@ jobs:
|
|||||||
- ghcr
|
- ghcr
|
||||||
- dockerhub
|
- dockerhub
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@v5.0.0
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||||
with:
|
with:
|
||||||
pattern: digests-*
|
pattern: digests-*
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.11.1
|
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
if: matrix.registry == 'dockerhub'
|
if: matrix.registry == 'dockerhub'
|
||||||
uses: docker/login-action@v3.5.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
if: matrix.registry == 'ghcr'
|
if: matrix.registry == 'ghcr'
|
||||||
uses: docker/login-action@v3.5.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -220,7 +220,7 @@ jobs:
|
|||||||
- deploy-manifest
|
- deploy-manifest
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger Workflow
|
- name: Trigger Workflow
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
@@ -246,7 +246,7 @@ jobs:
|
|||||||
environment: ${{ needs.init.outputs.deploy_env }}
|
environment: ${{ needs.init.outputs.deploy_env }}
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger Workflow
|
- name: Trigger Workflow
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.DEPLOY_ESPHOME_SCHEMA_REPO_TOKEN }}
|
github-token: ${{ secrets.DEPLOY_ESPHOME_SCHEMA_REPO_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v10.0.0
|
- uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||||
with:
|
with:
|
||||||
days-before-pr-stale: 90
|
days-before-pr-stale: 90
|
||||||
days-before-pr-close: 7
|
days-before-pr-close: 7
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
close-issues:
|
close-issues:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v10.0.0
|
- uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||||
with:
|
with:
|
||||||
days-before-pr-stale: -1
|
days-before-pr-stale: -1
|
||||||
days-before-pr-close: -1
|
days-before-pr-close: -1
|
||||||
|
2
.github/workflows/status-check-labels.yml
vendored
2
.github/workflows/status-check-labels.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- merge-after-release
|
- merge-after-release
|
||||||
steps:
|
steps:
|
||||||
- name: Check for ${{ matrix.label }} label
|
- name: Check for ${{ matrix.label }} label
|
||||||
uses: actions/github-script@v8.0.0
|
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
|
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
|
||||||
|
8
.github/workflows/sync-device-classes.yml
vendored
8
.github/workflows/sync-device-classes.yml
vendored
@@ -13,16 +13,16 @@ jobs:
|
|||||||
if: github.repository == 'esphome/esphome'
|
if: github.repository == 'esphome/esphome'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Checkout Home Assistant
|
- name: Checkout Home Assistant
|
||||||
uses: actions/checkout@v5.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
repository: home-assistant/core
|
repository: home-assistant/core
|
||||||
path: lib/home-assistant
|
path: lib/home-assistant
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v6.0.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.13
|
python-version: 3.13
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
python script/run-in-env.py pre-commit run --all-files
|
python script/run-in-env.py pre-commit run --all-files
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: peter-evans/create-pull-request@v7.0.8
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
with:
|
with:
|
||||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||||
committer: esphomebot <esphome@openhomefoundation.org>
|
committer: esphomebot <esphome@openhomefoundation.org>
|
||||||
|
@@ -534,6 +534,7 @@ esphome/components/wk2204_spi/* @DrCoolZic
|
|||||||
esphome/components/wk2212_i2c/* @DrCoolZic
|
esphome/components/wk2212_i2c/* @DrCoolZic
|
||||||
esphome/components/wk2212_spi/* @DrCoolZic
|
esphome/components/wk2212_spi/* @DrCoolZic
|
||||||
esphome/components/wl_134/* @hobbypunk90
|
esphome/components/wl_134/* @hobbypunk90
|
||||||
|
esphome/components/wts01/* @alepee
|
||||||
esphome/components/x9c/* @EtienneMD
|
esphome/components/x9c/* @EtienneMD
|
||||||
esphome/components/xgzp68xx/* @gcormier
|
esphome/components/xgzp68xx/* @gcormier
|
||||||
esphome/components/xiaomi_hhccjcy10/* @fariouche
|
esphome/components/xiaomi_hhccjcy10/* @fariouche
|
||||||
|
@@ -738,11 +738,11 @@ def command_clean_mqtt(args: ArgsProtocol, config: ConfigType) -> int | None:
|
|||||||
return clean_mqtt(config, args)
|
return clean_mqtt(config, args)
|
||||||
|
|
||||||
|
|
||||||
def command_clean_platform(args: ArgsProtocol, config: ConfigType) -> int | None:
|
def command_clean_all(args: ArgsProtocol) -> int | None:
|
||||||
try:
|
try:
|
||||||
writer.clean_platform()
|
writer.clean_all(args.configuration)
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
_LOGGER.error("Error deleting platform files: %s", err)
|
_LOGGER.error("Error cleaning all files: %s", err)
|
||||||
return 1
|
return 1
|
||||||
_LOGGER.info("Done!")
|
_LOGGER.info("Done!")
|
||||||
return 0
|
return 0
|
||||||
@@ -938,6 +938,7 @@ PRE_CONFIG_ACTIONS = {
|
|||||||
"dashboard": command_dashboard,
|
"dashboard": command_dashboard,
|
||||||
"vscode": command_vscode,
|
"vscode": command_vscode,
|
||||||
"update-all": command_update_all,
|
"update-all": command_update_all,
|
||||||
|
"clean-all": command_clean_all,
|
||||||
}
|
}
|
||||||
|
|
||||||
POST_CONFIG_ACTIONS = {
|
POST_CONFIG_ACTIONS = {
|
||||||
@@ -948,7 +949,6 @@ POST_CONFIG_ACTIONS = {
|
|||||||
"run": command_run,
|
"run": command_run,
|
||||||
"clean": command_clean,
|
"clean": command_clean,
|
||||||
"clean-mqtt": command_clean_mqtt,
|
"clean-mqtt": command_clean_mqtt,
|
||||||
"clean-platform": command_clean_platform,
|
|
||||||
"mqtt-fingerprint": command_mqtt_fingerprint,
|
"mqtt-fingerprint": command_mqtt_fingerprint,
|
||||||
"idedata": command_idedata,
|
"idedata": command_idedata,
|
||||||
"rename": command_rename,
|
"rename": command_rename,
|
||||||
@@ -958,7 +958,6 @@ POST_CONFIG_ACTIONS = {
|
|||||||
SIMPLE_CONFIG_ACTIONS = [
|
SIMPLE_CONFIG_ACTIONS = [
|
||||||
"clean",
|
"clean",
|
||||||
"clean-mqtt",
|
"clean-mqtt",
|
||||||
"clean-platform",
|
|
||||||
"config",
|
"config",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1174,11 +1173,9 @@ def parse_args(argv):
|
|||||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
)
|
)
|
||||||
|
|
||||||
parser_clean = subparsers.add_parser(
|
parser_clean_all = subparsers.add_parser("clean-all", help="Clean all files.")
|
||||||
"clean-platform", help="Delete all platform files."
|
parser_clean_all.add_argument(
|
||||||
)
|
"configuration", help="Your YAML configuration directory.", nargs="*"
|
||||||
parser_clean.add_argument(
|
|
||||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
parser_dashboard = subparsers.add_parser(
|
parser_dashboard = subparsers.add_parser(
|
||||||
@@ -1227,7 +1224,7 @@ def parse_args(argv):
|
|||||||
|
|
||||||
parser_update = subparsers.add_parser("update-all")
|
parser_update = subparsers.add_parser("update-all")
|
||||||
parser_update.add_argument(
|
parser_update.add_argument(
|
||||||
"configuration", help="Your YAML configuration file directories.", nargs="+"
|
"configuration", help="Your YAML configuration file or directory.", nargs="+"
|
||||||
)
|
)
|
||||||
|
|
||||||
parser_idedata = subparsers.add_parser("idedata")
|
parser_idedata = subparsers.add_parser("idedata")
|
||||||
|
@@ -19,6 +19,15 @@ std::string build_json(const json_build_t &f) {
|
|||||||
|
|
||||||
bool parse_json(const std::string &data, const json_parse_t &f) {
|
bool parse_json(const std::string &data, const json_parse_t &f) {
|
||||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
JsonDocument doc = parse_json(data);
|
||||||
|
if (doc.overflowed() || doc.isNull())
|
||||||
|
return false;
|
||||||
|
return f(doc.as<JsonObject>());
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDocument parse_json(const std::string &data) {
|
||||||
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
#ifdef USE_PSRAM
|
#ifdef USE_PSRAM
|
||||||
auto doc_allocator = SpiRamAllocator();
|
auto doc_allocator = SpiRamAllocator();
|
||||||
JsonDocument json_document(&doc_allocator);
|
JsonDocument json_document(&doc_allocator);
|
||||||
@@ -27,20 +36,18 @@ bool parse_json(const std::string &data, const json_parse_t &f) {
|
|||||||
#endif
|
#endif
|
||||||
if (json_document.overflowed()) {
|
if (json_document.overflowed()) {
|
||||||
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
|
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
|
||||||
return false;
|
return JsonObject(); // return unbound object
|
||||||
}
|
}
|
||||||
DeserializationError err = deserializeJson(json_document, data);
|
DeserializationError err = deserializeJson(json_document, data);
|
||||||
|
|
||||||
JsonObject root = json_document.as<JsonObject>();
|
|
||||||
|
|
||||||
if (err == DeserializationError::Ok) {
|
if (err == DeserializationError::Ok) {
|
||||||
return f(root);
|
return json_document;
|
||||||
} else if (err == DeserializationError::NoMemory) {
|
} else if (err == DeserializationError::NoMemory) {
|
||||||
ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
|
ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
|
||||||
return false;
|
return JsonObject(); // return unbound object
|
||||||
}
|
}
|
||||||
ESP_LOGE(TAG, "Parse error: %s", err.c_str());
|
ESP_LOGE(TAG, "Parse error: %s", err.c_str());
|
||||||
return false;
|
return JsonObject(); // return unbound object
|
||||||
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -49,6 +49,8 @@ std::string build_json(const json_build_t &f);
|
|||||||
|
|
||||||
/// Parse a JSON string and run the provided json parse function if it's valid.
|
/// Parse a JSON string and run the provided json parse function if it's valid.
|
||||||
bool parse_json(const std::string &data, const json_parse_t &f);
|
bool parse_json(const std::string &data, const json_parse_t &f);
|
||||||
|
/// Parse a JSON string and return the root JsonDocument (or an unbound object on error)
|
||||||
|
JsonDocument parse_json(const std::string &data);
|
||||||
|
|
||||||
/// Builder class for creating JSON documents without lambdas
|
/// Builder class for creating JSON documents without lambdas
|
||||||
class JsonBuilder {
|
class JsonBuilder {
|
||||||
|
@@ -66,7 +66,7 @@ CONFIG_SCHEMA = (
|
|||||||
),
|
),
|
||||||
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure,
|
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure,
|
||||||
cv.Optional(CONF_TEMPERATURE_OFFSET): cv.All(
|
cv.Optional(CONF_TEMPERATURE_OFFSET): cv.All(
|
||||||
cv.temperature,
|
cv.temperature_delta,
|
||||||
cv.float_range(min=0, max=655.35),
|
cv.float_range(min=0, max=655.35),
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.All(
|
cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.All(
|
||||||
|
0
esphome/components/wts01/__init__.py
Normal file
0
esphome/components/wts01/__init__.py
Normal file
41
esphome/components/wts01/sensor.py
Normal file
41
esphome/components/wts01/sensor.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import sensor, uart
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import (
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
)
|
||||||
|
|
||||||
|
CONF_WTS01_ID = "wts01_id"
|
||||||
|
CODEOWNERS = ["@alepee"]
|
||||||
|
DEPENDENCIES = ["uart"]
|
||||||
|
|
||||||
|
wts01_ns = cg.esphome_ns.namespace("wts01")
|
||||||
|
WTS01Sensor = wts01_ns.class_(
|
||||||
|
"WTS01Sensor", cg.Component, uart.UARTDevice, sensor.Sensor
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
sensor.sensor_schema(
|
||||||
|
WTS01Sensor,
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||||
|
"wts01",
|
||||||
|
baud_rate=9600,
|
||||||
|
require_rx=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = await sensor.new_sensor(config)
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await uart.register_uart_device(var, config)
|
91
esphome/components/wts01/wts01.cpp
Normal file
91
esphome/components/wts01/wts01.cpp
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include "wts01.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace wts01 {
|
||||||
|
|
||||||
|
constexpr uint8_t HEADER_1 = 0x55;
|
||||||
|
constexpr uint8_t HEADER_2 = 0x01;
|
||||||
|
constexpr uint8_t HEADER_3 = 0x01;
|
||||||
|
constexpr uint8_t HEADER_4 = 0x04;
|
||||||
|
|
||||||
|
static const char *const TAG = "wts01";
|
||||||
|
|
||||||
|
void WTS01Sensor::loop() {
|
||||||
|
// Process all available data at once
|
||||||
|
while (this->available()) {
|
||||||
|
uint8_t c;
|
||||||
|
if (this->read_byte(&c)) {
|
||||||
|
this->handle_char_(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WTS01Sensor::dump_config() { LOG_SENSOR("", "WTS01 Sensor", this); }
|
||||||
|
|
||||||
|
void WTS01Sensor::handle_char_(uint8_t c) {
|
||||||
|
// State machine for processing the header. Reset if something doesn't match.
|
||||||
|
if (this->buffer_pos_ == 0 && c != HEADER_1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->buffer_pos_ == 1 && c != HEADER_2) {
|
||||||
|
this->buffer_pos_ = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->buffer_pos_ == 2 && c != HEADER_3) {
|
||||||
|
this->buffer_pos_ = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->buffer_pos_ == 3 && c != HEADER_4) {
|
||||||
|
this->buffer_pos_ = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add byte to buffer
|
||||||
|
this->buffer_[this->buffer_pos_++] = c;
|
||||||
|
|
||||||
|
// Process complete packet
|
||||||
|
if (this->buffer_pos_ >= PACKET_SIZE) {
|
||||||
|
this->process_packet_();
|
||||||
|
this->buffer_pos_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WTS01Sensor::process_packet_() {
|
||||||
|
// Based on Tasmota implementation
|
||||||
|
// Format: 55 01 01 04 01 11 16 12 95
|
||||||
|
// header T Td Ck - T = Temperature, Td = Temperature decimal, Ck = Checksum
|
||||||
|
uint8_t calculated_checksum = 0;
|
||||||
|
for (uint8_t i = 0; i < PACKET_SIZE - 1; i++) {
|
||||||
|
calculated_checksum += this->buffer_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t received_checksum = this->buffer_[PACKET_SIZE - 1];
|
||||||
|
if (calculated_checksum != received_checksum) {
|
||||||
|
ESP_LOGW(TAG, "WTS01 Checksum doesn't match: 0x%02X != 0x%02X", received_checksum, calculated_checksum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract temperature value
|
||||||
|
int8_t temp = this->buffer_[6];
|
||||||
|
int32_t sign = 1;
|
||||||
|
|
||||||
|
// Handle negative temperatures
|
||||||
|
if (temp < 0) {
|
||||||
|
sign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate temperature (temp + decimal/100)
|
||||||
|
float temperature = static_cast<float>(temp) + (sign * static_cast<float>(this->buffer_[7]) / 100.0f);
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Received new temperature: %.2f°C", temperature);
|
||||||
|
|
||||||
|
this->publish_state(temperature);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wts01
|
||||||
|
} // namespace esphome
|
27
esphome/components/wts01/wts01.h
Normal file
27
esphome/components/wts01/wts01.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace wts01 {
|
||||||
|
|
||||||
|
constexpr uint8_t PACKET_SIZE = 9;
|
||||||
|
|
||||||
|
class WTS01Sensor : public sensor::Sensor, public uart::UARTDevice, public Component {
|
||||||
|
public:
|
||||||
|
void loop() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t buffer_[PACKET_SIZE];
|
||||||
|
uint8_t buffer_pos_{0};
|
||||||
|
|
||||||
|
void handle_char_(uint8_t c);
|
||||||
|
void process_packet_();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wts01
|
||||||
|
} // namespace esphome
|
@@ -479,10 +479,12 @@ class EsphomeCleanMqttHandler(EsphomeCommandWebSocket):
|
|||||||
return [*DASHBOARD_COMMAND, "clean-mqtt", config_file]
|
return [*DASHBOARD_COMMAND, "clean-mqtt", config_file]
|
||||||
|
|
||||||
|
|
||||||
class EsphomeCleanPlatformHandler(EsphomeCommandWebSocket):
|
class EsphomeCleanAllHandler(EsphomeCommandWebSocket):
|
||||||
async def build_command(self, json_message: dict[str, Any]) -> list[str]:
|
async def build_command(self, json_message: dict[str, Any]) -> list[str]:
|
||||||
config_file = settings.rel_path(json_message["configuration"])
|
clean_build_dir = json_message.get("clean_build_dir", True)
|
||||||
return [*DASHBOARD_COMMAND, "clean-platform", config_file]
|
if clean_build_dir:
|
||||||
|
return [*DASHBOARD_COMMAND, "clean-all", settings.config_dir]
|
||||||
|
return [*DASHBOARD_COMMAND, "clean-all"]
|
||||||
|
|
||||||
|
|
||||||
class EsphomeCleanHandler(EsphomeCommandWebSocket):
|
class EsphomeCleanHandler(EsphomeCommandWebSocket):
|
||||||
@@ -1319,7 +1321,7 @@ def make_app(debug=get_bool_env(ENV_DEV)) -> tornado.web.Application:
|
|||||||
(f"{rel}compile", EsphomeCompileHandler),
|
(f"{rel}compile", EsphomeCompileHandler),
|
||||||
(f"{rel}validate", EsphomeValidateHandler),
|
(f"{rel}validate", EsphomeValidateHandler),
|
||||||
(f"{rel}clean-mqtt", EsphomeCleanMqttHandler),
|
(f"{rel}clean-mqtt", EsphomeCleanMqttHandler),
|
||||||
(f"{rel}clean-platform", EsphomeCleanPlatformHandler),
|
(f"{rel}clean-all", EsphomeCleanAllHandler),
|
||||||
(f"{rel}clean", EsphomeCleanHandler),
|
(f"{rel}clean", EsphomeCleanHandler),
|
||||||
(f"{rel}vscode", EsphomeVscodeHandler),
|
(f"{rel}vscode", EsphomeVscodeHandler),
|
||||||
(f"{rel}ace", EsphomeAceEditorHandler),
|
(f"{rel}ace", EsphomeAceEditorHandler),
|
||||||
|
@@ -335,13 +335,15 @@ def clean_build():
|
|||||||
shutil.rmtree(cache_dir)
|
shutil.rmtree(cache_dir)
|
||||||
|
|
||||||
|
|
||||||
def clean_platform():
|
def clean_all(configuration: list[str]):
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
# Clean entire build dir
|
# Clean entire build dir
|
||||||
if CORE.build_path.is_dir():
|
for dir in configuration:
|
||||||
_LOGGER.info("Deleting %s", CORE.build_path)
|
buid_dir = Path(dir) / ".esphome"
|
||||||
shutil.rmtree(CORE.build_path)
|
if buid_dir.is_dir():
|
||||||
|
_LOGGER.info("Deleting %s", buid_dir)
|
||||||
|
shutil.rmtree(buid_dir)
|
||||||
|
|
||||||
# Clean PlatformIO project files
|
# Clean PlatformIO project files
|
||||||
try:
|
try:
|
||||||
|
7
tests/components/wts01/common.yaml
Normal file
7
tests/components/wts01/common.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
uart:
|
||||||
|
rx_pin: ${rx_pin}
|
||||||
|
baud_rate: 9600
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: wts01
|
||||||
|
id: wts01_sensor
|
5
tests/components/wts01/test.esp32-ard.yaml
Normal file
5
tests/components/wts01/test.esp32-ard.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO16
|
||||||
|
rx_pin: GPIO17
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
5
tests/components/wts01/test.esp32-c3-ard.yaml
Normal file
5
tests/components/wts01/test.esp32-c3-ard.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO6
|
||||||
|
rx_pin: GPIO7
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
5
tests/components/wts01/test.esp32-c3-idf.yaml
Normal file
5
tests/components/wts01/test.esp32-c3-idf.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO6
|
||||||
|
rx_pin: GPIO7
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
5
tests/components/wts01/test.esp32-idf.yaml
Normal file
5
tests/components/wts01/test.esp32-idf.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO16
|
||||||
|
rx_pin: GPIO17
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
5
tests/components/wts01/test.esp8266-ard.yaml
Normal file
5
tests/components/wts01/test.esp8266-ard.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO1
|
||||||
|
rx_pin: GPIO3
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
5
tests/components/wts01/test.rp2040-ard.yaml
Normal file
5
tests/components/wts01/test.rp2040-ard.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
substitutions:
|
||||||
|
tx_pin: GPIO0
|
||||||
|
rx_pin: GPIO1
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from aioesphomeapi import APIConnectionError
|
from aioesphomeapi import APIConnectionError, InvalidAuthAPIError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from .types import APIClientConnectedFactory, RunCompiledFunction
|
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||||
@@ -48,6 +48,22 @@ async def test_host_mode_api_password(
|
|||||||
assert len(states) > 0
|
assert len(states) > 0
|
||||||
|
|
||||||
# Test with wrong password - should fail
|
# Test with wrong password - should fail
|
||||||
with pytest.raises(APIConnectionError, match="Invalid password"):
|
# Try connecting with wrong password
|
||||||
async with api_client_connected(password="wrong_password"):
|
try:
|
||||||
pass # Should not reach here
|
async with api_client_connected(
|
||||||
|
password="wrong_password", timeout=5
|
||||||
|
) as client:
|
||||||
|
# If we get here without exception, try to use the connection
|
||||||
|
# which should fail if auth failed
|
||||||
|
await client.device_info_and_list_entities()
|
||||||
|
# If we successfully got device info and entities, auth didn't fail properly
|
||||||
|
pytest.fail("Connection succeeded with wrong password")
|
||||||
|
except (InvalidAuthAPIError, APIConnectionError) as e:
|
||||||
|
# Expected - auth should fail
|
||||||
|
# Accept either InvalidAuthAPIError or generic APIConnectionError
|
||||||
|
# since the client might not always distinguish
|
||||||
|
assert (
|
||||||
|
"password" in str(e).lower()
|
||||||
|
or "auth" in str(e).lower()
|
||||||
|
or "invalid" in str(e).lower()
|
||||||
|
)
|
||||||
|
@@ -17,7 +17,7 @@ from esphome import platformio_api
|
|||||||
from esphome.__main__ import (
|
from esphome.__main__ import (
|
||||||
Purpose,
|
Purpose,
|
||||||
choose_upload_log_host,
|
choose_upload_log_host,
|
||||||
command_clean_platform,
|
command_clean_all,
|
||||||
command_rename,
|
command_rename,
|
||||||
command_update_all,
|
command_update_all,
|
||||||
command_wizard,
|
command_wizard,
|
||||||
@@ -1857,33 +1857,31 @@ esp32:
|
|||||||
assert "can only concatenate str" not in clean_output
|
assert "can only concatenate str" not in clean_output
|
||||||
|
|
||||||
|
|
||||||
def test_command_clean_platform_success(
|
def test_command_clean_all_success(
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test command_clean_platform when writer.clean_platform() succeeds."""
|
"""Test command_clean_all when writer.clean_all() succeeds."""
|
||||||
args = MockArgs()
|
args = MockArgs(configuration=["/path/to/config1", "/path/to/config2"])
|
||||||
config = {}
|
|
||||||
|
|
||||||
# Set logger level to capture INFO messages
|
# Set logger level to capture INFO messages
|
||||||
with (
|
with (
|
||||||
caplog.at_level(logging.INFO),
|
caplog.at_level(logging.INFO),
|
||||||
patch("esphome.writer.clean_platform") as mock_clean_platform,
|
patch("esphome.writer.clean_all") as mock_clean_all,
|
||||||
):
|
):
|
||||||
result = command_clean_platform(args, config)
|
result = command_clean_all(args)
|
||||||
|
|
||||||
assert result == 0
|
assert result == 0
|
||||||
mock_clean_platform.assert_called_once()
|
mock_clean_all.assert_called_once_with(["/path/to/config1", "/path/to/config2"])
|
||||||
|
|
||||||
# Check that success message was logged
|
# Check that success message was logged
|
||||||
assert "Done!" in caplog.text
|
assert "Done!" in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def test_command_clean_platform_oserror(
|
def test_command_clean_all_oserror(
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test command_clean_platform when writer.clean_platform() raises OSError."""
|
"""Test command_clean_all when writer.clean_all() raises OSError."""
|
||||||
args = MockArgs()
|
args = MockArgs(configuration=["/path/to/config1"])
|
||||||
config = {}
|
|
||||||
|
|
||||||
# Create a mock OSError with a specific message
|
# Create a mock OSError with a specific message
|
||||||
mock_error = OSError("Permission denied: cannot delete directory")
|
mock_error = OSError("Permission denied: cannot delete directory")
|
||||||
@@ -1891,30 +1889,27 @@ def test_command_clean_platform_oserror(
|
|||||||
# Set logger level to capture ERROR and INFO messages
|
# Set logger level to capture ERROR and INFO messages
|
||||||
with (
|
with (
|
||||||
caplog.at_level(logging.INFO),
|
caplog.at_level(logging.INFO),
|
||||||
patch(
|
patch("esphome.writer.clean_all", side_effect=mock_error) as mock_clean_all,
|
||||||
"esphome.writer.clean_platform", side_effect=mock_error
|
|
||||||
) as mock_clean_platform,
|
|
||||||
):
|
):
|
||||||
result = command_clean_platform(args, config)
|
result = command_clean_all(args)
|
||||||
|
|
||||||
assert result == 1
|
assert result == 1
|
||||||
mock_clean_platform.assert_called_once()
|
mock_clean_all.assert_called_once_with(["/path/to/config1"])
|
||||||
|
|
||||||
# Check that error message was logged
|
# Check that error message was logged
|
||||||
assert (
|
assert (
|
||||||
"Error deleting platform files: Permission denied: cannot delete directory"
|
"Error cleaning all files: Permission denied: cannot delete directory"
|
||||||
in caplog.text
|
in caplog.text
|
||||||
)
|
)
|
||||||
# Should not have success message
|
# Should not have success message
|
||||||
assert "Done!" not in caplog.text
|
assert "Done!" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def test_command_clean_platform_oserror_no_message(
|
def test_command_clean_all_oserror_no_message(
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test command_clean_platform when writer.clean_platform() raises OSError without message."""
|
"""Test command_clean_all when writer.clean_all() raises OSError without message."""
|
||||||
args = MockArgs()
|
args = MockArgs(configuration=["/path/to/config1"])
|
||||||
config = {}
|
|
||||||
|
|
||||||
# Create a mock OSError without a message
|
# Create a mock OSError without a message
|
||||||
mock_error = OSError()
|
mock_error = OSError()
|
||||||
@@ -1922,34 +1917,33 @@ def test_command_clean_platform_oserror_no_message(
|
|||||||
# Set logger level to capture ERROR and INFO messages
|
# Set logger level to capture ERROR and INFO messages
|
||||||
with (
|
with (
|
||||||
caplog.at_level(logging.INFO),
|
caplog.at_level(logging.INFO),
|
||||||
patch(
|
patch("esphome.writer.clean_all", side_effect=mock_error) as mock_clean_all,
|
||||||
"esphome.writer.clean_platform", side_effect=mock_error
|
|
||||||
) as mock_clean_platform,
|
|
||||||
):
|
):
|
||||||
result = command_clean_platform(args, config)
|
result = command_clean_all(args)
|
||||||
|
|
||||||
assert result == 1
|
assert result == 1
|
||||||
mock_clean_platform.assert_called_once()
|
mock_clean_all.assert_called_once_with(["/path/to/config1"])
|
||||||
|
|
||||||
# Check that error message was logged (should show empty string for OSError without message)
|
# Check that error message was logged (should show empty string for OSError without message)
|
||||||
assert "Error deleting platform files:" in caplog.text
|
assert "Error cleaning all files:" in caplog.text
|
||||||
# Should not have success message
|
# Should not have success message
|
||||||
assert "Done!" not in caplog.text
|
assert "Done!" not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def test_command_clean_platform_args_and_config_ignored() -> None:
|
def test_command_clean_all_args_used() -> None:
|
||||||
"""Test that command_clean_platform ignores args and config parameters."""
|
"""Test that command_clean_all uses args.configuration parameter."""
|
||||||
# Test with various args and config to ensure they don't affect the function
|
# Test with different configuration paths
|
||||||
args1 = MockArgs(name="test1", file="test.bin")
|
args1 = MockArgs(configuration=["/path/to/config1"])
|
||||||
config1 = {"wifi": {"ssid": "test"}}
|
args2 = MockArgs(configuration=["/path/to/config2", "/path/to/config3"])
|
||||||
|
|
||||||
args2 = MockArgs(name="test2", dashboard=True)
|
with patch("esphome.writer.clean_all") as mock_clean_all:
|
||||||
config2 = {"api": {}, "ota": {}}
|
result1 = command_clean_all(args1)
|
||||||
|
result2 = command_clean_all(args2)
|
||||||
with patch("esphome.writer.clean_platform") as mock_clean_platform:
|
|
||||||
result1 = command_clean_platform(args1, config1)
|
|
||||||
result2 = command_clean_platform(args2, config2)
|
|
||||||
|
|
||||||
assert result1 == 0
|
assert result1 == 0
|
||||||
assert result2 == 0
|
assert result2 == 0
|
||||||
assert mock_clean_platform.call_count == 2
|
assert mock_clean_all.call_count == 2
|
||||||
|
|
||||||
|
# Verify the correct configuration paths were passed
|
||||||
|
mock_clean_all.assert_any_call(["/path/to/config1"])
|
||||||
|
mock_clean_all.assert_any_call(["/path/to/config2", "/path/to/config3"])
|
||||||
|
@@ -739,16 +739,24 @@ def test_write_cpp_with_duplicate_markers(
|
|||||||
|
|
||||||
|
|
||||||
@patch("esphome.writer.CORE")
|
@patch("esphome.writer.CORE")
|
||||||
def test_clean_platform(
|
def test_clean_all(
|
||||||
mock_core: MagicMock,
|
mock_core: MagicMock,
|
||||||
tmp_path: Path,
|
tmp_path: Path,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test clean_platform removes build and PlatformIO dirs."""
|
"""Test clean_all removes build and PlatformIO dirs."""
|
||||||
# Create build directory
|
# Create build directories for multiple configurations
|
||||||
build_dir = tmp_path / "build"
|
config1_dir = tmp_path / "config1"
|
||||||
build_dir.mkdir()
|
config2_dir = tmp_path / "config2"
|
||||||
(build_dir / "dummy.txt").write_text("x")
|
config1_dir.mkdir()
|
||||||
|
config2_dir.mkdir()
|
||||||
|
|
||||||
|
build_dir1 = config1_dir / ".esphome"
|
||||||
|
build_dir2 = config2_dir / ".esphome"
|
||||||
|
build_dir1.mkdir()
|
||||||
|
build_dir2.mkdir()
|
||||||
|
(build_dir1 / "dummy.txt").write_text("x")
|
||||||
|
(build_dir2 / "dummy.txt").write_text("x")
|
||||||
|
|
||||||
# Create PlatformIO directories
|
# Create PlatformIO directories
|
||||||
pio_cache = tmp_path / "pio_cache"
|
pio_cache = tmp_path / "pio_cache"
|
||||||
@@ -759,9 +767,6 @@ def test_clean_platform(
|
|||||||
d.mkdir()
|
d.mkdir()
|
||||||
(d / "keep").write_text("x")
|
(d / "keep").write_text("x")
|
||||||
|
|
||||||
# Setup CORE
|
|
||||||
mock_core.build_path = build_dir
|
|
||||||
|
|
||||||
# Mock ProjectConfig
|
# Mock ProjectConfig
|
||||||
with patch(
|
with patch(
|
||||||
"platformio.project.config.ProjectConfig.get_instance"
|
"platformio.project.config.ProjectConfig.get_instance"
|
||||||
@@ -781,13 +786,14 @@ def test_clean_platform(
|
|||||||
mock_config.get.side_effect = cfg_get
|
mock_config.get.side_effect = cfg_get
|
||||||
|
|
||||||
# Call
|
# Call
|
||||||
from esphome.writer import clean_platform
|
from esphome.writer import clean_all
|
||||||
|
|
||||||
with caplog.at_level("INFO"):
|
with caplog.at_level("INFO"):
|
||||||
clean_platform()
|
clean_all([str(config1_dir), str(config2_dir)])
|
||||||
|
|
||||||
# Verify deletions
|
# Verify deletions
|
||||||
assert not build_dir.exists()
|
assert not build_dir1.exists()
|
||||||
|
assert not build_dir2.exists()
|
||||||
assert not pio_cache.exists()
|
assert not pio_cache.exists()
|
||||||
assert not pio_packages.exists()
|
assert not pio_packages.exists()
|
||||||
assert not pio_platforms.exists()
|
assert not pio_platforms.exists()
|
||||||
@@ -795,7 +801,8 @@ def test_clean_platform(
|
|||||||
|
|
||||||
# Verify logging mentions each
|
# Verify logging mentions each
|
||||||
assert "Deleting" in caplog.text
|
assert "Deleting" in caplog.text
|
||||||
assert str(build_dir) in caplog.text
|
assert str(build_dir1) in caplog.text
|
||||||
|
assert str(build_dir2) in caplog.text
|
||||||
assert "PlatformIO cache" in caplog.text
|
assert "PlatformIO cache" in caplog.text
|
||||||
assert "PlatformIO packages" in caplog.text
|
assert "PlatformIO packages" in caplog.text
|
||||||
assert "PlatformIO platforms" in caplog.text
|
assert "PlatformIO platforms" in caplog.text
|
||||||
@@ -803,28 +810,29 @@ def test_clean_platform(
|
|||||||
|
|
||||||
|
|
||||||
@patch("esphome.writer.CORE")
|
@patch("esphome.writer.CORE")
|
||||||
def test_clean_platform_platformio_not_available(
|
def test_clean_all_platformio_not_available(
|
||||||
mock_core: MagicMock,
|
mock_core: MagicMock,
|
||||||
tmp_path: Path,
|
tmp_path: Path,
|
||||||
caplog: pytest.LogCaptureFixture,
|
caplog: pytest.LogCaptureFixture,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test clean_platform when PlatformIO is not available."""
|
"""Test clean_all when PlatformIO is not available."""
|
||||||
# Build dir
|
# Build dirs
|
||||||
build_dir = tmp_path / "build"
|
config_dir = tmp_path / "config"
|
||||||
|
config_dir.mkdir()
|
||||||
|
build_dir = config_dir / ".esphome"
|
||||||
build_dir.mkdir()
|
build_dir.mkdir()
|
||||||
mock_core.build_path = build_dir
|
|
||||||
|
|
||||||
# PlatformIO dirs that should remain untouched
|
# PlatformIO dirs that should remain untouched
|
||||||
pio_cache = tmp_path / "pio_cache"
|
pio_cache = tmp_path / "pio_cache"
|
||||||
pio_cache.mkdir()
|
pio_cache.mkdir()
|
||||||
|
|
||||||
from esphome.writer import clean_platform
|
from esphome.writer import clean_all
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch.dict("sys.modules", {"platformio.project.config": None}),
|
patch.dict("sys.modules", {"platformio.project.config": None}),
|
||||||
caplog.at_level("INFO"),
|
caplog.at_level("INFO"),
|
||||||
):
|
):
|
||||||
clean_platform()
|
clean_all([str(config_dir)])
|
||||||
|
|
||||||
# Build dir removed, PlatformIO dirs remain
|
# Build dir removed, PlatformIO dirs remain
|
||||||
assert not build_dir.exists()
|
assert not build_dir.exists()
|
||||||
@@ -835,14 +843,15 @@ def test_clean_platform_platformio_not_available(
|
|||||||
|
|
||||||
|
|
||||||
@patch("esphome.writer.CORE")
|
@patch("esphome.writer.CORE")
|
||||||
def test_clean_platform_partial_exists(
|
def test_clean_all_partial_exists(
|
||||||
mock_core: MagicMock,
|
mock_core: MagicMock,
|
||||||
tmp_path: Path,
|
tmp_path: Path,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test clean_platform when only build dir exists."""
|
"""Test clean_all when only some build dirs exist."""
|
||||||
build_dir = tmp_path / "build"
|
config_dir = tmp_path / "config"
|
||||||
|
config_dir.mkdir()
|
||||||
|
build_dir = config_dir / ".esphome"
|
||||||
build_dir.mkdir()
|
build_dir.mkdir()
|
||||||
mock_core.build_path = build_dir
|
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"platformio.project.config.ProjectConfig.get_instance"
|
"platformio.project.config.ProjectConfig.get_instance"
|
||||||
@@ -854,8 +863,8 @@ def test_clean_platform_partial_exists(
|
|||||||
tmp_path / "does_not_exist"
|
tmp_path / "does_not_exist"
|
||||||
)
|
)
|
||||||
|
|
||||||
from esphome.writer import clean_platform
|
from esphome.writer import clean_all
|
||||||
|
|
||||||
clean_platform()
|
clean_all([str(config_dir)])
|
||||||
|
|
||||||
assert not build_dir.exists()
|
assert not build_dir.exists()
|
||||||
|
Reference in New Issue
Block a user