mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 15:38:11 +01:00
commit
8051c1ca99
861 changed files with 21802 additions and 8720 deletions
27
.clang-tidy
27
.clang-tidy
|
@ -2,9 +2,11 @@
|
||||||
Checks: >-
|
Checks: >-
|
||||||
*,
|
*,
|
||||||
-abseil-*,
|
-abseil-*,
|
||||||
|
-altera-*,
|
||||||
-android-*,
|
-android-*,
|
||||||
-boost-*,
|
-boost-*,
|
||||||
-bugprone-branch-clone,
|
-bugprone-branch-clone,
|
||||||
|
-bugprone-easily-swappable-parameters,
|
||||||
-bugprone-narrowing-conversions,
|
-bugprone-narrowing-conversions,
|
||||||
-bugprone-signed-char-misuse,
|
-bugprone-signed-char-misuse,
|
||||||
-bugprone-too-small-loop-variable,
|
-bugprone-too-small-loop-variable,
|
||||||
|
@ -20,6 +22,7 @@ Checks: >-
|
||||||
-clang-diagnostic-sign-compare,
|
-clang-diagnostic-sign-compare,
|
||||||
-clang-diagnostic-unused-variable,
|
-clang-diagnostic-unused-variable,
|
||||||
-clang-diagnostic-unused-const-variable,
|
-clang-diagnostic-unused-const-variable,
|
||||||
|
-concurrency-*,
|
||||||
-cppcoreguidelines-avoid-c-arrays,
|
-cppcoreguidelines-avoid-c-arrays,
|
||||||
-cppcoreguidelines-avoid-goto,
|
-cppcoreguidelines-avoid-goto,
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
|
@ -27,7 +30,6 @@ Checks: >-
|
||||||
-cppcoreguidelines-macro-usage,
|
-cppcoreguidelines-macro-usage,
|
||||||
-cppcoreguidelines-narrowing-conversions,
|
-cppcoreguidelines-narrowing-conversions,
|
||||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||||
-cppcoreguidelines-owning-memory,
|
|
||||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||||
|
@ -61,17 +63,21 @@ Checks: >-
|
||||||
-misc-no-recursion,
|
-misc-no-recursion,
|
||||||
-misc-unused-parameters,
|
-misc-unused-parameters,
|
||||||
-modernize-avoid-c-arrays,
|
-modernize-avoid-c-arrays,
|
||||||
|
-modernize-avoid-bind,
|
||||||
|
-modernize-concat-nested-namespaces,
|
||||||
-modernize-return-braced-init-list,
|
-modernize-return-braced-init-list,
|
||||||
-modernize-use-auto,
|
-modernize-use-auto,
|
||||||
-modernize-use-default-member-init,
|
-modernize-use-default-member-init,
|
||||||
-modernize-use-equals-default,
|
-modernize-use-equals-default,
|
||||||
-modernize-use-trailing-return-type,
|
-modernize-use-trailing-return-type,
|
||||||
|
-modernize-use-nodiscard,
|
||||||
-mpi-*,
|
-mpi-*,
|
||||||
-objc-*,
|
-objc-*,
|
||||||
-readability-braces-around-statements,
|
-readability-braces-around-statements,
|
||||||
-readability-const-return-type,
|
-readability-const-return-type,
|
||||||
-readability-convert-member-functions-to-static,
|
-readability-convert-member-functions-to-static,
|
||||||
-readability-else-after-return,
|
-readability-else-after-return,
|
||||||
|
-readability-function-cognitive-complexity,
|
||||||
-readability-implicit-bool-conversion,
|
-readability-implicit-bool-conversion,
|
||||||
-readability-isolate-declaration,
|
-readability-isolate-declaration,
|
||||||
-readability-magic-numbers,
|
-readability-magic-numbers,
|
||||||
|
@ -83,7 +89,6 @@ Checks: >-
|
||||||
-readability-redundant-string-init,
|
-readability-redundant-string-init,
|
||||||
-readability-uppercase-literal-suffix,
|
-readability-uppercase-literal-suffix,
|
||||||
-readability-use-anyofallof,
|
-readability-use-anyofallof,
|
||||||
-warnings-as-errors
|
|
||||||
WarningsAsErrors: '*'
|
WarningsAsErrors: '*'
|
||||||
AnalyzeTemporaryDtors: false
|
AnalyzeTemporaryDtors: false
|
||||||
FormatStyle: google
|
FormatStyle: google
|
||||||
|
@ -108,6 +113,10 @@ CheckOptions:
|
||||||
value: llvm
|
value: llvm
|
||||||
- key: modernize-use-nullptr.NullMacros
|
- key: modernize-use-nullptr.NullMacros
|
||||||
value: 'NULL'
|
value: 'NULL'
|
||||||
|
- key: modernize-make-unique.MakeSmartPtrFunction
|
||||||
|
value: 'make_unique'
|
||||||
|
- key: modernize-make-unique.MakeSmartPtrFunctionHeader
|
||||||
|
value: 'esphome/core/helpers.h'
|
||||||
- key: readability-identifier-naming.LocalVariableCase
|
- key: readability-identifier-naming.LocalVariableCase
|
||||||
value: 'lower_case'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.ClassCase
|
- key: readability-identifier-naming.ClassCase
|
||||||
|
@ -121,15 +130,19 @@ CheckOptions:
|
||||||
- key: readability-identifier-naming.StaticConstantCase
|
- key: readability-identifier-naming.StaticConstantCase
|
||||||
value: 'UPPER_CASE'
|
value: 'UPPER_CASE'
|
||||||
- key: readability-identifier-naming.StaticVariableCase
|
- key: readability-identifier-naming.StaticVariableCase
|
||||||
value: 'UPPER_CASE'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.GlobalConstantCase
|
- key: readability-identifier-naming.GlobalConstantCase
|
||||||
value: 'UPPER_CASE'
|
value: 'UPPER_CASE'
|
||||||
- key: readability-identifier-naming.ParameterCase
|
- key: readability-identifier-naming.ParameterCase
|
||||||
value: 'lower_case'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.PrivateMemberPrefix
|
- key: readability-identifier-naming.PrivateMemberCase
|
||||||
value: 'NO_PRIVATE_MEMBERS_ALWAYS_USE_PROTECTED'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.PrivateMethodPrefix
|
- key: readability-identifier-naming.PrivateMemberSuffix
|
||||||
value: 'NO_PRIVATE_METHODS_ALWAYS_USE_PROTECTED'
|
value: '_'
|
||||||
|
- key: readability-identifier-naming.PrivateMethodCase
|
||||||
|
value: 'lower_case'
|
||||||
|
- key: readability-identifier-naming.PrivateMethodSuffix
|
||||||
|
value: '_'
|
||||||
- key: readability-identifier-naming.ClassMemberCase
|
- key: readability-identifier-naming.ClassMemberCase
|
||||||
value: 'lower_case'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.ClassMemberCase
|
- key: readability-identifier-naming.ClassMemberCase
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ESPHome Dev",
|
"name": "ESPHome Dev",
|
||||||
"context": "..",
|
"image": "esphome/esphome-lint:dev",
|
||||||
"dockerFile": "../docker/Dockerfile.dev",
|
|
||||||
"postCreateCommand": [
|
"postCreateCommand": [
|
||||||
"script/devcontainer-post-create"
|
"script/devcontainer-post-create"
|
||||||
],
|
],
|
||||||
|
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Normalize line endings to LF in the repository
|
||||||
|
* text eol=lf
|
59
.github/stale.yml
vendored
59
.github/stale.yml
vendored
|
@ -1,59 +0,0 @@
|
||||||
# Configuration for probot-stale - https://github.com/probot/stale
|
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
|
||||||
daysUntilStale: 60
|
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
|
||||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
|
||||||
daysUntilClose: 7
|
|
||||||
|
|
||||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
|
||||||
onlyLabels: []
|
|
||||||
|
|
||||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
|
||||||
exemptLabels:
|
|
||||||
- not-stale
|
|
||||||
|
|
||||||
# Set to true to ignore issues in a project (defaults to false)
|
|
||||||
exemptProjects: false
|
|
||||||
|
|
||||||
# Set to true to ignore issues in a milestone (defaults to false)
|
|
||||||
exemptMilestones: true
|
|
||||||
|
|
||||||
# Set to true to ignore issues with an assignee (defaults to false)
|
|
||||||
exemptAssignees: false
|
|
||||||
|
|
||||||
# Label to use when marking as stale
|
|
||||||
staleLabel: stale
|
|
||||||
|
|
||||||
# Comment to post when marking as stale. Set to `false` to disable
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed if no further activity occurs. Thank you
|
|
||||||
for your contributions.
|
|
||||||
|
|
||||||
# Comment to post when removing the stale label.
|
|
||||||
# unmarkComment: >
|
|
||||||
# Your comment here.
|
|
||||||
|
|
||||||
# Comment to post when closing a stale Issue or Pull Request.
|
|
||||||
# closeComment: >
|
|
||||||
# Your comment here.
|
|
||||||
|
|
||||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
|
||||||
limitPerRun: 10
|
|
||||||
|
|
||||||
# Limit to only `issues` or `pulls`
|
|
||||||
only: pulls
|
|
||||||
|
|
||||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
|
||||||
# pulls:
|
|
||||||
# daysUntilStale: 30
|
|
||||||
# markComment: >
|
|
||||||
# This pull request has been automatically marked as stale because it has not had
|
|
||||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
|
||||||
# for your contributions.
|
|
||||||
|
|
||||||
# issues:
|
|
||||||
# exemptLabels:
|
|
||||||
# - confirmed
|
|
9
.github/workflows/ci-docker.yml
vendored
9
.github/workflows/ci-docker.yml
vendored
|
@ -7,11 +7,15 @@ on:
|
||||||
paths:
|
paths:
|
||||||
- 'docker/**'
|
- 'docker/**'
|
||||||
- '.github/workflows/**'
|
- '.github/workflows/**'
|
||||||
|
- 'requirements*.txt'
|
||||||
|
- 'platformio.ini'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'docker/**'
|
- 'docker/**'
|
||||||
- '.github/workflows/**'
|
- '.github/workflows/**'
|
||||||
|
- 'requirements*.txt'
|
||||||
|
- 'platformio.ini'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-docker:
|
check-docker:
|
||||||
|
@ -27,6 +31,11 @@ jobs:
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
|
||||||
- name: Set TAG
|
- name: Set TAG
|
||||||
run: |
|
run: |
|
||||||
echo "TAG=check" >> $GITHUB_ENV
|
echo "TAG=check" >> $GITHUB_ENV
|
||||||
|
|
151
.github/workflows/ci.yml
vendored
151
.github/workflows/ci.yml
vendored
|
@ -9,58 +9,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci-with-container:
|
|
||||||
name: ${{ matrix.name }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- id: clang-format
|
|
||||||
name: Run script/clang-format
|
|
||||||
- id: clang-tidy
|
|
||||||
name: Run script/clang-tidy 1/4
|
|
||||||
split: 1
|
|
||||||
- id: clang-tidy
|
|
||||||
name: Run script/clang-tidy 2/4
|
|
||||||
split: 2
|
|
||||||
- id: clang-tidy
|
|
||||||
name: Run script/clang-tidy 3/4
|
|
||||||
split: 3
|
|
||||||
- id: clang-tidy
|
|
||||||
name: Run script/clang-tidy 4/4
|
|
||||||
split: 4
|
|
||||||
|
|
||||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
|
||||||
# doesn't have to be installed
|
|
||||||
container: ghcr.io/esphome/esphome-lint:1.1
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Register problem matchers
|
|
||||||
run: |
|
|
||||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
|
||||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
|
||||||
|
|
||||||
# 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 --split-num 4 --split-at ${{ matrix.split }}
|
|
||||||
if: ${{ matrix.id == 'clang-tidy' }}
|
|
||||||
|
|
||||||
- name: Suggested changes
|
|
||||||
run: script/ci-suggest-changes
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
ci:
|
ci:
|
||||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
|
||||||
# This way, all dependencies are cached via the cache action.
|
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
@ -74,48 +23,87 @@ jobs:
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test1.yaml
|
file: tests/test1.yaml
|
||||||
name: Test tests/test1.yaml
|
name: Test tests/test1.yaml
|
||||||
|
pio_cache_key: test1
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test2.yaml
|
file: tests/test2.yaml
|
||||||
name: Test tests/test2.yaml
|
name: Test tests/test2.yaml
|
||||||
|
pio_cache_key: test2
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test3.yaml
|
file: tests/test3.yaml
|
||||||
name: Test tests/test3.yaml
|
name: Test tests/test3.yaml
|
||||||
|
pio_cache_key: test1
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test4.yaml
|
file: tests/test4.yaml
|
||||||
name: Test tests/test4.yaml
|
name: Test tests/test4.yaml
|
||||||
|
pio_cache_key: test4
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test5.yaml
|
file: tests/test5.yaml
|
||||||
name: Test tests/test5.yaml
|
name: Test tests/test5.yaml
|
||||||
|
pio_cache_key: test5
|
||||||
- id: pytest
|
- id: pytest
|
||||||
name: Run 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-tidy --grep USE_ESP8266
|
||||||
|
pio_cache_key: tidyesp8266
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 1/4
|
||||||
|
options: --environment esp32-tidy --split-num 4 --split-at 1
|
||||||
|
pio_cache_key: tidyesp32
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 2/4
|
||||||
|
options: --environment esp32-tidy --split-num 4 --split-at 2
|
||||||
|
pio_cache_key: tidyesp32
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 3/4
|
||||||
|
options: --environment esp32-tidy --split-num 4 --split-at 3
|
||||||
|
pio_cache_key: tidyesp32
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 4/4
|
||||||
|
options: --environment esp32-tidy --split-num 4 --split-at 4
|
||||||
|
pio_cache_key: tidyesp32
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 esp-idf
|
||||||
|
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
||||||
|
pio_cache_key: tidyesp32-idf
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
|
id: python
|
||||||
with:
|
with:
|
||||||
python-version: '3.7'
|
python-version: '3.7'
|
||||||
|
|
||||||
- name: Cache pip modules
|
- name: Cache pip modules
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
key: pip-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
esphome-pip-3.7-
|
pip-${{ steps.python.outputs.python-version }}-
|
||||||
|
|
||||||
# Use per test platformio cache because tests have different platform versions
|
|
||||||
- name: Cache ~/.platformio
|
|
||||||
uses: actions/cache@v1
|
|
||||||
with:
|
|
||||||
path: ~/.platformio
|
|
||||||
key: test-home-platformio-${{ matrix.file }}-${{ hashFiles('esphome/core/config.py') }}
|
|
||||||
restore-keys: |
|
|
||||||
test-home-platformio-${{ matrix.file }}-
|
|
||||||
if: ${{ matrix.id == 'test' }}
|
|
||||||
|
|
||||||
- name: Set up python environment
|
- name: Set up python environment
|
||||||
run: script/setup
|
run: |
|
||||||
|
pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||||
|
pip3 install -e .
|
||||||
|
|
||||||
|
# Use per check platformio cache because checks use different parts
|
||||||
|
- name: Cache platformio
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.platformio
|
||||||
|
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||||
|
if: matrix.id == 'test' || matrix.id == 'clang-tidy'
|
||||||
|
|
||||||
|
- 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
|
- name: Register problem matchers
|
||||||
run: |
|
run: |
|
||||||
|
@ -124,20 +112,45 @@ jobs:
|
||||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||||
|
|
||||||
- name: Lint Custom
|
- name: Lint Custom
|
||||||
run: |
|
run: |
|
||||||
script/ci-custom.py
|
script/ci-custom.py
|
||||||
script/build_codeowners.py --check
|
script/build_codeowners.py --check
|
||||||
if: ${{ matrix.id == 'ci-custom' }}
|
if: matrix.id == 'ci-custom'
|
||||||
|
|
||||||
- name: Lint Python
|
- name: Lint Python
|
||||||
run: script/lint-python
|
run: script/lint-python
|
||||||
if: ${{ matrix.id == 'lint-python' }}
|
if: matrix.id == 'lint-python'
|
||||||
|
|
||||||
- run: esphome compile ${{ matrix.file }}
|
- run: esphome compile ${{ matrix.file }}
|
||||||
if: ${{ matrix.id == 'test' }}
|
if: matrix.id == 'test'
|
||||||
|
env:
|
||||||
|
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||||
|
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
||||||
|
|
||||||
- name: Run pytest
|
- name: Run pytest
|
||||||
run: |
|
run: |
|
||||||
pytest -vv --tb=native tests
|
pytest -vv --tb=native tests
|
||||||
if: ${{ matrix.id == 'pytest' }}
|
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: Suggested changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format')
|
||||||
|
|
100
.github/workflows/docker-lint-build.yml
vendored
100
.github/workflows/docker-lint-build.yml
vendored
|
@ -1,100 +0,0 @@
|
||||||
name: Build and publish lint docker image
|
|
||||||
|
|
||||||
# Only run when docker paths change
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev]
|
|
||||||
paths:
|
|
||||||
- 'docker/Dockerfile.lint'
|
|
||||||
- 'requirements.txt'
|
|
||||||
- 'requirements_optional.txt'
|
|
||||||
- 'requirements_test.txt'
|
|
||||||
- 'platformio.ini'
|
|
||||||
- '.github/workflows/docker-lint-build.yml'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy-docker:
|
|
||||||
name: Build and publish docker containers
|
|
||||||
if: github.repository == 'esphome/esphome'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
arch: [amd64, armv7, aarch64]
|
|
||||||
build_type: ["lint"]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: '3.9'
|
|
||||||
- name: Set TAG
|
|
||||||
run: |
|
|
||||||
echo "TAG=1.1" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Run build
|
|
||||||
run: |
|
|
||||||
docker/build.py \
|
|
||||||
--tag "${TAG}" \
|
|
||||||
--arch "${{ matrix.arch }}" \
|
|
||||||
--build-type "${{ matrix.build_type }}" \
|
|
||||||
build
|
|
||||||
|
|
||||||
- name: Log in to docker hub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
- name: Log in to the GitHub container registry
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Run push
|
|
||||||
run: |
|
|
||||||
docker/build.py \
|
|
||||||
--tag "${TAG}" \
|
|
||||||
--arch "${{ matrix.arch }}" \
|
|
||||||
--build-type "${{ matrix.build_type }}" \
|
|
||||||
push
|
|
||||||
|
|
||||||
deploy-docker-manifest:
|
|
||||||
if: github.repository == 'esphome/esphome'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [deploy-docker]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
build_type: ["lint"]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: '3.9'
|
|
||||||
- name: Set TAG
|
|
||||||
run: |
|
|
||||||
echo "TAG=1.1" >> $GITHUB_ENV
|
|
||||||
- name: Enable experimental manifest support
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.docker
|
|
||||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
|
||||||
|
|
||||||
- name: Log in to docker hub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
- name: Log in to the GitHub container registry
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Run manifest
|
|
||||||
run: |
|
|
||||||
docker/build.py \
|
|
||||||
--tag "${TAG}" \
|
|
||||||
--build-type "${{ matrix.build_type }}" \
|
|
||||||
manifest
|
|
21
.github/workflows/lock.yml
vendored
Normal file
21
.github/workflows/lock.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: Lock
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 0 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lock:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v2
|
||||||
|
with:
|
||||||
|
github-token: ${{ github.token }}
|
||||||
|
pr-lock-inactive-days: "1"
|
||||||
|
pr-lock-reason: ""
|
||||||
|
process-only: prs
|
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
|
@ -57,7 +57,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
arch: [amd64, armv7, aarch64]
|
arch: [amd64, armv7, aarch64]
|
||||||
build_type: ["ha-addon", "docker"]
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
|
@ -65,13 +65,10 @@ jobs:
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
|
|
||||||
- name: Run build
|
- name: Set up Docker Buildx
|
||||||
run: |
|
uses: docker/setup-buildx-action@v1
|
||||||
docker/build.py \
|
- name: Set up QEMU
|
||||||
--tag "${{ needs.init.outputs.tag }}" \
|
uses: docker/setup-qemu-action@v1
|
||||||
--arch "${{ matrix.arch }}" \
|
|
||||||
--build-type "${{ matrix.build_type }}" \
|
|
||||||
build
|
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
|
@ -85,13 +82,14 @@ jobs:
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Run push
|
- name: Build and push
|
||||||
run: |
|
run: |
|
||||||
docker/build.py \
|
docker/build.py \
|
||||||
--tag "${{ needs.init.outputs.tag }}" \
|
--tag "${{ needs.init.outputs.tag }}" \
|
||||||
--arch "${{ matrix.arch }}" \
|
--arch "${{ matrix.arch }}" \
|
||||||
--build-type "${{ matrix.build_type }}" \
|
--build-type "${{ matrix.build_type }}" \
|
||||||
push
|
build \
|
||||||
|
--push
|
||||||
|
|
||||||
deploy-docker-manifest:
|
deploy-docker-manifest:
|
||||||
if: github.repository == 'esphome/esphome'
|
if: github.repository == 'esphome/esphome'
|
||||||
|
@ -99,7 +97,7 @@ jobs:
|
||||||
needs: [init, deploy-docker]
|
needs: [init, deploy-docker]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
build_type: ["ha-addon", "docker"]
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
|
|
30
.github/workflows/stale.yml
vendored
Normal file
30
.github/workflows/stale.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
name: Stale
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 0 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v4
|
||||||
|
with:
|
||||||
|
repo-token: ${{ github.token }}
|
||||||
|
days-before-pr-stale: 90
|
||||||
|
days-before-pr-close: 7
|
||||||
|
days-before-issue-stale: -1
|
||||||
|
days-before-issue-close: -1
|
||||||
|
remove-stale-when-updated: true
|
||||||
|
stale-pr-label: "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
|
||||||
|
and will be closed if no further activity occurs within 7 days.
|
||||||
|
Thank you for your contributions.
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -102,10 +102,7 @@ CMakeLists.txt
|
||||||
.idea/**/dynamic.xml
|
.idea/**/dynamic.xml
|
||||||
|
|
||||||
# CMake
|
# CMake
|
||||||
cmake-build-debug/
|
cmake-build-*/
|
||||||
cmake-build-livingroom8266/
|
|
||||||
cmake-build-livingroom32/
|
|
||||||
cmake-build-release/
|
|
||||||
|
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
|
@ -127,3 +124,6 @@ tests/.esphome/
|
||||||
/.temp-clang-tidy.cpp
|
/.temp-clang-tidy.cpp
|
||||||
/.temp/
|
/.temp/
|
||||||
.pio/
|
.pio/
|
||||||
|
|
||||||
|
sdkconfig.*
|
||||||
|
!sdkconfig.defaults
|
||||||
|
|
20
CODEOWNERS
20
CODEOWNERS
|
@ -15,6 +15,7 @@ esphome/components/ac_dimmer/* @glmnet
|
||||||
esphome/components/adc/* @esphome/core
|
esphome/components/adc/* @esphome/core
|
||||||
esphome/components/addressable_light/* @justfalter
|
esphome/components/addressable_light/* @justfalter
|
||||||
esphome/components/airthings_ble/* @jeromelaban
|
esphome/components/airthings_ble/* @jeromelaban
|
||||||
|
esphome/components/airthings_wave_mini/* @ncareau
|
||||||
esphome/components/airthings_wave_plus/* @jeromelaban
|
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||||
esphome/components/am43/* @buxtronix
|
esphome/components/am43/* @buxtronix
|
||||||
esphome/components/am43/cover/* @buxtronix
|
esphome/components/am43/cover/* @buxtronix
|
||||||
|
@ -39,14 +40,19 @@ esphome/components/coolix/* @glmnet
|
||||||
esphome/components/cover/* @esphome/core
|
esphome/components/cover/* @esphome/core
|
||||||
esphome/components/cs5460a/* @balrog-kun
|
esphome/components/cs5460a/* @balrog-kun
|
||||||
esphome/components/ct_clamp/* @jesserockz
|
esphome/components/ct_clamp/* @jesserockz
|
||||||
|
esphome/components/current_based/* @djwmarcx
|
||||||
|
esphome/components/daly_bms/* @s1lvi0
|
||||||
|
esphome/components/dashboard_import/* @esphome/core
|
||||||
esphome/components/debug/* @OttoWinter
|
esphome/components/debug/* @OttoWinter
|
||||||
esphome/components/dfplayer/* @glmnet
|
esphome/components/dfplayer/* @glmnet
|
||||||
esphome/components/dht/* @OttoWinter
|
esphome/components/dht/* @OttoWinter
|
||||||
esphome/components/ds1307/* @badbadc0ffee
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
esphome/components/dsmr/* @glmnet @zuidwijk
|
esphome/components/dsmr/* @glmnet @zuidwijk
|
||||||
|
esphome/components/esp32/* @esphome/core
|
||||||
esphome/components/esp32_ble/* @jesserockz
|
esphome/components/esp32_ble/* @jesserockz
|
||||||
esphome/components/esp32_ble_server/* @jesserockz
|
esphome/components/esp32_ble_server/* @jesserockz
|
||||||
esphome/components/esp32_improv/* @jesserockz
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
|
esphome/components/esp8266/* @esphome/core
|
||||||
esphome/components/exposure_notifications/* @OttoWinter
|
esphome/components/exposure_notifications/* @OttoWinter
|
||||||
esphome/components/ezo/* @ssieb
|
esphome/components/ezo/* @ssieb
|
||||||
esphome/components/fastled_base/* @OttoWinter
|
esphome/components/fastled_base/* @OttoWinter
|
||||||
|
@ -54,9 +60,11 @@ esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||||
esphome/components/globals/* @esphome/core
|
esphome/components/globals/* @esphome/core
|
||||||
esphome/components/gpio/* @esphome/core
|
esphome/components/gpio/* @esphome/core
|
||||||
esphome/components/gps/* @coogle
|
esphome/components/gps/* @coogle
|
||||||
|
esphome/components/graph/* @synco
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||||
esphome/components/hbridge/light/* @DotNetDann
|
esphome/components/hbridge/light/* @DotNetDann
|
||||||
|
esphome/components/heatpumpir/* @rob-deutsch
|
||||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||||
esphome/components/homeassistant/* @OttoWinter
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||||
|
@ -70,6 +78,7 @@ esphome/components/json/* @OttoWinter
|
||||||
esphome/components/ledc/* @OttoWinter
|
esphome/components/ledc/* @OttoWinter
|
||||||
esphome/components/light/* @esphome/core
|
esphome/components/light/* @esphome/core
|
||||||
esphome/components/logger/* @esphome/core
|
esphome/components/logger/* @esphome/core
|
||||||
|
esphome/components/ltr390/* @sjtrny
|
||||||
esphome/components/max7219digit/* @rspaargaren
|
esphome/components/max7219digit/* @rspaargaren
|
||||||
esphome/components/mcp23008/* @jesserockz
|
esphome/components/mcp23008/* @jesserockz
|
||||||
esphome/components/mcp23017/* @jesserockz
|
esphome/components/mcp23017/* @jesserockz
|
||||||
|
@ -80,8 +89,16 @@ esphome/components/mcp23x17_base/* @jesserockz
|
||||||
esphome/components/mcp23xxx_base/* @jesserockz
|
esphome/components/mcp23xxx_base/* @jesserockz
|
||||||
esphome/components/mcp2515/* @danielschramm @mvturnho
|
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||||
esphome/components/mcp9808/* @k7hpn
|
esphome/components/mcp9808/* @k7hpn
|
||||||
|
esphome/components/mdns/* @esphome/core
|
||||||
esphome/components/midea/* @dudanov
|
esphome/components/midea/* @dudanov
|
||||||
esphome/components/mitsubishi/* @RubyBailey
|
esphome/components/mitsubishi/* @RubyBailey
|
||||||
|
esphome/components/modbus_controller/* @martgras
|
||||||
|
esphome/components/modbus_controller/binary_sensor/* @martgras
|
||||||
|
esphome/components/modbus_controller/number/* @martgras
|
||||||
|
esphome/components/modbus_controller/output/* @martgras
|
||||||
|
esphome/components/modbus_controller/sensor/* @martgras
|
||||||
|
esphome/components/modbus_controller/switch/* @martgras
|
||||||
|
esphome/components/modbus_controller/text_sensor/* @martgras
|
||||||
esphome/components/network/* @esphome/core
|
esphome/components/network/* @esphome/core
|
||||||
esphome/components/nextion/* @senexcrenshaw
|
esphome/components/nextion/* @senexcrenshaw
|
||||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||||
|
@ -100,6 +117,7 @@ esphome/components/pn532/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||||
esphome/components/power_supply/* @esphome/core
|
esphome/components/power_supply/* @esphome/core
|
||||||
|
esphome/components/preferences/* @esphome/core
|
||||||
esphome/components/pulse_meter/* @stevebaxter
|
esphome/components/pulse_meter/* @stevebaxter
|
||||||
esphome/components/pvvx_mithermometer/* @pasiz
|
esphome/components/pvvx_mithermometer/* @pasiz
|
||||||
esphome/components/rc522/* @glmnet
|
esphome/components/rc522/* @glmnet
|
||||||
|
@ -109,6 +127,8 @@ esphome/components/restart/* @esphome/core
|
||||||
esphome/components/rf_bridge/* @jesserockz
|
esphome/components/rf_bridge/* @jesserockz
|
||||||
esphome/components/rgbct/* @jesserockz
|
esphome/components/rgbct/* @jesserockz
|
||||||
esphome/components/rtttl/* @glmnet
|
esphome/components/rtttl/* @glmnet
|
||||||
|
esphome/components/safe_mode/* @paulmonigatti
|
||||||
|
esphome/components/scd4x/* @sjtrny
|
||||||
esphome/components/script/* @esphome/core
|
esphome/components/script/* @esphome/core
|
||||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||||
esphome/components/sdp3x/* @Azimath
|
esphome/components/sdp3x/* @Azimath
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
# Contributing to ESPHome
|
# Contributing to ESPHome
|
||||||
|
|
||||||
This python project is responsible for reading in YAML configuration files,
|
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphome
|
||||||
converting them to C++ code. This code is then converted to a platformio project and compiled
|
|
||||||
with [esphome-core](https://github.com/esphome/esphome-core), the C++ framework behind the project.
|
|
||||||
|
|
||||||
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphomeyaml
|
|
||||||
|
|
||||||
Things to note when contributing:
|
Things to note when contributing:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,60 @@
|
||||||
ARG BUILD_FROM=esphome/esphome-base:latest
|
# Build these with the build.py script
|
||||||
FROM ${BUILD_FROM}
|
# Example:
|
||||||
|
# python3 docker/build.py --tag dev --arch amd64 --build-type docker build
|
||||||
|
|
||||||
|
# One of "docker", "hassio"
|
||||||
|
ARG BASEIMGTYPE=docker
|
||||||
|
|
||||||
|
FROM ghcr.io/hassio-addons/debian-base/amd64:5.1.0 AS base-hassio-amd64
|
||||||
|
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.1.0 AS base-hassio-arm64
|
||||||
|
FROM ghcr.io/hassio-addons/debian-base/armv7:5.1.0 AS base-hassio-armv7
|
||||||
|
FROM debian:bullseye-20210902-slim AS base-docker-amd64
|
||||||
|
FROM debian:bullseye-20210902-slim AS base-docker-arm64
|
||||||
|
FROM debian:bullseye-20210902-slim AS base-docker-armv7
|
||||||
|
|
||||||
|
# Use TARGETARCH/TARGETVARIANT defined by docker
|
||||||
|
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
|
||||||
|
FROM base-${BASEIMGTYPE}-${TARGETARCH}${TARGETVARIANT} AS base
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update \
|
||||||
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
python3=3.9.2-3 \
|
||||||
|
python3-pip=20.3.4-4 \
|
||||||
|
python3-setuptools=52.0.0-4 \
|
||||||
|
python3-pil=8.1.2+dfsg-0.3 \
|
||||||
|
python3-cryptography=3.3.2-1 \
|
||||||
|
iputils-ping=3:20210202-1 \
|
||||||
|
git=1:2.30.2-1 \
|
||||||
|
curl=7.74.0-1.3+b1 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV \
|
||||||
|
# Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/
|
||||||
|
LANG=C.UTF-8 LC_ALL=C.UTF-8 \
|
||||||
|
# Store globally installed pio libs in /piolibs
|
||||||
|
PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
# Ubuntu python3-pip is missing wheel
|
||||||
|
pip3 install --no-cache-dir \
|
||||||
|
wheel==0.36.2 \
|
||||||
|
platformio==5.2.0 \
|
||||||
|
# Change some platformio settings
|
||||||
|
&& platformio settings set enable_telemetry No \
|
||||||
|
&& platformio settings set check_libraries_interval 1000000 \
|
||||||
|
&& platformio settings set check_platformio_interval 1000000 \
|
||||||
|
&& platformio settings set check_platforms_interval 1000000 \
|
||||||
|
&& mkdir -p /piolibs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= docker-type image =======================
|
||||||
|
FROM base AS docker
|
||||||
|
|
||||||
# First install requirements to leverage caching when requirements don't change
|
# First install requirements to leverage caching when requirements don't change
|
||||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||||
|
@ -7,9 +62,9 @@ RUN \
|
||||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||||
&& /platformio_install_deps.py /platformio.ini
|
&& /platformio_install_deps.py /platformio.ini
|
||||||
|
|
||||||
# Then copy esphome and install
|
# Copy esphome and install
|
||||||
COPY . .
|
COPY . /esphome
|
||||||
RUN pip3 install --no-cache-dir -e .
|
RUN pip3 install --no-cache-dir -e /esphome
|
||||||
|
|
||||||
# Settings for dashboard
|
# Settings for dashboard
|
||||||
ENV USERNAME="" PASSWORD=""
|
ENV USERNAME="" PASSWORD=""
|
||||||
|
@ -17,14 +72,85 @@ ENV USERNAME="" PASSWORD=""
|
||||||
# Expose the dashboard to Docker
|
# Expose the dashboard to Docker
|
||||||
EXPOSE 6052
|
EXPOSE 6052
|
||||||
|
|
||||||
# Run healthcheck (heartbeat)
|
COPY docker/docker_entrypoint.sh /entrypoint.sh
|
||||||
HEALTHCHECK --interval=30s --timeout=30s \
|
|
||||||
CMD curl --fail http://localhost:6052 || exit 1
|
|
||||||
|
|
||||||
# The directory the user should mount their configuration files to
|
# The directory the user should mount their configuration files to
|
||||||
|
VOLUME /config
|
||||||
WORKDIR /config
|
WORKDIR /config
|
||||||
# Set entrypoint to esphome so that the user doesn't have to type 'esphome'
|
# Set entrypoint to esphome (via a script) so that the user doesn't have to type 'esphome'
|
||||||
# in every docker command twice
|
# in every docker command twice
|
||||||
ENTRYPOINT ["esphome"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
# When no arguments given, start the dashboard in the workdir
|
# When no arguments given, start the dashboard in the workdir
|
||||||
CMD ["dashboard", "/config"]
|
CMD ["dashboard", "/config"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= hassio-type image =======================
|
||||||
|
FROM base AS hassio
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update \
|
||||||
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
nginx=1.18.0-6.1 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ARG BUILD_VERSION=dev
|
||||||
|
|
||||||
|
# Copy root filesystem
|
||||||
|
COPY docker/hassio-rootfs/ /
|
||||||
|
|
||||||
|
# First install requirements to leverage caching when requirements don't change
|
||||||
|
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
|
||||||
|
|
||||||
|
# Copy esphome and install
|
||||||
|
COPY . /esphome
|
||||||
|
RUN pip3 install --no-cache-dir -e /esphome
|
||||||
|
|
||||||
|
# Labels
|
||||||
|
LABEL \
|
||||||
|
io.hass.name="ESPHome" \
|
||||||
|
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
|
||||||
|
io.hass.type="addon" \
|
||||||
|
io.hass.version="${BUILD_VERSION}"
|
||||||
|
# io.hass.arch is inherited from addon-debian-base
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= lint-type image =======================
|
||||||
|
FROM base AS lint
|
||||||
|
|
||||||
|
ENV \
|
||||||
|
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update \
|
||||||
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
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 \
|
||||||
|
nano=5.4-2 \
|
||||||
|
build-essential=12.9 \
|
||||||
|
python3-dev=3.9.2-3 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
VOLUME ["/esphome"]
|
||||||
|
WORKDIR /esphome
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
FROM esphome/esphome-lint:1.1
|
|
|
@ -1,25 +0,0 @@
|
||||||
ARG BUILD_FROM=esphome/esphome-hassio-base:latest
|
|
||||||
FROM ${BUILD_FROM}
|
|
||||||
|
|
||||||
# First install requirements to leverage caching when requirements don't change
|
|
||||||
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
|
|
||||||
|
|
||||||
# Copy root filesystem
|
|
||||||
COPY docker/rootfs/ /
|
|
||||||
|
|
||||||
# Then copy esphome and install
|
|
||||||
COPY . /opt/esphome/
|
|
||||||
RUN pip3 install --no-cache-dir -e /opt/esphome
|
|
||||||
|
|
||||||
# Build arguments
|
|
||||||
ARG BUILD_VERSION=dev
|
|
||||||
|
|
||||||
# Labels
|
|
||||||
LABEL \
|
|
||||||
io.hass.name="ESPHome" \
|
|
||||||
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
|
|
||||||
io.hass.type="addon" \
|
|
||||||
io.hass.version=${BUILD_VERSION}
|
|
|
@ -1,10 +0,0 @@
|
||||||
ARG BUILD_FROM=esphome/esphome-lint-base:latest
|
|
||||||
FROM ${BUILD_FROM}
|
|
||||||
|
|
||||||
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
|
||||||
RUN \
|
|
||||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
|
|
||||||
&& /platformio_install_deps.py /platformio.ini
|
|
||||||
|
|
||||||
VOLUME ["/esphome"]
|
|
||||||
WORKDIR /esphome
|
|
100
docker/build.py
100
docker/build.py
|
@ -2,7 +2,7 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import subprocess
|
import subprocess
|
||||||
import argparse
|
import argparse
|
||||||
import platform
|
from platform import machine
|
||||||
import shlex
|
import shlex
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -24,9 +24,6 @@ TYPE_LINT = 'lint'
|
||||||
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
||||||
|
|
||||||
|
|
||||||
BASE_VERSION = "4.2.0"
|
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag")
|
parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag")
|
||||||
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
|
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
|
||||||
|
@ -34,27 +31,17 @@ parser.add_argument("--build-type", choices=TYPES, required=True, help="The type
|
||||||
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
|
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
|
||||||
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
|
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
|
||||||
build_parser = subparsers.add_parser("build", help="Build the image")
|
build_parser = subparsers.add_parser("build", help="Build the image")
|
||||||
push_parser = subparsers.add_parser("push", help="Tag the already built image and push it to docker hub")
|
build_parser.add_argument("--push", help="Also push the images", action="store_true")
|
||||||
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
|
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# only lists some possibilities, doesn't have to be perfect
|
|
||||||
# https://stackoverflow.com/a/45125525
|
|
||||||
UNAME_TO_ARCH = {
|
|
||||||
"x86_64": ARCH_AMD64,
|
|
||||||
"aarch64": ARCH_AARCH64,
|
|
||||||
"aarch64_be": ARCH_AARCH64,
|
|
||||||
"arm": ARCH_ARMV7,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class DockerParams:
|
class DockerParams:
|
||||||
build_from: str
|
|
||||||
build_to: str
|
build_to: str
|
||||||
manifest_to: str
|
manifest_to: str
|
||||||
dockerfile: str
|
baseimgtype: str
|
||||||
|
platform: str
|
||||||
|
target: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def for_type_arch(cls, build_type, arch):
|
def for_type_arch(cls, build_type, arch):
|
||||||
|
@ -63,18 +50,28 @@ class DockerParams:
|
||||||
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
||||||
TYPE_LINT: "esphome/esphome-lint"
|
TYPE_LINT: "esphome/esphome-lint"
|
||||||
}[build_type]
|
}[build_type]
|
||||||
build_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}"
|
|
||||||
build_to = f"{prefix}-{arch}"
|
build_to = f"{prefix}-{arch}"
|
||||||
dockerfile = {
|
baseimgtype = {
|
||||||
TYPE_DOCKER: "docker/Dockerfile",
|
TYPE_DOCKER: "docker",
|
||||||
TYPE_HA_ADDON: "docker/Dockerfile.hassio",
|
TYPE_HA_ADDON: "hassio",
|
||||||
TYPE_LINT: "docker/Dockerfile.lint",
|
TYPE_LINT: "docker",
|
||||||
|
}[build_type]
|
||||||
|
platform = {
|
||||||
|
ARCH_AMD64: "linux/amd64",
|
||||||
|
ARCH_ARMV7: "linux/arm/v7",
|
||||||
|
ARCH_AARCH64: "linux/arm64",
|
||||||
|
}[arch]
|
||||||
|
target = {
|
||||||
|
TYPE_DOCKER: "docker",
|
||||||
|
TYPE_HA_ADDON: "hassio",
|
||||||
|
TYPE_LINT: "lint",
|
||||||
}[build_type]
|
}[build_type]
|
||||||
return cls(
|
return cls(
|
||||||
build_from=build_from,
|
|
||||||
build_to=build_to,
|
build_to=build_to,
|
||||||
manifest_to=prefix,
|
manifest_to=prefix,
|
||||||
dockerfile=dockerfile
|
baseimgtype=baseimgtype,
|
||||||
|
platform=platform,
|
||||||
|
target=target,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,46 +109,31 @@ def main():
|
||||||
# 1. pull cache image
|
# 1. pull cache image
|
||||||
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
||||||
cache_tag = {
|
cache_tag = {
|
||||||
CHANNEL_DEV: "dev",
|
CHANNEL_DEV: "cache-dev",
|
||||||
CHANNEL_BETA: "beta",
|
CHANNEL_BETA: "cache-beta",
|
||||||
CHANNEL_RELEASE: "latest",
|
CHANNEL_RELEASE: "cache-latest",
|
||||||
}[channel]
|
}[channel]
|
||||||
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}"
|
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}"
|
||||||
run_command("docker", "pull", cache_img, ignore_error=True)
|
|
||||||
|
|
||||||
# 2. register QEMU binfmt (if not host arch)
|
|
||||||
is_native = UNAME_TO_ARCH.get(platform.machine()) == args.arch
|
|
||||||
if not is_native:
|
|
||||||
run_command(
|
|
||||||
"docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static:5.2.0-2",
|
|
||||||
"--reset", "-p", "yes"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 3. build
|
|
||||||
run_command(
|
|
||||||
"docker", "build",
|
|
||||||
"--build-arg", f"BUILD_FROM={params.build_from}",
|
|
||||||
"--build-arg", f"BUILD_VERSION={args.tag}",
|
|
||||||
"--tag", f"{params.build_to}:{args.tag}",
|
|
||||||
"--cache-from", cache_img,
|
|
||||||
"--file", params.dockerfile,
|
|
||||||
"."
|
|
||||||
)
|
|
||||||
elif args.command == "push":
|
|
||||||
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
|
||||||
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
|
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
|
||||||
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
||||||
src = imgs[0]
|
|
||||||
# 1. tag images
|
# 3. build
|
||||||
for img in imgs[1:]:
|
cmd = [
|
||||||
run_command(
|
"docker", "buildx", "build",
|
||||||
"docker", "tag", src, img
|
"--build-arg", f"BASEIMGTYPE={params.baseimgtype}",
|
||||||
)
|
"--build-arg", f"BUILD_VERSION={args.tag}",
|
||||||
# 2. push images
|
"--cache-from", f"type=registry,ref={cache_img}",
|
||||||
|
"--file", "docker/Dockerfile",
|
||||||
|
"--platform", params.platform,
|
||||||
|
"--target", params.target,
|
||||||
|
]
|
||||||
for img in imgs:
|
for img in imgs:
|
||||||
run_command(
|
cmd += ["--tag", img]
|
||||||
"docker", "push", img
|
if args.push:
|
||||||
)
|
cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"]
|
||||||
|
|
||||||
|
run_command(*cmd, ".")
|
||||||
elif args.command == "manifest":
|
elif args.command == "manifest":
|
||||||
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
|
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
|
||||||
|
|
||||||
|
|
24
docker/docker_entrypoint.sh
Executable file
24
docker/docker_entrypoint.sh
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# If /cache is mounted, use that as PIO's coredir
|
||||||
|
# otherwise use path in /config (so that PIO packages aren't downloaded on each compile)
|
||||||
|
|
||||||
|
if [[ -d /cache ]]; then
|
||||||
|
pio_cache_base=/cache/platformio
|
||||||
|
else
|
||||||
|
pio_cache_base=/config/.esphome/platformio
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "${pio_cache_base}" ]]; then
|
||||||
|
echo "Creating cache directory ${pio_cache_base}"
|
||||||
|
echo "You can change this behavior by mounting a directory to the container's /cache directory."
|
||||||
|
mkdir -p "${pio_cache_base}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
|
||||||
|
# setting `core_dir` would therefore prevent pio from accessing
|
||||||
|
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
||||||
|
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
||||||
|
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
||||||
|
|
||||||
|
exec esphome "$@"
|
9
docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh
Normal file
9
docker/hassio-rootfs/etc/cont-init.d/30-dirs.sh
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# ==============================================================================
|
||||||
|
# Community Hass.io Add-ons: ESPHome
|
||||||
|
# This files creates all directories used by esphome
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
pio_cache_base=/data/cache/platformio
|
||||||
|
|
||||||
|
mkdir -p "${pio_cache_base}"
|
|
@ -22,5 +22,14 @@ if bashio::config.has_value 'relative_url'; then
|
||||||
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
|
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
pio_cache_base=/data/cache/platformio
|
||||||
|
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
|
||||||
|
# setting `core_dir` would therefore prevent pio from accessing
|
||||||
|
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
||||||
|
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
||||||
|
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
||||||
|
|
||||||
|
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
||||||
|
|
||||||
bashio::log.info "Starting ESPHome dashboard..."
|
bashio::log.info "Starting ESPHome dashboard..."
|
||||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
|
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
|
|
@ -3,18 +3,11 @@
|
||||||
# all platformio libraries in the global storage
|
# all platformio libraries in the global storage
|
||||||
|
|
||||||
import configparser
|
import configparser
|
||||||
import re
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser(inline_comment_prefixes=(';', ))
|
||||||
config.read(sys.argv[1])
|
config.read(sys.argv[1])
|
||||||
libs = []
|
libs = [x for x in config['common']['lib_deps'].splitlines() if len(x) != 0]
|
||||||
for line in config['common']['lib_deps'].splitlines():
|
|
||||||
# Format: '1655@1.0.2 ; TinyGPSPlus (has name conflict)' (includes comment)
|
|
||||||
m = re.search(r'([a-zA-Z0-9-_/]+@[0-9\.]+)', line)
|
|
||||||
if m is None:
|
|
||||||
continue
|
|
||||||
libs.append(m.group(1))
|
|
||||||
|
|
||||||
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
|
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
|
||||||
|
|
|
@ -72,7 +72,7 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
|
||||||
if default == "OTA":
|
if default == "OTA":
|
||||||
return CORE.address
|
return CORE.address
|
||||||
if show_mqtt and "mqtt" in CORE.config:
|
if show_mqtt and "mqtt" in CORE.config:
|
||||||
options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT"))
|
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
|
||||||
if default == "OTA":
|
if default == "OTA":
|
||||||
return "MQTT"
|
return "MQTT"
|
||||||
if default is not None:
|
if default is not None:
|
||||||
|
@ -184,12 +184,30 @@ def compile_program(args, config):
|
||||||
|
|
||||||
|
|
||||||
def upload_using_esptool(config, port):
|
def upload_using_esptool(config, port):
|
||||||
path = CORE.firmware_bin
|
from esphome import platformio_api
|
||||||
|
|
||||||
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
||||||
"upload_speed", 460800
|
"upload_speed", 460800
|
||||||
)
|
)
|
||||||
|
|
||||||
def run_esptool(baud_rate):
|
def run_esptool(baud_rate):
|
||||||
|
idedata = platformio_api.get_idedata(config)
|
||||||
|
|
||||||
|
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
|
||||||
|
flash_images = [
|
||||||
|
platformio_api.FlashImage(
|
||||||
|
path=idedata.firmware_bin_path,
|
||||||
|
offset=firmware_offset,
|
||||||
|
),
|
||||||
|
*idedata.extra_flash_images,
|
||||||
|
]
|
||||||
|
|
||||||
|
mcu = "esp8266"
|
||||||
|
if CORE.is_esp32:
|
||||||
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
|
||||||
|
mcu = get_esp32_variant().lower()
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
"esptool.py",
|
"esptool.py",
|
||||||
"--before",
|
"--before",
|
||||||
|
@ -198,14 +216,15 @@ def upload_using_esptool(config, port):
|
||||||
"hard_reset",
|
"hard_reset",
|
||||||
"--baud",
|
"--baud",
|
||||||
str(baud_rate),
|
str(baud_rate),
|
||||||
"--chip",
|
|
||||||
"esp8266",
|
|
||||||
"--port",
|
"--port",
|
||||||
port,
|
port,
|
||||||
|
"--chip",
|
||||||
|
mcu,
|
||||||
"write_flash",
|
"write_flash",
|
||||||
"0x0",
|
"-z",
|
||||||
path,
|
|
||||||
]
|
]
|
||||||
|
for img in flash_images:
|
||||||
|
cmd += [img.offset, img.path]
|
||||||
|
|
||||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||||
import esptool
|
import esptool
|
||||||
|
@ -229,11 +248,7 @@ def upload_using_esptool(config, port):
|
||||||
def upload_program(config, args, host):
|
def upload_program(config, args, host):
|
||||||
# if upload is to a serial port use platformio, otherwise assume ota
|
# if upload is to a serial port use platformio, otherwise assume ota
|
||||||
if get_port_type(host) == "SERIAL":
|
if get_port_type(host) == "SERIAL":
|
||||||
from esphome import platformio_api
|
return upload_using_esptool(config, host)
|
||||||
|
|
||||||
if CORE.is_esp8266:
|
|
||||||
return upload_using_esptool(config, host)
|
|
||||||
return platformio_api.run_upload(config, CORE.verbose, host)
|
|
||||||
|
|
||||||
from esphome import espota2
|
from esphome import espota2
|
||||||
|
|
||||||
|
@ -245,7 +260,7 @@ def upload_program(config, args, host):
|
||||||
|
|
||||||
ota_conf = config[CONF_OTA]
|
ota_conf = config[CONF_OTA]
|
||||||
remote_port = ota_conf[CONF_PORT]
|
remote_port = ota_conf[CONF_PORT]
|
||||||
password = ota_conf[CONF_PASSWORD]
|
password = ota_conf.get(CONF_PASSWORD, "")
|
||||||
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
|
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
|
||||||
|
|
||||||
|
|
||||||
|
@ -415,30 +430,30 @@ def command_update_all(args):
|
||||||
click.echo(f"{half_line}{middle_text}{half_line}")
|
click.echo(f"{half_line}{middle_text}{half_line}")
|
||||||
|
|
||||||
for f in files:
|
for f in files:
|
||||||
print("Updating {}".format(color(Fore.CYAN, f)))
|
print(f"Updating {color(Fore.CYAN, f)}")
|
||||||
print("-" * twidth)
|
print("-" * twidth)
|
||||||
print()
|
print()
|
||||||
rc = run_external_process(
|
rc = run_external_process(
|
||||||
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
||||||
)
|
)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
print_bar(f"[{color(Fore.BOLD_GREEN, 'SUCCESS')}] {f}")
|
||||||
success[f] = True
|
success[f] = True
|
||||||
else:
|
else:
|
||||||
print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
|
print_bar(f"[{color(Fore.BOLD_RED, 'ERROR')}] {f}")
|
||||||
success[f] = False
|
success[f] = False
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print()
|
print()
|
||||||
print()
|
print()
|
||||||
|
|
||||||
print_bar("[{}]".format(color(Fore.BOLD_WHITE, "SUMMARY")))
|
print_bar(f"[{color(Fore.BOLD_WHITE, 'SUMMARY')}]")
|
||||||
failed = 0
|
failed = 0
|
||||||
for f in files:
|
for f in files:
|
||||||
if success[f]:
|
if success[f]:
|
||||||
print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
|
print(f" - {f}: {color(Fore.GREEN, 'SUCCESS')}")
|
||||||
else:
|
else:
|
||||||
print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
|
print(f" - {f}: {color(Fore.BOLD_RED, 'FAILED')}")
|
||||||
failed += 1
|
failed += 1
|
||||||
return failed
|
return failed
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ from esphome.const import (
|
||||||
CONF_ELSE,
|
CONF_ELSE,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_THEN,
|
CONF_THEN,
|
||||||
|
CONF_TIMEOUT,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_TYPE_ID,
|
CONF_TYPE_ID,
|
||||||
CONF_TIME,
|
CONF_TIME,
|
||||||
|
@ -244,6 +245,9 @@ def validate_wait_until(value):
|
||||||
schema = cv.Schema(
|
schema = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
|
cv.Optional(CONF_TIMEOUT): cv.templatable(
|
||||||
|
cv.positive_time_period_milliseconds
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if isinstance(value, dict) and CONF_CONDITION in value:
|
if isinstance(value, dict) and CONF_CONDITION in value:
|
||||||
|
@ -255,6 +259,9 @@ def validate_wait_until(value):
|
||||||
async def wait_until_action_to_code(config, action_id, template_arg, args):
|
async def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||||
|
if CONF_TIMEOUT in config:
|
||||||
|
template_ = await cg.templatable(config[CONF_TIMEOUT], args, cg.uint32)
|
||||||
|
cg.add(var.set_timeout_value(template_))
|
||||||
await cg.register_component(var, {})
|
await cg.register_component(var, {})
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ from esphome.cpp_generator import ( # noqa
|
||||||
add_library,
|
add_library,
|
||||||
add_build_flag,
|
add_build_flag,
|
||||||
add_define,
|
add_define,
|
||||||
|
add_platformio_option,
|
||||||
get_variable,
|
get_variable,
|
||||||
get_variable_with_full_id,
|
get_variable_with_full_id,
|
||||||
process_lambda,
|
process_lambda,
|
||||||
|
@ -66,7 +67,7 @@ from esphome.cpp_types import ( # noqa
|
||||||
NAN,
|
NAN,
|
||||||
esphome_ns,
|
esphome_ns,
|
||||||
App,
|
App,
|
||||||
Nameable,
|
EntityBase,
|
||||||
Component,
|
Component,
|
||||||
ComponentPtr,
|
ComponentPtr,
|
||||||
PollingComponent,
|
PollingComponent,
|
||||||
|
@ -78,4 +79,6 @@ from esphome.cpp_types import ( # noqa
|
||||||
JsonObjectConstRef,
|
JsonObjectConstRef,
|
||||||
Controller,
|
Controller,
|
||||||
GPIOPin,
|
GPIOPin,
|
||||||
|
InternalGPIOPin,
|
||||||
|
gpio_Flags,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/esphal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/stepper/stepper.h"
|
#include "esphome/components/stepper/stepper.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
|
#ifdef USE_ARDUINO
|
||||||
|
|
||||||
#include "ac_dimmer.h"
|
#include "ac_dimmer.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#include <core_esp8266_waveform.h>
|
#include <core_esp8266_waveform.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
||||||
|
#include <esp32-hal-timer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ac_dimmer {
|
namespace ac_dimmer {
|
||||||
|
@ -17,12 +23,15 @@ static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-no
|
||||||
/// Time in microseconds the gate should be held high
|
/// Time in microseconds the gate should be held high
|
||||||
/// 10µs should be long enough for most triacs
|
/// 10µs should be long enough for most triacs
|
||||||
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
||||||
static const uint32_t GATE_ENABLE_TIME = 10;
|
/// However other factors like gate driver propagation time
|
||||||
|
/// are also considered and a really low value is not important
|
||||||
|
/// See also: https://github.com/esphome/issues/issues/1632
|
||||||
|
static const uint32_t GATE_ENABLE_TIME = 50;
|
||||||
|
|
||||||
/// Function called from timer interrupt
|
/// Function called from timer interrupt
|
||||||
/// Input is current time in microseconds (micros())
|
/// Input is current time in microseconds (micros())
|
||||||
/// Returns when next "event" is expected in µs, or 0 if no such event known.
|
/// Returns when next "event" is expected in µs, or 0 if no such event known.
|
||||||
uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||||
// If no ZC signal received yet.
|
// If no ZC signal received yet.
|
||||||
if (this->crossed_zero_at == 0)
|
if (this->crossed_zero_at == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -34,13 +43,13 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||||
|
|
||||||
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
|
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
|
||||||
this->enable_time_us = 0;
|
this->enable_time_us = 0;
|
||||||
this->gate_pin->digital_write(true);
|
this->gate_pin.digital_write(true);
|
||||||
// Prevent too short pulses
|
// Prevent too short pulses
|
||||||
this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
|
this->disable_time_us = std::max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
|
||||||
}
|
}
|
||||||
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
|
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
|
||||||
this->disable_time_us = 0;
|
this->disable_time_us = 0;
|
||||||
this->gate_pin->digital_write(false);
|
this->gate_pin.digital_write(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time_since_zc < this->enable_time_us)
|
if (time_since_zc < this->enable_time_us)
|
||||||
|
@ -60,7 +69,7 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run timer interrupt code and return in how many µs the next event is expected
|
/// Run timer interrupt code and return in how many µs the next event is expected
|
||||||
uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
|
uint32_t IRAM_ATTR HOT timer_interrupt() {
|
||||||
// run at least with 1kHz
|
// run at least with 1kHz
|
||||||
uint32_t min_dt_us = 1000;
|
uint32_t min_dt_us = 1000;
|
||||||
uint32_t now = micros();
|
uint32_t now = micros();
|
||||||
|
@ -77,7 +86,7 @@ uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GPIO interrupt routine, called when ZC pin triggers
|
/// GPIO interrupt routine, called when ZC pin triggers
|
||||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||||
uint32_t prev_crossed = this->crossed_zero_at;
|
uint32_t prev_crossed = this->crossed_zero_at;
|
||||||
|
|
||||||
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
|
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
|
||||||
|
@ -94,7 +103,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||||
|
|
||||||
if (this->value == 65535) {
|
if (this->value == 65535) {
|
||||||
// fully on, enable output immediately
|
// fully on, enable output immediately
|
||||||
this->gate_pin->digital_write(true);
|
this->gate_pin.digital_write(true);
|
||||||
} else if (this->init_cycle) {
|
} else if (this->init_cycle) {
|
||||||
// send a full cycle
|
// send a full cycle
|
||||||
this->init_cycle = false;
|
this->init_cycle = false;
|
||||||
|
@ -102,29 +111,29 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||||
this->disable_time_us = cycle_time_us;
|
this->disable_time_us = cycle_time_us;
|
||||||
} else if (this->value == 0) {
|
} else if (this->value == 0) {
|
||||||
// fully off, disable output immediately
|
// fully off, disable output immediately
|
||||||
this->gate_pin->digital_write(false);
|
this->gate_pin.digital_write(false);
|
||||||
} else {
|
} else {
|
||||||
if (this->method == DIM_METHOD_TRAILING) {
|
if (this->method == DIM_METHOD_TRAILING) {
|
||||||
this->enable_time_us = 1; // cannot be 0
|
this->enable_time_us = 1; // cannot be 0
|
||||||
this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
||||||
} else {
|
} else {
|
||||||
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
||||||
// also take into account min_power
|
// also take into account min_power
|
||||||
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
||||||
this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
||||||
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
||||||
// Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
|
// Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
|
||||||
// this is for brightness near 99%
|
// this is for brightness near 99%
|
||||||
this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
|
this->disable_time_us = std::max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
|
||||||
} else {
|
} else {
|
||||||
this->gate_pin->digital_write(false);
|
this->gate_pin.digital_write(false);
|
||||||
this->disable_time_us = this->cycle_time_us;
|
this->disable_time_us = this->cycle_time_us;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
void IRAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||||
// Attaching pin interrupts on the same pin will override the previous interrupt
|
// Attaching pin interrupts on the same pin will override the previous interrupt
|
||||||
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
||||||
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
||||||
|
@ -138,11 +147,11 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
// ESP32 implementation, uses basically the same code but needs to wrap
|
// ESP32 implementation, uses basically the same code but needs to wrap
|
||||||
// timer_interrupt() function to auto-reschedule
|
// timer_interrupt() function to auto-reschedule
|
||||||
static hw_timer_t *dimmer_timer = nullptr;
|
static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
|
void IRAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void AcDimmer::setup() {
|
void AcDimmer::setup() {
|
||||||
|
@ -171,15 +180,16 @@ void AcDimmer::setup() {
|
||||||
if (setup_zero_cross_pin) {
|
if (setup_zero_cross_pin) {
|
||||||
this->zero_cross_pin_->setup();
|
this->zero_cross_pin_->setup();
|
||||||
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
|
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
|
||||||
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING);
|
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_,
|
||||||
|
gpio::INTERRUPT_FALLING_EDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
// Uses ESP8266 waveform (soft PWM) class
|
// Uses ESP8266 waveform (soft PWM) class
|
||||||
// PWM and AcDimmer can even run at the same time this way
|
// PWM and AcDimmer can even run at the same time this way
|
||||||
setTimer1Callback(&timer_interrupt);
|
setTimer1Callback(&timer_interrupt);
|
||||||
#endif
|
#endif
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
// 80 Divider -> 1 count=1µs
|
// 80 Divider -> 1 count=1µs
|
||||||
dimmer_timer = timerBegin(0, 80, true);
|
dimmer_timer = timerBegin(0, 80, true);
|
||||||
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
||||||
|
@ -215,3 +225,5 @@ void AcDimmer::dump_config() {
|
||||||
|
|
||||||
} // namespace ac_dimmer
|
} // namespace ac_dimmer
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ARDUINO
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef USE_ARDUINO
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/esphal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/output/float_output.h"
|
#include "esphome/components/output/float_output.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
@ -11,11 +13,11 @@ enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TR
|
||||||
|
|
||||||
struct AcDimmerDataStore {
|
struct AcDimmerDataStore {
|
||||||
/// Zero-cross pin
|
/// Zero-cross pin
|
||||||
ISRInternalGPIOPin *zero_cross_pin;
|
ISRInternalGPIOPin zero_cross_pin;
|
||||||
/// Zero-cross pin number - used to share ZC pin across multiple dimmers
|
/// Zero-cross pin number - used to share ZC pin across multiple dimmers
|
||||||
uint8_t zero_cross_pin_number;
|
uint8_t zero_cross_pin_number;
|
||||||
/// Output pin to write to
|
/// Output pin to write to
|
||||||
ISRInternalGPIOPin *gate_pin;
|
ISRInternalGPIOPin gate_pin;
|
||||||
/// Value of the dimmer - 0 to 65535.
|
/// Value of the dimmer - 0 to 65535.
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
/// Minimum power for activation
|
/// Minimum power for activation
|
||||||
|
@ -37,7 +39,7 @@ struct AcDimmerDataStore {
|
||||||
|
|
||||||
void gpio_intr();
|
void gpio_intr();
|
||||||
static void s_gpio_intr(AcDimmerDataStore *store);
|
static void s_gpio_intr(AcDimmerDataStore *store);
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
static void s_timer_intr();
|
static void s_timer_intr();
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
@ -47,16 +49,16 @@ class AcDimmer : public output::FloatOutput, public Component {
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; }
|
void set_gate_pin(InternalGPIOPin *gate_pin) { gate_pin_ = gate_pin; }
|
||||||
void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
|
void set_zero_cross_pin(InternalGPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
|
||||||
void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; }
|
void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; }
|
||||||
void set_method(DimMethod method) { method_ = method; }
|
void set_method(DimMethod method) { method_ = method; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void write_state(float state) override;
|
void write_state(float state) override;
|
||||||
|
|
||||||
GPIOPin *gate_pin_;
|
InternalGPIOPin *gate_pin_;
|
||||||
GPIOPin *zero_cross_pin_;
|
InternalGPIOPin *zero_cross_pin_;
|
||||||
AcDimmerDataStore store_;
|
AcDimmerDataStore store_;
|
||||||
bool init_with_half_cycle_;
|
bool init_with_half_cycle_;
|
||||||
DimMethod method_;
|
DimMethod method_;
|
||||||
|
@ -64,3 +66,5 @@ class AcDimmer : public output::FloatOutput, public Component {
|
||||||
|
|
||||||
} // namespace ac_dimmer
|
} // namespace ac_dimmer
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ARDUINO
|
||||||
|
|
|
@ -19,17 +19,20 @@ DIM_METHODS = {
|
||||||
CONF_GATE_PIN = "gate_pin"
|
CONF_GATE_PIN = "gate_pin"
|
||||||
CONF_ZERO_CROSS_PIN = "zero_cross_pin"
|
CONF_ZERO_CROSS_PIN = "zero_cross_pin"
|
||||||
CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
|
CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
|
||||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
CONFIG_SCHEMA = cv.All(
|
||||||
{
|
output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||||
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
|
{
|
||||||
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
|
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
|
||||||
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
|
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
|
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
|
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
|
||||||
DIM_METHODS, upper=True, space="_"
|
cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
|
||||||
),
|
DIM_METHODS, upper=True, space="_"
|
||||||
}
|
),
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
cv.only_with_arduino,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
#include "adc_sensor.h"
|
#include "adc_sensor.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
#include <Esp.h>
|
||||||
ADC_MODE(ADC_VCC)
|
ADC_MODE(ADC_VCC)
|
||||||
|
#else
|
||||||
|
#include <Arduino.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
@ -10,7 +15,7 @@ namespace adc {
|
||||||
|
|
||||||
static const char *const TAG = "adc";
|
static const char *const TAG = "adc";
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
||||||
|
|
||||||
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
||||||
|
@ -57,28 +62,28 @@ inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
||||||
void ADCSensor::setup() {
|
void ADCSensor::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
||||||
#ifndef USE_ADC_SENSOR_VCC
|
#ifndef USE_ADC_SENSOR_VCC
|
||||||
GPIOPin(this->pin_, INPUT).setup();
|
pin_->setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
|
adc1_config_channel_atten(gpio_to_adc1(pin_->get_pin()), attenuation_);
|
||||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||||
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
|
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
|
||||||
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_->get_pin()));
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void ADCSensor::dump_config() {
|
void ADCSensor::dump_config() {
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||||
#else
|
#else
|
||||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
LOG_PIN(" Pin: ", pin_);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
LOG_PIN(" Pin: ", pin_);
|
||||||
switch (this->attenuation_) {
|
switch (this->attenuation_) {
|
||||||
case ADC_ATTEN_DB_0:
|
case ADC_ATTEN_DB_0:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
|
||||||
|
@ -105,8 +110,8 @@ void ADCSensor::update() {
|
||||||
this->publish_state(value_v);
|
this->publish_state(value_v);
|
||||||
}
|
}
|
||||||
float ADCSensor::sample() {
|
float ADCSensor::sample() {
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
int raw = adc1_get_raw(gpio_to_adc1(pin_));
|
int raw = adc1_get_raw(gpio_to_adc1(pin_->get_pin()));
|
||||||
float value_v = raw / 4095.0f;
|
float value_v = raw / 4095.0f;
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
switch (this->attenuation_) {
|
switch (this->attenuation_) {
|
||||||
|
@ -146,15 +151,15 @@ float ADCSensor::sample() {
|
||||||
return value_v;
|
return value_v;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
return ESP.getVcc() / 1024.0f;
|
return ESP.getVcc() / 1024.0f; // NOLINT(readability-static-accessed-through-instance)
|
||||||
#else
|
#else
|
||||||
return analogRead(this->pin_) / 1024.0f; // NOLINT
|
return analogRead(this->pin_->get_pin()) / 1024.0f; // NOLINT
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/esphal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
#include "driver/adc.h"
|
#include "driver/adc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ namespace adc {
|
||||||
|
|
||||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
||||||
public:
|
public:
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
/// Set the attenuation for this pin. Only available on the ESP32.
|
/// Set the attenuation for this pin. Only available on the ESP32.
|
||||||
void set_attenuation(adc_atten_t attenuation);
|
void set_attenuation(adc_atten_t attenuation);
|
||||||
#endif
|
#endif
|
||||||
|
@ -27,17 +27,17 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
/// `HARDWARE_LATE` setup priority.
|
/// `HARDWARE_LATE` setup priority.
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
||||||
float sample() override;
|
float sample() override;
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
std::string unique_id() override;
|
std::string unique_id() override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t pin_;
|
InternalGPIOPin *pin_;
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,11 +5,13 @@ from esphome.components import sensor, voltage_sampler
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ATTENUATION,
|
CONF_ATTENUATION,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_INPUT,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
)
|
)
|
||||||
|
from esphome.core import CORE
|
||||||
|
|
||||||
|
|
||||||
AUTO_LOAD = ["voltage_sampler"]
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
|
@ -23,10 +25,34 @@ ATTENUATION_MODES = {
|
||||||
|
|
||||||
|
|
||||||
def validate_adc_pin(value):
|
def validate_adc_pin(value):
|
||||||
vcc = str(value).upper()
|
if str(value).upper() == "VCC":
|
||||||
if vcc == "VCC":
|
return cv.only_on_esp8266("VCC")
|
||||||
return cv.only_on_esp8266(vcc)
|
|
||||||
return pins.analog_pin(value)
|
if CORE.is_esp32:
|
||||||
|
from esphome.components.esp32 import is_esp32c3
|
||||||
|
|
||||||
|
value = pins.internal_gpio_input_pin_number(value)
|
||||||
|
if is_esp32c3():
|
||||||
|
if not (0 <= value <= 4): # ADC1
|
||||||
|
raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.")
|
||||||
|
if not (32 <= value <= 39): # ADC1
|
||||||
|
raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.")
|
||||||
|
elif 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)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
return pins.internal_gpio_input_pin_schema(value)
|
||||||
|
|
||||||
|
|
||||||
adc_ns = cg.esphome_ns.namespace("adc")
|
adc_ns = cg.esphome_ns.namespace("adc")
|
||||||
|
@ -62,7 +88,8 @@ async def to_code(config):
|
||||||
if config[CONF_PIN] == "VCC":
|
if config[CONF_PIN] == "VCC":
|
||||||
cg.add_define("USE_ADC_SENSOR_VCC")
|
cg.add_define("USE_ADC_SENSOR_VCC")
|
||||||
else:
|
else:
|
||||||
cg.add(var.set_pin(config[CONF_PIN]))
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
|
cg.add(var.set_pin(pin))
|
||||||
|
|
||||||
if CONF_ATTENUATION in config:
|
if CONF_ATTENUATION in config:
|
||||||
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
|
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
|
||||||
|
|
|
@ -8,9 +8,7 @@ static const char *const TAG = "ade7953";
|
||||||
|
|
||||||
void ADE7953::dump_config() {
|
void ADE7953::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "ADE7953:");
|
ESP_LOGCONFIG(TAG, "ADE7953:");
|
||||||
if (this->has_irq_) {
|
LOG_PIN(" IRQ Pin: ", irq_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " IRQ Pin: GPIO%u", this->irq_pin_number_);
|
|
||||||
}
|
|
||||||
LOG_I2C_DEVICE(this);
|
LOG_I2C_DEVICE(this);
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_);
|
LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_);
|
||||||
|
@ -20,27 +18,28 @@ void ADE7953::dump_config() {
|
||||||
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
|
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ADE_PUBLISH_(name, factor) \
|
#define ADE_PUBLISH_(name, val, factor) \
|
||||||
if ((name) && this->name##_sensor_) { \
|
if (err == i2c::ERROR_OK && this->name##_sensor_) { \
|
||||||
float value = *(name) / (factor); \
|
float value = (val) / (factor); \
|
||||||
this->name##_sensor_->publish_state(value); \
|
this->name##_sensor_->publish_state(value); \
|
||||||
}
|
}
|
||||||
#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor)
|
#define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor)
|
||||||
|
|
||||||
void ADE7953::update() {
|
void ADE7953::update() {
|
||||||
if (!this->is_setup_)
|
if (!this->is_setup_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto active_power_a = this->ade_read_<int32_t>(0x0312);
|
uint32_t val;
|
||||||
ADE_PUBLISH(active_power_a, 154.0f);
|
i2c::ErrorCode err = ade_read_32_(0x0312, &val);
|
||||||
auto active_power_b = this->ade_read_<int32_t>(0x0313);
|
ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f);
|
||||||
ADE_PUBLISH(active_power_b, 154.0f);
|
err = ade_read_32_(0x0313, &val);
|
||||||
auto current_a = this->ade_read_<uint32_t>(0x031A);
|
ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f);
|
||||||
ADE_PUBLISH(current_a, 100000.0f);
|
err = ade_read_32_(0x031A, &val);
|
||||||
auto current_b = this->ade_read_<uint32_t>(0x031B);
|
ADE_PUBLISH(current_a, (uint32_t) val, 100000.0f);
|
||||||
ADE_PUBLISH(current_b, 100000.0f);
|
err = ade_read_32_(0x031B, &val);
|
||||||
auto voltage = this->ade_read_<uint32_t>(0x031C);
|
ADE_PUBLISH(current_b, (uint32_t) val, 100000.0f);
|
||||||
ADE_PUBLISH(voltage, 26000.0f);
|
err = ade_read_32_(0x031C, &val);
|
||||||
|
ADE_PUBLISH(voltage, (uint32_t) val, 26000.0f);
|
||||||
|
|
||||||
// auto apparent_power_a = this->ade_read_<int32_t>(0x0310);
|
// auto apparent_power_a = this->ade_read_<int32_t>(0x0310);
|
||||||
// auto apparent_power_b = this->ade_read_<int32_t>(0x0311);
|
// auto apparent_power_b = this->ade_read_<int32_t>(0x0311);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
|
@ -9,10 +10,7 @@ namespace ade7953 {
|
||||||
|
|
||||||
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
||||||
public:
|
public:
|
||||||
void set_irq_pin(uint8_t irq_pin) {
|
void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; }
|
||||||
has_irq_ = true;
|
|
||||||
irq_pin_number_ = irq_pin;
|
|
||||||
}
|
|
||||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||||
void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
|
void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
|
||||||
void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
|
void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
|
||||||
|
@ -24,15 +22,13 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() override {
|
void setup() override {
|
||||||
if (this->has_irq_) {
|
if (this->irq_pin_ != nullptr) {
|
||||||
auto pin = GPIOPin(this->irq_pin_number_, INPUT);
|
|
||||||
this->irq_pin_ = &pin;
|
|
||||||
this->irq_pin_->setup();
|
this->irq_pin_->setup();
|
||||||
}
|
}
|
||||||
this->set_timeout(100, [this]() {
|
this->set_timeout(100, [this]() {
|
||||||
this->ade_write_<uint8_t>(0x0010, 0x04);
|
this->ade_write_8_(0x0010, 0x04);
|
||||||
this->ade_write_<uint8_t>(0x00FE, 0xAD);
|
this->ade_write_8_(0x00FE, 0xAD);
|
||||||
this->ade_write_<uint16_t>(0x0120, 0x0030);
|
this->ade_write_16_(0x0120, 0x0030);
|
||||||
this->is_setup_ = true;
|
this->is_setup_ = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -42,31 +38,51 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template<typename T> bool ade_write_(uint16_t reg, T value) {
|
i2c::ErrorCode ade_write_8_(uint16_t reg, uint8_t value) {
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
data.push_back(reg >> 8);
|
data.push_back(reg >> 8);
|
||||||
data.push_back(reg >> 0);
|
data.push_back(reg >> 0);
|
||||||
for (int i = sizeof(T) - 1; i >= 0; i--)
|
data.push_back(value);
|
||||||
data.push_back(value >> (i * 8));
|
return write(data.data(), data.size());
|
||||||
return this->write_bytes_raw(data);
|
|
||||||
}
|
}
|
||||||
template<typename T> optional<T> ade_read_(uint16_t reg) {
|
i2c::ErrorCode ade_write_16_(uint16_t reg, uint16_t value) {
|
||||||
uint8_t hi = reg >> 8;
|
std::vector<uint8_t> data;
|
||||||
uint8_t lo = reg >> 0;
|
data.push_back(reg >> 8);
|
||||||
if (!this->write_bytes_raw({hi, lo}))
|
data.push_back(reg >> 0);
|
||||||
return {};
|
data.push_back(value >> 8);
|
||||||
auto ret = this->read_bytes_raw<sizeof(T)>();
|
data.push_back(value >> 0);
|
||||||
if (!ret.has_value())
|
return write(data.data(), data.size());
|
||||||
return {};
|
}
|
||||||
T result = 0;
|
i2c::ErrorCode ade_write_32_(uint16_t reg, uint32_t value) {
|
||||||
for (int i = 0, j = sizeof(T) - 1; i < sizeof(T); i++, j--)
|
std::vector<uint8_t> data;
|
||||||
result |= T((*ret)[i]) << (j * 8);
|
data.push_back(reg >> 8);
|
||||||
return result;
|
data.push_back(reg >> 0);
|
||||||
|
data.push_back(value >> 24);
|
||||||
|
data.push_back(value >> 16);
|
||||||
|
data.push_back(value >> 8);
|
||||||
|
data.push_back(value >> 0);
|
||||||
|
return write(data.data(), data.size());
|
||||||
|
}
|
||||||
|
i2c::ErrorCode ade_read_32_(uint16_t reg, uint32_t *value) {
|
||||||
|
uint8_t reg_data[2];
|
||||||
|
reg_data[0] = reg >> 8;
|
||||||
|
reg_data[1] = reg >> 0;
|
||||||
|
i2c::ErrorCode err = write(reg_data, 2);
|
||||||
|
if (err != i2c::ERROR_OK)
|
||||||
|
return err;
|
||||||
|
uint8_t recv[4];
|
||||||
|
err = read(recv, 4);
|
||||||
|
if (err != i2c::ERROR_OK)
|
||||||
|
return err;
|
||||||
|
*value = 0;
|
||||||
|
*value |= ((uint32_t) recv[0]) << 24;
|
||||||
|
*value |= ((uint32_t) recv[1]) << 24;
|
||||||
|
*value |= ((uint32_t) recv[2]) << 24;
|
||||||
|
*value |= ((uint32_t) recv[3]) << 24;
|
||||||
|
return i2c::ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_irq_ = false;
|
InternalGPIOPin *irq_pin_ = nullptr;
|
||||||
uint8_t irq_pin_number_;
|
|
||||||
GPIOPin *irq_pin_{nullptr};
|
|
||||||
bool is_setup_{false};
|
bool is_setup_{false};
|
||||||
sensor::Sensor *voltage_sensor_{nullptr};
|
sensor::Sensor *voltage_sensor_{nullptr};
|
||||||
sensor::Sensor *current_a_sensor_{nullptr};
|
sensor::Sensor *current_a_sensor_{nullptr};
|
||||||
|
|
|
@ -29,7 +29,7 @@ CONFIG_SCHEMA = (
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(ADE7953),
|
cv.GenerateID(): cv.declare_id(ADE7953),
|
||||||
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
|
cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_VOLT,
|
unit_of_measurement=UNIT_VOLT,
|
||||||
accuracy_decimals=1,
|
accuracy_decimals=1,
|
||||||
|
@ -73,7 +73,8 @@ async def to_code(config):
|
||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
if CONF_IRQ_PIN in config:
|
if CONF_IRQ_PIN in config:
|
||||||
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
|
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||||
|
cg.add(var.set_irq_pin(irq_pin))
|
||||||
|
|
||||||
for key in [
|
for key in [
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "ads1115.h"
|
#include "ads1115.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ads1115 {
|
namespace ads1115 {
|
||||||
|
@ -159,7 +160,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
||||||
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
|
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
|
||||||
void ADS1115Sensor::update() {
|
void ADS1115Sensor::update() {
|
||||||
float v = this->parent_->request_measurement(this);
|
float v = this->parent_->request_measurement(this);
|
||||||
if (!isnan(v)) {
|
if (!std::isnan(v)) {
|
||||||
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
|
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
|
||||||
this->publish_state(v);
|
this->publish_state(v);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "aht10.h"
|
#include "aht10.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace aht10 {
|
namespace aht10 {
|
||||||
|
@ -33,8 +34,19 @@ void AHT10Component::setup() {
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t data;
|
uint8_t data = 0;
|
||||||
if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) {
|
if (this->write(&data, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delay(AHT10_DEFAULT_DELAY);
|
||||||
|
if (this->read(&data, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->read(&data, 1) != i2c::ERROR_OK) {
|
||||||
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
|
@ -55,15 +67,26 @@ void AHT10Component::update() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t data[6];
|
uint8_t data[6];
|
||||||
uint8_t delay = AHT10_DEFAULT_DELAY;
|
uint8_t delay_ms = AHT10_DEFAULT_DELAY;
|
||||||
if (this->humidity_sensor_ != nullptr)
|
if (this->humidity_sensor_ != nullptr)
|
||||||
delay = AHT10_HUMIDITY_DELAY;
|
delay_ms = AHT10_HUMIDITY_DELAY;
|
||||||
|
bool success = false;
|
||||||
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
||||||
ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
|
ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis());
|
||||||
delay_microseconds_accurate(4);
|
delay_microseconds_accurate(4);
|
||||||
if (!this->read_bytes(0, data, 6, delay)) {
|
|
||||||
|
uint8_t reg = 0;
|
||||||
|
if (this->write(®, 1) != i2c::ERROR_OK) {
|
||||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||||
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
continue;
|
||||||
|
}
|
||||||
|
delay(delay_ms);
|
||||||
|
if (this->read(data, 6) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||||
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
||||||
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
||||||
// Unrealistic humidity (0x0)
|
// Unrealistic humidity (0x0)
|
||||||
|
@ -80,11 +103,12 @@ void AHT10Component::update() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// data is valid, we can break the loop
|
// data is valid, we can break the loop
|
||||||
ESP_LOGVV(TAG, "Answer at %6ld", millis());
|
ESP_LOGVV(TAG, "Answer at %6u", millis());
|
||||||
|
success = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((data[0] & 0x80) == 0x80) {
|
if (!success || (data[0] & 0x80) == 0x80) {
|
||||||
ESP_LOGE(TAG, "Measurements reading timed-out!");
|
ESP_LOGE(TAG, "Measurements reading timed-out!");
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
|
@ -105,7 +129,7 @@ void AHT10Component::update() {
|
||||||
this->temperature_sensor_->publish_state(temperature);
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
}
|
}
|
||||||
if (this->humidity_sensor_ != nullptr) {
|
if (this->humidity_sensor_ != nullptr) {
|
||||||
if (isnan(humidity))
|
if (std::isnan(humidity))
|
||||||
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
||||||
this->humidity_sensor_->publish_state(humidity);
|
this->humidity_sensor_->publish_state(humidity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#include "airthings_listener.h"
|
#include "airthings_listener.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_ble {
|
namespace airthings_ble {
|
||||||
|
|
||||||
static const char *TAG = "airthings_ble";
|
static const char *const TAG = "airthings_ble";
|
||||||
|
|
||||||
bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
for (auto &it : device.get_manufacturer_datas()) {
|
for (auto &it : device.get_manufacturer_datas()) {
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
#include <BLEDevice.h>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_ble {
|
namespace airthings_ble {
|
||||||
|
|
1
esphome/components/airthings_wave_mini/__init__.py
Normal file
1
esphome/components/airthings_wave_mini/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@ncareau"]
|
113
esphome/components/airthings_wave_mini/airthings_wave_mini.cpp
Normal file
113
esphome/components/airthings_wave_mini/airthings_wave_mini.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#include "airthings_wave_mini.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace airthings_wave_mini {
|
||||||
|
|
||||||
|
static const char *const TAG = "airthings_wave_mini";
|
||||||
|
|
||||||
|
void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
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()->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) {
|
||||||
|
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 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()->gattc_if, this->parent()->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() {
|
||||||
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||||
|
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
65
esphome/components/airthings_wave_mini/airthings_wave_mini.h
Normal file
65
esphome/components/airthings_wave_mini/airthings_wave_mini.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#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_mini {
|
||||||
|
|
||||||
|
static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
|
||||||
|
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:
|
||||||
|
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;
|
||||||
|
uint16_t temperature;
|
||||||
|
uint16_t pressure;
|
||||||
|
uint16_t humidity;
|
||||||
|
uint16_t voc;
|
||||||
|
uint16_t unused02;
|
||||||
|
uint32_t unused03;
|
||||||
|
uint32_t unused04;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace airthings_wave_mini
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
82
esphome/components/airthings_wave_mini/sensor.py
Normal file
82
esphome/components/airthings_wave_mini/sensor.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
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 = ["ble_client"]
|
||||||
|
|
||||||
|
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
|
||||||
|
AirthingsWaveMini = airthings_wave_mini_ns.class_(
|
||||||
|
"AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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 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))
|
|
@ -1,10 +1,12 @@
|
||||||
#include "airthings_wave_plus.h"
|
#include "airthings_wave_plus.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_wave_plus {
|
namespace airthings_wave_plus {
|
||||||
|
|
||||||
|
static const char *const TAG = "airthings_wave_plus";
|
||||||
|
|
||||||
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) {
|
esp_ble_gattc_cb_param_t *param) {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
@ -21,15 +23,15 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
|
||||||
}
|
}
|
||||||
|
|
||||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
this->handle = 0;
|
this->handle_ = 0;
|
||||||
auto chr = this->parent()->get_characteristic(service_uuid, sensors_data_characteristic_uuid);
|
auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
|
||||||
if (chr == nullptr) {
|
if (chr == nullptr) {
|
||||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid.to_string().c_str(),
|
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());
|
sensors_data_characteristic_uuid_.to_string().c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this->handle = chr->handle;
|
this->handle_ = chr->handle;
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
|
||||||
|
|
||||||
request_read_values_();
|
request_read_values_();
|
||||||
break;
|
break;
|
||||||
|
@ -42,7 +44,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
|
||||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (param->read.handle == this->handle) {
|
if (param->read.handle == this->handle_) {
|
||||||
read_sensors_(param->read.value, param->read.value_len);
|
read_sensors_(param->read.value, param->read.value_len);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -88,16 +90,14 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_radon_value_(short radon) { return 0 <= radon && radon <= 16383; }
|
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_voc_value_(short voc) { return 0 <= voc && voc <= 16383; }
|
bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_co2_value_(short co2) { return 0 <= co2 && co2 <= 16383; }
|
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
|
||||||
|
|
||||||
void AirthingsWavePlus::loop() {}
|
|
||||||
|
|
||||||
void AirthingsWavePlus::update() {
|
void AirthingsWavePlus::update() {
|
||||||
if (this->node_state != espbt::ClientState::Established) {
|
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
|
||||||
if (!parent()->enabled) {
|
if (!parent()->enabled) {
|
||||||
ESP_LOGW(TAG, "Reconnecting to device");
|
ESP_LOGW(TAG, "Reconnecting to device");
|
||||||
parent()->set_enabled(true);
|
parent()->set_enabled(true);
|
||||||
|
@ -110,7 +110,7 @@ void AirthingsWavePlus::update() {
|
||||||
|
|
||||||
void AirthingsWavePlus::request_read_values_() {
|
void AirthingsWavePlus::request_read_values_() {
|
||||||
auto status =
|
auto status =
|
||||||
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
|
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
|
||||||
if (status) {
|
if (status) {
|
||||||
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
||||||
}
|
}
|
||||||
|
@ -126,17 +126,12 @@ void AirthingsWavePlus::dump_config() {
|
||||||
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
AirthingsWavePlus::AirthingsWavePlus() : PollingComponent(10000) {
|
AirthingsWavePlus::AirthingsWavePlus()
|
||||||
auto service_bt = *BLEUUID::fromString(std::string("b42e1c08-ade7-11e4-89d3-123b93f75cba")).getNative();
|
: PollingComponent(10000),
|
||||||
auto characteristic_bt = *BLEUUID::fromString(std::string("b42e2a68-ade7-11e4-89d3-123b93f75cba")).getNative();
|
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
|
||||||
|
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
|
||||||
service_uuid = espbt::ESPBTUUID::from_uuid(service_bt);
|
|
||||||
sensors_data_characteristic_uuid = espbt::ESPBTUUID::from_uuid(characteristic_bt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AirthingsWavePlus::setup() {}
|
|
||||||
|
|
||||||
} // namespace airthings_wave_plus
|
} // namespace airthings_wave_plus
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // USE_ESP32
|
||||||
|
|
|
@ -1,32 +1,28 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_gattc_api.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
#include "esphome/components/ble_client/ble_client.h"
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
#include <esp_gattc_api.h>
|
|
||||||
#include <BLEDevice.h>
|
|
||||||
|
|
||||||
using namespace esphome::ble_client;
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_wave_plus {
|
namespace airthings_wave_plus {
|
||||||
|
|
||||||
static const char *TAG = "airthings_wave_plus";
|
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
|
||||||
class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
|
||||||
public:
|
public:
|
||||||
AirthingsWavePlus();
|
AirthingsWavePlus();
|
||||||
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) override;
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
|
@ -40,9 +36,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
||||||
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool is_valid_radon_value_(short radon);
|
bool is_valid_radon_value_(uint16_t radon);
|
||||||
bool is_valid_voc_value_(short voc);
|
bool is_valid_voc_value_(uint16_t voc);
|
||||||
bool is_valid_co2_value_(short co2);
|
bool is_valid_co2_value_(uint16_t co2);
|
||||||
|
|
||||||
void read_sensors_(uint8_t *value, uint16_t value_len);
|
void read_sensors_(uint8_t *value, uint16_t value_len);
|
||||||
void request_read_values_();
|
void request_read_values_();
|
||||||
|
@ -55,9 +51,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
||||||
sensor::Sensor *co2_sensor_{nullptr};
|
sensor::Sensor *co2_sensor_{nullptr};
|
||||||
sensor::Sensor *tvoc_sensor_{nullptr};
|
sensor::Sensor *tvoc_sensor_{nullptr};
|
||||||
|
|
||||||
uint16_t handle;
|
uint16_t handle_;
|
||||||
espbt::ESPBTUUID service_uuid;
|
esp32_ble_tracker::ESPBTUUID service_uuid_;
|
||||||
espbt::ESPBTUUID sensors_data_characteristic_uuid;
|
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
|
||||||
|
|
||||||
struct WavePlusReadings {
|
struct WavePlusReadings {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
|
@ -76,4 +72,4 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
||||||
} // namespace airthings_wave_plus
|
} // namespace airthings_wave_plus
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // USE_ESP32
|
||||||
|
|
|
@ -34,7 +34,7 @@ AirthingsWavePlus = airthings_wave_plus_ns.class_(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
|
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
|
||||||
|
@ -82,8 +82,8 @@ CONFIG_SCHEMA = (
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("5mins"))
|
.extend(cv.polling_component_schema("5min"))
|
||||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
.extend(ble_client.BLE_CLIENT_SCHEMA),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "am2320.h"
|
#include "am2320.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am2320 {
|
namespace am2320 {
|
||||||
|
@ -77,7 +78,7 @@ bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len
|
||||||
|
|
||||||
if (conversion > 0)
|
if (conversion > 0)
|
||||||
delay(conversion);
|
delay(conversion);
|
||||||
return this->parent_->raw_receive(this->address_, data, len);
|
return this->read(data, len) == i2c::ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AM2320Component::read_data_(uint8_t *data) {
|
bool AM2320Component::read_data_(uint8_t *data) {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
#include "am43.h"
|
#include "am43.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am43 {
|
namespace am43 {
|
||||||
|
|
||||||
static const char *TAG = "am43";
|
static const char *const TAG = "am43";
|
||||||
|
|
||||||
void Am43::dump_config() {
|
void Am43::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "AM43");
|
ESP_LOGCONFIG(TAG, "AM43");
|
||||||
|
@ -15,8 +16,8 @@ void Am43::dump_config() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Am43::setup() {
|
void Am43::setup() {
|
||||||
this->encoder_ = new Am43Encoder();
|
this->encoder_ = make_unique<Am43Encoder>();
|
||||||
this->decoder_ = new Am43Decoder();
|
this->decoder_ = make_unique<Am43Decoder>();
|
||||||
this->logged_in_ = false;
|
this->logged_in_ = false;
|
||||||
this->last_battery_update_ = 0;
|
this->last_battery_update_ = 0;
|
||||||
this->current_sensor_ = 0;
|
this->current_sensor_ = 0;
|
||||||
|
@ -30,7 +31,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
||||||
}
|
}
|
||||||
case ESP_GATTC_DISCONNECT_EVT: {
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
this->logged_in_ = false;
|
this->logged_in_ = false;
|
||||||
this->node_state = espbt::ClientState::Idle;
|
this->node_state = espbt::ClientState::IDLE;
|
||||||
if (this->battery_ != nullptr)
|
if (this->battery_ != nullptr)
|
||||||
this->battery_->publish_state(NAN);
|
this->battery_->publish_state(NAN);
|
||||||
if (this->illuminance_ != nullptr)
|
if (this->illuminance_ != nullptr)
|
||||||
|
@ -53,7 +54,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
this->update();
|
this->update();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +93,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
||||||
}
|
}
|
||||||
|
|
||||||
void Am43::update() {
|
void Am43::update() {
|
||||||
if (this->node_state != espbt::ClientState::Established) {
|
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||||
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
|
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/am43/am43_base.h"
|
#include "esphome/components/am43/am43_base.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <esp_gattc_api.h>
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint16_t char_handle_;
|
uint16_t char_handle_;
|
||||||
Am43Encoder *encoder_;
|
std::unique_ptr<Am43Encoder> encoder_;
|
||||||
Am43Decoder *decoder_;
|
std::unique_ptr<Am43Decoder> decoder_;
|
||||||
bool logged_in_;
|
bool logged_in_;
|
||||||
sensor::Sensor *battery_{nullptr};
|
sensor::Sensor *battery_{nullptr};
|
||||||
sensor::Sensor *illuminance_{nullptr};
|
sensor::Sensor *illuminance_{nullptr};
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "am43_base.h"
|
#include "am43_base.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am43 {
|
namespace am43 {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#include "am43_cover.h"
|
#include "am43_cover.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am43 {
|
namespace am43 {
|
||||||
|
|
||||||
static const char *TAG = "am43_cover";
|
static const char *const TAG = "am43_cover";
|
||||||
|
|
||||||
using namespace esphome::cover;
|
using namespace esphome::cover;
|
||||||
|
|
||||||
|
@ -18,13 +18,13 @@ void Am43Component::dump_config() {
|
||||||
|
|
||||||
void Am43Component::setup() {
|
void Am43Component::setup() {
|
||||||
this->position = COVER_OPEN;
|
this->position = COVER_OPEN;
|
||||||
this->encoder_ = new Am43Encoder();
|
this->encoder_ = make_unique<Am43Encoder>();
|
||||||
this->decoder_ = new Am43Decoder();
|
this->decoder_ = make_unique<Am43Decoder>();
|
||||||
this->logged_in_ = false;
|
this->logged_in_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Am43Component::loop() {
|
void Am43Component::loop() {
|
||||||
if (this->node_state == espbt::ClientState::Established && !this->logged_in_) {
|
if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) {
|
||||||
auto packet = this->encoder_->get_send_pin_request(this->pin_);
|
auto packet = this->encoder_->get_send_pin_request(this->pin_);
|
||||||
auto status =
|
auto status =
|
||||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
|
@ -46,7 +46,7 @@ CoverTraits Am43Component::get_traits() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Am43Component::control(const CoverCall &call) {
|
void Am43Component::control(const CoverCall &call) {
|
||||||
if (this->node_state != espbt::ClientState::Established) {
|
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||||
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
|
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_NOTIFY_EVT: {
|
case ESP_GATTC_NOTIFY_EVT: {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "esphome/components/cover/cover.h"
|
#include "esphome/components/cover/cover.h"
|
||||||
#include "esphome/components/am43/am43_base.h"
|
#include "esphome/components/am43/am43_base.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <esp_gattc_api.h>
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ class Am43Component : public cover::Cover, public esphome::ble_client::BLEClient
|
||||||
uint16_t char_handle_;
|
uint16_t char_handle_;
|
||||||
uint16_t pin_;
|
uint16_t pin_;
|
||||||
bool invert_position_;
|
bool invert_position_;
|
||||||
Am43Encoder *encoder_;
|
std::unique_ptr<Am43Encoder> encoder_;
|
||||||
Am43Decoder *decoder_;
|
std::unique_ptr<Am43Decoder> decoder_;
|
||||||
bool logged_in_;
|
bool logged_in_;
|
||||||
|
|
||||||
float position_;
|
float position_;
|
||||||
|
|
|
@ -5,7 +5,7 @@ from esphome.components import display, font
|
||||||
import esphome.components.image as espImage
|
import esphome.components.image as espImage
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE
|
from esphome.const import CONF_FILE, CONF_ID, CONF_RAW_DATA_ID, CONF_RESIZE, CONF_TYPE
|
||||||
from esphome.core import CORE, HexInt
|
from esphome.core import CORE, HexInt
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -15,8 +15,6 @@ MULTI_CONF = True
|
||||||
|
|
||||||
Animation_ = display.display_ns.class_("Animation")
|
Animation_ = display.display_ns.class_("Animation")
|
||||||
|
|
||||||
CONF_RAW_DATA_ID = "raw_data_id"
|
|
||||||
|
|
||||||
ANIMATION_SCHEMA = cv.Schema(
|
ANIMATION_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
#include "anova.h"
|
#include "anova.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace anova {
|
namespace anova {
|
||||||
|
|
||||||
static const char *TAG = "anova";
|
static const char *const TAG = "anova";
|
||||||
|
|
||||||
using namespace esphome::climate;
|
using namespace esphome::climate;
|
||||||
|
|
||||||
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
|
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
|
||||||
|
|
||||||
void Anova::setup() {
|
void Anova::setup() {
|
||||||
this->codec_ = new AnovaCodec();
|
this->codec_ = make_unique<AnovaCodec>();
|
||||||
this->current_request_ = 0;
|
this->current_request_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
this->current_request_ = 0;
|
this->current_request_ = 0;
|
||||||
this->update();
|
this->update();
|
||||||
break;
|
break;
|
||||||
|
@ -129,13 +129,13 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
||||||
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
|
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
|
||||||
|
|
||||||
void Anova::update() {
|
void Anova::update() {
|
||||||
if (this->node_state != espbt::ClientState::Established)
|
if (this->node_state != espbt::ClientState::ESTABLISHED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this->current_request_ < 2) {
|
if (this->current_request_ < 2) {
|
||||||
auto pkt = this->codec_->get_read_device_status_request();
|
auto pkt = this->codec_->get_read_device_status_request();
|
||||||
if (this->current_request_ == 0)
|
if (this->current_request_ == 0)
|
||||||
auto pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
||||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
if (status)
|
if (status)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "esphome/components/climate/climate.h"
|
#include "esphome/components/climate/climate.h"
|
||||||
#include "anova_base.h"
|
#include "anova_base.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <esp_gattc_api.h>
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
|
||||||
esp_ble_gattc_cb_param_t *param) override;
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
climate::ClimateTraits traits() {
|
climate::ClimateTraits traits() override {
|
||||||
auto traits = climate::ClimateTraits();
|
auto traits = climate::ClimateTraits();
|
||||||
traits.set_supports_current_temperature(true);
|
traits.set_supports_current_temperature(true);
|
||||||
traits.set_supports_heat_mode(true);
|
traits.set_supports_heat_mode(true);
|
||||||
|
@ -39,7 +39,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
|
||||||
void set_unit_of_measurement(const char *);
|
void set_unit_of_measurement(const char *);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AnovaCodec *codec_;
|
std::unique_ptr<AnovaCodec> codec_;
|
||||||
void control(const climate::ClimateCall &call) override;
|
void control(const climate::ClimateCall &call) override;
|
||||||
uint16_t char_handle_;
|
uint16_t char_handle_;
|
||||||
uint8_t current_request_;
|
uint8_t current_request_;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "anova_base.h"
|
#include "anova_base.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace anova {
|
namespace anova {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "apds9960.h"
|
#include "apds9960.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace apds9960 {
|
namespace apds9960 {
|
||||||
|
|
|
@ -215,6 +215,7 @@ message ListEntitiesBinarySensorResponse {
|
||||||
string device_class = 5;
|
string device_class = 5;
|
||||||
bool is_status_binary_sensor = 6;
|
bool is_status_binary_sensor = 6;
|
||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
|
string icon = 8;
|
||||||
}
|
}
|
||||||
message BinarySensorStateResponse {
|
message BinarySensorStateResponse {
|
||||||
option (id) = 21;
|
option (id) = 21;
|
||||||
|
@ -245,6 +246,7 @@ message ListEntitiesCoverResponse {
|
||||||
bool supports_tilt = 7;
|
bool supports_tilt = 7;
|
||||||
string device_class = 8;
|
string device_class = 8;
|
||||||
bool disabled_by_default = 9;
|
bool disabled_by_default = 9;
|
||||||
|
string icon = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LegacyCoverState {
|
enum LegacyCoverState {
|
||||||
|
@ -313,6 +315,7 @@ message ListEntitiesFanResponse {
|
||||||
bool supports_direction = 7;
|
bool supports_direction = 7;
|
||||||
int32 supported_speed_count = 8;
|
int32 supported_speed_count = 8;
|
||||||
bool disabled_by_default = 9;
|
bool disabled_by_default = 9;
|
||||||
|
string icon = 10;
|
||||||
}
|
}
|
||||||
enum FanSpeed {
|
enum FanSpeed {
|
||||||
FAN_SPEED_LOW = 0;
|
FAN_SPEED_LOW = 0;
|
||||||
|
@ -388,6 +391,7 @@ message ListEntitiesLightResponse {
|
||||||
float max_mireds = 10;
|
float max_mireds = 10;
|
||||||
repeated string effects = 11;
|
repeated string effects = 11;
|
||||||
bool disabled_by_default = 13;
|
bool disabled_by_default = 13;
|
||||||
|
string icon = 14;
|
||||||
}
|
}
|
||||||
message LightStateResponse {
|
message LightStateResponse {
|
||||||
option (id) = 24;
|
option (id) = 24;
|
||||||
|
@ -790,6 +794,7 @@ message ListEntitiesClimateResponse {
|
||||||
repeated ClimatePreset supported_presets = 16;
|
repeated ClimatePreset supported_presets = 16;
|
||||||
repeated string supported_custom_presets = 17;
|
repeated string supported_custom_presets = 17;
|
||||||
bool disabled_by_default = 18;
|
bool disabled_by_default = 18;
|
||||||
|
string icon = 19;
|
||||||
}
|
}
|
||||||
message ClimateStateResponse {
|
message ClimateStateResponse {
|
||||||
option (id) = 47;
|
option (id) = 47;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "api_connection.h"
|
#include "api_connection.h"
|
||||||
|
#include "esphome/core/entity_base.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/util.h"
|
#include "esphome/components/network/util.h"
|
||||||
#include "esphome/core/version.h"
|
#include "esphome/core/version.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
#ifdef USE_DEEP_SLEEP
|
#ifdef USE_DEEP_SLEEP
|
||||||
|
@ -48,7 +50,7 @@ void APIConnection::loop() {
|
||||||
if (this->remove_)
|
if (this->remove_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!network_is_connected()) {
|
if (!network::is_connected()) {
|
||||||
// when network is disconnected force disconnect immediately
|
// when network is disconnected force disconnect immediately
|
||||||
// don't wait for timeout
|
// don't wait for timeout
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
|
@ -142,8 +144,21 @@ void APIConnection::loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) {
|
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
|
||||||
return App.get_name() + component_type + nameable->get_object_id();
|
return App.get_name() + component_type + entity->get_object_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
||||||
|
// remote initiated disconnect_client
|
||||||
|
// don't close yet, we still need to send the disconnect response
|
||||||
|
// close will happen on next loop
|
||||||
|
ESP_LOGD(TAG, "%s requested disconnected", client_info_.c_str());
|
||||||
|
this->next_close_ = true;
|
||||||
|
DisconnectResponse resp;
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
|
||||||
|
// pass
|
||||||
}
|
}
|
||||||
|
|
||||||
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
||||||
|
@ -179,6 +194,7 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
|
||||||
msg.device_class = binary_sensor->get_device_class();
|
msg.device_class = binary_sensor->get_device_class();
|
||||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||||
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
|
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
|
||||||
|
msg.icon = binary_sensor->get_icon();
|
||||||
return this->send_list_entities_binary_sensor_response(msg);
|
return this->send_list_entities_binary_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -211,6 +227,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
|
||||||
msg.supports_tilt = traits.get_supports_tilt();
|
msg.supports_tilt = traits.get_supports_tilt();
|
||||||
msg.device_class = cover->get_device_class();
|
msg.device_class = cover->get_device_class();
|
||||||
msg.disabled_by_default = cover->is_disabled_by_default();
|
msg.disabled_by_default = cover->is_disabled_by_default();
|
||||||
|
msg.icon = cover->get_icon();
|
||||||
return this->send_list_entities_cover_response(msg);
|
return this->send_list_entities_cover_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||||
|
@ -276,6 +293,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
|
||||||
msg.supports_direction = traits.supports_direction();
|
msg.supports_direction = traits.supports_direction();
|
||||||
msg.supported_speed_count = traits.supported_speed_count();
|
msg.supported_speed_count = traits.supported_speed_count();
|
||||||
msg.disabled_by_default = fan->is_disabled_by_default();
|
msg.disabled_by_default = fan->is_disabled_by_default();
|
||||||
|
msg.icon = fan->get_icon();
|
||||||
return this->send_list_entities_fan_response(msg);
|
return this->send_list_entities_fan_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||||
|
@ -338,6 +356,7 @@ bool APIConnection::send_light_info(light::LightState *light) {
|
||||||
msg.unique_id = get_default_unique_id("light", light);
|
msg.unique_id = get_default_unique_id("light", light);
|
||||||
|
|
||||||
msg.disabled_by_default = light->is_disabled_by_default();
|
msg.disabled_by_default = light->is_disabled_by_default();
|
||||||
|
msg.icon = light->get_icon();
|
||||||
|
|
||||||
for (auto mode : traits.get_supported_color_modes())
|
for (auto mode : traits.get_supported_color_modes())
|
||||||
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
||||||
|
@ -528,6 +547,7 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
||||||
msg.unique_id = get_default_unique_id("climate", climate);
|
msg.unique_id = get_default_unique_id("climate", climate);
|
||||||
|
|
||||||
msg.disabled_by_default = climate->is_disabled_by_default();
|
msg.disabled_by_default = climate->is_disabled_by_default();
|
||||||
|
msg.icon = climate->get_icon();
|
||||||
|
|
||||||
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
||||||
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
||||||
|
@ -600,7 +620,7 @@ bool APIConnection::send_number_info(number::Number *number) {
|
||||||
msg.object_id = number->get_object_id();
|
msg.object_id = number->get_object_id();
|
||||||
msg.name = number->get_name();
|
msg.name = number->get_name();
|
||||||
msg.unique_id = get_default_unique_id("number", number);
|
msg.unique_id = get_default_unique_id("number", number);
|
||||||
msg.icon = number->traits.get_icon();
|
msg.icon = number->get_icon();
|
||||||
msg.disabled_by_default = number->is_disabled_by_default();
|
msg.disabled_by_default = number->is_disabled_by_default();
|
||||||
|
|
||||||
msg.min_value = number->traits.get_min_value();
|
msg.min_value = number->traits.get_min_value();
|
||||||
|
@ -637,7 +657,7 @@ bool APIConnection::send_select_info(select::Select *select) {
|
||||||
msg.object_id = select->get_object_id();
|
msg.object_id = select->get_object_id();
|
||||||
msg.name = select->get_name();
|
msg.name = select->get_name();
|
||||||
msg.unique_id = get_default_unique_id("select", select);
|
msg.unique_id = get_default_unique_id("select", select);
|
||||||
msg.icon = select->traits.get_icon();
|
msg.icon = select->get_icon();
|
||||||
msg.disabled_by_default = select->is_disabled_by_default();
|
msg.disabled_by_default = select->is_disabled_by_default();
|
||||||
|
|
||||||
for (const auto &option : select->traits.get_options())
|
for (const auto &option : select->traits.get_options())
|
||||||
|
@ -662,7 +682,7 @@ void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage>
|
||||||
return;
|
return;
|
||||||
if (this->image_reader_.available())
|
if (this->image_reader_.available())
|
||||||
return;
|
return;
|
||||||
this->image_reader_.set_image(image);
|
this->image_reader_.set_image(std::move(image));
|
||||||
}
|
}
|
||||||
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
||||||
ListEntitiesCameraResponse msg;
|
ListEntitiesCameraResponse msg;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
|
@ -75,8 +76,8 @@ class APIFrameHelper {
|
||||||
class APINoiseFrameHelper : public APIFrameHelper {
|
class APINoiseFrameHelper : public APIFrameHelper {
|
||||||
public:
|
public:
|
||||||
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
|
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
|
||||||
: socket_(std::move(socket)), ctx_(ctx) {}
|
: socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {}
|
||||||
~APINoiseFrameHelper();
|
~APINoiseFrameHelper() override;
|
||||||
APIError init() override;
|
APIError init() override;
|
||||||
APIError loop() override;
|
APIError loop() override;
|
||||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||||
|
@ -136,7 +137,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
||||||
class APIPlaintextFrameHelper : public APIFrameHelper {
|
class APIPlaintextFrameHelper : public APIFrameHelper {
|
||||||
public:
|
public:
|
||||||
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
|
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
|
||||||
~APIPlaintextFrameHelper() = default;
|
~APIPlaintextFrameHelper() override = default;
|
||||||
APIError init() override;
|
APIError init() override;
|
||||||
APIError loop() override;
|
APIError loop() override;
|
||||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||||
|
|
|
@ -11,7 +11,7 @@ using psk_t = std::array<uint8_t, 32>;
|
||||||
|
|
||||||
class APINoiseContext {
|
class APINoiseContext {
|
||||||
public:
|
public:
|
||||||
void set_psk(psk_t psk) { psk_ = std::move(psk); }
|
void set_psk(psk_t psk) { psk_ = psk; }
|
||||||
const psk_t &get_psk() const { return psk_; }
|
const psk_t &get_psk() const { return psk_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -531,6 +531,10 @@ bool ListEntitiesBinarySensorResponse::decode_length(uint32_t field_id, ProtoLen
|
||||||
this->device_class = value.as_string();
|
this->device_class = value.as_string();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 8: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -553,6 +557,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_string(5, this->device_class);
|
buffer.encode_string(5, this->device_class);
|
||||||
buffer.encode_bool(6, this->is_status_binary_sensor);
|
buffer.encode_bool(6, this->is_status_binary_sensor);
|
||||||
buffer.encode_bool(7, this->disabled_by_default);
|
buffer.encode_bool(7, this->disabled_by_default);
|
||||||
|
buffer.encode_string(8, this->icon);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
|
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
|
||||||
|
@ -586,6 +591,10 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
|
||||||
out.append(" disabled_by_default: ");
|
out.append(" disabled_by_default: ");
|
||||||
out.append(YESNO(this->disabled_by_default));
|
out.append(YESNO(this->disabled_by_default));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -677,6 +686,10 @@ bool ListEntitiesCoverResponse::decode_length(uint32_t field_id, ProtoLengthDeli
|
||||||
this->device_class = value.as_string();
|
this->device_class = value.as_string();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 10: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -701,6 +714,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_bool(7, this->supports_tilt);
|
buffer.encode_bool(7, this->supports_tilt);
|
||||||
buffer.encode_string(8, this->device_class);
|
buffer.encode_string(8, this->device_class);
|
||||||
buffer.encode_bool(9, this->disabled_by_default);
|
buffer.encode_bool(9, this->disabled_by_default);
|
||||||
|
buffer.encode_string(10, this->icon);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||||
|
@ -742,6 +756,10 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||||
out.append(" disabled_by_default: ");
|
out.append(" disabled_by_default: ");
|
||||||
out.append(YESNO(this->disabled_by_default));
|
out.append(YESNO(this->disabled_by_default));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -948,6 +966,10 @@ bool ListEntitiesFanResponse::decode_length(uint32_t field_id, ProtoLengthDelimi
|
||||||
this->unique_id = value.as_string();
|
this->unique_id = value.as_string();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 10: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -972,6 +994,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_bool(7, this->supports_direction);
|
buffer.encode_bool(7, this->supports_direction);
|
||||||
buffer.encode_int32(8, this->supported_speed_count);
|
buffer.encode_int32(8, this->supported_speed_count);
|
||||||
buffer.encode_bool(9, this->disabled_by_default);
|
buffer.encode_bool(9, this->disabled_by_default);
|
||||||
|
buffer.encode_string(10, this->icon);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||||
|
@ -1014,6 +1037,10 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||||
out.append(" disabled_by_default: ");
|
out.append(" disabled_by_default: ");
|
||||||
out.append(YESNO(this->disabled_by_default));
|
out.append(YESNO(this->disabled_by_default));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1262,6 +1289,10 @@ bool ListEntitiesLightResponse::decode_length(uint32_t field_id, ProtoLengthDeli
|
||||||
this->effects.push_back(value.as_string());
|
this->effects.push_back(value.as_string());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 14: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1302,6 +1333,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_string(11, it, true);
|
buffer.encode_string(11, it, true);
|
||||||
}
|
}
|
||||||
buffer.encode_bool(13, this->disabled_by_default);
|
buffer.encode_bool(13, this->disabled_by_default);
|
||||||
|
buffer.encode_string(14, this->icon);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
||||||
|
@ -1365,6 +1397,10 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
||||||
out.append(" disabled_by_default: ");
|
out.append(" disabled_by_default: ");
|
||||||
out.append(YESNO(this->disabled_by_default));
|
out.append(YESNO(this->disabled_by_default));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -3072,6 +3108,10 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
|
||||||
this->supported_custom_presets.push_back(value.as_string());
|
this->supported_custom_presets.push_back(value.as_string());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 19: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -3129,6 +3169,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_string(17, it, true);
|
buffer.encode_string(17, it, true);
|
||||||
}
|
}
|
||||||
buffer.encode_bool(18, this->disabled_by_default);
|
buffer.encode_bool(18, this->disabled_by_default);
|
||||||
|
buffer.encode_string(19, this->icon);
|
||||||
}
|
}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||||
|
@ -3221,6 +3262,10 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||||
out.append(" disabled_by_default: ");
|
out.append(" disabled_by_default: ");
|
||||||
out.append(YESNO(this->disabled_by_default));
|
out.append(YESNO(this->disabled_by_default));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -269,6 +269,7 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
bool is_status_binary_sensor{false};
|
bool is_status_binary_sensor{false};
|
||||||
bool disabled_by_default{false};
|
bool disabled_by_default{false};
|
||||||
|
std::string icon{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
@ -304,6 +305,7 @@ class ListEntitiesCoverResponse : public ProtoMessage {
|
||||||
bool supports_tilt{false};
|
bool supports_tilt{false};
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
bool disabled_by_default{false};
|
bool disabled_by_default{false};
|
||||||
|
std::string icon{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
@ -360,6 +362,7 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
||||||
bool supports_direction{false};
|
bool supports_direction{false};
|
||||||
int32_t supported_speed_count{0};
|
int32_t supported_speed_count{0};
|
||||||
bool disabled_by_default{false};
|
bool disabled_by_default{false};
|
||||||
|
std::string icon{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
@ -424,6 +427,7 @@ class ListEntitiesLightResponse : public ProtoMessage {
|
||||||
float max_mireds{0.0f};
|
float max_mireds{0.0f};
|
||||||
std::vector<std::string> effects{};
|
std::vector<std::string> effects{};
|
||||||
bool disabled_by_default{false};
|
bool disabled_by_default{false};
|
||||||
|
std::string icon{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
@ -856,6 +860,7 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
||||||
std::vector<enums::ClimatePreset> supported_presets{};
|
std::vector<enums::ClimatePreset> supported_presets{};
|
||||||
std::vector<std::string> supported_custom_presets{};
|
std::vector<std::string> supported_custom_presets{};
|
||||||
bool disabled_by_default{false};
|
bool disabled_by_default{false};
|
||||||
|
std::string icon{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/util.h"
|
#include "esphome/core/util.h"
|
||||||
#include "esphome/core/version.h"
|
#include "esphome/core/version.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/components/network/util.h"
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
#ifdef USE_LOGGER
|
#ifdef USE_LOGGER
|
||||||
|
@ -64,7 +66,7 @@ void APIServer::setup() {
|
||||||
#ifdef USE_LOGGER
|
#ifdef USE_LOGGER
|
||||||
if (logger::global_logger != nullptr) {
|
if (logger::global_logger != nullptr) {
|
||||||
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
||||||
for (auto *c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
if (!c->remove_)
|
if (!c->remove_)
|
||||||
c->send_log_message(level, tag, message);
|
c->send_log_message(level, tag, message);
|
||||||
}
|
}
|
||||||
|
@ -76,11 +78,12 @@ void APIServer::setup() {
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
if (esp32_camera::global_esp32_camera != nullptr) {
|
if (esp32_camera::global_esp32_camera != nullptr) {
|
||||||
esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) {
|
esp32_camera::global_esp32_camera->add_image_callback(
|
||||||
for (auto *c : this->clients_)
|
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
|
||||||
if (!c->remove_)
|
for (auto &c : this->clients_)
|
||||||
c->send_camera_state(image);
|
if (!c->remove_)
|
||||||
});
|
c->send_camera_state(image);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -95,25 +98,21 @@ void APIServer::loop() {
|
||||||
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
|
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
|
||||||
|
|
||||||
auto *conn = new APIConnection(std::move(sock), this);
|
auto *conn = new APIConnection(std::move(sock), this);
|
||||||
clients_.push_back(conn);
|
clients_.emplace_back(conn);
|
||||||
conn->start();
|
conn->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Partition clients into remove and active
|
// Partition clients into remove and active
|
||||||
auto new_end =
|
auto new_end = std::partition(this->clients_.begin(), this->clients_.end(),
|
||||||
std::partition(this->clients_.begin(), this->clients_.end(), [](APIConnection *conn) { return !conn->remove_; });
|
[](const std::unique_ptr<APIConnection> &conn) { return !conn->remove_; });
|
||||||
// print disconnection messages
|
// print disconnection messages
|
||||||
for (auto it = new_end; it != this->clients_.end(); ++it) {
|
for (auto it = new_end; it != this->clients_.end(); ++it) {
|
||||||
ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str());
|
ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str());
|
||||||
}
|
}
|
||||||
// only then delete the pointers, otherwise log routine
|
|
||||||
// would access freed memory
|
|
||||||
for (auto it = new_end; it != this->clients_.end(); ++it)
|
|
||||||
delete *it;
|
|
||||||
// resize vector
|
// resize vector
|
||||||
this->clients_.erase(new_end, this->clients_.end());
|
this->clients_.erase(new_end, this->clients_.end());
|
||||||
|
|
||||||
for (auto *client : this->clients_) {
|
for (auto &client : this->clients_) {
|
||||||
client->loop();
|
client->loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +132,7 @@ void APIServer::loop() {
|
||||||
}
|
}
|
||||||
void APIServer::dump_config() {
|
void APIServer::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "API Server:");
|
ESP_LOGCONFIG(TAG, "API Server:");
|
||||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_);
|
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
ESP_LOGCONFIG(TAG, " Using noise encryption: YES");
|
ESP_LOGCONFIG(TAG, " Using noise encryption: YES");
|
||||||
#else
|
#else
|
||||||
|
@ -174,7 +173,7 @@ void APIServer::handle_disconnect(APIConnection *conn) {}
|
||||||
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
|
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_binary_sensor_state(obj, state);
|
c->send_binary_sensor_state(obj, state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -183,7 +182,7 @@ void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s
|
||||||
void APIServer::on_cover_update(cover::Cover *obj) {
|
void APIServer::on_cover_update(cover::Cover *obj) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_cover_state(obj);
|
c->send_cover_state(obj);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -192,7 +191,7 @@ void APIServer::on_cover_update(cover::Cover *obj) {
|
||||||
void APIServer::on_fan_update(fan::FanState *obj) {
|
void APIServer::on_fan_update(fan::FanState *obj) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_fan_state(obj);
|
c->send_fan_state(obj);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -201,7 +200,7 @@ void APIServer::on_fan_update(fan::FanState *obj) {
|
||||||
void APIServer::on_light_update(light::LightState *obj) {
|
void APIServer::on_light_update(light::LightState *obj) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_light_state(obj);
|
c->send_light_state(obj);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -210,7 +209,7 @@ void APIServer::on_light_update(light::LightState *obj) {
|
||||||
void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_sensor_state(obj, state);
|
c->send_sensor_state(obj, state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -219,7 +218,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
||||||
void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_switch_state(obj, state);
|
c->send_switch_state(obj, state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -228,7 +227,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_text_sensor_state(obj, state);
|
c->send_text_sensor_state(obj, state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -237,7 +236,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s
|
||||||
void APIServer::on_climate_update(climate::Climate *obj) {
|
void APIServer::on_climate_update(climate::Climate *obj) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_climate_state(obj);
|
c->send_climate_state(obj);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -246,7 +245,7 @@ void APIServer::on_climate_update(climate::Climate *obj) {
|
||||||
void APIServer::on_number_update(number::Number *obj, float state) {
|
void APIServer::on_number_update(number::Number *obj, float state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_number_state(obj, state);
|
c->send_number_state(obj, state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -255,7 +254,7 @@ void APIServer::on_number_update(number::Number *obj, float state) {
|
||||||
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
|
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_select_state(obj, state);
|
c->send_select_state(obj, state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -266,7 +265,7 @@ APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-c
|
||||||
|
|
||||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
||||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
for (auto *client : this->clients_) {
|
for (auto &client : this->clients_) {
|
||||||
client->send_homeassistant_service_call(call);
|
client->send_homeassistant_service_call(call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,7 +285,7 @@ uint16_t APIServer::get_port() const { return this->port_; }
|
||||||
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
|
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
|
||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
void APIServer::request_time() {
|
void APIServer::request_time() {
|
||||||
for (auto *client : this->clients_) {
|
for (auto &client : this->clients_) {
|
||||||
if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED)
|
if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED)
|
||||||
client->send_time_request();
|
client->send_time_request();
|
||||||
}
|
}
|
||||||
|
@ -294,7 +293,7 @@ void APIServer::request_time() {
|
||||||
#endif
|
#endif
|
||||||
bool APIServer::is_connected() const { return !this->clients_.empty(); }
|
bool APIServer::is_connected() const { return !this->clients_.empty(); }
|
||||||
void APIServer::on_shutdown() {
|
void APIServer::on_shutdown() {
|
||||||
for (auto *c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
c->send_disconnect_request(DisconnectRequest());
|
c->send_disconnect_request(DisconnectRequest());
|
||||||
}
|
}
|
||||||
delay(10);
|
delay(10);
|
||||||
|
|
|
@ -32,7 +32,7 @@ class APIServer : public Component, public Controller {
|
||||||
void set_reboot_timeout(uint32_t reboot_timeout);
|
void set_reboot_timeout(uint32_t reboot_timeout);
|
||||||
|
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(std::move(psk)); }
|
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
|
||||||
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
|
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
|
||||||
#endif // USE_API_NOISE
|
#endif // USE_API_NOISE
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ class APIServer : public Component, public Controller {
|
||||||
uint16_t port_{6053};
|
uint16_t port_{6053};
|
||||||
uint32_t reboot_timeout_{300000};
|
uint32_t reboot_timeout_{300000};
|
||||||
uint32_t last_connected_{0};
|
uint32_t last_connected_{0};
|
||||||
std::vector<APIConnection *> clients_;
|
std::vector<std::unique_ptr<APIConnection>> clients_;
|
||||||
std::string password_;
|
std::string password_;
|
||||||
std::vector<HomeAssistantStateSubscription> state_subs_;
|
std::vector<HomeAssistantStateSubscription> state_subs_;
|
||||||
std::vector<UserServiceDescriptor *> user_services_;
|
std::vector<UserServiceDescriptor *> user_services_;
|
||||||
|
|
|
@ -49,7 +49,7 @@ class CustomAPIDevice {
|
||||||
template<typename T, typename... Ts>
|
template<typename T, typename... Ts>
|
||||||
void register_service(void (T::*callback)(Ts...), const std::string &name,
|
void register_service(void (T::*callback)(Ts...), const std::string &name,
|
||||||
const std::array<std::string, sizeof...(Ts)> &arg_names) {
|
const std::array<std::string, sizeof...(Ts)> &arg_names) {
|
||||||
auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback);
|
auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback); // NOLINT
|
||||||
global_api_server->register_user_service(service);
|
global_api_server->register_user_service(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ class CustomAPIDevice {
|
||||||
* @param name The name of the arguments for the service, must match the arguments of the function.
|
* @param name The name of the arguments for the service, must match the arguments of the function.
|
||||||
*/
|
*/
|
||||||
template<typename T> void register_service(void (T::*callback)(), const std::string &name) {
|
template<typename T> void register_service(void (T::*callback)(), const std::string &name) {
|
||||||
auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback);
|
auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback); // NOLINT
|
||||||
global_api_server->register_user_service(service);
|
global_api_server->register_user_service(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -246,6 +246,7 @@ class ProtoWriteBuffer {
|
||||||
|
|
||||||
class ProtoMessage {
|
class ProtoMessage {
|
||||||
public:
|
public:
|
||||||
|
virtual ~ProtoMessage() = default;
|
||||||
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
||||||
void decode(const uint8_t *buffer, size_t length);
|
void decode(const uint8_t *buffer, size_t length);
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,12 @@ void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
|
||||||
|
|
||||||
uint8_t I2CAS3935Component::read_register(uint8_t reg) {
|
uint8_t I2CAS3935Component::read_register(uint8_t reg) {
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
if (!this->read_byte(reg, &value, 2)) {
|
if (write(®, 1) != i2c::ERROR_OK) {
|
||||||
ESP_LOGW(TAG, "Read failed!");
|
ESP_LOGW(TAG, "Writing register failed!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (read(&value, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGW(TAG, "Reading register failed!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
# Dummy integration to allow relying on AsyncTCP
|
# Dummy integration to allow relying on AsyncTCP
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
|
||||||
CODEOWNERS = ["@OttoWinter"]
|
CODEOWNERS = ["@OttoWinter"]
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
cv.Schema({}),
|
||||||
|
cv.only_with_arduino,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(200.0)
|
@coroutine_with_priority(200.0)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
@ -12,4 +18,4 @@ async def to_code(config):
|
||||||
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
|
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
|
||||||
elif CORE.is_esp8266:
|
elif CORE.is_esp8266:
|
||||||
# https://github.com/OttoWinter/ESPAsyncTCP
|
# https://github.com/OttoWinter/ESPAsyncTCP
|
||||||
cg.add_library("ESPAsyncTCP-esphome", "1.2.3")
|
cg.add_library("ottowinter/ESPAsyncTCP-esphome", "1.2.3")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "atc_mithermometer.h"
|
#include "atc_mithermometer.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace atc_mithermometer {
|
namespace atc_mithermometer {
|
||||||
|
@ -25,14 +25,14 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
for (auto &service_data : device.get_service_datas()) {
|
for (auto &service_data : device.get_service_datas()) {
|
||||||
auto res = parse_header(service_data);
|
auto res = parse_header_(service_data);
|
||||||
if (res->is_duplicate) {
|
if (!res.has_value()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(parse_message(service_data.data, *res))) {
|
if (!(parse_message_(service_data.data, *res))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(report_results(res, device.address_str()))) {
|
if (!(report_results_(res, device.address_str()))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (res->temperature.has_value() && this->temperature_ != nullptr)
|
if (res->temperature.has_value() && this->temperature_ != nullptr)
|
||||||
|
@ -46,14 +46,10 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
return success;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) {
|
optional<ParseResult> ATCMiThermometer::parse_header_(const esp32_ble_tracker::ServiceData &service_data) {
|
||||||
ParseResult result;
|
ParseResult result;
|
||||||
if (!service_data.uuid.contains(0x1A, 0x18)) {
|
if (!service_data.uuid.contains(0x1A, 0x18)) {
|
||||||
ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
|
ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
|
||||||
|
@ -64,17 +60,15 @@ optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::Se
|
||||||
|
|
||||||
static uint8_t last_frame_count = 0;
|
static uint8_t last_frame_count = 0;
|
||||||
if (last_frame_count == raw[12]) {
|
if (last_frame_count == raw[12]) {
|
||||||
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%d).", static_cast<int>(last_frame_count));
|
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%hhu).", last_frame_count);
|
||||||
result.is_duplicate = true;
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
last_frame_count = raw[12];
|
last_frame_count = raw[12];
|
||||||
result.is_duplicate = false;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseResult &result) {
|
bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, ParseResult &result) {
|
||||||
// Byte 0-5 mac in correct order
|
// Byte 0-5 mac in correct order
|
||||||
// Byte 6-7 Temperature in uint16
|
// Byte 6-7 Temperature in uint16
|
||||||
// Byte 8 Humidity in percent
|
// Byte 8 Humidity in percent
|
||||||
|
@ -107,7 +101,7 @@ bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseR
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ATCMiThermometer::report_results(const optional<ParseResult> &result, const std::string &address) {
|
bool ATCMiThermometer::report_results_(const optional<ParseResult> &result, const std::string &address) {
|
||||||
if (!result.has_value()) {
|
if (!result.has_value()) {
|
||||||
ESP_LOGVV(TAG, "report_results(): no results available.");
|
ESP_LOGVV(TAG, "report_results(): no results available.");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace atc_mithermometer {
|
namespace atc_mithermometer {
|
||||||
|
@ -14,7 +14,6 @@ struct ParseResult {
|
||||||
optional<float> humidity;
|
optional<float> humidity;
|
||||||
optional<float> battery_level;
|
optional<float> battery_level;
|
||||||
optional<float> battery_voltage;
|
optional<float> battery_voltage;
|
||||||
bool is_duplicate;
|
|
||||||
int raw_offset;
|
int raw_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,9 +36,9 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
|
||||||
sensor::Sensor *battery_level_{nullptr};
|
sensor::Sensor *battery_level_{nullptr};
|
||||||
sensor::Sensor *battery_voltage_{nullptr};
|
sensor::Sensor *battery_voltage_{nullptr};
|
||||||
|
|
||||||
optional<ParseResult> parse_header(const esp32_ble_tracker::ServiceData &service_data);
|
optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
|
||||||
bool parse_message(const std::vector<uint8_t> &message, ParseResult &result);
|
bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
|
||||||
bool report_results(const optional<ParseResult> &result, const std::string &address);
|
bool report_results_(const optional<ParseResult> &result, const std::string &address);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace atc_mithermometer
|
} // namespace atc_mithermometer
|
||||||
|
|
|
@ -265,27 +265,57 @@ float ATM90E32Component::get_power_factor_c_() {
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_forward_active_energy_a_() {
|
float ATM90E32Component::get_forward_active_energy_a_() {
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA);
|
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA);
|
||||||
return (float) val * 10 / 3200; // convert register value to WattHours
|
if ((UINT32_MAX - this->phase_[0].cumulative_forward_active_energy_) > val) {
|
||||||
|
this->phase_[0].cumulative_forward_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->phase_[0].cumulative_forward_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return ((float) this->phase_[0].cumulative_forward_active_energy_ * 10 / 3200);
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_forward_active_energy_b_() {
|
float ATM90E32Component::get_forward_active_energy_b_() {
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB);
|
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB);
|
||||||
return (float) val * 10 / 3200;
|
if (UINT32_MAX - this->phase_[1].cumulative_forward_active_energy_ > val) {
|
||||||
|
this->phase_[1].cumulative_forward_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->phase_[1].cumulative_forward_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return ((float) this->phase_[1].cumulative_forward_active_energy_ * 10 / 3200);
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_forward_active_energy_c_() {
|
float ATM90E32Component::get_forward_active_energy_c_() {
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC);
|
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC);
|
||||||
return (float) val * 10 / 3200;
|
if (UINT32_MAX - this->phase_[2].cumulative_forward_active_energy_ > val) {
|
||||||
|
this->phase_[2].cumulative_forward_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->phase_[2].cumulative_forward_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return ((float) this->phase_[2].cumulative_forward_active_energy_ * 10 / 3200);
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_reverse_active_energy_a_() {
|
float ATM90E32Component::get_reverse_active_energy_a_() {
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA);
|
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA);
|
||||||
return (float) val * 10 / 3200;
|
if (UINT32_MAX - this->phase_[0].cumulative_reverse_active_energy_ > val) {
|
||||||
|
this->phase_[0].cumulative_reverse_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->phase_[0].cumulative_reverse_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return ((float) this->phase_[0].cumulative_reverse_active_energy_ * 10 / 3200);
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_reverse_active_energy_b_() {
|
float ATM90E32Component::get_reverse_active_energy_b_() {
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB);
|
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB);
|
||||||
return (float) val * 10 / 3200;
|
if (UINT32_MAX - this->phase_[1].cumulative_reverse_active_energy_ > val) {
|
||||||
|
this->phase_[1].cumulative_reverse_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->phase_[1].cumulative_reverse_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return ((float) this->phase_[1].cumulative_reverse_active_energy_ * 10 / 3200);
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_reverse_active_energy_c_() {
|
float ATM90E32Component::get_reverse_active_energy_c_() {
|
||||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC);
|
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC);
|
||||||
return (float) val * 10 / 3200;
|
if (UINT32_MAX - this->phase_[2].cumulative_reverse_active_energy_ > val) {
|
||||||
|
this->phase_[2].cumulative_reverse_active_energy_ += val;
|
||||||
|
} else {
|
||||||
|
this->phase_[2].cumulative_reverse_active_energy_ = val;
|
||||||
|
}
|
||||||
|
return ((float) this->phase_[2].cumulative_reverse_active_energy_ * 10 / 3200);
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_frequency_() {
|
float ATM90E32Component::get_frequency_() {
|
||||||
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
||||||
|
|
|
@ -77,6 +77,8 @@ class ATM90E32Component : public PollingComponent,
|
||||||
sensor::Sensor *power_factor_sensor_{nullptr};
|
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||||
sensor::Sensor *forward_active_energy_sensor_{nullptr};
|
sensor::Sensor *forward_active_energy_sensor_{nullptr};
|
||||||
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
|
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
|
||||||
|
uint32_t cumulative_forward_active_energy_{0};
|
||||||
|
uint32_t cumulative_reverse_active_energy_{0};
|
||||||
} phase_[3];
|
} phase_[3];
|
||||||
sensor::Sensor *freq_sensor_{nullptr};
|
sensor::Sensor *freq_sensor_{nullptr};
|
||||||
sensor::Sensor *chip_temperature_sensor_{nullptr};
|
sensor::Sensor *chip_temperature_sensor_{nullptr};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "b_parasite.h"
|
#include "b_parasite.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace b_parasite {
|
namespace b_parasite {
|
||||||
|
@ -14,6 +14,7 @@ void BParasite::dump_config() {
|
||||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||||
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
|
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
|
||||||
|
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
|
@ -36,6 +37,15 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
|
|
||||||
const auto &data = service_data.data;
|
const auto &data = service_data.data;
|
||||||
|
|
||||||
|
const uint8_t protocol_version = data[0] >> 4;
|
||||||
|
if (protocol_version != 1) {
|
||||||
|
ESP_LOGE(TAG, "Unsupported protocol version: %u", protocol_version);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some b-parasite versions have an (optional) illuminance sensor.
|
||||||
|
bool has_illuminance = data[0] & 0x1;
|
||||||
|
|
||||||
// Counter for deduplicating messages.
|
// Counter for deduplicating messages.
|
||||||
uint8_t counter = data[1] & 0x0f;
|
uint8_t counter = data[1] & 0x0f;
|
||||||
if (last_processed_counter_ == counter) {
|
if (last_processed_counter_ == counter) {
|
||||||
|
@ -59,6 +69,9 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
uint16_t soil_moisture = data[8] << 8 | data[9];
|
uint16_t soil_moisture = data[8] << 8 | data[9];
|
||||||
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
|
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
|
||||||
|
|
||||||
|
// Ambient light in lux.
|
||||||
|
float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f;
|
||||||
|
|
||||||
if (battery_voltage_ != nullptr) {
|
if (battery_voltage_ != nullptr) {
|
||||||
battery_voltage_->publish_state(battery_voltage);
|
battery_voltage_->publish_state(battery_voltage);
|
||||||
}
|
}
|
||||||
|
@ -71,6 +84,13 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
if (soil_moisture_ != nullptr) {
|
if (soil_moisture_ != nullptr) {
|
||||||
soil_moisture_->publish_state(moisture_percent);
|
soil_moisture_->publish_state(moisture_percent);
|
||||||
}
|
}
|
||||||
|
if (illuminance_ != nullptr) {
|
||||||
|
if (has_illuminance) {
|
||||||
|
illuminance_->publish_state(illuminance);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "No lux information is present in the BLE packet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
last_processed_counter_ = counter;
|
last_processed_counter_ = counter;
|
||||||
return true;
|
return true;
|
||||||
|
@ -79,4 +99,4 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
} // namespace b_parasite
|
} // namespace b_parasite
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // USE_ESP32
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace b_parasite {
|
namespace b_parasite {
|
||||||
|
@ -22,6 +22,7 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene
|
||||||
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
|
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
|
||||||
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
|
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
|
||||||
void set_soil_moisture(sensor::Sensor *soil_moisture) { soil_moisture_ = soil_moisture; }
|
void set_soil_moisture(sensor::Sensor *soil_moisture) { soil_moisture_ = soil_moisture; }
|
||||||
|
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// The received advertisement packet contains an unsigned 4 bits wrap-around counter
|
// The received advertisement packet contains an unsigned 4 bits wrap-around counter
|
||||||
|
@ -32,9 +33,10 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene
|
||||||
sensor::Sensor *temperature_{nullptr};
|
sensor::Sensor *temperature_{nullptr};
|
||||||
sensor::Sensor *humidity_{nullptr};
|
sensor::Sensor *humidity_{nullptr};
|
||||||
sensor::Sensor *soil_moisture_{nullptr};
|
sensor::Sensor *soil_moisture_{nullptr};
|
||||||
|
sensor::Sensor *illuminance_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace b_parasite
|
} // namespace b_parasite
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // USE_ESP32
|
||||||
|
|
|
@ -5,14 +5,17 @@ from esphome.const import (
|
||||||
CONF_BATTERY_VOLTAGE,
|
CONF_BATTERY_VOLTAGE,
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_ILLUMINANCE,
|
||||||
CONF_MOISTURE,
|
CONF_MOISTURE,
|
||||||
CONF_MAC_ADDRESS,
|
CONF_MAC_ADDRESS,
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
|
UNIT_LUX,
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
)
|
)
|
||||||
|
@ -55,6 +58,12 @@ CONFIG_SCHEMA = (
|
||||||
device_class=DEVICE_CLASS_HUMIDITY,
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_LUX,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||||
|
@ -74,6 +83,7 @@ async def to_code(config):
|
||||||
(CONF_HUMIDITY, var.set_humidity),
|
(CONF_HUMIDITY, var.set_humidity),
|
||||||
(CONF_BATTERY_VOLTAGE, var.set_battery_voltage),
|
(CONF_BATTERY_VOLTAGE, var.set_battery_voltage),
|
||||||
(CONF_MOISTURE, var.set_soil_moisture),
|
(CONF_MOISTURE, var.set_soil_moisture),
|
||||||
|
(CONF_ILLUMINANCE, var.set_illuminance),
|
||||||
]:
|
]:
|
||||||
if config_key in config:
|
if config_key in config:
|
||||||
sens = await sensor.new_sensor(config[config_key])
|
sens = await sensor.new_sensor(config[config_key])
|
||||||
|
|
|
@ -69,7 +69,8 @@ void BangBangClimate::compute_state_() {
|
||||||
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
|
if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature_low) ||
|
||||||
|
std::isnan(this->target_temperature_high)) {
|
||||||
// if any control parameters are nan, go to OFF action (not IDLE!)
|
// if any control parameters are nan, go to OFF action (not IDLE!)
|
||||||
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -71,10 +71,11 @@ void BH1750Sensor::update() {
|
||||||
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
void BH1750Sensor::read_data_() {
|
void BH1750Sensor::read_data_() {
|
||||||
uint16_t raw_value;
|
uint16_t raw_value;
|
||||||
if (!this->parent_->raw_receive_16(this->address_, &raw_value, 1)) {
|
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
raw_value = i2c::i2ctohs(raw_value);
|
||||||
|
|
||||||
float lx = float(raw_value) / 1.2f;
|
float lx = float(raw_value) / 1.2f;
|
||||||
lx *= 69.0f / this->measurement_duration_;
|
lx *= 69.0f / this->measurement_duration_;
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome.cpp_helpers import setup_entity
|
||||||
from esphome import automation, core
|
from esphome import automation, core
|
||||||
from esphome.automation import Condition, maybe_simple_id
|
from esphome.automation import Condition, maybe_simple_id
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_DELAY,
|
CONF_DELAY,
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_DISABLED_BY_DEFAULT,
|
|
||||||
CONF_FILTERS,
|
CONF_FILTERS,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_INTERNAL,
|
|
||||||
CONF_INVALID_COOLDOWN,
|
CONF_INVALID_COOLDOWN,
|
||||||
CONF_INVERTED,
|
CONF_INVERTED,
|
||||||
CONF_MAX_LENGTH,
|
CONF_MAX_LENGTH,
|
||||||
|
@ -88,7 +87,7 @@ DEVICE_CLASSES = [
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
|
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
|
||||||
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable)
|
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
|
||||||
BinarySensorInitiallyOff = binary_sensor_ns.class_(
|
BinarySensorInitiallyOff = binary_sensor_ns.class_(
|
||||||
"BinarySensorInitiallyOff", BinarySensor
|
"BinarySensorInitiallyOff", BinarySensor
|
||||||
)
|
)
|
||||||
|
@ -231,17 +230,16 @@ def parse_multi_click_timing_str(value):
|
||||||
parts = value.lower().split(" ")
|
parts = value.lower().split(" ")
|
||||||
if len(parts) != 5:
|
if len(parts) != 5:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"Multi click timing grammar consists of exactly 5 words, not {}"
|
f"Multi click timing grammar consists of exactly 5 words, not {len(parts)}"
|
||||||
"".format(len(parts))
|
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
state = cv.boolean(parts[0])
|
state = cv.boolean(parts[0])
|
||||||
except cv.Invalid:
|
except cv.Invalid:
|
||||||
# pylint: disable=raise-missing-from
|
# pylint: disable=raise-missing-from
|
||||||
raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0]))
|
raise cv.Invalid(f"First word must either be ON or OFF, not {parts[0]}")
|
||||||
|
|
||||||
if parts[1] != "for":
|
if parts[1] != "for":
|
||||||
raise cv.Invalid("Second word must be 'for', got {}".format(parts[1]))
|
raise cv.Invalid(f"Second word must be 'for', got {parts[1]}")
|
||||||
|
|
||||||
if parts[2] == "at":
|
if parts[2] == "at":
|
||||||
if parts[3] == "least":
|
if parts[3] == "least":
|
||||||
|
@ -250,8 +248,7 @@ def parse_multi_click_timing_str(value):
|
||||||
key = CONF_MAX_LENGTH
|
key = CONF_MAX_LENGTH
|
||||||
else:
|
else:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"Third word after at must either be 'least' or 'most', got {}"
|
f"Third word after at must either be 'least' or 'most', got {parts[3]}"
|
||||||
"".format(parts[3])
|
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
length = cv.positive_time_period_milliseconds(parts[4])
|
length = cv.positive_time_period_milliseconds(parts[4])
|
||||||
|
@ -296,13 +293,11 @@ def validate_multi_click_timing(value):
|
||||||
new_state = v_.get(CONF_STATE, not state)
|
new_state = v_.get(CONF_STATE, not state)
|
||||||
if new_state == state:
|
if new_state == state:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"Timings must have alternating state. Indices {} and {} have "
|
f"Timings must have alternating state. Indices {i} and {i + 1} have the same state {state}"
|
||||||
"the same state {}".format(i, i + 1, state)
|
|
||||||
)
|
)
|
||||||
if max_length is not None and max_length < min_length:
|
if max_length is not None and max_length < min_length:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"Max length ({}) must be larger than min length ({})."
|
f"Max length ({max_length}) must be larger than min length ({min_length})."
|
||||||
"".format(max_length, min_length)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
state = new_state
|
state = new_state
|
||||||
|
@ -318,7 +313,7 @@ def validate_multi_click_timing(value):
|
||||||
|
|
||||||
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||||
|
|
||||||
BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(BinarySensor),
|
cv.GenerateID(): cv.declare_id(BinarySensor),
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||||
|
@ -379,10 +374,8 @@ BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).exten
|
||||||
|
|
||||||
|
|
||||||
async def setup_binary_sensor_core_(var, config):
|
async def setup_binary_sensor_core_(var, config):
|
||||||
cg.add(var.set_name(config[CONF_NAME]))
|
await setup_entity(var, config)
|
||||||
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
|
|
||||||
if CONF_INTERNAL in config:
|
|
||||||
cg.add(var.set_internal(config[CONF_INTERNAL]))
|
|
||||||
if CONF_DEVICE_CLASS in config:
|
if CONF_DEVICE_CLASS in config:
|
||||||
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
|
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
|
||||||
if CONF_INVERTED in config:
|
if CONF_INVERTED in config:
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
|
@ -42,7 +42,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::string BinarySensor::device_class() { return ""; }
|
std::string BinarySensor::device_class() { return ""; }
|
||||||
BinarySensor::BinarySensor(const std::string &name) : Nameable(name), state(false) {}
|
BinarySensor::BinarySensor(const std::string &name) : EntityBase(name), state(false) {}
|
||||||
BinarySensor::BinarySensor() : BinarySensor("") {}
|
BinarySensor::BinarySensor() : BinarySensor("") {}
|
||||||
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||||
std::string BinarySensor::get_device_class() {
|
std::string BinarySensor::get_device_class() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/entity_base.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/components/binary_sensor/filter.h"
|
#include "esphome/components/binary_sensor/filter.h"
|
||||||
|
|
||||||
|
@ -10,7 +11,7 @@ namespace binary_sensor {
|
||||||
|
|
||||||
#define LOG_BINARY_SENSOR(prefix, type, obj) \
|
#define LOG_BINARY_SENSOR(prefix, type, obj) \
|
||||||
if ((obj) != nullptr) { \
|
if ((obj) != nullptr) { \
|
||||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||||
if (!(obj)->get_device_class().empty()) { \
|
if (!(obj)->get_device_class().empty()) { \
|
||||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||||
} \
|
} \
|
||||||
|
@ -22,7 +23,7 @@ namespace binary_sensor {
|
||||||
* The sub classes should notify the front-end of new states via the publish_state() method which
|
* The sub classes should notify the front-end of new states via the publish_state() method which
|
||||||
* handles inverted inputs for you.
|
* handles inverted inputs for you.
|
||||||
*/
|
*/
|
||||||
class BinarySensor : public Nameable {
|
class BinarySensor : public EntityBase {
|
||||||
public:
|
public:
|
||||||
explicit BinarySensor();
|
explicit BinarySensor();
|
||||||
/** Construct a binary sensor with the specified name
|
/** Construct a binary sensor with the specified name
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/components/ble_client/ble_client.h"
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_client {
|
namespace ble_client {
|
||||||
|
@ -11,11 +11,12 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode {
|
||||||
public:
|
public:
|
||||||
explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
|
explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
|
||||||
void loop() override {}
|
void loop() override {}
|
||||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) override {
|
||||||
if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK)
|
if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK)
|
||||||
this->trigger();
|
this->trigger();
|
||||||
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,11 +24,12 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
|
||||||
public:
|
public:
|
||||||
explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
|
explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
|
||||||
void loop() override {}
|
void loop() override {}
|
||||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) override {
|
||||||
if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0)
|
if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0)
|
||||||
this->trigger();
|
this->trigger();
|
||||||
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
#include "ble_client.h"
|
#include "ble_client.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_client {
|
namespace ble_client {
|
||||||
|
@ -17,12 +17,12 @@ void BLEClient::setup() {
|
||||||
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
|
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
}
|
}
|
||||||
this->set_states(espbt::ClientState::Idle);
|
this->set_states_(espbt::ClientState::IDLE);
|
||||||
this->enabled = true;
|
this->enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLEClient::loop() {
|
void BLEClient::loop() {
|
||||||
if (this->state() == espbt::ClientState::Discovered) {
|
if (this->state() == espbt::ClientState::DISCOVERED) {
|
||||||
this->connect();
|
this->connect();
|
||||||
}
|
}
|
||||||
for (auto *node : this->nodes_)
|
for (auto *node : this->nodes_)
|
||||||
|
@ -39,11 +39,11 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
|
||||||
return false;
|
return false;
|
||||||
if (device.address_uint64() != this->address)
|
if (device.address_uint64() != this->address)
|
||||||
return false;
|
return false;
|
||||||
if (this->state() != espbt::ClientState::Idle)
|
if (this->state() != espbt::ClientState::IDLE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str());
|
ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str());
|
||||||
this->set_states(espbt::ClientState::Discovered);
|
this->set_states_(espbt::ClientState::DISCOVERED);
|
||||||
|
|
||||||
auto addr = device.address_uint64();
|
auto addr = device.address_uint64();
|
||||||
this->remote_bda[0] = (addr >> 40) & 0xFF;
|
this->remote_bda[0] = (addr >> 40) & 0xFF;
|
||||||
|
@ -69,7 +69,7 @@ std::string BLEClient::address_str() const {
|
||||||
void BLEClient::set_enabled(bool enabled) {
|
void BLEClient::set_enabled(bool enabled) {
|
||||||
if (enabled == this->enabled)
|
if (enabled == this->enabled)
|
||||||
return;
|
return;
|
||||||
if (!enabled && this->state() != espbt::ClientState::Idle) {
|
if (!enabled && this->state() != espbt::ClientState::IDLE) {
|
||||||
ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str());
|
ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str());
|
||||||
auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id);
|
auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -84,9 +84,9 @@ void BLEClient::connect() {
|
||||||
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true);
|
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
|
ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
|
||||||
this->set_states(espbt::ClientState::Idle);
|
this->set_states_(espbt::ClientState::IDLE);
|
||||||
} else {
|
} else {
|
||||||
this->set_states(espbt::ClientState::Connecting);
|
this->set_states_(espbt::ClientState::CONNECTING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||||
if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
|
if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool all_established = this->all_nodes_established();
|
bool all_established = this->all_nodes_established_();
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ESP_GATTC_REG_EVT: {
|
case ESP_GATTC_REG_EVT: {
|
||||||
|
@ -113,7 +113,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
|
ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
|
||||||
if (param->open.status != ESP_GATT_OK) {
|
if (param->open.status != ESP_GATT_OK) {
|
||||||
ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
|
ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
|
||||||
this->set_states(espbt::ClientState::Idle);
|
this->set_states_(espbt::ClientState::IDLE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this->conn_id = param->open.conn_id;
|
this->conn_id = param->open.conn_id;
|
||||||
|
@ -126,11 +126,11 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||||
case ESP_GATTC_CFG_MTU_EVT: {
|
case ESP_GATTC_CFG_MTU_EVT: {
|
||||||
if (param->cfg_mtu.status != ESP_GATT_OK) {
|
if (param->cfg_mtu.status != ESP_GATT_OK) {
|
||||||
ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
|
ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
|
||||||
this->set_states(espbt::ClientState::Idle);
|
this->set_states_(espbt::ClientState::IDLE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
|
ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
|
||||||
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, NULL);
|
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_DISCONNECT_EVT: {
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
|
@ -139,13 +139,13 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str());
|
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str());
|
||||||
for (auto &svc : this->services_)
|
for (auto &svc : this->services_)
|
||||||
delete svc;
|
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
this->services_.clear();
|
this->services_.clear();
|
||||||
this->set_states(espbt::ClientState::Idle);
|
this->set_states_(espbt::ClientState::IDLE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_SEARCH_RES_EVT: {
|
case ESP_GATTC_SEARCH_RES_EVT: {
|
||||||
BLEService *ble_service = new BLEService();
|
BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid);
|
ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid);
|
||||||
ble_service->start_handle = param->search_res.start_handle;
|
ble_service->start_handle = param->search_res.start_handle;
|
||||||
ble_service->end_handle = param->search_res.end_handle;
|
ble_service->end_handle = param->search_res.end_handle;
|
||||||
|
@ -160,8 +160,8 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||||
ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle);
|
ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle);
|
||||||
svc->parse_characteristics();
|
svc->parse_characteristics();
|
||||||
}
|
}
|
||||||
this->set_states(espbt::ClientState::Connected);
|
this->set_states_(espbt::ClientState::CONNECTED);
|
||||||
this->set_state(espbt::ClientState::Established);
|
this->set_state(espbt::ClientState::ESTABLISHED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
|
@ -192,9 +192,9 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||||
node->gattc_event_handler(event, esp_gattc_if, param);
|
node->gattc_event_handler(event, esp_gattc_if, param);
|
||||||
|
|
||||||
// Delete characteristics after clients have used them to save RAM.
|
// Delete characteristics after clients have used them to save RAM.
|
||||||
if (!all_established && this->all_nodes_established()) {
|
if (!all_established && this->all_nodes_established_()) {
|
||||||
for (auto &svc : this->services_)
|
for (auto &svc : this->services_)
|
||||||
delete svc;
|
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
this->services_.clear();
|
this->services_.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,7 +307,7 @@ BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_
|
||||||
|
|
||||||
BLEService::~BLEService() {
|
BLEService::~BLEService() {
|
||||||
for (auto &chr : this->characteristics)
|
for (auto &chr : this->characteristics)
|
||||||
delete chr;
|
delete chr; // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLEService::parse_characteristics() {
|
void BLEService::parse_characteristics() {
|
||||||
|
@ -329,7 +329,7 @@ void BLEService::parse_characteristics() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
BLECharacteristic *characteristic = new BLECharacteristic();
|
BLECharacteristic *characteristic = new BLECharacteristic(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
|
characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
|
||||||
characteristic->properties = result.properties;
|
characteristic->properties = result.properties;
|
||||||
characteristic->handle = result.char_handle;
|
characteristic->handle = result.char_handle;
|
||||||
|
@ -344,7 +344,7 @@ void BLEService::parse_characteristics() {
|
||||||
|
|
||||||
BLECharacteristic::~BLECharacteristic() {
|
BLECharacteristic::~BLECharacteristic() {
|
||||||
for (auto &desc : this->descriptors)
|
for (auto &desc : this->descriptors)
|
||||||
delete desc;
|
delete desc; // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLECharacteristic::parse_descriptors() {
|
void BLECharacteristic::parse_descriptors() {
|
||||||
|
@ -366,7 +366,7 @@ void BLECharacteristic::parse_descriptors() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
BLEDescriptor *desc = new BLEDescriptor();
|
BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
|
desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
|
||||||
desc->handle = result.handle;
|
desc->handle = result.handle;
|
||||||
desc->characteristic = this;
|
desc->characteristic = this;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
@ -82,10 +82,11 @@ class BLEClient : public espbt::ESPBTClient, public Component {
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
bool parse_device(const espbt::ESPBTDevice &device) override;
|
bool parse_device(const espbt::ESPBTDevice &device) override;
|
||||||
void on_scan_end() override {}
|
void on_scan_end() override {}
|
||||||
void connect();
|
void connect() override;
|
||||||
|
|
||||||
void set_address(uint64_t address) { this->address = address; }
|
void set_address(uint64_t address) { this->address = address; }
|
||||||
|
|
||||||
|
@ -116,16 +117,16 @@ class BLEClient : public espbt::ESPBTClient, public Component {
|
||||||
std::string address_str() const;
|
std::string address_str() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void set_states(espbt::ClientState st) {
|
void set_states_(espbt::ClientState st) {
|
||||||
this->set_state(st);
|
this->set_state(st);
|
||||||
for (auto &node : nodes_)
|
for (auto &node : nodes_)
|
||||||
node->node_state = st;
|
node->node_state = st;
|
||||||
}
|
}
|
||||||
bool all_nodes_established() {
|
bool all_nodes_established_() {
|
||||||
if (this->state() != espbt::ClientState::Established)
|
if (this->state() != espbt::ClientState::ESTABLISHED)
|
||||||
return false;
|
return false;
|
||||||
for (auto &node : nodes_)
|
for (auto &node : nodes_)
|
||||||
if (node->node_state != espbt::ClientState::Established)
|
if (node->node_state != espbt::ClientState::ESTABLISHED)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue