Merge branch 'esphome:dev' into uponor-smatrix-target-temp-sensor

This commit is contained in:
Jan Schröter 2025-01-29 20:02:23 +01:00 committed by GitHub
commit 37a11c1e83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
613 changed files with 13769 additions and 5119 deletions

View File

@ -7,28 +7,39 @@ Checks: >-
-boost-*, -boost-*,
-bugprone-easily-swappable-parameters, -bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result, -bugprone-implicit-widening-of-multiplication-result,
-bugprone-multi-level-implicit-pointer-conversion,
-bugprone-narrowing-conversions, -bugprone-narrowing-conversions,
-bugprone-signed-char-misuse, -bugprone-signed-char-misuse,
-bugprone-switch-missing-default-case,
-cert-dcl50-cpp, -cert-dcl50-cpp,
-cert-err33-c, -cert-err33-c,
-cert-err58-cpp, -cert-err58-cpp,
-cert-oop57-cpp, -cert-oop57-cpp,
-cert-str34-c, -cert-str34-c,
-clang-analyzer-optin.core.EnumCastOutOfRange,
-clang-analyzer-optin.cplusplus.UninitializedObject, -clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-osx.*, -clang-analyzer-osx.*,
-clang-diagnostic-delete-abstract-non-virtual-dtor, -clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor, -clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-deprecated-declarations,
-clang-diagnostic-ignored-optimization-argument, -clang-diagnostic-ignored-optimization-argument,
-clang-diagnostic-missing-field-initializers,
-clang-diagnostic-shadow-field, -clang-diagnostic-shadow-field,
-clang-diagnostic-unused-const-variable, -clang-diagnostic-unused-const-variable,
-clang-diagnostic-unused-parameter, -clang-diagnostic-unused-parameter,
-clang-diagnostic-vla-cxx-extension,
-concurrency-*, -concurrency-*,
-cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-init-variables, -cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-to-enum,
-cppcoreguidelines-macro-usage, -cppcoreguidelines-macro-usage,
-cppcoreguidelines-missing-std-forward,
-cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-prefer-member-initializer, -cppcoreguidelines-prefer-member-initializer,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-constant-array-index,
@ -40,7 +51,9 @@ Checks: >-
-cppcoreguidelines-pro-type-static-cast-downcast, -cppcoreguidelines-pro-type-static-cast-downcast,
-cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-rvalue-reference-param-not-moved,
-cppcoreguidelines-special-member-functions, -cppcoreguidelines-special-member-functions,
-cppcoreguidelines-use-default-member-init,
-cppcoreguidelines-virtual-class-destructor, -cppcoreguidelines-virtual-class-destructor,
-fuchsia-multiple-inheritance, -fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator, -fuchsia-overloaded-operator,
@ -60,20 +73,32 @@ Checks: >-
-llvm-include-order, -llvm-include-order,
-llvm-qualified-auto, -llvm-qualified-auto,
-llvmlibc-*, -llvmlibc-*,
-misc-non-private-member-variables-in-classes, -misc-const-correctness,
-misc-include-cleaner,
-misc-no-recursion, -misc-no-recursion,
-misc-non-private-member-variables-in-classes,
-misc-unused-parameters, -misc-unused-parameters,
-modernize-avoid-c-arrays, -misc-use-anonymous-namespace,
-modernize-avoid-bind, -modernize-avoid-bind,
-modernize-avoid-c-arrays,
-modernize-concat-nested-namespaces, -modernize-concat-nested-namespaces,
-modernize-macro-to-enum,
-modernize-return-braced-init-list, -modernize-return-braced-init-list,
-modernize-type-traits,
-modernize-use-auto, -modernize-use-auto,
-modernize-use-constraints,
-modernize-use-default-member-init, -modernize-use-default-member-init,
-modernize-use-equals-default, -modernize-use-equals-default,
-modernize-use-trailing-return-type,
-modernize-use-nodiscard, -modernize-use-nodiscard,
-modernize-use-nullptr,
-modernize-use-nodiscard,
-modernize-use-nullptr,
-modernize-use-trailing-return-type,
-mpi-*, -mpi-*,
-objc-*, -objc-*,
-performance-enum-size,
-readability-avoid-nested-conditional-operator,
-readability-container-contains,
-readability-container-data-pointer, -readability-container-data-pointer,
-readability-convert-member-functions-to-static, -readability-convert-member-functions-to-static,
-readability-else-after-return, -readability-else-after-return,
@ -82,11 +107,14 @@ Checks: >-
-readability-isolate-declaration, -readability-isolate-declaration,
-readability-magic-numbers, -readability-magic-numbers,
-readability-make-member-function-const, -readability-make-member-function-const,
-readability-named-parameter,
-readability-redundant-casting,
-readability-redundant-inline-specifier,
-readability-redundant-member-init,
-readability-redundant-string-init, -readability-redundant-string-init,
-readability-uppercase-literal-suffix, -readability-uppercase-literal-suffix,
-readability-use-anyofallof, -readability-use-anyofallof,
WarningsAsErrors: '*' WarningsAsErrors: '*'
AnalyzeTemporaryDtors: false
FormatStyle: google FormatStyle: google
CheckOptions: CheckOptions:
- key: google-readability-function-size.StatementThreshold - key: google-readability-function-size.StatementThreshold

View File

@ -46,7 +46,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.9.0 uses: docker/build-push-action@v6.13.0
env: env:
DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false DOCKER_BUILD_RECORD_UPLOAD: false
@ -72,7 +72,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.9.0 uses: docker/build-push-action@v6.13.0
env: env:
DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false DOCKER_BUILD_RECORD_UPLOAD: false

View File

@ -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@v5.3.0 uses: actions/setup-python@v5.4.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.1.2 uses: actions/cache/restore@v4.2.0
with: with:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length

View File

@ -23,7 +23,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.3.0 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.11" python-version: "3.11"

View File

@ -42,13 +42,13 @@ jobs:
steps: steps:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.3.0 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.9" python-version: "3.9"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.7.1 uses: docker/setup-buildx-action@v3.8.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3.2.0 uses: docker/setup-qemu-action@v3.3.0
- name: Set TAG - name: Set TAG
run: | run: |

View File

@ -13,6 +13,7 @@ on:
- ".github/workflows/ci.yml" - ".github/workflows/ci.yml"
- "!.yamllint" - "!.yamllint"
- "!.github/dependabot.yml" - "!.github/dependabot.yml"
- "!docker/**"
merge_group: merge_group:
permissions: permissions:
@ -30,7 +31,7 @@ concurrency:
jobs: jobs:
common: common:
name: Create common environment name: Create common environment
runs-on: ubuntu-latest runs-on: ubuntu-24.04
outputs: outputs:
cache-key: ${{ steps.cache-key.outputs.key }} cache-key: ${{ steps.cache-key.outputs.key }}
steps: steps:
@ -41,12 +42,12 @@ jobs:
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v5.3.0 uses: actions/setup-python@v5.4.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.1.2 uses: actions/cache@v4.2.0
with: with:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
@ -62,7 +63,7 @@ jobs:
black: black:
name: Check black name: Check black
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -83,7 +84,7 @@ jobs:
flake8: flake8:
name: Check flake8 name: Check flake8
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -104,7 +105,7 @@ jobs:
pylint: pylint:
name: Check pylint name: Check pylint
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -125,7 +126,7 @@ jobs:
pyupgrade: pyupgrade:
name: Check pyupgrade name: Check pyupgrade
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -146,7 +147,7 @@ jobs:
ci-custom: ci-custom:
name: Run script/ci-custom name: Run script/ci-custom
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -219,13 +220,13 @@ jobs:
. venv/bin/activate . venv/bin/activate
pytest -vv --cov-report=xml --tb=native tests pytest -vv --cov-report=xml --tb=native tests
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
clang-format: clang-format:
name: Check clang-format name: Check clang-format
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
steps: steps:
@ -251,7 +252,7 @@ jobs:
clang-tidy: clang-tidy:
name: ${{ matrix.name }} name: ${{ matrix.name }}
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- black - black
@ -302,23 +303,18 @@ jobs:
- name: Cache platformio - name: Cache platformio
if: github.ref == 'refs/heads/dev' if: github.ref == 'refs/heads/dev'
uses: actions/cache@v4.1.2 uses: actions/cache@v4.2.0
with: with:
path: ~/.platformio path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }} key: platformio-${{ matrix.pio_cache_key }}
- name: Cache platformio - name: Cache platformio
if: github.ref != 'refs/heads/dev' if: github.ref != 'refs/heads/dev'
uses: actions/cache/restore@v4.1.2 uses: actions/cache/restore@v4.2.0
with: with:
path: ~/.platformio path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }} key: platformio-${{ matrix.pio_cache_key }}
- name: Install clang-tidy
run: |
sudo apt-get update
sudo apt-get install clang-tidy-14
- name: Register problem matchers - name: Register problem matchers
run: | run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json" echo "::add-matcher::.github/workflows/matchers/gcc.json"
@ -345,7 +341,7 @@ jobs:
if: always() if: always()
list-components: list-components:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
@ -387,7 +383,7 @@ jobs:
test-build-components: test-build-components:
name: Component test ${{ matrix.file }} name: Component test ${{ matrix.file }}
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- list-components - list-components
@ -421,7 +417,7 @@ jobs:
test-build-components-splitter: test-build-components-splitter:
name: Split components for testing into 20 groups maximum name: Split components for testing into 20 groups maximum
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- list-components - list-components
@ -439,7 +435,7 @@ jobs:
test-build-components-split: test-build-components-split:
name: Test split components name: Test split components
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- list-components - list-components
@ -483,7 +479,7 @@ jobs:
ci-status: ci-status:
name: CI Status name: CI Status
runs-on: ubuntu-latest runs-on: ubuntu-24.04
needs: needs:
- common - common
- black - black

View File

@ -53,7 +53,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.3.0 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.x" python-version: "3.x"
- name: Set up python environment - name: Set up python environment
@ -65,7 +65,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.12.2 uses: pypa/gh-action-pypi-publish@v1.12.4
deploy-docker: deploy-docker:
name: Build ESPHome ${{ matrix.platform }} name: Build ESPHome ${{ matrix.platform }}
@ -85,15 +85,15 @@ jobs:
steps: steps:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.3.0 uses: actions/setup-python@v5.4.0
with: with:
python-version: "3.9" python-version: "3.9"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.7.1 uses: docker/setup-buildx-action@v3.8.0
- name: Set up QEMU - name: Set up QEMU
if: matrix.platform != 'linux/amd64' if: matrix.platform != 'linux/amd64'
uses: docker/setup-qemu-action@v3.2.0 uses: docker/setup-qemu-action@v3.3.0
- name: Log in to docker hub - name: Log in to docker hub
uses: docker/login-action@v3.3.0 uses: docker/login-action@v3.3.0
@ -141,7 +141,7 @@ jobs:
echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
- name: Upload digests - name: Upload digests
uses: actions/upload-artifact@v4.4.3 uses: actions/upload-artifact@v4.6.0
with: with:
name: digests-${{ steps.sanitize.outputs.name }} name: digests-${{ steps.sanitize.outputs.name }}
path: /tmp/digests path: /tmp/digests
@ -184,7 +184,7 @@ jobs:
merge-multiple: true merge-multiple: true
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.7.1 uses: docker/setup-buildx-action@v3.8.0
- name: Log in to docker hub - name: Log in to docker hub
if: matrix.registry == 'dockerhub' if: matrix.registry == 'dockerhub'

View File

@ -17,7 +17,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v9.0.0 - uses: actions/stale@v9.1.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@v9.0.0 - uses: actions/stale@v9.1.0
with: with:
days-before-pr-stale: -1 days-before-pr-stale: -1
days-before-pr-close: -1 days-before-pr-close: -1

View File

@ -22,7 +22,7 @@ jobs:
path: lib/home-assistant path: lib/home-assistant
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5.3.0 uses: actions/setup-python@v5.4.0
with: with:
python-version: 3.12 python-version: 3.12
@ -36,7 +36,7 @@ jobs:
python ./script/sync-device_class.py python ./script/sync-device_class.py
- name: Commit changes - name: Commit changes
uses: peter-evans/create-pull-request@v7.0.5 uses: peter-evans/create-pull-request@v7.0.6
with: with:
commit-message: "Synchronise Device Classes from Home Assistant" commit-message: "Synchronise Device Classes from Home Assistant"
committer: esphomebot <esphome@nabucasa.com> committer: esphomebot <esphome@nabucasa.com>

View File

@ -11,14 +11,6 @@ repos:
args: [--fix] args: [--fix]
# Run the formatter. # Run the formatter.
- id: ruff-format - id: ruff-format
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.4.2
hooks:
- id: black
args:
- --safe
- --quiet
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 6.1.0 rev: 6.1.0
hooks: hooks:
@ -53,6 +45,6 @@ repos:
hooks: hooks:
- id: pylint - id: pylint
name: pylint name: pylint
entry: script/run-in-env.sh pylint entry: python script/run-in-env pylint
language: script language: system
types: [python] types: [python]

View File

@ -49,6 +49,7 @@ esphome/components/atc_mithermometer/* @ahpohl
esphome/components/atm90e26/* @danieltwagner esphome/components/atm90e26/* @danieltwagner
esphome/components/atm90e32/* @circuitsetup @descipher esphome/components/atm90e32/* @circuitsetup @descipher
esphome/components/audio/* @kahrendt esphome/components/audio/* @kahrendt
esphome/components/audio_adc/* @kbx81
esphome/components/audio_dac/* @kbx81 esphome/components/audio_dac/* @kbx81
esphome/components/axs15231/* @clydebarrow esphome/components/axs15231/* @clydebarrow
esphome/components/b_parasite/* @rbaron esphome/components/b_parasite/* @rbaron
@ -131,6 +132,9 @@ esphome/components/ens160_base/* @latonita @vincentscode
esphome/components/ens160_i2c/* @latonita esphome/components/ens160_i2c/* @latonita
esphome/components/ens160_spi/* @latonita esphome/components/ens160_spi/* @latonita
esphome/components/ens210/* @itn3rd77 esphome/components/ens210/* @itn3rd77
esphome/components/es7210/* @kahrendt
esphome/components/es7243e/* @kbx81
esphome/components/es8156/* @kbx81
esphome/components/es8311/* @kahrendt @kroimon esphome/components/es8311/* @kahrendt @kroimon
esphome/components/esp32/* @esphome/core esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @Rapsssito @jesserockz esphome/components/esp32_ble/* @Rapsssito @jesserockz
@ -144,6 +148,7 @@ esphome/components/esp32_rmt_led_strip/* @jesserockz
esphome/components/esp8266/* @esphome/core esphome/components/esp8266/* @esphome/core
esphome/components/ethernet_info/* @gtjadsonsantos esphome/components/ethernet_info/* @gtjadsonsantos
esphome/components/event/* @nohat esphome/components/event/* @nohat
esphome/components/event_emitter/* @Rapsssito
esphome/components/exposure_notifications/* @OttoWinter esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb esphome/components/ezo/* @ssieb
esphome/components/ezo_pmp/* @carlos-sarmiento esphome/components/ezo_pmp/* @carlos-sarmiento
@ -179,6 +184,7 @@ esphome/components/haier/text_sensor/* @paveldn
esphome/components/havells_solar/* @sourabhjaiswal esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann esphome/components/hbridge/light/* @DotNetDann
esphome/components/hbridge/switch/* @dwmw2
esphome/components/he60r/* @clydebarrow esphome/components/he60r/* @clydebarrow
esphome/components/heatpumpir/* @rob-deutsch esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hitachi_ac424/* @sourabhjaiswal
@ -301,7 +307,7 @@ esphome/components/noblex/* @AGalfra
esphome/components/npi19/* @bakerkj esphome/components/npi19/* @bakerkj
esphome/components/number/* @esphome/core esphome/components/number/* @esphome/core
esphome/components/one_wire/* @ssieb esphome/components/one_wire/* @ssieb
esphome/components/online_image/* @guillempages esphome/components/online_image/* @clydebarrow @guillempages
esphome/components/opentherm/* @olegtarasov esphome/components/opentherm/* @olegtarasov
esphome/components/ota/* @esphome/core esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core esphome/components/output/* @esphome/core
@ -337,7 +343,6 @@ esphome/components/radon_eye_rd200/* @jeffeb3
esphome/components/rc522/* @glmnet esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet esphome/components/rc522_spi/* @glmnet
esphome/components/resistance_sampler/* @jesserockz
esphome/components/restart/* @esphome/core esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz esphome/components/rgbct/* @jesserockz
@ -354,6 +359,8 @@ esphome/components/sdl/* @clydebarrow
esphome/components/sdm_meter/* @jesserockz @polyfaces esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath esphome/components/sdp3x/* @Azimath
esphome/components/seeed_mr24hpc1/* @limengdu esphome/components/seeed_mr24hpc1/* @limengdu
esphome/components/seeed_mr60bha2/* @limengdu
esphome/components/seeed_mr60fda2/* @limengdu
esphome/components/selec_meter/* @sourabhjaiswal esphome/components/selec_meter/* @sourabhjaiswal
esphome/components/select/* @esphome/core esphome/components/select/* @esphome/core
esphome/components/sen0321/* @notjj esphome/components/sen0321/* @notjj
@ -408,6 +415,7 @@ esphome/components/substitutions/* @esphome/core
esphome/components/sun/* @OttoWinter esphome/components/sun/* @OttoWinter
esphome/components/sun_gtil2/* @Mat931 esphome/components/sun_gtil2/* @Mat931
esphome/components/switch/* @esphome/core esphome/components/switch/* @esphome/core
esphome/components/switch/binary_sensor/* @ssieb
esphome/components/t6615/* @tylermenezes esphome/components/t6615/* @tylermenezes
esphome/components/tc74/* @sethgirvan esphome/components/tc74/* @sethgirvan
esphome/components/tca9548a/* @andreashergert1984 esphome/components/tca9548a/* @andreashergert1984

View File

@ -1,6 +1,11 @@
# ESPHome [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/) # ESPHome [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/)
[![ESPHome Logo](https://esphome.io/_images/logo-text.png)](https://esphome.io/) <a href="https://esphome.io/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://esphome.io/_static/logo-text-on-dark.svg", alt="ESPHome Logo">
<img src="https://esphome.io/_static/logo-text-on-light.svg" alt="ESPHome Logo">
</picture>
</a>
**Documentation:** https://esphome.io/ **Documentation:** https://esphome.io/

View File

@ -29,7 +29,7 @@ RUN \
# Use pinned versions so that we get updates with build caching # Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
python3-pip=23.0.1+dfsg-1 \ python3-pip=23.0.1+dfsg-1 \
python3-setuptools=66.1.1-1 \ python3-setuptools=66.1.1-1+deb12u1 \
python3-venv=3.11.2-1+b1 \ python3-venv=3.11.2-1+b1 \
python3-wheel=0.38.4-2 \ python3-wheel=0.38.4-2 \
iputils-ping=3:20221126-1+deb12u1 \ iputils-ping=3:20221126-1+deb12u1 \
@ -99,15 +99,17 @@ BUILD_DEPS="
libfreetype-dev=2.12.1+dfsg-5+deb12u3 libfreetype-dev=2.12.1+dfsg-5+deb12u3
libssl-dev=3.0.15-1~deb12u1 libssl-dev=3.0.15-1~deb12u1
libffi-dev=3.4.4-1 libffi-dev=3.4.4-1
libopenjp2-7=2.5.0-2
libtiff6=4.5.0-6+deb12u1
cargo=0.66.0+ds1-1 cargo=0.66.0+ds1-1
pkg-config=1.8.1-1 pkg-config=1.8.1-1
" "
LIB_DEPS="
libtiff6=4.5.0-6+deb12u1
libopenjp2-7=2.5.0-2
"
if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ] || [ "$TARGETARCH$TARGETVARIANT" = "armv7" ] if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ] || [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]
then then
apt-get update apt-get update
apt-get install -y --no-install-recommends $BUILD_DEPS apt-get install -y --no-install-recommends $BUILD_DEPS $LIB_DEPS
fi fi
CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo
@ -161,6 +163,18 @@ ENTRYPOINT ["/entrypoint.sh"]
CMD ["dashboard", "/config"] CMD ["dashboard", "/config"]
ARG BUILD_VERSION=dev
# Labels
LABEL \
org.opencontainers.image.authors="The ESPHome Authors" \
org.opencontainers.image.title="ESPHome" \
org.opencontainers.image.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
org.opencontainers.image.url="https://esphome.io/" \
org.opencontainers.image.documentation="https://esphome.io/" \
org.opencontainers.image.source="https://github.com/esphome/esphome" \
org.opencontainers.image.licenses="ESPHome" \
org.opencontainers.image.version=${BUILD_VERSION}
# ======================= hassio-type image ======================= # ======================= hassio-type image =======================
@ -192,7 +206,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
# Labels # Labels
LABEL \ LABEL \
io.hass.name="ESPHome" \ io.hass.name="ESPHome" \
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ io.hass.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
io.hass.type="addon" \ io.hass.type="addon" \
io.hass.version="${BUILD_VERSION}" io.hass.version="${BUILD_VERSION}"
# io.hass.arch is inherited from addon-debian-base # io.hass.arch is inherited from addon-debian-base
@ -207,17 +221,22 @@ ENV \
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
RUN \ RUN \
apt-get update \ curl -L https://apt.llvm.org/llvm-snapshot.gpg.key -o /etc/apt/trusted.gpg.d/apt.llvm.org.asc \
&& echo "deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-18 main" > /etc/apt/sources.list.d/llvm.sources.list \
&& apt-get update \
# Use pinned versions so that we get updates with build caching # Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
clang-format-13=1:13.0.1-11+b2 \ clang-format-13=1:13.0.1-11+b2 \
clang-tidy-14=1:14.0.6-12 \
patch=2.7.6-7 \ patch=2.7.6-7 \
software-properties-common=0.99.30-4.1~deb12u1 \ software-properties-common=0.99.30-4.1~deb12u1 \
nano=7.2-1+deb12u1 \ nano=7.2-1+deb12u1 \
build-essential=12.9 \ build-essential=12.9 \
python3-dev=3.11.2-1+b1 \ python3-dev=3.11.2-1+b1 \
&& rm -rf \ && if [ "$TARGETARCH$TARGETVARIANT" != "armv7" ]; then \
# move this up after armv7 is retired
apt-get install -y --no-install-recommends clang-tidy-18=1:18.1.8~++20240731024826+3b5b5c1ec4a3-1~exp1~20240731144843.145 ; \
fi; \
rm -rf \
/tmp/* \ /tmp/* \
/var/{cache,log}/* \ /var/{cache,log}/* \
/var/lib/apt/lists/* /var/lib/apt/lists/*

View File

@ -20,6 +20,8 @@ from esphome.const import (
CONF_DEASSERT_RTS_DTR, CONF_DEASSERT_RTS_DTR,
CONF_DISABLED, CONF_DISABLED,
CONF_ESPHOME, CONF_ESPHOME,
CONF_LEVEL,
CONF_LOG_TOPIC,
CONF_LOGGER, CONF_LOGGER,
CONF_MDNS, CONF_MDNS,
CONF_MQTT, CONF_MQTT,
@ -30,6 +32,7 @@ from esphome.const import (
CONF_PLATFORMIO_OPTIONS, CONF_PLATFORMIO_OPTIONS,
CONF_PORT, CONF_PORT,
CONF_SUBSTITUTIONS, CONF_SUBSTITUTIONS,
CONF_TOPIC,
PLATFORM_BK72XX, PLATFORM_BK72XX,
PLATFORM_ESP32, PLATFORM_ESP32,
PLATFORM_ESP8266, PLATFORM_ESP8266,
@ -95,8 +98,12 @@ def choose_upload_log_host(
options.append((f"Over The Air ({CORE.address})", CORE.address)) options.append((f"Over The Air ({CORE.address})", CORE.address))
if default == "OTA": if default == "OTA":
return CORE.address return CORE.address
if show_mqtt and CONF_MQTT in CORE.config: if (
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT")) show_mqtt
and (mqtt_config := CORE.config.get(CONF_MQTT))
and mqtt_logging_enabled(mqtt_config)
):
options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT"))
if default == "OTA": if default == "OTA":
return "MQTT" return "MQTT"
if default is not None: if default is not None:
@ -106,6 +113,17 @@ def choose_upload_log_host(
return choose_prompt(options, purpose=purpose) return choose_prompt(options, purpose=purpose)
def mqtt_logging_enabled(mqtt_config):
log_topic = mqtt_config[CONF_LOG_TOPIC]
if log_topic is None:
return False
if CONF_TOPIC not in log_topic:
return False
if log_topic.get(CONF_LEVEL, None) == "NONE":
return False
return True
def get_port_type(port): def get_port_type(port):
if port.startswith("/") or port.startswith("COM"): if port.startswith("/") or port.startswith("COM"):
return "SERIAL" return "SERIAL"
@ -345,7 +363,7 @@ def upload_program(config, args, host):
from esphome import espota2 from esphome import espota2
remote_port = ota_conf[CONF_PORT] remote_port = int(ota_conf[CONF_PORT])
password = ota_conf.get(CONF_PASSWORD, "") password = ota_conf.get(CONF_PASSWORD, "")
if ( if (
@ -740,6 +758,14 @@ def parse_args(argv):
options_parser.add_argument( options_parser.add_argument(
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true" "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
) )
options_parser.add_argument(
"-l",
"--log-level",
help="Set the log level.",
default=os.getenv("ESPHOME_LOG_LEVEL", "INFO"),
action="store",
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
)
options_parser.add_argument( options_parser.add_argument(
"--dashboard", help=argparse.SUPPRESS, action="store_true" "--dashboard", help=argparse.SUPPRESS, action="store_true"
) )
@ -969,11 +995,16 @@ def run_esphome(argv):
args = parse_args(argv) args = parse_args(argv)
CORE.dashboard = args.dashboard CORE.dashboard = args.dashboard
# Override log level if verbose is set
if args.verbose:
args.log_level = "DEBUG"
elif args.quiet:
args.log_level = "CRITICAL"
setup_log( setup_log(
args.verbose, log_level=args.log_level,
args.quiet,
# Show timestamp for dashboard access logs # Show timestamp for dashboard access logs
args.command == "dashboard", include_timestamp=args.command == "dashboard",
) )
if args.command in PRE_CONFIG_ACTIONS: if args.command in PRE_CONFIG_ACTIONS:

View File

@ -1,11 +1,6 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER import esphome.codegen as cg
from esphome.core import CORE
from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32 import get_esp32_variant
from esphome.const import PLATFORM_ESP8266
from esphome.components.esp32.const import ( from esphome.components.esp32.const import (
VARIANT_ESP32, VARIANT_ESP32,
VARIANT_ESP32C2, VARIANT_ESP32C2,
@ -15,6 +10,9 @@ from esphome.components.esp32.const import (
VARIANT_ESP32S2, VARIANT_ESP32S2,
VARIANT_ESP32S3, VARIANT_ESP32S3,
) )
import esphome.config_validation as cv
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266
from esphome.core import CORE
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
@ -102,11 +100,11 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
6: adc1_channel_t.ADC1_CHANNEL_6, 6: adc1_channel_t.ADC1_CHANNEL_6,
}, },
VARIANT_ESP32H2: { VARIANT_ESP32H2: {
0: adc1_channel_t.ADC1_CHANNEL_0, 1: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1, 2: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2, 3: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3, 4: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4, 5: adc1_channel_t.ADC1_CHANNEL_4,
}, },
} }

View File

@ -3,13 +3,12 @@
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h" #include "esphome/components/voltage_sampler/voltage_sampler.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <esp_adc_cal.h> #include <esp_adc_cal.h>
#include "driver/adc.h" #include "driver/adc.h"
#endif #endif // USE_ESP32
namespace esphome { namespace esphome {
namespace adc { namespace adc {
@ -43,7 +42,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
this->channel1_ = ADC1_CHANNEL_MAX; this->channel1_ = ADC1_CHANNEL_MAX;
} }
void set_autorange(bool autorange) { this->autorange_ = autorange; } void set_autorange(bool autorange) { this->autorange_ = autorange; }
#endif #endif // USE_ESP32
/// Update ADC values /// Update ADC values
void update() override; void update() override;
@ -59,11 +58,11 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_ESP8266 #ifdef USE_ESP8266
std::string unique_id() override; std::string unique_id() override;
#endif #endif // USE_ESP8266
#ifdef USE_RP2040 #ifdef USE_RP2040
void set_is_temperature() { this->is_temperature_ = true; } void set_is_temperature() { this->is_temperature_ = true; }
#endif #endif // USE_RP2040
protected: protected:
InternalGPIOPin *pin_; InternalGPIOPin *pin_;
@ -72,7 +71,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_RP2040 #ifdef USE_RP2040
bool is_temperature_{false}; bool is_temperature_{false};
#endif #endif // USE_RP2040
#ifdef USE_ESP32 #ifdef USE_ESP32
adc_atten_t attenuation_{ADC_ATTEN_DB_0}; adc_atten_t attenuation_{ADC_ATTEN_DB_0};
@ -83,8 +82,8 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {}; esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
#else #else
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {}; esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
#endif #endif // ESP_IDF_VERSION_MAJOR
#endif #endif // USE_ESP32
}; };
} // namespace adc } // namespace adc

View File

@ -0,0 +1,24 @@
#include "adc_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc {
static const char *const TAG = "adc.common";
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}
void ADCSensor::set_sample_count(uint8_t sample_count) {
if (sample_count != 0) {
this->sample_count_ = sample_count;
}
}
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
} // namespace adc
} // namespace esphome

View File

@ -1,30 +1,13 @@
#ifdef USE_ESP32
#include "adc_sensor.h" #include "adc_sensor.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#ifdef USE_ESP8266
#ifdef USE_ADC_SENSOR_VCC
#include <Esp.h>
ADC_MODE(ADC_VCC)
#else
#include <Arduino.h>
#endif
#endif
#ifdef USE_RP2040
#ifdef CYW43_USES_VSYS_PIN
#include "pico/cyw43_arch.h"
#endif
#include <hardware/adc.h>
#endif
namespace esphome { namespace esphome {
namespace adc { namespace adc {
static const char *const TAG = "adc"; static const char *const TAG = "adc.esp32";
// 13-bit for S2, 12-bit for all other ESP32 variants
#ifdef USE_ESP32
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1); static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
#ifndef SOC_ADC_RTC_MAX_BITWIDTH #ifndef SOC_ADC_RTC_MAX_BITWIDTH
@ -32,24 +15,15 @@ static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_widt
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13; static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
#else #else
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12; static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
#endif #endif // USE_ESP32_VARIANT_ESP32S2
#endif #endif // SOC_ADC_RTC_MAX_BITWIDTH
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit) static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit) static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;
#endif
#ifdef USE_RP2040 void ADCSensor::setup() {
extern "C"
#endif
void
ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
this->pin_->setup();
#endif
#ifdef USE_ESP32
if (this->channel1_ != ADC1_CHANNEL_MAX) { if (this->channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!this->autorange_) { if (!this->autorange_) {
@ -61,7 +35,6 @@ extern "C"
} }
} }
// load characteristics for each attenuation
for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) { for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2; auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS, auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
@ -79,31 +52,10 @@ extern "C"
break; break;
} }
} }
#endif // USE_ESP32
#ifdef USE_RP2040
static bool initialized = false;
if (!initialized) {
adc_init();
initialized = true;
}
#endif
ESP_LOGCONFIG(TAG, "ADC '%s' setup finished!", this->get_name().c_str());
} }
void ADCSensor::dump_config() { void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this); LOG_SENSOR("", "ADC Sensor", this);
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", this->pin_);
#endif
#endif // USE_ESP8266 || USE_LIBRETINY
#ifdef USE_ESP32
LOG_PIN(" Pin: ", this->pin_); LOG_PIN(" Pin: ", this->pin_);
if (this->autorange_) { if (this->autorange_) {
ESP_LOGCONFIG(TAG, " Attenuation: auto"); ESP_LOGCONFIG(TAG, " Attenuation: auto");
@ -125,55 +77,10 @@ void ADCSensor::dump_config() {
break; break;
} }
} }
#endif // USE_ESP32
#ifdef USE_RP2040
if (this->is_temperature_) {
ESP_LOGCONFIG(TAG, " Pin: Temperature");
} else {
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
}
#endif // USE_RP2040
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}
void ADCSensor::set_sample_count(uint8_t sample_count) {
if (sample_count != 0) {
this->sample_count_ = sample_count;
}
}
#ifdef USE_ESP8266
float ADCSensor::sample() {
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
#ifdef USE_ADC_SENSOR_VCC
raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
raw += analogRead(this->pin_->get_pin()); // NOLINT
#endif
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
if (this->output_raw_) {
return raw;
}
return raw / 1024.0f;
}
#endif
#ifdef USE_ESP32
float ADCSensor::sample() { float ADCSensor::sample() {
if (!this->autorange_) { if (!this->autorange_) {
uint32_t sum = 0; uint32_t sum = 0;
@ -240,93 +147,17 @@ float ADCSensor::sample() {
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]); uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]); uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
uint32_t c12 = std::min(raw12, ADC_HALF); uint32_t c12 = std::min(raw12, ADC_HALF);
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF); uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF); uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF); uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
// max theoretical csum value is 4096*4 = 16384
uint32_t csum = c12 + c6 + c2 + c0; uint32_t csum = c12 + c6 + c2 + c0;
// each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0); uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U); return mv_scaled / (float) (csum * 1000U);
} }
#endif // USE_ESP32
#ifdef USE_RP2040
float ADCSensor::sample() {
if (this->is_temperature_) {
adc_set_temp_sensor_enabled(true);
delay(1);
adc_select_input(4);
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
adc_set_temp_sensor_enabled(false);
if (this->output_raw_) {
return raw;
}
return raw * 3.3f / 4096.0f;
} else {
uint8_t pin = this->pin_->get_pin();
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
// VSYS ADC both share GPIO29
cyw43_thread_enter();
}
#endif // CYW43_USES_VSYS_PIN
adc_gpio_init(pin);
adc_select_input(pin - 26);
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
cyw43_thread_exit();
}
#endif // CYW43_USES_VSYS_PIN
if (this->output_raw_) {
return raw;
}
float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
return raw * 3.3f / 4096.0f * coeff;
}
}
#endif
#ifdef USE_LIBRETINY
float ADCSensor::sample() {
uint32_t raw = 0;
if (this->output_raw_) {
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += analogRead(this->pin_->get_pin()); // NOLINT
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
return raw;
}
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
return raw / 1000.0f;
}
#endif // USE_LIBRETINY
#ifdef USE_ESP8266
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
#endif
} // namespace adc } // namespace adc
} // namespace esphome } // namespace esphome
#endif // USE_ESP32

View File

@ -0,0 +1,58 @@
#ifdef USE_ESP8266
#include "adc_sensor.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#ifdef USE_ADC_SENSOR_VCC
#include <Esp.h>
ADC_MODE(ADC_VCC)
#else
#include <Arduino.h>
#endif // USE_ADC_SENSOR_VCC
namespace esphome {
namespace adc {
static const char *const TAG = "adc.esp8266";
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
this->pin_->setup();
#endif
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::sample() {
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
#ifdef USE_ADC_SENSOR_VCC
raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
raw += analogRead(this->pin_->get_pin()); // NOLINT
#endif // USE_ADC_SENSOR_VCC
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
if (this->output_raw_) {
return raw;
}
return raw / 1024.0f;
}
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
} // namespace adc
} // namespace esphome
#endif // USE_ESP8266

View File

@ -0,0 +1,48 @@
#ifdef USE_LIBRETINY
#include "adc_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace adc {
static const char *const TAG = "adc.libretiny";
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
this->pin_->setup();
#endif // !USE_ADC_SENSOR_VCC
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else // USE_ADC_SENSOR_VCC
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::sample() {
uint32_t raw = 0;
if (this->output_raw_) {
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += analogRead(this->pin_->get_pin()); // NOLINT
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
return raw;
}
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
return raw / 1000.0f;
}
} // namespace adc
} // namespace esphome
#endif // USE_LIBRETINY

View File

@ -0,0 +1,93 @@
#ifdef USE_RP2040
#include "adc_sensor.h"
#include "esphome/core/log.h"
#ifdef CYW43_USES_VSYS_PIN
#include "pico/cyw43_arch.h"
#endif // CYW43_USES_VSYS_PIN
#include <hardware/adc.h>
namespace esphome {
namespace adc {
static const char *const TAG = "adc.rp2040";
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
static bool initialized = false;
if (!initialized) {
adc_init();
initialized = true;
}
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
if (this->is_temperature_) {
ESP_LOGCONFIG(TAG, " Pin: Temperature");
} else {
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
}
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::sample() {
if (this->is_temperature_) {
adc_set_temp_sensor_enabled(true);
delay(1);
adc_select_input(4);
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
adc_set_temp_sensor_enabled(false);
if (this->output_raw_) {
return raw;
}
return raw * 3.3f / 4096.0f;
}
uint8_t pin = this->pin_->get_pin();
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
// VSYS ADC both share GPIO29
cyw43_thread_enter();
}
#endif // CYW43_USES_VSYS_PIN
adc_gpio_init(pin);
adc_select_input(pin - 26);
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
cyw43_thread_exit();
}
#endif // CYW43_USES_VSYS_PIN
if (this->output_raw_) {
return raw;
}
float coeff = pin == PICO_VSYS_PIN ? 3.0f : 1.0f;
return raw * 3.3f / 4096.0f * coeff;
}
} // namespace adc
} // namespace esphome
#endif // USE_RP2040

View File

@ -9,8 +9,6 @@ static const char *const TAG = "ads1115";
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00; static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01; static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
static const uint8_t ADS1115_DATA_RATE_860_SPS = 0b111; // 3300_SPS for ADS1015
void ADS1115Component::setup() { void ADS1115Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADS1115..."); ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
uint16_t value; uint16_t value;
@ -43,9 +41,9 @@ void ADS1115Component::setup() {
config |= 0b0000000100000000; config |= 0b0000000100000000;
} }
// Set data rate - 860 samples per second (we're in singleshot mode) // Set data rate - 860 samples per second
// 0bxxxxxxxx100xxxxx // 0bxxxxxxxx100xxxxx
config |= ADS1115_DATA_RATE_860_SPS << 5; config |= ADS1115_860SPS << 5;
// Set comparator mode - hysteresis // Set comparator mode - hysteresis
// 0bxxxxxxxxxxx0xxxx // 0bxxxxxxxxxxx0xxxx
@ -77,7 +75,7 @@ void ADS1115Component::dump_config() {
} }
} }
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
ADS1115Resolution resolution) { ADS1115Resolution resolution, ADS1115Samplerate samplerate) {
uint16_t config = this->prev_config_; uint16_t config = this->prev_config_;
// Multiplexer // Multiplexer
// 0bxBBBxxxxxxxxxxxx // 0bxBBBxxxxxxxxxxxx
@ -89,6 +87,11 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
config &= 0b1111000111111111; config &= 0b1111000111111111;
config |= (gain & 0b111) << 9; config |= (gain & 0b111) << 9;
// Sample rate
// 0bxxxxxxxxBBBxxxxx
config &= 0b1111111100011111;
config |= (samplerate & 0b111) << 5;
if (!this->continuous_mode_) { if (!this->continuous_mode_) {
// Start conversion // Start conversion
config |= 0b1000000000000000; config |= 0b1000000000000000;
@ -101,8 +104,54 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
} }
this->prev_config_ = config; this->prev_config_ = config;
// about 1.2 ms with 860 samples per second // Delay calculated as: ceil((1000/SPS)+.5)
delay(2); if (resolution == ADS1015_12_BITS) {
switch (samplerate) {
case ADS1115_8SPS:
delay(9);
break;
case ADS1115_16SPS:
delay(5);
break;
case ADS1115_32SPS:
delay(3);
break;
case ADS1115_64SPS:
case ADS1115_128SPS:
delay(2);
break;
default:
delay(1);
break;
}
} else {
switch (samplerate) {
case ADS1115_8SPS:
delay(126); // NOLINT
break;
case ADS1115_16SPS:
delay(63); // NOLINT
break;
case ADS1115_32SPS:
delay(32);
break;
case ADS1115_64SPS:
delay(17);
break;
case ADS1115_128SPS:
delay(9);
break;
case ADS1115_250SPS:
delay(5);
break;
case ADS1115_475SPS:
delay(3);
break;
case ADS1115_860SPS:
delay(2);
break;
}
}
// in continuous mode, conversion will always be running, rely on the delay // in continuous mode, conversion will always be running, rely on the delay
// to ensure conversion is taking place with the correct settings // to ensure conversion is taking place with the correct settings

View File

@ -33,6 +33,17 @@ enum ADS1115Resolution {
ADS1015_12_BITS = 12, ADS1015_12_BITS = 12,
}; };
enum ADS1115Samplerate {
ADS1115_8SPS = 0b000,
ADS1115_16SPS = 0b001,
ADS1115_32SPS = 0b010,
ADS1115_64SPS = 0b011,
ADS1115_128SPS = 0b100,
ADS1115_250SPS = 0b101,
ADS1115_475SPS = 0b110,
ADS1115_860SPS = 0b111
};
class ADS1115Component : public Component, public i2c::I2CDevice { class ADS1115Component : public Component, public i2c::I2CDevice {
public: public:
void setup() override; void setup() override;
@ -42,7 +53,8 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; } void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
/// Helper method to request a measurement from a sensor. /// Helper method to request a measurement from a sensor.
float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution); float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution,
ADS1115Samplerate samplerate);
protected: protected:
uint16_t prev_config_{0}; uint16_t prev_config_{0};

View File

@ -5,6 +5,7 @@ from esphome.const import (
CONF_GAIN, CONF_GAIN,
CONF_MULTIPLEXER, CONF_MULTIPLEXER,
CONF_RESOLUTION, CONF_RESOLUTION,
CONF_SAMPLE_RATE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
@ -43,6 +44,17 @@ RESOLUTION = {
"12_BITS": ADS1115Resolution.ADS1015_12_BITS, "12_BITS": ADS1115Resolution.ADS1015_12_BITS,
} }
ADS1115Samplerate = ads1115_ns.enum("ADS1115Samplerate")
SAMPLERATE = {
"8": ADS1115Samplerate.ADS1115_8SPS,
"16": ADS1115Samplerate.ADS1115_16SPS,
"32": ADS1115Samplerate.ADS1115_32SPS,
"64": ADS1115Samplerate.ADS1115_64SPS,
"128": ADS1115Samplerate.ADS1115_128SPS,
"250": ADS1115Samplerate.ADS1115_250SPS,
"475": ADS1115Samplerate.ADS1115_475SPS,
"860": ADS1115Samplerate.ADS1115_860SPS,
}
ADS1115Sensor = ads1115_ns.class_( ADS1115Sensor = ads1115_ns.class_(
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler "ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
@ -64,6 +76,9 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum( cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
RESOLUTION, upper=True, space="_" RESOLUTION, upper=True, space="_"
), ),
cv.Optional(CONF_SAMPLE_RATE, default="860"): cv.enum(
SAMPLERATE, string=True
),
} }
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s"))
@ -79,3 +94,4 @@ async def to_code(config):
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
cg.add(var.set_gain(config[CONF_GAIN])) cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_resolution(config[CONF_RESOLUTION])) cg.add(var.set_resolution(config[CONF_RESOLUTION]))
cg.add(var.set_samplerate(config[CONF_SAMPLE_RATE]))

View File

@ -8,7 +8,7 @@ namespace ads1115 {
static const char *const TAG = "ads1115.sensor"; static const char *const TAG = "ads1115.sensor";
float ADS1115Sensor::sample() { float ADS1115Sensor::sample() {
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_); return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
} }
void ADS1115Sensor::update() { void ADS1115Sensor::update() {
@ -24,6 +24,7 @@ void ADS1115Sensor::dump_config() {
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_); ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_); ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_); ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
ESP_LOGCONFIG(TAG, " Sample rate: %u", this->samplerate_);
} }
} // namespace ads1115 } // namespace ads1115

View File

@ -21,6 +21,7 @@ class ADS1115Sensor : public sensor::Sensor,
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
void set_gain(ADS1115Gain gain) { this->gain_ = gain; } void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; } void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
void set_samplerate(ADS1115Samplerate samplerate) { this->samplerate_ = samplerate; }
float sample() override; float sample() override;
void dump_config() override; void dump_config() override;
@ -29,6 +30,7 @@ class ADS1115Sensor : public sensor::Sensor,
ADS1115Multiplexer multiplexer_; ADS1115Multiplexer multiplexer_;
ADS1115Gain gain_; ADS1115Gain gain_;
ADS1115Resolution resolution_; ADS1115Resolution resolution_;
ADS1115Samplerate samplerate_;
}; };
} // namespace ads1115 } // namespace ads1115

View File

@ -72,10 +72,9 @@ void AlarmControlPanelCall::validate_() {
this->state_.reset(); this->state_.reset();
return; return;
} }
if (state == ACP_STATE_DISARMED && if (state == ACP_STATE_DISARMED && !this->parent_->is_state_armed(this->parent_->get_state()) &&
!(this->parent_->is_state_armed(this->parent_->get_state()) || this->parent_->get_state() != ACP_STATE_PENDING && this->parent_->get_state() != ACP_STATE_ARMING &&
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_ARMING || this->parent_->get_state() != ACP_STATE_TRIGGERED) {
this->parent_->get_state() == ACP_STATE_TRIGGERED)) {
ESP_LOGW(TAG, "Cannot disarm when not armed"); ESP_LOGW(TAG, "Cannot disarm when not armed");
this->state_.reset(); this->state_.reset();
return; return;

View File

@ -1,29 +1,10 @@
import logging import logging
from esphome import automation, core from esphome import automation
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import font
import esphome.components.image as espImage import esphome.components.image as espImage
from esphome.components.image import (
CONF_USE_TRANSPARENCY,
LOCAL_SCHEMA,
SOURCE_LOCAL,
SOURCE_WEB,
WEB_SCHEMA,
)
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import CONF_ID, CONF_REPEAT
CONF_FILE,
CONF_ID,
CONF_PATH,
CONF_RAW_DATA_ID,
CONF_REPEAT,
CONF_RESIZE,
CONF_SOURCE,
CONF_TYPE,
CONF_URL,
)
from esphome.core import CORE, HexInt
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -31,6 +12,7 @@ AUTO_LOAD = ["image"]
CODEOWNERS = ["@syndlex"] CODEOWNERS = ["@syndlex"]
DEPENDENCIES = ["display"] DEPENDENCIES = ["display"]
MULTI_CONF = True MULTI_CONF = True
MULTI_CONF_NO_DEFAULT = True
CONF_LOOP = "loop" CONF_LOOP = "loop"
CONF_START_FRAME = "start_frame" CONF_START_FRAME = "start_frame"
@ -52,86 +34,19 @@ SetFrameAction = animation_ns.class_(
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_) "AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
) )
TYPED_FILE_SCHEMA = cv.typed_schema( CONFIG_SCHEMA = espImage.IMAGE_SCHEMA.extend(
{ {
SOURCE_LOCAL: LOCAL_SCHEMA, cv.Required(CONF_ID): cv.declare_id(Animation_),
SOURCE_WEB: WEB_SCHEMA, cv.Optional(CONF_LOOP): cv.All(
},
key=CONF_SOURCE,
)
def _file_schema(value):
if isinstance(value, str):
return validate_file_shorthand(value)
return TYPED_FILE_SCHEMA(value)
FILE_SCHEMA = cv.Schema(_file_schema)
def validate_file_shorthand(value):
value = cv.string_strict(value)
if value.startswith("http://") or value.startswith("https://"):
return FILE_SCHEMA(
{ {
CONF_SOURCE: SOURCE_WEB, cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
CONF_URL: value, cv.Optional(CONF_END_FRAME): cv.positive_int,
cv.Optional(CONF_REPEAT): cv.positive_int,
} }
) ),
return FILE_SCHEMA( },
{
CONF_SOURCE: SOURCE_LOCAL,
CONF_PATH: value,
}
)
def validate_cross_dependencies(config):
"""
Validate fields whose possible values depend on other fields.
For example, validate that explicitly transparent image types
have "use_transparency" set to True.
Also set the default value for those kind of dependent fields.
"""
image_type = config[CONF_TYPE]
is_transparent_type = image_type in ["TRANSPARENT_BINARY", "RGBA"]
# If the use_transparency option was not specified, set the default depending on the image type
if CONF_USE_TRANSPARENCY not in config:
config[CONF_USE_TRANSPARENCY] = is_transparent_type
if is_transparent_type and not config[CONF_USE_TRANSPARENCY]:
raise cv.Invalid(f"Image type {image_type} must always be transparent.")
return config
ANIMATION_SCHEMA = cv.Schema(
cv.All(
{
cv.Required(CONF_ID): cv.declare_id(Animation_),
cv.Required(CONF_FILE): FILE_SCHEMA,
cv.Optional(CONF_RESIZE): cv.dimensions,
cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
espImage.IMAGE_TYPE, upper=True
),
# Not setting default here on purpose; the default depends on the image type,
# and thus will be set in the "validate_cross_dependencies" validator.
cv.Optional(CONF_USE_TRANSPARENCY): cv.boolean,
cv.Optional(CONF_LOOP): cv.All(
{
cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
cv.Optional(CONF_END_FRAME): cv.positive_int,
cv.Optional(CONF_REPEAT): cv.positive_int,
}
),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
},
validate_cross_dependencies,
)
) )
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
NEXT_FRAME_SCHEMA = automation.maybe_simple_id( NEXT_FRAME_SCHEMA = automation.maybe_simple_id(
{ {
@ -165,180 +80,26 @@ async def animation_action_to_code(config, action_id, template_arg, args):
async def to_code(config): async def to_code(config):
from PIL import Image (
prog_arr,
width,
height,
image_type,
trans_value,
frame_count,
) = await espImage.write_image(config, all_frames=True)
conf_file = config[CONF_FILE]
if conf_file[CONF_SOURCE] == SOURCE_LOCAL:
path = CORE.relative_config_path(conf_file[CONF_PATH])
elif conf_file[CONF_SOURCE] == SOURCE_WEB:
path = espImage.compute_local_image_path(conf_file).as_posix()
else:
raise core.EsphomeError(f"Unknown animation source: {conf_file[CONF_SOURCE]}")
try:
image = Image.open(path)
except Exception as e:
raise core.EsphomeError(f"Could not load image file {path}: {e}")
width, height = image.size
frames = image.n_frames
if CONF_RESIZE in config:
new_width_max, new_height_max = config[CONF_RESIZE]
ratio = min(new_width_max / width, new_height_max / height)
width, height = int(width * ratio), int(height * ratio)
elif width > 500 or height > 500:
_LOGGER.warning(
'The image "%s" you requested is very big. Please consider'
" using the resize parameter.",
path,
)
transparent = config[CONF_USE_TRANSPARENCY]
if config[CONF_TYPE] == "GRAYSCALE":
data = [0 for _ in range(height * width * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("LA", dither=Image.Dither.NONE)
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
)
for pix, a in pixels:
if transparent:
if pix == 1:
pix = 0
if a < 0x80:
pix = 1
data[pos] = pix
pos += 1
elif config[CONF_TYPE] == "RGBA":
data = [0 for _ in range(height * width * 4 * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("RGBA")
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
)
for pix in pixels:
data[pos] = pix[0]
pos += 1
data[pos] = pix[1]
pos += 1
data[pos] = pix[2]
pos += 1
data[pos] = pix[3]
pos += 1
elif config[CONF_TYPE] == "RGB24":
data = [0 for _ in range(height * width * 3 * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("RGBA")
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
)
for r, g, b, a in pixels:
if transparent:
if r == 0 and g == 0 and b == 1:
b = 0
if a < 0x80:
r = 0
g = 0
b = 1
data[pos] = r
pos += 1
data[pos] = g
pos += 1
data[pos] = b
pos += 1
elif config[CONF_TYPE] in ["RGB565", "TRANSPARENT_IMAGE"]:
bytes_per_pixel = 3 if transparent else 2
data = [0 for _ in range(height * width * bytes_per_pixel * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("RGBA")
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
)
for r, g, b, a in pixels:
R = r >> 3
G = g >> 2
B = b >> 3
rgb = (R << 11) | (G << 5) | B
data[pos] = rgb >> 8
pos += 1
data[pos] = rgb & 0xFF
pos += 1
if transparent:
data[pos] = a
pos += 1
elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]:
width8 = ((width + 7) // 8) * 8
data = [0 for _ in range((height * width8 // 8) * frames)]
for frameIndex in range(frames):
image.seek(frameIndex)
if transparent:
alpha = image.split()[-1]
has_alpha = alpha.getextrema()[0] < 0xFF
else:
has_alpha = False
frame = image.convert("1", dither=Image.Dither.NONE)
if CONF_RESIZE in config:
frame = frame.resize([width, height])
if transparent:
alpha = alpha.resize([width, height])
for x, y in [(i, j) for i in range(width) for j in range(height)]:
if transparent and has_alpha:
if not alpha.getpixel((x, y)):
continue
elif frame.getpixel((x, y)):
continue
pos = x + y * width8 + (height * width8 * frameIndex)
data[pos // 8] |= 0x80 >> (pos % 8)
else:
raise core.EsphomeError(
f"Animation f{config[CONF_ID]} has not supported type {config[CONF_TYPE]}."
)
rhs = [HexInt(x) for x in data]
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
var = cg.new_Pvariable( var = cg.new_Pvariable(
config[CONF_ID], config[CONF_ID],
prog_arr, prog_arr,
width, width,
height, height,
frames, frame_count,
espImage.IMAGE_TYPE[config[CONF_TYPE]], image_type,
trans_value,
) )
cg.add(var.set_transparency(transparent))
if loop_config := config.get(CONF_LOOP): if loop_config := config.get(CONF_LOOP):
start = loop_config[CONF_START_FRAME] start = loop_config[CONF_START_FRAME]
end = loop_config.get(CONF_END_FRAME, frames) end = loop_config.get(CONF_END_FRAME, frame_count)
count = loop_config.get(CONF_REPEAT, -1) count = loop_config.get(CONF_REPEAT, -1)
cg.add(var.set_loop(start, end, count)) cg.add(var.set_loop(start, end, count))

View File

@ -6,8 +6,8 @@ namespace esphome {
namespace animation { namespace animation {
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
image::ImageType type) image::ImageType type, image::Transparency transparent)
: Image(data_start, width, height, type), : Image(data_start, width, height, type, transparent),
animation_data_start_(data_start), animation_data_start_(data_start),
current_frame_(0), current_frame_(0),
animation_frame_count_(animation_frame_count), animation_frame_count_(animation_frame_count),

View File

@ -8,7 +8,8 @@ namespace animation {
class Animation : public image::Image { class Animation : public image::Image {
public: public:
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type); Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type,
image::Transparency transparent);
uint32_t get_animation_frame_count() const; uint32_t get_animation_frame_count() const;
int get_current_frame() const; int get_current_frame() const;

View File

@ -122,7 +122,8 @@ void APDS9306::update() {
this->status_clear_warning(); this->status_clear_warning();
if (!(status &= 0b00001000)) { // No new data status &= 0b00001000;
if (!status) { // No new data
return; return;
} }

View File

@ -1381,6 +1381,7 @@ message BluetoothConnectionsFreeResponse {
uint32 free = 1; uint32 free = 1;
uint32 limit = 2; uint32 limit = 2;
repeated uint64 allocated = 3;
} }
message BluetoothGATTErrorResponse { message BluetoothGATTErrorResponse {

View File

@ -6430,6 +6430,10 @@ bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVar
this->limit = value.as_uint32(); this->limit = value.as_uint32();
return true; return true;
} }
case 3: {
this->allocated.push_back(value.as_uint64());
return true;
}
default: default:
return false; return false;
} }
@ -6437,6 +6441,9 @@ bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVar
void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const { void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->free); buffer.encode_uint32(1, this->free);
buffer.encode_uint32(2, this->limit); buffer.encode_uint32(2, this->limit);
for (auto &it : this->allocated) {
buffer.encode_uint64(3, it, true);
}
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const { void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const {
@ -6451,6 +6458,13 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const {
sprintf(buffer, "%" PRIu32, this->limit); sprintf(buffer, "%" PRIu32, this->limit);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
for (const auto &it : this->allocated) {
out.append(" allocated: ");
sprintf(buffer, "%llu", it);
out.append(buffer);
out.append("\n");
}
out.append("}"); out.append("}");
} }
#endif #endif

View File

@ -1624,6 +1624,7 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage {
public: public:
uint32_t free{0}; uint32_t free{0};
uint32_t limit{0}; uint32_t limit{0};
std::vector<uint64_t> allocated{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <stddef.h>
namespace esphome { namespace esphome {
namespace audio { namespace audio {

View File

@ -0,0 +1,41 @@
from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MIC_GAIN
from esphome.core import coroutine_with_priority
CODEOWNERS = ["@kbx81"]
IS_PLATFORM_COMPONENT = True
audio_adc_ns = cg.esphome_ns.namespace("audio_adc")
AudioAdc = audio_adc_ns.class_("AudioAdc")
SetMicGainAction = audio_adc_ns.class_("SetMicGainAction", automation.Action)
SET_MIC_GAIN_ACTION_SCHEMA = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(AudioAdc),
cv.Required(CONF_MIC_GAIN): cv.templatable(cv.decibel),
},
key=CONF_MIC_GAIN,
)
@automation.register_action(
"audio_adc.set_mic_gain", SetMicGainAction, SET_MIC_GAIN_ACTION_SCHEMA
)
async def audio_adc_set_mic_gain_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config.get(CONF_MIC_GAIN), args, float)
cg.add(var.set_mic_gain(template_))
return var
@coroutine_with_priority(100.0)
async def to_code(config):
cg.add_define("USE_AUDIO_ADC")
cg.add_global(audio_adc_ns.using)

View File

@ -0,0 +1,17 @@
#pragma once
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace audio_adc {
class AudioAdc {
public:
virtual bool set_mic_gain(float mic_gain) = 0;
virtual float mic_gain() = 0;
};
} // namespace audio_adc
} // namespace esphome

View File

@ -0,0 +1,23 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "audio_adc.h"
namespace esphome {
namespace audio_adc {
template<typename... Ts> class SetMicGainAction : public Action<Ts...> {
public:
explicit SetMicGainAction(AudioAdc *audio_adc) : audio_adc_(audio_adc) {}
TEMPLATABLE_VALUE(float, mic_gain)
void play(Ts... x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); }
protected:
AudioAdc *audio_adc_;
};
} // namespace audio_adc
} // namespace esphome

View File

@ -58,7 +58,7 @@ class BinarySensor : public EntityBase, public EntityBase_DeviceClass {
void publish_initial_state(bool state); void publish_initial_state(bool state);
/// The current reported state of the binary sensor. /// The current reported state of the binary sensor.
bool state; bool state{false};
void add_filter(Filter *filter); void add_filter(Filter *filter);
void add_filters(const std::vector<Filter *> &filters); void add_filters(const std::vector<Filter *> &filters);

View File

@ -15,10 +15,11 @@ from esphome.components.libretiny.const import (
) )
from esphome.core import CORE from esphome.core import CORE
from .boards import BK72XX_BOARDS, BK72XX_BOARD_PINS from .boards import BK72XX_BOARD_PINS, BK72XX_BOARDS
CODEOWNERS = ["@kuba2k2"] CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["libretiny"] AUTO_LOAD = ["libretiny"]
IS_TARGET_PLATFORM = True
COMPONENT_DATA = LibreTinyComponent( COMPONENT_DATA = LibreTinyComponent(
name=COMPONENT_BK72XX, name=COMPONENT_BK72XX,

View File

@ -25,8 +25,7 @@ void BLEClient::loop() {
void BLEClient::dump_config() { void BLEClient::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Client:"); ESP_LOGCONFIG(TAG, "BLE Client:");
ESP_LOGCONFIG(TAG, " Address: %s", this->address_str().c_str()); BLEClientBase::dump_config();
ESP_LOGCONFIG(TAG, " Auto-Connect: %s", TRUEFALSE(this->auto_connect_));
} }
bool BLEClient::parse_device(const espbt::ESPBTDevice &device) { bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {

View File

@ -11,6 +11,7 @@ from esphome.const import (
DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_SIGNAL_STRENGTH,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_DECIBEL_MILLIWATT, UNIT_DECIBEL_MILLIWATT,
CONF_NOTIFY,
) )
from .. import ble_client_ns from .. import ble_client_ns
@ -19,7 +20,6 @@ DEPENDENCIES = ["ble_client"]
CONF_DESCRIPTOR_UUID = "descriptor_uuid" CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify"
CONF_ON_NOTIFY = "on_notify" CONF_ON_NOTIFY = "on_notify"
TYPE_CHARACTERISTIC = "characteristic" TYPE_CHARACTERISTIC = "characteristic"
TYPE_RSSI = "rssi" TYPE_RSSI = "rssi"

View File

@ -6,6 +6,7 @@ from esphome.const import (
CONF_CHARACTERISTIC_UUID, CONF_CHARACTERISTIC_UUID,
CONF_ID, CONF_ID,
CONF_SERVICE_UUID, CONF_SERVICE_UUID,
CONF_NOTIFY,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
) )
@ -15,7 +16,6 @@ DEPENDENCIES = ["ble_client"]
CONF_DESCRIPTOR_UUID = "descriptor_uuid" CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify"
CONF_ON_NOTIFY = "on_notify" CONF_ON_NOTIFY = "on_notify"
adv_data_t = cg.std_vector.template(cg.uint8) adv_data_t = cg.std_vector.template(cg.uint8)

View File

@ -13,6 +13,11 @@ namespace bluetooth_proxy {
static const char *const TAG = "bluetooth_proxy.connection"; static const char *const TAG = "bluetooth_proxy.connection";
void BluetoothConnection::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Connection:");
BLEClientBase::dump_config();
}
bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) { esp_ble_gattc_cb_param_t *param) {
if (!BLEClientBase::gattc_event_handler(event, gattc_if, param)) if (!BLEClientBase::gattc_event_handler(event, gattc_if, param))

View File

@ -11,6 +11,7 @@ class BluetoothProxy;
class BluetoothConnection : public esp32_ble_client::BLEClientBase { class BluetoothConnection : public esp32_ble_client::BLEClientBase {
public: public:
void dump_config() override;
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override; esp_ble_gattc_cb_param_t *param) override;
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;

View File

@ -475,6 +475,11 @@ void BluetoothProxy::send_connections_free() {
api::BluetoothConnectionsFreeResponse call; api::BluetoothConnectionsFreeResponse call;
call.free = this->get_bluetooth_connections_free(); call.free = this->get_bluetooth_connections_free();
call.limit = this->get_bluetooth_connections_limit(); call.limit = this->get_bluetooth_connections_limit();
for (auto *connection : this->connections_) {
if (connection->address_ != 0) {
call.allocated.push_back(connection->address_);
}
}
this->api_connection_->send_bluetooth_connections_free_response(call); this->api_connection_->send_bluetooth_connections_free_response(call);
} }

View File

@ -204,11 +204,11 @@ void BME68xBSEC2Component::update_subscription_() {
} }
void BME68xBSEC2Component::run_() { void BME68xBSEC2Component::run_() {
this->op_mode_ = this->bsec_settings_.op_mode;
int64_t curr_time_ns = this->get_time_ns_(); int64_t curr_time_ns = this->get_time_ns_();
if (curr_time_ns < this->next_call_ns_) { if (curr_time_ns < this->bsec_settings_.next_call) {
return; return;
} }
this->op_mode_ = this->bsec_settings_.op_mode;
uint8_t status; uint8_t status;
ESP_LOGV(TAG, "Performing sensor run"); ESP_LOGV(TAG, "Performing sensor run");
@ -219,57 +219,60 @@ void BME68xBSEC2Component::run_() {
ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_); ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_);
return; return;
} }
this->next_call_ns_ = this->bsec_settings_.next_call;
if (this->bsec_settings_.trigger_measurement) { switch (this->bsec_settings_.op_mode) {
bme68x_get_conf(&bme68x_conf, &this->bme68x_); case BME68X_FORCED_MODE:
bme68x_get_conf(&bme68x_conf, &this->bme68x_);
bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling; bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling;
bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling; bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling;
bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling; bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling;
bme68x_set_conf(&bme68x_conf, &this->bme68x_); bme68x_set_conf(&bme68x_conf, &this->bme68x_);
this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature;
this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration;
// status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_);
status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_);
this->op_mode_ = BME68X_FORCED_MODE;
ESP_LOGV(TAG, "Using forced mode");
break;
case BME68X_PARALLEL_MODE:
if (this->op_mode_ != this->bsec_settings_.op_mode) {
bme68x_get_conf(&bme68x_conf, &this->bme68x_);
bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling;
bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling;
bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling;
bme68x_set_conf(&bme68x_conf, &this->bme68x_);
switch (this->bsec_settings_.op_mode) {
case BME68X_FORCED_MODE:
this->bme68x_heatr_conf_.enable = BME68X_ENABLE; this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature; this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile;
this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration; this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile;
this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len;
this->bme68x_heatr_conf_.shared_heatr_dur =
BSEC_TOTAL_HEAT_DUR -
(bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000));
status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_); status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_);
this->op_mode_ = BME68X_FORCED_MODE;
this->sleep_mode_ = false;
ESP_LOGV(TAG, "Using forced mode");
break; status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
case BME68X_PARALLEL_MODE: this->op_mode_ = BME68X_PARALLEL_MODE;
if (this->op_mode_ != this->bsec_settings_.op_mode) { ESP_LOGV(TAG, "Using parallel mode");
this->bme68x_heatr_conf_.enable = BME68X_ENABLE; }
this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile; break;
this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile; case BME68X_SLEEP_MODE:
this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len; if (this->op_mode_ != this->bsec_settings_.op_mode) {
this->bme68x_heatr_conf_.shared_heatr_dur = bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_);
BSEC_TOTAL_HEAT_DUR - this->op_mode_ = BME68X_SLEEP_MODE;
(bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000)); ESP_LOGV(TAG, "Using sleep mode");
}
status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); break;
}
status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
this->op_mode_ = BME68X_PARALLEL_MODE;
this->sleep_mode_ = false;
ESP_LOGV(TAG, "Using parallel mode");
}
break;
case BME68X_SLEEP_MODE:
if (!this->sleep_mode_) {
bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_);
this->sleep_mode_ = true;
ESP_LOGV(TAG, "Using sleep mode");
}
break;
}
if (this->bsec_settings_.trigger_measurement && this->bsec_settings_.op_mode != BME68X_SLEEP_MODE) {
uint32_t meas_dur = 0; uint32_t meas_dur = 0;
meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_); meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_);
ESP_LOGV(TAG, "Queueing read in %uus", meas_dur); ESP_LOGV(TAG, "Queueing read in %uus", meas_dur);

View File

@ -113,13 +113,11 @@ class BME68xBSEC2Component : public Component {
struct bme68x_heatr_conf bme68x_heatr_conf_; struct bme68x_heatr_conf bme68x_heatr_conf_;
uint8_t op_mode_; // operating mode of sensor uint8_t op_mode_; // operating mode of sensor
bool sleep_mode_;
bsec_library_return_t bsec_status_{BSEC_OK}; bsec_library_return_t bsec_status_{BSEC_OK};
int8_t bme68x_status_{BME68X_OK}; int8_t bme68x_status_{BME68X_OK};
int64_t last_time_ms_{0}; int64_t last_time_ms_{0};
uint32_t millis_overflow_counter_{0}; uint32_t millis_overflow_counter_{0};
int64_t next_call_ns_{0};
std::queue<std::function<void()>> queue_; std::queue<std::function<void()>> queue_;

View File

@ -115,7 +115,7 @@ CONF_MAX_HUMIDITY = "max_humidity"
CONF_TARGET_HUMIDITY = "target_humidity" CONF_TARGET_HUMIDITY = "target_humidity"
visual_temperature = cv.float_with_unit( visual_temperature = cv.float_with_unit(
"visual_temperature", "(°C|° C|°|C|° K|° K|K|°F|° F|F)?" "visual_temperature", "(°C|° C|°|C|°K|° K|K|°F|° F|F)?"
) )

View File

@ -37,8 +37,9 @@ void ClimateIR::setup() {
this->publish_state(); this->publish_state();
}); });
this->current_temperature = this->sensor_->state; this->current_temperature = this->sensor_->state;
} else } else {
this->current_temperature = NAN; this->current_temperature = NAN;
}
// restore set points // restore set points
auto restore = this->restore_state_(); auto restore = this->restore_state_();
if (restore.has_value()) { if (restore.has_value()) {

View File

@ -131,8 +131,9 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei
} else { } else {
parent->mode = climate::CLIMATE_MODE_FAN_ONLY; parent->mode = climate::CLIMATE_MODE_FAN_ONLY;
} }
} else } else {
parent->mode = climate::CLIMATE_MODE_COOL; parent->mode = climate::CLIMATE_MODE_COOL;
}
// Fan Speed // Fan Speed
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL || if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL ||

View File

@ -43,7 +43,7 @@ bool CSE7766Component::check_byte_() {
uint8_t index = this->raw_data_index_; uint8_t index = this->raw_data_index_;
uint8_t byte = this->raw_data_[index]; uint8_t byte = this->raw_data_[index];
if (index == 0) { if (index == 0) {
return !((byte != 0x55) && ((byte & 0xF0) != 0xF0) && (byte != 0xAA)); return (byte == 0x55) || ((byte & 0xF0) == 0xF0) || (byte == 0xAA);
} }
if (index == 1) { if (index == 1) {

View File

@ -1,3 +0,0 @@
import esphome.codegen as cg
custom_ns = cg.esphome_ns.namespace("custom")

View File

@ -1,31 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA
from .. import custom_ns
CustomBinarySensorConstructor = custom_ns.class_("CustomBinarySensorConstructor") CONFIG_SCHEMA = cv.invalid(
'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(
binary_sensor.binary_sensor_schema()
),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(binary_sensor.BinarySensorPtr),
)
rhs = CustomBinarySensorConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_BINARY_SENSORS]):
rhs = custom.Pget_binary_sensor(i)
await binary_sensor.register_binary_sensor(rhs, conf)

View File

@ -1,16 +0,0 @@
#include "custom_binary_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace custom {
static const char *const TAG = "custom.binary_sensor";
void CustomBinarySensorConstructor::dump_config() {
for (auto *child : this->binary_sensors_) {
LOG_BINARY_SENSOR("", "Custom Binary Sensor", child);
}
}
} // namespace custom
} // namespace esphome

View File

@ -1,26 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomBinarySensorConstructor : public Component {
public:
CustomBinarySensorConstructor(const std::function<std::vector<binary_sensor::BinarySensor *>()> &init) {
this->binary_sensors_ = init();
}
binary_sensor::BinarySensor *get_binary_sensor(int i) { return this->binary_sensors_[i]; }
void dump_config() override;
protected:
std::vector<binary_sensor::BinarySensor *> binary_sensors_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,30 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import climate
from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns
CustomClimateConstructor = custom_ns.class_("CustomClimateConstructor") CONFIG_SCHEMA = cv.invalid(
CONF_CLIMATES = "climates" 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomClimateConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(climate.Climate.operator("ptr")),
)
rhs = CustomClimateConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_CLIMATES]):
rhs = custom.Pget_climate(i)
await climate.register_climate(rhs, conf)

View File

@ -1,22 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/climate/climate.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomClimateConstructor {
public:
CustomClimateConstructor(const std::function<std::vector<climate::Climate *>()> &init) { this->climates_ = init(); }
climate::Climate *get_climate(int i) { return this->climates_[i]; }
protected:
std::vector<climate::Climate *> climates_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,30 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import cover
from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns
CustomCoverConstructor = custom_ns.class_("CustomCoverConstructor") CONFIG_SCHEMA = cv.invalid(
CONF_COVERS = "covers" 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomCoverConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(cover.Cover.operator("ptr")),
)
rhs = CustomCoverConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_COVERS]):
rhs = custom.Pget_cover(i)
await cover.register_cover(rhs, conf)

View File

@ -1,21 +0,0 @@
#pragma once
#include "esphome/components/cover/cover.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomCoverConstructor {
public:
CustomCoverConstructor(const std::function<std::vector<cover::Cover *>()> &init) { this->covers_ = init(); }
cover::Cover *get_cover(int i) { return this->covers_[i]; }
protected:
std::vector<cover::Cover *> covers_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,30 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import light
from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns
CustomLightOutputConstructor = custom_ns.class_("CustomLightOutputConstructor") CONFIG_SCHEMA = cv.invalid(
CONF_LIGHTS = "lights" 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(light.LightOutput.operator("ptr")),
)
rhs = CustomLightOutputConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_LIGHTS]):
rhs = custom.Pget_light(i)
await light.register_light(rhs, conf)

View File

@ -1,24 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/light/light_output.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomLightOutputConstructor {
public:
CustomLightOutputConstructor(const std::function<std::vector<light::LightOutput *>()> &init) {
this->outputs_ = init();
}
light::LightOutput *get_light(int i) { return this->outputs_[i]; }
protected:
std::vector<light::LightOutput *> outputs_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,61 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import output
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE, CONF_BINARY
from .. import custom_ns
CustomBinaryOutputConstructor = custom_ns.class_("CustomBinaryOutputConstructor") CONFIG_SCHEMA = cv.invalid(
CustomFloatOutputConstructor = custom_ns.class_("CustomFloatOutputConstructor") 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONF_FLOAT = "float"
CONFIG_SCHEMA = cv.typed_schema(
{
CONF_BINARY: cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_OUTPUTS): cv.ensure_list(
output.BINARY_OUTPUT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(output.BinaryOutput),
}
)
),
}
),
CONF_FLOAT: cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_OUTPUTS): cv.ensure_list(
output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(output.FloatOutput),
}
)
),
}
),
},
lower=True,
) )
async def to_code(config):
type = config[CONF_TYPE]
if type == "binary":
ret_type = output.BinaryOutputPtr
klass = CustomBinaryOutputConstructor
else:
ret_type = output.FloatOutputPtr
klass = CustomFloatOutputConstructor
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(ret_type)
)
rhs = klass(template_)
custom = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_OUTPUTS]):
out = cg.Pvariable(conf[CONF_ID], custom.get_output(i))
await output.register_output(out, conf)

View File

@ -1,37 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/output/binary_output.h"
#include "esphome/components/output/float_output.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomBinaryOutputConstructor {
public:
CustomBinaryOutputConstructor(const std::function<std::vector<output::BinaryOutput *>()> &init) {
this->outputs_ = init();
}
output::BinaryOutput *get_output(int i) { return this->outputs_[i]; }
protected:
std::vector<output::BinaryOutput *> outputs_;
};
class CustomFloatOutputConstructor {
public:
CustomFloatOutputConstructor(const std::function<std::vector<output::FloatOutput *>()> &init) {
this->outputs_ = init();
}
output::FloatOutput *get_output(int i) { return this->outputs_[i]; }
protected:
std::vector<output::FloatOutput *> outputs_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,27 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS
from .. import custom_ns
CustomSensorConstructor = custom_ns.class_("CustomSensorConstructor") CONFIG_SCHEMA = cv.invalid(
'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.sensor_schema()),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(sensor.SensorPtr)
)
rhs = CustomSensorConstructor(template_)
var = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_SENSORS]):
sens = cg.Pvariable(conf[CONF_ID], var.get_sensor(i))
await sensor.register_sensor(sens, conf)

View File

@ -1,16 +0,0 @@
#include "custom_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace custom {
static const char *const TAG = "custom.sensor";
void CustomSensorConstructor::dump_config() {
for (auto *child : this->sensors_) {
LOG_SENSOR("", "Custom Sensor", child);
}
}
} // namespace custom
} // namespace esphome

View File

@ -1,24 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomSensorConstructor : public Component {
public:
CustomSensorConstructor(const std::function<std::vector<sensor::Sensor *>()> &init) { this->sensors_ = init(); }
sensor::Sensor *get_sensor(int i) { return this->sensors_[i]; }
void dump_config() override;
protected:
std::vector<sensor::Sensor *> sensors_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,27 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import switch
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES
from .. import custom_ns
CustomSwitchConstructor = custom_ns.class_("CustomSwitchConstructor") CONFIG_SCHEMA = cv.invalid(
'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SWITCHES): cv.ensure_list(switch.switch_schema(switch.Switch)),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(switch.SwitchPtr)
)
rhs = CustomSwitchConstructor(template_)
var = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_SWITCHES]):
switch_ = cg.Pvariable(conf[CONF_ID], var.get_switch(i))
await switch.register_switch(switch_, conf)

View File

@ -1,16 +0,0 @@
#include "custom_switch.h"
#include "esphome/core/log.h"
namespace esphome {
namespace custom {
static const char *const TAG = "custom.switch";
void CustomSwitchConstructor::dump_config() {
for (auto *child : this->switches_) {
LOG_SWITCH("", "Custom Switch", child);
}
}
} // namespace custom
} // namespace esphome

View File

@ -1,24 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/switch/switch.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomSwitchConstructor : public Component {
public:
CustomSwitchConstructor(const std::function<std::vector<switch_::Switch *>()> &init) { this->switches_ = init(); }
switch_::Switch *get_switch(int i) { return this->switches_[i]; }
void dump_config() override;
protected:
std::vector<switch_::Switch *> switches_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,32 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS
from .. import custom_ns
CustomTextSensorConstructor = custom_ns.class_("CustomTextSensorConstructor") CONFIG_SCHEMA = cv.invalid(
'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_TEXT_SENSORS): cv.ensure_list(
text_sensor.text_sensor_schema()
),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(text_sensor.TextSensorPtr),
)
rhs = CustomTextSensorConstructor(template_)
var = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config[CONF_TEXT_SENSORS]):
text = cg.Pvariable(conf[CONF_ID], var.get_text_sensor(i))
await text_sensor.register_text_sensor(text, conf)

View File

@ -1,16 +0,0 @@
#include "custom_text_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace custom {
static const char *const TAG = "custom.text_sensor";
void CustomTextSensorConstructor::dump_config() {
for (auto *child : this->text_sensors_) {
LOG_TEXT_SENSOR("", "Custom Text Sensor", child);
}
}
} // namespace custom
} // namespace esphome

View File

@ -1,26 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/text_sensor/text_sensor.h"
#include <vector>
namespace esphome {
namespace custom {
class CustomTextSensorConstructor : public Component {
public:
CustomTextSensorConstructor(const std::function<std::vector<text_sensor::TextSensor *>()> &init) {
this->text_sensors_ = init();
}
text_sensor::TextSensor *get_text_sensor(int i) { return this->text_sensors_[i]; }
void dump_config() override;
protected:
std::vector<text_sensor::TextSensor *> text_sensors_;
};
} // namespace custom
} // namespace esphome

View File

@ -1,31 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_LAMBDA
custom_component_ns = cg.esphome_ns.namespace("custom_component")
CustomComponentConstructor = custom_component_ns.class_("CustomComponentConstructor")
MULTI_CONF = True MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema(
{ CONFIG_SCHEMA = cv.invalid(
cv.GenerateID(): cv.declare_id(CustomComponentConstructor), 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components'
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_COMPONENTS): cv.ensure_list(
cv.Schema({cv.GenerateID(): cv.declare_id(cg.Component)}).extend(
cv.COMPONENT_SCHEMA
)
),
}
) )
async def to_code(config):
template_ = await cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(cg.ComponentPtr)
)
rhs = CustomComponentConstructor(template_)
var = cg.variable(config[CONF_ID], rhs)
for i, conf in enumerate(config.get(CONF_COMPONENTS, [])):
comp = cg.Pvariable(conf[CONF_ID], var.get_component(i))
await cg.register_component(comp, conf)

View File

@ -1,28 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/application.h"
#include <vector>
namespace esphome {
namespace custom_component {
class CustomComponentConstructor {
public:
CustomComponentConstructor(const std::function<std::vector<Component *>()> &init) {
this->components_ = init();
for (auto *comp : this->components_) {
App.register_component(comp);
}
}
Component *get_component(int i) const { return this->components_[i]; }
protected:
std::vector<Component *> components_;
};
} // namespace custom_component
} // namespace esphome

View File

@ -298,6 +298,12 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
if (this->cell_16_voltage_sensor_) { if (this->cell_16_voltage_sensor_) {
this->cell_16_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000); this->cell_16_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
} }
if (this->cell_17_voltage_sensor_) {
this->cell_17_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
}
if (this->cell_18_voltage_sensor_) {
this->cell_18_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
}
break; break;
} }
break; break;

View File

@ -54,6 +54,8 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
SUB_SENSOR(cell_14_voltage) SUB_SENSOR(cell_14_voltage)
SUB_SENSOR(cell_15_voltage) SUB_SENSOR(cell_15_voltage)
SUB_SENSOR(cell_16_voltage) SUB_SENSOR(cell_16_voltage)
SUB_SENSOR(cell_17_voltage)
SUB_SENSOR(cell_18_voltage)
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR

View File

@ -52,6 +52,8 @@ CONF_CELL_13_VOLTAGE = "cell_13_voltage"
CONF_CELL_14_VOLTAGE = "cell_14_voltage" CONF_CELL_14_VOLTAGE = "cell_14_voltage"
CONF_CELL_15_VOLTAGE = "cell_15_voltage" CONF_CELL_15_VOLTAGE = "cell_15_voltage"
CONF_CELL_16_VOLTAGE = "cell_16_voltage" CONF_CELL_16_VOLTAGE = "cell_16_voltage"
CONF_CELL_17_VOLTAGE = "cell_17_voltage"
CONF_CELL_18_VOLTAGE = "cell_18_voltage"
ICON_CURRENT_DC = "mdi:current-dc" ICON_CURRENT_DC = "mdi:current-dc"
ICON_BATTERY_OUTLINE = "mdi:battery-outline" ICON_BATTERY_OUTLINE = "mdi:battery-outline"
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up" ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
@ -92,6 +94,8 @@ TYPES = [
CONF_CELL_14_VOLTAGE, CONF_CELL_14_VOLTAGE,
CONF_CELL_15_VOLTAGE, CONF_CELL_15_VOLTAGE,
CONF_CELL_16_VOLTAGE, CONF_CELL_16_VOLTAGE,
CONF_CELL_17_VOLTAGE,
CONF_CELL_18_VOLTAGE,
] ]
CELL_VOLTAGE_SCHEMA = sensor.sensor_schema( CELL_VOLTAGE_SCHEMA = sensor.sensor_schema(
@ -212,6 +216,8 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_CELL_14_VOLTAGE): CELL_VOLTAGE_SCHEMA, cv.Optional(CONF_CELL_14_VOLTAGE): CELL_VOLTAGE_SCHEMA,
cv.Optional(CONF_CELL_15_VOLTAGE): CELL_VOLTAGE_SCHEMA, cv.Optional(CONF_CELL_15_VOLTAGE): CELL_VOLTAGE_SCHEMA,
cv.Optional(CONF_CELL_16_VOLTAGE): CELL_VOLTAGE_SCHEMA, cv.Optional(CONF_CELL_16_VOLTAGE): CELL_VOLTAGE_SCHEMA,
cv.Optional(CONF_CELL_17_VOLTAGE): CELL_VOLTAGE_SCHEMA,
cv.Optional(CONF_CELL_18_VOLTAGE): CELL_VOLTAGE_SCHEMA,
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
) )

View File

@ -60,9 +60,7 @@ ESPTime DateTimeEntity::state_as_esptime() const {
obj.hour = this->hour_; obj.hour = this->hour_;
obj.minute = this->minute_; obj.minute = this->minute_;
obj.second = this->second_; obj.second = this->second_;
obj.day_of_week = 1; // Required to be valid for recalc_timestamp_local but not used. obj.recalc_timestamp_local();
obj.day_of_year = 1; // Required to be valid for recalc_timestamp_local but not used.
obj.recalc_timestamp_local(false);
return obj; return obj;
} }

View File

@ -50,6 +50,10 @@ void DebugComponent::dump_config() {
this->reset_reason_->publish_state(get_reset_reason_()); this->reset_reason_->publish_state(get_reset_reason_());
} }
#endif // USE_TEXT_SENSOR #endif // USE_TEXT_SENSOR
#ifdef USE_ESP32
this->log_partition_info_(); // Log partition information for ESP32
#endif // USE_ESP32
} }
void DebugComponent::loop() { void DebugComponent::loop() {

View File

@ -55,6 +55,20 @@ class DebugComponent : public PollingComponent {
#endif // USE_ESP32 #endif // USE_ESP32
#endif // USE_SENSOR #endif // USE_SENSOR
#ifdef USE_ESP32
/**
* @brief Logs information about the device's partition table.
*
* This function iterates through the ESP32's partition table and logs details
* about each partition, including its name, type, subtype, starting address,
* and size. The information is useful for diagnosing issues related to flash
* memory or verifying the partition configuration dynamically at runtime.
*
* Only available when compiled for ESP32 platforms.
*/
void log_partition_info_();
#endif // USE_ESP32
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
text_sensor::TextSensor *device_info_{nullptr}; text_sensor::TextSensor *device_info_{nullptr};
text_sensor::TextSensor *reset_reason_{nullptr}; text_sensor::TextSensor *reset_reason_{nullptr};

View File

@ -5,6 +5,7 @@
#include <esp_heap_caps.h> #include <esp_heap_caps.h>
#include <esp_system.h> #include <esp_system.h>
#include <esp_chip_info.h> #include <esp_chip_info.h>
#include <esp_partition.h>
#if defined(USE_ESP32_VARIANT_ESP32) #if defined(USE_ESP32_VARIANT_ESP32)
#include <esp32/rom/rtc.h> #include <esp32/rom/rtc.h>
@ -28,111 +29,177 @@ namespace debug {
static const char *const TAG = "debug"; static const char *const TAG = "debug";
void DebugComponent::log_partition_info_() {
ESP_LOGCONFIG(TAG, "Partition table:");
ESP_LOGCONFIG(TAG, " %-12s %-4s %-8s %-10s %-10s", "Name", "Type", "Subtype", "Address", "Size");
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
while (it != NULL) {
const esp_partition_t *partition = esp_partition_get(it);
ESP_LOGCONFIG(TAG, " %-12s %-4d %-8d 0x%08" PRIX32 " 0x%08" PRIX32, partition->label, partition->type,
partition->subtype, partition->address, partition->size);
it = esp_partition_next(it);
}
esp_partition_iterator_release(it);
}
std::string DebugComponent::get_reset_reason_() { std::string DebugComponent::get_reset_reason_() {
std::string reset_reason; std::string reset_reason;
switch (rtc_get_reset_reason(0)) { switch (esp_reset_reason()) {
case POWERON_RESET: case ESP_RST_POWERON:
reset_reason = "Power On Reset"; reset_reason = "Reset due to power-on event";
break; break;
case ESP_RST_EXT:
reset_reason = "Reset by external pin";
break;
case ESP_RST_SW:
reset_reason = "Software reset via esp_restart";
break;
case ESP_RST_PANIC:
reset_reason = "Software reset due to exception/panic";
break;
case ESP_RST_INT_WDT:
reset_reason = "Reset (software or hardware) due to interrupt watchdog";
break;
case ESP_RST_TASK_WDT:
reset_reason = "Reset due to task watchdog";
break;
case ESP_RST_WDT:
reset_reason = "Reset due to other watchdogs";
break;
case ESP_RST_DEEPSLEEP:
reset_reason = "Reset after exiting deep sleep mode";
break;
case ESP_RST_BROWNOUT:
reset_reason = "Brownout reset (software or hardware)";
break;
case ESP_RST_SDIO:
reset_reason = "Reset over SDIO";
break;
#ifdef USE_ESP32_VARIANT_ESP32
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4))
case ESP_RST_USB:
reset_reason = "Reset by USB peripheral";
break;
case ESP_RST_JTAG:
reset_reason = "Reset by JTAG";
break;
case ESP_RST_EFUSE:
reset_reason = "Reset due to efuse error";
break;
case ESP_RST_PWR_GLITCH:
reset_reason = "Reset due to power glitch detected";
break;
case ESP_RST_CPU_LOCKUP:
reset_reason = "Reset due to CPU lock up (double exception)";
break;
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4)
#endif // USE_ESP32_VARIANT_ESP32
default: // Includes ESP_RST_UNKNOWN
switch (rtc_get_reset_reason(0)) {
case POWERON_RESET:
reset_reason = "Power On Reset";
break;
#if defined(USE_ESP32_VARIANT_ESP32) #if defined(USE_ESP32_VARIANT_ESP32)
case SW_RESET: case SW_RESET:
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6)
case RTC_SW_SYS_RESET: case RTC_SW_SYS_RESET:
#endif #endif
reset_reason = "Software Reset Digital Core"; reset_reason = "Software Reset Digital Core";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32) #if defined(USE_ESP32_VARIANT_ESP32)
case OWDT_RESET: case OWDT_RESET:
reset_reason = "Watch Dog Reset Digital Core"; reset_reason = "Watch Dog Reset Digital Core";
break; break;
#endif #endif
case DEEPSLEEP_RESET: case DEEPSLEEP_RESET:
reset_reason = "Deep Sleep Reset Digital Core"; reset_reason = "Deep Sleep Reset Digital Core";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32) #if defined(USE_ESP32_VARIANT_ESP32)
case SDIO_RESET: case SDIO_RESET:
reset_reason = "SLC Module Reset Digital Core"; reset_reason = "SLC Module Reset Digital Core";
break; break;
#endif #endif
case TG0WDT_SYS_RESET: case TG0WDT_SYS_RESET:
reset_reason = "Timer Group 0 Watch Dog Reset Digital Core"; reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
break; break;
case TG1WDT_SYS_RESET: case TG1WDT_SYS_RESET:
reset_reason = "Timer Group 1 Watch Dog Reset Digital Core"; reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
break; break;
case RTCWDT_SYS_RESET: case RTCWDT_SYS_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core"; reset_reason = "RTC Watch Dog Reset Digital Core";
break; break;
#if !defined(USE_ESP32_VARIANT_ESP32C6) && !defined(USE_ESP32_VARIANT_ESP32H2) #if !defined(USE_ESP32_VARIANT_ESP32C6) && !defined(USE_ESP32_VARIANT_ESP32H2)
case INTRUSION_RESET: case INTRUSION_RESET:
reset_reason = "Intrusion Reset CPU"; reset_reason = "Intrusion Reset CPU";
break; break;
#endif #endif
#if defined(USE_ESP32_VARIANT_ESP32) #if defined(USE_ESP32_VARIANT_ESP32)
case TGWDT_CPU_RESET: case TGWDT_CPU_RESET:
reset_reason = "Timer Group Reset CPU"; reset_reason = "Timer Group Reset CPU";
break; break;
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6)
case TG0WDT_CPU_RESET: case TG0WDT_CPU_RESET:
reset_reason = "Timer Group 0 Reset CPU"; reset_reason = "Timer Group 0 Reset CPU";
break; break;
#endif #endif
#if defined(USE_ESP32_VARIANT_ESP32) #if defined(USE_ESP32_VARIANT_ESP32)
case SW_CPU_RESET: case SW_CPU_RESET:
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6)
case RTC_SW_CPU_RESET: case RTC_SW_CPU_RESET:
#endif #endif
reset_reason = "Software Reset CPU"; reset_reason = "Software Reset CPU";
break; break;
case RTCWDT_CPU_RESET: case RTCWDT_CPU_RESET:
reset_reason = "RTC Watch Dog Reset CPU"; reset_reason = "RTC Watch Dog Reset CPU";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32) #if defined(USE_ESP32_VARIANT_ESP32)
case EXT_CPU_RESET: case EXT_CPU_RESET:
reset_reason = "External CPU Reset"; reset_reason = "External CPU Reset";
break; break;
#endif #endif
case RTCWDT_BROWN_OUT_RESET: case RTCWDT_BROWN_OUT_RESET:
reset_reason = "Voltage Unstable Reset"; reset_reason = "Voltage Unstable Reset";
break; break;
case RTCWDT_RTC_RESET: case RTCWDT_RTC_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module"; reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \ #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \
defined(USE_ESP32_VARIANT_ESP32C6) defined(USE_ESP32_VARIANT_ESP32C6)
case TG1WDT_CPU_RESET: case TG1WDT_CPU_RESET:
reset_reason = "Timer Group 1 Reset CPU"; reset_reason = "Timer Group 1 Reset CPU";
break; break;
case SUPER_WDT_RESET: case SUPER_WDT_RESET:
reset_reason = "Super Watchdog Reset Digital Core And RTC Module"; reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
break; break;
case EFUSE_RESET: case EFUSE_RESET:
reset_reason = "eFuse Reset Digital Core"; reset_reason = "eFuse Reset Digital Core";
break; break;
#endif #endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case GLITCH_RTC_RESET: case GLITCH_RTC_RESET:
reset_reason = "Glitch Reset Digital Core And RTC Module"; reset_reason = "Glitch Reset Digital Core And RTC Module";
break; break;
#endif #endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6)
case USB_UART_CHIP_RESET: case USB_UART_CHIP_RESET:
reset_reason = "USB UART Reset Digital Core"; reset_reason = "USB UART Reset Digital Core";
break; break;
case USB_JTAG_CHIP_RESET: case USB_JTAG_CHIP_RESET:
reset_reason = "USB JTAG Reset Digital Core"; reset_reason = "USB JTAG Reset Digital Core";
break; break;
#endif #endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
case POWER_GLITCH_RESET: case POWER_GLITCH_RESET:
reset_reason = "Power Glitch Reset Digital Core And RTC Module"; reset_reason = "Power Glitch Reset Digital Core And RTC Module";
break; break;
#endif #endif
default: default:
reset_reason = "Unknown Reset Reason"; reset_reason = "Unknown Reset Reason";
}
break;
} }
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str()); ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
return reset_reason; return reset_reason;
@ -223,6 +290,19 @@ void DebugComponent::get_device_info_(std::string &device_info) {
device_info += " Cores:" + to_string(info.cores); device_info += " Cores:" + to_string(info.cores);
device_info += " Revision:" + to_string(info.revision); device_info += " Revision:" + to_string(info.revision);
// Framework detection
device_info += "|Framework: ";
#ifdef USE_ARDUINO
ESP_LOGD(TAG, "Framework: Arduino");
device_info += "Arduino";
#elif defined(USE_ESP_IDF)
ESP_LOGD(TAG, "Framework: ESP-IDF");
device_info += "ESP-IDF";
#else
ESP_LOGW(TAG, "Framework: UNKNOWN");
device_info += "UNKNOWN";
#endif
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version()); ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
device_info += "|ESP-IDF: "; device_info += "|ESP-IDF: ";
device_info += esp_get_idf_version(); device_info += esp_get_idf_version();
@ -294,4 +374,4 @@ void DebugComponent::update_platform_() {
} // namespace debug } // namespace debug
} // namespace esphome } // namespace esphome
#endif #endif // USE_ESP32

View File

@ -52,11 +52,11 @@ void DeepSleepComponent::dump_config_platform_() {
bool DeepSleepComponent::prepare_to_sleep_() { bool DeepSleepComponent::prepare_to_sleep_() {
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr && if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr &&
!this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) { this->wakeup_pin_->digital_read()) {
// Defer deep sleep until inactive // Defer deep sleep until inactive
if (!this->next_enter_deep_sleep_) { if (!this->next_enter_deep_sleep_) {
this->status_set_warning(); this->status_set_warning();
ESP_LOGW(TAG, "Waiting for pin_ to switch state to enter deep sleep..."); ESP_LOGW(TAG, "Waiting wakeup pin state change to enter deep sleep...");
} }
this->next_enter_deep_sleep_ = true; this->next_enter_deep_sleep_ = true;
return false; return false;

View File

@ -6,7 +6,104 @@ namespace dfplayer {
static const char *const TAG = "dfplayer"; static const char *const TAG = "dfplayer";
void DFPlayer::next() {
this->ack_set_is_playing_ = true;
ESP_LOGD(TAG, "Playing next track");
this->send_cmd_(0x01);
}
void DFPlayer::previous() {
this->ack_set_is_playing_ = true;
ESP_LOGD(TAG, "Playing previous track");
this->send_cmd_(0x02);
}
void DFPlayer::play_mp3(uint16_t file) {
this->ack_set_is_playing_ = true;
ESP_LOGD(TAG, "Playing file %d in mp3 folder", file);
this->send_cmd_(0x12, file);
}
void DFPlayer::play_file(uint16_t file) {
this->ack_set_is_playing_ = true;
ESP_LOGD(TAG, "Playing file %d", file);
this->send_cmd_(0x03, file);
}
void DFPlayer::play_file_loop(uint16_t file) {
this->ack_set_is_playing_ = true;
ESP_LOGD(TAG, "Playing file %d in loop", file);
this->send_cmd_(0x08, file);
}
void DFPlayer::play_folder_loop(uint16_t folder) {
this->ack_set_is_playing_ = true;
ESP_LOGD(TAG, "Playing folder %d in loop", folder);
this->send_cmd_(0x17, folder);
}
void DFPlayer::volume_up() {
ESP_LOGD(TAG, "Increasing volume");
this->send_cmd_(0x04);
}
void DFPlayer::volume_down() {
ESP_LOGD(TAG, "Decreasing volume");
this->send_cmd_(0x05);
}
void DFPlayer::set_device(Device device) {
ESP_LOGD(TAG, "Setting device to %d", device);
this->send_cmd_(0x09, device);
}
void DFPlayer::set_volume(uint8_t volume) {
ESP_LOGD(TAG, "Setting volume to %d", volume);
this->send_cmd_(0x06, volume);
}
void DFPlayer::set_eq(EqPreset preset) {
ESP_LOGD(TAG, "Setting EQ to %d", preset);
this->send_cmd_(0x07, preset);
}
void DFPlayer::sleep() {
this->ack_reset_is_playing_ = true;
ESP_LOGD(TAG, "Putting DFPlayer to sleep");
this->send_cmd_(0x0A);
}
void DFPlayer::reset() {
this->ack_reset_is_playing_ = true;
ESP_LOGD(TAG, "Resetting DFPlayer");
this->send_cmd_(0x0C);
}
void DFPlayer::start() {
this->ack_set_is_playing_ = true;
ESP_LOGD(TAG, "Starting playback");
this->send_cmd_(0x0D);
}
void DFPlayer::pause() {
this->ack_reset_is_playing_ = true;
ESP_LOGD(TAG, "Pausing playback");
this->send_cmd_(0x0E);
}
void DFPlayer::stop() {
this->ack_reset_is_playing_ = true;
ESP_LOGD(TAG, "Stopping playback");
this->send_cmd_(0x16);
}
void DFPlayer::random() {
this->ack_set_is_playing_ = true;
ESP_LOGD(TAG, "Playing random file");
this->send_cmd_(0x18);
}
void DFPlayer::play_folder(uint16_t folder, uint16_t file) { void DFPlayer::play_folder(uint16_t folder, uint16_t file) {
ESP_LOGD(TAG, "Playing file %d in folder %d", file, folder);
if (folder < 100 && file < 256) { if (folder < 100 && file < 256) {
this->ack_set_is_playing_ = true; this->ack_set_is_playing_ = true;
this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file); this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file);
@ -29,7 +126,7 @@ void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) {
this->sent_cmd_ = cmd; this->sent_cmd_ = cmd;
ESP_LOGD(TAG, "Send Command %#02x arg %#04x", cmd, argument); ESP_LOGV(TAG, "Send Command %#02x arg %#04x", cmd, argument);
this->write_array(buffer, 10); this->write_array(buffer, 10);
} }
@ -62,6 +159,15 @@ void DFPlayer::loop() {
} }
break; break;
case 9: // End byte case 9: // End byte
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
char byte_sequence[100];
byte_sequence[0] = '\0';
for (size_t i = 0; i < this->read_pos_ + 1; ++i) {
snprintf(byte_sequence + strlen(byte_sequence), sizeof(byte_sequence) - strlen(byte_sequence), "%02X ",
this->read_buffer_[i]);
}
ESP_LOGVV(TAG, "Received byte sequence: %s", byte_sequence);
#endif
if (byte != 0xEF) { if (byte != 0xEF) {
ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte); ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte);
this->read_pos_ = 0; this->read_pos_ = 0;
@ -101,9 +207,37 @@ void DFPlayer::loop() {
ESP_LOGV(TAG, "Nack"); ESP_LOGV(TAG, "Nack");
this->ack_set_is_playing_ = false; this->ack_set_is_playing_ = false;
this->ack_reset_is_playing_ = false; this->ack_reset_is_playing_ = false;
if (argument == 6) { switch (argument) {
ESP_LOGV(TAG, "File not found"); case 0x01:
this->is_playing_ = false; ESP_LOGE(TAG, "Module is busy or uninitialized");
break;
case 0x02:
ESP_LOGE(TAG, "Module is in sleep mode");
break;
case 0x03:
ESP_LOGE(TAG, "Serial receive error");
break;
case 0x04:
ESP_LOGE(TAG, "Checksum incorrect");
break;
case 0x05:
ESP_LOGE(TAG, "Specified track is out of current track scope");
this->is_playing_ = false;
break;
case 0x06:
ESP_LOGE(TAG, "Specified track is not found");
this->is_playing_ = false;
break;
case 0x07:
ESP_LOGE(TAG, "Insertion error (an inserting operation only can be done when a track is being played)");
break;
case 0x08:
ESP_LOGE(TAG, "SD card reading failed (SD card pulled out or damaged)");
break;
case 0x09:
ESP_LOGE(TAG, "Entered into sleep mode");
this->is_playing_ = false;
break;
} }
break; break;
case 0x41: case 0x41:
@ -113,12 +247,17 @@ void DFPlayer::loop() {
this->ack_set_is_playing_ = false; this->ack_set_is_playing_ = false;
this->ack_reset_is_playing_ = false; this->ack_reset_is_playing_ = false;
break; break;
case 0x3D: // Playback finished case 0x3C:
ESP_LOGV(TAG, "Playback finished (USB drive)");
this->is_playing_ = false;
this->on_finished_playback_callback_.call();
case 0x3D:
ESP_LOGV(TAG, "Playback finished (SD card)");
this->is_playing_ = false; this->is_playing_ = false;
this->on_finished_playback_callback_.call(); this->on_finished_playback_callback_.call();
break; break;
default: default:
ESP_LOGD(TAG, "Command %#02x arg %#04x", cmd, argument); ESP_LOGE(TAG, "Received unknown cmd %#02x arg %#04x", cmd, argument);
} }
this->sent_cmd_ = 0; this->sent_cmd_ = 0;
this->read_pos_ = 0; this->read_pos_ = 0;

View File

@ -23,64 +23,30 @@ enum Device {
TF_CARD = 2, TF_CARD = 2,
}; };
// See the datasheet here:
// https://github.com/DFRobot/DFRobotDFPlayerMini/blob/master/doc/FN-M16P%2BEmbedded%2BMP3%2BAudio%2BModule%2BDatasheet.pdf
class DFPlayer : public uart::UARTDevice, public Component { class DFPlayer : public uart::UARTDevice, public Component {
public: public:
void loop() override; void loop() override;
void next() { void next();
this->ack_set_is_playing_ = true; void previous();
this->send_cmd_(0x01); void play_mp3(uint16_t file);
} void play_file(uint16_t file);
void previous() { void play_file_loop(uint16_t file);
this->ack_set_is_playing_ = true;
this->send_cmd_(0x02);
}
void play_mp3(uint16_t file) {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x12, file);
}
void play_file(uint16_t file) {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x03, file);
}
void play_file_loop(uint16_t file) {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x08, file);
}
void play_folder(uint16_t folder, uint16_t file); void play_folder(uint16_t folder, uint16_t file);
void play_folder_loop(uint16_t folder) { void play_folder_loop(uint16_t folder);
this->ack_set_is_playing_ = true; void volume_up();
this->send_cmd_(0x17, folder); void volume_down();
} void set_device(Device device);
void volume_up() { this->send_cmd_(0x04); } void set_volume(uint8_t volume);
void volume_down() { this->send_cmd_(0x05); } void set_eq(EqPreset preset);
void set_device(Device device) { this->send_cmd_(0x09, device); } void sleep();
void set_volume(uint8_t volume) { this->send_cmd_(0x06, volume); } void reset();
void set_eq(EqPreset preset) { this->send_cmd_(0x07, preset); } void start();
void sleep() { void pause();
this->ack_reset_is_playing_ = true; void stop();
this->send_cmd_(0x0A); void random();
}
void reset() {
this->ack_reset_is_playing_ = true;
this->send_cmd_(0x0C);
}
void start() {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x0D);
}
void pause() {
this->ack_reset_is_playing_ = true;
this->send_cmd_(0x0E);
}
void stop() {
this->ack_reset_is_playing_ = true;
this->send_cmd_(0x16);
}
void random() {
this->ack_set_is_playing_ = true;
this->send_cmd_(0x18);
}
bool is_playing() { return is_playing_; } bool is_playing() { return is_playing_; }
void dump_config() override; void dump_config() override;

View File

@ -118,8 +118,9 @@ std::unique_ptr<Command> CircularCommandQueue::dequeue() {
if (front_ == rear_) { if (front_ == rear_) {
front_ = -1; front_ = -1;
rear_ = -1; rear_ = -1;
} else } else {
front_ = (front_ + 1) % COMMAND_QUEUE_SIZE; front_ = (front_ + 1) % COMMAND_QUEUE_SIZE;
}
return dequeued_cmd; return dequeued_cmd;
} }

View File

@ -135,7 +135,8 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
// Wait for falling edge // Wait for falling edge
while (this->pin_->digital_read()) { while (this->pin_->digital_read()) {
if ((end_time = micros()) - start_time > 90) { end_time = micros();
if (end_time - start_time > 90) {
if (i < 0) { if (i < 0) {
error_code = 3; error_code = 3;
} else { } else {
@ -156,8 +157,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
if (bit == 0) { if (bit == 0) {
bit = 7; bit = 7;
byte++; byte++;
} else } else {
bit--; bit--;
}
} }
} }
if (!report_errors && error_code != 0) if (!report_errors && error_code != 0)

View File

@ -39,6 +39,7 @@ DisplayOnPageChangeTrigger = display_ns.class_(
CONF_ON_PAGE_CHANGE = "on_page_change" CONF_ON_PAGE_CHANGE = "on_page_change"
CONF_SHOW_TEST_CARD = "show_test_card" CONF_SHOW_TEST_CARD = "show_test_card"
CONF_UNSPECIFIED = "unspecified"
DISPLAY_ROTATIONS = { DISPLAY_ROTATIONS = {
0: display_ns.DISPLAY_ROTATION_0_DEGREES, 0: display_ns.DISPLAY_ROTATION_0_DEGREES,
@ -55,16 +56,22 @@ def validate_rotation(value):
return cv.enum(DISPLAY_ROTATIONS, int=True)(value) return cv.enum(DISPLAY_ROTATIONS, int=True)(value)
def validate_auto_clear(value):
if value == CONF_UNSPECIFIED:
return value
return cv.boolean(value)
BASIC_DISPLAY_SCHEMA = cv.Schema( BASIC_DISPLAY_SCHEMA = cv.Schema(
{ {
cv.Optional(CONF_LAMBDA): cv.lambda_, cv.Exclusive(CONF_LAMBDA, CONF_LAMBDA): cv.lambda_,
} }
).extend(cv.polling_component_schema("1s")) ).extend(cv.polling_component_schema("1s"))
FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend( FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
{ {
cv.Optional(CONF_ROTATION): validate_rotation, cv.Optional(CONF_ROTATION): validate_rotation,
cv.Optional(CONF_PAGES): cv.All( cv.Exclusive(CONF_PAGES, CONF_LAMBDA): cv.All(
cv.ensure_list( cv.ensure_list(
{ {
cv.GenerateID(): cv.declare_id(DisplayPage), cv.GenerateID(): cv.declare_id(DisplayPage),
@ -82,7 +89,9 @@ FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
cv.Optional(CONF_TO): cv.use_id(DisplayPage), cv.Optional(CONF_TO): cv.use_id(DisplayPage),
} }
), ),
cv.Optional(CONF_AUTO_CLEAR_ENABLED, default=True): cv.boolean, cv.Optional(
CONF_AUTO_CLEAR_ENABLED, default=CONF_UNSPECIFIED
): validate_auto_clear,
cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean, cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean,
} }
) )
@ -92,8 +101,12 @@ async def setup_display_core_(var, config):
if CONF_ROTATION in config: if CONF_ROTATION in config:
cg.add(var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]])) cg.add(var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]]))
if CONF_AUTO_CLEAR_ENABLED in config: if (auto_clear := config.get(CONF_AUTO_CLEAR_ENABLED)) is not None:
cg.add(var.set_auto_clear(config[CONF_AUTO_CLEAR_ENABLED])) # Default to true if pages or lambda is specified. Ideally this would be done during validation, but
# the possible schemas are too complex to do this easily.
if auto_clear == CONF_UNSPECIFIED:
auto_clear = CONF_LAMBDA in config or CONF_PAGES in config
cg.add(var.set_auto_clear(auto_clear))
if CONF_PAGES in config: if CONF_PAGES in config:
pages = [] pages = []

View File

@ -1,6 +1,6 @@
#include "display.h" #include "display.h"
#include "display_color_utils.h"
#include <utility> #include <utility>
#include "display_color_utils.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@ -266,8 +266,9 @@ void Display::filled_gauge(int center_x, int center_y, int radius1, int radius2,
if (dymax < float(-dxmax) * tan_a) { if (dymax < float(-dxmax) * tan_a) {
upd_dxmax = ceil(float(dymax) / tan_a); upd_dxmax = ceil(float(dymax) / tan_a);
hline_width = -dxmax - upd_dxmax + 1; hline_width = -dxmax - upd_dxmax + 1;
} else } else {
hline_width = 0; hline_width = 0;
}
} }
if (hline_width > 0) if (hline_width > 0)
this->horizontal_line(center_x + dxmax, center_y - dymax, hline_width, color); this->horizontal_line(center_x + dxmax, center_y - dymax, hline_width, color);
@ -662,20 +663,24 @@ void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to)) if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
this->trigger(from, to); this->trigger(from, to);
} }
void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) { void Display::strftime(int x, int y, BaseFont *font, Color color, Color background, TextAlign align, const char *format,
ESPTime time) {
char buffer[64]; char buffer[64];
size_t ret = time.strftime(buffer, sizeof(buffer), format); size_t ret = time.strftime(buffer, sizeof(buffer), format);
if (ret > 0) if (ret > 0)
this->print(x, y, font, color, align, buffer); this->print(x, y, font, color, align, buffer, background);
}
void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) {
this->strftime(x, y, font, color, COLOR_OFF, align, format, time);
} }
void Display::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) { void Display::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) {
this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time); this->strftime(x, y, font, color, COLOR_OFF, TextAlign::TOP_LEFT, format, time);
} }
void Display::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) { void Display::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) {
this->strftime(x, y, font, COLOR_ON, align, format, time); this->strftime(x, y, font, COLOR_ON, COLOR_OFF, align, format, time);
} }
void Display::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) { void Display::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) {
this->strftime(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, time); this->strftime(x, y, font, COLOR_ON, COLOR_OFF, TextAlign::TOP_LEFT, format, time);
} }
void Display::start_clipping(Rect rect) { void Display::start_clipping(Rect rect) {

View File

@ -437,6 +437,20 @@ class Display : public PollingComponent {
*/ */
void printf(int x, int y, BaseFont *font, const char *format, ...) __attribute__((format(printf, 5, 6))); void printf(int x, int y, BaseFont *font, const char *format, ...) __attribute__((format(printf, 5, 6)));
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
*
* @param x The x coordinate of the text alignment anchor point.
* @param y The y coordinate of the text alignment anchor point.
* @param font The font to draw the text with.
* @param color The color to draw the text with.
* @param background The background color to draw the text with.
* @param align The alignment of the text.
* @param format The format to use.
* @param ... The arguments to use for the text formatting.
*/
void strftime(int x, int y, BaseFont *font, Color color, Color background, TextAlign align, const char *format,
ESPTime time) __attribute__((format(strftime, 8, 0)));
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`. /** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
* *
* @param x The x coordinate of the text alignment anchor point. * @param x The x coordinate of the text alignment anchor point.

View File

@ -90,8 +90,9 @@ void Rect::info(const std::string &prefix) {
if (this->is_set()) { if (this->is_set()) {
ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d] (%3d,%3d)", prefix.c_str(), this->x, this->y, this->w, this->h, this->x2(), ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d] (%3d,%3d)", prefix.c_str(), this->x, this->y, this->w, this->h, this->x2(),
this->y2()); this->y2());
} else } else {
ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str()); ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str());
}
} }
} // namespace display } // namespace display

View File

@ -68,8 +68,6 @@ IsActiveCondition = display_menu_base_ns.class_(
"IsActiveCondition", automation.Condition "IsActiveCondition", automation.Condition
) )
MULTI_CONF = True
MenuItemType = display_menu_base_ns.enum("MenuItemType") MenuItemType = display_menu_base_ns.enum("MenuItemType")
MENU_ITEM_TYPES = { MENU_ITEM_TYPES = {

View File

@ -280,7 +280,7 @@ bool DisplayMenuComponent::cursor_down_() {
bool DisplayMenuComponent::enter_menu_() { bool DisplayMenuComponent::enter_menu_() {
this->displayed_item_->on_leave(); this->displayed_item_->on_leave();
this->displayed_item_ = static_cast<MenuItemMenu *>(this->get_selected_item_()); this->displayed_item_ = static_cast<MenuItemMenu *>(this->get_selected_item_());
this->selection_stack_.push_front({this->top_index_, this->cursor_index_}); this->selection_stack_.emplace_front(this->top_index_, this->cursor_index_);
this->cursor_index_ = this->top_index_ = 0; this->cursor_index_ = this->top_index_ = 0;
this->displayed_item_->on_enter(); this->displayed_item_->on_enter();

View File

@ -296,7 +296,7 @@ void Dsmr::dump_config() {
} }
void Dsmr::set_decryption_key(const std::string &decryption_key) { void Dsmr::set_decryption_key(const std::string &decryption_key) {
if (decryption_key.length() == 0) { if (decryption_key.empty()) {
ESP_LOGI(TAG, "Disabling decryption"); ESP_LOGI(TAG, "Disabling decryption");
this->decryption_key_.clear(); this->decryption_key_.clear();
if (this->crypt_telegram_ != nullptr) { if (this->crypt_telegram_ != nullptr) {

View File

View File

@ -0,0 +1,51 @@
import esphome.codegen as cg
from esphome.components import i2c
from esphome.components.audio_adc import AudioAdc
import esphome.config_validation as cv
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE
CODEOWNERS = ["@kahrendt"]
DEPENDENCIES = ["i2c"]
es7210_ns = cg.esphome_ns.namespace("es7210")
ES7210 = es7210_ns.class_("ES7210", AudioAdc, cg.Component, i2c.I2CDevice)
es7210_bits_per_sample = es7210_ns.enum("ES7210BitsPerSample")
ES7210_BITS_PER_SAMPLE_ENUM = {
16: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_16,
24: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_24,
32: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_32,
}
ES7210_MIC_GAINS = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 34.5, 36, 37.5]
_validate_bits = cv.float_with_unit("bits", "bit")
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ES7210),
cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All(
_validate_bits, cv.enum(ES7210_BITS_PER_SAMPLE_ENUM)
),
cv.Optional(CONF_MIC_GAIN, default="24db"): cv.All(
cv.decibel, cv.one_of(*ES7210_MIC_GAINS)
),
cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x40))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE]))
cg.add(var.set_mic_gain(config[CONF_MIC_GAIN]))
cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))

View File

@ -0,0 +1,228 @@
#include "es7210.h"
#include "es7210_const.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace es7210 {
static const char *const TAG = "es7210";
static const size_t MCLK_DIV_FRE = 256;
// Mark the component as failed; use only in setup
#define ES7210_ERROR_FAILED(func) \
if (!(func)) { \
this->mark_failed(); \
return; \
}
// Return false; use outside of setup
#define ES7210_ERROR_CHECK(func) \
if (!(func)) { \
return false; \
}
void ES7210::dump_config() {
ESP_LOGCONFIG(TAG, "ES7210 audio ADC:");
ESP_LOGCONFIG(TAG, " Bits Per Sample: %" PRIu8, this->bits_per_sample_);
ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_rate_);
if (this->is_failed()) {
ESP_LOGE(TAG, " Failed to initialize");
return;
}
}
void ES7210::setup() {
ESP_LOGCONFIG(TAG, "Setting up ES7210...");
// Software reset
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0xff));
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x32));
ES7210_ERROR_FAILED(this->write_byte(ES7210_CLOCK_OFF_REG01, 0x3f));
// Set initialization time when device powers up
ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL0_REG09, 0x30));
ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL1_REG0A, 0x30));
// Configure HFP for all ADC channels
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF2_REG23, 0x2a));
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF1_REG22, 0x0a));
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF2_REG20, 0x0a));
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF1_REG21, 0x2a));
// Secondary I2S mode settings
ES7210_ERROR_FAILED(this->es7210_update_reg_bit_(ES7210_MODE_CONFIG_REG08, 0x01, 0x00));
// Configure analog power
ES7210_ERROR_FAILED(this->write_byte(ES7210_ANALOG_REG40, 0xC3));
// Set mic bias
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_BIAS_REG41, 0x70));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_BIAS_REG42, 0x70));
// Configure I2S settings, sample rate, and microphone gains
ES7210_ERROR_FAILED(this->configure_i2s_format_());
ES7210_ERROR_FAILED(this->configure_sample_rate_());
ES7210_ERROR_FAILED(this->configure_mic_gain_());
// Power on mics 1 through 4
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC1_POWER_REG47, 0x08));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC2_POWER_REG48, 0x08));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC3_POWER_REG49, 0x08));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC4_POWER_REG4A, 0x08));
// Power down DLL
ES7210_ERROR_FAILED(this->write_byte(ES7210_POWER_DOWN_REG06, 0x04));
// Power on MIC1-4 bias & ADC1-4 & PGA1-4 Power
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x0F));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x0F));
// Enable device
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x71));
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x41));
this->setup_complete_ = true;
}
bool ES7210::set_mic_gain(float mic_gain) {
this->mic_gain_ = clamp<float>(mic_gain, ES7210_MIC_GAIN_MIN, ES7210_MIC_GAIN_MAX);
if (this->setup_complete_) {
return this->configure_mic_gain_();
}
return true;
}
bool ES7210::configure_sample_rate_() {
int mclk_fre = this->sample_rate_ * MCLK_DIV_FRE;
int coeff = -1;
for (int i = 0; i < (sizeof(ES7210_COEFFICIENTS) / sizeof(ES7210_COEFFICIENTS[0])); ++i) {
if (ES7210_COEFFICIENTS[i].lrclk == this->sample_rate_ && ES7210_COEFFICIENTS[i].mclk == mclk_fre)
coeff = i;
}
if (coeff >= 0) {
// Set adc_div & doubler & dll
uint8_t regv;
ES7210_ERROR_CHECK(this->read_byte(ES7210_MAINCLK_REG02, &regv));
regv = regv & 0x00;
regv |= ES7210_COEFFICIENTS[coeff].adc_div;
regv |= ES7210_COEFFICIENTS[coeff].doubler << 6;
regv |= ES7210_COEFFICIENTS[coeff].dll << 7;
ES7210_ERROR_CHECK(this->write_byte(ES7210_MAINCLK_REG02, regv));
// Set osr
regv = ES7210_COEFFICIENTS[coeff].osr;
ES7210_ERROR_CHECK(this->write_byte(ES7210_OSR_REG07, regv));
// Set lrck
regv = ES7210_COEFFICIENTS[coeff].lrck_h;
ES7210_ERROR_CHECK(this->write_byte(ES7210_LRCK_DIVH_REG04, regv));
regv = ES7210_COEFFICIENTS[coeff].lrck_l;
ES7210_ERROR_CHECK(this->write_byte(ES7210_LRCK_DIVL_REG05, regv));
} else {
// Invalid sample frequency
ESP_LOGE(TAG, "Invalid sample rate");
return false;
}
return true;
}
bool ES7210::configure_mic_gain_() {
auto regv = this->es7210_gain_reg_value_(this->mic_gain_);
for (uint8_t i = 0; i < 4; ++i) {
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00));
}
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0xff));
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0xff));
// Configure mic 1
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00));
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x10, 0x10));
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x0f, regv));
// Configure mic 2
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00));
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x10, 0x10));
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x0f, regv));
// Configure mic 3
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00));
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x10, 0x10));
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x0f, regv));
// Configure mic 4
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00));
ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00));
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x10, 0x10));
ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x0f, regv));
return true;
}
uint8_t ES7210::es7210_gain_reg_value_(float mic_gain) {
// reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB
mic_gain += 0.5;
if (mic_gain <= 33.0) {
return (uint8_t) mic_gain / 3;
}
if (mic_gain < 36.0) {
return 12;
}
if (mic_gain < 37.0) {
return 13;
}
return 14;
}
bool ES7210::configure_i2s_format_() {
// Configure bits per sample
uint8_t reg_val = 0;
switch (this->bits_per_sample_) {
case ES7210_BITS_PER_SAMPLE_16:
reg_val = 0x60;
break;
case ES7210_BITS_PER_SAMPLE_18:
reg_val = 0x40;
break;
case ES7210_BITS_PER_SAMPLE_20:
reg_val = 0x20;
break;
case ES7210_BITS_PER_SAMPLE_24:
reg_val = 0x00;
break;
case ES7210_BITS_PER_SAMPLE_32:
reg_val = 0x80;
break;
default:
return false;
}
ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE1_REG11, reg_val));
if (this->enable_tdm_) {
ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE2_REG12, 0x02));
} else {
// Microphones 1 and 2 output on SDOUT1, microphones 3 and 4 output on SDOUT2
ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE2_REG12, 0x00));
}
return true;
}
bool ES7210::es7210_update_reg_bit_(uint8_t reg_addr, uint8_t update_bits, uint8_t data) {
uint8_t regv;
ES7210_ERROR_CHECK(this->read_byte(reg_addr, &regv));
regv = (regv & (~update_bits)) | (update_bits & data);
return this->write_byte(reg_addr, regv);
}
} // namespace es7210
} // namespace esphome

View File

@ -0,0 +1,62 @@
#pragma once
#include "esphome/components/audio_adc/audio_adc.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
#include "es7210_const.h"
namespace esphome {
namespace es7210 {
enum ES7210BitsPerSample : uint8_t {
ES7210_BITS_PER_SAMPLE_16 = 16,
ES7210_BITS_PER_SAMPLE_18 = 18,
ES7210_BITS_PER_SAMPLE_20 = 20,
ES7210_BITS_PER_SAMPLE_24 = 24,
ES7210_BITS_PER_SAMPLE_32 = 32,
};
class ES7210 : public audio_adc::AudioAdc, public Component, public i2c::I2CDevice {
/* Class for configuring an ES7210 ADC for microphone input.
* Based on code from:
* - https://github.com/espressif/esp-bsp/ (accessed 20241219)
* - https://github.com/espressif/esp-adf/ (accessed 20241219)
*/
public:
void setup() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void dump_config() override;
void set_bits_per_sample(ES7210BitsPerSample bits_per_sample) { this->bits_per_sample_ = bits_per_sample; }
bool set_mic_gain(float mic_gain) override;
void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; }
float mic_gain() override { return this->mic_gain_; };
protected:
/// @brief Updates an I2C registry address by modifying the current state
/// @param reg_addr I2C register address
/// @param update_bits Mask of allowed bits to be modified
/// @param data Bit values to be written
/// @return True if successful, false otherwise
bool es7210_update_reg_bit_(uint8_t reg_addr, uint8_t update_bits, uint8_t data);
/// @brief Convert floating point mic gain value to register value
/// @param mic_gain Gain value to convert
/// @return Corresponding register value for specified gain
uint8_t es7210_gain_reg_value_(float mic_gain);
bool configure_i2s_format_();
bool configure_mic_gain_();
bool configure_sample_rate_();
bool setup_complete_{false};
bool enable_tdm_{false}; // TDM is unsupported in ESPHome as of version 2024.12
float mic_gain_{0};
ES7210BitsPerSample bits_per_sample_{ES7210_BITS_PER_SAMPLE_16};
uint32_t sample_rate_{0};
};
} // namespace es7210
} // namespace esphome

View File

@ -0,0 +1,129 @@
#pragma once
#include <cinttypes>
namespace esphome {
namespace es7210 {
// ES7210 register addresses
static const uint8_t ES7210_RESET_REG00 = 0x00; /* Reset control */
static const uint8_t ES7210_CLOCK_OFF_REG01 = 0x01; /* Used to turn off the ADC clock */
static const uint8_t ES7210_MAINCLK_REG02 = 0x02; /* Set ADC clock frequency division */
static const uint8_t ES7210_MASTER_CLK_REG03 = 0x03; /* MCLK source $ SCLK division */
static const uint8_t ES7210_LRCK_DIVH_REG04 = 0x04; /* lrck_divh */
static const uint8_t ES7210_LRCK_DIVL_REG05 = 0x05; /* lrck_divl */
static const uint8_t ES7210_POWER_DOWN_REG06 = 0x06; /* power down */
static const uint8_t ES7210_OSR_REG07 = 0x07;
static const uint8_t ES7210_MODE_CONFIG_REG08 = 0x08; /* Set primary/secondary & channels */
static const uint8_t ES7210_TIME_CONTROL0_REG09 = 0x09; /* Set Chip intial state period*/
static const uint8_t ES7210_TIME_CONTROL1_REG0A = 0x0A; /* Set Power up state period */
static const uint8_t ES7210_SDP_INTERFACE1_REG11 = 0x11; /* Set sample & fmt */
static const uint8_t ES7210_SDP_INTERFACE2_REG12 = 0x12; /* Pins state */
static const uint8_t ES7210_ADC_AUTOMUTE_REG13 = 0x13; /* Set mute */
static const uint8_t ES7210_ADC34_MUTERANGE_REG14 = 0x14; /* Set mute range */
static const uint8_t ES7210_ADC12_MUTERANGE_REG15 = 0x15; /* Set mute range */
static const uint8_t ES7210_ADC34_HPF2_REG20 = 0x20; /* HPF */
static const uint8_t ES7210_ADC34_HPF1_REG21 = 0x21; /* HPF */
static const uint8_t ES7210_ADC12_HPF1_REG22 = 0x22; /* HPF */
static const uint8_t ES7210_ADC12_HPF2_REG23 = 0x23; /* HPF */
static const uint8_t ES7210_ANALOG_REG40 = 0x40; /* ANALOG Power */
static const uint8_t ES7210_MIC12_BIAS_REG41 = 0x41;
static const uint8_t ES7210_MIC34_BIAS_REG42 = 0x42;
static const uint8_t ES7210_MIC1_GAIN_REG43 = 0x43;
static const uint8_t ES7210_MIC2_GAIN_REG44 = 0x44;
static const uint8_t ES7210_MIC3_GAIN_REG45 = 0x45;
static const uint8_t ES7210_MIC4_GAIN_REG46 = 0x46;
static const uint8_t ES7210_MIC1_POWER_REG47 = 0x47;
static const uint8_t ES7210_MIC2_POWER_REG48 = 0x48;
static const uint8_t ES7210_MIC3_POWER_REG49 = 0x49;
static const uint8_t ES7210_MIC4_POWER_REG4A = 0x4A;
static const uint8_t ES7210_MIC12_POWER_REG4B = 0x4B; /* MICBias & ADC & PGA Power */
static const uint8_t ES7210_MIC34_POWER_REG4C = 0x4C;
/*
* Clock coefficient structure
*/
struct ES7210Coefficient {
uint32_t mclk; // mclk frequency
uint32_t lrclk;
uint8_t ss_ds;
uint8_t adc_div;
uint8_t dll; // dll_bypass
uint8_t doubler; // doubler_enable
uint8_t osr; // adc osr
uint8_t mclk_src; // sselect mclk source
uint8_t lrck_h; // High 4 bits of lrck
uint8_t lrck_l; // Low 8 bits of lrck
};
/* Codec hifi mclk clock divider coefficients
* MEMBER REG
* mclk: 0x03
* lrck: standard
* ss_ds: --
* adc_div: 0x02
* dll: 0x06
* doubler: 0x02
* osr: 0x07
* mclk_src: 0x03
* lrckh: 0x04
* lrckl: 0x05
*/
static const ES7210Coefficient ES7210_COEFFICIENTS[] = {
// mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl
/* 8k */
{12288000, 8000, 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00},
{16384000, 8000, 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00},
{19200000, 8000, 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60},
{4096000, 8000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
/* 11.025k */
{11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
/* 12k */
{12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
{19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40},
/* 16k */
{4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
{19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80},
{16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00},
{12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00},
/* 22.05k */
{11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
/* 24k */
{12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
{19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20},
/* 32k */
{12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80},
{16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00},
{19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58},
/* 44.1k */
{11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
/* 48k */
{12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00},
{19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90},
/* 64k */
{16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00},
{19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c},
/* 88.2k */
{11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
/* 96k */
{12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80},
{19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8},
};
static const float ES7210_MIC_GAIN_MIN = 0.0;
static const float ES7210_MIC_GAIN_MAX = 37.5;
} // namespace es7210
} // namespace esphome

View File

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