mirror of
https://github.com/esphome/esphome.git
synced 2024-11-27 17:27:59 +01:00
Merge branch 'dev' into jesserockz-2023-232
This commit is contained in:
commit
d7b0060265
601 changed files with 26520 additions and 6006 deletions
15
.clang-tidy
15
.clang-tidy
|
@ -5,9 +5,12 @@ Checks: >-
|
||||||
-altera-*,
|
-altera-*,
|
||||||
-android-*,
|
-android-*,
|
||||||
-boost-*,
|
-boost-*,
|
||||||
|
-bugprone-easily-swappable-parameters,
|
||||||
|
-bugprone-implicit-widening-of-multiplication-result,
|
||||||
-bugprone-narrowing-conversions,
|
-bugprone-narrowing-conversions,
|
||||||
-bugprone-signed-char-misuse,
|
-bugprone-signed-char-misuse,
|
||||||
-cert-dcl50-cpp,
|
-cert-dcl50-cpp,
|
||||||
|
-cert-err33-c,
|
||||||
-cert-err58-cpp,
|
-cert-err58-cpp,
|
||||||
-cert-oop57-cpp,
|
-cert-oop57-cpp,
|
||||||
-cert-str34-c,
|
-cert-str34-c,
|
||||||
|
@ -15,6 +18,7 @@ Checks: >-
|
||||||
-clang-analyzer-osx.*,
|
-clang-analyzer-osx.*,
|
||||||
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||||
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||||
|
-clang-diagnostic-ignored-optimization-argument,
|
||||||
-clang-diagnostic-shadow-field,
|
-clang-diagnostic-shadow-field,
|
||||||
-clang-diagnostic-unused-const-variable,
|
-clang-diagnostic-unused-const-variable,
|
||||||
-clang-diagnostic-unused-parameter,
|
-clang-diagnostic-unused-parameter,
|
||||||
|
@ -25,6 +29,7 @@ Checks: >-
|
||||||
-cppcoreguidelines-macro-usage,
|
-cppcoreguidelines-macro-usage,
|
||||||
-cppcoreguidelines-narrowing-conversions,
|
-cppcoreguidelines-narrowing-conversions,
|
||||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||||
|
-cppcoreguidelines-prefer-member-initializer,
|
||||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||||
|
@ -36,6 +41,7 @@ Checks: >-
|
||||||
-cppcoreguidelines-pro-type-union-access,
|
-cppcoreguidelines-pro-type-union-access,
|
||||||
-cppcoreguidelines-pro-type-vararg,
|
-cppcoreguidelines-pro-type-vararg,
|
||||||
-cppcoreguidelines-special-member-functions,
|
-cppcoreguidelines-special-member-functions,
|
||||||
|
-cppcoreguidelines-virtual-class-destructor,
|
||||||
-fuchsia-multiple-inheritance,
|
-fuchsia-multiple-inheritance,
|
||||||
-fuchsia-overloaded-operator,
|
-fuchsia-overloaded-operator,
|
||||||
-fuchsia-statically-constructed-objects,
|
-fuchsia-statically-constructed-objects,
|
||||||
|
@ -68,6 +74,7 @@ Checks: >-
|
||||||
-modernize-use-nodiscard,
|
-modernize-use-nodiscard,
|
||||||
-mpi-*,
|
-mpi-*,
|
||||||
-objc-*,
|
-objc-*,
|
||||||
|
-readability-container-data-pointer,
|
||||||
-readability-convert-member-functions-to-static,
|
-readability-convert-member-functions-to-static,
|
||||||
-readability-else-after-return,
|
-readability-else-after-return,
|
||||||
-readability-function-cognitive-complexity,
|
-readability-function-cognitive-complexity,
|
||||||
|
@ -82,8 +89,6 @@ WarningsAsErrors: '*'
|
||||||
AnalyzeTemporaryDtors: false
|
AnalyzeTemporaryDtors: false
|
||||||
FormatStyle: google
|
FormatStyle: google
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
|
||||||
value: '1'
|
|
||||||
- key: google-readability-function-size.StatementThreshold
|
- key: google-readability-function-size.StatementThreshold
|
||||||
value: '800'
|
value: '800'
|
||||||
- key: google-runtime-int.TypeSuffix
|
- key: google-runtime-int.TypeSuffix
|
||||||
|
@ -158,3 +163,9 @@ CheckOptions:
|
||||||
value: ''
|
value: ''
|
||||||
- key: readability-qualified-auto.AddConstToQualified
|
- key: readability-qualified-auto.AddConstToQualified
|
||||||
value: 0
|
value: 0
|
||||||
|
- key: readability-identifier-length.MinimumVariableNameLength
|
||||||
|
value: 0
|
||||||
|
- key: readability-identifier-length.MinimumParameterNameLength
|
||||||
|
value: 0
|
||||||
|
- key: readability-identifier-length.MinimumLoopCounterNameLength
|
||||||
|
value: 0
|
||||||
|
|
38
.github/actions/restore-python/action.yml
vendored
Normal file
38
.github/actions/restore-python/action.yml
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
name: Restore Python
|
||||||
|
inputs:
|
||||||
|
python-version:
|
||||||
|
description: Python version to restore
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
cache-key:
|
||||||
|
description: Cache key to use
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
outputs:
|
||||||
|
python-version:
|
||||||
|
description: Python version restored
|
||||||
|
value: ${{ steps.python.outputs.python-version }}
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Set up Python ${{ inputs.python-version }}
|
||||||
|
id: python
|
||||||
|
uses: actions/setup-python@v4.7.0
|
||||||
|
with:
|
||||||
|
python-version: ${{ inputs.python-version }}
|
||||||
|
- name: Restore Python virtual environment
|
||||||
|
id: cache-venv
|
||||||
|
uses: actions/cache/restore@v3.3.2
|
||||||
|
with:
|
||||||
|
path: venv
|
||||||
|
# yamllint disable-line rule:line-length
|
||||||
|
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ inputs.cache-key }}
|
||||||
|
- name: Create Python virtual environment
|
||||||
|
if: steps.cache-venv.outputs.cache-hit != 'true'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
python -m venv venv
|
||||||
|
. venv/bin/activate
|
||||||
|
python --version
|
||||||
|
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||||
|
pip install -e .
|
12
.github/workflows/ci-docker.yml
vendored
12
.github/workflows/ci-docker.yml
vendored
|
@ -8,7 +8,7 @@ on:
|
||||||
branches: [dev, beta, release]
|
branches: [dev, beta, release]
|
||||||
paths:
|
paths:
|
||||||
- "docker/**"
|
- "docker/**"
|
||||||
- ".github/workflows/**"
|
- ".github/workflows/ci-docker.yml"
|
||||||
- "requirements*.txt"
|
- "requirements*.txt"
|
||||||
- "platformio.ini"
|
- "platformio.ini"
|
||||||
- "script/platformio_install_deps.py"
|
- "script/platformio_install_deps.py"
|
||||||
|
@ -16,7 +16,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "docker/**"
|
- "docker/**"
|
||||||
- ".github/workflows/**"
|
- ".github/workflows/ci-docker.yml"
|
||||||
- "requirements*.txt"
|
- "requirements*.txt"
|
||||||
- "platformio.ini"
|
- "platformio.ini"
|
||||||
- "script/platformio_install_deps.py"
|
- "script/platformio_install_deps.py"
|
||||||
|
@ -40,15 +40,15 @@ jobs:
|
||||||
arch: [amd64, armv7, aarch64]
|
arch: [amd64, armv7, aarch64]
|
||||||
build_type: ["ha-addon", "docker", "lint"]
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4.0.0
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4.7.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3.0.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
|
||||||
- name: Set TAG
|
- name: Set TAG
|
||||||
run: |
|
run: |
|
||||||
|
|
158
.github/workflows/ci.yml
vendored
158
.github/workflows/ci.yml
vendored
|
@ -7,6 +7,10 @@ on:
|
||||||
branches: [dev, beta, release]
|
branches: [dev, beta, release]
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "**"
|
||||||
|
- "!.github/workflows/*.yml"
|
||||||
|
- ".github/workflows/ci.yml"
|
||||||
merge_group:
|
merge_group:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
@ -26,20 +30,26 @@ jobs:
|
||||||
common:
|
common:
|
||||||
name: Create common environment
|
name: Create common environment
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
cache-key: ${{ steps.cache-key.outputs.key }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
|
- name: Generate cache-key
|
||||||
|
id: cache-key
|
||||||
|
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
uses: actions/setup-python@v4.6.0
|
id: python
|
||||||
|
uses: actions/setup-python@v4.7.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache@v3.3.1
|
uses: actions/cache@v3.3.2
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ steps.cache-key.outputs.key }}
|
||||||
- name: Create Python virtual environment
|
- name: Create Python virtual environment
|
||||||
if: steps.cache-venv.outputs.cache-hit != 'true'
|
if: steps.cache-venv.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
|
@ -49,15 +59,6 @@ jobs:
|
||||||
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|
||||||
yamllint:
|
|
||||||
name: yamllint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v3.5.2
|
|
||||||
- name: Run yamllint
|
|
||||||
uses: frenck/action-yamllint@v1.4.1
|
|
||||||
|
|
||||||
black:
|
black:
|
||||||
name: Check black
|
name: Check black
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -65,13 +66,12 @@ jobs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
|
||||||
- name: Run black
|
- name: Run black
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
|
@ -87,13 +87,12 @@ jobs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
|
||||||
- name: Run flake8
|
- name: Run flake8
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
|
@ -109,13 +108,12 @@ jobs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
|
||||||
- name: Run pylint
|
- name: Run pylint
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
|
@ -131,13 +129,12 @@ jobs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
|
||||||
- name: Run pyupgrade
|
- name: Run pyupgrade
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
|
@ -153,13 +150,12 @@ jobs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
|
||||||
- name: Register matcher
|
- name: Register matcher
|
||||||
run: echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
run: echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||||
- name: Run script/ci-custom
|
- name: Run script/ci-custom
|
||||||
|
@ -175,13 +171,12 @@ jobs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
|
||||||
- name: Register matcher
|
- name: Register matcher
|
||||||
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||||
- name: Run pytest
|
- name: Run pytest
|
||||||
|
@ -196,13 +191,12 @@ jobs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
|
||||||
- name: Install clang-format
|
- name: Install clang-format
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
|
@ -216,6 +210,17 @@ jobs:
|
||||||
run: script/ci-suggest-changes
|
run: script/ci-suggest-changes
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
|
compile-tests-list:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||||
|
steps:
|
||||||
|
- name: Check out code from GitHub
|
||||||
|
uses: actions/checkout@v4.0.0
|
||||||
|
- name: Find all YAML test files
|
||||||
|
id: set-matrix
|
||||||
|
run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
compile-tests:
|
compile-tests:
|
||||||
name: Run YAML test ${{ matrix.file }}
|
name: Run YAML test ${{ matrix.file }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -228,31 +233,24 @@ jobs:
|
||||||
- pylint
|
- pylint
|
||||||
- pytest
|
- pytest
|
||||||
- pyupgrade
|
- pyupgrade
|
||||||
- yamllint
|
- compile-tests-list
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
max-parallel: 2
|
max-parallel: 2
|
||||||
matrix:
|
matrix:
|
||||||
file: [1, 2, 3, 3.1, 4, 5, 6, 7, 8]
|
file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
- name: Run esphome compile ${{ matrix.file }}
|
||||||
- name: Cache platformio
|
|
||||||
uses: actions/cache@v3.3.1
|
|
||||||
with:
|
|
||||||
path: ~/.platformio
|
|
||||||
# yamllint disable-line rule:line-length
|
|
||||||
key: platformio-test${{ matrix.file }}-${{ hashFiles('platformio.ini') }}
|
|
||||||
- name: Run esphome compile tests/test${{ matrix.file }}.yaml
|
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
esphome compile tests/test${{ matrix.file }}.yaml
|
esphome compile ${{ matrix.file }}
|
||||||
|
|
||||||
clang-tidy:
|
clang-tidy:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
|
@ -266,7 +264,6 @@ jobs:
|
||||||
- pylint
|
- pylint
|
||||||
- pytest
|
- pytest
|
||||||
- pyupgrade
|
- pyupgrade
|
||||||
- yamllint
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
max-parallel: 2
|
max-parallel: 2
|
||||||
|
@ -299,23 +296,21 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
uses: actions/checkout@v3.5.2
|
uses: actions/checkout@v4.0.0
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python
|
||||||
uses: actions/cache/restore@v3.3.1
|
uses: ./.github/actions/restore-python
|
||||||
with:
|
with:
|
||||||
path: venv
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
# yamllint disable-line rule:line-length
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
key: ${{ runner.os }}-${{ env.DEFAULT_PYTHON }}-venv-${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}
|
|
||||||
# Use per check platformio cache because checks use different parts
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
uses: actions/cache@v3.3.1
|
uses: actions/cache@v3.3.2
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||||
|
|
||||||
- name: Install clang-tidy
|
- name: Install clang-tidy
|
||||||
run: sudo apt-get install clang-tidy-11
|
run: sudo apt-get install clang-tidy-14
|
||||||
|
|
||||||
- name: Register problem matchers
|
- name: Register problem matchers
|
||||||
run: |
|
run: |
|
||||||
|
@ -347,7 +342,6 @@ jobs:
|
||||||
- pylint
|
- pylint
|
||||||
- pytest
|
- pytest
|
||||||
- pyupgrade
|
- pyupgrade
|
||||||
- yamllint
|
|
||||||
- compile-tests
|
- compile-tests
|
||||||
- clang-tidy
|
- clang-tidy
|
||||||
if: always()
|
if: always()
|
||||||
|
|
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v4
|
- uses: dessant/lock-threads@v4.0.1
|
||||||
with:
|
with:
|
||||||
pr-inactive-days: "1"
|
pr-inactive-days: "1"
|
||||||
pr-lock-reason: ""
|
pr-lock-reason: ""
|
||||||
|
|
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
tag: ${{ steps.tag.outputs.tag }}
|
tag: ${{ steps.tag.outputs.tag }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4.0.0
|
||||||
- name: Get tag
|
- name: Get tag
|
||||||
id: tag
|
id: tag
|
||||||
# yamllint disable rule:line-length
|
# yamllint disable rule:line-length
|
||||||
|
@ -43,15 +43,17 @@ jobs:
|
||||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4.0.0
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4.7.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Set up python environment
|
- name: Set up python environment
|
||||||
|
env:
|
||||||
|
ESPHOME_NO_VENV: 1
|
||||||
run: |
|
run: |
|
||||||
script/setup
|
script/setup
|
||||||
pip install setuptools wheel twine
|
pip install twine
|
||||||
- name: Build
|
- name: Build
|
||||||
run: python setup.py sdist bdist_wheel
|
run: python setup.py sdist bdist_wheel
|
||||||
- name: Upload
|
- name: Upload
|
||||||
|
@ -86,24 +88,24 @@ jobs:
|
||||||
target: "lint"
|
target: "lint"
|
||||||
baseimg: "docker"
|
baseimg: "docker"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4.0.0
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4.7.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3.0.0
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3.0.0
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3.0.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3.0.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
@ -117,7 +119,7 @@ jobs:
|
||||||
--suffix "${{ matrix.image.suffix }}"
|
--suffix "${{ matrix.image.suffix }}"
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v5.0.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile
|
file: ./docker/Dockerfile
|
||||||
|
@ -139,7 +141,7 @@ jobs:
|
||||||
needs: [deploy-docker]
|
needs: [deploy-docker]
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger Workflow
|
- name: Trigger Workflow
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v6.4.1
|
||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
||||||
script: |
|
script: |
|
||||||
|
|
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v8
|
- uses: actions/stale@v8.0.0
|
||||||
with:
|
with:
|
||||||
days-before-pr-stale: 90
|
days-before-pr-stale: 90
|
||||||
days-before-pr-close: 7
|
days-before-pr-close: 7
|
||||||
|
@ -38,7 +38,7 @@ jobs:
|
||||||
close-issues:
|
close-issues:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v8
|
- uses: actions/stale@v8.0.0
|
||||||
with:
|
with:
|
||||||
days-before-pr-stale: -1
|
days-before-pr-stale: -1
|
||||||
days-before-pr-close: -1
|
days-before-pr-close: -1
|
||||||
|
|
26
.github/workflows/sync-device-classes.yml
vendored
26
.github/workflows/sync-device-classes.yml
vendored
|
@ -4,28 +4,25 @@ name: Synchronise Device Classes from Home Assistant
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '45 6 * * *'
|
- cron: "45 6 * * *"
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sync:
|
sync:
|
||||||
name: Sync Device Classes
|
name: Sync Device Classes
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4.0.0
|
||||||
|
|
||||||
- name: Checkout Home Assistant
|
- name: Checkout Home Assistant
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4.0.0
|
||||||
with:
|
with:
|
||||||
repository: home-assistant/core
|
repository: home-assistant/core
|
||||||
path: lib/home-assistant
|
path: lib/home-assistant
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4.7.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.11
|
python-version: 3.11
|
||||||
|
|
||||||
|
@ -38,17 +35,8 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
python ./script/sync-device_class.py
|
python ./script/sync-device_class.py
|
||||||
|
|
||||||
- name: Get PR template
|
|
||||||
id: pr-template-body
|
|
||||||
run: |
|
|
||||||
body=$(cat .github/PULL_REQUEST_TEMPLATE.md)
|
|
||||||
delimiter="$(openssl rand -hex 8)"
|
|
||||||
echo "body<<$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
echo "$body" >> $GITHUB_OUTPUT
|
|
||||||
echo "$delimiter" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: peter-evans/create-pull-request@v5
|
uses: peter-evans/create-pull-request@v5.0.2
|
||||||
with:
|
with:
|
||||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||||
committer: esphomebot <esphome@nabucasa.com>
|
committer: esphomebot <esphome@nabucasa.com>
|
||||||
|
@ -56,5 +44,5 @@ jobs:
|
||||||
branch: sync/device-classes
|
branch: sync/device-classes
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
title: "Synchronise Device Classes from Home Assistant"
|
title: "Synchronise Device Classes from Home Assistant"
|
||||||
body: ${{ steps.pr-template-body.outputs.body }}
|
body-path: .github/PULL_REQUEST_TEMPLATE.md
|
||||||
token: ${{ secrets.DEVICE_CLASS_SYNC_TOKEN }}
|
token: ${{ secrets.DEVICE_CLASS_SYNC_TOKEN }}
|
||||||
|
|
22
.github/workflows/yaml-lint.yml
vendored
Normal file
22
.github/workflows/yaml-lint.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
name: YAML lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [dev, beta, release]
|
||||||
|
paths:
|
||||||
|
- "**.yaml"
|
||||||
|
- "**.yml"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "**.yaml"
|
||||||
|
- "**.yml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
yamllint:
|
||||||
|
name: yamllint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out code from GitHub
|
||||||
|
uses: actions/checkout@v4.0.0
|
||||||
|
- name: Run yamllint
|
||||||
|
uses: frenck/action-yamllint@v1.4.1
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -13,6 +13,12 @@ __pycache__/
|
||||||
# Intellij Idea
|
# Intellij Idea
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
.project
|
||||||
|
.cproject
|
||||||
|
.pydevproject
|
||||||
|
.settings/
|
||||||
|
|
||||||
# Vim
|
# Vim
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
@ -130,3 +136,5 @@ sdkconfig.*
|
||||||
!sdkconfig.defaults
|
!sdkconfig.defaults
|
||||||
|
|
||||||
.tests/
|
.tests/
|
||||||
|
|
||||||
|
/components
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
# See https://pre-commit.com for more information
|
# See https://pre-commit.com for more information
|
||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||||
rev: 23.3.0
|
rev: 23.9.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args:
|
args:
|
||||||
|
@ -27,7 +27,7 @@ repos:
|
||||||
- --branch=release
|
- --branch=release
|
||||||
- --branch=beta
|
- --branch=beta
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v3.4.0
|
rev: v3.10.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py39-plus]
|
args: [--py39-plus]
|
||||||
|
|
34
CODEOWNERS
34
CODEOWNERS
|
@ -11,15 +11,18 @@ esphome/*.py @esphome/core
|
||||||
esphome/core/* @esphome/core
|
esphome/core/* @esphome/core
|
||||||
|
|
||||||
# Integrations
|
# Integrations
|
||||||
|
esphome/components/a01nyub/* @MrSuicideParrot
|
||||||
esphome/components/absolute_humidity/* @DAVe3283
|
esphome/components/absolute_humidity/* @DAVe3283
|
||||||
esphome/components/ac_dimmer/* @glmnet
|
esphome/components/ac_dimmer/* @glmnet
|
||||||
esphome/components/adc/* @esphome/core
|
esphome/components/adc/* @esphome/core
|
||||||
esphome/components/adc128s102/* @DeerMaximum
|
esphome/components/adc128s102/* @DeerMaximum
|
||||||
esphome/components/addressable_light/* @justfalter
|
esphome/components/addressable_light/* @justfalter
|
||||||
esphome/components/airthings_ble/* @jeromelaban
|
esphome/components/airthings_ble/* @jeromelaban
|
||||||
|
esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau
|
||||||
esphome/components/airthings_wave_mini/* @ncareau
|
esphome/components/airthings_wave_mini/* @ncareau
|
||||||
esphome/components/airthings_wave_plus/* @jeromelaban
|
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||||
esphome/components/alarm_control_panel/* @grahambrown11
|
esphome/components/alarm_control_panel/* @grahambrown11
|
||||||
|
esphome/components/alpha3/* @jan-hofmeier
|
||||||
esphome/components/am43/* @buxtronix
|
esphome/components/am43/* @buxtronix
|
||||||
esphome/components/am43/cover/* @buxtronix
|
esphome/components/am43/cover/* @buxtronix
|
||||||
esphome/components/am43/sensor/* @buxtronix
|
esphome/components/am43/sensor/* @buxtronix
|
||||||
|
@ -30,6 +33,7 @@ esphome/components/api/* @OttoWinter
|
||||||
esphome/components/as7341/* @mrgnr
|
esphome/components/as7341/* @mrgnr
|
||||||
esphome/components/async_tcp/* @OttoWinter
|
esphome/components/async_tcp/* @OttoWinter
|
||||||
esphome/components/atc_mithermometer/* @ahpohl
|
esphome/components/atc_mithermometer/* @ahpohl
|
||||||
|
esphome/components/atm90e26/* @danieltwagner
|
||||||
esphome/components/b_parasite/* @rbaron
|
esphome/components/b_parasite/* @rbaron
|
||||||
esphome/components/ballu/* @bazuchan
|
esphome/components/ballu/* @bazuchan
|
||||||
esphome/components/bang_bang/* @OttoWinter
|
esphome/components/bang_bang/* @OttoWinter
|
||||||
|
@ -38,18 +42,21 @@ esphome/components/bedjet/climate/* @jhansche
|
||||||
esphome/components/bedjet/fan/* @jhansche
|
esphome/components/bedjet/fan/* @jhansche
|
||||||
esphome/components/bh1750/* @OttoWinter
|
esphome/components/bh1750/* @OttoWinter
|
||||||
esphome/components/binary_sensor/* @esphome/core
|
esphome/components/binary_sensor/* @esphome/core
|
||||||
|
esphome/components/bk72xx/* @kuba2k2
|
||||||
esphome/components/bl0939/* @ziceva
|
esphome/components/bl0939/* @ziceva
|
||||||
esphome/components/bl0940/* @tobias-
|
esphome/components/bl0940/* @tobias-
|
||||||
esphome/components/bl0942/* @dbuezas
|
esphome/components/bl0942/* @dbuezas
|
||||||
esphome/components/ble_client/* @buxtronix
|
esphome/components/ble_client/* @buxtronix
|
||||||
esphome/components/bluetooth_proxy/* @jesserockz
|
esphome/components/bluetooth_proxy/* @jesserockz
|
||||||
esphome/components/bme680_bsec/* @trvrnrth
|
esphome/components/bme680_bsec/* @trvrnrth
|
||||||
|
esphome/components/bmi160/* @flaviut
|
||||||
esphome/components/bmp3xx/* @martgras
|
esphome/components/bmp3xx/* @martgras
|
||||||
|
esphome/components/bmp581/* @kahrendt
|
||||||
esphome/components/bp1658cj/* @Cossid
|
esphome/components/bp1658cj/* @Cossid
|
||||||
esphome/components/bp5758d/* @Cossid
|
esphome/components/bp5758d/* @Cossid
|
||||||
esphome/components/button/* @esphome/core
|
esphome/components/button/* @esphome/core
|
||||||
esphome/components/canbus/* @danielschramm @mvturnho
|
esphome/components/canbus/* @danielschramm @mvturnho
|
||||||
esphome/components/cap1188/* @MrEditor97
|
esphome/components/cap1188/* @mreditor97
|
||||||
esphome/components/captive_portal/* @OttoWinter
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
esphome/components/ccs811/* @habbie
|
esphome/components/ccs811/* @habbie
|
||||||
esphome/components/cd74hc4067/* @asoehlke
|
esphome/components/cd74hc4067/* @asoehlke
|
||||||
|
@ -75,13 +82,14 @@ esphome/components/display_menu_base/* @numo68
|
||||||
esphome/components/dps310/* @kbx81
|
esphome/components/dps310/* @kbx81
|
||||||
esphome/components/ds1307/* @badbadc0ffee
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
esphome/components/dsmr/* @glmnet @zuidwijk
|
esphome/components/dsmr/* @glmnet @zuidwijk
|
||||||
|
esphome/components/duty_time/* @dudanov
|
||||||
esphome/components/ee895/* @Stock-M
|
esphome/components/ee895/* @Stock-M
|
||||||
esphome/components/ektf2232/* @jesserockz
|
esphome/components/ektf2232/* @jesserockz
|
||||||
esphome/components/ens210/* @itn3rd77
|
esphome/components/ens210/* @itn3rd77
|
||||||
esphome/components/esp32/* @esphome/core
|
esphome/components/esp32/* @esphome/core
|
||||||
esphome/components/esp32_ble/* @jesserockz
|
esphome/components/esp32_ble/* @jesserockz
|
||||||
esphome/components/esp32_ble_client/* @jesserockz
|
esphome/components/esp32_ble_client/* @jesserockz
|
||||||
esphome/components/esp32_ble_server/* @jesserockz
|
esphome/components/esp32_ble_server/* @clydebarrow @jesserockz
|
||||||
esphome/components/esp32_camera_web_server/* @ayufan
|
esphome/components/esp32_camera_web_server/* @ayufan
|
||||||
esphome/components/esp32_can/* @Sympatron
|
esphome/components/esp32_can/* @Sympatron
|
||||||
esphome/components/esp32_improv/* @jesserockz
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
|
@ -96,11 +104,13 @@ esphome/components/fastled_base/* @OttoWinter
|
||||||
esphome/components/feedback/* @ianchi
|
esphome/components/feedback/* @ianchi
|
||||||
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||||
esphome/components/fs3000/* @kahrendt
|
esphome/components/fs3000/* @kahrendt
|
||||||
|
esphome/components/gcja5/* @gcormier
|
||||||
esphome/components/globals/* @esphome/core
|
esphome/components/globals/* @esphome/core
|
||||||
esphome/components/gp8403/* @jesserockz
|
esphome/components/gp8403/* @jesserockz
|
||||||
esphome/components/gpio/* @esphome/core
|
esphome/components/gpio/* @esphome/core
|
||||||
esphome/components/gps/* @coogle
|
esphome/components/gps/* @coogle
|
||||||
esphome/components/graph/* @synco
|
esphome/components/graph/* @synco
|
||||||
|
esphome/components/grove_tb6612fng/* @max246
|
||||||
esphome/components/growatt_solar/* @leeuwte
|
esphome/components/growatt_solar/* @leeuwte
|
||||||
esphome/components/haier/* @paveldn
|
esphome/components/haier/* @paveldn
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
|
@ -124,7 +134,7 @@ esphome/components/i2s_audio/speaker/* @jesserockz
|
||||||
esphome/components/ili9xxx/* @nielsnl68
|
esphome/components/ili9xxx/* @nielsnl68
|
||||||
esphome/components/improv_base/* @esphome/core
|
esphome/components/improv_base/* @esphome/core
|
||||||
esphome/components/improv_serial/* @esphome/core
|
esphome/components/improv_serial/* @esphome/core
|
||||||
esphome/components/ina260/* @MrEditor97
|
esphome/components/ina260/* @mreditor97
|
||||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||||
esphome/components/inkplate6/* @jesserockz
|
esphome/components/inkplate6/* @jesserockz
|
||||||
esphome/components/integration/* @OttoWinter
|
esphome/components/integration/* @OttoWinter
|
||||||
|
@ -136,9 +146,12 @@ esphome/components/key_collector/* @ssieb
|
||||||
esphome/components/key_provider/* @ssieb
|
esphome/components/key_provider/* @ssieb
|
||||||
esphome/components/kuntze/* @ssieb
|
esphome/components/kuntze/* @ssieb
|
||||||
esphome/components/lcd_menu/* @numo68
|
esphome/components/lcd_menu/* @numo68
|
||||||
esphome/components/ld2410/* @sebcaps
|
esphome/components/ld2410/* @regevbr @sebcaps
|
||||||
esphome/components/ledc/* @OttoWinter
|
esphome/components/ledc/* @OttoWinter
|
||||||
|
esphome/components/libretiny/* @kuba2k2
|
||||||
|
esphome/components/libretiny_pwm/* @kuba2k2
|
||||||
esphome/components/light/* @esphome/core
|
esphome/components/light/* @esphome/core
|
||||||
|
esphome/components/lightwaverf/* @max246
|
||||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||||
esphome/components/lock/* @esphome/core
|
esphome/components/lock/* @esphome/core
|
||||||
esphome/components/logger/* @esphome/core
|
esphome/components/logger/* @esphome/core
|
||||||
|
@ -160,7 +173,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||||
esphome/components/mcp3204/* @rsumner
|
esphome/components/mcp3204/* @rsumner
|
||||||
esphome/components/mcp4728/* @berfenger
|
esphome/components/mcp4728/* @berfenger
|
||||||
esphome/components/mcp47a1/* @jesserockz
|
esphome/components/mcp47a1/* @jesserockz
|
||||||
esphome/components/mcp9600/* @MrEditor97
|
esphome/components/mcp9600/* @mreditor97
|
||||||
esphome/components/mcp9808/* @k7hpn
|
esphome/components/mcp9808/* @k7hpn
|
||||||
esphome/components/md5/* @esphome/core
|
esphome/components/md5/* @esphome/core
|
||||||
esphome/components/mdns/* @esphome/core
|
esphome/components/mdns/* @esphome/core
|
||||||
|
@ -199,10 +212,12 @@ esphome/components/output/* @esphome/core
|
||||||
esphome/components/pca6416a/* @Mat931
|
esphome/components/pca6416a/* @Mat931
|
||||||
esphome/components/pca9554/* @hwstar
|
esphome/components/pca9554/* @hwstar
|
||||||
esphome/components/pcf85063/* @brogon
|
esphome/components/pcf85063/* @brogon
|
||||||
|
esphome/components/pcf8563/* @KoenBreeman
|
||||||
esphome/components/pid/* @OttoWinter
|
esphome/components/pid/* @OttoWinter
|
||||||
esphome/components/pipsolar/* @andreashergert1984
|
esphome/components/pipsolar/* @andreashergert1984
|
||||||
esphome/components/pm1006/* @habbie
|
esphome/components/pm1006/* @habbie
|
||||||
esphome/components/pmsa003i/* @sjtrny
|
esphome/components/pmsa003i/* @sjtrny
|
||||||
|
esphome/components/pmwcs3/* @SeByDocKy
|
||||||
esphome/components/pn532/* @OttoWinter @jesserockz
|
esphome/components/pn532/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||||
|
@ -224,6 +239,7 @@ esphome/components/rgbct/* @jesserockz
|
||||||
esphome/components/rp2040/* @jesserockz
|
esphome/components/rp2040/* @jesserockz
|
||||||
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
|
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
|
||||||
esphome/components/rp2040_pwm/* @jesserockz
|
esphome/components/rp2040_pwm/* @jesserockz
|
||||||
|
esphome/components/rtl87xx/* @kuba2k2
|
||||||
esphome/components/rtttl/* @glmnet
|
esphome/components/rtttl/* @glmnet
|
||||||
esphome/components/safe_mode/* @jsuanet @paulmonigatti
|
esphome/components/safe_mode/* @jsuanet @paulmonigatti
|
||||||
esphome/components/scd4x/* @martgras @sjtrny
|
esphome/components/scd4x/* @martgras @sjtrny
|
||||||
|
@ -232,6 +248,7 @@ esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||||
esphome/components/sdp3x/* @Azimath
|
esphome/components/sdp3x/* @Azimath
|
||||||
esphome/components/selec_meter/* @sourabhjaiswal
|
esphome/components/selec_meter/* @sourabhjaiswal
|
||||||
esphome/components/select/* @esphome/core
|
esphome/components/select/* @esphome/core
|
||||||
|
esphome/components/sen0321/* @notjj
|
||||||
esphome/components/sen21231/* @shreyaskarnik
|
esphome/components/sen21231/* @shreyaskarnik
|
||||||
esphome/components/sen5x/* @martgras
|
esphome/components/sen5x/* @martgras
|
||||||
esphome/components/sensirion_common/* @martgras
|
esphome/components/sensirion_common/* @martgras
|
||||||
|
@ -254,6 +271,8 @@ esphome/components/socket/* @esphome/core
|
||||||
esphome/components/sonoff_d1/* @anatoly-savchenkov
|
esphome/components/sonoff_d1/* @anatoly-savchenkov
|
||||||
esphome/components/speaker/* @jesserockz
|
esphome/components/speaker/* @jesserockz
|
||||||
esphome/components/spi/* @esphome/core
|
esphome/components/spi/* @esphome/core
|
||||||
|
esphome/components/spi_device/* @clydebarrow
|
||||||
|
esphome/components/spi_led_strip/* @clydebarrow
|
||||||
esphome/components/sprinkler/* @kbx81
|
esphome/components/sprinkler/* @kbx81
|
||||||
esphome/components/sps30/* @martgras
|
esphome/components/sps30/* @martgras
|
||||||
esphome/components/ssd1322_base/* @kbx81
|
esphome/components/ssd1322_base/* @kbx81
|
||||||
|
@ -293,6 +312,7 @@ esphome/components/tof10120/* @wstrzalka
|
||||||
esphome/components/toshiba/* @kbx81
|
esphome/components/toshiba/* @kbx81
|
||||||
esphome/components/touchscreen/* @jesserockz
|
esphome/components/touchscreen/* @jesserockz
|
||||||
esphome/components/tsl2591/* @wjcarpenter
|
esphome/components/tsl2591/* @wjcarpenter
|
||||||
|
esphome/components/tt21100/* @kroimon
|
||||||
esphome/components/tuya/binary_sensor/* @jesserockz
|
esphome/components/tuya/binary_sensor/* @jesserockz
|
||||||
esphome/components/tuya/climate/* @jesserockz
|
esphome/components/tuya/climate/* @jesserockz
|
||||||
esphome/components/tuya/number/* @frankiboy1
|
esphome/components/tuya/number/* @frankiboy1
|
||||||
|
@ -309,13 +329,17 @@ esphome/components/version/* @esphome/core
|
||||||
esphome/components/voice_assistant/* @jesserockz
|
esphome/components/voice_assistant/* @jesserockz
|
||||||
esphome/components/wake_on_lan/* @willwill2will54
|
esphome/components/wake_on_lan/* @willwill2will54
|
||||||
esphome/components/web_server_base/* @OttoWinter
|
esphome/components/web_server_base/* @OttoWinter
|
||||||
|
esphome/components/web_server_idf/* @dentra
|
||||||
esphome/components/whirlpool/* @glmnet
|
esphome/components/whirlpool/* @glmnet
|
||||||
esphome/components/whynter/* @aeonsablaze
|
esphome/components/whynter/* @aeonsablaze
|
||||||
esphome/components/wiegand/* @ssieb
|
esphome/components/wiegand/* @ssieb
|
||||||
|
esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
|
||||||
esphome/components/wl_134/* @hobbypunk90
|
esphome/components/wl_134/* @hobbypunk90
|
||||||
esphome/components/x9c/* @EtienneMD
|
esphome/components/x9c/* @EtienneMD
|
||||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||||
esphome/components/xiaomi_mhoc303/* @drug123
|
esphome/components/xiaomi_mhoc303/* @drug123
|
||||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
||||||
|
esphome/components/xl9535/* @mreditor97
|
||||||
esphome/components/xpt2046/* @nielsnl68 @numo68
|
esphome/components/xpt2046/* @nielsnl68 @numo68
|
||||||
|
esphome/components/zio_ultrasonic/* @kahrendt
|
||||||
|
|
|
@ -22,16 +22,24 @@ RUN \
|
||||||
python3=3.9.2-3 \
|
python3=3.9.2-3 \
|
||||||
python3-pip=20.3.4-4+deb11u1 \
|
python3-pip=20.3.4-4+deb11u1 \
|
||||||
python3-setuptools=52.0.0-4 \
|
python3-setuptools=52.0.0-4 \
|
||||||
python3-pil=8.1.2+dfsg-0.3+deb11u1 \
|
|
||||||
python3-cryptography=3.3.2-1 \
|
python3-cryptography=3.3.2-1 \
|
||||||
python3-venv=3.9.2-3 \
|
python3-venv=3.9.2-3 \
|
||||||
iputils-ping=3:20210202-1 \
|
iputils-ping=3:20210202-1 \
|
||||||
git=1:2.30.2-1+deb11u2 \
|
git=1:2.30.2-1+deb11u2 \
|
||||||
curl=7.74.0-1.3+deb11u7 \
|
curl=7.74.0-1.3+deb11u7 \
|
||||||
openssh-client=1:8.4p1-5+deb11u1 \
|
openssh-client=1:8.4p1-5+deb11u1 \
|
||||||
libcairo2=1.16.0-5 \
|
|
||||||
python3-cffi=1.14.5-1 \
|
python3-cffi=1.14.5-1 \
|
||||||
&& rm -rf \
|
libcairo2=1.16.0-5 \
|
||||||
|
patch=2.7.6-7; \
|
||||||
|
if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
build-essential=12.9 \
|
||||||
|
python3-dev=3.9.2-3 \
|
||||||
|
zlib1g-dev=1:1.2.11.dfsg-2+deb11u2 \
|
||||||
|
libjpeg-dev=1:2.0.6-4 \
|
||||||
|
libfreetype-dev=2.10.4+dfsg-1+deb11u1; \
|
||||||
|
fi; \
|
||||||
|
rm -rf \
|
||||||
/tmp/* \
|
/tmp/* \
|
||||||
/var/{cache,log}/* \
|
/var/{cache,log}/* \
|
||||||
/var/lib/apt/lists/*
|
/var/lib/apt/lists/*
|
||||||
|
@ -54,7 +62,7 @@ RUN \
|
||||||
# Ubuntu python3-pip is missing wheel
|
# Ubuntu python3-pip is missing wheel
|
||||||
pip3 install --no-cache-dir \
|
pip3 install --no-cache-dir \
|
||||||
wheel==0.37.1 \
|
wheel==0.37.1 \
|
||||||
platformio==6.1.7 \
|
platformio==6.1.11 \
|
||||||
# Change some platformio settings
|
# Change some platformio settings
|
||||||
&& platformio settings set enable_telemetry No \
|
&& platformio settings set enable_telemetry No \
|
||||||
&& platformio settings set check_platformio_interval 1000000 \
|
&& platformio settings set check_platformio_interval 1000000 \
|
||||||
|
|
|
@ -35,11 +35,16 @@ if bashio::config.has_value 'default_compile_process_limit'; then
|
||||||
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit')
|
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit')
|
||||||
else
|
else
|
||||||
if grep -q 'Raspberry Pi 3' /proc/cpuinfo; then
|
if grep -q 'Raspberry Pi 3' /proc/cpuinfo; then
|
||||||
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=1;
|
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "${pio_cache_base}"
|
mkdir -p "${pio_cache_base}"
|
||||||
|
|
||||||
|
if bashio::fs.directory_exists '/config/esphome/.esphome'; then
|
||||||
|
bashio::log.info "Removing old .esphome directory..."
|
||||||
|
rm -rf /config/esphome/.esphome
|
||||||
|
fi
|
||||||
|
|
||||||
bashio::log.info "Starting ESPHome dashboard..."
|
bashio::log.info "Starting ESPHome dashboard..."
|
||||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
|
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
|
||||||
|
|
|
@ -26,13 +26,15 @@ from esphome.const import (
|
||||||
CONF_ESPHOME,
|
CONF_ESPHOME,
|
||||||
CONF_PLATFORMIO_OPTIONS,
|
CONF_PLATFORMIO_OPTIONS,
|
||||||
CONF_SUBSTITUTIONS,
|
CONF_SUBSTITUTIONS,
|
||||||
|
PLATFORM_BK72XX,
|
||||||
|
PLATFORM_RTL87XX,
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
SECRETS_FILES,
|
SECRETS_FILES,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, EsphomeError, coroutine
|
from esphome.core import CORE, EsphomeError, coroutine
|
||||||
from esphome.helpers import indent
|
from esphome.helpers import indent, is_ip_address
|
||||||
from esphome.util import (
|
from esphome.util import (
|
||||||
run_external_command,
|
run_external_command,
|
||||||
run_external_process,
|
run_external_process,
|
||||||
|
@ -83,6 +85,8 @@ def choose_upload_log_host(
|
||||||
options = []
|
options = []
|
||||||
for port in get_serial_ports():
|
for port in get_serial_ports():
|
||||||
options.append((f"{port.path} ({port.description})", port.path))
|
options.append((f"{port.path} ({port.description})", port.path))
|
||||||
|
if default == "SERIAL":
|
||||||
|
return choose_prompt(options, purpose=purpose)
|
||||||
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
|
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
|
||||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||||
if default == "OTA":
|
if default == "OTA":
|
||||||
|
@ -216,14 +220,16 @@ def compile_program(args, config):
|
||||||
return 0 if idedata is not None else 1
|
return 0 if idedata is not None else 1
|
||||||
|
|
||||||
|
|
||||||
def upload_using_esptool(config, port):
|
def upload_using_esptool(config, port, file):
|
||||||
from esphome import platformio_api
|
from esphome import platformio_api
|
||||||
|
|
||||||
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
||||||
"upload_speed", 460800
|
"upload_speed", 460800
|
||||||
)
|
)
|
||||||
|
|
||||||
def run_esptool(baud_rate):
|
if file is not None:
|
||||||
|
flash_images = [platformio_api.FlashImage(path=file, offset="0x0")]
|
||||||
|
else:
|
||||||
idedata = platformio_api.get_idedata(config)
|
idedata = platformio_api.get_idedata(config)
|
||||||
|
|
||||||
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
|
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
|
||||||
|
@ -234,12 +240,13 @@ def upload_using_esptool(config, port):
|
||||||
*idedata.extra_flash_images,
|
*idedata.extra_flash_images,
|
||||||
]
|
]
|
||||||
|
|
||||||
mcu = "esp8266"
|
mcu = "esp8266"
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
|
||||||
mcu = get_esp32_variant().lower()
|
mcu = get_esp32_variant().lower()
|
||||||
|
|
||||||
|
def run_esptool(baud_rate):
|
||||||
cmd = [
|
cmd = [
|
||||||
"esptool.py",
|
"esptool.py",
|
||||||
"--before",
|
"--before",
|
||||||
|
@ -278,20 +285,26 @@ def upload_using_esptool(config, port):
|
||||||
return run_esptool(115200)
|
return run_esptool(115200)
|
||||||
|
|
||||||
|
|
||||||
|
def upload_using_platformio(config, port):
|
||||||
|
from esphome import platformio_api
|
||||||
|
|
||||||
|
upload_args = ["-t", "upload", "-t", "nobuild"]
|
||||||
|
if port is not None:
|
||||||
|
upload_args += ["--upload-port", port]
|
||||||
|
return platformio_api.run_platformio_cli_run(config, CORE.verbose, *upload_args)
|
||||||
|
|
||||||
|
|
||||||
def upload_program(config, args, host):
|
def upload_program(config, args, host):
|
||||||
if get_port_type(host) == "SERIAL":
|
if get_port_type(host) == "SERIAL":
|
||||||
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
||||||
return upload_using_esptool(config, host)
|
file = getattr(args, "file", None)
|
||||||
|
return upload_using_esptool(config, host, file)
|
||||||
|
|
||||||
if CORE.target_platform in (PLATFORM_RP2040):
|
if CORE.target_platform in (PLATFORM_RP2040):
|
||||||
from esphome import platformio_api
|
return upload_using_platformio(config, args.device)
|
||||||
|
|
||||||
upload_args = ["-t", "upload"]
|
if CORE.target_platform in (PLATFORM_BK72XX, PLATFORM_RTL87XX):
|
||||||
if args.device is not None:
|
return upload_using_platformio(config, host)
|
||||||
upload_args += ["--upload-port", args.device]
|
|
||||||
return platformio_api.run_platformio_cli_run(
|
|
||||||
config, CORE.verbose, *upload_args
|
|
||||||
)
|
|
||||||
|
|
||||||
return 1 # Unknown target platform
|
return 1 # Unknown target platform
|
||||||
|
|
||||||
|
@ -308,8 +321,10 @@ def upload_program(config, args, host):
|
||||||
password = ota_conf.get(CONF_PASSWORD, "")
|
password = ota_conf.get(CONF_PASSWORD, "")
|
||||||
|
|
||||||
if (
|
if (
|
||||||
get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED]
|
not is_ip_address(CORE.address)
|
||||||
) and CONF_MQTT in config:
|
and (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED])
|
||||||
|
and CONF_MQTT in config
|
||||||
|
):
|
||||||
from esphome import mqtt
|
from esphome import mqtt
|
||||||
|
|
||||||
host = mqtt.get_esphome_device_ip(
|
host = mqtt.get_esphome_device_ip(
|
||||||
|
@ -363,10 +378,16 @@ def command_wizard(args):
|
||||||
|
|
||||||
|
|
||||||
def command_config(args, config):
|
def command_config(args, config):
|
||||||
_LOGGER.info("Configuration is valid!")
|
|
||||||
if not CORE.verbose:
|
if not CORE.verbose:
|
||||||
config = strip_default_ids(config)
|
config = strip_default_ids(config)
|
||||||
safe_print(yaml_util.dump(config, args.show_secrets))
|
output = yaml_util.dump(config, args.show_secrets)
|
||||||
|
# add the console decoration so the front-end can hide the secrets
|
||||||
|
if not args.show_secrets:
|
||||||
|
output = re.sub(
|
||||||
|
r"(password|key|psk|ssid)\: (.+)", r"\1: \\033[5m\2\\033[6m", output
|
||||||
|
)
|
||||||
|
safe_print(output)
|
||||||
|
_LOGGER.info("Configuration is valid!")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|
1
esphome/components/a01nyub/__init__.py
Normal file
1
esphome/components/a01nyub/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@MrSuicideParrot"]
|
57
esphome/components/a01nyub/a01nyub.cpp
Normal file
57
esphome/components/a01nyub/a01nyub.cpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// Datasheet https://wiki.dfrobot.com/A01NYUB%20Waterproof%20Ultrasonic%20Sensor%20SKU:%20SEN0313
|
||||||
|
|
||||||
|
#include "a01nyub.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace a01nyub {
|
||||||
|
|
||||||
|
static const char *const TAG = "a01nyub.sensor";
|
||||||
|
static const uint8_t MAX_DATA_LENGTH_BYTES = 4;
|
||||||
|
|
||||||
|
void A01nyubComponent::loop() {
|
||||||
|
uint8_t data;
|
||||||
|
while (this->available() > 0) {
|
||||||
|
if (this->read_byte(&data)) {
|
||||||
|
buffer_.push_back(data);
|
||||||
|
this->check_buffer_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void A01nyubComponent::check_buffer_() {
|
||||||
|
if (this->buffer_.size() >= MAX_DATA_LENGTH_BYTES) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < this->buffer_.size(); i++) {
|
||||||
|
// Look for the first packet
|
||||||
|
if (this->buffer_[i] == 0xFF) {
|
||||||
|
if (i + 1 + 3 < this->buffer_.size()) { // Packet is not complete
|
||||||
|
return; // Wait for completion
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t checksum = (this->buffer_[i] + this->buffer_[i + 1] + this->buffer_[i + 2]) & 0xFF;
|
||||||
|
if (this->buffer_[i + 3] == checksum) {
|
||||||
|
float distance = (this->buffer_[i + 1] << 8) + this->buffer_[i + 2];
|
||||||
|
if (distance > 280) {
|
||||||
|
float meters = distance / 1000.0;
|
||||||
|
ESP_LOGV(TAG, "Distance from sensor: %f mm, %f m", distance, meters);
|
||||||
|
this->publish_state(meters);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->buffer_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void A01nyubComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "A01nyub Sensor:");
|
||||||
|
LOG_SENSOR(" ", "Distance", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace a01nyub
|
||||||
|
} // namespace esphome
|
27
esphome/components/a01nyub/a01nyub.h
Normal file
27
esphome/components/a01nyub/a01nyub.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace a01nyub {
|
||||||
|
|
||||||
|
class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
|
||||||
|
public:
|
||||||
|
// Nothing really public.
|
||||||
|
|
||||||
|
// ========== INTERNAL METHODS ==========
|
||||||
|
void loop() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void check_buffer_();
|
||||||
|
|
||||||
|
std::vector<uint8_t> buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace a01nyub
|
||||||
|
} // namespace esphome
|
41
esphome/components/a01nyub/sensor.py
Normal file
41
esphome/components/a01nyub/sensor.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import sensor, uart
|
||||||
|
from esphome.const import (
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_METER,
|
||||||
|
ICON_ARROW_EXPAND_VERTICAL,
|
||||||
|
DEVICE_CLASS_DISTANCE,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@MrSuicideParrot"]
|
||||||
|
DEPENDENCIES = ["uart"]
|
||||||
|
|
||||||
|
a01nyub_ns = cg.esphome_ns.namespace("a01nyub")
|
||||||
|
A01nyubComponent = a01nyub_ns.class_(
|
||||||
|
"A01nyubComponent", sensor.Sensor, cg.Component, uart.UARTDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||||
|
A01nyubComponent,
|
||||||
|
unit_of_measurement=UNIT_METER,
|
||||||
|
icon=ICON_ARROW_EXPAND_VERTICAL,
|
||||||
|
accuracy_decimals=3,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
device_class=DEVICE_CLASS_DISTANCE,
|
||||||
|
).extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||||
|
"a01nyub",
|
||||||
|
baud_rate=9600,
|
||||||
|
require_tx=False,
|
||||||
|
require_rx=True,
|
||||||
|
data_bits=8,
|
||||||
|
parity=None,
|
||||||
|
stop_bits=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = await sensor.new_sensor(config)
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await uart.register_uart_device(var, config)
|
|
@ -28,6 +28,6 @@ async def to_code(config):
|
||||||
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
||||||
cg.add(var.set_dir_pin(dir_pin))
|
cg.add(var.set_dir_pin(dir_pin))
|
||||||
|
|
||||||
if CONF_SLEEP_PIN in config:
|
if sleep_pin_config := config.get(CONF_SLEEP_PIN):
|
||||||
sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
sleep_pin = await cg.gpio_pin_expression(sleep_pin_config)
|
||||||
cg.add(var.set_sleep_pin(sleep_pin))
|
cg.add(var.set_sleep_pin(sleep_pin))
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.const import CONF_INPUT
|
from esphome.const import CONF_ANALOG, CONF_INPUT
|
||||||
|
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
from esphome.const import PLATFORM_ESP8266
|
||||||
from esphome.components.esp32.const import (
|
from esphome.components.esp32.const import (
|
||||||
VARIANT_ESP32,
|
VARIANT_ESP32,
|
||||||
|
VARIANT_ESP32C2,
|
||||||
VARIANT_ESP32C3,
|
VARIANT_ESP32C3,
|
||||||
|
VARIANT_ESP32C6,
|
||||||
VARIANT_ESP32H2,
|
VARIANT_ESP32H2,
|
||||||
VARIANT_ESP32S2,
|
VARIANT_ESP32S2,
|
||||||
VARIANT_ESP32S3,
|
VARIANT_ESP32S3,
|
||||||
|
@ -24,6 +27,7 @@ ATTENUATION_MODES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
|
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
|
||||||
|
adc2_channel_t = cg.global_ns.enum("adc2_channel_t")
|
||||||
|
|
||||||
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
|
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
|
||||||
# pin to adc1 channel mapping
|
# pin to adc1 channel mapping
|
||||||
|
@ -69,6 +73,22 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
},
|
},
|
||||||
|
VARIANT_ESP32C2: {
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32C6: {
|
||||||
|
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,
|
||||||
|
5: adc1_channel_t.ADC1_CHANNEL_5,
|
||||||
|
6: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
|
},
|
||||||
VARIANT_ESP32H2: {
|
VARIANT_ESP32H2: {
|
||||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
@ -78,10 +98,55 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
||||||
|
# TODO: add other variants
|
||||||
|
VARIANT_ESP32: {
|
||||||
|
4: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
|
0: adc2_channel_t.ADC2_CHANNEL_1,
|
||||||
|
2: adc2_channel_t.ADC2_CHANNEL_2,
|
||||||
|
15: adc2_channel_t.ADC2_CHANNEL_3,
|
||||||
|
13: adc2_channel_t.ADC2_CHANNEL_4,
|
||||||
|
12: adc2_channel_t.ADC2_CHANNEL_5,
|
||||||
|
14: adc2_channel_t.ADC2_CHANNEL_6,
|
||||||
|
27: adc2_channel_t.ADC2_CHANNEL_7,
|
||||||
|
25: adc2_channel_t.ADC2_CHANNEL_8,
|
||||||
|
26: adc2_channel_t.ADC2_CHANNEL_9,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32S2: {
|
||||||
|
11: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
|
12: adc2_channel_t.ADC2_CHANNEL_1,
|
||||||
|
13: adc2_channel_t.ADC2_CHANNEL_2,
|
||||||
|
14: adc2_channel_t.ADC2_CHANNEL_3,
|
||||||
|
15: adc2_channel_t.ADC2_CHANNEL_4,
|
||||||
|
16: adc2_channel_t.ADC2_CHANNEL_5,
|
||||||
|
17: adc2_channel_t.ADC2_CHANNEL_6,
|
||||||
|
18: adc2_channel_t.ADC2_CHANNEL_7,
|
||||||
|
19: adc2_channel_t.ADC2_CHANNEL_8,
|
||||||
|
20: adc2_channel_t.ADC2_CHANNEL_9,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32S3: {
|
||||||
|
11: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
|
12: adc2_channel_t.ADC2_CHANNEL_1,
|
||||||
|
13: adc2_channel_t.ADC2_CHANNEL_2,
|
||||||
|
14: adc2_channel_t.ADC2_CHANNEL_3,
|
||||||
|
15: adc2_channel_t.ADC2_CHANNEL_4,
|
||||||
|
16: adc2_channel_t.ADC2_CHANNEL_5,
|
||||||
|
17: adc2_channel_t.ADC2_CHANNEL_6,
|
||||||
|
18: adc2_channel_t.ADC2_CHANNEL_7,
|
||||||
|
19: adc2_channel_t.ADC2_CHANNEL_8,
|
||||||
|
20: adc2_channel_t.ADC2_CHANNEL_9,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32C3: {
|
||||||
|
5: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def validate_adc_pin(value):
|
def validate_adc_pin(value):
|
||||||
if str(value).upper() == "VCC":
|
if str(value).upper() == "VCC":
|
||||||
return cv.only_on_esp8266("VCC")
|
if CORE.is_rp2040:
|
||||||
|
return pins.internal_gpio_input_pin_schema(29)
|
||||||
|
return cv.only_on([PLATFORM_ESP8266])("VCC")
|
||||||
|
|
||||||
if str(value).upper() == "TEMPERATURE":
|
if str(value).upper() == "TEMPERATURE":
|
||||||
return cv.only_on_rp2040("TEMPERATURE")
|
return cv.only_on_rp2040("TEMPERATURE")
|
||||||
|
@ -89,22 +154,27 @@ def validate_adc_pin(value):
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
value = pins.internal_gpio_input_pin_number(value)
|
value = pins.internal_gpio_input_pin_number(value)
|
||||||
variant = get_esp32_variant()
|
variant = get_esp32_variant()
|
||||||
if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
|
if (
|
||||||
|
variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL
|
||||||
|
and variant not in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL
|
||||||
|
):
|
||||||
raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
|
raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
|
||||||
|
|
||||||
if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
|
if (
|
||||||
|
value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]
|
||||||
|
and value not in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
|
||||||
|
):
|
||||||
raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
|
raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
|
||||||
|
|
||||||
return pins.internal_gpio_input_pin_schema(value)
|
return pins.internal_gpio_input_pin_schema(value)
|
||||||
|
|
||||||
if CORE.is_esp8266:
|
if CORE.is_esp8266:
|
||||||
from esphome.components.esp8266.gpio import CONF_ANALOG
|
|
||||||
|
|
||||||
value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
|
value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
|
||||||
value
|
value
|
||||||
)
|
)
|
||||||
|
|
||||||
if value != 17: # A0
|
if value != 17: # A0
|
||||||
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
|
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC")
|
||||||
return pins.gpio_pin_schema(
|
return pins.gpio_pin_schema(
|
||||||
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
|
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
|
||||||
)(value)
|
)(value)
|
||||||
|
@ -112,7 +182,12 @@ def validate_adc_pin(value):
|
||||||
if CORE.is_rp2040:
|
if CORE.is_rp2040:
|
||||||
value = pins.internal_gpio_input_pin_number(value)
|
value = pins.internal_gpio_input_pin_number(value)
|
||||||
if value not in (26, 27, 28, 29):
|
if value not in (26, 27, 28, 29):
|
||||||
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC.")
|
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC")
|
||||||
return pins.internal_gpio_input_pin_schema(value)
|
return pins.internal_gpio_input_pin_schema(value)
|
||||||
|
|
||||||
|
if CORE.is_libretiny:
|
||||||
|
return pins.gpio_pin_schema(
|
||||||
|
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
|
||||||
|
)(value)
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "adc_sensor.h"
|
#include "adc_sensor.h"
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
@ -12,6 +12,9 @@ ADC_MODE(ADC_VCC)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
|
#ifdef CYW43_USES_VSYS_PIN
|
||||||
|
#include "pico/cyw43_arch.h"
|
||||||
|
#endif
|
||||||
#include <hardware/adc.h>
|
#include <hardware/adc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -20,15 +23,15 @@ namespace adc {
|
||||||
|
|
||||||
static const char *const TAG = "adc";
|
static const char *const TAG = "adc";
|
||||||
|
|
||||||
// 13bit for S2, and 12bit for all other esp32 variants
|
// 13-bit for S2, 12-bit for all other ESP32 variants
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
||||||
|
|
||||||
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
|
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
|
||||||
#if USE_ESP32_VARIANT_ESP32S2
|
#if USE_ESP32_VARIANT_ESP32S2
|
||||||
static const int SOC_ADC_RTC_MAX_BITWIDTH = 13;
|
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
|
||||||
#else
|
#else
|
||||||
static const int SOC_ADC_RTC_MAX_BITWIDTH = 12;
|
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -47,14 +50,21 @@ extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
if (channel1_ != ADC1_CHANNEL_MAX) {
|
||||||
if (!autorange_) {
|
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
||||||
adc1_config_channel_atten(channel_, attenuation_);
|
if (!autorange_) {
|
||||||
|
adc1_config_channel_atten(channel1_, attenuation_);
|
||||||
|
}
|
||||||
|
} else if (channel2_ != ADC2_CHANNEL_MAX) {
|
||||||
|
if (!autorange_) {
|
||||||
|
adc2_config_channel_atten(channel2_, attenuation_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load characteristics for each attenuation
|
// load characteristics for each attenuation
|
||||||
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
|
for (int32_t i = 0; i <= ADC_ATTEN_DB_11; i++) {
|
||||||
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
|
auto adc_unit = 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,
|
||||||
1100, // default vref
|
1100, // default vref
|
||||||
&cal_characteristics_[i]);
|
&cal_characteristics_[i]);
|
||||||
switch (cal_value) {
|
switch (cal_value) {
|
||||||
|
@ -85,13 +95,13 @@ extern "C"
|
||||||
|
|
||||||
void ADCSensor::dump_config() {
|
void ADCSensor::dump_config() {
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
#ifdef USE_ESP8266
|
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||||
#else
|
#else
|
||||||
LOG_PIN(" Pin: ", pin_);
|
LOG_PIN(" Pin: ", pin_);
|
||||||
#endif
|
#endif
|
||||||
#endif // USE_ESP8266
|
#endif // USE_ESP8266 || USE_LIBRETINY
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
LOG_PIN(" Pin: ", pin_);
|
LOG_PIN(" Pin: ", pin_);
|
||||||
|
@ -116,13 +126,19 @@ void ADCSensor::dump_config() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // USE_ESP32
|
#endif // USE_ESP32
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
if (this->is_temperature_) {
|
if (this->is_temperature_) {
|
||||||
ESP_LOGCONFIG(TAG, " Pin: Temperature");
|
ESP_LOGCONFIG(TAG, " Pin: Temperature");
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||||
|
#else
|
||||||
LOG_PIN(" Pin: ", pin_);
|
LOG_PIN(" Pin: ", pin_);
|
||||||
|
#endif // USE_ADC_SENSOR_VCC
|
||||||
}
|
}
|
||||||
#endif
|
#endif // USE_RP2040
|
||||||
|
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,9 +152,9 @@ void ADCSensor::update() {
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
float ADCSensor::sample() {
|
float ADCSensor::sample() {
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
int32_t raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
||||||
#else
|
#else
|
||||||
int raw = analogRead(this->pin_->get_pin()); // NOLINT
|
int32_t raw = analogRead(this->pin_->get_pin()); // NOLINT
|
||||||
#endif
|
#endif
|
||||||
if (output_raw_) {
|
if (output_raw_) {
|
||||||
return raw;
|
return raw;
|
||||||
|
@ -150,29 +166,53 @@ float ADCSensor::sample() {
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
float ADCSensor::sample() {
|
float ADCSensor::sample() {
|
||||||
if (!autorange_) {
|
if (!autorange_) {
|
||||||
int raw = adc1_get_raw(channel_);
|
int raw = -1;
|
||||||
|
if (channel1_ != ADC1_CHANNEL_MAX) {
|
||||||
|
raw = adc1_get_raw(channel1_);
|
||||||
|
} else if (channel2_ != ADC2_CHANNEL_MAX) {
|
||||||
|
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
|
||||||
|
}
|
||||||
|
|
||||||
if (raw == -1) {
|
if (raw == -1) {
|
||||||
return NAN;
|
return NAN;
|
||||||
}
|
}
|
||||||
if (output_raw_) {
|
if (output_raw_) {
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
|
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int32_t) attenuation_]);
|
||||||
return mv / 1000.0f;
|
return mv / 1000.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
int raw11, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
|
int raw11 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
|
||||||
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
|
|
||||||
raw11 = adc1_get_raw(channel_);
|
if (channel1_ != ADC1_CHANNEL_MAX) {
|
||||||
if (raw11 < ADC_MAX) {
|
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_11);
|
||||||
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
|
raw11 = adc1_get_raw(channel1_);
|
||||||
raw6 = adc1_get_raw(channel_);
|
if (raw11 < ADC_MAX) {
|
||||||
if (raw6 < ADC_MAX) {
|
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6);
|
||||||
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
|
raw6 = adc1_get_raw(channel1_);
|
||||||
raw2 = adc1_get_raw(channel_);
|
if (raw6 < ADC_MAX) {
|
||||||
if (raw2 < ADC_MAX) {
|
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_2_5);
|
||||||
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
|
raw2 = adc1_get_raw(channel1_);
|
||||||
raw0 = adc1_get_raw(channel_);
|
if (raw2 < ADC_MAX) {
|
||||||
|
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_0);
|
||||||
|
raw0 = adc1_get_raw(channel1_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (channel2_ != ADC2_CHANNEL_MAX) {
|
||||||
|
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_11);
|
||||||
|
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw11);
|
||||||
|
if (raw11 < ADC_MAX) {
|
||||||
|
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6);
|
||||||
|
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
|
||||||
|
if (raw6 < ADC_MAX) {
|
||||||
|
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_2_5);
|
||||||
|
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
|
||||||
|
if (raw2 < ADC_MAX) {
|
||||||
|
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_0);
|
||||||
|
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,10 +221,10 @@ float ADCSensor::sample() {
|
||||||
return NAN;
|
return NAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
|
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_11]);
|
||||||
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
|
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
|
||||||
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
|
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
|
||||||
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
|
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &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)
|
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
|
||||||
uint32_t c11 = std::min(raw11, ADC_HALF);
|
uint32_t c11 = std::min(raw11, ADC_HALF);
|
||||||
|
@ -206,23 +246,54 @@ float ADCSensor::sample() {
|
||||||
adc_set_temp_sensor_enabled(true);
|
adc_set_temp_sensor_enabled(true);
|
||||||
delay(1);
|
delay(1);
|
||||||
adc_select_input(4);
|
adc_select_input(4);
|
||||||
|
|
||||||
|
int32_t raw = adc_read();
|
||||||
|
adc_set_temp_sensor_enabled(false);
|
||||||
|
if (this->output_raw_) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
return raw * 3.3f / 4096.0f;
|
||||||
} else {
|
} else {
|
||||||
uint8_t pin = this->pin_->get_pin();
|
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_gpio_init(pin);
|
||||||
adc_select_input(pin - 26);
|
adc_select_input(pin - 26);
|
||||||
}
|
|
||||||
|
|
||||||
int raw = adc_read();
|
int32_t raw = adc_read();
|
||||||
if (this->is_temperature_) {
|
|
||||||
adc_set_temp_sensor_enabled(false);
|
#ifdef CYW43_USES_VSYS_PIN
|
||||||
|
if (pin == PICO_VSYS_PIN) {
|
||||||
|
cyw43_thread_exit();
|
||||||
|
}
|
||||||
|
#endif // CYW43_USES_VSYS_PIN
|
||||||
|
|
||||||
|
if (output_raw_) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
|
||||||
|
return raw * 3.3f / 4096.0f * coeff;
|
||||||
}
|
}
|
||||||
if (output_raw_) {
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
return raw * 3.3f / 4096.0f;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_LIBRETINY
|
||||||
|
float ADCSensor::sample() {
|
||||||
|
if (output_raw_) {
|
||||||
|
return analogRead(this->pin_->get_pin()); // NOLINT
|
||||||
|
}
|
||||||
|
return analogReadVoltage(this->pin_->get_pin()) / 1000.0f; // NOLINT
|
||||||
|
}
|
||||||
|
#endif // USE_LIBRETINY
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,16 +19,23 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
/// Set the attenuation for this pin. Only available on the ESP32.
|
/// Set the attenuation for this pin. Only available on the ESP32.
|
||||||
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
|
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
|
||||||
void set_channel(adc1_channel_t channel) { channel_ = channel; }
|
void set_channel1(adc1_channel_t channel) {
|
||||||
|
channel1_ = channel;
|
||||||
|
channel2_ = ADC2_CHANNEL_MAX;
|
||||||
|
}
|
||||||
|
void set_channel2(adc2_channel_t channel) {
|
||||||
|
channel2_ = channel;
|
||||||
|
channel1_ = ADC1_CHANNEL_MAX;
|
||||||
|
}
|
||||||
void set_autorange(bool autorange) { autorange_ = autorange; }
|
void set_autorange(bool autorange) { autorange_ = autorange; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Update adc values.
|
/// Update ADC values
|
||||||
void update() override;
|
void update() override;
|
||||||
/// Setup ADc
|
/// Setup ADC
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
/// `HARDWARE_LATE` setup priority.
|
/// `HARDWARE_LATE` setup priority
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
||||||
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
|
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
|
||||||
|
@ -52,9 +59,14 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||||
adc1_channel_t channel_{};
|
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
|
||||||
|
adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
|
||||||
bool autorange_{false};
|
bool autorange_{false};
|
||||||
esp_adc_cal_characteristics_t cal_characteristics_[(int) ADC_ATTEN_MAX] = {};
|
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||||
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
import esphome.final_validate as fv
|
||||||
|
from esphome.core import CORE
|
||||||
from esphome.components import sensor, voltage_sampler
|
from esphome.components import sensor, voltage_sampler
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
@ -8,15 +10,15 @@ from esphome.const import (
|
||||||
CONF_NUMBER,
|
CONF_NUMBER,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
CONF_RAW,
|
CONF_RAW,
|
||||||
|
CONF_WIFI,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE
|
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTENUATION_MODES,
|
ATTENUATION_MODES,
|
||||||
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
|
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
|
||||||
|
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
|
||||||
validate_adc_pin,
|
validate_adc_pin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +27,23 @@ AUTO_LOAD = ["voltage_sampler"]
|
||||||
|
|
||||||
def validate_config(config):
|
def validate_config(config):
|
||||||
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
|
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
|
||||||
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set.")
|
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set")
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def final_validate_config(config):
|
||||||
|
if CORE.is_esp32:
|
||||||
|
variant = get_esp32_variant()
|
||||||
|
if (
|
||||||
|
CONF_WIFI in fv.full_config.get()
|
||||||
|
and config[CONF_PIN][CONF_NUMBER]
|
||||||
|
in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
|
||||||
|
):
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{variant} doesn't support ADC on this pin when Wi-Fi is configured"
|
||||||
|
)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,6 +73,8 @@ CONFIG_SCHEMA = cv.All(
|
||||||
validate_config,
|
validate_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = final_validate_config
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
@ -69,17 +89,26 @@ async def to_code(config):
|
||||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
cg.add(var.set_pin(pin))
|
cg.add(var.set_pin(pin))
|
||||||
|
|
||||||
if CONF_RAW in config:
|
cg.add(var.set_output_raw(config[CONF_RAW]))
|
||||||
cg.add(var.set_output_raw(config[CONF_RAW]))
|
|
||||||
|
|
||||||
if CONF_ATTENUATION in config:
|
if attenuation := config.get(CONF_ATTENUATION):
|
||||||
if config[CONF_ATTENUATION] == "auto":
|
if attenuation == "auto":
|
||||||
cg.add(var.set_autorange(cg.global_ns.true))
|
cg.add(var.set_autorange(cg.global_ns.true))
|
||||||
else:
|
else:
|
||||||
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
|
cg.add(var.set_attenuation(attenuation))
|
||||||
|
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
variant = get_esp32_variant()
|
variant = get_esp32_variant()
|
||||||
pin_num = config[CONF_PIN][CONF_NUMBER]
|
pin_num = config[CONF_PIN][CONF_NUMBER]
|
||||||
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
|
if (
|
||||||
cg.add(var.set_channel(chan))
|
variant in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL
|
||||||
|
and pin_num in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]
|
||||||
|
):
|
||||||
|
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
|
||||||
|
cg.add(var.set_channel1(chan))
|
||||||
|
elif (
|
||||||
|
variant in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL
|
||||||
|
and pin_num in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
|
||||||
|
):
|
||||||
|
chan = ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant][pin_num]
|
||||||
|
cg.add(var.set_channel2(chan))
|
||||||
|
|
|
@ -48,16 +48,16 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await display.register_display(var, config)
|
await display.register_display(var, config)
|
||||||
|
|
||||||
if CONF_PIXEL_MAPPER in config:
|
if pixel_mapper := config.get(CONF_PIXEL_MAPPER):
|
||||||
pixel_mapper_template_ = await cg.process_lambda(
|
pixel_mapper_template_ = await cg.process_lambda(
|
||||||
config[CONF_PIXEL_MAPPER],
|
pixel_mapper,
|
||||||
[(int, "x"), (int, "y")],
|
[(int, "x"), (int, "y")],
|
||||||
return_type=cg.int_,
|
return_type=cg.int_,
|
||||||
)
|
)
|
||||||
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
|
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
|
||||||
|
|
||||||
if CONF_LAMBDA in config:
|
if lambda_config := config.get(CONF_LAMBDA):
|
||||||
lambda_ = await cg.process_lambda(
|
lambda_ = await cg.process_lambda(
|
||||||
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
|
lambda_config, [(display.DisplayRef, "it")], return_type=cg.void
|
||||||
)
|
)
|
||||||
cg.add(var.set_writer(lambda_))
|
cg.add(var.set_writer(lambda_))
|
||||||
|
|
|
@ -72,8 +72,8 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
if CONF_IRQ_PIN in config:
|
if irq_pin_config := config.get(CONF_IRQ_PIN):
|
||||||
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
irq_pin = await cg.gpio_pin_expression(irq_pin_config)
|
||||||
cg.add(var.set_irq_pin(irq_pin))
|
cg.add(var.set_irq_pin(irq_pin))
|
||||||
|
|
||||||
for key in [
|
for key in [
|
||||||
|
|
|
@ -45,10 +45,10 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
if CONF_TEMPERATURE in config:
|
if temperature := config.get(CONF_TEMPERATURE):
|
||||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
sens = await sensor.new_sensor(temperature)
|
||||||
cg.add(var.set_temperature_sensor(sens))
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
|
||||||
if CONF_HUMIDITY in config:
|
if humidity := config.get(CONF_HUMIDITY):
|
||||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
sens = await sensor.new_sensor(humidity)
|
||||||
cg.add(var.set_humidity_sensor(sens))
|
cg.add(var.set_humidity_sensor(sens))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "airthings_listener.h"
|
#include "airthings_listener.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &devic
|
||||||
sn |= ((uint32_t) it.data[2] << 16);
|
sn |= ((uint32_t) it.data[2] << 16);
|
||||||
sn |= ((uint32_t) it.data[3] << 24);
|
sn |= ((uint32_t) it.data[3] << 24);
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Found AirThings device Serial:%u (MAC: %s)", sn, device.address_str().c_str());
|
ESP_LOGD(TAG, "Found AirThings device Serial:%" PRIu32 " (MAC: %s)", sn, device.address_str().c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
103
esphome/components/airthings_wave_base/__init__.py
Normal file
103
esphome/components/airthings_wave_base/__init__.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, ble_client
|
||||||
|
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_BATTERY_VOLTAGE,
|
||||||
|
CONF_HUMIDITY,
|
||||||
|
CONF_PRESSURE,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
CONF_TVOC,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_HECTOPASCAL,
|
||||||
|
UNIT_PARTS_PER_BILLION,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
UNIT_VOLT,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@ncareau", "@jeromelaban", "@kpfleming"]
|
||||||
|
|
||||||
|
DEPENDENCIES = ["ble_client"]
|
||||||
|
|
||||||
|
CONF_BATTERY_UPDATE_INTERVAL = "battery_update_interval"
|
||||||
|
|
||||||
|
airthings_wave_base_ns = cg.esphome_ns.namespace("airthings_wave_base")
|
||||||
|
AirthingsWaveBase = airthings_wave_base_ns.class_(
|
||||||
|
"AirthingsWaveBase", cg.PollingComponent, ble_client.BLEClientNode
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
BASE_SCHEMA = (
|
||||||
|
sensor.SENSOR_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_PRESSURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TVOC): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PARTS_PER_BILLION,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=3,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
cv.Optional(
|
||||||
|
CONF_BATTERY_UPDATE_INTERVAL,
|
||||||
|
default="24h",
|
||||||
|
): cv.update_interval,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("5min"))
|
||||||
|
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def wave_base_to_code(var, config):
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
await ble_client.register_ble_node(var, config)
|
||||||
|
|
||||||
|
if config_humidity := config.get(CONF_HUMIDITY):
|
||||||
|
sens = await sensor.new_sensor(config_humidity)
|
||||||
|
cg.add(var.set_humidity(sens))
|
||||||
|
if config_temperature := config.get(CONF_TEMPERATURE):
|
||||||
|
sens = await sensor.new_sensor(config_temperature)
|
||||||
|
cg.add(var.set_temperature(sens))
|
||||||
|
if config_pressure := config.get(CONF_PRESSURE):
|
||||||
|
sens = await sensor.new_sensor(config_pressure)
|
||||||
|
cg.add(var.set_pressure(sens))
|
||||||
|
if config_tvoc := config.get(CONF_TVOC):
|
||||||
|
sens = await sensor.new_sensor(config_tvoc)
|
||||||
|
cg.add(var.set_tvoc(sens))
|
||||||
|
if config_battery_voltage := config.get(CONF_BATTERY_VOLTAGE):
|
||||||
|
sens = await sensor.new_sensor(config_battery_voltage)
|
||||||
|
cg.add(var.set_battery_voltage(sens))
|
||||||
|
if config_battery_update_interval := config.get(CONF_BATTERY_UPDATE_INTERVAL):
|
||||||
|
cg.add(var.set_battery_update_interval(config_battery_update_interval))
|
211
esphome/components/airthings_wave_base/airthings_wave_base.cpp
Normal file
211
esphome/components/airthings_wave_base/airthings_wave_base.cpp
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
#include "airthings_wave_base.h"
|
||||||
|
|
||||||
|
// All information related to reading battery information came from the sensors.airthings_wave
|
||||||
|
// project by Sverre Hamre (https://github.com/sverrham/sensor.airthings_wave)
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace airthings_wave_base {
|
||||||
|
|
||||||
|
static const char *const TAG = "airthings_wave_base";
|
||||||
|
|
||||||
|
void AirthingsWaveBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) {
|
||||||
|
switch (event) {
|
||||||
|
case ESP_GATTC_OPEN_EVT: {
|
||||||
|
if (param->open.status == ESP_GATT_OK) {
|
||||||
|
ESP_LOGI(TAG, "Connected successfully!");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
|
this->handle_ = 0;
|
||||||
|
this->acp_handle_ = 0;
|
||||||
|
this->cccd_handle_ = 0;
|
||||||
|
ESP_LOGW(TAG, "Disconnected!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
|
if (this->request_read_values_()) {
|
||||||
|
if (!this->read_battery_next_update_) {
|
||||||
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
|
} else {
|
||||||
|
// delay setting node_state to ESTABLISHED until confirmation of the notify registration
|
||||||
|
this->request_battery_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that the client will be disconnected even if no responses arrive
|
||||||
|
this->set_response_timeout_();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GATTC_READ_CHAR_EVT: {
|
||||||
|
if (param->read.conn_id != this->parent()->get_conn_id())
|
||||||
|
break;
|
||||||
|
if (param->read.status != ESP_GATT_OK) {
|
||||||
|
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (param->read.handle == this->handle_) {
|
||||||
|
this->read_sensors(param->read.value, param->read.value_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GATTC_NOTIFY_EVT: {
|
||||||
|
if (param->notify.conn_id != this->parent()->get_conn_id())
|
||||||
|
break;
|
||||||
|
if (param->notify.handle == this->acp_handle_) {
|
||||||
|
this->read_battery_(param->notify.value, param->notify.value_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AirthingsWaveBase::is_valid_voc_value_(uint16_t voc) { return voc <= 16383; }
|
||||||
|
|
||||||
|
void AirthingsWaveBase::update() {
|
||||||
|
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||||
|
if (!this->parent()->enabled) {
|
||||||
|
ESP_LOGW(TAG, "Reconnecting to device");
|
||||||
|
this->parent()->set_enabled(true);
|
||||||
|
this->parent()->connect();
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Connection in progress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AirthingsWaveBase::request_read_values_() {
|
||||||
|
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->sensors_data_characteristic_uuid_);
|
||||||
|
if (chr == nullptr) {
|
||||||
|
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_string().c_str(),
|
||||||
|
this->sensors_data_characteristic_uuid_.to_string().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->handle_ = chr->handle;
|
||||||
|
|
||||||
|
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
|
||||||
|
ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status) {
|
||||||
|
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->response_pending_();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AirthingsWaveBase::request_battery_() {
|
||||||
|
uint8_t battery_command = ACCESS_CONTROL_POINT_COMMAND;
|
||||||
|
uint8_t cccd_value[2] = {1, 0};
|
||||||
|
|
||||||
|
auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->access_control_point_characteristic_uuid_);
|
||||||
|
if (chr == nullptr) {
|
||||||
|
ESP_LOGW(TAG, "No access control point characteristic found at service %s char %s",
|
||||||
|
this->service_uuid_.to_string().c_str(),
|
||||||
|
this->access_control_point_characteristic_uuid_.to_string().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *descr = this->parent()->get_descriptor(this->service_uuid_, this->access_control_point_characteristic_uuid_,
|
||||||
|
CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_UUID);
|
||||||
|
if (descr == nullptr) {
|
||||||
|
ESP_LOGW(TAG, "No CCC descriptor found at service %s char %s", this->service_uuid_.to_string().c_str(),
|
||||||
|
this->access_control_point_characteristic_uuid_.to_string().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reg_status =
|
||||||
|
esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(), this->parent()->get_remote_bda(), chr->handle);
|
||||||
|
if (reg_status) {
|
||||||
|
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", reg_status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->acp_handle_ = chr->handle;
|
||||||
|
this->cccd_handle_ = descr->handle;
|
||||||
|
|
||||||
|
auto descr_status =
|
||||||
|
esp_ble_gattc_write_char_descr(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->cccd_handle_,
|
||||||
|
2, cccd_value, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (descr_status) {
|
||||||
|
ESP_LOGW(TAG, "Error sending CCC descriptor write request, status=%d", descr_status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto chr_status =
|
||||||
|
esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->acp_handle_, 1,
|
||||||
|
&battery_command, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (chr_status) {
|
||||||
|
ESP_LOGW(TAG, "Error sending read request for battery, status=%d", chr_status);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->response_pending_();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AirthingsWaveBase::read_battery_(uint8_t *raw_value, uint16_t value_len) {
|
||||||
|
auto *value = (AccessControlPointResponse *) (&raw_value[2]);
|
||||||
|
|
||||||
|
if ((value_len >= (sizeof(AccessControlPointResponse) + 2)) && (raw_value[0] == ACCESS_CONTROL_POINT_COMMAND)) {
|
||||||
|
ESP_LOGD(TAG, "Battery received: %u mV", (unsigned int) value->battery);
|
||||||
|
|
||||||
|
if (this->battery_voltage_ != nullptr) {
|
||||||
|
float voltage = value->battery / 1000.0f;
|
||||||
|
|
||||||
|
this->battery_voltage_->publish_state(voltage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the battery again at the configured update interval
|
||||||
|
if (this->battery_update_interval_ != this->update_interval_) {
|
||||||
|
this->read_battery_next_update_ = false;
|
||||||
|
this->set_timeout("battery", this->battery_update_interval_,
|
||||||
|
[this]() { this->read_battery_next_update_ = true; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->response_received_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AirthingsWaveBase::response_pending_() {
|
||||||
|
this->responses_pending_++;
|
||||||
|
this->set_response_timeout_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AirthingsWaveBase::response_received_() {
|
||||||
|
if (--this->responses_pending_ == 0) {
|
||||||
|
// This instance must not stay connected
|
||||||
|
// so other clients can connect to it (e.g. the
|
||||||
|
// mobile app).
|
||||||
|
this->parent()->set_enabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AirthingsWaveBase::set_response_timeout_() {
|
||||||
|
this->set_timeout("response_timeout", 30 * 1000, [this]() {
|
||||||
|
this->responses_pending_ = 1;
|
||||||
|
this->response_received_();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace airthings_wave_base
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
90
esphome/components/airthings_wave_base/airthings_wave_base.h
Normal file
90
esphome/components/airthings_wave_base/airthings_wave_base.h
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// All information related to reading battery levels came from the sensors.airthings_wave
|
||||||
|
// project by Sverre Hamre (https://github.com/sverrham/sensor.airthings_wave)
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_gattc_api.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace airthings_wave_base {
|
||||||
|
|
||||||
|
namespace espbt = esphome::esp32_ble_tracker;
|
||||||
|
|
||||||
|
static const uint8_t ACCESS_CONTROL_POINT_COMMAND = 0x6d;
|
||||||
|
static const auto CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR_UUID = espbt::ESPBTUUID::from_uint16(0x2902);
|
||||||
|
|
||||||
|
class AirthingsWaveBase : public PollingComponent, public ble_client::BLEClientNode {
|
||||||
|
public:
|
||||||
|
AirthingsWaveBase() = default;
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
|
|
||||||
|
void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
|
||||||
|
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
|
||||||
|
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
|
||||||
|
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
||||||
|
void set_battery_voltage(sensor::Sensor *voltage) {
|
||||||
|
battery_voltage_ = voltage;
|
||||||
|
this->read_battery_next_update_ = true;
|
||||||
|
}
|
||||||
|
void set_battery_update_interval(uint32_t interval) { battery_update_interval_ = interval; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool is_valid_voc_value_(uint16_t voc);
|
||||||
|
|
||||||
|
bool request_read_values_();
|
||||||
|
virtual void read_sensors(uint8_t *raw_value, uint16_t value_len) = 0;
|
||||||
|
|
||||||
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
|
sensor::Sensor *humidity_sensor_{nullptr};
|
||||||
|
sensor::Sensor *pressure_sensor_{nullptr};
|
||||||
|
sensor::Sensor *tvoc_sensor_{nullptr};
|
||||||
|
sensor::Sensor *battery_voltage_{nullptr};
|
||||||
|
|
||||||
|
uint16_t handle_;
|
||||||
|
espbt::ESPBTUUID service_uuid_;
|
||||||
|
espbt::ESPBTUUID sensors_data_characteristic_uuid_;
|
||||||
|
|
||||||
|
uint16_t acp_handle_{0};
|
||||||
|
uint16_t cccd_handle_{0};
|
||||||
|
espbt::ESPBTUUID access_control_point_characteristic_uuid_;
|
||||||
|
|
||||||
|
uint8_t responses_pending_{0};
|
||||||
|
void response_pending_();
|
||||||
|
void response_received_();
|
||||||
|
void set_response_timeout_();
|
||||||
|
|
||||||
|
// default to *not* reading battery voltage from the device; the
|
||||||
|
// set_* function for the battery sensor will set this to 'true'
|
||||||
|
bool read_battery_next_update_{false};
|
||||||
|
bool request_battery_();
|
||||||
|
void read_battery_(uint8_t *raw_value, uint16_t value_len);
|
||||||
|
uint32_t battery_update_interval_{};
|
||||||
|
|
||||||
|
struct AccessControlPointResponse {
|
||||||
|
uint32_t unused1;
|
||||||
|
uint8_t unused2;
|
||||||
|
uint8_t illuminance;
|
||||||
|
uint8_t unused3[10];
|
||||||
|
uint16_t unused4[4];
|
||||||
|
uint16_t battery;
|
||||||
|
uint16_t unused5;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace airthings_wave_base
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
|
@ -7,105 +7,47 @@ namespace airthings_wave_mini {
|
||||||
|
|
||||||
static const char *const TAG = "airthings_wave_mini";
|
static const char *const TAG = "airthings_wave_mini";
|
||||||
|
|
||||||
void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void AirthingsWaveMini::read_sensors(uint8_t *raw_value, uint16_t value_len) {
|
||||||
esp_ble_gattc_cb_param_t *param) {
|
|
||||||
switch (event) {
|
|
||||||
case ESP_GATTC_OPEN_EVT: {
|
|
||||||
if (param->open.status == ESP_GATT_OK) {
|
|
||||||
ESP_LOGI(TAG, "Connected successfully!");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ESP_GATTC_DISCONNECT_EVT: {
|
|
||||||
ESP_LOGW(TAG, "Disconnected!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
|
||||||
this->handle_ = 0;
|
|
||||||
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
|
|
||||||
if (chr == nullptr) {
|
|
||||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
|
|
||||||
sensors_data_characteristic_uuid_.to_string().c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->handle_ = chr->handle;
|
|
||||||
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
|
|
||||||
|
|
||||||
request_read_values_();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ESP_GATTC_READ_CHAR_EVT: {
|
|
||||||
if (param->read.conn_id != this->parent()->get_conn_id())
|
|
||||||
break;
|
|
||||||
if (param->read.status != ESP_GATT_OK) {
|
|
||||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (param->read.handle == this->handle_) {
|
|
||||||
read_sensors_(param->read.value, param->read.value_len);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
|
||||||
auto *value = (WaveMiniReadings *) raw_value;
|
auto *value = (WaveMiniReadings *) raw_value;
|
||||||
|
|
||||||
if (sizeof(WaveMiniReadings) <= value_len) {
|
if (sizeof(WaveMiniReadings) <= value_len) {
|
||||||
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
|
if (this->humidity_sensor_ != nullptr) {
|
||||||
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
|
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
|
||||||
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
|
}
|
||||||
if (is_valid_voc_value_(value->voc)) {
|
|
||||||
|
if (this->pressure_sensor_ != nullptr) {
|
||||||
|
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
|
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) {
|
||||||
this->tvoc_sensor_->publish_state(value->voc);
|
this->tvoc_sensor_->publish_state(value->voc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This instance must not stay connected
|
|
||||||
// so other clients can connect to it (e.g. the
|
|
||||||
// mobile app).
|
|
||||||
parent()->set_enabled(false);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
|
this->response_received_();
|
||||||
|
|
||||||
void AirthingsWaveMini::update() {
|
|
||||||
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
|
|
||||||
if (!parent()->enabled) {
|
|
||||||
ESP_LOGW(TAG, "Reconnecting to device");
|
|
||||||
parent()->set_enabled(true);
|
|
||||||
parent()->connect();
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "Connection in progress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AirthingsWaveMini::request_read_values_() {
|
|
||||||
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
|
|
||||||
ESP_GATT_AUTH_REQ_NONE);
|
|
||||||
if (status) {
|
|
||||||
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AirthingsWaveMini::dump_config() {
|
void AirthingsWaveMini::dump_config() {
|
||||||
|
// these really don't belong here, but there doesn't seem to be a
|
||||||
|
// practical way to have the base class use LOG_SENSOR and include
|
||||||
|
// the TAG from this component
|
||||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||||
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
|
||||||
}
|
}
|
||||||
|
|
||||||
AirthingsWaveMini::AirthingsWaveMini()
|
AirthingsWaveMini::AirthingsWaveMini() {
|
||||||
: PollingComponent(10000),
|
this->service_uuid_ = espbt::ESPBTUUID::from_raw(SERVICE_UUID);
|
||||||
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
|
this->sensors_data_characteristic_uuid_ = espbt::ESPBTUUID::from_raw(CHARACTERISTIC_UUID);
|
||||||
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
|
this->access_control_point_characteristic_uuid_ =
|
||||||
|
espbt::ESPBTUUID::from_raw(ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace airthings_wave_mini
|
} // namespace airthings_wave_mini
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -2,50 +2,25 @@
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <esp_gattc_api.h>
|
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
#include "esphome/components/ble_client/ble_client.h"
|
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_wave_mini {
|
namespace airthings_wave_mini {
|
||||||
|
|
||||||
|
namespace espbt = esphome::esp32_ble_tracker;
|
||||||
|
|
||||||
static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
|
static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
|
||||||
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
|
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
static const char *const ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID = "b42e3ef4-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
|
||||||
class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode {
|
class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase {
|
||||||
public:
|
public:
|
||||||
AirthingsWaveMini();
|
AirthingsWaveMini();
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void update() override;
|
|
||||||
|
|
||||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
|
||||||
esp_ble_gattc_cb_param_t *param) override;
|
|
||||||
|
|
||||||
void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
|
|
||||||
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
|
|
||||||
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
|
|
||||||
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool is_valid_voc_value_(uint16_t voc);
|
void read_sensors(uint8_t *raw_value, uint16_t value_len) override;
|
||||||
|
|
||||||
void read_sensors_(uint8_t *value, uint16_t value_len);
|
|
||||||
void request_read_values_();
|
|
||||||
|
|
||||||
sensor::Sensor *temperature_sensor_{nullptr};
|
|
||||||
sensor::Sensor *humidity_sensor_{nullptr};
|
|
||||||
sensor::Sensor *pressure_sensor_{nullptr};
|
|
||||||
sensor::Sensor *tvoc_sensor_{nullptr};
|
|
||||||
|
|
||||||
uint16_t handle_;
|
|
||||||
esp32_ble_tracker::ESPBTUUID service_uuid_;
|
|
||||||
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
|
|
||||||
|
|
||||||
struct WaveMiniReadings {
|
struct WaveMiniReadings {
|
||||||
uint16_t unused01;
|
uint16_t unused01;
|
||||||
|
|
|
@ -1,82 +1,28 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import sensor, ble_client
|
from esphome.components import airthings_wave_base
|
||||||
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
DEVICE_CLASS_HUMIDITY,
|
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
|
||||||
DEVICE_CLASS_PRESSURE,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
UNIT_PERCENT,
|
|
||||||
UNIT_CELSIUS,
|
|
||||||
UNIT_HECTOPASCAL,
|
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_HUMIDITY,
|
|
||||||
CONF_TVOC,
|
|
||||||
CONF_PRESSURE,
|
|
||||||
CONF_TEMPERATURE,
|
|
||||||
UNIT_PARTS_PER_BILLION,
|
|
||||||
ICON_RADIATOR,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["ble_client"]
|
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
|
||||||
|
|
||||||
|
AUTO_LOAD = ["airthings_wave_base"]
|
||||||
|
|
||||||
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
|
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
|
||||||
AirthingsWaveMini = airthings_wave_mini_ns.class_(
|
AirthingsWaveMini = airthings_wave_mini_ns.class_(
|
||||||
"AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode
|
"AirthingsWaveMini", airthings_wave_base.AirthingsWaveBase
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend(
|
||||||
cv.Schema(
|
{
|
||||||
{
|
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
|
||||||
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
|
}
|
||||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_PERCENT,
|
|
||||||
device_class=DEVICE_CLASS_HUMIDITY,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_CELSIUS,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
device_class=DEVICE_CLASS_PRESSURE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_TVOC): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_PARTS_PER_BILLION,
|
|
||||||
icon=ICON_RADIATOR,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(cv.polling_component_schema("5min"))
|
|
||||||
.extend(ble_client.BLE_CLIENT_SCHEMA),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await airthings_wave_base.wave_base_to_code(var, config)
|
||||||
|
|
||||||
await ble_client.register_ble_node(var, config)
|
|
||||||
|
|
||||||
if CONF_HUMIDITY in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
|
||||||
cg.add(var.set_humidity(sens))
|
|
||||||
if CONF_TEMPERATURE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
|
||||||
cg.add(var.set_temperature(sens))
|
|
||||||
if CONF_PRESSURE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_PRESSURE])
|
|
||||||
cg.add(var.set_pressure(sens))
|
|
||||||
if CONF_TVOC in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_TVOC])
|
|
||||||
cg.add(var.set_tvoc(sens))
|
|
||||||
|
|
|
@ -7,55 +7,7 @@ namespace airthings_wave_plus {
|
||||||
|
|
||||||
static const char *const TAG = "airthings_wave_plus";
|
static const char *const TAG = "airthings_wave_plus";
|
||||||
|
|
||||||
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
|
||||||
esp_ble_gattc_cb_param_t *param) {
|
|
||||||
switch (event) {
|
|
||||||
case ESP_GATTC_OPEN_EVT: {
|
|
||||||
if (param->open.status == ESP_GATT_OK) {
|
|
||||||
ESP_LOGI(TAG, "Connected successfully!");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ESP_GATTC_DISCONNECT_EVT: {
|
|
||||||
ESP_LOGW(TAG, "Disconnected!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
|
||||||
this->handle_ = 0;
|
|
||||||
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
|
|
||||||
if (chr == nullptr) {
|
|
||||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
|
|
||||||
sensors_data_characteristic_uuid_.to_string().c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->handle_ = chr->handle;
|
|
||||||
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
|
|
||||||
|
|
||||||
request_read_values_();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ESP_GATTC_READ_CHAR_EVT: {
|
|
||||||
if (param->read.conn_id != this->parent()->get_conn_id())
|
|
||||||
break;
|
|
||||||
if (param->read.status != ESP_GATT_OK) {
|
|
||||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (param->read.handle == this->handle_) {
|
|
||||||
read_sensors_(param->read.value, param->read.value_len);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
|
||||||
auto *value = (WavePlusReadings *) raw_value;
|
auto *value = (WavePlusReadings *) raw_value;
|
||||||
|
|
||||||
if (sizeof(WavePlusReadings) <= value_len) {
|
if (sizeof(WavePlusReadings) <= value_len) {
|
||||||
|
@ -64,72 +16,66 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
||||||
if (value->version == 1) {
|
if (value->version == 1) {
|
||||||
ESP_LOGD(TAG, "ambient light = %d", value->ambientLight);
|
ESP_LOGD(TAG, "ambient light = %d", value->ambientLight);
|
||||||
|
|
||||||
this->humidity_sensor_->publish_state(value->humidity / 2.0f);
|
if (this->humidity_sensor_ != nullptr) {
|
||||||
if (is_valid_radon_value_(value->radon)) {
|
this->humidity_sensor_->publish_state(value->humidity / 2.0f);
|
||||||
this->radon_sensor_->publish_state(value->radon);
|
|
||||||
}
|
|
||||||
if (is_valid_radon_value_(value->radon_lt)) {
|
|
||||||
this->radon_long_term_sensor_->publish_state(value->radon_lt);
|
|
||||||
}
|
|
||||||
this->temperature_sensor_->publish_state(value->temperature / 100.0f);
|
|
||||||
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
|
|
||||||
if (is_valid_co2_value_(value->co2)) {
|
|
||||||
this->co2_sensor_->publish_state(value->co2);
|
|
||||||
}
|
|
||||||
if (is_valid_voc_value_(value->voc)) {
|
|
||||||
this->tvoc_sensor_->publish_state(value->voc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This instance must not stay connected
|
if ((this->radon_sensor_ != nullptr) && this->is_valid_radon_value_(value->radon)) {
|
||||||
// so other clients can connect to it (e.g. the
|
this->radon_sensor_->publish_state(value->radon);
|
||||||
// mobile app).
|
}
|
||||||
parent()->set_enabled(false);
|
|
||||||
|
if ((this->radon_long_term_sensor_ != nullptr) && this->is_valid_radon_value_(value->radon_lt)) {
|
||||||
|
this->radon_long_term_sensor_->publish_state(value->radon_lt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
|
this->temperature_sensor_->publish_state(value->temperature / 100.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->pressure_sensor_ != nullptr) {
|
||||||
|
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this->co2_sensor_ != nullptr) && this->is_valid_co2_value_(value->co2)) {
|
||||||
|
this->co2_sensor_->publish_state(value->co2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) {
|
||||||
|
this->tvoc_sensor_->publish_state(value->voc);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version);
|
ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->response_received_();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
|
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return radon <= 16383; }
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
|
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return co2 <= 16383; }
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
|
|
||||||
|
|
||||||
void AirthingsWavePlus::update() {
|
|
||||||
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
|
|
||||||
if (!parent()->enabled) {
|
|
||||||
ESP_LOGW(TAG, "Reconnecting to device");
|
|
||||||
parent()->set_enabled(true);
|
|
||||||
parent()->connect();
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "Connection in progress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AirthingsWavePlus::request_read_values_() {
|
|
||||||
auto status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->handle_,
|
|
||||||
ESP_GATT_AUTH_REQ_NONE);
|
|
||||||
if (status) {
|
|
||||||
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AirthingsWavePlus::dump_config() {
|
void AirthingsWavePlus::dump_config() {
|
||||||
|
// these really don't belong here, but there doesn't seem to be a
|
||||||
|
// practical way to have the base class use LOG_SENSOR and include
|
||||||
|
// the TAG from this component
|
||||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
LOG_SENSOR(" ", "Radon", this->radon_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||||
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
|
|
||||||
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
|
||||||
|
|
||||||
|
LOG_SENSOR(" ", "Radon", this->radon_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_);
|
||||||
|
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
AirthingsWavePlus::AirthingsWavePlus()
|
AirthingsWavePlus::AirthingsWavePlus() {
|
||||||
: PollingComponent(10000),
|
this->service_uuid_ = espbt::ESPBTUUID::from_raw(SERVICE_UUID);
|
||||||
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
|
this->sensors_data_characteristic_uuid_ = espbt::ESPBTUUID::from_raw(CHARACTERISTIC_UUID);
|
||||||
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
|
this->access_control_point_characteristic_uuid_ =
|
||||||
|
espbt::ESPBTUUID::from_raw(ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace airthings_wave_plus
|
} // namespace airthings_wave_plus
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -2,58 +2,36 @@
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <esp_gattc_api.h>
|
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
#include "esphome/components/ble_client/ble_client.h"
|
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_wave_plus {
|
namespace airthings_wave_plus {
|
||||||
|
|
||||||
|
namespace espbt = esphome::esp32_ble_tracker;
|
||||||
|
|
||||||
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
|
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
|
||||||
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
|
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
static const char *const ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID = "b42e2d06-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
|
||||||
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
|
class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
|
||||||
public:
|
public:
|
||||||
AirthingsWavePlus();
|
AirthingsWavePlus();
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void update() override;
|
|
||||||
|
|
||||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
|
||||||
esp_ble_gattc_cb_param_t *param) override;
|
|
||||||
|
|
||||||
void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
|
|
||||||
void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; }
|
void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; }
|
||||||
void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; }
|
void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; }
|
||||||
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
|
|
||||||
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
|
|
||||||
void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; }
|
void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; }
|
||||||
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool is_valid_radon_value_(uint16_t radon);
|
bool is_valid_radon_value_(uint16_t radon);
|
||||||
bool is_valid_voc_value_(uint16_t voc);
|
|
||||||
bool is_valid_co2_value_(uint16_t co2);
|
bool is_valid_co2_value_(uint16_t co2);
|
||||||
|
|
||||||
void read_sensors_(uint8_t *value, uint16_t value_len);
|
void read_sensors(uint8_t *raw_value, uint16_t value_len) override;
|
||||||
void request_read_values_();
|
|
||||||
|
|
||||||
sensor::Sensor *temperature_sensor_{nullptr};
|
|
||||||
sensor::Sensor *radon_sensor_{nullptr};
|
sensor::Sensor *radon_sensor_{nullptr};
|
||||||
sensor::Sensor *radon_long_term_sensor_{nullptr};
|
sensor::Sensor *radon_long_term_sensor_{nullptr};
|
||||||
sensor::Sensor *humidity_sensor_{nullptr};
|
|
||||||
sensor::Sensor *pressure_sensor_{nullptr};
|
|
||||||
sensor::Sensor *co2_sensor_{nullptr};
|
sensor::Sensor *co2_sensor_{nullptr};
|
||||||
sensor::Sensor *tvoc_sensor_{nullptr};
|
|
||||||
|
|
||||||
uint16_t handle_;
|
|
||||||
esp32_ble_tracker::ESPBTUUID service_uuid_;
|
|
||||||
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
|
|
||||||
|
|
||||||
struct WavePlusReadings {
|
struct WavePlusReadings {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
|
|
|
@ -1,116 +1,64 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import sensor, ble_client
|
from esphome.components import sensor, airthings_wave_base
|
||||||
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
DEVICE_CLASS_CARBON_DIOXIDE,
|
DEVICE_CLASS_CARBON_DIOXIDE,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
|
||||||
DEVICE_CLASS_PRESSURE,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_PERCENT,
|
|
||||||
UNIT_CELSIUS,
|
|
||||||
UNIT_HECTOPASCAL,
|
|
||||||
ICON_RADIOACTIVE,
|
ICON_RADIOACTIVE,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_RADON,
|
CONF_RADON,
|
||||||
CONF_RADON_LONG_TERM,
|
CONF_RADON_LONG_TERM,
|
||||||
CONF_HUMIDITY,
|
|
||||||
CONF_TVOC,
|
|
||||||
CONF_CO2,
|
CONF_CO2,
|
||||||
CONF_PRESSURE,
|
|
||||||
CONF_TEMPERATURE,
|
|
||||||
UNIT_BECQUEREL_PER_CUBIC_METER,
|
UNIT_BECQUEREL_PER_CUBIC_METER,
|
||||||
UNIT_PARTS_PER_MILLION,
|
UNIT_PARTS_PER_MILLION,
|
||||||
UNIT_PARTS_PER_BILLION,
|
|
||||||
ICON_RADIATOR,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["ble_client"]
|
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
|
||||||
|
|
||||||
|
AUTO_LOAD = ["airthings_wave_base"]
|
||||||
|
|
||||||
airthings_wave_plus_ns = cg.esphome_ns.namespace("airthings_wave_plus")
|
airthings_wave_plus_ns = cg.esphome_ns.namespace("airthings_wave_plus")
|
||||||
AirthingsWavePlus = airthings_wave_plus_ns.class_(
|
AirthingsWavePlus = airthings_wave_plus_ns.class_(
|
||||||
"AirthingsWavePlus", cg.PollingComponent, ble_client.BLEClientNode
|
"AirthingsWavePlus", airthings_wave_base.AirthingsWaveBase
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend(
|
||||||
cv.Schema(
|
{
|
||||||
{
|
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
|
||||||
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
|
cv.Optional(CONF_RADON): sensor.sensor_schema(
|
||||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
|
||||||
unit_of_measurement=UNIT_PERCENT,
|
icon=ICON_RADIOACTIVE,
|
||||||
device_class=DEVICE_CLASS_HUMIDITY,
|
accuracy_decimals=0,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
accuracy_decimals=0,
|
),
|
||||||
),
|
cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema(
|
||||||
cv.Optional(CONF_RADON): sensor.sensor_schema(
|
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
|
||||||
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
|
icon=ICON_RADIOACTIVE,
|
||||||
icon=ICON_RADIOACTIVE,
|
accuracy_decimals=0,
|
||||||
accuracy_decimals=0,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
),
|
||||||
),
|
cv.Optional(CONF_CO2): sensor.sensor_schema(
|
||||||
cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema(
|
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
||||||
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
|
accuracy_decimals=0,
|
||||||
icon=ICON_RADIOACTIVE,
|
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
|
||||||
accuracy_decimals=0,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
),
|
||||||
),
|
}
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_CELSIUS,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
|
||||||
accuracy_decimals=1,
|
|
||||||
device_class=DEVICE_CLASS_PRESSURE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_CO2): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_TVOC): sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_PARTS_PER_BILLION,
|
|
||||||
icon=ICON_RADIATOR,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(cv.polling_component_schema("5min"))
|
|
||||||
.extend(ble_client.BLE_CLIENT_SCHEMA),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await airthings_wave_base.wave_base_to_code(var, config)
|
||||||
|
|
||||||
await ble_client.register_ble_node(var, config)
|
if config_radon := config.get(CONF_RADON):
|
||||||
|
sens = await sensor.new_sensor(config_radon)
|
||||||
if CONF_HUMIDITY in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
|
||||||
cg.add(var.set_humidity(sens))
|
|
||||||
if CONF_RADON in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_RADON])
|
|
||||||
cg.add(var.set_radon(sens))
|
cg.add(var.set_radon(sens))
|
||||||
if CONF_RADON_LONG_TERM in config:
|
if config_radon_long_term := config.get(CONF_RADON_LONG_TERM):
|
||||||
sens = await sensor.new_sensor(config[CONF_RADON_LONG_TERM])
|
sens = await sensor.new_sensor(config_radon_long_term)
|
||||||
cg.add(var.set_radon_long_term(sens))
|
cg.add(var.set_radon_long_term(sens))
|
||||||
if CONF_TEMPERATURE in config:
|
if config_co2 := config.get(CONF_CO2):
|
||||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
sens = await sensor.new_sensor(config_co2)
|
||||||
cg.add(var.set_temperature(sens))
|
|
||||||
if CONF_PRESSURE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_PRESSURE])
|
|
||||||
cg.add(var.set_pressure(sens))
|
|
||||||
if CONF_CO2 in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_CO2])
|
|
||||||
cg.add(var.set_co2(sens))
|
cg.add(var.set_co2(sens))
|
||||||
if CONF_TVOC in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_TVOC])
|
|
||||||
cg.add(var.set_tvoc(sens))
|
|
||||||
|
|
|
@ -16,6 +16,12 @@ IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
CONF_ON_TRIGGERED = "on_triggered"
|
CONF_ON_TRIGGERED = "on_triggered"
|
||||||
CONF_ON_CLEARED = "on_cleared"
|
CONF_ON_CLEARED = "on_cleared"
|
||||||
|
CONF_ON_ARMING = "on_arming"
|
||||||
|
CONF_ON_PENDING = "on_pending"
|
||||||
|
CONF_ON_ARMED_HOME = "on_armed_home"
|
||||||
|
CONF_ON_ARMED_NIGHT = "on_armed_night"
|
||||||
|
CONF_ON_ARMED_AWAY = "on_armed_away"
|
||||||
|
CONF_ON_DISARMED = "on_disarmed"
|
||||||
|
|
||||||
alarm_control_panel_ns = cg.esphome_ns.namespace("alarm_control_panel")
|
alarm_control_panel_ns = cg.esphome_ns.namespace("alarm_control_panel")
|
||||||
AlarmControlPanel = alarm_control_panel_ns.class_("AlarmControlPanel", cg.EntityBase)
|
AlarmControlPanel = alarm_control_panel_ns.class_("AlarmControlPanel", cg.EntityBase)
|
||||||
|
@ -29,8 +35,27 @@ TriggeredTrigger = alarm_control_panel_ns.class_(
|
||||||
ClearedTrigger = alarm_control_panel_ns.class_(
|
ClearedTrigger = alarm_control_panel_ns.class_(
|
||||||
"ClearedTrigger", automation.Trigger.template()
|
"ClearedTrigger", automation.Trigger.template()
|
||||||
)
|
)
|
||||||
|
ArmingTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"ArmingTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
PendingTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"PendingTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ArmedHomeTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"ArmedHomeTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ArmedNightTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"ArmedNightTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
ArmedAwayTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"ArmedAwayTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
|
DisarmedTrigger = alarm_control_panel_ns.class_(
|
||||||
|
"DisarmedTrigger", automation.Trigger.template()
|
||||||
|
)
|
||||||
ArmAwayAction = alarm_control_panel_ns.class_("ArmAwayAction", automation.Action)
|
ArmAwayAction = alarm_control_panel_ns.class_("ArmAwayAction", automation.Action)
|
||||||
ArmHomeAction = alarm_control_panel_ns.class_("ArmHomeAction", automation.Action)
|
ArmHomeAction = alarm_control_panel_ns.class_("ArmHomeAction", automation.Action)
|
||||||
|
ArmNightAction = alarm_control_panel_ns.class_("ArmNightAction", automation.Action)
|
||||||
DisarmAction = alarm_control_panel_ns.class_("DisarmAction", automation.Action)
|
DisarmAction = alarm_control_panel_ns.class_("DisarmAction", automation.Action)
|
||||||
PendingAction = alarm_control_panel_ns.class_("PendingAction", automation.Action)
|
PendingAction = alarm_control_panel_ns.class_("PendingAction", automation.Action)
|
||||||
TriggeredAction = alarm_control_panel_ns.class_("TriggeredAction", automation.Action)
|
TriggeredAction = alarm_control_panel_ns.class_("TriggeredAction", automation.Action)
|
||||||
|
@ -51,6 +76,36 @@ ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_ON_ARMING): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_PENDING): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ON_DISARMED): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger),
|
||||||
|
}
|
||||||
|
),
|
||||||
cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
|
cv.Optional(CONF_ON_CLEARED): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
|
||||||
|
@ -81,6 +136,24 @@ async def setup_alarm_control_panel_core_(var, config):
|
||||||
for conf in config.get(CONF_ON_TRIGGERED, []):
|
for conf in config.get(CONF_ON_TRIGGERED, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
for conf in config.get(CONF_ON_ARMING, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
for conf in config.get(CONF_ON_PENDING, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
for conf in config.get(CONF_ON_ARMED_HOME, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
for conf in config.get(CONF_ON_ARMED_NIGHT, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
for conf in config.get(CONF_ON_ARMED_AWAY, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
for conf in config.get(CONF_ON_DISARMED, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
|
await automation.build_automation(trigger, [], conf)
|
||||||
for conf in config.get(CONF_ON_CLEARED, []):
|
for conf in config.get(CONF_ON_CLEARED, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
@ -99,8 +172,8 @@ async def register_alarm_control_panel(var, config):
|
||||||
async def alarm_action_arm_away_to_code(config, action_id, template_arg, args):
|
async def alarm_action_arm_away_to_code(config, action_id, template_arg, args):
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
if CONF_CODE in config:
|
if code_config := config.get(CONF_CODE):
|
||||||
templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
|
templatable_ = await cg.templatable(code_config, args, cg.std_string)
|
||||||
cg.add(var.set_code(templatable_))
|
cg.add(var.set_code(templatable_))
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
@ -109,6 +182,18 @@ async def alarm_action_arm_away_to_code(config, action_id, template_arg, args):
|
||||||
"alarm_control_panel.arm_home", ArmHomeAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
"alarm_control_panel.arm_home", ArmHomeAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
||||||
)
|
)
|
||||||
async def alarm_action_arm_home_to_code(config, action_id, template_arg, args):
|
async def alarm_action_arm_home_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)
|
||||||
|
if code_config := config.get(CONF_CODE):
|
||||||
|
templatable_ = await cg.templatable(code_config, args, cg.std_string)
|
||||||
|
cg.add(var.set_code(templatable_))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"alarm_control_panel.arm_night", ArmNightAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
||||||
|
)
|
||||||
|
async def alarm_action_arm_night_to_code(config, action_id, template_arg, args):
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
if CONF_CODE in config:
|
if CONF_CODE in config:
|
||||||
|
@ -123,8 +208,8 @@ async def alarm_action_arm_home_to_code(config, action_id, template_arg, args):
|
||||||
async def alarm_action_disarm_to_code(config, action_id, template_arg, args):
|
async def alarm_action_disarm_to_code(config, action_id, template_arg, args):
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
if CONF_CODE in config:
|
if code_config := config.get(CONF_CODE):
|
||||||
templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
|
templatable_ = await cg.templatable(code_config, args, cg.std_string)
|
||||||
cg.add(var.set_code(templatable_))
|
cg.add(var.set_code(templatable_))
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,20 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
|
||||||
this->state_callback_.call();
|
this->state_callback_.call();
|
||||||
if (state == ACP_STATE_TRIGGERED) {
|
if (state == ACP_STATE_TRIGGERED) {
|
||||||
this->triggered_callback_.call();
|
this->triggered_callback_.call();
|
||||||
|
} else if (state == ACP_STATE_ARMING) {
|
||||||
|
this->arming_callback_.call();
|
||||||
|
} else if (state == ACP_STATE_PENDING) {
|
||||||
|
this->pending_callback_.call();
|
||||||
|
} else if (state == ACP_STATE_ARMED_HOME) {
|
||||||
|
this->armed_home_callback_.call();
|
||||||
|
} else if (state == ACP_STATE_ARMED_NIGHT) {
|
||||||
|
this->armed_night_callback_.call();
|
||||||
|
} else if (state == ACP_STATE_ARMED_AWAY) {
|
||||||
|
this->armed_away_callback_.call();
|
||||||
|
} else if (state == ACP_STATE_DISARMED) {
|
||||||
|
this->disarmed_callback_.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prev_state == ACP_STATE_TRIGGERED) {
|
if (prev_state == ACP_STATE_TRIGGERED) {
|
||||||
this->cleared_callback_.call();
|
this->cleared_callback_.call();
|
||||||
}
|
}
|
||||||
|
@ -55,6 +68,30 @@ void AlarmControlPanel::add_on_triggered_callback(std::function<void()> &&callba
|
||||||
this->triggered_callback_.add(std::move(callback));
|
this->triggered_callback_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_arming_callback(std::function<void()> &&callback) {
|
||||||
|
this->arming_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_armed_home_callback(std::function<void()> &&callback) {
|
||||||
|
this->armed_home_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_armed_night_callback(std::function<void()> &&callback) {
|
||||||
|
this->armed_night_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_armed_away_callback(std::function<void()> &&callback) {
|
||||||
|
this->armed_away_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_pending_callback(std::function<void()> &&callback) {
|
||||||
|
this->pending_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmControlPanel::add_on_disarmed_callback(std::function<void()> &&callback) {
|
||||||
|
this->disarmed_callback_.add(std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
void AlarmControlPanel::add_on_cleared_callback(std::function<void()> &&callback) {
|
void AlarmControlPanel::add_on_cleared_callback(std::function<void()> &&callback) {
|
||||||
this->cleared_callback_.add(std::move(callback));
|
this->cleared_callback_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,42 @@ class AlarmControlPanel : public EntityBase {
|
||||||
*/
|
*/
|
||||||
void add_on_triggered_callback(std::function<void()> &&callback);
|
void add_on_triggered_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel chanes to arming
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_arming_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel changes to pending
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_pending_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel changes to armed_home
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_armed_home_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel changes to armed_night
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_armed_night_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel changes to armed_away
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_armed_away_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
|
/** Add a callback for when the state of the alarm_control_panel changes to disarmed
|
||||||
|
*
|
||||||
|
* @param callback The callback function
|
||||||
|
*/
|
||||||
|
void add_on_disarmed_callback(std::function<void()> &&callback);
|
||||||
|
|
||||||
/** Add a callback for when the state of the alarm_control_panel clears from triggered
|
/** Add a callback for when the state of the alarm_control_panel clears from triggered
|
||||||
*
|
*
|
||||||
* @param callback The callback function
|
* @param callback The callback function
|
||||||
|
@ -128,6 +164,18 @@ class AlarmControlPanel : public EntityBase {
|
||||||
CallbackManager<void()> state_callback_{};
|
CallbackManager<void()> state_callback_{};
|
||||||
// trigger callback
|
// trigger callback
|
||||||
CallbackManager<void()> triggered_callback_{};
|
CallbackManager<void()> triggered_callback_{};
|
||||||
|
// arming callback
|
||||||
|
CallbackManager<void()> arming_callback_{};
|
||||||
|
// pending callback
|
||||||
|
CallbackManager<void()> pending_callback_{};
|
||||||
|
// armed_home callback
|
||||||
|
CallbackManager<void()> armed_home_callback_{};
|
||||||
|
// armed_night callback
|
||||||
|
CallbackManager<void()> armed_night_callback_{};
|
||||||
|
// armed_away callback
|
||||||
|
CallbackManager<void()> armed_away_callback_{};
|
||||||
|
// disarmed callback
|
||||||
|
CallbackManager<void()> disarmed_callback_{};
|
||||||
// clear callback
|
// clear callback
|
||||||
CallbackManager<void()> cleared_callback_{};
|
CallbackManager<void()> cleared_callback_{};
|
||||||
};
|
};
|
||||||
|
|
|
@ -85,6 +85,11 @@ void AlarmControlPanelCall::validate_() {
|
||||||
this->state_.reset();
|
this->state_.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (state == ACP_STATE_ARMED_NIGHT && (this->parent_->get_supported_features() & ACP_FEAT_ARM_NIGHT) == 0) {
|
||||||
|
ESP_LOGW(TAG, "Cannot arm night when not supported");
|
||||||
|
this->state_.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState stat
|
||||||
case ACP_STATE_ARMED_AWAY:
|
case ACP_STATE_ARMED_AWAY:
|
||||||
return LOG_STR("ARMED_AWAY");
|
return LOG_STR("ARMED_AWAY");
|
||||||
case ACP_STATE_ARMED_NIGHT:
|
case ACP_STATE_ARMED_NIGHT:
|
||||||
return LOG_STR("NIGHT");
|
return LOG_STR("ARMED_NIGHT");
|
||||||
case ACP_STATE_ARMED_VACATION:
|
case ACP_STATE_ARMED_VACATION:
|
||||||
return LOG_STR("ARMED_VACATION");
|
return LOG_STR("ARMED_VACATION");
|
||||||
case ACP_STATE_ARMED_CUSTOM_BYPASS:
|
case ACP_STATE_ARMED_CUSTOM_BYPASS:
|
||||||
|
|
|
@ -20,6 +20,48 @@ class TriggeredTrigger : public Trigger<> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ArmingTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ArmingTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_arming_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PendingTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit PendingTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_pending_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArmedHomeTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ArmedHomeTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_armed_home_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArmedNightTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ArmedNightTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_armed_night_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArmedAwayTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit ArmedAwayTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_armed_away_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DisarmedTrigger : public Trigger<> {
|
||||||
|
public:
|
||||||
|
explicit DisarmedTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
alarm_control_panel->add_on_disarmed_callback([this]() { this->trigger(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class ClearedTrigger : public Trigger<> {
|
class ClearedTrigger : public Trigger<> {
|
||||||
public:
|
public:
|
||||||
explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) {
|
explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) {
|
||||||
|
@ -67,6 +109,26 @@ template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
|
||||||
AlarmControlPanel *alarm_control_panel_;
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class ArmNightAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
explicit ArmNightAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||||
|
|
||||||
|
TEMPLATABLE_VALUE(std::string, code)
|
||||||
|
|
||||||
|
void play(Ts... x) override {
|
||||||
|
auto call = this->alarm_control_panel_->make_call();
|
||||||
|
auto code = this->code_.optional_value(x...);
|
||||||
|
if (code.has_value()) {
|
||||||
|
call.set_code(code.value());
|
||||||
|
}
|
||||||
|
call.arm_night();
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AlarmControlPanel *alarm_control_panel_;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename... Ts> class DisarmAction : public Action<Ts...> {
|
template<typename... Ts> class DisarmAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||||
|
|
1
esphome/components/alpha3/__init__.py
Normal file
1
esphome/components/alpha3/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@jan-hofmeier"]
|
189
esphome/components/alpha3/alpha3.cpp
Normal file
189
esphome/components/alpha3/alpha3.cpp
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
#include "alpha3.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include <lwip/sockets.h> //gives ntohl
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alpha3 {
|
||||||
|
|
||||||
|
static const char *const TAG = "alpha3";
|
||||||
|
|
||||||
|
void Alpha3::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "ALPHA3");
|
||||||
|
LOG_SENSOR(" ", "Flow", this->flow_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Head", this->head_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Current", this->current_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Speed", this->speed_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Alpha3::setup() {}
|
||||||
|
|
||||||
|
void Alpha3::extract_publish_sensor_value_(const uint8_t *response, int16_t length, int16_t response_offset,
|
||||||
|
int16_t value_offset, sensor::Sensor *sensor, float factor) {
|
||||||
|
if (sensor == nullptr)
|
||||||
|
return;
|
||||||
|
// we need to handle cases where a value is split over two packets
|
||||||
|
const int16_t value_length = 4; // 32bit float
|
||||||
|
// offset inside current response packet
|
||||||
|
auto rel_offset = value_offset - response_offset;
|
||||||
|
if (rel_offset <= -value_length)
|
||||||
|
return; // aready passed the value completly
|
||||||
|
if (rel_offset >= length)
|
||||||
|
return; // value not in this packet
|
||||||
|
|
||||||
|
auto start_offset = std::max(0, rel_offset);
|
||||||
|
auto end_offset = std::min((int16_t) (rel_offset + value_length), length);
|
||||||
|
auto copy_length = end_offset - start_offset;
|
||||||
|
auto buffer_offset = std::max(-rel_offset, 0);
|
||||||
|
std::memcpy(this->buffer_ + buffer_offset, response + start_offset, copy_length);
|
||||||
|
|
||||||
|
if (rel_offset + value_length <= length) {
|
||||||
|
// we have the whole value
|
||||||
|
void *buffer = this->buffer_; // to prevent warnings when casting the pointer
|
||||||
|
*((int32_t *) buffer) = ntohl(*((int32_t *) buffer)); // values are big endian
|
||||||
|
float fvalue = *((float *) buffer);
|
||||||
|
sensor->publish_state(fvalue * factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Alpha3::is_current_response_type_(const uint8_t *response_type) {
|
||||||
|
return !std::memcmp(this->response_type_, response_type, GENI_RESPONSE_TYPE_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Alpha3::handle_geni_response_(const uint8_t *response, uint16_t length) {
|
||||||
|
if (this->response_offset_ >= this->response_length_) {
|
||||||
|
ESP_LOGD(TAG, "[%s] GENI response begin", this->parent_->address_str().c_str());
|
||||||
|
if (length < GENI_RESPONSE_HEADER_LENGTH) {
|
||||||
|
ESP_LOGW(TAG, "[%s] response to short", this->parent_->address_str().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (response[0] != 36 || response[2] != 248 || response[3] != 231 || response[4] != 10) {
|
||||||
|
ESP_LOGW(TAG, "[%s] response bytes %d %d %d %d %d don't match GENI HEADER", this->parent_->address_str().c_str(),
|
||||||
|
response[0], response[1], response[2], response[3], response[4]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->response_length_ = response[1] - GENI_RESPONSE_HEADER_LENGTH + 2; // maybe 2 byte checksum
|
||||||
|
this->response_offset_ = -GENI_RESPONSE_HEADER_LENGTH;
|
||||||
|
std::memcpy(this->response_type_, response + 5, GENI_RESPONSE_TYPE_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto extract_publish_sensor_value = [response, length, this](int16_t value_offset, sensor::Sensor *sensor,
|
||||||
|
float factor) {
|
||||||
|
this->extract_publish_sensor_value_(response, length, this->response_offset_, value_offset, sensor, factor);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this->is_current_response_type_(GENI_RESPONSE_TYPE_FLOW_HEAD)) {
|
||||||
|
ESP_LOGD(TAG, "[%s] FLOW HEAD Response", this->parent_->address_str().c_str());
|
||||||
|
extract_publish_sensor_value(GENI_RESPONSE_FLOW_OFFSET, this->flow_sensor_, 3600.0F);
|
||||||
|
extract_publish_sensor_value(GENI_RESPONSE_HEAD_OFFSET, this->head_sensor_, .0001F);
|
||||||
|
} else if (this->is_current_response_type_(GENI_RESPONSE_TYPE_POWER)) {
|
||||||
|
ESP_LOGD(TAG, "[%s] POWER Response", this->parent_->address_str().c_str());
|
||||||
|
extract_publish_sensor_value(GENI_RESPONSE_POWER_OFFSET, this->power_sensor_, 1.0F);
|
||||||
|
extract_publish_sensor_value(GENI_RESPONSE_CURRENT_OFFSET, this->current_sensor_, 1.0F);
|
||||||
|
extract_publish_sensor_value(GENI_RESPONSE_MOTOR_SPEED_OFFSET, this->speed_sensor_, 1.0F);
|
||||||
|
extract_publish_sensor_value(GENI_RESPONSE_VOLTAGE_AC_OFFSET, this->voltage_sensor_, 1.0F);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "unkown GENI response Type %d %d %d %d %d %d %d %d", this->response_type_[0], this->response_type_[1],
|
||||||
|
this->response_type_[2], this->response_type_[3], this->response_type_[4], this->response_type_[5],
|
||||||
|
this->response_type_[6], this->response_type_[7]);
|
||||||
|
}
|
||||||
|
this->response_offset_ += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Alpha3::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||||
|
switch (event) {
|
||||||
|
case ESP_GATTC_OPEN_EVT: {
|
||||||
|
this->response_offset_ = 0;
|
||||||
|
this->response_length_ = 0;
|
||||||
|
ESP_LOGI(TAG, "[%s] connection open", this->parent_->address_str().c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_CONNECT_EVT: {
|
||||||
|
if (std::memcmp(param->connect.remote_bda, this->parent_->get_remote_bda(), 6) != 0)
|
||||||
|
return;
|
||||||
|
auto ret = esp_ble_set_encryption(param->connect.remote_bda, ESP_BLE_SEC_ENCRYPT);
|
||||||
|
if (ret) {
|
||||||
|
ESP_LOGW(TAG, "esp_ble_set_encryption failed, status=%x", ret);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
|
this->node_state = espbt::ClientState::IDLE;
|
||||||
|
if (this->flow_sensor_ != nullptr)
|
||||||
|
this->flow_sensor_->publish_state(NAN);
|
||||||
|
if (this->head_sensor_ != nullptr)
|
||||||
|
this->head_sensor_->publish_state(NAN);
|
||||||
|
if (this->power_sensor_ != nullptr)
|
||||||
|
this->power_sensor_->publish_state(NAN);
|
||||||
|
if (this->current_sensor_ != nullptr)
|
||||||
|
this->current_sensor_->publish_state(NAN);
|
||||||
|
if (this->speed_sensor_ != nullptr)
|
||||||
|
this->speed_sensor_->publish_state(NAN);
|
||||||
|
if (this->speed_sensor_ != nullptr)
|
||||||
|
this->voltage_sensor_->publish_state(NAN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
|
auto *chr = this->parent_->get_characteristic(ALPHA3_GENI_SERVICE_UUID, ALPHA3_GENI_CHARACTERISTIC_UUID);
|
||||||
|
if (chr == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "[%s] No GENI service found at device, not an Alpha3..?", this->parent_->address_str().c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
|
||||||
|
chr->handle);
|
||||||
|
if (status) {
|
||||||
|
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
|
||||||
|
}
|
||||||
|
this->geni_handle_ = chr->handle;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
|
this->update();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_NOTIFY_EVT: {
|
||||||
|
if (param->notify.handle == this->geni_handle_) {
|
||||||
|
this->handle_geni_response_(param->notify.value, param->notify.value_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Alpha3::send_request_(uint8_t *request, size_t len) {
|
||||||
|
auto status =
|
||||||
|
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->geni_handle_, len,
|
||||||
|
request, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Alpha3::update() {
|
||||||
|
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||||
|
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->flow_sensor_ != nullptr || this->head_sensor_ != nullptr) {
|
||||||
|
uint8_t geni_request_flow_head[] = {39, 7, 231, 248, 10, 3, 93, 1, 33, 82, 31};
|
||||||
|
this->send_request_(geni_request_flow_head, sizeof(geni_request_flow_head));
|
||||||
|
delay(25); // need to wait between requests
|
||||||
|
}
|
||||||
|
if (this->power_sensor_ != nullptr || this->current_sensor_ != nullptr || this->speed_sensor_ != nullptr ||
|
||||||
|
this->voltage_sensor_ != nullptr) {
|
||||||
|
uint8_t geni_request_power[] = {39, 7, 231, 248, 10, 3, 87, 0, 69, 138, 205};
|
||||||
|
this->send_request_(geni_request_power, sizeof(geni_request_power));
|
||||||
|
delay(25); // need to wait between requests
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace alpha3
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
73
esphome/components/alpha3/alpha3.h
Normal file
73
esphome/components/alpha3/alpha3.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace alpha3 {
|
||||||
|
|
||||||
|
namespace espbt = esphome::esp32_ble_tracker;
|
||||||
|
|
||||||
|
static const espbt::ESPBTUUID ALPHA3_GENI_SERVICE_UUID = espbt::ESPBTUUID::from_uint16(0xfe5d);
|
||||||
|
static const espbt::ESPBTUUID ALPHA3_GENI_CHARACTERISTIC_UUID =
|
||||||
|
espbt::ESPBTUUID::from_raw({static_cast<char>(0xa9), 0x7b, static_cast<char>(0xb8), static_cast<char>(0x85), 0x0,
|
||||||
|
0x1a, 0x28, static_cast<char>(0xaa), 0x2a, 0x43, 0x6e, 0x3, static_cast<char>(0xd1),
|
||||||
|
static_cast<char>(0xff), static_cast<char>(0x9c), static_cast<char>(0x85)});
|
||||||
|
static const int16_t GENI_RESPONSE_HEADER_LENGTH = 13;
|
||||||
|
static const size_t GENI_RESPONSE_TYPE_LENGTH = 8;
|
||||||
|
|
||||||
|
static const uint8_t GENI_RESPONSE_TYPE_FLOW_HEAD[GENI_RESPONSE_TYPE_LENGTH] = {31, 0, 1, 48, 1, 0, 0, 24};
|
||||||
|
static const int16_t GENI_RESPONSE_FLOW_OFFSET = 0;
|
||||||
|
static const int16_t GENI_RESPONSE_HEAD_OFFSET = 4;
|
||||||
|
|
||||||
|
static const uint8_t GENI_RESPONSE_TYPE_POWER[GENI_RESPONSE_TYPE_LENGTH] = {44, 0, 1, 0, 1, 0, 0, 37};
|
||||||
|
static const int16_t GENI_RESPONSE_VOLTAGE_AC_OFFSET = 0;
|
||||||
|
static const int16_t GENI_RESPONSE_VOLTAGE_DC_OFFSET = 4;
|
||||||
|
static const int16_t GENI_RESPONSE_CURRENT_OFFSET = 8;
|
||||||
|
static const int16_t GENI_RESPONSE_POWER_OFFSET = 12;
|
||||||
|
static const int16_t GENI_RESPONSE_MOTOR_POWER_OFFSET = 16; // not sure
|
||||||
|
static const int16_t GENI_RESPONSE_MOTOR_SPEED_OFFSET = 20;
|
||||||
|
|
||||||
|
class Alpha3 : public esphome::ble_client::BLEClientNode, public PollingComponent {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void update() override;
|
||||||
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
void set_flow_sensor(sensor::Sensor *sensor) { this->flow_sensor_ = sensor; }
|
||||||
|
void set_head_sensor(sensor::Sensor *sensor) { this->head_sensor_ = sensor; }
|
||||||
|
void set_power_sensor(sensor::Sensor *sensor) { this->power_sensor_ = sensor; }
|
||||||
|
void set_current_sensor(sensor::Sensor *sensor) { this->current_sensor_ = sensor; }
|
||||||
|
void set_speed_sensor(sensor::Sensor *sensor) { this->speed_sensor_ = sensor; }
|
||||||
|
void set_voltage_sensor(sensor::Sensor *sensor) { this->voltage_sensor_ = sensor; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sensor::Sensor *flow_sensor_{nullptr};
|
||||||
|
sensor::Sensor *head_sensor_{nullptr};
|
||||||
|
sensor::Sensor *power_sensor_{nullptr};
|
||||||
|
sensor::Sensor *current_sensor_{nullptr};
|
||||||
|
sensor::Sensor *speed_sensor_{nullptr};
|
||||||
|
sensor::Sensor *voltage_sensor_{nullptr};
|
||||||
|
uint16_t geni_handle_;
|
||||||
|
int16_t response_length_;
|
||||||
|
int16_t response_offset_;
|
||||||
|
uint8_t response_type_[GENI_RESPONSE_TYPE_LENGTH];
|
||||||
|
uint8_t buffer_[4];
|
||||||
|
void extract_publish_sensor_value_(const uint8_t *response, int16_t length, int16_t response_offset,
|
||||||
|
int16_t value_offset, sensor::Sensor *sensor, float factor);
|
||||||
|
void handle_geni_response_(const uint8_t *response, uint16_t length);
|
||||||
|
void send_request_(uint8_t *request, size_t len);
|
||||||
|
bool is_current_response_type_(const uint8_t *response_type);
|
||||||
|
};
|
||||||
|
} // namespace alpha3
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
85
esphome/components/alpha3/sensor.py
Normal file
85
esphome/components/alpha3/sensor.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, ble_client
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_CURRENT,
|
||||||
|
CONF_FLOW,
|
||||||
|
CONF_HEAD,
|
||||||
|
CONF_POWER,
|
||||||
|
CONF_SPEED,
|
||||||
|
CONF_VOLTAGE,
|
||||||
|
UNIT_AMPERE,
|
||||||
|
UNIT_VOLT,
|
||||||
|
UNIT_WATT,
|
||||||
|
UNIT_METER,
|
||||||
|
UNIT_CUBIC_METER_PER_HOUR,
|
||||||
|
UNIT_REVOLUTIONS_PER_MINUTE,
|
||||||
|
)
|
||||||
|
|
||||||
|
alpha3_ns = cg.esphome_ns.namespace("alpha3")
|
||||||
|
Alpha3 = alpha3_ns.class_("Alpha3", ble_client.BLEClientNode, cg.PollingComponent)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(Alpha3),
|
||||||
|
cv.Optional(CONF_FLOW): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CUBIC_METER_PER_HOUR,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HEAD): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_METER,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_AMPERE,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_SPEED): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_REVOLUTIONS_PER_MINUTE,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||||
|
.extend(cv.polling_component_schema("15s"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await ble_client.register_ble_node(var, config)
|
||||||
|
|
||||||
|
if flow_config := config.get(CONF_FLOW):
|
||||||
|
sens = await sensor.new_sensor(flow_config)
|
||||||
|
cg.add(var.set_flow_sensor(sens))
|
||||||
|
|
||||||
|
if head_config := config.get(CONF_HEAD):
|
||||||
|
sens = await sensor.new_sensor(head_config)
|
||||||
|
cg.add(var.set_head_sensor(sens))
|
||||||
|
|
||||||
|
if power_config := config.get(CONF_POWER):
|
||||||
|
sens = await sensor.new_sensor(power_config)
|
||||||
|
cg.add(var.set_power_sensor(sens))
|
||||||
|
|
||||||
|
if current_config := config.get(CONF_CURRENT):
|
||||||
|
sens = await sensor.new_sensor(current_config)
|
||||||
|
cg.add(var.set_current_sensor(sens))
|
||||||
|
|
||||||
|
if speed_config := config.get(CONF_SPEED):
|
||||||
|
sens = await sensor.new_sensor(speed_config)
|
||||||
|
cg.add(var.set_speed_sensor(sens))
|
||||||
|
|
||||||
|
if voltage_config := config.get(CONF_VOLTAGE):
|
||||||
|
sens = await sensor.new_sensor(voltage_config)
|
||||||
|
cg.add(var.set_voltage_sensor(sens))
|
|
@ -47,10 +47,10 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
if CONF_TEMPERATURE in config:
|
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
sens = await sensor.new_sensor(temperature_config)
|
||||||
cg.add(var.set_temperature_sensor(sens))
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
|
||||||
if CONF_HUMIDITY in config:
|
if humidity_config := config.get(CONF_HUMIDITY):
|
||||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
sens = await sensor.new_sensor(humidity_config)
|
||||||
cg.add(var.set_humidity_sensor(sens))
|
cg.add(var.set_humidity_sensor(sens))
|
||||||
|
|
|
@ -44,10 +44,10 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await ble_client.register_ble_node(var, config)
|
await ble_client.register_ble_node(var, config)
|
||||||
|
|
||||||
if CONF_BATTERY_LEVEL in config:
|
if battery_level_config := config.get(CONF_BATTERY_LEVEL):
|
||||||
sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
sens = await sensor.new_sensor(battery_level_config)
|
||||||
cg.add(var.set_battery(sens))
|
cg.add(var.set_battery(sens))
|
||||||
|
|
||||||
if CONF_ILLUMINANCE in config:
|
if illuminance_config := config.get(CONF_ILLUMINANCE):
|
||||||
sens = await sensor.new_sensor(config[CONF_ILLUMINANCE])
|
sens = await sensor.new_sensor(illuminance_config)
|
||||||
cg.add(var.set_illuminance(sens))
|
cg.add(var.set_illuminance(sens))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from esphome import core
|
from esphome import automation, core
|
||||||
from esphome.components import display, font
|
from esphome.components import font
|
||||||
import esphome.components.image as espImage
|
import esphome.components.image as espImage
|
||||||
from esphome.components.image import CONF_USE_TRANSPARENCY
|
from esphome.components.image import CONF_USE_TRANSPARENCY
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
@ -18,14 +18,30 @@ from esphome.core import CORE, HexInt
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
AUTO_LOAD = ["image"]
|
||||||
|
CODEOWNERS = ["@syndlex"]
|
||||||
DEPENDENCIES = ["display"]
|
DEPENDENCIES = ["display"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
CONF_LOOP = "loop"
|
CONF_LOOP = "loop"
|
||||||
CONF_START_FRAME = "start_frame"
|
CONF_START_FRAME = "start_frame"
|
||||||
CONF_END_FRAME = "end_frame"
|
CONF_END_FRAME = "end_frame"
|
||||||
|
CONF_FRAME = "frame"
|
||||||
|
|
||||||
Animation_ = display.display_ns.class_("Animation", espImage.Image_)
|
animation_ns = cg.esphome_ns.namespace("animation")
|
||||||
|
|
||||||
|
Animation_ = animation_ns.class_("Animation", espImage.Image_)
|
||||||
|
|
||||||
|
# Actions
|
||||||
|
NextFrameAction = animation_ns.class_(
|
||||||
|
"AnimationNextFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||||
|
)
|
||||||
|
PrevFrameAction = animation_ns.class_(
|
||||||
|
"AnimationPrevFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||||
|
)
|
||||||
|
SetFrameAction = animation_ns.class_(
|
||||||
|
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_cross_dependencies(config):
|
def validate_cross_dependencies(config):
|
||||||
|
@ -74,7 +90,35 @@ ANIMATION_SCHEMA = cv.Schema(
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
|
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
|
||||||
|
|
||||||
CODEOWNERS = ["@syndlex"]
|
NEXT_FRAME_SCHEMA = automation.maybe_simple_id(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(Animation_),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
PREV_FRAME_SCHEMA = automation.maybe_simple_id(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(Animation_),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
SET_FRAME_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(Animation_),
|
||||||
|
cv.Required(CONF_FRAME): cv.uint16_t,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action("animation.next_frame", NextFrameAction, NEXT_FRAME_SCHEMA)
|
||||||
|
@automation.register_action("animation.prev_frame", PrevFrameAction, PREV_FRAME_SCHEMA)
|
||||||
|
@automation.register_action("animation.set_frame", SetFrameAction, SET_FRAME_SCHEMA)
|
||||||
|
async def animation_action_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)
|
||||||
|
|
||||||
|
if (frame := config.get(CONF_FRAME)) is not None:
|
||||||
|
template_ = await cg.templatable(frame, args, cg.uint16)
|
||||||
|
cg.add(var.set_frame(template_))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
@ -245,8 +289,8 @@ async def to_code(config):
|
||||||
espImage.IMAGE_TYPE[config[CONF_TYPE]],
|
espImage.IMAGE_TYPE[config[CONF_TYPE]],
|
||||||
)
|
)
|
||||||
cg.add(var.set_transparency(transparent))
|
cg.add(var.set_transparency(transparent))
|
||||||
if CONF_LOOP in config:
|
if loop_config := config.get(CONF_LOOP):
|
||||||
start = config[CONF_LOOP][CONF_START_FRAME]
|
start = loop_config[CONF_START_FRAME]
|
||||||
end = config[CONF_LOOP].get(CONF_END_FRAME, frames)
|
end = loop_config.get(CONF_END_FRAME, frames)
|
||||||
count = config[CONF_LOOP].get(CONF_REPEAT, -1)
|
count = loop_config.get(CONF_REPEAT, -1)
|
||||||
cg.add(var.set_loop(start, end, count))
|
cg.add(var.set_loop(start, end, count))
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace display {
|
namespace animation {
|
||||||
|
|
||||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, ImageType type)
|
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(data_start, width, height, type),
|
||||||
animation_data_start_(data_start),
|
animation_data_start_(data_start),
|
||||||
current_frame_(0),
|
current_frame_(0),
|
||||||
|
@ -65,5 +66,5 @@ void Animation::update_data_start_() {
|
||||||
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
|
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace display
|
} // namespace animation
|
||||||
} // namespace esphome
|
} // namespace esphome
|
67
esphome/components/animation/animation.h
Normal file
67
esphome/components/animation/animation.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#pragma once
|
||||||
|
#include "esphome/components/image/image.h"
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
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);
|
||||||
|
|
||||||
|
uint32_t get_animation_frame_count() const;
|
||||||
|
int get_current_frame() const;
|
||||||
|
void next_frame();
|
||||||
|
void prev_frame();
|
||||||
|
|
||||||
|
/** Selects a specific frame within the animation.
|
||||||
|
*
|
||||||
|
* @param frame If possitive, advance to the frame. If negative, recede to that frame from the end frame.
|
||||||
|
*/
|
||||||
|
void set_frame(int frame);
|
||||||
|
|
||||||
|
void set_loop(uint32_t start_frame, uint32_t end_frame, int count);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void update_data_start_();
|
||||||
|
|
||||||
|
const uint8_t *animation_data_start_;
|
||||||
|
int current_frame_;
|
||||||
|
uint32_t animation_frame_count_;
|
||||||
|
uint32_t loop_start_frame_;
|
||||||
|
uint32_t loop_end_frame_;
|
||||||
|
int loop_count_;
|
||||||
|
int loop_current_iteration_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class AnimationNextFrameAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
AnimationNextFrameAction(Animation *parent) : parent_(parent) {}
|
||||||
|
void play(Ts... x) override { this->parent_->next_frame(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Animation *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class AnimationPrevFrameAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
AnimationPrevFrameAction(Animation *parent) : parent_(parent) {}
|
||||||
|
void play(Ts... x) override { this->parent_->prev_frame(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Animation *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class AnimationSetFrameAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
AnimationSetFrameAction(Animation *parent) : parent_(parent) {}
|
||||||
|
TEMPLATABLE_VALUE(uint16_t, frame)
|
||||||
|
void play(Ts... x) override { this->parent_->set_frame(this->frame_.value(x...)); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Animation *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace animation
|
||||||
|
} // namespace esphome
|
|
@ -116,9 +116,8 @@ async def to_code(config):
|
||||||
cg.add(var.register_user_service(trigger))
|
cg.add(var.register_user_service(trigger))
|
||||||
await automation.build_automation(trigger, func_args, conf)
|
await automation.build_automation(trigger, func_args, conf)
|
||||||
|
|
||||||
if CONF_ENCRYPTION in config:
|
if encryption_config := config.get(CONF_ENCRYPTION):
|
||||||
conf = config[CONF_ENCRYPTION]
|
decoded = base64.b64decode(encryption_config[CONF_KEY])
|
||||||
decoded = base64.b64decode(conf[CONF_KEY])
|
|
||||||
cg.add(var.set_noise_psk(list(decoded)))
|
cg.add(var.set_noise_psk(list(decoded)))
|
||||||
cg.add_define("USE_API_NOISE")
|
cg.add_define("USE_API_NOISE")
|
||||||
cg.add_library("esphome/noise-c", "0.1.4")
|
cg.add_library("esphome/noise-c", "0.1.4")
|
||||||
|
|
|
@ -1420,6 +1420,7 @@ message VoiceAssistantRequest {
|
||||||
|
|
||||||
bool start = 1;
|
bool start = 1;
|
||||||
string conversation_id = 2;
|
string conversation_id = 2;
|
||||||
|
bool use_vad = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message VoiceAssistantResponse {
|
message VoiceAssistantResponse {
|
||||||
|
|
|
@ -907,12 +907,13 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id) {
|
bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad) {
|
||||||
if (!this->voice_assistant_subscription_)
|
if (!this->voice_assistant_subscription_)
|
||||||
return false;
|
return false;
|
||||||
VoiceAssistantRequest msg;
|
VoiceAssistantRequest msg;
|
||||||
msg.start = start;
|
msg.start = start;
|
||||||
msg.conversation_id = conversation_id;
|
msg.conversation_id = conversation_id;
|
||||||
|
msg.use_vad = use_vad;
|
||||||
return this->send_voice_assistant_request(msg);
|
return this->send_voice_assistant_request(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
|
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
|
||||||
|
@ -1050,6 +1051,10 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||||
resp.manufacturer = "Espressif";
|
resp.manufacturer = "Espressif";
|
||||||
#elif defined(USE_RP2040)
|
#elif defined(USE_RP2040)
|
||||||
resp.manufacturer = "Raspberry Pi";
|
resp.manufacturer = "Raspberry Pi";
|
||||||
|
#elif defined(USE_BK72XX)
|
||||||
|
resp.manufacturer = "Beken";
|
||||||
|
#elif defined(USE_RTL87XX)
|
||||||
|
resp.manufacturer = "Realtek";
|
||||||
#elif defined(USE_HOST)
|
#elif defined(USE_HOST)
|
||||||
resp.manufacturer = "Host";
|
resp.manufacturer = "Host";
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -124,7 +124,7 @@ class APIConnection : public APIServerConnection {
|
||||||
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
|
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
|
||||||
this->voice_assistant_subscription_ = msg.subscribe;
|
this->voice_assistant_subscription_ = msg.subscribe;
|
||||||
}
|
}
|
||||||
bool request_voice_assistant(bool start, const std::string &conversation_id);
|
bool request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad);
|
||||||
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
|
||||||
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "api_pb2.h"
|
#include "api_pb2.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
@ -522,12 +524,12 @@ void HelloRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" api_version_major: ");
|
out.append(" api_version_major: ");
|
||||||
sprintf(buffer, "%u", this->api_version_major);
|
sprintf(buffer, "%" PRIu32, this->api_version_major);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" api_version_minor: ");
|
out.append(" api_version_minor: ");
|
||||||
sprintf(buffer, "%u", this->api_version_minor);
|
sprintf(buffer, "%" PRIu32, this->api_version_minor);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -572,12 +574,12 @@ void HelloResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("HelloResponse {\n");
|
out.append("HelloResponse {\n");
|
||||||
out.append(" api_version_major: ");
|
out.append(" api_version_major: ");
|
||||||
sprintf(buffer, "%u", this->api_version_major);
|
sprintf(buffer, "%" PRIu32, this->api_version_major);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" api_version_minor: ");
|
out.append(" api_version_minor: ");
|
||||||
sprintf(buffer, "%u", this->api_version_minor);
|
sprintf(buffer, "%" PRIu32, this->api_version_minor);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -783,17 +785,17 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" webserver_port: ");
|
out.append(" webserver_port: ");
|
||||||
sprintf(buffer, "%u", this->webserver_port);
|
sprintf(buffer, "%" PRIu32, this->webserver_port);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" legacy_bluetooth_proxy_version: ");
|
out.append(" legacy_bluetooth_proxy_version: ");
|
||||||
sprintf(buffer, "%u", this->legacy_bluetooth_proxy_version);
|
sprintf(buffer, "%" PRIu32, this->legacy_bluetooth_proxy_version);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" bluetooth_proxy_feature_flags: ");
|
out.append(" bluetooth_proxy_feature_flags: ");
|
||||||
sprintf(buffer, "%u", this->bluetooth_proxy_feature_flags);
|
sprintf(buffer, "%" PRIu32, this->bluetooth_proxy_feature_flags);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -806,7 +808,7 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" voice_assistant_version: ");
|
out.append(" voice_assistant_version: ");
|
||||||
sprintf(buffer, "%u", this->voice_assistant_version);
|
sprintf(buffer, "%" PRIu32, this->voice_assistant_version);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -898,7 +900,7 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -966,7 +968,7 @@ void BinarySensorStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("BinarySensorStateResponse {\n");
|
out.append("BinarySensorStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1069,7 +1071,7 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1159,7 +1161,7 @@ void CoverStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("CoverStateResponse {\n");
|
out.append("CoverStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1242,7 +1244,7 @@ void CoverCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("CoverCommandRequest {\n");
|
out.append("CoverCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1362,7 +1364,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1387,7 +1389,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" supported_speed_count: ");
|
out.append(" supported_speed_count: ");
|
||||||
sprintf(buffer, "%d", this->supported_speed_count);
|
sprintf(buffer, "%" PRId32, this->supported_speed_count);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1454,7 +1456,7 @@ void FanStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("FanStateResponse {\n");
|
out.append("FanStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1475,7 +1477,7 @@ void FanStateResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" speed_level: ");
|
out.append(" speed_level: ");
|
||||||
sprintf(buffer, "%d", this->speed_level);
|
sprintf(buffer, "%" PRId32, this->speed_level);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -1555,7 +1557,7 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("FanCommandRequest {\n");
|
out.append("FanCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1596,7 +1598,7 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" speed_level: ");
|
out.append(" speed_level: ");
|
||||||
sprintf(buffer, "%d", this->speed_level);
|
sprintf(buffer, "%" PRId32, this->speed_level);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -1710,7 +1712,7 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -1864,7 +1866,7 @@ void LightStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("LightStateResponse {\n");
|
out.append("LightStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2087,7 +2089,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("LightCommandRequest {\n");
|
out.append("LightCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2185,7 +2187,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" transition_length: ");
|
out.append(" transition_length: ");
|
||||||
sprintf(buffer, "%u", this->transition_length);
|
sprintf(buffer, "%" PRIu32, this->transition_length);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2194,7 +2196,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" flash_length: ");
|
out.append(" flash_length: ");
|
||||||
sprintf(buffer, "%u", this->flash_length);
|
sprintf(buffer, "%" PRIu32, this->flash_length);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2302,7 +2304,7 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2323,7 +2325,7 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" accuracy_decimals: ");
|
out.append(" accuracy_decimals: ");
|
||||||
sprintf(buffer, "%d", this->accuracy_decimals);
|
sprintf(buffer, "%" PRId32, this->accuracy_decimals);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2387,7 +2389,7 @@ void SensorStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("SensorStateResponse {\n");
|
out.append("SensorStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2476,7 +2478,7 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2539,7 +2541,7 @@ void SwitchStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("SwitchStateResponse {\n");
|
out.append("SwitchStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2578,7 +2580,7 @@ void SwitchCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("SwitchCommandRequest {\n");
|
out.append("SwitchCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2652,7 +2654,7 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -2718,7 +2720,7 @@ void TextSensorStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("TextSensorStateResponse {\n");
|
out.append("TextSensorStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3025,7 +3027,7 @@ void GetTimeResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("GetTimeResponse {\n");
|
out.append("GetTimeResponse {\n");
|
||||||
out.append(" epoch_seconds: ");
|
out.append(" epoch_seconds: ");
|
||||||
sprintf(buffer, "%u", this->epoch_seconds);
|
sprintf(buffer, "%" PRIu32, this->epoch_seconds);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -3109,7 +3111,7 @@ void ListEntitiesServicesResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3203,7 +3205,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" legacy_int: ");
|
out.append(" legacy_int: ");
|
||||||
sprintf(buffer, "%d", this->legacy_int);
|
sprintf(buffer, "%" PRId32, this->legacy_int);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3217,7 +3219,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" int_: ");
|
out.append(" int_: ");
|
||||||
sprintf(buffer, "%d", this->int_);
|
sprintf(buffer, "%" PRId32, this->int_);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3229,7 +3231,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
|
||||||
|
|
||||||
for (const auto &it : this->int_array) {
|
for (const auto &it : this->int_array) {
|
||||||
out.append(" int_array: ");
|
out.append(" int_array: ");
|
||||||
sprintf(buffer, "%d", it);
|
sprintf(buffer, "%" PRId32, it);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
}
|
}
|
||||||
|
@ -3280,7 +3282,7 @@ void ExecuteServiceRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("ExecuteServiceRequest {\n");
|
out.append("ExecuteServiceRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3356,7 +3358,7 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3422,7 +3424,7 @@ void CameraImageResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("CameraImageResponse {\n");
|
out.append("CameraImageResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3614,7 +3616,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3802,7 +3804,7 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("ClimateStateResponse {\n");
|
out.append("ClimateStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -3990,7 +3992,7 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("ClimateCommandRequest {\n");
|
out.append("ClimateCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4173,7 +4175,7 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4260,7 +4262,7 @@ void NumberStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("NumberStateResponse {\n");
|
out.append("NumberStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4298,7 +4300,7 @@ void NumberCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("NumberCommandRequest {\n");
|
out.append("NumberCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4380,7 +4382,7 @@ void ListEntitiesSelectResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4452,7 +4454,7 @@ void SelectStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("SelectStateResponse {\n");
|
out.append("SelectStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4495,7 +4497,7 @@ void SelectCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("SelectCommandRequest {\n");
|
out.append("SelectCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4589,7 +4591,7 @@ void ListEntitiesLockResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4660,7 +4662,7 @@ void LockStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("LockStateResponse {\n");
|
out.append("LockStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4715,7 +4717,7 @@ void LockCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("LockCommandRequest {\n");
|
out.append("LockCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4802,7 +4804,7 @@ void ListEntitiesButtonResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4848,7 +4850,7 @@ void ButtonCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("ButtonCommandRequest {\n");
|
out.append("ButtonCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -4923,7 +4925,7 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -4992,7 +4994,7 @@ void MediaPlayerStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("MediaPlayerStateResponse {\n");
|
out.append("MediaPlayerStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5071,7 +5073,7 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("MediaPlayerCommandRequest {\n");
|
out.append("MediaPlayerCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5120,7 +5122,7 @@ void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("SubscribeBluetoothLEAdvertisementsRequest {\n");
|
out.append("SubscribeBluetoothLEAdvertisementsRequest {\n");
|
||||||
out.append(" flags: ");
|
out.append(" flags: ");
|
||||||
sprintf(buffer, "%u", this->flags);
|
sprintf(buffer, "%" PRIu32, this->flags);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -5167,7 +5169,7 @@ void BluetoothServiceData::dump_to(std::string &out) const {
|
||||||
|
|
||||||
for (const auto &it : this->legacy_data) {
|
for (const auto &it : this->legacy_data) {
|
||||||
out.append(" legacy_data: ");
|
out.append(" legacy_data: ");
|
||||||
sprintf(buffer, "%u", it);
|
sprintf(buffer, "%" PRIu32, it);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
}
|
}
|
||||||
|
@ -5247,7 +5249,7 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" rssi: ");
|
out.append(" rssi: ");
|
||||||
sprintf(buffer, "%d", this->rssi);
|
sprintf(buffer, "%" PRId32, this->rssi);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5270,7 +5272,7 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append(" address_type: ");
|
out.append(" address_type: ");
|
||||||
sprintf(buffer, "%u", this->address_type);
|
sprintf(buffer, "%" PRIu32, this->address_type);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -5320,12 +5322,12 @@ void BluetoothLERawAdvertisement::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" rssi: ");
|
out.append(" rssi: ");
|
||||||
sprintf(buffer, "%d", this->rssi);
|
sprintf(buffer, "%" PRId32, this->rssi);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" address_type: ");
|
out.append(" address_type: ");
|
||||||
sprintf(buffer, "%u", this->address_type);
|
sprintf(buffer, "%" PRIu32, this->address_type);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5408,7 +5410,7 @@ void BluetoothDeviceRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" address_type: ");
|
out.append(" address_type: ");
|
||||||
sprintf(buffer, "%u", this->address_type);
|
sprintf(buffer, "%" PRIu32, this->address_type);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -5456,12 +5458,12 @@ void BluetoothDeviceConnectionResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" mtu: ");
|
out.append(" mtu: ");
|
||||||
sprintf(buffer, "%u", this->mtu);
|
sprintf(buffer, "%" PRIu32, this->mtu);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" error: ");
|
out.append(" error: ");
|
||||||
sprintf(buffer, "%d", this->error);
|
sprintf(buffer, "%" PRId32, this->error);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -5521,7 +5523,7 @@ void BluetoothGATTDescriptor::dump_to(std::string &out) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -5577,12 +5579,12 @@ void BluetoothGATTCharacteristic::dump_to(std::string &out) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" properties: ");
|
out.append(" properties: ");
|
||||||
sprintf(buffer, "%u", this->properties);
|
sprintf(buffer, "%" PRIu32, this->properties);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5639,7 +5641,7 @@ void BluetoothGATTService::dump_to(std::string &out) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5746,7 +5748,7 @@ void BluetoothGATTReadRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -5791,7 +5793,7 @@ void BluetoothGATTReadResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5845,7 +5847,7 @@ void BluetoothGATTWriteRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5887,7 +5889,7 @@ void BluetoothGATTReadDescriptorRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -5932,7 +5934,7 @@ void BluetoothGATTWriteDescriptorRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -5975,7 +5977,7 @@ void BluetoothGATTNotifyRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -6024,7 +6026,7 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -6063,12 +6065,12 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("BluetoothConnectionsFreeResponse {\n");
|
out.append("BluetoothConnectionsFreeResponse {\n");
|
||||||
out.append(" free: ");
|
out.append(" free: ");
|
||||||
sprintf(buffer, "%u", this->free);
|
sprintf(buffer, "%" PRIu32, this->free);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" limit: ");
|
out.append(" limit: ");
|
||||||
sprintf(buffer, "%u", this->limit);
|
sprintf(buffer, "%" PRIu32, this->limit);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -6107,12 +6109,12 @@ void BluetoothGATTErrorResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" error: ");
|
out.append(" error: ");
|
||||||
sprintf(buffer, "%d", this->error);
|
sprintf(buffer, "%" PRId32, this->error);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -6146,7 +6148,7 @@ void BluetoothGATTWriteResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -6180,7 +6182,7 @@ void BluetoothGATTNotifyResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" handle: ");
|
out.append(" handle: ");
|
||||||
sprintf(buffer, "%u", this->handle);
|
sprintf(buffer, "%" PRIu32, this->handle);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -6223,7 +6225,7 @@ void BluetoothDevicePairingResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" error: ");
|
out.append(" error: ");
|
||||||
sprintf(buffer, "%d", this->error);
|
sprintf(buffer, "%" PRId32, this->error);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -6266,7 +6268,7 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" error: ");
|
out.append(" error: ");
|
||||||
sprintf(buffer, "%d", this->error);
|
sprintf(buffer, "%" PRId32, this->error);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -6315,7 +6317,7 @@ void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" error: ");
|
out.append(" error: ");
|
||||||
sprintf(buffer, "%d", this->error);
|
sprintf(buffer, "%" PRId32, this->error);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
|
@ -6348,6 +6350,10 @@ bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||||
this->start = value.as_bool();
|
this->start = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 3: {
|
||||||
|
this->use_vad = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -6365,6 +6371,7 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
|
||||||
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
|
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_bool(1, this->start);
|
buffer.encode_bool(1, this->start);
|
||||||
buffer.encode_string(2, this->conversation_id);
|
buffer.encode_string(2, this->conversation_id);
|
||||||
|
buffer.encode_bool(3, this->use_vad);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void VoiceAssistantRequest::dump_to(std::string &out) const {
|
void VoiceAssistantRequest::dump_to(std::string &out) const {
|
||||||
|
@ -6377,6 +6384,10 @@ void VoiceAssistantRequest::dump_to(std::string &out) const {
|
||||||
out.append(" conversation_id: ");
|
out.append(" conversation_id: ");
|
||||||
out.append("'").append(this->conversation_id).append("'");
|
out.append("'").append(this->conversation_id).append("'");
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" use_vad: ");
|
||||||
|
out.append(YESNO(this->use_vad));
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -6403,7 +6414,7 @@ void VoiceAssistantResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("VoiceAssistantResponse {\n");
|
out.append("VoiceAssistantResponse {\n");
|
||||||
out.append(" port: ");
|
out.append(" port: ");
|
||||||
sprintf(buffer, "%u", this->port);
|
sprintf(buffer, "%" PRIu32, this->port);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -6566,7 +6577,7 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -6591,7 +6602,7 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" supported_features: ");
|
out.append(" supported_features: ");
|
||||||
sprintf(buffer, "%u", this->supported_features);
|
sprintf(buffer, "%" PRIu32, this->supported_features);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -6634,7 +6645,7 @@ void AlarmControlPanelStateResponse::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("AlarmControlPanelStateResponse {\n");
|
out.append("AlarmControlPanelStateResponse {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
@ -6684,7 +6695,7 @@ void AlarmControlPanelCommandRequest::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("AlarmControlPanelCommandRequest {\n");
|
out.append("AlarmControlPanelCommandRequest {\n");
|
||||||
out.append(" key: ");
|
out.append(" key: ");
|
||||||
sprintf(buffer, "%u", this->key);
|
sprintf(buffer, "%" PRIu32, this->key);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
|
|
@ -1655,6 +1655,7 @@ class VoiceAssistantRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
bool start{false};
|
bool start{false};
|
||||||
std::string conversation_id{};
|
std::string conversation_id{};
|
||||||
|
bool use_vad{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
|
@ -323,16 +323,16 @@ void APIServer::on_shutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
bool APIServer::start_voice_assistant(const std::string &conversation_id) {
|
bool APIServer::start_voice_assistant(const std::string &conversation_id, bool use_vad) {
|
||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
if (c->request_voice_assistant(true, conversation_id))
|
if (c->request_voice_assistant(true, conversation_id, use_vad))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void APIServer::stop_voice_assistant() {
|
void APIServer::stop_voice_assistant() {
|
||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
if (c->request_voice_assistant(false, ""))
|
if (c->request_voice_assistant(false, "", false))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ class APIServer : public Component, public Controller {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
bool start_voice_assistant(const std::string &conversation_id);
|
bool start_voice_assistant(const std::string &conversation_id, bool use_vad);
|
||||||
void stop_voice_assistant();
|
void stop_voice_assistant();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ async def async_run_logs(config, address):
|
||||||
except APIConnectionError:
|
except APIConnectionError:
|
||||||
cli.disconnect()
|
cli.disconnect()
|
||||||
|
|
||||||
async def on_disconnect():
|
async def on_disconnect(expected_disconnect: bool) -> None:
|
||||||
_LOGGER.warning("Disconnected from API")
|
_LOGGER.warning("Disconnected from API")
|
||||||
|
|
||||||
zc = zeroconf.Zeroconf()
|
zc = zeroconf.Zeroconf()
|
||||||
|
|
|
@ -31,12 +31,10 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
hub = await cg.get_variable(config[CONF_AS3935_ID])
|
hub = await cg.get_variable(config[CONF_AS3935_ID])
|
||||||
|
|
||||||
if CONF_DISTANCE in config:
|
if distance_config := config.get(CONF_DISTANCE):
|
||||||
conf = config[CONF_DISTANCE]
|
sens = await sensor.new_sensor(distance_config)
|
||||||
distance_sensor = await sensor.new_sensor(conf)
|
cg.add(hub.set_distance_sensor(sens))
|
||||||
cg.add(hub.set_distance_sensor(distance_sensor))
|
|
||||||
|
|
||||||
if CONF_LIGHTNING_ENERGY in config:
|
if lightning_energy_config := config.get(CONF_LIGHTNING_ENERGY):
|
||||||
conf = config[CONF_LIGHTNING_ENERGY]
|
sens = await sensor.new_sensor(lightning_energy_config)
|
||||||
lightning_energy_sensor = await sensor.new_sensor(conf)
|
cg.add(hub.set_energy_sensor(sens))
|
||||||
cg.add(hub.set_energy_sensor(lightning_energy_sensor))
|
|
||||||
|
|
|
@ -107,6 +107,6 @@ async def to_code(config):
|
||||||
cg.add(var.set_astep(config[CONF_ASTEP]))
|
cg.add(var.set_astep(config[CONF_ASTEP]))
|
||||||
|
|
||||||
for conf_id, set_sensor_func in SENSORS.items():
|
for conf_id, set_sensor_func in SENSORS.items():
|
||||||
if conf_id in config:
|
if sens_config := config.get(conf_id):
|
||||||
sens = await sensor.new_sensor(config[conf_id])
|
sens = await sensor.new_sensor(sens_config)
|
||||||
cg.add(getattr(var, set_sensor_func)(sens))
|
cg.add(getattr(var, set_sensor_func)(sens))
|
||||||
|
|
|
@ -8,15 +8,15 @@ CODEOWNERS = ["@OttoWinter"]
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema({}),
|
cv.Schema({}),
|
||||||
cv.only_with_arduino,
|
cv.only_with_arduino,
|
||||||
cv.only_on(["esp32", "esp8266"]),
|
cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(200.0)
|
@coroutine_with_priority(200.0)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32 or CORE.is_libretiny:
|
||||||
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
||||||
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
|
cg.add_library("esphome/AsyncTCP-esphome", "2.0.1")
|
||||||
elif CORE.is_esp8266:
|
elif CORE.is_esp8266:
|
||||||
# https://github.com/esphome/ESPAsyncTCP
|
# https://github.com/esphome/ESPAsyncTCP
|
||||||
cg.add_library("esphome/ESPAsyncTCP-esphome", "1.2.3")
|
cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")
|
||||||
|
|
|
@ -83,18 +83,18 @@ async def to_code(config):
|
||||||
|
|
||||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||||
|
|
||||||
if CONF_TEMPERATURE in config:
|
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
sens = await sensor.new_sensor(temperature_config)
|
||||||
cg.add(var.set_temperature(sens))
|
cg.add(var.set_temperature(sens))
|
||||||
if CONF_HUMIDITY in config:
|
if humidity_config := config.get(CONF_HUMIDITY):
|
||||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
sens = await sensor.new_sensor(humidity_config)
|
||||||
cg.add(var.set_humidity(sens))
|
cg.add(var.set_humidity(sens))
|
||||||
if CONF_BATTERY_LEVEL in config:
|
if battery_level_config := config.get(CONF_BATTERY_LEVEL):
|
||||||
sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
sens = await sensor.new_sensor(battery_level_config)
|
||||||
cg.add(var.set_battery_level(sens))
|
cg.add(var.set_battery_level(sens))
|
||||||
if CONF_BATTERY_VOLTAGE in config:
|
if battery_voltage_config := config.get(CONF_BATTERY_VOLTAGE):
|
||||||
sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
|
sens = await sensor.new_sensor(battery_voltage_config)
|
||||||
cg.add(var.set_battery_voltage(sens))
|
cg.add(var.set_battery_voltage(sens))
|
||||||
if CONF_SIGNAL_STRENGTH in config:
|
if signal_strength_config := config.get(CONF_SIGNAL_STRENGTH):
|
||||||
sens = await sensor.new_sensor(config[CONF_SIGNAL_STRENGTH])
|
sens = await sensor.new_sensor(signal_strength_config)
|
||||||
cg.add(var.set_signal_strength(sens))
|
cg.add(var.set_signal_strength(sens))
|
||||||
|
|
1
esphome/components/atm90e26/__init__.py
Normal file
1
esphome/components/atm90e26/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@danieltwagner"]
|
235
esphome/components/atm90e26/atm90e26.cpp
Normal file
235
esphome/components/atm90e26/atm90e26.cpp
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
#include "atm90e26.h"
|
||||||
|
#include "atm90e26_reg.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace atm90e26 {
|
||||||
|
|
||||||
|
static const char *const TAG = "atm90e26";
|
||||||
|
|
||||||
|
void ATM90E26Component::update() {
|
||||||
|
if (this->read16_(ATM90E26_REGISTER_FUNCEN) != 0x0030) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->voltage_sensor_ != nullptr) {
|
||||||
|
this->voltage_sensor_->publish_state(this->get_line_voltage_());
|
||||||
|
}
|
||||||
|
if (this->current_sensor_ != nullptr) {
|
||||||
|
this->current_sensor_->publish_state(this->get_line_current_());
|
||||||
|
}
|
||||||
|
if (this->power_sensor_ != nullptr) {
|
||||||
|
this->power_sensor_->publish_state(this->get_active_power_());
|
||||||
|
}
|
||||||
|
if (this->reactive_power_sensor_ != nullptr) {
|
||||||
|
this->reactive_power_sensor_->publish_state(this->get_reactive_power_());
|
||||||
|
}
|
||||||
|
if (this->power_factor_sensor_ != nullptr) {
|
||||||
|
this->power_factor_sensor_->publish_state(this->get_power_factor_());
|
||||||
|
}
|
||||||
|
if (this->forward_active_energy_sensor_ != nullptr) {
|
||||||
|
this->forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_());
|
||||||
|
}
|
||||||
|
if (this->reverse_active_energy_sensor_ != nullptr) {
|
||||||
|
this->reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_());
|
||||||
|
}
|
||||||
|
if (this->freq_sensor_ != nullptr) {
|
||||||
|
this->freq_sensor_->publish_state(this->get_frequency_());
|
||||||
|
}
|
||||||
|
this->status_clear_warning();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATM90E26Component::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up ATM90E26 Component...");
|
||||||
|
this->spi_setup();
|
||||||
|
|
||||||
|
uint16_t mmode = 0x422; // default values for everything but L/N line current gains
|
||||||
|
mmode |= (gain_pga_ & 0x7) << 13;
|
||||||
|
mmode |= (n_line_gain_ & 0x3) << 11;
|
||||||
|
|
||||||
|
this->write16_(ATM90E26_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
|
||||||
|
this->write16_(ATM90E26_REGISTER_FUNCEN,
|
||||||
|
0x0030); // Voltage sag irq=1, report on warnout pin=1, energy dir change irq=0
|
||||||
|
uint16_t read = this->read16_(ATM90E26_REGISTER_LASTDATA);
|
||||||
|
if (read != 0x0030) {
|
||||||
|
ESP_LOGW(TAG, "Could not initialize ATM90E26 IC, check SPI settings: %d", read);
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: 100 * <nominal voltage, e.g. 230> * sqrt(2) * <fraction of nominal, e.g. 0.9> / (4 * gain_voltage/32768)
|
||||||
|
this->write16_(ATM90E26_REGISTER_SAGTH, 0x17DD); // Voltage sag threshhold 0x1F2F
|
||||||
|
|
||||||
|
// Set metering calibration values
|
||||||
|
this->write16_(ATM90E26_REGISTER_CALSTART, 0x5678); // CAL Metering calibration startup command
|
||||||
|
|
||||||
|
// Configure
|
||||||
|
this->write16_(ATM90E26_REGISTER_MMODE, mmode); // Metering Mode Configuration (see above)
|
||||||
|
|
||||||
|
this->write16_(ATM90E26_REGISTER_PLCONSTH, (pl_const_ >> 16)); // PL Constant MSB
|
||||||
|
this->write16_(ATM90E26_REGISTER_PLCONSTL, pl_const_ & 0xFFFF); // PL Constant LSB
|
||||||
|
|
||||||
|
// Calibrate this to be 1 pulse per Wh
|
||||||
|
this->write16_(ATM90E26_REGISTER_LGAIN, gain_metering_); // L Line Calibration Gain (active power metering)
|
||||||
|
this->write16_(ATM90E26_REGISTER_LPHI, 0x0000); // L Line Calibration Angle
|
||||||
|
this->write16_(ATM90E26_REGISTER_NGAIN, 0x0000); // N Line Calibration Gain
|
||||||
|
this->write16_(ATM90E26_REGISTER_NPHI, 0x0000); // N Line Calibration Angle
|
||||||
|
this->write16_(ATM90E26_REGISTER_PSTARTTH, 0x08BD); // Active Startup Power Threshold (default) = 2237
|
||||||
|
this->write16_(ATM90E26_REGISTER_PNOLTH, 0x0000); // Active No-Load Power Threshold
|
||||||
|
this->write16_(ATM90E26_REGISTER_QSTARTTH, 0x0AEC); // Reactive Startup Power Threshold (default) = 2796
|
||||||
|
this->write16_(ATM90E26_REGISTER_QNOLTH, 0x0000); // Reactive No-Load Power Threshold
|
||||||
|
|
||||||
|
// Compute Checksum for the registers we set above
|
||||||
|
// low byte = sum of all bytes
|
||||||
|
uint16_t cs =
|
||||||
|
((mmode >> 8) + (mmode & 0xFF) + (pl_const_ >> 24) + ((pl_const_ >> 16) & 0xFF) + ((pl_const_ >> 8) & 0xFF) +
|
||||||
|
(pl_const_ & 0xFF) + (gain_metering_ >> 8) + (gain_metering_ & 0xFF) + 0x08 + 0xBD + 0x0A + 0xEC) &
|
||||||
|
0xFF;
|
||||||
|
// high byte = XOR of all bytes
|
||||||
|
cs |= ((mmode >> 8) ^ (mmode & 0xFF) ^ (pl_const_ >> 24) ^ ((pl_const_ >> 16) & 0xFF) ^ ((pl_const_ >> 8) & 0xFF) ^
|
||||||
|
(pl_const_ & 0xFF) ^ (gain_metering_ >> 8) ^ (gain_metering_ & 0xFF) ^ 0x08 ^ 0xBD ^ 0x0A ^ 0xEC)
|
||||||
|
<< 8;
|
||||||
|
|
||||||
|
this->write16_(ATM90E26_REGISTER_CS1, cs);
|
||||||
|
ESP_LOGVV(TAG, "Set CS1 to: 0x%04X", cs);
|
||||||
|
|
||||||
|
// Set measurement calibration values
|
||||||
|
this->write16_(ATM90E26_REGISTER_ADJSTART, 0x5678); // Measurement calibration startup command, registers 31-3A
|
||||||
|
this->write16_(ATM90E26_REGISTER_UGAIN, gain_voltage_); // Voltage RMS gain
|
||||||
|
this->write16_(ATM90E26_REGISTER_IGAINL, gain_ct_); // L line current RMS gain
|
||||||
|
this->write16_(ATM90E26_REGISTER_IGAINN, 0x7530); // N Line Current RMS Gain
|
||||||
|
this->write16_(ATM90E26_REGISTER_UOFFSET, 0x0000); // Voltage Offset
|
||||||
|
this->write16_(ATM90E26_REGISTER_IOFFSETL, 0x0000); // L Line Current Offset
|
||||||
|
this->write16_(ATM90E26_REGISTER_IOFFSETN, 0x0000); // N Line Current Offse
|
||||||
|
this->write16_(ATM90E26_REGISTER_POFFSETL, 0x0000); // L Line Active Power Offset
|
||||||
|
this->write16_(ATM90E26_REGISTER_QOFFSETL, 0x0000); // L Line Reactive Power Offset
|
||||||
|
this->write16_(ATM90E26_REGISTER_POFFSETN, 0x0000); // N Line Active Power Offset
|
||||||
|
this->write16_(ATM90E26_REGISTER_QOFFSETN, 0x0000); // N Line Reactive Power Offset
|
||||||
|
|
||||||
|
// Compute Checksum for the registers we set above
|
||||||
|
cs = ((gain_voltage_ >> 8) + (gain_voltage_ & 0xFF) + (gain_ct_ >> 8) + (gain_ct_ & 0xFF) + 0x75 + 0x30) & 0xFF;
|
||||||
|
cs |= ((gain_voltage_ >> 8) ^ (gain_voltage_ & 0xFF) ^ (gain_ct_ >> 8) ^ (gain_ct_ & 0xFF) ^ 0x75 ^ 0x30) << 8;
|
||||||
|
this->write16_(ATM90E26_REGISTER_CS2, cs);
|
||||||
|
ESP_LOGVV(TAG, "Set CS2 to: 0x%04X", cs);
|
||||||
|
|
||||||
|
this->write16_(ATM90E26_REGISTER_CALSTART,
|
||||||
|
0x8765); // Checks correctness of 21-2B registers and starts normal metering if ok
|
||||||
|
this->write16_(ATM90E26_REGISTER_ADJSTART,
|
||||||
|
0x8765); // Checks correctness of 31-3A registers and starts normal measurement if ok
|
||||||
|
|
||||||
|
uint16_t sys_status = this->read16_(ATM90E26_REGISTER_SYSSTATUS);
|
||||||
|
if (sys_status & 0xC000) { // Checksum 1 Error
|
||||||
|
|
||||||
|
ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS1 was incorrect, expected: 0x%04X",
|
||||||
|
this->read16_(ATM90E26_REGISTER_CS1));
|
||||||
|
this->mark_failed();
|
||||||
|
}
|
||||||
|
if (sys_status & 0x3000) { // Checksum 2 Error
|
||||||
|
ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS2 was incorrect, expected: 0x%04X",
|
||||||
|
this->read16_(ATM90E26_REGISTER_CS2));
|
||||||
|
this->mark_failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATM90E26Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG("", "ATM90E26:");
|
||||||
|
LOG_PIN(" CS Pin: ", this->cs_);
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Communication with ATM90E26 failed!");
|
||||||
|
}
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
LOG_SENSOR(" ", "Voltage A", this->voltage_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Current A", this->current_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Power A", this->power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Reactive Power A", this->reactive_power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "PF A", this->power_factor_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Active Forward Energy A", this->forward_active_energy_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Active Reverse Energy A", this->reverse_active_energy_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
|
||||||
|
}
|
||||||
|
float ATM90E26Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
uint16_t ATM90E26Component::read16_(uint8_t a_register) {
|
||||||
|
uint8_t data[2];
|
||||||
|
uint16_t output;
|
||||||
|
|
||||||
|
this->enable();
|
||||||
|
delayMicroseconds(4);
|
||||||
|
this->write_byte(a_register | 0x80);
|
||||||
|
delayMicroseconds(4);
|
||||||
|
this->read_array(data, 2);
|
||||||
|
this->disable();
|
||||||
|
|
||||||
|
output = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
|
||||||
|
ESP_LOGVV(TAG, "read16_ 0x%04X output 0x%04X", a_register, output);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATM90E26Component::write16_(uint8_t a_register, uint16_t val) {
|
||||||
|
ESP_LOGVV(TAG, "write16_ 0x%04X val 0x%04X", a_register, val);
|
||||||
|
this->enable();
|
||||||
|
delayMicroseconds(4);
|
||||||
|
this->write_byte(a_register & 0x7F);
|
||||||
|
delayMicroseconds(4);
|
||||||
|
this->write_byte((val >> 8) & 0xFF);
|
||||||
|
this->write_byte(val & 0xFF);
|
||||||
|
this->disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E26Component::get_line_current_() {
|
||||||
|
uint16_t current = this->read16_(ATM90E26_REGISTER_IRMS);
|
||||||
|
return current / 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E26Component::get_line_voltage_() {
|
||||||
|
uint16_t voltage = this->read16_(ATM90E26_REGISTER_URMS);
|
||||||
|
return voltage / 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E26Component::get_active_power_() {
|
||||||
|
int16_t val = this->read16_(ATM90E26_REGISTER_PMEAN); // two's complement
|
||||||
|
return (float) val;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E26Component::get_reactive_power_() {
|
||||||
|
int16_t val = this->read16_(ATM90E26_REGISTER_QMEAN); // two's complement
|
||||||
|
return (float) val;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E26Component::get_power_factor_() {
|
||||||
|
uint16_t val = this->read16_(ATM90E26_REGISTER_POWERF); // signed
|
||||||
|
if (val & 0x8000) {
|
||||||
|
return -(val & 0x7FF) / 1000.0f;
|
||||||
|
} else {
|
||||||
|
return val / 1000.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E26Component::get_forward_active_energy_() {
|
||||||
|
uint16_t val = this->read16_(ATM90E26_REGISTER_APENERGY);
|
||||||
|
if ((UINT32_MAX - this->cumulative_forward_active_energy_) > val) {
|
||||||
|
this->cumulative_forward_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->cumulative_forward_active_energy_ = val;
|
||||||
|
}
|
||||||
|
// The register holds thenths of pulses, we want to output Wh
|
||||||
|
return (this->cumulative_forward_active_energy_ * 100.0f / meter_constant_);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E26Component::get_reverse_active_energy_() {
|
||||||
|
uint16_t val = this->read16_(ATM90E26_REGISTER_ANENERGY);
|
||||||
|
if (UINT32_MAX - this->cumulative_reverse_active_energy_ > val) {
|
||||||
|
this->cumulative_reverse_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->cumulative_reverse_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return (this->cumulative_reverse_active_energy_ * 100.0f / meter_constant_);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ATM90E26Component::get_frequency_() {
|
||||||
|
uint16_t freq = this->read16_(ATM90E26_REGISTER_FREQ);
|
||||||
|
return freq / 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace atm90e26
|
||||||
|
} // namespace esphome
|
72
esphome/components/atm90e26/atm90e26.h
Normal file
72
esphome/components/atm90e26/atm90e26.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/spi/spi.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace atm90e26 {
|
||||||
|
|
||||||
|
class ATM90E26Component : public PollingComponent,
|
||||||
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
|
||||||
|
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_200KHZ> {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void set_voltage_sensor(sensor::Sensor *obj) { this->voltage_sensor_ = obj; }
|
||||||
|
void set_current_sensor(sensor::Sensor *obj) { this->current_sensor_ = obj; }
|
||||||
|
void set_power_sensor(sensor::Sensor *obj) { this->power_sensor_ = obj; }
|
||||||
|
void set_reactive_power_sensor(sensor::Sensor *obj) { this->reactive_power_sensor_ = obj; }
|
||||||
|
void set_forward_active_energy_sensor(sensor::Sensor *obj) { this->forward_active_energy_sensor_ = obj; }
|
||||||
|
void set_reverse_active_energy_sensor(sensor::Sensor *obj) { this->reverse_active_energy_sensor_ = obj; }
|
||||||
|
void set_power_factor_sensor(sensor::Sensor *obj) { this->power_factor_sensor_ = obj; }
|
||||||
|
void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; }
|
||||||
|
void set_line_freq(int freq) { line_freq_ = freq; }
|
||||||
|
void set_meter_constant(float val) { meter_constant_ = val; }
|
||||||
|
void set_pl_const(uint32_t pl_const) { pl_const_ = pl_const; }
|
||||||
|
void set_gain_metering(uint16_t gain) { this->gain_metering_ = gain; }
|
||||||
|
void set_gain_voltage(uint16_t gain) { this->gain_voltage_ = gain; }
|
||||||
|
void set_gain_ct(uint16_t gain) { this->gain_ct_ = gain; }
|
||||||
|
void set_gain_pga(uint16_t gain) { gain_pga_ = gain; }
|
||||||
|
void set_n_line_gain(uint16_t gain) { n_line_gain_ = gain; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t read16_(uint8_t a_register);
|
||||||
|
int read32_(uint8_t addr_h, uint8_t addr_l);
|
||||||
|
void write16_(uint8_t a_register, uint16_t val);
|
||||||
|
|
||||||
|
float get_line_voltage_();
|
||||||
|
float get_line_current_();
|
||||||
|
float get_active_power_();
|
||||||
|
float get_reactive_power_();
|
||||||
|
float get_power_factor_();
|
||||||
|
float get_forward_active_energy_();
|
||||||
|
float get_reverse_active_energy_();
|
||||||
|
float get_frequency_();
|
||||||
|
float get_chip_temperature_();
|
||||||
|
|
||||||
|
sensor::Sensor *freq_sensor_{nullptr};
|
||||||
|
sensor::Sensor *voltage_sensor_{nullptr};
|
||||||
|
sensor::Sensor *current_sensor_{nullptr};
|
||||||
|
sensor::Sensor *power_sensor_{nullptr};
|
||||||
|
sensor::Sensor *reactive_power_sensor_{nullptr};
|
||||||
|
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||||
|
sensor::Sensor *forward_active_energy_sensor_{nullptr};
|
||||||
|
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
|
||||||
|
uint32_t cumulative_forward_active_energy_{0};
|
||||||
|
uint32_t cumulative_reverse_active_energy_{0};
|
||||||
|
uint16_t gain_metering_{7481};
|
||||||
|
uint16_t gain_voltage_{26400};
|
||||||
|
uint16_t gain_ct_{31251};
|
||||||
|
uint16_t gain_pga_{0x4};
|
||||||
|
uint16_t n_line_gain_{0x2};
|
||||||
|
int line_freq_{60};
|
||||||
|
float meter_constant_{3200.0f};
|
||||||
|
uint32_t pl_const_{1429876};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace atm90e26
|
||||||
|
} // namespace esphome
|
70
esphome/components/atm90e26/atm90e26_reg.h
Normal file
70
esphome/components/atm90e26/atm90e26_reg.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace atm90e26 {
|
||||||
|
|
||||||
|
/* Status and Special Register */
|
||||||
|
static const uint8_t ATM90E26_REGISTER_SOFTRESET = 0x00; // Software Reset
|
||||||
|
static const uint8_t ATM90E26_REGISTER_SYSSTATUS = 0x01; // System Status
|
||||||
|
static const uint8_t ATM90E26_REGISTER_FUNCEN = 0x02; // Function Enable
|
||||||
|
static const uint8_t ATM90E26_REGISTER_SAGTH = 0x03; // Voltage Sag Threshold
|
||||||
|
static const uint8_t ATM90E26_REGISTER_SMALLPMOD = 0x04; // Small-Power Mode
|
||||||
|
static const uint8_t ATM90E26_REGISTER_LASTDATA = 0x06; // Last Read/Write SPI/UART Value
|
||||||
|
|
||||||
|
/* Metering Calibration and Configuration Register */
|
||||||
|
static const uint8_t ATM90E26_REGISTER_LSB = 0x08; // RMS/Power 16-bit LSB
|
||||||
|
static const uint8_t ATM90E26_REGISTER_CALSTART = 0x20; // Calibration Start Command
|
||||||
|
static const uint8_t ATM90E26_REGISTER_PLCONSTH = 0x21; // High Word of PL_Constant
|
||||||
|
static const uint8_t ATM90E26_REGISTER_PLCONSTL = 0x22; // Low Word of PL_Constant
|
||||||
|
static const uint8_t ATM90E26_REGISTER_LGAIN = 0x23; // L Line Calibration Gain
|
||||||
|
static const uint8_t ATM90E26_REGISTER_LPHI = 0x24; // L Line Calibration Angle
|
||||||
|
static const uint8_t ATM90E26_REGISTER_NGAIN = 0x25; // N Line Calibration Gain
|
||||||
|
static const uint8_t ATM90E26_REGISTER_NPHI = 0x26; // N Line Calibration Angle
|
||||||
|
static const uint8_t ATM90E26_REGISTER_PSTARTTH = 0x27; // Active Startup Power Threshold
|
||||||
|
static const uint8_t ATM90E26_REGISTER_PNOLTH = 0x28; // Active No-Load Power Threshold
|
||||||
|
static const uint8_t ATM90E26_REGISTER_QSTARTTH = 0x29; // Reactive Startup Power Threshold
|
||||||
|
static const uint8_t ATM90E26_REGISTER_QNOLTH = 0x2A; // Reactive No-Load Power Threshold
|
||||||
|
static const uint8_t ATM90E26_REGISTER_MMODE = 0x2B; // Metering Mode Configuration
|
||||||
|
static const uint8_t ATM90E26_REGISTER_CS1 = 0x2C; // Checksum 1
|
||||||
|
|
||||||
|
/* Measurement Calibration Register */
|
||||||
|
static const uint8_t ATM90E26_REGISTER_ADJSTART = 0x30; // Measurement Calibration Start Command
|
||||||
|
static const uint8_t ATM90E26_REGISTER_UGAIN = 0x31; // Voltage RMS Gain
|
||||||
|
static const uint8_t ATM90E26_REGISTER_IGAINL = 0x32; // L Line Current RMS Gain
|
||||||
|
static const uint8_t ATM90E26_REGISTER_IGAINN = 0x33; // N Line Current RMS Gain
|
||||||
|
static const uint8_t ATM90E26_REGISTER_UOFFSET = 0x34; // Voltage Offset
|
||||||
|
static const uint8_t ATM90E26_REGISTER_IOFFSETL = 0x35; // L Line Current Offset
|
||||||
|
static const uint8_t ATM90E26_REGISTER_IOFFSETN = 0x36; // N Line Current Offse
|
||||||
|
static const uint8_t ATM90E26_REGISTER_POFFSETL = 0x37; // L Line Active Power Offset
|
||||||
|
static const uint8_t ATM90E26_REGISTER_QOFFSETL = 0x38; // L Line Reactive Power Offset
|
||||||
|
static const uint8_t ATM90E26_REGISTER_POFFSETN = 0x39; // N Line Active Power Offset
|
||||||
|
static const uint8_t ATM90E26_REGISTER_QOFFSETN = 0x3A; // N Line Reactive Power Offset
|
||||||
|
static const uint8_t ATM90E26_REGISTER_CS2 = 0x3B; // Checksum 2
|
||||||
|
|
||||||
|
/* Energy Register */
|
||||||
|
static const uint8_t ATM90E26_REGISTER_APENERGY = 0x40; // Forward Active Energy
|
||||||
|
static const uint8_t ATM90E26_REGISTER_ANENERGY = 0x41; // Reverse Active Energy
|
||||||
|
static const uint8_t ATM90E26_REGISTER_ATENERGY = 0x42; // Absolute Active Energy
|
||||||
|
static const uint8_t ATM90E26_REGISTER_RPENERGY = 0x43; // Forward (Inductive) Reactive Energy
|
||||||
|
static const uint8_t ATM90E26_REGISTER_RNENERG = 0x44; // Reverse (Capacitive) Reactive Energy
|
||||||
|
static const uint8_t ATM90E26_REGISTER_RTENERGY = 0x45; // Absolute Reactive Energy
|
||||||
|
static const uint8_t ATM90E26_REGISTER_ENSTATUS = 0x46; // Metering Status
|
||||||
|
|
||||||
|
/* Measurement Register */
|
||||||
|
static const uint8_t ATM90E26_REGISTER_IRMS = 0x48; // L Line Current RMS
|
||||||
|
static const uint8_t ATM90E26_REGISTER_URMS = 0x49; // Voltage RMS
|
||||||
|
static const uint8_t ATM90E26_REGISTER_PMEAN = 0x4A; // L Line Mean Active Power
|
||||||
|
static const uint8_t ATM90E26_REGISTER_QMEAN = 0x4B; // L Line Mean Reactive Power
|
||||||
|
static const uint8_t ATM90E26_REGISTER_FREQ = 0x4C; // Voltage Frequency
|
||||||
|
static const uint8_t ATM90E26_REGISTER_POWERF = 0x4D; // L Line Power Factor
|
||||||
|
static const uint8_t ATM90E26_REGISTER_PANGLE = 0x4E; // Phase Angle between Voltage and L Line Current
|
||||||
|
static const uint8_t ATM90E26_REGISTER_SMEAN = 0x4F; // L Line Mean Apparent Power
|
||||||
|
static const uint8_t ATM90E26_REGISTER_IRMS2 = 0x68; // N Line Current rms
|
||||||
|
static const uint8_t ATM90E26_REGISTER_PMEAN2 = 0x6A; // N Line Mean Active Power
|
||||||
|
static const uint8_t ATM90E26_REGISTER_QMEAN2 = 0x6B; // N Line Mean Reactive Power
|
||||||
|
static const uint8_t ATM90E26_REGISTER_POWERF2 = 0x6D; // N Line Power Factor
|
||||||
|
static const uint8_t ATM90E26_REGISTER_PANGLE2 = 0x6E; // Phase Angle between Voltage and N Line Current
|
||||||
|
static const uint8_t ATM90E26_REGISTER_SMEAN2 = 0x6F; // N Line Mean Apparent Power
|
||||||
|
|
||||||
|
} // namespace atm90e26
|
||||||
|
} // namespace esphome
|
157
esphome/components/atm90e26/sensor.py
Normal file
157
esphome/components/atm90e26/sensor.py
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, spi
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_REACTIVE_POWER,
|
||||||
|
CONF_VOLTAGE,
|
||||||
|
CONF_CURRENT,
|
||||||
|
CONF_POWER,
|
||||||
|
CONF_POWER_FACTOR,
|
||||||
|
CONF_FREQUENCY,
|
||||||
|
CONF_FORWARD_ACTIVE_ENERGY,
|
||||||
|
CONF_REVERSE_ACTIVE_ENERGY,
|
||||||
|
DEVICE_CLASS_CURRENT,
|
||||||
|
DEVICE_CLASS_ENERGY,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
ICON_LIGHTBULB,
|
||||||
|
ICON_CURRENT_AC,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
UNIT_HERTZ,
|
||||||
|
UNIT_VOLT,
|
||||||
|
UNIT_AMPERE,
|
||||||
|
UNIT_WATT,
|
||||||
|
UNIT_VOLT_AMPS_REACTIVE,
|
||||||
|
UNIT_WATT_HOURS,
|
||||||
|
)
|
||||||
|
|
||||||
|
CONF_LINE_FREQUENCY = "line_frequency"
|
||||||
|
CONF_METER_CONSTANT = "meter_constant"
|
||||||
|
CONF_PL_CONST = "pl_const"
|
||||||
|
CONF_GAIN_PGA = "gain_pga"
|
||||||
|
CONF_GAIN_METERING = "gain_metering"
|
||||||
|
CONF_GAIN_VOLTAGE = "gain_voltage"
|
||||||
|
CONF_GAIN_CT = "gain_ct"
|
||||||
|
LINE_FREQS = {
|
||||||
|
"50HZ": 50,
|
||||||
|
"60HZ": 60,
|
||||||
|
}
|
||||||
|
PGA_GAINS = {
|
||||||
|
"1X": 0x4,
|
||||||
|
"4X": 0x0,
|
||||||
|
"8X": 0x1,
|
||||||
|
"16X": 0x2,
|
||||||
|
"24X": 0x3,
|
||||||
|
}
|
||||||
|
|
||||||
|
atm90e26_ns = cg.esphome_ns.namespace("atm90e26")
|
||||||
|
ATM90E26Component = atm90e26_ns.class_(
|
||||||
|
"ATM90E26Component", cg.PollingComponent, spi.SPIDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(ATM90E26Component),
|
||||||
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_AMPERE,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_CURRENT,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_POWER,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
|
||||||
|
icon=ICON_LIGHTBULB,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT_HOURS,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_WATT_HOURS,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_ENERGY,
|
||||||
|
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_HERTZ,
|
||||||
|
icon=ICON_CURRENT_AC,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
|
||||||
|
cv.Required(CONF_METER_CONSTANT): cv.positive_float,
|
||||||
|
cv.Optional(CONF_PL_CONST, default=1429876): cv.uint32_t,
|
||||||
|
cv.Optional(CONF_GAIN_METERING, default=7481): cv.uint16_t,
|
||||||
|
cv.Optional(CONF_GAIN_VOLTAGE, default=26400): cv.int_range(
|
||||||
|
min=0, max=32767
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_GAIN_CT, default=31251): cv.uint16_t,
|
||||||
|
cv.Optional(CONF_GAIN_PGA, default="1X"): cv.enum(PGA_GAINS, upper=True),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(spi.spi_device_schema())
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await spi.register_spi_device(var, config)
|
||||||
|
|
||||||
|
if voltage_config := config.get(CONF_VOLTAGE):
|
||||||
|
sens = await sensor.new_sensor(voltage_config)
|
||||||
|
cg.add(var.set_voltage_sensor(sens))
|
||||||
|
if current_config := config.get(CONF_CURRENT):
|
||||||
|
sens = await sensor.new_sensor(current_config)
|
||||||
|
cg.add(var.set_current_sensor(sens))
|
||||||
|
if power_config := config.get(CONF_POWER):
|
||||||
|
sens = await sensor.new_sensor(power_config)
|
||||||
|
cg.add(var.set_power_sensor(sens))
|
||||||
|
if reactive_power_config := config.get(CONF_REACTIVE_POWER):
|
||||||
|
sens = await sensor.new_sensor(reactive_power_config)
|
||||||
|
cg.add(var.set_reactive_power_sensor(sens))
|
||||||
|
if power_factor_config := config.get(CONF_POWER_FACTOR):
|
||||||
|
sens = await sensor.new_sensor(power_factor_config)
|
||||||
|
cg.add(var.set_power_factor_sensor(sens))
|
||||||
|
if forward_active_energy_config := config.get(CONF_FORWARD_ACTIVE_ENERGY):
|
||||||
|
sens = await sensor.new_sensor(forward_active_energy_config)
|
||||||
|
cg.add(var.set_forward_active_energy_sensor(sens))
|
||||||
|
if reverse_active_energy_config := config.get(CONF_REVERSE_ACTIVE_ENERGY):
|
||||||
|
sens = await sensor.new_sensor(reverse_active_energy_config)
|
||||||
|
cg.add(var.set_reverse_active_energy_sensor(sens))
|
||||||
|
if frequency_config := config.get(CONF_FREQUENCY):
|
||||||
|
sens = await sensor.new_sensor(frequency_config)
|
||||||
|
cg.add(var.set_freq_sensor(sens))
|
||||||
|
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
|
||||||
|
cg.add(var.set_meter_constant(config[CONF_METER_CONSTANT]))
|
||||||
|
cg.add(var.set_pl_const(config[CONF_PL_CONST]))
|
||||||
|
cg.add(var.set_gain_metering(config[CONF_GAIN_METERING]))
|
||||||
|
cg.add(var.set_gain_voltage(config[CONF_GAIN_VOLTAGE]))
|
||||||
|
cg.add(var.set_gain_ct(config[CONF_GAIN_CT]))
|
||||||
|
cg.add(var.set_gain_pga(config[CONF_GAIN_PGA]))
|
|
@ -6,6 +6,9 @@ from esphome.const import (
|
||||||
CONF_REACTIVE_POWER,
|
CONF_REACTIVE_POWER,
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
CONF_CURRENT,
|
CONF_CURRENT,
|
||||||
|
CONF_PHASE_A,
|
||||||
|
CONF_PHASE_B,
|
||||||
|
CONF_PHASE_C,
|
||||||
CONF_POWER,
|
CONF_POWER,
|
||||||
CONF_POWER_FACTOR,
|
CONF_POWER_FACTOR,
|
||||||
CONF_FREQUENCY,
|
CONF_FREQUENCY,
|
||||||
|
@ -31,10 +34,6 @@ from esphome.const import (
|
||||||
UNIT_WATT_HOURS,
|
UNIT_WATT_HOURS,
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_PHASE_A = "phase_a"
|
|
||||||
CONF_PHASE_B = "phase_b"
|
|
||||||
CONF_PHASE_C = "phase_c"
|
|
||||||
|
|
||||||
CONF_LINE_FREQUENCY = "line_frequency"
|
CONF_LINE_FREQUENCY = "line_frequency"
|
||||||
CONF_CHIP_TEMPERATURE = "chip_temperature"
|
CONF_CHIP_TEMPERATURE = "chip_temperature"
|
||||||
CONF_GAIN_PGA = "gain_pga"
|
CONF_GAIN_PGA = "gain_pga"
|
||||||
|
@ -151,33 +150,35 @@ async def to_code(config):
|
||||||
conf = config[phase]
|
conf = config[phase]
|
||||||
cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE]))
|
cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE]))
|
||||||
cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT]))
|
cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT]))
|
||||||
if CONF_VOLTAGE in conf:
|
if voltage_config := conf.get(CONF_VOLTAGE):
|
||||||
sens = await sensor.new_sensor(conf[CONF_VOLTAGE])
|
sens = await sensor.new_sensor(voltage_config)
|
||||||
cg.add(var.set_voltage_sensor(i, sens))
|
cg.add(var.set_voltage_sensor(i, sens))
|
||||||
if CONF_CURRENT in conf:
|
if current_config := conf.get(CONF_CURRENT):
|
||||||
sens = await sensor.new_sensor(conf[CONF_CURRENT])
|
sens = await sensor.new_sensor(current_config)
|
||||||
cg.add(var.set_current_sensor(i, sens))
|
cg.add(var.set_current_sensor(i, sens))
|
||||||
if CONF_POWER in conf:
|
if power_config := conf.get(CONF_POWER):
|
||||||
sens = await sensor.new_sensor(conf[CONF_POWER])
|
sens = await sensor.new_sensor(power_config)
|
||||||
cg.add(var.set_power_sensor(i, sens))
|
cg.add(var.set_power_sensor(i, sens))
|
||||||
if CONF_REACTIVE_POWER in conf:
|
if reactive_power_config := conf.get(CONF_REACTIVE_POWER):
|
||||||
sens = await sensor.new_sensor(conf[CONF_REACTIVE_POWER])
|
sens = await sensor.new_sensor(reactive_power_config)
|
||||||
cg.add(var.set_reactive_power_sensor(i, sens))
|
cg.add(var.set_reactive_power_sensor(i, sens))
|
||||||
if CONF_POWER_FACTOR in conf:
|
if power_factor_config := conf.get(CONF_POWER_FACTOR):
|
||||||
sens = await sensor.new_sensor(conf[CONF_POWER_FACTOR])
|
sens = await sensor.new_sensor(power_factor_config)
|
||||||
cg.add(var.set_power_factor_sensor(i, sens))
|
cg.add(var.set_power_factor_sensor(i, sens))
|
||||||
if CONF_FORWARD_ACTIVE_ENERGY in conf:
|
if forward_active_energy_config := conf.get(CONF_FORWARD_ACTIVE_ENERGY):
|
||||||
sens = await sensor.new_sensor(conf[CONF_FORWARD_ACTIVE_ENERGY])
|
sens = await sensor.new_sensor(forward_active_energy_config)
|
||||||
cg.add(var.set_forward_active_energy_sensor(i, sens))
|
cg.add(var.set_forward_active_energy_sensor(i, sens))
|
||||||
if CONF_REVERSE_ACTIVE_ENERGY in conf:
|
if reverse_active_energy_config := conf.get(CONF_REVERSE_ACTIVE_ENERGY):
|
||||||
sens = await sensor.new_sensor(conf[CONF_REVERSE_ACTIVE_ENERGY])
|
sens = await sensor.new_sensor(reverse_active_energy_config)
|
||||||
cg.add(var.set_reverse_active_energy_sensor(i, sens))
|
cg.add(var.set_reverse_active_energy_sensor(i, sens))
|
||||||
if CONF_FREQUENCY in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_FREQUENCY])
|
if frequency_config := config.get(CONF_FREQUENCY):
|
||||||
|
sens = await sensor.new_sensor(frequency_config)
|
||||||
cg.add(var.set_freq_sensor(sens))
|
cg.add(var.set_freq_sensor(sens))
|
||||||
if CONF_CHIP_TEMPERATURE in config:
|
if chip_temperature_config := config.get(CONF_CHIP_TEMPERATURE):
|
||||||
sens = await sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
|
sens = await sensor.new_sensor(chip_temperature_config)
|
||||||
cg.add(var.set_chip_temperature_sensor(sens))
|
cg.add(var.set_chip_temperature_sensor(sens))
|
||||||
|
|
||||||
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
|
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
|
||||||
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
|
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
|
||||||
cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
|
cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
|
||||||
|
|
|
@ -87,6 +87,6 @@ async def to_code(config):
|
||||||
(CONF_MOISTURE, var.set_soil_moisture),
|
(CONF_MOISTURE, var.set_soil_moisture),
|
||||||
(CONF_ILLUMINANCE, var.set_illuminance),
|
(CONF_ILLUMINANCE, var.set_illuminance),
|
||||||
]:
|
]:
|
||||||
if config_key in config:
|
if sensor_config := config.get(config_key):
|
||||||
sens = await sensor.new_sensor(config[config_key])
|
sens = await sensor.new_sensor(sensor_config)
|
||||||
cg.add(setter(sens))
|
cg.add(setter(sens))
|
||||||
|
|
|
@ -57,19 +57,18 @@ async def to_code(config):
|
||||||
var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]
|
var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]
|
||||||
)
|
)
|
||||||
|
|
||||||
if CONF_COOL_ACTION in config:
|
if cool_action_config := config.get(CONF_COOL_ACTION):
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
var.get_cool_trigger(), [], config[CONF_COOL_ACTION]
|
var.get_cool_trigger(), [], cool_action_config
|
||||||
)
|
)
|
||||||
cg.add(var.set_supports_cool(True))
|
cg.add(var.set_supports_cool(True))
|
||||||
if CONF_HEAT_ACTION in config:
|
if heat_action_config := config.get(CONF_HEAT_ACTION):
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
var.get_heat_trigger(), [], config[CONF_HEAT_ACTION]
|
var.get_heat_trigger(), [], heat_action_config
|
||||||
)
|
)
|
||||||
cg.add(var.set_supports_heat(True))
|
cg.add(var.set_supports_heat(True))
|
||||||
|
|
||||||
if CONF_AWAY_CONFIG in config:
|
if away := config.get(CONF_AWAY_CONFIG):
|
||||||
away = config[CONF_AWAY_CONFIG]
|
|
||||||
away_config = BangBangClimateTargetTempConfig(
|
away_config = BangBangClimateTargetTempConfig(
|
||||||
away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
|
away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
|
||||||
away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH],
|
away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH],
|
||||||
|
|
|
@ -45,8 +45,8 @@ async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await ble_client.register_ble_node(var, config)
|
await ble_client.register_ble_node(var, config)
|
||||||
if CONF_TIME_ID in config:
|
if time_id := config.get(CONF_TIME_ID):
|
||||||
time_ = await cg.get_variable(config[CONF_TIME_ID])
|
time_ = await cg.get_variable(time_id)
|
||||||
cg.add(var.set_time_id(time_))
|
cg.add(var.set_time_id(time_))
|
||||||
if CONF_RECEIVE_TIMEOUT in config:
|
if (receive_timeout := config.get(CONF_RECEIVE_TIMEOUT)) is not None:
|
||||||
cg.add(var.set_status_timeout(config[CONF_RECEIVE_TIMEOUT]))
|
cg.add(var.set_status_timeout(receive_timeout))
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "bedjet_hub.h"
|
#include "bedjet_hub.h"
|
||||||
#include "bedjet_child.h"
|
#include "bedjet_child.h"
|
||||||
#include "bedjet_const.h"
|
#include "bedjet_const.h"
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bedjet {
|
namespace bedjet {
|
||||||
|
@ -373,7 +374,7 @@ void BedJetHub::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
|
||||||
if (this->last_notify_ == 0 || delta > MIN_NOTIFY_THROTTLE || this->force_refresh_) {
|
if (this->last_notify_ == 0 || delta > MIN_NOTIFY_THROTTLE || this->force_refresh_) {
|
||||||
// Set reentrant flag to prevent processing multiple packets.
|
// Set reentrant flag to prevent processing multiple packets.
|
||||||
this->processing_ = true;
|
this->processing_ = true;
|
||||||
ESP_LOGVV(TAG, "[%s] Decoding packet: last=%d, delta=%d, force=%s", this->get_name().c_str(),
|
ESP_LOGVV(TAG, "[%s] Decoding packet: last=%" PRId32 ", delta=%" PRId32 ", force=%s", this->get_name().c_str(),
|
||||||
this->last_notify_, delta, this->force_refresh_ ? "y" : "n");
|
this->last_notify_, delta, this->force_refresh_ ? "y" : "n");
|
||||||
bool needs_extra = this->codec_->decode_notify(param->notify.value, param->notify.value_len);
|
bool needs_extra = this->codec_->decode_notify(param->notify.value, param->notify.value_len);
|
||||||
|
|
||||||
|
@ -523,11 +524,11 @@ void BedJetHub::dispatch_status_() {
|
||||||
|
|
||||||
ESP_LOGI(TAG, "[%s] Still waiting for first GATT notify event.", this->get_name().c_str());
|
ESP_LOGI(TAG, "[%s] Still waiting for first GATT notify event.", this->get_name().c_str());
|
||||||
} else if (diff > NOTIFY_WARN_THRESHOLD) {
|
} else if (diff > NOTIFY_WARN_THRESHOLD) {
|
||||||
ESP_LOGW(TAG, "[%s] Last GATT notify was %d seconds ago.", this->get_name().c_str(), diff / 1000);
|
ESP_LOGW(TAG, "[%s] Last GATT notify was %" PRId32 " seconds ago.", this->get_name().c_str(), diff / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) {
|
if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) {
|
||||||
ESP_LOGW(TAG, "[%s] Timed out after %d sec. Retrying...", this->get_name().c_str(), this->timeout_);
|
ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying...", this->get_name().c_str(), this->timeout_);
|
||||||
// set_enabled(false) will only close the connection if state != IDLE.
|
// set_enabled(false) will only close the connection if state != IDLE.
|
||||||
this->parent()->set_state(espbt::ClientState::CONNECTING);
|
this->parent()->set_state(espbt::ClientState::CONNECTING);
|
||||||
this->parent()->set_enabled(false);
|
this->parent()->set_enabled(false);
|
||||||
|
|
|
@ -29,10 +29,10 @@ async def to_code(config):
|
||||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||||
cg.add(var.set_output(output_))
|
cg.add(var.set_output(output_))
|
||||||
|
|
||||||
if CONF_OSCILLATION_OUTPUT in config:
|
if oscillation_output_id := config.get(CONF_OSCILLATION_OUTPUT):
|
||||||
oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
|
oscillation_output = await cg.get_variable(oscillation_output_id)
|
||||||
cg.add(var.set_oscillating(oscillation_output))
|
cg.add(var.set_oscillating(oscillation_output))
|
||||||
|
|
||||||
if CONF_DIRECTION_OUTPUT in config:
|
if direction_output_id := config.get(CONF_DIRECTION_OUTPUT):
|
||||||
direction_output = await cg.get_variable(config[CONF_DIRECTION_OUTPUT])
|
direction_output = await cg.get_variable(direction_output_id)
|
||||||
cg.add(var.set_direction(direction_output))
|
cg.add(var.set_direction(direction_output))
|
||||||
|
|
|
@ -95,6 +95,14 @@ DEVICE_CLASSES = [
|
||||||
|
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
|
CONF_TIME_OFF = "time_off"
|
||||||
|
CONF_TIME_ON = "time_on"
|
||||||
|
|
||||||
|
DEFAULT_DELAY = "1s"
|
||||||
|
DEFAULT_TIME_OFF = "100ms"
|
||||||
|
DEFAULT_TIME_ON = "900ms"
|
||||||
|
|
||||||
|
|
||||||
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
|
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
|
||||||
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
|
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
|
||||||
BinarySensorInitiallyOff = binary_sensor_ns.class_(
|
BinarySensorInitiallyOff = binary_sensor_ns.class_(
|
||||||
|
@ -138,47 +146,75 @@ FILTER_REGISTRY = Registry()
|
||||||
validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
|
validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register("invert", InvertFilter, {})
|
def register_filter(name, filter_type, schema):
|
||||||
|
return FILTER_REGISTRY.register(name, filter_type, schema)
|
||||||
|
|
||||||
|
|
||||||
|
@register_filter("invert", InvertFilter, {})
|
||||||
async def invert_filter_to_code(config, filter_id):
|
async def invert_filter_to_code(config, filter_id):
|
||||||
return cg.new_Pvariable(filter_id)
|
return cg.new_Pvariable(filter_id)
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register(
|
@register_filter(
|
||||||
"delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds
|
"delayed_on_off",
|
||||||
|
DelayedOnOffFilter,
|
||||||
|
cv.Any(
|
||||||
|
cv.templatable(cv.positive_time_period_milliseconds),
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_TIME_ON): cv.templatable(
|
||||||
|
cv.positive_time_period_milliseconds
|
||||||
|
),
|
||||||
|
cv.Required(CONF_TIME_OFF): cv.templatable(
|
||||||
|
cv.positive_time_period_milliseconds
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
msg="'delayed_on_off' filter requires either a delay time to be used for both "
|
||||||
|
"turn-on and turn-off delays, or two parameters 'time_on' and 'time_off' if "
|
||||||
|
"different delay times are required.",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
async def delayed_on_off_filter_to_code(config, filter_id):
|
async def delayed_on_off_filter_to_code(config, filter_id):
|
||||||
var = cg.new_Pvariable(filter_id, config)
|
var = cg.new_Pvariable(filter_id)
|
||||||
await cg.register_component(var, {})
|
await cg.register_component(var, {})
|
||||||
|
if isinstance(config, dict):
|
||||||
|
template_ = await cg.templatable(config[CONF_TIME_ON], [], cg.uint32)
|
||||||
|
cg.add(var.set_on_delay(template_))
|
||||||
|
template_ = await cg.templatable(config[CONF_TIME_OFF], [], cg.uint32)
|
||||||
|
cg.add(var.set_off_delay(template_))
|
||||||
|
else:
|
||||||
|
template_ = await cg.templatable(config, [], cg.uint32)
|
||||||
|
cg.add(var.set_on_delay(template_))
|
||||||
|
cg.add(var.set_off_delay(template_))
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register(
|
@register_filter(
|
||||||
"delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds
|
"delayed_on", DelayedOnFilter, cv.templatable(cv.positive_time_period_milliseconds)
|
||||||
)
|
)
|
||||||
async def delayed_on_filter_to_code(config, filter_id):
|
async def delayed_on_filter_to_code(config, filter_id):
|
||||||
var = cg.new_Pvariable(filter_id, config)
|
var = cg.new_Pvariable(filter_id)
|
||||||
await cg.register_component(var, {})
|
await cg.register_component(var, {})
|
||||||
|
template_ = await cg.templatable(config, [], cg.uint32)
|
||||||
|
cg.add(var.set_delay(template_))
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register(
|
@register_filter(
|
||||||
"delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds
|
"delayed_off",
|
||||||
|
DelayedOffFilter,
|
||||||
|
cv.templatable(cv.positive_time_period_milliseconds),
|
||||||
)
|
)
|
||||||
async def delayed_off_filter_to_code(config, filter_id):
|
async def delayed_off_filter_to_code(config, filter_id):
|
||||||
var = cg.new_Pvariable(filter_id, config)
|
var = cg.new_Pvariable(filter_id)
|
||||||
await cg.register_component(var, {})
|
await cg.register_component(var, {})
|
||||||
|
template_ = await cg.templatable(config, [], cg.uint32)
|
||||||
|
cg.add(var.set_delay(template_))
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
CONF_TIME_OFF = "time_off"
|
@register_filter(
|
||||||
CONF_TIME_ON = "time_on"
|
|
||||||
|
|
||||||
DEFAULT_DELAY = "1s"
|
|
||||||
DEFAULT_TIME_OFF = "100ms"
|
|
||||||
DEFAULT_TIME_ON = "900ms"
|
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register(
|
|
||||||
"autorepeat",
|
"autorepeat",
|
||||||
AutorepeatFilter,
|
AutorepeatFilter,
|
||||||
cv.All(
|
cv.All(
|
||||||
|
@ -215,7 +251,7 @@ async def autorepeat_filter_to_code(config, filter_id):
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda)
|
@register_filter("lambda", LambdaFilter, cv.returning_lambda)
|
||||||
async def lambda_filter_to_code(config, filter_id):
|
async def lambda_filter_to_code(config, filter_id):
|
||||||
lambda_ = await cg.process_lambda(
|
lambda_ = await cg.process_lambda(
|
||||||
config, [(bool, "x")], return_type=cg.optional.template(bool)
|
config, [(bool, "x")], return_type=cg.optional.template(bool)
|
||||||
|
@ -323,6 +359,18 @@ def validate_multi_click_timing(value):
|
||||||
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_click_timing(value):
|
||||||
|
for v in value:
|
||||||
|
min_length = v.get(CONF_MIN_LENGTH)
|
||||||
|
max_length = v.get(CONF_MAX_LENGTH)
|
||||||
|
if max_length < min_length:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"Max length ({max_length}) must be larger than min length ({min_length})."
|
||||||
|
)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(BinarySensor),
|
cv.GenerateID(): cv.declare_id(BinarySensor),
|
||||||
|
@ -342,27 +390,33 @@ BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).ex
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_CLICK): automation.validate_automation(
|
cv.Optional(CONF_ON_CLICK): cv.All(
|
||||||
{
|
automation.validate_automation(
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
|
{
|
||||||
cv.Optional(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
|
||||||
CONF_MIN_LENGTH, default="50ms"
|
cv.Optional(
|
||||||
): cv.positive_time_period_milliseconds,
|
CONF_MIN_LENGTH, default="50ms"
|
||||||
cv.Optional(
|
): cv.positive_time_period_milliseconds,
|
||||||
CONF_MAX_LENGTH, default="350ms"
|
cv.Optional(
|
||||||
): cv.positive_time_period_milliseconds,
|
CONF_MAX_LENGTH, default="350ms"
|
||||||
}
|
): cv.positive_time_period_milliseconds,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
validate_click_timing,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation(
|
cv.Optional(CONF_ON_DOUBLE_CLICK): cv.All(
|
||||||
{
|
automation.validate_automation(
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
|
{
|
||||||
cv.Optional(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
|
||||||
CONF_MIN_LENGTH, default="50ms"
|
cv.Optional(
|
||||||
): cv.positive_time_period_milliseconds,
|
CONF_MIN_LENGTH, default="50ms"
|
||||||
cv.Optional(
|
): cv.positive_time_period_milliseconds,
|
||||||
CONF_MAX_LENGTH, default="350ms"
|
cv.Optional(
|
||||||
): cv.positive_time_period_milliseconds,
|
CONF_MAX_LENGTH, default="350ms"
|
||||||
}
|
): cv.positive_time_period_milliseconds,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
validate_click_timing,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
|
cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
|
||||||
{
|
{
|
||||||
|
@ -413,14 +467,14 @@ def binary_sensor_schema(
|
||||||
async def setup_binary_sensor_core_(var, config):
|
async def setup_binary_sensor_core_(var, config):
|
||||||
await setup_entity(var, config)
|
await setup_entity(var, config)
|
||||||
|
|
||||||
if CONF_DEVICE_CLASS in config:
|
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
|
||||||
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
|
cg.add(var.set_device_class(device_class))
|
||||||
if CONF_PUBLISH_INITIAL_STATE in config:
|
if publish_initial_state := config.get(CONF_PUBLISH_INITIAL_STATE):
|
||||||
cg.add(var.set_publish_initial_state(config[CONF_PUBLISH_INITIAL_STATE]))
|
cg.add(var.set_publish_initial_state(publish_initial_state))
|
||||||
if CONF_INVERTED in config:
|
if inverted := config.get(CONF_INVERTED):
|
||||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
cg.add(var.set_inverted(inverted))
|
||||||
if CONF_FILTERS in config:
|
if filters_config := config.get(CONF_FILTERS):
|
||||||
filters = await cg.build_registry_list(FILTER_REGISTRY, config[CONF_FILTERS])
|
filters = await cg.build_registry_list(FILTER_REGISTRY, filters_config)
|
||||||
cg.add(var.add_filters(filters))
|
cg.add(var.add_filters(filters))
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_PRESS, []):
|
for conf in config.get(CONF_ON_PRESS, []):
|
||||||
|
@ -464,8 +518,8 @@ async def setup_binary_sensor_core_(var, config):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [(bool, "x")], conf)
|
await automation.build_automation(trigger, [(bool, "x")], conf)
|
||||||
|
|
||||||
if CONF_MQTT_ID in config:
|
if mqtt_id := config.get(CONF_MQTT_ID):
|
||||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
|
mqtt_ = cg.new_Pvariable(mqtt_id, var)
|
||||||
await mqtt.register_mqtt_component(mqtt_, config)
|
await mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,22 +26,20 @@ void Filter::input(bool value, bool is_initial) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelayedOnOffFilter::DelayedOnOffFilter(uint32_t delay) : delay_(delay) {}
|
|
||||||
optional<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) {
|
optional<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) {
|
||||||
if (value) {
|
if (value) {
|
||||||
this->set_timeout("ON_OFF", this->delay_, [this, is_initial]() { this->output(true, is_initial); });
|
this->set_timeout("ON_OFF", this->on_delay_.value(), [this, is_initial]() { this->output(true, is_initial); });
|
||||||
} else {
|
} else {
|
||||||
this->set_timeout("ON_OFF", this->delay_, [this, is_initial]() { this->output(false, is_initial); });
|
this->set_timeout("ON_OFF", this->off_delay_.value(), [this, is_initial]() { this->output(false, is_initial); });
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
DelayedOnFilter::DelayedOnFilter(uint32_t delay) : delay_(delay) {}
|
|
||||||
optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
||||||
if (value) {
|
if (value) {
|
||||||
this->set_timeout("ON", this->delay_, [this, is_initial]() { this->output(true, is_initial); });
|
this->set_timeout("ON", this->delay_.value(), [this, is_initial]() { this->output(true, is_initial); });
|
||||||
return {};
|
return {};
|
||||||
} else {
|
} else {
|
||||||
this->cancel_timeout("ON");
|
this->cancel_timeout("ON");
|
||||||
|
@ -51,10 +49,9 @@ optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
||||||
|
|
||||||
float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
DelayedOffFilter::DelayedOffFilter(uint32_t delay) : delay_(delay) {}
|
|
||||||
optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
|
optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
this->set_timeout("OFF", this->delay_, [this, is_initial]() { this->output(false, is_initial); });
|
this->set_timeout("OFF", this->delay_.value(), [this, is_initial]() { this->output(false, is_initial); });
|
||||||
return {};
|
return {};
|
||||||
} else {
|
} else {
|
||||||
this->cancel_timeout("OFF");
|
this->cancel_timeout("OFF");
|
||||||
|
@ -114,15 +111,6 @@ LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move
|
||||||
|
|
||||||
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
|
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
|
||||||
|
|
||||||
optional<bool> UniqueFilter::new_value(bool value, bool is_initial) {
|
|
||||||
if (this->last_value_.has_value() && *this->last_value_ == value) {
|
|
||||||
return {};
|
|
||||||
} else {
|
|
||||||
this->last_value_ = value;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace binary_sensor
|
} // namespace binary_sensor
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
@ -29,38 +30,40 @@ class Filter {
|
||||||
|
|
||||||
class DelayedOnOffFilter : public Filter, public Component {
|
class DelayedOnOffFilter : public Filter, public Component {
|
||||||
public:
|
public:
|
||||||
explicit DelayedOnOffFilter(uint32_t delay);
|
|
||||||
|
|
||||||
optional<bool> new_value(bool value, bool is_initial) override;
|
optional<bool> new_value(bool value, bool is_initial) override;
|
||||||
|
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
template<typename T> void set_on_delay(T delay) { this->on_delay_ = delay; }
|
||||||
|
template<typename T> void set_off_delay(T delay) { this->off_delay_ = delay; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t delay_;
|
TemplatableValue<uint32_t> on_delay_{};
|
||||||
|
TemplatableValue<uint32_t> off_delay_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class DelayedOnFilter : public Filter, public Component {
|
class DelayedOnFilter : public Filter, public Component {
|
||||||
public:
|
public:
|
||||||
explicit DelayedOnFilter(uint32_t delay);
|
|
||||||
|
|
||||||
optional<bool> new_value(bool value, bool is_initial) override;
|
optional<bool> new_value(bool value, bool is_initial) override;
|
||||||
|
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
template<typename T> void set_delay(T delay) { this->delay_ = delay; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t delay_;
|
TemplatableValue<uint32_t> delay_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class DelayedOffFilter : public Filter, public Component {
|
class DelayedOffFilter : public Filter, public Component {
|
||||||
public:
|
public:
|
||||||
explicit DelayedOffFilter(uint32_t delay);
|
|
||||||
|
|
||||||
optional<bool> new_value(bool value, bool is_initial) override;
|
optional<bool> new_value(bool value, bool is_initial) override;
|
||||||
|
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
template<typename T> void set_delay(T delay) { this->delay_ = delay; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint32_t delay_;
|
TemplatableValue<uint32_t> delay_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class InvertFilter : public Filter {
|
class InvertFilter : public Filter {
|
||||||
|
@ -105,14 +108,6 @@ class LambdaFilter : public Filter {
|
||||||
std::function<optional<bool>(bool)> f_;
|
std::function<optional<bool>(bool)> f_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UniqueFilter : public Filter {
|
|
||||||
public:
|
|
||||||
optional<bool> new_value(bool value, bool is_initial) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
optional<bool> last_value_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace binary_sensor
|
} // namespace binary_sensor
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
51
esphome/components/bk72xx/__init__.py
Normal file
51
esphome/components/bk72xx/__init__.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# This file was auto-generated by libretiny/generate_components.py
|
||||||
|
# Do not modify its contents.
|
||||||
|
# For custom pin validators, put validate_pin() or validate_usage()
|
||||||
|
# in gpio.py file in this directory.
|
||||||
|
# For changing schema/pin schema, put COMPONENT_SCHEMA or COMPONENT_PIN_SCHEMA
|
||||||
|
# in schema.py file in this directory.
|
||||||
|
|
||||||
|
from esphome import pins
|
||||||
|
from esphome.components import libretiny
|
||||||
|
from esphome.components.libretiny.const import (
|
||||||
|
COMPONENT_BK72XX,
|
||||||
|
KEY_COMPONENT_DATA,
|
||||||
|
KEY_LIBRETINY,
|
||||||
|
LibreTinyComponent,
|
||||||
|
)
|
||||||
|
from esphome.core import CORE
|
||||||
|
|
||||||
|
from .boards import BK72XX_BOARDS, BK72XX_BOARD_PINS
|
||||||
|
|
||||||
|
CODEOWNERS = ["@kuba2k2"]
|
||||||
|
AUTO_LOAD = ["libretiny"]
|
||||||
|
|
||||||
|
COMPONENT_DATA = LibreTinyComponent(
|
||||||
|
name=COMPONENT_BK72XX,
|
||||||
|
boards=BK72XX_BOARDS,
|
||||||
|
board_pins=BK72XX_BOARD_PINS,
|
||||||
|
pin_validation=None,
|
||||||
|
usage_validation=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _set_core_data(config):
|
||||||
|
CORE.data[KEY_LIBRETINY] = {}
|
||||||
|
CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA] = COMPONENT_DATA
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = libretiny.BASE_SCHEMA
|
||||||
|
|
||||||
|
PIN_SCHEMA = libretiny.gpio.BASE_PIN_SCHEMA
|
||||||
|
|
||||||
|
CONFIG_SCHEMA.prepend_extra(_set_core_data)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
return await libretiny.component_to_code(config)
|
||||||
|
|
||||||
|
|
||||||
|
@pins.PIN_SCHEMA_REGISTRY.register("bk72xx", PIN_SCHEMA)
|
||||||
|
async def pin_to_code(config):
|
||||||
|
return await libretiny.gpio.component_pin_to_code(config)
|
1264
esphome/components/bk72xx/boards.py
Normal file
1264
esphome/components/bk72xx/boards.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -93,35 +93,27 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await uart.register_uart_device(var, config)
|
await uart.register_uart_device(var, config)
|
||||||
|
|
||||||
if CONF_VOLTAGE in config:
|
if voltage_config := config.get(CONF_VOLTAGE):
|
||||||
conf = config[CONF_VOLTAGE]
|
sens = await sensor.new_sensor(voltage_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_voltage_sensor(sens))
|
cg.add(var.set_voltage_sensor(sens))
|
||||||
if CONF_CURRENT_1 in config:
|
if current_1_config := config.get(CONF_CURRENT_1):
|
||||||
conf = config[CONF_CURRENT_1]
|
sens = await sensor.new_sensor(current_1_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_current_sensor_1(sens))
|
cg.add(var.set_current_sensor_1(sens))
|
||||||
if CONF_CURRENT_2 in config:
|
if current_2_config := config.get(CONF_CURRENT_2):
|
||||||
conf = config[CONF_CURRENT_2]
|
sens = await sensor.new_sensor(current_2_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_current_sensor_2(sens))
|
cg.add(var.set_current_sensor_2(sens))
|
||||||
if CONF_ACTIVE_POWER_1 in config:
|
if active_power_1_config := config.get(CONF_ACTIVE_POWER_1):
|
||||||
conf = config[CONF_ACTIVE_POWER_1]
|
sens = await sensor.new_sensor(active_power_1_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_power_sensor_1(sens))
|
cg.add(var.set_power_sensor_1(sens))
|
||||||
if CONF_ACTIVE_POWER_2 in config:
|
if active_power_2_config := config.get(CONF_ACTIVE_POWER_2):
|
||||||
conf = config[CONF_ACTIVE_POWER_2]
|
sens = await sensor.new_sensor(active_power_2_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_power_sensor_2(sens))
|
cg.add(var.set_power_sensor_2(sens))
|
||||||
if CONF_ENERGY_1 in config:
|
if energy_1_config := config.get(CONF_ENERGY_1):
|
||||||
conf = config[CONF_ENERGY_1]
|
sens = await sensor.new_sensor(energy_1_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_energy_sensor_1(sens))
|
cg.add(var.set_energy_sensor_1(sens))
|
||||||
if CONF_ENERGY_2 in config:
|
if energy_2_config := config.get(CONF_ENERGY_2):
|
||||||
conf = config[CONF_ENERGY_2]
|
sens = await sensor.new_sensor(energy_2_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_energy_sensor_2(sens))
|
cg.add(var.set_energy_sensor_2(sens))
|
||||||
if CONF_ENERGY_TOTAL in config:
|
if energy_total_config := config.get(CONF_ENERGY_TOTAL):
|
||||||
conf = config[CONF_ENERGY_TOTAL]
|
sens = await sensor.new_sensor(energy_total_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_energy_sensor_sum(sens))
|
cg.add(var.set_energy_sensor_sum(sens))
|
||||||
|
|
|
@ -79,27 +79,21 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await uart.register_uart_device(var, config)
|
await uart.register_uart_device(var, config)
|
||||||
|
|
||||||
if CONF_VOLTAGE in config:
|
if voltage_config := config.get(CONF_VOLTAGE):
|
||||||
conf = config[CONF_VOLTAGE]
|
sens = await sensor.new_sensor(voltage_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_voltage_sensor(sens))
|
cg.add(var.set_voltage_sensor(sens))
|
||||||
if CONF_CURRENT in config:
|
if current_config := config.get(CONF_CURRENT):
|
||||||
conf = config[CONF_CURRENT]
|
sens = await sensor.new_sensor(current_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_current_sensor(sens))
|
cg.add(var.set_current_sensor(sens))
|
||||||
if CONF_POWER in config:
|
if power_config := config.get(CONF_POWER):
|
||||||
conf = config[CONF_POWER]
|
sens = await sensor.new_sensor(power_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_power_sensor(sens))
|
cg.add(var.set_power_sensor(sens))
|
||||||
if CONF_ENERGY in config:
|
if energy_config := config.get(CONF_ENERGY):
|
||||||
conf = config[CONF_ENERGY]
|
sens = await sensor.new_sensor(energy_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_energy_sensor(sens))
|
cg.add(var.set_energy_sensor(sens))
|
||||||
if CONF_INTERNAL_TEMPERATURE in config:
|
if internal_temperature_config := config.get(CONF_INTERNAL_TEMPERATURE):
|
||||||
conf = config[CONF_INTERNAL_TEMPERATURE]
|
sens = await sensor.new_sensor(internal_temperature_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_internal_temperature_sensor(sens))
|
cg.add(var.set_internal_temperature_sensor(sens))
|
||||||
if CONF_EXTERNAL_TEMPERATURE in config:
|
if external_temperature_config := config.get(CONF_EXTERNAL_TEMPERATURE):
|
||||||
conf = config[CONF_EXTERNAL_TEMPERATURE]
|
sens = await sensor.new_sensor(external_temperature_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_external_temperature_sensor(sens))
|
cg.add(var.set_external_temperature_sensor(sens))
|
||||||
|
|
|
@ -71,23 +71,18 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await uart.register_uart_device(var, config)
|
await uart.register_uart_device(var, config)
|
||||||
|
|
||||||
if CONF_VOLTAGE in config:
|
if voltage_config := config.get(CONF_VOLTAGE):
|
||||||
conf = config[CONF_VOLTAGE]
|
sens = await sensor.new_sensor(voltage_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_voltage_sensor(sens))
|
cg.add(var.set_voltage_sensor(sens))
|
||||||
if CONF_CURRENT in config:
|
if current_config := config.get(CONF_CURRENT):
|
||||||
conf = config[CONF_CURRENT]
|
sens = await sensor.new_sensor(current_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_current_sensor(sens))
|
cg.add(var.set_current_sensor(sens))
|
||||||
if CONF_POWER in config:
|
if power_config := config.get(CONF_POWER):
|
||||||
conf = config[CONF_POWER]
|
sens = await sensor.new_sensor(power_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_power_sensor(sens))
|
cg.add(var.set_power_sensor(sens))
|
||||||
if CONF_ENERGY in config:
|
if energy_config := config.get(CONF_ENERGY):
|
||||||
conf = config[CONF_ENERGY]
|
sens = await sensor.new_sensor(energy_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_energy_sensor(sens))
|
cg.add(var.set_energy_sensor(sens))
|
||||||
if CONF_FREQUENCY in config:
|
if frequency_config := config.get(CONF_FREQUENCY):
|
||||||
conf = config[CONF_FREQUENCY]
|
sens = await sensor.new_sensor(frequency_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_frequency_sensor(sens))
|
cg.add(var.set_frequency_sensor(sens))
|
||||||
|
|
|
@ -129,32 +129,18 @@ async def characteristic_sensor_to_code(config):
|
||||||
)
|
)
|
||||||
cg.add(var.set_char_uuid128(uuid128))
|
cg.add(var.set_char_uuid128(uuid128))
|
||||||
|
|
||||||
if CONF_DESCRIPTOR_UUID in config:
|
if descriptor_uuid := config.get(CONF_DESCRIPTOR_UUID):
|
||||||
if len(config[CONF_DESCRIPTOR_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
if len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||||
cg.add(
|
cg.add(var.set_descr_uuid16(esp32_ble_tracker.as_hex(descriptor_uuid)))
|
||||||
var.set_descr_uuid16(
|
elif len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid32_format):
|
||||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
cg.add(var.set_descr_uuid32(esp32_ble_tracker.as_hex(descriptor_uuid)))
|
||||||
)
|
elif len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||||
)
|
uuid128 = esp32_ble_tracker.as_reversed_hex_array(descriptor_uuid)
|
||||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
|
||||||
esp32_ble_tracker.bt_uuid32_format
|
|
||||||
):
|
|
||||||
cg.add(
|
|
||||||
var.set_descr_uuid32(
|
|
||||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
|
||||||
esp32_ble_tracker.bt_uuid128_format
|
|
||||||
):
|
|
||||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
|
||||||
config[CONF_DESCRIPTOR_UUID]
|
|
||||||
)
|
|
||||||
cg.add(var.set_descr_uuid128(uuid128))
|
cg.add(var.set_descr_uuid128(uuid128))
|
||||||
|
|
||||||
if CONF_LAMBDA in config:
|
if lambda_config := config.get(CONF_LAMBDA):
|
||||||
lambda_ = await cg.process_lambda(
|
lambda_ = await cg.process_lambda(
|
||||||
config[CONF_LAMBDA], [(adv_data_t_const_ref, "x")], return_type=cg.float_
|
lambda_config, [(adv_data_t_const_ref, "x")], return_type=cg.float_
|
||||||
)
|
)
|
||||||
cg.add(var.set_data_to_value(lambda_))
|
cg.add(var.set_data_to_value(lambda_))
|
||||||
|
|
||||||
|
|
|
@ -88,27 +88,13 @@ async def to_code(config):
|
||||||
)
|
)
|
||||||
cg.add(var.set_char_uuid128(uuid128))
|
cg.add(var.set_char_uuid128(uuid128))
|
||||||
|
|
||||||
if CONF_DESCRIPTOR_UUID in config:
|
if descriptor_uuid := config:
|
||||||
if len(config[CONF_DESCRIPTOR_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
if len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||||
cg.add(
|
cg.add(var.set_descr_uuid16(esp32_ble_tracker.as_hex(descriptor_uuid)))
|
||||||
var.set_descr_uuid16(
|
elif len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid32_format):
|
||||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
cg.add(var.set_descr_uuid32(esp32_ble_tracker.as_hex(descriptor_uuid)))
|
||||||
)
|
elif len(descriptor_uuid) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||||
)
|
uuid128 = esp32_ble_tracker.as_reversed_hex_array(descriptor_uuid)
|
||||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
|
||||||
esp32_ble_tracker.bt_uuid32_format
|
|
||||||
):
|
|
||||||
cg.add(
|
|
||||||
var.set_descr_uuid32(
|
|
||||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
|
||||||
esp32_ble_tracker.bt_uuid128_format
|
|
||||||
):
|
|
||||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
|
||||||
config[CONF_DESCRIPTOR_UUID]
|
|
||||||
)
|
|
||||||
cg.add(var.set_descr_uuid128(uuid128))
|
cg.add(var.set_descr_uuid128(uuid128))
|
||||||
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
|
@ -39,7 +39,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
|
cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
|
||||||
cv.Optional(CONF_IBEACON_UUID): cv.uuid,
|
cv.Optional(CONF_IBEACON_UUID): cv.uuid,
|
||||||
cv.Optional(CONF_MIN_RSSI): cv.All(
|
cv.Optional(CONF_MIN_RSSI): cv.All(
|
||||||
cv.decibel, cv.int_range(min=-90, max=-30)
|
cv.decibel, cv.int_range(min=-100, max=-30)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -55,35 +55,27 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await esp32_ble_tracker.register_ble_device(var, config)
|
await esp32_ble_tracker.register_ble_device(var, config)
|
||||||
|
|
||||||
if CONF_MIN_RSSI in config:
|
if min_rssi := config.get(CONF_MIN_RSSI):
|
||||||
cg.add(var.set_minimum_rssi(config[CONF_MIN_RSSI]))
|
cg.add(var.set_minimum_rssi(min_rssi))
|
||||||
|
|
||||||
if CONF_MAC_ADDRESS in config:
|
if mac_address := config.get(CONF_MAC_ADDRESS):
|
||||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
cg.add(var.set_address(mac_address.as_hex))
|
||||||
|
|
||||||
if CONF_SERVICE_UUID in config:
|
if service_uuid := config.get(CONF_SERVICE_UUID):
|
||||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
if len(service_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||||
cg.add(
|
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(service_uuid)))
|
||||||
var.set_service_uuid16(
|
elif len(service_uuid) == len(esp32_ble_tracker.bt_uuid32_format):
|
||||||
esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
|
cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(service_uuid)))
|
||||||
)
|
elif len(service_uuid) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||||
)
|
uuid128 = esp32_ble_tracker.as_reversed_hex_array(service_uuid)
|
||||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
|
|
||||||
cg.add(
|
|
||||||
var.set_service_uuid32(
|
|
||||||
esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
|
|
||||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
|
|
||||||
cg.add(var.set_service_uuid128(uuid128))
|
cg.add(var.set_service_uuid128(uuid128))
|
||||||
|
|
||||||
if CONF_IBEACON_UUID in config:
|
if ibeacon_uuid := config.get(CONF_IBEACON_UUID):
|
||||||
ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(config[CONF_IBEACON_UUID]))
|
ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid))
|
||||||
cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
|
cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
|
||||||
|
|
||||||
if CONF_IBEACON_MAJOR in config:
|
if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None:
|
||||||
cg.add(var.set_ibeacon_major(config[CONF_IBEACON_MAJOR]))
|
cg.add(var.set_ibeacon_major(ibeacon_major))
|
||||||
|
|
||||||
if CONF_IBEACON_MINOR in config:
|
if (ibeacon_minor := config.get(CONF_IBEACON_MINOR)) is not None:
|
||||||
cg.add(var.set_ibeacon_minor(config[CONF_IBEACON_MINOR]))
|
cg.add(var.set_ibeacon_minor(ibeacon_minor))
|
||||||
|
|
|
@ -51,7 +51,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||||
this->found_ = false;
|
this->found_ = false;
|
||||||
}
|
}
|
||||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
||||||
if (this->check_minimum_rssi_ && this->minimum_rssi_ <= device.get_rssi()) {
|
if (this->check_minimum_rssi_ && this->minimum_rssi_ > device.get_rssi()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
switch (this->match_by_) {
|
switch (this->match_by_) {
|
||||||
|
|
|
@ -57,32 +57,24 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await esp32_ble_tracker.register_ble_device(var, config)
|
await esp32_ble_tracker.register_ble_device(var, config)
|
||||||
|
|
||||||
if CONF_MAC_ADDRESS in config:
|
if mac_address := config.get(CONF_MAC_ADDRESS):
|
||||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
cg.add(var.set_address(mac_address.as_hex))
|
||||||
|
|
||||||
if CONF_SERVICE_UUID in config:
|
if service_uuid := config.get(CONF_SERVICE_UUID):
|
||||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
if len(service_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||||
cg.add(
|
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(service_uuid)))
|
||||||
var.set_service_uuid16(
|
elif len(service_uuid) == len(esp32_ble_tracker.bt_uuid32_format):
|
||||||
esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
|
cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(service_uuid)))
|
||||||
)
|
elif len(service_uuid) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||||
)
|
uuid128 = esp32_ble_tracker.as_reversed_hex_array(service_uuid)
|
||||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
|
|
||||||
cg.add(
|
|
||||||
var.set_service_uuid32(
|
|
||||||
esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
|
|
||||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
|
|
||||||
cg.add(var.set_service_uuid128(uuid128))
|
cg.add(var.set_service_uuid128(uuid128))
|
||||||
|
|
||||||
if CONF_IBEACON_UUID in config:
|
if ibeacon_uuid := config.get(CONF_IBEACON_UUID):
|
||||||
ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(config[CONF_IBEACON_UUID]))
|
ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid))
|
||||||
cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
|
cg.add(var.set_ibeacon_uuid(ibeacon_uuid))
|
||||||
|
|
||||||
if CONF_IBEACON_MAJOR in config:
|
if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None:
|
||||||
cg.add(var.set_ibeacon_major(config[CONF_IBEACON_MAJOR]))
|
cg.add(var.set_ibeacon_major(ibeacon_major))
|
||||||
|
|
||||||
if CONF_IBEACON_MINOR in config:
|
if (ibeacon_minor := config.get(CONF_IBEACON_MINOR)) is not None:
|
||||||
cg.add(var.set_ibeacon_minor(config[CONF_IBEACON_MINOR]))
|
cg.add(var.set_ibeacon_minor(ibeacon_minor))
|
||||||
|
|
|
@ -275,6 +275,10 @@ esp_err_t BluetoothConnection::notify_characteristic(uint16_t handle, bool enabl
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp32_ble_tracker::AdvertisementParserType BluetoothConnection::get_advertisement_parser_type() {
|
||||||
|
return this->proxy_->get_advertisement_parser_type();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace bluetooth_proxy
|
} // namespace bluetooth_proxy
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase {
|
||||||
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) override;
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
||||||
|
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
|
||||||
|
|
||||||
esp_err_t read_characteristic(uint16_t handle);
|
esp_err_t read_characteristic(uint16_t handle);
|
||||||
esp_err_t write_characteristic(uint16_t handle, const std::string &data, bool response);
|
esp_err_t write_characteristic(uint16_t handle, const std::string &data, bool response);
|
||||||
|
|
|
@ -198,6 +198,12 @@ void BluetoothProxy::loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp32_ble_tracker::AdvertisementParserType BluetoothProxy::get_advertisement_parser_type() {
|
||||||
|
if (this->raw_advertisements_)
|
||||||
|
return esp32_ble_tracker::AdvertisementParserType::RAW_ADVERTISEMENTS;
|
||||||
|
return esp32_ble_tracker::AdvertisementParserType::PARSED_ADVERTISEMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) {
|
BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) {
|
||||||
for (auto *connection : this->connections_) {
|
for (auto *connection : this->connections_) {
|
||||||
if (connection->get_address() == address)
|
if (connection->get_address() == address)
|
||||||
|
@ -435,6 +441,7 @@ void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection
|
||||||
}
|
}
|
||||||
this->api_connection_ = api_connection;
|
this->api_connection_ = api_connection;
|
||||||
this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS;
|
this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS;
|
||||||
|
this->parent_->recalculate_advertisement_parser_types();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connection) {
|
void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connection) {
|
||||||
|
@ -444,6 +451,7 @@ void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connecti
|
||||||
}
|
}
|
||||||
this->api_connection_ = nullptr;
|
this->api_connection_ = nullptr;
|
||||||
this->raw_advertisements_ = false;
|
this->raw_advertisements_ = false;
|
||||||
|
this->parent_->recalculate_advertisement_parser_types();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluetoothProxy::send_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {
|
void BluetoothProxy::send_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
||||||
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
|
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
|
||||||
|
|
||||||
void register_connection(BluetoothConnection *connection) {
|
void register_connection(BluetoothConnection *connection) {
|
||||||
this->connections_.push_back(connection);
|
this->connections_.push_back(connection);
|
||||||
|
|
|
@ -98,22 +98,19 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
if CONF_TEMPERATURE in config:
|
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||||
conf = config[CONF_TEMPERATURE]
|
sens = await sensor.new_sensor(temperature_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_temperature_sensor(sens))
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
|
cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
if CONF_PRESSURE in config:
|
if pressure_config := config.get(CONF_PRESSURE):
|
||||||
conf = config[CONF_PRESSURE]
|
sens = await sensor.new_sensor(pressure_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_pressure_sensor(sens))
|
cg.add(var.set_pressure_sensor(sens))
|
||||||
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
|
cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
if CONF_HUMIDITY in config:
|
if humidity_config := config.get(CONF_HUMIDITY):
|
||||||
conf = config[CONF_HUMIDITY]
|
sens = await sensor.new_sensor(humidity_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_humidity_sensor(sens))
|
cg.add(var.set_humidity_sensor(sens))
|
||||||
cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
|
cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))
|
cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))
|
||||||
|
|
|
@ -130,27 +130,23 @@ async def to_code(config):
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
if CONF_TEMPERATURE in config:
|
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||||
conf = config[CONF_TEMPERATURE]
|
sens = await sensor.new_sensor(temperature_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_temperature_sensor(sens))
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
|
cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
if CONF_PRESSURE in config:
|
if pressure_config := config.get(CONF_PRESSURE):
|
||||||
conf = config[CONF_PRESSURE]
|
sens = await sensor.new_sensor(pressure_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_pressure_sensor(sens))
|
cg.add(var.set_pressure_sensor(sens))
|
||||||
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
|
cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
if CONF_HUMIDITY in config:
|
if humidity_config := config.get(CONF_HUMIDITY):
|
||||||
conf = config[CONF_HUMIDITY]
|
sens = await sensor.new_sensor(humidity_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_humidity_sensor(sens))
|
cg.add(var.set_humidity_sensor(sens))
|
||||||
cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
|
cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING]))
|
||||||
|
|
||||||
if CONF_GAS_RESISTANCE in config:
|
if gas_resistance_config := config.get(CONF_GAS_RESISTANCE):
|
||||||
conf = config[CONF_GAS_RESISTANCE]
|
sens = await sensor.new_sensor(gas_resistance_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(var.set_gas_resistance_sensor(sens))
|
cg.add(var.set_gas_resistance_sensor(sens))
|
||||||
|
|
||||||
cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]))
|
cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]))
|
||||||
|
|
|
@ -6,8 +6,10 @@ from esphome.const import (
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
CONF_PRESSURE,
|
CONF_PRESSURE,
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_CARBON_DIOXIDE,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_PRESSURE,
|
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
|
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
|
@ -17,8 +19,6 @@ from esphome.const import (
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
ICON_GAS_CYLINDER,
|
ICON_GAS_CYLINDER,
|
||||||
ICON_GAUGE,
|
ICON_GAUGE,
|
||||||
ICON_THERMOMETER,
|
|
||||||
ICON_WATER_PERCENT,
|
|
||||||
)
|
)
|
||||||
from . import (
|
from . import (
|
||||||
BME680BSECComponent,
|
BME680BSECComponent,
|
||||||
|
@ -35,7 +35,6 @@ CONF_CO2_EQUIVALENT = "co2_equivalent"
|
||||||
CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
|
CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
|
||||||
UNIT_IAQ = "IAQ"
|
UNIT_IAQ = "IAQ"
|
||||||
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
|
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
|
||||||
ICON_TEST_TUBE = "mdi:test-tube"
|
|
||||||
|
|
||||||
TYPES = [
|
TYPES = [
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
|
@ -53,7 +52,6 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
|
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_CELSIUS,
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
icon=ICON_THERMOMETER,
|
|
||||||
accuracy_decimals=1,
|
accuracy_decimals=1,
|
||||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
@ -62,16 +60,14 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||||
icon=ICON_GAUGE,
|
|
||||||
accuracy_decimals=1,
|
accuracy_decimals=1,
|
||||||
device_class=DEVICE_CLASS_PRESSURE,
|
device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
).extend(
|
).extend(
|
||||||
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
|
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_PERCENT,
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
icon=ICON_WATER_PERCENT,
|
|
||||||
accuracy_decimals=1,
|
accuracy_decimals=1,
|
||||||
device_class=DEVICE_CLASS_HUMIDITY,
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
@ -97,14 +93,14 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema(
|
cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
||||||
icon=ICON_TEST_TUBE,
|
|
||||||
accuracy_decimals=1,
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema(
|
cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
||||||
icon=ICON_TEST_TUBE,
|
|
||||||
accuracy_decimals=1,
|
accuracy_decimals=1,
|
||||||
|
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -112,12 +108,13 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
|
|
||||||
|
|
||||||
async def setup_conf(config, key, hub):
|
async def setup_conf(config, key, hub):
|
||||||
if key in config:
|
if sensor_config := config.get(key):
|
||||||
conf = config[key]
|
sens = await sensor.new_sensor(sensor_config)
|
||||||
sens = await sensor.new_sensor(conf)
|
|
||||||
cg.add(getattr(hub, f"set_{key}_sensor")(sens))
|
cg.add(getattr(hub, f"set_{key}_sensor")(sens))
|
||||||
if CONF_SAMPLE_RATE in conf:
|
if CONF_SAMPLE_RATE in sensor_config:
|
||||||
cg.add(getattr(hub, f"set_{key}_sample_rate")(conf[CONF_SAMPLE_RATE]))
|
cg.add(
|
||||||
|
getattr(hub, f"set_{key}_sample_rate")(sensor_config[CONF_SAMPLE_RATE])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
|
|
@ -21,9 +21,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||||
|
|
||||||
|
|
||||||
async def setup_conf(config, key, hub):
|
async def setup_conf(config, key, hub):
|
||||||
if key in config:
|
if sensor_config := config.get(key):
|
||||||
conf = config[key]
|
sens = await text_sensor.new_text_sensor(sensor_config)
|
||||||
sens = await text_sensor.new_text_sensor(conf)
|
|
||||||
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
|
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
|
||||||
|
|
||||||
|
|
||||||
|
|
1
esphome/components/bmi160/__init__.py
Normal file
1
esphome/components/bmi160/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@flaviut"]
|
270
esphome/components/bmi160/bmi160.cpp
Normal file
270
esphome/components/bmi160/bmi160.cpp
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
#include "bmi160.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace bmi160 {
|
||||||
|
|
||||||
|
static const char *const TAG = "bmi160";
|
||||||
|
|
||||||
|
const uint8_t BMI160_REGISTER_CHIPID = 0x00;
|
||||||
|
|
||||||
|
const uint8_t BMI160_REGISTER_CMD = 0x7E;
|
||||||
|
enum class Cmd : uint8_t {
|
||||||
|
START_FOC = 0x03,
|
||||||
|
ACCL_SET_PMU_MODE = 0b00010000, // last 2 bits are mode
|
||||||
|
GYRO_SET_PMU_MODE = 0b00010100, // last 2 bits are mode
|
||||||
|
MAG_SET_PMU_MODE = 0b00011000, // last 2 bits are mode
|
||||||
|
PROG_NVM = 0xA0,
|
||||||
|
FIFO_FLUSH = 0xB0,
|
||||||
|
INT_RESET = 0xB1,
|
||||||
|
SOFT_RESET = 0xB6,
|
||||||
|
STEP_CNT_CLR = 0xB2,
|
||||||
|
};
|
||||||
|
enum class GyroPmuMode : uint8_t {
|
||||||
|
SUSPEND = 0b00,
|
||||||
|
NORMAL = 0b01,
|
||||||
|
LOW_POWER = 0b10,
|
||||||
|
};
|
||||||
|
enum class AcclPmuMode : uint8_t {
|
||||||
|
SUSPEND = 0b00,
|
||||||
|
NORMAL = 0b01,
|
||||||
|
FAST_STARTUP = 0b11,
|
||||||
|
};
|
||||||
|
enum class MagPmuMode : uint8_t {
|
||||||
|
SUSPEND = 0b00,
|
||||||
|
NORMAL = 0b01,
|
||||||
|
LOW_POWER = 0b10,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t BMI160_REGISTER_ACCEL_CONFIG = 0x40;
|
||||||
|
enum class AcclFilterMode : uint8_t {
|
||||||
|
POWER_SAVING = 0b00000000,
|
||||||
|
PERF = 0b10000000,
|
||||||
|
};
|
||||||
|
enum class AcclBandwidth : uint8_t {
|
||||||
|
OSR4_AVG1 = 0b00000000,
|
||||||
|
OSR2_AVG2 = 0b00010000,
|
||||||
|
NORMAL_AVG4 = 0b00100000,
|
||||||
|
RES_AVG8 = 0b00110000,
|
||||||
|
RES_AVG16 = 0b01000000,
|
||||||
|
RES_AVG32 = 0b01010000,
|
||||||
|
RES_AVG64 = 0b01100000,
|
||||||
|
RES_AVG128 = 0b01110000,
|
||||||
|
};
|
||||||
|
enum class AccelOutputDataRate : uint8_t {
|
||||||
|
HZ_25_32 = 0b0001, // 25/32 Hz
|
||||||
|
HZ_25_16 = 0b0010, // 25/16 Hz
|
||||||
|
HZ_25_8 = 0b0011, // 25/8 Hz
|
||||||
|
HZ_25_4 = 0b0100, // 25/4 Hz
|
||||||
|
HZ_25_2 = 0b0101, // 25/2 Hz
|
||||||
|
HZ_25 = 0b0110, // 25 Hz
|
||||||
|
HZ_50 = 0b0111, // 50 Hz
|
||||||
|
HZ_100 = 0b1000, // 100 Hz
|
||||||
|
HZ_200 = 0b1001, // 200 Hz
|
||||||
|
HZ_400 = 0b1010, // 400 Hz
|
||||||
|
HZ_800 = 0b1011, // 800 Hz
|
||||||
|
HZ_1600 = 0b1100, // 1600 Hz
|
||||||
|
};
|
||||||
|
const uint8_t BMI160_REGISTER_ACCEL_RANGE = 0x41;
|
||||||
|
enum class AccelRange : uint8_t {
|
||||||
|
RANGE_2G = 0b0011,
|
||||||
|
RANGE_4G = 0b0101,
|
||||||
|
RANGE_8G = 0b1000,
|
||||||
|
RANGE_16G = 0b1100,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t BMI160_REGISTER_GYRO_CONFIG = 0x42;
|
||||||
|
enum class GyroBandwidth : uint8_t {
|
||||||
|
OSR4 = 0x00,
|
||||||
|
OSR2 = 0x10,
|
||||||
|
NORMAL = 0x20,
|
||||||
|
};
|
||||||
|
enum class GyroOuputDataRate : uint8_t {
|
||||||
|
HZ_25 = 0x06,
|
||||||
|
HZ_50 = 0x07,
|
||||||
|
HZ_100 = 0x08,
|
||||||
|
HZ_200 = 0x09,
|
||||||
|
HZ_400 = 0x0A,
|
||||||
|
HZ_800 = 0x0B,
|
||||||
|
HZ_1600 = 0x0C,
|
||||||
|
HZ_3200 = 0x0D,
|
||||||
|
};
|
||||||
|
const uint8_t BMI160_REGISTER_GYRO_RANGE = 0x43;
|
||||||
|
enum class GyroRange : uint8_t {
|
||||||
|
RANGE_2000_DPS = 0x0, // ±2000 °/s
|
||||||
|
RANGE_1000_DPS = 0x1,
|
||||||
|
RANGE_500_DPS = 0x2,
|
||||||
|
RANGE_250_DPS = 0x3,
|
||||||
|
RANGE_125_DPS = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_GYRO_X_LSB = 0x0C;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_GYRO_X_MSB = 0x0D;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_GYRO_Y_LSB = 0x0E;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_GYRO_Y_MSB = 0x0F;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_GYRO_Z_LSB = 0x10;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_GYRO_Z_MSB = 0x11;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_ACCEL_X_LSB = 0x12;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_ACCEL_X_MSB = 0x13;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_LSB = 0x14;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_MSB = 0x15;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_LSB = 0x16;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_MSB = 0x17;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_TEMP_LSB = 0x20;
|
||||||
|
const uint8_t BMI160_REGISTER_DATA_TEMP_MSB = 0x21;
|
||||||
|
|
||||||
|
const float GRAVITY_EARTH = 9.80665f;
|
||||||
|
|
||||||
|
void BMI160Component::internal_setup_(int stage) {
|
||||||
|
switch (stage) {
|
||||||
|
case 0:
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up BMI160...");
|
||||||
|
uint8_t chipid;
|
||||||
|
if (!this->read_byte(BMI160_REGISTER_CHIPID, &chipid) || (chipid != 0b11010001)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, " Bringing accelerometer out of sleep...");
|
||||||
|
if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::ACCL_SET_PMU_MODE | (uint8_t) AcclPmuMode::NORMAL)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGV(TAG, " Waiting for accelerometer to wake up...");
|
||||||
|
// need to wait (max delay in datasheet) because we can't send commands while another is in progress
|
||||||
|
// min 5ms, 10ms
|
||||||
|
this->set_timeout(10, [this]() { this->internal_setup_(1); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
ESP_LOGV(TAG, " Bringing gyroscope out of sleep...");
|
||||||
|
if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::GYRO_SET_PMU_MODE | (uint8_t) GyroPmuMode::NORMAL)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGV(TAG, " Waiting for gyroscope to wake up...");
|
||||||
|
// wait between 51 & 81ms, doing 100 to be safe
|
||||||
|
this->set_timeout(10, [this]() { this->internal_setup_(2); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
ESP_LOGV(TAG, " Setting up Gyro Config...");
|
||||||
|
uint8_t gyro_config = (uint8_t) GyroBandwidth::OSR4 | (uint8_t) GyroOuputDataRate::HZ_25;
|
||||||
|
ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
|
||||||
|
if (!this->write_byte(BMI160_REGISTER_GYRO_CONFIG, gyro_config)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGV(TAG, " Setting up Gyro Range...");
|
||||||
|
uint8_t gyro_range = (uint8_t) GyroRange::RANGE_2000_DPS;
|
||||||
|
ESP_LOGV(TAG, " Output gyro_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_range));
|
||||||
|
if (!this->write_byte(BMI160_REGISTER_GYRO_RANGE, gyro_range)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, " Setting up Accel Config...");
|
||||||
|
uint8_t accel_config =
|
||||||
|
(uint8_t) AcclFilterMode::PERF | (uint8_t) AcclBandwidth::RES_AVG16 | (uint8_t) AccelOutputDataRate::HZ_25;
|
||||||
|
ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
|
||||||
|
if (!this->write_byte(BMI160_REGISTER_ACCEL_CONFIG, accel_config)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGV(TAG, " Setting up Accel Range...");
|
||||||
|
uint8_t accel_range = (uint8_t) AccelRange::RANGE_16G;
|
||||||
|
ESP_LOGV(TAG, " Output accel_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_range));
|
||||||
|
if (!this->write_byte(BMI160_REGISTER_ACCEL_RANGE, accel_range)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->setup_complete_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BMI160Component::setup() { this->internal_setup_(0); }
|
||||||
|
void BMI160Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "BMI160:");
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Communication with BMI160 failed!");
|
||||||
|
}
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Acceleration Y", this->accel_y_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Acceleration Z", this->accel_z_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Gyro X", this->gyro_x_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Gyro Y", this->gyro_y_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Gyro Z", this->gyro_z_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c::ErrorCode BMI160Component::read_le_int16_(uint8_t reg, int16_t *value, uint8_t len) {
|
||||||
|
uint8_t raw_data[len * 2];
|
||||||
|
// read using read_register because we have little-endian data, and read_bytes_16 will swap it
|
||||||
|
i2c::ErrorCode err = this->read_register(reg, raw_data, len * 2, true);
|
||||||
|
if (err != i2c::ERROR_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
value[i] = (int16_t) ((uint16_t) raw_data[i * 2] | ((uint16_t) raw_data[i * 2 + 1] << 8));
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BMI160Component::update() {
|
||||||
|
if (!this->setup_complete_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, " Updating BMI160...");
|
||||||
|
int16_t data[6];
|
||||||
|
if (this->read_le_int16_(BMI160_REGISTER_DATA_GYRO_X_LSB, data, 6) != i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float gyro_x = (float) data[0] / (float) INT16_MAX * 2000.f;
|
||||||
|
float gyro_y = (float) data[1] / (float) INT16_MAX * 2000.f;
|
||||||
|
float gyro_z = (float) data[2] / (float) INT16_MAX * 2000.f;
|
||||||
|
float accel_x = (float) data[3] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
|
||||||
|
float accel_y = (float) data[4] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
|
||||||
|
float accel_z = (float) data[5] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
|
||||||
|
|
||||||
|
int16_t raw_temperature;
|
||||||
|
if (this->read_le_int16_(BMI160_REGISTER_DATA_TEMP_LSB, &raw_temperature, 1) != i2c::ERROR_OK) {
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float temperature = (float) raw_temperature / (float) INT16_MAX * 64.5f + 23.f;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG,
|
||||||
|
"Got accel={x=%.3f m/s², y=%.3f m/s², z=%.3f m/s²}, "
|
||||||
|
"gyro={x=%.3f °/s, y=%.3f °/s, z=%.3f °/s}, temp=%.3f°C",
|
||||||
|
accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z, temperature);
|
||||||
|
|
||||||
|
if (this->accel_x_sensor_ != nullptr)
|
||||||
|
this->accel_x_sensor_->publish_state(accel_x);
|
||||||
|
if (this->accel_y_sensor_ != nullptr)
|
||||||
|
this->accel_y_sensor_->publish_state(accel_y);
|
||||||
|
if (this->accel_z_sensor_ != nullptr)
|
||||||
|
this->accel_z_sensor_->publish_state(accel_z);
|
||||||
|
|
||||||
|
if (this->temperature_sensor_ != nullptr)
|
||||||
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
|
|
||||||
|
if (this->gyro_x_sensor_ != nullptr)
|
||||||
|
this->gyro_x_sensor_->publish_state(gyro_x);
|
||||||
|
if (this->gyro_y_sensor_ != nullptr)
|
||||||
|
this->gyro_y_sensor_->publish_state(gyro_y);
|
||||||
|
if (this->gyro_z_sensor_ != nullptr)
|
||||||
|
this->gyro_z_sensor_->publish_state(gyro_z);
|
||||||
|
|
||||||
|
this->status_clear_warning();
|
||||||
|
}
|
||||||
|
float BMI160Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
} // namespace bmi160
|
||||||
|
} // namespace esphome
|
44
esphome/components/bmi160/bmi160.h
Normal file
44
esphome/components/bmi160/bmi160.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace bmi160 {
|
||||||
|
|
||||||
|
class BMI160Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
void set_accel_x_sensor(sensor::Sensor *accel_x_sensor) { accel_x_sensor_ = accel_x_sensor; }
|
||||||
|
void set_accel_y_sensor(sensor::Sensor *accel_y_sensor) { accel_y_sensor_ = accel_y_sensor; }
|
||||||
|
void set_accel_z_sensor(sensor::Sensor *accel_z_sensor) { accel_z_sensor_ = accel_z_sensor; }
|
||||||
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
|
void set_gyro_x_sensor(sensor::Sensor *gyro_x_sensor) { gyro_x_sensor_ = gyro_x_sensor; }
|
||||||
|
void set_gyro_y_sensor(sensor::Sensor *gyro_y_sensor) { gyro_y_sensor_ = gyro_y_sensor; }
|
||||||
|
void set_gyro_z_sensor(sensor::Sensor *gyro_z_sensor) { gyro_z_sensor_ = gyro_z_sensor; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sensor::Sensor *accel_x_sensor_{nullptr};
|
||||||
|
sensor::Sensor *accel_y_sensor_{nullptr};
|
||||||
|
sensor::Sensor *accel_z_sensor_{nullptr};
|
||||||
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
|
sensor::Sensor *gyro_x_sensor_{nullptr};
|
||||||
|
sensor::Sensor *gyro_y_sensor_{nullptr};
|
||||||
|
sensor::Sensor *gyro_z_sensor_{nullptr};
|
||||||
|
|
||||||
|
void internal_setup_(int stage);
|
||||||
|
bool setup_complete_{false};
|
||||||
|
|
||||||
|
/** reads `len` 16-bit little-endian integers from the given i2c register */
|
||||||
|
i2c::ErrorCode read_le_int16_(uint8_t reg, int16_t *value, uint8_t len);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bmi160
|
||||||
|
} // namespace esphome
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue