Merge remote-tracking branch 'origin/dev' into ltr390

This commit is contained in:
Otto winter 2021-08-03 19:07:39 +02:00
commit 157e2731cc
No known key found for this signature in database
GPG Key ID: 48ED2DDB96D7682C
1139 changed files with 38801 additions and 13965 deletions

View File

@ -49,7 +49,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
DerivePointerAlignment: true DerivePointerAlignment: false
DisableFormat: false DisableFormat: false
ExperimentalAutoDetectBinPacking: false ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true FixNamespaceComments: true

View File

@ -4,14 +4,24 @@ Checks: >-
-abseil-*, -abseil-*,
-android-*, -android-*,
-boost-*, -boost-*,
-bugprone-macro-parentheses, -bugprone-branch-clone,
-bugprone-narrowing-conversions,
-bugprone-signed-char-misuse,
-bugprone-too-small-loop-variable,
-cert-dcl50-cpp, -cert-dcl50-cpp,
-cert-err58-cpp, -cert-err58-cpp,
-clang-analyzer-core.CallAndMessage, -cert-oop57-cpp,
-cert-str34-c,
-clang-analyzer-optin.cplusplus.UninitializedObject,
-clang-analyzer-osx.*, -clang-analyzer-osx.*,
-clang-analyzer-security.*, -clang-diagnostic-shadow-field,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-goto,
-cppcoreguidelines-c-copy-assignment-signature, -cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory, -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,
@ -24,40 +34,51 @@ Checks: >-
-cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions, -cppcoreguidelines-special-member-functions,
-fuchsia-*,
-fuchsia-default-arguments, -fuchsia-default-arguments,
-fuchsia-multiple-inheritance, -fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator, -fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects, -fuchsia-statically-constructed-objects,
-fuchsia-default-arguments-declarations,
-fuchsia-default-arguments-calls,
-google-build-using-namespace, -google-build-using-namespace,
-google-explicit-constructor, -google-explicit-constructor,
-google-readability-braces-around-statements, -google-readability-braces-around-statements,
-google-readability-casting, -google-readability-casting,
-google-readability-todo, -google-readability-todo,
-google-runtime-int,
-google-runtime-references, -google-runtime-references,
-hicpp-*, -hicpp-*,
-llvm-else-after-return,
-llvm-header-guard, -llvm-header-guard,
-llvm-include-order, -llvm-include-order,
-misc-unconventional-assign-operator, -llvm-qualified-auto,
-llvmlibc-*,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-misc-unused-parameters, -misc-unused-parameters,
-modernize-deprecated-headers, -modernize-avoid-c-arrays,
-modernize-pass-by-value,
-modernize-pass-by-value,
-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,
-mpi-*, -mpi-*,
-objc-*, -objc-*,
-performance-unnecessary-value-param,
-readability-braces-around-statements, -readability-braces-around-statements,
-readability-const-return-type,
-readability-convert-member-functions-to-static,
-readability-else-after-return, -readability-else-after-return,
-readability-implicit-bool-conversion, -readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-make-member-function-const,
-readability-named-parameter, -readability-named-parameter,
-readability-qualified-auto,
-readability-redundant-access-specifiers,
-readability-redundant-member-init, -readability-redundant-member-init,
-warnings-as-errors, -readability-redundant-string-init,
-zircon-* -readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-warnings-as-errors
WarningsAsErrors: '*' WarningsAsErrors: '*'
HeaderFilterRegex: '^.*/src/esphome/.*' HeaderFilterRegex: '^.*/src/esphome/.*'
AnalyzeTemporaryDtors: false AnalyzeTemporaryDtors: false

View File

@ -2,16 +2,29 @@
"name": "ESPHome Dev", "name": "ESPHome Dev",
"context": "..", "context": "..",
"dockerFile": "../docker/Dockerfile.dev", "dockerFile": "../docker/Dockerfile.dev",
"postCreateCommand": "mkdir -p config && pip3 install -e .", "postCreateCommand": [
"runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"], "script/devcontainer-post-create"
],
"runArgs": [
"--privileged",
"-e",
"ESPHOME_DASHBOARD_USE_PING=1"
],
"appPort": 6052, "appPort": 6052,
"extensions": [ "extensions": [
// python
"ms-python.python", "ms-python.python",
"visualstudioexptteam.vscodeintellicode", "visualstudioexptteam.vscodeintellicode",
"redhat.vscode-yaml" // yaml
"redhat.vscode-yaml",
// cpp
"ms-vscode.cpptools",
// editorconfig
"editorconfig.editorconfig",
], ],
"settings": { "settings": {
"python.pythonPath": "/usr/local/bin/python", "python.languageServer": "Pylance",
"python.pythonPath": "/usr/bin/python3",
"python.linting.pylintEnabled": true, "python.linting.pylintEnabled": true,
"python.linting.enabled": true, "python.linting.enabled": true,
"python.formatting.provider": "black", "python.formatting.provider": "black",
@ -19,7 +32,7 @@
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.formatOnType": true, "editor.formatOnType": true,
"files.trimTrailingWhitespace": true, "files.trimTrailingWhitespace": true,
"terminal.integrated.shell.linux": "/bin/bash", "terminal.integrated.defaultProfile.linux": "bash",
"yaml.customTags": [ "yaml.customTags": [
"!secret scalar", "!secret scalar",
"!lambda scalar", "!lambda scalar",
@ -27,6 +40,18 @@
"!include_dir_list scalar", "!include_dir_list scalar",
"!include_dir_merge_list scalar", "!include_dir_merge_list scalar",
"!include_dir_merge_named scalar" "!include_dir_merge_named scalar"
] ],
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/*.pyc": {
"when": "$(basename).py"
},
"**/__pycache__": true
},
"files.associations": {
"**/.vscode/*.json": "jsonc"
},
"C_Cpp.clang_format_path": "/usr/bin/clang-format-11",
} }
} }

View File

@ -103,6 +103,10 @@ venv.bak/
# mypy # mypy
.mypy_cache/ .mypy_cache/
# PlatformIO
.pio/
# ESPHome
config/ config/
examples/ examples/
Dockerfile Dockerfile

View File

@ -7,7 +7,7 @@ insert_final_newline = true
charset = utf-8 charset = utf-8
# python # python
[*.{py}] [*.py]
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
@ -26,3 +26,9 @@ indent_size = 2
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
quote_type = single quote_type = single
# JSON
[*.json]
indent_style = space
indent_size = 2

7
.github/FUNDING.yml vendored
View File

@ -1,8 +1,3 @@
# These are supported funding model platforms # These are supported funding model platforms
github: custom: https://www.nabucasa.com
patreon: ottowinter
open_collective:
ko_fi:
tidelift:
custom: https://esphome.io/guides/supporters.html

View File

@ -1,25 +1,22 @@
# What does this implement/fix? # What does this implement/fix?
Quick description Quick description and explanation of changes
## Types of changes ## Types of changes
- [ ] Bugfix (non-breaking change which fixes an issue) - [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality) - [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Configuration change (this will require users to update their yaml configuration files to keep working) - [ ] Other
**Related issue or feature (if applicable):** fixes <link to issue> **Related issue or feature (if applicable):** fixes <link to issue>
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here> **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
# Test Environment ## Test Environment
- [ ] ESP32 - [ ] ESP32
- [ ] ESP8266 - [ ] ESP8266
- [ ] Windows
- [ ] Mac OS
- [ ] Linux
## Example entry for `config.yaml`: ## Example entry for `config.yaml`:
<!-- <!--
@ -34,11 +31,6 @@ Quick description
``` ```
# Explain your changes
Describe your changes here to communicate to the maintainers **why we should accept this pull request**.
Very important to fill if no issue linked
## Checklist: ## Checklist:
- [ ] The code change is tested and works locally. - [ ] The code change is tested and works locally.
- [ ] Tests have been added to verify that the new code works (under `tests/` folder). - [ ] Tests have been added to verify that the new code works (under `tests/` folder).

View File

@ -3,7 +3,7 @@ name: CI for docker images
# Only run when docker paths change # Only run when docker paths change
on: on:
push: push:
branches: [dev, beta, master] branches: [dev, beta, release]
paths: paths:
- 'docker/**' - 'docker/**'
- '.github/workflows/**' - '.github/workflows/**'
@ -18,38 +18,23 @@ jobs:
name: Build docker containers name: Build docker containers
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
arch: [amd64, armv7, aarch64] arch: [amd64, armv7, aarch64]
build_type: ["hassio", "docker"] build_type: ["ha-addon", "docker", "lint"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up env variables - name: Set up Python
run: | uses: actions/setup-python@v2
base_version="2.6.0" with:
python-version: '3.9'
- name: Set TAG
run: |
echo "TAG=check" >> $GITHUB_ENV
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then - name: Run build
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" run: |
build_to="esphome/esphome-hassio-${{ matrix.arch }}" docker/build.py \
dockerfile="docker/Dockerfile.hassio" --tag "${TAG}" \
else --arch "${{ matrix.arch }}" \
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" --build-type "${{ matrix.build_type }}" \
build_to="esphome/esphome-${{ matrix.arch }}" build
dockerfile="docker/Dockerfile"
fi
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
- name: Pull for cache
run: |
docker pull "${BUILD_TO}:dev" || true
- name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes
- run: |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "BUILD_VERSION=ci" \
--cache-from "${BUILD_TO}:dev" \
--file "${DOCKERFILE}" \
.

View File

@ -4,85 +4,102 @@ name: CI
on: on:
push: push:
# On dev branch release-dev already performs CI checks branches: [dev, beta, release]
# On other branches the `pull_request` trigger will be used
branches: [beta, master]
pull_request: pull_request:
jobs: jobs:
lint-clang-format: ci-with-container:
name: ${{ matrix.name }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:latest
steps:
- uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Run clang-format
run: script/clang-format -i
- name: Suggest changes
run: script/ci-suggest-changes
lint-clang-tidy:
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:latest
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
split: [1, 2, 3, 4] 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: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc) # (build flags, libraries etc)
- name: Set up platformio environment - name: Set up platformio environment
run: pio init --ide atom run: pio init --ide atom
- name: Register problem matchers - name: Register problem matchers
run: | run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
echo "::add-matcher::.github/workflows/matchers/gcc.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 - name: Run clang-tidy
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
- name: Suggest changes if: ${{ matrix.id == 'clang-tidy' }}
run: script/ci-suggest-changes
lint-python: - name: Suggested changes
run: script/ci-suggest-changes
if: always()
ci:
# Don't use the esphome-lint docker image because it may contain outdated requirements. # Don't use the esphome-lint docker image because it may contain outdated requirements.
# This way, all dependencies are cached via the cache action. # This way, all dependencies are cached via the cache action.
name: ${{ matrix.name }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- id: ci-custom
name: Run script/ci-custom
- id: lint-python
name: Run script/lint-python
- id: test
file: tests/test1.yaml
name: Test tests/test1.yaml
- id: test
file: tests/test2.yaml
name: Test tests/test2.yaml
- id: test
file: tests/test3.yaml
name: Test tests/test3.yaml
- id: test
file: tests/test4.yaml
name: Test tests/test4.yaml
- id: test
file: tests/test5.yaml
name: Test tests/test5.yaml
- id: pytest
name: Run pytest
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
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@v1
with: with:
@ -90,6 +107,17 @@ jobs:
key: esphome-pip-3.7-${{ hashFiles('setup.py') }} key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: | restore-keys: |
esphome-pip-3.7- esphome-pip-3.7-
# 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: script/setup
@ -98,81 +126,22 @@ jobs:
echo "::add-matcher::.github/workflows/matchers/ci-custom.json" echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
echo "::add-matcher::.github/workflows/matchers/lint-python.json" echo "::add-matcher::.github/workflows/matchers/lint-python.json"
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/gcc.json"
- name: Lint Custom - name: Lint Custom
run: script/ci-custom.py run: |
script/ci-custom.py
script/build_codeowners.py --check
if: ${{ matrix.id == 'ci-custom' }}
- name: Lint Python - name: Lint Python
run: script/lint-python run: script/lint-python
- name: Lint CODEOWNERS if: ${{ matrix.id == 'lint-python' }}
run: script/build_codeowners.py --check
test: - run: esphome compile ${{ matrix.file }}
runs-on: ubuntu-latest if: ${{ matrix.id == 'test' }}
strategy:
fail-fast: false
matrix:
test:
- test1
- test2
- test3
- test4
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
# 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.test }}-${{ hashFiles('esphome/core_config.py') }}
restore-keys: |
test-home-platformio-${{ matrix.test }}-
- name: Set up environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome tests/${{ matrix.test }}.yaml compile
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up environment
run: script/setup
- name: Install Github Actions annotator
run: pip install pytest-github-actions-annotate-failures
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Run pytest - name: Run pytest
run: | run: |
pytest \ pytest -vv --tb=native tests
-qq \ if: ${{ matrix.id == 'pytest' }}
--durations=10 \
-o console_output_style=count \
tests

100
.github/workflows/docker-lint-build.yml vendored Normal file
View File

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

19
.github/workflows/matchers/pytest.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"problemMatcher": [
{
"owner": "pytest",
"fileLocation": "absolute",
"pattern": [
{
"regexp": "^\\s+File \"(.*)\", line (\\d+), in (.*)$",
"file": 1,
"line": 2
},
{
"regexp": "^\\s+(.*)$",
"message": 1
}
]
}
]
}

View File

@ -1,264 +0,0 @@
name: Publish dev releases to docker hub
on:
push:
branches:
- dev
jobs:
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
lint-clang-format:
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:latest
steps:
- uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Run clang-format
run: script/clang-format -i
- name: Suggest changes
run: script/ci-suggest-changes
lint-clang-tidy:
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:latest
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy:
fail-fast: false
matrix:
split: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
echo "::add-matcher::.github/workflows/matchers/gcc.json"
- name: Run clang-tidy
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
- name: Suggest changes
run: script/ci-suggest-changes
lint-python:
# Don't use the esphome-lint docker image because it may contain outdated requirements.
# This way, all dependencies are cached via the cache action.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up python environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Lint Custom
run: script/ci-custom.py
- name: Lint Python
run: script/lint-python
- name: Lint CODEOWNERS
run: script/build_codeowners.py --check
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
test:
- test1
- test2
- test3
- test4
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
# 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.test }}-${{ hashFiles('esphome/core_config.py') }}
restore-keys: |
test-home-platformio-${{ matrix.test }}-
- name: Set up environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome tests/${{ matrix.test }}.yaml compile
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up environment
run: script/setup
- name: Install Github Actions annotator
run: pip install pytest-github-actions-annotate-failures
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Run pytest
run: |
pytest \
-qq \
--durations=10 \
-o console_output_style=count \
tests
deploy-docker:
name: Build and publish docker containers
if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
strategy:
matrix:
arch: [amd64, armv7, aarch64]
# Hassio dev image doesn't use esphome/esphome-hassio-$arch and uses base directly
build_type: ["docker"]
steps:
- uses: actions/checkout@v2
- name: Set TAG
run: |
TAG="${GITHUB_SHA:0:7}"
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables
run: |
base_version="2.6.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
dockerfile="docker/Dockerfile.hassio"
else
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
build_to="esphome/esphome-${{ matrix.arch }}"
dockerfile="docker/Dockerfile"
fi
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
- name: Pull for cache
run: |
docker pull "${BUILD_TO}:dev" || true
- name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes
- run: |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "BUILD_VERSION=${TAG}" \
--tag "${BUILD_TO}:${TAG}" \
--tag "${BUILD_TO}:dev" \
--cache-from "${BUILD_TO}:dev" \
--file "${DOCKERFILE}" \
.
- name: Log in to docker hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- run: |
docker push "${BUILD_TO}:${TAG}"
docker push "${BUILD_TO}:dev"
deploy-docker-manifest:
if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest
needs: [deploy-docker]
steps:
- name: Enable experimental manifest support
run: |
mkdir -p ~/.docker
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
- name: Set TAG
run: |
TAG="${GITHUB_SHA:0:7}"
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Log in to docker hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- name: "Create the manifest"
run: |
docker manifest create esphome/esphome:${TAG} \
esphome/esphome-aarch64:${TAG} \
esphome/esphome-amd64:${TAG} \
esphome/esphome-armv7:${TAG}
docker manifest push esphome/esphome:${TAG}
docker manifest create esphome/esphome:dev \
esphome/esphome-aarch64:${TAG} \
esphome/esphome-amd64:${TAG} \
esphome/esphome-armv7:${TAG}
docker manifest push esphome/esphome:dev

View File

@ -1,181 +1,35 @@
name: Publish Release name: Publish Release
on: on:
workflow_dispatch:
release: release:
types: [published] types: [published]
schedule:
- cron: "0 2 * * *"
jobs: jobs:
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml init:
name: Initialize build
lint-clang-format:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-* outputs:
# doesn't have to be installed tag: ${{ steps.tag.outputs.tag }}
container: esphome/esphome-lint:latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc) - name: Get tag
# Note: platformio platform versions should be cached via the esphome-lint image id: tag
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Run clang-format
run: script/clang-format -i
- name: Suggest changes
run: script/ci-suggest-changes
lint-clang-tidy:
runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed
container: esphome/esphome-lint:latest
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy:
fail-fast: false
matrix:
split: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc)
- name: Set up platformio environment
run: pio init --ide atom
- name: Register problem matchers
run: | run: |
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
echo "::add-matcher::.github/workflows/matchers/gcc.json" TAG="${GITHUB_REF#refs/tags/v}"
- name: Run clang-tidy else
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
- name: Suggest changes today="$(date --utc '+%Y%m%d')"
run: script/ci-suggest-changes TAG="${TAG}${today}"
fi
lint-python: echo "::set-output name=tag::${TAG}"
# Don't use the esphome-lint docker image because it may contain outdated requirements.
# This way, all dependencies are cached via the cache action.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up python environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Lint Custom
run: script/ci-custom.py
- name: Lint Python
run: script/lint-python
- name: Lint CODEOWNERS
run: script/build_codeowners.py --check
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
test:
- test1
- test2
- test3
- test4
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
# 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.test }}-${{ hashFiles('esphome/core_config.py') }}
restore-keys: |
test-home-platformio-${{ matrix.test }}-
- name: Set up environment
run: script/setup
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome tests/${{ matrix.test }}.yaml compile
pytest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.7'
- name: Cache pip modules
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
restore-keys: |
esphome-pip-3.7-
- name: Set up environment
run: script/setup
- name: Install Github Actions annotator
run: pip install pytest-github-actions-annotate-failures
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/python.json"
- name: Run pytest
run: |
pytest \
-qq \
--durations=10 \
-o console_output_style=count \
tests
deploy-pypi: deploy-pypi:
name: Build and publish to PyPi name: Build and publish to PyPi
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome' && github.event_name == 'release'
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -199,119 +53,85 @@ jobs:
name: Build and publish docker containers name: Build and publish docker containers
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest] needs: [init]
strategy: strategy:
matrix: matrix:
arch: [amd64, armv7, aarch64] arch: [amd64, armv7, aarch64]
build_type: ["hassio", "docker"] build_type: ["ha-addon", "docker"]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set TAG - name: Set up Python
run: | uses: actions/setup-python@v2
TAG="${GITHUB_REF#refs/tags/v}" with:
echo "TAG=${TAG}" >> $GITHUB_ENV python-version: '3.9'
- name: Set up env variables
run: |
base_version="2.6.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then - name: Run build
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" run: |
build_to="esphome/esphome-hassio-${{ matrix.arch }}" docker/build.py \
dockerfile="docker/Dockerfile.hassio" --tag "${{ needs.init.outputs.tag }}" \
else --arch "${{ matrix.arch }}" \
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" --build-type "${{ matrix.build_type }}" \
build_to="esphome/esphome-${{ matrix.arch }}" build
dockerfile="docker/Dockerfile"
fi
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then - name: Log in to docker hub
cache_tag="beta" uses: docker/login-action@v1
else with:
cache_tag="latest" username: ${{ secrets.DOCKER_USER }}
fi 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 }}
# Set env variables so these values don't need to be calculated again - name: Run push
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV run: |
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV docker/build.py \
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV --tag "${{ needs.init.outputs.tag }}" \
echo "CACHE_TAG=${cache_tag}" >> $GITHUB_ENV --arch "${{ matrix.arch }}" \
- name: Pull for cache --build-type "${{ matrix.build_type }}" \
run: | push
docker pull "${BUILD_TO}:${CACHE_TAG}" || true
- name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes
- run: |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \
--build-arg "BUILD_VERSION=${TAG}" \
--tag "${BUILD_TO}:${TAG}" \
--cache-from "${BUILD_TO}:${CACHE_TAG}" \
--file "${DOCKERFILE}" \
.
- name: Log in to docker hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- run: docker push "${BUILD_TO}:${TAG}"
# Always publish to beta tag (also full releases)
- name: Publish docker beta tag
run: |
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:beta"
docker push "${BUILD_TO}:beta"
- if: ${{ !github.event.release.prerelease }}
name: Publish docker latest tag
run: |
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:latest"
docker push "${BUILD_TO}:latest"
deploy-docker-manifest: deploy-docker-manifest:
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [deploy-docker] needs: [init, deploy-docker]
strategy:
matrix:
build_type: ["ha-addon", "docker"]
steps: steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Enable experimental manifest support - name: Enable experimental manifest support
run: | run: |
mkdir -p ~/.docker mkdir -p ~/.docker
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
- name: Set TAG
run: |
TAG="${GITHUB_REF#refs/tags/v}"
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Log in to docker hub - name: Log in to docker hub
env: uses: docker/login-action@v1
DOCKER_USER: ${{ secrets.DOCKER_USER }} with:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} username: ${{ secrets.DOCKER_USER }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" password: ${{ secrets.DOCKER_PASSWORD }}
- name: "Create the manifest" - name: Log in to the GitHub container registry
run: | uses: docker/login-action@v1
docker manifest create esphome/esphome:${TAG} \ with:
esphome/esphome-aarch64:${TAG} \ registry: ghcr.io
esphome/esphome-amd64:${TAG} \ username: ${{ github.actor }}
esphome/esphome-armv7:${TAG} password: ${{ secrets.GITHUB_TOKEN }}
docker manifest push esphome/esphome:${TAG}
- name: Publish docker beta tag - name: Run manifest
run: | run: |
docker manifest create esphome/esphome:beta \ docker/build.py \
esphome/esphome-aarch64:${TAG} \ --tag "${{ needs.init.outputs.tag }}" \
esphome/esphome-amd64:${TAG} \ --build-type "${{ matrix.build_type }}" \
esphome/esphome-armv7:${TAG} manifest
docker manifest push esphome/esphome:beta
- name: Publish docker latest tag
if: ${{ !github.event.release.prerelease }}
run: |
docker manifest create esphome/esphome:latest \
esphome/esphome-aarch64:${TAG} \
esphome/esphome-amd64:${TAG} \
esphome/esphome-armv7:${TAG}
docker manifest push esphome/esphome:latest
deploy-hassio-repo: deploy-hassio-repo:
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [deploy-docker] needs: [deploy-docker]
steps: steps:
@ -324,4 +144,4 @@ jobs:
-X POST \ -X POST \
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \ https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
-d "{\"ref\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}" -d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"

5
.gitignore vendored
View File

@ -13,6 +13,9 @@ __pycache__/
# Intellij Idea # Intellij Idea
.idea .idea
# Vim
*.swp
# Hide some OS X stuff # Hide some OS X stuff
.DS_Store .DS_Store
.AppleDouble .AppleDouble
@ -100,6 +103,8 @@ CMakeLists.txt
# CMake # CMake
cmake-build-debug/ cmake-build-debug/
cmake-build-livingroom8266/
cmake-build-livingroom32/
cmake-build-release/ cmake-build-release/
CMakeCache.txt CMakeCache.txt

View File

@ -23,5 +23,5 @@ repos:
- id: no-commit-to-branch - id: no-commit-to-branch
args: args:
- --branch=dev - --branch=dev
- --branch=master - --branch=release
- --branch=beta - --branch=beta

35
.vscode/tasks.json vendored
View File

@ -1,11 +1,32 @@
{ {
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{
"label": "run",
"type": "shell",
"command": "python3 -m esphome dashboard config/",
"problemMatcher": []
},
{
"label": "clang-tidy",
"type": "shell",
"command": "test -f .gcc-flags.json || pio init --silent --ide atom; ./script/clang-tidy",
"problemMatcher": [
{ {
"label": "run", "owner": "clang-tidy",
"type": "shell", "fileLocation": "absolute",
"command": "python3 -m esphome config dashboard", "pattern": [
"problemMatcher": [] {
"regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
} }
] ]
}
]
} }

View File

@ -13,30 +13,45 @@ esphome/core/* @esphome/core
# Integrations # Integrations
esphome/components/ac_dimmer/* @glmnet esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core esphome/components/adc/* @esphome/core
esphome/components/addressable_light/* @justfalter
esphome/components/animation/* @syndlex esphome/components/animation/* @syndlex
esphome/components/anova/* @buxtronix
esphome/components/api/* @OttoWinter esphome/components/api/* @OttoWinter
esphome/components/async_tcp/* @OttoWinter esphome/components/async_tcp/* @OttoWinter
esphome/components/atc_mithermometer/* @ahpohl esphome/components/atc_mithermometer/* @ahpohl
esphome/components/b_parasite/* @rbaron
esphome/components/ballu/* @bazuchan
esphome/components/bang_bang/* @OttoWinter esphome/components/bang_bang/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core esphome/components/binary_sensor/* @esphome/core
esphome/components/ble_client/* @buxtronix
esphome/components/bme680_bsec/* @trvrnrth
esphome/components/canbus/* @danielschramm @mvturnho esphome/components/canbus/* @danielschramm @mvturnho
esphome/components/captive_portal/* @OttoWinter esphome/components/captive_portal/* @OttoWinter
esphome/components/climate/* @esphome/core esphome/components/climate/* @esphome/core
esphome/components/climate_ir/* @glmnet esphome/components/climate_ir/* @glmnet
esphome/components/coolix/* @glmnet esphome/components/coolix/* @glmnet
esphome/components/cover/* @esphome/core esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun
esphome/components/ct_clamp/* @jesserockz esphome/components/ct_clamp/* @jesserockz
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/esp32_ble/* @jesserockz
esphome/components/esp32_ble_server/* @jesserockz
esphome/components/esp32_improv/* @jesserockz
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
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/havells_solar/* @sourabhjaiswal
esphome/components/homeassistant/* @OttoWinter esphome/components/homeassistant/* @OttoWinter
esphome/components/hrxl_maxsonar_wr/* @netmikey
esphome/components/i2c/* @esphome/core esphome/components/i2c/* @esphome/core
esphome/components/improv/* @jesserockz
esphome/components/inkbird_ibsth1_mini/* @fkirill esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter esphome/components/integration/* @OttoWinter
@ -56,8 +71,17 @@ 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/midea_ac/* @dudanov
esphome/components/midea_dongle/* @dudanov
esphome/components/mitsubishi/* @RubyBailey
esphome/components/network/* @esphome/core esphome/components/network/* @esphome/core
esphome/components/nextion/* @senexcrenshaw
esphome/components/nextion/binary_sensor/* @senexcrenshaw
esphome/components/nextion/sensor/* @senexcrenshaw
esphome/components/nextion/switch/* @senexcrenshaw
esphome/components/nextion/text_sensor/* @senexcrenshaw
esphome/components/nfc/* @jesserockz esphome/components/nfc/* @jesserockz
esphome/components/number/* @esphome/core
esphome/components/ota/* @esphome/core esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core esphome/components/output/* @esphome/core
esphome/components/pid/* @OttoWinter esphome/components/pid/* @OttoWinter
@ -65,6 +89,8 @@ 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/pulse_meter/* @stevebaxter
esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/rc522/* @glmnet esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet esphome/components/rc522_spi/* @glmnet
@ -72,9 +98,16 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz esphome/components/rf_bridge/* @jesserockz
esphome/components/rtttl/* @glmnet esphome/components/rtttl/* @glmnet
esphome/components/script/* @esphome/core esphome/components/script/* @esphome/core
esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath
esphome/components/selec_meter/* @sourabhjaiswal
esphome/components/select/* @esphome/core
esphome/components/sensor/* @esphome/core esphome/components/sensor/* @esphome/core
esphome/components/sgp40/* @SenexCrenshaw
esphome/components/sht4x/* @sjtrny
esphome/components/shutdown/* @esphome/core esphome/components/shutdown/* @esphome/core
esphome/components/sim800l/* @glmnet esphome/components/sim800l/* @glmnet
esphome/components/sm2135/* @BoukeHaarsma23
esphome/components/spi/* @esphome/core esphome/components/spi/* @esphome/core
esphome/components/ssd1322_base/* @kbx81 esphome/components/ssd1322_base/* @kbx81
esphome/components/ssd1322_spi/* @kbx81 esphome/components/ssd1322_spi/* @kbx81
@ -92,12 +125,17 @@ esphome/components/st7789v/* @kbx81
esphome/components/substitutions/* @esphome/core esphome/components/substitutions/* @esphome/core
esphome/components/sun/* @OttoWinter esphome/components/sun/* @OttoWinter
esphome/components/switch/* @esphome/core esphome/components/switch/* @esphome/core
esphome/components/t6615/* @tylermenezes
esphome/components/tca9548a/* @andreashergert1984
esphome/components/tcl112/* @glmnet esphome/components/tcl112/* @glmnet
esphome/components/teleinfo/* @0hax esphome/components/teleinfo/* @0hax
esphome/components/thermostat/* @kbx81 esphome/components/thermostat/* @kbx81
esphome/components/time/* @OttoWinter esphome/components/time/* @OttoWinter
esphome/components/tlc5947/* @rnauber
esphome/components/tm1637/* @glmnet esphome/components/tm1637/* @glmnet
esphome/components/tmp102/* @timsavage esphome/components/tmp102/* @timsavage
esphome/components/tmp117/* @Azimath
esphome/components/tof10120/* @wstrzalka
esphome/components/tuya/binary_sensor/* @jesserockz esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz esphome/components/tuya/climate/* @jesserockz
esphome/components/tuya/sensor/* @jesserockz esphome/components/tuya/sensor/* @jesserockz
@ -109,3 +147,4 @@ esphome/components/web_server_base/* @OttoWinter
esphome/components/whirlpool/* @glmnet esphome/components/whirlpool/* @glmnet
esphome/components/xiaomi_lywsd03mmc/* @ahpohl esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc401/* @vevsvevs esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xpt2046/* @numo68

View File

@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
Examples of behavior that contributes to creating a positive environment include: Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language - Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences - Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism - Gracefully accepting constructive criticism
* Focusing on what is best for the community - Focusing on what is best for the community
* Showing empathy towards other community members - Showing empathy towards other community members
Examples of unacceptable behavior by participants include: Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances - The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks - Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment - Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission - Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting - Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities ## Our Responsibilities
@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@otto-winter.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@nabucasa.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

View File

@ -1,4 +1,4 @@
# ESPHome [![Build Status](https://travis-ci.org/esphome/esphome.svg?branch=master)](https://travis-ci.org/esphome/esphome) [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/) # ESPHome [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/)
[![ESPHome Logo](https://esphome.io/_images/logo-text.png)](https://esphome.io/) [![ESPHome Logo](https://esphome.io/_images/logo-text.png)](https://esphome.io/)

View File

@ -1,9 +1,11 @@
ARG BUILD_FROM=esphome/esphome-base-amd64:2.6.0 ARG BUILD_FROM=esphome/esphome-base:latest
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
# 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 / COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
RUN pip3 install --no-cache-dir -r /requirements.txt RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
&& /platformio_install_deps.py /platformio.ini
# Then copy esphome and install # Then copy esphome and install
COPY . . COPY . .
@ -25,4 +27,4 @@ WORKDIR /config
# in every docker command twice # in every docker command twice
ENTRYPOINT ["esphome"] ENTRYPOINT ["esphome"]
# When no arguments given, start the dashboard in the workdir # When no arguments given, start the dashboard in the workdir
CMD ["/config", "dashboard"] CMD ["dashboard", "/config"]

View File

@ -1,13 +1 @@
FROM esphome/esphome-base-amd64:2.6.0 FROM esphome/esphome-lint:1.1
COPY . .
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
python3-wheel \
net-tools \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspaces
ENV SHELL /bin/bash

View File

@ -1,9 +1,11 @@
ARG BUILD_FROM ARG BUILD_FROM=esphome/esphome-hassio-base:latest
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
# 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 / COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
RUN pip3 install --no-cache-dir -r /requirements.txt RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
&& /platformio_install_deps.py /platformio.ini
# Copy root filesystem # Copy root filesystem
COPY docker/rootfs/ / COPY docker/rootfs/ /

View File

@ -1,7 +1,10 @@
FROM esphome/esphome-lint-base:2.6.0 ARG BUILD_FROM=esphome/esphome-lint-base:latest
FROM ${BUILD_FROM}
COPY requirements.txt requirements_test.txt / 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_test.txt 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"] VOLUME ["/esphome"]
WORKDIR /esphome WORKDIR /esphome

177
docker/build.py Executable file
View File

@ -0,0 +1,177 @@
#!/usr/bin/env python3
from dataclasses import dataclass
import subprocess
import argparse
import platform
import shlex
import re
import sys
CHANNEL_DEV = 'dev'
CHANNEL_BETA = 'beta'
CHANNEL_RELEASE = 'release'
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
ARCH_AMD64 = 'amd64'
ARCH_ARMV7 = 'armv7'
ARCH_AARCH64 = 'aarch64'
ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64]
TYPE_DOCKER = 'docker'
TYPE_HA_ADDON = 'ha-addon'
TYPE_LINT = 'lint'
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
BASE_VERSION = "3.6.0"
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("--arch", choices=ARCHS, required=False, help="The architecture to build for")
parser.add_argument("--build-type", choices=TYPES, required=True, help="The type of build to run")
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)
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")
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)
class DockerParams:
build_from: str
build_to: str
manifest_to: str
dockerfile: str
@classmethod
def for_type_arch(cls, build_type, arch):
prefix = {
TYPE_DOCKER: "esphome/esphome",
TYPE_HA_ADDON: "esphome/esphome-hassio",
TYPE_LINT: "esphome/esphome-lint"
}[build_type]
build_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}"
build_to = f"{prefix}-{arch}"
dockerfile = {
TYPE_DOCKER: "docker/Dockerfile",
TYPE_HA_ADDON: "docker/Dockerfile.hassio",
TYPE_LINT: "docker/Dockerfile.lint",
}[build_type]
return cls(
build_from=build_from,
build_to=build_to,
manifest_to=prefix,
dockerfile=dockerfile
)
def main():
args = parser.parse_args()
def run_command(*cmd, ignore_error: bool = False):
print(f"$ {shlex.join(list(cmd))}")
if not args.dry_run:
rc = subprocess.call(list(cmd))
if rc != 0 and not ignore_error:
print("Command failed")
sys.exit(1)
# detect channel from tag
match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag)
if match is None:
channel = CHANNEL_DEV
elif match.group(1) is None:
channel = CHANNEL_RELEASE
else:
channel = CHANNEL_BETA
tags_to_push = [args.tag]
if channel == CHANNEL_DEV:
tags_to_push.append("dev")
elif channel == CHANNEL_BETA:
tags_to_push.append("beta")
elif channel == CHANNEL_RELEASE:
# Additionally push to beta
tags_to_push.append("beta")
tags_to_push.append("latest")
if args.command == "build":
# 1. pull cache image
params = DockerParams.for_type_arch(args.build_type, args.arch)
cache_tag = {
CHANNEL_DEV: "dev",
CHANNEL_BETA: "beta",
CHANNEL_RELEASE: "latest",
}[channel]
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"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
src = imgs[0]
# 1. tag images
for img in imgs[1:]:
run_command(
"docker", "tag", src, img
)
# 2. push images
for img in imgs:
run_command(
"docker", "push", img
)
elif args.command == "manifest":
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
targets = [f"{manifest}:{tag}" for tag in tags_to_push]
targets += [f"ghcr.io/{manifest}:{tag}" for tag in tags_to_push]
# 1. Create manifests
for target in targets:
cmd = ["docker", "manifest", "create", target]
for arch in ARCHS:
src = f"{DockerParams.for_type_arch(args.build_type, arch).build_to}:{args.tag}"
if target.startswith("ghcr.io"):
src = f"ghcr.io/{src}"
cmd.append(src)
run_command(*cmd)
# 2. Push manifests
for target in targets:
run_command(
"docker", "manifest", "push", target
)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# This script is used in the docker containers to preinstall
# all platformio libraries in the global storage
import configparser
import re
import subprocess
import sys
config = configparser.ConfigParser()
config.read(sys.argv[1])
libs = []
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])

View File

@ -23,4 +23,4 @@ if bashio::config.has_value 'relative_url'; then
fi fi
bashio::log.info "Starting ESPHome dashboard..." bashio::log.info "Starting ESPHome dashboard..."
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio

View File

@ -18,8 +18,8 @@ from esphome.const import (
CONF_ESPHOME, CONF_ESPHOME,
CONF_PLATFORMIO_OPTIONS, CONF_PLATFORMIO_OPTIONS,
) )
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority from esphome.core import CORE, EsphomeError, coroutine
from esphome.helpers import color, indent from esphome.helpers import indent
from esphome.util import ( from esphome.util import (
run_external_command, run_external_command,
run_external_process, run_external_process,
@ -27,6 +27,7 @@ from esphome.util import (
list_yaml_files, list_yaml_files,
get_serial_ports, get_serial_ports,
) )
from esphome.log import color, setup_log, Fore
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -57,7 +58,7 @@ def choose_prompt(options):
raise ValueError raise ValueError
break break
except ValueError: except ValueError:
safe_print(color("red", f"Invalid option: '{opt}'")) safe_print(color(Fore.RED, f"Invalid option: '{opt}'"))
return options[opt - 1][1] return options[opt - 1][1]
@ -126,15 +127,16 @@ def wrap_to_code(name, comp):
coro = coroutine(comp.to_code) coro = coroutine(comp.to_code)
@functools.wraps(comp.to_code) @functools.wraps(comp.to_code)
@coroutine_with_priority(coro.priority) async def wrapped(conf):
def wrapped(conf):
cg.add(cg.LineComment(f"{name}:")) cg.add(cg.LineComment(f"{name}:"))
if comp.config_schema is not None: if comp.config_schema is not None:
conf_str = yaml_util.dump(conf) conf_str = yaml_util.dump(conf)
conf_str = conf_str.replace("//", "") conf_str = conf_str.replace("//", "")
cg.add(cg.LineComment(indent(conf_str))) cg.add(cg.LineComment(indent(conf_str)))
yield coro(conf) await coro(conf)
if hasattr(coro, "priority"):
wrapped.priority = coro.priority
return wrapped return wrapped
@ -263,50 +265,10 @@ def clean_mqtt(config, args):
) )
def setup_log(debug=False, quiet=False):
if debug:
log_level = logging.DEBUG
CORE.verbose = True
elif quiet:
log_level = logging.CRITICAL
else:
log_level = logging.INFO
logging.basicConfig(level=log_level)
fmt = "%(levelname)s %(message)s"
colorfmt = f"%(log_color)s{fmt}%(reset)s"
datefmt = "%H:%M:%S"
logging.getLogger("urllib3").setLevel(logging.WARNING)
try:
import colorama
colorama.init(strip=True)
from colorlog import ColoredFormatter
logging.getLogger().handlers[0].setFormatter(
ColoredFormatter(
colorfmt,
datefmt=datefmt,
reset=True,
log_colors={
"DEBUG": "cyan",
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "red",
},
)
)
except ImportError:
pass
def command_wizard(args): def command_wizard(args):
from esphome import wizard from esphome import wizard
return wizard.wizard(args.configuration[0]) return wizard.wizard(args.configuration)
def command_config(args, config): def command_config(args, config):
@ -322,7 +284,7 @@ def command_vscode(args):
logging.disable(logging.INFO) logging.disable(logging.INFO)
logging.disable(logging.WARNING) logging.disable(logging.WARNING)
CORE.config_path = args.configuration[0] CORE.config_path = args.configuration
vscode.read_config(args) vscode.read_config(args)
@ -342,7 +304,7 @@ def command_compile(args, config):
def command_upload(args, config): def command_upload(args, config):
port = choose_upload_log_host( port = choose_upload_log_host(
default=args.upload_port, default=args.device,
check_default=None, check_default=None,
show_ota=True, show_ota=True,
show_mqtt=False, show_mqtt=False,
@ -357,7 +319,7 @@ def command_upload(args, config):
def command_logs(args, config): def command_logs(args, config):
port = choose_upload_log_host( port = choose_upload_log_host(
default=args.serial_port, default=args.device,
check_default=None, check_default=None,
show_ota=False, show_ota=False,
show_mqtt=True, show_mqtt=True,
@ -375,7 +337,7 @@ def command_run(args, config):
return exit_code return exit_code
_LOGGER.info("Successfully compiled program.") _LOGGER.info("Successfully compiled program.")
port = choose_upload_log_host( port = choose_upload_log_host(
default=args.upload_port, default=args.device,
check_default=None, check_default=None,
show_ota=True, show_ota=True,
show_mqtt=False, show_mqtt=False,
@ -388,7 +350,7 @@ def command_run(args, config):
if args.no_logs: if args.no_logs:
return 0 return 0
port = choose_upload_log_host( port = choose_upload_log_host(
default=args.upload_port, default=args.device,
check_default=port, check_default=port,
show_ota=False, show_ota=False,
show_mqtt=True, show_mqtt=True,
@ -442,30 +404,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("cyan", f))) print("Updating {}".format(color(Fore.CYAN, f)))
print("-" * twidth) print("-" * twidth)
print() print()
rc = run_external_process( rc = run_external_process(
"esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA" "esphome", "--dashboard", "run", "--no-logs", "--device", "OTA", f
) )
if rc == 0: if rc == 0:
print_bar("[{}] {}".format(color("bold_green", "SUCCESS"), f)) print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
success[f] = True success[f] = True
else: else:
print_bar("[{}] {}".format(color("bold_red", "ERROR"), f)) print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
success[f] = False success[f] = False
print() print()
print() print()
print() print()
print_bar("[{}]".format(color("bold_white", "SUMMARY"))) print_bar("[{}]".format(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("green", "SUCCESS"))) print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
else: else:
print(" - {}: {}".format(f, color("bold_red", "FAILED"))) print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
failed += 1 failed += 1
return failed return failed
@ -491,15 +453,17 @@ POST_CONFIG_ACTIONS = {
def parse_args(argv): def parse_args(argv):
parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}") options_parser = argparse.ArgumentParser(add_help=False)
parser.add_argument( options_parser.add_argument(
"-v", "--verbose", help="Enable verbose esphome logs.", action="store_true" "-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
) )
parser.add_argument( options_parser.add_argument(
"-q", "--quiet", help="Disable all esphome logs.", action="store_true" "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
) )
parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true") options_parser.add_argument(
parser.add_argument( "--dashboard", help=argparse.SUPPRESS, action="store_true"
)
options_parser.add_argument(
"-s", "-s",
"--substitution", "--substitution",
nargs=2, nargs=2,
@ -507,17 +471,100 @@ def parse_args(argv):
help="Add a substitution", help="Add a substitution",
metavar=("key", "value"), metavar=("key", "value"),
) )
parser.add_argument(
"configuration", help="Your YAML configuration file.", nargs="*" # Keep backward compatibility with the old command line format of
# esphome <config> <command>.
#
# Unfortunately this can't be done by adding another configuration argument to the
# main config parser, as argparse is greedy when parsing arguments, so in regular
# usage it'll eat the command as the configuration argument and error out out
# because it can't parse the configuration as a command.
#
# Instead, construct an ad-hoc parser for the old format that doesn't actually
# process the arguments, but parses them enough to let us figure out if the old
# format is used. In that case, swap the command and configuration in the arguments
# and continue on with the normal parser (after raising a deprecation warning).
#
# Disable argparse's built-in help option and add it manually to prevent this
# parser from printing the help messagefor the old format when invoked with -h.
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
compat_parser.add_argument("-h", "--help")
compat_parser.add_argument("configuration", nargs="*")
compat_parser.add_argument(
"command",
choices=[
"config",
"compile",
"upload",
"logs",
"run",
"clean-mqtt",
"wizard",
"mqtt-fingerprint",
"version",
"clean",
"dashboard",
"vscode",
],
) )
subparsers = parser.add_subparsers(help="Commands", dest="command") # on Python 3.9+ we can simply set exit_on_error=False in the constructor
def _raise(x):
raise argparse.ArgumentError(None, x)
compat_parser.error = _raise
deprecated_argv_suggestion = None
if ["dashboard", "config"] == argv[1:3] or ["version"] == argv[1:2]:
# this is most likely meant in new-style arg format. do not try compat parsing
pass
else:
try:
result, unparsed = compat_parser.parse_known_args(argv[1:])
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
unparsed = [
"--device" if arg in ("--upload-port", "--serial-port") else arg
for arg in unparsed
]
argv = (
argv[0:last_option] + [result.command] + result.configuration + unparsed
)
deprecated_argv_suggestion = argv
except argparse.ArgumentError:
# This is not an old-style command line, so we don't have to do anything.
pass
# And continue on with regular parsing
parser = argparse.ArgumentParser(
description=f"ESPHome v{const.__version__}", parents=[options_parser]
)
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
mqtt_options = argparse.ArgumentParser(add_help=False)
mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
mqtt_options.add_argument("--username", help="Manually set the MQTT username.")
mqtt_options.add_argument("--password", help="Manually set the MQTT password.")
mqtt_options.add_argument("--client-id", help="Manually set the MQTT client id.")
subparsers = parser.add_subparsers(
help="Command to run:", dest="command", metavar="command"
)
subparsers.required = True subparsers.required = True
subparsers.add_parser("config", help="Validate the configuration and spit it out.")
parser_config = subparsers.add_parser(
"config", help="Validate the configuration and spit it out."
)
parser_config.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
)
parser_compile = subparsers.add_parser( parser_compile = subparsers.add_parser(
"compile", help="Read the configuration and compile a program." "compile", help="Read the configuration and compile a program."
) )
parser_compile.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
)
parser_compile.add_argument( parser_compile.add_argument(
"--only-generate", "--only-generate",
help="Only generate source code, do not compile.", help="Only generate source code, do not compile.",
@ -525,106 +572,124 @@ def parse_args(argv):
) )
parser_upload = subparsers.add_parser( parser_upload = subparsers.add_parser(
"upload", help="Validate the configuration " "and upload the latest binary." "upload", help="Validate the configuration and upload the latest binary."
) )
parser_upload.add_argument( parser_upload.add_argument(
"--upload-port", "configuration", help="Your YAML configuration file(s).", nargs="+"
help="Manually specify the upload port to use. " )
"For example /dev/cu.SLAB_USBtoUART.", parser_upload.add_argument(
"--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_logs = subparsers.add_parser( parser_logs = subparsers.add_parser(
"logs", help="Validate the configuration " "and show all MQTT logs." "logs",
help="Validate the configuration and show all logs.",
parents=[mqtt_options],
) )
parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.")
parser_logs.add_argument("--username", help="Manually set the username.")
parser_logs.add_argument("--password", help="Manually set the password.")
parser_logs.add_argument("--client-id", help="Manually set the client id.")
parser_logs.add_argument( parser_logs.add_argument(
"--serial-port", "configuration", help="Your YAML configuration file.", nargs=1
help="Manually specify a serial port to use" )
"For example /dev/cu.SLAB_USBtoUART.", parser_logs.add_argument(
"--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_run = subparsers.add_parser( parser_run = subparsers.add_parser(
"run", "run",
help="Validate the configuration, create a binary, " help="Validate the configuration, create a binary, upload it, and start logs.",
"upload it, and start MQTT logs.", parents=[mqtt_options],
) )
parser_run.add_argument( parser_run.add_argument(
"--upload-port", "configuration", help="Your YAML configuration file(s).", nargs="+"
help="Manually specify the upload port/ip to use. "
"For example /dev/cu.SLAB_USBtoUART.",
) )
parser_run.add_argument( parser_run.add_argument(
"--no-logs", help="Disable starting MQTT logs.", action="store_true" "--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_run.add_argument( parser_run.add_argument(
"--topic", help="Manually set the topic to subscribe to for logs." "--no-logs", help="Disable starting logs.", action="store_true"
) )
parser_run.add_argument(
"--username", help="Manually set the MQTT username for logs."
)
parser_run.add_argument(
"--password", help="Manually set the MQTT password for logs."
)
parser_run.add_argument("--client-id", help="Manually set the client id for logs.")
parser_clean = subparsers.add_parser( parser_clean = subparsers.add_parser(
"clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages." "clean-mqtt",
help="Helper to clear retained messages from an MQTT topic.",
parents=[mqtt_options],
)
parser_clean.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
) )
parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.")
parser_clean.add_argument("--username", help="Manually set the username.")
parser_clean.add_argument("--password", help="Manually set the password.")
parser_clean.add_argument("--client-id", help="Manually set the client id.")
subparsers.add_parser( parser_wizard = subparsers.add_parser(
"wizard", "wizard",
help="A helpful setup wizard that will guide " help="A helpful setup wizard that will guide you through setting up ESPHome.",
"you through setting up esphome.", )
parser_wizard.add_argument(
"configuration",
help="Your YAML configuration file.",
) )
subparsers.add_parser( parser_fingerprint = subparsers.add_parser(
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker." "mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
) )
parser_fingerprint.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
)
subparsers.add_parser("version", help="Print the esphome version and exit.") subparsers.add_parser("version", help="Print the ESPHome version and exit.")
subparsers.add_parser("clean", help="Delete all temporary build files.") parser_clean = subparsers.add_parser(
"clean", help="Delete all temporary build files."
)
parser_clean.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
)
dashboard = subparsers.add_parser( parser_dashboard = subparsers.add_parser(
"dashboard", help="Create a simple web server for a dashboard." "dashboard", help="Create a simple web server for a dashboard."
) )
dashboard.add_argument( parser_dashboard.add_argument(
"configuration",
help="Your YAML configuration file directory.",
)
parser_dashboard.add_argument(
"--port", "--port",
help="The HTTP port to open connections on. Defaults to 6052.", help="The HTTP port to open connections on. Defaults to 6052.",
type=int, type=int,
default=6052, default=6052,
) )
dashboard.add_argument( parser_dashboard.add_argument(
"--username", "--username",
help="The optional username to require " "for authentication.", help="The optional username to require for authentication.",
type=str, type=str,
default="", default="",
) )
dashboard.add_argument( parser_dashboard.add_argument(
"--password", "--password",
help="The optional password to require " "for authentication.", help="The optional password to require for authentication.",
type=str, type=str,
default="", default="",
) )
dashboard.add_argument( parser_dashboard.add_argument(
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true" "--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
) )
dashboard.add_argument("--hassio", help=argparse.SUPPRESS, action="store_true") parser_dashboard.add_argument(
dashboard.add_argument( "--hassio", help=argparse.SUPPRESS, action="store_true"
)
parser_dashboard.add_argument(
"--socket", help="Make the dashboard serve under a unix socket", type=str "--socket", help="Make the dashboard serve under a unix socket", type=str
) )
vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS) parser_vscode = subparsers.add_parser("vscode")
vscode.add_argument("--ace", action="store_true") parser_vscode.add_argument(
"configuration", help="Your YAML configuration file.", nargs=1
)
parser_vscode.add_argument("--ace", action="store_true")
subparsers.add_parser("update-all", help=argparse.SUPPRESS) parser_update = subparsers.add_parser("update-all")
parser_update.add_argument(
"configuration", help="Your YAML configuration file directory.", nargs=1
)
return parser.parse_args(argv[1:]) return parser.parse_args(argv[1:])
@ -634,14 +699,18 @@ def run_esphome(argv):
CORE.dashboard = args.dashboard CORE.dashboard = args.dashboard
setup_log(args.verbose, args.quiet) setup_log(args.verbose, args.quiet)
if args.command != "version" and not args.configuration: if args.deprecated_argv_suggestion is not None and args.command != "vscode":
_LOGGER.error("Missing configuration parameter, see esphome --help.") _LOGGER.warning(
return 1 "Calling ESPHome with the configuration before the command is deprecated "
"and will be removed in the future. "
)
_LOGGER.warning("Please instead use:")
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion[1:]))
if sys.version_info < (3, 6, 0): if sys.version_info < (3, 7, 0):
_LOGGER.error( _LOGGER.error(
"You're running ESPHome with Python <3.6. ESPHome is no longer compatible " "You're running ESPHome with Python <3.7. ESPHome is no longer compatible "
"with this Python version. Please reinstall ESPHome with Python 3.6+" "with this Python version. Please reinstall ESPHome with Python 3.7+"
) )
return 1 return 1
@ -649,7 +718,7 @@ def run_esphome(argv):
try: try:
return PRE_CONFIG_ACTIONS[args.command](args) return PRE_CONFIG_ACTIONS[args.command](args)
except EsphomeError as e: except EsphomeError as e:
_LOGGER.error(e) _LOGGER.error(e, exc_info=args.verbose)
return 1 return 1
for conf_path in args.configuration: for conf_path in args.configuration:
@ -667,7 +736,7 @@ def run_esphome(argv):
try: try:
rc = POST_CONFIG_ACTIONS[args.command](args, config) rc = POST_CONFIG_ACTIONS[args.command](args, config)
except EsphomeError as e: except EsphomeError as e:
_LOGGER.error(e) _LOGGER.error(e, exc_info=args.verbose)
return 1 return 1
if rc != 0: if rc != 0:
return rc return rc

View File

@ -13,7 +13,8 @@ from esphome import const
import esphome.api.api_pb2 as pb import esphome.api.api_pb2 as pb
from esphome.const import CONF_PASSWORD, CONF_PORT from esphome.const import CONF_PASSWORD, CONF_PORT
from esphome.core import EsphomeError from esphome.core import EsphomeError
from esphome.helpers import resolve_ip_address, indent, color from esphome.helpers import resolve_ip_address, indent
from esphome.log import color, Fore
from esphome.util import safe_print from esphome.util import safe_print
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -488,7 +489,7 @@ def run_logs(config, address):
text = msg.message text = msg.message
if msg.send_failed: if msg.send_failed:
text = color( text = color(
"white", Fore.WHITE,
"(Message skipped because it was too big to fit in " "(Message skipped because it was too big to fit in "
"TCP buffer - This is only cosmetic)", "TCP buffer - This is only cosmetic)",
) )

View File

@ -10,7 +10,6 @@ from esphome.const import (
CONF_TYPE_ID, CONF_TYPE_ID,
CONF_TIME, CONF_TIME,
) )
from esphome.core import coroutine
from esphome.jsonschema import jschema_extractor from esphome.jsonschema import jschema_extractor
from esphome.util import Registry from esphome.util import Registry
@ -142,27 +141,27 @@ NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
@register_condition("and", AndCondition, validate_condition_list) @register_condition("and", AndCondition, validate_condition_list)
def and_condition_to_code(config, condition_id, template_arg, args): async def and_condition_to_code(config, condition_id, template_arg, args):
conditions = yield build_condition_list(config, template_arg, args) conditions = await build_condition_list(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, conditions) return cg.new_Pvariable(condition_id, template_arg, conditions)
@register_condition("or", OrCondition, validate_condition_list) @register_condition("or", OrCondition, validate_condition_list)
def or_condition_to_code(config, condition_id, template_arg, args): async def or_condition_to_code(config, condition_id, template_arg, args):
conditions = yield build_condition_list(config, template_arg, args) conditions = await build_condition_list(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, conditions) return cg.new_Pvariable(condition_id, template_arg, conditions)
@register_condition("not", NotCondition, validate_potentially_and_condition) @register_condition("not", NotCondition, validate_potentially_and_condition)
def not_condition_to_code(config, condition_id, template_arg, args): async def not_condition_to_code(config, condition_id, template_arg, args):
condition = yield build_condition(config, template_arg, args) condition = await build_condition(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, condition) return cg.new_Pvariable(condition_id, template_arg, condition)
@register_condition("lambda", LambdaCondition, cv.lambda_) @register_condition("lambda", LambdaCondition, cv.returning_lambda)
def lambda_condition_to_code(config, condition_id, template_arg, args): async def lambda_condition_to_code(config, condition_id, template_arg, args):
lambda_ = yield cg.process_lambda(config, args, return_type=bool) lambda_ = await cg.process_lambda(config, args, return_type=bool)
yield cg.new_Pvariable(condition_id, template_arg, lambda_) return cg.new_Pvariable(condition_id, template_arg, lambda_)
@register_condition( @register_condition(
@ -177,26 +176,26 @@ def lambda_condition_to_code(config, condition_id, template_arg, args):
} }
).extend(cv.COMPONENT_SCHEMA), ).extend(cv.COMPONENT_SCHEMA),
) )
def for_condition_to_code(config, condition_id, template_arg, args): async def for_condition_to_code(config, condition_id, template_arg, args):
condition = yield build_condition( condition = await build_condition(
config[CONF_CONDITION], cg.TemplateArguments(), [] config[CONF_CONDITION], cg.TemplateArguments(), []
) )
var = cg.new_Pvariable(condition_id, template_arg, condition) var = cg.new_Pvariable(condition_id, template_arg, condition)
yield cg.register_component(var, config) await cg.register_component(var, config)
templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32) templ = await cg.templatable(config[CONF_TIME], args, cg.uint32)
cg.add(var.set_time(templ)) cg.add(var.set_time(templ))
yield var return var
@register_action( @register_action(
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds) "delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
) )
def delay_action_to_code(config, action_id, template_arg, args): async def delay_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_component(var, {}) await cg.register_component(var, {})
template_ = yield cg.templatable(config, args, cg.uint32) template_ = await cg.templatable(config, args, cg.uint32)
cg.add(var.set_delay(template_)) cg.add(var.set_delay(template_))
yield var return var
@register_action( @register_action(
@ -211,16 +210,16 @@ def delay_action_to_code(config, action_id, template_arg, args):
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE), cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
), ),
) )
def if_action_to_code(config, action_id, template_arg, args): async def if_action_to_code(config, action_id, template_arg, args):
conditions = yield 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_THEN in config: if CONF_THEN in config:
actions = yield build_action_list(config[CONF_THEN], template_arg, args) actions = await build_action_list(config[CONF_THEN], template_arg, args)
cg.add(var.add_then(actions)) cg.add(var.add_then(actions))
if CONF_ELSE in config: if CONF_ELSE in config:
actions = yield build_action_list(config[CONF_ELSE], template_arg, args) actions = await build_action_list(config[CONF_ELSE], template_arg, args)
cg.add(var.add_else(actions)) cg.add(var.add_else(actions))
yield var return var
@register_action( @register_action(
@ -233,12 +232,12 @@ def if_action_to_code(config, action_id, template_arg, args):
} }
), ),
) )
def while_action_to_code(config, action_id, template_arg, args): async def while_action_to_code(config, action_id, template_arg, args):
conditions = yield 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)
actions = yield build_action_list(config[CONF_THEN], template_arg, args) actions = await build_action_list(config[CONF_THEN], template_arg, args)
cg.add(var.add_then(actions)) cg.add(var.add_then(actions))
yield var return var
def validate_wait_until(value): def validate_wait_until(value):
@ -253,17 +252,17 @@ def validate_wait_until(value):
@register_action("wait_until", WaitUntilAction, validate_wait_until) @register_action("wait_until", WaitUntilAction, validate_wait_until)
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 = yield 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)
yield cg.register_component(var, {}) await cg.register_component(var, {})
yield var return var
@register_action("lambda", LambdaAction, cv.lambda_) @register_action("lambda", LambdaAction, cv.lambda_)
def lambda_action_to_code(config, action_id, template_arg, args): async def lambda_action_to_code(config, action_id, template_arg, args):
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void) lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
yield cg.new_Pvariable(action_id, template_arg, lambda_) return cg.new_Pvariable(action_id, template_arg, lambda_)
@register_action( @register_action(
@ -275,54 +274,51 @@ def lambda_action_to_code(config, action_id, template_arg, args):
} }
), ),
) )
def component_update_action_to_code(config, action_id, template_arg, args): async def component_update_action_to_code(config, action_id, template_arg, args):
comp = yield cg.get_variable(config[CONF_ID]) comp = await cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, comp) return cg.new_Pvariable(action_id, template_arg, comp)
@coroutine async def build_action(full_config, template_arg, args):
def build_action(full_config, template_arg, args):
registry_entry, config = cg.extract_registry_entry_config( registry_entry, config = cg.extract_registry_entry_config(
ACTION_REGISTRY, full_config ACTION_REGISTRY, full_config
) )
action_id = full_config[CONF_TYPE_ID] action_id = full_config[CONF_TYPE_ID]
builder = registry_entry.coroutine_fun builder = registry_entry.coroutine_fun
yield builder(config, action_id, template_arg, args) ret = await builder(config, action_id, template_arg, args)
return ret
@coroutine async def build_action_list(config, templ, arg_type):
def build_action_list(config, templ, arg_type):
actions = [] actions = []
for conf in config: for conf in config:
action = yield build_action(conf, templ, arg_type) action = await build_action(conf, templ, arg_type)
actions.append(action) actions.append(action)
yield actions return actions
@coroutine async def build_condition(full_config, template_arg, args):
def build_condition(full_config, template_arg, args):
registry_entry, config = cg.extract_registry_entry_config( registry_entry, config = cg.extract_registry_entry_config(
CONDITION_REGISTRY, full_config CONDITION_REGISTRY, full_config
) )
action_id = full_config[CONF_TYPE_ID] action_id = full_config[CONF_TYPE_ID]
builder = registry_entry.coroutine_fun builder = registry_entry.coroutine_fun
yield builder(config, action_id, template_arg, args) ret = await builder(config, action_id, template_arg, args)
return ret
@coroutine async def build_condition_list(config, templ, args):
def build_condition_list(config, templ, args):
conditions = [] conditions = []
for conf in config: for conf in config:
condition = yield build_condition(conf, templ, args) condition = await build_condition(conf, templ, args)
conditions.append(condition) conditions.append(condition)
yield conditions return conditions
@coroutine async def build_automation(trigger, args, config):
def build_automation(trigger, args, config):
arg_types = [arg[0] for arg in args] arg_types = [arg[0] for arg in args]
templ = cg.TemplateArguments(*arg_types) templ = cg.TemplateArguments(*arg_types)
obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger) obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
actions = yield build_action_list(config[CONF_THEN], templ, args) actions = await build_action_list(config[CONF_THEN], templ, args)
cg.add(obj.add_actions(actions)) cg.add(obj.add_actions(actions))
yield obj return obj

875
esphome/boards.py Normal file
View File

@ -0,0 +1,875 @@
ESP8266_BASE_PINS = {
"A0": 17,
"SS": 15,
"MOSI": 13,
"MISO": 12,
"SCK": 14,
"SDA": 4,
"SCL": 5,
"RX": 3,
"TX": 1,
}
ESP8266_BOARD_PINS = {
"d1": {
"D0": 3,
"D1": 1,
"D2": 16,
"D3": 5,
"D4": 4,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 0,
"D9": 2,
"D10": 15,
"D11": 13,
"D12": 14,
"D13": 14,
"D14": 4,
"D15": 5,
"LED": 2,
},
"d1_mini": {
"D0": 16,
"D1": 5,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"LED": 2,
},
"d1_mini_lite": "d1_mini",
"d1_mini_pro": "d1_mini",
"esp01": {},
"esp01_1m": {},
"esp07": {},
"esp12e": {},
"esp210": {},
"esp8285": {},
"esp_wroom_02": {},
"espduino": {"LED": 16},
"espectro": {"LED": 15, "BUTTON": 2},
"espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0},
"espinotee": {"LED": 16},
"espresso_lite_v1": {"LED": 16},
"espresso_lite_v2": {"LED": 2},
"gen4iod": {},
"heltec_wifi_kit_8": "d1_mini",
"huzzah": {
"LED": 0,
"LED_RED": 0,
"LED_BLUE": 2,
"D4": 4,
"D5": 5,
"D12": 12,
"D13": 13,
"D14": 14,
"D15": 15,
"D16": 16,
},
"inventone": {},
"modwifi": {},
"nodemcu": {
"D0": 16,
"D1": 5,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 3,
"D10": 1,
"LED": 16,
},
"nodemcuv2": "nodemcu",
"oak": {
"P0": 2,
"P1": 5,
"P2": 0,
"P3": 3,
"P4": 1,
"P5": 4,
"P6": 15,
"P7": 13,
"P8": 12,
"P9": 14,
"P10": 16,
"P11": 17,
"LED": 5,
},
"phoenix_v1": {"LED": 16},
"phoenix_v2": {"LED": 2},
"sparkfunBlynk": "thing",
"thing": {"LED": 5, "SDA": 2, "SCL": 14},
"thingdev": "thing",
"wifi_slot": {"LED": 2},
"wifiduino": {
"D0": 3,
"D1": 1,
"D2": 2,
"D3": 0,
"D4": 4,
"D5": 5,
"D6": 16,
"D7": 14,
"D8": 12,
"D9": 13,
"D10": 15,
"D11": 13,
"D12": 12,
"D13": 14,
},
"wifinfo": {
"LED": 12,
"D0": 16,
"D1": 5,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 3,
"D10": 1,
},
"wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0},
"wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0},
"xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13},
}
FLASH_SIZE_1_MB = 2 ** 20
FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2
FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB
FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB
FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB
ESP8266_FLASH_SIZES = {
"d1": FLASH_SIZE_4_MB,
"d1_mini": FLASH_SIZE_4_MB,
"d1_mini_lite": FLASH_SIZE_1_MB,
"d1_mini_pro": FLASH_SIZE_16_MB,
"esp01": FLASH_SIZE_512_KB,
"esp01_1m": FLASH_SIZE_1_MB,
"esp07": FLASH_SIZE_4_MB,
"esp12e": FLASH_SIZE_4_MB,
"esp210": FLASH_SIZE_4_MB,
"esp8285": FLASH_SIZE_1_MB,
"esp_wroom_02": FLASH_SIZE_2_MB,
"espduino": FLASH_SIZE_4_MB,
"espectro": FLASH_SIZE_4_MB,
"espino": FLASH_SIZE_4_MB,
"espinotee": FLASH_SIZE_4_MB,
"espresso_lite_v1": FLASH_SIZE_4_MB,
"espresso_lite_v2": FLASH_SIZE_4_MB,
"gen4iod": FLASH_SIZE_512_KB,
"heltec_wifi_kit_8": FLASH_SIZE_4_MB,
"huzzah": FLASH_SIZE_4_MB,
"inventone": FLASH_SIZE_4_MB,
"modwifi": FLASH_SIZE_2_MB,
"nodemcu": FLASH_SIZE_4_MB,
"nodemcuv2": FLASH_SIZE_4_MB,
"oak": FLASH_SIZE_4_MB,
"phoenix_v1": FLASH_SIZE_4_MB,
"phoenix_v2": FLASH_SIZE_4_MB,
"sparkfunBlynk": FLASH_SIZE_4_MB,
"thing": FLASH_SIZE_512_KB,
"thingdev": FLASH_SIZE_512_KB,
"wifi_slot": FLASH_SIZE_1_MB,
"wifiduino": FLASH_SIZE_4_MB,
"wifinfo": FLASH_SIZE_1_MB,
"wio_link": FLASH_SIZE_4_MB,
"wio_node": FLASH_SIZE_4_MB,
"xinabox_cw01": FLASH_SIZE_4_MB,
}
ESP8266_LD_SCRIPTS = {
FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"),
FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"),
FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"),
FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"),
FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"),
}
ESP32_BASE_PINS = {
"TX": 1,
"RX": 3,
"SDA": 21,
"SCL": 22,
"SS": 5,
"MOSI": 23,
"MISO": 19,
"SCK": 18,
"A0": 36,
"A3": 39,
"A4": 32,
"A5": 33,
"A6": 34,
"A7": 35,
"A10": 4,
"A11": 0,
"A12": 2,
"A13": 15,
"A14": 13,
"A15": 12,
"A16": 14,
"A17": 27,
"A18": 25,
"A19": 26,
"T0": 4,
"T1": 0,
"T2": 2,
"T3": 15,
"T4": 13,
"T5": 12,
"T6": 14,
"T7": 27,
"T8": 33,
"T9": 32,
"DAC1": 25,
"DAC2": 26,
"SVP": 36,
"SVN": 39,
}
ESP32_BOARD_PINS = {
"alksesp32": {
"A0": 32,
"A1": 33,
"A2": 25,
"A3": 26,
"A4": 27,
"A5": 14,
"A6": 12,
"A7": 15,
"D0": 40,
"D1": 41,
"D10": 19,
"D11": 21,
"D12": 22,
"D13": 23,
"D2": 15,
"D3": 2,
"D4": 0,
"D5": 4,
"D6": 16,
"D7": 17,
"D8": 5,
"D9": 18,
"DHT_PIN": 26,
"LED": 23,
"L_B": 5,
"L_G": 17,
"L_R": 22,
"L_RGB_B": 16,
"L_RGB_G": 21,
"L_RGB_R": 4,
"L_Y": 23,
"MISO": 22,
"MOSI": 21,
"PHOTO": 25,
"PIEZO1": 19,
"PIEZO2": 18,
"POT1": 32,
"POT2": 33,
"S1": 4,
"S2": 16,
"S3": 18,
"S4": 19,
"S5": 21,
"SCK": 23,
"SCL": 14,
"SDA": 27,
"SS": 19,
"SW1": 15,
"SW2": 2,
"SW3": 0,
},
"bpi-bit": {
"BUTTON_A": 35,
"BUTTON_B": 27,
"BUZZER": 25,
"LIGHT_SENSOR1": 36,
"LIGHT_SENSOR2": 39,
"MPU9250_INT": 0,
"P0": 25,
"P1": 32,
"P10": 26,
"P11": 27,
"P12": 2,
"P13": 18,
"P14": 19,
"P15": 23,
"P16": 5,
"P19": 22,
"P2": 33,
"P20": 21,
"P3": 13,
"P4": 15,
"P5": 35,
"P6": 12,
"P7": 14,
"P8": 16,
"P9": 17,
"RGB_LED": 4,
"TEMPERATURE_SENSOR": 34,
},
"d-duino-32": {
"D1": 5,
"D10": 1,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 3,
"MISO": 12,
"MOSI": 13,
"SCK": 14,
"SCL": 4,
"SDA": 5,
"SS": 15,
},
"esp-wrover-kit": {},
"esp32-devkitlipo": {},
"esp32-evb": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
"SS": 17,
},
"esp32-gateway": {"BUTTON": 34, "LED": 33, "SCL": 16, "SDA": 32},
"esp32-poe-iso": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
},
"esp32-poe": {"BUTTON": 34, "MISO": 15, "MOSI": 2, "SCK": 14, "SCL": 16, "SDA": 13},
"esp32-pro": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
"SS": 17,
},
"esp320": {
"LED": 5,
"MISO": 12,
"MOSI": 13,
"SCK": 14,
"SCL": 14,
"SDA": 2,
"SS": 15,
},
"esp32cam": {},
"esp32dev": {},
"esp32doit-devkit-v1": {"LED": 2},
"esp32thing": {"BUTTON": 0, "LED": 5, "SS": 2},
"esp32vn-iot-uno": {},
"espea32": {"BUTTON": 0, "LED": 5},
"espectro32": {"LED": 15, "SD_SS": 33},
"espino32": {"BUTTON": 0, "LED": 16},
"featheresp32": {
"A0": 26,
"A1": 25,
"A10": 27,
"A11": 12,
"A12": 13,
"A13": 35,
"A2": 34,
"A4": 36,
"A5": 4,
"A6": 14,
"A7": 32,
"A8": 15,
"A9": 33,
"Ax": 2,
"LED": 13,
"MOSI": 18,
"RX": 16,
"SCK": 5,
"SDA": 23,
"SS": 33,
"TX": 17,
},
"firebeetle32": {"LED": 2},
"fm-devkit": {
"D0": 34,
"D1": 35,
"D10": 0,
"D2": 32,
"D3": 33,
"D4": 27,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 23,
"I2S_DOUT": 22,
"I2S_LRCLK": 25,
"I2S_MCLK": 2,
"I2S_SCLK": 26,
"LED": 5,
"SCL": 17,
"SDA": 16,
"SW1": 4,
"SW2": 18,
"SW3": 19,
"SW4": 21,
},
"frogboard": {},
"heltec_wifi_kit_32": {
"A1": 37,
"A2": 38,
"BUTTON": 0,
"LED": 25,
"RST_OLED": 16,
"SCL_OLED": 15,
"SDA_OLED": 4,
"Vext": 21,
},
"heltec_wifi_lora_32": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 33,
"DIO2": 32,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"heltec_wifi_lora_32_V2": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 35,
"DIO2": 34,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"heltec_wireless_stick": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 35,
"DIO2": 34,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"hornbill32dev": {"BUTTON": 0, "LED": 13},
"hornbill32minima": {"SS": 2},
"intorobot": {
"A1": 39,
"A2": 35,
"A3": 25,
"A4": 26,
"A5": 14,
"A6": 12,
"A7": 15,
"A8": 13,
"A9": 2,
"BUTTON": 0,
"D0": 19,
"D1": 23,
"D2": 18,
"D3": 17,
"D4": 16,
"D5": 5,
"D6": 4,
"LED": 4,
"MISO": 17,
"MOSI": 16,
"RGB_B_BUILTIN": 22,
"RGB_G_BUILTIN": 21,
"RGB_R_BUILTIN": 27,
"SCL": 19,
"SDA": 23,
"T0": 19,
"T1": 23,
"T2": 18,
"T3": 17,
"T4": 16,
"T5": 5,
"T6": 4,
},
"iotaap_magnolia": {},
"iotbusio": {},
"iotbusproteus": {},
"lolin32": {"LED": 5},
"lolin32_lite": {"LED": 22},
"lolin_d32": {"LED": 5, "_VBAT": 35},
"lolin_d32_pro": {"LED": 5, "_VBAT": 35},
"lopy": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 17,
},
"lopy4": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 18,
},
"m5stack-core-esp32": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
"RXD2": 16,
"TXD2": 17,
},
"m5stack-fire": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
},
"m5stack-grey": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
"RXD2": 16,
"TXD2": 17,
},
"m5stick-c": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G10": 10,
"G26": 26,
"G32": 32,
"G33": 33,
"G36": 36,
"G37": 37,
"G39": 39,
"G9": 9,
"MISO": 36,
"MOSI": 15,
"SCK": 13,
"SCL": 33,
"SDA": 32,
},
"magicbit": {
"BLUE_LED": 17,
"BUZZER": 25,
"GREEN_LED": 16,
"LDR": 36,
"LED": 16,
"LEFT_BUTTON": 35,
"MOTOR1A": 27,
"MOTOR1B": 18,
"MOTOR2A": 16,
"MOTOR2B": 17,
"POT": 39,
"RED_LED": 27,
"RIGHT_PUTTON": 34,
"YELLOW_LED": 18,
},
"mhetesp32devkit": {"LED": 2},
"mhetesp32minikit": {"LED": 2},
"microduino-core-esp32": {
"A0": 12,
"A1": 13,
"A10": 25,
"A11": 26,
"A12": 27,
"A13": 14,
"A2": 15,
"A3": 4,
"A6": 38,
"A7": 37,
"A8": 32,
"A9": 33,
"D0": 3,
"D1": 1,
"D10": 5,
"D11": 23,
"D12": 19,
"D13": 18,
"D14": 12,
"D15": 13,
"D16": 15,
"D17": 4,
"D18": 22,
"D19": 21,
"D2": 16,
"D20": 38,
"D21": 37,
"D3": 17,
"D4": 32,
"D5": 33,
"D6": 25,
"D7": 26,
"D8": 27,
"D9": 14,
"SCL": 21,
"SCL1": 13,
"SDA": 22,
"SDA1": 12,
},
"nano32": {"BUTTON": 0, "LED": 16},
"nina_w10": {
"D0": 3,
"D1": 1,
"D10": 5,
"D11": 19,
"D12": 23,
"D13": 18,
"D14": 13,
"D15": 12,
"D16": 32,
"D17": 33,
"D18": 21,
"D19": 34,
"D2": 26,
"D20": 36,
"D21": 39,
"D3": 25,
"D4": 35,
"D5": 27,
"D6": 22,
"D7": 0,
"D8": 15,
"D9": 14,
"LED_BLUE": 21,
"LED_GREEN": 33,
"LED_RED": 23,
"SCL": 13,
"SDA": 12,
"SW1": 33,
"SW2": 27,
},
"node32s": {},
"nodemcu-32s": {"BUTTON": 0, "LED": 2},
"odroid_esp32": {"ADC1": 35, "ADC2": 36, "LED": 2, "SCL": 4, "SDA": 15, "SS": 22},
"onehorse32dev": {"A1": 37, "A2": 38, "BUTTON": 0, "LED": 5},
"oroca_edubot": {
"A0": 34,
"A1": 39,
"A2": 36,
"A3": 33,
"D0": 4,
"D1": 16,
"D2": 17,
"D3": 22,
"D4": 23,
"D5": 5,
"D6": 18,
"D7": 19,
"D8": 33,
"LED": 13,
"MOSI": 18,
"RX": 16,
"SCK": 5,
"SDA": 23,
"SS": 2,
"TX": 17,
"VBAT": 35,
},
"pico32": {},
"pocket_32": {"LED": 16},
"pycom_gpy": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 17,
},
"quantum": {},
"sparkfun_lora_gateway_1-channel": {"MISO": 12, "MOSI": 13, "SCK": 14, "SS": 16},
"tinypico": {},
"ttgo-lora32-v1": {
"A1": 37,
"A2": 38,
"BUTTON": 0,
"LED": 2,
"MOSI": 27,
"SCK": 5,
"SS": 18,
},
"ttgo-t-beam": {"BUTTON": 39, "LED": 14, "MOSI": 27, "SCK": 5, "SS": 18},
"ttgo-t-watch": {"BUTTON": 36, "MISO": 2, "MOSI": 15, "SCK": 14, "SS": 13},
"ttgo-t1": {"LED": 22, "MISO": 2, "MOSI": 15, "SCK": 14, "SCL": 23, "SS": 13},
"ttgo-t7-v13-mini32": {"LED": 22},
"ttgo-t7-v14-mini32": {"LED": 19},
"turta_iot_node": {},
"vintlabs-devkit-v1": {
"LED": 2,
"PWM0": 12,
"PWM1": 13,
"PWM2": 14,
"PWM3": 15,
"PWM4": 16,
"PWM5": 17,
"PWM6": 18,
"PWM7": 19,
},
"wemos_d1_mini32": {
"D0": 26,
"D1": 22,
"D2": 21,
"D3": 17,
"D4": 16,
"D5": 18,
"D6": 19,
"D7": 23,
"D8": 5,
"LED": 2,
"RXD": 3,
"TXD": 1,
"_VBAT": 35,
},
"wemosbat": {"LED": 16},
"wesp32": {"MISO": 32, "SCL": 4, "SDA": 15},
"widora-air": {
"A1": 39,
"A2": 35,
"A3": 25,
"A4": 26,
"A5": 14,
"A6": 12,
"A7": 15,
"A8": 13,
"A9": 2,
"BUTTON": 0,
"D0": 19,
"D1": 23,
"D2": 18,
"D3": 17,
"D4": 16,
"D5": 5,
"D6": 4,
"LED": 25,
"MISO": 17,
"MOSI": 16,
"SCL": 19,
"SDA": 23,
"T0": 19,
"T1": 23,
"T2": 18,
"T3": 17,
"T4": 16,
"T5": 5,
"T6": 4,
},
"xinabox_cw02": {"LED": 27},
}
ESP32_C3_BASE_PINS = {
"TX": 21,
"RX": 20,
"ADC1_0": 0,
"ADC1_1": 1,
"ADC1_2": 2,
"ADC1_3": 3,
"ADC1_4": 4,
"ADC2_0": 5,
}
ESP32_C3_BOARD_PINS = {
"esp32-c3-devkitm-1": {"LED": 8},
"esp32-c3-devkitc-02": "esp32-c3-devkitm-1",
}

View File

@ -19,8 +19,10 @@ from esphome.cpp_generator import ( # noqa
Statement, Statement,
LineComment, LineComment,
progmem_array, progmem_array,
static_const_array,
statement, statement,
variable, variable,
new_variable,
Pvariable, Pvariable,
new_Pvariable, new_Pvariable,
add, add,
@ -58,6 +60,7 @@ from esphome.cpp_types import ( # noqa
uint8, uint8,
uint16, uint16,
uint32, uint32,
uint64,
int32, int32,
const_char_ptr, const_char_ptr,
NAN, NAN,

View File

@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace a4988 { namespace a4988 {
static const char *TAG = "a4988.stepper"; static const char *const TAG = "a4988.stepper";
void A4988::setup() { void A4988::setup() {
ESP_LOGCONFIG(TAG, "Setting up A4988..."); ESP_LOGCONFIG(TAG, "Setting up A4988...");

View File

@ -18,16 +18,16 @@ CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield stepper.register_stepper(var, config) await stepper.register_stepper(var, config)
step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN]) step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
cg.add(var.set_step_pin(step_pin)) cg.add(var.set_step_pin(step_pin))
dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN]) dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
cg.add(var.set_dir_pin(dir_pin)) cg.add(var.set_dir_pin(dir_pin))
if CONF_SLEEP_PIN in config: if CONF_SLEEP_PIN in config:
sleep_pin = yield cg.gpio_pin_expression(config[CONF_SLEEP_PIN]) sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
cg.add(var.set_sleep_pin(sleep_pin)) cg.add(var.set_sleep_pin(sleep_pin))

View File

@ -9,15 +9,15 @@
namespace esphome { namespace esphome {
namespace ac_dimmer { namespace ac_dimmer {
static const char *TAG = "ac_dimmer"; static const char *const TAG = "ac_dimmer";
// Global array to store dimmer objects // Global array to store dimmer objects
static AcDimmerDataStore *all_dimmers[32]; static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
/// 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 uint32_t GATE_ENABLE_TIME = 10; static const uint32_t GATE_ENABLE_TIME = 10;
/// Function called from timer interrupt /// Function called from timer interrupt
/// Input is current time in microseconds (micros()) /// Input is current time in microseconds (micros())
@ -125,7 +125,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
} }
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) { void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
// Attaching pin interrupts on the same pin will override the previous interupt // 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
// if any of them are using the same ZC pin, and also trigger the interrupt for *them*. // if any of them are using the same ZC pin, and also trigger the interrupt for *them*.

View File

@ -32,18 +32,18 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
# override default min power to 10% # override default min power to 10%
if CONF_MIN_POWER not in config: if CONF_MIN_POWER not in config:
config[CONF_MIN_POWER] = 0.1 config[CONF_MIN_POWER] = 0.1
yield output.register_output(var, config) await output.register_output(var, config)
pin = yield cg.gpio_pin_expression(config[CONF_GATE_PIN]) pin = await cg.gpio_pin_expression(config[CONF_GATE_PIN])
cg.add(var.set_gate_pin(pin)) cg.add(var.set_gate_pin(pin))
pin = yield cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN]) pin = await cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
cg.add(var.set_zero_cross_pin(pin)) cg.add(var.set_zero_cross_pin(pin))
cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE])) cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
cg.add(var.set_method(config[CONF_METHOD])) cg.add(var.set_method(config[CONF_METHOD]))

View File

@ -21,8 +21,7 @@ CONFIG_SCHEMA = cv.Schema({})
"Adalight", "Adalight",
{cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)}, {cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)},
) )
def adalight_light_effect_to_code(config, effect_id): async def adalight_light_effect_to_code(config, effect_id):
effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
yield uart.register_uart_device(effect, config) await uart.register_uart_device(effect, config)
return effect
yield effect

View File

@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace adalight { namespace adalight {
static const char *TAG = "adalight_light_effect"; static const char *const TAG = "adalight_light_effect";
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000; static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000; static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;

View File

@ -8,10 +8,50 @@ ADC_MODE(ADC_VCC)
namespace esphome { namespace esphome {
namespace adc { namespace adc {
static const char *TAG = "adc"; static const char *const TAG = "adc";
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
void ADCSensor::set_attenuation(adc_attenuation_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) {
#if CONFIG_IDF_TARGET_ESP32
switch (pin) {
case 36:
return ADC1_CHANNEL_0;
case 37:
return ADC1_CHANNEL_1;
case 38:
return ADC1_CHANNEL_2;
case 39:
return ADC1_CHANNEL_3;
case 32:
return ADC1_CHANNEL_4;
case 33:
return ADC1_CHANNEL_5;
case 34:
return ADC1_CHANNEL_6;
case 35:
return ADC1_CHANNEL_7;
default:
return ADC1_CHANNEL_MAX;
}
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
switch (pin) {
case 0:
return ADC1_CHANNEL_0;
case 1:
return ADC1_CHANNEL_1;
case 2:
return ADC1_CHANNEL_2;
case 3:
return ADC1_CHANNEL_3;
case 4:
return ADC1_CHANNEL_4;
default:
return ADC1_CHANNEL_MAX;
}
#endif
}
#endif #endif
void ADCSensor::setup() { void ADCSensor::setup() {
@ -21,7 +61,11 @@ void ADCSensor::setup() {
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
analogSetPinAttenuation(this->pin_, this->attenuation_); adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
adc1_config_width(ADC_WIDTH_BIT_12);
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
#endif
#endif #endif
} }
void ADCSensor::dump_config() { void ADCSensor::dump_config() {
@ -36,18 +80,20 @@ void ADCSensor::dump_config() {
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
switch (this->attenuation_) { switch (this->attenuation_) {
case ADC_0db: case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)"); ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
break; break;
case ADC_2_5db: case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)"); ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
break; break;
case ADC_6db: case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)"); ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
break; break;
case ADC_11db: case ADC_ATTEN_DB_11:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)"); ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
break; break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
} }
#endif #endif
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
@ -60,21 +106,43 @@ void ADCSensor::update() {
} }
float ADCSensor::sample() { float ADCSensor::sample() {
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
float value_v = analogRead(this->pin_) / 4095.0f; // NOLINT int raw = adc1_get_raw(gpio_to_adc1(pin_));
float value_v = raw / 4095.0f;
#if CONFIG_IDF_TARGET_ESP32
switch (this->attenuation_) { switch (this->attenuation_) {
case ADC_0db: case ADC_ATTEN_DB_0:
value_v *= 1.1; value_v *= 1.1;
break; break;
case ADC_2_5db: case ADC_ATTEN_DB_2_5:
value_v *= 1.5; value_v *= 1.5;
break; break;
case ADC_6db: case ADC_ATTEN_DB_6:
value_v *= 2.2; value_v *= 2.2;
break; break;
case ADC_11db: case ADC_ATTEN_DB_11:
value_v *= 3.9; value_v *= 3.9;
break; break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
} }
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
value_v *= 0.84;
break;
case ADC_ATTEN_DB_2_5:
value_v *= 1.13;
break;
case ADC_ATTEN_DB_6:
value_v *= 1.56;
break;
case ADC_ATTEN_DB_11:
value_v *= 3.0;
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
#endif
return value_v; return value_v;
#endif #endif

View File

@ -6,6 +6,10 @@
#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
#include "driver/adc.h"
#endif
namespace esphome { namespace esphome {
namespace adc { namespace adc {
@ -13,7 +17,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
public: public:
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_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_attenuation_t attenuation); void set_attenuation(adc_atten_t attenuation);
#endif #endif
/// Update adc values. /// Update adc values.
@ -34,7 +38,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
uint8_t pin_; uint8_t pin_;
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
adc_attenuation_t attenuation_{ADC_0db}; adc_atten_t attenuation_{ADC_ATTEN_DB_0};
#endif #endif
}; };

View File

@ -7,7 +7,7 @@ from esphome.const import (
CONF_ID, CONF_ID,
CONF_PIN, CONF_PIN,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ICON_EMPTY, STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
) )
@ -15,10 +15,10 @@ from esphome.const import (
AUTO_LOAD = ["voltage_sampler"] AUTO_LOAD = ["voltage_sampler"]
ATTENUATION_MODES = { ATTENUATION_MODES = {
"0db": cg.global_ns.ADC_0db, "0db": cg.global_ns.ADC_ATTEN_DB_0,
"2.5db": cg.global_ns.ADC_2_5db, "2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
"6db": cg.global_ns.ADC_6db, "6db": cg.global_ns.ADC_ATTEN_DB_6,
"11db": cg.global_ns.ADC_11db, "11db": cg.global_ns.ADC_ATTEN_DB_11,
} }
@ -35,7 +35,12 @@ ADCSensor = adc_ns.class_(
) )
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE) sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend( .extend(
{ {
cv.GenerateID(): cv.declare_id(ADCSensor), cv.GenerateID(): cv.declare_id(ADCSensor),
@ -49,10 +54,10 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield sensor.register_sensor(var, config) await sensor.register_sensor(var, 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")

View File

@ -0,0 +1,67 @@
#include "addressable_light_display.h"
#include "esphome/core/log.h"
namespace esphome {
namespace addressable_light {
static const char *const TAG = "addressable_light.display";
int AddressableLightDisplay::get_width_internal() { return this->width_; }
int AddressableLightDisplay::get_height_internal() { return this->height_; }
void AddressableLightDisplay::setup() {
this->addressable_light_buffer_.resize(this->width_ * this->height_, {0, 0, 0, 0});
}
void AddressableLightDisplay::update() {
if (!this->enabled_)
return;
this->do_update_();
this->display();
}
void AddressableLightDisplay::display() {
bool dirty = false;
uint8_t old_r, old_g, old_b, old_w;
Color *c;
for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
c = &(this->addressable_light_buffer_[offset]);
light::ESPColorView pixel = (*this->light_)[offset];
// Track the original values for the pixel view. If it has changed updating, then
// we trigger a redraw. Avoiding redraws == avoiding flicker!
old_r = pixel.get_red();
old_g = pixel.get_green();
old_b = pixel.get_blue();
old_w = pixel.get_white();
pixel.set_rgbw(c->r, c->g, c->b, c->w);
// If the actual value of the pixel changed, then schedule a redraw.
if (pixel.get_red() != old_r || pixel.get_green() != old_g || pixel.get_blue() != old_b ||
pixel.get_white() != old_w) {
dirty = true;
}
}
if (dirty) {
this->light_->schedule_show();
}
}
void HOT AddressableLightDisplay::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
return;
if (this->pixel_mapper_f_.has_value()) {
// Params are passed by reference, so they may be modified in call.
this->addressable_light_buffer_[(*this->pixel_mapper_f_)(x, y)] = color;
} else {
this->addressable_light_buffer_[y * this->get_width_internal() + x] = color;
}
}
} // namespace addressable_light
} // namespace esphome

View File

@ -0,0 +1,59 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/color.h"
#include "esphome/components/display/display_buffer.h"
#include "esphome/components/light/addressable_light.h"
namespace esphome {
namespace addressable_light {
class AddressableLightDisplay : public display::DisplayBuffer, public PollingComponent {
public:
light::AddressableLight *get_light() const { return this->light_; }
void set_width(int32_t width) { width_ = width; }
void set_height(int32_t height) { height_ = height; }
void set_light(light::LightState *state) {
light_state_ = state;
light_ = static_cast<light::AddressableLight *>(state->get_output());
}
void set_enabled(bool enabled) {
if (light_state_) {
if (enabled_ && !enabled) { // enabled -> disabled
// - Tell the parent light to refresh, effectively wiping the display. Also
// restores the previous effect (if any).
light_state_->make_call().set_effect(this->last_effect_).perform();
} else if (!enabled_ && enabled) { // disabled -> enabled
// - Save the current effect.
this->last_effect_ = light_state_->get_effect_name();
// - Disable any current effect.
light_state_->make_call().set_effect(0).perform();
}
}
enabled_ = enabled;
}
bool get_enabled() { return enabled_; }
void set_pixel_mapper(std::function<int(int, int)> &&pixel_mapper_f) { this->pixel_mapper_f_ = pixel_mapper_f; }
void setup() override;
void display();
protected:
int get_width_internal() override;
int get_height_internal() override;
void draw_absolute_pixel_internal(int x, int y, Color color) override;
void update() override;
light::LightState *light_state_;
light::AddressableLight *light_;
bool enabled_{true};
int32_t width_;
int32_t height_;
std::vector<Color> addressable_light_buffer_;
optional<std::string> last_effect_;
optional<std::function<int(int, int)>> pixel_mapper_f_;
};
} // namespace addressable_light
} // namespace esphome

View File

@ -0,0 +1,63 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import display, light
from esphome.const import (
CONF_ID,
CONF_LAMBDA,
CONF_PAGES,
CONF_ADDRESSABLE_LIGHT_ID,
CONF_HEIGHT,
CONF_WIDTH,
CONF_UPDATE_INTERVAL,
CONF_PIXEL_MAPPER,
)
CODEOWNERS = ["@justfalter"]
addressable_light_ns = cg.esphome_ns.namespace("addressable_light")
AddressableLightDisplay = addressable_light_ns.class_(
"AddressableLightDisplay", display.DisplayBuffer, cg.PollingComponent
)
CONFIG_SCHEMA = cv.All(
display.FULL_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AddressableLightDisplay),
cv.Required(CONF_ADDRESSABLE_LIGHT_ID): cv.use_id(
light.AddressableLightState
),
cv.Required(CONF_WIDTH): cv.positive_int,
cv.Required(CONF_HEIGHT): cv.positive_int,
cv.Optional(
CONF_UPDATE_INTERVAL, default="16ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_PIXEL_MAPPER): cv.returning_lambda,
}
),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
wrapped_light = await cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
cg.add(var.set_width(config[CONF_WIDTH]))
cg.add(var.set_height(config[CONF_HEIGHT]))
cg.add(var.set_light(wrapped_light))
await cg.register_component(var, config)
await display.register_display(var, config)
if CONF_PIXEL_MAPPER in config:
pixel_mapper_template_ = await cg.process_lambda(
config[CONF_PIXEL_MAPPER],
[(int, "x"), (int, "y")],
return_type=cg.int_,
)
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda(
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))

View File

@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace ade7953 { namespace ade7953 {
static const char *TAG = "ade7953"; static const char *const TAG = "ade7953";
void ADE7953::dump_config() { void ADE7953::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7953:"); ESP_LOGCONFIG(TAG, "ADE7953:");
@ -21,8 +21,8 @@ void ADE7953::dump_config() {
} }
#define ADE_PUBLISH_(name, factor) \ #define ADE_PUBLISH_(name, factor) \
if (name && this->name##_sensor_) { \ if ((name) && this->name##_sensor_) { \
float value = *name / factor; \ float value = *(name) / (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, factor) ADE_PUBLISH_(name, factor)

View File

@ -8,7 +8,7 @@ from esphome.const import (
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ICON_EMPTY, STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_WATT, UNIT_WATT,
@ -31,19 +31,34 @@ CONFIG_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.input_pin,
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE unit_of_measurement=UNIT_VOLT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema( cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema( cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema( cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema( cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
), ),
} }
) )
@ -52,10 +67,10 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield 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])) cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
@ -70,5 +85,5 @@ def to_code(config):
if key not in config: if key not in config:
continue continue
conf = config[key] conf = config[key]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(getattr(var, f"set_{key}_sensor")(sens)) cg.add(getattr(var, f"set_{key}_sensor")(sens))

View File

@ -23,9 +23,9 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE])) cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))

View File

@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace ads1115 { namespace ads1115 {
static const char *TAG = "ads1115"; static const char *const TAG = "ads1115";
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00; static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01; static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
@ -64,11 +64,6 @@ void ADS1115Component::setup() {
return; return;
} }
this->prev_config_ = config; this->prev_config_ = config;
for (auto *sensor : this->sensors_) {
this->set_interval(sensor->get_name(), sensor->update_interval(),
[this, sensor] { this->request_measurement(sensor); });
}
} }
void ADS1115Component::dump_config() { void ADS1115Component::dump_config() {
ESP_LOGCONFIG(TAG, "Setting up ADS1115..."); ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
@ -107,17 +102,22 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
} }
this->prev_config_ = config; this->prev_config_ = config;
// about 1.6 ms with 860 samples per second // about 1.2 ms with 860 samples per second
delay(2); delay(2);
uint32_t start = millis(); // in continuous mode, conversion will always be running, rely on the delay
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) { // to ensure conversion is taking place with the correct settings
if (millis() - start > 100) { // can we use the rdy pin to trigger when a conversion is done?
ESP_LOGW(TAG, "Reading ADS1115 timed out"); if (!this->continuous_mode_) {
this->status_set_warning(); uint32_t start = millis();
return NAN; while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
if (millis() - start > 100) {
ESP_LOGW(TAG, "Reading ADS1115 timed out");
this->status_set_warning();
return NAN;
}
yield();
} }
yield();
} }
} }

View File

@ -5,7 +5,7 @@ from esphome.const import (
CONF_GAIN, CONF_GAIN,
CONF_MULTIPLEXER, CONF_MULTIPLEXER,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ICON_EMPTY, STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
CONF_ID, CONF_ID,
) )
@ -51,7 +51,12 @@ ADS1115Sensor = ads1115_ns.class_(
CONF_ADS1115_ID = "ads1115_id" CONF_ADS1115_ID = "ads1115_id"
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE) sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend( .extend(
{ {
cv.GenerateID(): cv.declare_id(ADS1115Sensor), cv.GenerateID(): cv.declare_id(ADS1115Sensor),
@ -64,11 +69,11 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
paren = yield cg.get_variable(config[CONF_ADS1115_ID]) paren = await cg.get_variable(config[CONF_ADS1115_ID])
var = cg.new_Pvariable(config[CONF_ID], paren) var = cg.new_Pvariable(config[CONF_ID], paren)
yield sensor.register_sensor(var, config) await sensor.register_sensor(var, config)
yield cg.register_component(var, config) await cg.register_component(var, config)
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
cg.add(var.set_gain(config[CONF_GAIN])) cg.add(var.set_gain(config[CONF_GAIN]))

View File

@ -10,7 +10,7 @@
// //
// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost // According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best // immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
// results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time. // results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
#include "aht10.h" #include "aht10.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@ -18,12 +18,12 @@
namespace esphome { namespace esphome {
namespace aht10 { namespace aht10 {
static const char *TAG = "aht10"; static const char *const TAG = "aht10";
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1}; static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1};
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00}; static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
static const uint8_t AHT10_ATTEMPS = 3; // safety margin, normally 3 attemps are enough: 3*30=90ms static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
void AHT10Component::setup() { void AHT10Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AHT10..."); ESP_LOGCONFIG(TAG, "Setting up AHT10...");
@ -58,8 +58,9 @@ void AHT10Component::update() {
uint8_t delay = AHT10_DEFAULT_DELAY; uint8_t delay = AHT10_DEFAULT_DELAY;
if (this->humidity_sensor_ != nullptr) if (this->humidity_sensor_ != nullptr)
delay = AHT10_HUMIDITY_DELAY; delay = AHT10_HUMIDITY_DELAY;
for (int i = 0; i < AHT10_ATTEMPS; ++i) { for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis()); ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
delay_microseconds_accurate(4);
if (!this->read_bytes(0, data, 6, delay)) { if (!this->read_bytes(0, data, 6, delay)) {
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 } else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy

View File

@ -7,7 +7,7 @@ from esphome.const import (
CONF_TEMPERATURE, CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_PERCENT, UNIT_PERCENT,
) )
@ -22,10 +22,16 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(AHT10Component), cv.GenerateID(): cv.declare_id(AHT10Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 2, DEVICE_CLASS_HUMIDITY unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
), ),
} }
) )
@ -34,15 +40,15 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature_sensor(sens)) cg.add(var.set_temperature_sensor(sens))
if CONF_HUMIDITY in config: if CONF_HUMIDITY in config:
sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity_sensor(sens)) cg.add(var.set_humidity_sensor(sens))

View File

@ -9,7 +9,7 @@
namespace esphome { namespace esphome {
namespace am2320 { namespace am2320 {
static const char *TAG = "am2320"; static const char *const TAG = "am2320";
// ---=== Calc CRC16 ===--- // ---=== Calc CRC16 ===---
uint16_t crc_16(uint8_t *ptr, uint8_t length) { uint16_t crc_16(uint8_t *ptr, uint8_t length) {

View File

@ -7,8 +7,8 @@ from esphome.const import (
CONF_TEMPERATURE, CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
ICON_EMPTY,
UNIT_PERCENT, UNIT_PERCENT,
) )
@ -24,10 +24,16 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(AM2320Component), cv.GenerateID(): cv.declare_id(AM2320Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
), ),
} }
) )
@ -36,15 +42,15 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature_sensor(sens)) cg.add(var.set_temperature_sensor(sens))
if CONF_HUMIDITY in config: if CONF_HUMIDITY in config:
sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity_sensor(sens)) cg.add(var.set_humidity_sensor(sens))

View File

@ -34,7 +34,7 @@ CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
CODEOWNERS = ["@syndlex"] CODEOWNERS = ["@syndlex"]
def to_code(config): async def to_code(config):
from PIL import Image from PIL import Image
path = CORE.relative_config_path(config[CONF_FILE]) path = CORE.relative_config_path(config[CONF_FILE])

View File

@ -0,0 +1,141 @@
#include "anova.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace anova {
static const char *TAG = "anova";
using namespace esphome::climate;
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
void Anova::setup() {
this->codec_ = new AnovaCodec();
this->current_request_ = 0;
}
void Anova::loop() {}
void Anova::control(const ClimateCall &call) {
if (call.get_mode().has_value()) {
ClimateMode mode = *call.get_mode();
AnovaPacket *pkt;
switch (mode) {
case climate::CLIMATE_MODE_OFF:
pkt = this->codec_->get_stop_request();
break;
case climate::CLIMATE_MODE_HEAT:
pkt = this->codec_->get_start_request();
break;
default:
ESP_LOGW(TAG, "Unsupported mode: %d", mode);
return;
}
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);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
}
if (call.get_target_temperature().has_value()) {
auto pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
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);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
}
}
void Anova::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_DISCONNECT_EVT: {
this->current_temperature = NAN;
this->target_temperature = NAN;
this->publish_state();
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
break;
}
this->char_handle_ = chr->handle;
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
}
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established;
this->current_request_ = 0;
this->update();
break;
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.handle != this->char_handle_)
break;
this->codec_->decode(param->notify.value, param->notify.value_len);
if (this->codec_->has_target_temp()) {
this->target_temperature = this->codec_->target_temp_;
}
if (this->codec_->has_current_temp()) {
this->current_temperature = this->codec_->current_temp_;
}
if (this->codec_->has_running()) {
this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF;
}
this->publish_state();
if (this->current_request_ > 0) {
AnovaPacket *pkt = nullptr;
switch (this->current_request_++) {
case 1:
pkt = this->codec_->get_read_target_temp_request();
break;
case 2:
pkt = this->codec_->get_read_current_temp_request();
break;
default:
this->current_request_ = 0;
break;
}
if (pkt != nullptr) {
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);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
status);
}
}
break;
}
default:
break;
}
}
void Anova::update() {
if (this->node_state != espbt::ClientState::Established)
return;
if (this->current_request_ == 0) {
auto pkt = this->codec_->get_read_device_status_request();
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);
if (status)
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
this->current_request_++;
}
}
} // namespace anova
} // namespace esphome
#endif

View File

@ -0,0 +1,50 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/climate/climate.h"
#include "anova_base.h"
#ifdef ARDUINO_ARCH_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace anova {
namespace espbt = esphome::esp32_ble_tracker;
static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0;
static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1;
class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
void setup() override;
void loop() override;
void update() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
climate::ClimateTraits traits() {
auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true);
traits.set_supports_heat_mode(true);
traits.set_visual_min_temperature(25.0);
traits.set_visual_max_temperature(100.0);
traits.set_visual_temperature_step(0.1);
return traits;
}
protected:
AnovaCodec *codec_;
void control(const climate::ClimateCall &call) override;
uint16_t char_handle_;
uint8_t current_request_;
};
} // namespace anova
} // namespace esphome
#endif

View File

@ -0,0 +1,119 @@
#include "anova_base.h"
namespace esphome {
namespace anova {
AnovaPacket *AnovaCodec::clean_packet_() {
this->packet_.length = strlen((char *) this->packet_.data);
this->packet_.data[this->packet_.length] = '\0';
ESP_LOGV("anova", "SendPkt: %s\n", this->packet_.data);
return &this->packet_;
}
AnovaPacket *AnovaCodec::get_read_device_status_request() {
this->current_query_ = READ_DEVICE_STATUS;
sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_target_temp_request() {
this->current_query_ = READ_TARGET_TEMPERATURE;
sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_current_temp_request() {
this->current_query_ = READ_CURRENT_TEMPERATURE;
sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_unit_request() {
this->current_query_ = READ_UNIT;
sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_read_data_request() {
this->current_query_ = READ_DATA;
sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
this->current_query_ = SET_TARGET_TEMPERATURE;
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_set_unit_request(char unit) {
this->current_query_ = SET_UNIT;
sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_start_request() {
this->current_query_ = START;
sprintf((char *) this->packet_.data, CMD_START);
return this->clean_packet_();
}
AnovaPacket *AnovaCodec::get_stop_request() {
this->current_query_ = STOP;
sprintf((char *) this->packet_.data, CMD_STOP);
return this->clean_packet_();
}
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
memset(this->buf_, 0, 32);
strncpy(this->buf_, (char *) data, length);
ESP_LOGV("anova", "Received: %s\n", this->buf_);
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
switch (this->current_query_) {
case READ_DEVICE_STATUS: {
if (!strncmp(this->buf_, "stopped", 7)) {
this->has_running_ = true;
this->running_ = false;
}
if (!strncmp(this->buf_, "running", 7)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case START: {
if (!strncmp(this->buf_, "start", 5)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case STOP: {
if (!strncmp(this->buf_, "stop", 4)) {
this->has_running_ = true;
this->running_ = false;
}
break;
}
case READ_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr);
this->has_target_temp_ = true;
break;
}
case SET_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr);
this->has_target_temp_ = true;
break;
}
case READ_CURRENT_TEMPERATURE: {
this->current_temp_ = strtof(this->buf_, nullptr);
this->has_current_temp_ = true;
break;
}
default:
break;
}
}
} // namespace anova
} // namespace esphome

View File

@ -0,0 +1,79 @@
#pragma once
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace anova {
enum CurrentQuery {
NONE,
READ_DEVICE_STATUS,
READ_TARGET_TEMPERATURE,
READ_CURRENT_TEMPERATURE,
READ_DATA,
READ_UNIT,
SET_TARGET_TEMPERATURE,
SET_UNIT,
START,
STOP,
};
struct AnovaPacket {
uint16_t length;
uint8_t data[24];
};
#define CMD_READ_DEVICE_STATUS "status\r"
#define CMD_READ_TARGET_TEMP "read set temp\r"
#define CMD_READ_CURRENT_TEMP "read temp\r"
#define CMD_READ_UNIT "read unit\r"
#define CMD_READ_DATA "read data\r"
#define CMD_SET_TARGET_TEMP "set temp %.1f\r"
#define CMD_SET_TEMP_UNIT "set unit %c\r"
#define CMD_START "start\r"
#define CMD_STOP "stop\r"
class AnovaCodec {
public:
AnovaPacket *get_read_device_status_request();
AnovaPacket *get_read_target_temp_request();
AnovaPacket *get_read_current_temp_request();
AnovaPacket *get_read_data_request();
AnovaPacket *get_read_unit_request();
AnovaPacket *get_set_target_temp_request(float temperature);
AnovaPacket *get_set_unit_request(char unit);
AnovaPacket *get_start_request();
AnovaPacket *get_stop_request();
void decode(const uint8_t *data, uint16_t length);
bool has_target_temp() { return this->has_target_temp_; }
bool has_current_temp() { return this->has_current_temp_; }
bool has_unit() { return this->has_unit_; }
bool has_running() { return this->has_running_; }
union {
float target_temp_;
float current_temp_;
char unit_;
bool running_;
};
protected:
AnovaPacket *clean_packet_();
AnovaPacket packet_;
bool has_target_temp_;
bool has_current_temp_;
bool has_unit_;
bool has_running_;
char buf_[32];
CurrentQuery current_query_;
};
} // namespace anova
} // namespace esphome

View File

@ -0,0 +1,25 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import climate, ble_client
from esphome.const import CONF_ID
CODEOWNERS = ["@buxtronix"]
DEPENDENCIES = ["ble_client"]
anova_ns = cg.esphome_ns.namespace("anova")
Anova = anova_ns.class_(
"Anova", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
)
CONFIG_SCHEMA = (
climate.CLIMATE_SCHEMA.extend({cv.GenerateID(): cv.declare_id(Anova)})
.extend(ble_client.BLE_CLIENT_SCHEMA)
.extend(cv.polling_component_schema("60s"))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await climate.register_climate(var, config)
await ble_client.register_ble_node(var, config)

View File

@ -23,7 +23,7 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)

View File

@ -4,10 +4,10 @@
namespace esphome { namespace esphome {
namespace apds9960 { namespace apds9960 {
static const char *TAG = "apds9960"; static const char *const TAG = "apds9960";
#define APDS9960_ERROR_CHECK(func) \ #define APDS9960_ERROR_CHECK(func) \
if (!func) { \ if (!(func)) { \
this->mark_failed(); \ this->mark_failed(); \
return; \ return; \
} }

View File

@ -24,8 +24,8 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
) )
def to_code(config): async def to_code(config):
hub = yield cg.get_variable(config[CONF_APDS9960_ID]) hub = await cg.get_variable(config[CONF_APDS9960_ID])
var = yield binary_sensor.new_binary_sensor(config) var = await binary_sensor.new_binary_sensor(config)
func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]]) func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
cg.add(func(var)) cg.add(func(var))

View File

@ -1,7 +1,12 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
from esphome.const import CONF_TYPE, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_LIGHTBULB from esphome.const import (
CONF_TYPE,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
ICON_LIGHTBULB,
)
from . import APDS9960, CONF_APDS9960_ID from . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ["apds9960"] DEPENDENCIES = ["apds9960"]
@ -15,7 +20,10 @@ TYPES = {
} }
CONFIG_SCHEMA = sensor.sensor_schema( CONFIG_SCHEMA = sensor.sensor_schema(
UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY unit_of_measurement=UNIT_PERCENT,
icon=ICON_LIGHTBULB,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True), cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
@ -24,8 +32,8 @@ CONFIG_SCHEMA = sensor.sensor_schema(
) )
def to_code(config): async def to_code(config):
hub = yield cg.get_variable(config[CONF_APDS9960_ID]) hub = await cg.get_variable(config[CONF_APDS9960_ID])
var = yield sensor.new_sensor(config) var = await sensor.new_sensor(config)
func = getattr(hub, TYPES[config[CONF_TYPE]]) func = getattr(hub, TYPES[config[CONF_TYPE]])
cg.add(func(var)) cg.add(func(var))

View File

@ -68,9 +68,9 @@ CONFIG_SCHEMA = cv.Schema(
@coroutine_with_priority(40.0) @coroutine_with_priority(40.0)
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
cg.add(var.set_port(config[CONF_PORT])) cg.add(var.set_port(config[CONF_PORT]))
cg.add(var.set_password(config[CONF_PASSWORD])) cg.add(var.set_password(config[CONF_PASSWORD]))
@ -90,7 +90,7 @@ def to_code(config):
conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
) )
cg.add(var.register_user_service(trigger)) cg.add(var.register_user_service(trigger))
yield automation.build_automation(trigger, func_args, conf) await automation.build_automation(trigger, func_args, conf)
cg.add_define("USE_API") cg.add_define("USE_API")
cg.add_global(api_ns.using) cg.add_global(api_ns.using)
@ -116,21 +116,21 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
HomeAssistantServiceCallAction, HomeAssistantServiceCallAction,
HOMEASSISTANT_SERVICE_ACTION_SCHEMA, HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
) )
def homeassistant_service_to_code(config, action_id, template_arg, args): async def homeassistant_service_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID]) serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, False) var = cg.new_Pvariable(action_id, template_arg, serv, False)
templ = yield cg.templatable(config[CONF_SERVICE], args, None) templ = await cg.templatable(config[CONF_SERVICE], args, None)
cg.add(var.set_service(templ)) cg.add(var.set_service(templ))
for key, value in config[CONF_DATA].items(): for key, value in config[CONF_DATA].items():
templ = yield cg.templatable(value, args, None) templ = await cg.templatable(value, args, None)
cg.add(var.add_data(key, templ)) cg.add(var.add_data(key, templ))
for key, value in config[CONF_DATA_TEMPLATE].items(): for key, value in config[CONF_DATA_TEMPLATE].items():
templ = yield cg.templatable(value, args, None) templ = await cg.templatable(value, args, None)
cg.add(var.add_data_template(key, templ)) cg.add(var.add_data_template(key, templ))
for key, value in config[CONF_VARIABLES].items(): for key, value in config[CONF_VARIABLES].items():
templ = yield cg.templatable(value, args, None) templ = await cg.templatable(value, args, None)
cg.add(var.add_variable(key, templ)) cg.add(var.add_variable(key, templ))
yield var return var
def validate_homeassistant_event(value): def validate_homeassistant_event(value):
@ -159,21 +159,21 @@ HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
HomeAssistantServiceCallAction, HomeAssistantServiceCallAction,
HOMEASSISTANT_EVENT_ACTION_SCHEMA, HOMEASSISTANT_EVENT_ACTION_SCHEMA,
) )
def homeassistant_event_to_code(config, action_id, template_arg, args): async def homeassistant_event_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID]) serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, True) var = cg.new_Pvariable(action_id, template_arg, serv, True)
templ = yield cg.templatable(config[CONF_EVENT], args, None) templ = await cg.templatable(config[CONF_EVENT], args, None)
cg.add(var.set_service(templ)) cg.add(var.set_service(templ))
for key, value in config[CONF_DATA].items(): for key, value in config[CONF_DATA].items():
templ = yield cg.templatable(value, args, None) templ = await cg.templatable(value, args, None)
cg.add(var.add_data(key, templ)) cg.add(var.add_data(key, templ))
for key, value in config[CONF_DATA_TEMPLATE].items(): for key, value in config[CONF_DATA_TEMPLATE].items():
templ = yield cg.templatable(value, args, None) templ = await cg.templatable(value, args, None)
cg.add(var.add_data_template(key, templ)) cg.add(var.add_data_template(key, templ))
for key, value in config[CONF_VARIABLES].items(): for key, value in config[CONF_VARIABLES].items():
templ = yield cg.templatable(value, args, None) templ = await cg.templatable(value, args, None)
cg.add(var.add_variable(key, templ)) cg.add(var.add_variable(key, templ))
yield var return var
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value( HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
@ -190,15 +190,15 @@ HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
HomeAssistantServiceCallAction, HomeAssistantServiceCallAction,
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA, HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
) )
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args): async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID]) serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, True) var = cg.new_Pvariable(action_id, template_arg, serv, True)
cg.add(var.set_service("esphome.tag_scanned")) cg.add(var.set_service("esphome.tag_scanned"))
templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string) templ = await cg.templatable(config[CONF_TAG], args, cg.std_string)
cg.add(var.add_data("tag_id", templ)) cg.add(var.add_data("tag_id", templ))
yield var return var
@automation.register_condition("api.connected", APIConnectedCondition, {}) @automation.register_condition("api.connected", APIConnectedCondition, {})
def api_connected_to_code(config, condition_id, template_arg, args): async def api_connected_to_code(config, condition_id, template_arg, args):
yield cg.new_Pvariable(condition_id, template_arg) return cg.new_Pvariable(condition_id, template_arg)

View File

@ -38,6 +38,8 @@ service APIConnection {
rpc switch_command (SwitchCommandRequest) returns (void) {} rpc switch_command (SwitchCommandRequest) returns (void) {}
rpc camera_image (CameraImageRequest) returns (void) {} rpc camera_image (CameraImageRequest) returns (void) {}
rpc climate_command (ClimateCommandRequest) returns (void) {} rpc climate_command (ClimateCommandRequest) returns (void) {}
rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {}
} }
@ -176,6 +178,10 @@ message DeviceInfoResponse {
string model = 6; string model = 6;
bool has_deep_sleep = 7; bool has_deep_sleep = 7;
// The esphome project details if set
string project_name = 8;
string project_version = 9;
} }
message ListEntitiesRequest { message ListEntitiesRequest {
@ -303,6 +309,7 @@ message ListEntitiesFanResponse {
bool supports_oscillation = 5; bool supports_oscillation = 5;
bool supports_speed = 6; bool supports_speed = 6;
bool supports_direction = 7; bool supports_direction = 7;
int32 supported_speed_count = 8;
} }
enum FanSpeed { enum FanSpeed {
FAN_SPEED_LOW = 0; FAN_SPEED_LOW = 0;
@ -322,8 +329,9 @@ message FanStateResponse {
fixed32 key = 1; fixed32 key = 1;
bool state = 2; bool state = 2;
bool oscillating = 3; bool oscillating = 3;
FanSpeed speed = 4; FanSpeed speed = 4 [deprecated = true];
FanDirection direction = 5; FanDirection direction = 5;
int32 speed_level = 6;
} }
message FanCommandRequest { message FanCommandRequest {
option (id) = 31; option (id) = 31;
@ -334,15 +342,29 @@ message FanCommandRequest {
fixed32 key = 1; fixed32 key = 1;
bool has_state = 2; bool has_state = 2;
bool state = 3; bool state = 3;
bool has_speed = 4; bool has_speed = 4 [deprecated = true];
FanSpeed speed = 5; FanSpeed speed = 5 [deprecated = true];
bool has_oscillating = 6; bool has_oscillating = 6;
bool oscillating = 7; bool oscillating = 7;
bool has_direction = 8; bool has_direction = 8;
FanDirection direction = 9; FanDirection direction = 9;
bool has_speed_level = 10;
int32 speed_level = 11;
} }
// ==================== LIGHT ==================== // ==================== LIGHT ====================
enum ColorMode {
COLOR_MODE_UNKNOWN = 0;
COLOR_MODE_ON_OFF = 1;
COLOR_MODE_BRIGHTNESS = 2;
COLOR_MODE_WHITE = 7;
COLOR_MODE_COLOR_TEMPERATURE = 11;
COLOR_MODE_COLD_WARM_WHITE = 19;
COLOR_MODE_RGB = 35;
COLOR_MODE_RGB_WHITE = 39;
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47;
COLOR_MODE_RGB_COLD_WARM_WHITE = 51;
}
message ListEntitiesLightResponse { message ListEntitiesLightResponse {
option (id) = 15; option (id) = 15;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@ -353,10 +375,12 @@ message ListEntitiesLightResponse {
string name = 3; string name = 3;
string unique_id = 4; string unique_id = 4;
bool supports_brightness = 5; repeated ColorMode supported_color_modes = 12;
bool supports_rgb = 6; // next four supports_* are for legacy clients, newer clients should use color modes
bool supports_white_value = 7; bool legacy_supports_brightness = 5 [deprecated=true];
bool supports_color_temperature = 8; bool legacy_supports_rgb = 6 [deprecated=true];
bool legacy_supports_white_value = 7 [deprecated=true];
bool legacy_supports_color_temperature = 8 [deprecated=true];
float min_mireds = 9; float min_mireds = 9;
float max_mireds = 10; float max_mireds = 10;
repeated string effects = 11; repeated string effects = 11;
@ -370,11 +394,15 @@ message LightStateResponse {
fixed32 key = 1; fixed32 key = 1;
bool state = 2; bool state = 2;
float brightness = 3; float brightness = 3;
ColorMode color_mode = 11;
float color_brightness = 10;
float red = 4; float red = 4;
float green = 5; float green = 5;
float blue = 6; float blue = 6;
float white = 7; float white = 7;
float color_temperature = 8; float color_temperature = 8;
float cold_white = 12;
float warm_white = 13;
string effect = 9; string effect = 9;
} }
message LightCommandRequest { message LightCommandRequest {
@ -388,6 +416,10 @@ message LightCommandRequest {
bool state = 3; bool state = 3;
bool has_brightness = 4; bool has_brightness = 4;
float brightness = 5; float brightness = 5;
bool has_color_mode = 22;
ColorMode color_mode = 23;
bool has_color_brightness = 20;
float color_brightness = 21;
bool has_rgb = 6; bool has_rgb = 6;
float red = 7; float red = 7;
float green = 8; float green = 8;
@ -396,6 +428,10 @@ message LightCommandRequest {
float white = 11; float white = 11;
bool has_color_temperature = 12; bool has_color_temperature = 12;
float color_temperature = 13; float color_temperature = 13;
bool has_cold_white = 24;
float cold_white = 25;
bool has_warm_white = 26;
float warm_white = 27;
bool has_transition_length = 14; bool has_transition_length = 14;
uint32 transition_length = 15; uint32 transition_length = 15;
bool has_flash_length = 16; bool has_flash_length = 16;
@ -405,6 +441,17 @@ message LightCommandRequest {
} }
// ==================== SENSOR ==================== // ==================== SENSOR ====================
enum SensorStateClass {
STATE_CLASS_NONE = 0;
STATE_CLASS_MEASUREMENT = 1;
}
enum SensorLastResetType {
LAST_RESET_NONE = 0;
LAST_RESET_NEVER = 1;
LAST_RESET_AUTO = 2;
}
message ListEntitiesSensorResponse { message ListEntitiesSensorResponse {
option (id) = 16; option (id) = 16;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@ -420,6 +467,8 @@ message ListEntitiesSensorResponse {
int32 accuracy_decimals = 7; int32 accuracy_decimals = 7;
bool force_update = 8; bool force_update = 8;
string device_class = 9; string device_class = 9;
SensorStateClass state_class = 10;
SensorLastResetType last_reset_type = 11;
} }
message SensorStateResponse { message SensorStateResponse {
option (id) = 25; option (id) = 25;
@ -557,6 +606,7 @@ message SubscribeHomeAssistantStateResponse {
option (id) = 39; option (id) = 39;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
string entity_id = 1; string entity_id = 1;
string attribute = 2;
} }
message HomeAssistantStateResponse { message HomeAssistantStateResponse {
@ -566,6 +616,7 @@ message HomeAssistantStateResponse {
string entity_id = 1; string entity_id = 1;
string state = 2; string state = 2;
string attribute = 3;
} }
// ==================== IMPORT TIME ==================== // ==================== IMPORT TIME ====================
@ -660,11 +711,12 @@ message CameraImageRequest {
// ==================== CLIMATE ==================== // ==================== CLIMATE ====================
enum ClimateMode { enum ClimateMode {
CLIMATE_MODE_OFF = 0; CLIMATE_MODE_OFF = 0;
CLIMATE_MODE_AUTO = 1; CLIMATE_MODE_HEAT_COOL = 1;
CLIMATE_MODE_COOL = 2; CLIMATE_MODE_COOL = 2;
CLIMATE_MODE_HEAT = 3; CLIMATE_MODE_HEAT = 3;
CLIMATE_MODE_FAN_ONLY = 4; CLIMATE_MODE_FAN_ONLY = 4;
CLIMATE_MODE_DRY = 5; CLIMATE_MODE_DRY = 5;
CLIMATE_MODE_AUTO = 6;
} }
enum ClimateFanMode { enum ClimateFanMode {
CLIMATE_FAN_ON = 0; CLIMATE_FAN_ON = 0;
@ -692,6 +744,16 @@ enum ClimateAction {
CLIMATE_ACTION_DRYING = 5; CLIMATE_ACTION_DRYING = 5;
CLIMATE_ACTION_FAN = 6; CLIMATE_ACTION_FAN = 6;
} }
enum ClimatePreset {
CLIMATE_PRESET_NONE = 0;
CLIMATE_PRESET_HOME = 1;
CLIMATE_PRESET_AWAY = 2;
CLIMATE_PRESET_BOOST = 3;
CLIMATE_PRESET_COMFORT = 4;
CLIMATE_PRESET_ECO = 5;
CLIMATE_PRESET_SLEEP = 6;
CLIMATE_PRESET_ACTIVITY = 7;
}
message ListEntitiesClimateResponse { message ListEntitiesClimateResponse {
option (id) = 46; option (id) = 46;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@ -708,10 +770,15 @@ message ListEntitiesClimateResponse {
float visual_min_temperature = 8; float visual_min_temperature = 8;
float visual_max_temperature = 9; float visual_max_temperature = 9;
float visual_temperature_step = 10; float visual_temperature_step = 10;
bool supports_away = 11; // for older peer versions - in new system this
// is if CLIMATE_PRESET_AWAY exists is supported_presets
bool legacy_supports_away = 11;
bool supports_action = 12; bool supports_action = 12;
repeated ClimateFanMode supported_fan_modes = 13; repeated ClimateFanMode supported_fan_modes = 13;
repeated ClimateSwingMode supported_swing_modes = 14; repeated ClimateSwingMode supported_swing_modes = 14;
repeated string supported_custom_fan_modes = 15;
repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17;
} }
message ClimateStateResponse { message ClimateStateResponse {
option (id) = 47; option (id) = 47;
@ -725,10 +792,14 @@ message ClimateStateResponse {
float target_temperature = 4; float target_temperature = 4;
float target_temperature_low = 5; float target_temperature_low = 5;
float target_temperature_high = 6; float target_temperature_high = 6;
bool away = 7; // For older peers, equal to preset == CLIMATE_PRESET_AWAY
bool legacy_away = 7;
ClimateAction action = 8; ClimateAction action = 8;
ClimateFanMode fan_mode = 9; ClimateFanMode fan_mode = 9;
ClimateSwingMode swing_mode = 10; ClimateSwingMode swing_mode = 10;
string custom_fan_mode = 11;
ClimatePreset preset = 12;
string custom_preset = 13;
} }
message ClimateCommandRequest { message ClimateCommandRequest {
option (id) = 48; option (id) = 48;
@ -745,10 +816,91 @@ message ClimateCommandRequest {
float target_temperature_low = 7; float target_temperature_low = 7;
bool has_target_temperature_high = 8; bool has_target_temperature_high = 8;
float target_temperature_high = 9; float target_temperature_high = 9;
bool has_away = 10; // legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
bool away = 11; bool has_legacy_away = 10;
bool legacy_away = 11;
bool has_fan_mode = 12; bool has_fan_mode = 12;
ClimateFanMode fan_mode = 13; ClimateFanMode fan_mode = 13;
bool has_swing_mode = 14; bool has_swing_mode = 14;
ClimateSwingMode swing_mode = 15; ClimateSwingMode swing_mode = 15;
bool has_custom_fan_mode = 16;
string custom_fan_mode = 17;
bool has_preset = 18;
ClimatePreset preset = 19;
bool has_custom_preset = 20;
string custom_preset = 21;
}
// ==================== NUMBER ====================
message ListEntitiesNumberResponse {
option (id) = 49;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_NUMBER";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
float min_value = 6;
float max_value = 7;
float step = 8;
}
message NumberStateResponse {
option (id) = 50;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_NUMBER";
option (no_delay) = true;
fixed32 key = 1;
float state = 2;
// If the number does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
}
message NumberCommandRequest {
option (id) = 51;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_NUMBER";
option (no_delay) = true;
fixed32 key = 1;
float state = 2;
}
// ==================== SELECT ====================
message ListEntitiesSelectResponse {
option (id) = 52;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SELECT";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
repeated string options = 6;
}
message SelectStateResponse {
option (id) = 53;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SELECT";
option (no_delay) = true;
fixed32 key = 1;
string state = 2;
// If the select does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
}
message SelectCommandRequest {
option (id) = 54;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SELECT";
option (no_delay) = true;
fixed32 key = 1;
string state = 2;
} }

View File

@ -9,11 +9,14 @@
#ifdef USE_HOMEASSISTANT_TIME #ifdef USE_HOMEASSISTANT_TIME
#include "esphome/components/homeassistant/time/homeassistant_time.h" #include "esphome/components/homeassistant/time/homeassistant_time.h"
#endif #endif
#ifdef USE_FAN
#include "esphome/components/fan/fan_helpers.h"
#endif
namespace esphome { namespace esphome {
namespace api { namespace api {
static const char *TAG = "api.connection"; static const char *const TAG = "api.connection";
APIConnection::APIConnection(AsyncClient *client, APIServer *parent) APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
: client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { : client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
@ -246,8 +249,10 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
resp.state = fan->state; resp.state = fan->state;
if (traits.supports_oscillation()) if (traits.supports_oscillation())
resp.oscillating = fan->oscillating; resp.oscillating = fan->oscillating;
if (traits.supports_speed()) if (traits.supports_speed()) {
resp.speed = static_cast<enums::FanSpeed>(fan->speed); resp.speed_level = fan->speed;
resp.speed = static_cast<enums::FanSpeed>(fan::speed_level_to_enum(fan->speed, traits.supported_speed_count()));
}
if (traits.supports_direction()) if (traits.supports_direction())
resp.direction = static_cast<enums::FanDirection>(fan->direction); resp.direction = static_cast<enums::FanDirection>(fan->direction);
return this->send_fan_state_response(resp); return this->send_fan_state_response(resp);
@ -262,6 +267,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
msg.supports_oscillation = traits.supports_oscillation(); msg.supports_oscillation = traits.supports_oscillation();
msg.supports_speed = traits.supports_speed(); msg.supports_speed = traits.supports_speed();
msg.supports_direction = traits.supports_direction(); msg.supports_direction = traits.supports_direction();
msg.supported_speed_count = traits.supported_speed_count();
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) {
@ -269,13 +275,19 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
if (fan == nullptr) if (fan == nullptr)
return; return;
auto traits = fan->get_traits();
auto call = fan->make_call(); auto call = fan->make_call();
if (msg.has_state) if (msg.has_state)
call.set_state(msg.state); call.set_state(msg.state);
if (msg.has_oscillating) if (msg.has_oscillating)
call.set_oscillating(msg.oscillating); call.set_oscillating(msg.oscillating);
if (msg.has_speed) if (msg.has_speed_level) {
call.set_speed(static_cast<fan::FanSpeed>(msg.speed)); // Prefer level
call.set_speed(msg.speed_level);
} else if (msg.has_speed) {
call.set_speed(fan::speed_enum_to_level(static_cast<fan::FanSpeed>(msg.speed), traits.supported_speed_count()));
}
if (msg.has_direction) if (msg.has_direction)
call.set_direction(static_cast<fan::FanDirection>(msg.direction)); call.set_direction(static_cast<fan::FanDirection>(msg.direction));
call.perform(); call.perform();
@ -289,21 +301,28 @@ bool APIConnection::send_light_state(light::LightState *light) {
auto traits = light->get_traits(); auto traits = light->get_traits();
auto values = light->remote_values; auto values = light->remote_values;
auto color_mode = values.get_color_mode();
LightStateResponse resp{}; LightStateResponse resp{};
resp.key = light->get_object_id_hash(); resp.key = light->get_object_id_hash();
resp.state = values.is_on(); resp.state = values.is_on();
if (traits.get_supports_brightness()) resp.color_mode = static_cast<enums::ColorMode>(color_mode);
if (color_mode & light::ColorCapability::BRIGHTNESS)
resp.brightness = values.get_brightness(); resp.brightness = values.get_brightness();
if (traits.get_supports_rgb()) { if (color_mode & light::ColorCapability::RGB) {
resp.color_brightness = values.get_color_brightness();
resp.red = values.get_red(); resp.red = values.get_red();
resp.green = values.get_green(); resp.green = values.get_green();
resp.blue = values.get_blue(); resp.blue = values.get_blue();
} }
if (traits.get_supports_rgb_white_value()) if (color_mode & light::ColorCapability::WHITE)
resp.white = values.get_white(); resp.white = values.get_white();
if (traits.get_supports_color_temperature()) if (color_mode & light::ColorCapability::COLOR_TEMPERATURE)
resp.color_temperature = values.get_color_temperature(); resp.color_temperature = values.get_color_temperature();
if (color_mode & light::ColorCapability::COLD_WARM_WHITE) {
resp.cold_white = values.get_cold_white();
resp.warm_white = values.get_warm_white();
}
if (light->supports_effects()) if (light->supports_effects())
resp.effect = light->get_effect_name(); resp.effect = light->get_effect_name();
return this->send_light_state_response(resp); return this->send_light_state_response(resp);
@ -315,11 +334,18 @@ bool APIConnection::send_light_info(light::LightState *light) {
msg.object_id = light->get_object_id(); msg.object_id = light->get_object_id();
msg.name = light->get_name(); msg.name = light->get_name();
msg.unique_id = get_default_unique_id("light", light); msg.unique_id = get_default_unique_id("light", light);
msg.supports_brightness = traits.get_supports_brightness(); for (auto mode : traits.get_supported_color_modes())
msg.supports_rgb = traits.get_supports_rgb(); msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
msg.supports_white_value = traits.get_supports_rgb_white_value();
msg.supports_color_temperature = traits.get_supports_color_temperature(); msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
if (msg.supports_color_temperature) { msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
msg.legacy_supports_white_value =
msg.legacy_supports_rgb && (traits.supports_color_capability(light::ColorCapability::WHITE) ||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
if (msg.legacy_supports_color_temperature) {
msg.min_mireds = traits.get_min_mireds(); msg.min_mireds = traits.get_min_mireds();
msg.max_mireds = traits.get_max_mireds(); msg.max_mireds = traits.get_max_mireds();
} }
@ -340,6 +366,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
call.set_state(msg.state); call.set_state(msg.state);
if (msg.has_brightness) if (msg.has_brightness)
call.set_brightness(msg.brightness); call.set_brightness(msg.brightness);
if (msg.has_color_mode)
call.set_color_mode(static_cast<light::ColorMode>(msg.color_mode));
if (msg.has_color_brightness)
call.set_color_brightness(msg.color_brightness);
if (msg.has_rgb) { if (msg.has_rgb) {
call.set_red(msg.red); call.set_red(msg.red);
call.set_green(msg.green); call.set_green(msg.green);
@ -349,6 +379,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
call.set_white(msg.white); call.set_white(msg.white);
if (msg.has_color_temperature) if (msg.has_color_temperature)
call.set_color_temperature(msg.color_temperature); call.set_color_temperature(msg.color_temperature);
if (msg.has_cold_white)
call.set_cold_white(msg.cold_white);
if (msg.has_warm_white)
call.set_warm_white(msg.warm_white);
if (msg.has_transition_length) if (msg.has_transition_length)
call.set_transition_length(msg.transition_length); call.set_transition_length(msg.transition_length);
if (msg.has_flash_length) if (msg.has_flash_length)
@ -383,6 +417,9 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
msg.accuracy_decimals = sensor->get_accuracy_decimals(); msg.accuracy_decimals = sensor->get_accuracy_decimals();
msg.force_update = sensor->get_force_update(); msg.force_update = sensor->get_force_update();
msg.device_class = sensor->get_device_class(); msg.device_class = sensor->get_device_class();
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
msg.last_reset_type = static_cast<enums::SensorLastResetType>(sensor->last_reset_type);
return this->send_list_entities_sensor_response(msg); return this->send_list_entities_sensor_response(msg);
} }
#endif #endif
@ -461,10 +498,16 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
} else { } else {
resp.target_temperature = climate->target_temperature; resp.target_temperature = climate->target_temperature;
} }
if (traits.get_supports_away()) if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
resp.away = climate->away; resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
if (traits.get_supports_fan_modes()) if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode); resp.custom_fan_mode = climate->custom_fan_mode.value();
if (traits.get_supports_presets() && climate->preset.has_value()) {
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
resp.legacy_away = resp.preset == enums::CLIMATE_PRESET_AWAY;
}
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
resp.custom_preset = climate->custom_preset.value();
if (traits.get_supports_swing_modes()) if (traits.get_supports_swing_modes())
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode); resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
return this->send_climate_state_response(resp); return this->send_climate_state_response(resp);
@ -478,27 +521,26 @@ 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.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();
for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) { for (auto mode : traits.get_supported_modes())
if (traits.supports_mode(mode)) msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
}
msg.visual_min_temperature = traits.get_visual_min_temperature(); msg.visual_min_temperature = traits.get_visual_min_temperature();
msg.visual_max_temperature = traits.get_visual_max_temperature(); msg.visual_max_temperature = traits.get_visual_max_temperature();
msg.visual_temperature_step = traits.get_visual_temperature_step(); msg.visual_temperature_step = traits.get_visual_temperature_step();
msg.supports_away = traits.get_supports_away(); msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
msg.supports_action = traits.get_supports_action(); msg.supports_action = traits.get_supports_action();
for (auto fan_mode : {climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_AUTO,
climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH, for (auto fan_mode : traits.get_supported_fan_modes())
climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) { msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
if (traits.supports_fan_mode(fan_mode)) for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode)); msg.supported_custom_fan_modes.push_back(custom_fan_mode);
} for (auto preset : traits.get_supported_presets())
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL, msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
climate::CLIMATE_SWING_HORIZONTAL}) { for (auto const &custom_preset : traits.get_supported_custom_presets())
if (traits.supports_swing_mode(swing_mode)) msg.supported_custom_presets.push_back(custom_preset);
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode)); for (auto swing_mode : traits.get_supported_swing_modes())
} msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
return this->send_list_entities_climate_response(msg); return this->send_list_entities_climate_response(msg);
} }
void APIConnection::climate_command(const ClimateCommandRequest &msg) { void APIConnection::climate_command(const ClimateCommandRequest &msg) {
@ -515,16 +557,93 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
call.set_target_temperature_low(msg.target_temperature_low); call.set_target_temperature_low(msg.target_temperature_low);
if (msg.has_target_temperature_high) if (msg.has_target_temperature_high)
call.set_target_temperature_high(msg.target_temperature_high); call.set_target_temperature_high(msg.target_temperature_high);
if (msg.has_away) if (msg.has_legacy_away)
call.set_away(msg.away); call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
if (msg.has_fan_mode) if (msg.has_fan_mode)
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode)); call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
if (msg.has_custom_fan_mode)
call.set_fan_mode(msg.custom_fan_mode);
if (msg.has_preset)
call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
if (msg.has_custom_preset)
call.set_preset(msg.custom_preset);
if (msg.has_swing_mode) if (msg.has_swing_mode)
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode)); call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
call.perform(); call.perform();
} }
#endif #endif
#ifdef USE_NUMBER
bool APIConnection::send_number_state(number::Number *number, float state) {
if (!this->state_subscription_)
return false;
NumberStateResponse resp{};
resp.key = number->get_object_id_hash();
resp.state = state;
resp.missing_state = !number->has_state();
return this->send_number_state_response(resp);
}
bool APIConnection::send_number_info(number::Number *number) {
ListEntitiesNumberResponse msg;
msg.key = number->get_object_id_hash();
msg.object_id = number->get_object_id();
msg.name = number->get_name();
msg.unique_id = get_default_unique_id("number", number);
msg.icon = number->traits.get_icon();
msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value();
msg.step = number->traits.get_step();
return this->send_list_entities_number_response(msg);
}
void APIConnection::number_command(const NumberCommandRequest &msg) {
number::Number *number = App.get_number_by_key(msg.key);
if (number == nullptr)
return;
auto call = number->make_call();
call.set_value(msg.state);
call.perform();
}
#endif
#ifdef USE_SELECT
bool APIConnection::send_select_state(select::Select *select, std::string state) {
if (!this->state_subscription_)
return false;
SelectStateResponse resp{};
resp.key = select->get_object_id_hash();
resp.state = std::move(state);
resp.missing_state = !select->has_state();
return this->send_select_state_response(resp);
}
bool APIConnection::send_select_info(select::Select *select) {
ListEntitiesSelectResponse msg;
msg.key = select->get_object_id_hash();
msg.object_id = select->get_object_id();
msg.name = select->get_name();
msg.unique_id = get_default_unique_id("select", select);
msg.icon = select->traits.get_icon();
for (const auto &option : select->traits.get_options())
msg.options.push_back(option);
return this->send_list_entities_select_response(msg);
}
void APIConnection::select_command(const SelectCommandRequest &msg) {
select::Select *select = App.get_select_by_key(msg.key);
if (select == nullptr)
return;
auto call = select->make_call();
call.set_option(msg.state);
call.perform();
}
#endif
#ifdef USE_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) { void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_) if (!this->state_subscription_)
@ -590,7 +709,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
HelloResponse resp; HelloResponse resp;
resp.api_version_major = 1; resp.api_version_major = 1;
resp.api_version_minor = 3; resp.api_version_minor = 6;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
this->connection_state_ = ConnectionState::CONNECTED; this->connection_state_ = ConnectionState::CONNECTED;
return resp; return resp;
@ -625,13 +744,18 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
#endif #endif
#ifdef USE_DEEP_SLEEP #ifdef USE_DEEP_SLEEP
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep; resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
#endif
#ifdef ESPHOME_PROJECT_NAME
resp.project_name = ESPHOME_PROJECT_NAME;
resp.project_version = ESPHOME_PROJECT_VERSION;
#endif #endif
return resp; return resp;
} }
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) { void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
for (auto &it : this->parent_->get_state_subs()) for (auto &it : this->parent_->get_state_subs())
if (it.entity_id == msg.entity_id) if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
it.callback(msg.state); it.callback(msg.state);
}
} }
void APIConnection::execute_service(const ExecuteServiceRequest &msg) { void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
bool found = false; bool found = false;
@ -648,6 +772,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
for (auto &it : this->parent_->get_state_subs()) { for (auto &it : this->parent_->get_state_subs()) {
SubscribeHomeAssistantStateResponse resp; SubscribeHomeAssistantStateResponse resp;
resp.entity_id = it.entity_id; resp.entity_id = it.entity_id;
resp.attribute = it.attribute.value();
if (!this->send_subscribe_home_assistant_state_response(resp)) { if (!this->send_subscribe_home_assistant_state_response(resp)) {
this->on_fatal_error(); this->on_fatal_error();
return; return;

View File

@ -62,6 +62,16 @@ class APIConnection : public APIServerConnection {
bool send_climate_state(climate::Climate *climate); bool send_climate_state(climate::Climate *climate);
bool send_climate_info(climate::Climate *climate); bool send_climate_info(climate::Climate *climate);
void climate_command(const ClimateCommandRequest &msg) override; void climate_command(const ClimateCommandRequest &msg) override;
#endif
#ifdef USE_NUMBER
bool send_number_state(number::Number *number, float state);
bool send_number_info(number::Number *number);
void number_command(const NumberCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
bool send_select_state(select::Select *select, std::string state);
bool send_select_info(select::Select *select);
void select_command(const SelectCommandRequest &msg) override;
#endif #endif
bool send_log_message(int level, const char *tag, const char *line); bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

File diff suppressed because it is too large Load Diff

View File

@ -32,6 +32,27 @@ enum FanDirection : uint32_t {
FAN_DIRECTION_FORWARD = 0, FAN_DIRECTION_FORWARD = 0,
FAN_DIRECTION_REVERSE = 1, FAN_DIRECTION_REVERSE = 1,
}; };
enum ColorMode : uint32_t {
COLOR_MODE_UNKNOWN = 0,
COLOR_MODE_ON_OFF = 1,
COLOR_MODE_BRIGHTNESS = 2,
COLOR_MODE_WHITE = 7,
COLOR_MODE_COLOR_TEMPERATURE = 11,
COLOR_MODE_COLD_WARM_WHITE = 19,
COLOR_MODE_RGB = 35,
COLOR_MODE_RGB_WHITE = 39,
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47,
COLOR_MODE_RGB_COLD_WARM_WHITE = 51,
};
enum SensorStateClass : uint32_t {
STATE_CLASS_NONE = 0,
STATE_CLASS_MEASUREMENT = 1,
};
enum SensorLastResetType : uint32_t {
LAST_RESET_NONE = 0,
LAST_RESET_NEVER = 1,
LAST_RESET_AUTO = 2,
};
enum LogLevel : uint32_t { enum LogLevel : uint32_t {
LOG_LEVEL_NONE = 0, LOG_LEVEL_NONE = 0,
LOG_LEVEL_ERROR = 1, LOG_LEVEL_ERROR = 1,
@ -53,11 +74,12 @@ enum ServiceArgType : uint32_t {
}; };
enum ClimateMode : uint32_t { enum ClimateMode : uint32_t {
CLIMATE_MODE_OFF = 0, CLIMATE_MODE_OFF = 0,
CLIMATE_MODE_AUTO = 1, CLIMATE_MODE_HEAT_COOL = 1,
CLIMATE_MODE_COOL = 2, CLIMATE_MODE_COOL = 2,
CLIMATE_MODE_HEAT = 3, CLIMATE_MODE_HEAT = 3,
CLIMATE_MODE_FAN_ONLY = 4, CLIMATE_MODE_FAN_ONLY = 4,
CLIMATE_MODE_DRY = 5, CLIMATE_MODE_DRY = 5,
CLIMATE_MODE_AUTO = 6,
}; };
enum ClimateFanMode : uint32_t { enum ClimateFanMode : uint32_t {
CLIMATE_FAN_ON = 0, CLIMATE_FAN_ON = 0,
@ -84,12 +106,22 @@ enum ClimateAction : uint32_t {
CLIMATE_ACTION_DRYING = 5, CLIMATE_ACTION_DRYING = 5,
CLIMATE_ACTION_FAN = 6, CLIMATE_ACTION_FAN = 6,
}; };
enum ClimatePreset : uint32_t {
CLIMATE_PRESET_NONE = 0,
CLIMATE_PRESET_HOME = 1,
CLIMATE_PRESET_AWAY = 2,
CLIMATE_PRESET_BOOST = 3,
CLIMATE_PRESET_COMFORT = 4,
CLIMATE_PRESET_ECO = 5,
CLIMATE_PRESET_SLEEP = 6,
CLIMATE_PRESET_ACTIVITY = 7,
};
} // namespace enums } // namespace enums
class HelloRequest : public ProtoMessage { class HelloRequest : public ProtoMessage {
public: public:
std::string client_info{}; // NOLINT std::string client_info{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -98,9 +130,9 @@ class HelloRequest : public ProtoMessage {
}; };
class HelloResponse : public ProtoMessage { class HelloResponse : public ProtoMessage {
public: public:
uint32_t api_version_major{0}; // NOLINT uint32_t api_version_major{0};
uint32_t api_version_minor{0}; // NOLINT uint32_t api_version_minor{0};
std::string server_info{}; // NOLINT std::string server_info{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -110,7 +142,7 @@ class HelloResponse : public ProtoMessage {
}; };
class ConnectRequest : public ProtoMessage { class ConnectRequest : public ProtoMessage {
public: public:
std::string password{}; // NOLINT std::string password{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -119,7 +151,7 @@ class ConnectRequest : public ProtoMessage {
}; };
class ConnectResponse : public ProtoMessage { class ConnectResponse : public ProtoMessage {
public: public:
bool invalid_password{false}; // NOLINT bool invalid_password{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -163,13 +195,15 @@ class DeviceInfoRequest : public ProtoMessage {
}; };
class DeviceInfoResponse : public ProtoMessage { class DeviceInfoResponse : public ProtoMessage {
public: public:
bool uses_password{false}; // NOLINT bool uses_password{false};
std::string name{}; // NOLINT std::string name{};
std::string mac_address{}; // NOLINT std::string mac_address{};
std::string esphome_version{}; // NOLINT std::string esphome_version{};
std::string compilation_time{}; // NOLINT std::string compilation_time{};
std::string model{}; // NOLINT std::string model{};
bool has_deep_sleep{false}; // NOLINT bool has_deep_sleep{false};
std::string project_name{};
std::string project_version{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -200,12 +234,12 @@ class SubscribeStatesRequest : public ProtoMessage {
}; };
class ListEntitiesBinarySensorResponse : public ProtoMessage { class ListEntitiesBinarySensorResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
std::string device_class{}; // NOLINT std::string device_class{};
bool is_status_binary_sensor{false}; // NOLINT bool is_status_binary_sensor{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -216,9 +250,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
}; };
class BinarySensorStateResponse : public ProtoMessage { class BinarySensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
bool missing_state{false}; // NOLINT bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -228,14 +262,14 @@ class BinarySensorStateResponse : public ProtoMessage {
}; };
class ListEntitiesCoverResponse : public ProtoMessage { class ListEntitiesCoverResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
bool assumed_state{false}; // NOLINT bool assumed_state{false};
bool supports_position{false}; // NOLINT bool supports_position{false};
bool supports_tilt{false}; // NOLINT bool supports_tilt{false};
std::string device_class{}; // NOLINT std::string device_class{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -246,11 +280,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
}; };
class CoverStateResponse : public ProtoMessage { class CoverStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
enums::LegacyCoverState legacy_state{}; // NOLINT enums::LegacyCoverState legacy_state{};
float position{0.0f}; // NOLINT float position{0.0f};
float tilt{0.0f}; // NOLINT float tilt{0.0f};
enums::CoverOperation current_operation{}; // NOLINT enums::CoverOperation current_operation{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -260,14 +294,14 @@ class CoverStateResponse : public ProtoMessage {
}; };
class CoverCommandRequest : public ProtoMessage { class CoverCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool has_legacy_command{false}; // NOLINT bool has_legacy_command{false};
enums::LegacyCoverCommand legacy_command{}; // NOLINT enums::LegacyCoverCommand legacy_command{};
bool has_position{false}; // NOLINT bool has_position{false};
float position{0.0f}; // NOLINT float position{0.0f};
bool has_tilt{false}; // NOLINT bool has_tilt{false};
float tilt{0.0f}; // NOLINT float tilt{0.0f};
bool stop{false}; // NOLINT bool stop{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -277,13 +311,14 @@ class CoverCommandRequest : public ProtoMessage {
}; };
class ListEntitiesFanResponse : public ProtoMessage { class ListEntitiesFanResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
bool supports_oscillation{false}; // NOLINT bool supports_oscillation{false};
bool supports_speed{false}; // NOLINT bool supports_speed{false};
bool supports_direction{false}; // NOLINT bool supports_direction{false};
int32_t supported_speed_count{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -294,11 +329,12 @@ class ListEntitiesFanResponse : public ProtoMessage {
}; };
class FanStateResponse : public ProtoMessage { class FanStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
bool oscillating{false}; // NOLINT bool oscillating{false};
enums::FanSpeed speed{}; // NOLINT enums::FanSpeed speed{};
enums::FanDirection direction{}; // NOLINT enums::FanDirection direction{};
int32_t speed_level{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -308,15 +344,17 @@ class FanStateResponse : public ProtoMessage {
}; };
class FanCommandRequest : public ProtoMessage { class FanCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool has_state{false}; // NOLINT bool has_state{false};
bool state{false}; // NOLINT bool state{false};
bool has_speed{false}; // NOLINT bool has_speed{false};
enums::FanSpeed speed{}; // NOLINT enums::FanSpeed speed{};
bool has_oscillating{false}; // NOLINT bool has_oscillating{false};
bool oscillating{false}; // NOLINT bool oscillating{false};
bool has_direction{false}; // NOLINT bool has_direction{false};
enums::FanDirection direction{}; // NOLINT enums::FanDirection direction{};
bool has_speed_level{false};
int32_t speed_level{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -326,17 +364,18 @@ class FanCommandRequest : public ProtoMessage {
}; };
class ListEntitiesLightResponse : public ProtoMessage { class ListEntitiesLightResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
bool supports_brightness{false}; // NOLINT std::vector<enums::ColorMode> supported_color_modes{};
bool supports_rgb{false}; // NOLINT bool legacy_supports_brightness{false};
bool supports_white_value{false}; // NOLINT bool legacy_supports_rgb{false};
bool supports_color_temperature{false}; // NOLINT bool legacy_supports_white_value{false};
float min_mireds{0.0f}; // NOLINT bool legacy_supports_color_temperature{false};
float max_mireds{0.0f}; // NOLINT float min_mireds{0.0f};
std::vector<std::string> effects{}; // NOLINT float max_mireds{0.0f};
std::vector<std::string> effects{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -347,15 +386,19 @@ class ListEntitiesLightResponse : public ProtoMessage {
}; };
class LightStateResponse : public ProtoMessage { class LightStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
float brightness{0.0f}; // NOLINT float brightness{0.0f};
float red{0.0f}; // NOLINT enums::ColorMode color_mode{};
float green{0.0f}; // NOLINT float color_brightness{0.0f};
float blue{0.0f}; // NOLINT float red{0.0f};
float white{0.0f}; // NOLINT float green{0.0f};
float color_temperature{0.0f}; // NOLINT float blue{0.0f};
std::string effect{}; // NOLINT float white{0.0f};
float color_temperature{0.0f};
float cold_white{0.0f};
float warm_white{0.0f};
std::string effect{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -366,25 +409,33 @@ class LightStateResponse : public ProtoMessage {
}; };
class LightCommandRequest : public ProtoMessage { class LightCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool has_state{false}; // NOLINT bool has_state{false};
bool state{false}; // NOLINT bool state{false};
bool has_brightness{false}; // NOLINT bool has_brightness{false};
float brightness{0.0f}; // NOLINT float brightness{0.0f};
bool has_rgb{false}; // NOLINT bool has_color_mode{false};
float red{0.0f}; // NOLINT enums::ColorMode color_mode{};
float green{0.0f}; // NOLINT bool has_color_brightness{false};
float blue{0.0f}; // NOLINT float color_brightness{0.0f};
bool has_white{false}; // NOLINT bool has_rgb{false};
float white{0.0f}; // NOLINT float red{0.0f};
bool has_color_temperature{false}; // NOLINT float green{0.0f};
float color_temperature{0.0f}; // NOLINT float blue{0.0f};
bool has_transition_length{false}; // NOLINT bool has_white{false};
uint32_t transition_length{0}; // NOLINT float white{0.0f};
bool has_flash_length{false}; // NOLINT bool has_color_temperature{false};
uint32_t flash_length{0}; // NOLINT float color_temperature{0.0f};
bool has_effect{false}; // NOLINT bool has_cold_white{false};
std::string effect{}; // NOLINT float cold_white{0.0f};
bool has_warm_white{false};
float warm_white{0.0f};
bool has_transition_length{false};
uint32_t transition_length{0};
bool has_flash_length{false};
uint32_t flash_length{0};
bool has_effect{false};
std::string effect{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -395,15 +446,17 @@ class LightCommandRequest : public ProtoMessage {
}; };
class ListEntitiesSensorResponse : public ProtoMessage { class ListEntitiesSensorResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
std::string icon{}; // NOLINT std::string icon{};
std::string unit_of_measurement{}; // NOLINT std::string unit_of_measurement{};
std::string device_class{}; // NOLINT int32_t accuracy_decimals{0};
int32_t accuracy_decimals{0}; // NOLINT bool force_update{false};
bool force_update{false}; // NOLINT std::string device_class{};
enums::SensorStateClass state_class{};
enums::SensorLastResetType last_reset_type{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -414,9 +467,9 @@ class ListEntitiesSensorResponse : public ProtoMessage {
}; };
class SensorStateResponse : public ProtoMessage { class SensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
float state{0.0f}; // NOLINT float state{0.0f};
bool missing_state{false}; // NOLINT bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -426,12 +479,12 @@ class SensorStateResponse : public ProtoMessage {
}; };
class ListEntitiesSwitchResponse : public ProtoMessage { class ListEntitiesSwitchResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
std::string icon{}; // NOLINT std::string icon{};
bool assumed_state{false}; // NOLINT bool assumed_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -442,8 +495,8 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
}; };
class SwitchStateResponse : public ProtoMessage { class SwitchStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -453,8 +506,8 @@ class SwitchStateResponse : public ProtoMessage {
}; };
class SwitchCommandRequest : public ProtoMessage { class SwitchCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -464,11 +517,11 @@ class SwitchCommandRequest : public ProtoMessage {
}; };
class ListEntitiesTextSensorResponse : public ProtoMessage { class ListEntitiesTextSensorResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
std::string icon{}; // NOLINT std::string icon{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -478,9 +531,9 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
}; };
class TextSensorStateResponse : public ProtoMessage { class TextSensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string state{}; // NOLINT std::string state{};
bool missing_state{false}; // NOLINT bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -491,8 +544,8 @@ class TextSensorStateResponse : public ProtoMessage {
}; };
class SubscribeLogsRequest : public ProtoMessage { class SubscribeLogsRequest : public ProtoMessage {
public: public:
enums::LogLevel level{}; // NOLINT enums::LogLevel level{};
bool dump_config{false}; // NOLINT bool dump_config{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -501,10 +554,10 @@ class SubscribeLogsRequest : public ProtoMessage {
}; };
class SubscribeLogsResponse : public ProtoMessage { class SubscribeLogsResponse : public ProtoMessage {
public: public:
enums::LogLevel level{}; // NOLINT enums::LogLevel level{};
std::string tag{}; // NOLINT std::string tag{};
std::string message{}; // NOLINT std::string message{};
bool send_failed{false}; // NOLINT bool send_failed{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -521,8 +574,8 @@ class SubscribeHomeassistantServicesRequest : public ProtoMessage {
}; };
class HomeassistantServiceMap : public ProtoMessage { class HomeassistantServiceMap : public ProtoMessage {
public: public:
std::string key{}; // NOLINT std::string key{};
std::string value{}; // NOLINT std::string value{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -531,11 +584,11 @@ class HomeassistantServiceMap : public ProtoMessage {
}; };
class HomeassistantServiceResponse : public ProtoMessage { class HomeassistantServiceResponse : public ProtoMessage {
public: public:
std::string service{}; // NOLINT std::string service{};
std::vector<HomeassistantServiceMap> data{}; // NOLINT std::vector<HomeassistantServiceMap> data{};
std::vector<HomeassistantServiceMap> data_template{}; // NOLINT std::vector<HomeassistantServiceMap> data_template{};
std::vector<HomeassistantServiceMap> variables{}; // NOLINT std::vector<HomeassistantServiceMap> variables{};
bool is_event{false}; // NOLINT bool is_event{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -552,7 +605,8 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
}; };
class SubscribeHomeAssistantStateResponse : public ProtoMessage { class SubscribeHomeAssistantStateResponse : public ProtoMessage {
public: public:
std::string entity_id{}; // NOLINT std::string entity_id{};
std::string attribute{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -561,8 +615,9 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
}; };
class HomeAssistantStateResponse : public ProtoMessage { class HomeAssistantStateResponse : public ProtoMessage {
public: public:
std::string entity_id{}; // NOLINT std::string entity_id{};
std::string state{}; // NOLINT std::string state{};
std::string attribute{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -578,7 +633,7 @@ class GetTimeRequest : public ProtoMessage {
}; };
class GetTimeResponse : public ProtoMessage { class GetTimeResponse : public ProtoMessage {
public: public:
uint32_t epoch_seconds{0}; // NOLINT uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -587,8 +642,8 @@ class GetTimeResponse : public ProtoMessage {
}; };
class ListEntitiesServicesArgument : public ProtoMessage { class ListEntitiesServicesArgument : public ProtoMessage {
public: public:
std::string name{}; // NOLINT std::string name{};
enums::ServiceArgType type{}; // NOLINT enums::ServiceArgType type{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -598,9 +653,9 @@ class ListEntitiesServicesArgument : public ProtoMessage {
}; };
class ListEntitiesServicesResponse : public ProtoMessage { class ListEntitiesServicesResponse : public ProtoMessage {
public: public:
std::string name{}; // NOLINT std::string name{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::vector<ListEntitiesServicesArgument> args{}; // NOLINT std::vector<ListEntitiesServicesArgument> args{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -610,15 +665,15 @@ class ListEntitiesServicesResponse : public ProtoMessage {
}; };
class ExecuteServiceArgument : public ProtoMessage { class ExecuteServiceArgument : public ProtoMessage {
public: public:
bool bool_{false}; // NOLINT bool bool_{false};
int32_t legacy_int{0}; // NOLINT int32_t legacy_int{0};
float float_{0.0f}; // NOLINT float float_{0.0f};
std::string string_{}; // NOLINT std::string string_{};
int32_t int_{0}; // NOLINT int32_t int_{0};
std::vector<bool> bool_array{}; // NOLINT std::vector<bool> bool_array{};
std::vector<int32_t> int_array{}; // NOLINT std::vector<int32_t> int_array{};
std::vector<float> float_array{}; // NOLINT std::vector<float> float_array{};
std::vector<std::string> string_array{}; // NOLINT std::vector<std::string> string_array{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -629,8 +684,8 @@ class ExecuteServiceArgument : public ProtoMessage {
}; };
class ExecuteServiceRequest : public ProtoMessage { class ExecuteServiceRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
std::vector<ExecuteServiceArgument> args{}; // NOLINT std::vector<ExecuteServiceArgument> args{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -640,10 +695,10 @@ class ExecuteServiceRequest : public ProtoMessage {
}; };
class ListEntitiesCameraResponse : public ProtoMessage { class ListEntitiesCameraResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -653,9 +708,9 @@ class ListEntitiesCameraResponse : public ProtoMessage {
}; };
class CameraImageResponse : public ProtoMessage { class CameraImageResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string data{}; // NOLINT std::string data{};
bool done{false}; // NOLINT bool done{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -666,8 +721,8 @@ class CameraImageResponse : public ProtoMessage {
}; };
class CameraImageRequest : public ProtoMessage { class CameraImageRequest : public ProtoMessage {
public: public:
bool single{false}; // NOLINT bool single{false};
bool stream{false}; // NOLINT bool stream{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -676,20 +731,23 @@ class CameraImageRequest : public ProtoMessage {
}; };
class ListEntitiesClimateResponse : public ProtoMessage { class ListEntitiesClimateResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
bool supports_current_temperature{false}; // NOLINT bool supports_current_temperature{false};
bool supports_two_point_target_temperature{false}; // NOLINT bool supports_two_point_target_temperature{false};
std::vector<enums::ClimateMode> supported_modes{}; // NOLINT std::vector<enums::ClimateMode> supported_modes{};
float visual_min_temperature{0.0f}; // NOLINT float visual_min_temperature{0.0f};
float visual_max_temperature{0.0f}; // NOLINT float visual_max_temperature{0.0f};
float visual_temperature_step{0.0f}; // NOLINT float visual_temperature_step{0.0f};
bool supports_away{false}; // NOLINT bool legacy_supports_away{false};
bool supports_action{false}; // NOLINT bool supports_action{false};
std::vector<enums::ClimateFanMode> supported_fan_modes{}; // NOLINT std::vector<enums::ClimateFanMode> supported_fan_modes{};
std::vector<enums::ClimateSwingMode> supported_swing_modes{}; // NOLINT std::vector<enums::ClimateSwingMode> supported_swing_modes{};
std::vector<std::string> supported_custom_fan_modes{};
std::vector<enums::ClimatePreset> supported_presets{};
std::vector<std::string> supported_custom_presets{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -700,40 +758,80 @@ class ListEntitiesClimateResponse : public ProtoMessage {
}; };
class ClimateStateResponse : public ProtoMessage { class ClimateStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
enums::ClimateMode mode{}; // NOLINT enums::ClimateMode mode{};
float current_temperature{0.0f}; // NOLINT float current_temperature{0.0f};
float target_temperature{0.0f}; // NOLINT float target_temperature{0.0f};
float target_temperature_low{0.0f}; // NOLINT float target_temperature_low{0.0f};
float target_temperature_high{0.0f}; // NOLINT float target_temperature_high{0.0f};
bool away{false}; // NOLINT bool legacy_away{false};
enums::ClimateAction action{}; // NOLINT enums::ClimateAction action{};
enums::ClimateFanMode fan_mode{}; // NOLINT enums::ClimateFanMode fan_mode{};
enums::ClimateSwingMode swing_mode{}; // NOLINT enums::ClimateSwingMode swing_mode{};
std::string custom_fan_mode{};
enums::ClimatePreset preset{};
std::string custom_preset{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
protected: protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class ClimateCommandRequest : public ProtoMessage { class ClimateCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool has_mode{false}; // NOLINT bool has_mode{false};
enums::ClimateMode mode{}; // NOLINT enums::ClimateMode mode{};
bool has_target_temperature{false}; // NOLINT bool has_target_temperature{false};
float target_temperature{0.0f}; // NOLINT float target_temperature{0.0f};
bool has_target_temperature_low{false}; // NOLINT bool has_target_temperature_low{false};
float target_temperature_low{0.0f}; // NOLINT float target_temperature_low{0.0f};
bool has_target_temperature_high{false}; // NOLINT bool has_target_temperature_high{false};
float target_temperature_high{0.0f}; // NOLINT float target_temperature_high{0.0f};
bool has_away{false}; // NOLINT bool has_legacy_away{false};
bool away{false}; // NOLINT bool legacy_away{false};
bool has_fan_mode{false}; // NOLINT bool has_fan_mode{false};
enums::ClimateFanMode fan_mode{}; // NOLINT enums::ClimateFanMode fan_mode{};
bool has_swing_mode{false}; // NOLINT bool has_swing_mode{false};
enums::ClimateSwingMode swing_mode{}; // NOLINT enums::ClimateSwingMode swing_mode{};
bool has_custom_fan_mode{false};
std::string custom_fan_mode{};
bool has_preset{false};
enums::ClimatePreset preset{};
bool has_custom_preset{false};
std::string custom_preset{};
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesNumberResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
float min_value{0.0f};
float max_value{0.0f};
float step{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class NumberStateResponse : public ProtoMessage {
public:
uint32_t key{0};
float state{0.0f};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -741,6 +839,55 @@ class ClimateCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class NumberCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
float state{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
class ListEntitiesSelectResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
std::vector<std::string> options{};
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class SelectStateResponse : public ProtoMessage {
public:
uint32_t key{0};
std::string state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SelectCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
std::string state{};
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View File

@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
static const char *TAG = "api.service"; static const char *const TAG = "api.service";
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) { bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
@ -184,6 +184,34 @@ bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResp
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
#endif #endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
}
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
return this->send_message_<NumberStateResponse>(msg, 50);
}
#endif
#ifdef USE_NUMBER
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) {
ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str());
return this->send_message_<ListEntitiesSelectResponse>(msg, 52);
}
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) {
ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str());
return this->send_message_<SelectStateResponse>(msg, 53);
}
#endif
#ifdef USE_SELECT
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) { switch (msg_type) {
case 1: { case 1: {
@ -349,6 +377,24 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
msg.decode(msg_data, msg_size); msg.decode(msg_data, msg_size);
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
this->on_climate_command_request(msg); this->on_climate_command_request(msg);
#endif
break;
}
case 51: {
#ifdef USE_NUMBER
NumberCommandRequest msg;
msg.decode(msg_data, msg_size);
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
this->on_number_command_request(msg);
#endif
break;
}
case 54: {
#ifdef USE_SELECT
SelectCommandRequest msg;
msg.decode(msg_data, msg_size);
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
this->on_select_command_request(msg);
#endif #endif
break; break;
} }
@ -547,6 +593,32 @@ void APIServerConnection::on_climate_command_request(const ClimateCommandRequest
this->climate_command(msg); this->climate_command(msg);
} }
#endif #endif
#ifdef USE_NUMBER
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->number_command(msg);
}
#endif
#ifdef USE_SELECT
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->select_command(msg);
}
#endif
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View File

@ -111,6 +111,24 @@ class APIServerConnectionBase : public ProtoService {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
virtual void on_climate_command_request(const ClimateCommandRequest &value){}; virtual void on_climate_command_request(const ClimateCommandRequest &value){};
#endif
#ifdef USE_NUMBER
bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg);
#endif
#ifdef USE_NUMBER
bool send_number_state_response(const NumberStateResponse &msg);
#endif
#ifdef USE_NUMBER
virtual void on_number_command_request(const NumberCommandRequest &value){};
#endif
#ifdef USE_SELECT
bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg);
#endif
#ifdef USE_SELECT
bool send_select_state_response(const SelectStateResponse &msg);
#endif
#ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif #endif
protected: protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@ -147,6 +165,12 @@ class APIServerConnection : public APIServerConnectionBase {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
virtual void climate_command(const ClimateCommandRequest &msg) = 0; virtual void climate_command(const ClimateCommandRequest &msg) = 0;
#endif
#ifdef USE_NUMBER
virtual void number_command(const NumberCommandRequest &msg) = 0;
#endif
#ifdef USE_SELECT
virtual void select_command(const SelectCommandRequest &msg) = 0;
#endif #endif
protected: protected:
void on_hello_request(const HelloRequest &msg) override; void on_hello_request(const HelloRequest &msg) override;
@ -179,6 +203,12 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
void on_climate_command_request(const ClimateCommandRequest &msg) override; void on_climate_command_request(const ClimateCommandRequest &msg) override;
#endif #endif
#ifdef USE_NUMBER
void on_number_command_request(const NumberCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
void on_select_command_request(const SelectCommandRequest &msg) override;
#endif
}; };
} // namespace api } // namespace api

View File

@ -15,7 +15,7 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
static const char *TAG = "api"; static const char *const TAG = "api";
// APIServer // APIServer
void APIServer::setup() { void APIServer::setup() {
@ -180,7 +180,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, 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_)
@ -197,9 +197,27 @@ void APIServer::on_climate_update(climate::Climate *obj) {
} }
#endif #endif
#ifdef USE_NUMBER
void APIServer::on_number_update(number::Number *obj, float state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
c->send_number_state(obj, state);
}
#endif
#ifdef USE_SELECT
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
if (obj->is_internal())
return;
for (auto *c : this->clients_)
c->send_select_state(obj, state);
}
#endif
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
void APIServer::set_port(uint16_t port) { this->port_ = port; } void APIServer::set_port(uint16_t port) { this->port_ = port; }
APIServer *global_api_server = nullptr; APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
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) {
@ -208,9 +226,11 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
} }
} }
APIServer::APIServer() { global_api_server = this; } APIServer::APIServer() { global_api_server = this; }
void APIServer::subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f) { void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->state_subs_.push_back(HomeAssistantStateSubscription{ this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = std::move(entity_id), .entity_id = std::move(entity_id),
.attribute = std::move(attribute),
.callback = std::move(f), .callback = std::move(f),
}); });
} }

View File

@ -9,7 +9,6 @@
#include "util.h" #include "util.h"
#include "list_entities.h" #include "list_entities.h"
#include "subscribe_state.h" #include "subscribe_state.h"
#include "homeassistant_service.h"
#include "user_services.h" #include "user_services.h"
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
@ -56,10 +55,16 @@ class APIServer : public Component, public Controller {
void on_switch_update(switch_::Switch *obj, bool state) override; void on_switch_update(switch_::Switch *obj, bool state) override;
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) override; void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
void on_climate_update(climate::Climate *obj) override; void on_climate_update(climate::Climate *obj) override;
#endif
#ifdef USE_NUMBER
void on_number_update(number::Number *obj, float state) override;
#endif
#ifdef USE_SELECT
void on_select_update(select::Select *obj, const std::string &state) override;
#endif #endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call); void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
@ -71,10 +76,12 @@ class APIServer : public Component, public Controller {
struct HomeAssistantStateSubscription { struct HomeAssistantStateSubscription {
std::string entity_id; std::string entity_id;
optional<std::string> attribute;
std::function<void(std::string)> callback; std::function<void(std::string)> callback;
}; };
void subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f); void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f);
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const; const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; } const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
@ -89,7 +96,7 @@ class APIServer : public Component, public Controller {
std::vector<UserServiceDescriptor *> user_services_; std::vector<UserServiceDescriptor *> user_services_;
}; };
extern APIServer *global_api_server; extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> { template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
public: public:

View File

@ -76,13 +76,13 @@ class CustomAPIDevice {
global_api_server->register_user_service(service); global_api_server->register_user_service(service);
} }
/** Subscribe to the state of an entity from Home Assistant. /** Subscribe to the state (or attribute state) of an entity from Home Assistant.
* *
* Usage: * Usage:
* *
* ```cpp * ```cpp
* void setup() override { * void setup() override {
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast"); * subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "climate.kitchen", "current_temperature");
* } * }
* *
* void on_state_changed(std::string state) { * void on_state_changed(std::string state) {
@ -93,17 +93,19 @@ class CustomAPIDevice {
* @tparam T The class type creating the service, automatically deduced from the function pointer. * @tparam T The class type creating the service, automatically deduced from the function pointer.
* @param callback The member function to call when the entity state changes. * @param callback The member function to call when the entity state changes.
* @param entity_id The entity_id to track. * @param entity_id The entity_id to track.
* @param attribute The entity state attribute to track.
*/ */
template<typename T> template<typename T>
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id) { void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
const std::string &attribute = "") {
auto f = std::bind(callback, (T *) this, std::placeholders::_1); auto f = std::bind(callback, (T *) this, std::placeholders::_1);
global_api_server->subscribe_home_assistant_state(entity_id, f); global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
} }
/** Subscribe to the state of an entity from Home Assistant. /** Subscribe to the state (or attribute state) of an entity from Home Assistant.
* *
* Usage: * Usage:
* *å
* ```cpp * ```cpp
* void setup() override { * void setup() override {
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast"); * subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
@ -117,11 +119,13 @@ class CustomAPIDevice {
* @tparam T The class type creating the service, automatically deduced from the function pointer. * @tparam T The class type creating the service, automatically deduced from the function pointer.
* @param callback The member function to call when the entity state changes. * @param callback The member function to call when the entity state changes.
* @param entity_id The entity_id to track. * @param entity_id The entity_id to track.
* @param attribute The entity state attribute to track.
*/ */
template<typename T> template<typename T>
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id) { void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
const std::string &attribute = "") {
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1); auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
global_api_server->subscribe_home_assistant_state(entity_id, f); global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
} }
/** Call a Home Assistant service from ESPHome. /** Call a Home Assistant service from ESPHome.

View File

@ -51,5 +51,13 @@ bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); } bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); }
#endif #endif
#ifdef USE_NUMBER
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
#endif
#ifdef USE_SELECT
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
#endif
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View File

@ -39,6 +39,12 @@ class ListEntitiesIterator : public ComponentIterator {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
bool on_climate(climate::Climate *climate) override; bool on_climate(climate::Climate *climate) override;
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif #endif
bool on_end() override; bool on_end() override;

View File

@ -5,7 +5,7 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
static const char *TAG = "api.proto"; static const char *const TAG = "api.proto";
void ProtoMessage::decode(const uint8_t *buffer, size_t length) { void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
uint32_t i = 0; uint32_t i = 0;

View File

@ -37,6 +37,16 @@ bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); } bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
#endif #endif
#ifdef USE_NUMBER
bool InitialStateIterator::on_number(number::Number *number) {
return this->client_->send_number_state(number, number->state);
}
#endif
#ifdef USE_SELECT
bool InitialStateIterator::on_select(select::Select *select) {
return this->client_->send_select_state(select, select->state);
}
#endif
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client) InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
: ComponentIterator(server), client_(client) {} : ComponentIterator(server), client_(client) {}

View File

@ -36,6 +36,12 @@ class InitialStateIterator : public ComponentIterator {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
bool on_climate(climate::Climate *climate) override; bool on_climate(climate::Climate *climate) override;
#endif
#ifdef USE_NUMBER
bool on_number(number::Number *number) override;
#endif
#ifdef USE_SELECT
bool on_select(select::Select *select) override;
#endif #endif
protected: protected:
APIConnection *client_; APIConnection *client_;

View File

@ -15,7 +15,7 @@ template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceAr
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) { template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
return arg.bool_array; return arg.bool_array;
} }
template<> std::vector<int> get_execute_arg_value<std::vector<int>>(const ExecuteServiceArgument &arg) { template<> std::vector<int32_t> get_execute_arg_value<std::vector<int32_t>>(const ExecuteServiceArgument &arg) {
return arg.int_array; return arg.int_array;
} }
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) { template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {

View File

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <utility>
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "api_pb2.h" #include "api_pb2.h"
@ -20,8 +22,8 @@ template<typename T> enums::ServiceArgType to_service_arg_type();
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor { template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
public: public:
UserServiceBase(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names) UserServiceBase(std::string name, const std::array<std::string, sizeof...(Ts)> &arg_names)
: name_(name), arg_names_(arg_names) { : name_(std::move(name)), arg_names_(arg_names) {
this->key_ = fnv1_hash(this->name_); this->key_ = fnv1_hash(this->name_);
} }

View File

@ -167,6 +167,36 @@ void ComponentIterator::advance() {
} }
} }
break; break;
#endif
#ifdef USE_NUMBER
case IteratorState::NUMBER:
if (this->at_ >= App.get_numbers().size()) {
advance_platform = true;
} else {
auto *number = App.get_numbers()[this->at_];
if (number->is_internal()) {
success = true;
break;
} else {
success = this->on_number(number);
}
}
break;
#endif
#ifdef USE_SELECT
case IteratorState::SELECT:
if (this->at_ >= App.get_selects().size()) {
advance_platform = true;
} else {
auto *select = App.get_selects()[this->at_];
if (select->is_internal()) {
success = true;
break;
} else {
success = this->on_select(select);
}
}
break;
#endif #endif
case IteratorState::MAX: case IteratorState::MAX:
if (this->on_end()) { if (this->on_end()) {

View File

@ -47,6 +47,12 @@ class ComponentIterator {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
virtual bool on_climate(climate::Climate *climate) = 0; virtual bool on_climate(climate::Climate *climate) = 0;
#endif
#ifdef USE_NUMBER
virtual bool on_number(number::Number *number) = 0;
#endif
#ifdef USE_SELECT
virtual bool on_select(select::Select *select) = 0;
#endif #endif
virtual bool on_end(); virtual bool on_end();
@ -81,6 +87,12 @@ class ComponentIterator {
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
CLIMATE, CLIMATE,
#endif
#ifdef USE_NUMBER
NUMBER,
#endif
#ifdef USE_SELECT
SELECT,
#endif #endif
MAX, MAX,
} state_{IteratorState::NONE}; } state_{IteratorState::NONE};

View File

@ -11,7 +11,6 @@ from esphome.const import (
CONF_DIV_RATIO, CONF_DIV_RATIO,
CONF_CAPACITANCE, CONF_CAPACITANCE,
) )
from esphome.core import coroutine
AUTO_LOAD = ["sensor", "binary_sensor"] AUTO_LOAD = ["sensor", "binary_sensor"]
MULTI_CONF = True MULTI_CONF = True
@ -40,11 +39,10 @@ AS3935_SCHEMA = cv.Schema(
) )
@coroutine async def setup_as3935(var, config):
def setup_as3935(var, config): await cg.register_component(var, config)
yield cg.register_component(var, config)
irq_pin = yield cg.gpio_pin_expression(config[CONF_IRQ_PIN]) irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
cg.add(var.set_irq_pin(irq_pin)) cg.add(var.set_irq_pin(irq_pin))
cg.add(var.set_indoor(config[CONF_INDOOR])) cg.add(var.set_indoor(config[CONF_INDOOR]))
cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL])) cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))

View File

@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace as3935 { namespace as3935 {
static const char *TAG = "as3935"; static const char *const TAG = "as3935";
void AS3935Component::setup() { void AS3935Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AS3935..."); ESP_LOGCONFIG(TAG, "Setting up AS3935...");

View File

@ -12,7 +12,7 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
) )
def to_code(config): async def to_code(config):
hub = yield cg.get_variable(config[CONF_AS3935_ID]) hub = await cg.get_variable(config[CONF_AS3935_ID])
var = yield binary_sensor.new_binary_sensor(config) var = await binary_sensor.new_binary_sensor(config)
cg.add(hub.set_thunder_alert_binary_sensor(var)) cg.add(hub.set_thunder_alert_binary_sensor(var))

View File

@ -4,9 +4,8 @@ from esphome.components import sensor
from esphome.const import ( from esphome.const import (
CONF_DISTANCE, CONF_DISTANCE,
CONF_LIGHTNING_ENERGY, CONF_LIGHTNING_ENERGY,
DEVICE_CLASS_EMPTY, STATE_CLASS_NONE,
UNIT_KILOMETER, UNIT_KILOMETER,
UNIT_EMPTY,
ICON_SIGNAL_DISTANCE_VARIANT, ICON_SIGNAL_DISTANCE_VARIANT,
ICON_FLASH, ICON_FLASH,
) )
@ -18,24 +17,29 @@ CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
cv.Optional(CONF_DISTANCE): sensor.sensor_schema( cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY unit_of_measurement=UNIT_KILOMETER,
icon=ICON_SIGNAL_DISTANCE_VARIANT,
accuracy_decimals=1,
state_class=STATE_CLASS_NONE,
), ),
cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema( cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY icon=ICON_FLASH,
accuracy_decimals=1,
state_class=STATE_CLASS_NONE,
), ),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
def to_code(config): async def to_code(config):
hub = yield cg.get_variable(config[CONF_AS3935_ID]) hub = await cg.get_variable(config[CONF_AS3935_ID])
if CONF_DISTANCE in config: if CONF_DISTANCE in config:
conf = config[CONF_DISTANCE] conf = config[CONF_DISTANCE]
distance_sensor = yield sensor.new_sensor(conf) distance_sensor = await sensor.new_sensor(conf)
cg.add(hub.set_distance_sensor(distance_sensor)) cg.add(hub.set_distance_sensor(distance_sensor))
if CONF_LIGHTNING_ENERGY in config: if CONF_LIGHTNING_ENERGY in config:
conf = config[CONF_LIGHTNING_ENERGY] conf = config[CONF_LIGHTNING_ENERGY]
lightning_energy_sensor = yield sensor.new_sensor(conf) lightning_energy_sensor = await sensor.new_sensor(conf)
cg.add(hub.set_energy_sensor(lightning_energy_sensor)) cg.add(hub.set_energy_sensor(lightning_energy_sensor))

View File

@ -20,7 +20,7 @@ CONFIG_SCHEMA = cv.All(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield as3935.setup_as3935(var, config) await as3935.setup_as3935(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)

View File

@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace as3935_i2c { namespace as3935_i2c {
static const char *TAG = "as3935_i2c"; static const char *const TAG = "as3935_i2c";
void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_pos) { void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_pos) {
uint8_t write_reg; uint8_t write_reg;

View File

@ -20,7 +20,7 @@ CONFIG_SCHEMA = cv.All(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield as3935.setup_as3935(var, config) await as3935.setup_as3935(var, config)
yield spi.register_spi_device(var, config) await spi.register_spi_device(var, config)

View File

@ -4,7 +4,7 @@
namespace esphome { namespace esphome {
namespace as3935_spi { namespace as3935_spi {
static const char *TAG = "as3935_spi"; static const char *const TAG = "as3935_spi";
void SPIAS3935Component::setup() { void SPIAS3935Component::setup() {
ESP_LOGI(TAG, "SPIAS3935Component setup started!"); ESP_LOGI(TAG, "SPIAS3935Component setup started!");
@ -33,7 +33,7 @@ void SPIAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
uint8_t SPIAS3935Component::read_register(uint8_t reg) { uint8_t SPIAS3935Component::read_register(uint8_t reg) {
uint8_t value = 0; uint8_t value = 0;
this->enable(); this->enable();
this->write_byte(reg |= SPI_READ_M); this->write_byte(reg | SPI_READ_M);
value = this->read_byte(); value = this->read_byte();
// According to datsheet, the chip select must be written HIGH, LOW, HIGH // According to datsheet, the chip select must be written HIGH, LOW, HIGH
// to correctly end the READ command. // to correctly end the READ command.

View File

@ -6,10 +6,10 @@ CODEOWNERS = ["@OttoWinter"]
@coroutine_with_priority(200.0) @coroutine_with_priority(200.0)
def to_code(config): async def to_code(config):
if CORE.is_esp32: if CORE.is_esp32:
# https://github.com/OttoWinter/AsyncTCP/blob/master/library.json # https://github.com/esphome/AsyncTCP/blob/master/library.json
cg.add_library("AsyncTCP-esphome", "1.1.1") 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("ESPAsyncTCP-esphome", "1.2.3")

View File

@ -6,7 +6,7 @@
namespace esphome { namespace esphome {
namespace atc_mithermometer { namespace atc_mithermometer {
static const char *TAG = "atc_mithermometer"; static const char *const TAG = "atc_mithermometer";
void ATCMiThermometer::dump_config() { void ATCMiThermometer::dump_config() {
ESP_LOGCONFIG(TAG, "ATC MiThermometer"); ESP_LOGCONFIG(TAG, "ATC MiThermometer");

View File

@ -12,7 +12,7 @@ from esphome.const import (
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ICON_EMPTY, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_PERCENT, UNIT_PERCENT,
UNIT_VOLT, UNIT_VOLT,
@ -33,16 +33,28 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ATCMiThermometer), cv.GenerateID(): cv.declare_id(ATCMiThermometer),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_BATTERY,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema( cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
), ),
} }
) )
@ -51,22 +63,22 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config) await esp32_ble_tracker.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens)) cg.add(var.set_temperature(sens))
if CONF_HUMIDITY in config: if CONF_HUMIDITY in config:
sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens)) cg.add(var.set_humidity(sens))
if CONF_BATTERY_LEVEL in config: if CONF_BATTERY_LEVEL in config:
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL]) sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
cg.add(var.set_battery_level(sens)) cg.add(var.set_battery_level(sens))
if CONF_BATTERY_VOLTAGE in config: if CONF_BATTERY_VOLTAGE in config:
sens = yield sensor.new_sensor(config[CONF_BATTERY_VOLTAGE]) sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
cg.add(var.set_battery_voltage(sens)) cg.add(var.set_battery_voltage(sens))

View File

@ -5,7 +5,7 @@
namespace esphome { namespace esphome {
namespace atm90e32 { namespace atm90e32 {
static const char *TAG = "atm90e32"; static const char *const TAG = "atm90e32";
void ATM90E32Component::update() { void ATM90E32Component::update() {
if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) { if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {
@ -58,6 +58,24 @@ void ATM90E32Component::update() {
if (this->phase_[2].power_factor_sensor_ != nullptr) { if (this->phase_[2].power_factor_sensor_ != nullptr) {
this->phase_[2].power_factor_sensor_->publish_state(this->get_power_factor_c_()); this->phase_[2].power_factor_sensor_->publish_state(this->get_power_factor_c_());
} }
if (this->phase_[0].forward_active_energy_sensor_ != nullptr) {
this->phase_[0].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_a_());
}
if (this->phase_[1].forward_active_energy_sensor_ != nullptr) {
this->phase_[1].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_b_());
}
if (this->phase_[2].forward_active_energy_sensor_ != nullptr) {
this->phase_[2].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_c_());
}
if (this->phase_[0].reverse_active_energy_sensor_ != nullptr) {
this->phase_[0].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_a_());
}
if (this->phase_[1].reverse_active_energy_sensor_ != nullptr) {
this->phase_[1].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_b_());
}
if (this->phase_[2].reverse_active_energy_sensor_ != nullptr) {
this->phase_[2].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_c_());
}
if (this->freq_sensor_ != nullptr) { if (this->freq_sensor_ != nullptr) {
this->freq_sensor_->publish_state(this->get_frequency_()); this->freq_sensor_->publish_state(this->get_frequency_());
} }
@ -119,16 +137,22 @@ void ATM90E32Component::dump_config() {
LOG_SENSOR(" ", "Power A", this->phase_[0].power_sensor_); LOG_SENSOR(" ", "Power A", this->phase_[0].power_sensor_);
LOG_SENSOR(" ", "Reactive Power A", this->phase_[0].reactive_power_sensor_); LOG_SENSOR(" ", "Reactive Power A", this->phase_[0].reactive_power_sensor_);
LOG_SENSOR(" ", "PF A", this->phase_[0].power_factor_sensor_); LOG_SENSOR(" ", "PF A", this->phase_[0].power_factor_sensor_);
LOG_SENSOR(" ", "Active Forward Energy A", this->phase_[0].forward_active_energy_sensor_);
LOG_SENSOR(" ", "Active Reverse Energy A", this->phase_[0].reverse_active_energy_sensor_);
LOG_SENSOR(" ", "Voltage B", this->phase_[1].voltage_sensor_); LOG_SENSOR(" ", "Voltage B", this->phase_[1].voltage_sensor_);
LOG_SENSOR(" ", "Current B", this->phase_[1].current_sensor_); LOG_SENSOR(" ", "Current B", this->phase_[1].current_sensor_);
LOG_SENSOR(" ", "Power B", this->phase_[1].power_sensor_); LOG_SENSOR(" ", "Power B", this->phase_[1].power_sensor_);
LOG_SENSOR(" ", "Reactive Power B", this->phase_[1].reactive_power_sensor_); LOG_SENSOR(" ", "Reactive Power B", this->phase_[1].reactive_power_sensor_);
LOG_SENSOR(" ", "PF B", this->phase_[1].power_factor_sensor_); LOG_SENSOR(" ", "PF B", this->phase_[1].power_factor_sensor_);
LOG_SENSOR(" ", "Active Forward Energy B", this->phase_[1].forward_active_energy_sensor_);
LOG_SENSOR(" ", "Active Reverse Energy B", this->phase_[1].reverse_active_energy_sensor_);
LOG_SENSOR(" ", "Voltage C", this->phase_[2].voltage_sensor_); LOG_SENSOR(" ", "Voltage C", this->phase_[2].voltage_sensor_);
LOG_SENSOR(" ", "Current C", this->phase_[2].current_sensor_); LOG_SENSOR(" ", "Current C", this->phase_[2].current_sensor_);
LOG_SENSOR(" ", "Power C", this->phase_[2].power_sensor_); LOG_SENSOR(" ", "Power C", this->phase_[2].power_sensor_);
LOG_SENSOR(" ", "Reactive Power C", this->phase_[2].reactive_power_sensor_); LOG_SENSOR(" ", "Reactive Power C", this->phase_[2].reactive_power_sensor_);
LOG_SENSOR(" ", "PF C", this->phase_[2].power_factor_sensor_); LOG_SENSOR(" ", "PF C", this->phase_[2].power_factor_sensor_);
LOG_SENSOR(" ", "Active Forward Energy C", this->phase_[2].forward_active_energy_sensor_);
LOG_SENSOR(" ", "Active Reverse Energy C", this->phase_[2].reverse_active_energy_sensor_);
LOG_SENSOR(" ", "Frequency", this->freq_sensor_); LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_); LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
} }
@ -239,6 +263,30 @@ float ATM90E32Component::get_power_factor_c_() {
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANC); int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANC);
return (float) pf / 1000; return (float) pf / 1000;
} }
float ATM90E32Component::get_forward_active_energy_a_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA);
return (float) val * 10 / 3200; // convert register value to WattHours
}
float ATM90E32Component::get_forward_active_energy_b_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB);
return (float) val * 10 / 3200;
}
float ATM90E32Component::get_forward_active_energy_c_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC);
return (float) val * 10 / 3200;
}
float ATM90E32Component::get_reverse_active_energy_a_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA);
return (float) val * 10 / 3200;
}
float ATM90E32Component::get_reverse_active_energy_b_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB);
return (float) val * 10 / 3200;
}
float ATM90E32Component::get_reverse_active_energy_c_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC);
return (float) val * 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);
return (float) freq / 100; return (float) freq / 100;

View File

@ -20,6 +20,12 @@ class ATM90E32Component : public PollingComponent,
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; } void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; } void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; } void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; }
void set_forward_active_energy_sensor(int phase, sensor::Sensor *obj) {
this->phase_[phase].forward_active_energy_sensor_ = obj;
}
void set_reverse_active_energy_sensor(int phase, sensor::Sensor *obj) {
this->phase_[phase].reverse_active_energy_sensor_ = obj;
}
void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; } void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; }
void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_gain_ = gain; } void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_gain_ = gain; }
void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; } void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; }
@ -52,6 +58,12 @@ class ATM90E32Component : public PollingComponent,
float get_power_factor_a_(); float get_power_factor_a_();
float get_power_factor_b_(); float get_power_factor_b_();
float get_power_factor_c_(); float get_power_factor_c_();
float get_forward_active_energy_a_();
float get_forward_active_energy_b_();
float get_forward_active_energy_c_();
float get_reverse_active_energy_a_();
float get_reverse_active_energy_b_();
float get_reverse_active_energy_c_();
float get_frequency_(); float get_frequency_();
float get_chip_temperature_(); float get_chip_temperature_();
@ -63,6 +75,8 @@ class ATM90E32Component : public PollingComponent,
sensor::Sensor *power_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr};
sensor::Sensor *reactive_power_sensor_{nullptr}; sensor::Sensor *reactive_power_sensor_{nullptr};
sensor::Sensor *power_factor_sensor_{nullptr}; sensor::Sensor *power_factor_sensor_{nullptr};
sensor::Sensor *forward_active_energy_sensor_{nullptr};
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
} 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};

View File

@ -39,7 +39,7 @@ static const uint16_t ATM90E32_STATUS_S0_OVPHASEBST = 1 << 11; // Over voltage
static const uint16_t ATM90E32_STATUS_S0_OVPHASECST = 1 << 10; // Over voltage on phase C static const uint16_t ATM90E32_STATUS_S0_OVPHASECST = 1 << 10; // Over voltage on phase C
static const uint16_t ATM90E32_STATUS_S0_UREVWNST = 1 << 9; // Voltage Phase Sequence Error status static const uint16_t ATM90E32_STATUS_S0_UREVWNST = 1 << 9; // Voltage Phase Sequence Error status
static const uint16_t ATM90E32_STATUS_S0_IREVWNST = 1 << 8; // Current Phase Sequence Error status static const uint16_t ATM90E32_STATUS_S0_IREVWNST = 1 << 8; // Current Phase Sequence Error status
static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater tha INWarnTh reg static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater than INWarnTh reg
static const uint16_t ATM90E32_STATUS_S0_TQNOLOADST = 1 << 6; // All phase sum reactive power no-load condition status static const uint16_t ATM90E32_STATUS_S0_TQNOLOADST = 1 << 6; // All phase sum reactive power no-load condition status
static const uint16_t ATM90E32_STATUS_S0_TPNOLOADST = 1 << 5; // All phase sum active power no-load condition status static const uint16_t ATM90E32_STATUS_S0_TPNOLOADST = 1 << 5; // All phase sum active power no-load condition status
static const uint16_t ATM90E32_STATUS_S0_TASNOLOADST = 1 << 4; // All phase sum apparent power no-load status static const uint16_t ATM90E32_STATUS_S0_TASNOLOADST = 1 << 4; // All phase sum apparent power no-load status

View File

@ -3,34 +3,37 @@ import esphome.config_validation as cv
from esphome.components import sensor, spi from esphome.components import sensor, spi
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_REACTIVE_POWER,
CONF_VOLTAGE, CONF_VOLTAGE,
CONF_CURRENT, CONF_CURRENT,
CONF_POWER, CONF_POWER,
CONF_POWER_FACTOR, CONF_POWER_FACTOR,
CONF_FREQUENCY, CONF_FREQUENCY,
CONF_FORWARD_ACTIVE_ENERGY,
CONF_REVERSE_ACTIVE_ENERGY,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
ICON_LIGHTBULB, ICON_LIGHTBULB,
ICON_CURRENT_AC, ICON_CURRENT_AC,
LAST_RESET_TYPE_AUTO,
STATE_CLASS_MEASUREMENT,
UNIT_HERTZ, UNIT_HERTZ,
UNIT_VOLT, UNIT_VOLT,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_WATT, UNIT_WATT,
UNIT_EMPTY,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_VOLT_AMPS_REACTIVE, UNIT_VOLT_AMPS_REACTIVE,
UNIT_WATT_HOURS,
) )
CONF_PHASE_A = "phase_a" CONF_PHASE_A = "phase_a"
CONF_PHASE_B = "phase_b" CONF_PHASE_B = "phase_b"
CONF_PHASE_C = "phase_c" CONF_PHASE_C = "phase_c"
CONF_REACTIVE_POWER = "reactive_power"
CONF_LINE_FREQUENCY = "line_frequency" CONF_LINE_FREQUENCY = "line_frequency"
CONF_CHIP_TEMPERATURE = "chip_temperature" CONF_CHIP_TEMPERATURE = "chip_temperature"
CONF_GAIN_PGA = "gain_pga" CONF_GAIN_PGA = "gain_pga"
@ -59,19 +62,47 @@ ATM90E32Component = atm90e32_ns.class_(
ATM90E32_PHASE_SCHEMA = cv.Schema( ATM90E32_PHASE_SCHEMA = cv.Schema(
{ {
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_CURRENT): sensor.sensor_schema( cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_POWER): sensor.sensor_schema( cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER unit_of_measurement=UNIT_WATT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema( cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE, ICON_LIGHTBULB, 2, DEVICE_CLASS_EMPTY unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
icon=ICON_LIGHTBULB,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema( cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_POWER_FACTOR accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_MEASUREMENT,
last_reset_type=LAST_RESET_TYPE_AUTO,
),
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_MEASUREMENT,
last_reset_type=LAST_RESET_TYPE_AUTO,
), ),
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t, cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t, cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
@ -86,10 +117,16 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY unit_of_measurement=UNIT_HERTZ,
icon=ICON_CURRENT_AC,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum( cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(
@ -103,10 +140,10 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield spi.register_spi_device(var, config) await spi.register_spi_device(var, config)
for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]): for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]):
if phase not in config: if phase not in config:
@ -115,25 +152,31 @@ def to_code(config):
cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE])) cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE]))
cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT])) cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT]))
if CONF_VOLTAGE in conf: if CONF_VOLTAGE in conf:
sens = yield sensor.new_sensor(conf[CONF_VOLTAGE]) sens = await sensor.new_sensor(conf[CONF_VOLTAGE])
cg.add(var.set_voltage_sensor(i, sens)) cg.add(var.set_voltage_sensor(i, sens))
if CONF_CURRENT in conf: if CONF_CURRENT in conf:
sens = yield sensor.new_sensor(conf[CONF_CURRENT]) sens = await sensor.new_sensor(conf[CONF_CURRENT])
cg.add(var.set_current_sensor(i, sens)) cg.add(var.set_current_sensor(i, sens))
if CONF_POWER in conf: if CONF_POWER in conf:
sens = yield sensor.new_sensor(conf[CONF_POWER]) sens = await sensor.new_sensor(conf[CONF_POWER])
cg.add(var.set_power_sensor(i, sens)) cg.add(var.set_power_sensor(i, sens))
if CONF_REACTIVE_POWER in conf: if CONF_REACTIVE_POWER in conf:
sens = yield sensor.new_sensor(conf[CONF_REACTIVE_POWER]) sens = await sensor.new_sensor(conf[CONF_REACTIVE_POWER])
cg.add(var.set_reactive_power_sensor(i, sens)) cg.add(var.set_reactive_power_sensor(i, sens))
if CONF_POWER_FACTOR in conf: if CONF_POWER_FACTOR in conf:
sens = yield sensor.new_sensor(conf[CONF_POWER_FACTOR]) sens = await sensor.new_sensor(conf[CONF_POWER_FACTOR])
cg.add(var.set_power_factor_sensor(i, sens)) cg.add(var.set_power_factor_sensor(i, sens))
if CONF_FORWARD_ACTIVE_ENERGY in conf:
sens = await sensor.new_sensor(conf[CONF_FORWARD_ACTIVE_ENERGY])
cg.add(var.set_forward_active_energy_sensor(i, sens))
if CONF_REVERSE_ACTIVE_ENERGY in conf:
sens = await sensor.new_sensor(conf[CONF_REVERSE_ACTIVE_ENERGY])
cg.add(var.set_reverse_active_energy_sensor(i, sens))
if CONF_FREQUENCY in config: if CONF_FREQUENCY in config:
sens = yield sensor.new_sensor(config[CONF_FREQUENCY]) sens = await sensor.new_sensor(config[CONF_FREQUENCY])
cg.add(var.set_freq_sensor(sens)) cg.add(var.set_freq_sensor(sens))
if CONF_CHIP_TEMPERATURE in config: if CONF_CHIP_TEMPERATURE in config:
sens = yield sensor.new_sensor(config[CONF_CHIP_TEMPERATURE]) sens = await sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
cg.add(var.set_chip_temperature_sensor(sens)) cg.add(var.set_chip_temperature_sensor(sens))
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY])) cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES])) cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))

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