mirror of
https://github.com/esphome/esphome.git
synced 2025-01-30 23:02:14 +01:00
Merge branch 'esphome:dev' into uponor-smatrix-target-temp-sensor
This commit is contained in:
commit
37a11c1e83
36
.clang-tidy
36
.clang-tidy
@ -7,28 +7,39 @@ Checks: >-
|
||||
-boost-*,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-multi-level-implicit-pointer-conversion,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-signed-char-misuse,
|
||||
-bugprone-switch-missing-default-case,
|
||||
-cert-dcl50-cpp,
|
||||
-cert-err33-c,
|
||||
-cert-err58-cpp,
|
||||
-cert-oop57-cpp,
|
||||
-cert-str34-c,
|
||||
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
||||
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||
-clang-analyzer-osx.*,
|
||||
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||
-clang-diagnostic-deprecated-declarations,
|
||||
-clang-diagnostic-ignored-optimization-argument,
|
||||
-clang-diagnostic-missing-field-initializers,
|
||||
-clang-diagnostic-shadow-field,
|
||||
-clang-diagnostic-unused-const-variable,
|
||||
-clang-diagnostic-unused-parameter,
|
||||
-clang-diagnostic-vla-cxx-extension,
|
||||
-concurrency-*,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||
-cppcoreguidelines-avoid-do-while,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-init-variables,
|
||||
-cppcoreguidelines-macro-to-enum,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-missing-std-forward,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-prefer-member-initializer,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
@ -40,7 +51,9 @@ Checks: >-
|
||||
-cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-rvalue-reference-param-not-moved,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
-cppcoreguidelines-use-default-member-init,
|
||||
-cppcoreguidelines-virtual-class-destructor,
|
||||
-fuchsia-multiple-inheritance,
|
||||
-fuchsia-overloaded-operator,
|
||||
@ -60,20 +73,32 @@ Checks: >-
|
||||
-llvm-include-order,
|
||||
-llvm-qualified-auto,
|
||||
-llvmlibc-*,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-const-correctness,
|
||||
-misc-include-cleaner,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-unused-parameters,
|
||||
-modernize-avoid-c-arrays,
|
||||
-misc-use-anonymous-namespace,
|
||||
-modernize-avoid-bind,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-concat-nested-namespaces,
|
||||
-modernize-macro-to-enum,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-type-traits,
|
||||
-modernize-use-auto,
|
||||
-modernize-use-constraints,
|
||||
-modernize-use-default-member-init,
|
||||
-modernize-use-equals-default,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-use-nullptr,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-use-nullptr,
|
||||
-modernize-use-trailing-return-type,
|
||||
-mpi-*,
|
||||
-objc-*,
|
||||
-performance-enum-size,
|
||||
-readability-avoid-nested-conditional-operator,
|
||||
-readability-container-contains,
|
||||
-readability-container-data-pointer,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-else-after-return,
|
||||
@ -82,11 +107,14 @@ Checks: >-
|
||||
-readability-isolate-declaration,
|
||||
-readability-magic-numbers,
|
||||
-readability-make-member-function-const,
|
||||
-readability-named-parameter,
|
||||
-readability-redundant-casting,
|
||||
-readability-redundant-inline-specifier,
|
||||
-readability-redundant-member-init,
|
||||
-readability-redundant-string-init,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-readability-use-anyofallof,
|
||||
WarningsAsErrors: '*'
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: google
|
||||
CheckOptions:
|
||||
- key: google-readability-function-size.StatementThreshold
|
||||
|
4
.github/actions/build-image/action.yaml
vendored
4
.github/actions/build-image/action.yaml
vendored
@ -46,7 +46,7 @@ runs:
|
||||
|
||||
- name: Build and push to ghcr by digest
|
||||
id: build-ghcr
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
uses: docker/build-push-action@v6.13.0
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: false
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
@ -72,7 +72,7 @@ runs:
|
||||
|
||||
- name: Build and push to dockerhub by digest
|
||||
id: build-dockerhub
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
uses: docker/build-push-action@v6.13.0
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: false
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
|
4
.github/actions/restore-python/action.yml
vendored
4
.github/actions/restore-python/action.yml
vendored
@ -17,12 +17,12 @@ runs:
|
||||
steps:
|
||||
- name: Set up Python ${{ inputs.python-version }}
|
||||
id: python
|
||||
uses: actions/setup-python@v5.3.0
|
||||
uses: actions/setup-python@v5.4.0
|
||||
with:
|
||||
python-version: ${{ inputs.python-version }}
|
||||
- name: Restore Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache/restore@v4.1.2
|
||||
uses: actions/cache/restore@v4.2.0
|
||||
with:
|
||||
path: venv
|
||||
# yamllint disable-line rule:line-length
|
||||
|
2
.github/workflows/ci-api-proto.yml
vendored
2
.github/workflows/ci-api-proto.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.3.0
|
||||
uses: actions/setup-python@v5.4.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
|
6
.github/workflows/ci-docker.yml
vendored
6
.github/workflows/ci-docker.yml
vendored
@ -42,13 +42,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.3.0
|
||||
uses: actions/setup-python@v5.4.0
|
||||
with:
|
||||
python-version: "3.9"
|
||||
- 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
|
||||
uses: docker/setup-qemu-action@v3.2.0
|
||||
uses: docker/setup-qemu-action@v3.3.0
|
||||
|
||||
- name: Set TAG
|
||||
run: |
|
||||
|
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@ -13,6 +13,7 @@ on:
|
||||
- ".github/workflows/ci.yml"
|
||||
- "!.yamllint"
|
||||
- "!.github/dependabot.yml"
|
||||
- "!docker/**"
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
@ -30,7 +31,7 @@ concurrency:
|
||||
jobs:
|
||||
common:
|
||||
name: Create common environment
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
||||
steps:
|
||||
@ -41,12 +42,12 @@ jobs:
|
||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@v5.3.0
|
||||
uses: actions/setup-python@v5.4.0
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
- name: Restore Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v4.1.2
|
||||
uses: actions/cache@v4.2.0
|
||||
with:
|
||||
path: venv
|
||||
# yamllint disable-line rule:line-length
|
||||
@ -62,7 +63,7 @@ jobs:
|
||||
|
||||
black:
|
||||
name: Check black
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
steps:
|
||||
@ -83,7 +84,7 @@ jobs:
|
||||
|
||||
flake8:
|
||||
name: Check flake8
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
steps:
|
||||
@ -104,7 +105,7 @@ jobs:
|
||||
|
||||
pylint:
|
||||
name: Check pylint
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
steps:
|
||||
@ -125,7 +126,7 @@ jobs:
|
||||
|
||||
pyupgrade:
|
||||
name: Check pyupgrade
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
steps:
|
||||
@ -146,7 +147,7 @@ jobs:
|
||||
|
||||
ci-custom:
|
||||
name: Run script/ci-custom
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
steps:
|
||||
@ -219,13 +220,13 @@ jobs:
|
||||
. venv/bin/activate
|
||||
pytest -vv --cov-report=xml --tb=native tests
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
clang-format:
|
||||
name: Check clang-format
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
steps:
|
||||
@ -251,7 +252,7 @@ jobs:
|
||||
|
||||
clang-tidy:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- black
|
||||
@ -302,23 +303,18 @@ jobs:
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: actions/cache@v4.1.2
|
||||
uses: actions/cache@v4.2.0
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: platformio-${{ matrix.pio_cache_key }}
|
||||
|
||||
- name: Cache platformio
|
||||
if: github.ref != 'refs/heads/dev'
|
||||
uses: actions/cache/restore@v4.1.2
|
||||
uses: actions/cache/restore@v4.2.0
|
||||
with:
|
||||
path: ~/.platformio
|
||||
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
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
@ -345,7 +341,7 @@ jobs:
|
||||
if: always()
|
||||
|
||||
list-components:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
if: github.event_name == 'pull_request'
|
||||
@ -387,7 +383,7 @@ jobs:
|
||||
|
||||
test-build-components:
|
||||
name: Component test ${{ matrix.file }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- list-components
|
||||
@ -421,7 +417,7 @@ jobs:
|
||||
|
||||
test-build-components-splitter:
|
||||
name: Split components for testing into 20 groups maximum
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- list-components
|
||||
@ -439,7 +435,7 @@ jobs:
|
||||
|
||||
test-build-components-split:
|
||||
name: Test split components
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- list-components
|
||||
@ -483,7 +479,7 @@ jobs:
|
||||
|
||||
ci-status:
|
||||
name: CI Status
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- black
|
||||
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@ -53,7 +53,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.3.0
|
||||
uses: actions/setup-python@v5.4.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Set up python environment
|
||||
@ -65,7 +65,7 @@ jobs:
|
||||
pip3 install build
|
||||
python3 -m build
|
||||
- name: Publish
|
||||
uses: pypa/gh-action-pypi-publish@v1.12.2
|
||||
uses: pypa/gh-action-pypi-publish@v1.12.4
|
||||
|
||||
deploy-docker:
|
||||
name: Build ESPHome ${{ matrix.platform }}
|
||||
@ -85,15 +85,15 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5.3.0
|
||||
uses: actions/setup-python@v5.4.0
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- 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
|
||||
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
|
||||
uses: docker/login-action@v3.3.0
|
||||
@ -141,7 +141,7 @@ jobs:
|
||||
echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload digests
|
||||
uses: actions/upload-artifact@v4.4.3
|
||||
uses: actions/upload-artifact@v4.6.0
|
||||
with:
|
||||
name: digests-${{ steps.sanitize.outputs.name }}
|
||||
path: /tmp/digests
|
||||
@ -184,7 +184,7 @@ jobs:
|
||||
merge-multiple: true
|
||||
|
||||
- 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
|
||||
if: matrix.registry == 'dockerhub'
|
||||
|
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9.0.0
|
||||
- uses: actions/stale@v9.1.0
|
||||
with:
|
||||
days-before-pr-stale: 90
|
||||
days-before-pr-close: 7
|
||||
@ -37,7 +37,7 @@ jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9.0.0
|
||||
- uses: actions/stale@v9.1.0
|
||||
with:
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
|
4
.github/workflows/sync-device-classes.yml
vendored
4
.github/workflows/sync-device-classes.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
path: lib/home-assistant
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5.3.0
|
||||
uses: actions/setup-python@v5.4.0
|
||||
with:
|
||||
python-version: 3.12
|
||||
|
||||
@ -36,7 +36,7 @@ jobs:
|
||||
python ./script/sync-device_class.py
|
||||
|
||||
- name: Commit changes
|
||||
uses: peter-evans/create-pull-request@v7.0.5
|
||||
uses: peter-evans/create-pull-request@v7.0.6
|
||||
with:
|
||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||
committer: esphomebot <esphome@nabucasa.com>
|
||||
|
@ -11,14 +11,6 @@ repos:
|
||||
args: [--fix]
|
||||
# Run the formatter.
|
||||
- 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
|
||||
rev: 6.1.0
|
||||
hooks:
|
||||
@ -53,6 +45,6 @@ repos:
|
||||
hooks:
|
||||
- id: pylint
|
||||
name: pylint
|
||||
entry: script/run-in-env.sh pylint
|
||||
language: script
|
||||
entry: python script/run-in-env pylint
|
||||
language: system
|
||||
types: [python]
|
||||
|
12
CODEOWNERS
12
CODEOWNERS
@ -49,6 +49,7 @@ esphome/components/atc_mithermometer/* @ahpohl
|
||||
esphome/components/atm90e26/* @danieltwagner
|
||||
esphome/components/atm90e32/* @circuitsetup @descipher
|
||||
esphome/components/audio/* @kahrendt
|
||||
esphome/components/audio_adc/* @kbx81
|
||||
esphome/components/audio_dac/* @kbx81
|
||||
esphome/components/axs15231/* @clydebarrow
|
||||
esphome/components/b_parasite/* @rbaron
|
||||
@ -131,6 +132,9 @@ esphome/components/ens160_base/* @latonita @vincentscode
|
||||
esphome/components/ens160_i2c/* @latonita
|
||||
esphome/components/ens160_spi/* @latonita
|
||||
esphome/components/ens210/* @itn3rd77
|
||||
esphome/components/es7210/* @kahrendt
|
||||
esphome/components/es7243e/* @kbx81
|
||||
esphome/components/es8156/* @kbx81
|
||||
esphome/components/es8311/* @kahrendt @kroimon
|
||||
esphome/components/esp32/* @esphome/core
|
||||
esphome/components/esp32_ble/* @Rapsssito @jesserockz
|
||||
@ -144,6 +148,7 @@ esphome/components/esp32_rmt_led_strip/* @jesserockz
|
||||
esphome/components/esp8266/* @esphome/core
|
||||
esphome/components/ethernet_info/* @gtjadsonsantos
|
||||
esphome/components/event/* @nohat
|
||||
esphome/components/event_emitter/* @Rapsssito
|
||||
esphome/components/exposure_notifications/* @OttoWinter
|
||||
esphome/components/ezo/* @ssieb
|
||||
esphome/components/ezo_pmp/* @carlos-sarmiento
|
||||
@ -179,6 +184,7 @@ esphome/components/haier/text_sensor/* @paveldn
|
||||
esphome/components/havells_solar/* @sourabhjaiswal
|
||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||
esphome/components/hbridge/light/* @DotNetDann
|
||||
esphome/components/hbridge/switch/* @dwmw2
|
||||
esphome/components/he60r/* @clydebarrow
|
||||
esphome/components/heatpumpir/* @rob-deutsch
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
@ -301,7 +307,7 @@ esphome/components/noblex/* @AGalfra
|
||||
esphome/components/npi19/* @bakerkj
|
||||
esphome/components/number/* @esphome/core
|
||||
esphome/components/one_wire/* @ssieb
|
||||
esphome/components/online_image/* @guillempages
|
||||
esphome/components/online_image/* @clydebarrow @guillempages
|
||||
esphome/components/opentherm/* @olegtarasov
|
||||
esphome/components/ota/* @esphome/core
|
||||
esphome/components/output/* @esphome/core
|
||||
@ -337,7 +343,6 @@ esphome/components/radon_eye_rd200/* @jeffeb3
|
||||
esphome/components/rc522/* @glmnet
|
||||
esphome/components/rc522_i2c/* @glmnet
|
||||
esphome/components/rc522_spi/* @glmnet
|
||||
esphome/components/resistance_sampler/* @jesserockz
|
||||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/rf_bridge/* @jesserockz
|
||||
esphome/components/rgbct/* @jesserockz
|
||||
@ -354,6 +359,8 @@ esphome/components/sdl/* @clydebarrow
|
||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||
esphome/components/sdp3x/* @Azimath
|
||||
esphome/components/seeed_mr24hpc1/* @limengdu
|
||||
esphome/components/seeed_mr60bha2/* @limengdu
|
||||
esphome/components/seeed_mr60fda2/* @limengdu
|
||||
esphome/components/selec_meter/* @sourabhjaiswal
|
||||
esphome/components/select/* @esphome/core
|
||||
esphome/components/sen0321/* @notjj
|
||||
@ -408,6 +415,7 @@ esphome/components/substitutions/* @esphome/core
|
||||
esphome/components/sun/* @OttoWinter
|
||||
esphome/components/sun_gtil2/* @Mat931
|
||||
esphome/components/switch/* @esphome/core
|
||||
esphome/components/switch/binary_sensor/* @ssieb
|
||||
esphome/components/t6615/* @tylermenezes
|
||||
esphome/components/tc74/* @sethgirvan
|
||||
esphome/components/tca9548a/* @andreashergert1984
|
||||
|
@ -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 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/
|
||||
|
||||
|
@ -29,7 +29,7 @@ RUN \
|
||||
# Use pinned versions so that we get updates with build caching
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
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-wheel=0.38.4-2 \
|
||||
iputils-ping=3:20221126-1+deb12u1 \
|
||||
@ -99,15 +99,17 @@ BUILD_DEPS="
|
||||
libfreetype-dev=2.12.1+dfsg-5+deb12u3
|
||||
libssl-dev=3.0.15-1~deb12u1
|
||||
libffi-dev=3.4.4-1
|
||||
libopenjp2-7=2.5.0-2
|
||||
libtiff6=4.5.0-6+deb12u1
|
||||
cargo=0.66.0+ds1-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" ]
|
||||
then
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends $BUILD_DEPS
|
||||
apt-get install -y --no-install-recommends $BUILD_DEPS $LIB_DEPS
|
||||
fi
|
||||
|
||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo
|
||||
@ -161,6 +163,18 @@ ENTRYPOINT ["/entrypoint.sh"]
|
||||
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 =======================
|
||||
@ -192,7 +206,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
||||
# Labels
|
||||
LABEL \
|
||||
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.version="${BUILD_VERSION}"
|
||||
# io.hass.arch is inherited from addon-debian-base
|
||||
@ -207,17 +221,22 @@ ENV \
|
||||
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
|
||||
|
||||
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
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
clang-format-13=1:13.0.1-11+b2 \
|
||||
clang-tidy-14=1:14.0.6-12 \
|
||||
patch=2.7.6-7 \
|
||||
software-properties-common=0.99.30-4.1~deb12u1 \
|
||||
nano=7.2-1+deb12u1 \
|
||||
build-essential=12.9 \
|
||||
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/* \
|
||||
/var/{cache,log}/* \
|
||||
/var/lib/apt/lists/*
|
||||
|
@ -20,6 +20,8 @@ from esphome.const import (
|
||||
CONF_DEASSERT_RTS_DTR,
|
||||
CONF_DISABLED,
|
||||
CONF_ESPHOME,
|
||||
CONF_LEVEL,
|
||||
CONF_LOG_TOPIC,
|
||||
CONF_LOGGER,
|
||||
CONF_MDNS,
|
||||
CONF_MQTT,
|
||||
@ -30,6 +32,7 @@ from esphome.const import (
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
CONF_PORT,
|
||||
CONF_SUBSTITUTIONS,
|
||||
CONF_TOPIC,
|
||||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
@ -95,8 +98,12 @@ def choose_upload_log_host(
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if default == "OTA":
|
||||
return CORE.address
|
||||
if show_mqtt and CONF_MQTT in CORE.config:
|
||||
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
|
||||
if (
|
||||
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":
|
||||
return "MQTT"
|
||||
if default is not None:
|
||||
@ -106,6 +113,17 @@ def choose_upload_log_host(
|
||||
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):
|
||||
if port.startswith("/") or port.startswith("COM"):
|
||||
return "SERIAL"
|
||||
@ -345,7 +363,7 @@ def upload_program(config, args, host):
|
||||
|
||||
from esphome import espota2
|
||||
|
||||
remote_port = ota_conf[CONF_PORT]
|
||||
remote_port = int(ota_conf[CONF_PORT])
|
||||
password = ota_conf.get(CONF_PASSWORD, "")
|
||||
|
||||
if (
|
||||
@ -740,6 +758,14 @@ def parse_args(argv):
|
||||
options_parser.add_argument(
|
||||
"-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(
|
||||
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
||||
)
|
||||
@ -969,11 +995,16 @@ def run_esphome(argv):
|
||||
args = parse_args(argv)
|
||||
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(
|
||||
args.verbose,
|
||||
args.quiet,
|
||||
log_level=args.log_level,
|
||||
# Show timestamp for dashboard access logs
|
||||
args.command == "dashboard",
|
||||
include_timestamp=args.command == "dashboard",
|
||||
)
|
||||
|
||||
if args.command in PRE_CONFIG_ACTIONS:
|
||||
|
@ -1,11 +1,6 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER
|
||||
|
||||
from esphome.core import CORE
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
from esphome.const import PLATFORM_ESP8266
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32C2,
|
||||
@ -15,6 +10,9 @@ from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32S2,
|
||||
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"]
|
||||
|
||||
@ -102,11 +100,11 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
||||
6: adc1_channel_t.ADC1_CHANNEL_6,
|
||||
},
|
||||
VARIANT_ESP32H2: {
|
||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||
2: adc1_channel_t.ADC1_CHANNEL_2,
|
||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||
1: adc1_channel_t.ADC1_CHANNEL_0,
|
||||
2: adc1_channel_t.ADC1_CHANNEL_1,
|
||||
3: adc1_channel_t.ADC1_CHANNEL_2,
|
||||
4: adc1_channel_t.ADC1_CHANNEL_3,
|
||||
5: adc1_channel_t.ADC1_CHANNEL_4,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,12 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_adc_cal.h>
|
||||
#include "driver/adc.h"
|
||||
#endif
|
||||
#endif // USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
@ -43,7 +42,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
this->channel1_ = ADC1_CHANNEL_MAX;
|
||||
}
|
||||
void set_autorange(bool autorange) { this->autorange_ = autorange; }
|
||||
#endif
|
||||
#endif // USE_ESP32
|
||||
|
||||
/// Update ADC values
|
||||
void update() override;
|
||||
@ -59,11 +58,11 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
std::string unique_id() override;
|
||||
#endif
|
||||
#endif // USE_ESP8266
|
||||
|
||||
#ifdef USE_RP2040
|
||||
void set_is_temperature() { this->is_temperature_ = true; }
|
||||
#endif
|
||||
#endif // USE_RP2040
|
||||
|
||||
protected:
|
||||
InternalGPIOPin *pin_;
|
||||
@ -72,7 +71,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
|
||||
#ifdef USE_RP2040
|
||||
bool is_temperature_{false};
|
||||
#endif
|
||||
#endif // USE_RP2040
|
||||
|
||||
#ifdef USE_ESP32
|
||||
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] = {};
|
||||
#else
|
||||
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
|
||||
#endif
|
||||
#endif
|
||||
#endif // ESP_IDF_VERSION_MAJOR
|
||||
#endif // USE_ESP32
|
||||
};
|
||||
|
||||
} // namespace adc
|
||||
|
24
esphome/components/adc/adc_sensor_common.cpp
Normal file
24
esphome/components/adc/adc_sensor_common.cpp
Normal 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
|
@ -1,30 +1,13 @@
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "adc_sensor.h"
|
||||
#include "esphome/core/helpers.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 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);
|
||||
|
||||
#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;
|
||||
#else
|
||||
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
|
||||
#endif
|
||||
#endif
|
||||
#endif // USE_ESP32_VARIANT_ESP32S2
|
||||
#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_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
|
||||
#endif
|
||||
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
|
||||
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;
|
||||
|
||||
#ifdef USE_RP2040
|
||||
extern "C"
|
||||
#endif
|
||||
void
|
||||
ADCSensor::setup() {
|
||||
void ADCSensor::setup() {
|
||||
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) {
|
||||
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
||||
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++) {
|
||||
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,
|
||||
@ -79,31 +52,10 @@ extern "C"
|
||||
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() {
|
||||
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_);
|
||||
if (this->autorange_) {
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
||||
@ -125,55 +77,10 @@ void ADCSensor::dump_config() {
|
||||
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_);
|
||||
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() {
|
||||
if (!this->autorange_) {
|
||||
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 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 c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
|
||||
uint32_t c2 = ADC_HALF - std::abs(raw2 - 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;
|
||||
|
||||
// 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);
|
||||
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 esphome
|
||||
|
||||
#endif // USE_ESP32
|
58
esphome/components/adc/adc_sensor_esp8266.cpp
Normal file
58
esphome/components/adc/adc_sensor_esp8266.cpp
Normal 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
|
48
esphome/components/adc/adc_sensor_libretiny.cpp
Normal file
48
esphome/components/adc/adc_sensor_libretiny.cpp
Normal 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
|
93
esphome/components/adc/adc_sensor_rp2040.cpp
Normal file
93
esphome/components/adc/adc_sensor_rp2040.cpp
Normal 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
|
@ -9,8 +9,6 @@ static const char *const TAG = "ads1115";
|
||||
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
|
||||
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() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
|
||||
uint16_t value;
|
||||
@ -43,9 +41,9 @@ void ADS1115Component::setup() {
|
||||
config |= 0b0000000100000000;
|
||||
}
|
||||
|
||||
// Set data rate - 860 samples per second (we're in singleshot mode)
|
||||
// Set data rate - 860 samples per second
|
||||
// 0bxxxxxxxx100xxxxx
|
||||
config |= ADS1115_DATA_RATE_860_SPS << 5;
|
||||
config |= ADS1115_860SPS << 5;
|
||||
|
||||
// Set comparator mode - hysteresis
|
||||
// 0bxxxxxxxxxxx0xxxx
|
||||
@ -77,7 +75,7 @@ void ADS1115Component::dump_config() {
|
||||
}
|
||||
}
|
||||
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
|
||||
ADS1115Resolution resolution) {
|
||||
ADS1115Resolution resolution, ADS1115Samplerate samplerate) {
|
||||
uint16_t config = this->prev_config_;
|
||||
// Multiplexer
|
||||
// 0bxBBBxxxxxxxxxxxx
|
||||
@ -89,6 +87,11 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
|
||||
config &= 0b1111000111111111;
|
||||
config |= (gain & 0b111) << 9;
|
||||
|
||||
// Sample rate
|
||||
// 0bxxxxxxxxBBBxxxxx
|
||||
config &= 0b1111111100011111;
|
||||
config |= (samplerate & 0b111) << 5;
|
||||
|
||||
if (!this->continuous_mode_) {
|
||||
// Start conversion
|
||||
config |= 0b1000000000000000;
|
||||
@ -101,8 +104,54 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
|
||||
}
|
||||
this->prev_config_ = config;
|
||||
|
||||
// about 1.2 ms with 860 samples per second
|
||||
delay(2);
|
||||
// Delay calculated as: ceil((1000/SPS)+.5)
|
||||
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
|
||||
// to ensure conversion is taking place with the correct settings
|
||||
|
@ -33,6 +33,17 @@ enum ADS1115Resolution {
|
||||
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 {
|
||||
public:
|
||||
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; }
|
||||
|
||||
/// 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:
|
||||
uint16_t prev_config_{0};
|
||||
|
@ -5,6 +5,7 @@ from esphome.const import (
|
||||
CONF_GAIN,
|
||||
CONF_MULTIPLEXER,
|
||||
CONF_RESOLUTION,
|
||||
CONF_SAMPLE_RATE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
@ -43,6 +44,17 @@ RESOLUTION = {
|
||||
"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", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
||||
@ -64,6 +76,9 @@ CONFIG_SCHEMA = (
|
||||
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
|
||||
RESOLUTION, upper=True, space="_"
|
||||
),
|
||||
cv.Optional(CONF_SAMPLE_RATE, default="860"): cv.enum(
|
||||
SAMPLERATE, string=True
|
||||
),
|
||||
}
|
||||
)
|
||||
.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_gain(config[CONF_GAIN]))
|
||||
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
||||
cg.add(var.set_samplerate(config[CONF_SAMPLE_RATE]))
|
||||
|
@ -8,7 +8,7 @@ namespace ads1115 {
|
||||
static const char *const TAG = "ads1115.sensor";
|
||||
|
||||
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() {
|
||||
@ -24,6 +24,7 @@ void ADS1115Sensor::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
|
||||
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
|
||||
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
|
||||
ESP_LOGCONFIG(TAG, " Sample rate: %u", this->samplerate_);
|
||||
}
|
||||
|
||||
} // namespace ads1115
|
||||
|
@ -21,6 +21,7 @@ class ADS1115Sensor : public sensor::Sensor,
|
||||
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
|
||||
void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
|
||||
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
|
||||
void set_samplerate(ADS1115Samplerate samplerate) { this->samplerate_ = samplerate; }
|
||||
float sample() override;
|
||||
|
||||
void dump_config() override;
|
||||
@ -29,6 +30,7 @@ class ADS1115Sensor : public sensor::Sensor,
|
||||
ADS1115Multiplexer multiplexer_;
|
||||
ADS1115Gain gain_;
|
||||
ADS1115Resolution resolution_;
|
||||
ADS1115Samplerate samplerate_;
|
||||
};
|
||||
|
||||
} // namespace ads1115
|
||||
|
@ -72,10 +72,9 @@ void AlarmControlPanelCall::validate_() {
|
||||
this->state_.reset();
|
||||
return;
|
||||
}
|
||||
if (state == ACP_STATE_DISARMED &&
|
||||
!(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_TRIGGERED)) {
|
||||
if (state == ACP_STATE_DISARMED && !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_TRIGGERED) {
|
||||
ESP_LOGW(TAG, "Cannot disarm when not armed");
|
||||
this->state_.reset();
|
||||
return;
|
||||
|
@ -1,29 +1,10 @@
|
||||
import logging
|
||||
|
||||
from esphome import automation, core
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import font
|
||||
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
|
||||
from esphome.const import (
|
||||
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
|
||||
from esphome.const import CONF_ID, CONF_REPEAT
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -31,6 +12,7 @@ AUTO_LOAD = ["image"]
|
||||
CODEOWNERS = ["@syndlex"]
|
||||
DEPENDENCIES = ["display"]
|
||||
MULTI_CONF = True
|
||||
MULTI_CONF_NO_DEFAULT = True
|
||||
|
||||
CONF_LOOP = "loop"
|
||||
CONF_START_FRAME = "start_frame"
|
||||
@ -52,86 +34,19 @@ SetFrameAction = animation_ns.class_(
|
||||
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||
)
|
||||
|
||||
TYPED_FILE_SCHEMA = cv.typed_schema(
|
||||
CONFIG_SCHEMA = espImage.IMAGE_SCHEMA.extend(
|
||||
{
|
||||
SOURCE_LOCAL: LOCAL_SCHEMA,
|
||||
SOURCE_WEB: WEB_SCHEMA,
|
||||
},
|
||||
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(
|
||||
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
||||
cv.Optional(CONF_LOOP): cv.All(
|
||||
{
|
||||
CONF_SOURCE: SOURCE_WEB,
|
||||
CONF_URL: value,
|
||||
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,
|
||||
}
|
||||
)
|
||||
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(
|
||||
{
|
||||
@ -165,180 +80,26 @@ async def animation_action_to_code(config, action_id, template_arg, args):
|
||||
|
||||
|
||||
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(
|
||||
config[CONF_ID],
|
||||
prog_arr,
|
||||
width,
|
||||
height,
|
||||
frames,
|
||||
espImage.IMAGE_TYPE[config[CONF_TYPE]],
|
||||
frame_count,
|
||||
image_type,
|
||||
trans_value,
|
||||
)
|
||||
cg.add(var.set_transparency(transparent))
|
||||
if loop_config := config.get(CONF_LOOP):
|
||||
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)
|
||||
cg.add(var.set_loop(start, end, count))
|
||||
|
@ -6,8 +6,8 @@ namespace esphome {
|
||||
namespace animation {
|
||||
|
||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
|
||||
image::ImageType type)
|
||||
: Image(data_start, width, height, type),
|
||||
image::ImageType type, image::Transparency transparent)
|
||||
: Image(data_start, width, height, type, transparent),
|
||||
animation_data_start_(data_start),
|
||||
current_frame_(0),
|
||||
animation_frame_count_(animation_frame_count),
|
||||
|
@ -8,7 +8,8 @@ namespace animation {
|
||||
|
||||
class Animation : public image::Image {
|
||||
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;
|
||||
int get_current_frame() const;
|
||||
|
@ -122,7 +122,8 @@ void APDS9306::update() {
|
||||
|
||||
this->status_clear_warning();
|
||||
|
||||
if (!(status &= 0b00001000)) { // No new data
|
||||
status &= 0b00001000;
|
||||
if (!status) { // No new data
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1381,6 +1381,7 @@ message BluetoothConnectionsFreeResponse {
|
||||
|
||||
uint32 free = 1;
|
||||
uint32 limit = 2;
|
||||
repeated uint64 allocated = 3;
|
||||
}
|
||||
|
||||
message BluetoothGATTErrorResponse {
|
||||
|
@ -6430,6 +6430,10 @@ bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVar
|
||||
this->limit = value.as_uint32();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->allocated.push_back(value.as_uint64());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -6437,6 +6441,9 @@ bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVar
|
||||
void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_uint32(1, this->free);
|
||||
buffer.encode_uint32(2, this->limit);
|
||||
for (auto &it : this->allocated) {
|
||||
buffer.encode_uint64(3, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
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);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
for (const auto &it : this->allocated) {
|
||||
out.append(" allocated: ");
|
||||
sprintf(buffer, "%llu", it);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
@ -1624,6 +1624,7 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t free{0};
|
||||
uint32_t limit{0};
|
||||
std::vector<uint64_t> allocated{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace audio {
|
||||
|
41
esphome/components/audio_adc/__init__.py
Normal file
41
esphome/components/audio_adc/__init__.py
Normal 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)
|
17
esphome/components/audio_adc/audio_adc.h
Normal file
17
esphome/components/audio_adc/audio_adc.h
Normal 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
|
23
esphome/components/audio_adc/automation.h
Normal file
23
esphome/components/audio_adc/automation.h
Normal 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
|
@ -58,7 +58,7 @@ class BinarySensor : public EntityBase, public EntityBase_DeviceClass {
|
||||
void publish_initial_state(bool state);
|
||||
|
||||
/// The current reported state of the binary sensor.
|
||||
bool state;
|
||||
bool state{false};
|
||||
|
||||
void add_filter(Filter *filter);
|
||||
void add_filters(const std::vector<Filter *> &filters);
|
||||
|
@ -15,10 +15,11 @@ from esphome.components.libretiny.const import (
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
from .boards import BK72XX_BOARDS, BK72XX_BOARD_PINS
|
||||
from .boards import BK72XX_BOARD_PINS, BK72XX_BOARDS
|
||||
|
||||
CODEOWNERS = ["@kuba2k2"]
|
||||
AUTO_LOAD = ["libretiny"]
|
||||
IS_TARGET_PLATFORM = True
|
||||
|
||||
COMPONENT_DATA = LibreTinyComponent(
|
||||
name=COMPONENT_BK72XX,
|
||||
|
@ -25,8 +25,7 @@ void BLEClient::loop() {
|
||||
|
||||
void BLEClient::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "BLE Client:");
|
||||
ESP_LOGCONFIG(TAG, " Address: %s", this->address_str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Auto-Connect: %s", TRUEFALSE(this->auto_connect_));
|
||||
BLEClientBase::dump_config();
|
||||
}
|
||||
|
||||
bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
|
||||
|
@ -11,6 +11,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_DECIBEL_MILLIWATT,
|
||||
CONF_NOTIFY,
|
||||
)
|
||||
|
||||
from .. import ble_client_ns
|
||||
@ -19,7 +20,6 @@ DEPENDENCIES = ["ble_client"]
|
||||
|
||||
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
|
||||
|
||||
CONF_NOTIFY = "notify"
|
||||
CONF_ON_NOTIFY = "on_notify"
|
||||
TYPE_CHARACTERISTIC = "characteristic"
|
||||
TYPE_RSSI = "rssi"
|
||||
|
@ -6,6 +6,7 @@ from esphome.const import (
|
||||
CONF_CHARACTERISTIC_UUID,
|
||||
CONF_ID,
|
||||
CONF_SERVICE_UUID,
|
||||
CONF_NOTIFY,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
|
||||
@ -15,7 +16,6 @@ DEPENDENCIES = ["ble_client"]
|
||||
|
||||
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
|
||||
|
||||
CONF_NOTIFY = "notify"
|
||||
CONF_ON_NOTIFY = "on_notify"
|
||||
|
||||
adv_data_t = cg.std_vector.template(cg.uint8)
|
||||
|
@ -13,6 +13,11 @@ namespace bluetooth_proxy {
|
||||
|
||||
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,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
if (!BLEClientBase::gattc_event_handler(event, gattc_if, param))
|
||||
|
@ -11,6 +11,7 @@ class BluetoothProxy;
|
||||
|
||||
class BluetoothConnection : public esp32_ble_client::BLEClientBase {
|
||||
public:
|
||||
void dump_config() override;
|
||||
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
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;
|
||||
|
@ -475,6 +475,11 @@ void BluetoothProxy::send_connections_free() {
|
||||
api::BluetoothConnectionsFreeResponse call;
|
||||
call.free = this->get_bluetooth_connections_free();
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -204,11 +204,11 @@ void BME68xBSEC2Component::update_subscription_() {
|
||||
}
|
||||
|
||||
void BME68xBSEC2Component::run_() {
|
||||
this->op_mode_ = this->bsec_settings_.op_mode;
|
||||
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;
|
||||
}
|
||||
this->op_mode_ = this->bsec_settings_.op_mode;
|
||||
uint8_t status;
|
||||
|
||||
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_);
|
||||
return;
|
||||
}
|
||||
this->next_call_ns_ = this->bsec_settings_.next_call;
|
||||
|
||||
if (this->bsec_settings_.trigger_measurement) {
|
||||
bme68x_get_conf(&bme68x_conf, &this->bme68x_);
|
||||
switch (this->bsec_settings_.op_mode) {
|
||||
case BME68X_FORCED_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_);
|
||||
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_);
|
||||
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_.heatr_temp = this->bsec_settings_.heater_temperature;
|
||||
this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration;
|
||||
this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile;
|
||||
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_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");
|
||||
status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
|
||||
|
||||
break;
|
||||
case BME68X_PARALLEL_MODE:
|
||||
if (this->op_mode_ != this->bsec_settings_.op_mode) {
|
||||
this->bme68x_heatr_conf_.enable = BME68X_ENABLE;
|
||||
this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile;
|
||||
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_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_);
|
||||
|
||||
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;
|
||||
}
|
||||
status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_);
|
||||
this->op_mode_ = BME68X_PARALLEL_MODE;
|
||||
ESP_LOGV(TAG, "Using parallel mode");
|
||||
}
|
||||
break;
|
||||
case BME68X_SLEEP_MODE:
|
||||
if (this->op_mode_ != this->bsec_settings_.op_mode) {
|
||||
bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_);
|
||||
this->op_mode_ = BME68X_SLEEP_MODE;
|
||||
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;
|
||||
meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_);
|
||||
ESP_LOGV(TAG, "Queueing read in %uus", meas_dur);
|
||||
|
@ -113,13 +113,11 @@ class BME68xBSEC2Component : public Component {
|
||||
|
||||
struct bme68x_heatr_conf bme68x_heatr_conf_;
|
||||
uint8_t op_mode_; // operating mode of sensor
|
||||
bool sleep_mode_;
|
||||
bsec_library_return_t bsec_status_{BSEC_OK};
|
||||
int8_t bme68x_status_{BME68X_OK};
|
||||
|
||||
int64_t last_time_ms_{0};
|
||||
uint32_t millis_overflow_counter_{0};
|
||||
int64_t next_call_ns_{0};
|
||||
|
||||
std::queue<std::function<void()>> queue_;
|
||||
|
||||
|
@ -115,7 +115,7 @@ CONF_MAX_HUMIDITY = "max_humidity"
|
||||
CONF_TARGET_HUMIDITY = "target_humidity"
|
||||
|
||||
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)?"
|
||||
)
|
||||
|
||||
|
||||
|
@ -37,8 +37,9 @@ void ClimateIR::setup() {
|
||||
this->publish_state();
|
||||
});
|
||||
this->current_temperature = this->sensor_->state;
|
||||
} else
|
||||
} else {
|
||||
this->current_temperature = NAN;
|
||||
}
|
||||
// restore set points
|
||||
auto restore = this->restore_state_();
|
||||
if (restore.has_value()) {
|
||||
|
@ -131,8 +131,9 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei
|
||||
} else {
|
||||
parent->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
parent->mode = climate::CLIMATE_MODE_COOL;
|
||||
}
|
||||
|
||||
// Fan Speed
|
||||
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL ||
|
||||
|
@ -43,7 +43,7 @@ bool CSE7766Component::check_byte_() {
|
||||
uint8_t index = this->raw_data_index_;
|
||||
uint8_t byte = this->raw_data_[index];
|
||||
if (index == 0) {
|
||||
return !((byte != 0x55) && ((byte & 0xF0) != 0xF0) && (byte != 0xAA));
|
||||
return (byte == 0x55) || ((byte & 0xF0) == 0xF0) || (byte == 0xAA);
|
||||
}
|
||||
|
||||
if (index == 1) {
|
||||
|
@ -1,3 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
custom_ns = cg.esphome_ns.namespace("custom")
|
@ -1,31 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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.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()
|
||||
),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -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
|
@ -1,30 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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")
|
||||
CONF_CLIMATES = "climates"
|
||||
|
||||
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),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -1,30 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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")
|
||||
CONF_COVERS = "covers"
|
||||
|
||||
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),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -1,30 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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")
|
||||
CONF_LIGHTS = "lights"
|
||||
|
||||
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),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -1,61 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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")
|
||||
CustomFloatOutputConstructor = custom_ns.class_("CustomFloatOutputConstructor")
|
||||
|
||||
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,
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -1,27 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.sensor_schema()),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -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
|
@ -1,27 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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.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)),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -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
|
@ -1,32 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
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.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()
|
||||
),
|
||||
}
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -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
|
@ -1,31 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
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
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CustomComponentConstructor),
|
||||
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
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
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'
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
@ -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
|
@ -298,6 +298,12 @@ void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
|
||||
if (this->cell_16_voltage_sensor_) {
|
||||
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;
|
||||
|
@ -54,6 +54,8 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
|
||||
SUB_SENSOR(cell_14_voltage)
|
||||
SUB_SENSOR(cell_15_voltage)
|
||||
SUB_SENSOR(cell_16_voltage)
|
||||
SUB_SENSOR(cell_17_voltage)
|
||||
SUB_SENSOR(cell_18_voltage)
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
|
@ -52,6 +52,8 @@ CONF_CELL_13_VOLTAGE = "cell_13_voltage"
|
||||
CONF_CELL_14_VOLTAGE = "cell_14_voltage"
|
||||
CONF_CELL_15_VOLTAGE = "cell_15_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_BATTERY_OUTLINE = "mdi:battery-outline"
|
||||
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
|
||||
@ -92,6 +94,8 @@ TYPES = [
|
||||
CONF_CELL_14_VOLTAGE,
|
||||
CONF_CELL_15_VOLTAGE,
|
||||
CONF_CELL_16_VOLTAGE,
|
||||
CONF_CELL_17_VOLTAGE,
|
||||
CONF_CELL_18_VOLTAGE,
|
||||
]
|
||||
|
||||
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_15_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)
|
||||
)
|
||||
|
@ -60,9 +60,7 @@ ESPTime DateTimeEntity::state_as_esptime() const {
|
||||
obj.hour = this->hour_;
|
||||
obj.minute = this->minute_;
|
||||
obj.second = this->second_;
|
||||
obj.day_of_week = 1; // Required to be valid for recalc_timestamp_local but not used.
|
||||
obj.day_of_year = 1; // Required to be valid for recalc_timestamp_local but not used.
|
||||
obj.recalc_timestamp_local(false);
|
||||
obj.recalc_timestamp_local();
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,10 @@ void DebugComponent::dump_config() {
|
||||
this->reset_reason_->publish_state(get_reset_reason_());
|
||||
}
|
||||
#endif // USE_TEXT_SENSOR
|
||||
|
||||
#ifdef USE_ESP32
|
||||
this->log_partition_info_(); // Log partition information for ESP32
|
||||
#endif // USE_ESP32
|
||||
}
|
||||
|
||||
void DebugComponent::loop() {
|
||||
|
@ -55,6 +55,20 @@ class DebugComponent : public PollingComponent {
|
||||
#endif // USE_ESP32
|
||||
#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
|
||||
text_sensor::TextSensor *device_info_{nullptr};
|
||||
text_sensor::TextSensor *reset_reason_{nullptr};
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_chip_info.h>
|
||||
#include <esp_partition.h>
|
||||
|
||||
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||
#include <esp32/rom/rtc.h>
|
||||
@ -28,111 +29,177 @@ namespace 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 reset_reason;
|
||||
switch (rtc_get_reset_reason(0)) {
|
||||
case POWERON_RESET:
|
||||
reset_reason = "Power On Reset";
|
||||
switch (esp_reset_reason()) {
|
||||
case ESP_RST_POWERON:
|
||||
reset_reason = "Reset due to power-on event";
|
||||
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)
|
||||
case SW_RESET:
|
||||
case SW_RESET:
|
||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6)
|
||||
case RTC_SW_SYS_RESET:
|
||||
case RTC_SW_SYS_RESET:
|
||||
#endif
|
||||
reset_reason = "Software Reset Digital Core";
|
||||
break;
|
||||
reset_reason = "Software Reset Digital Core";
|
||||
break;
|
||||
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||
case OWDT_RESET:
|
||||
reset_reason = "Watch Dog Reset Digital Core";
|
||||
break;
|
||||
case OWDT_RESET:
|
||||
reset_reason = "Watch Dog Reset Digital Core";
|
||||
break;
|
||||
#endif
|
||||
case DEEPSLEEP_RESET:
|
||||
reset_reason = "Deep Sleep Reset Digital Core";
|
||||
break;
|
||||
case DEEPSLEEP_RESET:
|
||||
reset_reason = "Deep Sleep Reset Digital Core";
|
||||
break;
|
||||
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||
case SDIO_RESET:
|
||||
reset_reason = "SLC Module Reset Digital Core";
|
||||
break;
|
||||
case SDIO_RESET:
|
||||
reset_reason = "SLC Module Reset Digital Core";
|
||||
break;
|
||||
#endif
|
||||
case TG0WDT_SYS_RESET:
|
||||
reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
|
||||
break;
|
||||
case TG1WDT_SYS_RESET:
|
||||
reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
|
||||
break;
|
||||
case RTCWDT_SYS_RESET:
|
||||
reset_reason = "RTC Watch Dog Reset Digital Core";
|
||||
break;
|
||||
case TG0WDT_SYS_RESET:
|
||||
reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
|
||||
break;
|
||||
case TG1WDT_SYS_RESET:
|
||||
reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
|
||||
break;
|
||||
case RTCWDT_SYS_RESET:
|
||||
reset_reason = "RTC Watch Dog Reset Digital Core";
|
||||
break;
|
||||
#if !defined(USE_ESP32_VARIANT_ESP32C6) && !defined(USE_ESP32_VARIANT_ESP32H2)
|
||||
case INTRUSION_RESET:
|
||||
reset_reason = "Intrusion Reset CPU";
|
||||
break;
|
||||
case INTRUSION_RESET:
|
||||
reset_reason = "Intrusion Reset CPU";
|
||||
break;
|
||||
#endif
|
||||
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||
case TGWDT_CPU_RESET:
|
||||
reset_reason = "Timer Group Reset CPU";
|
||||
break;
|
||||
case TGWDT_CPU_RESET:
|
||||
reset_reason = "Timer Group Reset CPU";
|
||||
break;
|
||||
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6)
|
||||
case TG0WDT_CPU_RESET:
|
||||
reset_reason = "Timer Group 0 Reset CPU";
|
||||
break;
|
||||
case TG0WDT_CPU_RESET:
|
||||
reset_reason = "Timer Group 0 Reset CPU";
|
||||
break;
|
||||
#endif
|
||||
#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) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6)
|
||||
case RTC_SW_CPU_RESET:
|
||||
case RTC_SW_CPU_RESET:
|
||||
#endif
|
||||
reset_reason = "Software Reset CPU";
|
||||
break;
|
||||
case RTCWDT_CPU_RESET:
|
||||
reset_reason = "RTC Watch Dog Reset CPU";
|
||||
break;
|
||||
reset_reason = "Software Reset CPU";
|
||||
break;
|
||||
case RTCWDT_CPU_RESET:
|
||||
reset_reason = "RTC Watch Dog Reset CPU";
|
||||
break;
|
||||
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||
case EXT_CPU_RESET:
|
||||
reset_reason = "External CPU Reset";
|
||||
break;
|
||||
case EXT_CPU_RESET:
|
||||
reset_reason = "External CPU Reset";
|
||||
break;
|
||||
#endif
|
||||
case RTCWDT_BROWN_OUT_RESET:
|
||||
reset_reason = "Voltage Unstable Reset";
|
||||
break;
|
||||
case RTCWDT_RTC_RESET:
|
||||
reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
|
||||
break;
|
||||
case RTCWDT_BROWN_OUT_RESET:
|
||||
reset_reason = "Voltage Unstable Reset";
|
||||
break;
|
||||
case RTCWDT_RTC_RESET:
|
||||
reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
|
||||
break;
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \
|
||||
defined(USE_ESP32_VARIANT_ESP32C6)
|
||||
case TG1WDT_CPU_RESET:
|
||||
reset_reason = "Timer Group 1 Reset CPU";
|
||||
break;
|
||||
case SUPER_WDT_RESET:
|
||||
reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
|
||||
break;
|
||||
case EFUSE_RESET:
|
||||
reset_reason = "eFuse Reset Digital Core";
|
||||
break;
|
||||
case TG1WDT_CPU_RESET:
|
||||
reset_reason = "Timer Group 1 Reset CPU";
|
||||
break;
|
||||
case SUPER_WDT_RESET:
|
||||
reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
|
||||
break;
|
||||
case EFUSE_RESET:
|
||||
reset_reason = "eFuse Reset Digital Core";
|
||||
break;
|
||||
#endif
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
case GLITCH_RTC_RESET:
|
||||
reset_reason = "Glitch Reset Digital Core And RTC Module";
|
||||
break;
|
||||
case GLITCH_RTC_RESET:
|
||||
reset_reason = "Glitch Reset Digital Core And RTC Module";
|
||||
break;
|
||||
#endif
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6)
|
||||
case USB_UART_CHIP_RESET:
|
||||
reset_reason = "USB UART Reset Digital Core";
|
||||
break;
|
||||
case USB_JTAG_CHIP_RESET:
|
||||
reset_reason = "USB JTAG Reset Digital Core";
|
||||
break;
|
||||
case USB_UART_CHIP_RESET:
|
||||
reset_reason = "USB UART Reset Digital Core";
|
||||
break;
|
||||
case USB_JTAG_CHIP_RESET:
|
||||
reset_reason = "USB JTAG Reset Digital Core";
|
||||
break;
|
||||
#endif
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
case POWER_GLITCH_RESET:
|
||||
reset_reason = "Power Glitch Reset Digital Core And RTC Module";
|
||||
break;
|
||||
case POWER_GLITCH_RESET:
|
||||
reset_reason = "Power Glitch Reset Digital Core And RTC Module";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
reset_reason = "Unknown Reset Reason";
|
||||
default:
|
||||
reset_reason = "Unknown Reset Reason";
|
||||
}
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
|
||||
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 += " 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());
|
||||
device_info += "|ESP-IDF: ";
|
||||
device_info += esp_get_idf_version();
|
||||
@ -294,4 +374,4 @@ void DebugComponent::update_platform_() {
|
||||
|
||||
} // namespace debug
|
||||
} // namespace esphome
|
||||
#endif
|
||||
#endif // USE_ESP32
|
||||
|
@ -52,11 +52,11 @@ void DeepSleepComponent::dump_config_platform_() {
|
||||
|
||||
bool DeepSleepComponent::prepare_to_sleep_() {
|
||||
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
|
||||
if (!this->next_enter_deep_sleep_) {
|
||||
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;
|
||||
return false;
|
||||
|
@ -6,7 +6,104 @@ namespace 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) {
|
||||
ESP_LOGD(TAG, "Playing file %d in folder %d", file, folder);
|
||||
if (folder < 100 && file < 256) {
|
||||
this->ack_set_is_playing_ = true;
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -62,6 +159,15 @@ void DFPlayer::loop() {
|
||||
}
|
||||
break;
|
||||
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) {
|
||||
ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte);
|
||||
this->read_pos_ = 0;
|
||||
@ -101,9 +207,37 @@ void DFPlayer::loop() {
|
||||
ESP_LOGV(TAG, "Nack");
|
||||
this->ack_set_is_playing_ = false;
|
||||
this->ack_reset_is_playing_ = false;
|
||||
if (argument == 6) {
|
||||
ESP_LOGV(TAG, "File not found");
|
||||
this->is_playing_ = false;
|
||||
switch (argument) {
|
||||
case 0x01:
|
||||
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;
|
||||
case 0x41:
|
||||
@ -113,12 +247,17 @@ void DFPlayer::loop() {
|
||||
this->ack_set_is_playing_ = false;
|
||||
this->ack_reset_is_playing_ = false;
|
||||
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->on_finished_playback_callback_.call();
|
||||
break;
|
||||
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->read_pos_ = 0;
|
||||
|
@ -23,64 +23,30 @@ enum Device {
|
||||
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 {
|
||||
public:
|
||||
void loop() override;
|
||||
|
||||
void next() {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x01);
|
||||
}
|
||||
void previous() {
|
||||
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 next();
|
||||
void previous();
|
||||
void play_mp3(uint16_t file);
|
||||
void play_file(uint16_t file);
|
||||
void play_file_loop(uint16_t file);
|
||||
void play_folder(uint16_t folder, uint16_t file);
|
||||
void play_folder_loop(uint16_t folder) {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x17, folder);
|
||||
}
|
||||
void volume_up() { this->send_cmd_(0x04); }
|
||||
void volume_down() { this->send_cmd_(0x05); }
|
||||
void set_device(Device device) { this->send_cmd_(0x09, device); }
|
||||
void set_volume(uint8_t volume) { this->send_cmd_(0x06, volume); }
|
||||
void set_eq(EqPreset preset) { this->send_cmd_(0x07, preset); }
|
||||
void sleep() {
|
||||
this->ack_reset_is_playing_ = true;
|
||||
this->send_cmd_(0x0A);
|
||||
}
|
||||
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);
|
||||
}
|
||||
void play_folder_loop(uint16_t folder);
|
||||
void volume_up();
|
||||
void volume_down();
|
||||
void set_device(Device device);
|
||||
void set_volume(uint8_t volume);
|
||||
void set_eq(EqPreset preset);
|
||||
void sleep();
|
||||
void reset();
|
||||
void start();
|
||||
void pause();
|
||||
void stop();
|
||||
void random();
|
||||
|
||||
bool is_playing() { return is_playing_; }
|
||||
void dump_config() override;
|
||||
|
@ -118,8 +118,9 @@ std::unique_ptr<Command> CircularCommandQueue::dequeue() {
|
||||
if (front_ == rear_) {
|
||||
front_ = -1;
|
||||
rear_ = -1;
|
||||
} else
|
||||
} else {
|
||||
front_ = (front_ + 1) % COMMAND_QUEUE_SIZE;
|
||||
}
|
||||
|
||||
return dequeued_cmd;
|
||||
}
|
||||
|
@ -135,7 +135,8 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
||||
|
||||
// Wait for falling edge
|
||||
while (this->pin_->digital_read()) {
|
||||
if ((end_time = micros()) - start_time > 90) {
|
||||
end_time = micros();
|
||||
if (end_time - start_time > 90) {
|
||||
if (i < 0) {
|
||||
error_code = 3;
|
||||
} else {
|
||||
@ -156,8 +157,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
||||
if (bit == 0) {
|
||||
bit = 7;
|
||||
byte++;
|
||||
} else
|
||||
} else {
|
||||
bit--;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!report_errors && error_code != 0)
|
||||
|
@ -39,6 +39,7 @@ DisplayOnPageChangeTrigger = display_ns.class_(
|
||||
|
||||
CONF_ON_PAGE_CHANGE = "on_page_change"
|
||||
CONF_SHOW_TEST_CARD = "show_test_card"
|
||||
CONF_UNSPECIFIED = "unspecified"
|
||||
|
||||
DISPLAY_ROTATIONS = {
|
||||
0: display_ns.DISPLAY_ROTATION_0_DEGREES,
|
||||
@ -55,16 +56,22 @@ def validate_rotation(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(
|
||||
{
|
||||
cv.Optional(CONF_LAMBDA): cv.lambda_,
|
||||
cv.Exclusive(CONF_LAMBDA, CONF_LAMBDA): cv.lambda_,
|
||||
}
|
||||
).extend(cv.polling_component_schema("1s"))
|
||||
|
||||
FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.Optional(CONF_ROTATION): validate_rotation,
|
||||
cv.Optional(CONF_PAGES): cv.All(
|
||||
cv.Exclusive(CONF_PAGES, CONF_LAMBDA): cv.All(
|
||||
cv.ensure_list(
|
||||
{
|
||||
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_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,
|
||||
}
|
||||
)
|
||||
@ -92,8 +101,12 @@ async def setup_display_core_(var, config):
|
||||
if CONF_ROTATION in config:
|
||||
cg.add(var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]]))
|
||||
|
||||
if CONF_AUTO_CLEAR_ENABLED in config:
|
||||
cg.add(var.set_auto_clear(config[CONF_AUTO_CLEAR_ENABLED]))
|
||||
if (auto_clear := config.get(CONF_AUTO_CLEAR_ENABLED)) is not None:
|
||||
# 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:
|
||||
pages = []
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "display.h"
|
||||
#include "display_color_utils.h"
|
||||
#include <utility>
|
||||
#include "display_color_utils.h"
|
||||
#include "esphome/core/hal.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) {
|
||||
upd_dxmax = ceil(float(dymax) / tan_a);
|
||||
hline_width = -dxmax - upd_dxmax + 1;
|
||||
} else
|
||||
} else {
|
||||
hline_width = 0;
|
||||
}
|
||||
}
|
||||
if (hline_width > 0)
|
||||
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))
|
||||
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];
|
||||
size_t ret = time.strftime(buffer, sizeof(buffer), format);
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
|
@ -437,6 +437,20 @@ class Display : public PollingComponent {
|
||||
*/
|
||||
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`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
|
@ -90,8 +90,9 @@ void Rect::info(const std::string &prefix) {
|
||||
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(),
|
||||
this->y2());
|
||||
} else
|
||||
} else {
|
||||
ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace display
|
||||
|
@ -68,8 +68,6 @@ IsActiveCondition = display_menu_base_ns.class_(
|
||||
"IsActiveCondition", automation.Condition
|
||||
)
|
||||
|
||||
MULTI_CONF = True
|
||||
|
||||
MenuItemType = display_menu_base_ns.enum("MenuItemType")
|
||||
|
||||
MENU_ITEM_TYPES = {
|
||||
|
@ -280,7 +280,7 @@ bool DisplayMenuComponent::cursor_down_() {
|
||||
bool DisplayMenuComponent::enter_menu_() {
|
||||
this->displayed_item_->on_leave();
|
||||
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->displayed_item_->on_enter();
|
||||
|
||||
|
@ -296,7 +296,7 @@ void Dsmr::dump_config() {
|
||||
}
|
||||
|
||||
void Dsmr::set_decryption_key(const std::string &decryption_key) {
|
||||
if (decryption_key.length() == 0) {
|
||||
if (decryption_key.empty()) {
|
||||
ESP_LOGI(TAG, "Disabling decryption");
|
||||
this->decryption_key_.clear();
|
||||
if (this->crypt_telegram_ != nullptr) {
|
||||
|
0
esphome/components/es7210/__init__.py
Normal file
0
esphome/components/es7210/__init__.py
Normal file
51
esphome/components/es7210/audio_adc.py
Normal file
51
esphome/components/es7210/audio_adc.py
Normal 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]))
|
228
esphome/components/es7210/es7210.cpp
Normal file
228
esphome/components/es7210/es7210.cpp
Normal 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, ®v));
|
||||
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, ®v));
|
||||
regv = (regv & (~update_bits)) | (update_bits & data);
|
||||
return this->write_byte(reg_addr, regv);
|
||||
}
|
||||
|
||||
} // namespace es7210
|
||||
} // namespace esphome
|
62
esphome/components/es7210/es7210.h
Normal file
62
esphome/components/es7210/es7210.h
Normal 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
|
129
esphome/components/es7210/es7210_const.h
Normal file
129
esphome/components/es7210/es7210_const.h
Normal 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
|
0
esphome/components/es7243e/__init__.py
Normal file
0
esphome/components/es7243e/__init__.py
Normal file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user