1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-03 08:31:47 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Jesse Hills
751853efdc Move replaceme check therefore allowing actual line to be shown 2023-02-02 19:39:29 +13:00
938 changed files with 9103 additions and 43547 deletions

View File

@@ -5,12 +5,9 @@ Checks: >-
-altera-*,
-android-*,
-boost-*,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-narrowing-conversions,
-bugprone-signed-char-misuse,
-cert-dcl50-cpp,
-cert-err33-c,
-cert-err58-cpp,
-cert-oop57-cpp,
-cert-str34-c,
@@ -18,7 +15,6 @@ Checks: >-
-clang-analyzer-osx.*,
-clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-ignored-optimization-argument,
-clang-diagnostic-shadow-field,
-clang-diagnostic-unused-const-variable,
-clang-diagnostic-unused-parameter,
@@ -29,7 +25,6 @@ Checks: >-
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-prefer-member-initializer,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
@@ -41,7 +36,6 @@ Checks: >-
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions,
-cppcoreguidelines-virtual-class-destructor,
-fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects,
@@ -74,7 +68,6 @@ Checks: >-
-modernize-use-nodiscard,
-mpi-*,
-objc-*,
-readability-container-data-pointer,
-readability-convert-member-functions-to-static,
-readability-else-after-return,
-readability-function-cognitive-complexity,
@@ -89,6 +82,8 @@ WarningsAsErrors: '*'
AnalyzeTemporaryDtors: false
FormatStyle: google
CheckOptions:
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-runtime-int.TypeSuffix
@@ -163,9 +158,3 @@ CheckOptions:
value: ''
- key: readability-qualified-auto.AddConstToQualified
value: 0
- key: readability-identifier-length.MinimumVariableNameLength
value: 0
- key: readability-identifier-length.MinimumParameterNameLength
value: 0
- key: readability-identifier-length.MinimumLoopCounterNameLength
value: 0

View File

@@ -4,61 +4,53 @@
"postCreateCommand": [
"script/devcontainer-post-create"
],
"containerEnv": {
"DEVCONTAINER": "1"
},
"runArgs": [
"--privileged",
"-e",
"ESPHOME_DASHBOARD_USE_PING=1"
],
"appPort": 6052,
"customizations": {
"vscode": {
"extensions": [
// python
"ms-python.python",
"visualstudioexptteam.vscodeintellicode",
// yaml
"redhat.vscode-yaml",
// cpp
"ms-vscode.cpptools",
// editorconfig
"editorconfig.editorconfig",
],
"settings": {
"python.languageServer": "Pylance",
"python.pythonPath": "/usr/bin/python3",
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "black",
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true,
"terminal.integrated.defaultProfile.linux": "bash",
"yaml.customTags": [
"!secret scalar",
"!lambda scalar",
"!extend scalar",
"!include_dir_named scalar",
"!include_dir_list scalar",
"!include_dir_merge_list scalar",
"!include_dir_merge_named scalar"
],
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/*.pyc": {
"when": "$(basename).py"
},
"**/__pycache__": true
},
"files.associations": {
"**/.vscode/*.json": "jsonc"
},
"C_Cpp.clang_format_path": "/usr/bin/clang-format-13"
}
}
"extensions": [
// python
"ms-python.python",
"visualstudioexptteam.vscodeintellicode",
// yaml
"redhat.vscode-yaml",
// cpp
"ms-vscode.cpptools",
// editorconfig
"editorconfig.editorconfig",
],
"settings": {
"python.languageServer": "Pylance",
"python.pythonPath": "/usr/bin/python3",
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "black",
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true,
"terminal.integrated.defaultProfile.linux": "bash",
"yaml.customTags": [
"!secret scalar",
"!lambda scalar",
"!include_dir_named scalar",
"!include_dir_list scalar",
"!include_dir_merge_list scalar",
"!include_dir_merge_named scalar"
],
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/*.pyc": {
"when": "$(basename).py"
},
"**/__pycache__": true
},
"files.associations": {
"**/.vscode/*.json": "jsonc"
},
"C_Cpp.clang_format_path": "/usr/bin/clang-format-11",
}
}

1
.gitattributes vendored
View File

@@ -1,3 +1,2 @@
# Normalize line endings to LF in the repository
* text eol=lf
*.png binary

View File

@@ -1,38 +0,0 @@
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.6.0
with:
python-version: ${{ inputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache/restore@v3.3.1
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 .

View File

@@ -11,7 +11,6 @@ on:
- ".github/workflows/**"
- "requirements*.txt"
- "platformio.ini"
- "script/platformio_install_deps.py"
pull_request:
paths:
@@ -19,17 +18,11 @@ on:
- ".github/workflows/**"
- "requirements*.txt"
- "platformio.ini"
- "script/platformio_install_deps.py"
permissions:
contents: read
packages: read
concurrency:
# yamllint disable-line rule:line-length
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
check-docker:
name: Build docker containers

View File

@@ -7,263 +7,59 @@ on:
branches: [dev, beta, release]
pull_request:
merge_group:
permissions:
contents: read
env:
DEFAULT_PYTHON: "3.9"
PYUPGRADE_TARGET: "--py39-plus"
CLANG_FORMAT_VERSION: "13.0.1"
concurrency:
# yamllint disable-line rule:line-length
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
common:
name: Create common environment
runs-on: ubuntu-latest
outputs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- 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 }}
id: python
uses: actions/setup-python@v4.6.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.3.1
with:
path: venv
# yamllint disable-line rule:line-length
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ steps.cache-key.outputs.key }}
- name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true'
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 .
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:
name: Check black
runs-on: ubuntu-latest
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run black
run: |
. venv/bin/activate
black --verbose esphome tests
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
flake8:
name: Check flake8
runs-on: ubuntu-latest
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run flake8
run: |
. venv/bin/activate
flake8 esphome
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
pylint:
name: Check pylint
runs-on: ubuntu-latest
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run pylint
run: |
. venv/bin/activate
pylint -f parseable --persistent=n esphome
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
pyupgrade:
name: Check pyupgrade
runs-on: ubuntu-latest
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run pyupgrade
run: |
. venv/bin/activate
pyupgrade ${{ env.PYUPGRADE_TARGET }} `find esphome -name "*.py" -type f`
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
ci-custom:
name: Run script/ci-custom
runs-on: ubuntu-latest
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Register matcher
run: echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
- name: Run script/ci-custom
run: |
. venv/bin/activate
script/ci-custom.py
script/build_codeowners.py --check
pytest:
name: Run pytest
runs-on: ubuntu-latest
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Register matcher
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
- name: Run pytest
run: |
. venv/bin/activate
pytest -vv --tb=native tests
clang-format:
name: Check clang-format
runs-on: ubuntu-latest
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Install clang-format
run: |
. venv/bin/activate
pip install clang-format==${{ env.CLANG_FORMAT_VERSION }}
- name: Run clang-format
run: |
. venv/bin/activate
script/clang-format -i
git diff-index --quiet HEAD --
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
compile-tests:
name: Run YAML test ${{ matrix.file }}
runs-on: ubuntu-latest
needs:
- common
- black
- ci-custom
- clang-format
- flake8
- pylint
- pytest
- pyupgrade
- yamllint
strategy:
fail-fast: false
max-parallel: 2
matrix:
file: [1, 2, 3, 3.1, 4, 5, 6, 7, 8]
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run esphome compile tests/test${{ matrix.file }}.yaml
run: |
. venv/bin/activate
esphome compile tests/test${{ matrix.file }}.yaml
clang-tidy:
ci:
name: ${{ matrix.name }}
runs-on: ubuntu-latest
needs:
- common
- black
- ci-custom
- clang-format
- flake8
- pylint
- pytest
- pyupgrade
- yamllint
strategy:
fail-fast: false
max-parallel: 2
matrix:
include:
- id: ci-custom
name: Run script/ci-custom
- id: lint-python
name: Run script/lint-python
- id: test
file: tests/test1.yaml
name: Test tests/test1.yaml
pio_cache_key: test1
- id: test
file: tests/test2.yaml
name: Test tests/test2.yaml
pio_cache_key: test2
- id: test
file: tests/test3.yaml
name: Test tests/test3.yaml
pio_cache_key: test3
- id: test
file: tests/test4.yaml
name: Test tests/test4.yaml
pio_cache_key: test4
- id: test
file: tests/test5.yaml
name: Test tests/test5.yaml
pio_cache_key: test5
- id: test
file: tests/test6.yaml
name: Test tests/test6.yaml
pio_cache_key: test6
- id: test
file: tests/test7.yaml
name: Test tests/test7.yaml
pio_cache_key: test7
- id: pytest
name: Run pytest
- id: clang-format
name: Run script/clang-format
- id: clang-tidy
name: Run script/clang-tidy for ESP8266
options: --environment esp8266-arduino-tidy --grep USE_ESP8266
@@ -288,63 +84,106 @@ jobs:
name: Run script/clang-tidy for ESP32 IDF
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
pio_cache_key: tidyesp32-idf
- id: yamllint
name: Run yamllint
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Restore Python
uses: ./.github/actions/restore-python
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
python-version: "3.9"
- name: Cache virtualenv
uses: actions/cache@v3
with:
path: .venv
# yamllint disable-line rule:line-length
key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
restore-keys: |
venv-${{ steps.python.outputs.python-version }}-
- name: Set up virtualenv
# yamllint disable rule:line-length
run: |
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
pip install -e .
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
# yamllint enable rule:line-length
# Use per check platformio cache because checks use different parts
- name: Cache platformio
uses: actions/cache@v3.3.1
uses: actions/cache@v3
with:
path: ~/.platformio
# yamllint disable-line rule:line-length
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
if: matrix.id == 'test' || matrix.id == 'clang-tidy'
- name: Install clang-tidy
run: sudo apt-get install clang-tidy-14
- name: Install clang tools
run: |
sudo apt-get install \
clang-format-11 \
clang-tidy-11
if: matrix.id == 'clang-tidy' || matrix.id == 'clang-format'
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
echo "::add-matcher::.github/workflows/matchers/pytest.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
- name: Run clang-tidy
- name: Lint Custom
run: |
. venv/bin/activate
script/clang-tidy --all-headers --fix ${{ matrix.options }}
script/ci-custom.py
script/build_codeowners.py --check
if: matrix.id == 'ci-custom'
- name: Lint Python
run: script/lint-python -a
if: matrix.id == 'lint-python'
- run: esphome compile ${{ matrix.file }}
if: matrix.id == 'test'
env:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Run pytest
run: |
pytest -vv --tb=native tests
if: matrix.id == 'pytest'
# Also run git-diff-index so that the step is marked as failed on
# formatting errors, since clang-format doesn't do anything but
# change files if -i is passed.
- name: Run clang-format
run: |
script/clang-format -i
git diff-index --quiet HEAD --
if: matrix.id == 'clang-format'
- name: Run clang-tidy
run: |
script/clang-tidy --all-headers --fix ${{ matrix.options }}
if: matrix.id == 'clang-tidy'
env:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Run yamllint
if: matrix.id == 'yamllint'
uses: frenck/action-yamllint@v1.3.1
- name: Suggested changes
run: script/ci-suggest-changes
# yamllint disable-line rule:line-length
if: always()
ci-status:
name: CI Status
runs-on: ubuntu-latest
needs:
- common
- black
- ci-custom
- clang-format
- flake8
- pylint
- pytest
- pyupgrade
- yamllint
- compile-tests
- clang-tidy
if: always()
steps:
- name: Success
if: ${{ !(contains(needs.*.result, 'failure')) }}
run: exit 0
- name: Failure
if: ${{ contains(needs.*.result, 'failure') }}
run: exit 1
if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format' || matrix.id == 'lint-python')

View File

@@ -49,11 +49,9 @@ jobs:
with:
python-version: "3.x"
- name: Set up python environment
env:
ESPHOME_NO_VENV: 1
run: |
script/setup
pip install twine
pip install setuptools wheel twine
- name: Build
run: python setup.py sdist bdist_wheel
- name: Upload
@@ -119,7 +117,7 @@ jobs:
--suffix "${{ matrix.image.suffix }}"
- name: Build and push
uses: docker/build-push-action@v4
uses: docker/build-push-action@v3
with:
context: .
file: ./docker/Dockerfile
@@ -140,18 +138,14 @@ jobs:
runs-on: ubuntu-latest
needs: [deploy-docker]
steps:
- name: Trigger Workflow
uses: actions/github-script@v6
with:
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
script: |
github.rest.actions.createWorkflowDispatch({
owner: "esphome",
repo: "home-assistant-addon",
workflow_id: "bump-version.yml",
ref: "main",
inputs: {
version: "${{ github.event.release.tag_name }}",
content: ${{ toJSON(github.event.release.body) }}
}
})
- env:
TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
# yamllint disable rule:line-length
run: |
curl \
-u ":$TOKEN" \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
-d '{"ref":"main","inputs":{"version":"${{ github.event.release.tag_name }}","content":${{ toJSON(github.event.release.body) }}}}'
# yamllint enable rule:line-length

View File

@@ -18,7 +18,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
- uses: actions/stale@v7
with:
days-before-pr-stale: 90
days-before-pr-close: 7
@@ -26,7 +26,7 @@ jobs:
days-before-issue-close: -1
remove-stale-when-updated: true
stale-pr-label: "stale"
exempt-pr-labels: "not-stale"
exempt-pr-labels: "no-stale"
stale-pr-message: >
There hasn't been any activity on this pull request recently. This
pull request has been automatically marked as stale because of that
@@ -38,7 +38,7 @@ jobs:
close-issues:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
- uses: actions/stale@v7
with:
days-before-pr-stale: -1
days-before-pr-close: -1

View File

@@ -1,49 +0,0 @@
---
name: Synchronise Device Classes from Home Assistant
on:
workflow_dispatch:
schedule:
- cron: '45 6 * * *'
jobs:
sync:
name: Sync Device Classes
runs-on: ubuntu-latest
if: github.repository == 'esphome/esphome'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Checkout Home Assistant
uses: actions/checkout@v3
with:
repository: home-assistant/core
path: lib/home-assistant
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Install Home Assistant
run: |
python -m pip install --upgrade pip
pip install -e lib/home-assistant
- name: Sync
run: |
python ./script/sync-device_class.py
- name: Commit changes
uses: peter-evans/create-pull-request@v5
with:
commit-message: "Synchronise Device Classes from Home Assistant"
committer: esphomebot <esphome@nabucasa.com>
author: esphomebot <esphome@nabucasa.com>
branch: sync/device-classes
delete-branch: true
title: "Synchronise Device Classes from Home Assistant"
body-path: .github/PULL_REQUEST_TEMPLATE.md
token: ${{ secrets.DEVICE_CLASS_SYNC_TOKEN }}

4
.gitignore vendored
View File

@@ -128,7 +128,3 @@ tests/.esphome/
sdkconfig.*
!sdkconfig.defaults
.tests/
/components

View File

@@ -2,8 +2,8 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/psf/black
rev: 23.7.0
- repo: https://github.com/ambv/black
rev: 22.12.0
hooks:
- id: black
args:
@@ -27,7 +27,7 @@ repos:
- --branch=release
- --branch=beta
- repo: https://github.com/asottile/pyupgrade
rev: v3.10.1
rev: v3.3.0
hooks:
- id: pyupgrade
args: [--py39-plus]

33
.vscode/tasks.json vendored
View File

@@ -2,24 +2,15 @@
"version": "2.0.0",
"tasks": [
{
"label": "Run Dashboard",
"label": "run",
"type": "shell",
"command": "${command:python.interpreterPath}",
"args": [
"-m",
"esphome",
"dashboard",
"config/"
],
"command": "python3 -m esphome dashboard config/",
"problemMatcher": []
},
{
"label": "clang-tidy",
"type": "shell",
"command": "${command:python.interpreterPath}",
"args": [
"./script/clang-tidy"
],
"command": "./script/clang-tidy",
"problemMatcher": [
{
"owner": "clang-tidy",
@@ -36,24 +27,6 @@
]
}
]
},
{
"label": "Generate proto files",
"type": "shell",
"command": "${command:python.interpreterPath}",
"args": [
"./script/api_protobuf/api_protobuf.py"
],
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "never",
"close": true,
"panel": "new"
},
"problemMatcher": []
}
]
}

View File

@@ -11,29 +11,21 @@ esphome/*.py @esphome/core
esphome/core/* @esphome/core
# Integrations
esphome/components/a01nyub/* @MrSuicideParrot
esphome/components/absolute_humidity/* @DAVe3283
esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core
esphome/components/adc128s102/* @DeerMaximum
esphome/components/addressable_light/* @justfalter
esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau
esphome/components/airthings_wave_mini/* @ncareau
esphome/components/airthings_wave_plus/* @jeromelaban
esphome/components/alarm_control_panel/* @grahambrown11
esphome/components/alpha3/* @jan-hofmeier
esphome/components/am43/* @buxtronix
esphome/components/am43/cover/* @buxtronix
esphome/components/am43/sensor/* @buxtronix
esphome/components/analog_threshold/* @ianchi
esphome/components/animation/* @syndlex
esphome/components/anova/* @buxtronix
esphome/components/api/* @OttoWinter
esphome/components/as7341/* @mrgnr
esphome/components/async_tcp/* @OttoWinter
esphome/components/atc_mithermometer/* @ahpohl
esphome/components/atm90e26/* @danieltwagner
esphome/components/b_parasite/* @rbaron
esphome/components/ballu/* @bazuchan
esphome/components/bang_bang/* @OttoWinter
@@ -49,12 +41,11 @@ esphome/components/ble_client/* @buxtronix
esphome/components/bluetooth_proxy/* @jesserockz
esphome/components/bme680_bsec/* @trvrnrth
esphome/components/bmp3xx/* @martgras
esphome/components/bmp581/* @kahrendt
esphome/components/bp1658cj/* @Cossid
esphome/components/bp5758d/* @Cossid
esphome/components/button/* @esphome/core
esphome/components/canbus/* @danielschramm @mvturnho
esphome/components/cap1188/* @mreditor97
esphome/components/cap1188/* @MrEditor97
esphome/components/captive_portal/* @OttoWinter
esphome/components/ccs811/* @habbie
esphome/components/cd74hc4067/* @asoehlke
@@ -80,20 +71,17 @@ esphome/components/display_menu_base/* @numo68
esphome/components/dps310/* @kbx81
esphome/components/ds1307/* @badbadc0ffee
esphome/components/dsmr/* @glmnet @zuidwijk
esphome/components/duty_time/* @dudanov
esphome/components/ee895/* @Stock-M
esphome/components/ektf2232/* @jesserockz
esphome/components/ens210/* @itn3rd77
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble_client/* @jesserockz
esphome/components/esp32_ble_server/* @clydebarrow @jesserockz
esphome/components/esp32_ble_server/* @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron
esphome/components/esp32_improv/* @jesserockz
esphome/components/esp32_rmt_led_strip/* @jesserockz
esphome/components/esp8266/* @esphome/core
esphome/components/esp_adf/* @jesserockz
esphome/components/ethernet_info/* @gtjadsonsantos
esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb
@@ -102,50 +90,35 @@ esphome/components/factory_reset/* @anatoly-savchenkov
esphome/components/fastled_base/* @OttoWinter
esphome/components/feedback/* @ianchi
esphome/components/fingerprint_grow/* @OnFreund @loongyh
esphome/components/fs3000/* @kahrendt
esphome/components/gcja5/* @gcormier
esphome/components/globals/* @esphome/core
esphome/components/gp8403/* @jesserockz
esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle
esphome/components/graph/* @synco
esphome/components/grove_tb6612fng/* @max246
esphome/components/growatt_solar/* @leeuwte
esphome/components/haier/* @paveldn
esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann
esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal
esphome/components/hm3301/* @freekode
esphome/components/homeassistant/* @OttoWinter
esphome/components/honeywellabp/* @RubyBailey
esphome/components/host/* @esphome/core
esphome/components/hrxl_maxsonar_wr/* @netmikey
esphome/components/hte501/* @Stock-M
esphome/components/hydreon_rgxx/* @functionpointer
esphome/components/hyt271/* @Philippe12
esphome/components/i2c/* @esphome/core
esphome/components/i2s_audio/* @jesserockz
esphome/components/i2s_audio/media_player/* @jesserockz
esphome/components/i2s_audio/microphone/* @jesserockz
esphome/components/i2s_audio/speaker/* @jesserockz
esphome/components/ili9xxx/* @nielsnl68
esphome/components/improv_base/* @esphome/core
esphome/components/improv_serial/* @esphome/core
esphome/components/ina260/* @mreditor97
esphome/components/ina260/* @MrEditor97
esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter
esphome/components/internal_temperature/* @Mat931
esphome/components/interval/* @esphome/core
esphome/components/json/* @OttoWinter
esphome/components/kalman_combinator/* @Cat-Ion
esphome/components/key_collector/* @ssieb
esphome/components/key_provider/* @ssieb
esphome/components/kuntze/* @ssieb
esphome/components/lcd_menu/* @numo68
esphome/components/ld2410/* @regevbr @sebcaps
esphome/components/ledc/* @OttoWinter
esphome/components/light/* @esphome/core
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
@@ -155,7 +128,6 @@ esphome/components/ltr390/* @sjtrny
esphome/components/matrix_keypad/* @ssieb
esphome/components/max31865/* @DAVe3283
esphome/components/max44009/* @berfenger
esphome/components/max6956/* @looping40
esphome/components/max7219digit/* @rspaargaren
esphome/components/max9611/* @mckaymatthew
esphome/components/mcp23008/* @jesserockz
@@ -169,19 +141,16 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp3204/* @rsumner
esphome/components/mcp4728/* @berfenger
esphome/components/mcp47a1/* @jesserockz
esphome/components/mcp9600/* @mreditor97
esphome/components/mcp9600/* @MrEditor97
esphome/components/mcp9808/* @k7hpn
esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core
esphome/components/media_player/* @jesserockz
esphome/components/microphone/* @jesserockz
esphome/components/mics_4514/* @jesserockz
esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov
esphome/components/mitsubishi/* @RubyBailey
esphome/components/mlx90393/* @functionpointer
esphome/components/mlx90614/* @jesserockz
esphome/components/mmc5603/* @benhoff
esphome/components/modbus_controller/* @martgras
esphome/components/modbus_controller/binary_sensor/* @martgras
esphome/components/modbus_controller/number/* @martgras
@@ -190,9 +159,8 @@ esphome/components/modbus_controller/select/* @martgras @stegm
esphome/components/modbus_controller/sensor/* @martgras
esphome/components/modbus_controller/switch/* @martgras
esphome/components/modbus_controller/text_sensor/* @martgras
esphome/components/mopeka_ble/* @Fabian-Schmidt @spbrogan
esphome/components/mopeka_ble/* @spbrogan
esphome/components/mopeka_pro_check/* @spbrogan
esphome/components/mopeka_std_check/* @Fabian-Schmidt
esphome/components/mpl3115a2/* @kbickar
esphome/components/mpu6886/* @fabaff
esphome/components/network/* @esphome/core
@@ -205,15 +173,12 @@ esphome/components/nfc/* @jesserockz
esphome/components/number/* @esphome/core
esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core
esphome/components/pca6416a/* @Mat931
esphome/components/pca9554/* @hwstar
esphome/components/pcf85063/* @brogon
esphome/components/pcf8563/* @KoenBreeman
esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984
esphome/components/pm1006/* @habbie
esphome/components/pmsa003i/* @sjtrny
esphome/components/pmwcs3/* @SeByDocKy
esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz
@@ -233,7 +198,6 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
esphome/components/rp2040/* @jesserockz
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
esphome/components/rp2040_pwm/* @jesserockz
esphome/components/rtttl/* @glmnet
esphome/components/safe_mode/* @jsuanet @paulmonigatti
@@ -243,7 +207,6 @@ esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath
esphome/components/selec_meter/* @sourabhjaiswal
esphome/components/select/* @esphome/core
esphome/components/sen21231/* @shreyaskarnik
esphome/components/sen5x/* @martgras
esphome/components/sensirion_common/* @martgras
esphome/components/sensor/* @esphome/core
@@ -255,7 +218,7 @@ esphome/components/shutdown/* @esphome/core @jsuanet
esphome/components/sigma_delta_output/* @Cat-Ion
esphome/components/sim800l/* @glmnet
esphome/components/sm10bit_base/* @Cossid
esphome/components/sm2135/* @BoukeHaarsma23 @dd32 @matika77
esphome/components/sm2135/* @BoukeHaarsma23
esphome/components/sm2235/* @Cossid
esphome/components/sm2335/* @Cossid
esphome/components/sml/* @alengwenus
@@ -263,7 +226,6 @@ esphome/components/smt100/* @piechade
esphome/components/sn74hc165/* @jesserockz
esphome/components/socket/* @esphome/core
esphome/components/sonoff_d1/* @anatoly-savchenkov
esphome/components/speaker/* @jesserockz
esphome/components/spi/* @esphome/core
esphome/components/sprinkler/* @kbx81
esphome/components/sps30/* @martgras
@@ -289,22 +251,18 @@ esphome/components/tca9548a/* @andreashergert1984
esphome/components/tcl112/* @glmnet
esphome/components/tee501/* @Stock-M
esphome/components/teleinfo/* @0hax
esphome/components/template/alarm_control_panel/* @grahambrown11
esphome/components/thermostat/* @kbx81
esphome/components/time/* @OttoWinter
esphome/components/tlc5947/* @rnauber
esphome/components/tm1621/* @Philippe12
esphome/components/tm1637/* @glmnet
esphome/components/tm1638/* @skykingjwc
esphome/components/tm1651/* @freekode
esphome/components/tmp102/* @timsavage
esphome/components/tmp1075/* @sybrenstuvel
esphome/components/tmp117/* @Azimath
esphome/components/tof10120/* @wstrzalka
esphome/components/toshiba/* @kbx81
esphome/components/touchscreen/* @jesserockz
esphome/components/tsl2591/* @wjcarpenter
esphome/components/tt21100/* @kroimon
esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz
esphome/components/tuya/number/* @frankiboy1
@@ -316,12 +274,9 @@ esphome/components/uart/* @esphome/core
esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli
esphome/components/ultrasonic/* @OttoWinter
esphome/components/vbus/* @ssieb
esphome/components/version/* @esphome/core
esphome/components/voice_assistant/* @jesserockz
esphome/components/wake_on_lan/* @willwill2will54
esphome/components/web_server_base/* @OttoWinter
esphome/components/web_server_idf/* @dentra
esphome/components/whirlpool/* @glmnet
esphome/components/whynter/* @aeonsablaze
esphome/components/wiegand/* @ssieb
@@ -331,6 +286,4 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
esphome/components/xl9535/* @mreditor97
esphome/components/xpt2046/* @nielsnl68 @numo68
esphome/components/zio_ultrasonic/* @kahrendt

View File

@@ -1,6 +1,8 @@
include LICENSE
include README.md
include requirements.txt
recursive-include esphome *.cpp *.h *.tcc *.c
include esphome/dashboard/templates/*.html
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
recursive-include esphome *.cpp *.h *.tcc
recursive-include esphome *.py.script
recursive-include esphome LICENSE.txt

View File

@@ -6,9 +6,9 @@
ARG BASEIMGTYPE=docker
# https://github.com/hassio-addons/addon-debian-base/releases
FROM ghcr.io/hassio-addons/debian-base:6.2.3 AS base-hassio
FROM ghcr.io/hassio-addons/debian-base:6.2.0 AS base-hassio
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye
FROM debian:bullseye-20230208-slim AS base-docker
FROM debian:bullseye-20221024-slim AS base-docker
FROM base-${BASEIMGTYPE} AS base
@@ -22,23 +22,13 @@ RUN \
python3=3.9.2-3 \
python3-pip=20.3.4-4+deb11u1 \
python3-setuptools=52.0.0-4 \
python3-pil=8.1.2+dfsg-0.3+deb11u1 \
python3-cryptography=3.3.2-1 \
python3-venv=3.9.2-3 \
iputils-ping=3:20210202-1 \
git=1:2.30.2-1+deb11u2 \
curl=7.74.0-1.3+deb11u7 \
git=1:2.30.2-1 \
curl=7.74.0-1.3+deb11u3 \
openssh-client=1:8.4p1-5+deb11u1 \
python3-cffi=1.14.5-1 \
libcairo2=1.16.0-5; \
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 \
&& rm -rf \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/*
@@ -61,7 +51,7 @@ RUN \
# Ubuntu python3-pip is missing wheel
pip3 install --no-cache-dir \
wheel==0.37.1 \
platformio==6.1.10 \
platformio==6.1.5 \
# Change some platformio settings
&& platformio settings set enable_telemetry No \
&& platformio settings set check_platformio_interval 1000000 \
@@ -69,10 +59,10 @@ RUN \
# First install requirements to leverage caching when requirements don't change
COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini /
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
&& /platformio_install_deps.py /platformio.ini --libraries
&& /platformio_install_deps.py /platformio.ini
# ======================= docker-type image =======================
@@ -145,7 +135,7 @@ RUN \
apt-get update \
# Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \
clang-format-13=1:13.0.1-6~deb11u1 \
clang-format-11=1:11.0.1-2 \
clang-tidy-11=1:11.0.1-2 \
patch=2.7.6-7 \
software-properties-common=0.96.20.2-2.1 \

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env python3
# This script is used in the docker containers to preinstall
# all platformio libraries in the global storage
import configparser
import subprocess
import sys
config = configparser.ConfigParser(inline_comment_prefixes=(';', ))
config.read(sys.argv[1])
libs = []
# Extract from every lib_deps key in all sections
for section in config.sections():
conf = config[section]
if "lib_deps" not in conf:
continue
for lib_dep in conf["lib_deps"].splitlines():
if not lib_dep:
# Empty line or comment
continue
if lib_dep.startswith("${"):
# Extending from another section
continue
if "@" not in lib_dep:
# No version pinned, this is an internal lib
continue
libs.append(lib_dep)
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])

View File

@@ -18,9 +18,6 @@ from esphome.const import (
CONF_LOGGER,
CONF_NAME,
CONF_OTA,
CONF_MQTT,
CONF_MDNS,
CONF_DISABLED,
CONF_PASSWORD,
CONF_PORT,
CONF_ESPHOME,
@@ -32,7 +29,7 @@ from esphome.const import (
SECRETS_FILES,
)
from esphome.core import CORE, EsphomeError, coroutine
from esphome.helpers import indent, is_ip_address
from esphome.helpers import indent
from esphome.util import (
run_external_command,
run_external_process,
@@ -45,7 +42,7 @@ from esphome.log import color, setup_log, Fore
_LOGGER = logging.getLogger(__name__)
def choose_prompt(options, purpose: str = None):
def choose_prompt(options):
if not options:
raise EsphomeError(
"Found no valid options for upload/logging, please make sure relevant "
@@ -56,9 +53,7 @@ def choose_prompt(options, purpose: str = None):
if len(options) == 1:
return options[0][1]
safe_print(
f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:'
)
safe_print("Found multiple options, please choose one:")
for i, (desc, _) in enumerate(options):
safe_print(f" [{i+1}] {desc}")
@@ -77,9 +72,7 @@ def choose_prompt(options, purpose: str = None):
return options[opt - 1][1]
def choose_upload_log_host(
default, check_default, show_ota, show_mqtt, show_api, purpose: str = None
):
def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
options = []
for port in get_serial_ports():
options.append((f"{port.path} ({port.description})", port.path))
@@ -87,7 +80,7 @@ def choose_upload_log_host(
options.append((f"Over The Air ({CORE.address})", CORE.address))
if default == "OTA":
return CORE.address
if show_mqtt and CONF_MQTT in CORE.config:
if show_mqtt and "mqtt" in CORE.config:
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
if default == "OTA":
return "MQTT"
@@ -95,7 +88,7 @@ def choose_upload_log_host(
return default
if check_default is not None and check_default in [opt[1] for opt in options]:
return check_default
return choose_prompt(options, purpose=purpose)
return choose_prompt(options)
def get_port_type(port):
@@ -159,8 +152,6 @@ def run_miniterm(config, port):
_LOGGER.error("Could not connect to serial port %s", port)
return 1
return 0
def wrap_to_code(name, comp):
coro = coroutine(comp.to_code)
@@ -295,32 +286,19 @@ def upload_program(config, args, host):
return 1 # Unknown target platform
from esphome import espota2
if CONF_OTA not in config:
raise EsphomeError(
"Cannot upload Over the Air as the config does not include the ota: "
"component"
)
from esphome import espota2
ota_conf = config[CONF_OTA]
remote_port = ota_conf[CONF_PORT]
password = ota_conf.get(CONF_PASSWORD, "")
if (
not is_ip_address(CORE.address)
and (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED])
and CONF_MQTT in config
):
from esphome import mqtt
host = mqtt.get_esphome_device_ip(
config, args.username, args.password, args.client_id
)
if getattr(args, "file", None) is not None:
return espota2.run_ota(host, remote_port, password, args.file)
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
@@ -330,13 +308,6 @@ def show_logs(config, args, port):
if get_port_type(port) == "SERIAL":
return run_miniterm(config, port)
if get_port_type(port) == "NETWORK" and "api" in config:
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
from esphome import mqtt
port = mqtt.get_esphome_device_ip(
config, args.username, args.password, args.client_id
)
from esphome.components.api.client import run_logs
return run_logs(config, port)
@@ -365,16 +336,10 @@ def command_wizard(args):
def command_config(args, config):
_LOGGER.info("Configuration is valid!")
if not CORE.verbose:
config = strip_default_ids(config)
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)\:\s(.*)", r"\1: \\033[5m\2\\033[6m", output
)
safe_print(output)
_LOGGER.info("Configuration is valid!")
safe_print(yaml_util.dump(config, args.show_secrets))
return 0
@@ -407,7 +372,6 @@ def command_upload(args, config):
show_ota=True,
show_mqtt=False,
show_api=False,
purpose="uploading",
)
exit_code = upload_program(config, args, port)
if exit_code != 0:
@@ -416,15 +380,6 @@ def command_upload(args, config):
return 0
def command_discover(args, config):
if "mqtt" in config:
from esphome import mqtt
return mqtt.show_discover(config, args.username, args.password, args.client_id)
raise EsphomeError("No discover method configured (mqtt)")
def command_logs(args, config):
port = choose_upload_log_host(
default=args.device,
@@ -432,7 +387,6 @@ def command_logs(args, config):
show_ota=False,
show_mqtt=True,
show_api=True,
purpose="logging",
)
return show_logs(config, args, port)
@@ -451,7 +405,6 @@ def command_run(args, config):
show_ota=True,
show_mqtt=False,
show_api=True,
purpose="uploading",
)
exit_code = upload_program(config, args, port)
if exit_code != 0:
@@ -465,7 +418,6 @@ def command_run(args, config):
show_ota=False,
show_mqtt=True,
show_api=True,
purpose="logging",
)
return show_logs(config, args, port)
@@ -669,7 +621,6 @@ POST_CONFIG_ACTIONS = {
"clean": command_clean,
"idedata": command_idedata,
"rename": command_rename,
"discover": command_discover,
}
@@ -758,15 +709,6 @@ def parse_args(argv):
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
)
parser_discover = subparsers.add_parser(
"discover",
help="Validate the configuration and show all discovered devices.",
parents=[mqtt_options],
)
parser_discover.add_argument(
"configuration", help="Your YAML configuration file.", nargs=1
)
parser_run = subparsers.add_parser(
"run",
help="Validate the configuration, create a binary, upload it, and start logs.",
@@ -988,8 +930,6 @@ def run_esphome(argv):
_LOGGER.error(e, exc_info=args.verbose)
return 1
_LOGGER.info("ESPHome %s", const.__version__)
for conf_path in args.configuration:
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
_LOGGER.warning("Skipping secrets file %s", conf_path)

View File

@@ -254,11 +254,7 @@ async def repeat_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
count_template = await cg.templatable(config[CONF_COUNT], args, cg.uint32)
cg.add(var.set_count(count_template))
actions = await build_action_list(
config[CONF_THEN],
cg.TemplateArguments(cg.uint32, *template_arg.args),
[(cg.uint32, "iteration"), *args],
)
actions = await build_action_list(config[CONF_THEN], template_arg, args)
cg.add(var.add_then(actions))
return var

View File

@@ -47,7 +47,6 @@ from esphome.cpp_helpers import ( # noqa
build_registry_list,
extract_registry_entry_config,
register_parented,
past_safe_mode,
)
from esphome.cpp_types import ( # noqa
global_ns,
@@ -64,7 +63,6 @@ from esphome.cpp_types import ( # noqa
uint16,
uint32,
uint64,
int16,
int32,
int64,
size_t,

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@MrSuicideParrot"]

View File

@@ -1,57 +0,0 @@
// 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

View File

@@ -1,27 +0,0 @@
#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

View File

@@ -1,41 +0,0 @@
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)

View File

@@ -28,6 +28,6 @@ async def to_code(config):
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
cg.add(var.set_dir_pin(dir_pin))
if sleep_pin_config := config.get(CONF_SLEEP_PIN):
sleep_pin = await cg.gpio_pin_expression(sleep_pin_config)
if CONF_SLEEP_PIN in config:
sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
cg.add(var.set_sleep_pin(sleep_pin))

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@DAVe3283"]

View File

@@ -1,182 +0,0 @@
#include "esphome/core/log.h"
#include "absolute_humidity.h"
namespace esphome {
namespace absolute_humidity {
static const char *const TAG = "absolute_humidity.sensor";
void AbsoluteHumidityComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up absolute humidity '%s'...", this->get_name().c_str());
ESP_LOGD(TAG, " Added callback for temperature '%s'", this->temperature_sensor_->get_name().c_str());
this->temperature_sensor_->add_on_state_callback([this](float state) { this->temperature_callback_(state); });
if (this->temperature_sensor_->has_state()) {
this->temperature_callback_(this->temperature_sensor_->get_state());
}
ESP_LOGD(TAG, " Added callback for relative humidity '%s'", this->humidity_sensor_->get_name().c_str());
this->humidity_sensor_->add_on_state_callback([this](float state) { this->humidity_callback_(state); });
if (this->humidity_sensor_->has_state()) {
this->humidity_callback_(this->humidity_sensor_->get_state());
}
}
void AbsoluteHumidityComponent::dump_config() {
LOG_SENSOR("", "Absolute Humidity", this);
switch (this->equation_) {
case BUCK:
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Buck");
break;
case TETENS:
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Tetens");
break;
case WOBUS:
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Wobus");
break;
default:
ESP_LOGE(TAG, "Invalid saturation vapor pressure equation selection!");
break;
}
ESP_LOGCONFIG(TAG, "Sources");
ESP_LOGCONFIG(TAG, " Temperature: '%s'", this->temperature_sensor_->get_name().c_str());
ESP_LOGCONFIG(TAG, " Relative Humidity: '%s'", this->humidity_sensor_->get_name().c_str());
}
float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; }
void AbsoluteHumidityComponent::loop() {
if (!this->next_update_) {
return;
}
this->next_update_ = false;
// Ensure we have source data
const bool no_temperature = std::isnan(this->temperature_);
const bool no_humidity = std::isnan(this->humidity_);
if (no_temperature || no_humidity) {
if (no_temperature) {
ESP_LOGW(TAG, "No valid state from temperature sensor!");
}
if (no_humidity) {
ESP_LOGW(TAG, "No valid state from temperature sensor!");
}
ESP_LOGW(TAG, "Unable to calculate absolute humidity.");
this->publish_state(NAN);
this->status_set_warning();
return;
}
// Convert to desired units
const float temperature_c = this->temperature_;
const float temperature_k = temperature_c + 273.15;
const float hr = this->humidity_ / 100;
// Calculate saturation vapor pressure
float es;
switch (this->equation_) {
case BUCK:
es = es_buck(temperature_c);
break;
case TETENS:
es = es_tetens(temperature_c);
break;
case WOBUS:
es = es_wobus(temperature_c);
break;
default:
ESP_LOGE(TAG, "Invalid saturation vapor pressure equation selection!");
this->publish_state(NAN);
this->status_set_error();
return;
}
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
// Calculate absolute humidity
const float absolute_humidity = vapor_density(es, hr, temperature_k);
// Publish absolute humidity
ESP_LOGD(TAG, "Publishing absolute humidity %f g/m³", absolute_humidity);
this->status_clear_warning();
this->publish_state(absolute_humidity);
}
// Buck equation (https://en.wikipedia.org/wiki/Arden_Buck_equation)
// More accurate than Tetens in normal meteorologic conditions
float AbsoluteHumidityComponent::es_buck(float temperature_c) {
float a, b, c, d;
if (temperature_c >= 0) {
a = 0.61121;
b = 18.678;
c = 234.5;
d = 257.14;
} else {
a = 0.61115;
b = 18.678;
c = 233.7;
d = 279.82;
}
return a * expf((b - (temperature_c / c)) * (temperature_c / (d + temperature_c)));
}
// Tetens equation (https://en.wikipedia.org/wiki/Tetens_equation)
float AbsoluteHumidityComponent::es_tetens(float temperature_c) {
float a, b;
if (temperature_c >= 0) {
a = 17.27;
b = 237.3;
} else {
a = 21.875;
b = 265.5;
}
return 0.61078 * expf((a * temperature_c) / (temperature_c + b));
}
// Wobus equation
// https://wahiduddin.net/calc/density_altitude.htm
// https://wahiduddin.net/calc/density_algorithms.htm
// Calculate the saturation vapor pressure (kPa)
float AbsoluteHumidityComponent::es_wobus(float t) {
// THIS FUNCTION RETURNS THE SATURATION VAPOR PRESSURE ESW (MILLIBARS)
// OVER LIQUID WATER GIVEN THE TEMPERATURE T (CELSIUS). THE POLYNOMIAL
// APPROXIMATION BELOW IS DUE TO HERMAN WOBUS, A MATHEMATICIAN WHO
// WORKED AT THE NAVY WEATHER RESEARCH FACILITY, NORFOLK, VIRGINIA,
// BUT WHO IS NOW RETIRED. THE COEFFICIENTS OF THE POLYNOMIAL WERE
// CHOSEN TO FIT THE VALUES IN TABLE 94 ON PP. 351-353 OF THE SMITH-
// SONIAN METEOROLOGICAL TABLES BY ROLAND LIST (6TH EDITION). THE
// APPROXIMATION IS VALID FOR -50 < T < 100C.
//
// Baker, Schlatter 17-MAY-1982 Original version.
const float c0 = +0.99999683e00;
const float c1 = -0.90826951e-02;
const float c2 = +0.78736169e-04;
const float c3 = -0.61117958e-06;
const float c4 = +0.43884187e-08;
const float c5 = -0.29883885e-10;
const float c6 = +0.21874425e-12;
const float c7 = -0.17892321e-14;
const float c8 = +0.11112018e-16;
const float c9 = -0.30994571e-19;
const float p = c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * (c6 + t * (c7 + t * (c8 + t * (c9)))))))));
return 0.61078 / pow(p, 8);
}
// From https://www.environmentalbiophysics.org/chalk-talk-how-to-calculate-absolute-humidity/
// H/T to https://esphome.io/cookbook/bme280_environment.html
// H/T to https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
float AbsoluteHumidityComponent::vapor_density(float es, float hr, float ta) {
// es = saturated vapor pressure (kPa)
// hr = relative humidity [0-1]
// ta = absolute temperature (K)
const float ea = hr * es * 1000; // vapor pressure of the air (Pa)
const float mw = 18.01528; // molar mass of water (g⋅mol⁻¹)
const float r = 8.31446261815324; // molar gas constant (J⋅K⁻¹)
return (ea * mw) / (r * ta);
}
} // namespace absolute_humidity
} // namespace esphome

View File

@@ -1,76 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace absolute_humidity {
/// Enum listing all implemented saturation vapor pressure equations.
enum SaturationVaporPressureEquation {
BUCK,
TETENS,
WOBUS,
};
/// This class implements calculation of absolute humidity from temperature and relative humidity.
class AbsoluteHumidityComponent : public sensor::Sensor, public Component {
public:
AbsoluteHumidityComponent() = default;
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
void set_equation(SaturationVaporPressureEquation equation) { this->equation_ = equation; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void loop() override;
protected:
void temperature_callback_(float state) {
this->next_update_ = true;
this->temperature_ = state;
}
void humidity_callback_(float state) {
this->next_update_ = true;
this->humidity_ = state;
}
/** Buck equation for saturation vapor pressure in kPa.
*
* @param temperature_c Air temperature in °C.
*/
static float es_buck(float temperature_c);
/** Tetens equation for saturation vapor pressure in kPa.
*
* @param temperature_c Air temperature in °C.
*/
static float es_tetens(float temperature_c);
/** Wobus equation for saturation vapor pressure in kPa.
*
* @param temperature_c Air temperature in °C.
*/
static float es_wobus(float temperature_c);
/** Calculate vapor density (absolute humidity) in g/m³.
*
* @param es Saturation vapor pressure in kPa.
* @param hr Relative humidity 0 to 1.
* @param ta Absolute temperature in K.
* @param heater_duration The duration in ms that the heater should turn on for when measuring.
*/
static float vapor_density(float es, float hr, float ta);
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
bool next_update_{false};
float temperature_{NAN};
float humidity_{NAN};
SaturationVaporPressureEquation equation_;
};
} // namespace absolute_humidity
} // namespace esphome

View File

@@ -1,56 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_HUMIDITY,
CONF_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
CONF_EQUATION,
ICON_WATER,
UNIT_GRAMS_PER_CUBIC_METER,
)
absolute_humidity_ns = cg.esphome_ns.namespace("absolute_humidity")
AbsoluteHumidityComponent = absolute_humidity_ns.class_(
"AbsoluteHumidityComponent", sensor.Sensor, cg.Component
)
SaturationVaporPressureEquation = absolute_humidity_ns.enum(
"SaturationVaporPressureEquation"
)
EQUATION = {
"BUCK": SaturationVaporPressureEquation.BUCK,
"TETENS": SaturationVaporPressureEquation.TETENS,
"WOBUS": SaturationVaporPressureEquation.WOBUS,
}
CONFIG_SCHEMA = (
sensor.sensor_schema(
unit_of_measurement=UNIT_GRAMS_PER_CUBIC_METER,
icon=ICON_WATER,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{
cv.GenerateID(): cv.declare_id(AbsoluteHumidityComponent),
cv.Required(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
cv.Required(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
cv.Optional(CONF_EQUATION, default="WOBUS"): cv.enum(EQUATION, upper=True),
}
)
.extend(cv.COMPONENT_SCHEMA)
)
async def to_code(config):
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
temperature_sensor = await cg.get_variable(config[CONF_TEMPERATURE])
cg.add(var.set_temperature_sensor(temperature_sensor))
humidity_sensor = await cg.get_variable(config[CONF_HUMIDITY])
cg.add(var.set_humidity_sensor(humidity_sensor))
cg.add(var.set_equation(config[CONF_EQUATION]))

View File

@@ -1,187 +1 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import CONF_INPUT
from esphome.core import CORE
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32,
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
CODEOWNERS = ["@esphome/core"]
ATTENUATION_MODES = {
"0db": cg.global_ns.ADC_ATTEN_DB_0,
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
"6db": cg.global_ns.ADC_ATTEN_DB_6,
"11db": cg.global_ns.ADC_ATTEN_DB_11,
"auto": "auto",
}
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
# pin to adc1 channel mapping
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
VARIANT_ESP32: {
36: adc1_channel_t.ADC1_CHANNEL_0,
37: adc1_channel_t.ADC1_CHANNEL_1,
38: adc1_channel_t.ADC1_CHANNEL_2,
39: adc1_channel_t.ADC1_CHANNEL_3,
32: adc1_channel_t.ADC1_CHANNEL_4,
33: adc1_channel_t.ADC1_CHANNEL_5,
34: adc1_channel_t.ADC1_CHANNEL_6,
35: adc1_channel_t.ADC1_CHANNEL_7,
},
VARIANT_ESP32S2: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
},
VARIANT_ESP32S3: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
},
VARIANT_ESP32C3: {
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_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: {
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,
},
}
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):
if str(value).upper() == "VCC":
return cv.only_on_esp8266("VCC")
if str(value).upper() == "TEMPERATURE":
return cv.only_on_rp2040("TEMPERATURE")
if CORE.is_esp32:
value = pins.internal_gpio_input_pin_number(value)
variant = get_esp32_variant()
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")
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")
return pins.internal_gpio_input_pin_schema(value)
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
)
if value != 17: # A0
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC")
return pins.gpio_pin_schema(
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value)
if CORE.is_rp2040:
value = pins.internal_gpio_input_pin_number(value)
if value not in (26, 27, 28, 29):
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC")
return pins.internal_gpio_input_pin_schema(value)
raise NotImplementedError

View File

@@ -20,15 +20,15 @@ namespace adc {
static const char *const TAG = "adc";
// 13-bit for S2, 12-bit for all other ESP32 variants
// 13bit for S2, and 12bit for all other esp32 variants
#ifdef USE_ESP32
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
#if USE_ESP32_VARIANT_ESP32S2
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
static const int SOC_ADC_RTC_MAX_BITWIDTH = 13;
#else
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
static const int SOC_ADC_RTC_MAX_BITWIDTH = 12;
#endif
#endif
@@ -47,21 +47,14 @@ extern "C"
#endif
#ifdef USE_ESP32
if (channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!autorange_) {
adc1_config_channel_atten(channel1_, attenuation_);
}
} else if (channel2_ != ADC2_CHANNEL_MAX) {
if (!autorange_) {
adc2_config_channel_atten(channel2_, attenuation_);
}
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!autorange_) {
adc1_config_channel_atten(channel_, attenuation_);
}
// load characteristics for each attenuation
for (int32_t i = 0; i <= ADC_ATTEN_DB_11; i++) {
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,
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
1100, // default vref
&cal_characteristics_[i]);
switch (cal_value) {
@@ -143,9 +136,9 @@ void ADCSensor::update() {
#ifdef USE_ESP8266
float ADCSensor::sample() {
#ifdef USE_ADC_SENSOR_VCC
int32_t raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
int32_t raw = analogRead(this->pin_->get_pin()); // NOLINT
int raw = analogRead(this->pin_->get_pin()); // NOLINT
#endif
if (output_raw_) {
return raw;
@@ -157,53 +150,29 @@ float ADCSensor::sample() {
#ifdef USE_ESP32
float ADCSensor::sample() {
if (!autorange_) {
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);
}
int raw = adc1_get_raw(channel_);
if (raw == -1) {
return NAN;
}
if (output_raw_) {
return raw;
}
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int32_t) attenuation_]);
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
return mv / 1000.0f;
}
int raw11 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
if (channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel1_);
if (raw11 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel1_);
if (raw6 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel1_);
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);
}
int raw11, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel_);
if (raw11 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel_);
if (raw6 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel_);
if (raw2 < ADC_MAX) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel_);
}
}
}
@@ -212,10 +181,10 @@ float ADCSensor::sample() {
return NAN;
}
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_[(int32_t) ADC_ATTEN_DB_6]);
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_[(int32_t) ADC_ATTEN_DB_0]);
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
// 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);
@@ -243,7 +212,7 @@ float ADCSensor::sample() {
adc_select_input(pin - 26);
}
int32_t raw = adc_read();
int raw = adc_read();
if (this->is_temperature_) {
adc_set_temp_sensor_enabled(false);
}

View File

@@ -19,23 +19,16 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_ESP32
/// Set the attenuation for this pin. Only available on the ESP32.
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
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_channel(adc1_channel_t channel) { channel_ = channel; }
void set_autorange(bool autorange) { autorange_ = autorange; }
#endif
/// Update ADC values
/// Update adc values.
void update() override;
/// Setup ADC
/// Setup ADc
void setup() override;
void dump_config() override;
/// `HARDWARE_LATE` setup priority
/// `HARDWARE_LATE` setup priority.
float get_setup_priority() const override;
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
@@ -59,10 +52,9 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#ifdef USE_ESP32
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
adc1_channel_t channel_{};
bool autorange_{false};
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
esp_adc_cal_characteristics_t cal_characteristics_[(int) ADC_ATTEN_MAX] = {};
#endif
};

View File

@@ -1,49 +1,137 @@
import esphome.codegen as cg
import esphome.config_validation as cv
import esphome.final_validate as fv
from esphome.core import CORE
from esphome import pins
from esphome.components import sensor, voltage_sampler
from esphome.components.esp32 import get_esp32_variant
from esphome.const import (
CONF_ATTENUATION,
CONF_RAW,
CONF_ID,
CONF_INPUT,
CONF_NUMBER,
CONF_PIN,
CONF_RAW,
CONF_WIFI,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
)
from . import (
ATTENUATION_MODES,
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
validate_adc_pin,
from esphome.core import CORE
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32,
VARIANT_ESP32C3,
VARIANT_ESP32H2,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
AUTO_LOAD = ["voltage_sampler"]
ATTENUATION_MODES = {
"0db": cg.global_ns.ADC_ATTEN_DB_0,
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
"6db": cg.global_ns.ADC_ATTEN_DB_6,
"11db": cg.global_ns.ADC_ATTEN_DB_11,
"auto": "auto",
}
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
# pin to adc1 channel mapping
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
VARIANT_ESP32: {
36: adc1_channel_t.ADC1_CHANNEL_0,
37: adc1_channel_t.ADC1_CHANNEL_1,
38: adc1_channel_t.ADC1_CHANNEL_2,
39: adc1_channel_t.ADC1_CHANNEL_3,
32: adc1_channel_t.ADC1_CHANNEL_4,
33: adc1_channel_t.ADC1_CHANNEL_5,
34: adc1_channel_t.ADC1_CHANNEL_6,
35: adc1_channel_t.ADC1_CHANNEL_7,
},
VARIANT_ESP32S2: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
},
VARIANT_ESP32S3: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
},
VARIANT_ESP32C3: {
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_ESP32H2: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
}
def validate_adc_pin(value):
if str(value).upper() == "VCC":
return cv.only_on_esp8266("VCC")
if str(value).upper() == "TEMPERATURE":
return cv.only_on_rp2040("TEMPERATURE")
if CORE.is_esp32:
value = pins.internal_gpio_input_pin_number(value)
variant = get_esp32_variant()
if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
return pins.internal_gpio_input_pin_schema(value)
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
)
if value != 17: # A0
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
return pins.gpio_pin_schema(
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value)
if CORE.is_rp2040:
value = pins.internal_gpio_input_pin_number(value)
if value not in (26, 27, 28, 29):
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC.")
return pins.internal_gpio_input_pin_schema(value)
raise NotImplementedError
def validate_config(config):
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
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"
)
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set.")
return config
@@ -73,8 +161,6 @@ CONFIG_SCHEMA = cv.All(
validate_config,
)
FINAL_VALIDATE_SCHEMA = final_validate_config
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
@@ -89,26 +175,17 @@ async def to_code(config):
pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))
cg.add(var.set_output_raw(config[CONF_RAW]))
if CONF_RAW in config:
cg.add(var.set_output_raw(config[CONF_RAW]))
if attenuation := config.get(CONF_ATTENUATION):
if attenuation == "auto":
if CONF_ATTENUATION in config:
if config[CONF_ATTENUATION] == "auto":
cg.add(var.set_autorange(cg.global_ns.true))
else:
cg.add(var.set_attenuation(attenuation))
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
if CORE.is_esp32:
variant = get_esp32_variant()
pin_num = config[CONF_PIN][CONF_NUMBER]
if (
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))
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel(chan))

View File

@@ -16,16 +16,13 @@ ADC128S102Sensor = adc128s102_ns.class_(
)
CONF_ADC128S102_ID = "adc128s102_id"
CONFIG_SCHEMA = (
sensor.sensor_schema(ADC128S102Sensor)
.extend(
{
cv.GenerateID(CONF_ADC128S102_ID): cv.use_id(ADC128S102),
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
}
)
.extend(cv.polling_component_schema("60s"))
)
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(ADC128S102Sensor),
cv.GenerateID(CONF_ADC128S102_ID): cv.use_id(ADC128S102),
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
}
).extend(cv.polling_component_schema("60s"))
async def to_code(config):

View File

@@ -48,16 +48,16 @@ async def to_code(config):
await cg.register_component(var, config)
await display.register_display(var, config)
if pixel_mapper := config.get(CONF_PIXEL_MAPPER):
if CONF_PIXEL_MAPPER in config:
pixel_mapper_template_ = await cg.process_lambda(
pixel_mapper,
config[CONF_PIXEL_MAPPER],
[(int, "x"), (int, "y")],
return_type=cg.int_,
)
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
if lambda_config := config.get(CONF_LAMBDA):
if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda(
lambda_config, [(display.DisplayRef, "it")], return_type=cg.void
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))

View File

@@ -72,8 +72,8 @@ async def to_code(config):
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if irq_pin_config := config.get(CONF_IRQ_PIN):
irq_pin = await cg.gpio_pin_expression(irq_pin_config)
if CONF_IRQ_PIN in config:
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
cg.add(var.set_irq_pin(irq_pin))
for key in [

View File

@@ -45,10 +45,10 @@ async def to_code(config):
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if temperature := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature)
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature_sensor(sens))
if humidity := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humidity)
if CONF_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity_sensor(sens))

View File

@@ -1,6 +1,5 @@
#include "airthings_listener.h"
#include "esphome/core/log.h"
#include <cinttypes>
#ifdef USE_ESP32
@@ -20,7 +19,7 @@ bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &devic
sn |= ((uint32_t) it.data[2] << 16);
sn |= ((uint32_t) it.data[3] << 24);
ESP_LOGD(TAG, "Found AirThings device Serial:%" PRIu32 " (MAC: %s)", sn, device.address_str().c_str());
ESP_LOGD(TAG, "Found AirThings device Serial:%u (MAC: %s)", sn, device.address_str().c_str());
return true;
}
}

View File

@@ -1,103 +0,0 @@
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))

View File

@@ -1,211 +0,0 @@
#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

View File

@@ -1,90 +0,0 @@
#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

View File

@@ -7,47 +7,105 @@ namespace airthings_wave_mini {
static const char *const TAG = "airthings_wave_mini";
void AirthingsWaveMini::read_sensors(uint8_t *raw_value, uint16_t value_len) {
void AirthingsWaveMini::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: {
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;
if (sizeof(WaveMiniReadings) <= value_len) {
if (this->humidity_sensor_ != nullptr) {
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
}
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->humidity_sensor_->publish_state(value->humidity / 100.0f);
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
if (is_valid_voc_value_(value->voc)) {
this->tvoc_sensor_->publish_state(value->voc);
}
}
this->response_received_();
// 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; }
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() {
// 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(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
}
AirthingsWaveMini::AirthingsWaveMini() {
this->service_uuid_ = espbt::ESPBTUUID::from_raw(SERVICE_UUID);
this->sensors_data_characteristic_uuid_ = espbt::ESPBTUUID::from_raw(CHARACTERISTIC_UUID);
this->access_control_point_characteristic_uuid_ =
espbt::ESPBTUUID::from_raw(ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID);
}
AirthingsWaveMini::AirthingsWaveMini()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
} // namespace airthings_wave_mini
} // namespace esphome

View File

@@ -2,25 +2,50 @@
#ifdef USE_ESP32
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
#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_mini {
namespace espbt = esphome::esp32_ble_tracker;
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 ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID = "b42e3ef4-ade7-11e4-89d3-123b93f75cba";
class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase {
class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode {
public:
AirthingsWaveMini();
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:
void read_sensors(uint8_t *raw_value, uint16_t value_len) override;
bool is_valid_voc_value_(uint16_t voc);
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 {
uint16_t unused01;

View File

@@ -1,28 +1,82 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import airthings_wave_base
from esphome.components import sensor, ble_client
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_HUMIDITY,
CONF_TVOC,
CONF_PRESSURE,
CONF_TEMPERATURE,
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
)
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
AUTO_LOAD = ["airthings_wave_base"]
DEPENDENCIES = ["ble_client"]
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
AirthingsWaveMini = airthings_wave_mini_ns.class_(
"AirthingsWaveMini", airthings_wave_base.AirthingsWaveBase
"AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode
)
CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
}
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
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):
var = cg.new_Pvariable(config[CONF_ID])
await airthings_wave_base.wave_base_to_code(var, config)
await cg.register_component(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))

View File

@@ -7,7 +7,55 @@ namespace airthings_wave_plus {
static const char *const TAG = "airthings_wave_plus";
void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
void AirthingsWavePlus::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: {
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;
if (sizeof(WavePlusReadings) <= value_len) {
@@ -16,66 +64,72 @@ void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
if (value->version == 1) {
ESP_LOGD(TAG, "ambient light = %d", value->ambientLight);
if (this->humidity_sensor_ != nullptr) {
this->humidity_sensor_->publish_state(value->humidity / 2.0f);
}
if ((this->radon_sensor_ != nullptr) && this->is_valid_radon_value_(value->radon)) {
this->humidity_sensor_->publish_state(value->humidity / 2.0f);
if (is_valid_radon_value_(value->radon)) {
this->radon_sensor_->publish_state(value->radon);
}
if ((this->radon_long_term_sensor_ != nullptr) && this->is_valid_radon_value_(value->radon_lt)) {
if (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->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 ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) {
if (is_valid_voc_value_(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);
} else {
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 radon <= 16383; }
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return co2 <= 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 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() {
// 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(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_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(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
}
AirthingsWavePlus::AirthingsWavePlus() {
this->service_uuid_ = espbt::ESPBTUUID::from_raw(SERVICE_UUID);
this->sensors_data_characteristic_uuid_ = espbt::ESPBTUUID::from_raw(CHARACTERISTIC_UUID);
this->access_control_point_characteristic_uuid_ =
espbt::ESPBTUUID::from_raw(ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID);
}
AirthingsWavePlus::AirthingsWavePlus()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
} // namespace airthings_wave_plus
} // namespace esphome

View File

@@ -2,36 +2,58 @@
#ifdef USE_ESP32
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
#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_plus {
namespace espbt = esphome::esp32_ble_tracker;
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 ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID = "b42e2d06-ade7-11e4-89d3-123b93f75cba";
class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
public:
AirthingsWavePlus();
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_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_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
protected:
bool is_valid_radon_value_(uint16_t radon);
bool is_valid_voc_value_(uint16_t voc);
bool is_valid_co2_value_(uint16_t co2);
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 *radon_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 *tvoc_sensor_{nullptr};
uint16_t handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
struct WavePlusReadings {
uint8_t version;

View File

@@ -1,64 +1,116 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, airthings_wave_base
from esphome.components import sensor, ble_client
from esphome.const import (
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
ICON_RADIOACTIVE,
CONF_ID,
CONF_RADON,
CONF_RADON_LONG_TERM,
CONF_HUMIDITY,
CONF_TVOC,
CONF_CO2,
CONF_PRESSURE,
CONF_TEMPERATURE,
UNIT_BECQUEREL_PER_CUBIC_METER,
UNIT_PARTS_PER_MILLION,
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
)
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
AUTO_LOAD = ["airthings_wave_base"]
DEPENDENCIES = ["ble_client"]
airthings_wave_plus_ns = cg.esphome_ns.namespace("airthings_wave_plus")
AirthingsWavePlus = airthings_wave_plus_ns.class_(
"AirthingsWavePlus", airthings_wave_base.AirthingsWaveBase
"AirthingsWavePlus", cg.PollingComponent, ble_client.BLEClientNode
)
CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
cv.Optional(CONF_RADON): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
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,
),
}
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
accuracy_decimals=0,
),
cv.Optional(CONF_RADON): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADON_LONG_TERM): sensor.sensor_schema(
unit_of_measurement=UNIT_BECQUEREL_PER_CUBIC_METER,
icon=ICON_RADIOACTIVE,
accuracy_decimals=0,
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):
var = cg.new_Pvariable(config[CONF_ID])
await airthings_wave_base.wave_base_to_code(var, config)
await cg.register_component(var, config)
if config_radon := config.get(CONF_RADON):
sens = await sensor.new_sensor(config_radon)
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_RADON in config:
sens = await sensor.new_sensor(config[CONF_RADON])
cg.add(var.set_radon(sens))
if config_radon_long_term := config.get(CONF_RADON_LONG_TERM):
sens = await sensor.new_sensor(config_radon_long_term)
if CONF_RADON_LONG_TERM in config:
sens = await sensor.new_sensor(config[CONF_RADON_LONG_TERM])
cg.add(var.set_radon_long_term(sens))
if config_co2 := config.get(CONF_CO2):
sens = await sensor.new_sensor(config_co2)
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_CO2 in config:
sens = await sensor.new_sensor(config[CONF_CO2])
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))

View File

@@ -1,250 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.core import CORE, coroutine_with_priority
from esphome.const import (
CONF_ID,
CONF_ON_STATE,
CONF_TRIGGER_ID,
CONF_CODE,
)
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@grahambrown11"]
IS_PLATFORM_COMPONENT = True
CONF_ON_TRIGGERED = "on_triggered"
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")
AlarmControlPanel = alarm_control_panel_ns.class_("AlarmControlPanel", cg.EntityBase)
StateTrigger = alarm_control_panel_ns.class_(
"StateTrigger", automation.Trigger.template()
)
TriggeredTrigger = alarm_control_panel_ns.class_(
"TriggeredTrigger", automation.Trigger.template()
)
ClearedTrigger = alarm_control_panel_ns.class_(
"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)
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)
PendingAction = alarm_control_panel_ns.class_("PendingAction", automation.Action)
TriggeredAction = alarm_control_panel_ns.class_("TriggeredAction", automation.Action)
AlarmControlPanelCondition = alarm_control_panel_ns.class_(
"AlarmControlPanelCondition", automation.Condition
)
ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AlarmControlPanel),
cv.Optional(CONF_ON_STATE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation(
{
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.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger),
}
),
}
)
ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
{
cv.GenerateID(): cv.use_id(AlarmControlPanel),
cv.Optional(CONF_CODE): cv.templatable(cv.string),
}
)
ALARM_CONTROL_PANEL_CONDITION_SCHEMA = maybe_simple_id(
{
cv.GenerateID(): cv.use_id(AlarmControlPanel),
}
)
async def setup_alarm_control_panel_core_(var, config):
await setup_entity(var, config)
for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_TRIGGERED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
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, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
async def register_alarm_control_panel(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_alarm_control_panel(var))
await setup_alarm_control_panel_core_(var, config)
@automation.register_action(
"alarm_control_panel.arm_away", ArmAwayAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
)
async def alarm_action_arm_away_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_home", ArmHomeAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
)
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])
var = cg.new_Pvariable(action_id, template_arg, paren)
if CONF_CODE in config:
templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
@automation.register_action(
"alarm_control_panel.disarm", DisarmAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
)
async def alarm_action_disarm_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.pending", PendingAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
)
async def alarm_action_pending_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)
return var
@automation.register_action(
"alarm_control_panel.triggered", TriggeredAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
)
async def alarm_action_trigger_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)
return var
@automation.register_condition(
"alarm_control_panel.is_armed",
AlarmControlPanelCondition,
ALARM_CONTROL_PANEL_CONDITION_SCHEMA,
)
async def alarm_control_panel_is_armed_to_code(
config, condition_id, template_arg, args
):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(condition_id, template_arg, paren)
@coroutine_with_priority(100.0)
async def to_code(config):
cg.add_global(alarm_control_panel_ns.using)
cg.add_define("USE_ALARM_CONTROL_PANEL")

View File

@@ -1,148 +0,0 @@
#include <utility>
#include "alarm_control_panel.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace alarm_control_panel {
static const char *const TAG = "alarm_control_panel";
AlarmControlPanelCall AlarmControlPanel::make_call() { return AlarmControlPanelCall(this); }
bool AlarmControlPanel::is_state_armed(AlarmControlPanelState state) {
switch (state) {
case ACP_STATE_ARMED_AWAY:
case ACP_STATE_ARMED_HOME:
case ACP_STATE_ARMED_NIGHT:
case ACP_STATE_ARMED_VACATION:
case ACP_STATE_ARMED_CUSTOM_BYPASS:
return true;
default:
return false;
}
};
void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
this->last_update_ = millis();
if (state != this->current_state_) {
auto prev_state = this->current_state_;
ESP_LOGD(TAG, "Set state to: %s, previous: %s", LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
this->current_state_ = state;
this->state_callback_.call();
if (state == ACP_STATE_TRIGGERED) {
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) {
this->cleared_callback_.call();
}
if (state == this->desired_state_) {
// only store when in the desired state
this->pref_.save(&state);
}
}
}
void AlarmControlPanel::add_on_state_callback(std::function<void()> &&callback) {
this->state_callback_.add(std::move(callback));
}
void AlarmControlPanel::add_on_triggered_callback(std::function<void()> &&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) {
this->cleared_callback_.add(std::move(callback));
}
void AlarmControlPanel::arm_away(optional<std::string> code) {
auto call = this->make_call();
call.arm_away();
if (code.has_value())
call.set_code(code.value());
call.perform();
}
void AlarmControlPanel::arm_home(optional<std::string> code) {
auto call = this->make_call();
call.arm_home();
if (code.has_value())
call.set_code(code.value());
call.perform();
}
void AlarmControlPanel::arm_night(optional<std::string> code) {
auto call = this->make_call();
call.arm_night();
if (code.has_value())
call.set_code(code.value());
call.perform();
}
void AlarmControlPanel::arm_vacation(optional<std::string> code) {
auto call = this->make_call();
call.arm_vacation();
if (code.has_value())
call.set_code(code.value());
call.perform();
}
void AlarmControlPanel::arm_custom_bypass(optional<std::string> code) {
auto call = this->make_call();
call.arm_custom_bypass();
if (code.has_value())
call.set_code(code.value());
call.perform();
}
void AlarmControlPanel::disarm(optional<std::string> code) {
auto call = this->make_call();
call.disarm();
if (code.has_value())
call.set_code(code.value());
call.perform();
}
} // namespace alarm_control_panel
} // namespace esphome

View File

@@ -1,184 +0,0 @@
#pragma once
#include <map>
#include "alarm_control_panel_call.h"
#include "alarm_control_panel_state.h"
#include "esphome/core/automation.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/log.h"
namespace esphome {
namespace alarm_control_panel {
enum AlarmControlPanelFeature : uint8_t {
// Matches Home Assistant values
ACP_FEAT_ARM_HOME = 1 << 0,
ACP_FEAT_ARM_AWAY = 1 << 1,
ACP_FEAT_ARM_NIGHT = 1 << 2,
ACP_FEAT_TRIGGER = 1 << 3,
ACP_FEAT_ARM_CUSTOM_BYPASS = 1 << 4,
ACP_FEAT_ARM_VACATION = 1 << 5,
};
class AlarmControlPanel : public EntityBase {
public:
/** Make a AlarmControlPanelCall
*
*/
AlarmControlPanelCall make_call();
/** Set the state of the alarm_control_panel.
*
* @param state The AlarmControlPanelState.
*/
void publish_state(AlarmControlPanelState state);
/** Add a callback for when the state of the alarm_control_panel changes
*
* @param callback The callback function
*/
void add_on_state_callback(std::function<void()> &&callback);
/** Add a callback for when the state of the alarm_control_panel chanes to triggered
*
* @param callback The callback function
*/
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
*
* @param callback The callback function
*/
void add_on_cleared_callback(std::function<void()> &&callback);
/** A numeric representation of the supported features as per HomeAssistant
*
*/
virtual uint32_t get_supported_features() const = 0;
/** Returns if the alarm_control_panel has a code
*
*/
virtual bool get_requires_code() const = 0;
/** Returns if the alarm_control_panel requires a code to arm
*
*/
virtual bool get_requires_code_to_arm() const = 0;
/** arm the alarm in away mode
*
* @param code The code
*/
void arm_away(optional<std::string> code = nullopt);
/** arm the alarm in home mode
*
* @param code The code
*/
void arm_home(optional<std::string> code = nullopt);
/** arm the alarm in night mode
*
* @param code The code
*/
void arm_night(optional<std::string> code = nullopt);
/** arm the alarm in vacation mode
*
* @param code The code
*/
void arm_vacation(optional<std::string> code = nullopt);
/** arm the alarm in custom bypass mode
*
* @param code The code
*/
void arm_custom_bypass(optional<std::string> code = nullopt);
/** disarm the alarm
*
* @param code The code
*/
void disarm(optional<std::string> code = nullopt);
/** Get the state
*
*/
AlarmControlPanelState get_state() const { return this->current_state_; }
// is the state one of the armed states
bool is_state_armed(AlarmControlPanelState state);
protected:
friend AlarmControlPanelCall;
// in order to store last panel state in flash
ESPPreferenceObject pref_;
// current state
AlarmControlPanelState current_state_;
// the desired (or previous) state
AlarmControlPanelState desired_state_;
// last time the state was updated
uint32_t last_update_;
// the call control function
virtual void control(const AlarmControlPanelCall &call) = 0;
// state callback
CallbackManager<void()> state_callback_{};
// trigger 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
CallbackManager<void()> cleared_callback_{};
};
} // namespace alarm_control_panel
} // namespace esphome

View File

@@ -1,104 +0,0 @@
#include "alarm_control_panel_call.h"
#include "alarm_control_panel.h"
#include "esphome/core/log.h"
namespace esphome {
namespace alarm_control_panel {
static const char *const TAG = "alarm_control_panel";
AlarmControlPanelCall::AlarmControlPanelCall(AlarmControlPanel *parent) : parent_(parent) {}
AlarmControlPanelCall &AlarmControlPanelCall::set_code(const std::string &code) {
this->code_ = code;
return *this;
}
AlarmControlPanelCall &AlarmControlPanelCall::arm_away() {
this->state_ = ACP_STATE_ARMED_AWAY;
return *this;
}
AlarmControlPanelCall &AlarmControlPanelCall::arm_home() {
this->state_ = ACP_STATE_ARMED_HOME;
return *this;
}
AlarmControlPanelCall &AlarmControlPanelCall::arm_night() {
this->state_ = ACP_STATE_ARMED_NIGHT;
return *this;
}
AlarmControlPanelCall &AlarmControlPanelCall::arm_vacation() {
this->state_ = ACP_STATE_ARMED_VACATION;
return *this;
}
AlarmControlPanelCall &AlarmControlPanelCall::arm_custom_bypass() {
this->state_ = ACP_STATE_ARMED_CUSTOM_BYPASS;
return *this;
}
AlarmControlPanelCall &AlarmControlPanelCall::disarm() {
this->state_ = ACP_STATE_DISARMED;
return *this;
}
AlarmControlPanelCall &AlarmControlPanelCall::pending() {
this->state_ = ACP_STATE_PENDING;
return *this;
}
AlarmControlPanelCall &AlarmControlPanelCall::triggered() {
this->state_ = ACP_STATE_TRIGGERED;
return *this;
}
const optional<AlarmControlPanelState> &AlarmControlPanelCall::get_state() const { return this->state_; }
const optional<std::string> &AlarmControlPanelCall::get_code() const { return this->code_; }
void AlarmControlPanelCall::validate_() {
if (this->state_.has_value()) {
auto state = *this->state_;
if (this->parent_->is_state_armed(state) && this->parent_->get_state() != ACP_STATE_DISARMED) {
ESP_LOGW(TAG, "Cannot arm when not disarmed");
this->state_.reset();
return;
}
if (state == ACP_STATE_PENDING && this->parent_->get_state() == ACP_STATE_DISARMED) {
ESP_LOGW(TAG, "Cannot trip alarm when disarmed");
this->state_.reset();
return;
}
if (state == ACP_STATE_DISARMED &&
!(this->parent_->is_state_armed(this->parent_->get_state()) ||
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_ARMING ||
this->parent_->get_state() == ACP_STATE_TRIGGERED)) {
ESP_LOGW(TAG, "Cannot disarm when not armed");
this->state_.reset();
return;
}
if (state == ACP_STATE_ARMED_HOME && (this->parent_->get_supported_features() & ACP_FEAT_ARM_HOME) == 0) {
ESP_LOGW(TAG, "Cannot arm home when not supported");
this->state_.reset();
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;
}
}
}
void AlarmControlPanelCall::perform() {
this->validate_();
if (this->state_) {
this->parent_->control(*this);
}
}
} // namespace alarm_control_panel
} // namespace esphome

View File

@@ -1,40 +0,0 @@
#pragma once
#include <string>
#include "alarm_control_panel_state.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace alarm_control_panel {
class AlarmControlPanel;
class AlarmControlPanelCall {
public:
AlarmControlPanelCall(AlarmControlPanel *parent);
AlarmControlPanelCall &set_code(const std::string &code);
AlarmControlPanelCall &arm_away();
AlarmControlPanelCall &arm_home();
AlarmControlPanelCall &arm_night();
AlarmControlPanelCall &arm_vacation();
AlarmControlPanelCall &arm_custom_bypass();
AlarmControlPanelCall &disarm();
AlarmControlPanelCall &pending();
AlarmControlPanelCall &triggered();
void perform();
const optional<AlarmControlPanelState> &get_state() const;
const optional<std::string> &get_code() const;
protected:
AlarmControlPanel *parent_;
optional<std::string> code_{};
optional<AlarmControlPanelState> state_{};
void validate_();
};
} // namespace alarm_control_panel
} // namespace esphome

View File

@@ -1,34 +0,0 @@
#include "alarm_control_panel_state.h"
namespace esphome {
namespace alarm_control_panel {
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state) {
switch (state) {
case ACP_STATE_DISARMED:
return LOG_STR("DISARMED");
case ACP_STATE_ARMED_HOME:
return LOG_STR("ARMED_HOME");
case ACP_STATE_ARMED_AWAY:
return LOG_STR("ARMED_AWAY");
case ACP_STATE_ARMED_NIGHT:
return LOG_STR("ARMED_NIGHT");
case ACP_STATE_ARMED_VACATION:
return LOG_STR("ARMED_VACATION");
case ACP_STATE_ARMED_CUSTOM_BYPASS:
return LOG_STR("ARMED_CUSTOM_BYPASS");
case ACP_STATE_PENDING:
return LOG_STR("PENDING");
case ACP_STATE_ARMING:
return LOG_STR("ARMING");
case ACP_STATE_DISARMING:
return LOG_STR("DISARMING");
case ACP_STATE_TRIGGERED:
return LOG_STR("TRIGGERED");
default:
return LOG_STR("UNKNOWN");
}
}
} // namespace alarm_control_panel
} // namespace esphome

View File

@@ -1,29 +0,0 @@
#pragma once
#include <cstdint>
#include "esphome/core/log.h"
namespace esphome {
namespace alarm_control_panel {
enum AlarmControlPanelState : uint8_t {
ACP_STATE_DISARMED = 0,
ACP_STATE_ARMED_HOME = 1,
ACP_STATE_ARMED_AWAY = 2,
ACP_STATE_ARMED_NIGHT = 3,
ACP_STATE_ARMED_VACATION = 4,
ACP_STATE_ARMED_CUSTOM_BYPASS = 5,
ACP_STATE_PENDING = 6,
ACP_STATE_ARMING = 7,
ACP_STATE_DISARMING = 8,
ACP_STATE_TRIGGERED = 9
};
/** Returns a string representation of the state.
*
* @param state The AlarmControlPanelState.
*/
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state);
} // namespace alarm_control_panel
} // namespace esphome

View File

@@ -1,177 +0,0 @@
#pragma once
#include "esphome/core/automation.h"
#include "alarm_control_panel.h"
namespace esphome {
namespace alarm_control_panel {
class StateTrigger : public Trigger<> {
public:
explicit StateTrigger(AlarmControlPanel *alarm_control_panel) {
alarm_control_panel->add_on_state_callback([this]() { this->trigger(); });
}
};
class TriggeredTrigger : public Trigger<> {
public:
explicit TriggeredTrigger(AlarmControlPanel *alarm_control_panel) {
alarm_control_panel->add_on_triggered_callback([this]() { this->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<> {
public:
explicit ClearedTrigger(AlarmControlPanel *alarm_control_panel) {
alarm_control_panel->add_on_cleared_callback([this]() { this->trigger(); });
}
};
template<typename... Ts> class ArmAwayAction : public Action<Ts...> {
public:
explicit ArmAwayAction(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_away();
call.perform();
}
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
public:
explicit ArmHomeAction(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_home();
call.perform();
}
protected:
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...> {
public:
explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(Ts... x) override { this->alarm_control_panel_->disarm(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class PendingAction : public Action<Ts...> {
public:
explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
void play(Ts... x) override { this->alarm_control_panel_->make_call().pending().perform(); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class TriggeredAction : public Action<Ts...> {
public:
explicit TriggeredAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
void play(Ts... x) override { this->alarm_control_panel_->make_call().triggered().perform(); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class AlarmControlPanelCondition : public Condition<Ts...> {
public:
AlarmControlPanelCondition(AlarmControlPanel *parent) : parent_(parent) {}
bool check(Ts... x) override {
return this->parent_->is_state_armed(this->parent_->get_state()) ||
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_TRIGGERED;
}
protected:
AlarmControlPanel *parent_;
};
} // namespace alarm_control_panel
} // namespace esphome

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@jan-hofmeier"]

View File

@@ -1,189 +0,0 @@
#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

View File

@@ -1,73 +0,0 @@
#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

View File

@@ -1,85 +0,0 @@
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))

View File

@@ -47,10 +47,10 @@ async def to_code(config):
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature_sensor(sens))
if humidity_config := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humidity_config)
if CONF_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity_sensor(sens))

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@buxtronix"]

View File

@@ -1,6 +1,6 @@
#include "am43_sensor.h"
#include "esphome/core/hal.h"
#include "am43.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#ifdef USE_ESP32

View File

@@ -5,7 +5,7 @@ from esphome.const import CONF_ID, CONF_PIN
CODEOWNERS = ["@buxtronix"]
DEPENDENCIES = ["ble_client"]
AUTO_LOAD = ["am43"]
AUTO_LOAD = ["am43", "sensor"]
CONF_INVERT_POSITION = "invert_position"
@@ -27,10 +27,10 @@ CONFIG_SCHEMA = (
)
async def to_code(config):
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_pin(config[CONF_PIN]))
cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
await cg.register_component(var, config)
await cover.register_cover(var, config)
await ble_client.register_ble_node(var, config)
yield cg.register_component(var, config)
yield cover.register_cover(var, config)
yield ble_client.register_ble_node(var, config)

View File

@@ -40,7 +40,6 @@ void Am43Component::loop() {
CoverTraits Am43Component::get_traits() {
auto traits = CoverTraits();
traits.set_supports_stop(true);
traits.set_supports_position(true);
traits.set_supports_tilt(false);
traits.set_is_assumed_state(false);
@@ -66,7 +65,7 @@ void Am43Component::control(const CoverCall &call) {
if (this->invert_position_)
pos = 1 - pos;
auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t) (pos * 100));
auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
auto status =
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);

View File

@@ -11,7 +11,6 @@ from esphome.const import (
UNIT_PERCENT,
)
AUTO_LOAD = ["am43"]
CODEOWNERS = ["@buxtronix"]
am43_ns = cg.esphome_ns.namespace("am43")
@@ -39,15 +38,15 @@ CONFIG_SCHEMA = (
)
async def to_code(config):
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)
yield cg.register_component(var, config)
yield ble_client.register_ble_node(var, config)
if battery_level_config := config.get(CONF_BATTERY_LEVEL):
sens = await sensor.new_sensor(battery_level_config)
if CONF_BATTERY_LEVEL in config:
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
cg.add(var.set_battery(sens))
if illuminance_config := config.get(CONF_ILLUMINANCE):
sens = await sensor.new_sensor(illuminance_config)
if CONF_ILLUMINANCE in config:
sens = yield sensor.new_sensor(config[CONF_ILLUMINANCE])
cg.add(var.set_illuminance(sens))

View File

@@ -15,24 +15,18 @@ AnalogThresholdBinarySensor = analog_threshold_ns.class_(
CONF_UPPER = "upper"
CONF_LOWER = "lower"
CONFIG_SCHEMA = (
binary_sensor.binary_sensor_schema(AnalogThresholdBinarySensor)
.extend(
{
cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
cv.Required(CONF_THRESHOLD): cv.Any(
cv.float_,
cv.Schema(
{
cv.Required(CONF_UPPER): cv.float_,
cv.Required(CONF_LOWER): cv.float_,
}
),
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AnalogThresholdBinarySensor),
cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
cv.Required(CONF_THRESHOLD): cv.Any(
cv.float_,
cv.Schema(
{cv.Required(CONF_UPPER): cv.float_, cv.Required(CONF_LOWER): cv.float_}
),
}
)
.extend(cv.COMPONENT_SCHEMA)
)
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):

View File

@@ -1,124 +1,35 @@
import logging
from esphome import automation, core
from esphome.components import font
from esphome import core
from esphome.components import display, font
import esphome.components.image as espImage
from esphome.components.image import CONF_USE_TRANSPARENCY
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import (
CONF_FILE,
CONF_ID,
CONF_RAW_DATA_ID,
CONF_REPEAT,
CONF_RESIZE,
CONF_TYPE,
)
from esphome.const import CONF_FILE, CONF_ID, CONF_RAW_DATA_ID, CONF_RESIZE, CONF_TYPE
from esphome.core import CORE, HexInt
_LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["image"]
CODEOWNERS = ["@syndlex"]
DEPENDENCIES = ["display"]
MULTI_CONF = True
CONF_LOOP = "loop"
CONF_START_FRAME = "start_frame"
CONF_END_FRAME = "end_frame"
CONF_FRAME = "frame"
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):
"""
Validate fields whose possible values depend on other fields.
For example, validate that explicitly transparent image types
have "use_transparency" set to True.
Also set the default value for those kind of dependent fields.
"""
image_type = config[CONF_TYPE]
is_transparent_type = image_type in ["TRANSPARENT_BINARY", "RGBA"]
# If the use_transparency option was not specified, set the default depending on the image type
if CONF_USE_TRANSPARENCY not in config:
config[CONF_USE_TRANSPARENCY] = is_transparent_type
if is_transparent_type and not config[CONF_USE_TRANSPARENCY]:
raise cv.Invalid(f"Image type {image_type} must always be transparent.")
return config
Animation_ = display.display_ns.class_("Animation", espImage.Image_)
ANIMATION_SCHEMA = cv.Schema(
cv.All(
{
cv.Required(CONF_ID): cv.declare_id(Animation_),
cv.Required(CONF_FILE): cv.file_,
cv.Optional(CONF_RESIZE): cv.dimensions,
cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
espImage.IMAGE_TYPE, upper=True
),
# Not setting default here on purpose; the default depends on the image type,
# and thus will be set in the "validate_cross_dependencies" validator.
cv.Optional(CONF_USE_TRANSPARENCY): cv.boolean,
cv.Optional(CONF_LOOP): cv.All(
{
cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
cv.Optional(CONF_END_FRAME): cv.positive_int,
cv.Optional(CONF_REPEAT): cv.positive_int,
}
),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
},
validate_cross_dependencies,
)
{
cv.Required(CONF_ID): cv.declare_id(Animation_),
cv.Required(CONF_FILE): cv.file_,
cv.Optional(CONF_RESIZE): cv.dimensions,
cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
espImage.IMAGE_TYPE, upper=True
),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
}
)
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
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
CODEOWNERS = ["@syndlex"]
async def to_code(config):
@@ -139,19 +50,16 @@ async def to_code(config):
else:
if width > 500 or height > 500:
_LOGGER.warning(
'The image "%s" you requested is very big. Please consider'
" using the resize parameter.",
path,
"The image you requested is very big. Please consider using"
" the resize parameter."
)
transparent = config[CONF_USE_TRANSPARENCY]
if config[CONF_TYPE] == "GRAYSCALE":
data = [0 for _ in range(height * width * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("LA", dither=Image.NONE)
frame = image.convert("L", dither=Image.NONE)
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
@@ -159,22 +67,18 @@ async def to_code(config):
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
)
for pix, a in pixels:
if transparent:
if pix == 1:
pix = 0
if a < 0x80:
pix = 1
for pix in pixels:
data[pos] = pix
pos += 1
elif config[CONF_TYPE] == "RGBA":
data = [0 for _ in range(height * width * 4 * frames)]
elif config[CONF_TYPE] == "RGB24":
data = [0 for _ in range(height * width * 3 * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("RGBA")
if CONF_RESIZE in config:
image.thumbnail(config[CONF_RESIZE])
frame = image.convert("RGB")
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
@@ -189,44 +93,13 @@ async def to_code(config):
pos += 1
data[pos] = pix[2]
pos += 1
data[pos] = pix[3]
pos += 1
elif config[CONF_TYPE] == "RGB24":
data = [0 for _ in range(height * width * 3 * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("RGBA")
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
)
for r, g, b, a in pixels:
if transparent:
if r == 0 and g == 0 and b == 1:
b = 0
if a < 0x80:
r = 0
g = 0
b = 1
data[pos] = r
pos += 1
data[pos] = g
pos += 1
data[pos] = b
pos += 1
elif config[CONF_TYPE] in ["RGB565", "TRANSPARENT_IMAGE"]:
elif config[CONF_TYPE] == "RGB565":
data = [0 for _ in range(height * width * 2 * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert("RGBA")
frame = image.convert("RGB")
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata())
@@ -234,21 +107,14 @@ async def to_code(config):
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
)
for r, g, b, a in pixels:
R = r >> 3
G = g >> 2
B = b >> 3
for pix in pixels:
R = pix[0] >> 3
G = pix[1] >> 2
B = pix[2] >> 3
rgb = (R << 11) | (G << 5) | B
if transparent:
if rgb == 0x0020:
rgb = 0
if a < 0x80:
rgb = 0x0020
data[pos] = rgb >> 8
pos += 1
data[pos] = rgb & 0xFF
data[pos] = rgb & 255
pos += 1
elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]:
@@ -256,31 +122,19 @@ async def to_code(config):
data = [0 for _ in range((height * width8 // 8) * frames)]
for frameIndex in range(frames):
image.seek(frameIndex)
if transparent:
alpha = image.split()[-1]
has_alpha = alpha.getextrema()[0] < 0xFF
frame = image.convert("1", dither=Image.NONE)
if CONF_RESIZE in config:
frame = frame.resize([width, height])
if transparent:
alpha = alpha.resize([width, height])
for x, y in [(i, j) for i in range(width) for j in range(height)]:
if transparent and has_alpha:
if not alpha.getpixel((x, y)):
for y in range(height):
for x in range(width):
if frame.getpixel((x, y)):
continue
elif frame.getpixel((x, y)):
continue
pos = x + y * width8 + (height * width8 * frameIndex)
data[pos // 8] |= 0x80 >> (pos % 8)
else:
raise core.EsphomeError(
f"Animation f{config[CONF_ID]} has not supported type {config[CONF_TYPE]}."
)
pos = x + y * width8 + (height * width8 * frameIndex)
data[pos // 8] |= 0x80 >> (pos % 8)
rhs = [HexInt(x) for x in data]
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
var = cg.new_Pvariable(
cg.new_Pvariable(
config[CONF_ID],
prog_arr,
width,
@@ -288,9 +142,3 @@ async def to_code(config):
frames,
espImage.IMAGE_TYPE[config[CONF_TYPE]],
)
cg.add(var.set_transparency(transparent))
if loop_config := config.get(CONF_LOOP):
start = loop_config[CONF_START_FRAME]
end = loop_config.get(CONF_END_FRAME, frames)
count = loop_config.get(CONF_REPEAT, -1)
cg.add(var.set_loop(start, end, count))

View File

@@ -1,70 +0,0 @@
#include "animation.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace animation {
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
image::ImageType type)
: Image(data_start, width, height, type),
animation_data_start_(data_start),
current_frame_(0),
animation_frame_count_(animation_frame_count),
loop_start_frame_(0),
loop_end_frame_(animation_frame_count_),
loop_count_(0),
loop_current_iteration_(1) {}
void Animation::set_loop(uint32_t start_frame, uint32_t end_frame, int count) {
loop_start_frame_ = std::min(start_frame, animation_frame_count_);
loop_end_frame_ = std::min(end_frame, animation_frame_count_);
loop_count_ = count;
loop_current_iteration_ = 1;
}
uint32_t Animation::get_animation_frame_count() const { return this->animation_frame_count_; }
int Animation::get_current_frame() const { return this->current_frame_; }
void Animation::next_frame() {
this->current_frame_++;
if (loop_count_ && this->current_frame_ == loop_end_frame_ &&
(this->loop_current_iteration_ < loop_count_ || loop_count_ < 0)) {
this->current_frame_ = loop_start_frame_;
this->loop_current_iteration_++;
}
if (this->current_frame_ >= animation_frame_count_) {
this->loop_current_iteration_ = 1;
this->current_frame_ = 0;
}
this->update_data_start_();
}
void Animation::prev_frame() {
this->current_frame_--;
if (this->current_frame_ < 0) {
this->current_frame_ = this->animation_frame_count_ - 1;
}
this->update_data_start_();
}
void Animation::set_frame(int frame) {
unsigned abs_frame = abs(frame);
if (abs_frame < this->animation_frame_count_) {
if (frame >= 0) {
this->current_frame_ = frame;
} else {
this->current_frame_ = this->animation_frame_count_ - abs_frame;
}
}
this->update_data_start_();
}
void Animation::update_data_start_() {
const uint32_t image_size = image_type_to_width_stride(this->width_, this->type_) * this->height_;
this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_;
}
} // namespace animation
} // namespace esphome

View File

@@ -1,67 +0,0 @@
#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

View File

@@ -4,6 +4,7 @@ from esphome.components import i2c
from esphome.const import CONF_ID
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["sensor", "binary_sensor"]
MULTI_CONF = True
CONF_APDS9960_ID = "apds9960_id"

View File

@@ -116,12 +116,8 @@ void APDS9960::setup() {
APDS9960_WRITE_BYTE(0x80, val);
}
bool APDS9960::is_color_enabled_() const {
#ifdef USE_SENSOR
return this->red_sensor_ != nullptr || this->green_sensor_ != nullptr || this->blue_sensor_ != nullptr ||
this->clear_sensor_ != nullptr;
#else
return false;
#endif
return this->red_channel_ != nullptr || this->green_channel_ != nullptr || this->blue_channel_ != nullptr ||
this->clear_channel_ != nullptr;
}
void APDS9960::dump_config() {
@@ -129,15 +125,6 @@ void APDS9960::dump_config() {
LOG_I2C_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Red channel", this->red_sensor_);
LOG_SENSOR(" ", "Green channel", this->green_sensor_);
LOG_SENSOR(" ", "Blue channel", this->blue_sensor_);
LOG_SENSOR(" ", "Clear channel", this->clear_sensor_);
LOG_SENSOR(" ", "Proximity", this->proximity_sensor_);
#endif
if (this->is_failed()) {
switch (this->error_code_) {
case COMMUNICATION_FAILED:
@@ -194,22 +181,17 @@ void APDS9960::read_color_data_(uint8_t status) {
float blue_perc = (uint_blue / float(UINT16_MAX)) * 100.0f;
ESP_LOGD(TAG, "Got clear=%.1f%% red=%.1f%% green=%.1f%% blue=%.1f%%", clear_perc, red_perc, green_perc, blue_perc);
#ifdef USE_SENSOR
if (this->clear_sensor_ != nullptr)
this->clear_sensor_->publish_state(clear_perc);
if (this->red_sensor_ != nullptr)
this->red_sensor_->publish_state(red_perc);
if (this->green_sensor_ != nullptr)
this->green_sensor_->publish_state(green_perc);
if (this->blue_sensor_ != nullptr)
this->blue_sensor_->publish_state(blue_perc);
#endif
if (this->clear_channel_ != nullptr)
this->clear_channel_->publish_state(clear_perc);
if (this->red_channel_ != nullptr)
this->red_channel_->publish_state(red_perc);
if (this->green_channel_ != nullptr)
this->green_channel_->publish_state(green_perc);
if (this->blue_channel_ != nullptr)
this->blue_channel_->publish_state(blue_perc);
}
void APDS9960::read_proximity_data_(uint8_t status) {
#ifndef USE_SENSOR
return;
#else
if (this->proximity_sensor_ == nullptr)
if (this->proximity_ == nullptr)
return;
if ((status & 0b10) == 0x00) {
@@ -222,8 +204,7 @@ void APDS9960::read_proximity_data_(uint8_t status) {
float prox_perc = (prox / float(UINT8_MAX)) * 100.0f;
ESP_LOGD(TAG, "Got proximity=%.1f%%", prox_perc);
this->proximity_sensor_->publish_state(prox_perc);
#endif
this->proximity_->publish_state(prox_perc);
}
void APDS9960::read_gesture_data_() {
if (!this->is_gesture_enabled_())
@@ -275,29 +256,28 @@ void APDS9960::read_gesture_data_() {
}
}
void APDS9960::report_gesture_(int gesture) {
#ifdef USE_BINARY_SENSOR
binary_sensor::BinarySensor *bin;
switch (gesture) {
case 1:
bin = this->up_direction_binary_sensor_;
bin = this->up_direction_;
this->gesture_up_started_ = false;
this->gesture_down_started_ = false;
ESP_LOGD(TAG, "Got gesture UP");
break;
case 2:
bin = this->down_direction_binary_sensor_;
bin = this->down_direction_;
this->gesture_up_started_ = false;
this->gesture_down_started_ = false;
ESP_LOGD(TAG, "Got gesture DOWN");
break;
case 3:
bin = this->left_direction_binary_sensor_;
bin = this->left_direction_;
this->gesture_left_started_ = false;
this->gesture_right_started_ = false;
ESP_LOGD(TAG, "Got gesture LEFT");
break;
case 4:
bin = this->right_direction_binary_sensor_;
bin = this->right_direction_;
this->gesture_left_started_ = false;
this->gesture_right_started_ = false;
ESP_LOGD(TAG, "Got gesture RIGHT");
@@ -310,7 +290,6 @@ void APDS9960::report_gesture_(int gesture) {
bin->publish_state(true);
bin->publish_state(false);
}
#endif
}
void APDS9960::process_dataset_(int up, int down, int left, int right) {
/* Algorithm: (see Figure 11 in datasheet)
@@ -386,22 +365,10 @@ void APDS9960::process_dataset_(int up, int down, int left, int right) {
}
}
float APDS9960::get_setup_priority() const { return setup_priority::DATA; }
bool APDS9960::is_proximity_enabled_() const {
return
#ifdef USE_SENSOR
this->proximity_sensor_ != nullptr
#else
false
#endif
|| this->is_gesture_enabled_();
}
bool APDS9960::is_proximity_enabled_() const { return this->proximity_ != nullptr || this->is_gesture_enabled_(); }
bool APDS9960::is_gesture_enabled_() const {
#ifdef USE_BINARY_SENSOR
return this->up_direction_binary_sensor_ != nullptr || this->left_direction_binary_sensor_ != nullptr ||
this->down_direction_binary_sensor_ != nullptr || this->right_direction_binary_sensor_ != nullptr;
#else
return false;
#endif
return this->up_direction_ != nullptr || this->left_direction_ != nullptr || this->down_direction_ != nullptr ||
this->right_direction_ != nullptr;
}
} // namespace apds9960

View File

@@ -1,34 +1,14 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#ifdef USE_SENSOR
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#endif
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif
namespace esphome {
namespace apds9960 {
class APDS9960 : public PollingComponent, public i2c::I2CDevice {
#ifdef USE_SENSOR
SUB_SENSOR(red)
SUB_SENSOR(green)
SUB_SENSOR(blue)
SUB_SENSOR(clear)
SUB_SENSOR(proximity)
#endif
#ifdef USE_BINARY_SENSOR
SUB_BINARY_SENSOR(up_direction)
SUB_BINARY_SENSOR(right_direction)
SUB_BINARY_SENSOR(down_direction)
SUB_BINARY_SENSOR(left_direction)
#endif
public:
void setup() override;
void dump_config() override;
@@ -43,6 +23,16 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
void set_gesture_gain(uint8_t gain) { this->gesture_gain_ = gain; }
void set_gesture_wait_time(uint8_t wait_time) { this->gesture_wait_time_ = wait_time; }
void set_red_channel(sensor::Sensor *red_channel) { red_channel_ = red_channel; }
void set_green_channel(sensor::Sensor *green_channel) { green_channel_ = green_channel; }
void set_blue_channel(sensor::Sensor *blue_channel) { blue_channel_ = blue_channel; }
void set_clear_channel(sensor::Sensor *clear_channel) { clear_channel_ = clear_channel; }
void set_up_direction(binary_sensor::BinarySensor *up_direction) { up_direction_ = up_direction; }
void set_right_direction(binary_sensor::BinarySensor *right_direction) { right_direction_ = right_direction; }
void set_down_direction(binary_sensor::BinarySensor *down_direction) { down_direction_ = down_direction; }
void set_left_direction(binary_sensor::BinarySensor *left_direction) { left_direction_ = left_direction; }
void set_proximity(sensor::Sensor *proximity) { proximity_ = proximity; }
protected:
bool is_color_enabled_() const;
bool is_proximity_enabled_() const;
@@ -60,6 +50,15 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
uint8_t gesture_gain_;
uint8_t gesture_wait_time_;
sensor::Sensor *red_channel_{nullptr};
sensor::Sensor *green_channel_{nullptr};
sensor::Sensor *blue_channel_{nullptr};
sensor::Sensor *clear_channel_{nullptr};
binary_sensor::BinarySensor *up_direction_{nullptr};
binary_sensor::BinarySensor *right_direction_{nullptr};
binary_sensor::BinarySensor *down_direction_{nullptr};
binary_sensor::BinarySensor *left_direction_{nullptr};
sensor::Sensor *proximity_{nullptr};
enum ErrorCode {
NONE = 0,
COMMUNICATION_FAILED,

View File

@@ -6,14 +6,19 @@ from . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ["apds9960"]
DIRECTIONS = ["up", "down", "left", "right"]
DIRECTIONS = {
"UP": "set_up_direction",
"DOWN": "set_down_direction",
"LEFT": "set_left_direction",
"RIGHT": "set_right_direction",
}
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_MOVING
).extend(
{
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, lower=True),
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
}
)
@@ -21,5 +26,5 @@ CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
async def to_code(config):
hub = await cg.get_variable(config[CONF_APDS9960_ID])
var = await binary_sensor.new_binary_sensor(config)
func = getattr(hub, f"set_{config[CONF_DIRECTION]}_direction_binary_sensor")
func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
cg.add(func(var))

View File

@@ -11,7 +11,13 @@ from . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ["apds9960"]
TYPES = ["clear", "red", "green", "blue", "proximity"]
TYPES = {
"CLEAR": "set_clear_channel",
"RED": "set_red_channel",
"GREEN": "set_green_channel",
"BLUE": "set_blue_channel",
"PROXIMITY": "set_proximity",
}
CONFIG_SCHEMA = sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
@@ -20,7 +26,7 @@ CONFIG_SCHEMA = sensor.sensor_schema(
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Required(CONF_TYPE): cv.one_of(*TYPES, lower=True),
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
}
)
@@ -29,5 +35,5 @@ CONFIG_SCHEMA = sensor.sensor_schema(
async def to_code(config):
hub = await cg.get_variable(config[CONF_APDS9960_ID])
var = await sensor.new_sensor(config)
func = getattr(hub, f"set_{config[CONF_TYPE]}_sensor")
func = getattr(hub, TYPES[config[CONF_TYPE]])
cg.add(func(var))

View File

@@ -116,8 +116,9 @@ async def to_code(config):
cg.add(var.register_user_service(trigger))
await automation.build_automation(trigger, func_args, conf)
if encryption_config := config.get(CONF_ENCRYPTION):
decoded = base64.b64decode(encryption_config[CONF_KEY])
if CONF_ENCRYPTION in config:
conf = config[CONF_ENCRYPTION]
decoded = base64.b64decode(conf[CONF_KEY])
cg.add(var.set_noise_psk(list(decoded)))
cg.add_define("USE_API_NOISE")
cg.add_library("esphome/noise-c", "0.1.4")

View File

@@ -53,11 +53,6 @@ service APIConnection {
rpc bluetooth_gatt_write_descriptor(BluetoothGATTWriteDescriptorRequest) returns (void) {}
rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {}
}
@@ -208,14 +203,11 @@ message DeviceInfoResponse {
uint32 webserver_port = 10;
uint32 legacy_bluetooth_proxy_version = 11;
uint32 bluetooth_proxy_feature_flags = 15;
uint32 bluetooth_proxy_version = 11;
string manufacturer = 12;
string friendly_name = 13;
uint32 voice_assistant_version = 14;
}
message ListEntitiesRequest {
@@ -291,7 +283,6 @@ message ListEntitiesCoverResponse {
bool disabled_by_default = 9;
string icon = 10;
EntityCategory entity_category = 11;
bool supports_stop = 12;
}
enum LegacyCoverState {
@@ -796,7 +787,6 @@ enum ClimateFanMode {
CLIMATE_FAN_MIDDLE = 6;
CLIMATE_FAN_FOCUS = 7;
CLIMATE_FAN_DIFFUSE = 8;
CLIMATE_FAN_QUIET = 9;
}
enum ClimateSwingMode {
CLIMATE_SWING_OFF = 0;
@@ -838,7 +828,7 @@ message ListEntitiesClimateResponse {
repeated ClimateMode supported_modes = 7;
float visual_min_temperature = 8;
float visual_max_temperature = 9;
float visual_target_temperature_step = 10;
float visual_temperature_step = 10;
// for older peer versions - in new system this
// is if CLIMATE_PRESET_AWAY exists is supported_presets
bool legacy_supports_away = 11;
@@ -851,7 +841,6 @@ message ListEntitiesClimateResponse {
bool disabled_by_default = 18;
string icon = 19;
EntityCategory entity_category = 20;
float visual_current_temperature_step = 21;
}
message ClimateStateResponse {
option (id) = 47;
@@ -865,7 +854,8 @@ message ClimateStateResponse {
float target_temperature = 4;
float target_temperature_low = 5;
float target_temperature_high = 6;
bool unused_legacy_away = 7;
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
bool legacy_away = 7;
ClimateAction action = 8;
ClimateFanMode fan_mode = 9;
ClimateSwingMode swing_mode = 10;
@@ -888,8 +878,9 @@ message ClimateCommandRequest {
float target_temperature_low = 7;
bool has_target_temperature_high = 8;
float target_temperature_high = 9;
bool unused_has_legacy_away = 10;
bool unused_legacy_away = 11;
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
bool has_legacy_away = 10;
bool legacy_away = 11;
bool has_fan_mode = 12;
ClimateFanMode fan_mode = 13;
bool has_swing_mode = 14;
@@ -1132,9 +1123,6 @@ message MediaPlayerCommandRequest {
message SubscribeBluetoothLEAdvertisementsRequest {
option (id) = 66;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint32 flags = 1;
}
message BluetoothServiceData {
@@ -1159,23 +1147,6 @@ message BluetoothLEAdvertisementResponse {
uint32 address_type = 7;
}
message BluetoothLERawAdvertisement {
uint64 address = 1;
sint32 rssi = 2;
uint32 address_type = 3;
bytes data = 4;
}
message BluetoothLERawAdvertisementsResponse {
option (id) = 93;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
option (no_delay) = true;
repeated BluetoothLERawAdvertisement advertisements = 1;
}
enum BluetoothDeviceRequestType {
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0;
BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1;
@@ -1183,7 +1154,6 @@ enum BluetoothDeviceRequestType {
BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3;
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4;
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5;
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6;
}
message BluetoothDeviceRequest {
@@ -1367,153 +1337,3 @@ message BluetoothGATTNotifyResponse {
uint64 address = 1;
uint32 handle = 2;
}
message BluetoothDevicePairingResponse {
option (id) = 85;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
bool paired = 2;
int32 error = 3;
}
message BluetoothDeviceUnpairingResponse {
option (id) = 86;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
bool success = 2;
int32 error = 3;
}
message UnsubscribeBluetoothLEAdvertisementsRequest {
option (id) = 87;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BLUETOOTH_PROXY";
}
message BluetoothDeviceClearCacheResponse {
option (id) = 88;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BLUETOOTH_PROXY";
uint64 address = 1;
bool success = 2;
int32 error = 3;
}
// ==================== PUSH TO TALK ====================
message SubscribeVoiceAssistantRequest {
option (id) = 89;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_VOICE_ASSISTANT";
bool subscribe = 1;
}
message VoiceAssistantRequest {
option (id) = 90;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_VOICE_ASSISTANT";
bool start = 1;
string conversation_id = 2;
bool use_vad = 3;
}
message VoiceAssistantResponse {
option (id) = 91;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_VOICE_ASSISTANT";
uint32 port = 1;
bool error = 2;
}
enum VoiceAssistantEvent {
VOICE_ASSISTANT_ERROR = 0;
VOICE_ASSISTANT_RUN_START = 1;
VOICE_ASSISTANT_RUN_END = 2;
VOICE_ASSISTANT_STT_START = 3;
VOICE_ASSISTANT_STT_END = 4;
VOICE_ASSISTANT_INTENT_START = 5;
VOICE_ASSISTANT_INTENT_END = 6;
VOICE_ASSISTANT_TTS_START = 7;
VOICE_ASSISTANT_TTS_END = 8;
}
message VoiceAssistantEventData {
string name = 1;
string value = 2;
}
message VoiceAssistantEventResponse {
option (id) = 92;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_VOICE_ASSISTANT";
VoiceAssistantEvent event_type = 1;
repeated VoiceAssistantEventData data = 2;
}
// ==================== ALARM CONTROL PANEL ====================
enum AlarmControlPanelState {
ALARM_STATE_DISARMED = 0;
ALARM_STATE_ARMED_HOME = 1;
ALARM_STATE_ARMED_AWAY = 2;
ALARM_STATE_ARMED_NIGHT = 3;
ALARM_STATE_ARMED_VACATION = 4;
ALARM_STATE_ARMED_CUSTOM_BYPASS = 5;
ALARM_STATE_PENDING = 6;
ALARM_STATE_ARMING = 7;
ALARM_STATE_DISARMING = 8;
ALARM_STATE_TRIGGERED = 9;
}
enum AlarmControlPanelStateCommand {
ALARM_CONTROL_PANEL_DISARM = 0;
ALARM_CONTROL_PANEL_ARM_AWAY = 1;
ALARM_CONTROL_PANEL_ARM_HOME = 2;
ALARM_CONTROL_PANEL_ARM_NIGHT = 3;
ALARM_CONTROL_PANEL_ARM_VACATION = 4;
ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS = 5;
ALARM_CONTROL_PANEL_TRIGGER = 6;
}
message ListEntitiesAlarmControlPanelResponse {
option (id) = 94;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 supported_features = 8;
bool requires_code = 9;
bool requires_code_to_arm = 10;
}
message AlarmControlPanelStateResponse {
option (id) = 95;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
option (no_delay) = true;
fixed32 key = 1;
AlarmControlPanelState state = 2;
}
message AlarmControlPanelCommandRequest {
option (id) = 96;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
option (no_delay) = true;
fixed32 key = 1;
AlarmControlPanelStateCommand command = 2;
string code = 3;
}

View File

@@ -1,6 +1,5 @@
#include "api_connection.h"
#include <cerrno>
#include <cinttypes>
#include "esphome/components/network/util.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/hal.h"
@@ -16,9 +15,6 @@
#ifdef USE_BLUETOOTH_PROXY
#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h"
#endif
#ifdef USE_VOICE_ASSISTANT
#include "esphome/components/voice_assistant/voice_assistant.h"
#endif
namespace esphome {
namespace api {
@@ -51,14 +47,6 @@ void APIConnection::start() {
helper_->set_log_info(client_info_);
}
APIConnection::~APIConnection() {
#ifdef USE_BLUETOOTH_PROXY
if (bluetooth_proxy::global_bluetooth_proxy->get_api_connection() == this) {
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
}
#endif
}
void APIConnection::loop() {
if (this->remove_)
return;
@@ -192,8 +180,7 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
ListEntitiesBinarySensorResponse msg;
msg.object_id = binary_sensor->get_object_id();
msg.key = binary_sensor->get_object_id_hash();
if (binary_sensor->has_own_name())
msg.name = binary_sensor->get_name();
msg.name = binary_sensor->get_name();
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
@@ -225,13 +212,11 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
ListEntitiesCoverResponse msg;
msg.key = cover->get_object_id_hash();
msg.object_id = cover->get_object_id();
if (cover->has_own_name())
msg.name = cover->get_name();
msg.name = cover->get_name();
msg.unique_id = get_default_unique_id("cover", cover);
msg.assumed_state = traits.get_is_assumed_state();
msg.supports_position = traits.get_supports_position();
msg.supports_tilt = traits.get_supports_tilt();
msg.supports_stop = traits.get_supports_stop();
msg.device_class = cover->get_device_class();
msg.disabled_by_default = cover->is_disabled_by_default();
msg.icon = cover->get_icon();
@@ -290,8 +275,7 @@ bool APIConnection::send_fan_info(fan::Fan *fan) {
ListEntitiesFanResponse msg;
msg.key = fan->get_object_id_hash();
msg.object_id = fan->get_object_id();
if (fan->has_own_name())
msg.name = fan->get_name();
msg.name = fan->get_name();
msg.unique_id = get_default_unique_id("fan", fan);
msg.supports_oscillation = traits.supports_oscillation();
msg.supports_speed = traits.supports_speed();
@@ -353,8 +337,7 @@ bool APIConnection::send_light_info(light::LightState *light) {
ListEntitiesLightResponse msg;
msg.key = light->get_object_id_hash();
msg.object_id = light->get_object_id();
if (light->has_own_name())
msg.name = light->get_name();
msg.name = light->get_name();
msg.unique_id = get_default_unique_id("light", light);
msg.disabled_by_default = light->is_disabled_by_default();
@@ -435,8 +418,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
ListEntitiesSensorResponse msg;
msg.key = sensor->get_object_id_hash();
msg.object_id = sensor->get_object_id();
if (sensor->has_own_name())
msg.name = sensor->get_name();
msg.name = sensor->get_name();
msg.unique_id = sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("sensor", sensor);
@@ -466,8 +448,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
ListEntitiesSwitchResponse msg;
msg.key = a_switch->get_object_id_hash();
msg.object_id = a_switch->get_object_id();
if (a_switch->has_own_name())
msg.name = a_switch->get_name();
msg.name = a_switch->get_name();
msg.unique_id = get_default_unique_id("switch", a_switch);
msg.icon = a_switch->get_icon();
msg.assumed_state = a_switch->assumed_state();
@@ -539,6 +520,7 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
resp.custom_fan_mode = climate->custom_fan_mode.value();
if (traits.get_supports_presets() && climate->preset.has_value()) {
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
resp.legacy_away = resp.preset == enums::CLIMATE_PRESET_AWAY;
}
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
resp.custom_preset = climate->custom_preset.value();
@@ -551,8 +533,7 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
ListEntitiesClimateResponse msg;
msg.key = climate->get_object_id_hash();
msg.object_id = climate->get_object_id();
if (climate->has_own_name())
msg.name = climate->get_name();
msg.name = climate->get_name();
msg.unique_id = get_default_unique_id("climate", climate);
msg.disabled_by_default = climate->is_disabled_by_default();
@@ -567,9 +548,7 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.visual_min_temperature = traits.get_visual_min_temperature();
msg.visual_max_temperature = traits.get_visual_max_temperature();
msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
msg.visual_temperature_step = traits.get_visual_temperature_step();
msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
msg.supports_action = traits.get_supports_action();
@@ -599,6 +578,8 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
call.set_target_temperature_low(msg.target_temperature_low);
if (msg.has_target_temperature_high)
call.set_target_temperature_high(msg.target_temperature_high);
if (msg.has_legacy_away)
call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
if (msg.has_fan_mode)
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
if (msg.has_custom_fan_mode)
@@ -628,8 +609,7 @@ bool APIConnection::send_number_info(number::Number *number) {
ListEntitiesNumberResponse msg;
msg.key = number->get_object_id_hash();
msg.object_id = number->get_object_id();
if (number->has_own_name())
msg.name = number->get_name();
msg.name = number->get_name();
msg.unique_id = get_default_unique_id("number", number);
msg.icon = number->get_icon();
msg.disabled_by_default = number->is_disabled_by_default();
@@ -670,8 +650,7 @@ bool APIConnection::send_select_info(select::Select *select) {
ListEntitiesSelectResponse msg;
msg.key = select->get_object_id_hash();
msg.object_id = select->get_object_id();
if (select->has_own_name())
msg.name = select->get_name();
msg.name = select->get_name();
msg.unique_id = get_default_unique_id("select", select);
msg.icon = select->get_icon();
msg.disabled_by_default = select->is_disabled_by_default();
@@ -698,8 +677,7 @@ bool APIConnection::send_button_info(button::Button *button) {
ListEntitiesButtonResponse msg;
msg.key = button->get_object_id_hash();
msg.object_id = button->get_object_id();
if (button->has_own_name())
msg.name = button->get_name();
msg.name = button->get_name();
msg.unique_id = get_default_unique_id("button", button);
msg.icon = button->get_icon();
msg.disabled_by_default = button->is_disabled_by_default();
@@ -730,8 +708,7 @@ bool APIConnection::send_lock_info(lock::Lock *a_lock) {
ListEntitiesLockResponse msg;
msg.key = a_lock->get_object_id_hash();
msg.object_id = a_lock->get_object_id();
if (a_lock->has_own_name())
msg.name = a_lock->get_name();
msg.name = a_lock->get_name();
msg.unique_id = get_default_unique_id("lock", a_lock);
msg.icon = a_lock->get_icon();
msg.assumed_state = a_lock->traits.get_assumed_state();
@@ -776,8 +753,7 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play
ListEntitiesMediaPlayerResponse msg;
msg.key = media_player->get_object_id_hash();
msg.object_id = media_player->get_object_id();
if (media_player->has_own_name())
msg.name = media_player->get_name();
msg.name = media_player->get_name();
msg.unique_id = get_default_unique_id("media_player", media_player);
msg.icon = media_player->get_icon();
msg.disabled_by_default = media_player->is_disabled_by_default();
@@ -821,8 +797,7 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
ListEntitiesCameraResponse msg;
msg.key = camera->get_object_id_hash();
msg.object_id = camera->get_object_id();
if (camera->has_own_name())
msg.name = camera->get_name();
msg.name = camera->get_name();
msg.unique_id = get_default_unique_id("camera", camera);
msg.disabled_by_default = camera->is_disabled_by_default();
msg.icon = camera->get_icon();
@@ -853,13 +828,9 @@ void APIConnection::on_get_time_response(const GetTimeResponse &value) {
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
}
void APIConnection::unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
}
bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) {
if (!this->bluetooth_le_advertisement_subscription_)
return false;
if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) {
BluetoothLEAdvertisementResponse resp = msg;
for (auto &service : resp.service_data) {
@@ -906,90 +877,6 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
}
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad) {
if (!this->voice_assistant_subscription_)
return false;
VoiceAssistantRequest msg;
msg.start = start;
msg.conversation_id = conversation_id;
msg.use_vad = use_vad;
return this->send_voice_assistant_request(msg);
}
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
if (voice_assistant::global_voice_assistant != nullptr) {
struct sockaddr_storage storage;
socklen_t len = sizeof(storage);
this->helper_->getpeername((struct sockaddr *) &storage, &len);
voice_assistant::global_voice_assistant->start(&storage, msg.port);
}
};
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
if (voice_assistant::global_voice_assistant != nullptr) {
voice_assistant::global_voice_assistant->on_event(msg);
}
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
if (!this->state_subscription_)
return false;
AlarmControlPanelStateResponse resp{};
resp.key = a_alarm_control_panel->get_object_id_hash();
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
return this->send_alarm_control_panel_state_response(resp);
}
bool APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
ListEntitiesAlarmControlPanelResponse msg;
msg.key = a_alarm_control_panel->get_object_id_hash();
msg.object_id = a_alarm_control_panel->get_object_id();
msg.name = a_alarm_control_panel->get_name();
msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
msg.icon = a_alarm_control_panel->get_icon();
msg.disabled_by_default = a_alarm_control_panel->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_alarm_control_panel->get_entity_category());
msg.supported_features = a_alarm_control_panel->get_supported_features();
msg.requires_code = a_alarm_control_panel->get_requires_code();
msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
return this->send_list_entities_alarm_control_panel_response(msg);
}
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key);
if (a_alarm_control_panel == nullptr)
return;
auto call = a_alarm_control_panel->make_call();
switch (msg.command) {
case enums::ALARM_CONTROL_PANEL_DISARM:
call.disarm();
break;
case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
call.arm_away();
break;
case enums::ALARM_CONTROL_PANEL_ARM_HOME:
call.arm_home();
break;
case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
call.arm_night();
break;
case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
call.arm_vacation();
break;
case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
call.arm_custom_bypass();
break;
case enums::ALARM_CONTROL_PANEL_TRIGGER:
call.pending();
break;
}
call.set_code(msg.code);
call.perform();
}
#endif
bool APIConnection::send_log_message(int level, const char *tag, const char *line) {
if (this->log_subscription_ < level)
return false;
@@ -1009,12 +896,12 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
this->helper_->set_log_info(client_info_);
this->client_api_version_major_ = msg.api_version_major;
this->client_api_version_minor_ = msg.api_version_minor;
ESP_LOGV(TAG, "Hello from client: '%s' | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
ESP_LOGV(TAG, "Hello from client: '%s' | API Version %d.%d", this->client_info_.c_str(),
this->client_api_version_major_, this->client_api_version_minor_);
HelloResponse resp;
resp.api_version_major = 1;
resp.api_version_minor = 9;
resp.api_version_minor = 7;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
resp.name = App.get_name();
@@ -1051,8 +938,6 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
resp.manufacturer = "Espressif";
#elif defined(USE_RP2040)
resp.manufacturer = "Raspberry Pi";
#elif defined(USE_HOST)
resp.manufacturer = "Host";
#endif
resp.model = ESPHOME_BOARD;
#ifdef USE_DEEP_SLEEP
@@ -1066,11 +951,7 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
resp.webserver_port = USE_WEBSERVER_PORT;
#endif
#ifdef USE_BLUETOOTH_PROXY
resp.legacy_bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->get_legacy_version();
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
#endif
#ifdef USE_VOICE_ASSISTANT
resp.voice_assistant_version = voice_assistant::global_voice_assistant->get_version();
resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 3 : 1;
#endif
return resp;
}

View File

@@ -6,7 +6,6 @@
#include "api_server.h"
#include "esphome/core/application.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include <vector>
@@ -16,7 +15,7 @@ namespace api {
class APIConnection : public APIServerConnection {
public:
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
virtual ~APIConnection();
virtual ~APIConnection() = default;
void start();
void loop();
@@ -98,8 +97,6 @@ class APIConnection : public APIServerConnection {
this->send_homeassistant_service_response(call);
}
#ifdef USE_BLUETOOTH_PROXY
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg);
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
@@ -120,21 +117,6 @@ class APIConnection : public APIServerConnection {
}
#endif
#ifdef USE_VOICE_ASSISTANT
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
this->voice_assistant_subscription_ = msg.subscribe;
}
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_event_response(const VoiceAssistantEventResponse &msg) override;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
bool send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
#endif
void on_disconnect_response(const DisconnectResponse &value) override;
void on_ping_response(const PingResponse &value) override {
// we initiated ping
@@ -168,7 +150,9 @@ class APIConnection : public APIServerConnection {
return {};
}
void execute_service(const ExecuteServiceRequest &msg) override;
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
this->bluetooth_le_advertisement_subscription_ = true;
}
bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
bool is_connection_setup() override {
return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated();
@@ -213,9 +197,7 @@ class APIConnection : public APIServerConnection {
uint32_t last_traffic_;
bool sent_ping_{false};
bool service_call_subscription_{false};
#ifdef USE_VOICE_ASSISTANT
bool voice_assistant_subscription_{false};
#endif
bool bluetooth_le_advertisement_subscription_{false};
bool next_close_ = false;
APIServer *parent_;
InitialStateIterator initial_state_iterator_;

View File

@@ -295,7 +295,7 @@ APIError APINoiseFrameHelper::state_action_() {
if (aerr != APIError::OK)
return aerr;
// ignore contents, may be used in future for flags
prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
prologue_.push_back((uint8_t)(frame.msg.size() >> 8));
prologue_.push_back((uint8_t) frame.msg.size());
prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
@@ -492,9 +492,9 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload
// tmpbuf[1], tmpbuf[2] to be set later
const uint8_t msg_offset = 3;
const uint8_t payload_offset = msg_offset + 4;
tmpbuf[msg_offset + 0] = (uint8_t) (type >> 8); // type
tmpbuf[msg_offset + 0] = (uint8_t)(type >> 8); // type
tmpbuf[msg_offset + 1] = (uint8_t) type;
tmpbuf[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len
tmpbuf[msg_offset + 2] = (uint8_t)(payload_len >> 8); // data_len
tmpbuf[msg_offset + 3] = (uint8_t) payload_len;
// copy data
std::copy(payload, payload + payload_len, &tmpbuf[payload_offset]);
@@ -512,7 +512,7 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload
}
size_t total_len = 3 + mbuf.size;
tmpbuf[1] = (uint8_t) (mbuf.size >> 8);
tmpbuf[1] = (uint8_t)(mbuf.size >> 8);
tmpbuf[2] = (uint8_t) mbuf.size;
struct iovec iov;
@@ -610,7 +610,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
uint8_t header[3];
header[0] = 0x01; // indicator
header[1] = (uint8_t) (len >> 8);
header[1] = (uint8_t)(len >> 8);
header[2] = (uint8_t) len;
struct iovec iov[2];

View File

@@ -10,8 +10,8 @@
#include "noise/protocol.h"
#endif
#include "api_noise_context.h"
#include "esphome/components/socket/socket.h"
#include "api_noise_context.h"
namespace esphome {
namespace api {
@@ -67,7 +67,6 @@ class APIFrameHelper {
virtual bool can_write_without_blocking() = 0;
virtual APIError write_packet(uint16_t type, const uint8_t *data, size_t len) = 0;
virtual std::string getpeername() = 0;
virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;
virtual APIError close() = 0;
virtual APIError shutdown(int how) = 0;
// Give this helper a name for logging
@@ -85,10 +84,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
APIError read_packet(ReadPacketBuffer *buffer) override;
bool can_write_without_blocking() override;
APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
std::string getpeername() override { return this->socket_->getpeername(); }
int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
return this->socket_->getpeername(addr, addrlen);
}
std::string getpeername() override { return socket_->getpeername(); }
APIError close() override;
APIError shutdown(int how) override;
// Give this helper a name for logging
@@ -148,10 +144,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
APIError read_packet(ReadPacketBuffer *buffer) override;
bool can_write_without_blocking() override;
APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override;
std::string getpeername() override { return this->socket_->getpeername(); }
int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
return this->socket_->getpeername(addr, addrlen);
}
std::string getpeername() override { return socket_->getpeername(); }
APIError close() override;
APIError shutdown(int how) override;
// Give this helper a name for logging

View File

@@ -235,8 +235,6 @@ template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::Climat
return "CLIMATE_FAN_FOCUS";
case enums::CLIMATE_FAN_DIFFUSE:
return "CLIMATE_FAN_DIFFUSE";
case enums::CLIMATE_FAN_QUIET:
return "CLIMATE_FAN_QUIET";
default:
return "UNKNOWN";
}
@@ -400,85 +398,6 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE";
case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE:
return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE";
case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE:
return "BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE";
default:
return "UNKNOWN";
}
}
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::VoiceAssistantEvent value) {
switch (value) {
case enums::VOICE_ASSISTANT_ERROR:
return "VOICE_ASSISTANT_ERROR";
case enums::VOICE_ASSISTANT_RUN_START:
return "VOICE_ASSISTANT_RUN_START";
case enums::VOICE_ASSISTANT_RUN_END:
return "VOICE_ASSISTANT_RUN_END";
case enums::VOICE_ASSISTANT_STT_START:
return "VOICE_ASSISTANT_STT_START";
case enums::VOICE_ASSISTANT_STT_END:
return "VOICE_ASSISTANT_STT_END";
case enums::VOICE_ASSISTANT_INTENT_START:
return "VOICE_ASSISTANT_INTENT_START";
case enums::VOICE_ASSISTANT_INTENT_END:
return "VOICE_ASSISTANT_INTENT_END";
case enums::VOICE_ASSISTANT_TTS_START:
return "VOICE_ASSISTANT_TTS_START";
case enums::VOICE_ASSISTANT_TTS_END:
return "VOICE_ASSISTANT_TTS_END";
default:
return "UNKNOWN";
}
}
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
template<> const char *proto_enum_to_string<enums::AlarmControlPanelState>(enums::AlarmControlPanelState value) {
switch (value) {
case enums::ALARM_STATE_DISARMED:
return "ALARM_STATE_DISARMED";
case enums::ALARM_STATE_ARMED_HOME:
return "ALARM_STATE_ARMED_HOME";
case enums::ALARM_STATE_ARMED_AWAY:
return "ALARM_STATE_ARMED_AWAY";
case enums::ALARM_STATE_ARMED_NIGHT:
return "ALARM_STATE_ARMED_NIGHT";
case enums::ALARM_STATE_ARMED_VACATION:
return "ALARM_STATE_ARMED_VACATION";
case enums::ALARM_STATE_ARMED_CUSTOM_BYPASS:
return "ALARM_STATE_ARMED_CUSTOM_BYPASS";
case enums::ALARM_STATE_PENDING:
return "ALARM_STATE_PENDING";
case enums::ALARM_STATE_ARMING:
return "ALARM_STATE_ARMING";
case enums::ALARM_STATE_DISARMING:
return "ALARM_STATE_DISARMING";
case enums::ALARM_STATE_TRIGGERED:
return "ALARM_STATE_TRIGGERED";
default:
return "UNKNOWN";
}
}
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
template<>
const char *proto_enum_to_string<enums::AlarmControlPanelStateCommand>(enums::AlarmControlPanelStateCommand value) {
switch (value) {
case enums::ALARM_CONTROL_PANEL_DISARM:
return "ALARM_CONTROL_PANEL_DISARM";
case enums::ALARM_CONTROL_PANEL_ARM_AWAY:
return "ALARM_CONTROL_PANEL_ARM_AWAY";
case enums::ALARM_CONTROL_PANEL_ARM_HOME:
return "ALARM_CONTROL_PANEL_ARM_HOME";
case enums::ALARM_CONTROL_PANEL_ARM_NIGHT:
return "ALARM_CONTROL_PANEL_ARM_NIGHT";
case enums::ALARM_CONTROL_PANEL_ARM_VACATION:
return "ALARM_CONTROL_PANEL_ARM_VACATION";
case enums::ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS:
return "ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS";
case enums::ALARM_CONTROL_PANEL_TRIGGER:
return "ALARM_CONTROL_PANEL_TRIGGER";
default:
return "UNKNOWN";
}
@@ -668,15 +587,7 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
return true;
}
case 11: {
this->legacy_bluetooth_proxy_version = value.as_uint32();
return true;
}
case 15: {
this->bluetooth_proxy_feature_flags = value.as_uint32();
return true;
}
case 14: {
this->voice_assistant_version = value.as_uint32();
this->bluetooth_proxy_version = value.as_uint32();
return true;
}
default:
@@ -736,11 +647,9 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(8, this->project_name);
buffer.encode_string(9, this->project_version);
buffer.encode_uint32(10, this->webserver_port);
buffer.encode_uint32(11, this->legacy_bluetooth_proxy_version);
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
buffer.encode_uint32(11, this->bluetooth_proxy_version);
buffer.encode_string(12, this->manufacturer);
buffer.encode_string(13, this->friendly_name);
buffer.encode_uint32(14, this->voice_assistant_version);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoResponse::dump_to(std::string &out) const {
@@ -787,13 +696,8 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append(buffer);
out.append("\n");
out.append(" legacy_bluetooth_proxy_version: ");
sprintf(buffer, "%u", this->legacy_bluetooth_proxy_version);
out.append(buffer);
out.append("\n");
out.append(" bluetooth_proxy_feature_flags: ");
sprintf(buffer, "%u", this->bluetooth_proxy_feature_flags);
out.append(" bluetooth_proxy_version: ");
sprintf(buffer, "%u", this->bluetooth_proxy_version);
out.append(buffer);
out.append("\n");
@@ -804,11 +708,6 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append(" friendly_name: ");
out.append("'").append(this->friendly_name).append("'");
out.append("\n");
out.append(" voice_assistant_version: ");
sprintf(buffer, "%u", this->voice_assistant_version);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
@@ -1002,10 +901,6 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
case 12: {
this->supports_stop = value.as_bool();
return true;
}
default:
return false;
}
@@ -1058,7 +953,6 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_string(10, this->icon);
buffer.encode_enum<enums::EntityCategory>(11, this->entity_category);
buffer.encode_bool(12, this->supports_stop);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
@@ -1108,10 +1002,6 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" supports_stop: ");
out.append(YESNO(this->supports_stop));
out.append("\n");
out.append("}");
}
#endif
@@ -3559,11 +3449,7 @@ bool ListEntitiesClimateResponse::decode_32bit(uint32_t field_id, Proto32Bit val
return true;
}
case 10: {
this->visual_target_temperature_step = value.as_float();
return true;
}
case 21: {
this->visual_current_temperature_step = value.as_float();
this->visual_temperature_step = value.as_float();
return true;
}
default:
@@ -3582,7 +3468,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
}
buffer.encode_float(8, this->visual_min_temperature);
buffer.encode_float(9, this->visual_max_temperature);
buffer.encode_float(10, this->visual_target_temperature_step);
buffer.encode_float(10, this->visual_temperature_step);
buffer.encode_bool(11, this->legacy_supports_away);
buffer.encode_bool(12, this->supports_action);
for (auto &it : this->supported_fan_modes) {
@@ -3603,7 +3489,6 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(18, this->disabled_by_default);
buffer.encode_string(19, this->icon);
buffer.encode_enum<enums::EntityCategory>(20, this->entity_category);
buffer.encode_float(21, this->visual_current_temperature_step);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
@@ -3650,8 +3535,8 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append(buffer);
out.append("\n");
out.append(" visual_target_temperature_step: ");
sprintf(buffer, "%g", this->visual_target_temperature_step);
out.append(" visual_temperature_step: ");
sprintf(buffer, "%g", this->visual_temperature_step);
out.append(buffer);
out.append("\n");
@@ -3704,11 +3589,6 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" visual_current_temperature_step: ");
sprintf(buffer, "%g", this->visual_current_temperature_step);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
@@ -3719,7 +3599,7 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
return true;
}
case 7: {
this->unused_legacy_away = value.as_bool();
this->legacy_away = value.as_bool();
return true;
}
case 8: {
@@ -3789,7 +3669,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(4, this->target_temperature);
buffer.encode_float(5, this->target_temperature_low);
buffer.encode_float(6, this->target_temperature_high);
buffer.encode_bool(7, this->unused_legacy_away);
buffer.encode_bool(7, this->legacy_away);
buffer.encode_enum<enums::ClimateAction>(8, this->action);
buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
@@ -3830,8 +3710,8 @@ void ClimateStateResponse::dump_to(std::string &out) const {
out.append(buffer);
out.append("\n");
out.append(" unused_legacy_away: ");
out.append(YESNO(this->unused_legacy_away));
out.append(" legacy_away: ");
out.append(YESNO(this->legacy_away));
out.append("\n");
out.append(" action: ");
@@ -3883,11 +3763,11 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
return true;
}
case 10: {
this->unused_has_legacy_away = value.as_bool();
this->has_legacy_away = value.as_bool();
return true;
}
case 11: {
this->unused_legacy_away = value.as_bool();
this->legacy_away = value.as_bool();
return true;
}
case 12: {
@@ -3972,8 +3852,8 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(7, this->target_temperature_low);
buffer.encode_bool(8, this->has_target_temperature_high);
buffer.encode_float(9, this->target_temperature_high);
buffer.encode_bool(10, this->unused_has_legacy_away);
buffer.encode_bool(11, this->unused_legacy_away);
buffer.encode_bool(10, this->has_legacy_away);
buffer.encode_bool(11, this->legacy_away);
buffer.encode_bool(12, this->has_fan_mode);
buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
buffer.encode_bool(14, this->has_swing_mode);
@@ -4029,12 +3909,12 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
out.append(buffer);
out.append("\n");
out.append(" unused_has_legacy_away: ");
out.append(YESNO(this->unused_has_legacy_away));
out.append(" has_legacy_away: ");
out.append(YESNO(this->has_legacy_away));
out.append("\n");
out.append(" unused_legacy_away: ");
out.append(YESNO(this->unused_legacy_away));
out.append(" legacy_away: ");
out.append(YESNO(this->legacy_away));
out.append("\n");
out.append(" has_fan_mode: ");
@@ -5102,28 +4982,10 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool SubscribeBluetoothLEAdvertisementsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->flags = value.as_uint32();
return true;
}
default:
return false;
}
}
void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->flags);
}
void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("SubscribeBluetoothLEAdvertisementsRequest {\n");
out.append(" flags: ");
sprintf(buffer, "%u", this->flags);
out.append(buffer);
out.append("\n");
out.append("}");
out.append("SubscribeBluetoothLEAdvertisementsRequest {}");
}
#endif
bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) {
@@ -5276,92 +5138,6 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool BluetoothLERawAdvertisement::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->address = value.as_uint64();
return true;
}
case 2: {
this->rssi = value.as_sint32();
return true;
}
case 3: {
this->address_type = value.as_uint32();
return true;
}
default:
return false;
}
}
bool BluetoothLERawAdvertisement::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 4: {
this->data = value.as_string();
return true;
}
default:
return false;
}
}
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_sint32(2, this->rssi);
buffer.encode_uint32(3, this->address_type);
buffer.encode_string(4, this->data);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BluetoothLERawAdvertisement::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("BluetoothLERawAdvertisement {\n");
out.append(" address: ");
sprintf(buffer, "%llu", this->address);
out.append(buffer);
out.append("\n");
out.append(" rssi: ");
sprintf(buffer, "%d", this->rssi);
out.append(buffer);
out.append("\n");
out.append(" address_type: ");
sprintf(buffer, "%u", this->address_type);
out.append(buffer);
out.append("\n");
out.append(" data: ");
out.append("'").append(this->data).append("'");
out.append("\n");
out.append("}");
}
#endif
bool BluetoothLERawAdvertisementsResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->advertisements.push_back(value.as_message<BluetoothLERawAdvertisement>());
return true;
}
default:
return false;
}
}
void BluetoothLERawAdvertisementsResponse::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->advertisements) {
buffer.encode_message<BluetoothLERawAdvertisement>(1, it, true);
}
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BluetoothLERawAdvertisementsResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("BluetoothLERawAdvertisementsResponse {\n");
for (const auto &it : this->advertisements) {
out.append(" advertisements: ");
it.dump_to(out);
out.append("\n");
}
out.append("}");
}
#endif
bool BluetoothDeviceRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
@@ -6186,527 +5962,6 @@ void BluetoothGATTNotifyResponse::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool BluetoothDevicePairingResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->address = value.as_uint64();
return true;
}
case 2: {
this->paired = value.as_bool();
return true;
}
case 3: {
this->error = value.as_int32();
return true;
}
default:
return false;
}
}
void BluetoothDevicePairingResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_bool(2, this->paired);
buffer.encode_int32(3, this->error);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BluetoothDevicePairingResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("BluetoothDevicePairingResponse {\n");
out.append(" address: ");
sprintf(buffer, "%llu", this->address);
out.append(buffer);
out.append("\n");
out.append(" paired: ");
out.append(YESNO(this->paired));
out.append("\n");
out.append(" error: ");
sprintf(buffer, "%d", this->error);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
bool BluetoothDeviceUnpairingResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->address = value.as_uint64();
return true;
}
case 2: {
this->success = value.as_bool();
return true;
}
case 3: {
this->error = value.as_int32();
return true;
}
default:
return false;
}
}
void BluetoothDeviceUnpairingResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_bool(2, this->success);
buffer.encode_int32(3, this->error);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("BluetoothDeviceUnpairingResponse {\n");
out.append(" address: ");
sprintf(buffer, "%llu", this->address);
out.append(buffer);
out.append("\n");
out.append(" success: ");
out.append(YESNO(this->success));
out.append("\n");
out.append(" error: ");
sprintf(buffer, "%d", this->error);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
}
#endif
bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->address = value.as_uint64();
return true;
}
case 2: {
this->success = value.as_bool();
return true;
}
case 3: {
this->error = value.as_int32();
return true;
}
default:
return false;
}
}
void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_bool(2, this->success);
buffer.encode_int32(3, this->error);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("BluetoothDeviceClearCacheResponse {\n");
out.append(" address: ");
sprintf(buffer, "%llu", this->address);
out.append(buffer);
out.append("\n");
out.append(" success: ");
out.append(YESNO(this->success));
out.append("\n");
out.append(" error: ");
sprintf(buffer, "%d", this->error);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->subscribe = value.as_bool();
return true;
}
default:
return false;
}
}
void SubscribeVoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->subscribe); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("SubscribeVoiceAssistantRequest {\n");
out.append(" subscribe: ");
out.append(YESNO(this->subscribe));
out.append("\n");
out.append("}");
}
#endif
bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->start = value.as_bool();
return true;
}
case 3: {
this->use_vad = value.as_bool();
return true;
}
default:
return false;
}
}
bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->conversation_id = value.as_string();
return true;
}
default:
return false;
}
}
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(1, this->start);
buffer.encode_string(2, this->conversation_id);
buffer.encode_bool(3, this->use_vad);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("VoiceAssistantRequest {\n");
out.append(" start: ");
out.append(YESNO(this->start));
out.append("\n");
out.append(" conversation_id: ");
out.append("'").append(this->conversation_id).append("'");
out.append("\n");
out.append(" use_vad: ");
out.append(YESNO(this->use_vad));
out.append("\n");
out.append("}");
}
#endif
bool VoiceAssistantResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->port = value.as_uint32();
return true;
}
case 2: {
this->error = value.as_bool();
return true;
}
default:
return false;
}
}
void VoiceAssistantResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->port);
buffer.encode_bool(2, this->error);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("VoiceAssistantResponse {\n");
out.append(" port: ");
sprintf(buffer, "%u", this->port);
out.append(buffer);
out.append("\n");
out.append(" error: ");
out.append(YESNO(this->error));
out.append("\n");
out.append("}");
}
#endif
bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->name = value.as_string();
return true;
}
case 2: {
this->value = value.as_string();
return true;
}
default:
return false;
}
}
void VoiceAssistantEventData::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->name);
buffer.encode_string(2, this->value);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantEventData::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("VoiceAssistantEventData {\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" value: ");
out.append("'").append(this->value).append("'");
out.append("\n");
out.append("}");
}
#endif
bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->event_type = value.as_enum<enums::VoiceAssistantEvent>();
return true;
}
default:
return false;
}
}
bool VoiceAssistantEventResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->data.push_back(value.as_message<VoiceAssistantEventData>());
return true;
}
default:
return false;
}
}
void VoiceAssistantEventResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::VoiceAssistantEvent>(1, this->event_type);
for (auto &it : this->data) {
buffer.encode_message<VoiceAssistantEventData>(2, it, true);
}
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantEventResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("VoiceAssistantEventResponse {\n");
out.append(" event_type: ");
out.append(proto_enum_to_string<enums::VoiceAssistantEvent>(this->event_type));
out.append("\n");
for (const auto &it : this->data) {
out.append(" data: ");
it.dump_to(out);
out.append("\n");
}
out.append("}");
}
#endif
bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
case 8: {
this->supported_features = value.as_uint32();
return true;
}
case 9: {
this->requires_code = value.as_bool();
return true;
}
case 10: {
this->requires_code_to_arm = value.as_bool();
return true;
}
default:
return false;
}
}
bool ListEntitiesAlarmControlPanelResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesAlarmControlPanelResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_uint32(8, this->supported_features);
buffer.encode_bool(9, this->requires_code);
buffer.encode_bool(10, this->requires_code_to_arm);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesAlarmControlPanelResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
out.append("\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" supported_features: ");
sprintf(buffer, "%u", this->supported_features);
out.append(buffer);
out.append("\n");
out.append(" requires_code: ");
out.append(YESNO(this->requires_code));
out.append("\n");
out.append(" requires_code_to_arm: ");
out.append(YESNO(this->requires_code_to_arm));
out.append("\n");
out.append("}");
}
#endif
bool AlarmControlPanelStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->state = value.as_enum<enums::AlarmControlPanelState>();
return true;
}
default:
return false;
}
}
bool AlarmControlPanelStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void AlarmControlPanelStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_enum<enums::AlarmControlPanelState>(2, this->state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void AlarmControlPanelStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("AlarmControlPanelStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" state: ");
out.append(proto_enum_to_string<enums::AlarmControlPanelState>(this->state));
out.append("\n");
out.append("}");
}
#endif
bool AlarmControlPanelCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->command = value.as_enum<enums::AlarmControlPanelStateCommand>();
return true;
}
default:
return false;
}
}
bool AlarmControlPanelCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 3: {
this->code = value.as_string();
return true;
}
default:
return false;
}
}
bool AlarmControlPanelCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void AlarmControlPanelCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_enum<enums::AlarmControlPanelStateCommand>(2, this->command);
buffer.encode_string(3, this->code);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void AlarmControlPanelCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("AlarmControlPanelCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" command: ");
out.append(proto_enum_to_string<enums::AlarmControlPanelStateCommand>(this->command));
out.append("\n");
out.append(" code: ");
out.append("'").append(this->code).append("'");
out.append("\n");
out.append("}");
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -99,7 +99,6 @@ enum ClimateFanMode : uint32_t {
CLIMATE_FAN_MIDDLE = 6,
CLIMATE_FAN_FOCUS = 7,
CLIMATE_FAN_DIFFUSE = 8,
CLIMATE_FAN_QUIET = 9,
};
enum ClimateSwingMode : uint32_t {
CLIMATE_SWING_OFF = 0,
@@ -163,39 +162,6 @@ enum BluetoothDeviceRequestType : uint32_t {
BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3,
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4,
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
};
enum VoiceAssistantEvent : uint32_t {
VOICE_ASSISTANT_ERROR = 0,
VOICE_ASSISTANT_RUN_START = 1,
VOICE_ASSISTANT_RUN_END = 2,
VOICE_ASSISTANT_STT_START = 3,
VOICE_ASSISTANT_STT_END = 4,
VOICE_ASSISTANT_INTENT_START = 5,
VOICE_ASSISTANT_INTENT_END = 6,
VOICE_ASSISTANT_TTS_START = 7,
VOICE_ASSISTANT_TTS_END = 8,
};
enum AlarmControlPanelState : uint32_t {
ALARM_STATE_DISARMED = 0,
ALARM_STATE_ARMED_HOME = 1,
ALARM_STATE_ARMED_AWAY = 2,
ALARM_STATE_ARMED_NIGHT = 3,
ALARM_STATE_ARMED_VACATION = 4,
ALARM_STATE_ARMED_CUSTOM_BYPASS = 5,
ALARM_STATE_PENDING = 6,
ALARM_STATE_ARMING = 7,
ALARM_STATE_DISARMING = 8,
ALARM_STATE_TRIGGERED = 9,
};
enum AlarmControlPanelStateCommand : uint32_t {
ALARM_CONTROL_PANEL_DISARM = 0,
ALARM_CONTROL_PANEL_ARM_AWAY = 1,
ALARM_CONTROL_PANEL_ARM_HOME = 2,
ALARM_CONTROL_PANEL_ARM_NIGHT = 3,
ALARM_CONTROL_PANEL_ARM_VACATION = 4,
ALARM_CONTROL_PANEL_ARM_CUSTOM_BYPASS = 5,
ALARM_CONTROL_PANEL_TRIGGER = 6,
};
} // namespace enums
@@ -308,11 +274,9 @@ class DeviceInfoResponse : public ProtoMessage {
std::string project_name{};
std::string project_version{};
uint32_t webserver_port{0};
uint32_t legacy_bluetooth_proxy_version{0};
uint32_t bluetooth_proxy_feature_flags{0};
uint32_t bluetooth_proxy_version{0};
std::string manufacturer{};
std::string friendly_name{};
uint32_t voice_assistant_version{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -397,7 +361,6 @@ class ListEntitiesCoverResponse : public ProtoMessage {
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
bool supports_stop{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -951,7 +914,7 @@ class ListEntitiesClimateResponse : public ProtoMessage {
std::vector<enums::ClimateMode> supported_modes{};
float visual_min_temperature{0.0f};
float visual_max_temperature{0.0f};
float visual_target_temperature_step{0.0f};
float visual_temperature_step{0.0f};
bool legacy_supports_away{false};
bool supports_action{false};
std::vector<enums::ClimateFanMode> supported_fan_modes{};
@@ -962,7 +925,6 @@ class ListEntitiesClimateResponse : public ProtoMessage {
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
float visual_current_temperature_step{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -981,7 +943,7 @@ class ClimateStateResponse : public ProtoMessage {
float target_temperature{0.0f};
float target_temperature_low{0.0f};
float target_temperature_high{0.0f};
bool unused_legacy_away{false};
bool legacy_away{false};
enums::ClimateAction action{};
enums::ClimateFanMode fan_mode{};
enums::ClimateSwingMode swing_mode{};
@@ -1009,8 +971,8 @@ class ClimateCommandRequest : public ProtoMessage {
float target_temperature_low{0.0f};
bool has_target_temperature_high{false};
float target_temperature_high{0.0f};
bool unused_has_legacy_away{false};
bool unused_legacy_away{false};
bool has_legacy_away{false};
bool legacy_away{false};
bool has_fan_mode{false};
enums::ClimateFanMode fan_mode{};
bool has_swing_mode{false};
@@ -1269,14 +1231,12 @@ class MediaPlayerCommandRequest : public ProtoMessage {
};
class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
public:
uint32_t flags{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothServiceData : public ProtoMessage {
public:
@@ -1310,32 +1270,6 @@ class BluetoothLEAdvertisementResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothLERawAdvertisement : public ProtoMessage {
public:
uint64_t address{0};
int32_t rssi{0};
uint32_t address_type{0};
std::string data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
public:
std::vector<BluetoothLERawAdvertisement> advertisements{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class BluetoothDeviceRequest : public ProtoMessage {
public:
uint64_t address{0};
@@ -1592,166 +1526,6 @@ class BluetoothGATTNotifyResponse : public ProtoMessage {
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothDevicePairingResponse : public ProtoMessage {
public:
uint64_t address{0};
bool paired{false};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothDeviceUnpairingResponse : public ProtoMessage {
public:
uint64_t address{0};
bool success{false};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
public:
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
class BluetoothDeviceClearCacheResponse : public ProtoMessage {
public:
uint64_t address{0};
bool success{false};
int32_t error{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SubscribeVoiceAssistantRequest : public ProtoMessage {
public:
bool subscribe{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantRequest : public ProtoMessage {
public:
bool start{false};
std::string conversation_id{};
bool use_vad{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantResponse : public ProtoMessage {
public:
uint32_t port{0};
bool error{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantEventData : public ProtoMessage {
public:
std::string name{};
std::string value{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class VoiceAssistantEventResponse : public ProtoMessage {
public:
enums::VoiceAssistantEvent event_type{};
std::vector<VoiceAssistantEventData> data{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
uint32_t supported_features{0};
bool requires_code{false};
bool requires_code_to_arm{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class AlarmControlPanelStateResponse : public ProtoMessage {
public:
uint32_t key{0};
enums::AlarmControlPanelState state{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class AlarmControlPanelCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
enums::AlarmControlPanelStateCommand command{};
std::string code{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
} // namespace api
} // namespace esphome

View File

@@ -329,8 +329,6 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer
#ifdef USE_MEDIA_PLAYER
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
@@ -339,15 +337,6 @@ bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const Blu
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_le_raw_advertisements_response(
const BluetoothLERawAdvertisementsResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_le_raw_advertisements_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothLERawAdvertisementsResponse>(msg, 93);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) {
@@ -436,65 +425,6 @@ bool APIServerConnectionBase::send_bluetooth_gatt_notify_response(const Bluetoot
return this->send_message_<BluetoothGATTNotifyResponse>(msg, 84);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_pairing_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDevicePairingResponse>(msg, 85);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_unpairing_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceUnpairingResponse>(msg, 86);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantRequest>(msg, 90);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
const ListEntitiesAlarmControlPanelResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_alarm_control_panel_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesAlarmControlPanelResponse>(msg, 94);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool APIServerConnectionBase::send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_alarm_control_panel_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<AlarmControlPanelStateResponse>(msg, 95);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@@ -763,14 +693,12 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
break;
}
case 66: {
#ifdef USE_BLUETOOTH_PROXY
SubscribeBluetoothLEAdvertisementsRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_bluetooth_le_advertisements_request(msg);
#endif
break;
}
case 68: {
@@ -858,61 +786,6 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_subscribe_bluetooth_connections_free_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_bluetooth_connections_free_request(msg);
#endif
break;
}
case 87: {
#ifdef USE_BLUETOOTH_PROXY
UnsubscribeBluetoothLEAdvertisementsRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_unsubscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
#endif
this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
#endif
break;
}
case 89: {
#ifdef USE_VOICE_ASSISTANT
SubscribeVoiceAssistantRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_subscribe_voice_assistant_request: %s", msg.dump().c_str());
#endif
this->on_subscribe_voice_assistant_request(msg);
#endif
break;
}
case 91: {
#ifdef USE_VOICE_ASSISTANT
VoiceAssistantResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_voice_assistant_response: %s", msg.dump().c_str());
#endif
this->on_voice_assistant_response(msg);
#endif
break;
}
case 92: {
#ifdef USE_VOICE_ASSISTANT
VoiceAssistantEventResponse msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_voice_assistant_event_response: %s", msg.dump().c_str());
#endif
this->on_voice_assistant_event_response(msg);
#endif
break;
}
case 96: {
#ifdef USE_ALARM_CONTROL_PANEL
AlarmControlPanelCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_alarm_control_panel_command_request: %s", msg.dump().c_str());
#endif
this->on_alarm_control_panel_command_request(msg);
#endif
break;
}
@@ -1176,7 +1049,6 @@ void APIServerConnection::on_media_player_command_request(const MediaPlayerComma
this->media_player_command(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
if (!this->is_connection_setup()) {
@@ -1189,7 +1061,6 @@ void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
}
this->subscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
if (!this->is_connection_setup()) {
@@ -1298,46 +1169,6 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
}
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->unsubscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_voice_assistant(msg);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->alarm_control_panel_command(msg);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -154,16 +154,11 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_MEDIA_PLAYER
virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_raw_advertisements_response(const BluetoothLERawAdvertisementsResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){};
#endif
@@ -214,40 +209,6 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_request(const VoiceAssistantRequest &msg);
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){};
#endif
protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@@ -300,9 +261,7 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_MEDIA_PLAYER
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
#endif
@@ -327,15 +286,6 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_BLUETOOTH_PROXY
virtual BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
@@ -383,9 +333,7 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_MEDIA_PLAYER
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
#endif
@@ -410,16 +358,6 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_BLUETOOTH_PROXY
void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
#endif
#ifdef USE_VOICE_ASSISTANT
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
#endif
};
} // namespace api

View File

@@ -45,7 +45,7 @@ void APIServer::setup() {
struct sockaddr_storage server;
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_);
socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_));
if (sl == 0) {
ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
this->mark_failed();
@@ -291,7 +291,79 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
client->send_homeassistant_service_call(call);
}
}
#ifdef USE_BLUETOOTH_PROXY
void APIServer::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_le_advertisement(call);
}
}
void APIServer::send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu, esp_err_t error) {
BluetoothDeviceConnectionResponse call;
call.address = address;
call.connected = connected;
call.mtu = mtu;
call.error = error;
for (auto &client : this->clients_) {
client->send_bluetooth_device_connection_response(call);
}
}
void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) {
BluetoothConnectionsFreeResponse call;
call.free = free;
call.limit = limit;
for (auto &client : this->clients_) {
client->send_bluetooth_connections_free_response(call);
}
}
void APIServer::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_read_response(call);
}
}
void APIServer::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_write_response(call);
}
}
void APIServer::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_notify_data_response(call);
}
}
void APIServer::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_notify_response(call);
}
}
void APIServer::send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call) {
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_get_services_response(call);
}
}
void APIServer::send_bluetooth_gatt_services_done(uint64_t address) {
BluetoothGATTGetServicesDoneResponse call;
call.address = address;
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_get_services_done_response(call);
}
}
void APIServer::send_bluetooth_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
BluetoothGATTErrorResponse call;
call.address = address;
call.handle = handle;
call.error = error;
for (auto &client : this->clients_) {
client->send_bluetooth_gatt_error_response(call);
}
}
#endif
APIServer::APIServer() { global_api_server = this; }
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
@@ -322,30 +394,5 @@ void APIServer::on_shutdown() {
delay(10);
}
#ifdef USE_VOICE_ASSISTANT
bool APIServer::start_voice_assistant(const std::string &conversation_id, bool use_vad) {
for (auto &c : this->clients_) {
if (c->request_voice_assistant(true, conversation_id, use_vad))
return true;
}
return false;
}
void APIServer::stop_voice_assistant() {
for (auto &c : this->clients_) {
if (c->request_voice_assistant(false, "", false))
return;
}
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_alarm_control_panel_state(obj);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -75,20 +75,23 @@ class APIServer : public Component, public Controller {
void on_media_player_update(media_player::MediaPlayer *obj) override;
#endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
#ifdef USE_BLUETOOTH_PROXY
void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call);
void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK);
void send_bluetooth_connections_free(uint8_t free, uint8_t limit);
void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call);
void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call);
void send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &call);
void send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &call);
void send_bluetooth_gatt_services(const BluetoothGATTGetServicesResponse &call);
void send_bluetooth_gatt_services_done(uint64_t address);
void send_bluetooth_gatt_error(uint64_t address, uint16_t handle, esp_err_t error);
#endif
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
#ifdef USE_HOMEASSISTANT_TIME
void request_time();
#endif
#ifdef USE_VOICE_ASSISTANT
bool start_voice_assistant(const std::string &conversation_id, bool use_vad);
void stop_voice_assistant();
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override;
#endif
bool is_connected() const;
struct HomeAssistantStateSubscription {

View File

@@ -47,7 +47,7 @@ async def async_run_logs(config, address):
except APIConnectionError:
cli.disconnect()
async def on_disconnect(expected_disconnect: bool) -> None:
async def on_disconnect():
_LOGGER.warning("Disconnected from API")
zc = zeroconf.Zeroconf()

View File

@@ -69,11 +69,6 @@ bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_play
return this->client_->send_media_player_info(media_player);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
return this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -54,9 +54,6 @@ class ListEntitiesIterator : public ComponentIterator {
#endif
#ifdef USE_MEDIA_PLAYER
bool on_media_player(media_player::MediaPlayer *media_player) override;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
#endif
bool on_end() override;

View File

@@ -1,5 +1,4 @@
#include "proto.h"
#include <cinttypes>
#include "esphome/core/log.h"
namespace esphome {
@@ -14,7 +13,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
uint32_t consumed;
auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i);
ESP_LOGV(TAG, "Invalid field start at %u", i);
break;
}
@@ -26,12 +25,12 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
case 0: { // VarInt
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i);
ESP_LOGV(TAG, "Invalid VarInt at %u", i);
error = true;
break;
}
if (!this->decode_varint(field_id, *res)) {
ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
ESP_LOGV(TAG, "Cannot decode VarInt field %u with value %u!", field_id, res->as_uint32());
}
i += consumed;
break;
@@ -39,38 +38,38 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
case 2: { // Length-delimited
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i);
ESP_LOGV(TAG, "Invalid Length Delimited at %u", i);
error = true;
break;
}
uint32_t field_length = res->as_uint32();
i += consumed;
if (field_length > length - i) {
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i);
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %u", i);
error = true;
break;
}
if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id);
ESP_LOGV(TAG, "Cannot decode Length Delimited field %u!", field_id);
}
i += field_length;
break;
}
case 5: { // 32-bit
if (length - i < 4) {
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %" PRIu32, i);
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %u", i);
error = true;
break;
}
uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
if (!this->decode_32bit(field_id, Proto32Bit(val))) {
ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val);
ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
}
i += 4;
break;
}
default:
ESP_LOGV(TAG, "Invalid field type at %" PRIu32, i);
ESP_LOGV(TAG, "Invalid field type at %u", i);
error = true;
break;
}

View File

@@ -55,11 +55,6 @@ bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_play
return this->client_->send_media_player_state(media_player);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool InitialStateIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
return this->client_->send_alarm_control_panel_state(a_alarm_control_panel);
}
#endif
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
} // namespace api

View File

@@ -51,9 +51,6 @@ class InitialStateIterator : public ComponentIterator {
#endif
#ifdef USE_MEDIA_PLAYER
bool on_media_player(media_player::MediaPlayer *media_player) override;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
#endif
protected:
APIConnection *client_;

View File

@@ -12,6 +12,7 @@ from esphome.const import (
CONF_CAPACITANCE,
)
AUTO_LOAD = ["sensor", "binary_sensor"]
MULTI_CONF = True
CONF_AS3935_ID = "as3935_id"

View File

@@ -26,13 +26,9 @@ void AS3935Component::setup() {
void AS3935Component::dump_config() {
ESP_LOGCONFIG(TAG, "AS3935:");
LOG_PIN(" Interrupt Pin: ", this->irq_pin_);
#ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "Thunder alert", this->thunder_alert_binary_sensor_);
#endif
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Distance", this->distance_sensor_);
LOG_SENSOR(" ", "Lightning energy", this->energy_sensor_);
#endif
}
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
@@ -48,22 +44,16 @@ void AS3935Component::loop() {
ESP_LOGI(TAG, "Disturber was detected - try increasing the spike rejection value!");
} else if (int_value == LIGHTNING_INT) {
ESP_LOGI(TAG, "Lightning has been detected!");
#ifdef USE_BINARY_SENSOR
if (this->thunder_alert_binary_sensor_ != nullptr) {
if (this->thunder_alert_binary_sensor_ != nullptr)
this->thunder_alert_binary_sensor_->publish_state(true);
this->set_timeout(10, [this]() { this->thunder_alert_binary_sensor_->publish_state(false); });
}
#endif
#ifdef USE_SENSOR
uint8_t distance = this->get_distance_to_storm_();
if (this->distance_sensor_ != nullptr)
this->distance_sensor_->publish_state(distance);
uint32_t energy = this->get_lightning_energy_();
if (this->energy_sensor_ != nullptr)
this->energy_sensor_->publish_state(energy);
#endif
}
this->thunder_alert_binary_sensor_->publish_state(false);
}
void AS3935Component::write_indoor(bool indoor) {

View File

@@ -1,14 +1,9 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif
namespace esphome {
namespace as3935 {
@@ -57,15 +52,6 @@ enum AS3935Values {
};
class AS3935Component : public Component {
#ifdef USE_SENSOR
SUB_SENSOR(distance)
SUB_SENSOR(energy)
#endif
#ifdef USE_BINARY_SENSOR
SUB_BINARY_SENSOR(thunder_alert)
#endif
public:
void setup() override;
void dump_config() override;
@@ -73,7 +59,11 @@ class AS3935Component : public Component {
void loop() override;
void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; }
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) {
thunder_alert_binary_sensor_ = thunder_alert_binary_sensor;
}
void set_indoor(bool indoor) { indoor_ = indoor; }
void write_indoor(bool indoor);
void set_noise_level(uint8_t noise_level) { noise_level_ = noise_level; }
@@ -102,6 +92,9 @@ class AS3935Component : public Component {
virtual void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) = 0;
sensor::Sensor *distance_sensor_{nullptr};
sensor::Sensor *energy_sensor_{nullptr};
binary_sensor::BinarySensor *thunder_alert_binary_sensor_{nullptr};
GPIOPin *irq_pin_;
bool indoor_;

View File

@@ -31,10 +31,12 @@ CONFIG_SCHEMA = cv.Schema(
async def to_code(config):
hub = await cg.get_variable(config[CONF_AS3935_ID])
if distance_config := config.get(CONF_DISTANCE):
sens = await sensor.new_sensor(distance_config)
cg.add(hub.set_distance_sensor(sens))
if CONF_DISTANCE in config:
conf = config[CONF_DISTANCE]
distance_sensor = await sensor.new_sensor(conf)
cg.add(hub.set_distance_sensor(distance_sensor))
if lightning_energy_config := config.get(CONF_LIGHTNING_ENERGY):
sens = await sensor.new_sensor(lightning_energy_config)
cg.add(hub.set_energy_sensor(sens))
if CONF_LIGHTNING_ENERGY in config:
conf = config[CONF_LIGHTNING_ENERGY]
lightning_energy_sensor = await sensor.new_sensor(conf)
cg.add(hub.set_energy_sensor(lightning_energy_sensor))

View File

@@ -1,271 +0,0 @@
#include "as7341.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace as7341 {
static const char *const TAG = "as7341";
void AS7341Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AS7341...");
LOG_I2C_DEVICE(this);
// Verify device ID
uint8_t id;
this->read_byte(AS7341_ID, &id);
ESP_LOGCONFIG(TAG, " Read ID: 0x%X", id);
if ((id & 0xFC) != (AS7341_CHIP_ID << 2)) {
this->mark_failed();
return;
}
// Power on (enter IDLE state)
if (!this->enable_power(true)) {
ESP_LOGE(TAG, " Power on failed!");
this->mark_failed();
return;
}
// Set configuration
this->write_byte(AS7341_CONFIG, 0x00);
this->setup_atime(this->atime_);
this->setup_astep(this->astep_);
this->setup_gain(this->gain_);
}
void AS7341Component::dump_config() {
ESP_LOGCONFIG(TAG, "AS7341:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with AS7341 failed!");
}
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG, " Gain: %u", get_gain());
ESP_LOGCONFIG(TAG, " ATIME: %u", get_atime());
ESP_LOGCONFIG(TAG, " ASTEP: %u", get_astep());
LOG_SENSOR(" ", "F1", this->f1_);
LOG_SENSOR(" ", "F2", this->f2_);
LOG_SENSOR(" ", "F3", this->f3_);
LOG_SENSOR(" ", "F4", this->f4_);
LOG_SENSOR(" ", "F5", this->f5_);
LOG_SENSOR(" ", "F6", this->f6_);
LOG_SENSOR(" ", "F7", this->f7_);
LOG_SENSOR(" ", "F8", this->f8_);
LOG_SENSOR(" ", "Clear", this->clear_);
LOG_SENSOR(" ", "NIR", this->nir_);
}
float AS7341Component::get_setup_priority() const { return setup_priority::DATA; }
void AS7341Component::update() {
this->read_channels(this->channel_readings_);
if (this->f1_ != nullptr) {
this->f1_->publish_state(this->channel_readings_[0]);
}
if (this->f2_ != nullptr) {
this->f2_->publish_state(this->channel_readings_[1]);
}
if (this->f3_ != nullptr) {
this->f3_->publish_state(this->channel_readings_[2]);
}
if (this->f4_ != nullptr) {
this->f4_->publish_state(this->channel_readings_[3]);
}
if (this->f5_ != nullptr) {
this->f5_->publish_state(this->channel_readings_[6]);
}
if (this->f6_ != nullptr) {
this->f6_->publish_state(this->channel_readings_[7]);
}
if (this->f7_ != nullptr) {
this->f7_->publish_state(this->channel_readings_[8]);
}
if (this->f8_ != nullptr) {
this->f8_->publish_state(this->channel_readings_[9]);
}
if (this->clear_ != nullptr) {
this->clear_->publish_state(this->channel_readings_[10]);
}
if (this->nir_ != nullptr) {
this->nir_->publish_state(this->channel_readings_[11]);
}
}
AS7341Gain AS7341Component::get_gain() {
uint8_t data;
this->read_byte(AS7341_CFG1, &data);
return (AS7341Gain) data;
}
uint8_t AS7341Component::get_atime() {
uint8_t data;
this->read_byte(AS7341_ATIME, &data);
return data;
}
uint16_t AS7341Component::get_astep() {
uint16_t data;
this->read_byte_16(AS7341_ASTEP, &data);
return this->swap_bytes(data);
}
bool AS7341Component::setup_gain(AS7341Gain gain) { return this->write_byte(AS7341_CFG1, gain); }
bool AS7341Component::setup_atime(uint8_t atime) { return this->write_byte(AS7341_ATIME, atime); }
bool AS7341Component::setup_astep(uint16_t astep) { return this->write_byte_16(AS7341_ASTEP, swap_bytes(astep)); }
bool AS7341Component::read_channels(uint16_t *data) {
this->set_smux_low_channels(true);
this->enable_spectral_measurement(true);
this->wait_for_data();
bool low_success = this->read_bytes_16(AS7341_CH0_DATA_L, data, 6);
this->set_smux_low_channels(false);
this->enable_spectral_measurement(true);
this->wait_for_data();
bool high_sucess = this->read_bytes_16(AS7341_CH0_DATA_L, &data[6], 6);
return low_success && high_sucess;
}
void AS7341Component::set_smux_low_channels(bool enable) {
this->enable_spectral_measurement(false);
this->set_smux_command(AS7341_SMUX_CMD_WRITE);
if (enable) {
this->configure_smux_low_channels();
} else {
this->configure_smux_high_channels();
}
this->enable_smux();
}
bool AS7341Component::set_smux_command(AS7341SmuxCommand command) {
uint8_t data = command << 3; // Write to bits 4:3 of the register
return this->write_byte(AS7341_CFG6, data);
}
void AS7341Component::configure_smux_low_channels() {
// SMUX Config for F1,F2,F3,F4,NIR,Clear
this->write_byte(0x00, 0x30); // F3 left set to ADC2
this->write_byte(0x01, 0x01); // F1 left set to ADC0
this->write_byte(0x02, 0x00); // Reserved or disabled
this->write_byte(0x03, 0x00); // F8 left disabled
this->write_byte(0x04, 0x00); // F6 left disabled
this->write_byte(0x05, 0x42); // F4 left connected to ADC3/f2 left connected to ADC1
this->write_byte(0x06, 0x00); // F5 left disbled
this->write_byte(0x07, 0x00); // F7 left disbled
this->write_byte(0x08, 0x50); // CLEAR connected to ADC4
this->write_byte(0x09, 0x00); // F5 right disabled
this->write_byte(0x0A, 0x00); // F7 right disabled
this->write_byte(0x0B, 0x00); // Reserved or disabled
this->write_byte(0x0C, 0x20); // F2 right connected to ADC1
this->write_byte(0x0D, 0x04); // F4 right connected to ADC3
this->write_byte(0x0E, 0x00); // F6/F8 right disabled
this->write_byte(0x0F, 0x30); // F3 right connected to AD2
this->write_byte(0x10, 0x01); // F1 right connected to AD0
this->write_byte(0x11, 0x50); // CLEAR right connected to AD4
this->write_byte(0x12, 0x00); // Reserved or disabled
this->write_byte(0x13, 0x06); // NIR connected to ADC5
}
void AS7341Component::configure_smux_high_channels() {
// SMUX Config for F5,F6,F7,F8,NIR,Clear
this->write_byte(0x00, 0x00); // F3 left disable
this->write_byte(0x01, 0x00); // F1 left disable
this->write_byte(0x02, 0x00); // reserved/disable
this->write_byte(0x03, 0x40); // F8 left connected to ADC3
this->write_byte(0x04, 0x02); // F6 left connected to ADC1
this->write_byte(0x05, 0x00); // F4/ F2 disabled
this->write_byte(0x06, 0x10); // F5 left connected to ADC0
this->write_byte(0x07, 0x03); // F7 left connected to ADC2
this->write_byte(0x08, 0x50); // CLEAR Connected to ADC4
this->write_byte(0x09, 0x10); // F5 right connected to ADC0
this->write_byte(0x0A, 0x03); // F7 right connected to ADC2
this->write_byte(0x0B, 0x00); // Reserved or disabled
this->write_byte(0x0C, 0x00); // F2 right disabled
this->write_byte(0x0D, 0x00); // F4 right disabled
this->write_byte(0x0E, 0x24); // F8 right connected to ADC2/ F6 right connected to ADC1
this->write_byte(0x0F, 0x00); // F3 right disabled
this->write_byte(0x10, 0x00); // F1 right disabled
this->write_byte(0x11, 0x50); // CLEAR right connected to AD4
this->write_byte(0x12, 0x00); // Reserved or disabled
this->write_byte(0x13, 0x06); // NIR connected to ADC5
}
bool AS7341Component::enable_smux() {
this->set_register_bit(AS7341_ENABLE, 4);
uint16_t timeout = 1000;
for (uint16_t time = 0; time < timeout; time++) {
// The SMUXEN bit is cleared once the SMUX operation is finished
bool smuxen = this->read_register_bit(AS7341_ENABLE, 4);
if (!smuxen) {
return true;
}
delay(1);
}
return false;
}
bool AS7341Component::wait_for_data() {
uint16_t timeout = 1000;
for (uint16_t time = 0; time < timeout; time++) {
if (this->is_data_ready()) {
return true;
}
delay(1);
}
return false;
}
bool AS7341Component::is_data_ready() { return this->read_register_bit(AS7341_STATUS2, 6); }
bool AS7341Component::enable_power(bool enable) { return this->write_register_bit(AS7341_ENABLE, enable, 0); }
bool AS7341Component::enable_spectral_measurement(bool enable) {
return this->write_register_bit(AS7341_ENABLE, enable, 1);
}
bool AS7341Component::read_register_bit(uint8_t address, uint8_t bit_position) {
uint8_t data;
this->read_byte(address, &data);
bool bit = (data & (1 << bit_position)) > 0;
return bit;
}
bool AS7341Component::write_register_bit(uint8_t address, bool value, uint8_t bit_position) {
if (value) {
return this->set_register_bit(address, bit_position);
}
return this->clear_register_bit(address, bit_position);
}
bool AS7341Component::set_register_bit(uint8_t address, uint8_t bit_position) {
uint8_t data;
this->read_byte(address, &data);
data |= (1 << bit_position);
return this->write_byte(address, data);
}
bool AS7341Component::clear_register_bit(uint8_t address, uint8_t bit_position) {
uint8_t data;
this->read_byte(address, &data);
data &= ~(1 << bit_position);
return this->write_byte(address, data);
}
uint16_t AS7341Component::swap_bytes(uint16_t data) { return (data >> 8) | (data << 8); }
} // namespace as7341
} // namespace esphome

View File

@@ -1,144 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace as7341 {
static const uint8_t AS7341_CHIP_ID = 0X09;
static const uint8_t AS7341_CONFIG = 0x70;
static const uint8_t AS7341_LED = 0x74;
static const uint8_t AS7341_ENABLE = 0x80;
static const uint8_t AS7341_ATIME = 0x81;
static const uint8_t AS7341_WTIME = 0x83;
static const uint8_t AS7341_AUXID = 0x90;
static const uint8_t AS7341_REVID = 0x91;
static const uint8_t AS7341_ID = 0x92;
static const uint8_t AS7341_STATUS = 0x93;
static const uint8_t AS7341_CH0_DATA_L = 0x95;
static const uint8_t AS7341_CH0_DATA_H = 0x96;
static const uint8_t AS7341_CH1_DATA_L = 0x97;
static const uint8_t AS7341_CH1_DATA_H = 0x98;
static const uint8_t AS7341_CH2_DATA_L = 0x99;
static const uint8_t AS7341_CH2_DATA_H = 0x9A;
static const uint8_t AS7341_CH3_DATA_L = 0x9B;
static const uint8_t AS7341_CH3_DATA_H = 0x9C;
static const uint8_t AS7341_CH4_DATA_L = 0x9D;
static const uint8_t AS7341_CH4_DATA_H = 0x9E;
static const uint8_t AS7341_CH5_DATA_L = 0x9F;
static const uint8_t AS7341_CH5_DATA_H = 0xA0;
static const uint8_t AS7341_STATUS2 = 0xA3;
static const uint8_t AS7341_CFG1 = 0xAA; ///< Controls ADC Gain
static const uint8_t AS7341_CFG6 = 0xAF; // Stores SMUX command
static const uint8_t AS7341_CFG9 = 0xB2; // Config for system interrupts (SMUX, Flicker detection)
static const uint8_t AS7341_ASTEP = 0xCA; // LSB
static const uint8_t AS7341_ASTEP_MSB = 0xCB; // MSB
enum AS7341AdcChannel {
AS7341_ADC_CHANNEL_0,
AS7341_ADC_CHANNEL_1,
AS7341_ADC_CHANNEL_2,
AS7341_ADC_CHANNEL_3,
AS7341_ADC_CHANNEL_4,
AS7341_ADC_CHANNEL_5,
};
enum AS7341SmuxCommand {
AS7341_SMUX_CMD_ROM_RESET, ///< ROM code initialization of SMUX
AS7341_SMUX_CMD_READ, ///< Read SMUX configuration to RAM from SMUX chain
AS7341_SMUX_CMD_WRITE, ///< Write SMUX configuration from RAM to SMUX chain
};
enum AS7341Gain {
AS7341_GAIN_0_5X,
AS7341_GAIN_1X,
AS7341_GAIN_2X,
AS7341_GAIN_4X,
AS7341_GAIN_8X,
AS7341_GAIN_16X,
AS7341_GAIN_32X,
AS7341_GAIN_64X,
AS7341_GAIN_128X,
AS7341_GAIN_256X,
AS7341_GAIN_512X,
};
class AS7341Component : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_f1_sensor(sensor::Sensor *f1_sensor) { this->f1_ = f1_sensor; }
void set_f2_sensor(sensor::Sensor *f2_sensor) { f2_ = f2_sensor; }
void set_f3_sensor(sensor::Sensor *f3_sensor) { f3_ = f3_sensor; }
void set_f4_sensor(sensor::Sensor *f4_sensor) { f4_ = f4_sensor; }
void set_f5_sensor(sensor::Sensor *f5_sensor) { f5_ = f5_sensor; }
void set_f6_sensor(sensor::Sensor *f6_sensor) { f6_ = f6_sensor; }
void set_f7_sensor(sensor::Sensor *f7_sensor) { f7_ = f7_sensor; }
void set_f8_sensor(sensor::Sensor *f8_sensor) { f8_ = f8_sensor; }
void set_clear_sensor(sensor::Sensor *clear_sensor) { clear_ = clear_sensor; }
void set_nir_sensor(sensor::Sensor *nir_sensor) { nir_ = nir_sensor; }
void set_gain(AS7341Gain gain) { gain_ = gain; }
void set_atime(uint8_t atime) { atime_ = atime; }
void set_astep(uint16_t astep) { astep_ = astep; }
AS7341Gain get_gain();
uint8_t get_atime();
uint16_t get_astep();
bool setup_gain(AS7341Gain gain);
bool setup_atime(uint8_t atime);
bool setup_astep(uint16_t astep);
uint16_t read_channel(AS7341AdcChannel channel);
bool read_channels(uint16_t *data);
void set_smux_low_channels(bool enable);
bool set_smux_command(AS7341SmuxCommand command);
void configure_smux_low_channels();
void configure_smux_high_channels();
bool enable_smux();
bool wait_for_data();
bool is_data_ready();
bool enable_power(bool enable);
bool enable_spectral_measurement(bool enable);
bool read_register_bit(uint8_t address, uint8_t bit_position);
bool write_register_bit(uint8_t address, bool value, uint8_t bit_position);
bool set_register_bit(uint8_t address, uint8_t bit_position);
bool clear_register_bit(uint8_t address, uint8_t bit_position);
uint16_t swap_bytes(uint16_t data);
protected:
sensor::Sensor *f1_{nullptr};
sensor::Sensor *f2_{nullptr};
sensor::Sensor *f3_{nullptr};
sensor::Sensor *f4_{nullptr};
sensor::Sensor *f5_{nullptr};
sensor::Sensor *f6_{nullptr};
sensor::Sensor *f7_{nullptr};
sensor::Sensor *f8_{nullptr};
sensor::Sensor *clear_{nullptr};
sensor::Sensor *nir_{nullptr};
uint16_t astep_;
AS7341Gain gain_;
uint8_t atime_;
uint16_t channel_readings_[12];
};
} // namespace as7341
} // namespace esphome

View File

@@ -1,112 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_GAIN,
CONF_ID,
DEVICE_CLASS_ILLUMINANCE,
ICON_BRIGHTNESS_5,
STATE_CLASS_MEASUREMENT,
)
CODEOWNERS = ["@mrgnr"]
DEPENDENCIES = ["i2c"]
as7341_ns = cg.esphome_ns.namespace("as7341")
AS7341Component = as7341_ns.class_(
"AS7341Component", cg.PollingComponent, i2c.I2CDevice
)
CONF_ATIME = "atime"
CONF_ASTEP = "astep"
CONF_F1 = "f1"
CONF_F2 = "f2"
CONF_F3 = "f3"
CONF_F4 = "f4"
CONF_F5 = "f5"
CONF_F6 = "f6"
CONF_F7 = "f7"
CONF_F8 = "f8"
CONF_CLEAR = "clear"
CONF_NIR = "nir"
UNIT_COUNTS = "#"
AS7341_GAIN = as7341_ns.enum("AS7341Gain")
GAIN_OPTIONS = {
"X0.5": AS7341_GAIN.AS7341_GAIN_0_5X,
"X1": AS7341_GAIN.AS7341_GAIN_1X,
"X2": AS7341_GAIN.AS7341_GAIN_2X,
"X4": AS7341_GAIN.AS7341_GAIN_4X,
"X8": AS7341_GAIN.AS7341_GAIN_8X,
"X16": AS7341_GAIN.AS7341_GAIN_16X,
"X32": AS7341_GAIN.AS7341_GAIN_32X,
"X64": AS7341_GAIN.AS7341_GAIN_64X,
"X128": AS7341_GAIN.AS7341_GAIN_128X,
"X256": AS7341_GAIN.AS7341_GAIN_256X,
"X512": AS7341_GAIN.AS7341_GAIN_512X,
}
SENSOR_SCHEMA = sensor.sensor_schema(
unit_of_measurement=UNIT_COUNTS,
icon=ICON_BRIGHTNESS_5,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AS7341Component),
cv.Optional(CONF_F1): SENSOR_SCHEMA,
cv.Optional(CONF_F2): SENSOR_SCHEMA,
cv.Optional(CONF_F3): SENSOR_SCHEMA,
cv.Optional(CONF_F4): SENSOR_SCHEMA,
cv.Optional(CONF_F5): SENSOR_SCHEMA,
cv.Optional(CONF_F6): SENSOR_SCHEMA,
cv.Optional(CONF_F7): SENSOR_SCHEMA,
cv.Optional(CONF_F8): SENSOR_SCHEMA,
cv.Optional(CONF_CLEAR): SENSOR_SCHEMA,
cv.Optional(CONF_NIR): SENSOR_SCHEMA,
cv.Optional(CONF_GAIN, default="X8"): cv.enum(GAIN_OPTIONS),
cv.Optional(CONF_ATIME, default=29): cv.int_range(min=0, max=255),
cv.Optional(CONF_ASTEP, default=599): cv.int_range(min=0, max=65534),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x39))
)
SENSORS = {
CONF_F1: "set_f1_sensor",
CONF_F2: "set_f2_sensor",
CONF_F3: "set_f3_sensor",
CONF_F4: "set_f4_sensor",
CONF_F5: "set_f5_sensor",
CONF_F6: "set_f6_sensor",
CONF_F7: "set_f7_sensor",
CONF_F8: "set_f8_sensor",
CONF_CLEAR: "set_clear_sensor",
CONF_NIR: "set_nir_sensor",
}
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_atime(config[CONF_ATIME]))
cg.add(var.set_astep(config[CONF_ASTEP]))
for conf_id, set_sensor_func in SENSORS.items():
if sens_config := config.get(conf_id):
sens = await sensor.new_sensor(sens_config)
cg.add(getattr(var, set_sensor_func)(sens))

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