mirror of
https://github.com/esphome/esphome.git
synced 2025-11-02 16:11:53 +00:00
Compare commits
1 Commits
jesserockz
...
jesserockz
Author | SHA1 | Date | |
---|---|---|---|
|
7a80bfae50 |
51
.clang-tidy
51
.clang-tidy
@@ -5,42 +5,26 @@ Checks: >-
|
|||||||
-altera-*,
|
-altera-*,
|
||||||
-android-*,
|
-android-*,
|
||||||
-boost-*,
|
-boost-*,
|
||||||
-bugprone-easily-swappable-parameters,
|
|
||||||
-bugprone-implicit-widening-of-multiplication-result,
|
|
||||||
-bugprone-multi-level-implicit-pointer-conversion,
|
|
||||||
-bugprone-narrowing-conversions,
|
-bugprone-narrowing-conversions,
|
||||||
-bugprone-signed-char-misuse,
|
-bugprone-signed-char-misuse,
|
||||||
-bugprone-switch-missing-default-case,
|
|
||||||
-cert-dcl50-cpp,
|
-cert-dcl50-cpp,
|
||||||
-cert-err33-c,
|
|
||||||
-cert-err58-cpp,
|
-cert-err58-cpp,
|
||||||
-cert-oop57-cpp,
|
-cert-oop57-cpp,
|
||||||
-cert-str34-c,
|
-cert-str34-c,
|
||||||
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
|
||||||
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||||
-clang-analyzer-osx.*,
|
-clang-analyzer-osx.*,
|
||||||
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||||
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||||
-clang-diagnostic-deprecated-declarations,
|
|
||||||
-clang-diagnostic-ignored-optimization-argument,
|
|
||||||
-clang-diagnostic-missing-field-initializers,
|
|
||||||
-clang-diagnostic-shadow-field,
|
-clang-diagnostic-shadow-field,
|
||||||
-clang-diagnostic-unused-const-variable,
|
-clang-diagnostic-unused-const-variable,
|
||||||
-clang-diagnostic-unused-parameter,
|
-clang-diagnostic-unused-parameter,
|
||||||
-clang-diagnostic-vla-cxx-extension,
|
|
||||||
-concurrency-*,
|
-concurrency-*,
|
||||||
-cppcoreguidelines-avoid-c-arrays,
|
-cppcoreguidelines-avoid-c-arrays,
|
||||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
|
||||||
-cppcoreguidelines-avoid-do-while,
|
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
-cppcoreguidelines-init-variables,
|
-cppcoreguidelines-init-variables,
|
||||||
-cppcoreguidelines-macro-to-enum,
|
|
||||||
-cppcoreguidelines-macro-usage,
|
-cppcoreguidelines-macro-usage,
|
||||||
-cppcoreguidelines-missing-std-forward,
|
|
||||||
-cppcoreguidelines-narrowing-conversions,
|
-cppcoreguidelines-narrowing-conversions,
|
||||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||||
-cppcoreguidelines-owning-memory,
|
|
||||||
-cppcoreguidelines-prefer-member-initializer,
|
|
||||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||||
@@ -51,10 +35,7 @@ Checks: >-
|
|||||||
-cppcoreguidelines-pro-type-static-cast-downcast,
|
-cppcoreguidelines-pro-type-static-cast-downcast,
|
||||||
-cppcoreguidelines-pro-type-union-access,
|
-cppcoreguidelines-pro-type-union-access,
|
||||||
-cppcoreguidelines-pro-type-vararg,
|
-cppcoreguidelines-pro-type-vararg,
|
||||||
-cppcoreguidelines-rvalue-reference-param-not-moved,
|
|
||||||
-cppcoreguidelines-special-member-functions,
|
-cppcoreguidelines-special-member-functions,
|
||||||
-cppcoreguidelines-use-default-member-init,
|
|
||||||
-cppcoreguidelines-virtual-class-destructor,
|
|
||||||
-fuchsia-multiple-inheritance,
|
-fuchsia-multiple-inheritance,
|
||||||
-fuchsia-overloaded-operator,
|
-fuchsia-overloaded-operator,
|
||||||
-fuchsia-statically-constructed-objects,
|
-fuchsia-statically-constructed-objects,
|
||||||
@@ -73,33 +54,20 @@ Checks: >-
|
|||||||
-llvm-include-order,
|
-llvm-include-order,
|
||||||
-llvm-qualified-auto,
|
-llvm-qualified-auto,
|
||||||
-llvmlibc-*,
|
-llvmlibc-*,
|
||||||
-misc-const-correctness,
|
|
||||||
-misc-include-cleaner,
|
|
||||||
-misc-no-recursion,
|
|
||||||
-misc-non-private-member-variables-in-classes,
|
-misc-non-private-member-variables-in-classes,
|
||||||
|
-misc-no-recursion,
|
||||||
-misc-unused-parameters,
|
-misc-unused-parameters,
|
||||||
-misc-use-anonymous-namespace,
|
|
||||||
-modernize-avoid-bind,
|
|
||||||
-modernize-avoid-c-arrays,
|
-modernize-avoid-c-arrays,
|
||||||
|
-modernize-avoid-bind,
|
||||||
-modernize-concat-nested-namespaces,
|
-modernize-concat-nested-namespaces,
|
||||||
-modernize-macro-to-enum,
|
|
||||||
-modernize-return-braced-init-list,
|
-modernize-return-braced-init-list,
|
||||||
-modernize-type-traits,
|
|
||||||
-modernize-use-auto,
|
-modernize-use-auto,
|
||||||
-modernize-use-constraints,
|
|
||||||
-modernize-use-default-member-init,
|
-modernize-use-default-member-init,
|
||||||
-modernize-use-equals-default,
|
-modernize-use-equals-default,
|
||||||
-modernize-use-nodiscard,
|
|
||||||
-modernize-use-nullptr,
|
|
||||||
-modernize-use-nodiscard,
|
|
||||||
-modernize-use-nullptr,
|
|
||||||
-modernize-use-trailing-return-type,
|
-modernize-use-trailing-return-type,
|
||||||
|
-modernize-use-nodiscard,
|
||||||
-mpi-*,
|
-mpi-*,
|
||||||
-objc-*,
|
-objc-*,
|
||||||
-performance-enum-size,
|
|
||||||
-readability-avoid-nested-conditional-operator,
|
|
||||||
-readability-container-contains,
|
|
||||||
-readability-container-data-pointer,
|
|
||||||
-readability-convert-member-functions-to-static,
|
-readability-convert-member-functions-to-static,
|
||||||
-readability-else-after-return,
|
-readability-else-after-return,
|
||||||
-readability-function-cognitive-complexity,
|
-readability-function-cognitive-complexity,
|
||||||
@@ -107,16 +75,15 @@ Checks: >-
|
|||||||
-readability-isolate-declaration,
|
-readability-isolate-declaration,
|
||||||
-readability-magic-numbers,
|
-readability-magic-numbers,
|
||||||
-readability-make-member-function-const,
|
-readability-make-member-function-const,
|
||||||
-readability-named-parameter,
|
|
||||||
-readability-redundant-casting,
|
|
||||||
-readability-redundant-inline-specifier,
|
|
||||||
-readability-redundant-member-init,
|
|
||||||
-readability-redundant-string-init,
|
-readability-redundant-string-init,
|
||||||
-readability-uppercase-literal-suffix,
|
-readability-uppercase-literal-suffix,
|
||||||
-readability-use-anyofallof,
|
-readability-use-anyofallof,
|
||||||
WarningsAsErrors: '*'
|
WarningsAsErrors: '*'
|
||||||
|
AnalyzeTemporaryDtors: false
|
||||||
FormatStyle: google
|
FormatStyle: google
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
|
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||||
|
value: '1'
|
||||||
- key: google-readability-function-size.StatementThreshold
|
- key: google-readability-function-size.StatementThreshold
|
||||||
value: '800'
|
value: '800'
|
||||||
- key: google-runtime-int.TypeSuffix
|
- key: google-runtime-int.TypeSuffix
|
||||||
@@ -191,9 +158,3 @@ CheckOptions:
|
|||||||
value: ''
|
value: ''
|
||||||
- key: readability-qualified-auto.AddConstToQualified
|
- key: readability-qualified-auto.AddConstToQualified
|
||||||
value: 0
|
value: 0
|
||||||
- key: readability-identifier-length.MinimumVariableNameLength
|
|
||||||
value: 0
|
|
||||||
- key: readability-identifier-length.MinimumParameterNameLength
|
|
||||||
value: 0
|
|
||||||
- key: readability-identifier-length.MinimumLoopCounterNameLength
|
|
||||||
value: 0
|
|
||||||
|
@@ -4,85 +4,53 @@
|
|||||||
"postCreateCommand": [
|
"postCreateCommand": [
|
||||||
"script/devcontainer-post-create"
|
"script/devcontainer-post-create"
|
||||||
],
|
],
|
||||||
"containerEnv": {
|
|
||||||
"DEVCONTAINER": "1",
|
|
||||||
"PIP_BREAK_SYSTEM_PACKAGES": "1",
|
|
||||||
"PIP_ROOT_USER_ACTION": "ignore"
|
|
||||||
},
|
|
||||||
"runArgs": [
|
"runArgs": [
|
||||||
"--privileged",
|
"--privileged",
|
||||||
"-e",
|
"-e",
|
||||||
"ESPHOME_DASHBOARD_USE_PING=1"
|
"ESPHOME_DASHBOARD_USE_PING=1"
|
||||||
// uncomment and edit the path in order to pass though local USB serial to the conatiner
|
|
||||||
// , "--device=/dev/ttyACM0"
|
|
||||||
],
|
],
|
||||||
"appPort": 6052,
|
"appPort": 6052,
|
||||||
// if you are using avahi in the host device, uncomment these to allow the
|
"extensions": [
|
||||||
// devcontainer to find devices via mdns
|
// python
|
||||||
//"mounts": [
|
"ms-python.python",
|
||||||
// "type=bind,source=/dev/bus/usb,target=/dev/bus/usb",
|
"visualstudioexptteam.vscodeintellicode",
|
||||||
// "type=bind,source=/var/run/dbus,target=/var/run/dbus",
|
// yaml
|
||||||
// "type=bind,source=/var/run/avahi-daemon/socket,target=/var/run/avahi-daemon/socket"
|
"redhat.vscode-yaml",
|
||||||
//],
|
// cpp
|
||||||
"customizations": {
|
"ms-vscode.cpptools",
|
||||||
"vscode": {
|
// editorconfig
|
||||||
"extensions": [
|
"editorconfig.editorconfig",
|
||||||
// python
|
],
|
||||||
"ms-python.python",
|
"settings": {
|
||||||
"ms-python.pylint",
|
"python.languageServer": "Pylance",
|
||||||
"ms-python.flake8",
|
"python.pythonPath": "/usr/bin/python3",
|
||||||
"charliermarsh.ruff",
|
"python.linting.pylintEnabled": true,
|
||||||
"visualstudioexptteam.vscodeintellicode",
|
"python.linting.enabled": true,
|
||||||
// yaml
|
"python.formatting.provider": "black",
|
||||||
"redhat.vscode-yaml",
|
"editor.formatOnPaste": false,
|
||||||
// cpp
|
"editor.formatOnSave": true,
|
||||||
"ms-vscode.cpptools",
|
"editor.formatOnType": true,
|
||||||
// editorconfig
|
"files.trimTrailingWhitespace": true,
|
||||||
"editorconfig.editorconfig"
|
"terminal.integrated.defaultProfile.linux": "bash",
|
||||||
],
|
"yaml.customTags": [
|
||||||
"settings": {
|
"!secret scalar",
|
||||||
"python.languageServer": "Pylance",
|
"!lambda scalar",
|
||||||
"python.pythonPath": "/usr/bin/python3",
|
"!include_dir_named scalar",
|
||||||
"pylint.args": [
|
"!include_dir_list scalar",
|
||||||
"--rcfile=${workspaceFolder}/pyproject.toml"
|
"!include_dir_merge_list scalar",
|
||||||
],
|
"!include_dir_merge_named scalar"
|
||||||
"flake8.args": [
|
],
|
||||||
"--config=${workspaceFolder}/.flake8"
|
"files.exclude": {
|
||||||
],
|
"**/.git": true,
|
||||||
"ruff.configuration": "${workspaceFolder}/pyproject.toml",
|
"**/.DS_Store": true,
|
||||||
"[python]": {
|
"**/*.pyc": {
|
||||||
// VS will say "Value is not accepted" before building the devcontainer, but the warning
|
"when": "$(basename).py"
|
||||||
// should go away after build is completed.
|
},
|
||||||
"editor.defaultFormatter": "charliermarsh.ruff"
|
"**/__pycache__": true
|
||||||
},
|
},
|
||||||
"editor.formatOnPaste": false,
|
"files.associations": {
|
||||||
"editor.formatOnSave": true,
|
"**/.vscode/*.json": "jsonc"
|
||||||
"editor.formatOnType": true,
|
},
|
||||||
"files.trimTrailingWhitespace": true,
|
"C_Cpp.clang_format_path": "/usr/bin/clang-format-11",
|
||||||
"terminal.integrated.defaultProfile.linux": "bash",
|
|
||||||
"yaml.customTags": [
|
|
||||||
"!secret scalar",
|
|
||||||
"!lambda scalar",
|
|
||||||
"!extend scalar",
|
|
||||||
"!remove scalar",
|
|
||||||
"!include_dir_named scalar",
|
|
||||||
"!include_dir_list scalar",
|
|
||||||
"!include_dir_merge_list scalar",
|
|
||||||
"!include_dir_merge_named scalar"
|
|
||||||
],
|
|
||||||
"files.exclude": {
|
|
||||||
"**/.git": true,
|
|
||||||
"**/.DS_Store": true,
|
|
||||||
"**/*.pyc": {
|
|
||||||
"when": "$(basename).py"
|
|
||||||
},
|
|
||||||
"**/__pycache__": true
|
|
||||||
},
|
|
||||||
"files.associations": {
|
|
||||||
"**/.vscode/*.json": "jsonc"
|
|
||||||
},
|
|
||||||
"C_Cpp.clang_format_path": "/usr/bin/clang-format-13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -75,9 +75,6 @@ target/
|
|||||||
# pyenv
|
# pyenv
|
||||||
.python-version
|
.python-version
|
||||||
|
|
||||||
# asdf
|
|
||||||
.tool-versions
|
|
||||||
|
|
||||||
# celery beat schedule file
|
# celery beat schedule file
|
||||||
celerybeat-schedule
|
celerybeat-schedule
|
||||||
|
|
||||||
@@ -114,5 +111,4 @@ config/
|
|||||||
examples/
|
examples/
|
||||||
Dockerfile
|
Dockerfile
|
||||||
.git/
|
.git/
|
||||||
tests/
|
tests/build/
|
||||||
.*
|
|
||||||
|
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,3 +1,2 @@
|
|||||||
# Normalize line endings to LF in the repository
|
# Normalize line endings to LF in the repository
|
||||||
* text eol=lf
|
* text eol=lf
|
||||||
*.png binary
|
|
||||||
|
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
custom: https://www.nabucasa.com
|
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -7,16 +7,11 @@
|
|||||||
- [ ] Bugfix (non-breaking change which fixes an issue)
|
- [ ] Bugfix (non-breaking change which fixes an issue)
|
||||||
- [ ] New feature (non-breaking change which adds functionality)
|
- [ ] New feature (non-breaking change which adds functionality)
|
||||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||||
- [ ] Code quality improvements to existing code or addition of tests
|
|
||||||
- [ ] Other
|
- [ ] Other
|
||||||
|
|
||||||
**Related issue or feature (if applicable):**
|
**Related issue or feature (if applicable):** fixes <link to issue>
|
||||||
|
|
||||||
- fixes <link to issue>
|
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
|
||||||
|
|
||||||
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):**
|
|
||||||
|
|
||||||
- esphome/esphome-docs#<esphome-docs PR number goes here>
|
|
||||||
|
|
||||||
## Test Environment
|
## Test Environment
|
||||||
|
|
||||||
@@ -24,10 +19,14 @@
|
|||||||
- [ ] ESP32 IDF
|
- [ ] ESP32 IDF
|
||||||
- [ ] ESP8266
|
- [ ] ESP8266
|
||||||
- [ ] RP2040
|
- [ ] RP2040
|
||||||
- [ ] BK72xx
|
|
||||||
- [ ] RTL87xx
|
|
||||||
|
|
||||||
## Example entry for `config.yaml`:
|
## Example entry for `config.yaml`:
|
||||||
|
<!--
|
||||||
|
Supplying a configuration snippet, makes it easier for a maintainer to test
|
||||||
|
your PR. Furthermore, for new integrations, it gives an impression of how
|
||||||
|
the configuration would look like.
|
||||||
|
Note: Remove this section if this PR does not have an example entry.
|
||||||
|
-->
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# Example config.yaml
|
# Example config.yaml
|
||||||
|
98
.github/actions/build-image/action.yaml
vendored
98
.github/actions/build-image/action.yaml
vendored
@@ -1,98 +0,0 @@
|
|||||||
name: Build Image
|
|
||||||
inputs:
|
|
||||||
target:
|
|
||||||
description: "Target to build"
|
|
||||||
required: true
|
|
||||||
example: "docker"
|
|
||||||
build_type:
|
|
||||||
description: "Build type"
|
|
||||||
required: true
|
|
||||||
example: "docker"
|
|
||||||
suffix:
|
|
||||||
description: "Suffix to add to tags"
|
|
||||||
required: true
|
|
||||||
version:
|
|
||||||
description: "Version to build"
|
|
||||||
required: true
|
|
||||||
example: "2023.12.0"
|
|
||||||
base_os:
|
|
||||||
description: "Base OS to use"
|
|
||||||
required: false
|
|
||||||
default: "debian"
|
|
||||||
example: "debian"
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- name: Generate short tags
|
|
||||||
id: tags
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
output=$(docker/generate_tags.py \
|
|
||||||
--tag "${{ inputs.version }}" \
|
|
||||||
--suffix "${{ inputs.suffix }}")
|
|
||||||
echo $output
|
|
||||||
for l in $output; do
|
|
||||||
echo $l >> $GITHUB_OUTPUT
|
|
||||||
done
|
|
||||||
|
|
||||||
# set cache-to only if dev branch
|
|
||||||
- id: cache-to
|
|
||||||
shell: bash
|
|
||||||
run: |-
|
|
||||||
if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
|
|
||||||
echo "value=type=gha,mode=max" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "value=" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build and push to ghcr by digest
|
|
||||||
id: build-ghcr
|
|
||||||
uses: docker/build-push-action@v6.16.0
|
|
||||||
env:
|
|
||||||
DOCKER_BUILD_SUMMARY: false
|
|
||||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./docker/Dockerfile
|
|
||||||
target: ${{ inputs.target }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: ${{ steps.cache-to.outputs.value }}
|
|
||||||
build-args: |
|
|
||||||
BUILD_TYPE=${{ inputs.build_type }}
|
|
||||||
BUILD_VERSION=${{ inputs.version }}
|
|
||||||
BUILD_OS=${{ inputs.base_os }}
|
|
||||||
outputs: |
|
|
||||||
type=image,name=ghcr.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
|
||||||
|
|
||||||
- name: Export ghcr digests
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests/${{ inputs.build_type }}/ghcr
|
|
||||||
digest="${{ steps.build-ghcr.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${{ inputs.build_type }}/ghcr/${digest#sha256:}"
|
|
||||||
|
|
||||||
- name: Build and push to dockerhub by digest
|
|
||||||
id: build-dockerhub
|
|
||||||
uses: docker/build-push-action@v6.16.0
|
|
||||||
env:
|
|
||||||
DOCKER_BUILD_SUMMARY: false
|
|
||||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./docker/Dockerfile
|
|
||||||
target: ${{ inputs.target }}
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: ${{ steps.cache-to.outputs.value }}
|
|
||||||
build-args: |
|
|
||||||
BUILD_TYPE=${{ inputs.build_type }}
|
|
||||||
BUILD_VERSION=${{ inputs.version }}
|
|
||||||
BUILD_OS=${{ inputs.base_os }}
|
|
||||||
outputs: |
|
|
||||||
type=image,name=docker.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
|
||||||
|
|
||||||
- name: Export dockerhub digests
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
mkdir -p /tmp/digests/${{ inputs.build_type }}/dockerhub
|
|
||||||
digest="${{ steps.build-dockerhub.outputs.digest }}"
|
|
||||||
touch "/tmp/digests/${{ inputs.build_type }}/dockerhub/${digest#sha256:}"
|
|
47
.github/actions/restore-python/action.yml
vendored
47
.github/actions/restore-python/action.yml
vendored
@@ -1,47 +0,0 @@
|
|||||||
name: Restore Python
|
|
||||||
inputs:
|
|
||||||
python-version:
|
|
||||||
description: Python version to restore
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
cache-key:
|
|
||||||
description: Cache key to use
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
outputs:
|
|
||||||
python-version:
|
|
||||||
description: Python version restored
|
|
||||||
value: ${{ steps.python.outputs.python-version }}
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- name: Set up Python ${{ inputs.python-version }}
|
|
||||||
id: python
|
|
||||||
uses: actions/setup-python@v5.6.0
|
|
||||||
with:
|
|
||||||
python-version: ${{ inputs.python-version }}
|
|
||||||
- name: Restore Python virtual environment
|
|
||||||
id: cache-venv
|
|
||||||
uses: actions/cache/restore@v4.2.3
|
|
||||||
with:
|
|
||||||
path: venv
|
|
||||||
# yamllint disable-line rule:line-length
|
|
||||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ inputs.cache-key }}
|
|
||||||
- name: Create Python virtual environment
|
|
||||||
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os != 'Windows'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
python -m venv venv
|
|
||||||
source venv/bin/activate
|
|
||||||
python --version
|
|
||||||
pip install -r requirements.txt -r requirements_test.txt
|
|
||||||
pip install -e .
|
|
||||||
- name: Create Python virtual environment
|
|
||||||
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
python -m venv venv
|
|
||||||
./venv/Scripts/activate
|
|
||||||
python --version
|
|
||||||
pip install -r requirements.txt -r requirements_test.txt
|
|
||||||
pip install -e .
|
|
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
@@ -13,19 +13,3 @@ updates:
|
|||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: daily
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
groups:
|
|
||||||
docker-actions:
|
|
||||||
applies-to: version-updates
|
|
||||||
patterns:
|
|
||||||
- "docker/login-action"
|
|
||||||
- "docker/setup-buildx-action"
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: "/.github/actions/build-image"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
open-pull-requests-limit: 10
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: "/.github/actions/restore-python"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
open-pull-requests-limit: 10
|
|
||||||
|
80
.github/workflows/ci-api-proto.yml
vendored
80
.github/workflows/ci-api-proto.yml
vendored
@@ -1,80 +0,0 @@
|
|||||||
name: API Proto CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "esphome/components/api/api.proto"
|
|
||||||
- "esphome/components/api/api_pb2.cpp"
|
|
||||||
- "esphome/components/api/api_pb2.h"
|
|
||||||
- "esphome/components/api/api_pb2_service.cpp"
|
|
||||||
- "esphome/components/api/api_pb2_service.h"
|
|
||||||
- "script/api_protobuf/api_protobuf.py"
|
|
||||||
- ".github/workflows/ci-api-proto.yml"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
name: Check generated files
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5.6.0
|
|
||||||
with:
|
|
||||||
python-version: "3.11"
|
|
||||||
|
|
||||||
- name: Install apt dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt update
|
|
||||||
sudo apt-cache show protobuf-compiler
|
|
||||||
sudo apt install -y protobuf-compiler
|
|
||||||
protoc --version
|
|
||||||
- name: Install python dependencies
|
|
||||||
run: pip install aioesphomeapi -c requirements.txt -r requirements_dev.txt
|
|
||||||
- name: Generate files
|
|
||||||
run: script/api_protobuf/api_protobuf.py
|
|
||||||
- name: Check for changes
|
|
||||||
run: |
|
|
||||||
if ! git diff --quiet; then
|
|
||||||
echo "## Job Failed" | tee -a $GITHUB_STEP_SUMMARY
|
|
||||||
echo "You have altered the generated proto files but they do not match what is expected." | tee -a $GITHUB_STEP_SUMMARY
|
|
||||||
echo "Please run 'script/api_protobuf/api_protobuf.py' and commit the changes." | tee -a $GITHUB_STEP_SUMMARY
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
- if: failure()
|
|
||||||
name: Review PR
|
|
||||||
uses: actions/github-script@v7.0.1
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
await github.rest.pulls.createReview({
|
|
||||||
pull_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
event: 'REQUEST_CHANGES',
|
|
||||||
body: 'You have altered the generated proto files but they do not match what is expected.\nPlease run "script/api_protobuf/api_protobuf.py" and commit the changes.'
|
|
||||||
})
|
|
||||||
- if: success()
|
|
||||||
name: Dismiss review
|
|
||||||
uses: actions/github-script@v7.0.1
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
let reviews = await github.rest.pulls.listReviews({
|
|
||||||
pull_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo
|
|
||||||
});
|
|
||||||
for (let review of reviews.data) {
|
|
||||||
if (review.user.login === 'github-actions[bot]' && review.state === 'CHANGES_REQUESTED') {
|
|
||||||
await github.rest.pulls.dismissReview({
|
|
||||||
pull_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
review_id: review.id,
|
|
||||||
message: 'Files now match the expected proto files.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
32
.github/workflows/ci-docker.yml
vendored
32
.github/workflows/ci-docker.yml
vendored
@@ -2,54 +2,46 @@
|
|||||||
name: CI for docker images
|
name: CI for docker images
|
||||||
|
|
||||||
# Only run when docker paths change
|
# Only run when docker paths change
|
||||||
|
# yamllint disable-line rule:truthy
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [dev, beta, release]
|
branches: [dev, beta, release]
|
||||||
paths:
|
paths:
|
||||||
- "docker/**"
|
- "docker/**"
|
||||||
- ".github/workflows/ci-docker.yml"
|
- ".github/workflows/**"
|
||||||
- "requirements*.txt"
|
- "requirements*.txt"
|
||||||
- "platformio.ini"
|
- "platformio.ini"
|
||||||
- "script/platformio_install_deps.py"
|
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "docker/**"
|
- "docker/**"
|
||||||
- ".github/workflows/ci-docker.yml"
|
- ".github/workflows/**"
|
||||||
- "requirements*.txt"
|
- "requirements*.txt"
|
||||||
- "platformio.ini"
|
- "platformio.ini"
|
||||||
- "script/platformio_install_deps.py"
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: read
|
packages: read
|
||||||
|
|
||||||
concurrency:
|
|
||||||
# yamllint disable-line rule:line-length
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-docker:
|
check-docker:
|
||||||
name: Build docker containers
|
name: Build docker containers
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: ["ubuntu-24.04", "ubuntu-24.04-arm"]
|
arch: [amd64, armv7, aarch64]
|
||||||
build_type:
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
- "ha-addon"
|
|
||||||
- "docker"
|
|
||||||
# - "lint"
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- name: Set TAG
|
- name: Set TAG
|
||||||
run: |
|
run: |
|
||||||
@@ -59,6 +51,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
docker/build.py \
|
docker/build.py \
|
||||||
--tag "${TAG}" \
|
--tag "${TAG}" \
|
||||||
--arch "${{ matrix.os == 'ubuntu-24.04-arm' && 'aarch64' || 'amd64' }}" \
|
--arch "${{ matrix.arch }}" \
|
||||||
--build-type "${{ matrix.build_type }}" \
|
--build-type "${{ matrix.build_type }}" \
|
||||||
build
|
build
|
||||||
|
554
.github/workflows/ci.yml
vendored
554
.github/workflows/ci.yml
vendored
@@ -1,273 +1,61 @@
|
|||||||
---
|
---
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
|
# yamllint disable-line rule:truthy
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [dev, beta, release]
|
branches: [dev, beta, release]
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
|
||||||
- "**"
|
|
||||||
- "!.github/workflows/*.yml"
|
|
||||||
- "!.github/actions/build-image/*"
|
|
||||||
- ".github/workflows/ci.yml"
|
|
||||||
- "!.yamllint"
|
|
||||||
- "!.github/dependabot.yml"
|
|
||||||
- "!docker/**"
|
|
||||||
merge_group:
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
env:
|
|
||||||
DEFAULT_PYTHON: "3.9"
|
|
||||||
PYUPGRADE_TARGET: "--py39-plus"
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
common:
|
ci:
|
||||||
name: Create common environment
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
outputs:
|
|
||||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Generate cache-key
|
|
||||||
id: cache-key
|
|
||||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
|
||||||
id: python
|
|
||||||
uses: actions/setup-python@v5.6.0
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
- name: Restore Python virtual environment
|
|
||||||
id: cache-venv
|
|
||||||
uses: actions/cache@v4.2.3
|
|
||||||
with:
|
|
||||||
path: venv
|
|
||||||
# yamllint disable-line rule:line-length
|
|
||||||
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ steps.cache-key.outputs.key }}
|
|
||||||
- name: Create Python virtual environment
|
|
||||||
if: steps.cache-venv.outputs.cache-hit != 'true'
|
|
||||||
run: |
|
|
||||||
python -m venv venv
|
|
||||||
. venv/bin/activate
|
|
||||||
python --version
|
|
||||||
pip install -r requirements.txt -r requirements_test.txt
|
|
||||||
pip install -e .
|
|
||||||
|
|
||||||
ruff:
|
|
||||||
name: Check ruff
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Run Ruff
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
ruff format esphome tests
|
|
||||||
- name: Suggested changes
|
|
||||||
run: script/ci-suggest-changes
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
flake8:
|
|
||||||
name: Check flake8
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Run flake8
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
flake8 esphome
|
|
||||||
- name: Suggested changes
|
|
||||||
run: script/ci-suggest-changes
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
pylint:
|
|
||||||
name: Check pylint
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Run pylint
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
pylint -f parseable --persistent=n esphome
|
|
||||||
- name: Suggested changes
|
|
||||||
run: script/ci-suggest-changes
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
pyupgrade:
|
|
||||||
name: Check pyupgrade
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Run pyupgrade
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
pyupgrade ${{ env.PYUPGRADE_TARGET }} `find esphome -name "*.py" -type f`
|
|
||||||
- name: Suggested changes
|
|
||||||
run: script/ci-suggest-changes
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
ci-custom:
|
|
||||||
name: Run script/ci-custom
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Register matcher
|
|
||||||
run: echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
|
||||||
- name: Run script/ci-custom
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
script/ci-custom.py
|
|
||||||
script/build_codeowners.py --check
|
|
||||||
script/build_language_schema.py --check
|
|
||||||
|
|
||||||
pytest:
|
|
||||||
name: Run pytest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
python-version:
|
|
||||||
- "3.9"
|
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
|
||||||
- "3.12"
|
|
||||||
os:
|
|
||||||
- ubuntu-latest
|
|
||||||
- macOS-latest
|
|
||||||
- windows-latest
|
|
||||||
exclude:
|
|
||||||
# Minimize CI resource usage
|
|
||||||
# by only running the Python version
|
|
||||||
# version used for docker images on Windows and macOS
|
|
||||||
- python-version: "3.12"
|
|
||||||
os: windows-latest
|
|
||||||
- python-version: "3.10"
|
|
||||||
os: windows-latest
|
|
||||||
- python-version: "3.9"
|
|
||||||
os: windows-latest
|
|
||||||
- python-version: "3.12"
|
|
||||||
os: macOS-latest
|
|
||||||
- python-version: "3.10"
|
|
||||||
os: macOS-latest
|
|
||||||
- python-version: "3.9"
|
|
||||||
os: macOS-latest
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Register matcher
|
|
||||||
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
|
||||||
- name: Run pytest
|
|
||||||
if: matrix.os == 'windows-latest'
|
|
||||||
run: |
|
|
||||||
./venv/Scripts/activate
|
|
||||||
pytest -vv --cov-report=xml --tb=native tests
|
|
||||||
- name: Run pytest
|
|
||||||
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest'
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
pytest -vv --cov-report=xml --tb=native tests
|
|
||||||
- name: Upload coverage to Codecov
|
|
||||||
uses: codecov/codecov-action@v5.4.2
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
|
|
||||||
clang-format:
|
|
||||||
name: Check clang-format
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Install clang-format
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
pip install clang-format -c requirements_dev.txt
|
|
||||||
- name: Run clang-format
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
script/clang-format -i
|
|
||||||
git diff-index --quiet HEAD --
|
|
||||||
- name: Suggested changes
|
|
||||||
run: script/ci-suggest-changes
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
clang-tidy:
|
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
- ruff
|
|
||||||
- ci-custom
|
|
||||||
- clang-format
|
|
||||||
- flake8
|
|
||||||
- pylint
|
|
||||||
- pytest
|
|
||||||
- pyupgrade
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
max-parallel: 2
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
|
- id: ci-custom
|
||||||
|
name: Run script/ci-custom
|
||||||
|
- id: lint-python
|
||||||
|
name: Run script/lint-python
|
||||||
|
- id: test
|
||||||
|
file: tests/test1.yaml
|
||||||
|
name: Test tests/test1.yaml
|
||||||
|
pio_cache_key: test1
|
||||||
|
- id: test
|
||||||
|
file: tests/test2.yaml
|
||||||
|
name: Test tests/test2.yaml
|
||||||
|
pio_cache_key: test2
|
||||||
|
- id: test
|
||||||
|
file: tests/test3.yaml
|
||||||
|
name: Test tests/test3.yaml
|
||||||
|
pio_cache_key: test3
|
||||||
|
- id: test
|
||||||
|
file: tests/test4.yaml
|
||||||
|
name: Test tests/test4.yaml
|
||||||
|
pio_cache_key: test4
|
||||||
|
- id: test
|
||||||
|
file: tests/test5.yaml
|
||||||
|
name: Test tests/test5.yaml
|
||||||
|
pio_cache_key: test5
|
||||||
|
- id: test
|
||||||
|
file: tests/test6.yaml
|
||||||
|
name: Test tests/test6.yaml
|
||||||
|
pio_cache_key: test6
|
||||||
|
- id: pytest
|
||||||
|
name: Run pytest
|
||||||
|
- id: clang-format
|
||||||
|
name: Run script/clang-format
|
||||||
- id: clang-tidy
|
- id: clang-tidy
|
||||||
name: Run script/clang-tidy for ESP8266
|
name: Run script/clang-tidy for ESP8266
|
||||||
options: --environment esp8266-arduino-tidy --grep USE_ESP8266
|
options: --environment esp8266-arduino-tidy --grep USE_ESP8266
|
||||||
@@ -292,218 +80,106 @@ jobs:
|
|||||||
name: Run script/clang-tidy for ESP32 IDF
|
name: Run script/clang-tidy for ESP32 IDF
|
||||||
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
||||||
pio_cache_key: tidyesp32-idf
|
pio_cache_key: tidyesp32-idf
|
||||||
|
- id: yamllint
|
||||||
|
name: Run yamllint
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- uses: actions/checkout@v3
|
||||||
uses: actions/checkout@v4.1.7
|
- name: Set up Python
|
||||||
- name: Restore Python
|
uses: actions/setup-python@v4
|
||||||
uses: ./.github/actions/restore-python
|
id: python
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: "3.9"
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
|
|
||||||
|
- name: Cache virtualenv
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: .venv
|
||||||
|
# yamllint disable-line rule:line-length
|
||||||
|
key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
|
||||||
|
restore-keys: |
|
||||||
|
venv-${{ steps.python.outputs.python-version }}-
|
||||||
|
|
||||||
|
- name: Set up virtualenv
|
||||||
|
# yamllint disable rule:line-length
|
||||||
|
run: |
|
||||||
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -U pip
|
||||||
|
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||||
|
pip install -e .
|
||||||
|
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
|
||||||
|
echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
|
||||||
|
# yamllint enable rule:line-length
|
||||||
|
|
||||||
|
# Use per check platformio cache because checks use different parts
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref == 'refs/heads/dev'
|
uses: actions/cache@v3
|
||||||
uses: actions/cache@v4.2.3
|
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ matrix.pio_cache_key }}
|
# yamllint disable-line rule:line-length
|
||||||
|
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||||
|
if: matrix.id == 'test' || matrix.id == 'clang-tidy'
|
||||||
|
|
||||||
- name: Cache platformio
|
- name: Install clang tools
|
||||||
if: github.ref != 'refs/heads/dev'
|
run: |
|
||||||
uses: actions/cache/restore@v4.2.3
|
sudo apt-get install \
|
||||||
with:
|
clang-format-11 \
|
||||||
path: ~/.platformio
|
clang-tidy-11
|
||||||
key: platformio-${{ matrix.pio_cache_key }}
|
if: matrix.id == 'clang-tidy' || matrix.id == 'clang-format'
|
||||||
|
|
||||||
- name: Register problem matchers
|
- name: Register problem matchers
|
||||||
run: |
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||||
|
|
||||||
- name: Run 'pio run --list-targets -e esp32-idf-tidy'
|
- name: Lint Custom
|
||||||
if: matrix.name == 'Run script/clang-tidy for ESP32 IDF'
|
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
script/ci-custom.py
|
||||||
mkdir -p .temp
|
script/build_codeowners.py --check
|
||||||
pio run --list-targets -e esp32-idf-tidy
|
if: matrix.id == 'ci-custom'
|
||||||
|
|
||||||
- name: Run clang-tidy
|
- name: Lint Python
|
||||||
run: |
|
run: script/lint-python -a
|
||||||
. venv/bin/activate
|
if: matrix.id == 'lint-python'
|
||||||
script/clang-tidy --all-headers --fix ${{ matrix.options }}
|
|
||||||
|
- run: esphome compile ${{ matrix.file }}
|
||||||
|
if: matrix.id == 'test'
|
||||||
env:
|
env:
|
||||||
# Also cache libdeps, store them in a ~/.platformio subfolder
|
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||||
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
||||||
|
|
||||||
|
- name: Run pytest
|
||||||
|
run: |
|
||||||
|
pytest -vv --tb=native tests
|
||||||
|
if: matrix.id == 'pytest'
|
||||||
|
|
||||||
|
# Also run git-diff-index so that the step is marked as failed on
|
||||||
|
# formatting errors, since clang-format doesn't do anything but
|
||||||
|
# change files if -i is passed.
|
||||||
|
- name: Run clang-format
|
||||||
|
run: |
|
||||||
|
script/clang-format -i
|
||||||
|
git diff-index --quiet HEAD --
|
||||||
|
if: matrix.id == 'clang-format'
|
||||||
|
|
||||||
|
- name: Run clang-tidy
|
||||||
|
run: |
|
||||||
|
script/clang-tidy --all-headers --fix ${{ matrix.options }}
|
||||||
|
if: matrix.id == 'clang-tidy'
|
||||||
|
env:
|
||||||
|
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||||
|
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
||||||
|
|
||||||
|
- name: Run yamllint
|
||||||
|
if: matrix.id == 'yamllint'
|
||||||
|
uses: frenck/action-yamllint@v1.3.1
|
||||||
|
|
||||||
- name: Suggested changes
|
- name: Suggested changes
|
||||||
run: script/ci-suggest-changes
|
run: script/ci-suggest-changes
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
if: always()
|
if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format' || matrix.id == 'lint-python')
|
||||||
|
|
||||||
list-components:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
outputs:
|
|
||||||
components: ${{ steps.list-components.outputs.components }}
|
|
||||||
count: ${{ steps.list-components.outputs.count }}
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
with:
|
|
||||||
# Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
|
|
||||||
fetch-depth: 500
|
|
||||||
- name: Get target branch
|
|
||||||
id: target-branch
|
|
||||||
run: |
|
|
||||||
echo "branch=${{ github.event.pull_request.base.ref }}" >> $GITHUB_OUTPUT
|
|
||||||
- name: Fetch ${{ steps.target-branch.outputs.branch }} branch
|
|
||||||
run: |
|
|
||||||
git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +refs/heads/${{ steps.target-branch.outputs.branch }}:refs/remotes/origin/${{ steps.target-branch.outputs.branch }}
|
|
||||||
git merge-base refs/remotes/origin/${{ steps.target-branch.outputs.branch }} HEAD
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Find changed components
|
|
||||||
id: list-components
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
components=$(script/list-components.py --changed --branch ${{ steps.target-branch.outputs.branch }})
|
|
||||||
output_components=$(echo "$components" | jq -R -s -c 'split("\n")[:-1] | map(select(length > 0))')
|
|
||||||
count=$(echo "$output_components" | jq length)
|
|
||||||
|
|
||||||
echo "components=$output_components" >> $GITHUB_OUTPUT
|
|
||||||
echo "count=$count" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
echo "$count Components:"
|
|
||||||
echo "$output_components" | jq
|
|
||||||
|
|
||||||
test-build-components:
|
|
||||||
name: Component test ${{ matrix.file }}
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
- list-components
|
|
||||||
if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) > 0 && fromJSON(needs.list-components.outputs.count) < 100
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
max-parallel: 2
|
|
||||||
matrix:
|
|
||||||
file: ${{ fromJson(needs.list-components.outputs.components) }}
|
|
||||||
steps:
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install libsdl2-dev
|
|
||||||
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: test_build_components -e config -c ${{ matrix.file }}
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
./script/test_build_components -e config -c ${{ matrix.file }}
|
|
||||||
- name: test_build_components -e compile -c ${{ matrix.file }}
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
./script/test_build_components -e compile -c ${{ matrix.file }}
|
|
||||||
|
|
||||||
test-build-components-splitter:
|
|
||||||
name: Split components for testing into 20 groups maximum
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
- list-components
|
|
||||||
if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) >= 100
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.split.outputs.components }}
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Split components into 20 groups
|
|
||||||
id: split
|
|
||||||
run: |
|
|
||||||
components=$(echo '${{ needs.list-components.outputs.components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(20) | join(" ")]')
|
|
||||||
echo "components=$components" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
test-build-components-split:
|
|
||||||
name: Test split components
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
- list-components
|
|
||||||
- test-build-components-splitter
|
|
||||||
if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) >= 100
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
max-parallel: 4
|
|
||||||
matrix:
|
|
||||||
components: ${{ fromJson(needs.test-build-components-splitter.outputs.matrix) }}
|
|
||||||
steps:
|
|
||||||
- name: List components
|
|
||||||
run: echo ${{ matrix.components }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install libsdl2-dev
|
|
||||||
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Validate config
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
for component in ${{ matrix.components }}; do
|
|
||||||
./script/test_build_components -e config -c $component
|
|
||||||
done
|
|
||||||
- name: Compile config
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
mkdir build_cache
|
|
||||||
export PLATFORMIO_BUILD_CACHE_DIR=$PWD/build_cache
|
|
||||||
for component in ${{ matrix.components }}; do
|
|
||||||
./script/test_build_components -e compile -c $component
|
|
||||||
done
|
|
||||||
|
|
||||||
ci-status:
|
|
||||||
name: CI Status
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
- ruff
|
|
||||||
- ci-custom
|
|
||||||
- clang-format
|
|
||||||
- flake8
|
|
||||||
- pylint
|
|
||||||
- pytest
|
|
||||||
- pyupgrade
|
|
||||||
- clang-tidy
|
|
||||||
- list-components
|
|
||||||
- test-build-components
|
|
||||||
- test-build-components-splitter
|
|
||||||
- test-build-components-split
|
|
||||||
if: always()
|
|
||||||
steps:
|
|
||||||
- name: Success
|
|
||||||
if: ${{ !(contains(needs.*.result, 'failure')) }}
|
|
||||||
run: exit 0
|
|
||||||
- name: Failure
|
|
||||||
if: ${{ contains(needs.*.result, 'failure') }}
|
|
||||||
env:
|
|
||||||
JSON_DOC: ${{ toJSON(needs) }}
|
|
||||||
run: |
|
|
||||||
echo $JSON_DOC | jq
|
|
||||||
exit 1
|
|
||||||
|
91
.github/workflows/codeql.yml
vendored
91
.github/workflows/codeql.yml
vendored
@@ -1,91 +0,0 @@
|
|||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
#
|
|
||||||
# ******** NOTE ********
|
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
|
||||||
# supported CodeQL languages.
|
|
||||||
#
|
|
||||||
name: "CodeQL Advanced"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "30 18 * * 4"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze (${{ matrix.language }})
|
|
||||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
|
||||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
|
||||||
# - https://gh.io/supported-runners-and-hardware-resources
|
|
||||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
|
||||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
|
||||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
|
||||||
permissions:
|
|
||||||
# required for all workflows
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
# required to fetch internal or private CodeQL packs
|
|
||||||
packages: read
|
|
||||||
|
|
||||||
# only required for workflows in private repositories
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# - language: c-cpp
|
|
||||||
# build-mode: autobuild
|
|
||||||
- language: python
|
|
||||||
build-mode: none
|
|
||||||
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
|
||||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
|
||||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
|
||||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
|
||||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
|
||||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
|
||||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
|
||||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v3
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
build-mode: ${{ matrix.build-mode }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
|
|
||||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
||||||
# queries: security-extended,security-and-quality
|
|
||||||
|
|
||||||
# If the analyze step fails for one of the languages you are analyzing with
|
|
||||||
# "We were unable to automatically build your code", modify the matrix above
|
|
||||||
# to set the build mode to "manual" for that language. Then modify this step
|
|
||||||
# to build your code.
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
||||||
- if: matrix.build-mode == 'manual'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
|
||||||
'languages you are analyzing, replace this with the commands to build' \
|
|
||||||
'your code, for example:'
|
|
||||||
echo ' make bootstrap'
|
|
||||||
echo ' make release'
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v3
|
|
||||||
with:
|
|
||||||
category: "/language:${{matrix.language}}"
|
|
3
.github/workflows/lock.yml
vendored
3
.github/workflows/lock.yml
vendored
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Lock
|
name: Lock
|
||||||
|
|
||||||
|
# yamllint disable-line rule:truthy
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "30 0 * * *"
|
- cron: "30 0 * * *"
|
||||||
@@ -17,7 +18,7 @@ jobs:
|
|||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v5.0.1
|
- uses: dessant/lock-threads@v3
|
||||||
with:
|
with:
|
||||||
pr-inactive-days: "1"
|
pr-inactive-days: "1"
|
||||||
pr-lock-reason: ""
|
pr-lock-reason: ""
|
||||||
|
4
.github/workflows/matchers/lint-python.json
vendored
4
.github/workflows/matchers/lint-python.json
vendored
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"problemMatcher": [
|
"problemMatcher": [
|
||||||
{
|
{
|
||||||
"owner": "ruff",
|
"owner": "black",
|
||||||
"severity": "error",
|
"severity": "error",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^(.*): (Please format this file with the ruff formatter)",
|
"regexp": "^(.*): (Please format this file with the black formatter)",
|
||||||
"file": 1,
|
"file": 1,
|
||||||
"message": 2
|
"message": 2
|
||||||
}
|
}
|
||||||
|
24
.github/workflows/needs-docs.yml
vendored
24
.github/workflows/needs-docs.yml
vendored
@@ -1,24 +0,0 @@
|
|||||||
name: Needs Docs
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [labeled, unlabeled]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
name: Check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check for needs-docs label
|
|
||||||
uses: actions/github-script@v7.0.1
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const { data: labels } = await github.rest.issues.listLabelsOnIssue({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number
|
|
||||||
});
|
|
||||||
const needsDocs = labels.find(label => label.name === 'needs-docs');
|
|
||||||
if (needsDocs) {
|
|
||||||
core.setFailed('Pull request needs docs');
|
|
||||||
}
|
|
205
.github/workflows/release.yml
vendored
205
.github/workflows/release.yml
vendored
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Publish Release
|
name: Publish Release
|
||||||
|
|
||||||
|
# yamllint disable-line rule:truthy
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
@@ -17,217 +18,143 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
tag: ${{ steps.tag.outputs.tag }}
|
tag: ${{ steps.tag.outputs.tag }}
|
||||||
branch_build: ${{ steps.tag.outputs.branch_build }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v3
|
||||||
- name: Get tag
|
- name: Get tag
|
||||||
id: tag
|
id: tag
|
||||||
# yamllint disable rule:line-length
|
# yamllint disable rule:line-length
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.event_name }}" = "release" ]]; then
|
if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
|
||||||
TAG="${{ github.event.release.tag_name}}"
|
TAG="${GITHUB_REF#refs/tags/}"
|
||||||
BRANCH_BUILD="false"
|
|
||||||
else
|
else
|
||||||
TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
|
TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
|
||||||
today="$(date --utc '+%Y%m%d')"
|
today="$(date --utc '+%Y%m%d')"
|
||||||
TAG="${TAG}${today}"
|
TAG="${TAG}${today}"
|
||||||
BRANCH=${GITHUB_REF#refs/heads/}
|
|
||||||
if [[ "$BRANCH" != "dev" ]]; then
|
|
||||||
TAG="${TAG}-${BRANCH}"
|
|
||||||
BRANCH_BUILD="true"
|
|
||||||
else
|
|
||||||
BRANCH_BUILD="false"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||||
echo "branch_build=${BRANCH_BUILD}" >> $GITHUB_OUTPUT
|
|
||||||
# yamllint enable rule:line-length
|
# yamllint enable rule:line-length
|
||||||
|
|
||||||
deploy-pypi:
|
deploy-pypi:
|
||||||
name: Build and publish to PyPi
|
name: Build and publish to PyPi
|
||||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
id-token: write
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Set up python environment
|
- name: Set up python environment
|
||||||
env:
|
run: |
|
||||||
ESPHOME_NO_VENV: 1
|
script/setup
|
||||||
run: script/setup
|
pip install setuptools wheel twine
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |-
|
run: python setup.py sdist bdist_wheel
|
||||||
pip3 install build
|
- name: Upload
|
||||||
python3 -m build
|
env:
|
||||||
- name: Publish
|
TWINE_USERNAME: __token__
|
||||||
uses: pypa/gh-action-pypi-publish@v1.12.4
|
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||||
|
run: twine upload dist/*
|
||||||
|
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
name: Build ESPHome ${{ matrix.platform.arch }}
|
name: Build and publish docker containers
|
||||||
if: github.repository == 'esphome/esphome'
|
if: github.repository == 'esphome/esphome'
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: ubuntu-latest
|
||||||
needs: [init]
|
needs: [init]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
arch: [amd64, armv7, aarch64]
|
||||||
- arch: amd64
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
os: "ubuntu-24.04"
|
|
||||||
- arch: arm64
|
|
||||||
os: "ubuntu-24.04-arm"
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v3.4.0
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
uses: docker/login-action@v3.4.0
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build docker
|
- name: Build and push
|
||||||
uses: ./.github/actions/build-image
|
run: |
|
||||||
with:
|
docker/build.py \
|
||||||
target: final
|
--tag "${{ needs.init.outputs.tag }}" \
|
||||||
build_type: docker
|
--arch "${{ matrix.arch }}" \
|
||||||
suffix: ""
|
--build-type "${{ matrix.build_type }}" \
|
||||||
version: ${{ needs.init.outputs.tag }}
|
build \
|
||||||
|
--push
|
||||||
|
|
||||||
- name: Build ha-addon
|
deploy-docker-manifest:
|
||||||
uses: ./.github/actions/build-image
|
|
||||||
with:
|
|
||||||
target: final
|
|
||||||
build_type: ha-addon
|
|
||||||
suffix: "hassio"
|
|
||||||
version: ${{ needs.init.outputs.tag }}
|
|
||||||
|
|
||||||
# - name: Build lint
|
|
||||||
# uses: ./.github/actions/build-image
|
|
||||||
# with:
|
|
||||||
# target: lint
|
|
||||||
# build_type: lint
|
|
||||||
# suffix: lint
|
|
||||||
# version: ${{ needs.init.outputs.tag }}
|
|
||||||
|
|
||||||
- name: Upload digests
|
|
||||||
uses: actions/upload-artifact@v4.6.2
|
|
||||||
with:
|
|
||||||
name: digests-${{ matrix.platform.arch }}
|
|
||||||
path: /tmp/digests
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
deploy-manifest:
|
|
||||||
name: Publish ESPHome ${{ matrix.image.build_type }} to ${{ matrix.registry }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- init
|
|
||||||
- deploy-docker
|
|
||||||
if: github.repository == 'esphome/esphome'
|
if: github.repository == 'esphome/esphome'
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [init, deploy-docker]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
image:
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
- build_type: "docker"
|
|
||||||
suffix: ""
|
|
||||||
- build_type: "ha-addon"
|
|
||||||
suffix: "hassio"
|
|
||||||
# - build_type: "lint"
|
|
||||||
# suffix: "lint"
|
|
||||||
registry:
|
|
||||||
- ghcr
|
|
||||||
- dockerhub
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python
|
||||||
- name: Download digests
|
uses: actions/setup-python@v4
|
||||||
uses: actions/download-artifact@v4.3.0
|
|
||||||
with:
|
with:
|
||||||
pattern: digests-*
|
python-version: "3.9"
|
||||||
path: /tmp/digests
|
- name: Enable experimental manifest support
|
||||||
merge-multiple: true
|
run: |
|
||||||
|
mkdir -p ~/.docker
|
||||||
- name: Set up Docker Buildx
|
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
if: matrix.registry == 'dockerhub'
|
uses: docker/login-action@v2
|
||||||
uses: docker/login-action@v3.4.0
|
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
if: matrix.registry == 'ghcr'
|
uses: docker/login-action@v2
|
||||||
uses: docker/login-action@v3.4.0
|
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Generate short tags
|
- name: Run manifest
|
||||||
id: tags
|
|
||||||
run: |
|
run: |
|
||||||
output=$(docker/generate_tags.py \
|
docker/build.py \
|
||||||
--tag "${{ needs.init.outputs.tag }}" \
|
--tag "${{ needs.init.outputs.tag }}" \
|
||||||
--suffix "${{ matrix.image.suffix }}" \
|
--build-type "${{ matrix.build_type }}" \
|
||||||
--registry "${{ matrix.registry }}")
|
manifest
|
||||||
echo $output
|
|
||||||
for l in $output; do
|
|
||||||
echo $l >> $GITHUB_OUTPUT
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Create manifest list and push
|
|
||||||
working-directory: /tmp/digests/${{ matrix.image.build_type }}/${{ matrix.registry }}
|
|
||||||
run: |
|
|
||||||
docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \
|
|
||||||
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)
|
|
||||||
|
|
||||||
deploy-ha-addon-repo:
|
deploy-ha-addon-repo:
|
||||||
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
|
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs: [deploy-docker]
|
||||||
- init
|
|
||||||
- deploy-manifest
|
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger Workflow
|
- env:
|
||||||
uses: actions/github-script@v7.0.1
|
TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
||||||
with:
|
# yamllint disable rule:line-length
|
||||||
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
run: |
|
||||||
script: |
|
TAG="${GITHUB_REF#refs/tags/}"
|
||||||
let description = "ESPHome";
|
curl \
|
||||||
if (context.eventName == "release") {
|
-u ":$TOKEN" \
|
||||||
description = ${{ toJSON(github.event.release.body) }};
|
-X POST \
|
||||||
}
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
github.rest.actions.createWorkflowDispatch({
|
https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
|
||||||
owner: "esphome",
|
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
|
||||||
repo: "home-assistant-addon",
|
# yamllint enable rule:line-length
|
||||||
workflow_id: "bump-version.yml",
|
|
||||||
ref: "main",
|
|
||||||
inputs: {
|
|
||||||
version: "${{ needs.init.outputs.tag }}",
|
|
||||||
content: description
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
7
.github/workflows/stale.yml
vendored
7
.github/workflows/stale.yml
vendored
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: Stale
|
name: Stale
|
||||||
|
|
||||||
|
# yamllint disable-line rule:truthy
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "30 0 * * *"
|
- cron: "30 0 * * *"
|
||||||
@@ -17,7 +18,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9.1.0
|
- uses: actions/stale@v6
|
||||||
with:
|
with:
|
||||||
days-before-pr-stale: 90
|
days-before-pr-stale: 90
|
||||||
days-before-pr-close: 7
|
days-before-pr-close: 7
|
||||||
@@ -25,7 +26,7 @@ jobs:
|
|||||||
days-before-issue-close: -1
|
days-before-issue-close: -1
|
||||||
remove-stale-when-updated: true
|
remove-stale-when-updated: true
|
||||||
stale-pr-label: "stale"
|
stale-pr-label: "stale"
|
||||||
exempt-pr-labels: "not-stale"
|
exempt-pr-labels: "no-stale"
|
||||||
stale-pr-message: >
|
stale-pr-message: >
|
||||||
There hasn't been any activity on this pull request recently. This
|
There hasn't been any activity on this pull request recently. This
|
||||||
pull request has been automatically marked as stale because of that
|
pull request has been automatically marked as stale because of that
|
||||||
@@ -37,7 +38,7 @@ jobs:
|
|||||||
close-issues:
|
close-issues:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9.1.0
|
- uses: actions/stale@v6
|
||||||
with:
|
with:
|
||||||
days-before-pr-stale: -1
|
days-before-pr-stale: -1
|
||||||
days-before-pr-close: -1
|
days-before-pr-close: -1
|
||||||
|
48
.github/workflows/sync-device-classes.yml
vendored
48
.github/workflows/sync-device-classes.yml
vendored
@@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
name: Synchronise Device Classes from Home Assistant
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "45 6 * * *"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sync:
|
|
||||||
name: Sync Device Classes
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.repository == 'esphome/esphome'
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
|
|
||||||
- name: Checkout Home Assistant
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
with:
|
|
||||||
repository: home-assistant/core
|
|
||||||
path: lib/home-assistant
|
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v5.6.0
|
|
||||||
with:
|
|
||||||
python-version: 3.12
|
|
||||||
|
|
||||||
- name: Install Home Assistant
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install -e lib/home-assistant
|
|
||||||
|
|
||||||
- name: Sync
|
|
||||||
run: |
|
|
||||||
python ./script/sync-device_class.py
|
|
||||||
|
|
||||||
- name: Commit changes
|
|
||||||
uses: peter-evans/create-pull-request@v7.0.8
|
|
||||||
with:
|
|
||||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
|
||||||
committer: esphomebot <esphome@openhomefoundation.org>
|
|
||||||
author: esphomebot <esphome@openhomefoundation.org>
|
|
||||||
branch: sync/device-classes
|
|
||||||
delete-branch: true
|
|
||||||
title: "Synchronise Device Classes from Home Assistant"
|
|
||||||
body-path: .github/PULL_REQUEST_TEMPLATE.md
|
|
||||||
token: ${{ secrets.DEVICE_CLASS_SYNC_TOKEN }}
|
|
25
.github/workflows/yaml-lint.yml
vendored
25
.github/workflows/yaml-lint.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
name: YAML lint
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev, beta, release]
|
|
||||||
paths:
|
|
||||||
- "**.yaml"
|
|
||||||
- "**.yml"
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "**.yaml"
|
|
||||||
- "**.yml"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
yamllint:
|
|
||||||
name: yamllint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.1.7
|
|
||||||
- name: Run yamllint
|
|
||||||
uses: frenck/action-yamllint@v1.5.0
|
|
||||||
with:
|
|
||||||
strict: true
|
|
15
.gitignore
vendored
15
.gitignore
vendored
@@ -13,12 +13,6 @@ __pycache__/
|
|||||||
# Intellij Idea
|
# Intellij Idea
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
# Eclipse
|
|
||||||
.project
|
|
||||||
.cproject
|
|
||||||
.pydevproject
|
|
||||||
.settings/
|
|
||||||
|
|
||||||
# Vim
|
# Vim
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
@@ -75,9 +69,6 @@ cov.xml
|
|||||||
# pyenv
|
# pyenv
|
||||||
.python-version
|
.python-version
|
||||||
|
|
||||||
# asdf
|
|
||||||
.tool-versions
|
|
||||||
|
|
||||||
# Environments
|
# Environments
|
||||||
.env
|
.env
|
||||||
.venv
|
.venv
|
||||||
@@ -137,9 +128,3 @@ tests/.esphome/
|
|||||||
|
|
||||||
sdkconfig.*
|
sdkconfig.*
|
||||||
!sdkconfig.defaults
|
!sdkconfig.defaults
|
||||||
|
|
||||||
.tests/
|
|
||||||
|
|
||||||
/components
|
|
||||||
/managed_components
|
|
||||||
|
|
||||||
|
@@ -2,21 +2,20 @@
|
|||||||
# See https://pre-commit.com for more information
|
# See https://pre-commit.com for more information
|
||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/ambv/black
|
||||||
# Ruff version.
|
rev: 22.10.0
|
||||||
rev: v0.11.0
|
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
- id: black
|
||||||
- id: ruff
|
args:
|
||||||
args: [--fix]
|
- --safe
|
||||||
# Run the formatter.
|
- --quiet
|
||||||
- id: ruff-format
|
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 7.2.0
|
rev: 6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- flake8-docstrings==1.7.0
|
- flake8-docstrings==1.5.0
|
||||||
- pydocstyle==5.1.1
|
- pydocstyle==5.1.1
|
||||||
files: ^(esphome|tests)/.+\.py$
|
files: ^(esphome|tests)/.+\.py$
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
@@ -28,23 +27,7 @@ repos:
|
|||||||
- --branch=release
|
- --branch=release
|
||||||
- --branch=beta
|
- --branch=beta
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v3.15.2
|
rev: v3.2.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py39-plus]
|
args: [--py39-plus]
|
||||||
- repo: https://github.com/adrienverge/yamllint.git
|
|
||||||
rev: v1.35.1
|
|
||||||
hooks:
|
|
||||||
- id: yamllint
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
|
||||||
rev: v13.0.1
|
|
||||||
hooks:
|
|
||||||
- id: clang-format
|
|
||||||
types_or: [c, c++]
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: pylint
|
|
||||||
name: pylint
|
|
||||||
entry: python3 script/run-in-env.py pylint
|
|
||||||
language: system
|
|
||||||
types: [python]
|
|
||||||
|
33
.vscode/tasks.json
vendored
33
.vscode/tasks.json
vendored
@@ -2,24 +2,15 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "Run Dashboard",
|
"label": "run",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${command:python.interpreterPath}",
|
"command": "python3 -m esphome dashboard config/",
|
||||||
"args": [
|
|
||||||
"-m",
|
|
||||||
"esphome",
|
|
||||||
"dashboard",
|
|
||||||
"config/"
|
|
||||||
],
|
|
||||||
"problemMatcher": []
|
"problemMatcher": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "clang-tidy",
|
"label": "clang-tidy",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "${command:python.interpreterPath}",
|
"command": "./script/clang-tidy",
|
||||||
"args": [
|
|
||||||
"./script/clang-tidy"
|
|
||||||
],
|
|
||||||
"problemMatcher": [
|
"problemMatcher": [
|
||||||
{
|
{
|
||||||
"owner": "clang-tidy",
|
"owner": "clang-tidy",
|
||||||
@@ -36,24 +27,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Generate proto files",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "${command:python.interpreterPath}",
|
|
||||||
"args": [
|
|
||||||
"./script/api_protobuf/api_protobuf.py"
|
|
||||||
],
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true
|
|
||||||
},
|
|
||||||
"presentation": {
|
|
||||||
"reveal": "never",
|
|
||||||
"close": true,
|
|
||||||
"panel": "new"
|
|
||||||
},
|
|
||||||
"problemMatcher": []
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
20
.yamllint
20
.yamllint
@@ -1,19 +1,3 @@
|
|||||||
---
|
---
|
||||||
extends: default
|
ignore: |
|
||||||
|
venv/
|
||||||
ignore-from-file: .gitignore
|
|
||||||
|
|
||||||
rules:
|
|
||||||
document-start: disable
|
|
||||||
empty-lines:
|
|
||||||
level: error
|
|
||||||
max: 1
|
|
||||||
max-start: 0
|
|
||||||
max-end: 1
|
|
||||||
indentation:
|
|
||||||
level: error
|
|
||||||
spaces: 2
|
|
||||||
indent-sequences: true
|
|
||||||
check-multi-line-strings: false
|
|
||||||
line-length: disable
|
|
||||||
truthy: disable
|
|
||||||
|
296
CODEOWNERS
296
CODEOWNERS
@@ -6,257 +6,120 @@
|
|||||||
# the integration's code owner is automatically notified.
|
# the integration's code owner is automatically notified.
|
||||||
|
|
||||||
# Core Code
|
# Core Code
|
||||||
pyproject.toml @esphome/core
|
setup.py @esphome/core
|
||||||
esphome/*.py @esphome/core
|
esphome/*.py @esphome/core
|
||||||
esphome/core/* @esphome/core
|
esphome/core/* @esphome/core
|
||||||
|
|
||||||
# Integrations
|
# Integrations
|
||||||
esphome/components/a01nyub/* @MrSuicideParrot
|
|
||||||
esphome/components/a02yyuw/* @TH-Braemer
|
|
||||||
esphome/components/absolute_humidity/* @DAVe3283
|
|
||||||
esphome/components/ac_dimmer/* @glmnet
|
esphome/components/ac_dimmer/* @glmnet
|
||||||
esphome/components/adc/* @esphome/core
|
esphome/components/adc/* @esphome/core
|
||||||
esphome/components/adc128s102/* @DeerMaximum
|
esphome/components/adc128s102/* @DeerMaximum
|
||||||
esphome/components/addressable_light/* @justfalter
|
esphome/components/addressable_light/* @justfalter
|
||||||
esphome/components/ade7880/* @kpfleming
|
|
||||||
esphome/components/ade7953/* @angelnu
|
|
||||||
esphome/components/ade7953_i2c/* @angelnu
|
|
||||||
esphome/components/ade7953_spi/* @angelnu
|
|
||||||
esphome/components/ads1118/* @solomondg1
|
|
||||||
esphome/components/ags10/* @mak-42
|
|
||||||
esphome/components/aic3204/* @kbx81
|
|
||||||
esphome/components/airthings_ble/* @jeromelaban
|
esphome/components/airthings_ble/* @jeromelaban
|
||||||
esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau
|
|
||||||
esphome/components/airthings_wave_mini/* @ncareau
|
esphome/components/airthings_wave_mini/* @ncareau
|
||||||
esphome/components/airthings_wave_plus/* @jeromelaban
|
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||||
esphome/components/alarm_control_panel/* @grahambrown11 @hwstar
|
|
||||||
esphome/components/alpha3/* @jan-hofmeier
|
|
||||||
esphome/components/am2315c/* @swoboda1337
|
|
||||||
esphome/components/am43/* @buxtronix
|
esphome/components/am43/* @buxtronix
|
||||||
esphome/components/am43/cover/* @buxtronix
|
esphome/components/am43/cover/* @buxtronix
|
||||||
esphome/components/am43/sensor/* @buxtronix
|
|
||||||
esphome/components/analog_threshold/* @ianchi
|
esphome/components/analog_threshold/* @ianchi
|
||||||
esphome/components/animation/* @syndlex
|
esphome/components/animation/* @syndlex
|
||||||
esphome/components/anova/* @buxtronix
|
esphome/components/anova/* @buxtronix
|
||||||
esphome/components/apds9306/* @aodrenah
|
|
||||||
esphome/components/api/* @OttoWinter
|
esphome/components/api/* @OttoWinter
|
||||||
esphome/components/as5600/* @ammmze
|
|
||||||
esphome/components/as5600/sensor/* @ammmze
|
|
||||||
esphome/components/as7341/* @mrgnr
|
|
||||||
esphome/components/async_tcp/* @OttoWinter
|
esphome/components/async_tcp/* @OttoWinter
|
||||||
esphome/components/at581x/* @X-Ryl669
|
|
||||||
esphome/components/atc_mithermometer/* @ahpohl
|
esphome/components/atc_mithermometer/* @ahpohl
|
||||||
esphome/components/atm90e26/* @danieltwagner
|
|
||||||
esphome/components/atm90e32/* @circuitsetup @descipher
|
|
||||||
esphome/components/audio/* @kahrendt
|
|
||||||
esphome/components/audio_adc/* @kbx81
|
|
||||||
esphome/components/audio_dac/* @kbx81
|
|
||||||
esphome/components/axs15231/* @clydebarrow
|
|
||||||
esphome/components/b_parasite/* @rbaron
|
esphome/components/b_parasite/* @rbaron
|
||||||
esphome/components/ballu/* @bazuchan
|
esphome/components/ballu/* @bazuchan
|
||||||
esphome/components/bang_bang/* @OttoWinter
|
esphome/components/bang_bang/* @OttoWinter
|
||||||
esphome/components/bedjet/* @jhansche
|
esphome/components/bedjet/* @jhansche
|
||||||
esphome/components/bedjet/climate/* @jhansche
|
esphome/components/bedjet/climate/* @jhansche
|
||||||
esphome/components/bedjet/fan/* @jhansche
|
esphome/components/bedjet/fan/* @jhansche
|
||||||
esphome/components/bedjet/sensor/* @javawizard @jhansche
|
|
||||||
esphome/components/beken_spi_led_strip/* @Mat931
|
|
||||||
esphome/components/bh1750/* @OttoWinter
|
esphome/components/bh1750/* @OttoWinter
|
||||||
esphome/components/binary_sensor/* @esphome/core
|
esphome/components/binary_sensor/* @esphome/core
|
||||||
esphome/components/bk72xx/* @kuba2k2
|
|
||||||
esphome/components/bl0906/* @athom-tech @jesserockz @tarontop
|
|
||||||
esphome/components/bl0939/* @ziceva
|
esphome/components/bl0939/* @ziceva
|
||||||
esphome/components/bl0940/* @tobias-
|
esphome/components/bl0940/* @tobias-
|
||||||
esphome/components/bl0942/* @dbuezas @dwmw2
|
esphome/components/bl0942/* @dbuezas
|
||||||
esphome/components/ble_client/* @buxtronix @clydebarrow
|
esphome/components/ble_client/* @buxtronix
|
||||||
esphome/components/bluetooth_proxy/* @jesserockz
|
esphome/components/bluetooth_proxy/* @jesserockz
|
||||||
esphome/components/bme280_base/* @esphome/core
|
|
||||||
esphome/components/bme280_spi/* @apbodrov
|
|
||||||
esphome/components/bme680_bsec/* @trvrnrth
|
esphome/components/bme680_bsec/* @trvrnrth
|
||||||
esphome/components/bme68x_bsec2/* @kbx81 @neffs
|
esphome/components/bmp3xx/* @martgras
|
||||||
esphome/components/bme68x_bsec2_i2c/* @kbx81 @neffs
|
|
||||||
esphome/components/bmi160/* @flaviut
|
|
||||||
esphome/components/bmp280_base/* @ademuri
|
|
||||||
esphome/components/bmp280_i2c/* @ademuri
|
|
||||||
esphome/components/bmp280_spi/* @ademuri
|
|
||||||
esphome/components/bmp3xx/* @latonita
|
|
||||||
esphome/components/bmp3xx_base/* @latonita @martgras
|
|
||||||
esphome/components/bmp3xx_i2c/* @latonita
|
|
||||||
esphome/components/bmp3xx_spi/* @latonita
|
|
||||||
esphome/components/bmp581/* @kahrendt
|
|
||||||
esphome/components/bp1658cj/* @Cossid
|
|
||||||
esphome/components/bp5758d/* @Cossid
|
|
||||||
esphome/components/button/* @esphome/core
|
esphome/components/button/* @esphome/core
|
||||||
esphome/components/bytebuffer/* @clydebarrow
|
|
||||||
esphome/components/canbus/* @danielschramm @mvturnho
|
esphome/components/canbus/* @danielschramm @mvturnho
|
||||||
esphome/components/cap1188/* @mreditor97
|
esphome/components/cap1188/* @MrEditor97
|
||||||
esphome/components/captive_portal/* @OttoWinter
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
esphome/components/ccs811/* @habbie
|
esphome/components/ccs811/* @habbie
|
||||||
esphome/components/cd74hc4067/* @asoehlke
|
esphome/components/cd74hc4067/* @asoehlke
|
||||||
esphome/components/ch422g/* @clydebarrow @jesterret
|
|
||||||
esphome/components/chsc6x/* @kkosik20
|
|
||||||
esphome/components/climate/* @esphome/core
|
esphome/components/climate/* @esphome/core
|
||||||
esphome/components/climate_ir/* @glmnet
|
esphome/components/climate_ir/* @glmnet
|
||||||
esphome/components/color_temperature/* @jesserockz
|
esphome/components/color_temperature/* @jesserockz
|
||||||
esphome/components/combination/* @Cat-Ion @kahrendt
|
|
||||||
esphome/components/const/* @esphome/core
|
|
||||||
esphome/components/coolix/* @glmnet
|
esphome/components/coolix/* @glmnet
|
||||||
esphome/components/copy/* @OttoWinter
|
esphome/components/copy/* @OttoWinter
|
||||||
esphome/components/cover/* @esphome/core
|
esphome/components/cover/* @esphome/core
|
||||||
esphome/components/cs5460a/* @balrog-kun
|
esphome/components/cs5460a/* @balrog-kun
|
||||||
esphome/components/cse7761/* @berfenger
|
esphome/components/cse7761/* @berfenger
|
||||||
esphome/components/cst226/* @clydebarrow
|
|
||||||
esphome/components/cst816/* @clydebarrow
|
|
||||||
esphome/components/ct_clamp/* @jesserockz
|
esphome/components/ct_clamp/* @jesserockz
|
||||||
esphome/components/current_based/* @djwmarcx
|
esphome/components/current_based/* @djwmarcx
|
||||||
esphome/components/dac7678/* @NickB1
|
esphome/components/dac7678/* @NickB1
|
||||||
esphome/components/daikin_arc/* @MagicBear
|
|
||||||
esphome/components/daikin_brc/* @hagak
|
esphome/components/daikin_brc/* @hagak
|
||||||
esphome/components/dallas_temp/* @ssieb
|
|
||||||
esphome/components/daly_bms/* @s1lvi0
|
esphome/components/daly_bms/* @s1lvi0
|
||||||
esphome/components/dashboard_import/* @esphome/core
|
esphome/components/dashboard_import/* @esphome/core
|
||||||
esphome/components/datetime/* @jesserockz @rfdarter
|
|
||||||
esphome/components/debug/* @OttoWinter
|
esphome/components/debug/* @OttoWinter
|
||||||
esphome/components/delonghi/* @grob6000
|
esphome/components/delonghi/* @grob6000
|
||||||
esphome/components/dfplayer/* @glmnet
|
esphome/components/dfplayer/* @glmnet
|
||||||
esphome/components/dfrobot_sen0395/* @niklasweber
|
|
||||||
esphome/components/dht/* @OttoWinter
|
esphome/components/dht/* @OttoWinter
|
||||||
esphome/components/display_menu_base/* @numo68
|
esphome/components/display_menu_base/* @numo68
|
||||||
esphome/components/dps310/* @kbx81
|
esphome/components/dps310/* @kbx81
|
||||||
esphome/components/ds1307/* @badbadc0ffee
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
esphome/components/dsmr/* @glmnet @zuidwijk
|
esphome/components/dsmr/* @glmnet @zuidwijk
|
||||||
esphome/components/duty_time/* @dudanov
|
esphome/components/ektf2232/* @jesserockz
|
||||||
esphome/components/ee895/* @Stock-M
|
|
||||||
esphome/components/ektf2232/touchscreen/* @jesserockz
|
|
||||||
esphome/components/emc2101/* @ellull
|
|
||||||
esphome/components/emmeti/* @E440QF
|
|
||||||
esphome/components/ens160/* @latonita
|
|
||||||
esphome/components/ens160_base/* @latonita @vincentscode
|
|
||||||
esphome/components/ens160_i2c/* @latonita
|
|
||||||
esphome/components/ens160_spi/* @latonita
|
|
||||||
esphome/components/ens210/* @itn3rd77
|
esphome/components/ens210/* @itn3rd77
|
||||||
esphome/components/es7210/* @kahrendt
|
|
||||||
esphome/components/es7243e/* @kbx81
|
|
||||||
esphome/components/es8156/* @kbx81
|
|
||||||
esphome/components/es8311/* @kahrendt @kroimon
|
|
||||||
esphome/components/esp32/* @esphome/core
|
esphome/components/esp32/* @esphome/core
|
||||||
esphome/components/esp32_ble/* @Rapsssito @jesserockz
|
esphome/components/esp32_ble/* @jesserockz
|
||||||
esphome/components/esp32_ble_client/* @jesserockz
|
esphome/components/esp32_ble_client/* @jesserockz
|
||||||
esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
|
esphome/components/esp32_ble_server/* @jesserockz
|
||||||
esphome/components/esp32_camera_web_server/* @ayufan
|
esphome/components/esp32_camera_web_server/* @ayufan
|
||||||
esphome/components/esp32_can/* @Sympatron
|
esphome/components/esp32_can/* @Sympatron
|
||||||
esphome/components/esp32_improv/* @jesserockz
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
esphome/components/esp32_rmt/* @jesserockz
|
|
||||||
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
|
||||||
esphome/components/esp8266/* @esphome/core
|
esphome/components/esp8266/* @esphome/core
|
||||||
esphome/components/ethernet_info/* @gtjadsonsantos
|
esphome/components/ethernet_info/* @gtjadsonsantos
|
||||||
esphome/components/event/* @nohat
|
|
||||||
esphome/components/event_emitter/* @Rapsssito
|
|
||||||
esphome/components/exposure_notifications/* @OttoWinter
|
esphome/components/exposure_notifications/* @OttoWinter
|
||||||
esphome/components/ezo/* @ssieb
|
esphome/components/ezo/* @ssieb
|
||||||
esphome/components/ezo_pmp/* @carlos-sarmiento
|
esphome/components/ezo_pmp/* @carlos-sarmiento
|
||||||
esphome/components/factory_reset/* @anatoly-savchenkov
|
esphome/components/factory_reset/* @anatoly-savchenkov
|
||||||
esphome/components/fastled_base/* @OttoWinter
|
esphome/components/fastled_base/* @OttoWinter
|
||||||
esphome/components/feedback/* @ianchi
|
esphome/components/feedback/* @ianchi
|
||||||
esphome/components/fingerprint_grow/* @OnFreund @alexborro @loongyh
|
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||||
esphome/components/font/* @clydebarrow @esphome/core
|
|
||||||
esphome/components/fs3000/* @kahrendt
|
|
||||||
esphome/components/ft5x06/* @clydebarrow
|
|
||||||
esphome/components/ft63x6/* @gpambrozio
|
|
||||||
esphome/components/gcja5/* @gcormier
|
|
||||||
esphome/components/gdk101/* @Szewcson
|
|
||||||
esphome/components/globals/* @esphome/core
|
esphome/components/globals/* @esphome/core
|
||||||
esphome/components/gp2y1010au0f/* @zry98
|
|
||||||
esphome/components/gp8403/* @jesserockz
|
|
||||||
esphome/components/gpio/* @esphome/core
|
esphome/components/gpio/* @esphome/core
|
||||||
esphome/components/gpio/one_wire/* @ssieb
|
|
||||||
esphome/components/gps/* @coogle
|
esphome/components/gps/* @coogle
|
||||||
esphome/components/graph/* @synco
|
esphome/components/graph/* @synco
|
||||||
esphome/components/graphical_display_menu/* @MrMDavidson
|
|
||||||
esphome/components/gree/* @orestismers
|
|
||||||
esphome/components/grove_gas_mc_v2/* @YorkshireIoT
|
|
||||||
esphome/components/grove_tb6612fng/* @max246
|
|
||||||
esphome/components/growatt_solar/* @leeuwte
|
esphome/components/growatt_solar/* @leeuwte
|
||||||
esphome/components/gt911/* @clydebarrow @jesserockz
|
|
||||||
esphome/components/haier/* @paveldn
|
|
||||||
esphome/components/haier/binary_sensor/* @paveldn
|
|
||||||
esphome/components/haier/button/* @paveldn
|
|
||||||
esphome/components/haier/sensor/* @paveldn
|
|
||||||
esphome/components/haier/switch/* @paveldn
|
|
||||||
esphome/components/haier/text_sensor/* @paveldn
|
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||||
esphome/components/hbridge/light/* @DotNetDann
|
esphome/components/hbridge/light/* @DotNetDann
|
||||||
esphome/components/hbridge/switch/* @dwmw2
|
|
||||||
esphome/components/he60r/* @clydebarrow
|
|
||||||
esphome/components/heatpumpir/* @rob-deutsch
|
esphome/components/heatpumpir/* @rob-deutsch
|
||||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||||
esphome/components/hm3301/* @freekode
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
esphome/components/hmac_md5/* @dwmw2
|
|
||||||
esphome/components/homeassistant/* @OttoWinter @esphome/core
|
|
||||||
esphome/components/homeassistant/number/* @landonr
|
|
||||||
esphome/components/homeassistant/switch/* @Links2004
|
|
||||||
esphome/components/honeywell_hih_i2c/* @Benichou34
|
|
||||||
esphome/components/honeywellabp/* @RubyBailey
|
esphome/components/honeywellabp/* @RubyBailey
|
||||||
esphome/components/honeywellabp2_i2c/* @jpfaff
|
|
||||||
esphome/components/host/* @clydebarrow @esphome/core
|
|
||||||
esphome/components/host/time/* @clydebarrow
|
|
||||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||||
esphome/components/hte501/* @Stock-M
|
|
||||||
esphome/components/http_request/ota/* @oarcher
|
|
||||||
esphome/components/http_request/update/* @jesserockz
|
|
||||||
esphome/components/htu31d/* @betterengineering
|
|
||||||
esphome/components/hydreon_rgxx/* @functionpointer
|
esphome/components/hydreon_rgxx/* @functionpointer
|
||||||
esphome/components/hyt271/* @Philippe12
|
|
||||||
esphome/components/i2c/* @esphome/core
|
esphome/components/i2c/* @esphome/core
|
||||||
esphome/components/i2c_device/* @gabest11
|
|
||||||
esphome/components/i2s_audio/* @jesserockz
|
esphome/components/i2s_audio/* @jesserockz
|
||||||
esphome/components/i2s_audio/media_player/* @jesserockz
|
|
||||||
esphome/components/i2s_audio/microphone/* @jesserockz
|
|
||||||
esphome/components/i2s_audio/speaker/* @jesserockz @kahrendt
|
|
||||||
esphome/components/iaqcore/* @yozik04
|
|
||||||
esphome/components/ili9xxx/* @clydebarrow @nielsnl68
|
|
||||||
esphome/components/improv_base/* @esphome/core
|
|
||||||
esphome/components/improv_serial/* @esphome/core
|
esphome/components/improv_serial/* @esphome/core
|
||||||
esphome/components/ina226/* @Sergio303 @latonita
|
esphome/components/ina260/* @MrEditor97
|
||||||
esphome/components/ina260/* @mreditor97
|
|
||||||
esphome/components/ina2xx_base/* @latonita
|
|
||||||
esphome/components/ina2xx_i2c/* @latonita
|
|
||||||
esphome/components/ina2xx_spi/* @latonita
|
|
||||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||||
esphome/components/inkplate6/* @jesserockz
|
esphome/components/inkplate6/* @jesserockz
|
||||||
esphome/components/integration/* @OttoWinter
|
esphome/components/integration/* @OttoWinter
|
||||||
esphome/components/internal_temperature/* @Mat931
|
|
||||||
esphome/components/interval/* @esphome/core
|
esphome/components/interval/* @esphome/core
|
||||||
esphome/components/jsn_sr04t/* @Mafus1
|
|
||||||
esphome/components/json/* @OttoWinter
|
esphome/components/json/* @OttoWinter
|
||||||
esphome/components/kamstrup_kmp/* @cfeenstra1024
|
esphome/components/kalman_combinator/* @Cat-Ion
|
||||||
esphome/components/key_collector/* @ssieb
|
|
||||||
esphome/components/key_provider/* @ssieb
|
|
||||||
esphome/components/kuntze/* @ssieb
|
|
||||||
esphome/components/lcd_menu/* @numo68
|
esphome/components/lcd_menu/* @numo68
|
||||||
esphome/components/ld2410/* @regevbr @sebcaps
|
|
||||||
esphome/components/ld2420/* @descipher
|
|
||||||
esphome/components/ld2450/* @hareeshmu
|
|
||||||
esphome/components/ledc/* @OttoWinter
|
esphome/components/ledc/* @OttoWinter
|
||||||
esphome/components/libretiny/* @kuba2k2
|
|
||||||
esphome/components/libretiny_pwm/* @kuba2k2
|
|
||||||
esphome/components/light/* @esphome/core
|
esphome/components/light/* @esphome/core
|
||||||
esphome/components/lightwaverf/* @max246
|
|
||||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||||
esphome/components/lock/* @esphome/core
|
esphome/components/lock/* @esphome/core
|
||||||
esphome/components/logger/* @esphome/core
|
esphome/components/logger/* @esphome/core
|
||||||
esphome/components/logger/select/* @clydebarrow
|
esphome/components/ltr390/* @sjtrny
|
||||||
esphome/components/ltr390/* @latonita @sjtrny
|
|
||||||
esphome/components/ltr501/* @latonita
|
|
||||||
esphome/components/ltr_als_ps/* @latonita
|
|
||||||
esphome/components/lvgl/* @clydebarrow
|
|
||||||
esphome/components/m5stack_8angle/* @rnauber
|
|
||||||
esphome/components/mapping/* @clydebarrow
|
|
||||||
esphome/components/matrix_keypad/* @ssieb
|
|
||||||
esphome/components/max17043/* @blacknell
|
|
||||||
esphome/components/max31865/* @DAVe3283
|
esphome/components/max31865/* @DAVe3283
|
||||||
esphome/components/max44009/* @berfenger
|
esphome/components/max44009/* @berfenger
|
||||||
esphome/components/max6956/* @looping40
|
|
||||||
esphome/components/max7219digit/* @rspaargaren
|
esphome/components/max7219digit/* @rspaargaren
|
||||||
esphome/components/max9611/* @mckaymatthew
|
esphome/components/max9611/* @mckaymatthew
|
||||||
esphome/components/mcp23008/* @jesserockz
|
esphome/components/mcp23008/* @jesserockz
|
||||||
@@ -268,26 +131,17 @@ esphome/components/mcp23x17_base/* @jesserockz
|
|||||||
esphome/components/mcp23xxx_base/* @jesserockz
|
esphome/components/mcp23xxx_base/* @jesserockz
|
||||||
esphome/components/mcp2515/* @danielschramm @mvturnho
|
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||||
esphome/components/mcp3204/* @rsumner
|
esphome/components/mcp3204/* @rsumner
|
||||||
esphome/components/mcp4461/* @p1ngb4ck
|
|
||||||
esphome/components/mcp4728/* @berfenger
|
esphome/components/mcp4728/* @berfenger
|
||||||
esphome/components/mcp47a1/* @jesserockz
|
esphome/components/mcp47a1/* @jesserockz
|
||||||
esphome/components/mcp9600/* @mreditor97
|
esphome/components/mcp9600/* @MrEditor97
|
||||||
esphome/components/mcp9808/* @k7hpn
|
esphome/components/mcp9808/* @k7hpn
|
||||||
esphome/components/md5/* @esphome/core
|
esphome/components/md5/* @esphome/core
|
||||||
esphome/components/mdns/* @esphome/core
|
esphome/components/mdns/* @esphome/core
|
||||||
esphome/components/media_player/* @jesserockz
|
esphome/components/media_player/* @jesserockz
|
||||||
esphome/components/micro_wake_word/* @jesserockz @kahrendt
|
|
||||||
esphome/components/micronova/* @jorre05
|
|
||||||
esphome/components/microphone/* @jesserockz @kahrendt
|
|
||||||
esphome/components/mics_4514/* @jesserockz
|
|
||||||
esphome/components/midea/* @dudanov
|
esphome/components/midea/* @dudanov
|
||||||
esphome/components/midea_ir/* @dudanov
|
esphome/components/midea_ir/* @dudanov
|
||||||
esphome/components/mitsubishi/* @RubyBailey
|
esphome/components/mitsubishi/* @RubyBailey
|
||||||
esphome/components/mixer/speaker/* @kahrendt
|
|
||||||
esphome/components/mlx90393/* @functionpointer
|
esphome/components/mlx90393/* @functionpointer
|
||||||
esphome/components/mlx90614/* @jesserockz
|
|
||||||
esphome/components/mmc5603/* @benhoff
|
|
||||||
esphome/components/mmc5983/* @agoode
|
|
||||||
esphome/components/modbus_controller/* @martgras
|
esphome/components/modbus_controller/* @martgras
|
||||||
esphome/components/modbus_controller/binary_sensor/* @martgras
|
esphome/components/modbus_controller/binary_sensor/* @martgras
|
||||||
esphome/components/modbus_controller/number/* @martgras
|
esphome/components/modbus_controller/number/* @martgras
|
||||||
@@ -296,113 +150,67 @@ esphome/components/modbus_controller/select/* @martgras @stegm
|
|||||||
esphome/components/modbus_controller/sensor/* @martgras
|
esphome/components/modbus_controller/sensor/* @martgras
|
||||||
esphome/components/modbus_controller/switch/* @martgras
|
esphome/components/modbus_controller/switch/* @martgras
|
||||||
esphome/components/modbus_controller/text_sensor/* @martgras
|
esphome/components/modbus_controller/text_sensor/* @martgras
|
||||||
esphome/components/mopeka_ble/* @Fabian-Schmidt @spbrogan
|
esphome/components/mopeka_ble/* @spbrogan
|
||||||
esphome/components/mopeka_pro_check/* @spbrogan
|
esphome/components/mopeka_pro_check/* @spbrogan
|
||||||
esphome/components/mopeka_std_check/* @Fabian-Schmidt
|
|
||||||
esphome/components/mpl3115a2/* @kbickar
|
esphome/components/mpl3115a2/* @kbickar
|
||||||
esphome/components/mpu6886/* @fabaff
|
esphome/components/mpu6886/* @fabaff
|
||||||
esphome/components/ms8607/* @e28eta
|
|
||||||
esphome/components/msa3xx/* @latonita
|
|
||||||
esphome/components/nau7802/* @cujomalainey
|
|
||||||
esphome/components/network/* @esphome/core
|
esphome/components/network/* @esphome/core
|
||||||
esphome/components/nextion/* @edwardtfn @senexcrenshaw
|
esphome/components/nextion/* @senexcrenshaw
|
||||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||||
esphome/components/nextion/sensor/* @senexcrenshaw
|
esphome/components/nextion/sensor/* @senexcrenshaw
|
||||||
esphome/components/nextion/switch/* @senexcrenshaw
|
esphome/components/nextion/switch/* @senexcrenshaw
|
||||||
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
||||||
esphome/components/nfc/* @jesserockz @kbx81
|
esphome/components/nfc/* @jesserockz
|
||||||
esphome/components/noblex/* @AGalfra
|
|
||||||
esphome/components/npi19/* @bakerkj
|
|
||||||
esphome/components/number/* @esphome/core
|
esphome/components/number/* @esphome/core
|
||||||
esphome/components/one_wire/* @ssieb
|
|
||||||
esphome/components/online_image/* @clydebarrow @guillempages
|
|
||||||
esphome/components/opentherm/* @olegtarasov
|
|
||||||
esphome/components/ota/* @esphome/core
|
esphome/components/ota/* @esphome/core
|
||||||
esphome/components/output/* @esphome/core
|
esphome/components/output/* @esphome/core
|
||||||
esphome/components/packet_transport/* @clydebarrow
|
|
||||||
esphome/components/pca6416a/* @Mat931
|
|
||||||
esphome/components/pca9554/* @clydebarrow @hwstar
|
|
||||||
esphome/components/pcf85063/* @brogon
|
|
||||||
esphome/components/pcf8563/* @KoenBreeman
|
|
||||||
esphome/components/pid/* @OttoWinter
|
esphome/components/pid/* @OttoWinter
|
||||||
esphome/components/pipsolar/* @andreashergert1984
|
esphome/components/pipsolar/* @andreashergert1984
|
||||||
esphome/components/pm1006/* @habbie
|
esphome/components/pm1006/* @habbie
|
||||||
esphome/components/pm2005/* @andrewjswan
|
|
||||||
esphome/components/pmsa003i/* @sjtrny
|
esphome/components/pmsa003i/* @sjtrny
|
||||||
esphome/components/pmsx003/* @ximex
|
|
||||||
esphome/components/pmwcs3/* @SeByDocKy
|
|
||||||
esphome/components/pn532/* @OttoWinter @jesserockz
|
esphome/components/pn532/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn7150/* @jesserockz @kbx81
|
|
||||||
esphome/components/pn7150_i2c/* @jesserockz @kbx81
|
|
||||||
esphome/components/pn7160/* @jesserockz @kbx81
|
|
||||||
esphome/components/pn7160_i2c/* @jesserockz @kbx81
|
|
||||||
esphome/components/pn7160_spi/* @jesserockz @kbx81
|
|
||||||
esphome/components/power_supply/* @esphome/core
|
esphome/components/power_supply/* @esphome/core
|
||||||
esphome/components/preferences/* @esphome/core
|
esphome/components/preferences/* @esphome/core
|
||||||
esphome/components/psram/* @esphome/core
|
esphome/components/psram/* @esphome/core
|
||||||
esphome/components/pulse_meter/* @TrentHouliston @cstaahl @stevebaxter
|
esphome/components/pulse_meter/* @cstaahl @stevebaxter
|
||||||
esphome/components/pvvx_mithermometer/* @pasiz
|
esphome/components/pvvx_mithermometer/* @pasiz
|
||||||
esphome/components/pylontech/* @functionpointer
|
|
||||||
esphome/components/qmp6988/* @andrewpc
|
esphome/components/qmp6988/* @andrewpc
|
||||||
esphome/components/qr_code/* @wjtje
|
esphome/components/qr_code/* @wjtje
|
||||||
esphome/components/qspi_dbi/* @clydebarrow
|
|
||||||
esphome/components/qwiic_pir/* @kahrendt
|
|
||||||
esphome/components/radon_eye_ble/* @jeffeb3
|
esphome/components/radon_eye_ble/* @jeffeb3
|
||||||
esphome/components/radon_eye_rd200/* @jeffeb3
|
esphome/components/radon_eye_rd200/* @jeffeb3
|
||||||
esphome/components/rc522/* @glmnet
|
esphome/components/rc522/* @glmnet
|
||||||
esphome/components/rc522_i2c/* @glmnet
|
esphome/components/rc522_i2c/* @glmnet
|
||||||
esphome/components/rc522_spi/* @glmnet
|
esphome/components/rc522_spi/* @glmnet
|
||||||
esphome/components/resampler/speaker/* @kahrendt
|
|
||||||
esphome/components/restart/* @esphome/core
|
esphome/components/restart/* @esphome/core
|
||||||
esphome/components/rf_bridge/* @jesserockz
|
esphome/components/rf_bridge/* @jesserockz
|
||||||
esphome/components/rgbct/* @jesserockz
|
esphome/components/rgbct/* @jesserockz
|
||||||
esphome/components/rp2040/* @jesserockz
|
esphome/components/rp2040/* @jesserockz
|
||||||
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
|
|
||||||
esphome/components/rp2040_pwm/* @jesserockz
|
esphome/components/rp2040_pwm/* @jesserockz
|
||||||
esphome/components/rpi_dpi_rgb/* @clydebarrow
|
|
||||||
esphome/components/rtl87xx/* @kuba2k2
|
|
||||||
esphome/components/rtttl/* @glmnet
|
esphome/components/rtttl/* @glmnet
|
||||||
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
|
esphome/components/safe_mode/* @jsuanet @paulmonigatti
|
||||||
esphome/components/scd4x/* @martgras @sjtrny
|
esphome/components/scd4x/* @martgras @sjtrny
|
||||||
esphome/components/script/* @esphome/core
|
esphome/components/script/* @esphome/core
|
||||||
esphome/components/sdl/* @bdm310 @clydebarrow
|
|
||||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||||
esphome/components/sdp3x/* @Azimath
|
esphome/components/sdp3x/* @Azimath
|
||||||
esphome/components/seeed_mr24hpc1/* @limengdu
|
|
||||||
esphome/components/seeed_mr60bha2/* @limengdu
|
|
||||||
esphome/components/seeed_mr60fda2/* @limengdu
|
|
||||||
esphome/components/selec_meter/* @sourabhjaiswal
|
esphome/components/selec_meter/* @sourabhjaiswal
|
||||||
esphome/components/select/* @esphome/core
|
esphome/components/select/* @esphome/core
|
||||||
esphome/components/sen0321/* @notjj
|
|
||||||
esphome/components/sen21231/* @shreyaskarnik
|
|
||||||
esphome/components/sen5x/* @martgras
|
esphome/components/sen5x/* @martgras
|
||||||
esphome/components/sensirion_common/* @martgras
|
esphome/components/sensirion_common/* @martgras
|
||||||
esphome/components/sensor/* @esphome/core
|
esphome/components/sensor/* @esphome/core
|
||||||
esphome/components/sfa30/* @ghsensdev
|
|
||||||
esphome/components/sgp40/* @SenexCrenshaw
|
esphome/components/sgp40/* @SenexCrenshaw
|
||||||
esphome/components/sgp4x/* @SenexCrenshaw @martgras
|
esphome/components/sgp4x/* @SenexCrenshaw @martgras
|
||||||
esphome/components/shelly_dimmer/* @edge90 @rnauber
|
esphome/components/shelly_dimmer/* @edge90 @rnauber
|
||||||
esphome/components/sht3xd/* @mrtoy-me
|
|
||||||
esphome/components/sht4x/* @sjtrny
|
esphome/components/sht4x/* @sjtrny
|
||||||
esphome/components/shutdown/* @esphome/core @jsuanet
|
esphome/components/shutdown/* @esphome/core @jsuanet
|
||||||
esphome/components/sigma_delta_output/* @Cat-Ion
|
|
||||||
esphome/components/sim800l/* @glmnet
|
esphome/components/sim800l/* @glmnet
|
||||||
esphome/components/sm10bit_base/* @Cossid
|
esphome/components/sm2135/* @BoukeHaarsma23
|
||||||
esphome/components/sm2135/* @BoukeHaarsma23 @dd32 @matika77
|
|
||||||
esphome/components/sm2235/* @Cossid
|
|
||||||
esphome/components/sm2335/* @Cossid
|
|
||||||
esphome/components/sml/* @alengwenus
|
esphome/components/sml/* @alengwenus
|
||||||
esphome/components/smt100/* @piechade
|
esphome/components/smt100/* @piechade
|
||||||
esphome/components/sn74hc165/* @jesserockz
|
|
||||||
esphome/components/socket/* @esphome/core
|
esphome/components/socket/* @esphome/core
|
||||||
esphome/components/sonoff_d1/* @anatoly-savchenkov
|
esphome/components/sonoff_d1/* @anatoly-savchenkov
|
||||||
esphome/components/speaker/* @jesserockz @kahrendt
|
esphome/components/spi/* @esphome/core
|
||||||
esphome/components/speaker/media_player/* @kahrendt @synesthesiam
|
|
||||||
esphome/components/spi/* @clydebarrow @esphome/core
|
|
||||||
esphome/components/spi_device/* @clydebarrow
|
|
||||||
esphome/components/spi_led_strip/* @clydebarrow
|
|
||||||
esphome/components/sprinkler/* @kbx81
|
esphome/components/sprinkler/* @kbx81
|
||||||
esphome/components/sps30/* @martgras
|
esphome/components/sps30/* @martgras
|
||||||
esphome/components/ssd1322_base/* @kbx81
|
esphome/components/ssd1322_base/* @kbx81
|
||||||
@@ -416,50 +224,28 @@ esphome/components/ssd1331_base/* @kbx81
|
|||||||
esphome/components/ssd1331_spi/* @kbx81
|
esphome/components/ssd1331_spi/* @kbx81
|
||||||
esphome/components/ssd1351_base/* @kbx81
|
esphome/components/ssd1351_base/* @kbx81
|
||||||
esphome/components/ssd1351_spi/* @kbx81
|
esphome/components/ssd1351_spi/* @kbx81
|
||||||
esphome/components/st7567_base/* @latonita
|
|
||||||
esphome/components/st7567_i2c/* @latonita
|
|
||||||
esphome/components/st7567_spi/* @latonita
|
|
||||||
esphome/components/st7701s/* @clydebarrow
|
|
||||||
esphome/components/st7735/* @SenexCrenshaw
|
esphome/components/st7735/* @SenexCrenshaw
|
||||||
esphome/components/st7789v/* @kbx81
|
esphome/components/st7789v/* @kbx81
|
||||||
esphome/components/st7920/* @marsjan155
|
esphome/components/st7920/* @marsjan155
|
||||||
esphome/components/statsd/* @Links2004
|
|
||||||
esphome/components/substitutions/* @esphome/core
|
esphome/components/substitutions/* @esphome/core
|
||||||
esphome/components/sun/* @OttoWinter
|
esphome/components/sun/* @OttoWinter
|
||||||
esphome/components/sun_gtil2/* @Mat931
|
|
||||||
esphome/components/switch/* @esphome/core
|
esphome/components/switch/* @esphome/core
|
||||||
esphome/components/switch/binary_sensor/* @ssieb
|
|
||||||
esphome/components/syslog/* @clydebarrow
|
|
||||||
esphome/components/t6615/* @tylermenezes
|
esphome/components/t6615/* @tylermenezes
|
||||||
esphome/components/tc74/* @sethgirvan
|
|
||||||
esphome/components/tca9548a/* @andreashergert1984
|
esphome/components/tca9548a/* @andreashergert1984
|
||||||
esphome/components/tca9555/* @mobrembski
|
|
||||||
esphome/components/tcl112/* @glmnet
|
esphome/components/tcl112/* @glmnet
|
||||||
esphome/components/tee501/* @Stock-M
|
|
||||||
esphome/components/teleinfo/* @0hax
|
esphome/components/teleinfo/* @0hax
|
||||||
esphome/components/tem3200/* @bakerkj
|
|
||||||
esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar
|
|
||||||
esphome/components/template/datetime/* @rfdarter
|
|
||||||
esphome/components/template/event/* @nohat
|
|
||||||
esphome/components/template/fan/* @ssieb
|
|
||||||
esphome/components/text/* @mauritskorse
|
|
||||||
esphome/components/thermostat/* @kbx81
|
esphome/components/thermostat/* @kbx81
|
||||||
esphome/components/time/* @OttoWinter
|
esphome/components/time/* @OttoWinter
|
||||||
esphome/components/tlc5947/* @rnauber
|
esphome/components/tlc5947/* @rnauber
|
||||||
esphome/components/tlc5971/* @IJIJI
|
|
||||||
esphome/components/tm1621/* @Philippe12
|
esphome/components/tm1621/* @Philippe12
|
||||||
esphome/components/tm1637/* @glmnet
|
esphome/components/tm1637/* @glmnet
|
||||||
esphome/components/tm1638/* @skykingjwc
|
esphome/components/tm1638/* @skykingjwc
|
||||||
esphome/components/tm1651/* @freekode
|
|
||||||
esphome/components/tmp102/* @timsavage
|
esphome/components/tmp102/* @timsavage
|
||||||
esphome/components/tmp1075/* @sybrenstuvel
|
|
||||||
esphome/components/tmp117/* @Azimath
|
esphome/components/tmp117/* @Azimath
|
||||||
esphome/components/tof10120/* @wstrzalka
|
esphome/components/tof10120/* @wstrzalka
|
||||||
esphome/components/tormatic/* @ti-mo
|
|
||||||
esphome/components/toshiba/* @kbx81
|
esphome/components/toshiba/* @kbx81
|
||||||
esphome/components/touchscreen/* @jesserockz @nielsnl68
|
esphome/components/touchscreen/* @jesserockz
|
||||||
esphome/components/tsl2591/* @wjcarpenter
|
esphome/components/tsl2591/* @wjcarpenter
|
||||||
esphome/components/tt21100/* @kroimon
|
|
||||||
esphome/components/tuya/binary_sensor/* @jesserockz
|
esphome/components/tuya/binary_sensor/* @jesserockz
|
||||||
esphome/components/tuya/climate/* @jesserockz
|
esphome/components/tuya/climate/* @jesserockz
|
||||||
esphome/components/tuya/number/* @frankiboy1
|
esphome/components/tuya/number/* @frankiboy1
|
||||||
@@ -468,51 +254,17 @@ esphome/components/tuya/sensor/* @jesserockz
|
|||||||
esphome/components/tuya/switch/* @jesserockz
|
esphome/components/tuya/switch/* @jesserockz
|
||||||
esphome/components/tuya/text_sensor/* @dentra
|
esphome/components/tuya/text_sensor/* @dentra
|
||||||
esphome/components/uart/* @esphome/core
|
esphome/components/uart/* @esphome/core
|
||||||
esphome/components/uart/button/* @ssieb
|
|
||||||
esphome/components/uart/packet_transport/* @clydebarrow
|
|
||||||
esphome/components/udp/* @clydebarrow
|
|
||||||
esphome/components/ufire_ec/* @pvizeli
|
esphome/components/ufire_ec/* @pvizeli
|
||||||
esphome/components/ufire_ise/* @pvizeli
|
esphome/components/ufire_ise/* @pvizeli
|
||||||
esphome/components/ultrasonic/* @OttoWinter
|
esphome/components/ultrasonic/* @OttoWinter
|
||||||
esphome/components/update/* @jesserockz
|
|
||||||
esphome/components/uponor_smatrix/* @kroimon
|
|
||||||
esphome/components/valve/* @esphome/core
|
|
||||||
esphome/components/vbus/* @ssieb
|
|
||||||
esphome/components/veml3235/* @kbx81
|
|
||||||
esphome/components/veml7700/* @latonita
|
|
||||||
esphome/components/version/* @esphome/core
|
esphome/components/version/* @esphome/core
|
||||||
esphome/components/voice_assistant/* @jesserockz
|
esphome/components/wake_on_lan/* @willwill2will54
|
||||||
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
|
|
||||||
esphome/components/watchdog/* @oarcher
|
|
||||||
esphome/components/waveshare_epaper/* @clydebarrow
|
|
||||||
esphome/components/web_server_base/* @OttoWinter
|
esphome/components/web_server_base/* @OttoWinter
|
||||||
esphome/components/web_server_idf/* @dentra
|
|
||||||
esphome/components/weikai/* @DrCoolZic
|
|
||||||
esphome/components/weikai_i2c/* @DrCoolZic
|
|
||||||
esphome/components/weikai_spi/* @DrCoolZic
|
|
||||||
esphome/components/whirlpool/* @glmnet
|
esphome/components/whirlpool/* @glmnet
|
||||||
esphome/components/whynter/* @aeonsablaze
|
esphome/components/whynter/* @aeonsablaze
|
||||||
esphome/components/wiegand/* @ssieb
|
|
||||||
esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
|
|
||||||
esphome/components/wk2132_i2c/* @DrCoolZic
|
|
||||||
esphome/components/wk2132_spi/* @DrCoolZic
|
|
||||||
esphome/components/wk2168_i2c/* @DrCoolZic
|
|
||||||
esphome/components/wk2168_spi/* @DrCoolZic
|
|
||||||
esphome/components/wk2204_i2c/* @DrCoolZic
|
|
||||||
esphome/components/wk2204_spi/* @DrCoolZic
|
|
||||||
esphome/components/wk2212_i2c/* @DrCoolZic
|
|
||||||
esphome/components/wk2212_spi/* @DrCoolZic
|
|
||||||
esphome/components/wl_134/* @hobbypunk90
|
esphome/components/wl_134/* @hobbypunk90
|
||||||
esphome/components/x9c/* @EtienneMD
|
|
||||||
esphome/components/xgzp68xx/* @gcormier
|
|
||||||
esphome/components/xiaomi_hhccjcy10/* @fariouche
|
|
||||||
esphome/components/xiaomi_lywsd02mmc/* @juanluss31
|
|
||||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||||
esphome/components/xiaomi_mhoc303/* @drug123
|
esphome/components/xiaomi_mhoc303/* @drug123
|
||||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
||||||
esphome/components/xl9535/* @mreditor97
|
esphome/components/xpt2046/* @nielsnl68 @numo68
|
||||||
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
|
|
||||||
esphome/components/xxtea/* @clydebarrow
|
|
||||||
esphome/components/zhlt01/* @cfeenstra1024
|
|
||||||
esphome/components/zio_ultrasonic/* @kahrendt
|
|
||||||
|
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
|
|||||||
|
|
||||||
## Enforcement
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@openhomefoundation.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@nabucasa.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
# Contributing to ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
# Contributing to ESPHome
|
||||||
|
|
||||||
We welcome contributions to the ESPHome suite of code and documentation!
|
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphome
|
||||||
|
|
||||||
Please read our [contributing guide](https://esphome.io/guides/contributing.html) if you wish to contribute to the
|
Things to note when contributing:
|
||||||
project and be sure to join us on [Discord](https://discord.gg/KhAMKrd).
|
|
||||||
|
|
||||||
**See also:**
|
- Please test your changes :)
|
||||||
|
- If a new feature is added or an existing user-facing feature is changed, you should also
|
||||||
[Documentation](https://esphome.io) -- [Issues](https://github.com/esphome/issues/issues) -- [Feature requests](https://github.com/esphome/feature-requests/issues)
|
update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs)
|
||||||
|
for more information.
|
||||||
---
|
- Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files
|
||||||
|
which checks if your new feature compiles correctly.
|
||||||
[](https://www.openhomefoundation.org/)
|
- Sometimes I will let pull requests linger because I'm not 100% sure about them. Please feel free to ping
|
||||||
|
me after some time.
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
include LICENSE
|
include LICENSE
|
||||||
include README.md
|
include README.md
|
||||||
include requirements.txt
|
include requirements.txt
|
||||||
recursive-include esphome *.cpp *.h *.tcc *.c
|
include esphome/dashboard/templates/*.html
|
||||||
|
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
|
||||||
|
recursive-include esphome *.cpp *.h *.tcc
|
||||||
recursive-include esphome *.py.script
|
recursive-include esphome *.py.script
|
||||||
recursive-include esphome LICENSE.txt
|
recursive-include esphome LICENSE.txt
|
||||||
|
15
README.md
15
README.md
@@ -1,16 +1,9 @@
|
|||||||
# ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
# ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
||||||
|
|
||||||
<a href="https://esphome.io/">
|
[](https://esphome.io/)
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://esphome.io/_static/logo-text-on-dark.svg", alt="ESPHome Logo">
|
|
||||||
<img src="https://esphome.io/_static/logo-text-on-light.svg" alt="ESPHome Logo">
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
---
|
**Documentation:** https://esphome.io/
|
||||||
|
|
||||||
[Documentation](https://esphome.io) -- [Issues](https://github.com/esphome/issues/issues) -- [Feature requests](https://github.com/esphome/feature-requests/issues)
|
For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues).
|
||||||
|
|
||||||
---
|
For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues).
|
||||||
|
|
||||||
[](https://www.openhomefoundation.org/)
|
|
||||||
|
@@ -1,55 +1,72 @@
|
|||||||
ARG BUILD_VERSION=dev
|
# Build these with the build.py script
|
||||||
ARG BUILD_OS=alpine
|
# Example:
|
||||||
ARG BUILD_BASE_VERSION=2025.04.0
|
# python3 docker/build.py --tag dev --arch amd64 --build-type docker build
|
||||||
ARG BUILD_TYPE=docker
|
|
||||||
|
|
||||||
FROM ghcr.io/esphome/docker-base:${BUILD_OS}-${BUILD_BASE_VERSION} AS base-source-docker
|
# One of "docker", "hassio"
|
||||||
FROM ghcr.io/esphome/docker-base:${BUILD_OS}-ha-addon-${BUILD_BASE_VERSION} AS base-source-ha-addon
|
ARG BASEIMGTYPE=docker
|
||||||
|
|
||||||
ARG BUILD_TYPE
|
# https://github.com/hassio-addons/addon-debian-base/releases
|
||||||
FROM base-source-${BUILD_TYPE} AS base
|
FROM ghcr.io/hassio-addons/debian-base:6.1.3 AS base-hassio
|
||||||
|
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye
|
||||||
|
FROM debian:bullseye-20221024-slim AS base-docker
|
||||||
|
|
||||||
RUN git config --system --add safe.directory "*"
|
FROM base-${BASEIMGTYPE} AS base
|
||||||
|
|
||||||
RUN pip install uv==0.6.14
|
|
||||||
|
|
||||||
COPY requirements.txt /
|
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
uv pip install --no-cache-dir \
|
apt-get update \
|
||||||
-r /requirements.txt
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
python3=3.9.2-3 \
|
||||||
|
python3-pip=20.3.4-4+deb11u1 \
|
||||||
|
python3-setuptools=52.0.0-4 \
|
||||||
|
python3-pil=8.1.2+dfsg-0.3+deb11u1 \
|
||||||
|
python3-cryptography=3.3.2-1 \
|
||||||
|
iputils-ping=3:20210202-1 \
|
||||||
|
git=1:2.30.2-1 \
|
||||||
|
curl=7.74.0-1.3+deb11u3 \
|
||||||
|
openssh-client=1:8.4p1-5+deb11u1 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV \
|
||||||
|
# Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/
|
||||||
|
LANG=C.UTF-8 LC_ALL=C.UTF-8 \
|
||||||
|
# Store globally installed pio libs in /piolibs
|
||||||
|
PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
platformio settings set enable_telemetry No \
|
# Ubuntu python3-pip is missing wheel
|
||||||
|
pip3 install --no-cache-dir \
|
||||||
|
wheel==0.37.1 \
|
||||||
|
platformio==6.1.5 \
|
||||||
|
# Change some platformio settings
|
||||||
|
&& platformio settings set enable_telemetry No \
|
||||||
&& platformio settings set check_platformio_interval 1000000 \
|
&& platformio settings set check_platformio_interval 1000000 \
|
||||||
&& mkdir -p /piolibs
|
&& mkdir -p /piolibs
|
||||||
|
|
||||||
COPY script/platformio_install_deps.py platformio.ini /
|
|
||||||
RUN /platformio_install_deps.py /platformio.ini --libraries
|
|
||||||
|
|
||||||
ARG BUILD_VERSION
|
# First install requirements to leverage caching when requirements don't change
|
||||||
|
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||||
LABEL \
|
RUN \
|
||||||
org.opencontainers.image.authors="The ESPHome Authors" \
|
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||||
org.opencontainers.image.title="ESPHome" \
|
&& /platformio_install_deps.py /platformio.ini
|
||||||
org.opencontainers.image.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
|
|
||||||
org.opencontainers.image.url="https://esphome.io/" \
|
|
||||||
org.opencontainers.image.documentation="https://esphome.io/" \
|
|
||||||
org.opencontainers.image.source="https://github.com/esphome/esphome" \
|
|
||||||
org.opencontainers.image.licenses="ESPHome" \
|
|
||||||
org.opencontainers.image.version=${BUILD_VERSION}
|
|
||||||
|
|
||||||
|
|
||||||
# ======================= docker-type image =======================
|
# ======================= docker-type image =======================
|
||||||
FROM base AS base-docker
|
FROM base AS docker
|
||||||
|
|
||||||
|
# Copy esphome and install
|
||||||
|
COPY . /esphome
|
||||||
|
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
|
||||||
|
|
||||||
|
# Settings for dashboard
|
||||||
|
ENV USERNAME="" PASSWORD=""
|
||||||
|
|
||||||
# Expose the dashboard to Docker
|
# Expose the dashboard to Docker
|
||||||
EXPOSE 6052
|
EXPOSE 6052
|
||||||
|
|
||||||
# Run healthcheck (heartbeat)
|
|
||||||
HEALTHCHECK --interval=30s --timeout=30s \
|
|
||||||
CMD curl --fail http://localhost:6052/version -A "HealthCheck" || exit 1
|
|
||||||
|
|
||||||
COPY docker/docker_entrypoint.sh /entrypoint.sh
|
COPY docker/docker_entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
# The directory the user should mount their configuration files to
|
# The directory the user should mount their configuration files to
|
||||||
@@ -62,23 +79,65 @@ ENTRYPOINT ["/entrypoint.sh"]
|
|||||||
CMD ["dashboard", "/config"]
|
CMD ["dashboard", "/config"]
|
||||||
|
|
||||||
|
|
||||||
# ======================= ha-addon-type image =======================
|
|
||||||
FROM base AS base-ha-addon
|
|
||||||
|
# ======================= hassio-type image =======================
|
||||||
|
FROM base AS hassio
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update \
|
||||||
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
nginx-light=1.18.0-6.1+deb11u3 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ARG BUILD_VERSION=dev
|
||||||
|
|
||||||
# Copy root filesystem
|
# Copy root filesystem
|
||||||
COPY docker/ha-addon-rootfs/ /
|
COPY docker/ha-addon-rootfs/ /
|
||||||
|
|
||||||
ARG BUILD_VERSION
|
# Copy esphome and install
|
||||||
|
COPY . /esphome
|
||||||
|
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
|
||||||
|
|
||||||
|
# Labels
|
||||||
LABEL \
|
LABEL \
|
||||||
io.hass.name="ESPHome" \
|
io.hass.name="ESPHome" \
|
||||||
io.hass.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
|
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
|
||||||
io.hass.type="addon" \
|
io.hass.type="addon" \
|
||||||
io.hass.version="${BUILD_VERSION}"
|
io.hass.version="${BUILD_VERSION}"
|
||||||
# io.hass.arch is inherited from addon-debian-base
|
# io.hass.arch is inherited from addon-debian-base
|
||||||
|
|
||||||
ARG BUILD_TYPE
|
|
||||||
FROM base-${BUILD_TYPE} AS final
|
|
||||||
|
|
||||||
# Copy esphome and install
|
|
||||||
COPY . /esphome
|
|
||||||
RUN uv pip install --no-cache-dir -e /esphome
|
# ======================= lint-type image =======================
|
||||||
|
FROM base AS lint
|
||||||
|
|
||||||
|
ENV \
|
||||||
|
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update \
|
||||||
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
clang-format-11=1:11.0.1-2 \
|
||||||
|
clang-tidy-11=1:11.0.1-2 \
|
||||||
|
patch=2.7.6-7 \
|
||||||
|
software-properties-common=0.96.20.2-2.1 \
|
||||||
|
nano=5.4-2+deb11u1 \
|
||||||
|
build-essential=12.9 \
|
||||||
|
python3-dev=3.9.2-3 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY requirements_test.txt /
|
||||||
|
RUN pip3 install --no-cache-dir -r /requirements_test.txt
|
||||||
|
|
||||||
|
VOLUME ["/esphome"]
|
||||||
|
WORKDIR /esphome
|
||||||
|
100
docker/build.py
100
docker/build.py
@@ -1,60 +1,46 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import re
|
|
||||||
import shlex
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
from platform import machine
|
||||||
|
import shlex
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
CHANNEL_DEV = "dev"
|
|
||||||
CHANNEL_BETA = "beta"
|
CHANNEL_DEV = 'dev'
|
||||||
CHANNEL_RELEASE = "release"
|
CHANNEL_BETA = 'beta'
|
||||||
|
CHANNEL_RELEASE = 'release'
|
||||||
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
|
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
|
||||||
|
|
||||||
ARCH_AMD64 = "amd64"
|
ARCH_AMD64 = 'amd64'
|
||||||
ARCH_AARCH64 = "aarch64"
|
ARCH_ARMV7 = 'armv7'
|
||||||
ARCHS = [ARCH_AMD64, ARCH_AARCH64]
|
ARCH_AARCH64 = 'aarch64'
|
||||||
|
ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64]
|
||||||
|
|
||||||
TYPE_DOCKER = "docker"
|
TYPE_DOCKER = 'docker'
|
||||||
TYPE_HA_ADDON = "ha-addon"
|
TYPE_HA_ADDON = 'ha-addon'
|
||||||
TYPE_LINT = "lint"
|
TYPE_LINT = 'lint'
|
||||||
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
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")
|
||||||
"--tag",
|
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
|
||||||
type=str,
|
parser.add_argument("--build-type", choices=TYPES, required=True, help="The type of build to run")
|
||||||
required=True,
|
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
|
||||||
help="The main docker tag to push to. If a version number also adds latest and/or beta tag",
|
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
|
||||||
)
|
|
||||||
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")
|
build_parser = subparsers.add_parser("build", help="Build the image")
|
||||||
build_parser.add_argument("--push", help="Also push the images", action="store_true")
|
build_parser.add_argument("--push", help="Also push the images", action="store_true")
|
||||||
build_parser.add_argument(
|
build_parser.add_argument("--load", help="Load the docker image locally", action="store_true")
|
||||||
"--load", help="Load the docker image locally", action="store_true"
|
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
|
||||||
)
|
|
||||||
manifest_parser = subparsers.add_parser(
|
|
||||||
"manifest", help="Create a manifest from already pushed images"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class DockerParams:
|
class DockerParams:
|
||||||
build_to: str
|
build_to: str
|
||||||
manifest_to: str
|
manifest_to: str
|
||||||
build_type: str
|
baseimgtype: str
|
||||||
platform: str
|
platform: str
|
||||||
target: str
|
target: str
|
||||||
|
|
||||||
@@ -63,22 +49,28 @@ class DockerParams:
|
|||||||
prefix = {
|
prefix = {
|
||||||
TYPE_DOCKER: "esphome/esphome",
|
TYPE_DOCKER: "esphome/esphome",
|
||||||
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
||||||
TYPE_LINT: "esphome/esphome-lint",
|
TYPE_LINT: "esphome/esphome-lint"
|
||||||
}[build_type]
|
}[build_type]
|
||||||
build_to = f"{prefix}-{arch}"
|
build_to = f"{prefix}-{arch}"
|
||||||
|
baseimgtype = {
|
||||||
|
TYPE_DOCKER: "docker",
|
||||||
|
TYPE_HA_ADDON: "hassio",
|
||||||
|
TYPE_LINT: "docker",
|
||||||
|
}[build_type]
|
||||||
platform = {
|
platform = {
|
||||||
ARCH_AMD64: "linux/amd64",
|
ARCH_AMD64: "linux/amd64",
|
||||||
|
ARCH_ARMV7: "linux/arm/v7",
|
||||||
ARCH_AARCH64: "linux/arm64",
|
ARCH_AARCH64: "linux/arm64",
|
||||||
}[arch]
|
}[arch]
|
||||||
target = {
|
target = {
|
||||||
TYPE_DOCKER: "final",
|
TYPE_DOCKER: "docker",
|
||||||
TYPE_HA_ADDON: "final",
|
TYPE_HA_ADDON: "hassio",
|
||||||
TYPE_LINT: "lint",
|
TYPE_LINT: "lint",
|
||||||
}[build_type]
|
}[build_type]
|
||||||
return cls(
|
return cls(
|
||||||
build_to=build_to,
|
build_to=build_to,
|
||||||
manifest_to=prefix,
|
manifest_to=prefix,
|
||||||
build_type=build_type,
|
baseimgtype=baseimgtype,
|
||||||
platform=platform,
|
platform=platform,
|
||||||
target=target,
|
target=target,
|
||||||
)
|
)
|
||||||
@@ -136,21 +128,13 @@ def main():
|
|||||||
|
|
||||||
# 3. build
|
# 3. build
|
||||||
cmd = [
|
cmd = [
|
||||||
"docker",
|
"docker", "buildx", "build",
|
||||||
"buildx",
|
"--build-arg", f"BASEIMGTYPE={params.baseimgtype}",
|
||||||
"build",
|
"--build-arg", f"BUILD_VERSION={args.tag}",
|
||||||
"--build-arg",
|
"--cache-from", f"type=registry,ref={cache_img}",
|
||||||
f"BUILD_TYPE={params.build_type}",
|
"--file", "docker/Dockerfile",
|
||||||
"--build-arg",
|
"--platform", params.platform,
|
||||||
f"BUILD_VERSION={args.tag}",
|
"--target", params.target,
|
||||||
"--cache-from",
|
|
||||||
f"type=registry,ref={cache_img}",
|
|
||||||
"--file",
|
|
||||||
"docker/Dockerfile",
|
|
||||||
"--platform",
|
|
||||||
params.platform,
|
|
||||||
"--target",
|
|
||||||
params.target,
|
|
||||||
]
|
]
|
||||||
for img in imgs:
|
for img in imgs:
|
||||||
cmd += ["--tag", img]
|
cmd += ["--tag", img]
|
||||||
@@ -176,7 +160,9 @@ def main():
|
|||||||
run_command(*cmd)
|
run_command(*cmd)
|
||||||
# 2. Push manifests
|
# 2. Push manifests
|
||||||
for target in targets:
|
for target in targets:
|
||||||
run_command("docker", "manifest", "push", target)
|
run_command(
|
||||||
|
"docker", "manifest", "push", target
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/bash
|
||||||
|
|
||||||
# If /cache is mounted, use that as PIO's coredir
|
# If /cache is mounted, use that as PIO's coredir
|
||||||
# otherwise use path in /config (so that PIO packages aren't downloaded on each compile)
|
# otherwise use path in /config (so that PIO packages aren't downloaded on each compile)
|
||||||
@@ -21,10 +21,4 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
|||||||
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
||||||
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
||||||
|
|
||||||
# If /build is mounted, use that as the build path
|
|
||||||
# otherwise use path in /config (so that builds aren't lost on container restart)
|
|
||||||
if [[ -d /build ]]; then
|
|
||||||
export ESPHOME_BUILD_PATH=/build
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec esphome "$@"
|
exec esphome "$@"
|
||||||
|
@@ -1,92 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import argparse
|
|
||||||
import re
|
|
||||||
|
|
||||||
CHANNEL_DEV = "dev"
|
|
||||||
CHANNEL_BETA = "beta"
|
|
||||||
CHANNEL_RELEASE = "release"
|
|
||||||
|
|
||||||
GHCR = "ghcr"
|
|
||||||
DOCKERHUB = "dockerhub"
|
|
||||||
|
|
||||||
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(
|
|
||||||
"--suffix",
|
|
||||||
type=str,
|
|
||||||
required=True,
|
|
||||||
help="The suffix of the tag.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--registry",
|
|
||||||
type=str,
|
|
||||||
choices=[GHCR, DOCKERHUB],
|
|
||||||
required=False,
|
|
||||||
action="append",
|
|
||||||
help="The registry to build tags for.",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# detect channel from tag
|
|
||||||
match = re.match(r"^(\d+\.\d+)(?:\.\d+)(?:(b\d+)|(-dev\d+))?$", args.tag)
|
|
||||||
major_minor_version = None
|
|
||||||
if match is None: # eg 2023.12.0-dev20231109-testbranch
|
|
||||||
channel = None # Ran with custom tag for a branch etc
|
|
||||||
elif match.group(3) is not None: # eg 2023.12.0-dev20231109
|
|
||||||
channel = CHANNEL_DEV
|
|
||||||
elif match.group(2) is not None: # eg 2023.12.0b1
|
|
||||||
channel = CHANNEL_BETA
|
|
||||||
else: # eg 2023.12.0
|
|
||||||
major_minor_version = match.group(1)
|
|
||||||
channel = CHANNEL_RELEASE
|
|
||||||
|
|
||||||
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 major_minor_version:
|
|
||||||
tags_to_push.append("stable")
|
|
||||||
tags_to_push.append(major_minor_version)
|
|
||||||
|
|
||||||
suffix = f"-{args.suffix}" if args.suffix else ""
|
|
||||||
|
|
||||||
image_name = f"esphome/esphome{suffix}"
|
|
||||||
|
|
||||||
print(f"channel={channel}")
|
|
||||||
|
|
||||||
if args.registry is None:
|
|
||||||
args.registry = [GHCR, DOCKERHUB]
|
|
||||||
elif len(args.registry) == 1:
|
|
||||||
if GHCR in args.registry:
|
|
||||||
print(f"image=ghcr.io/{image_name}")
|
|
||||||
if DOCKERHUB in args.registry:
|
|
||||||
print(f"image=docker.io/{image_name}")
|
|
||||||
|
|
||||||
print(f"image_name={image_name}")
|
|
||||||
|
|
||||||
full_tags = []
|
|
||||||
|
|
||||||
for tag in tags_to_push:
|
|
||||||
if GHCR in args.registry:
|
|
||||||
full_tags += [f"ghcr.io/{image_name}:{tag}"]
|
|
||||||
if DOCKERHUB in args.registry:
|
|
||||||
full_tags += [f"docker.io/{image_name}:{tag}"]
|
|
||||||
print(f"tags={','.join(full_tags)}")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
41
docker/ha-addon-rootfs/etc/cont-init.d/10-requirements.sh
Executable file
41
docker/ha-addon-rootfs/etc/cont-init.d/10-requirements.sh
Executable file
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# ==============================================================================
|
||||||
|
# Community Hass.io Add-ons: ESPHome
|
||||||
|
# This files check if all user configuration requirements are met
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# Check SSL requirements, if enabled
|
||||||
|
if bashio::config.true 'ssl'; then
|
||||||
|
if ! bashio::config.has_value 'certfile'; then
|
||||||
|
bashio::log.fatal 'SSL is enabled, but no certfile was specified.'
|
||||||
|
bashio::exit.nok
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! bashio::config.has_value 'keyfile'; then
|
||||||
|
bashio::log.fatal 'SSL is enabled, but no keyfile was specified'
|
||||||
|
bashio::exit.nok
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
certfile="/ssl/$(bashio::config 'certfile')"
|
||||||
|
keyfile="/ssl/$(bashio::config 'keyfile')"
|
||||||
|
|
||||||
|
if ! bashio::fs.file_exists "${certfile}"; then
|
||||||
|
if ! bashio::fs.file_exists "${keyfile}"; then
|
||||||
|
# Both files are missing, let's print a friendlier error message
|
||||||
|
bashio::log.fatal 'You enabled encrypted connections using the "ssl": true option.'
|
||||||
|
bashio::log.fatal "However, the SSL files '${certfile}' and '${keyfile}'"
|
||||||
|
bashio::log.fatal "were not found. If you're using Hass.io on your local network and don't want"
|
||||||
|
bashio::log.fatal 'to encrypt connections to the ESPHome dashboard, you can manually disable'
|
||||||
|
bashio::log.fatal 'SSL by setting "ssl" to false."'
|
||||||
|
bashio::exit.nok
|
||||||
|
fi
|
||||||
|
bashio::log.fatal "The configured certfile '${certfile}' was not found."
|
||||||
|
bashio::exit.nok
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! bashio::fs.file_exists "/ssl/$(bashio::config 'keyfile')"; then
|
||||||
|
bashio::log.fatal "The configured keyfile '${keyfile}' was not found."
|
||||||
|
bashio::exit.nok
|
||||||
|
fi
|
||||||
|
fi
|
34
docker/ha-addon-rootfs/etc/cont-init.d/20-nginx.sh
Executable file
34
docker/ha-addon-rootfs/etc/cont-init.d/20-nginx.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# ==============================================================================
|
||||||
|
# Community Hass.io Add-ons: ESPHome
|
||||||
|
# Configures NGINX for use with ESPHome
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
declare certfile
|
||||||
|
declare keyfile
|
||||||
|
declare direct_port
|
||||||
|
declare ingress_interface
|
||||||
|
declare ingress_port
|
||||||
|
|
||||||
|
mkdir -p /var/log/nginx
|
||||||
|
|
||||||
|
direct_port=$(bashio::addon.port 6052)
|
||||||
|
if bashio::var.has_value "${direct_port}"; then
|
||||||
|
if bashio::config.true 'ssl'; then
|
||||||
|
certfile=$(bashio::config 'certfile')
|
||||||
|
keyfile=$(bashio::config 'keyfile')
|
||||||
|
|
||||||
|
mv /etc/nginx/servers/direct-ssl.disabled /etc/nginx/servers/direct.conf
|
||||||
|
sed -i "s/%%certfile%%/${certfile}/g" /etc/nginx/servers/direct.conf
|
||||||
|
sed -i "s/%%keyfile%%/${keyfile}/g" /etc/nginx/servers/direct.conf
|
||||||
|
else
|
||||||
|
mv /etc/nginx/servers/direct.disabled /etc/nginx/servers/direct.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
sed -i "s/%%port%%/${direct_port}/g" /etc/nginx/servers/direct.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
ingress_port=$(bashio::addon.ingress_port)
|
||||||
|
ingress_interface=$(bashio::addon.ip_address)
|
||||||
|
sed -i "s/%%port%%/${ingress_port}/g" /etc/nginx/servers/ingress.conf
|
||||||
|
sed -i "s/%%interface%%/${ingress_interface}/g" /etc/nginx/servers/ingress.conf
|
9
docker/ha-addon-rootfs/etc/cont-init.d/30-dirs.sh
Executable file
9
docker/ha-addon-rootfs/etc/cont-init.d/30-dirs.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# ==============================================================================
|
||||||
|
# Community Hass.io Add-ons: ESPHome
|
||||||
|
# This files creates all directories used by esphome
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
pio_cache_base=/data/cache/platformio
|
||||||
|
|
||||||
|
mkdir -p "${pio_cache_base}"
|
@@ -1,47 +0,0 @@
|
|||||||
#!/usr/bin/with-contenv bashio
|
|
||||||
# ==============================================================================
|
|
||||||
# This file installs the user ESPHome fork if specified.
|
|
||||||
# The fork must be up to date with the latest ESPHome dev branch
|
|
||||||
# and have no conflicts.
|
|
||||||
# This config option only exists in the ESPHome Dev add-on.
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
declare esphome_fork
|
|
||||||
|
|
||||||
if bashio::config.has_value 'esphome_fork'; then
|
|
||||||
esphome_fork=$(bashio::config 'esphome_fork')
|
|
||||||
# format: [username][/repository]:ref
|
|
||||||
if [[ "$esphome_fork" =~ ^(([^/]+)(/([^:]+))?:)?([^:/]+)$ ]]; then
|
|
||||||
username="${BASH_REMATCH[2]:-esphome}"
|
|
||||||
repository="${BASH_REMATCH[4]:-esphome}"
|
|
||||||
ref="${BASH_REMATCH[5]}"
|
|
||||||
else
|
|
||||||
bashio::exit.nok "Invalid esphome_fork format: $esphome_fork"
|
|
||||||
fi
|
|
||||||
full_url="https://github.com/${username}/${repository}/archive/${ref}.tar.gz"
|
|
||||||
bashio::log.info "Checking forked ESPHome"
|
|
||||||
dev_version=$(python3 -c "from esphome.const import __version__; print(__version__)")
|
|
||||||
bashio::log.info "Downloading ESPHome from fork '${esphome_fork}' (${full_url})..."
|
|
||||||
curl -L -o /tmp/esphome.tar.gz "${full_url}" -qq ||
|
|
||||||
bashio::exit.nok "Failed downloading ESPHome fork."
|
|
||||||
bashio::log.info "Installing ESPHome from fork '${esphome_fork}' (${full_url})..."
|
|
||||||
rm -rf /esphome || bashio::exit.nok "Failed to remove ESPHome."
|
|
||||||
mkdir /esphome
|
|
||||||
tar -zxf /tmp/esphome.tar.gz -C /esphome --strip-components=1 ||
|
|
||||||
bashio::exit.nok "Failed installing ESPHome from fork."
|
|
||||||
pip install -U -e /esphome || bashio::exit.nok "Failed installing ESPHome from fork."
|
|
||||||
rm -f /tmp/esphome.tar.gz
|
|
||||||
fork_version=$(python3 -c "from esphome.const import __version__; print(__version__)")
|
|
||||||
|
|
||||||
if [[ "$fork_version" != "$dev_version" ]]; then
|
|
||||||
bashio::log.error "############################"
|
|
||||||
bashio::log.error "Uninstalled fork as version does not match"
|
|
||||||
bashio::log.error "Update (or ask the author to update) the branch"
|
|
||||||
bashio::log.error "This is important as the dev addon and the dev ESPHome"
|
|
||||||
bashio::log.error "branch can have changes that are not compatible with old forks"
|
|
||||||
bashio::log.error "and get reported as bugs which we cannot solve easily."
|
|
||||||
bashio::log.error "############################"
|
|
||||||
bashio::exit.nok
|
|
||||||
fi
|
|
||||||
bashio::log.info "Installed ESPHome from fork '${esphome_fork}' (${full_url})..."
|
|
||||||
fi
|
|
@@ -1,9 +1,9 @@
|
|||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_ignore_client_abort off;
|
proxy_ignore_client_abort off;
|
||||||
proxy_read_timeout 86400s;
|
proxy_read_timeout 86400s;
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
proxy_send_timeout 86400s;
|
proxy_send_timeout 86400s;
|
||||||
proxy_max_temp_file_size 0;
|
proxy_max_temp_file_size 0;
|
||||||
|
|
||||||
proxy_set_header Accept-Encoding "";
|
proxy_set_header Accept-Encoding "";
|
||||||
proxy_set_header Connection $connection_upgrade;
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
root /dev/null;
|
root /dev/null;
|
||||||
server_name $hostname;
|
server_name $hostname;
|
||||||
|
|
||||||
client_max_body_size 512m;
|
|
||||||
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
add_header X-Content-Type-Options nosniff;
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
ssl_protocols TLSv1.2;
|
||||||
ssl_prefer_server_ciphers off;
|
ssl_prefer_server_ciphers on;
|
||||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA;
|
||||||
|
ssl_ecdh_curve secp384r1;
|
||||||
ssl_session_timeout 10m;
|
ssl_session_timeout 10m;
|
||||||
ssl_session_cache shared:SSL:10m;
|
ssl_session_cache shared:SSL:10m;
|
||||||
ssl_session_tickets off;
|
ssl_session_tickets off;
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
upstream esphome {
|
|
||||||
server unix:/var/run/esphome.sock;
|
|
||||||
}
|
|
@@ -2,6 +2,7 @@ daemon off;
|
|||||||
user root;
|
user root;
|
||||||
pid /var/run/nginx.pid;
|
pid /var/run/nginx.pid;
|
||||||
worker_processes 1;
|
worker_processes 1;
|
||||||
|
# Hass.io addon log
|
||||||
error_log /proc/1/fd/1 error;
|
error_log /proc/1/fd/1 error;
|
||||||
events {
|
events {
|
||||||
worker_connections 1024;
|
worker_connections 1024;
|
||||||
@@ -9,22 +10,24 @@ events {
|
|||||||
|
|
||||||
http {
|
http {
|
||||||
include /etc/nginx/includes/mime.types;
|
include /etc/nginx/includes/mime.types;
|
||||||
|
access_log stdout;
|
||||||
access_log off;
|
default_type application/octet-stream;
|
||||||
default_type application/octet-stream;
|
gzip on;
|
||||||
gzip on;
|
keepalive_timeout 65;
|
||||||
keepalive_timeout 65;
|
sendfile on;
|
||||||
sendfile on;
|
server_tokens off;
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
tcp_nodelay on;
|
|
||||||
tcp_nopush on;
|
|
||||||
|
|
||||||
map $http_upgrade $connection_upgrade {
|
map $http_upgrade $connection_upgrade {
|
||||||
default upgrade;
|
default upgrade;
|
||||||
'' close;
|
'' close;
|
||||||
}
|
}
|
||||||
|
|
||||||
include /etc/nginx/includes/upstream.conf;
|
# Use Hass.io supervisor as resolver
|
||||||
|
resolver 172.30.32.2;
|
||||||
|
|
||||||
|
upstream esphome {
|
||||||
|
server unix:/var/run/esphome.sock;
|
||||||
|
}
|
||||||
|
|
||||||
include /etc/nginx/servers/*.conf;
|
include /etc/nginx/servers/*.conf;
|
||||||
}
|
}
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
Without requirements or design, programming is the art of adding bugs to an empty text file. (Louis Srygley)
|
|
@@ -1,26 +1,20 @@
|
|||||||
server {
|
server {
|
||||||
{{ if not .ssl }}
|
listen %%port%% default_server ssl http2;
|
||||||
listen 6052 default_server;
|
|
||||||
{{ else }}
|
|
||||||
listen 6052 default_server ssl http2;
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
include /etc/nginx/includes/server_params.conf;
|
include /etc/nginx/includes/server_params.conf;
|
||||||
include /etc/nginx/includes/proxy_params.conf;
|
include /etc/nginx/includes/proxy_params.conf;
|
||||||
|
|
||||||
{{ if .ssl }}
|
|
||||||
include /etc/nginx/includes/ssl_params.conf;
|
include /etc/nginx/includes/ssl_params.conf;
|
||||||
|
|
||||||
ssl_certificate /ssl/{{ .certfile }};
|
ssl on;
|
||||||
ssl_certificate_key /ssl/{{ .keyfile }};
|
ssl_certificate /ssl/%%certfile%%;
|
||||||
|
ssl_certificate_key /ssl/%%keyfile%%;
|
||||||
|
|
||||||
|
# Clear Hass.io Ingress header
|
||||||
|
proxy_set_header X-HA-Ingress "";
|
||||||
|
|
||||||
# Redirect http requests to https on the same port.
|
# Redirect http requests to https on the same port.
|
||||||
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
|
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
|
||||||
error_page 497 https://$http_host$request_uri;
|
error_page 497 https://$http_host$request_uri;
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
# Clear Home Assistant Ingress header
|
|
||||||
proxy_set_header X-HA-Ingress "";
|
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://esphome;
|
proxy_pass http://esphome;
|
12
docker/ha-addon-rootfs/etc/nginx/servers/direct.disabled
Normal file
12
docker/ha-addon-rootfs/etc/nginx/servers/direct.disabled
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
server {
|
||||||
|
listen %%port%% default_server;
|
||||||
|
|
||||||
|
include /etc/nginx/includes/server_params.conf;
|
||||||
|
include /etc/nginx/includes/proxy_params.conf;
|
||||||
|
# Clear Hass.io Ingress header
|
||||||
|
proxy_set_header X-HA-Ingress "";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://esphome;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,16 +1,14 @@
|
|||||||
server {
|
server {
|
||||||
listen 127.0.0.1:{{ .port }} default_server;
|
listen %%interface%%:%%port%% default_server;
|
||||||
listen {{ .interface }}:{{ .port }} default_server;
|
|
||||||
|
|
||||||
include /etc/nginx/includes/server_params.conf;
|
include /etc/nginx/includes/server_params.conf;
|
||||||
include /etc/nginx/includes/proxy_params.conf;
|
include /etc/nginx/includes/proxy_params.conf;
|
||||||
|
|
||||||
# Set Home Assistant Ingress header
|
# Set Home Assistant Ingress header
|
||||||
proxy_set_header X-HA-Ingress "YES";
|
proxy_set_header X-HA-Ingress "YES";
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
# Only allow from Hass.io supervisor
|
||||||
allow 172.30.32.2;
|
allow 172.30.32.2;
|
||||||
allow 127.0.0.1;
|
|
||||||
deny all;
|
deny all;
|
||||||
|
|
||||||
proxy_pass http://esphome;
|
proxy_pass http://esphome;
|
@@ -1,32 +0,0 @@
|
|||||||
#!/command/with-contenv bashio
|
|
||||||
# shellcheck shell=bash
|
|
||||||
# ==============================================================================
|
|
||||||
# Home Assistant Add-on: ESPHome
|
|
||||||
# Sends discovery information to Home Assistant.
|
|
||||||
# ==============================================================================
|
|
||||||
declare config
|
|
||||||
declare port
|
|
||||||
|
|
||||||
# We only disable it when disabled explicitly
|
|
||||||
if bashio::config.false 'home_assistant_dashboard_integration';
|
|
||||||
then
|
|
||||||
bashio::log.info "Home Assistant discovery is disabled for this add-on."
|
|
||||||
bashio::exit.ok
|
|
||||||
fi
|
|
||||||
|
|
||||||
port=$(bashio::addon.ingress_port)
|
|
||||||
|
|
||||||
# Wait for NGINX to become available
|
|
||||||
bashio::net.wait_for "${port}" "127.0.0.1" 300
|
|
||||||
|
|
||||||
config=$(\
|
|
||||||
bashio::var.json \
|
|
||||||
host "127.0.0.1" \
|
|
||||||
port "^${port}" \
|
|
||||||
)
|
|
||||||
|
|
||||||
if bashio::discovery "esphome" "${config}" > /dev/null; then
|
|
||||||
bashio::log.info "Successfully send discovery information to Home Assistant."
|
|
||||||
else
|
|
||||||
bashio::log.error "Discovery message to Home Assistant failed!"
|
|
||||||
fi
|
|
@@ -1 +0,0 @@
|
|||||||
oneshot
|
|
@@ -1 +0,0 @@
|
|||||||
/etc/s6-overlay/s6-rc.d/discovery/run
|
|
@@ -1,26 +0,0 @@
|
|||||||
#!/command/with-contenv bashio
|
|
||||||
# shellcheck shell=bash
|
|
||||||
# ==============================================================================
|
|
||||||
# Home Assistant Community Add-on: ESPHome
|
|
||||||
# Take down the S6 supervision tree when ESPHome dashboard fails
|
|
||||||
# ==============================================================================
|
|
||||||
declare exit_code
|
|
||||||
readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode)
|
|
||||||
readonly exit_code_service="${1}"
|
|
||||||
readonly exit_code_signal="${2}"
|
|
||||||
|
|
||||||
bashio::log.info \
|
|
||||||
"Service ESPHome dashboard exited with code ${exit_code_service}" \
|
|
||||||
"(by signal ${exit_code_signal})"
|
|
||||||
|
|
||||||
if [[ "${exit_code_service}" -eq 256 ]]; then
|
|
||||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
|
||||||
echo $((128 + $exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
|
||||||
fi
|
|
||||||
[[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt
|
|
||||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
|
||||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
|
||||||
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
|
||||||
fi
|
|
||||||
exec /run/s6/basedir/bin/halt
|
|
||||||
fi
|
|
@@ -1 +0,0 @@
|
|||||||
longrun
|
|
@@ -1,27 +0,0 @@
|
|||||||
#!/command/with-contenv bashio
|
|
||||||
# shellcheck shell=bash
|
|
||||||
# ==============================================================================
|
|
||||||
# Community Hass.io Add-ons: ESPHome
|
|
||||||
# Configures NGINX for use with ESPHome
|
|
||||||
# ==============================================================================
|
|
||||||
mkdir -p /var/log/nginx
|
|
||||||
|
|
||||||
# Generate Ingress configuration
|
|
||||||
bashio::var.json \
|
|
||||||
interface "$(bashio::addon.ip_address)" \
|
|
||||||
port "^$(bashio::addon.ingress_port)" \
|
|
||||||
| tempio \
|
|
||||||
-template /etc/nginx/templates/ingress.gtpl \
|
|
||||||
-out /etc/nginx/servers/ingress.conf
|
|
||||||
|
|
||||||
# Generate direct access configuration, if enabled.
|
|
||||||
if bashio::var.has_value "$(bashio::addon.port 6052)"; then
|
|
||||||
bashio::config.require.ssl
|
|
||||||
bashio::var.json \
|
|
||||||
certfile "$(bashio::config 'certfile')" \
|
|
||||||
keyfile "$(bashio::config 'keyfile')" \
|
|
||||||
ssl "^$(bashio::config 'ssl')" \
|
|
||||||
| tempio \
|
|
||||||
-template /etc/nginx/templates/direct.gtpl \
|
|
||||||
-out /etc/nginx/servers/direct.conf
|
|
||||||
fi
|
|
@@ -1 +0,0 @@
|
|||||||
oneshot
|
|
@@ -1 +0,0 @@
|
|||||||
/etc/s6-overlay/s6-rc.d/init-nginx/run
|
|
@@ -1,25 +0,0 @@
|
|||||||
#!/command/with-contenv bashio
|
|
||||||
# ==============================================================================
|
|
||||||
# Community Hass.io Add-ons: ESPHome
|
|
||||||
# Take down the S6 supervision tree when NGINX fails
|
|
||||||
# ==============================================================================
|
|
||||||
declare exit_code
|
|
||||||
readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode)
|
|
||||||
readonly exit_code_service="${1}"
|
|
||||||
readonly exit_code_signal="${2}"
|
|
||||||
|
|
||||||
bashio::log.info \
|
|
||||||
"Service NGINX exited with code ${exit_code_service}" \
|
|
||||||
"(by signal ${exit_code_signal})"
|
|
||||||
|
|
||||||
if [[ "${exit_code_service}" -eq 256 ]]; then
|
|
||||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
|
||||||
echo $((128 + $exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
|
||||||
fi
|
|
||||||
[[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt
|
|
||||||
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
|
||||||
if [[ "${exit_code_container}" -eq 0 ]]; then
|
|
||||||
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
|
||||||
fi
|
|
||||||
exec /run/s6/basedir/bin/halt
|
|
||||||
fi
|
|
@@ -1 +0,0 @@
|
|||||||
longrun
|
|
15
docker/ha-addon-rootfs/etc/services.d/esphome/finish
Executable file
15
docker/ha-addon-rootfs/etc/services.d/esphome/finish
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/execlineb -S0
|
||||||
|
# ==============================================================================
|
||||||
|
# Community Hass.io Add-ons: ESPHome
|
||||||
|
# Take down the S6 supervision tree when ESPHome fails
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
declare APP_EXIT_CODE=${1}
|
||||||
|
|
||||||
|
if [[ "${APP_EXIT_CODE}" -ne 0 ]] && [[ "${APP_EXIT_CODE}" -ne 256 ]]; then
|
||||||
|
bashio::log.warning "Halt add-on with exit code ${APP_EXIT_CODE}"
|
||||||
|
echo "${APP_EXIT_CODE}" > /run/s6-linux-init-container-results/exitcode
|
||||||
|
exec /run/s6/basedir/bin/halt
|
||||||
|
fi
|
||||||
|
|
||||||
|
bashio::log.info "Service restart after closing"
|
@@ -1,19 +1,10 @@
|
|||||||
#!/command/with-contenv bashio
|
#!/usr/bin/with-contenv bashio
|
||||||
# shellcheck shell=bash
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Community Hass.io Add-ons: ESPHome
|
# Community Hass.io Add-ons: ESPHome
|
||||||
# Runs the ESPHome dashboard
|
# Runs the ESPHome dashboard
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
readonly pio_cache_base=/data/cache/platformio
|
|
||||||
|
|
||||||
export ESPHOME_IS_HA_ADDON=true
|
export ESPHOME_IS_HA_ADDON=true
|
||||||
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
|
||||||
|
|
||||||
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
|
|
||||||
# setting `core_dir` would therefore prevent pio from accessing
|
|
||||||
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
|
||||||
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
|
||||||
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
|
||||||
|
|
||||||
if bashio::config.true 'leave_front_door_open'; then
|
if bashio::config.true 'leave_front_door_open'; then
|
||||||
export DISABLE_HA_AUTHENTICATION=true
|
export DISABLE_HA_AUTHENTICATION=true
|
||||||
@@ -23,6 +14,10 @@ if bashio::config.true 'streamer_mode'; then
|
|||||||
export ESPHOME_STREAMER_MODE=true
|
export ESPHOME_STREAMER_MODE=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if bashio::config.true 'status_use_ping'; then
|
||||||
|
export ESPHOME_DASHBOARD_USE_PING=true
|
||||||
|
fi
|
||||||
|
|
||||||
if bashio::config.has_value 'relative_url'; then
|
if bashio::config.has_value 'relative_url'; then
|
||||||
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
|
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
|
||||||
fi
|
fi
|
||||||
@@ -31,23 +26,18 @@ if bashio::config.has_value 'default_compile_process_limit'; then
|
|||||||
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit')
|
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit')
|
||||||
else
|
else
|
||||||
if grep -q 'Raspberry Pi 3' /proc/cpuinfo; then
|
if grep -q 'Raspberry Pi 3' /proc/cpuinfo; then
|
||||||
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=1
|
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=1;
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "${pio_cache_base}"
|
pio_cache_base=/data/cache/platformio
|
||||||
|
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
|
||||||
|
# setting `core_dir` would therefore prevent pio from accessing
|
||||||
|
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
||||||
|
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
||||||
|
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
||||||
|
|
||||||
mkdir -p /config/esphome
|
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
||||||
|
|
||||||
if bashio::fs.directory_exists '/config/esphome/.esphome'; then
|
|
||||||
bashio::log.info "Migrating old .esphome directory..."
|
|
||||||
if bashio::fs.file_exists '/config/esphome/.esphome/esphome.json'; then
|
|
||||||
mv /config/esphome/.esphome/esphome.json /data/esphome.json
|
|
||||||
fi
|
|
||||||
mkdir -p "/data/storage"
|
|
||||||
mv /config/esphome/.esphome/*.json /data/storage/ || true
|
|
||||||
rm -rf /config/esphome/.esphome
|
|
||||||
fi
|
|
||||||
|
|
||||||
bashio::log.info "Starting ESPHome dashboard..."
|
bashio::log.info "Starting ESPHome dashboard..."
|
||||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
|
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
|
15
docker/ha-addon-rootfs/etc/services.d/nginx/finish
Executable file
15
docker/ha-addon-rootfs/etc/services.d/nginx/finish
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/execlineb -S0
|
||||||
|
# ==============================================================================
|
||||||
|
# Community Hass.io Add-ons: ESPHome
|
||||||
|
# Take down the S6 supervision tree when NGINX fails
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
declare APP_EXIT_CODE=${1}
|
||||||
|
|
||||||
|
if [[ "${APP_EXIT_CODE}" -ne 0 ]] && [[ "${APP_EXIT_CODE}" -ne 256 ]]; then
|
||||||
|
bashio::log.warning "Halt add-on with exit code ${APP_EXIT_CODE}"
|
||||||
|
echo "${APP_EXIT_CODE}" > /run/s6-linux-init-container-results/exitcode
|
||||||
|
exec /run/s6/basedir/bin/halt
|
||||||
|
fi
|
||||||
|
|
||||||
|
bashio::log.info "Service restart after closing"
|
@@ -1,11 +1,10 @@
|
|||||||
#!/command/with-contenv bashio
|
#!/usr/bin/with-contenv bashio
|
||||||
# shellcheck shell=bash
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# Community Hass.io Add-ons: ESPHome
|
# Community Hass.io Add-ons: ESPHome
|
||||||
# Runs the NGINX proxy
|
# Runs the NGINX proxy
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
bashio::log.info "Waiting for ESPHome dashboard to come up..."
|
bashio::log.info "Waiting for dashboard to come up..."
|
||||||
|
|
||||||
while [[ ! -S /var/run/esphome.sock ]]; do
|
while [[ ! -S /var/run/esphome.sock ]]; do
|
||||||
sleep 0.5
|
sleep 0.5
|
30
docker/platformio_install_deps.py
Executable file
30
docker/platformio_install_deps.py
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# This script is used in the docker containers to preinstall
|
||||||
|
# all platformio libraries in the global storage
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
config = configparser.ConfigParser(inline_comment_prefixes=(';', ))
|
||||||
|
config.read(sys.argv[1])
|
||||||
|
|
||||||
|
libs = []
|
||||||
|
# Extract from every lib_deps key in all sections
|
||||||
|
for section in config.sections():
|
||||||
|
conf = config[section]
|
||||||
|
if "lib_deps" not in conf:
|
||||||
|
continue
|
||||||
|
for lib_dep in conf["lib_deps"].splitlines():
|
||||||
|
if not lib_dep:
|
||||||
|
# Empty line or comment
|
||||||
|
continue
|
||||||
|
if lib_dep.startswith("${"):
|
||||||
|
# Extending from another section
|
||||||
|
continue
|
||||||
|
if "@" not in lib_dep:
|
||||||
|
# No version pinned, this is an internal lib
|
||||||
|
continue
|
||||||
|
libs.append(lib_dep)
|
||||||
|
|
||||||
|
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
|
@@ -1,61 +1,48 @@
|
|||||||
# PYTHON_ARGCOMPLETE_OK
|
|
||||||
import argparse
|
import argparse
|
||||||
from datetime import datetime
|
|
||||||
import functools
|
import functools
|
||||||
import importlib
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
import argcomplete
|
|
||||||
|
|
||||||
from esphome import const, writer, yaml_util
|
from esphome import const, writer, yaml_util
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.config import iter_component_configs, read_config, strip_default_ids
|
from esphome.config import iter_components, read_config, strip_default_ids
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
ALLOWED_NAME_CHARS,
|
ALLOWED_NAME_CHARS,
|
||||||
CONF_BAUD_RATE,
|
CONF_BAUD_RATE,
|
||||||
CONF_BROKER,
|
CONF_BROKER,
|
||||||
CONF_DEASSERT_RTS_DTR,
|
CONF_DEASSERT_RTS_DTR,
|
||||||
CONF_DISABLED,
|
|
||||||
CONF_ESPHOME,
|
|
||||||
CONF_LEVEL,
|
|
||||||
CONF_LOG_TOPIC,
|
|
||||||
CONF_LOGGER,
|
CONF_LOGGER,
|
||||||
CONF_MDNS,
|
|
||||||
CONF_MQTT,
|
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_OTA,
|
CONF_OTA,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_PLATFORM,
|
|
||||||
CONF_PLATFORMIO_OPTIONS,
|
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
|
CONF_ESPHOME,
|
||||||
|
CONF_PLATFORMIO_OPTIONS,
|
||||||
CONF_SUBSTITUTIONS,
|
CONF_SUBSTITUTIONS,
|
||||||
CONF_TOPIC,
|
|
||||||
PLATFORM_BK72XX,
|
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
PLATFORM_RTL87XX,
|
|
||||||
SECRETS_FILES,
|
SECRETS_FILES,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, EsphomeError, coroutine
|
from esphome.core import CORE, EsphomeError, coroutine
|
||||||
from esphome.helpers import get_bool_env, indent, is_ip_address
|
from esphome.helpers import indent
|
||||||
from esphome.log import Fore, color, setup_log
|
|
||||||
from esphome.util import (
|
from esphome.util import (
|
||||||
get_serial_ports,
|
|
||||||
list_yaml_files,
|
|
||||||
run_external_command,
|
run_external_command,
|
||||||
run_external_process,
|
run_external_process,
|
||||||
safe_print,
|
safe_print,
|
||||||
|
list_yaml_files,
|
||||||
|
get_serial_ports,
|
||||||
)
|
)
|
||||||
|
from esphome.log import color, setup_log, Fore
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def choose_prompt(options, purpose: str = None):
|
def choose_prompt(options):
|
||||||
if not options:
|
if not options:
|
||||||
raise EsphomeError(
|
raise EsphomeError(
|
||||||
"Found no valid options for upload/logging, please make sure relevant "
|
"Found no valid options for upload/logging, please make sure relevant "
|
||||||
@@ -66,11 +53,9 @@ def choose_prompt(options, purpose: str = None):
|
|||||||
if len(options) == 1:
|
if len(options) == 1:
|
||||||
return options[0][1]
|
return options[0][1]
|
||||||
|
|
||||||
safe_print(
|
safe_print("Found multiple options, please choose one:")
|
||||||
f"Found multiple options{f' for {purpose}' if purpose else ''}, please choose one:"
|
|
||||||
)
|
|
||||||
for i, (desc, _) in enumerate(options):
|
for i, (desc, _) in enumerate(options):
|
||||||
safe_print(f" [{i + 1}] {desc}")
|
safe_print(f" [{i+1}] {desc}")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
opt = input("(number): ")
|
opt = input("(number): ")
|
||||||
@@ -87,42 +72,23 @@ def choose_prompt(options, purpose: str = None):
|
|||||||
return options[opt - 1][1]
|
return options[opt - 1][1]
|
||||||
|
|
||||||
|
|
||||||
def choose_upload_log_host(
|
def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
|
||||||
default, check_default, show_ota, show_mqtt, show_api, purpose: str = None
|
|
||||||
):
|
|
||||||
options = []
|
options = []
|
||||||
for port in get_serial_ports():
|
for port in get_serial_ports():
|
||||||
options.append((f"{port.path} ({port.description})", port.path))
|
options.append((f"{port.path} ({port.description})", port.path))
|
||||||
if default == "SERIAL":
|
|
||||||
return choose_prompt(options, purpose=purpose)
|
|
||||||
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
|
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
|
||||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||||
if default == "OTA":
|
if default == "OTA":
|
||||||
return CORE.address
|
return CORE.address
|
||||||
if (
|
if show_mqtt and "mqtt" in CORE.config:
|
||||||
show_mqtt
|
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
|
||||||
and (mqtt_config := CORE.config.get(CONF_MQTT))
|
|
||||||
and mqtt_logging_enabled(mqtt_config)
|
|
||||||
):
|
|
||||||
options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT"))
|
|
||||||
if default == "OTA":
|
if default == "OTA":
|
||||||
return "MQTT"
|
return "MQTT"
|
||||||
if default is not None:
|
if default is not None:
|
||||||
return default
|
return default
|
||||||
if check_default is not None and check_default in [opt[1] for opt in options]:
|
if check_default is not None and check_default in [opt[1] for opt in options]:
|
||||||
return check_default
|
return check_default
|
||||||
return choose_prompt(options, purpose=purpose)
|
return choose_prompt(options)
|
||||||
|
|
||||||
|
|
||||||
def mqtt_logging_enabled(mqtt_config):
|
|
||||||
log_topic = mqtt_config[CONF_LOG_TOPIC]
|
|
||||||
if log_topic is None:
|
|
||||||
return False
|
|
||||||
if CONF_TOPIC not in log_topic:
|
|
||||||
return False
|
|
||||||
if log_topic.get(CONF_LEVEL, None) == "NONE":
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def get_port_type(port):
|
def get_port_type(port):
|
||||||
@@ -133,9 +99,8 @@ def get_port_type(port):
|
|||||||
return "NETWORK"
|
return "NETWORK"
|
||||||
|
|
||||||
|
|
||||||
def run_miniterm(config, port, args):
|
def run_miniterm(config, port):
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
from esphome import platformio_api
|
from esphome import platformio_api
|
||||||
|
|
||||||
if CONF_LOGGER not in config:
|
if CONF_LOGGER not in config:
|
||||||
@@ -154,7 +119,7 @@ def run_miniterm(config, port, args):
|
|||||||
|
|
||||||
# We can't set to False by default since it leads to toggling and hence
|
# We can't set to False by default since it leads to toggling and hence
|
||||||
# ESP32 resets on some platforms.
|
# ESP32 resets on some platforms.
|
||||||
if config["logger"][CONF_DEASSERT_RTS_DTR] or args.reset:
|
if config["logger"][CONF_DEASSERT_RTS_DTR]:
|
||||||
ser.dtr = False
|
ser.dtr = False
|
||||||
ser.rts = False
|
ser.rts = False
|
||||||
|
|
||||||
@@ -187,8 +152,6 @@ def run_miniterm(config, port, args):
|
|||||||
_LOGGER.error("Could not connect to serial port %s", port)
|
_LOGGER.error("Could not connect to serial port %s", port)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def wrap_to_code(name, comp):
|
def wrap_to_code(name, comp):
|
||||||
coro = coroutine(comp.to_code)
|
coro = coroutine(comp.to_code)
|
||||||
@@ -217,7 +180,7 @@ def write_cpp(config):
|
|||||||
def generate_cpp_contents(config):
|
def generate_cpp_contents(config):
|
||||||
_LOGGER.info("Generating C++ source...")
|
_LOGGER.info("Generating C++ source...")
|
||||||
|
|
||||||
for name, component, conf in iter_component_configs(CORE.config):
|
for name, component, conf in iter_components(CORE.config):
|
||||||
if component.to_code is not None:
|
if component.to_code is not None:
|
||||||
coro = wrap_to_code(name, component)
|
coro = wrap_to_code(name, component)
|
||||||
CORE.add_job(coro, conf)
|
CORE.add_job(coro, conf)
|
||||||
@@ -244,16 +207,14 @@ def compile_program(args, config):
|
|||||||
return 0 if idedata is not None else 1
|
return 0 if idedata is not None else 1
|
||||||
|
|
||||||
|
|
||||||
def upload_using_esptool(config, port, file, speed):
|
def upload_using_esptool(config, port):
|
||||||
from esphome import platformio_api
|
from esphome import platformio_api
|
||||||
|
|
||||||
first_baudrate = speed or config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
||||||
"upload_speed", os.getenv("ESPHOME_UPLOAD_SPEED", "460800")
|
"upload_speed", 460800
|
||||||
)
|
)
|
||||||
|
|
||||||
if file is not None:
|
def run_esptool(baud_rate):
|
||||||
flash_images = [platformio_api.FlashImage(path=file, offset="0x0")]
|
|
||||||
else:
|
|
||||||
idedata = platformio_api.get_idedata(config)
|
idedata = platformio_api.get_idedata(config)
|
||||||
|
|
||||||
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
|
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
|
||||||
@@ -264,13 +225,12 @@ def upload_using_esptool(config, port, file, speed):
|
|||||||
*idedata.extra_flash_images,
|
*idedata.extra_flash_images,
|
||||||
]
|
]
|
||||||
|
|
||||||
mcu = "esp8266"
|
mcu = "esp8266"
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
|
||||||
mcu = get_esp32_variant().lower()
|
mcu = get_esp32_variant().lower()
|
||||||
|
|
||||||
def run_esptool(baud_rate):
|
|
||||||
cmd = [
|
cmd = [
|
||||||
"esptool.py",
|
"esptool.py",
|
||||||
"--before",
|
"--before",
|
||||||
@@ -309,88 +269,36 @@ def upload_using_esptool(config, port, file, speed):
|
|||||||
return run_esptool(115200)
|
return run_esptool(115200)
|
||||||
|
|
||||||
|
|
||||||
def upload_using_platformio(config, port):
|
|
||||||
from esphome import platformio_api
|
|
||||||
|
|
||||||
upload_args = ["-t", "upload", "-t", "nobuild"]
|
|
||||||
if port is not None:
|
|
||||||
upload_args += ["--upload-port", port]
|
|
||||||
return platformio_api.run_platformio_cli_run(config, CORE.verbose, *upload_args)
|
|
||||||
|
|
||||||
|
|
||||||
def check_permissions(port):
|
|
||||||
if os.name == "posix" and get_port_type(port) == "SERIAL":
|
|
||||||
# Check if we can open selected serial port
|
|
||||||
if not os.access(port, os.F_OK):
|
|
||||||
raise EsphomeError(
|
|
||||||
"The selected serial port does not exist. To resolve this issue, "
|
|
||||||
"check that the device is connected to this computer with a USB cable and that "
|
|
||||||
"the USB cable can be used for data and is not a power-only cable."
|
|
||||||
)
|
|
||||||
if not (os.access(port, os.R_OK | os.W_OK)):
|
|
||||||
raise EsphomeError(
|
|
||||||
"You do not have read or write permission on the selected serial port. "
|
|
||||||
"To resolve this issue, you can add your user to the dialout group "
|
|
||||||
f"by running the following command: sudo usermod -a -G dialout {os.getlogin()}. "
|
|
||||||
"You will need to log out & back in or reboot to activate the new group access."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def upload_program(config, args, host):
|
def upload_program(config, args, host):
|
||||||
try:
|
|
||||||
module = importlib.import_module("esphome.components." + CORE.target_platform)
|
|
||||||
if getattr(module, "upload_program")(config, args, host):
|
|
||||||
return 0
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if get_port_type(host) == "SERIAL":
|
if get_port_type(host) == "SERIAL":
|
||||||
check_permissions(host)
|
|
||||||
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
||||||
file = getattr(args, "file", None)
|
return upload_using_esptool(config, host)
|
||||||
return upload_using_esptool(config, host, file, args.upload_speed)
|
|
||||||
|
|
||||||
if CORE.target_platform in (PLATFORM_RP2040):
|
if CORE.target_platform in (PLATFORM_RP2040):
|
||||||
return upload_using_platformio(config, args.device)
|
from esphome import platformio_api
|
||||||
|
|
||||||
if CORE.target_platform in (PLATFORM_BK72XX, PLATFORM_RTL87XX):
|
upload_args = ["-t", "upload"]
|
||||||
return upload_using_platformio(config, host)
|
if args.device is not None:
|
||||||
|
upload_args += ["--upload-port", args.device]
|
||||||
|
return platformio_api.run_platformio_cli_run(
|
||||||
|
config, CORE.verbose, *upload_args
|
||||||
|
)
|
||||||
|
|
||||||
return 1 # Unknown target platform
|
return 1 # Unknown target platform
|
||||||
|
|
||||||
ota_conf = {}
|
|
||||||
for ota_item in config.get(CONF_OTA, []):
|
|
||||||
if ota_item[CONF_PLATFORM] == CONF_ESPHOME:
|
|
||||||
ota_conf = ota_item
|
|
||||||
break
|
|
||||||
|
|
||||||
if not ota_conf:
|
|
||||||
raise EsphomeError(
|
|
||||||
f"Cannot upload Over the Air as the {CONF_OTA} configuration is not present or does not include {CONF_PLATFORM}: {CONF_ESPHOME}"
|
|
||||||
)
|
|
||||||
|
|
||||||
from esphome import espota2
|
from esphome import espota2
|
||||||
|
|
||||||
remote_port = int(ota_conf[CONF_PORT])
|
if CONF_OTA not in config:
|
||||||
|
raise EsphomeError(
|
||||||
|
"Cannot upload Over the Air as the config does not include the ota: "
|
||||||
|
"component"
|
||||||
|
)
|
||||||
|
|
||||||
|
ota_conf = config[CONF_OTA]
|
||||||
|
remote_port = ota_conf[CONF_PORT]
|
||||||
password = ota_conf.get(CONF_PASSWORD, "")
|
password = ota_conf.get(CONF_PASSWORD, "")
|
||||||
|
|
||||||
if (
|
|
||||||
CONF_MQTT in config # pylint: disable=too-many-boolean-expressions
|
|
||||||
and (not args.device or args.device in ("MQTT", "OTA"))
|
|
||||||
and (
|
|
||||||
((config[CONF_MDNS][CONF_DISABLED]) and not is_ip_address(CORE.address))
|
|
||||||
or get_port_type(host) == "MQTT"
|
|
||||||
)
|
|
||||||
):
|
|
||||||
from esphome import mqtt
|
|
||||||
|
|
||||||
host = mqtt.get_esphome_device_ip(
|
|
||||||
config, args.username, args.password, args.client_id
|
|
||||||
)
|
|
||||||
|
|
||||||
if getattr(args, "file", None) is not None:
|
if getattr(args, "file", None) is not None:
|
||||||
return espota2.run_ota(host, remote_port, password, args.file)
|
return espota2.run_ota(host, remote_port, password, args.file)
|
||||||
|
|
||||||
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
|
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
|
||||||
|
|
||||||
|
|
||||||
@@ -398,16 +306,8 @@ def show_logs(config, args, port):
|
|||||||
if "logger" not in config:
|
if "logger" not in config:
|
||||||
raise EsphomeError("Logger is not configured!")
|
raise EsphomeError("Logger is not configured!")
|
||||||
if get_port_type(port) == "SERIAL":
|
if get_port_type(port) == "SERIAL":
|
||||||
check_permissions(port)
|
return run_miniterm(config, port)
|
||||||
return run_miniterm(config, port, args)
|
|
||||||
if get_port_type(port) == "NETWORK" and "api" in config:
|
if get_port_type(port) == "NETWORK" and "api" in config:
|
||||||
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
|
|
||||||
from esphome import mqtt
|
|
||||||
|
|
||||||
port = mqtt.get_esphome_device_ip(
|
|
||||||
config, args.username, args.password, args.client_id
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
from esphome.components.api.client import run_logs
|
from esphome.components.api.client import run_logs
|
||||||
|
|
||||||
return run_logs(config, port)
|
return run_logs(config, port)
|
||||||
@@ -436,17 +336,10 @@ def command_wizard(args):
|
|||||||
|
|
||||||
|
|
||||||
def command_config(args, config):
|
def command_config(args, config):
|
||||||
|
_LOGGER.info("Configuration is valid!")
|
||||||
if not CORE.verbose:
|
if not CORE.verbose:
|
||||||
config = strip_default_ids(config)
|
config = strip_default_ids(config)
|
||||||
output = yaml_util.dump(config, args.show_secrets)
|
safe_print(yaml_util.dump(config))
|
||||||
# add the console decoration so the front-end can hide the secrets
|
|
||||||
if not args.show_secrets:
|
|
||||||
output = re.sub(
|
|
||||||
r"(password|key|psk|ssid)\: (.+)", r"\1: \\033[5m\2\\033[6m", output
|
|
||||||
)
|
|
||||||
if not CORE.quiet:
|
|
||||||
safe_print(output)
|
|
||||||
_LOGGER.info("Configuration is valid!")
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@@ -479,7 +372,6 @@ def command_upload(args, config):
|
|||||||
show_ota=True,
|
show_ota=True,
|
||||||
show_mqtt=False,
|
show_mqtt=False,
|
||||||
show_api=False,
|
show_api=False,
|
||||||
purpose="uploading",
|
|
||||||
)
|
)
|
||||||
exit_code = upload_program(config, args, port)
|
exit_code = upload_program(config, args, port)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
@@ -488,15 +380,6 @@ def command_upload(args, config):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def command_discover(args, config):
|
|
||||||
if "mqtt" in config:
|
|
||||||
from esphome import mqtt
|
|
||||||
|
|
||||||
return mqtt.show_discover(config, args.username, args.password, args.client_id)
|
|
||||||
|
|
||||||
raise EsphomeError("No discover method configured (mqtt)")
|
|
||||||
|
|
||||||
|
|
||||||
def command_logs(args, config):
|
def command_logs(args, config):
|
||||||
port = choose_upload_log_host(
|
port = choose_upload_log_host(
|
||||||
default=args.device,
|
default=args.device,
|
||||||
@@ -504,7 +387,6 @@ def command_logs(args, config):
|
|||||||
show_ota=False,
|
show_ota=False,
|
||||||
show_mqtt=True,
|
show_mqtt=True,
|
||||||
show_api=True,
|
show_api=True,
|
||||||
purpose="logging",
|
|
||||||
)
|
)
|
||||||
return show_logs(config, args, port)
|
return show_logs(config, args, port)
|
||||||
|
|
||||||
@@ -517,22 +399,12 @@ def command_run(args, config):
|
|||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
_LOGGER.info("Successfully compiled program.")
|
_LOGGER.info("Successfully compiled program.")
|
||||||
if CORE.is_host:
|
|
||||||
from esphome.platformio_api import get_idedata
|
|
||||||
|
|
||||||
idedata = get_idedata(config)
|
|
||||||
if idedata is None:
|
|
||||||
return 1
|
|
||||||
program_path = idedata.raw["prog_path"]
|
|
||||||
return run_external_process(program_path)
|
|
||||||
|
|
||||||
port = choose_upload_log_host(
|
port = choose_upload_log_host(
|
||||||
default=args.device,
|
default=args.device,
|
||||||
check_default=None,
|
check_default=None,
|
||||||
show_ota=True,
|
show_ota=True,
|
||||||
show_mqtt=False,
|
show_mqtt=False,
|
||||||
show_api=True,
|
show_api=True,
|
||||||
purpose="uploading",
|
|
||||||
)
|
)
|
||||||
exit_code = upload_program(config, args, port)
|
exit_code = upload_program(config, args, port)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
@@ -546,7 +418,6 @@ def command_run(args, config):
|
|||||||
show_ota=False,
|
show_ota=False,
|
||||||
show_mqtt=True,
|
show_mqtt=True,
|
||||||
show_api=True,
|
show_api=True,
|
||||||
purpose="logging",
|
|
||||||
)
|
)
|
||||||
return show_logs(config, args, port)
|
return show_logs(config, args, port)
|
||||||
|
|
||||||
@@ -579,7 +450,7 @@ def command_clean(args, config):
|
|||||||
def command_dashboard(args):
|
def command_dashboard(args):
|
||||||
from esphome.dashboard import dashboard
|
from esphome.dashboard import dashboard
|
||||||
|
|
||||||
return dashboard.start_dashboard(args)
|
return dashboard.start_web_server(args)
|
||||||
|
|
||||||
|
|
||||||
def command_update_all(args):
|
def command_update_all(args):
|
||||||
@@ -625,9 +496,8 @@ def command_update_all(args):
|
|||||||
|
|
||||||
|
|
||||||
def command_idedata(args, config):
|
def command_idedata(args, config):
|
||||||
import json
|
|
||||||
|
|
||||||
from esphome import platformio_api
|
from esphome import platformio_api
|
||||||
|
import json
|
||||||
|
|
||||||
logging.disable(logging.INFO)
|
logging.disable(logging.INFO)
|
||||||
logging.disable(logging.WARNING)
|
logging.disable(logging.WARNING)
|
||||||
@@ -725,8 +595,7 @@ def command_rename(args, config):
|
|||||||
os.remove(new_path)
|
os.remove(new_path)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if CORE.config_path != new_path:
|
os.remove(CORE.config_path)
|
||||||
os.remove(CORE.config_path)
|
|
||||||
|
|
||||||
print(color(Fore.BOLD_GREEN, "SUCCESS"))
|
print(color(Fore.BOLD_GREEN, "SUCCESS"))
|
||||||
print()
|
print()
|
||||||
@@ -752,30 +621,17 @@ POST_CONFIG_ACTIONS = {
|
|||||||
"clean": command_clean,
|
"clean": command_clean,
|
||||||
"idedata": command_idedata,
|
"idedata": command_idedata,
|
||||||
"rename": command_rename,
|
"rename": command_rename,
|
||||||
"discover": command_discover,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def parse_args(argv):
|
def parse_args(argv):
|
||||||
options_parser = argparse.ArgumentParser(add_help=False)
|
options_parser = argparse.ArgumentParser(add_help=False)
|
||||||
options_parser.add_argument(
|
options_parser.add_argument(
|
||||||
"-v",
|
"-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
|
||||||
"--verbose",
|
|
||||||
help="Enable verbose ESPHome logs.",
|
|
||||||
action="store_true",
|
|
||||||
default=get_bool_env("ESPHOME_VERBOSE"),
|
|
||||||
)
|
)
|
||||||
options_parser.add_argument(
|
options_parser.add_argument(
|
||||||
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
||||||
)
|
)
|
||||||
options_parser.add_argument(
|
|
||||||
"-l",
|
|
||||||
"--log-level",
|
|
||||||
help="Set the log level.",
|
|
||||||
default=os.getenv("ESPHOME_LOG_LEVEL", "INFO"),
|
|
||||||
action="store",
|
|
||||||
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
||||||
)
|
|
||||||
options_parser.add_argument(
|
options_parser.add_argument(
|
||||||
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
||||||
)
|
)
|
||||||
@@ -789,14 +645,7 @@ def parse_args(argv):
|
|||||||
)
|
)
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description=f"ESPHome {const.__version__}", parents=[options_parser]
|
description=f"ESPHome v{const.__version__}", parents=[options_parser]
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--version",
|
|
||||||
action="version",
|
|
||||||
version=f"Version: {const.__version__}",
|
|
||||||
help="Print the ESPHome version and exit.",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
mqtt_options = argparse.ArgumentParser(add_help=False)
|
mqtt_options = argparse.ArgumentParser(add_help=False)
|
||||||
@@ -816,9 +665,6 @@ def parse_args(argv):
|
|||||||
parser_config.add_argument(
|
parser_config.add_argument(
|
||||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
)
|
)
|
||||||
parser_config.add_argument(
|
|
||||||
"--show-secrets", help="Show secrets in output.", action="store_true"
|
|
||||||
)
|
|
||||||
|
|
||||||
parser_compile = subparsers.add_parser(
|
parser_compile = subparsers.add_parser(
|
||||||
"compile", help="Read the configuration and compile a program."
|
"compile", help="Read the configuration and compile a program."
|
||||||
@@ -833,9 +679,7 @@ def parse_args(argv):
|
|||||||
)
|
)
|
||||||
|
|
||||||
parser_upload = subparsers.add_parser(
|
parser_upload = subparsers.add_parser(
|
||||||
"upload",
|
"upload", help="Validate the configuration and upload the latest binary."
|
||||||
help="Validate the configuration and upload the latest binary.",
|
|
||||||
parents=[mqtt_options],
|
|
||||||
)
|
)
|
||||||
parser_upload.add_argument(
|
parser_upload.add_argument(
|
||||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
@@ -844,10 +688,6 @@ def parse_args(argv):
|
|||||||
"--device",
|
"--device",
|
||||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
)
|
)
|
||||||
parser_upload.add_argument(
|
|
||||||
"--upload_speed",
|
|
||||||
help="Override the default or configured upload speed.",
|
|
||||||
)
|
|
||||||
parser_upload.add_argument(
|
parser_upload.add_argument(
|
||||||
"--file",
|
"--file",
|
||||||
help="Manually specify the binary file to upload.",
|
help="Manually specify the binary file to upload.",
|
||||||
@@ -856,7 +696,6 @@ def parse_args(argv):
|
|||||||
parser_logs = subparsers.add_parser(
|
parser_logs = subparsers.add_parser(
|
||||||
"logs",
|
"logs",
|
||||||
help="Validate the configuration and show all logs.",
|
help="Validate the configuration and show all logs.",
|
||||||
aliases=["log"],
|
|
||||||
parents=[mqtt_options],
|
parents=[mqtt_options],
|
||||||
)
|
)
|
||||||
parser_logs.add_argument(
|
parser_logs.add_argument(
|
||||||
@@ -866,22 +705,6 @@ def parse_args(argv):
|
|||||||
"--device",
|
"--device",
|
||||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
)
|
)
|
||||||
parser_logs.add_argument(
|
|
||||||
"--reset",
|
|
||||||
"-r",
|
|
||||||
action="store_true",
|
|
||||||
help="Reset the device before starting serial logs.",
|
|
||||||
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
|
|
||||||
)
|
|
||||||
|
|
||||||
parser_discover = subparsers.add_parser(
|
|
||||||
"discover",
|
|
||||||
help="Validate the configuration and show all discovered devices.",
|
|
||||||
parents=[mqtt_options],
|
|
||||||
)
|
|
||||||
parser_discover.add_argument(
|
|
||||||
"configuration", help="Your YAML configuration file.", nargs=1
|
|
||||||
)
|
|
||||||
|
|
||||||
parser_run = subparsers.add_parser(
|
parser_run = subparsers.add_parser(
|
||||||
"run",
|
"run",
|
||||||
@@ -895,20 +718,9 @@ def parse_args(argv):
|
|||||||
"--device",
|
"--device",
|
||||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
)
|
)
|
||||||
parser_run.add_argument(
|
|
||||||
"--upload_speed",
|
|
||||||
help="Override the default or configured upload speed.",
|
|
||||||
)
|
|
||||||
parser_run.add_argument(
|
parser_run.add_argument(
|
||||||
"--no-logs", help="Disable starting logs.", action="store_true"
|
"--no-logs", help="Disable starting logs.", action="store_true"
|
||||||
)
|
)
|
||||||
parser_run.add_argument(
|
|
||||||
"--reset",
|
|
||||||
"-r",
|
|
||||||
action="store_true",
|
|
||||||
help="Reset the device before starting serial logs.",
|
|
||||||
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
|
|
||||||
)
|
|
||||||
|
|
||||||
parser_clean = subparsers.add_parser(
|
parser_clean = subparsers.add_parser(
|
||||||
"clean-mqtt",
|
"clean-mqtt",
|
||||||
@@ -1019,7 +831,67 @@ def parse_args(argv):
|
|||||||
# a deprecation warning).
|
# a deprecation warning).
|
||||||
arguments = argv[1:]
|
arguments = argv[1:]
|
||||||
|
|
||||||
argcomplete.autocomplete(parser)
|
# On Python 3.9+ we can simply set exit_on_error=False in the constructor
|
||||||
|
def _raise(x):
|
||||||
|
raise argparse.ArgumentError(None, x)
|
||||||
|
|
||||||
|
# First, try new-style parsing, but don't exit in case of failure
|
||||||
|
try:
|
||||||
|
# duplicate parser so that we can use the original one to raise errors later on
|
||||||
|
current_parser = argparse.ArgumentParser(add_help=False, parents=[parser])
|
||||||
|
current_parser.set_defaults(deprecated_argv_suggestion=None)
|
||||||
|
current_parser.error = _raise
|
||||||
|
return current_parser.parse_args(arguments)
|
||||||
|
except argparse.ArgumentError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Second, try compat parsing and rearrange the command-line if it succeeds
|
||||||
|
# Disable argparse's built-in help option and add it manually to prevent this
|
||||||
|
# parser from printing the help messagefor the old format when invoked with -h.
|
||||||
|
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
|
||||||
|
compat_parser.add_argument("-h", "--help", action="store_true")
|
||||||
|
compat_parser.add_argument("configuration", nargs="*")
|
||||||
|
compat_parser.add_argument(
|
||||||
|
"command",
|
||||||
|
choices=[
|
||||||
|
"config",
|
||||||
|
"compile",
|
||||||
|
"upload",
|
||||||
|
"logs",
|
||||||
|
"run",
|
||||||
|
"clean-mqtt",
|
||||||
|
"wizard",
|
||||||
|
"mqtt-fingerprint",
|
||||||
|
"version",
|
||||||
|
"clean",
|
||||||
|
"dashboard",
|
||||||
|
"vscode",
|
||||||
|
"update-all",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
compat_parser.error = _raise
|
||||||
|
result, unparsed = compat_parser.parse_known_args(argv[1:])
|
||||||
|
last_option = len(arguments) - len(unparsed) - 1 - len(result.configuration)
|
||||||
|
unparsed = [
|
||||||
|
"--device" if arg in ("--upload-port", "--serial-port") else arg
|
||||||
|
for arg in unparsed
|
||||||
|
]
|
||||||
|
arguments = (
|
||||||
|
arguments[0:last_option]
|
||||||
|
+ [result.command]
|
||||||
|
+ result.configuration
|
||||||
|
+ unparsed
|
||||||
|
)
|
||||||
|
deprecated_argv_suggestion = arguments
|
||||||
|
except argparse.ArgumentError:
|
||||||
|
# old-style parsing failed, don't suggest any argument
|
||||||
|
deprecated_argv_suggestion = None
|
||||||
|
|
||||||
|
# Finally, run the new-style parser again with the possibly swapped arguments,
|
||||||
|
# and let it error out if the command is unparsable.
|
||||||
|
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
|
||||||
return parser.parse_args(arguments)
|
return parser.parse_args(arguments)
|
||||||
|
|
||||||
|
|
||||||
@@ -1027,17 +899,26 @@ def run_esphome(argv):
|
|||||||
args = parse_args(argv)
|
args = parse_args(argv)
|
||||||
CORE.dashboard = args.dashboard
|
CORE.dashboard = args.dashboard
|
||||||
|
|
||||||
# Override log level if verbose is set
|
|
||||||
if args.verbose:
|
|
||||||
args.log_level = "DEBUG"
|
|
||||||
elif args.quiet:
|
|
||||||
args.log_level = "CRITICAL"
|
|
||||||
|
|
||||||
setup_log(
|
setup_log(
|
||||||
log_level=args.log_level,
|
args.verbose,
|
||||||
|
args.quiet,
|
||||||
# Show timestamp for dashboard access logs
|
# Show timestamp for dashboard access logs
|
||||||
include_timestamp=args.command == "dashboard",
|
args.command == "dashboard",
|
||||||
)
|
)
|
||||||
|
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Calling ESPHome with the configuration before the command is deprecated "
|
||||||
|
"and will be removed in the future. "
|
||||||
|
)
|
||||||
|
_LOGGER.warning("Please instead use:")
|
||||||
|
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion))
|
||||||
|
|
||||||
|
if sys.version_info < (3, 8, 0):
|
||||||
|
_LOGGER.error(
|
||||||
|
"You're running ESPHome with Python <3.8. ESPHome is no longer compatible "
|
||||||
|
"with this Python version. Please reinstall ESPHome with Python 3.8+"
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
if args.command in PRE_CONFIG_ACTIONS:
|
if args.command in PRE_CONFIG_ACTIONS:
|
||||||
try:
|
try:
|
||||||
@@ -1046,8 +927,6 @@ def run_esphome(argv):
|
|||||||
_LOGGER.error(e, exc_info=args.verbose)
|
_LOGGER.error(e, exc_info=args.verbose)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
_LOGGER.info("ESPHome %s", const.__version__)
|
|
||||||
|
|
||||||
for conf_path in args.configuration:
|
for conf_path in args.configuration:
|
||||||
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
|
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
|
||||||
_LOGGER.warning("Skipping secrets file %s", conf_path)
|
_LOGGER.warning("Skipping secrets file %s", conf_path)
|
||||||
|
@@ -1,39 +1,26 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ALL,
|
|
||||||
CONF_ANY,
|
|
||||||
CONF_AUTOMATION_ID,
|
CONF_AUTOMATION_ID,
|
||||||
CONF_CONDITION,
|
CONF_CONDITION,
|
||||||
CONF_COUNT,
|
CONF_COUNT,
|
||||||
CONF_ELSE,
|
CONF_ELSE,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_THEN,
|
CONF_THEN,
|
||||||
CONF_TIME,
|
|
||||||
CONF_TIMEOUT,
|
CONF_TIMEOUT,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_TYPE_ID,
|
CONF_TYPE_ID,
|
||||||
CONF_UPDATE_INTERVAL,
|
CONF_TIME,
|
||||||
)
|
)
|
||||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
|
|
||||||
|
|
||||||
def maybe_simple_id(*validators):
|
def maybe_simple_id(*validators):
|
||||||
"""Allow a raw ID to be specified in place of a config block.
|
|
||||||
If the value that's being validated is a dictionary, it's passed as-is to the specified validators. Otherwise, it's
|
|
||||||
wrapped in a dict that looks like ``{"id": <value>}``, and that dict is then handed off to the specified validators.
|
|
||||||
"""
|
|
||||||
return maybe_conf(CONF_ID, *validators)
|
return maybe_conf(CONF_ID, *validators)
|
||||||
|
|
||||||
|
|
||||||
def maybe_conf(conf, *validators):
|
def maybe_conf(conf, *validators):
|
||||||
"""Allow a raw value to be specified in place of a config block.
|
|
||||||
If the value that's being validated is a dictionary, it's passed as-is to the specified validators. Otherwise, it's
|
|
||||||
wrapped in a dict that looks like ``{<conf>: <value>}``, and that dict is then handed off to the specified
|
|
||||||
validators.
|
|
||||||
(This is a general case of ``maybe_simple_id`` that allows the wrapping key to be something other than ``id``.)
|
|
||||||
"""
|
|
||||||
validator = cv.All(*validators)
|
validator = cv.All(*validators)
|
||||||
|
|
||||||
@schema_extractor("maybe")
|
@schema_extractor("maybe")
|
||||||
@@ -75,13 +62,6 @@ def validate_potentially_and_condition(value):
|
|||||||
return validate_condition(value)
|
return validate_condition(value)
|
||||||
|
|
||||||
|
|
||||||
def validate_potentially_or_condition(value):
|
|
||||||
if isinstance(value, list):
|
|
||||||
with cv.remove_prepend_path(["or"]):
|
|
||||||
return validate_condition({"or": value})
|
|
||||||
return validate_condition(value)
|
|
||||||
|
|
||||||
|
|
||||||
DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
|
DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
|
||||||
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
|
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
|
||||||
IfAction = cg.esphome_ns.class_("IfAction", Action)
|
IfAction = cg.esphome_ns.class_("IfAction", Action)
|
||||||
@@ -89,8 +69,6 @@ WhileAction = cg.esphome_ns.class_("WhileAction", Action)
|
|||||||
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
|
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
|
||||||
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
|
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
|
||||||
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
|
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
|
||||||
SuspendComponentAction = cg.esphome_ns.class_("SuspendComponentAction", Action)
|
|
||||||
ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action)
|
|
||||||
Automation = cg.esphome_ns.class_("Automation")
|
Automation = cg.esphome_ns.class_("Automation")
|
||||||
|
|
||||||
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
|
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
|
||||||
@@ -160,7 +138,6 @@ AUTOMATION_SCHEMA = cv.Schema(
|
|||||||
AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
|
AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
|
||||||
OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
|
OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
|
||||||
NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
|
NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
|
||||||
XorCondition = cg.esphome_ns.class_("XorCondition", Condition)
|
|
||||||
|
|
||||||
|
|
||||||
@register_condition("and", AndCondition, validate_condition_list)
|
@register_condition("and", AndCondition, validate_condition_list)
|
||||||
@@ -175,30 +152,12 @@ async def or_condition_to_code(config, condition_id, template_arg, args):
|
|||||||
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||||
|
|
||||||
|
|
||||||
@register_condition("all", AndCondition, validate_condition_list)
|
|
||||||
async def all_condition_to_code(config, condition_id, template_arg, args):
|
|
||||||
conditions = await build_condition_list(config, template_arg, args)
|
|
||||||
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
|
||||||
|
|
||||||
|
|
||||||
@register_condition("any", OrCondition, validate_condition_list)
|
|
||||||
async def any_condition_to_code(config, condition_id, template_arg, args):
|
|
||||||
conditions = await build_condition_list(config, template_arg, args)
|
|
||||||
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
|
||||||
|
|
||||||
|
|
||||||
@register_condition("not", NotCondition, validate_potentially_and_condition)
|
@register_condition("not", NotCondition, validate_potentially_and_condition)
|
||||||
async def not_condition_to_code(config, condition_id, template_arg, args):
|
async def not_condition_to_code(config, condition_id, template_arg, args):
|
||||||
condition = await build_condition(config, template_arg, args)
|
condition = await build_condition(config, template_arg, args)
|
||||||
return cg.new_Pvariable(condition_id, template_arg, condition)
|
return cg.new_Pvariable(condition_id, template_arg, condition)
|
||||||
|
|
||||||
|
|
||||||
@register_condition("xor", XorCondition, validate_condition_list)
|
|
||||||
async def xor_condition_to_code(config, condition_id, template_arg, args):
|
|
||||||
conditions = await build_condition_list(config, template_arg, args)
|
|
||||||
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
|
||||||
|
|
||||||
|
|
||||||
@register_condition("lambda", LambdaCondition, cv.returning_lambda)
|
@register_condition("lambda", LambdaCondition, cv.returning_lambda)
|
||||||
async def lambda_condition_to_code(config, condition_id, template_arg, args):
|
async def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||||
lambda_ = await cg.process_lambda(config, args, return_type=bool)
|
lambda_ = await cg.process_lambda(config, args, return_type=bool)
|
||||||
@@ -244,21 +203,15 @@ async def delay_action_to_code(config, action_id, template_arg, args):
|
|||||||
IfAction,
|
IfAction,
|
||||||
cv.All(
|
cv.All(
|
||||||
{
|
{
|
||||||
cv.Exclusive(
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
CONF_CONDITION, CONF_CONDITION
|
|
||||||
): validate_potentially_and_condition,
|
|
||||||
cv.Exclusive(CONF_ANY, CONF_CONDITION): validate_potentially_or_condition,
|
|
||||||
cv.Exclusive(CONF_ALL, CONF_CONDITION): validate_potentially_and_condition,
|
|
||||||
cv.Optional(CONF_THEN): validate_action_list,
|
cv.Optional(CONF_THEN): validate_action_list,
|
||||||
cv.Optional(CONF_ELSE): validate_action_list,
|
cv.Optional(CONF_ELSE): validate_action_list,
|
||||||
},
|
},
|
||||||
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
|
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
|
||||||
cv.has_at_least_one_key(CONF_CONDITION, CONF_ANY, CONF_ALL),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def if_action_to_code(config, action_id, template_arg, args):
|
async def if_action_to_code(config, action_id, template_arg, args):
|
||||||
cond_conf = next(el for el in config if el in (CONF_ANY, CONF_ALL, CONF_CONDITION))
|
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||||
conditions = await build_condition(config[cond_conf], template_arg, args)
|
|
||||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||||
if CONF_THEN in config:
|
if CONF_THEN in config:
|
||||||
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||||
@@ -301,11 +254,7 @@ async def repeat_action_to_code(config, action_id, template_arg, args):
|
|||||||
var = cg.new_Pvariable(action_id, template_arg)
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
count_template = await cg.templatable(config[CONF_COUNT], args, cg.uint32)
|
count_template = await cg.templatable(config[CONF_COUNT], args, cg.uint32)
|
||||||
cg.add(var.set_count(count_template))
|
cg.add(var.set_count(count_template))
|
||||||
actions = await build_action_list(
|
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||||
config[CONF_THEN],
|
|
||||||
cg.TemplateArguments(cg.uint32, *template_arg.args),
|
|
||||||
[(cg.uint32, "iteration"), *args],
|
|
||||||
)
|
|
||||||
cg.add(var.add_then(actions))
|
cg.add(var.add_then(actions))
|
||||||
return var
|
return var
|
||||||
|
|
||||||
@@ -350,41 +299,6 @@ async def component_update_action_to_code(config, action_id, template_arg, args)
|
|||||||
return cg.new_Pvariable(action_id, template_arg, comp)
|
return cg.new_Pvariable(action_id, template_arg, comp)
|
||||||
|
|
||||||
|
|
||||||
@register_action(
|
|
||||||
"component.suspend",
|
|
||||||
SuspendComponentAction,
|
|
||||||
maybe_simple_id(
|
|
||||||
{
|
|
||||||
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
async def component_suspend_action_to_code(config, action_id, template_arg, args):
|
|
||||||
comp = await cg.get_variable(config[CONF_ID])
|
|
||||||
return cg.new_Pvariable(action_id, template_arg, comp)
|
|
||||||
|
|
||||||
|
|
||||||
@register_action(
|
|
||||||
"component.resume",
|
|
||||||
ResumeComponentAction,
|
|
||||||
maybe_simple_id(
|
|
||||||
{
|
|
||||||
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
|
|
||||||
cv.Optional(CONF_UPDATE_INTERVAL): cv.templatable(
|
|
||||||
cv.positive_time_period_milliseconds
|
|
||||||
),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
async def component_resume_action_to_code(config, action_id, template_arg, args):
|
|
||||||
comp = await cg.get_variable(config[CONF_ID])
|
|
||||||
var = cg.new_Pvariable(action_id, template_arg, comp)
|
|
||||||
if CONF_UPDATE_INTERVAL in config:
|
|
||||||
template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, int)
|
|
||||||
cg.add(var.set_update_interval(template_))
|
|
||||||
return var
|
|
||||||
|
|
||||||
|
|
||||||
async def build_action(full_config, template_arg, args):
|
async def build_action(full_config, template_arg, args):
|
||||||
registry_entry, config = cg.extract_registry_entry_config(
|
registry_entry, config = cg.extract_registry_entry_config(
|
||||||
ACTION_REGISTRY, full_config
|
ACTION_REGISTRY, full_config
|
||||||
|
@@ -8,86 +8,81 @@
|
|||||||
# want to break suddenly due to a rename (this file will get backports for features).
|
# want to break suddenly due to a rename (this file will get backports for features).
|
||||||
|
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from esphome.cpp_generator import ( # noqa: F401
|
from esphome.cpp_generator import ( # noqa
|
||||||
ArrayInitializer,
|
|
||||||
Expression,
|
Expression,
|
||||||
LineComment,
|
|
||||||
MockObj,
|
|
||||||
MockObjClass,
|
|
||||||
Pvariable,
|
|
||||||
RawExpression,
|
RawExpression,
|
||||||
RawStatement,
|
RawStatement,
|
||||||
Statement,
|
|
||||||
StructInitializer,
|
|
||||||
TemplateArguments,
|
TemplateArguments,
|
||||||
|
StructInitializer,
|
||||||
|
ArrayInitializer,
|
||||||
|
safe_exp,
|
||||||
|
Statement,
|
||||||
|
LineComment,
|
||||||
|
progmem_array,
|
||||||
|
static_const_array,
|
||||||
|
statement,
|
||||||
|
variable,
|
||||||
|
with_local_variable,
|
||||||
|
new_variable,
|
||||||
|
Pvariable,
|
||||||
|
new_Pvariable,
|
||||||
add,
|
add,
|
||||||
add_build_flag,
|
|
||||||
add_define,
|
|
||||||
add_global,
|
add_global,
|
||||||
add_library,
|
add_library,
|
||||||
|
add_build_flag,
|
||||||
|
add_define,
|
||||||
add_platformio_option,
|
add_platformio_option,
|
||||||
get_variable,
|
get_variable,
|
||||||
get_variable_with_full_id,
|
get_variable_with_full_id,
|
||||||
is_template,
|
|
||||||
new_Pvariable,
|
|
||||||
new_variable,
|
|
||||||
process_lambda,
|
process_lambda,
|
||||||
progmem_array,
|
is_template,
|
||||||
safe_exp,
|
|
||||||
statement,
|
|
||||||
static_const_array,
|
|
||||||
templatable,
|
templatable,
|
||||||
variable,
|
MockObj,
|
||||||
with_local_variable,
|
MockObjClass,
|
||||||
)
|
)
|
||||||
from esphome.cpp_helpers import ( # noqa: F401
|
from esphome.cpp_helpers import ( # noqa
|
||||||
|
gpio_pin_expression,
|
||||||
|
register_component,
|
||||||
build_registry_entry,
|
build_registry_entry,
|
||||||
build_registry_list,
|
build_registry_list,
|
||||||
extract_registry_entry_config,
|
extract_registry_entry_config,
|
||||||
gpio_pin_expression,
|
|
||||||
past_safe_mode,
|
|
||||||
register_component,
|
|
||||||
register_parented,
|
register_parented,
|
||||||
)
|
)
|
||||||
from esphome.cpp_types import ( # noqa: F401
|
from esphome.cpp_types import ( # noqa
|
||||||
NAN,
|
|
||||||
App,
|
|
||||||
Application,
|
|
||||||
Component,
|
|
||||||
ComponentPtr,
|
|
||||||
Controller,
|
|
||||||
EntityBase,
|
|
||||||
EntityCategory,
|
|
||||||
ESPTime,
|
|
||||||
GPIOPin,
|
|
||||||
InternalGPIOPin,
|
|
||||||
JsonObject,
|
|
||||||
JsonObjectConst,
|
|
||||||
Parented,
|
|
||||||
PollingComponent,
|
|
||||||
arduino_json_ns,
|
|
||||||
bool_,
|
|
||||||
const_char_ptr,
|
|
||||||
double,
|
|
||||||
esphome_ns,
|
|
||||||
float_,
|
|
||||||
global_ns,
|
global_ns,
|
||||||
gpio_Flags,
|
void,
|
||||||
int16,
|
|
||||||
int32,
|
|
||||||
int64,
|
|
||||||
int_,
|
|
||||||
nullptr,
|
nullptr,
|
||||||
optional,
|
float_,
|
||||||
size_t,
|
double,
|
||||||
|
bool_,
|
||||||
|
int_,
|
||||||
std_ns,
|
std_ns,
|
||||||
std_shared_ptr,
|
|
||||||
std_string,
|
std_string,
|
||||||
std_string_ref,
|
|
||||||
std_vector,
|
std_vector,
|
||||||
uint8,
|
uint8,
|
||||||
uint16,
|
uint16,
|
||||||
uint32,
|
uint32,
|
||||||
uint64,
|
uint64,
|
||||||
void,
|
int32,
|
||||||
|
int64,
|
||||||
|
size_t,
|
||||||
|
const_char_ptr,
|
||||||
|
NAN,
|
||||||
|
esphome_ns,
|
||||||
|
App,
|
||||||
|
EntityBase,
|
||||||
|
Component,
|
||||||
|
ComponentPtr,
|
||||||
|
PollingComponent,
|
||||||
|
Application,
|
||||||
|
optional,
|
||||||
|
arduino_json_ns,
|
||||||
|
JsonObject,
|
||||||
|
JsonObjectConst,
|
||||||
|
Controller,
|
||||||
|
GPIOPin,
|
||||||
|
InternalGPIOPin,
|
||||||
|
gpio_Flags,
|
||||||
|
EntityCategory,
|
||||||
|
Parented,
|
||||||
)
|
)
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
CODEOWNERS = ["@MrSuicideParrot"]
|
|
@@ -1,44 +0,0 @@
|
|||||||
// Datasheet https://wiki.dfrobot.com/A01NYUB%20Waterproof%20Ultrasonic%20Sensor%20SKU:%20SEN0313
|
|
||||||
|
|
||||||
#include "a01nyub.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace a01nyub {
|
|
||||||
|
|
||||||
static const char *const TAG = "a01nyub.sensor";
|
|
||||||
|
|
||||||
void A01nyubComponent::loop() {
|
|
||||||
uint8_t data;
|
|
||||||
while (this->available() > 0) {
|
|
||||||
this->read_byte(&data);
|
|
||||||
if (this->buffer_.empty() && (data != 0xff))
|
|
||||||
continue;
|
|
||||||
buffer_.push_back(data);
|
|
||||||
if (this->buffer_.size() == 4)
|
|
||||||
this->check_buffer_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void A01nyubComponent::check_buffer_() {
|
|
||||||
uint8_t checksum = this->buffer_[0] + this->buffer_[1] + this->buffer_[2];
|
|
||||||
if (this->buffer_[3] == checksum) {
|
|
||||||
float distance = (this->buffer_[1] << 8) + this->buffer_[2];
|
|
||||||
if (distance > 280) {
|
|
||||||
float meters = distance / 1000.0;
|
|
||||||
ESP_LOGV(TAG, "Distance from sensor: %f mm, %f m", distance, meters);
|
|
||||||
this->publish_state(meters);
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "checksum failed: %02x != %02x", checksum, this->buffer_[3]);
|
|
||||||
}
|
|
||||||
this->buffer_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void A01nyubComponent::dump_config() { LOG_SENSOR("", "A01nyub Sensor", this); }
|
|
||||||
|
|
||||||
} // namespace a01nyub
|
|
||||||
} // namespace esphome
|
|
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/uart/uart.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace a01nyub {
|
|
||||||
|
|
||||||
class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
|
|
||||||
public:
|
|
||||||
// Nothing really public.
|
|
||||||
|
|
||||||
// ========== INTERNAL METHODS ==========
|
|
||||||
void loop() override;
|
|
||||||
void dump_config() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void check_buffer_();
|
|
||||||
|
|
||||||
std::vector<uint8_t> buffer_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace a01nyub
|
|
||||||
} // namespace esphome
|
|
@@ -1,41 +0,0 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import sensor, uart
|
|
||||||
from esphome.const import (
|
|
||||||
DEVICE_CLASS_DISTANCE,
|
|
||||||
ICON_ARROW_EXPAND_VERTICAL,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
UNIT_METER,
|
|
||||||
)
|
|
||||||
|
|
||||||
CODEOWNERS = ["@MrSuicideParrot"]
|
|
||||||
DEPENDENCIES = ["uart"]
|
|
||||||
|
|
||||||
a01nyub_ns = cg.esphome_ns.namespace("a01nyub")
|
|
||||||
A01nyubComponent = a01nyub_ns.class_(
|
|
||||||
"A01nyubComponent", sensor.Sensor, cg.Component, uart.UARTDevice
|
|
||||||
)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
|
||||||
A01nyubComponent,
|
|
||||||
unit_of_measurement=UNIT_METER,
|
|
||||||
icon=ICON_ARROW_EXPAND_VERTICAL,
|
|
||||||
accuracy_decimals=3,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
device_class=DEVICE_CLASS_DISTANCE,
|
|
||||||
).extend(uart.UART_DEVICE_SCHEMA)
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
|
||||||
"a01nyub",
|
|
||||||
baud_rate=9600,
|
|
||||||
require_tx=False,
|
|
||||||
require_rx=True,
|
|
||||||
data_bits=8,
|
|
||||||
parity=None,
|
|
||||||
stop_bits=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = await sensor.new_sensor(config)
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
await uart.register_uart_device(var, config)
|
|
@@ -1 +0,0 @@
|
|||||||
CODEOWNERS = ["@TH-Braemer"]
|
|
@@ -1,43 +0,0 @@
|
|||||||
// Datasheet https://wiki.dfrobot.com/_A02YYUW_Waterproof_Ultrasonic_Sensor_SKU_SEN0311
|
|
||||||
|
|
||||||
#include "a02yyuw.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace a02yyuw {
|
|
||||||
|
|
||||||
static const char *const TAG = "a02yyuw.sensor";
|
|
||||||
|
|
||||||
void A02yyuwComponent::loop() {
|
|
||||||
uint8_t data;
|
|
||||||
while (this->available() > 0) {
|
|
||||||
this->read_byte(&data);
|
|
||||||
if (this->buffer_.empty() && (data != 0xff))
|
|
||||||
continue;
|
|
||||||
buffer_.push_back(data);
|
|
||||||
if (this->buffer_.size() == 4)
|
|
||||||
this->check_buffer_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void A02yyuwComponent::check_buffer_() {
|
|
||||||
uint8_t checksum = this->buffer_[0] + this->buffer_[1] + this->buffer_[2];
|
|
||||||
if (this->buffer_[3] == checksum) {
|
|
||||||
float distance = (this->buffer_[1] << 8) + this->buffer_[2];
|
|
||||||
if (distance > 30) {
|
|
||||||
ESP_LOGV(TAG, "Distance from sensor: %f mm", distance);
|
|
||||||
this->publish_state(distance);
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "checksum failed: %02x != %02x", checksum, this->buffer_[3]);
|
|
||||||
}
|
|
||||||
this->buffer_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void A02yyuwComponent::dump_config() { LOG_SENSOR("", "A02yyuw Sensor", this); }
|
|
||||||
|
|
||||||
} // namespace a02yyuw
|
|
||||||
} // namespace esphome
|
|
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/uart/uart.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace a02yyuw {
|
|
||||||
|
|
||||||
class A02yyuwComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
|
|
||||||
public:
|
|
||||||
// Nothing really public.
|
|
||||||
|
|
||||||
// ========== INTERNAL METHODS ==========
|
|
||||||
void loop() override;
|
|
||||||
void dump_config() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void check_buffer_();
|
|
||||||
|
|
||||||
std::vector<uint8_t> buffer_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace a02yyuw
|
|
||||||
} // namespace esphome
|
|
@@ -1,41 +0,0 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import sensor, uart
|
|
||||||
from esphome.const import (
|
|
||||||
DEVICE_CLASS_DISTANCE,
|
|
||||||
ICON_ARROW_EXPAND_VERTICAL,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
UNIT_MILLIMETER,
|
|
||||||
)
|
|
||||||
|
|
||||||
CODEOWNERS = ["@TH-Braemer"]
|
|
||||||
DEPENDENCIES = ["uart"]
|
|
||||||
|
|
||||||
a02yyuw_ns = cg.esphome_ns.namespace("a02yyuw")
|
|
||||||
A02yyuwComponent = a02yyuw_ns.class_(
|
|
||||||
"A02yyuwComponent", sensor.Sensor, cg.Component, uart.UARTDevice
|
|
||||||
)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
|
||||||
A02yyuwComponent,
|
|
||||||
unit_of_measurement=UNIT_MILLIMETER,
|
|
||||||
icon=ICON_ARROW_EXPAND_VERTICAL,
|
|
||||||
accuracy_decimals=0,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
device_class=DEVICE_CLASS_DISTANCE,
|
|
||||||
).extend(uart.UART_DEVICE_SCHEMA)
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
|
||||||
"a02yyuw",
|
|
||||||
baud_rate=9600,
|
|
||||||
require_tx=False,
|
|
||||||
require_rx=True,
|
|
||||||
data_bits=8,
|
|
||||||
parity=None,
|
|
||||||
stop_bits=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = await sensor.new_sensor(config)
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
await uart.register_uart_device(var, config)
|
|
@@ -46,7 +46,6 @@ void A4988::loop() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this->dir_pin_->digital_write(dir == 1);
|
this->dir_pin_->digital_write(dir == 1);
|
||||||
delayMicroseconds(50);
|
|
||||||
this->step_pin_->digital_write(true);
|
this->step_pin_->digital_write(true);
|
||||||
delayMicroseconds(5);
|
delayMicroseconds(5);
|
||||||
this->step_pin_->digital_write(false);
|
this->step_pin_->digital_write(false);
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
from esphome import pins
|
from esphome import pins
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import stepper
|
from esphome.components import stepper
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
import esphome.codegen as cg
|
||||||
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
|
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
|
||||||
|
|
||||||
|
|
||||||
a4988_ns = cg.esphome_ns.namespace("a4988")
|
a4988_ns = cg.esphome_ns.namespace("a4988")
|
||||||
A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component)
|
A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component)
|
||||||
|
|
||||||
@@ -27,6 +28,6 @@ async def to_code(config):
|
|||||||
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
||||||
cg.add(var.set_dir_pin(dir_pin))
|
cg.add(var.set_dir_pin(dir_pin))
|
||||||
|
|
||||||
if sleep_pin_config := config.get(CONF_SLEEP_PIN):
|
if CONF_SLEEP_PIN in config:
|
||||||
sleep_pin = await cg.gpio_pin_expression(sleep_pin_config)
|
sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
||||||
cg.add(var.set_sleep_pin(sleep_pin))
|
cg.add(var.set_sleep_pin(sleep_pin))
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
CODEOWNERS = ["@DAVe3283"]
|
|
@@ -1,182 +0,0 @@
|
|||||||
#include "esphome/core/log.h"
|
|
||||||
#include "absolute_humidity.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace absolute_humidity {
|
|
||||||
|
|
||||||
static const char *const TAG = "absolute_humidity.sensor";
|
|
||||||
|
|
||||||
void AbsoluteHumidityComponent::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up absolute humidity '%s'...", this->get_name().c_str());
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, " Added callback for temperature '%s'", this->temperature_sensor_->get_name().c_str());
|
|
||||||
this->temperature_sensor_->add_on_state_callback([this](float state) { this->temperature_callback_(state); });
|
|
||||||
if (this->temperature_sensor_->has_state()) {
|
|
||||||
this->temperature_callback_(this->temperature_sensor_->get_state());
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, " Added callback for relative humidity '%s'", this->humidity_sensor_->get_name().c_str());
|
|
||||||
this->humidity_sensor_->add_on_state_callback([this](float state) { this->humidity_callback_(state); });
|
|
||||||
if (this->humidity_sensor_->has_state()) {
|
|
||||||
this->humidity_callback_(this->humidity_sensor_->get_state());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbsoluteHumidityComponent::dump_config() {
|
|
||||||
LOG_SENSOR("", "Absolute Humidity", this);
|
|
||||||
|
|
||||||
switch (this->equation_) {
|
|
||||||
case BUCK:
|
|
||||||
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Buck");
|
|
||||||
break;
|
|
||||||
case TETENS:
|
|
||||||
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Tetens");
|
|
||||||
break;
|
|
||||||
case WOBUS:
|
|
||||||
ESP_LOGCONFIG(TAG, "Saturation Vapor Pressure Equation: Wobus");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ESP_LOGE(TAG, "Invalid saturation vapor pressure equation selection!");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, "Sources");
|
|
||||||
ESP_LOGCONFIG(TAG, " Temperature: '%s'", this->temperature_sensor_->get_name().c_str());
|
|
||||||
ESP_LOGCONFIG(TAG, " Relative Humidity: '%s'", this->humidity_sensor_->get_name().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
void AbsoluteHumidityComponent::loop() {
|
|
||||||
if (!this->next_update_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->next_update_ = false;
|
|
||||||
|
|
||||||
// Ensure we have source data
|
|
||||||
const bool no_temperature = std::isnan(this->temperature_);
|
|
||||||
const bool no_humidity = std::isnan(this->humidity_);
|
|
||||||
if (no_temperature || no_humidity) {
|
|
||||||
if (no_temperature) {
|
|
||||||
ESP_LOGW(TAG, "No valid state from temperature sensor!");
|
|
||||||
}
|
|
||||||
if (no_humidity) {
|
|
||||||
ESP_LOGW(TAG, "No valid state from temperature sensor!");
|
|
||||||
}
|
|
||||||
ESP_LOGW(TAG, "Unable to calculate absolute humidity.");
|
|
||||||
this->publish_state(NAN);
|
|
||||||
this->status_set_warning();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to desired units
|
|
||||||
const float temperature_c = this->temperature_;
|
|
||||||
const float temperature_k = temperature_c + 273.15;
|
|
||||||
const float hr = this->humidity_ / 100;
|
|
||||||
|
|
||||||
// Calculate saturation vapor pressure
|
|
||||||
float es;
|
|
||||||
switch (this->equation_) {
|
|
||||||
case BUCK:
|
|
||||||
es = es_buck(temperature_c);
|
|
||||||
break;
|
|
||||||
case TETENS:
|
|
||||||
es = es_tetens(temperature_c);
|
|
||||||
break;
|
|
||||||
case WOBUS:
|
|
||||||
es = es_wobus(temperature_c);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ESP_LOGE(TAG, "Invalid saturation vapor pressure equation selection!");
|
|
||||||
this->publish_state(NAN);
|
|
||||||
this->status_set_error();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
|
|
||||||
|
|
||||||
// Calculate absolute humidity
|
|
||||||
const float absolute_humidity = vapor_density(es, hr, temperature_k);
|
|
||||||
|
|
||||||
// Publish absolute humidity
|
|
||||||
ESP_LOGD(TAG, "Publishing absolute humidity %f g/m³", absolute_humidity);
|
|
||||||
this->status_clear_warning();
|
|
||||||
this->publish_state(absolute_humidity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buck equation (https://en.wikipedia.org/wiki/Arden_Buck_equation)
|
|
||||||
// More accurate than Tetens in normal meteorologic conditions
|
|
||||||
float AbsoluteHumidityComponent::es_buck(float temperature_c) {
|
|
||||||
float a, b, c, d;
|
|
||||||
if (temperature_c >= 0) {
|
|
||||||
a = 0.61121;
|
|
||||||
b = 18.678;
|
|
||||||
c = 234.5;
|
|
||||||
d = 257.14;
|
|
||||||
} else {
|
|
||||||
a = 0.61115;
|
|
||||||
b = 18.678;
|
|
||||||
c = 233.7;
|
|
||||||
d = 279.82;
|
|
||||||
}
|
|
||||||
return a * expf((b - (temperature_c / c)) * (temperature_c / (d + temperature_c)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tetens equation (https://en.wikipedia.org/wiki/Tetens_equation)
|
|
||||||
float AbsoluteHumidityComponent::es_tetens(float temperature_c) {
|
|
||||||
float a, b;
|
|
||||||
if (temperature_c >= 0) {
|
|
||||||
a = 17.27;
|
|
||||||
b = 237.3;
|
|
||||||
} else {
|
|
||||||
a = 21.875;
|
|
||||||
b = 265.5;
|
|
||||||
}
|
|
||||||
return 0.61078 * expf((a * temperature_c) / (temperature_c + b));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wobus equation
|
|
||||||
// https://wahiduddin.net/calc/density_altitude.htm
|
|
||||||
// https://wahiduddin.net/calc/density_algorithms.htm
|
|
||||||
// Calculate the saturation vapor pressure (kPa)
|
|
||||||
float AbsoluteHumidityComponent::es_wobus(float t) {
|
|
||||||
// THIS FUNCTION RETURNS THE SATURATION VAPOR PRESSURE ESW (MILLIBARS)
|
|
||||||
// OVER LIQUID WATER GIVEN THE TEMPERATURE T (CELSIUS). THE POLYNOMIAL
|
|
||||||
// APPROXIMATION BELOW IS DUE TO HERMAN WOBUS, A MATHEMATICIAN WHO
|
|
||||||
// WORKED AT THE NAVY WEATHER RESEARCH FACILITY, NORFOLK, VIRGINIA,
|
|
||||||
// BUT WHO IS NOW RETIRED. THE COEFFICIENTS OF THE POLYNOMIAL WERE
|
|
||||||
// CHOSEN TO FIT THE VALUES IN TABLE 94 ON PP. 351-353 OF THE SMITH-
|
|
||||||
// SONIAN METEOROLOGICAL TABLES BY ROLAND LIST (6TH EDITION). THE
|
|
||||||
// APPROXIMATION IS VALID FOR -50 < T < 100C.
|
|
||||||
//
|
|
||||||
// Baker, Schlatter 17-MAY-1982 Original version.
|
|
||||||
|
|
||||||
const float c0 = +0.99999683e00;
|
|
||||||
const float c1 = -0.90826951e-02;
|
|
||||||
const float c2 = +0.78736169e-04;
|
|
||||||
const float c3 = -0.61117958e-06;
|
|
||||||
const float c4 = +0.43884187e-08;
|
|
||||||
const float c5 = -0.29883885e-10;
|
|
||||||
const float c6 = +0.21874425e-12;
|
|
||||||
const float c7 = -0.17892321e-14;
|
|
||||||
const float c8 = +0.11112018e-16;
|
|
||||||
const float c9 = -0.30994571e-19;
|
|
||||||
const float p = c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * (c6 + t * (c7 + t * (c8 + t * (c9)))))))));
|
|
||||||
return 0.61078 / pow(p, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
// From https://www.environmentalbiophysics.org/chalk-talk-how-to-calculate-absolute-humidity/
|
|
||||||
// H/T to https://esphome.io/cookbook/bme280_environment.html
|
|
||||||
// H/T to https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
|
|
||||||
float AbsoluteHumidityComponent::vapor_density(float es, float hr, float ta) {
|
|
||||||
// es = saturated vapor pressure (kPa)
|
|
||||||
// hr = relative humidity [0-1]
|
|
||||||
// ta = absolute temperature (K)
|
|
||||||
|
|
||||||
const float ea = hr * es * 1000; // vapor pressure of the air (Pa)
|
|
||||||
const float mw = 18.01528; // molar mass of water (g⋅mol⁻¹)
|
|
||||||
const float r = 8.31446261815324; // molar gas constant (J⋅K⁻¹)
|
|
||||||
return (ea * mw) / (r * ta);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace absolute_humidity
|
|
||||||
} // namespace esphome
|
|
@@ -1,76 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace absolute_humidity {
|
|
||||||
|
|
||||||
/// Enum listing all implemented saturation vapor pressure equations.
|
|
||||||
enum SaturationVaporPressureEquation {
|
|
||||||
BUCK,
|
|
||||||
TETENS,
|
|
||||||
WOBUS,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This class implements calculation of absolute humidity from temperature and relative humidity.
|
|
||||||
class AbsoluteHumidityComponent : public sensor::Sensor, public Component {
|
|
||||||
public:
|
|
||||||
AbsoluteHumidityComponent() = default;
|
|
||||||
|
|
||||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
|
|
||||||
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
|
|
||||||
void set_equation(SaturationVaporPressureEquation equation) { this->equation_ = equation; }
|
|
||||||
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
|
||||||
float get_setup_priority() const override;
|
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void temperature_callback_(float state) {
|
|
||||||
this->next_update_ = true;
|
|
||||||
this->temperature_ = state;
|
|
||||||
}
|
|
||||||
void humidity_callback_(float state) {
|
|
||||||
this->next_update_ = true;
|
|
||||||
this->humidity_ = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Buck equation for saturation vapor pressure in kPa.
|
|
||||||
*
|
|
||||||
* @param temperature_c Air temperature in °C.
|
|
||||||
*/
|
|
||||||
static float es_buck(float temperature_c);
|
|
||||||
/** Tetens equation for saturation vapor pressure in kPa.
|
|
||||||
*
|
|
||||||
* @param temperature_c Air temperature in °C.
|
|
||||||
*/
|
|
||||||
static float es_tetens(float temperature_c);
|
|
||||||
/** Wobus equation for saturation vapor pressure in kPa.
|
|
||||||
*
|
|
||||||
* @param temperature_c Air temperature in °C.
|
|
||||||
*/
|
|
||||||
static float es_wobus(float temperature_c);
|
|
||||||
|
|
||||||
/** Calculate vapor density (absolute humidity) in g/m³.
|
|
||||||
*
|
|
||||||
* @param es Saturation vapor pressure in kPa.
|
|
||||||
* @param hr Relative humidity 0 to 1.
|
|
||||||
* @param ta Absolute temperature in K.
|
|
||||||
* @param heater_duration The duration in ms that the heater should turn on for when measuring.
|
|
||||||
*/
|
|
||||||
static float vapor_density(float es, float hr, float ta);
|
|
||||||
|
|
||||||
sensor::Sensor *temperature_sensor_{nullptr};
|
|
||||||
sensor::Sensor *humidity_sensor_{nullptr};
|
|
||||||
|
|
||||||
bool next_update_{false};
|
|
||||||
|
|
||||||
float temperature_{NAN};
|
|
||||||
float humidity_{NAN};
|
|
||||||
SaturationVaporPressureEquation equation_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace absolute_humidity
|
|
||||||
} // namespace esphome
|
|
@@ -1,56 +0,0 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import sensor
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import (
|
|
||||||
CONF_EQUATION,
|
|
||||||
CONF_HUMIDITY,
|
|
||||||
CONF_TEMPERATURE,
|
|
||||||
ICON_WATER,
|
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
UNIT_GRAMS_PER_CUBIC_METER,
|
|
||||||
)
|
|
||||||
|
|
||||||
absolute_humidity_ns = cg.esphome_ns.namespace("absolute_humidity")
|
|
||||||
AbsoluteHumidityComponent = absolute_humidity_ns.class_(
|
|
||||||
"AbsoluteHumidityComponent", sensor.Sensor, cg.Component
|
|
||||||
)
|
|
||||||
|
|
||||||
SaturationVaporPressureEquation = absolute_humidity_ns.enum(
|
|
||||||
"SaturationVaporPressureEquation"
|
|
||||||
)
|
|
||||||
EQUATION = {
|
|
||||||
"BUCK": SaturationVaporPressureEquation.BUCK,
|
|
||||||
"TETENS": SaturationVaporPressureEquation.TETENS,
|
|
||||||
"WOBUS": SaturationVaporPressureEquation.WOBUS,
|
|
||||||
}
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
|
||||||
sensor.sensor_schema(
|
|
||||||
unit_of_measurement=UNIT_GRAMS_PER_CUBIC_METER,
|
|
||||||
icon=ICON_WATER,
|
|
||||||
accuracy_decimals=2,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
)
|
|
||||||
.extend(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(AbsoluteHumidityComponent),
|
|
||||||
cv.Required(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
|
|
||||||
cv.Required(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
|
|
||||||
cv.Optional(CONF_EQUATION, default="WOBUS"): cv.enum(EQUATION, upper=True),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = await sensor.new_sensor(config)
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
|
|
||||||
temperature_sensor = await cg.get_variable(config[CONF_TEMPERATURE])
|
|
||||||
cg.add(var.set_temperature_sensor(temperature_sensor))
|
|
||||||
|
|
||||||
humidity_sensor = await cg.get_variable(config[CONF_HUMIDITY])
|
|
||||||
cg.add(var.set_humidity_sensor(humidity_sensor))
|
|
||||||
|
|
||||||
cg.add(var.set_equation(config[CONF_EQUATION]))
|
|
@@ -114,14 +114,13 @@ void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
|||||||
// fully off, disable output immediately
|
// fully off, disable output immediately
|
||||||
this->gate_pin.digital_write(false);
|
this->gate_pin.digital_write(false);
|
||||||
} else {
|
} else {
|
||||||
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
|
||||||
if (this->method == DIM_METHOD_TRAILING) {
|
if (this->method == DIM_METHOD_TRAILING) {
|
||||||
this->enable_time_us = 1; // cannot be 0
|
this->enable_time_us = 1; // cannot be 0
|
||||||
// calculate time until disable in µs with integer arithmetic and take into account min_power
|
this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
||||||
this->disable_time_us = std::max((uint32_t) 10, this->value * (this->cycle_time_us - min_us) / 65535 + min_us);
|
|
||||||
} else {
|
} else {
|
||||||
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
||||||
// also take into account min_power
|
// also take into account min_power
|
||||||
|
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
||||||
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
||||||
|
|
||||||
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
from esphome import pins
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import output
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_METHOD, CONF_MIN_POWER
|
from esphome import pins
|
||||||
|
from esphome.components import output
|
||||||
|
from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD
|
||||||
|
|
||||||
CODEOWNERS = ["@glmnet"]
|
CODEOWNERS = ["@glmnet"]
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import uart
|
|
||||||
from esphome.components.light.effects import register_addressable_effect
|
|
||||||
from esphome.components.light.types import AddressableLightEffect
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import uart
|
||||||
|
from esphome.components.light.types import AddressableLightEffect
|
||||||
|
from esphome.components.light.effects import register_addressable_effect
|
||||||
from esphome.const import CONF_NAME, CONF_UART_ID
|
from esphome.const import CONF_NAME, CONF_UART_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["uart"]
|
DEPENDENCIES = ["uart"]
|
||||||
|
@@ -1,231 +1 @@
|
|||||||
from esphome import pins
|
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
|
||||||
from esphome.components.esp32.const import (
|
|
||||||
VARIANT_ESP32,
|
|
||||||
VARIANT_ESP32C2,
|
|
||||||
VARIANT_ESP32C3,
|
|
||||||
VARIANT_ESP32C6,
|
|
||||||
VARIANT_ESP32H2,
|
|
||||||
VARIANT_ESP32S2,
|
|
||||||
VARIANT_ESP32S3,
|
|
||||||
)
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266
|
|
||||||
from esphome.core import CORE
|
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
|
|
||||||
adc_ns = cg.esphome_ns.namespace("adc")
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
From the below patch versions (and 5.2+) ADC_ATTEN_DB_11 is deprecated and replaced with ADC_ATTEN_DB_12.
|
|
||||||
4.4.7
|
|
||||||
5.0.5
|
|
||||||
5.1.3
|
|
||||||
5.2+
|
|
||||||
"""
|
|
||||||
|
|
||||||
ATTENUATION_MODES = {
|
|
||||||
"0db": cg.global_ns.ADC_ATTEN_DB_0,
|
|
||||||
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
|
|
||||||
"6db": cg.global_ns.ADC_ATTEN_DB_6,
|
|
||||||
"11db": adc_ns.ADC_ATTEN_DB_12_COMPAT,
|
|
||||||
"12db": adc_ns.ADC_ATTEN_DB_12_COMPAT,
|
|
||||||
"auto": "auto",
|
|
||||||
}
|
|
||||||
|
|
||||||
sampling_mode = adc_ns.enum("SamplingMode", is_class=True)
|
|
||||||
|
|
||||||
SAMPLING_MODES = {
|
|
||||||
"avg": sampling_mode.AVG,
|
|
||||||
"min": sampling_mode.MIN,
|
|
||||||
"max": sampling_mode.MAX,
|
|
||||||
}
|
|
||||||
|
|
||||||
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
|
|
||||||
adc2_channel_t = cg.global_ns.enum("adc2_channel_t")
|
|
||||||
|
|
||||||
# pin to adc1 channel mapping
|
|
||||||
# https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
|
|
||||||
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32: {
|
|
||||||
36: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
37: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
38: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
39: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
32: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
33: adc1_channel_t.ADC1_CHANNEL_5,
|
|
||||||
34: adc1_channel_t.ADC1_CHANNEL_6,
|
|
||||||
35: adc1_channel_t.ADC1_CHANNEL_7,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32C2: {
|
|
||||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32C3: {
|
|
||||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32C6: {
|
|
||||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
5: adc1_channel_t.ADC1_CHANNEL_5,
|
|
||||||
6: adc1_channel_t.ADC1_CHANNEL_6,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32H2: {
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
5: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32S2: {
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
5: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
6: adc1_channel_t.ADC1_CHANNEL_5,
|
|
||||||
7: adc1_channel_t.ADC1_CHANNEL_6,
|
|
||||||
8: adc1_channel_t.ADC1_CHANNEL_7,
|
|
||||||
9: adc1_channel_t.ADC1_CHANNEL_8,
|
|
||||||
10: adc1_channel_t.ADC1_CHANNEL_9,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32S3: {
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
5: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
6: adc1_channel_t.ADC1_CHANNEL_5,
|
|
||||||
7: adc1_channel_t.ADC1_CHANNEL_6,
|
|
||||||
8: adc1_channel_t.ADC1_CHANNEL_7,
|
|
||||||
9: adc1_channel_t.ADC1_CHANNEL_8,
|
|
||||||
10: adc1_channel_t.ADC1_CHANNEL_9,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# pin to adc2 channel mapping
|
|
||||||
# https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
|
|
||||||
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32: {
|
|
||||||
4: adc2_channel_t.ADC2_CHANNEL_0,
|
|
||||||
0: adc2_channel_t.ADC2_CHANNEL_1,
|
|
||||||
2: adc2_channel_t.ADC2_CHANNEL_2,
|
|
||||||
15: adc2_channel_t.ADC2_CHANNEL_3,
|
|
||||||
13: adc2_channel_t.ADC2_CHANNEL_4,
|
|
||||||
12: adc2_channel_t.ADC2_CHANNEL_5,
|
|
||||||
14: adc2_channel_t.ADC2_CHANNEL_6,
|
|
||||||
27: adc2_channel_t.ADC2_CHANNEL_7,
|
|
||||||
25: adc2_channel_t.ADC2_CHANNEL_8,
|
|
||||||
26: adc2_channel_t.ADC2_CHANNEL_9,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32C2: {
|
|
||||||
5: adc2_channel_t.ADC2_CHANNEL_0,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32C3: {
|
|
||||||
5: adc2_channel_t.ADC2_CHANNEL_0,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32C6: {}, # no ADC2
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32H2: {}, # no ADC2
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32S2: {
|
|
||||||
11: adc2_channel_t.ADC2_CHANNEL_0,
|
|
||||||
12: adc2_channel_t.ADC2_CHANNEL_1,
|
|
||||||
13: adc2_channel_t.ADC2_CHANNEL_2,
|
|
||||||
14: adc2_channel_t.ADC2_CHANNEL_3,
|
|
||||||
15: adc2_channel_t.ADC2_CHANNEL_4,
|
|
||||||
16: adc2_channel_t.ADC2_CHANNEL_5,
|
|
||||||
17: adc2_channel_t.ADC2_CHANNEL_6,
|
|
||||||
18: adc2_channel_t.ADC2_CHANNEL_7,
|
|
||||||
19: adc2_channel_t.ADC2_CHANNEL_8,
|
|
||||||
20: adc2_channel_t.ADC2_CHANNEL_9,
|
|
||||||
},
|
|
||||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
|
|
||||||
VARIANT_ESP32S3: {
|
|
||||||
11: adc2_channel_t.ADC2_CHANNEL_0,
|
|
||||||
12: adc2_channel_t.ADC2_CHANNEL_1,
|
|
||||||
13: adc2_channel_t.ADC2_CHANNEL_2,
|
|
||||||
14: adc2_channel_t.ADC2_CHANNEL_3,
|
|
||||||
15: adc2_channel_t.ADC2_CHANNEL_4,
|
|
||||||
16: adc2_channel_t.ADC2_CHANNEL_5,
|
|
||||||
17: adc2_channel_t.ADC2_CHANNEL_6,
|
|
||||||
18: adc2_channel_t.ADC2_CHANNEL_7,
|
|
||||||
19: adc2_channel_t.ADC2_CHANNEL_8,
|
|
||||||
20: adc2_channel_t.ADC2_CHANNEL_9,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def validate_adc_pin(value):
|
|
||||||
if str(value).upper() == "VCC":
|
|
||||||
if CORE.is_rp2040:
|
|
||||||
return pins.internal_gpio_input_pin_schema(29)
|
|
||||||
return cv.only_on([PLATFORM_ESP8266])("VCC")
|
|
||||||
|
|
||||||
if str(value).upper() == "TEMPERATURE":
|
|
||||||
return cv.only_on_rp2040("TEMPERATURE")
|
|
||||||
|
|
||||||
if CORE.is_esp32:
|
|
||||||
conf = pins.internal_gpio_input_pin_schema(value)
|
|
||||||
value = conf[CONF_NUMBER]
|
|
||||||
variant = get_esp32_variant()
|
|
||||||
if (
|
|
||||||
variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL
|
|
||||||
and variant not in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL
|
|
||||||
):
|
|
||||||
raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
|
|
||||||
|
|
||||||
if (
|
|
||||||
value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]
|
|
||||||
and value not in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
|
|
||||||
):
|
|
||||||
raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
|
|
||||||
|
|
||||||
return conf
|
|
||||||
|
|
||||||
if CORE.is_esp8266:
|
|
||||||
conf = pins.gpio_pin_schema(
|
|
||||||
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
|
|
||||||
)(value)
|
|
||||||
|
|
||||||
if conf[CONF_NUMBER] != 17: # A0
|
|
||||||
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC")
|
|
||||||
return conf
|
|
||||||
|
|
||||||
if CORE.is_rp2040:
|
|
||||||
conf = pins.internal_gpio_input_pin_schema(value)
|
|
||||||
number = conf[CONF_NUMBER]
|
|
||||||
if number not in (26, 27, 28, 29):
|
|
||||||
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC")
|
|
||||||
return conf
|
|
||||||
|
|
||||||
if CORE.is_libretiny:
|
|
||||||
return pins.gpio_pin_schema(
|
|
||||||
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
|
|
||||||
)(value)
|
|
||||||
|
|
||||||
raise NotImplementedError
|
|
||||||
|
231
esphome/components/adc/adc_sensor.cpp
Normal file
231
esphome/components/adc/adc_sensor.cpp
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
#include "adc_sensor.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
#include <Esp.h>
|
||||||
|
ADC_MODE(ADC_VCC)
|
||||||
|
#else
|
||||||
|
#include <Arduino.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_RP2040
|
||||||
|
#include <hardware/adc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adc {
|
||||||
|
|
||||||
|
static const char *const TAG = "adc";
|
||||||
|
|
||||||
|
// 13bit for S2, and 12bit for all other esp32 variants
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
||||||
|
|
||||||
|
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
|
||||||
|
#if USE_ESP32_VARIANT_ESP32S2
|
||||||
|
static const int SOC_ADC_RTC_MAX_BITWIDTH = 13;
|
||||||
|
#else
|
||||||
|
static const int SOC_ADC_RTC_MAX_BITWIDTH = 12;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
|
||||||
|
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_RP2040
|
||||||
|
extern "C"
|
||||||
|
#endif
|
||||||
|
void
|
||||||
|
ADCSensor::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
||||||
|
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
|
||||||
|
pin_->setup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
||||||
|
if (!autorange_) {
|
||||||
|
adc1_config_channel_atten(channel_, attenuation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load characteristics for each attenuation
|
||||||
|
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
|
||||||
|
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
|
||||||
|
1100, // default vref
|
||||||
|
&cal_characteristics_[i]);
|
||||||
|
switch (cal_value) {
|
||||||
|
case ESP_ADC_CAL_VAL_EFUSE_VREF:
|
||||||
|
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
|
||||||
|
break;
|
||||||
|
case ESP_ADC_CAL_VAL_EFUSE_TP:
|
||||||
|
ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
|
||||||
|
break;
|
||||||
|
case ESP_ADC_CAL_VAL_DEFAULT_VREF:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
||||||
|
|
||||||
|
#ifdef USE_RP2040
|
||||||
|
static bool initialized = false;
|
||||||
|
if (!initialized) {
|
||||||
|
adc_init();
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ESP_LOGCONFIG(TAG, "ADC '%s' setup finished!", this->get_name().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADCSensor::dump_config() {
|
||||||
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||||
|
#else
|
||||||
|
LOG_PIN(" Pin: ", pin_);
|
||||||
|
#endif
|
||||||
|
#endif // USE_ESP8266
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
LOG_PIN(" Pin: ", pin_);
|
||||||
|
if (autorange_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
||||||
|
} else {
|
||||||
|
switch (this->attenuation_) {
|
||||||
|
case ADC_ATTEN_DB_0:
|
||||||
|
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
|
||||||
|
break;
|
||||||
|
case ADC_ATTEN_DB_2_5:
|
||||||
|
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
|
||||||
|
break;
|
||||||
|
case ADC_ATTEN_DB_6:
|
||||||
|
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
|
||||||
|
break;
|
||||||
|
case ADC_ATTEN_DB_11:
|
||||||
|
ESP_LOGCONFIG(TAG, " Attenuation: 11db");
|
||||||
|
break;
|
||||||
|
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // USE_ESP32
|
||||||
|
#ifdef USE_RP2040
|
||||||
|
if (this->is_temperature_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Pin: Temperature");
|
||||||
|
} else {
|
||||||
|
LOG_PIN(" Pin: ", pin_);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
void ADCSensor::update() {
|
||||||
|
float value_v = this->sample();
|
||||||
|
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
|
||||||
|
this->publish_state(value_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
float ADCSensor::sample() {
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
||||||
|
#else
|
||||||
|
int raw = analogRead(this->pin_->get_pin()); // NOLINT
|
||||||
|
#endif
|
||||||
|
if (output_raw_) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
return raw / 1024.0f;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
float ADCSensor::sample() {
|
||||||
|
if (!autorange_) {
|
||||||
|
int raw = adc1_get_raw(channel_);
|
||||||
|
if (raw == -1) {
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
if (output_raw_) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
|
||||||
|
return mv / 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int raw11, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
|
||||||
|
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
|
||||||
|
raw11 = adc1_get_raw(channel_);
|
||||||
|
if (raw11 < ADC_MAX) {
|
||||||
|
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
|
||||||
|
raw6 = adc1_get_raw(channel_);
|
||||||
|
if (raw6 < ADC_MAX) {
|
||||||
|
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
|
||||||
|
raw2 = adc1_get_raw(channel_);
|
||||||
|
if (raw2 < ADC_MAX) {
|
||||||
|
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
|
||||||
|
raw0 = adc1_get_raw(channel_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) {
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
|
||||||
|
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
|
||||||
|
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
|
||||||
|
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
|
||||||
|
|
||||||
|
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
|
||||||
|
uint32_t c11 = std::min(raw11, ADC_HALF);
|
||||||
|
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
|
||||||
|
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
|
||||||
|
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
|
||||||
|
// max theoretical csum value is 4096*4 = 16384
|
||||||
|
uint32_t csum = c11 + c6 + c2 + c0;
|
||||||
|
|
||||||
|
// each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
|
||||||
|
uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
|
||||||
|
return mv_scaled / (float) (csum * 1000U);
|
||||||
|
}
|
||||||
|
#endif // USE_ESP32
|
||||||
|
|
||||||
|
#ifdef USE_RP2040
|
||||||
|
float ADCSensor::sample() {
|
||||||
|
if (this->is_temperature_) {
|
||||||
|
adc_set_temp_sensor_enabled(true);
|
||||||
|
delay(1);
|
||||||
|
adc_select_input(4);
|
||||||
|
} else {
|
||||||
|
uint8_t pin = this->pin_->get_pin();
|
||||||
|
adc_gpio_init(pin);
|
||||||
|
adc_select_input(pin - 26);
|
||||||
|
}
|
||||||
|
|
||||||
|
int raw = adc_read();
|
||||||
|
if (this->is_temperature_) {
|
||||||
|
adc_set_temp_sensor_enabled(false);
|
||||||
|
}
|
||||||
|
if (output_raw_) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
return raw * 3.3f / 4096.0f;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace adc
|
||||||
|
} // namespace esphome
|
@@ -1,106 +1,61 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include <esp_adc_cal.h>
|
|
||||||
#include "driver/adc.h"
|
#include "driver/adc.h"
|
||||||
#endif // USE_ESP32
|
#include <esp_adc_cal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace adc {
|
namespace adc {
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
// clang-format off
|
|
||||||
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 7)) || \
|
|
||||||
(ESP_IDF_VERSION_MAJOR == 5 && \
|
|
||||||
((ESP_IDF_VERSION_MINOR == 0 && ESP_IDF_VERSION_PATCH >= 5) || \
|
|
||||||
(ESP_IDF_VERSION_MINOR == 1 && ESP_IDF_VERSION_PATCH >= 3) || \
|
|
||||||
(ESP_IDF_VERSION_MINOR >= 2)) \
|
|
||||||
)
|
|
||||||
// clang-format on
|
|
||||||
static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_12;
|
|
||||||
#else
|
|
||||||
static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_11;
|
|
||||||
#endif
|
|
||||||
#endif // USE_ESP32
|
|
||||||
|
|
||||||
enum class SamplingMode : uint8_t { AVG = 0, MIN = 1, MAX = 2 };
|
|
||||||
const LogString *sampling_mode_to_str(SamplingMode mode);
|
|
||||||
|
|
||||||
class Aggregator {
|
|
||||||
public:
|
|
||||||
void add_sample(uint32_t value);
|
|
||||||
uint32_t aggregate();
|
|
||||||
Aggregator(SamplingMode mode);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
SamplingMode mode_{SamplingMode::AVG};
|
|
||||||
uint32_t aggr_{0};
|
|
||||||
uint32_t samples_{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
||||||
public:
|
public:
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
/// Set the attenuation for this pin. Only available on the ESP32.
|
/// Set the attenuation for this pin. Only available on the ESP32.
|
||||||
void set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
|
||||||
void set_channel1(adc1_channel_t channel) {
|
void set_channel(adc1_channel_t channel) { channel_ = channel; }
|
||||||
this->channel1_ = channel;
|
void set_autorange(bool autorange) { autorange_ = autorange; }
|
||||||
this->channel2_ = ADC2_CHANNEL_MAX;
|
#endif
|
||||||
}
|
|
||||||
void set_channel2(adc2_channel_t channel) {
|
|
||||||
this->channel2_ = channel;
|
|
||||||
this->channel1_ = ADC1_CHANNEL_MAX;
|
|
||||||
}
|
|
||||||
void set_autorange(bool autorange) { this->autorange_ = autorange; }
|
|
||||||
#endif // USE_ESP32
|
|
||||||
|
|
||||||
/// Update ADC values
|
/// Update adc values.
|
||||||
void update() override;
|
void update() override;
|
||||||
/// Setup ADC
|
/// Setup ADc
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
/// `HARDWARE_LATE` setup priority
|
/// `HARDWARE_LATE` setup priority.
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
||||||
void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; }
|
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
|
||||||
void set_sample_count(uint8_t sample_count);
|
|
||||||
void set_sampling_mode(SamplingMode sampling_mode);
|
|
||||||
float sample() override;
|
float sample() override;
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
std::string unique_id() override;
|
std::string unique_id() override;
|
||||||
#endif // USE_ESP8266
|
#endif
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
void set_is_temperature() { this->is_temperature_ = true; }
|
void set_is_temperature() { is_temperature_ = true; }
|
||||||
#endif // USE_RP2040
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
InternalGPIOPin *pin_;
|
InternalGPIOPin *pin_;
|
||||||
bool output_raw_{false};
|
bool output_raw_{false};
|
||||||
uint8_t sample_count_{1};
|
|
||||||
SamplingMode sampling_mode_{SamplingMode::AVG};
|
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
bool is_temperature_{false};
|
bool is_temperature_{false};
|
||||||
#endif // USE_RP2040
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||||
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
|
adc1_channel_t channel_{};
|
||||||
adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
|
|
||||||
bool autorange_{false};
|
bool autorange_{false};
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
esp_adc_cal_characteristics_t cal_characteristics_[(int) ADC_ATTEN_MAX] = {};
|
||||||
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
|
#endif
|
||||||
#else
|
|
||||||
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
|
|
||||||
#endif // ESP_IDF_VERSION_MAJOR
|
|
||||||
#endif // USE_ESP32
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace adc
|
} // namespace adc
|
||||||
|
@@ -1,79 +0,0 @@
|
|||||||
#include "adc_sensor.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace adc {
|
|
||||||
|
|
||||||
static const char *const TAG = "adc.common";
|
|
||||||
|
|
||||||
const LogString *sampling_mode_to_str(SamplingMode mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case SamplingMode::AVG:
|
|
||||||
return LOG_STR("average");
|
|
||||||
case SamplingMode::MIN:
|
|
||||||
return LOG_STR("minimum");
|
|
||||||
case SamplingMode::MAX:
|
|
||||||
return LOG_STR("maximum");
|
|
||||||
}
|
|
||||||
return LOG_STR("unknown");
|
|
||||||
}
|
|
||||||
|
|
||||||
Aggregator::Aggregator(SamplingMode mode) {
|
|
||||||
this->mode_ = mode;
|
|
||||||
// set to max uint if mode is "min"
|
|
||||||
if (mode == SamplingMode::MIN) {
|
|
||||||
this->aggr_ = UINT32_MAX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Aggregator::add_sample(uint32_t value) {
|
|
||||||
this->samples_ += 1;
|
|
||||||
|
|
||||||
switch (this->mode_) {
|
|
||||||
case SamplingMode::AVG:
|
|
||||||
this->aggr_ += value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SamplingMode::MIN:
|
|
||||||
if (value < this->aggr_) {
|
|
||||||
this->aggr_ = value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SamplingMode::MAX:
|
|
||||||
if (value > this->aggr_) {
|
|
||||||
this->aggr_ = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t Aggregator::aggregate() {
|
|
||||||
if (this->mode_ == SamplingMode::AVG) {
|
|
||||||
if (this->samples_ == 0) {
|
|
||||||
return this->aggr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (this->aggr_ + (this->samples_ >> 1)) / this->samples_; // NOLINT(clang-analyzer-core.DivideZero)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->aggr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ADCSensor::update() {
|
|
||||||
float value_v = this->sample();
|
|
||||||
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
|
|
||||||
this->publish_state(value_v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ADCSensor::set_sample_count(uint8_t sample_count) {
|
|
||||||
if (sample_count != 0) {
|
|
||||||
this->sample_count_ = sample_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
|
|
||||||
|
|
||||||
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
} // namespace adc
|
|
||||||
} // namespace esphome
|
|
@@ -1,166 +0,0 @@
|
|||||||
#ifdef USE_ESP32
|
|
||||||
|
|
||||||
#include "adc_sensor.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace adc {
|
|
||||||
|
|
||||||
static const char *const TAG = "adc.esp32";
|
|
||||||
|
|
||||||
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
|
||||||
|
|
||||||
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
|
|
||||||
#if USE_ESP32_VARIANT_ESP32S2
|
|
||||||
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
|
|
||||||
#else
|
|
||||||
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
|
|
||||||
#endif // USE_ESP32_VARIANT_ESP32S2
|
|
||||||
#endif // SOC_ADC_RTC_MAX_BITWIDTH
|
|
||||||
|
|
||||||
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
|
|
||||||
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;
|
|
||||||
|
|
||||||
void ADCSensor::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
|
||||||
|
|
||||||
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
|
||||||
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
|
||||||
if (!this->autorange_) {
|
|
||||||
adc1_config_channel_atten(this->channel1_, this->attenuation_);
|
|
||||||
}
|
|
||||||
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
|
|
||||||
if (!this->autorange_) {
|
|
||||||
adc2_config_channel_atten(this->channel2_, this->attenuation_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
|
|
||||||
auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
|
|
||||||
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
|
|
||||||
1100, // default vref
|
|
||||||
&this->cal_characteristics_[i]);
|
|
||||||
switch (cal_value) {
|
|
||||||
case ESP_ADC_CAL_VAL_EFUSE_VREF:
|
|
||||||
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
|
|
||||||
break;
|
|
||||||
case ESP_ADC_CAL_VAL_EFUSE_TP:
|
|
||||||
ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
|
|
||||||
break;
|
|
||||||
case ESP_ADC_CAL_VAL_DEFAULT_VREF:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ADCSensor::dump_config() {
|
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
|
||||||
if (this->autorange_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
|
||||||
} else {
|
|
||||||
switch (this->attenuation_) {
|
|
||||||
case ADC_ATTEN_DB_0:
|
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_2_5:
|
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_6:
|
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_12_COMPAT:
|
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 12db");
|
|
||||||
break;
|
|
||||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
|
||||||
LOG_UPDATE_INTERVAL(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ADCSensor::sample() {
|
|
||||||
if (!this->autorange_) {
|
|
||||||
auto aggr = Aggregator(this->sampling_mode_);
|
|
||||||
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
int raw = -1;
|
|
||||||
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
|
||||||
raw = adc1_get_raw(this->channel1_);
|
|
||||||
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
|
|
||||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
|
|
||||||
}
|
|
||||||
if (raw == -1) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
aggr.add_sample(raw);
|
|
||||||
}
|
|
||||||
if (this->output_raw_) {
|
|
||||||
return aggr.aggregate();
|
|
||||||
}
|
|
||||||
uint32_t mv =
|
|
||||||
esp_adc_cal_raw_to_voltage(aggr.aggregate(), &this->cal_characteristics_[(int32_t) this->attenuation_]);
|
|
||||||
return mv / 1000.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
int raw12 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
|
|
||||||
|
|
||||||
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
|
||||||
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_12_COMPAT);
|
|
||||||
raw12 = adc1_get_raw(this->channel1_);
|
|
||||||
if (raw12 < ADC_MAX) {
|
|
||||||
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_6);
|
|
||||||
raw6 = adc1_get_raw(this->channel1_);
|
|
||||||
if (raw6 < ADC_MAX) {
|
|
||||||
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_2_5);
|
|
||||||
raw2 = adc1_get_raw(this->channel1_);
|
|
||||||
if (raw2 < ADC_MAX) {
|
|
||||||
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_0);
|
|
||||||
raw0 = adc1_get_raw(this->channel1_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
|
|
||||||
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_12_COMPAT);
|
|
||||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12);
|
|
||||||
if (raw12 < ADC_MAX) {
|
|
||||||
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_6);
|
|
||||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
|
|
||||||
if (raw6 < ADC_MAX) {
|
|
||||||
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_2_5);
|
|
||||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
|
|
||||||
if (raw2 < ADC_MAX) {
|
|
||||||
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_0);
|
|
||||||
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw12 == -1) {
|
|
||||||
return NAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]);
|
|
||||||
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
|
|
||||||
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
|
|
||||||
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
|
|
||||||
|
|
||||||
uint32_t c12 = std::min(raw12, ADC_HALF);
|
|
||||||
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
|
|
||||||
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
|
|
||||||
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
|
|
||||||
uint32_t csum = c12 + c6 + c2 + c0;
|
|
||||||
|
|
||||||
uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
|
|
||||||
return mv_scaled / (float) (csum * 1000U);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace adc
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_ESP32
|
|
@@ -1,62 +0,0 @@
|
|||||||
#ifdef USE_ESP8266
|
|
||||||
|
|
||||||
#include "adc_sensor.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
#include <Esp.h>
|
|
||||||
ADC_MODE(ADC_VCC)
|
|
||||||
#else
|
|
||||||
#include <Arduino.h>
|
|
||||||
#endif // USE_ADC_SENSOR_VCC
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace adc {
|
|
||||||
|
|
||||||
static const char *const TAG = "adc.esp8266";
|
|
||||||
|
|
||||||
void ADCSensor::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
|
||||||
#ifndef USE_ADC_SENSOR_VCC
|
|
||||||
this->pin_->setup();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ADCSensor::dump_config() {
|
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
|
||||||
#else
|
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
|
||||||
#endif // USE_ADC_SENSOR_VCC
|
|
||||||
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
|
||||||
LOG_UPDATE_INTERVAL(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ADCSensor::sample() {
|
|
||||||
auto aggr = Aggregator(this->sampling_mode_);
|
|
||||||
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
uint32_t raw = 0;
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
|
||||||
#else
|
|
||||||
raw = analogRead(this->pin_->get_pin()); // NOLINT
|
|
||||||
#endif // USE_ADC_SENSOR_VCC
|
|
||||||
aggr.add_sample(raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->output_raw_) {
|
|
||||||
return aggr.aggregate();
|
|
||||||
}
|
|
||||||
return aggr.aggregate() / 1024.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
|
||||||
|
|
||||||
} // namespace adc
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_ESP8266
|
|
@@ -1,53 +0,0 @@
|
|||||||
#ifdef USE_LIBRETINY
|
|
||||||
|
|
||||||
#include "adc_sensor.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace adc {
|
|
||||||
|
|
||||||
static const char *const TAG = "adc.libretiny";
|
|
||||||
|
|
||||||
void ADCSensor::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
|
||||||
#ifndef USE_ADC_SENSOR_VCC
|
|
||||||
this->pin_->setup();
|
|
||||||
#endif // !USE_ADC_SENSOR_VCC
|
|
||||||
}
|
|
||||||
|
|
||||||
void ADCSensor::dump_config() {
|
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
|
||||||
#else // USE_ADC_SENSOR_VCC
|
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
|
||||||
#endif // USE_ADC_SENSOR_VCC
|
|
||||||
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
|
||||||
LOG_UPDATE_INTERVAL(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ADCSensor::sample() {
|
|
||||||
uint32_t raw = 0;
|
|
||||||
auto aggr = Aggregator(this->sampling_mode_);
|
|
||||||
|
|
||||||
if (this->output_raw_) {
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
raw = analogRead(this->pin_->get_pin()); // NOLINT
|
|
||||||
aggr.add_sample(raw);
|
|
||||||
}
|
|
||||||
return aggr.aggregate();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
raw = analogReadVoltage(this->pin_->get_pin()); // NOLINT
|
|
||||||
aggr.add_sample(raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
return aggr.aggregate() / 1000.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace adc
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_LIBRETINY
|
|
@@ -1,96 +0,0 @@
|
|||||||
#ifdef USE_RP2040
|
|
||||||
|
|
||||||
#include "adc_sensor.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
#ifdef CYW43_USES_VSYS_PIN
|
|
||||||
#include "pico/cyw43_arch.h"
|
|
||||||
#endif // CYW43_USES_VSYS_PIN
|
|
||||||
#include <hardware/adc.h>
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace adc {
|
|
||||||
|
|
||||||
static const char *const TAG = "adc.rp2040";
|
|
||||||
|
|
||||||
void ADCSensor::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
|
||||||
static bool initialized = false;
|
|
||||||
if (!initialized) {
|
|
||||||
adc_init();
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ADCSensor::dump_config() {
|
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
|
||||||
if (this->is_temperature_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Pin: Temperature");
|
|
||||||
} else {
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
|
||||||
#else
|
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
|
||||||
#endif // USE_ADC_SENSOR_VCC
|
|
||||||
}
|
|
||||||
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
|
||||||
LOG_UPDATE_INTERVAL(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ADCSensor::sample() {
|
|
||||||
uint32_t raw = 0;
|
|
||||||
auto aggr = Aggregator(this->sampling_mode_);
|
|
||||||
|
|
||||||
if (this->is_temperature_) {
|
|
||||||
adc_set_temp_sensor_enabled(true);
|
|
||||||
delay(1);
|
|
||||||
adc_select_input(4);
|
|
||||||
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
raw = adc_read();
|
|
||||||
aggr.add_sample(raw);
|
|
||||||
}
|
|
||||||
adc_set_temp_sensor_enabled(false);
|
|
||||||
if (this->output_raw_) {
|
|
||||||
return aggr.aggregate();
|
|
||||||
}
|
|
||||||
return aggr.aggregate() * 3.3f / 4096.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t pin = this->pin_->get_pin();
|
|
||||||
#ifdef CYW43_USES_VSYS_PIN
|
|
||||||
if (pin == PICO_VSYS_PIN) {
|
|
||||||
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
|
|
||||||
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
|
|
||||||
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
|
|
||||||
// VSYS ADC both share GPIO29
|
|
||||||
cyw43_thread_enter();
|
|
||||||
}
|
|
||||||
#endif // CYW43_USES_VSYS_PIN
|
|
||||||
|
|
||||||
adc_gpio_init(pin);
|
|
||||||
adc_select_input(pin - 26);
|
|
||||||
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
raw = adc_read();
|
|
||||||
aggr.add_sample(raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CYW43_USES_VSYS_PIN
|
|
||||||
if (pin == PICO_VSYS_PIN) {
|
|
||||||
cyw43_thread_exit();
|
|
||||||
}
|
|
||||||
#endif // CYW43_USES_VSYS_PIN
|
|
||||||
|
|
||||||
if (this->output_raw_) {
|
|
||||||
return aggr.aggregate();
|
|
||||||
}
|
|
||||||
float coeff = pin == PICO_VSYS_PIN ? 3.0f : 1.0f;
|
|
||||||
return aggr.aggregate() * 3.3f / 4096.0f * coeff;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace adc
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_RP2040
|
|
@@ -1,77 +1,141 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import sensor, voltage_sampler
|
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome import pins
|
||||||
|
from esphome.components import sensor, voltage_sampler
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ATTENUATION,
|
CONF_ATTENUATION,
|
||||||
|
CONF_RAW,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_INPUT,
|
||||||
CONF_NUMBER,
|
CONF_NUMBER,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
CONF_RAW,
|
|
||||||
CONF_WIFI,
|
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
import esphome.final_validate as fv
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
from esphome.components.esp32.const import (
|
||||||
from . import (
|
VARIANT_ESP32,
|
||||||
ATTENUATION_MODES,
|
VARIANT_ESP32C3,
|
||||||
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
|
VARIANT_ESP32H2,
|
||||||
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
|
VARIANT_ESP32S2,
|
||||||
SAMPLING_MODES,
|
VARIANT_ESP32S3,
|
||||||
adc_ns,
|
|
||||||
validate_adc_pin,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
AUTO_LOAD = ["voltage_sampler"]
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
|
|
||||||
CONF_SAMPLES = "samples"
|
ATTENUATION_MODES = {
|
||||||
CONF_SAMPLING_MODE = "sampling_mode"
|
"0db": cg.global_ns.ADC_ATTEN_DB_0,
|
||||||
|
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
|
||||||
|
"6db": cg.global_ns.ADC_ATTEN_DB_6,
|
||||||
|
"11db": cg.global_ns.ADC_ATTEN_DB_11,
|
||||||
|
"auto": "auto",
|
||||||
|
}
|
||||||
|
|
||||||
|
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
|
||||||
|
|
||||||
|
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
|
||||||
|
# pin to adc1 channel mapping
|
||||||
|
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
||||||
|
VARIANT_ESP32: {
|
||||||
|
36: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
37: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
38: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
39: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
32: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
33: adc1_channel_t.ADC1_CHANNEL_5,
|
||||||
|
34: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
|
35: adc1_channel_t.ADC1_CHANNEL_7,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32S2: {
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
5: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
6: adc1_channel_t.ADC1_CHANNEL_5,
|
||||||
|
7: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
|
8: adc1_channel_t.ADC1_CHANNEL_7,
|
||||||
|
9: adc1_channel_t.ADC1_CHANNEL_8,
|
||||||
|
10: adc1_channel_t.ADC1_CHANNEL_9,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32S3: {
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
5: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
6: adc1_channel_t.ADC1_CHANNEL_5,
|
||||||
|
7: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
|
8: adc1_channel_t.ADC1_CHANNEL_7,
|
||||||
|
9: adc1_channel_t.ADC1_CHANNEL_8,
|
||||||
|
10: adc1_channel_t.ADC1_CHANNEL_9,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32C3: {
|
||||||
|
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32H2: {
|
||||||
|
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
_attenuation = cv.enum(ATTENUATION_MODES, lower=True)
|
def validate_adc_pin(value):
|
||||||
_sampling_mode = cv.enum(SAMPLING_MODES, lower=True)
|
if str(value).upper() == "VCC":
|
||||||
|
return cv.only_on_esp8266("VCC")
|
||||||
|
|
||||||
|
if str(value).upper() == "TEMPERATURE":
|
||||||
|
return cv.only_on_rp2040("TEMPERATURE")
|
||||||
|
|
||||||
|
if CORE.is_esp32:
|
||||||
|
value = pins.internal_gpio_input_pin_number(value)
|
||||||
|
variant = get_esp32_variant()
|
||||||
|
if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
|
||||||
|
raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
|
||||||
|
|
||||||
|
if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
|
||||||
|
raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
|
||||||
|
return pins.internal_gpio_input_pin_schema(value)
|
||||||
|
|
||||||
|
if CORE.is_esp8266:
|
||||||
|
from esphome.components.esp8266.gpio import CONF_ANALOG
|
||||||
|
|
||||||
|
value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
|
||||||
|
if value != 17: # A0
|
||||||
|
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
|
||||||
|
return pins.gpio_pin_schema(
|
||||||
|
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
|
||||||
|
)(value)
|
||||||
|
|
||||||
|
if CORE.is_rp2040:
|
||||||
|
value = pins.internal_gpio_input_pin_number(value)
|
||||||
|
if value not in (26, 27, 28, 29):
|
||||||
|
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC.")
|
||||||
|
return pins.internal_gpio_input_pin_schema(value)
|
||||||
|
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
def validate_config(config):
|
def validate_config(config):
|
||||||
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
|
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
|
||||||
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set")
|
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set.")
|
||||||
|
|
||||||
if config.get(CONF_ATTENUATION, None) == "auto" and config.get(CONF_SAMPLES, 1) > 1:
|
|
||||||
raise cv.Invalid(
|
|
||||||
"Automatic attenuation cannot be used when multisampling is set"
|
|
||||||
)
|
|
||||||
if config.get(CONF_ATTENUATION) == "11db":
|
|
||||||
_LOGGER.warning(
|
|
||||||
"`attenuation: 11db` is deprecated, use `attenuation: 12db` instead"
|
|
||||||
)
|
|
||||||
# Alter value here so `config` command prints the recommended change
|
|
||||||
config[CONF_ATTENUATION] = _attenuation("12db")
|
|
||||||
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
def final_validate_config(config):
|
|
||||||
if CORE.is_esp32:
|
|
||||||
variant = get_esp32_variant()
|
|
||||||
if (
|
|
||||||
CONF_WIFI in fv.full_config.get()
|
|
||||||
and config[CONF_PIN][CONF_NUMBER]
|
|
||||||
in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
|
|
||||||
):
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"{variant} doesn't support ADC on this pin when Wi-Fi is configured"
|
|
||||||
)
|
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
adc_ns = cg.esphome_ns.namespace("adc")
|
||||||
ADCSensor = adc_ns.class_(
|
ADCSensor = adc_ns.class_(
|
||||||
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
||||||
)
|
)
|
||||||
@@ -89,18 +153,14 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Required(CONF_PIN): validate_adc_pin,
|
cv.Required(CONF_PIN): validate_adc_pin,
|
||||||
cv.Optional(CONF_RAW, default=False): cv.boolean,
|
cv.Optional(CONF_RAW, default=False): cv.boolean,
|
||||||
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
|
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
|
||||||
cv.only_on_esp32, _attenuation
|
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255),
|
|
||||||
cv.Optional(CONF_SAMPLING_MODE, default="avg"): _sampling_mode,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("60s")),
|
.extend(cv.polling_component_schema("60s")),
|
||||||
validate_config,
|
validate_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = final_validate_config
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
@@ -115,28 +175,17 @@ async def to_code(config):
|
|||||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
cg.add(var.set_pin(pin))
|
cg.add(var.set_pin(pin))
|
||||||
|
|
||||||
cg.add(var.set_output_raw(config[CONF_RAW]))
|
if CONF_RAW in config:
|
||||||
cg.add(var.set_sample_count(config[CONF_SAMPLES]))
|
cg.add(var.set_output_raw(config[CONF_RAW]))
|
||||||
cg.add(var.set_sampling_mode(config[CONF_SAMPLING_MODE]))
|
|
||||||
|
|
||||||
if attenuation := config.get(CONF_ATTENUATION):
|
if CONF_ATTENUATION in config:
|
||||||
if attenuation == "auto":
|
if config[CONF_ATTENUATION] == "auto":
|
||||||
cg.add(var.set_autorange(cg.global_ns.true))
|
cg.add(var.set_autorange(cg.global_ns.true))
|
||||||
else:
|
else:
|
||||||
cg.add(var.set_attenuation(attenuation))
|
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
|
||||||
|
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
variant = get_esp32_variant()
|
variant = get_esp32_variant()
|
||||||
pin_num = config[CONF_PIN][CONF_NUMBER]
|
pin_num = config[CONF_PIN][CONF_NUMBER]
|
||||||
if (
|
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
|
||||||
variant in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL
|
cg.add(var.set_channel(chan))
|
||||||
and pin_num in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]
|
|
||||||
):
|
|
||||||
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
|
|
||||||
cg.add(var.set_channel1(chan))
|
|
||||||
elif (
|
|
||||||
variant in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL
|
|
||||||
and pin_num in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
|
|
||||||
):
|
|
||||||
chan = ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant][pin_num]
|
|
||||||
cg.add(var.set_channel2(chan))
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user