mirror of
https://github.com/esphome/esphome.git
synced 2024-11-26 17:05:21 +01:00
commit
10a6e9b4ee
589 changed files with 10508 additions and 4240 deletions
|
@ -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
|
||||||
|
|
25
.clang-tidy
25
.clang-tidy
|
@ -5,30 +5,19 @@ Checks: >-
|
||||||
-android-*,
|
-android-*,
|
||||||
-boost-*,
|
-boost-*,
|
||||||
-bugprone-branch-clone,
|
-bugprone-branch-clone,
|
||||||
-bugprone-macro-parentheses,
|
|
||||||
-bugprone-narrowing-conversions,
|
-bugprone-narrowing-conversions,
|
||||||
-bugprone-reserved-identifier,
|
|
||||||
-bugprone-signed-char-misuse,
|
-bugprone-signed-char-misuse,
|
||||||
-bugprone-suspicious-include,
|
|
||||||
-bugprone-too-small-loop-variable,
|
-bugprone-too-small-loop-variable,
|
||||||
-bugprone-unhandled-self-assignment,
|
|
||||||
-cert-dcl37-c,
|
|
||||||
-cert-dcl50-cpp,
|
-cert-dcl50-cpp,
|
||||||
-cert-dcl51-cpp,
|
|
||||||
-cert-err58-cpp,
|
-cert-err58-cpp,
|
||||||
-cert-oop54-cpp,
|
|
||||||
-cert-oop57-cpp,
|
-cert-oop57-cpp,
|
||||||
-cert-str34-c,
|
-cert-str34-c,
|
||||||
-clang-analyzer-core.CallAndMessage,
|
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||||
-clang-analyzer-optin.*,
|
|
||||||
-clang-analyzer-osx.*,
|
-clang-analyzer-osx.*,
|
||||||
-clang-analyzer-security.*,
|
|
||||||
-clang-diagnostic-shadow-field,
|
-clang-diagnostic-shadow-field,
|
||||||
-cppcoreguidelines-avoid-c-arrays,
|
-cppcoreguidelines-avoid-c-arrays,
|
||||||
-cppcoreguidelines-avoid-goto,
|
-cppcoreguidelines-avoid-goto,
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
|
||||||
-cppcoreguidelines-c-copy-assignment-signature,
|
|
||||||
-cppcoreguidelines-init-variables,
|
-cppcoreguidelines-init-variables,
|
||||||
-cppcoreguidelines-macro-usage,
|
-cppcoreguidelines-macro-usage,
|
||||||
-cppcoreguidelines-narrowing-conversions,
|
-cppcoreguidelines-narrowing-conversions,
|
||||||
|
@ -45,17 +34,17 @@ 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-else-after-return,
|
||||||
|
@ -65,12 +54,8 @@ Checks: >-
|
||||||
-llvmlibc-*,
|
-llvmlibc-*,
|
||||||
-misc-non-private-member-variables-in-classes,
|
-misc-non-private-member-variables-in-classes,
|
||||||
-misc-no-recursion,
|
-misc-no-recursion,
|
||||||
-misc-unconventional-assign-operator,
|
|
||||||
-misc-unused-parameters,
|
-misc-unused-parameters,
|
||||||
-modernize-avoid-c-arrays,
|
-modernize-avoid-c-arrays,
|
||||||
-modernize-deprecated-headers,
|
|
||||||
-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,
|
||||||
|
@ -78,7 +63,6 @@ Checks: >-
|
||||||
-modernize-use-trailing-return-type,
|
-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-const-return-type,
|
||||||
-readability-convert-member-functions-to-static,
|
-readability-convert-member-functions-to-static,
|
||||||
|
@ -94,8 +78,7 @@ Checks: >-
|
||||||
-readability-redundant-string-init,
|
-readability-redundant-string-init,
|
||||||
-readability-uppercase-literal-suffix,
|
-readability-uppercase-literal-suffix,
|
||||||
-readability-use-anyofallof,
|
-readability-use-anyofallof,
|
||||||
-warnings-as-errors,
|
-warnings-as-errors
|
||||||
-zircon-*
|
|
||||||
WarningsAsErrors: '*'
|
WarningsAsErrors: '*'
|
||||||
HeaderFilterRegex: '^.*/src/esphome/.*'
|
HeaderFilterRegex: '^.*/src/esphome/.*'
|
||||||
AnalyzeTemporaryDtors: false
|
AnalyzeTemporaryDtors: false
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,10 @@ venv.bak/
|
||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
|
||||||
|
# PlatformIO
|
||||||
|
.pio/
|
||||||
|
|
||||||
|
# ESPHome
|
||||||
config/
|
config/
|
||||||
examples/
|
examples/
|
||||||
Dockerfile
|
Dockerfile
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
49
.github/workflows/ci-docker.yml
vendored
49
.github/workflows/ci-docker.yml
vendored
|
@ -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="3.4.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.2.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}" \
|
|
||||||
.
|
|
||||||
|
|
178
.github/workflows/ci.yml
vendored
178
.github/workflows/ci.yml
vendored
|
@ -4,40 +4,36 @@ 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:1.1
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
# 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:1.1
|
|
||||||
# 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
|
||||||
# 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
|
||||||
|
@ -45,26 +41,57 @@ jobs:
|
||||||
- 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"
|
||||||
|
|
||||||
|
- name: Run clang-format
|
||||||
|
run: script/clang-format -i
|
||||||
|
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 }}
|
||||||
|
if: ${{ matrix.id == 'clang-tidy' }}
|
||||||
|
|
||||||
- name: Suggest changes
|
- name: Suggest changes
|
||||||
run: script/ci-suggest-changes
|
run: script/ci-suggest-changes
|
||||||
|
|
||||||
lint-python:
|
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: 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:
|
||||||
|
@ -72,6 +99,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
|
||||||
|
|
||||||
|
@ -80,82 +118,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
|
|
||||||
- test5
|
|
||||||
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 compile tests/${{ matrix.test }}.yaml
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
108
.github/workflows/docker-lint-build.yml
vendored
108
.github/workflows/docker-lint-build.yml
vendored
|
@ -13,30 +13,88 @@ on:
|
||||||
- '.github/workflows/docker-lint-build.yml'
|
- '.github/workflows/docker-lint-build.yml'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-docker-lint-iage:
|
deploy-docker:
|
||||||
name: Build docker containers
|
name: Build and publish docker containers
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: [amd64, armv7, aarch64]
|
||||||
|
build_type: ["lint"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set TAG
|
- name: Set up Python
|
||||||
run: |
|
uses: actions/setup-python@v2
|
||||||
echo "TAG=1.1" >> $GITHUB_ENV
|
with:
|
||||||
- name: Pull for cache
|
python-version: '3.9'
|
||||||
run: |
|
- name: Set TAG
|
||||||
docker pull "esphome/esphome-lint:latest" || true
|
run: |
|
||||||
- name: Build
|
echo "TAG=1.1" >> $GITHUB_ENV
|
||||||
run: |
|
|
||||||
docker build \
|
- name: Run build
|
||||||
--cache-from "esphome/esphome-lint:latest" \
|
run: |
|
||||||
--file "docker/Dockerfile.lint" \
|
docker/build.py \
|
||||||
--tag "esphome/esphome-lint:latest" \
|
--tag "${TAG}" \
|
||||||
--tag "esphome/esphome-lint:${TAG}" \
|
--arch "${{ matrix.arch }}" \
|
||||||
.
|
--build-type "${{ matrix.build_type }}" \
|
||||||
- name: Log in to docker hub
|
build
|
||||||
env:
|
|
||||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
- name: Log in to docker hub
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
uses: docker/login-action@v1
|
||||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
with:
|
||||||
- run: |
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
docker push "esphome/esphome-lint:${TAG}"
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
docker push "esphome/esphome-lint:latest"
|
- 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
19
.github/workflows/matchers/pytest.json
vendored
Normal 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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
247
.github/workflows/release-dev.yml
vendored
247
.github/workflows/release-dev.yml
vendored
|
@ -1,247 +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:1.1
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
# 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:1.1
|
|
||||||
# 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
|
|
||||||
# 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
|
|
||||||
- test5
|
|
||||||
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 compile tests/${{ matrix.test }}.yaml
|
|
||||||
|
|
||||||
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="3.4.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.2.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
|
|
317
.github/workflows/release.yml
vendored
317
.github/workflows/release.yml
vendored
|
@ -1,164 +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:1.1
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
# Set up the pio project so that the cpp checks know how files are compiled
|
- name: Get tag
|
||||||
# (build flags, libraries etc)
|
id: tag
|
||||||
- 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:1.1
|
|
||||||
# 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
|
|
||||||
# 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
|
|
||||||
- test5
|
|
||||||
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 compile tests/${{ matrix.test }}.yaml
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -182,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="3.4.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.2.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:
|
||||||
|
@ -307,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\"}}"
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -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
|
||||||
|
|
|
@ -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
35
.vscode/tasks.json
vendored
|
@ -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 dashboard config",
|
"pattern": [
|
||||||
"problemMatcher": []
|
{
|
||||||
|
"regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,12 @@ esphome/components/ac_dimmer/* @glmnet
|
||||||
esphome/components/adc/* @esphome/core
|
esphome/components/adc/* @esphome/core
|
||||||
esphome/components/addressable_light/* @justfalter
|
esphome/components/addressable_light/* @justfalter
|
||||||
esphome/components/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/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/ble_client/* @buxtronix
|
||||||
|
@ -45,6 +47,7 @@ esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||||
esphome/components/globals/* @esphome/core
|
esphome/components/globals/* @esphome/core
|
||||||
esphome/components/gpio/* @esphome/core
|
esphome/components/gpio/* @esphome/core
|
||||||
esphome/components/gps/* @coogle
|
esphome/components/gps/* @coogle
|
||||||
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
esphome/components/homeassistant/* @OttoWinter
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
esphome/components/i2c/* @esphome/core
|
esphome/components/i2c/* @esphome/core
|
||||||
esphome/components/improv/* @jesserockz
|
esphome/components/improv/* @jesserockz
|
||||||
|
@ -70,7 +73,13 @@ esphome/components/midea_ac/* @dudanov
|
||||||
esphome/components/midea_dongle/* @dudanov
|
esphome/components/midea_dongle/* @dudanov
|
||||||
esphome/components/mitsubishi/* @RubyBailey
|
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
|
||||||
|
|
|
@ -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/)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
ARG BUILD_FROM=esphome/esphome-base-amd64:3.4.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
|
||||||
|
|
|
@ -1,13 +1 @@
|
||||||
FROM esphome/esphome-base-amd64:3.4.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
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
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
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
FROM esphome/esphome-lint-base:3.4.0
|
ARG BUILD_FROM=esphome/esphome-lint-base:latest
|
||||||
|
FROM ${BUILD_FROM}
|
||||||
|
|
||||||
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
||||||
RUN \
|
RUN \
|
||||||
|
|
177
docker/build.py
Executable file
177
docker/build.py
Executable 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()
|
|
@ -516,7 +516,7 @@ def parse_args(argv):
|
||||||
|
|
||||||
deprecated_argv_suggestion = None
|
deprecated_argv_suggestion = None
|
||||||
|
|
||||||
if ["dashboard", "config"] == argv[1:3]:
|
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
|
# this is most likely meant in new-style arg format. do not try compat parsing
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -60,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,
|
||||||
|
|
|
@ -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...");
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -8,10 +8,33 @@ 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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ADCSensor::setup() {
|
void ADCSensor::setup() {
|
||||||
|
@ -21,7 +44,9 @@ 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);
|
||||||
|
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void ADCSensor::dump_config() {
|
void ADCSensor::dump_config() {
|
||||||
|
@ -36,18 +61,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,20 +87,23 @@ 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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
return value_v;
|
return value_v;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace addressable_light {
|
namespace addressable_light {
|
||||||
|
|
||||||
static const char* TAG = "addressable_light.display";
|
static const char *const TAG = "addressable_light.display";
|
||||||
|
|
||||||
int AddressableLightDisplay::get_width_internal() { return this->width_; }
|
int AddressableLightDisplay::get_width_internal() { return this->width_; }
|
||||||
int AddressableLightDisplay::get_height_internal() { return this->height_; }
|
int AddressableLightDisplay::get_height_internal() { return this->height_; }
|
||||||
|
@ -24,7 +24,7 @@ void AddressableLightDisplay::update() {
|
||||||
void AddressableLightDisplay::display() {
|
void AddressableLightDisplay::display() {
|
||||||
bool dirty = false;
|
bool dirty = false;
|
||||||
uint8_t old_r, old_g, old_b, old_w;
|
uint8_t old_r, old_g, old_b, old_w;
|
||||||
Color* c;
|
Color *c;
|
||||||
|
|
||||||
for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
|
for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
|
||||||
c = &(this->addressable_light_buffer_[offset]);
|
c = &(this->addressable_light_buffer_[offset]);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
140
esphome/components/anova/anova.cpp
Normal file
140
esphome/components/anova/anova.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
#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());
|
||||||
|
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
|
50
esphome/components/anova/anova.h
Normal file
50
esphome/components/anova/anova.h
Normal 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
|
119
esphome/components/anova/anova_base.cpp
Normal file
119
esphome/components/anova/anova_base.cpp
Normal 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
|
79
esphome/components/anova/anova_base.h
Normal file
79
esphome/components/anova/anova_base.h
Normal 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
|
25
esphome/components/anova/climate.py
Normal file
25
esphome/components/anova/climate.py
Normal 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"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield climate.register_climate(var, config)
|
||||||
|
yield ble_client.register_ble_node(var, config)
|
|
@ -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; \
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ 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) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -378,6 +379,7 @@ message LightStateResponse {
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool state = 2;
|
bool state = 2;
|
||||||
float brightness = 3;
|
float brightness = 3;
|
||||||
|
float color_brightness = 10;
|
||||||
float red = 4;
|
float red = 4;
|
||||||
float green = 5;
|
float green = 5;
|
||||||
float blue = 6;
|
float blue = 6;
|
||||||
|
@ -396,6 +398,8 @@ 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_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;
|
||||||
|
@ -418,6 +422,12 @@ enum SensorStateClass {
|
||||||
STATE_CLASS_MEASUREMENT = 1;
|
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;
|
||||||
|
@ -434,6 +444,7 @@ message ListEntitiesSensorResponse {
|
||||||
bool force_update = 8;
|
bool force_update = 8;
|
||||||
string device_class = 9;
|
string device_class = 9;
|
||||||
SensorStateClass state_class = 10;
|
SensorStateClass state_class = 10;
|
||||||
|
SensorLastResetType last_reset_type = 11;
|
||||||
}
|
}
|
||||||
message SensorStateResponse {
|
message SensorStateResponse {
|
||||||
option (id) = 25;
|
option (id) = 25;
|
||||||
|
@ -795,3 +806,41 @@ message ClimateCommandRequest {
|
||||||
bool has_custom_preset = 20;
|
bool has_custom_preset = 20;
|
||||||
string custom_preset = 21;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
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) {
|
||||||
|
@ -308,6 +308,7 @@ bool APIConnection::send_light_state(light::LightState *light) {
|
||||||
if (traits.get_supports_brightness())
|
if (traits.get_supports_brightness())
|
||||||
resp.brightness = values.get_brightness();
|
resp.brightness = values.get_brightness();
|
||||||
if (traits.get_supports_rgb()) {
|
if (traits.get_supports_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();
|
||||||
|
@ -352,6 +353,8 @@ 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_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);
|
||||||
|
@ -396,6 +399,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||||
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.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);
|
||||||
}
|
}
|
||||||
|
@ -550,6 +554,42 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||||
}
|
}
|
||||||
#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_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_)
|
||||||
|
|
|
@ -62,6 +62,11 @@ 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
|
#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) {
|
||||||
|
|
|
@ -72,6 +72,18 @@ template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::Sens
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
template<> const char *proto_enum_to_string<enums::SensorLastResetType>(enums::SensorLastResetType value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::LAST_RESET_NONE:
|
||||||
|
return "LAST_RESET_NONE";
|
||||||
|
case enums::LAST_RESET_NEVER:
|
||||||
|
return "LAST_RESET_NEVER";
|
||||||
|
case enums::LAST_RESET_AUTO:
|
||||||
|
return "LAST_RESET_AUTO";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
|
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case enums::LOG_LEVEL_NONE:
|
case enums::LOG_LEVEL_NONE:
|
||||||
|
@ -1263,6 +1275,10 @@ bool LightStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
this->brightness = value.as_float();
|
this->brightness = value.as_float();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 10: {
|
||||||
|
this->color_brightness = value.as_float();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case 4: {
|
case 4: {
|
||||||
this->red = value.as_float();
|
this->red = value.as_float();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1291,6 +1307,7 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_bool(2, this->state);
|
buffer.encode_bool(2, this->state);
|
||||||
buffer.encode_float(3, this->brightness);
|
buffer.encode_float(3, this->brightness);
|
||||||
|
buffer.encode_float(10, this->color_brightness);
|
||||||
buffer.encode_float(4, this->red);
|
buffer.encode_float(4, this->red);
|
||||||
buffer.encode_float(5, this->green);
|
buffer.encode_float(5, this->green);
|
||||||
buffer.encode_float(6, this->blue);
|
buffer.encode_float(6, this->blue);
|
||||||
|
@ -1315,6 +1332,11 @@ void LightStateResponse::dump_to(std::string &out) const {
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" color_brightness: ");
|
||||||
|
sprintf(buffer, "%g", this->color_brightness);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" red: ");
|
out.append(" red: ");
|
||||||
sprintf(buffer, "%g", this->red);
|
sprintf(buffer, "%g", this->red);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
|
@ -1359,6 +1381,10 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
this->has_brightness = value.as_bool();
|
this->has_brightness = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 20: {
|
||||||
|
this->has_color_brightness = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case 6: {
|
case 6: {
|
||||||
this->has_rgb = value.as_bool();
|
this->has_rgb = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1415,6 +1441,10 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
this->brightness = value.as_float();
|
this->brightness = value.as_float();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 21: {
|
||||||
|
this->color_brightness = value.as_float();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case 7: {
|
case 7: {
|
||||||
this->red = value.as_float();
|
this->red = value.as_float();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1445,6 +1475,8 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_bool(3, this->state);
|
buffer.encode_bool(3, this->state);
|
||||||
buffer.encode_bool(4, this->has_brightness);
|
buffer.encode_bool(4, this->has_brightness);
|
||||||
buffer.encode_float(5, this->brightness);
|
buffer.encode_float(5, this->brightness);
|
||||||
|
buffer.encode_bool(20, this->has_color_brightness);
|
||||||
|
buffer.encode_float(21, this->color_brightness);
|
||||||
buffer.encode_bool(6, this->has_rgb);
|
buffer.encode_bool(6, this->has_rgb);
|
||||||
buffer.encode_float(7, this->red);
|
buffer.encode_float(7, this->red);
|
||||||
buffer.encode_float(8, this->green);
|
buffer.encode_float(8, this->green);
|
||||||
|
@ -1485,6 +1517,15 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" has_color_brightness: ");
|
||||||
|
out.append(YESNO(this->has_color_brightness));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" color_brightness: ");
|
||||||
|
sprintf(buffer, "%g", this->color_brightness);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" has_rgb: ");
|
out.append(" has_rgb: ");
|
||||||
out.append(YESNO(this->has_rgb));
|
out.append(YESNO(this->has_rgb));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
@ -1563,6 +1604,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
|
||||||
this->state_class = value.as_enum<enums::SensorStateClass>();
|
this->state_class = value.as_enum<enums::SensorStateClass>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 11: {
|
||||||
|
this->last_reset_type = value.as_enum<enums::SensorLastResetType>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1618,6 +1663,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_bool(8, this->force_update);
|
buffer.encode_bool(8, this->force_update);
|
||||||
buffer.encode_string(9, this->device_class);
|
buffer.encode_string(9, this->device_class);
|
||||||
buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
|
buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
|
||||||
|
buffer.encode_enum<enums::SensorLastResetType>(11, this->last_reset_type);
|
||||||
}
|
}
|
||||||
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
|
@ -1663,6 +1709,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
||||||
out.append(" state_class: ");
|
out.append(" state_class: ");
|
||||||
out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
|
out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" last_reset_type: ");
|
||||||
|
out.append(proto_enum_to_string<enums::SensorLastResetType>(this->last_reset_type));
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
@ -3227,6 +3277,179 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
|
bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->object_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
this->name = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
this->unique_id = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->icon = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ListEntitiesNumberResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 2: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 6: {
|
||||||
|
this->min_value = value.as_float();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 7: {
|
||||||
|
this->max_value = value.as_float();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 8: {
|
||||||
|
this->step = value.as_float();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_string(1, this->object_id);
|
||||||
|
buffer.encode_fixed32(2, this->key);
|
||||||
|
buffer.encode_string(3, this->name);
|
||||||
|
buffer.encode_string(4, this->unique_id);
|
||||||
|
buffer.encode_string(5, this->icon);
|
||||||
|
buffer.encode_float(6, this->min_value);
|
||||||
|
buffer.encode_float(7, this->max_value);
|
||||||
|
buffer.encode_float(8, this->step);
|
||||||
|
}
|
||||||
|
void ListEntitiesNumberResponse::dump_to(std::string &out) const {
|
||||||
|
char buffer[64];
|
||||||
|
out.append("ListEntitiesNumberResponse {\n");
|
||||||
|
out.append(" object_id: ");
|
||||||
|
out.append("'").append(this->object_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%u", this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" name: ");
|
||||||
|
out.append("'").append(this->name).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" unique_id: ");
|
||||||
|
out.append("'").append(this->unique_id).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" icon: ");
|
||||||
|
out.append("'").append(this->icon).append("'");
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" min_value: ");
|
||||||
|
sprintf(buffer, "%g", this->min_value);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" max_value: ");
|
||||||
|
sprintf(buffer, "%g", this->max_value);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" step: ");
|
||||||
|
sprintf(buffer, "%g", this->step);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
bool NumberStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 3: {
|
||||||
|
this->missing_state = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool NumberStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
this->state = value.as_float();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_float(2, this->state);
|
||||||
|
buffer.encode_bool(3, this->missing_state);
|
||||||
|
}
|
||||||
|
void NumberStateResponse::dump_to(std::string &out) const {
|
||||||
|
char buffer[64];
|
||||||
|
out.append("NumberStateResponse {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%u", this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" state: ");
|
||||||
|
sprintf(buffer, "%g", this->state);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" missing_state: ");
|
||||||
|
out.append(YESNO(this->missing_state));
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 1: {
|
||||||
|
this->key = value.as_fixed32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
this->state = value.as_float();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
|
buffer.encode_fixed32(1, this->key);
|
||||||
|
buffer.encode_float(2, this->state);
|
||||||
|
}
|
||||||
|
void NumberCommandRequest::dump_to(std::string &out) const {
|
||||||
|
char buffer[64];
|
||||||
|
out.append("NumberCommandRequest {\n");
|
||||||
|
out.append(" key: ");
|
||||||
|
sprintf(buffer, "%u", this->key);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" state: ");
|
||||||
|
sprintf(buffer, "%g", this->state);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
|
out.append("}");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -36,6 +36,11 @@ enum SensorStateClass : uint32_t {
|
||||||
STATE_CLASS_NONE = 0,
|
STATE_CLASS_NONE = 0,
|
||||||
STATE_CLASS_MEASUREMENT = 1,
|
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,
|
||||||
|
@ -371,6 +376,7 @@ class LightStateResponse : public ProtoMessage {
|
||||||
uint32_t key{0};
|
uint32_t key{0};
|
||||||
bool state{false};
|
bool state{false};
|
||||||
float brightness{0.0f};
|
float brightness{0.0f};
|
||||||
|
float color_brightness{0.0f};
|
||||||
float red{0.0f};
|
float red{0.0f};
|
||||||
float green{0.0f};
|
float green{0.0f};
|
||||||
float blue{0.0f};
|
float blue{0.0f};
|
||||||
|
@ -392,6 +398,8 @@ class LightCommandRequest : public ProtoMessage {
|
||||||
bool state{false};
|
bool state{false};
|
||||||
bool has_brightness{false};
|
bool has_brightness{false};
|
||||||
float brightness{0.0f};
|
float brightness{0.0f};
|
||||||
|
bool has_color_brightness{false};
|
||||||
|
float color_brightness{0.0f};
|
||||||
bool has_rgb{false};
|
bool has_rgb{false};
|
||||||
float red{0.0f};
|
float red{0.0f};
|
||||||
float green{0.0f};
|
float green{0.0f};
|
||||||
|
@ -426,6 +434,7 @@ class ListEntitiesSensorResponse : public ProtoMessage {
|
||||||
bool force_update{false};
|
bool force_update{false};
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
enums::SensorStateClass state_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;
|
||||||
|
|
||||||
|
@ -779,6 +788,45 @@ class ClimateCommandRequest : public ProtoMessage {
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited 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 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 dump_to(std::string &out) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit 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;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -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,20 @@ 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
|
||||||
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 +363,15 @@ 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
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -547,6 +570,19 @@ 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
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -111,6 +111,15 @@ 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
|
#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 +156,9 @@ 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
|
#endif
|
||||||
protected:
|
protected:
|
||||||
void on_hello_request(const HelloRequest &msg) override;
|
void on_hello_request(const HelloRequest &msg) override;
|
||||||
|
@ -179,6 +191,9 @@ 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
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|
|
@ -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,18 @@ 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
|
||||||
|
|
||||||
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) {
|
||||||
|
|
|
@ -56,10 +56,13 @@ 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
|
#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); }
|
||||||
|
@ -91,7 +94,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:
|
||||||
|
|
|
@ -51,5 +51,9 @@ 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
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -39,6 +39,9 @@ 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
|
#endif
|
||||||
bool on_end() override;
|
bool on_end() override;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -37,6 +37,11 @@ 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
|
||||||
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
|
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
|
||||||
: ComponentIterator(server), client_(client) {}
|
: ComponentIterator(server), client_(client) {}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,9 @@ 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
|
#endif
|
||||||
protected:
|
protected:
|
||||||
APIConnection *client_;
|
APIConnection *client_;
|
||||||
|
|
|
@ -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_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,21 @@ 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
|
#endif
|
||||||
case IteratorState::MAX:
|
case IteratorState::MAX:
|
||||||
if (this->on_end()) {
|
if (this->on_end()) {
|
||||||
|
|
|
@ -47,6 +47,9 @@ 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
|
#endif
|
||||||
virtual bool on_end();
|
virtual bool on_end();
|
||||||
|
|
||||||
|
@ -81,6 +84,9 @@ class ComponentIterator {
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
CLIMATE,
|
CLIMATE,
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
NUMBER,
|
||||||
#endif
|
#endif
|
||||||
MAX,
|
MAX,
|
||||||
} state_{IteratorState::NONE};
|
} state_{IteratorState::NONE};
|
||||||
|
|
|
@ -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...");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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!");
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ from esphome.const import (
|
||||||
ICON_EMPTY,
|
ICON_EMPTY,
|
||||||
ICON_LIGHTBULB,
|
ICON_LIGHTBULB,
|
||||||
ICON_CURRENT_AC,
|
ICON_CURRENT_AC,
|
||||||
|
LAST_RESET_TYPE_AUTO,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_HERTZ,
|
UNIT_HERTZ,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
|
@ -91,10 +92,20 @@ ATM90E32_PHASE_SCHEMA = cv.Schema(
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
|
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
|
||||||
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
|
UNIT_WATT_HOURS,
|
||||||
|
ICON_EMPTY,
|
||||||
|
2,
|
||||||
|
DEVICE_CLASS_ENERGY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
LAST_RESET_TYPE_AUTO,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
|
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
|
||||||
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
|
UNIT_WATT_HOURS,
|
||||||
|
ICON_EMPTY,
|
||||||
|
2,
|
||||||
|
DEVICE_CLASS_ENERGY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
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,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace b_parasite {
|
namespace b_parasite {
|
||||||
|
|
||||||
static const char* TAG = "b_parasite";
|
static const char *const TAG = "b_parasite";
|
||||||
|
|
||||||
void BParasite::dump_config() {
|
void BParasite::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "b_parasite");
|
ESP_LOGCONFIG(TAG, "b_parasite");
|
||||||
|
@ -16,25 +16,25 @@ void BParasite::dump_config() {
|
||||||
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
|
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice& device) {
|
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
if (device.address_uint64() != address_) {
|
if (device.address_uint64() != address_) {
|
||||||
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
|
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
|
ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
|
||||||
const auto& service_datas = device.get_service_datas();
|
const auto &service_datas = device.get_service_datas();
|
||||||
if (service_datas.size() != 1) {
|
if (service_datas.size() != 1) {
|
||||||
ESP_LOGE(TAG, "Unexpected service_datas size (%d)", service_datas.size());
|
ESP_LOGE(TAG, "Unexpected service_datas size (%d)", service_datas.size());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto& service_data = service_datas[0];
|
const auto &service_data = service_datas[0];
|
||||||
|
|
||||||
ESP_LOGVV(TAG, "Service data:");
|
ESP_LOGVV(TAG, "Service data:");
|
||||||
for (const uint8_t byte : service_data.data) {
|
for (const uint8_t byte : service_data.data) {
|
||||||
ESP_LOGVV(TAG, "0x%02x", byte);
|
ESP_LOGVV(TAG, "0x%02x", byte);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& data = service_data.data;
|
const auto &data = service_data.data;
|
||||||
|
|
||||||
// Counter for deduplicating messages.
|
// Counter for deduplicating messages.
|
||||||
uint8_t counter = data[1] & 0x0f;
|
uint8_t counter = data[1] & 0x0f;
|
||||||
|
|
239
esphome/components/ballu/ballu.cpp
Normal file
239
esphome/components/ballu/ballu.cpp
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
#include "ballu.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ballu {
|
||||||
|
|
||||||
|
static const char *const TAG = "ballu.climate";
|
||||||
|
|
||||||
|
const uint16_t BALLU_HEADER_MARK = 9000;
|
||||||
|
const uint16_t BALLU_HEADER_SPACE = 4500;
|
||||||
|
const uint16_t BALLU_BIT_MARK = 575;
|
||||||
|
const uint16_t BALLU_ONE_SPACE = 1675;
|
||||||
|
const uint16_t BALLU_ZERO_SPACE = 550;
|
||||||
|
|
||||||
|
const uint32_t BALLU_CARRIER_FREQUENCY = 38000;
|
||||||
|
|
||||||
|
const uint8_t BALLU_STATE_LENGTH = 13;
|
||||||
|
|
||||||
|
const uint8_t BALLU_AUTO = 0;
|
||||||
|
const uint8_t BALLU_COOL = 0x20;
|
||||||
|
const uint8_t BALLU_DRY = 0x40;
|
||||||
|
const uint8_t BALLU_HEAT = 0x80;
|
||||||
|
const uint8_t BALLU_FAN = 0xc0;
|
||||||
|
|
||||||
|
const uint8_t BALLU_FAN_AUTO = 0xa0;
|
||||||
|
const uint8_t BALLU_FAN_HIGH = 0x20;
|
||||||
|
const uint8_t BALLU_FAN_MED = 0x40;
|
||||||
|
const uint8_t BALLU_FAN_LOW = 0x60;
|
||||||
|
|
||||||
|
const uint8_t BALLU_SWING_VER = 0x07;
|
||||||
|
const uint8_t BALLU_SWING_HOR = 0xe0;
|
||||||
|
const uint8_t BALLU_POWER = 0x20;
|
||||||
|
|
||||||
|
void BalluClimate::transmit_state() {
|
||||||
|
uint8_t remote_state[BALLU_STATE_LENGTH] = {0};
|
||||||
|
|
||||||
|
auto temp = (uint8_t) roundf(clamp(this->target_temperature, YKR_K_002E_TEMP_MIN, YKR_K_002E_TEMP_MAX));
|
||||||
|
auto swing_ver =
|
||||||
|
((this->swing_mode == climate::CLIMATE_SWING_VERTICAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH));
|
||||||
|
auto swing_hor =
|
||||||
|
((this->swing_mode == climate::CLIMATE_SWING_HORIZONTAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH));
|
||||||
|
|
||||||
|
remote_state[0] = 0xc3;
|
||||||
|
remote_state[1] = ((temp - 8) << 3) | (swing_ver ? 0 : BALLU_SWING_VER);
|
||||||
|
remote_state[2] = swing_hor ? 0 : BALLU_SWING_HOR;
|
||||||
|
remote_state[9] = (this->mode == climate::CLIMATE_MODE_OFF) ? 0 : BALLU_POWER;
|
||||||
|
remote_state[11] = 0x1e;
|
||||||
|
|
||||||
|
// Fan speed
|
||||||
|
switch (this->fan_mode.value()) {
|
||||||
|
case climate::CLIMATE_FAN_HIGH:
|
||||||
|
remote_state[4] |= BALLU_FAN_HIGH;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_MEDIUM:
|
||||||
|
remote_state[4] |= BALLU_FAN_MED;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_LOW:
|
||||||
|
remote_state[4] |= BALLU_FAN_LOW;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_FAN_AUTO:
|
||||||
|
remote_state[4] |= BALLU_FAN_AUTO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
switch (this->mode) {
|
||||||
|
case climate::CLIMATE_MODE_AUTO:
|
||||||
|
remote_state[6] |= BALLU_AUTO;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_HEAT:
|
||||||
|
remote_state[6] |= BALLU_HEAT;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_COOL:
|
||||||
|
remote_state[6] |= BALLU_COOL;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_DRY:
|
||||||
|
remote_state[6] |= BALLU_DRY;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||||
|
remote_state[6] |= BALLU_FAN;
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_OFF:
|
||||||
|
remote_state[6] |= BALLU_AUTO;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checksum
|
||||||
|
for (uint8_t i = 0; i < BALLU_STATE_LENGTH - 1; i++)
|
||||||
|
remote_state[12] += remote_state[i];
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Sending: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0],
|
||||||
|
remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6],
|
||||||
|
remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12]);
|
||||||
|
|
||||||
|
// Send code
|
||||||
|
auto transmit = this->transmitter_->transmit();
|
||||||
|
auto data = transmit.get_data();
|
||||||
|
|
||||||
|
data->set_carrier_frequency(38000);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
data->mark(BALLU_HEADER_MARK);
|
||||||
|
data->space(BALLU_HEADER_SPACE);
|
||||||
|
// Data
|
||||||
|
for (uint8_t i : remote_state) {
|
||||||
|
for (uint8_t j = 0; j < 8; j++) {
|
||||||
|
data->mark(BALLU_BIT_MARK);
|
||||||
|
bool bit = i & (1 << j);
|
||||||
|
data->space(bit ? BALLU_ONE_SPACE : BALLU_ZERO_SPACE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Footer
|
||||||
|
data->mark(BALLU_BIT_MARK);
|
||||||
|
|
||||||
|
transmit.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BalluClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||||
|
// Validate header
|
||||||
|
if (!data.expect_item(BALLU_HEADER_MARK, BALLU_HEADER_SPACE)) {
|
||||||
|
ESP_LOGV(TAG, "Header fail");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t remote_state[BALLU_STATE_LENGTH] = {0};
|
||||||
|
// Read all bytes.
|
||||||
|
for (int i = 0; i < BALLU_STATE_LENGTH; i++) {
|
||||||
|
// Read bit
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE))
|
||||||
|
remote_state[i] |= 1 << j;
|
||||||
|
|
||||||
|
else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) {
|
||||||
|
ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGVV(TAG, "Byte %d %02X", i, remote_state[i]);
|
||||||
|
}
|
||||||
|
// Validate footer
|
||||||
|
if (!data.expect_mark(BALLU_BIT_MARK)) {
|
||||||
|
ESP_LOGV(TAG, "Footer fail");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
// Calculate checksum and compare with signal value.
|
||||||
|
for (uint8_t i = 0; i < BALLU_STATE_LENGTH - 1; i++)
|
||||||
|
checksum += remote_state[i];
|
||||||
|
|
||||||
|
if (checksum != remote_state[BALLU_STATE_LENGTH - 1]) {
|
||||||
|
ESP_LOGVV(TAG, "Checksum fail");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "Received: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0],
|
||||||
|
remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6],
|
||||||
|
remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12]);
|
||||||
|
|
||||||
|
// verify header remote code
|
||||||
|
if (remote_state[0] != 0xc3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// powr on/off button
|
||||||
|
ESP_LOGV(TAG, "Power: %02X", (remote_state[9] & BALLU_POWER));
|
||||||
|
|
||||||
|
if ((remote_state[9] & BALLU_POWER) != BALLU_POWER) {
|
||||||
|
this->mode = climate::CLIMATE_MODE_OFF;
|
||||||
|
} else {
|
||||||
|
auto mode = remote_state[6] & 0xe0;
|
||||||
|
ESP_LOGV(TAG, "Mode: %02X", mode);
|
||||||
|
switch (mode) {
|
||||||
|
case BALLU_HEAT:
|
||||||
|
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||||
|
break;
|
||||||
|
case BALLU_COOL:
|
||||||
|
this->mode = climate::CLIMATE_MODE_COOL;
|
||||||
|
break;
|
||||||
|
case BALLU_DRY:
|
||||||
|
this->mode = climate::CLIMATE_MODE_DRY;
|
||||||
|
break;
|
||||||
|
case BALLU_FAN:
|
||||||
|
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||||
|
break;
|
||||||
|
case BALLU_AUTO:
|
||||||
|
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set received temp
|
||||||
|
int temp = remote_state[1] & 0xf8;
|
||||||
|
ESP_LOGVV(TAG, "Temperature Raw: %02X", temp);
|
||||||
|
temp = ((uint8_t) temp >> 3) + 8;
|
||||||
|
ESP_LOGVV(TAG, "Temperature Climate: %u", temp);
|
||||||
|
this->target_temperature = temp;
|
||||||
|
|
||||||
|
// Set received fan speed
|
||||||
|
auto fan = remote_state[4] & 0xe0;
|
||||||
|
ESP_LOGVV(TAG, "Fan: %02X", fan);
|
||||||
|
switch (fan) {
|
||||||
|
case BALLU_FAN_HIGH:
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||||
|
break;
|
||||||
|
case BALLU_FAN_MED:
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
|
||||||
|
break;
|
||||||
|
case BALLU_FAN_LOW:
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||||
|
break;
|
||||||
|
case BALLU_FAN_AUTO:
|
||||||
|
default:
|
||||||
|
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set received swing status
|
||||||
|
ESP_LOGVV(TAG, "Swing status: %02X %02X", remote_state[1] & BALLU_SWING_VER, remote_state[2] & BALLU_SWING_HOR);
|
||||||
|
if (((remote_state[1] & BALLU_SWING_VER) != BALLU_SWING_VER) &&
|
||||||
|
((remote_state[2] & BALLU_SWING_HOR) != BALLU_SWING_HOR)) {
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_BOTH;
|
||||||
|
} else if ((remote_state[1] & BALLU_SWING_VER) != BALLU_SWING_VER) {
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||||
|
} else if ((remote_state[2] & BALLU_SWING_HOR) != BALLU_SWING_HOR) {
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
|
||||||
|
} else {
|
||||||
|
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->publish_state();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ballu
|
||||||
|
} // namespace esphome
|
31
esphome/components/ballu/ballu.h
Normal file
31
esphome/components/ballu/ballu.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/climate_ir/climate_ir.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ballu {
|
||||||
|
|
||||||
|
// Support for Ballu air conditioners with YKR-K/002E remote
|
||||||
|
|
||||||
|
// Temperature
|
||||||
|
const float YKR_K_002E_TEMP_MIN = 16.0;
|
||||||
|
const float YKR_K_002E_TEMP_MAX = 32.0;
|
||||||
|
|
||||||
|
class BalluClimate : public climate_ir::ClimateIR {
|
||||||
|
public:
|
||||||
|
BalluClimate()
|
||||||
|
: climate_ir::ClimateIR(YKR_K_002E_TEMP_MIN, YKR_K_002E_TEMP_MAX, 1.0f, true, true,
|
||||||
|
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
|
||||||
|
climate::CLIMATE_FAN_HIGH},
|
||||||
|
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL,
|
||||||
|
climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH}) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Transmit via IR the state of this climate controller.
|
||||||
|
void transmit_state() override;
|
||||||
|
/// Handle received IR Buffer
|
||||||
|
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ballu
|
||||||
|
} // namespace esphome
|
21
esphome/components/ballu/climate.py
Normal file
21
esphome/components/ballu/climate.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import climate_ir
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
AUTO_LOAD = ["climate_ir"]
|
||||||
|
CODEOWNERS = ["@bazuchan"]
|
||||||
|
|
||||||
|
ballu_ns = cg.esphome_ns.namespace("ballu")
|
||||||
|
BalluClimate = ballu_ns.class_("BalluClimate", climate_ir.ClimateIR)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(BalluClimate),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await climate_ir.register_climate_ir(var, config)
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bang_bang {
|
namespace bang_bang {
|
||||||
|
|
||||||
static const char *TAG = "bang_bang.climate";
|
static const char *const TAG = "bang_bang.climate";
|
||||||
|
|
||||||
void BangBangClimate::setup() {
|
void BangBangClimate::setup() {
|
||||||
this->sensor_->add_on_state_callback([this](float state) {
|
this->sensor_->add_on_state_callback([this](float state) {
|
||||||
|
@ -21,7 +21,12 @@ void BangBangClimate::setup() {
|
||||||
restore->to_call(this).perform();
|
restore->to_call(this).perform();
|
||||||
} else {
|
} else {
|
||||||
// restore from defaults, change_away handles those for us
|
// restore from defaults, change_away handles those for us
|
||||||
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
|
if (supports_cool_ && supports_heat_)
|
||||||
|
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
|
||||||
|
else if (supports_cool_)
|
||||||
|
this->mode = climate::CLIMATE_MODE_COOL;
|
||||||
|
else if (supports_heat_)
|
||||||
|
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||||
this->change_away_(false);
|
this->change_away_(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,12 +48,13 @@ climate::ClimateTraits BangBangClimate::traits() {
|
||||||
traits.set_supports_current_temperature(true);
|
traits.set_supports_current_temperature(true);
|
||||||
traits.set_supported_modes({
|
traits.set_supported_modes({
|
||||||
climate::CLIMATE_MODE_OFF,
|
climate::CLIMATE_MODE_OFF,
|
||||||
climate::CLIMATE_MODE_HEAT_COOL,
|
|
||||||
});
|
});
|
||||||
if (supports_cool_)
|
if (supports_cool_)
|
||||||
traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
|
traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
|
||||||
if (supports_heat_)
|
if (supports_heat_)
|
||||||
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT);
|
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT);
|
||||||
|
if (supports_cool_ && supports_heat_)
|
||||||
|
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL);
|
||||||
traits.set_supports_two_point_target_temperature(true);
|
traits.set_supports_two_point_target_temperature(true);
|
||||||
if (supports_away_)
|
if (supports_away_)
|
||||||
traits.set_supported_presets({
|
traits.set_supported_presets({
|
||||||
|
@ -59,12 +65,8 @@ climate::ClimateTraits BangBangClimate::traits() {
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
void BangBangClimate::compute_state_() {
|
void BangBangClimate::compute_state_() {
|
||||||
if (this->mode != climate::CLIMATE_MODE_HEAT_COOL) {
|
if (this->mode == climate::CLIMATE_MODE_OFF) {
|
||||||
// in non-auto mode, switch directly to appropriate action
|
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
||||||
// - HEAT mode -> HEATING action
|
|
||||||
// - COOL mode -> COOLING action
|
|
||||||
// - OFF mode -> OFF action (not IDLE!)
|
|
||||||
this->switch_to_action_(static_cast<climate::ClimateAction>(this->mode));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
|
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bh1750 {
|
namespace bh1750 {
|
||||||
|
|
||||||
static const char *TAG = "bh1750.sensor";
|
static const char *const TAG = "bh1750.sensor";
|
||||||
|
|
||||||
static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
|
static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
|
||||||
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits
|
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace binary {
|
namespace binary {
|
||||||
|
|
||||||
static const char *TAG = "binary.fan";
|
static const char *const TAG = "binary.fan";
|
||||||
|
|
||||||
void binary::BinaryFan::dump_config() {
|
void binary::BinaryFan::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str());
|
ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str());
|
||||||
|
|
|
@ -22,7 +22,6 @@ from esphome.const import (
|
||||||
CONF_STATE,
|
CONF_STATE,
|
||||||
CONF_TIMING,
|
CONF_TIMING,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_FOR,
|
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
DEVICE_CLASS_EMPTY,
|
DEVICE_CLASS_EMPTY,
|
||||||
|
@ -372,11 +371,6 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_INVERTED): cv.invalid(
|
|
||||||
"The inverted binary_sensor property has been replaced by the "
|
|
||||||
"new 'invert' binary sensor filter. Please see "
|
|
||||||
"https://esphome.io/components/binary_sensor/index.html."
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -455,10 +449,6 @@ async def new_binary_sensor(config):
|
||||||
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
|
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.use_id(BinarySensor),
|
cv.Required(CONF_ID): cv.use_id(BinarySensor),
|
||||||
cv.Optional(CONF_FOR): cv.invalid(
|
|
||||||
"This option has been removed in 1.13, please use the "
|
|
||||||
"'for' condition instead."
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace binary_sensor {
|
namespace binary_sensor {
|
||||||
|
|
||||||
static const char *TAG = "binary_sensor.automation";
|
static const char *const TAG = "binary_sensor.automation";
|
||||||
|
|
||||||
void binary_sensor::MultiClickTrigger::on_state_(bool state) {
|
void binary_sensor::MultiClickTrigger::on_state_(bool state) {
|
||||||
// Handle duplicate events
|
// Handle duplicate events
|
||||||
|
@ -80,6 +80,10 @@ void binary_sensor::MultiClickTrigger::schedule_cooldown_() {
|
||||||
this->cancel_timeout("is_not_valid");
|
this->cancel_timeout("is_not_valid");
|
||||||
}
|
}
|
||||||
void binary_sensor::MultiClickTrigger::schedule_is_valid_(uint32_t min_length) {
|
void binary_sensor::MultiClickTrigger::schedule_is_valid_(uint32_t min_length) {
|
||||||
|
if (min_length == 0) {
|
||||||
|
this->is_valid_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
this->is_valid_ = false;
|
this->is_valid_ = false;
|
||||||
this->set_timeout("is_valid", min_length, [this]() {
|
this->set_timeout("is_valid", min_length, [this]() {
|
||||||
ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS");
|
ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS");
|
||||||
|
|
|
@ -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 "esphome/components/binary_sensor/binary_sensor.h"
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
@ -87,8 +89,8 @@ class DoubleClickTrigger : public Trigger<> {
|
||||||
|
|
||||||
class MultiClickTrigger : public Trigger<>, public Component {
|
class MultiClickTrigger : public Trigger<>, public Component {
|
||||||
public:
|
public:
|
||||||
explicit MultiClickTrigger(BinarySensor *parent, const std::vector<MultiClickTriggerEvent> &timing)
|
explicit MultiClickTrigger(BinarySensor *parent, std::vector<MultiClickTriggerEvent> timing)
|
||||||
: parent_(parent), timing_(timing) {}
|
: parent_(parent), timing_(std::move(timing)) {}
|
||||||
|
|
||||||
void setup() override {
|
void setup() override {
|
||||||
this->last_state_ = this->parent_->state;
|
this->last_state_ = this->parent_->state;
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace esphome {
|
||||||
|
|
||||||
namespace binary_sensor {
|
namespace binary_sensor {
|
||||||
|
|
||||||
static const char *TAG = "binary_sensor";
|
static const char *const TAG = "binary_sensor";
|
||||||
|
|
||||||
void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) {
|
void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) {
|
||||||
this->state_callback_.add(std::move(callback));
|
this->state_callback_.add(std::move(callback));
|
||||||
|
@ -61,7 +61,7 @@ void BinarySensor::add_filter(Filter *filter) {
|
||||||
last_filter->next_ = filter;
|
last_filter->next_ = filter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void BinarySensor::add_filters(std::vector<Filter *> filters) {
|
void BinarySensor::add_filters(const std::vector<Filter *> &filters) {
|
||||||
for (Filter *filter : filters) {
|
for (Filter *filter : filters) {
|
||||||
this->add_filter(filter);
|
this->add_filter(filter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@ namespace esphome {
|
||||||
namespace binary_sensor {
|
namespace binary_sensor {
|
||||||
|
|
||||||
#define LOG_BINARY_SENSOR(prefix, type, obj) \
|
#define LOG_BINARY_SENSOR(prefix, type, obj) \
|
||||||
if (obj != nullptr) { \
|
if ((obj) != nullptr) { \
|
||||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \
|
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
||||||
if (!obj->get_device_class().empty()) { \
|
if (!(obj)->get_device_class().empty()) { \
|
||||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \
|
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class BinarySensor : public Nameable {
|
||||||
std::string get_device_class();
|
std::string get_device_class();
|
||||||
|
|
||||||
void add_filter(Filter *filter);
|
void add_filter(Filter *filter);
|
||||||
void add_filters(std::vector<Filter *> filters);
|
void add_filters(const std::vector<Filter *> &filters);
|
||||||
|
|
||||||
// ========== INTERNAL METHODS ==========
|
// ========== INTERNAL METHODS ==========
|
||||||
// (In most use cases you won't need these)
|
// (In most use cases you won't need these)
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
|
|
||||||
#include "binary_sensor.h"
|
#include "binary_sensor.h"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
||||||
namespace binary_sensor {
|
namespace binary_sensor {
|
||||||
|
|
||||||
static const char *TAG = "sensor.filter";
|
static const char *const TAG = "sensor.filter";
|
||||||
|
|
||||||
void Filter::output(bool value, bool is_initial) {
|
void Filter::output(bool value, bool is_initial) {
|
||||||
if (!this->dedup_.next(value))
|
if (!this->dedup_.next(value))
|
||||||
|
@ -64,7 +66,7 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD
|
||||||
|
|
||||||
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
|
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
|
||||||
|
|
||||||
AutorepeatFilter::AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings) : timings_(timings) {}
|
AutorepeatFilter::AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings) : timings_(std::move(timings)) {}
|
||||||
|
|
||||||
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
|
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -108,7 +110,7 @@ void AutorepeatFilter::next_value_(bool val) {
|
||||||
|
|
||||||
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
LambdaFilter::LambdaFilter(const std::function<optional<bool>(bool)> &f) : f_(f) {}
|
LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move(f)) {}
|
||||||
|
|
||||||
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
|
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ struct AutorepeatFilterTiming {
|
||||||
|
|
||||||
class AutorepeatFilter : public Filter, public Component {
|
class AutorepeatFilter : public Filter, public Component {
|
||||||
public:
|
public:
|
||||||
explicit AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings);
|
explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
|
||||||
|
|
||||||
optional<bool> new_value(bool value, bool is_initial) override;
|
optional<bool> new_value(bool value, bool is_initial) override;
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ class AutorepeatFilter : public Filter, public Component {
|
||||||
|
|
||||||
class LambdaFilter : public Filter {
|
class LambdaFilter : public Filter {
|
||||||
public:
|
public:
|
||||||
explicit LambdaFilter(const std::function<optional<bool>(bool)> &f);
|
explicit LambdaFilter(std::function<optional<bool>(bool)> f);
|
||||||
|
|
||||||
optional<bool> new_value(bool value, bool is_initial) override;
|
optional<bool> new_value(bool value, bool is_initial) override;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace binary_sensor_map {
|
namespace binary_sensor_map {
|
||||||
|
|
||||||
static const char *TAG = "binary_sensor_map";
|
static const char *const TAG = "binary_sensor_map";
|
||||||
|
|
||||||
void BinarySensorMap::dump_config() { LOG_SENSOR(" ", "binary_sensor_map", this); }
|
void BinarySensorMap::dump_config() { LOG_SENSOR(" ", "binary_sensor_map", this); }
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_client {
|
namespace ble_client {
|
||||||
|
|
||||||
static const char *TAG = "ble_client";
|
static const char *const TAG = "ble_client";
|
||||||
|
|
||||||
void BLEClient::setup() {
|
void BLEClient::setup() {
|
||||||
auto ret = esp_ble_gattc_app_register(this->app_id);
|
auto ret = esp_ble_gattc_app_register(this->app_id);
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_client {
|
namespace ble_client {
|
||||||
|
|
||||||
static const char *TAG = "ble_sensor";
|
static const char *const TAG = "ble_sensor";
|
||||||
|
|
||||||
uint32_t BLESensor::hash_base() { return 343459825UL; }
|
uint32_t BLESensor::hash_base() { return 343459825UL; }
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_client {
|
namespace ble_client {
|
||||||
|
|
||||||
static const char *TAG = "ble_switch";
|
static const char *const TAG = "ble_switch";
|
||||||
|
|
||||||
void BLEClientSwitch::write_state(bool state) {
|
void BLEClientSwitch::write_state(bool state) {
|
||||||
this->parent_->set_enabled(state);
|
this->parent_->set_enabled(state);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_presence {
|
namespace ble_presence {
|
||||||
|
|
||||||
static const char *TAG = "ble_presence";
|
static const char *const TAG = "ble_presence";
|
||||||
|
|
||||||
void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }
|
void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_rssi {
|
namespace ble_rssi {
|
||||||
|
|
||||||
static const char *TAG = "ble_rssi";
|
static const char *const TAG = "ble_rssi";
|
||||||
|
|
||||||
void BLERSSISensor::dump_config() { LOG_SENSOR("", "BLE RSSI Sensor", this); }
|
void BLERSSISensor::dump_config() { LOG_SENSOR("", "BLE RSSI Sensor", this); }
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_scanner {
|
namespace ble_scanner {
|
||||||
|
|
||||||
static const char *TAG = "ble_scanner";
|
static const char *const TAG = "ble_scanner";
|
||||||
|
|
||||||
void BLEScanner::dump_config() { LOG_TEXT_SENSOR("", "BLE Scanner", this); }
|
void BLEScanner::dump_config() { LOG_TEXT_SENSOR("", "BLE Scanner", this); }
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bme280 {
|
namespace bme280 {
|
||||||
|
|
||||||
static const char *TAG = "bme280.sensor";
|
static const char *const TAG = "bme280.sensor";
|
||||||
|
|
||||||
static const uint8_t BME280_REGISTER_DIG_T1 = 0x88;
|
static const uint8_t BME280_REGISTER_DIG_T1 = 0x88;
|
||||||
static const uint8_t BME280_REGISTER_DIG_T2 = 0x8A;
|
static const uint8_t BME280_REGISTER_DIG_T2 = 0x8A;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bme680 {
|
namespace bme680 {
|
||||||
|
|
||||||
static const char *TAG = "bme680.sensor";
|
static const char *const TAG = "bme680.sensor";
|
||||||
|
|
||||||
static const uint8_t BME680_REGISTER_COEFF1 = 0x89;
|
static const uint8_t BME680_REGISTER_COEFF1 = 0x89;
|
||||||
static const uint8_t BME680_REGISTER_COEFF2 = 0xE1;
|
static const uint8_t BME680_REGISTER_COEFF2 = 0xE1;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bme680_bsec {
|
namespace bme680_bsec {
|
||||||
#ifdef USE_BSEC
|
#ifdef USE_BSEC
|
||||||
static const char *TAG = "bme680_bsec.sensor";
|
static const char *const TAG = "bme680_bsec.sensor";
|
||||||
|
|
||||||
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
|
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bmp085 {
|
namespace bmp085 {
|
||||||
|
|
||||||
static const char *TAG = "bmp085.sensor";
|
static const char *const TAG = "bmp085.sensor";
|
||||||
|
|
||||||
static const uint8_t BMP085_ADDRESS = 0x77;
|
static const uint8_t BMP085_ADDRESS = 0x77;
|
||||||
static const uint8_t BMP085_REGISTER_AC1_H = 0xAA;
|
static const uint8_t BMP085_REGISTER_AC1_H = 0xAA;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace bmp280 {
|
namespace bmp280 {
|
||||||
|
|
||||||
static const char *TAG = "bmp280.sensor";
|
static const char *const TAG = "bmp280.sensor";
|
||||||
|
|
||||||
static const uint8_t BMP280_REGISTER_STATUS = 0xF3;
|
static const uint8_t BMP280_REGISTER_STATUS = 0xF3;
|
||||||
static const uint8_t BMP280_REGISTER_CONTROL = 0xF4;
|
static const uint8_t BMP280_REGISTER_CONTROL = 0xF4;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace canbus {
|
namespace canbus {
|
||||||
|
|
||||||
static const char *TAG = "canbus";
|
static const char *const TAG = "canbus";
|
||||||
|
|
||||||
void Canbus::setup() {
|
void Canbus::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up Canbus...");
|
ESP_LOGCONFIG(TAG, "Setting up Canbus...");
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace captive_portal {
|
namespace captive_portal {
|
||||||
|
|
||||||
static const char *TAG = "captive_portal";
|
static const char *const TAG = "captive_portal";
|
||||||
|
|
||||||
void CaptivePortal::handle_index(AsyncWebServerRequest *request) {
|
void CaptivePortal::handle_index(AsyncWebServerRequest *request) {
|
||||||
AsyncResponseStream *stream = request->beginResponseStream("text/html");
|
AsyncResponseStream *stream = request->beginResponseStream("text/html");
|
||||||
|
@ -147,7 +147,7 @@ float CaptivePortal::get_setup_priority() const {
|
||||||
}
|
}
|
||||||
void CaptivePortal::dump_config() { ESP_LOGCONFIG(TAG, "Captive Portal:"); }
|
void CaptivePortal::dump_config() { ESP_LOGCONFIG(TAG, "Captive Portal:"); }
|
||||||
|
|
||||||
CaptivePortal *global_captive_portal = nullptr;
|
CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
} // namespace captive_portal
|
} // namespace captive_portal
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -68,7 +68,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
||||||
DNSServer *dns_server_{nullptr};
|
DNSServer *dns_server_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CaptivePortal *global_captive_portal;
|
extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
} // namespace captive_portal
|
} // namespace captive_portal
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ccs811 {
|
namespace ccs811 {
|
||||||
|
|
||||||
static const char *TAG = "ccs811";
|
static const char *const TAG = "ccs811";
|
||||||
|
|
||||||
// based on
|
// based on
|
||||||
// - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf
|
// - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf
|
||||||
|
|
|
@ -8,6 +8,8 @@ from esphome.const import (
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_PARTS_PER_MILLION,
|
UNIT_PARTS_PER_MILLION,
|
||||||
UNIT_PARTS_PER_BILLION,
|
UNIT_PARTS_PER_BILLION,
|
||||||
|
CONF_BASELINE,
|
||||||
|
CONF_ECO2,
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
CONF_TVOC,
|
CONF_TVOC,
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
|
@ -21,9 +23,6 @@ CCS811Component = ccs811_ns.class_(
|
||||||
"CCS811Component", cg.PollingComponent, i2c.I2CDevice
|
"CCS811Component", cg.PollingComponent, i2c.I2CDevice
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_ECO2 = "eco2"
|
|
||||||
CONF_BASELINE = "baseline"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace climate {
|
namespace climate {
|
||||||
|
|
||||||
static const char *TAG = "climate";
|
static const char *const TAG = "climate";
|
||||||
|
|
||||||
void ClimateCall::perform() {
|
void ClimateCall::perform() {
|
||||||
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
|
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
|
||||||
|
|
|
@ -11,8 +11,8 @@ namespace esphome {
|
||||||
namespace climate {
|
namespace climate {
|
||||||
|
|
||||||
#define LOG_CLIMATE(prefix, type, obj) \
|
#define LOG_CLIMATE(prefix, type, obj) \
|
||||||
if (obj != nullptr) { \
|
if ((obj) != nullptr) { \
|
||||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \
|
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
||||||
}
|
}
|
||||||
|
|
||||||
class Climate;
|
class Climate;
|
||||||
|
|
|
@ -7,19 +7,22 @@ namespace climate {
|
||||||
|
|
||||||
/// Enum for all modes a climate device can be in.
|
/// Enum for all modes a climate device can be in.
|
||||||
enum ClimateMode : uint8_t {
|
enum ClimateMode : uint8_t {
|
||||||
/// The climate device is off (not in auto, heat or cool mode)
|
/// The climate device is off
|
||||||
CLIMATE_MODE_OFF = 0,
|
CLIMATE_MODE_OFF = 0,
|
||||||
/// The climate device is set to automatically change the heating/cooling cycle
|
/// The climate device is set to heat/cool to reach the target temperature.
|
||||||
CLIMATE_MODE_HEAT_COOL = 1,
|
CLIMATE_MODE_HEAT_COOL = 1,
|
||||||
/// The climate device is manually set to cool mode (not in auto mode!)
|
/// The climate device is set to cool to reach the target temperature
|
||||||
CLIMATE_MODE_COOL = 2,
|
CLIMATE_MODE_COOL = 2,
|
||||||
/// The climate device is manually set to heat mode (not in auto mode!)
|
/// The climate device is set to heat to reach the target temperature
|
||||||
CLIMATE_MODE_HEAT = 3,
|
CLIMATE_MODE_HEAT = 3,
|
||||||
/// The climate device is manually set to fan only mode
|
/// The climate device only has the fan enabled, no heating or cooling is taking place
|
||||||
CLIMATE_MODE_FAN_ONLY = 4,
|
CLIMATE_MODE_FAN_ONLY = 4,
|
||||||
/// The climate device is manually set to dry mode
|
/// The climate device is set to dry/humidity mode
|
||||||
CLIMATE_MODE_DRY = 5,
|
CLIMATE_MODE_DRY = 5,
|
||||||
/// The climate device is manually set to heat-cool mode
|
/** The climate device is adjusting the temperatre dynamically.
|
||||||
|
* For example, the target temperature can be adjusted based on a schedule, or learned behavior.
|
||||||
|
* The target temperature can't be adjusted when in this mode.
|
||||||
|
*/
|
||||||
CLIMATE_MODE_AUTO = 6
|
CLIMATE_MODE_AUTO = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,15 +30,15 @@ enum ClimateMode : uint8_t {
|
||||||
enum ClimateAction : uint8_t {
|
enum ClimateAction : uint8_t {
|
||||||
/// The climate device is off (inactive or no power)
|
/// The climate device is off (inactive or no power)
|
||||||
CLIMATE_ACTION_OFF = 0,
|
CLIMATE_ACTION_OFF = 0,
|
||||||
/// The climate device is actively cooling (usually in cool or auto mode)
|
/// The climate device is actively cooling
|
||||||
CLIMATE_ACTION_COOLING = 2,
|
CLIMATE_ACTION_COOLING = 2,
|
||||||
/// The climate device is actively heating (usually in heat or auto mode)
|
/// The climate device is actively heating
|
||||||
CLIMATE_ACTION_HEATING = 3,
|
CLIMATE_ACTION_HEATING = 3,
|
||||||
/// The climate device is idle (monitoring climate but no action needed)
|
/// The climate device is idle (monitoring climate but no action needed)
|
||||||
CLIMATE_ACTION_IDLE = 4,
|
CLIMATE_ACTION_IDLE = 4,
|
||||||
/// The climate device is drying (either mode DRY or AUTO)
|
/// The climate device is drying
|
||||||
CLIMATE_ACTION_DRYING = 5,
|
CLIMATE_ACTION_DRYING = 5,
|
||||||
/// The climate device is in fan only mode (either mode FAN_ONLY or AUTO)
|
/// The climate device is in fan only mode
|
||||||
CLIMATE_ACTION_FAN = 6,
|
CLIMATE_ACTION_FAN = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ class ClimateTraits {
|
||||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
|
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
|
||||||
void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); }
|
void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); }
|
||||||
bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); }
|
bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); }
|
||||||
bool get_supports_fan_modes() const { return !supported_fan_modes_.empty(); }
|
bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); }
|
||||||
const std::set<ClimateFanMode> get_supported_fan_modes() const { return supported_fan_modes_; }
|
const std::set<ClimateFanMode> get_supported_fan_modes() const { return supported_fan_modes_; }
|
||||||
|
|
||||||
void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) {
|
void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) {
|
||||||
|
@ -127,13 +127,13 @@ class ClimateTraits {
|
||||||
|
|
||||||
void set_supported_swing_modes(std::set<ClimateSwingMode> modes) { supported_swing_modes_ = std::move(modes); }
|
void set_supported_swing_modes(std::set<ClimateSwingMode> modes) { supported_swing_modes_ = std::move(modes); }
|
||||||
void add_supported_swing_mode(ClimateSwingMode mode) { supported_swing_modes_.insert(mode); }
|
void add_supported_swing_mode(ClimateSwingMode mode) { supported_swing_modes_.insert(mode); }
|
||||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
|
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead")
|
||||||
void set_supports_swing_mode_off(bool supported) { set_swing_mode_support_(CLIMATE_SWING_OFF, supported); }
|
void set_supports_swing_mode_off(bool supported) { set_swing_mode_support_(CLIMATE_SWING_OFF, supported); }
|
||||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
|
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead")
|
||||||
void set_supports_swing_mode_both(bool supported) { set_swing_mode_support_(CLIMATE_SWING_BOTH, supported); }
|
void set_supports_swing_mode_both(bool supported) { set_swing_mode_support_(CLIMATE_SWING_BOTH, supported); }
|
||||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
|
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead")
|
||||||
void set_supports_swing_mode_vertical(bool supported) { set_swing_mode_support_(CLIMATE_SWING_VERTICAL, supported); }
|
void set_supports_swing_mode_vertical(bool supported) { set_swing_mode_support_(CLIMATE_SWING_VERTICAL, supported); }
|
||||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead")
|
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead")
|
||||||
void set_supports_swing_mode_horizontal(bool supported) {
|
void set_supports_swing_mode_horizontal(bool supported) {
|
||||||
set_swing_mode_support_(CLIMATE_SWING_HORIZONTAL, supported);
|
set_swing_mode_support_(CLIMATE_SWING_HORIZONTAL, supported);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue