Merge pull request #1883 from esphome/bump-1.19.0b1

1.19.0b1
This commit is contained in:
Jesse Hills 2021-06-09 19:25:57 +12:00 committed by GitHub
commit c32fec7432
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
550 changed files with 11759 additions and 4099 deletions

View file

@ -4,14 +4,35 @@ Checks: >-
-abseil-*, -abseil-*,
-android-*, -android-*,
-boost-*, -boost-*,
-bugprone-branch-clone,
-bugprone-macro-parentheses, -bugprone-macro-parentheses,
-bugprone-narrowing-conversions,
-bugprone-reserved-identifier,
-bugprone-signed-char-misuse,
-bugprone-suspicious-include,
-bugprone-too-small-loop-variable,
-bugprone-unhandled-self-assignment,
-cert-dcl37-c,
-cert-dcl50-cpp, -cert-dcl50-cpp,
-cert-dcl51-cpp,
-cert-err58-cpp, -cert-err58-cpp,
-cert-oop54-cpp,
-cert-oop57-cpp,
-cert-str34-c,
-clang-analyzer-core.CallAndMessage, -clang-analyzer-core.CallAndMessage,
-clang-analyzer-optin.*,
-clang-analyzer-osx.*, -clang-analyzer-osx.*,
-clang-analyzer-security.*, -clang-analyzer-security.*,
-clang-diagnostic-shadow-field,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-c-copy-assignment-signature, -cppcoreguidelines-c-copy-assignment-signature,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-owning-memory, -cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-constant-array-index,
@ -37,10 +58,16 @@ Checks: >-
-google-runtime-int, -google-runtime-int,
-google-runtime-references, -google-runtime-references,
-hicpp-*, -hicpp-*,
-llvm-else-after-return,
-llvm-header-guard, -llvm-header-guard,
-llvm-include-order, -llvm-include-order,
-llvm-qualified-auto,
-llvmlibc-*,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-misc-unconventional-assign-operator, -misc-unconventional-assign-operator,
-misc-unused-parameters, -misc-unused-parameters,
-modernize-avoid-c-arrays,
-modernize-deprecated-headers, -modernize-deprecated-headers,
-modernize-pass-by-value, -modernize-pass-by-value,
-modernize-pass-by-value, -modernize-pass-by-value,
@ -48,14 +75,25 @@ Checks: >-
-modernize-use-auto, -modernize-use-auto,
-modernize-use-default-member-init, -modernize-use-default-member-init,
-modernize-use-equals-default, -modernize-use-equals-default,
-modernize-use-trailing-return-type,
-mpi-*, -mpi-*,
-objc-*, -objc-*,
-performance-unnecessary-value-param, -performance-unnecessary-value-param,
-readability-braces-around-statements, -readability-braces-around-statements,
-readability-const-return-type,
-readability-convert-member-functions-to-static,
-readability-else-after-return, -readability-else-after-return,
-readability-implicit-bool-conversion, -readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-make-member-function-const,
-readability-named-parameter, -readability-named-parameter,
-readability-qualified-auto,
-readability-redundant-access-specifiers,
-readability-redundant-member-init, -readability-redundant-member-init,
-readability-redundant-string-init,
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-warnings-as-errors, -warnings-as-errors,
-zircon-* -zircon-*
WarningsAsErrors: '*' WarningsAsErrors: '*'

View file

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

View file

@ -26,7 +26,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up env variables - name: Set up env variables
run: | run: |
base_version="3.0.0" base_version="3.4.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"

View file

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-* # cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed # doesn't have to be installed
container: esphome/esphome-lint:latest container: esphome/esphome-lint:1.1
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
@ -32,7 +32,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-* # cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed # doesn't have to be installed
container: esphome/esphome-lint:latest container: esphome/esphome-lint:1.1
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy: strategy:
fail-fast: false fail-fast: false
@ -97,6 +97,7 @@ jobs:
- test2 - test2
- test3 - test3
- test4 - test4
- test5
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python - name: Set up Python
@ -126,7 +127,7 @@ jobs:
run: | run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json" echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json" echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome tests/${{ matrix.test }}.yaml compile - run: esphome compile tests/${{ matrix.test }}.yaml
pytest: pytest:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View file

@ -7,6 +7,7 @@ on:
paths: paths:
- 'docker/Dockerfile.lint' - 'docker/Dockerfile.lint'
- 'requirements.txt' - 'requirements.txt'
- 'requirements_optional.txt'
- 'requirements_test.txt' - 'requirements_test.txt'
- 'platformio.ini' - 'platformio.ini'
- '.github/workflows/docker-lint-build.yml' - '.github/workflows/docker-lint-build.yml'
@ -17,6 +18,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set TAG
run: |
echo "TAG=1.1" >> $GITHUB_ENV
- name: Pull for cache - name: Pull for cache
run: | run: |
docker pull "esphome/esphome-lint:latest" || true docker pull "esphome/esphome-lint:latest" || true
@ -26,6 +30,7 @@ jobs:
--cache-from "esphome/esphome-lint:latest" \ --cache-from "esphome/esphome-lint:latest" \
--file "docker/Dockerfile.lint" \ --file "docker/Dockerfile.lint" \
--tag "esphome/esphome-lint:latest" \ --tag "esphome/esphome-lint:latest" \
--tag "esphome/esphome-lint:${TAG}" \
. .
- name: Log in to docker hub - name: Log in to docker hub
env: env:
@ -33,4 +38,5 @@ jobs:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
- run: | - run: |
docker push "esphome/esphome-lint:${TAG}"
docker push "esphome/esphome-lint:latest" docker push "esphome/esphome-lint:latest"

View file

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-* # cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed # doesn't have to be installed
container: esphome/esphome-lint:latest container: esphome/esphome-lint:1.1
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-* # cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed # doesn't have to be installed
container: esphome/esphome-lint:latest container: esphome/esphome-lint:1.1
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy: strategy:
fail-fast: false fail-fast: false
@ -94,6 +94,7 @@ jobs:
- test2 - test2
- test3 - test3
- test4 - test4
- test5
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python - name: Set up Python
@ -123,7 +124,7 @@ jobs:
run: | run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json" echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json" echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome tests/${{ matrix.test }}.yaml compile - run: esphome compile tests/${{ matrix.test }}.yaml
pytest: pytest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -174,7 +175,7 @@ jobs:
echo "TAG=${TAG}" >> $GITHUB_ENV echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables - name: Set up env variables
run: | run: |
base_version="3.0.0" base_version="3.4.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"

View file

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-* # cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed # doesn't have to be installed
container: esphome/esphome-lint:latest container: esphome/esphome-lint:1.1
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# cpp lint job runs with esphome-lint docker image so that clang-format-* # cpp lint job runs with esphome-lint docker image so that clang-format-*
# doesn't have to be installed # doesn't have to be installed
container: esphome/esphome-lint:latest container: esphome/esphome-lint:1.1
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
strategy: strategy:
fail-fast: false fail-fast: false
@ -93,6 +93,7 @@ jobs:
- test2 - test2
- test3 - test3
- test4 - test4
- test5
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up Python - name: Set up Python
@ -121,7 +122,7 @@ jobs:
run: | run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json" echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/python.json" echo "::add-matcher::.github/workflows/matchers/python.json"
- run: esphome tests/${{ matrix.test }}.yaml compile - run: esphome compile tests/${{ matrix.test }}.yaml
pytest: pytest:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -194,7 +195,7 @@ jobs:
echo "TAG=${TAG}" >> $GITHUB_ENV echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables - name: Set up env variables
run: | run: |
base_version="3.0.0" base_version="3.4.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"

2
.gitignore vendored
View file

@ -100,6 +100,8 @@ CMakeLists.txt
# CMake # CMake
cmake-build-debug/ cmake-build-debug/
cmake-build-livingroom8266/
cmake-build-livingroom32/
cmake-build-release/ cmake-build-release/
CMakeCache.txt CMakeCache.txt

2
.vscode/tasks.json vendored
View file

@ -4,7 +4,7 @@
{ {
"label": "run", "label": "run",
"type": "shell", "type": "shell",
"command": "python3 -m esphome config dashboard", "command": "python3 -m esphome dashboard config",
"problemMatcher": [] "problemMatcher": []
} }
] ]

View file

@ -29,11 +29,14 @@ esphome/components/climate/* @esphome/core
esphome/components/climate_ir/* @glmnet esphome/components/climate_ir/* @glmnet
esphome/components/coolix/* @glmnet esphome/components/coolix/* @glmnet
esphome/components/cover/* @esphome/core esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun
esphome/components/ct_clamp/* @jesserockz esphome/components/ct_clamp/* @jesserockz
esphome/components/debug/* @OttoWinter esphome/components/debug/* @OttoWinter
esphome/components/dfplayer/* @glmnet esphome/components/dfplayer/* @glmnet
esphome/components/dht/* @OttoWinter esphome/components/dht/* @OttoWinter
esphome/components/ds1307/* @badbadc0ffee esphome/components/ds1307/* @badbadc0ffee
esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_improv/* @jesserockz
esphome/components/exposure_notifications/* @OttoWinter esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb esphome/components/ezo/* @ssieb
esphome/components/fastled_base/* @OttoWinter esphome/components/fastled_base/* @OttoWinter
@ -43,6 +46,7 @@ esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle esphome/components/gps/* @coogle
esphome/components/homeassistant/* @OttoWinter esphome/components/homeassistant/* @OttoWinter
esphome/components/i2c/* @esphome/core esphome/components/i2c/* @esphome/core
esphome/components/improv/* @jesserockz
esphome/components/inkbird_ibsth1_mini/* @fkirill esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter esphome/components/integration/* @OttoWinter
@ -63,6 +67,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp9808/* @k7hpn esphome/components/mcp9808/* @k7hpn
esphome/components/midea_ac/* @dudanov esphome/components/midea_ac/* @dudanov
esphome/components/midea_dongle/* @dudanov esphome/components/midea_dongle/* @dudanov
esphome/components/mitsubishi/* @RubyBailey
esphome/components/network/* @esphome/core esphome/components/network/* @esphome/core
esphome/components/nfc/* @jesserockz esphome/components/nfc/* @jesserockz
esphome/components/ota/* @esphome/core esphome/components/ota/* @esphome/core
@ -80,6 +85,7 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz esphome/components/rf_bridge/* @jesserockz
esphome/components/rtttl/* @glmnet esphome/components/rtttl/* @glmnet
esphome/components/script/* @esphome/core esphome/components/script/* @esphome/core
esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sensor/* @esphome/core esphome/components/sensor/* @esphome/core
esphome/components/sgp40/* @SenexCrenshaw esphome/components/sgp40/* @SenexCrenshaw
esphome/components/sht4x/* @sjtrny esphome/components/sht4x/* @sjtrny
@ -122,3 +128,4 @@ esphome/components/web_server_base/* @OttoWinter
esphome/components/whirlpool/* @glmnet esphome/components/whirlpool/* @glmnet
esphome/components/xiaomi_lywsd03mmc/* @ahpohl esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc401/* @vevsvevs esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xpt2046/* @numo68

View file

@ -1,10 +1,10 @@
ARG BUILD_FROM=esphome/esphome-base-amd64:3.0.0 ARG BUILD_FROM=esphome/esphome-base-amd64:3.4.0
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
# First install requirements to leverage caching when requirements don't change # First install requirements to leverage caching when requirements don't change
COPY requirements.txt docker/platformio_install_deps.py platformio.ini / COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
RUN \ RUN \
pip3 install --no-cache-dir -r /requirements.txt \ pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
&& /platformio_install_deps.py /platformio.ini && /platformio_install_deps.py /platformio.ini
# Then copy esphome and install # Then copy esphome and install
@ -27,4 +27,4 @@ WORKDIR /config
# in every docker command twice # in every docker command twice
ENTRYPOINT ["esphome"] ENTRYPOINT ["esphome"]
# When no arguments given, start the dashboard in the workdir # When no arguments given, start the dashboard in the workdir
CMD ["/config", "dashboard"] CMD ["dashboard", "/config"]

View file

@ -1,4 +1,4 @@
FROM esphome/esphome-base-amd64:3.0.0 FROM esphome/esphome-base-amd64:3.4.0
COPY . . COPY . .

View file

@ -2,9 +2,9 @@ ARG BUILD_FROM
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
# First install requirements to leverage caching when requirements don't change # First install requirements to leverage caching when requirements don't change
COPY requirements.txt docker/platformio_install_deps.py platformio.ini / COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
RUN \ RUN \
pip3 install --no-cache-dir -r /requirements.txt \ pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
&& /platformio_install_deps.py /platformio.ini && /platformio_install_deps.py /platformio.ini
# Copy root filesystem # Copy root filesystem

View file

@ -1,8 +1,8 @@
FROM esphome/esphome-lint-base:3.0.0 FROM esphome/esphome-lint-base:3.4.0
COPY requirements.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini / COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
RUN \ RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt \ pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
&& /platformio_install_deps.py /platformio.ini && /platformio_install_deps.py /platformio.ini
VOLUME ["/esphome"] VOLUME ["/esphome"]

View file

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

View file

@ -18,7 +18,7 @@ from esphome.const import (
CONF_ESPHOME, CONF_ESPHOME,
CONF_PLATFORMIO_OPTIONS, CONF_PLATFORMIO_OPTIONS,
) )
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority from esphome.core import CORE, EsphomeError, coroutine
from esphome.helpers import indent from esphome.helpers import indent
from esphome.util import ( from esphome.util import (
run_external_command, run_external_command,
@ -127,15 +127,16 @@ def wrap_to_code(name, comp):
coro = coroutine(comp.to_code) coro = coroutine(comp.to_code)
@functools.wraps(comp.to_code) @functools.wraps(comp.to_code)
@coroutine_with_priority(coro.priority) async def wrapped(conf):
def wrapped(conf):
cg.add(cg.LineComment(f"{name}:")) cg.add(cg.LineComment(f"{name}:"))
if comp.config_schema is not None: if comp.config_schema is not None:
conf_str = yaml_util.dump(conf) conf_str = yaml_util.dump(conf)
conf_str = conf_str.replace("//", "") conf_str = conf_str.replace("//", "")
cg.add(cg.LineComment(indent(conf_str))) cg.add(cg.LineComment(indent(conf_str)))
yield coro(conf) await coro(conf)
if hasattr(coro, "priority"):
wrapped.priority = coro.priority
return wrapped return wrapped
@ -267,7 +268,7 @@ def clean_mqtt(config, args):
def command_wizard(args): def command_wizard(args):
from esphome import wizard from esphome import wizard
return wizard.wizard(args.configuration[0]) return wizard.wizard(args.configuration)
def command_config(args, config): def command_config(args, config):
@ -283,7 +284,7 @@ def command_vscode(args):
logging.disable(logging.INFO) logging.disable(logging.INFO)
logging.disable(logging.WARNING) logging.disable(logging.WARNING)
CORE.config_path = args.configuration[0] CORE.config_path = args.configuration
vscode.read_config(args) vscode.read_config(args)
@ -303,7 +304,7 @@ def command_compile(args, config):
def command_upload(args, config): def command_upload(args, config):
port = choose_upload_log_host( port = choose_upload_log_host(
default=args.upload_port, default=args.device,
check_default=None, check_default=None,
show_ota=True, show_ota=True,
show_mqtt=False, show_mqtt=False,
@ -318,7 +319,7 @@ def command_upload(args, config):
def command_logs(args, config): def command_logs(args, config):
port = choose_upload_log_host( port = choose_upload_log_host(
default=args.serial_port, default=args.device,
check_default=None, check_default=None,
show_ota=False, show_ota=False,
show_mqtt=True, show_mqtt=True,
@ -336,7 +337,7 @@ def command_run(args, config):
return exit_code return exit_code
_LOGGER.info("Successfully compiled program.") _LOGGER.info("Successfully compiled program.")
port = choose_upload_log_host( port = choose_upload_log_host(
default=args.upload_port, default=args.device,
check_default=None, check_default=None,
show_ota=True, show_ota=True,
show_mqtt=False, show_mqtt=False,
@ -349,7 +350,7 @@ def command_run(args, config):
if args.no_logs: if args.no_logs:
return 0 return 0
port = choose_upload_log_host( port = choose_upload_log_host(
default=args.upload_port, default=args.device,
check_default=port, check_default=port,
show_ota=False, show_ota=False,
show_mqtt=True, show_mqtt=True,
@ -393,7 +394,7 @@ def command_update_all(args):
import click import click
success = {} success = {}
files = list_yaml_files(args.configuration[0]) files = list_yaml_files(args.configuration)
twidth = 60 twidth = 60
def print_bar(middle_text): def print_bar(middle_text):
@ -407,7 +408,7 @@ def command_update_all(args):
print("-" * twidth) print("-" * twidth)
print() print()
rc = run_external_process( rc = run_external_process(
"esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA" "esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
) )
if rc == 0: if rc == 0:
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f)) print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
@ -452,15 +453,17 @@ POST_CONFIG_ACTIONS = {
def parse_args(argv): def parse_args(argv):
parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}") options_parser = argparse.ArgumentParser(add_help=False)
parser.add_argument( options_parser.add_argument(
"-v", "--verbose", help="Enable verbose esphome logs.", action="store_true" "-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
) )
parser.add_argument( options_parser.add_argument(
"-q", "--quiet", help="Disable all esphome logs.", action="store_true" "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
) )
parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true") options_parser.add_argument(
parser.add_argument( "--dashboard", help=argparse.SUPPRESS, action="store_true"
)
options_parser.add_argument(
"-s", "-s",
"--substitution", "--substitution",
nargs=2, nargs=2,
@ -468,17 +471,87 @@ def parse_args(argv):
help="Add a substitution", help="Add a substitution",
metavar=("key", "value"), metavar=("key", "value"),
) )
parser.add_argument(
"configuration", help="Your YAML configuration file.", nargs="*" # Keep backward compatibility with the old command line format of
# esphome <config> <command>.
#
# Unfortunately this can't be done by adding another configuration argument to the
# main config parser, as argparse is greedy when parsing arguments, so in regular
# usage it'll eat the command as the configuration argument and error out out
# because it can't parse the configuration as a command.
#
# Instead, construct an ad-hoc parser for the old format that doesn't actually
# process the arguments, but parses them enough to let us figure out if the old
# format is used. In that case, swap the command and configuration in the arguments
# and continue on with the normal parser (after raising a deprecation warning).
#
# Disable argparse's built-in help option and add it manually to prevent this
# parser from printing the help messagefor the old format when invoked with -h.
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
compat_parser.add_argument("-h", "--help")
compat_parser.add_argument("configuration", nargs="*")
compat_parser.add_argument(
"command",
choices=[
"config",
"compile",
"upload",
"logs",
"run",
"clean-mqtt",
"wizard",
"mqtt-fingerprint",
"version",
"clean",
"dashboard",
],
) )
subparsers = parser.add_subparsers(help="Commands", dest="command") # on Python 3.9+ we can simply set exit_on_error=False in the constructor
def _raise(x):
raise argparse.ArgumentError(None, x)
compat_parser.error = _raise
try:
result, unparsed = compat_parser.parse_known_args(argv[1:])
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
argv = argv[0:last_option] + [result.command] + result.configuration + unparsed
deprecated_argv_suggestion = argv
except argparse.ArgumentError:
# This is not an old-style command line, so we don't have to do anything.
deprecated_argv_suggestion = None
# And continue on with regular parsing
parser = argparse.ArgumentParser(
description=f"ESPHome v{const.__version__}", parents=[options_parser]
)
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
mqtt_options = argparse.ArgumentParser(add_help=False)
mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
mqtt_options.add_argument("--username", help="Manually set the MQTT username.")
mqtt_options.add_argument("--password", help="Manually set the MQTT password.")
mqtt_options.add_argument("--client-id", help="Manually set the MQTT client id.")
subparsers = parser.add_subparsers(
help="Command to run:", dest="command", metavar="command"
)
subparsers.required = True subparsers.required = True
subparsers.add_parser("config", help="Validate the configuration and spit it out.")
parser_config = subparsers.add_parser(
"config", help="Validate the configuration and spit it out."
)
parser_config.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
)
parser_compile = subparsers.add_parser( parser_compile = subparsers.add_parser(
"compile", help="Read the configuration and compile a program." "compile", help="Read the configuration and compile a program."
) )
parser_compile.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
)
parser_compile.add_argument( parser_compile.add_argument(
"--only-generate", "--only-generate",
help="Only generate source code, do not compile.", help="Only generate source code, do not compile.",
@ -486,106 +559,124 @@ def parse_args(argv):
) )
parser_upload = subparsers.add_parser( parser_upload = subparsers.add_parser(
"upload", help="Validate the configuration " "and upload the latest binary." "upload", help="Validate the configuration and upload the latest binary."
) )
parser_upload.add_argument( parser_upload.add_argument(
"--upload-port", "configuration", help="Your YAML configuration file(s).", nargs="+"
help="Manually specify the upload port to use. " )
"For example /dev/cu.SLAB_USBtoUART.", parser_upload.add_argument(
"--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_logs = subparsers.add_parser( parser_logs = subparsers.add_parser(
"logs", help="Validate the configuration " "and show all MQTT logs." "logs",
help="Validate the configuration and show all logs.",
parents=[mqtt_options],
) )
parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.")
parser_logs.add_argument("--username", help="Manually set the username.")
parser_logs.add_argument("--password", help="Manually set the password.")
parser_logs.add_argument("--client-id", help="Manually set the client id.")
parser_logs.add_argument( parser_logs.add_argument(
"--serial-port", "configuration", help="Your YAML configuration file.", nargs=1
help="Manually specify a serial port to use" )
"For example /dev/cu.SLAB_USBtoUART.", parser_logs.add_argument(
"--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_run = subparsers.add_parser( parser_run = subparsers.add_parser(
"run", "run",
help="Validate the configuration, create a binary, " help="Validate the configuration, create a binary, upload it, and start logs.",
"upload it, and start MQTT logs.", parents=[mqtt_options],
) )
parser_run.add_argument( parser_run.add_argument(
"--upload-port", "configuration", help="Your YAML configuration file(s).", nargs="+"
help="Manually specify the upload port/ip to use. "
"For example /dev/cu.SLAB_USBtoUART.",
) )
parser_run.add_argument( parser_run.add_argument(
"--no-logs", help="Disable starting MQTT logs.", action="store_true" "--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_run.add_argument( parser_run.add_argument(
"--topic", help="Manually set the topic to subscribe to for logs." "--no-logs", help="Disable starting logs.", action="store_true"
) )
parser_run.add_argument(
"--username", help="Manually set the MQTT username for logs."
)
parser_run.add_argument(
"--password", help="Manually set the MQTT password for logs."
)
parser_run.add_argument("--client-id", help="Manually set the client id for logs.")
parser_clean = subparsers.add_parser( parser_clean = subparsers.add_parser(
"clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages." "clean-mqtt",
help="Helper to clear retained messages from an MQTT topic.",
parents=[mqtt_options],
)
parser_clean.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
) )
parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.")
parser_clean.add_argument("--username", help="Manually set the username.")
parser_clean.add_argument("--password", help="Manually set the password.")
parser_clean.add_argument("--client-id", help="Manually set the client id.")
subparsers.add_parser( parser_wizard = subparsers.add_parser(
"wizard", "wizard",
help="A helpful setup wizard that will guide " help="A helpful setup wizard that will guide you through setting up ESPHome.",
"you through setting up esphome.", )
parser_wizard.add_argument(
"configuration",
help="Your YAML configuration file.",
) )
subparsers.add_parser( parser_fingerprint = subparsers.add_parser(
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker." "mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
) )
parser_fingerprint.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
)
subparsers.add_parser("version", help="Print the esphome version and exit.") subparsers.add_parser("version", help="Print the ESPHome version and exit.")
subparsers.add_parser("clean", help="Delete all temporary build files.") parser_clean = subparsers.add_parser(
"clean", help="Delete all temporary build files."
)
parser_clean.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
)
dashboard = subparsers.add_parser( parser_dashboard = subparsers.add_parser(
"dashboard", help="Create a simple web server for a dashboard." "dashboard", help="Create a simple web server for a dashboard."
) )
dashboard.add_argument( parser_dashboard.add_argument(
"configuration",
help="Your YAML configuration file directory.",
)
parser_dashboard.add_argument(
"--port", "--port",
help="The HTTP port to open connections on. Defaults to 6052.", help="The HTTP port to open connections on. Defaults to 6052.",
type=int, type=int,
default=6052, default=6052,
) )
dashboard.add_argument( parser_dashboard.add_argument(
"--username", "--username",
help="The optional username to require " "for authentication.", help="The optional username to require for authentication.",
type=str, type=str,
default="", default="",
) )
dashboard.add_argument( parser_dashboard.add_argument(
"--password", "--password",
help="The optional password to require " "for authentication.", help="The optional password to require for authentication.",
type=str, type=str,
default="", default="",
) )
dashboard.add_argument( parser_dashboard.add_argument(
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true" "--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
) )
dashboard.add_argument("--hassio", help=argparse.SUPPRESS, action="store_true") parser_dashboard.add_argument(
dashboard.add_argument( "--hassio", help=argparse.SUPPRESS, action="store_true"
)
parser_dashboard.add_argument(
"--socket", help="Make the dashboard serve under a unix socket", type=str "--socket", help="Make the dashboard serve under a unix socket", type=str
) )
vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS) parser_vscode = subparsers.add_parser("vscode")
vscode.add_argument("--ace", action="store_true") parser_vscode.add_argument(
"configuration", help="Your YAML configuration file.", nargs=1
)
parser_vscode.add_argument("--ace", action="store_true")
subparsers.add_parser("update-all", help=argparse.SUPPRESS) parser_update = subparsers.add_parser("update-all")
parser_update.add_argument(
"configuration", help="Your YAML configuration file directory.", nargs=1
)
return parser.parse_args(argv[1:]) return parser.parse_args(argv[1:])
@ -595,9 +686,13 @@ def run_esphome(argv):
CORE.dashboard = args.dashboard CORE.dashboard = args.dashboard
setup_log(args.verbose, args.quiet) setup_log(args.verbose, args.quiet)
if args.command != "version" and not args.configuration: if args.deprecated_argv_suggestion is not None:
_LOGGER.error("Missing configuration parameter, see esphome --help.") _LOGGER.warning(
return 1 "Calling ESPHome with the configuration before the command is deprecated "
"and will be removed in the future. "
)
_LOGGER.warning("Please instead use:")
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion[1:]))
if sys.version_info < (3, 7, 0): if sys.version_info < (3, 7, 0):
_LOGGER.error( _LOGGER.error(
@ -610,7 +705,7 @@ def run_esphome(argv):
try: try:
return PRE_CONFIG_ACTIONS[args.command](args) return PRE_CONFIG_ACTIONS[args.command](args)
except EsphomeError as e: except EsphomeError as e:
_LOGGER.error(e) _LOGGER.error(e, exc_info=args.verbose)
return 1 return 1
for conf_path in args.configuration: for conf_path in args.configuration:
@ -628,7 +723,7 @@ def run_esphome(argv):
try: try:
rc = POST_CONFIG_ACTIONS[args.command](args, config) rc = POST_CONFIG_ACTIONS[args.command](args, config)
except EsphomeError as e: except EsphomeError as e:
_LOGGER.error(e) _LOGGER.error(e, exc_info=args.verbose)
return 1 return 1
if rc != 0: if rc != 0:
return rc return rc

View file

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

View file

@ -19,6 +19,7 @@ from esphome.cpp_generator import ( # noqa
Statement, Statement,
LineComment, LineComment,
progmem_array, progmem_array,
static_const_array,
statement, statement,
variable, variable,
new_variable, new_variable,

View file

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

View file

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

View file

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

View file

@ -8,6 +8,7 @@ from esphome.const import (
CONF_PIN, CONF_PIN,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ICON_EMPTY, ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
) )
@ -35,7 +36,9 @@ ADCSensor = adc_ns.class_(
) )
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE) sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
)
.extend( .extend(
{ {
cv.GenerateID(): cv.declare_id(ADCSensor), cv.GenerateID(): cv.declare_id(ADCSensor),
@ -49,10 +52,10 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield sensor.register_sensor(var, config) await sensor.register_sensor(var, config)
if config[CONF_PIN] == "VCC": if config[CONF_PIN] == "VCC":
cg.add_define("USE_ADC_SENSOR_VCC") cg.add_define("USE_ADC_SENSOR_VCC")

View file

@ -38,18 +38,18 @@ CONFIG_SCHEMA = cv.All(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
wrapped_light = yield cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID]) wrapped_light = await cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
cg.add(var.set_width(config[CONF_WIDTH])) cg.add(var.set_width(config[CONF_WIDTH]))
cg.add(var.set_height(config[CONF_HEIGHT])) cg.add(var.set_height(config[CONF_HEIGHT]))
cg.add(var.set_light(wrapped_light)) cg.add(var.set_light(wrapped_light))
yield cg.register_component(var, config) await cg.register_component(var, config)
yield display.register_display(var, config) await display.register_display(var, config)
if CONF_PIXEL_MAPPER in config: if CONF_PIXEL_MAPPER in config:
pixel_mapper_template_ = yield cg.process_lambda( pixel_mapper_template_ = await cg.process_lambda(
config[CONF_PIXEL_MAPPER], config[CONF_PIXEL_MAPPER],
[(int, "x"), (int, "y")], [(int, "x"), (int, "y")],
return_type=cg.int_, return_type=cg.int_,
@ -57,7 +57,7 @@ def to_code(config):
cg.add(var.set_pixel_mapper(pixel_mapper_template_)) cg.add(var.set_pixel_mapper(pixel_mapper_template_))
if CONF_LAMBDA in config: if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda( lambda_ = await cg.process_lambda(
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
) )
cg.add(var.set_writer(lambda_)) cg.add(var.set_writer(lambda_))

View file

@ -9,6 +9,7 @@ from esphome.const import (
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ICON_EMPTY, ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_WATT, UNIT_WATT,
@ -31,19 +32,27 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ADE7953), cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_IRQ_PIN): pins.input_pin, cv.Optional(CONF_IRQ_PIN): pins.input_pin,
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema( cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT UNIT_AMPERE,
ICON_EMPTY,
2,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema( cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT UNIT_AMPERE,
ICON_EMPTY,
2,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema( cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema( cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
), ),
} }
) )
@ -52,10 +61,10 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
if CONF_IRQ_PIN in config: if CONF_IRQ_PIN in config:
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN])) cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
@ -70,5 +79,5 @@ def to_code(config):
if key not in config: if key not in config:
continue continue
conf = config[key] conf = config[key]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(getattr(var, f"set_{key}_sensor")(sens)) cg.add(getattr(var, f"set_{key}_sensor")(sens))

View file

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

View file

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

View file

@ -60,6 +60,7 @@ void AHT10Component::update() {
delay = AHT10_HUMIDITY_DELAY; delay = AHT10_HUMIDITY_DELAY;
for (int i = 0; i < AHT10_ATTEMPS; ++i) { for (int i = 0; i < AHT10_ATTEMPS; ++i) {
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis()); ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
delay_microseconds_accurate(4);
if (!this->read_bytes(0, data, 6, delay)) { if (!this->read_bytes(0, data, 6, delay)) {
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy } else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -176,6 +176,10 @@ message DeviceInfoResponse {
string model = 6; string model = 6;
bool has_deep_sleep = 7; bool has_deep_sleep = 7;
// The esphome project details if set
string project_name = 8;
string project_version = 9;
} }
message ListEntitiesRequest { message ListEntitiesRequest {
@ -409,6 +413,11 @@ message LightCommandRequest {
} }
// ==================== SENSOR ==================== // ==================== SENSOR ====================
enum SensorStateClass {
STATE_CLASS_NONE = 0;
STATE_CLASS_MEASUREMENT = 1;
}
message ListEntitiesSensorResponse { message ListEntitiesSensorResponse {
option (id) = 16; option (id) = 16;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@ -424,6 +433,7 @@ message ListEntitiesSensorResponse {
int32 accuracy_decimals = 7; int32 accuracy_decimals = 7;
bool force_update = 8; bool force_update = 8;
string device_class = 9; string device_class = 9;
SensorStateClass state_class = 10;
} }
message SensorStateResponse { message SensorStateResponse {
option (id) = 25; option (id) = 25;
@ -561,6 +571,7 @@ message SubscribeHomeAssistantStateResponse {
option (id) = 39; option (id) = 39;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
string entity_id = 1; string entity_id = 1;
string attribute = 2;
} }
message HomeAssistantStateResponse { message HomeAssistantStateResponse {
@ -570,6 +581,7 @@ message HomeAssistantStateResponse {
string entity_id = 1; string entity_id = 1;
string state = 2; string state = 2;
string attribute = 3;
} }
// ==================== IMPORT TIME ==================== // ==================== IMPORT TIME ====================
@ -664,11 +676,12 @@ message CameraImageRequest {
// ==================== CLIMATE ==================== // ==================== CLIMATE ====================
enum ClimateMode { enum ClimateMode {
CLIMATE_MODE_OFF = 0; CLIMATE_MODE_OFF = 0;
CLIMATE_MODE_AUTO = 1; CLIMATE_MODE_HEAT_COOL = 1;
CLIMATE_MODE_COOL = 2; CLIMATE_MODE_COOL = 2;
CLIMATE_MODE_HEAT = 3; CLIMATE_MODE_HEAT = 3;
CLIMATE_MODE_FAN_ONLY = 4; CLIMATE_MODE_FAN_ONLY = 4;
CLIMATE_MODE_DRY = 5; CLIMATE_MODE_DRY = 5;
CLIMATE_MODE_AUTO = 6;
} }
enum ClimateFanMode { enum ClimateFanMode {
CLIMATE_FAN_ON = 0; CLIMATE_FAN_ON = 0;
@ -696,6 +709,15 @@ enum ClimateAction {
CLIMATE_ACTION_DRYING = 5; CLIMATE_ACTION_DRYING = 5;
CLIMATE_ACTION_FAN = 6; CLIMATE_ACTION_FAN = 6;
} }
enum ClimatePreset {
CLIMATE_PRESET_ECO = 0;
CLIMATE_PRESET_AWAY = 1;
CLIMATE_PRESET_BOOST = 2;
CLIMATE_PRESET_COMFORT = 3;
CLIMATE_PRESET_HOME = 4;
CLIMATE_PRESET_SLEEP = 5;
CLIMATE_PRESET_ACTIVITY = 6;
}
message ListEntitiesClimateResponse { message ListEntitiesClimateResponse {
option (id) = 46; option (id) = 46;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@ -716,6 +738,9 @@ message ListEntitiesClimateResponse {
bool supports_action = 12; bool supports_action = 12;
repeated ClimateFanMode supported_fan_modes = 13; repeated ClimateFanMode supported_fan_modes = 13;
repeated ClimateSwingMode supported_swing_modes = 14; repeated ClimateSwingMode supported_swing_modes = 14;
repeated string supported_custom_fan_modes = 15;
repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17;
} }
message ClimateStateResponse { message ClimateStateResponse {
option (id) = 47; option (id) = 47;
@ -733,6 +758,9 @@ message ClimateStateResponse {
ClimateAction action = 8; ClimateAction action = 8;
ClimateFanMode fan_mode = 9; ClimateFanMode fan_mode = 9;
ClimateSwingMode swing_mode = 10; ClimateSwingMode swing_mode = 10;
string custom_fan_mode = 11;
ClimatePreset preset = 12;
string custom_preset = 13;
} }
message ClimateCommandRequest { message ClimateCommandRequest {
option (id) = 48; option (id) = 48;
@ -755,4 +783,10 @@ message ClimateCommandRequest {
ClimateFanMode fan_mode = 13; ClimateFanMode fan_mode = 13;
bool has_swing_mode = 14; bool has_swing_mode = 14;
ClimateSwingMode swing_mode = 15; ClimateSwingMode swing_mode = 15;
bool has_custom_fan_mode = 16;
string custom_fan_mode = 17;
bool has_preset = 18;
ClimatePreset preset = 19;
bool has_custom_preset = 20;
string custom_preset = 21;
} }

View file

@ -395,6 +395,8 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
msg.accuracy_decimals = sensor->get_accuracy_decimals(); msg.accuracy_decimals = sensor->get_accuracy_decimals();
msg.force_update = sensor->get_force_update(); msg.force_update = sensor->get_force_update();
msg.device_class = sensor->get_device_class(); msg.device_class = sensor->get_device_class();
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
return this->send_list_entities_sensor_response(msg); return this->send_list_entities_sensor_response(msg);
} }
#endif #endif
@ -475,8 +477,14 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
} }
if (traits.get_supports_away()) if (traits.get_supports_away())
resp.away = climate->away; resp.away = climate->away;
if (traits.get_supports_fan_modes()) if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode); resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
resp.custom_fan_mode = climate->custom_fan_mode.value();
if (traits.get_supports_presets() && climate->preset.has_value())
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
resp.custom_preset = climate->custom_preset.value();
if (traits.get_supports_swing_modes()) if (traits.get_supports_swing_modes())
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode); resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
return this->send_climate_state_response(resp); return this->send_climate_state_response(resp);
@ -490,8 +498,9 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.unique_id = get_default_unique_id("climate", climate); msg.unique_id = get_default_unique_id("climate", climate);
msg.supports_current_temperature = traits.get_supports_current_temperature(); msg.supports_current_temperature = traits.get_supports_current_temperature();
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature(); msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, for (auto mode :
climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) { {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT,
climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_HEAT_COOL}) {
if (traits.supports_mode(mode)) if (traits.supports_mode(mode))
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode)); msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
} }
@ -506,6 +515,18 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
if (traits.supports_fan_mode(fan_mode)) if (traits.supports_fan_mode(fan_mode))
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode)); msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
} }
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) {
msg.supported_custom_fan_modes.push_back(custom_fan_mode);
}
for (auto preset : {climate::CLIMATE_PRESET_ECO, climate::CLIMATE_PRESET_AWAY, climate::CLIMATE_PRESET_BOOST,
climate::CLIMATE_PRESET_COMFORT, climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_SLEEP,
climate::CLIMATE_PRESET_ACTIVITY}) {
if (traits.supports_preset(preset))
msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
}
for (auto const &custom_preset : traits.get_supported_custom_presets()) {
msg.supported_custom_presets.push_back(custom_preset);
}
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL, for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
climate::CLIMATE_SWING_HORIZONTAL}) { climate::CLIMATE_SWING_HORIZONTAL}) {
if (traits.supports_swing_mode(swing_mode)) if (traits.supports_swing_mode(swing_mode))
@ -531,6 +552,12 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
call.set_away(msg.away); call.set_away(msg.away);
if (msg.has_fan_mode) if (msg.has_fan_mode)
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode)); call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
if (msg.has_custom_fan_mode)
call.set_fan_mode(msg.custom_fan_mode);
if (msg.has_preset)
call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
if (msg.has_custom_preset)
call.set_preset(msg.custom_preset);
if (msg.has_swing_mode) if (msg.has_swing_mode)
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode)); call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
call.perform(); call.perform();
@ -637,14 +664,19 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
#endif #endif
#ifdef USE_DEEP_SLEEP #ifdef USE_DEEP_SLEEP
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep; resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
#endif
#ifdef ESPHOME_PROJECT_NAME
resp.project_name = ESPHOME_PROJECT_NAME;
resp.project_version = ESPHOME_PROJECT_VERSION;
#endif #endif
return resp; return resp;
} }
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) { void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
for (auto &it : this->parent_->get_state_subs()) for (auto &it : this->parent_->get_state_subs())
if (it.entity_id == msg.entity_id) if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
it.callback(msg.state); it.callback(msg.state);
} }
}
void APIConnection::execute_service(const ExecuteServiceRequest &msg) { void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
bool found = false; bool found = false;
for (auto *service : this->parent_->get_user_services()) { for (auto *service : this->parent_->get_user_services()) {
@ -660,6 +692,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
for (auto &it : this->parent_->get_state_subs()) { for (auto &it : this->parent_->get_state_subs()) {
SubscribeHomeAssistantStateResponse resp; SubscribeHomeAssistantStateResponse resp;
resp.entity_id = it.entity_id; resp.entity_id = it.entity_id;
resp.attribute = it.attribute.value();
if (!this->send_subscribe_home_assistant_state_response(resp)) { if (!this->send_subscribe_home_assistant_state_response(resp)) {
this->on_fatal_error(); this->on_fatal_error();
return; return;

View file

@ -62,6 +62,16 @@ template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirec
return "UNKNOWN"; return "UNKNOWN";
} }
} }
template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::SensorStateClass value) {
switch (value) {
case enums::STATE_CLASS_NONE:
return "STATE_CLASS_NONE";
case enums::STATE_CLASS_MEASUREMENT:
return "STATE_CLASS_MEASUREMENT";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) { template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
switch (value) { switch (value) {
case enums::LOG_LEVEL_NONE: case enums::LOG_LEVEL_NONE:
@ -108,8 +118,8 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
switch (value) { switch (value) {
case enums::CLIMATE_MODE_OFF: case enums::CLIMATE_MODE_OFF:
return "CLIMATE_MODE_OFF"; return "CLIMATE_MODE_OFF";
case enums::CLIMATE_MODE_AUTO: case enums::CLIMATE_MODE_HEAT_COOL:
return "CLIMATE_MODE_AUTO"; return "CLIMATE_MODE_HEAT_COOL";
case enums::CLIMATE_MODE_COOL: case enums::CLIMATE_MODE_COOL:
return "CLIMATE_MODE_COOL"; return "CLIMATE_MODE_COOL";
case enums::CLIMATE_MODE_HEAT: case enums::CLIMATE_MODE_HEAT:
@ -118,6 +128,8 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
return "CLIMATE_MODE_FAN_ONLY"; return "CLIMATE_MODE_FAN_ONLY";
case enums::CLIMATE_MODE_DRY: case enums::CLIMATE_MODE_DRY:
return "CLIMATE_MODE_DRY"; return "CLIMATE_MODE_DRY";
case enums::CLIMATE_MODE_AUTO:
return "CLIMATE_MODE_AUTO";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
@ -178,6 +190,26 @@ template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::Climate
return "UNKNOWN"; return "UNKNOWN";
} }
} }
template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::ClimatePreset value) {
switch (value) {
case enums::CLIMATE_PRESET_ECO:
return "CLIMATE_PRESET_ECO";
case enums::CLIMATE_PRESET_AWAY:
return "CLIMATE_PRESET_AWAY";
case enums::CLIMATE_PRESET_BOOST:
return "CLIMATE_PRESET_BOOST";
case enums::CLIMATE_PRESET_COMFORT:
return "CLIMATE_PRESET_COMFORT";
case enums::CLIMATE_PRESET_HOME:
return "CLIMATE_PRESET_HOME";
case enums::CLIMATE_PRESET_SLEEP:
return "CLIMATE_PRESET_SLEEP";
case enums::CLIMATE_PRESET_ACTIVITY:
return "CLIMATE_PRESET_ACTIVITY";
default:
return "UNKNOWN";
}
}
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) { switch (field_id) {
case 1: { case 1: {
@ -328,6 +360,14 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v
this->model = value.as_string(); this->model = value.as_string();
return true; return true;
} }
case 8: {
this->project_name = value.as_string();
return true;
}
case 9: {
this->project_version = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -340,6 +380,8 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->compilation_time); buffer.encode_string(5, this->compilation_time);
buffer.encode_string(6, this->model); buffer.encode_string(6, this->model);
buffer.encode_bool(7, this->has_deep_sleep); buffer.encode_bool(7, this->has_deep_sleep);
buffer.encode_string(8, this->project_name);
buffer.encode_string(9, this->project_version);
} }
void DeviceInfoResponse::dump_to(std::string &out) const { void DeviceInfoResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -371,6 +413,14 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append(" has_deep_sleep: "); out.append(" has_deep_sleep: ");
out.append(YESNO(this->has_deep_sleep)); out.append(YESNO(this->has_deep_sleep));
out.append("\n"); out.append("\n");
out.append(" project_name: ");
out.append("'").append(this->project_name).append("'");
out.append("\n");
out.append(" project_version: ");
out.append("'").append(this->project_version).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }
void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {} void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {}
@ -1507,6 +1557,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->force_update = value.as_bool(); this->force_update = value.as_bool();
return true; return true;
} }
case 10: {
this->state_class = value.as_enum<enums::SensorStateClass>();
return true;
}
default: default:
return false; return false;
} }
@ -1561,6 +1615,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_int32(7, this->accuracy_decimals); buffer.encode_int32(7, this->accuracy_decimals);
buffer.encode_bool(8, this->force_update); buffer.encode_bool(8, this->force_update);
buffer.encode_string(9, this->device_class); buffer.encode_string(9, this->device_class);
buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
} }
void ListEntitiesSensorResponse::dump_to(std::string &out) const { void ListEntitiesSensorResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -1602,6 +1657,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append(" device_class: "); out.append(" device_class: ");
out.append("'").append(this->device_class).append("'"); out.append("'").append(this->device_class).append("'");
out.append("\n"); out.append("\n");
out.append(" state_class: ");
out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
out.append("\n");
out.append("}"); out.append("}");
} }
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -2123,12 +2182,17 @@ bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, Proto
this->entity_id = value.as_string(); this->entity_id = value.as_string();
return true; return true;
} }
case 2: {
this->attribute = value.as_string();
return true;
}
default: default:
return false; return false;
} }
} }
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const { void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->entity_id); buffer.encode_string(1, this->entity_id);
buffer.encode_string(2, this->attribute);
} }
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const { void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -2136,6 +2200,10 @@ void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
out.append(" entity_id: "); out.append(" entity_id: ");
out.append("'").append(this->entity_id).append("'"); out.append("'").append(this->entity_id).append("'");
out.append("\n"); out.append("\n");
out.append(" attribute: ");
out.append("'").append(this->attribute).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }
bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
@ -2148,6 +2216,10 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->state = value.as_string(); this->state = value.as_string();
return true; return true;
} }
case 3: {
this->attribute = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -2155,6 +2227,7 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const { void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->entity_id); buffer.encode_string(1, this->entity_id);
buffer.encode_string(2, this->state); buffer.encode_string(2, this->state);
buffer.encode_string(3, this->attribute);
} }
void HomeAssistantStateResponse::dump_to(std::string &out) const { void HomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -2166,6 +2239,10 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
out.append(" state: "); out.append(" state: ");
out.append("'").append(this->state).append("'"); out.append("'").append(this->state).append("'");
out.append("\n"); out.append("\n");
out.append(" attribute: ");
out.append("'").append(this->attribute).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }
void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {} void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {}
@ -2610,6 +2687,10 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>()); this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>());
return true; return true;
} }
case 16: {
this->supported_presets.push_back(value.as_enum<enums::ClimatePreset>());
return true;
}
default: default:
return false; return false;
} }
@ -2628,6 +2709,14 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
this->unique_id = value.as_string(); this->unique_id = value.as_string();
return true; return true;
} }
case 15: {
this->supported_custom_fan_modes.push_back(value.as_string());
return true;
}
case 17: {
this->supported_custom_presets.push_back(value.as_string());
return true;
}
default: default:
return false; return false;
} }
@ -2675,6 +2764,15 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->supported_swing_modes) { for (auto &it : this->supported_swing_modes) {
buffer.encode_enum<enums::ClimateSwingMode>(14, it, true); buffer.encode_enum<enums::ClimateSwingMode>(14, it, true);
} }
for (auto &it : this->supported_custom_fan_modes) {
buffer.encode_string(15, it, true);
}
for (auto &it : this->supported_presets) {
buffer.encode_enum<enums::ClimatePreset>(16, it, true);
}
for (auto &it : this->supported_custom_presets) {
buffer.encode_string(17, it, true);
}
} }
void ListEntitiesClimateResponse::dump_to(std::string &out) const { void ListEntitiesClimateResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -2744,6 +2842,24 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append(proto_enum_to_string<enums::ClimateSwingMode>(it)); out.append(proto_enum_to_string<enums::ClimateSwingMode>(it));
out.append("\n"); out.append("\n");
} }
for (const auto &it : this->supported_custom_fan_modes) {
out.append(" supported_custom_fan_modes: ");
out.append("'").append(it).append("'");
out.append("\n");
}
for (const auto &it : this->supported_presets) {
out.append(" supported_presets: ");
out.append(proto_enum_to_string<enums::ClimatePreset>(it));
out.append("\n");
}
for (const auto &it : this->supported_custom_presets) {
out.append(" supported_custom_presets: ");
out.append("'").append(it).append("'");
out.append("\n");
}
out.append("}"); out.append("}");
} }
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -2768,6 +2884,24 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->swing_mode = value.as_enum<enums::ClimateSwingMode>(); this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
return true; return true;
} }
case 12: {
this->preset = value.as_enum<enums::ClimatePreset>();
return true;
}
default:
return false;
}
}
bool ClimateStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 11: {
this->custom_fan_mode = value.as_string();
return true;
}
case 13: {
this->custom_preset = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -2809,6 +2943,9 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::ClimateAction>(8, this->action); buffer.encode_enum<enums::ClimateAction>(8, this->action);
buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode); buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode); buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
buffer.encode_string(11, this->custom_fan_mode);
buffer.encode_enum<enums::ClimatePreset>(12, this->preset);
buffer.encode_string(13, this->custom_preset);
} }
void ClimateStateResponse::dump_to(std::string &out) const { void ClimateStateResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -2857,6 +2994,18 @@ void ClimateStateResponse::dump_to(std::string &out) const {
out.append(" swing_mode: "); out.append(" swing_mode: ");
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode)); out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
out.append("\n"); out.append("\n");
out.append(" custom_fan_mode: ");
out.append("'").append(this->custom_fan_mode).append("'");
out.append("\n");
out.append(" preset: ");
out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
out.append("\n");
out.append(" custom_preset: ");
out.append("'").append(this->custom_preset).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }
bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -2905,6 +3054,36 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
this->swing_mode = value.as_enum<enums::ClimateSwingMode>(); this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
return true; return true;
} }
case 16: {
this->has_custom_fan_mode = value.as_bool();
return true;
}
case 18: {
this->has_preset = value.as_bool();
return true;
}
case 19: {
this->preset = value.as_enum<enums::ClimatePreset>();
return true;
}
case 20: {
this->has_custom_preset = value.as_bool();
return true;
}
default:
return false;
}
}
bool ClimateCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 17: {
this->custom_fan_mode = value.as_string();
return true;
}
case 21: {
this->custom_preset = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -2947,6 +3126,12 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode); buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
buffer.encode_bool(14, this->has_swing_mode); buffer.encode_bool(14, this->has_swing_mode);
buffer.encode_enum<enums::ClimateSwingMode>(15, this->swing_mode); buffer.encode_enum<enums::ClimateSwingMode>(15, this->swing_mode);
buffer.encode_bool(16, this->has_custom_fan_mode);
buffer.encode_string(17, this->custom_fan_mode);
buffer.encode_bool(18, this->has_preset);
buffer.encode_enum<enums::ClimatePreset>(19, this->preset);
buffer.encode_bool(20, this->has_custom_preset);
buffer.encode_string(21, this->custom_preset);
} }
void ClimateCommandRequest::dump_to(std::string &out) const { void ClimateCommandRequest::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -3014,6 +3199,30 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
out.append(" swing_mode: "); out.append(" swing_mode: ");
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode)); out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
out.append("\n"); out.append("\n");
out.append(" has_custom_fan_mode: ");
out.append(YESNO(this->has_custom_fan_mode));
out.append("\n");
out.append(" custom_fan_mode: ");
out.append("'").append(this->custom_fan_mode).append("'");
out.append("\n");
out.append(" has_preset: ");
out.append(YESNO(this->has_preset));
out.append("\n");
out.append(" preset: ");
out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
out.append("\n");
out.append(" has_custom_preset: ");
out.append(YESNO(this->has_custom_preset));
out.append("\n");
out.append(" custom_preset: ");
out.append("'").append(this->custom_preset).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }

View file

@ -32,6 +32,10 @@ enum FanDirection : uint32_t {
FAN_DIRECTION_FORWARD = 0, FAN_DIRECTION_FORWARD = 0,
FAN_DIRECTION_REVERSE = 1, FAN_DIRECTION_REVERSE = 1,
}; };
enum SensorStateClass : uint32_t {
STATE_CLASS_NONE = 0,
STATE_CLASS_MEASUREMENT = 1,
};
enum LogLevel : uint32_t { enum LogLevel : uint32_t {
LOG_LEVEL_NONE = 0, LOG_LEVEL_NONE = 0,
LOG_LEVEL_ERROR = 1, LOG_LEVEL_ERROR = 1,
@ -53,11 +57,12 @@ enum ServiceArgType : uint32_t {
}; };
enum ClimateMode : uint32_t { enum ClimateMode : uint32_t {
CLIMATE_MODE_OFF = 0, CLIMATE_MODE_OFF = 0,
CLIMATE_MODE_AUTO = 1, CLIMATE_MODE_HEAT_COOL = 1,
CLIMATE_MODE_COOL = 2, CLIMATE_MODE_COOL = 2,
CLIMATE_MODE_HEAT = 3, CLIMATE_MODE_HEAT = 3,
CLIMATE_MODE_FAN_ONLY = 4, CLIMATE_MODE_FAN_ONLY = 4,
CLIMATE_MODE_DRY = 5, CLIMATE_MODE_DRY = 5,
CLIMATE_MODE_AUTO = 6,
}; };
enum ClimateFanMode : uint32_t { enum ClimateFanMode : uint32_t {
CLIMATE_FAN_ON = 0, CLIMATE_FAN_ON = 0,
@ -84,12 +89,21 @@ enum ClimateAction : uint32_t {
CLIMATE_ACTION_DRYING = 5, CLIMATE_ACTION_DRYING = 5,
CLIMATE_ACTION_FAN = 6, CLIMATE_ACTION_FAN = 6,
}; };
enum ClimatePreset : uint32_t {
CLIMATE_PRESET_ECO = 0,
CLIMATE_PRESET_AWAY = 1,
CLIMATE_PRESET_BOOST = 2,
CLIMATE_PRESET_COMFORT = 3,
CLIMATE_PRESET_HOME = 4,
CLIMATE_PRESET_SLEEP = 5,
CLIMATE_PRESET_ACTIVITY = 6,
};
} // namespace enums } // namespace enums
class HelloRequest : public ProtoMessage { class HelloRequest : public ProtoMessage {
public: public:
std::string client_info{}; // NOLINT std::string client_info{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -98,9 +112,9 @@ class HelloRequest : public ProtoMessage {
}; };
class HelloResponse : public ProtoMessage { class HelloResponse : public ProtoMessage {
public: public:
uint32_t api_version_major{0}; // NOLINT uint32_t api_version_major{0};
uint32_t api_version_minor{0}; // NOLINT uint32_t api_version_minor{0};
std::string server_info{}; // NOLINT std::string server_info{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -110,7 +124,7 @@ class HelloResponse : public ProtoMessage {
}; };
class ConnectRequest : public ProtoMessage { class ConnectRequest : public ProtoMessage {
public: public:
std::string password{}; // NOLINT std::string password{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -119,7 +133,7 @@ class ConnectRequest : public ProtoMessage {
}; };
class ConnectResponse : public ProtoMessage { class ConnectResponse : public ProtoMessage {
public: public:
bool invalid_password{false}; // NOLINT bool invalid_password{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -163,13 +177,15 @@ class DeviceInfoRequest : public ProtoMessage {
}; };
class DeviceInfoResponse : public ProtoMessage { class DeviceInfoResponse : public ProtoMessage {
public: public:
bool uses_password{false}; // NOLINT bool uses_password{false};
std::string name{}; // NOLINT std::string name{};
std::string mac_address{}; // NOLINT std::string mac_address{};
std::string esphome_version{}; // NOLINT std::string esphome_version{};
std::string compilation_time{}; // NOLINT std::string compilation_time{};
std::string model{}; // NOLINT std::string model{};
bool has_deep_sleep{false}; // NOLINT bool has_deep_sleep{false};
std::string project_name{};
std::string project_version{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -200,12 +216,12 @@ class SubscribeStatesRequest : public ProtoMessage {
}; };
class ListEntitiesBinarySensorResponse : public ProtoMessage { class ListEntitiesBinarySensorResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
std::string device_class{}; // NOLINT std::string device_class{};
bool is_status_binary_sensor{false}; // NOLINT bool is_status_binary_sensor{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -216,9 +232,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
}; };
class BinarySensorStateResponse : public ProtoMessage { class BinarySensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
bool missing_state{false}; // NOLINT bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -228,14 +244,14 @@ class BinarySensorStateResponse : public ProtoMessage {
}; };
class ListEntitiesCoverResponse : public ProtoMessage { class ListEntitiesCoverResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
bool assumed_state{false}; // NOLINT bool assumed_state{false};
bool supports_position{false}; // NOLINT bool supports_position{false};
bool supports_tilt{false}; // NOLINT bool supports_tilt{false};
std::string device_class{}; // NOLINT std::string device_class{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -246,11 +262,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
}; };
class CoverStateResponse : public ProtoMessage { class CoverStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
enums::LegacyCoverState legacy_state{}; // NOLINT enums::LegacyCoverState legacy_state{};
float position{0.0f}; // NOLINT float position{0.0f};
float tilt{0.0f}; // NOLINT float tilt{0.0f};
enums::CoverOperation current_operation{}; // NOLINT enums::CoverOperation current_operation{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -260,14 +276,14 @@ class CoverStateResponse : public ProtoMessage {
}; };
class CoverCommandRequest : public ProtoMessage { class CoverCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool has_legacy_command{false}; // NOLINT bool has_legacy_command{false};
enums::LegacyCoverCommand legacy_command{}; // NOLINT enums::LegacyCoverCommand legacy_command{};
bool has_position{false}; // NOLINT bool has_position{false};
float position{0.0f}; // NOLINT float position{0.0f};
bool has_tilt{false}; // NOLINT bool has_tilt{false};
float tilt{0.0f}; // NOLINT float tilt{0.0f};
bool stop{false}; // NOLINT bool stop{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -277,14 +293,14 @@ class CoverCommandRequest : public ProtoMessage {
}; };
class ListEntitiesFanResponse : public ProtoMessage { class ListEntitiesFanResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
bool supports_oscillation{false}; // NOLINT bool supports_oscillation{false};
bool supports_speed{false}; // NOLINT bool supports_speed{false};
bool supports_direction{false}; // NOLINT bool supports_direction{false};
int32_t supported_speed_count{0}; // NOLINT int32_t supported_speed_count{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -295,12 +311,12 @@ class ListEntitiesFanResponse : public ProtoMessage {
}; };
class FanStateResponse : public ProtoMessage { class FanStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
bool oscillating{false}; // NOLINT bool oscillating{false};
enums::FanSpeed speed{}; // NOLINT enums::FanSpeed speed{};
enums::FanDirection direction{}; // NOLINT enums::FanDirection direction{};
int32_t speed_level{0}; // NOLINT int32_t speed_level{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -310,17 +326,17 @@ class FanStateResponse : public ProtoMessage {
}; };
class FanCommandRequest : public ProtoMessage { class FanCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool has_state{false}; // NOLINT bool has_state{false};
bool state{false}; // NOLINT bool state{false};
bool has_speed{false}; // NOLINT bool has_speed{false};
enums::FanSpeed speed{}; // NOLINT enums::FanSpeed speed{};
bool has_oscillating{false}; // NOLINT bool has_oscillating{false};
bool oscillating{false}; // NOLINT bool oscillating{false};
bool has_direction{false}; // NOLINT bool has_direction{false};
enums::FanDirection direction{}; // NOLINT enums::FanDirection direction{};
bool has_speed_level{false}; // NOLINT bool has_speed_level{false};
int32_t speed_level{0}; // NOLINT int32_t speed_level{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -330,17 +346,17 @@ class FanCommandRequest : public ProtoMessage {
}; };
class ListEntitiesLightResponse : public ProtoMessage { class ListEntitiesLightResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
bool supports_brightness{false}; // NOLINT bool supports_brightness{false};
bool supports_rgb{false}; // NOLINT bool supports_rgb{false};
bool supports_white_value{false}; // NOLINT bool supports_white_value{false};
bool supports_color_temperature{false}; // NOLINT bool supports_color_temperature{false};
float min_mireds{0.0f}; // NOLINT float min_mireds{0.0f};
float max_mireds{0.0f}; // NOLINT float max_mireds{0.0f};
std::vector<std::string> effects{}; // NOLINT std::vector<std::string> effects{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -351,15 +367,15 @@ class ListEntitiesLightResponse : public ProtoMessage {
}; };
class LightStateResponse : public ProtoMessage { class LightStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
float brightness{0.0f}; // NOLINT float brightness{0.0f};
float red{0.0f}; // NOLINT float red{0.0f};
float green{0.0f}; // NOLINT float green{0.0f};
float blue{0.0f}; // NOLINT float blue{0.0f};
float white{0.0f}; // NOLINT float white{0.0f};
float color_temperature{0.0f}; // NOLINT float color_temperature{0.0f};
std::string effect{}; // NOLINT std::string effect{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -370,25 +386,25 @@ class LightStateResponse : public ProtoMessage {
}; };
class LightCommandRequest : public ProtoMessage { class LightCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool has_state{false}; // NOLINT bool has_state{false};
bool state{false}; // NOLINT bool state{false};
bool has_brightness{false}; // NOLINT bool has_brightness{false};
float brightness{0.0f}; // NOLINT float brightness{0.0f};
bool has_rgb{false}; // NOLINT bool has_rgb{false};
float red{0.0f}; // NOLINT float red{0.0f};
float green{0.0f}; // NOLINT float green{0.0f};
float blue{0.0f}; // NOLINT float blue{0.0f};
bool has_white{false}; // NOLINT bool has_white{false};
float white{0.0f}; // NOLINT float white{0.0f};
bool has_color_temperature{false}; // NOLINT bool has_color_temperature{false};
float color_temperature{0.0f}; // NOLINT float color_temperature{0.0f};
bool has_transition_length{false}; // NOLINT bool has_transition_length{false};
uint32_t transition_length{0}; // NOLINT uint32_t transition_length{0};
bool has_flash_length{false}; // NOLINT bool has_flash_length{false};
uint32_t flash_length{0}; // NOLINT uint32_t flash_length{0};
bool has_effect{false}; // NOLINT bool has_effect{false};
std::string effect{}; // NOLINT std::string effect{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -399,15 +415,16 @@ class LightCommandRequest : public ProtoMessage {
}; };
class ListEntitiesSensorResponse : public ProtoMessage { class ListEntitiesSensorResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
std::string icon{}; // NOLINT std::string icon{};
std::string unit_of_measurement{}; // NOLINT std::string unit_of_measurement{};
int32_t accuracy_decimals{0}; // NOLINT int32_t accuracy_decimals{0};
bool force_update{false}; // NOLINT bool force_update{false};
std::string device_class{}; // NOLINT std::string device_class{};
enums::SensorStateClass state_class{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -418,9 +435,9 @@ class ListEntitiesSensorResponse : public ProtoMessage {
}; };
class SensorStateResponse : public ProtoMessage { class SensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
float state{0.0f}; // NOLINT float state{0.0f};
bool missing_state{false}; // NOLINT bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -430,12 +447,12 @@ class SensorStateResponse : public ProtoMessage {
}; };
class ListEntitiesSwitchResponse : public ProtoMessage { class ListEntitiesSwitchResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
std::string icon{}; // NOLINT std::string icon{};
bool assumed_state{false}; // NOLINT bool assumed_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -446,8 +463,8 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
}; };
class SwitchStateResponse : public ProtoMessage { class SwitchStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -457,8 +474,8 @@ class SwitchStateResponse : public ProtoMessage {
}; };
class SwitchCommandRequest : public ProtoMessage { class SwitchCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool state{false}; // NOLINT bool state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -468,11 +485,11 @@ class SwitchCommandRequest : public ProtoMessage {
}; };
class ListEntitiesTextSensorResponse : public ProtoMessage { class ListEntitiesTextSensorResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
std::string icon{}; // NOLINT std::string icon{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -482,9 +499,9 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
}; };
class TextSensorStateResponse : public ProtoMessage { class TextSensorStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string state{}; // NOLINT std::string state{};
bool missing_state{false}; // NOLINT bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -495,8 +512,8 @@ class TextSensorStateResponse : public ProtoMessage {
}; };
class SubscribeLogsRequest : public ProtoMessage { class SubscribeLogsRequest : public ProtoMessage {
public: public:
enums::LogLevel level{}; // NOLINT enums::LogLevel level{};
bool dump_config{false}; // NOLINT bool dump_config{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -505,10 +522,10 @@ class SubscribeLogsRequest : public ProtoMessage {
}; };
class SubscribeLogsResponse : public ProtoMessage { class SubscribeLogsResponse : public ProtoMessage {
public: public:
enums::LogLevel level{}; // NOLINT enums::LogLevel level{};
std::string tag{}; // NOLINT std::string tag{};
std::string message{}; // NOLINT std::string message{};
bool send_failed{false}; // NOLINT bool send_failed{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -525,8 +542,8 @@ class SubscribeHomeassistantServicesRequest : public ProtoMessage {
}; };
class HomeassistantServiceMap : public ProtoMessage { class HomeassistantServiceMap : public ProtoMessage {
public: public:
std::string key{}; // NOLINT std::string key{};
std::string value{}; // NOLINT std::string value{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -535,11 +552,11 @@ class HomeassistantServiceMap : public ProtoMessage {
}; };
class HomeassistantServiceResponse : public ProtoMessage { class HomeassistantServiceResponse : public ProtoMessage {
public: public:
std::string service{}; // NOLINT std::string service{};
std::vector<HomeassistantServiceMap> data{}; // NOLINT std::vector<HomeassistantServiceMap> data{};
std::vector<HomeassistantServiceMap> data_template{}; // NOLINT std::vector<HomeassistantServiceMap> data_template{};
std::vector<HomeassistantServiceMap> variables{}; // NOLINT std::vector<HomeassistantServiceMap> variables{};
bool is_event{false}; // NOLINT bool is_event{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -556,7 +573,8 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
}; };
class SubscribeHomeAssistantStateResponse : public ProtoMessage { class SubscribeHomeAssistantStateResponse : public ProtoMessage {
public: public:
std::string entity_id{}; // NOLINT std::string entity_id{};
std::string attribute{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -565,8 +583,9 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
}; };
class HomeAssistantStateResponse : public ProtoMessage { class HomeAssistantStateResponse : public ProtoMessage {
public: public:
std::string entity_id{}; // NOLINT std::string entity_id{};
std::string state{}; // NOLINT std::string state{};
std::string attribute{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -582,7 +601,7 @@ class GetTimeRequest : public ProtoMessage {
}; };
class GetTimeResponse : public ProtoMessage { class GetTimeResponse : public ProtoMessage {
public: public:
uint32_t epoch_seconds{0}; // NOLINT uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -591,8 +610,8 @@ class GetTimeResponse : public ProtoMessage {
}; };
class ListEntitiesServicesArgument : public ProtoMessage { class ListEntitiesServicesArgument : public ProtoMessage {
public: public:
std::string name{}; // NOLINT std::string name{};
enums::ServiceArgType type{}; // NOLINT enums::ServiceArgType type{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -602,9 +621,9 @@ class ListEntitiesServicesArgument : public ProtoMessage {
}; };
class ListEntitiesServicesResponse : public ProtoMessage { class ListEntitiesServicesResponse : public ProtoMessage {
public: public:
std::string name{}; // NOLINT std::string name{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::vector<ListEntitiesServicesArgument> args{}; // NOLINT std::vector<ListEntitiesServicesArgument> args{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -614,15 +633,15 @@ class ListEntitiesServicesResponse : public ProtoMessage {
}; };
class ExecuteServiceArgument : public ProtoMessage { class ExecuteServiceArgument : public ProtoMessage {
public: public:
bool bool_{false}; // NOLINT bool bool_{false};
int32_t legacy_int{0}; // NOLINT int32_t legacy_int{0};
float float_{0.0f}; // NOLINT float float_{0.0f};
std::string string_{}; // NOLINT std::string string_{};
int32_t int_{0}; // NOLINT int32_t int_{0};
std::vector<bool> bool_array{}; // NOLINT std::vector<bool> bool_array{};
std::vector<int32_t> int_array{}; // NOLINT std::vector<int32_t> int_array{};
std::vector<float> float_array{}; // NOLINT std::vector<float> float_array{};
std::vector<std::string> string_array{}; // NOLINT std::vector<std::string> string_array{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -633,8 +652,8 @@ class ExecuteServiceArgument : public ProtoMessage {
}; };
class ExecuteServiceRequest : public ProtoMessage { class ExecuteServiceRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
std::vector<ExecuteServiceArgument> args{}; // NOLINT std::vector<ExecuteServiceArgument> args{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -644,10 +663,10 @@ class ExecuteServiceRequest : public ProtoMessage {
}; };
class ListEntitiesCameraResponse : public ProtoMessage { class ListEntitiesCameraResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -657,9 +676,9 @@ class ListEntitiesCameraResponse : public ProtoMessage {
}; };
class CameraImageResponse : public ProtoMessage { class CameraImageResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string data{}; // NOLINT std::string data{};
bool done{false}; // NOLINT bool done{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -670,8 +689,8 @@ class CameraImageResponse : public ProtoMessage {
}; };
class CameraImageRequest : public ProtoMessage { class CameraImageRequest : public ProtoMessage {
public: public:
bool single{false}; // NOLINT bool single{false};
bool stream{false}; // NOLINT bool stream{false};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -680,20 +699,23 @@ class CameraImageRequest : public ProtoMessage {
}; };
class ListEntitiesClimateResponse : public ProtoMessage { class ListEntitiesClimateResponse : public ProtoMessage {
public: public:
std::string object_id{}; // NOLINT std::string object_id{};
uint32_t key{0}; // NOLINT uint32_t key{0};
std::string name{}; // NOLINT std::string name{};
std::string unique_id{}; // NOLINT std::string unique_id{};
bool supports_current_temperature{false}; // NOLINT bool supports_current_temperature{false};
bool supports_two_point_target_temperature{false}; // NOLINT bool supports_two_point_target_temperature{false};
std::vector<enums::ClimateMode> supported_modes{}; // NOLINT std::vector<enums::ClimateMode> supported_modes{};
float visual_min_temperature{0.0f}; // NOLINT float visual_min_temperature{0.0f};
float visual_max_temperature{0.0f}; // NOLINT float visual_max_temperature{0.0f};
float visual_temperature_step{0.0f}; // NOLINT float visual_temperature_step{0.0f};
bool supports_away{false}; // NOLINT bool supports_away{false};
bool supports_action{false}; // NOLINT bool supports_action{false};
std::vector<enums::ClimateFanMode> supported_fan_modes{}; // NOLINT std::vector<enums::ClimateFanMode> supported_fan_modes{};
std::vector<enums::ClimateSwingMode> supported_swing_modes{}; // NOLINT std::vector<enums::ClimateSwingMode> supported_swing_modes{};
std::vector<std::string> supported_custom_fan_modes{};
std::vector<enums::ClimatePreset> supported_presets{};
std::vector<std::string> supported_custom_presets{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -704,45 +726,56 @@ class ListEntitiesClimateResponse : public ProtoMessage {
}; };
class ClimateStateResponse : public ProtoMessage { class ClimateStateResponse : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
enums::ClimateMode mode{}; // NOLINT enums::ClimateMode mode{};
float current_temperature{0.0f}; // NOLINT float current_temperature{0.0f};
float target_temperature{0.0f}; // NOLINT float target_temperature{0.0f};
float target_temperature_low{0.0f}; // NOLINT float target_temperature_low{0.0f};
float target_temperature_high{0.0f}; // NOLINT float target_temperature_high{0.0f};
bool away{false}; // NOLINT bool away{false};
enums::ClimateAction action{}; // NOLINT enums::ClimateAction action{};
enums::ClimateFanMode fan_mode{}; // NOLINT enums::ClimateFanMode fan_mode{};
enums::ClimateSwingMode swing_mode{}; // NOLINT enums::ClimateSwingMode swing_mode{};
std::string custom_fan_mode{};
enums::ClimatePreset preset{};
std::string custom_preset{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
protected: protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class ClimateCommandRequest : public ProtoMessage { class ClimateCommandRequest : public ProtoMessage {
public: public:
uint32_t key{0}; // NOLINT uint32_t key{0};
bool has_mode{false}; // NOLINT bool has_mode{false};
enums::ClimateMode mode{}; // NOLINT enums::ClimateMode mode{};
bool has_target_temperature{false}; // NOLINT bool has_target_temperature{false};
float target_temperature{0.0f}; // NOLINT float target_temperature{0.0f};
bool has_target_temperature_low{false}; // NOLINT bool has_target_temperature_low{false};
float target_temperature_low{0.0f}; // NOLINT float target_temperature_low{0.0f};
bool has_target_temperature_high{false}; // NOLINT bool has_target_temperature_high{false};
float target_temperature_high{0.0f}; // NOLINT float target_temperature_high{0.0f};
bool has_away{false}; // NOLINT bool has_away{false};
bool away{false}; // NOLINT bool away{false};
bool has_fan_mode{false}; // NOLINT bool has_fan_mode{false};
enums::ClimateFanMode fan_mode{}; // NOLINT enums::ClimateFanMode fan_mode{};
bool has_swing_mode{false}; // NOLINT bool has_swing_mode{false};
enums::ClimateSwingMode swing_mode{}; // NOLINT enums::ClimateSwingMode swing_mode{};
bool has_custom_fan_mode{false};
std::string custom_fan_mode{};
bool has_preset{false};
enums::ClimatePreset preset{};
bool has_custom_preset{false};
std::string custom_preset{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
protected: protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };

View file

@ -208,9 +208,11 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
} }
} }
APIServer::APIServer() { global_api_server = this; } APIServer::APIServer() { global_api_server = this; }
void APIServer::subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f) { void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->state_subs_.push_back(HomeAssistantStateSubscription{ this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = std::move(entity_id), .entity_id = std::move(entity_id),
.attribute = std::move(attribute),
.callback = std::move(f), .callback = std::move(f),
}); });
} }

View file

@ -71,10 +71,12 @@ class APIServer : public Component, public Controller {
struct HomeAssistantStateSubscription { struct HomeAssistantStateSubscription {
std::string entity_id; std::string entity_id;
optional<std::string> attribute;
std::function<void(std::string)> callback; std::function<void(std::string)> callback;
}; };
void subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f); void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f);
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const; const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; } const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }

View file

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

View file

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

View file

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

View file

@ -5,6 +5,7 @@ from esphome.const import (
CONF_DISTANCE, CONF_DISTANCE,
CONF_LIGHTNING_ENERGY, CONF_LIGHTNING_ENERGY,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
UNIT_KILOMETER, UNIT_KILOMETER,
UNIT_EMPTY, UNIT_EMPTY,
ICON_SIGNAL_DISTANCE_VARIANT, ICON_SIGNAL_DISTANCE_VARIANT,
@ -18,24 +19,28 @@ CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
cv.Optional(CONF_DISTANCE): sensor.sensor_schema( cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY UNIT_KILOMETER,
ICON_SIGNAL_DISTANCE_VARIANT,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
), ),
cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema( cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
), ),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
def to_code(config): async def to_code(config):
hub = yield cg.get_variable(config[CONF_AS3935_ID]) hub = await cg.get_variable(config[CONF_AS3935_ID])
if CONF_DISTANCE in config: if CONF_DISTANCE in config:
conf = config[CONF_DISTANCE] conf = config[CONF_DISTANCE]
distance_sensor = yield sensor.new_sensor(conf) distance_sensor = await sensor.new_sensor(conf)
cg.add(hub.set_distance_sensor(distance_sensor)) cg.add(hub.set_distance_sensor(distance_sensor))
if CONF_LIGHTNING_ENERGY in config: if CONF_LIGHTNING_ENERGY in config:
conf = config[CONF_LIGHTNING_ENERGY] conf = config[CONF_LIGHTNING_ENERGY]
lightning_energy_sensor = yield sensor.new_sensor(conf) lightning_energy_sensor = await sensor.new_sensor(conf)
cg.add(hub.set_energy_sensor(lightning_energy_sensor)) cg.add(hub.set_energy_sensor(lightning_energy_sensor))

View file

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

View file

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

View file

@ -33,7 +33,7 @@ void SPIAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
uint8_t SPIAS3935Component::read_register(uint8_t reg) { uint8_t SPIAS3935Component::read_register(uint8_t reg) {
uint8_t value = 0; uint8_t value = 0;
this->enable(); this->enable();
this->write_byte(reg |= SPI_READ_M); this->write_byte(reg | SPI_READ_M);
value = this->read_byte(); value = this->read_byte();
// According to datsheet, the chip select must be written HIGH, LOW, HIGH // According to datsheet, the chip select must be written HIGH, LOW, HIGH
// to correctly end the READ command. // to correctly end the READ command.

View file

@ -6,7 +6,7 @@ CODEOWNERS = ["@OttoWinter"]
@coroutine_with_priority(200.0) @coroutine_with_priority(200.0)
def to_code(config): async def to_code(config):
if CORE.is_esp32: if CORE.is_esp32:
# https://github.com/esphome/AsyncTCP/blob/master/library.json # https://github.com/esphome/AsyncTCP/blob/master/library.json
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2") cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")

View file

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

View file

@ -3,6 +3,7 @@ import esphome.config_validation as cv
from esphome.components import sensor, spi from esphome.components import sensor, spi
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_REACTIVE_POWER,
CONF_VOLTAGE, CONF_VOLTAGE,
CONF_CURRENT, CONF_CURRENT,
CONF_POWER, CONF_POWER,
@ -20,6 +21,7 @@ from esphome.const import (
ICON_EMPTY, ICON_EMPTY,
ICON_LIGHTBULB, ICON_LIGHTBULB,
ICON_CURRENT_AC, ICON_CURRENT_AC,
STATE_CLASS_MEASUREMENT,
UNIT_HERTZ, UNIT_HERTZ,
UNIT_VOLT, UNIT_VOLT,
UNIT_AMPERE, UNIT_AMPERE,
@ -34,7 +36,6 @@ CONF_PHASE_A = "phase_a"
CONF_PHASE_B = "phase_b" CONF_PHASE_B = "phase_b"
CONF_PHASE_C = "phase_c" CONF_PHASE_C = "phase_c"
CONF_REACTIVE_POWER = "reactive_power"
CONF_LINE_FREQUENCY = "line_frequency" CONF_LINE_FREQUENCY = "line_frequency"
CONF_CHIP_TEMPERATURE = "chip_temperature" CONF_CHIP_TEMPERATURE = "chip_temperature"
CONF_GAIN_PGA = "gain_pga" CONF_GAIN_PGA = "gain_pga"
@ -63,25 +64,37 @@ ATM90E32Component = atm90e32_ns.class_(
ATM90E32_PHASE_SCHEMA = cv.Schema( ATM90E32_PHASE_SCHEMA = cv.Schema(
{ {
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE UNIT_VOLT,
ICON_EMPTY,
2,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_CURRENT): sensor.sensor_schema( cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_POWER): sensor.sensor_schema( cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema( cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE, ICON_LIGHTBULB, 2, DEVICE_CLASS_EMPTY UNIT_VOLT_AMPS_REACTIVE,
ICON_LIGHTBULB,
2,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema( cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_POWER_FACTOR UNIT_EMPTY,
ICON_EMPTY,
2,
DEVICE_CLASS_POWER_FACTOR,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema( cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema( cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t, cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t, cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
@ -96,10 +109,18 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY UNIT_HERTZ,
ICON_CURRENT_AC,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
), ),
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum( cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(
@ -113,10 +134,10 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield spi.register_spi_device(var, config) await spi.register_spi_device(var, config)
for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]): for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]):
if phase not in config: if phase not in config:
@ -125,31 +146,31 @@ def to_code(config):
cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE])) cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE]))
cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT])) cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT]))
if CONF_VOLTAGE in conf: if CONF_VOLTAGE in conf:
sens = yield sensor.new_sensor(conf[CONF_VOLTAGE]) sens = await sensor.new_sensor(conf[CONF_VOLTAGE])
cg.add(var.set_voltage_sensor(i, sens)) cg.add(var.set_voltage_sensor(i, sens))
if CONF_CURRENT in conf: if CONF_CURRENT in conf:
sens = yield sensor.new_sensor(conf[CONF_CURRENT]) sens = await sensor.new_sensor(conf[CONF_CURRENT])
cg.add(var.set_current_sensor(i, sens)) cg.add(var.set_current_sensor(i, sens))
if CONF_POWER in conf: if CONF_POWER in conf:
sens = yield sensor.new_sensor(conf[CONF_POWER]) sens = await sensor.new_sensor(conf[CONF_POWER])
cg.add(var.set_power_sensor(i, sens)) cg.add(var.set_power_sensor(i, sens))
if CONF_REACTIVE_POWER in conf: if CONF_REACTIVE_POWER in conf:
sens = yield sensor.new_sensor(conf[CONF_REACTIVE_POWER]) sens = await sensor.new_sensor(conf[CONF_REACTIVE_POWER])
cg.add(var.set_reactive_power_sensor(i, sens)) cg.add(var.set_reactive_power_sensor(i, sens))
if CONF_POWER_FACTOR in conf: if CONF_POWER_FACTOR in conf:
sens = yield sensor.new_sensor(conf[CONF_POWER_FACTOR]) sens = await sensor.new_sensor(conf[CONF_POWER_FACTOR])
cg.add(var.set_power_factor_sensor(i, sens)) cg.add(var.set_power_factor_sensor(i, sens))
if CONF_FORWARD_ACTIVE_ENERGY in conf: if CONF_FORWARD_ACTIVE_ENERGY in conf:
sens = yield sensor.new_sensor(conf[CONF_FORWARD_ACTIVE_ENERGY]) sens = await sensor.new_sensor(conf[CONF_FORWARD_ACTIVE_ENERGY])
cg.add(var.set_forward_active_energy_sensor(i, sens)) cg.add(var.set_forward_active_energy_sensor(i, sens))
if CONF_REVERSE_ACTIVE_ENERGY in conf: if CONF_REVERSE_ACTIVE_ENERGY in conf:
sens = yield sensor.new_sensor(conf[CONF_REVERSE_ACTIVE_ENERGY]) sens = await sensor.new_sensor(conf[CONF_REVERSE_ACTIVE_ENERGY])
cg.add(var.set_reverse_active_energy_sensor(i, sens)) cg.add(var.set_reverse_active_energy_sensor(i, sens))
if CONF_FREQUENCY in config: if CONF_FREQUENCY in config:
sens = yield sensor.new_sensor(config[CONF_FREQUENCY]) sens = await sensor.new_sensor(config[CONF_FREQUENCY])
cg.add(var.set_freq_sensor(sens)) cg.add(var.set_freq_sensor(sens))
if CONF_CHIP_TEMPERATURE in config: if CONF_CHIP_TEMPERATURE in config:
sens = yield sensor.new_sensor(config[CONF_CHIP_TEMPERATURE]) sens = await sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
cg.add(var.set_chip_temperature_sensor(sens)) cg.add(var.set_chip_temperature_sensor(sens))
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY])) cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES])) cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))

View file

@ -12,6 +12,7 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ICON_EMPTY, ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_PERCENT, UNIT_PERCENT,
UNIT_VOLT, UNIT_VOLT,
@ -32,16 +33,28 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(BParasite), cv.GenerateID(): cv.declare_id(BParasite),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema( cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_MOISTURE): sensor.sensor_schema( cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
), ),
} }
) )
@ -50,10 +63,10 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config) await esp32_ble_tracker.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
@ -64,5 +77,5 @@ def to_code(config):
(CONF_MOISTURE, var.set_soil_moisture), (CONF_MOISTURE, var.set_soil_moisture),
]: ]:
if config_key in config: if config_key in config:
sens = yield sensor.new_sensor(config[config_key]) sens = await sensor.new_sensor(config[config_key])
cg.add(setter(sens)) cg.add(setter(sens))

View file

@ -39,12 +39,12 @@ CONFIG_SCHEMA = cv.All(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield climate.register_climate(var, config) await climate.register_climate(var, config)
sens = yield cg.get_variable(config[CONF_SENSOR]) sens = await cg.get_variable(config[CONF_SENSOR])
cg.add(var.set_sensor(sens)) cg.add(var.set_sensor(sens))
normal_config = BangBangClimateTargetTempConfig( normal_config = BangBangClimateTargetTempConfig(
@ -53,17 +53,17 @@ def to_code(config):
) )
cg.add(var.set_normal_config(normal_config)) cg.add(var.set_normal_config(normal_config))
yield automation.build_automation( await automation.build_automation(
var.get_idle_trigger(), [], config[CONF_IDLE_ACTION] var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]
) )
if CONF_COOL_ACTION in config: if CONF_COOL_ACTION in config:
yield automation.build_automation( await automation.build_automation(
var.get_cool_trigger(), [], config[CONF_COOL_ACTION] var.get_cool_trigger(), [], config[CONF_COOL_ACTION]
) )
cg.add(var.set_supports_cool(True)) cg.add(var.set_supports_cool(True))
if CONF_HEAT_ACTION in config: if CONF_HEAT_ACTION in config:
yield automation.build_automation( await automation.build_automation(
var.get_heat_trigger(), [], config[CONF_HEAT_ACTION] var.get_heat_trigger(), [], config[CONF_HEAT_ACTION]
) )
cg.add(var.set_supports_heat(True)) cg.add(var.set_supports_heat(True))

View file

@ -6,6 +6,7 @@ from esphome.const import (
CONF_RESOLUTION, CONF_RESOLUTION,
DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_ILLUMINANCE,
ICON_EMPTY, ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_LUX, UNIT_LUX,
CONF_MEASUREMENT_DURATION, CONF_MEASUREMENT_DURATION,
) )
@ -26,7 +27,9 @@ BH1750Sensor = bh1750_ns.class_(
CONF_MEASUREMENT_TIME = "measurement_time" CONF_MEASUREMENT_TIME = "measurement_time"
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE) sensor.sensor_schema(
UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE, STATE_CLASS_MEASUREMENT
)
.extend( .extend(
{ {
cv.GenerateID(): cv.declare_id(BH1750Sensor), cv.GenerateID(): cv.declare_id(BH1750Sensor),
@ -46,11 +49,11 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield sensor.register_sensor(var, config) await sensor.register_sensor(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
cg.add(var.set_resolution(config[CONF_RESOLUTION])) cg.add(var.set_resolution(config[CONF_RESOLUTION]))
cg.add(var.set_measurement_duration(config[CONF_MEASUREMENT_DURATION])) cg.add(var.set_measurement_duration(config[CONF_MEASUREMENT_DURATION]))

View file

@ -21,19 +21,19 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
fan_ = yield fan.create_fan_state(config) fan_ = await fan.create_fan_state(config)
cg.add(var.set_fan(fan_)) cg.add(var.set_fan(fan_))
output_ = yield cg.get_variable(config[CONF_OUTPUT]) output_ = await cg.get_variable(config[CONF_OUTPUT])
cg.add(var.set_output(output_)) cg.add(var.set_output(output_))
if CONF_OSCILLATION_OUTPUT in config: if CONF_OSCILLATION_OUTPUT in config:
oscillation_output = yield cg.get_variable(config[CONF_OSCILLATION_OUTPUT]) oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
cg.add(var.set_oscillating(oscillation_output)) cg.add(var.set_oscillating(oscillation_output))
if CONF_DIRECTION_OUTPUT in config: if CONF_DIRECTION_OUTPUT in config:
direction_output = yield cg.get_variable(config[CONF_DIRECTION_OUTPUT]) direction_output = await cg.get_variable(config[CONF_DIRECTION_OUTPUT])
cg.add(var.set_direction(direction_output)) cg.add(var.set_direction(direction_output))

View file

@ -14,9 +14,9 @@ CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
yield light.register_light(var, config) await light.register_light(var, config)
out = yield cg.get_variable(config[CONF_OUTPUT]) out = await cg.get_variable(config[CONF_OUTPUT])
cg.add(var.set_output(out)) cg.add(var.set_output(out))

View file

@ -4,6 +4,7 @@ from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt from esphome.components import mqtt
from esphome.const import ( from esphome.const import (
CONF_DELAY,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_FILTERS, CONF_FILTERS,
CONF_ID, CONF_ID,
@ -50,7 +51,7 @@ from esphome.const import (
DEVICE_CLASS_VIBRATION, DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW, DEVICE_CLASS_WINDOW,
) )
from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.util import Registry from esphome.util import Registry
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
@ -120,6 +121,7 @@ DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Co
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component) DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component) DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter) InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter) LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
FILTER_REGISTRY = Registry() FILTER_REGISTRY = Registry()
@ -127,43 +129,88 @@ validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
@FILTER_REGISTRY.register("invert", InvertFilter, {}) @FILTER_REGISTRY.register("invert", InvertFilter, {})
def invert_filter_to_code(config, filter_id): async def invert_filter_to_code(config, filter_id):
yield cg.new_Pvariable(filter_id) return cg.new_Pvariable(filter_id)
@FILTER_REGISTRY.register( @FILTER_REGISTRY.register(
"delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds "delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds
) )
def delayed_on_off_filter_to_code(config, filter_id): async def delayed_on_off_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config) var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(var, {}) await cg.register_component(var, {})
yield var return var
@FILTER_REGISTRY.register( @FILTER_REGISTRY.register(
"delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds "delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds
) )
def delayed_on_filter_to_code(config, filter_id): async def delayed_on_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config) var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(var, {}) await cg.register_component(var, {})
yield var return var
@FILTER_REGISTRY.register( @FILTER_REGISTRY.register(
"delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds "delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds
) )
def delayed_off_filter_to_code(config, filter_id): async def delayed_off_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config) var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(var, {}) await cg.register_component(var, {})
yield var return var
CONF_TIME_OFF = "time_off"
CONF_TIME_ON = "time_on"
DEFAULT_DELAY = "1s"
DEFAULT_TIME_OFF = "100ms"
DEFAULT_TIME_ON = "900ms"
@FILTER_REGISTRY.register(
"autorepeat",
AutorepeatFilter,
cv.All(
cv.ensure_list(
{
cv.Optional(
CONF_DELAY, default=DEFAULT_DELAY
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_TIME_OFF, default=DEFAULT_TIME_OFF
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_TIME_ON, default=DEFAULT_TIME_ON
): cv.positive_time_period_milliseconds,
}
),
),
)
async def autorepeat_filter_to_code(config, filter_id):
timings = []
if len(config) > 0:
for conf in config:
timings.append((conf[CONF_DELAY], conf[CONF_TIME_OFF], conf[CONF_TIME_ON]))
else:
timings.append(
(
cv.time_period_str_unit(DEFAULT_DELAY).total_milliseconds,
cv.time_period_str_unit(DEFAULT_TIME_OFF).total_milliseconds,
cv.time_period_str_unit(DEFAULT_TIME_ON).total_milliseconds,
)
)
var = cg.new_Pvariable(filter_id, timings)
await cg.register_component(var, {})
return var
@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda) @FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda)
def lambda_filter_to_code(config, filter_id): async def lambda_filter_to_code(config, filter_id):
lambda_ = yield cg.process_lambda( lambda_ = await cg.process_lambda(
config, [(bool, "x")], return_type=cg.optional.template(bool) config, [(bool, "x")], return_type=cg.optional.template(bool)
) )
yield cg.new_Pvariable(filter_id, lambda_) return cg.new_Pvariable(filter_id, lambda_)
MULTI_CLICK_TIMING_SCHEMA = cv.Schema( MULTI_CLICK_TIMING_SCHEMA = cv.Schema(
@ -334,8 +381,7 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
) )
@coroutine async def setup_binary_sensor_core_(var, config):
def setup_binary_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME])) cg.add(var.set_name(config[CONF_NAME]))
if CONF_INTERNAL in config: if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL])) cg.add(var.set_internal(config[CONF_INTERNAL]))
@ -344,28 +390,28 @@ def setup_binary_sensor_core_(var, config):
if CONF_INVERTED in config: if CONF_INVERTED in config:
cg.add(var.set_inverted(config[CONF_INVERTED])) cg.add(var.set_inverted(config[CONF_INVERTED]))
if CONF_FILTERS in config: if CONF_FILTERS in config:
filters = yield cg.build_registry_list(FILTER_REGISTRY, config[CONF_FILTERS]) filters = await cg.build_registry_list(FILTER_REGISTRY, config[CONF_FILTERS])
cg.add(var.add_filters(filters)) cg.add(var.add_filters(filters))
for conf in config.get(CONF_ON_PRESS, []): for conf in config.get(CONF_ON_PRESS, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_RELEASE, []): for conf in config.get(CONF_ON_RELEASE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_CLICK, []): for conf in config.get(CONF_ON_CLICK, []):
trigger = cg.new_Pvariable( trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH] conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
) )
yield automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_DOUBLE_CLICK, []): for conf in config.get(CONF_ON_DOUBLE_CLICK, []):
trigger = cg.new_Pvariable( trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH] conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
) )
yield automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_MULTI_CLICK, []): for conf in config.get(CONF_ON_MULTI_CLICK, []):
timings = [] timings = []
@ -381,31 +427,29 @@ def setup_binary_sensor_core_(var, config):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings)
if CONF_INVALID_COOLDOWN in conf: if CONF_INVALID_COOLDOWN in conf:
cg.add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN])) cg.add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN]))
yield cg.register_component(trigger, conf) await cg.register_component(trigger, conf)
yield automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_STATE, []): for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [(bool, "x")], conf) await automation.build_automation(trigger, [(bool, "x")], conf)
if CONF_MQTT_ID in config: if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
yield mqtt.register_mqtt_component(mqtt_, config) await mqtt.register_mqtt_component(mqtt_, config)
@coroutine async def register_binary_sensor(var, config):
def register_binary_sensor(var, config):
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var) var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_binary_sensor(var)) cg.add(cg.App.register_binary_sensor(var))
yield setup_binary_sensor_core_(var, config) await setup_binary_sensor_core_(var, config)
@coroutine async def new_binary_sensor(config):
def new_binary_sensor(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME]) var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
yield register_binary_sensor(var, config) await register_binary_sensor(var, config)
yield var return var
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id( BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
@ -422,20 +466,20 @@ BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
@automation.register_condition( @automation.register_condition(
"binary_sensor.is_on", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA "binary_sensor.is_on", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
) )
def binary_sensor_is_on_to_code(config, condition_id, template_arg, args): async def binary_sensor_is_on_to_code(config, condition_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(condition_id, template_arg, paren, True) return cg.new_Pvariable(condition_id, template_arg, paren, True)
@automation.register_condition( @automation.register_condition(
"binary_sensor.is_off", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA "binary_sensor.is_off", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
) )
def binary_sensor_is_off_to_code(config, condition_id, template_arg, args): async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(condition_id, template_arg, paren, False) return cg.new_Pvariable(condition_id, template_arg, paren, False)
@coroutine_with_priority(100.0) @coroutine_with_priority(100.0)
def to_code(config): async def to_code(config):
cg.add_define("USE_BINARY_SENSOR") cg.add_define("USE_BINARY_SENSOR")
cg.add_global(binary_sensor_ns.using) cg.add_global(binary_sensor_ns.using)

View file

@ -64,6 +64,50 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; } optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
AutorepeatFilter::AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings) : timings_(timings) {}
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
if (value) {
// Ignore if already running
if (this->active_timing_ != 0)
return {};
this->next_timing_();
return true;
} else {
this->cancel_timeout("TIMING");
this->cancel_timeout("ON_OFF");
this->active_timing_ = 0;
return false;
}
}
void AutorepeatFilter::next_timing_() {
// Entering this method
// 1st time: starts waiting the first delay
// 2nd time: starts waiting the second delay and starts toggling with the first time_off / _on
// last time: no delay to start but have to bump the index to reflect the last
if (this->active_timing_ < this->timings_.size())
this->set_timeout("TIMING", this->timings_[this->active_timing_].delay, [this]() { this->next_timing_(); });
if (this->active_timing_ <= this->timings_.size()) {
this->active_timing_++;
}
if (this->active_timing_ == 2)
this->next_value_(false);
// Leaving this method: if the toggling is started, it has to use [active_timing_ - 2] for the intervals
}
void AutorepeatFilter::next_value_(bool val) {
const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
this->output(val, false); // This is at least the second one so not initial
this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
}
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
LambdaFilter::LambdaFilter(const std::function<optional<bool>(bool)> &f) : f_(f) {} LambdaFilter::LambdaFilter(const std::function<optional<bool>(bool)> &f) : f_(f) {}
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); } optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }

View file

@ -66,6 +66,33 @@ class InvertFilter : public Filter {
optional<bool> new_value(bool value, bool is_initial) override; optional<bool> new_value(bool value, bool is_initial) override;
}; };
struct AutorepeatFilterTiming {
AutorepeatFilterTiming(uint32_t delay, uint32_t off, uint32_t on) {
this->delay = delay;
this->time_off = off;
this->time_on = on;
}
uint32_t delay;
uint32_t time_off;
uint32_t time_on;
};
class AutorepeatFilter : public Filter, public Component {
public:
explicit AutorepeatFilter(const std::vector<AutorepeatFilterTiming> &timings);
optional<bool> new_value(bool value, bool is_initial) override;
float get_setup_priority() const override;
protected:
void next_timing_();
void next_value_(bool val);
std::vector<AutorepeatFilterTiming> timings_;
uint8_t active_timing_{0};
};
class LambdaFilter : public Filter { class LambdaFilter : public Filter {
public: public:
explicit LambdaFilter(const std::function<optional<bool>(bool)> &f); explicit LambdaFilter(const std::function<optional<bool>(bool)> &f);

View file

@ -12,6 +12,7 @@ from esphome.const import (
ICON_CHECK_CIRCLE_OUTLINE, ICON_CHECK_CIRCLE_OUTLINE,
CONF_BINARY_SENSOR, CONF_BINARY_SENSOR,
CONF_GROUP, CONF_GROUP,
STATE_CLASS_NONE,
) )
DEPENDENCIES = ["binary_sensor"] DEPENDENCIES = ["binary_sensor"]
@ -34,7 +35,11 @@ entry = {
CONFIG_SCHEMA = cv.typed_schema( CONFIG_SCHEMA = cv.typed_schema(
{ {
CONF_GROUP: sensor.sensor_schema( CONF_GROUP: sensor.sensor_schema(
UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0, DEVICE_CLASS_EMPTY UNIT_EMPTY,
ICON_CHECK_CIRCLE_OUTLINE,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_NONE,
).extend( ).extend(
{ {
cv.GenerateID(): cv.declare_id(BinarySensorMap), cv.GenerateID(): cv.declare_id(BinarySensorMap),
@ -48,14 +53,14 @@ CONFIG_SCHEMA = cv.typed_schema(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield sensor.register_sensor(var, config) await sensor.register_sensor(var, config)
constant = SENSOR_MAP_TYPES[config[CONF_TYPE]] constant = SENSOR_MAP_TYPES[config[CONF_TYPE]]
cg.add(var.set_sensor_type(constant)) cg.add(var.set_sensor_type(constant))
for ch in config[CONF_CHANNELS]: for ch in config[CONF_CHANNELS]:
input_var = yield cg.get_variable(ch[CONF_BINARY_SENSOR]) input_var = await cg.get_variable(ch[CONF_BINARY_SENSOR])
cg.add(var.add_channel(input_var, ch[CONF_VALUE])) cg.add(var.add_channel(input_var, ch[CONF_VALUE]))

View file

@ -9,7 +9,6 @@ from esphome.const import (
CONF_ON_DISCONNECT, CONF_ON_DISCONNECT,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
) )
from esphome.core import coroutine
from esphome import automation from esphome import automation
CODEOWNERS = ["@buxtronix"] CODEOWNERS = ["@buxtronix"]
@ -68,20 +67,19 @@ BLE_CLIENT_SCHEMA = cv.Schema(
) )
@coroutine async def register_ble_node(var, config):
def register_ble_node(var, config): parent = await cg.get_variable(config[CONF_BLE_CLIENT_ID])
parent = yield cg.get_variable(config[CONF_BLE_CLIENT_ID])
cg.add(parent.register_ble_node(var)) cg.add(parent.register_ble_node(var))
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield esp32_ble_tracker.register_client(var, config) await esp32_ble_tracker.register_client(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
for conf in config.get(CONF_ON_CONNECT, []): for conf in config.get(CONF_ON_CONNECT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_DISCONNECT, []): for conf in config.get(CONF_ON_DISCONNECT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)

View file

@ -94,7 +94,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
esp_ble_gattc_cb_param_t *param) { esp_ble_gattc_cb_param_t *param) {
if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id) if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id)
return; return;
if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && gattc_if != this->gattc_if) if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
return; return;
bool all_established = this->all_nodes_established(); bool all_established = this->all_nodes_established();

View file

@ -4,6 +4,8 @@ from esphome.components import sensor, ble_client, esp32_ble_tracker
from esphome.const import ( from esphome.const import (
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
CONF_ID, CONF_ID,
CONF_LAMBDA,
STATE_CLASS_NONE,
UNIT_EMPTY, UNIT_EMPTY,
ICON_EMPTY, ICON_EMPTY,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
@ -20,6 +22,9 @@ CONF_DESCRIPTOR_UUID = "descriptor_uuid"
CONF_NOTIFY = "notify" CONF_NOTIFY = "notify"
CONF_ON_NOTIFY = "on_notify" CONF_ON_NOTIFY = "on_notify"
adv_data_t = cg.std_vector.template(cg.uint8)
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
BLESensor = ble_client_ns.class_( BLESensor = ble_client_ns.class_(
"BLESensor", sensor.Sensor, cg.PollingComponent, ble_client.BLEClientNode "BLESensor", sensor.Sensor, cg.PollingComponent, ble_client.BLEClientNode
) )
@ -28,13 +33,16 @@ BLESensorNotifyTrigger = ble_client_ns.class_(
) )
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY) sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_NONE
)
.extend( .extend(
{ {
cv.GenerateID(): cv.declare_id(BLESensor), cv.GenerateID(): cv.declare_id(BLESensor),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid, cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid, cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_NOTIFY, default=False): cv.boolean, cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
cv.Optional(CONF_ON_NOTIFY): automation.validate_automation( cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
{ {
@ -50,7 +58,7 @@ CONFIG_SCHEMA = cv.All(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add( cg.add(
@ -105,11 +113,17 @@ def to_code(config):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID]) uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID])
cg.add(var.set_descr_uuid128(uuid128)) cg.add(var.set_descr_uuid128(uuid128))
yield cg.register_component(var, config) if CONF_LAMBDA in config:
yield ble_client.register_ble_node(var, config) lambda_ = await cg.process_lambda(
config[CONF_LAMBDA], [(adv_data_t_const_ref, "x")], return_type=cg.float_
)
cg.add(var.set_data_to_value(lambda_))
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
cg.add(var.set_enable_notify(config[CONF_NOTIFY])) cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
yield sensor.register_sensor(var, config) await sensor.register_sensor(var, config)
for conf in config.get(CONF_ON_NOTIFY, []): for conf in config.get(CONF_ON_NOTIFY, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield ble_client.register_ble_node(trigger, config) await ble_client.register_ble_node(trigger, config)
yield automation.build_automation(trigger, [(float, "x")], conf) await automation.build_automation(trigger, [(float, "x")], conf)

View file

@ -84,7 +84,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
} }
if (param->read.handle == this->handle) { if (param->read.handle == this->handle) {
this->status_clear_warning(); this->status_clear_warning();
this->publish_state((float) param->read.value[0]); this->publish_state(this->parse_data(param->read.value, param->read.value_len));
} }
break; break;
} }
@ -93,7 +93,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
break; break;
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(), ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
param->notify.handle, param->notify.value[0]); param->notify.handle, param->notify.value[0]);
this->publish_state((float) param->notify.value[0]); this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
break; break;
} }
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
@ -105,6 +105,15 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
} }
} }
float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
if (this->data_to_value_func_.has_value()) {
std::vector<uint8_t> data(value, value + value_len);
return (*this->data_to_value_func_)(data);
} else {
return value[0];
}
}
void BLESensor::update() { void BLESensor::update() {
if (this->node_state != espbt::ClientState::Established) { if (this->node_state != espbt::ClientState::Established) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str()); ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());

View file

@ -13,6 +13,8 @@ namespace ble_client {
namespace espbt = esphome::esp32_ble_tracker; namespace espbt = esphome::esp32_ble_tracker;
using data_to_value_t = std::function<float(std::vector<uint8_t>)>;
class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode { class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode {
public: public:
void loop() override; void loop() override;
@ -30,11 +32,14 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); } void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); } void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_data_to_value(data_to_value_t &&lambda_) { this->data_to_value_func_ = lambda_; }
void set_enable_notify(bool notify) { this->notify_ = notify; } void set_enable_notify(bool notify) { this->notify_ = notify; }
uint16_t handle; uint16_t handle;
protected: protected:
uint32_t hash_base() override; uint32_t hash_base() override;
float parse_data(uint8_t *value, uint16_t value_len);
optional<data_to_value_t> data_to_value_func_{};
bool notify_; bool notify_;
espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_; espbt::ESPBTUUID char_uuid_;

View file

@ -23,8 +23,8 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield switch.register_switch(var, config) await switch.register_switch(var, config)
yield ble_client.register_ble_node(var, config) await ble_client.register_ble_node(var, config)

View file

@ -27,11 +27,11 @@ CONFIG_SCHEMA = cv.All(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config) await esp32_ble_tracker.register_ble_device(var, config)
yield binary_sensor.register_binary_sensor(var, config) await binary_sensor.register_binary_sensor(var, config)
if CONF_MAC_ADDRESS in config: if CONF_MAC_ADDRESS in config:
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))

View file

@ -6,6 +6,7 @@ from esphome.const import (
CONF_MAC_ADDRESS, CONF_MAC_ADDRESS,
CONF_ID, CONF_ID,
DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_SIGNAL_STRENGTH,
STATE_CLASS_MEASUREMENT,
UNIT_DECIBEL, UNIT_DECIBEL,
ICON_EMPTY, ICON_EMPTY,
) )
@ -18,7 +19,13 @@ BLERSSISensor = ble_rssi_ns.class_(
) )
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH) sensor.sensor_schema(
UNIT_DECIBEL,
ICON_EMPTY,
0,
DEVICE_CLASS_SIGNAL_STRENGTH,
STATE_CLASS_MEASUREMENT,
)
.extend( .extend(
{ {
cv.GenerateID(): cv.declare_id(BLERSSISensor), cv.GenerateID(): cv.declare_id(BLERSSISensor),
@ -32,11 +39,11 @@ CONFIG_SCHEMA = cv.All(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config) await esp32_ble_tracker.register_ble_device(var, config)
yield sensor.register_sensor(var, config) await sensor.register_sensor(var, config)
if CONF_MAC_ADDRESS in config: if CONF_MAC_ADDRESS in config:
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))

View file

@ -24,8 +24,8 @@ CONFIG_SCHEMA = cv.All(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config) await esp32_ble_tracker.register_ble_device(var, config)
yield text_sensor.register_text_sensor(var, config) await text_sensor.register_text_sensor(var, config)

View file

@ -12,6 +12,7 @@ from esphome.const import (
DEVICE_CLASS_PRESSURE, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY, ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_HECTOPASCAL, UNIT_HECTOPASCAL,
UNIT_PERCENT, UNIT_PERCENT,
@ -48,7 +49,11 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(BME280Component), cv.GenerateID(): cv.declare_id(BME280Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -57,7 +62,11 @@ CONFIG_SCHEMA = (
} }
), ),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema( cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE UNIT_HECTOPASCAL,
ICON_EMPTY,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -66,7 +75,11 @@ CONFIG_SCHEMA = (
} }
), ),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -84,26 +97,26 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE] conf = config[CONF_TEMPERATURE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_temperature_sensor(sens)) cg.add(var.set_temperature_sensor(sens))
cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING])) cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
if CONF_PRESSURE in config: if CONF_PRESSURE in config:
conf = config[CONF_PRESSURE] conf = config[CONF_PRESSURE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_pressure_sensor(sens)) cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING])) cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
if CONF_HUMIDITY in config: if CONF_HUMIDITY in config:
conf = config[CONF_HUMIDITY] conf = config[CONF_HUMIDITY]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_humidity_sensor(sens)) cg.add(var.set_humidity_sensor(sens))
cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING])) cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))

View file

@ -16,6 +16,7 @@ from esphome.const import (
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_OHM, UNIT_OHM,
ICON_GAS_CYLINDER, ICON_GAS_CYLINDER,
UNIT_CELSIUS, UNIT_CELSIUS,
@ -58,7 +59,11 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(BME680Component), cv.GenerateID(): cv.declare_id(BME680Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -67,7 +72,11 @@ CONFIG_SCHEMA = (
} }
), ),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema( cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE UNIT_HECTOPASCAL,
ICON_EMPTY,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -76,7 +85,11 @@ CONFIG_SCHEMA = (
} }
), ),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY UNIT_PERCENT,
ICON_EMPTY,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -85,7 +98,11 @@ CONFIG_SCHEMA = (
} }
), ),
cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema( cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
UNIT_OHM, ICON_GAS_CYLINDER, 1, DEVICE_CLASS_EMPTY UNIT_OHM,
ICON_GAS_CYLINDER,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True IIR_FILTER_OPTIONS, upper=True
@ -114,32 +131,32 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE] conf = config[CONF_TEMPERATURE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_temperature_sensor(sens)) cg.add(var.set_temperature_sensor(sens))
cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING])) cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
if CONF_PRESSURE in config: if CONF_PRESSURE in config:
conf = config[CONF_PRESSURE] conf = config[CONF_PRESSURE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_pressure_sensor(sens)) cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING])) cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
if CONF_HUMIDITY in config: if CONF_HUMIDITY in config:
conf = config[CONF_HUMIDITY] conf = config[CONF_HUMIDITY]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_humidity_sensor(sens)) cg.add(var.set_humidity_sensor(sens))
cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING])) cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
if CONF_GAS_RESISTANCE in config: if CONF_GAS_RESISTANCE in config:
conf = config[CONF_GAS_RESISTANCE] conf = config[CONF_GAS_RESISTANCE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_gas_resistance_sensor(sens)) cg.add(var.set_gas_resistance_sensor(sens))
cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]])) cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]))

View file

@ -48,10 +48,10 @@ CONFIG_SCHEMA = cv.Schema(
).extend(i2c.i2c_device_schema(0x76)) ).extend(i2c.i2c_device_schema(0x76))
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET])) cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
cg.add(var.set_iaq_mode(config[CONF_IAQ_MODE])) cg.add(var.set_iaq_mode(config[CONF_IAQ_MODE]))
@ -60,5 +60,5 @@ def to_code(config):
var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds) var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
) )
cg.add_define("USING_BSEC") cg.add_define("USE_BSEC")
cg.add_library("BSEC Software Library", "1.6.1480") cg.add_library("BSEC Software Library", "1.6.1480")

View file

@ -1,5 +1,3 @@
#include "bme680_bsec.h" #include "bme680_bsec.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
@ -7,7 +5,7 @@
namespace esphome { namespace esphome {
namespace bme680_bsec { namespace bme680_bsec {
#ifdef USING_BSEC #ifdef USE_BSEC
static const char *TAG = "bme680_bsec.sensor"; static const char *TAG = "bme680_bsec.sensor";
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"}; static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
@ -30,7 +28,6 @@ void BME680BSECComponent::setup() {
this->bme680_.write = BME680BSECComponent::write_bytes_wrapper; this->bme680_.write = BME680BSECComponent::write_bytes_wrapper;
this->bme680_.delay_ms = BME680BSECComponent::delay_ms; this->bme680_.delay_ms = BME680BSECComponent::delay_ms;
this->bme680_.amb_temp = 25; this->bme680_.amb_temp = 25;
this->bme680_.power_mode = BME680_FORCED_MODE;
this->bme680_status_ = bme680_init(&this->bme680_); this->bme680_status_ = bme680_init(&this->bme680_);
if (this->bme680_status_ != BME680_OK) { if (this->bme680_status_ != BME680_OK) {
@ -43,14 +40,13 @@ void BME680BSECComponent::setup() {
#include "config/generic_33v_300s_28d/bsec_iaq.txt" #include "config/generic_33v_300s_28d/bsec_iaq.txt"
}; };
this->set_config_(bsec_config); this->set_config_(bsec_config);
this->update_subscription_(BSEC_SAMPLE_RATE_ULP);
} else { } else {
const uint8_t bsec_config[] = { const uint8_t bsec_config[] = {
#include "config/generic_33v_3s_28d/bsec_iaq.txt" #include "config/generic_33v_3s_28d/bsec_iaq.txt"
}; };
this->set_config_(bsec_config); this->set_config_(bsec_config);
this->update_subscription_(BSEC_SAMPLE_RATE_LP);
} }
this->update_subscription_();
if (this->bsec_status_ != BSEC_OK) { if (this->bsec_status_ != BSEC_OK) {
this->mark_failed(); this->mark_failed();
return; return;
@ -64,50 +60,57 @@ void BME680BSECComponent::set_config_(const uint8_t *config) {
this->bsec_status_ = bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, work_buffer, sizeof(work_buffer)); this->bsec_status_ = bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, work_buffer, sizeof(work_buffer));
} }
void BME680BSECComponent::update_subscription_(float sample_rate) { float BME680BSECComponent::calc_sensor_sample_rate_(SampleRate sample_rate) {
if (sample_rate == SAMPLE_RATE_DEFAULT) {
sample_rate = this->sample_rate_;
}
return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
}
void BME680BSECComponent::update_subscription_() {
bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS]; bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
int num_virtual_sensors = 0; int num_virtual_sensors = 0;
if (this->iaq_sensor_) { if (this->iaq_sensor_) {
virtual_sensors[num_virtual_sensors].sensor_id = virtual_sensors[num_virtual_sensors].sensor_id =
this->iaq_mode_ == IAQ_MODE_STATIC ? BSEC_OUTPUT_STATIC_IAQ : BSEC_OUTPUT_IAQ; this->iaq_mode_ == IAQ_MODE_STATIC ? BSEC_OUTPUT_STATIC_IAQ : BSEC_OUTPUT_IAQ;
virtual_sensors[num_virtual_sensors].sample_rate = sample_rate; virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
num_virtual_sensors++; num_virtual_sensors++;
} }
if (this->co2_equivalent_sensor_) { if (this->co2_equivalent_sensor_) {
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT; virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
virtual_sensors[num_virtual_sensors].sample_rate = sample_rate; virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
num_virtual_sensors++; num_virtual_sensors++;
} }
if (this->breath_voc_equivalent_sensor_) { if (this->breath_voc_equivalent_sensor_) {
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT; virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
virtual_sensors[num_virtual_sensors].sample_rate = sample_rate; virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
num_virtual_sensors++; num_virtual_sensors++;
} }
if (this->pressure_sensor_) { if (this->pressure_sensor_) {
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE; virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
virtual_sensors[num_virtual_sensors].sample_rate = sample_rate; virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_);
num_virtual_sensors++; num_virtual_sensors++;
} }
if (this->gas_resistance_sensor_) { if (this->gas_resistance_sensor_) {
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS; virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
virtual_sensors[num_virtual_sensors].sample_rate = sample_rate; virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
num_virtual_sensors++; num_virtual_sensors++;
} }
if (this->temperature_sensor_) { if (this->temperature_sensor_) {
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE; virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
virtual_sensors[num_virtual_sensors].sample_rate = sample_rate; virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_);
num_virtual_sensors++; num_virtual_sensors++;
} }
if (this->humidity_sensor_) { if (this->humidity_sensor_) {
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY; virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
virtual_sensors[num_virtual_sensors].sample_rate = sample_rate; virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_);
num_virtual_sensors++; num_virtual_sensors++;
} }
@ -134,12 +137,15 @@ void BME680BSECComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_); ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_);
ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile"); ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile");
ESP_LOGCONFIG(TAG, " Sample Rate: %s", this->sample_rate_ == SAMPLE_RATE_ULP ? "ULP" : "LP"); ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_));
ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_); ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->pressure_sample_rate_));
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->humidity_sample_rate_));
LOG_SENSOR(" ", "Gas Resistance", this->gas_resistance_sensor_); LOG_SENSOR(" ", "Gas Resistance", this->gas_resistance_sensor_);
LOG_SENSOR(" ", "IAQ", this->iaq_sensor_); LOG_SENSOR(" ", "IAQ", this->iaq_sensor_);
LOG_SENSOR(" ", "Numeric IAQ Accuracy", this->iaq_accuracy_sensor_); LOG_SENSOR(" ", "Numeric IAQ Accuracy", this->iaq_accuracy_sensor_);
@ -181,14 +187,15 @@ void BME680BSECComponent::run_() {
} }
this->next_call_ns_ = bme680_settings.next_call; this->next_call_ns_ = bme680_settings.next_call;
this->bme680_.gas_sett.run_gas = bme680_settings.run_gas; if (bme680_settings.trigger_measurement) {
this->bme680_.tph_sett.os_hum = bme680_settings.humidity_oversampling;
this->bme680_.tph_sett.os_temp = bme680_settings.temperature_oversampling; this->bme680_.tph_sett.os_temp = bme680_settings.temperature_oversampling;
this->bme680_.tph_sett.os_pres = bme680_settings.pressure_oversampling; this->bme680_.tph_sett.os_pres = bme680_settings.pressure_oversampling;
this->bme680_.tph_sett.os_hum = bme680_settings.humidity_oversampling;
this->bme680_.gas_sett.run_gas = bme680_settings.run_gas;
this->bme680_.gas_sett.heatr_temp = bme680_settings.heater_temperature; this->bme680_.gas_sett.heatr_temp = bme680_settings.heater_temperature;
this->bme680_.gas_sett.heatr_dur = bme680_settings.heating_duration; this->bme680_.gas_sett.heatr_dur = bme680_settings.heating_duration;
uint16_t desired_settings = this->bme680_.power_mode = BME680_FORCED_MODE;
BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL; uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
this->bme680_status_ = bme680_set_sensor_settings(desired_settings, &this->bme680_); this->bme680_status_ = bme680_set_sensor_settings(desired_settings, &this->bme680_);
if (this->bme680_status_ != BME680_OK) { if (this->bme680_status_ != BME680_OK) {
ESP_LOGW(TAG, "Failed to set sensor settings (BME680 Error Code %d)", this->bme680_status_); ESP_LOGW(TAG, "Failed to set sensor settings (BME680 Error Code %d)", this->bme680_status_);
@ -204,11 +211,31 @@ void BME680BSECComponent::run_() {
uint16_t meas_dur = 0; uint16_t meas_dur = 0;
bme680_get_profile_dur(&meas_dur, &this->bme680_); bme680_get_profile_dur(&meas_dur, &this->bme680_);
ESP_LOGV(TAG, "Queueing read in %ums", meas_dur); ESP_LOGV(TAG, "Queueing read in %ums", meas_dur);
this->set_timeout("read", meas_dur, [this, bme680_settings]() { this->read_(bme680_settings); }); this->set_timeout("read", meas_dur,
[this, curr_time_ns, bme680_settings]() { this->read_(curr_time_ns, bme680_settings); });
} else {
ESP_LOGV(TAG, "Measurement not required");
this->read_(curr_time_ns, bme680_settings);
}
} }
void BME680BSECComponent::read_(bsec_bme_settings_t bme680_settings) { void BME680BSECComponent::read_(int64_t trigger_time_ns, bsec_bme_settings_t bme680_settings) {
ESP_LOGV(TAG, "Reading data"); ESP_LOGV(TAG, "Reading data");
if (bme680_settings.trigger_measurement) {
while (this->bme680_.power_mode != BME680_SLEEP_MODE) {
this->bme680_status_ = bme680_get_sensor_mode(&this->bme680_);
if (this->bme680_status_ != BME680_OK) {
ESP_LOGW(TAG, "Failed to get sensor mode (BME680 Error Code %d)", this->bme680_status_);
}
}
}
if (!bme680_settings.process_data) {
ESP_LOGV(TAG, "Data processing not required");
return;
}
struct bme680_field_data data; struct bme680_field_data data;
this->bme680_status_ = bme680_get_sensor_data(&data, &this->bme680_); this->bme680_status_ = bme680_get_sensor_data(&data, &this->bme680_);
@ -223,37 +250,40 @@ void BME680BSECComponent::read_(bsec_bme_settings_t bme680_settings) {
bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temperature, Pressure, Humidity & Gas Resistance bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temperature, Pressure, Humidity & Gas Resistance
uint8_t num_inputs = 0; uint8_t num_inputs = 0;
int64_t curr_time_ns = this->get_time_ns_();
if (bme680_settings.process_data & BSEC_PROCESS_TEMPERATURE) { if (bme680_settings.process_data & BSEC_PROCESS_TEMPERATURE) {
inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE; inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
inputs[num_inputs].signal = data.temperature / 100.0f; inputs[num_inputs].signal = data.temperature / 100.0f;
inputs[num_inputs].time_stamp = curr_time_ns; inputs[num_inputs].time_stamp = trigger_time_ns;
num_inputs++; num_inputs++;
// Temperature offset from the real temperature due to external heat sources // Temperature offset from the real temperature due to external heat sources
inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE; inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
inputs[num_inputs].signal = this->temperature_offset_; inputs[num_inputs].signal = this->temperature_offset_;
inputs[num_inputs].time_stamp = curr_time_ns; inputs[num_inputs].time_stamp = trigger_time_ns;
num_inputs++; num_inputs++;
} }
if (bme680_settings.process_data & BSEC_PROCESS_HUMIDITY) { if (bme680_settings.process_data & BSEC_PROCESS_HUMIDITY) {
inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY; inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
inputs[num_inputs].signal = data.humidity / 1000.0f; inputs[num_inputs].signal = data.humidity / 1000.0f;
inputs[num_inputs].time_stamp = curr_time_ns; inputs[num_inputs].time_stamp = trigger_time_ns;
num_inputs++; num_inputs++;
} }
if (bme680_settings.process_data & BSEC_PROCESS_PRESSURE) { if (bme680_settings.process_data & BSEC_PROCESS_PRESSURE) {
inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE; inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
inputs[num_inputs].signal = data.pressure; inputs[num_inputs].signal = data.pressure;
inputs[num_inputs].time_stamp = curr_time_ns; inputs[num_inputs].time_stamp = trigger_time_ns;
num_inputs++; num_inputs++;
} }
if (bme680_settings.process_data & BSEC_PROCESS_GAS) { if (bme680_settings.process_data & BSEC_PROCESS_GAS) {
if (data.status & BME680_GASM_VALID_MSK) {
inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR; inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
inputs[num_inputs].signal = data.gas_resistance; inputs[num_inputs].signal = data.gas_resistance;
inputs[num_inputs].time_stamp = curr_time_ns; inputs[num_inputs].time_stamp = trigger_time_ns;
num_inputs++; num_inputs++;
} else {
ESP_LOGD(TAG, "BME680 did not report gas data");
}
} }
if (num_inputs < 1) { if (num_inputs < 1) {
ESP_LOGD(TAG, "No signal inputs available for BSEC"); ESP_LOGD(TAG, "No signal inputs available for BSEC");

View file

@ -1,5 +1,3 @@
#pragma once #pragma once
#include "esphome/core/component.h" #include "esphome/core/component.h"
@ -9,13 +7,13 @@
#include "esphome/core/preferences.h" #include "esphome/core/preferences.h"
#include <map> #include <map>
#ifdef USING_BSEC #ifdef USE_BSEC
#include <bsec.h> #include <bsec.h>
#endif #endif
namespace esphome { namespace esphome {
namespace bme680_bsec { namespace bme680_bsec {
#ifdef USING_BSEC #ifdef USE_BSEC
enum IAQMode { enum IAQMode {
IAQ_MODE_STATIC = 0, IAQ_MODE_STATIC = 0,
@ -25,32 +23,31 @@ enum IAQMode {
enum SampleRate { enum SampleRate {
SAMPLE_RATE_LP = 0, SAMPLE_RATE_LP = 0,
SAMPLE_RATE_ULP = 1, SAMPLE_RATE_ULP = 1,
SAMPLE_RATE_DEFAULT = 2,
}; };
#define BME680_BSEC_SAMPLE_RATE_LOG(r) (r == SAMPLE_RATE_DEFAULT ? "Default" : (r == SAMPLE_RATE_ULP ? "ULP" : "LP"))
class BME680BSECComponent : public Component, public i2c::I2CDevice { class BME680BSECComponent : public Component, public i2c::I2CDevice {
public: public:
void set_temperature_offset(float offset) { this->temperature_offset_ = offset; } void set_temperature_offset(float offset) { this->temperature_offset_ = offset; }
void set_iaq_mode(IAQMode iaq_mode) { this->iaq_mode_ = iaq_mode; } void set_iaq_mode(IAQMode iaq_mode) { this->iaq_mode_ = iaq_mode; }
void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; }
void set_state_save_interval(uint32_t interval) { this->state_save_interval_ms_ = interval; } void set_state_save_interval(uint32_t interval) { this->state_save_interval_ms_ = interval; }
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; }
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; } void set_temperature_sample_rate(SampleRate sample_rate) { this->temperature_sample_rate_ = sample_rate; }
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } void set_pressure_sample_rate(SampleRate sample_rate) { this->pressure_sample_rate_ = sample_rate; }
void set_gas_resistance_sensor(sensor::Sensor *gas_resistance_sensor) { void set_humidity_sample_rate(SampleRate sample_rate) { this->humidity_sample_rate_ = sample_rate; }
gas_resistance_sensor_ = gas_resistance_sensor;
} void set_temperature_sensor(sensor::Sensor *sensor) { this->temperature_sensor_ = sensor; }
void set_iaq_sensor(sensor::Sensor *iaq_sensor) { iaq_sensor_ = iaq_sensor; } void set_pressure_sensor(sensor::Sensor *sensor) { this->pressure_sensor_ = sensor; }
void set_iaq_accuracy_text_sensor(text_sensor::TextSensor *iaq_accuracy_text_sensor) { void set_humidity_sensor(sensor::Sensor *sensor) { this->humidity_sensor_ = sensor; }
iaq_accuracy_text_sensor_ = iaq_accuracy_text_sensor; void set_gas_resistance_sensor(sensor::Sensor *sensor) { this->gas_resistance_sensor_ = sensor; }
} void set_iaq_sensor(sensor::Sensor *sensor) { this->iaq_sensor_ = sensor; }
void set_iaq_accuracy_sensor(sensor::Sensor *iaq_accuracy_sensor) { iaq_accuracy_sensor_ = iaq_accuracy_sensor; } void set_iaq_accuracy_text_sensor(text_sensor::TextSensor *sensor) { this->iaq_accuracy_text_sensor_ = sensor; }
void set_co2_equivalent_sensor(sensor::Sensor *co2_equivalent_sensor) { void set_iaq_accuracy_sensor(sensor::Sensor *sensor) { this->iaq_accuracy_sensor_ = sensor; }
co2_equivalent_sensor_ = co2_equivalent_sensor; void set_co2_equivalent_sensor(sensor::Sensor *sensor) { this->co2_equivalent_sensor_ = sensor; }
} void set_breath_voc_equivalent_sensor(sensor::Sensor *sensor) { this->breath_voc_equivalent_sensor_ = sensor; }
void set_breath_voc_equivalent_sensor(sensor::Sensor *breath_voc_equivalent_sensor) {
breath_voc_equivalent_sensor_ = breath_voc_equivalent_sensor;
}
static BME680BSECComponent *instance; static BME680BSECComponent *instance;
static int8_t read_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len); static int8_t read_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len);
@ -64,10 +61,11 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
protected: protected:
void set_config_(const uint8_t *config); void set_config_(const uint8_t *config);
void update_subscription_(float sample_rate); float calc_sensor_sample_rate_(SampleRate sample_rate);
void update_subscription_();
void run_(); void run_();
void read_(bsec_bme_settings_t bme680_settings); void read_(int64_t trigger_time_ns, bsec_bme_settings_t bme680_settings);
void publish_(const bsec_output_t *outputs, uint8_t num_outputs); void publish_(const bsec_output_t *outputs, uint8_t num_outputs);
int64_t get_time_ns_(); int64_t get_time_ns_();
@ -91,7 +89,11 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
float temperature_offset_{0}; float temperature_offset_{0};
IAQMode iaq_mode_{IAQ_MODE_STATIC}; IAQMode iaq_mode_{IAQ_MODE_STATIC};
SampleRate sample_rate_{SAMPLE_RATE_LP};
SampleRate sample_rate_{SAMPLE_RATE_LP}; // Core/gas sample rate
SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT};
SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT};
SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT};
sensor::Sensor *temperature_sensor_; sensor::Sensor *temperature_sensor_;
sensor::Sensor *pressure_sensor_; sensor::Sensor *pressure_sensor_;

View file

@ -10,6 +10,7 @@ from esphome.const import (
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_EMPTY, UNIT_EMPTY,
UNIT_HECTOPASCAL, UNIT_HECTOPASCAL,
@ -21,8 +22,12 @@ from esphome.const import (
ICON_THERMOMETER, ICON_THERMOMETER,
ICON_WATER_PERCENT, ICON_WATER_PERCENT,
) )
from esphome.core import coroutine from . import (
from . import BME680BSECComponent, CONF_BME680_BSEC_ID BME680BSECComponent,
CONF_BME680_BSEC_ID,
CONF_SAMPLE_RATE,
SAMPLE_RATE_OPTIONS,
)
DEPENDENCIES = ["bme680_bsec"] DEPENDENCIES = ["bme680_bsec"]
@ -34,58 +39,84 @@ UNIT_IAQ = "IAQ"
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
ICON_TEST_TUBE = "mdi:test-tube" ICON_TEST_TUBE = "mdi:test-tube"
TYPES = { TYPES = [
CONF_TEMPERATURE: "set_temperature_sensor", CONF_TEMPERATURE,
CONF_PRESSURE: "set_pressure_sensor", CONF_PRESSURE,
CONF_HUMIDITY: "set_humidity_sensor", CONF_HUMIDITY,
CONF_GAS_RESISTANCE: "set_gas_resistance_sensor", CONF_GAS_RESISTANCE,
CONF_IAQ: "set_iaq_sensor", CONF_IAQ,
CONF_IAQ_ACCURACY: "set_iaq_accuracy_sensor", CONF_IAQ_ACCURACY,
CONF_CO2_EQUIVALENT: "set_co2_equivalent_sensor", CONF_CO2_EQUIVALENT,
CONF_BREATH_VOC_EQUIVALENT: "set_breath_voc_equivalent_sensor", CONF_BREATH_VOC_EQUIVALENT,
} ]
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent), cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_THERMOMETER, 1, DEVICE_CLASS_TEMPERATURE UNIT_CELSIUS,
ICON_THERMOMETER,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
).extend(
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
), ),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema( cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL, ICON_GAUGE, 1, DEVICE_CLASS_PRESSURE UNIT_HECTOPASCAL,
ICON_GAUGE,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
).extend(
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
), ),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_WATER_PERCENT, 1, DEVICE_CLASS_HUMIDITY UNIT_PERCENT,
ICON_WATER_PERCENT,
1,
DEVICE_CLASS_HUMIDITY,
STATE_CLASS_MEASUREMENT,
).extend(
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
), ),
cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema( cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
UNIT_OHM, ICON_GAS_CYLINDER, 0, DEVICE_CLASS_EMPTY UNIT_OHM, ICON_GAS_CYLINDER, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_IAQ): sensor.sensor_schema( cv.Optional(CONF_IAQ): sensor.sensor_schema(
UNIT_IAQ, ICON_GAUGE, 0, DEVICE_CLASS_EMPTY UNIT_IAQ, ICON_GAUGE, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_IAQ_ACCURACY): sensor.sensor_schema( cv.Optional(CONF_IAQ_ACCURACY): sensor.sensor_schema(
UNIT_EMPTY, ICON_ACCURACY, 0, DEVICE_CLASS_EMPTY UNIT_EMPTY, ICON_ACCURACY, 0, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
), ),
cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema( cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION, ICON_TEST_TUBE, 1, DEVICE_CLASS_EMPTY UNIT_PARTS_PER_MILLION,
ICON_TEST_TUBE,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema( cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION, ICON_TEST_TUBE, 1, DEVICE_CLASS_EMPTY UNIT_PARTS_PER_MILLION,
ICON_TEST_TUBE,
1,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
), ),
} }
) )
@coroutine async def setup_conf(config, key, hub):
def setup_conf(config, key, hub, funcName):
if key in config: if key in config:
conf = config[key] conf = config[key]
var = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
func = getattr(hub, funcName) cg.add(getattr(hub, f"set_{key}_sensor")(sens))
cg.add(func(var)) if CONF_SAMPLE_RATE in conf:
cg.add(getattr(hub, f"set_{key}_sample_rate")(conf[CONF_SAMPLE_RATE]))
def to_code(config): async def to_code(config):
hub = yield cg.get_variable(config[CONF_BME680_BSEC_ID]) hub = await cg.get_variable(config[CONF_BME680_BSEC_ID])
for key, funcName in TYPES.items(): for key in TYPES:
yield setup_conf(config, key, hub, funcName) await setup_conf(config, key, hub)

View file

@ -2,7 +2,6 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import text_sensor from esphome.components import text_sensor
from esphome.const import CONF_ID, CONF_ICON from esphome.const import CONF_ID, CONF_ICON
from esphome.core import coroutine
from . import BME680BSECComponent, CONF_BME680_BSEC_ID from . import BME680BSECComponent, CONF_BME680_BSEC_ID
DEPENDENCIES = ["bme680_bsec"] DEPENDENCIES = ["bme680_bsec"]
@ -10,7 +9,7 @@ DEPENDENCIES = ["bme680_bsec"]
CONF_IAQ_ACCURACY = "iaq_accuracy" CONF_IAQ_ACCURACY = "iaq_accuracy"
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
TYPES = {CONF_IAQ_ACCURACY: "set_iaq_accuracy_text_sensor"} TYPES = [CONF_IAQ_ACCURACY]
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
@ -25,17 +24,15 @@ CONFIG_SCHEMA = cv.Schema(
) )
@coroutine async def setup_conf(config, key, hub):
def setup_conf(config, key, hub, funcName):
if key in config: if key in config:
conf = config[key] conf = config[key]
var = cg.new_Pvariable(conf[CONF_ID]) sens = cg.new_Pvariable(conf[CONF_ID])
yield text_sensor.register_text_sensor(var, conf) await text_sensor.register_text_sensor(sens, conf)
func = getattr(hub, funcName) cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
cg.add(func(var))
def to_code(config): async def to_code(config):
hub = yield cg.get_variable(config[CONF_BME680_BSEC_ID]) hub = await cg.get_variable(config[CONF_BME680_BSEC_ID])
for key, funcName in TYPES.items(): for key in TYPES:
yield setup_conf(config, key, hub, funcName) await setup_conf(config, key, hub)

View file

@ -7,6 +7,7 @@ from esphome.const import (
CONF_TEMPERATURE, CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
ICON_EMPTY, ICON_EMPTY,
UNIT_HECTOPASCAL, UNIT_HECTOPASCAL,
@ -24,10 +25,18 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(BMP085Component), cv.GenerateID(): cv.declare_id(BMP085Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema( cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE UNIT_HECTOPASCAL,
ICON_EMPTY,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
), ),
} }
) )
@ -36,17 +45,17 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE] conf = config[CONF_TEMPERATURE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_temperature(sens)) cg.add(var.set_temperature(sens))
if CONF_PRESSURE in config: if CONF_PRESSURE in config:
conf = config[CONF_PRESSURE] conf = config[CONF_PRESSURE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_pressure(sens)) cg.add(var.set_pressure(sens))

View file

@ -7,6 +7,7 @@ from esphome.const import (
CONF_TEMPERATURE, CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
ICON_EMPTY, ICON_EMPTY,
UNIT_HECTOPASCAL, UNIT_HECTOPASCAL,
@ -45,7 +46,11 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(BMP280Component), cv.GenerateID(): cv.declare_id(BMP280Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE UNIT_CELSIUS,
ICON_EMPTY,
1,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -54,7 +59,11 @@ CONFIG_SCHEMA = (
} }
), ),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema( cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE UNIT_HECTOPASCAL,
ICON_EMPTY,
1,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
).extend( ).extend(
{ {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
@ -72,19 +81,19 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
conf = config[CONF_TEMPERATURE] conf = config[CONF_TEMPERATURE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_temperature_sensor(sens)) cg.add(var.set_temperature_sensor(sens))
cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING])) cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
if CONF_PRESSURE in config: if CONF_PRESSURE in config:
conf = config[CONF_PRESSURE] conf = config[CONF_PRESSURE]
sens = yield sensor.new_sensor(conf) sens = await sensor.new_sensor(conf)
cg.add(var.set_pressure_sensor(sens)) cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING])) cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))

View file

@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.core import CORE, coroutine from esphome.core import CORE
from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA
CODEOWNERS = ["@mvturnho", "@danielschramm"] CODEOWNERS = ["@mvturnho", "@danielschramm"]
@ -82,10 +82,9 @@ CANBUS_SCHEMA = cv.Schema(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@coroutine async def setup_canbus_core_(var, config):
def setup_canbus_core_(var, config):
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID]) validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
cg.add(var.set_can_id([config[CONF_CAN_ID]])) cg.add(var.set_can_id([config[CONF_CAN_ID]]))
cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]])) cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]]))
cg.add(var.set_bitrate(CAN_SPEEDS[config[CONF_BIT_RATE]])) cg.add(var.set_bitrate(CAN_SPEEDS[config[CONF_BIT_RATE]]))
@ -95,17 +94,16 @@ def setup_canbus_core_(var, config):
ext_id = conf[CONF_USE_EXTENDED_ID] ext_id = conf[CONF_USE_EXTENDED_ID]
validate_id(can_id, ext_id) validate_id(can_id, ext_id)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id)
yield cg.register_component(trigger, conf) await cg.register_component(trigger, conf)
yield automation.build_automation( await automation.build_automation(
trigger, [(cg.std_vector.template(cg.uint8), "x")], conf trigger, [(cg.std_vector.template(cg.uint8), "x")], conf
) )
@coroutine async def register_canbus(var, config):
def register_canbus(var, config):
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.new_Pvariable(config[CONF_ID], var) var = cg.new_Pvariable(config[CONF_ID], var)
yield setup_canbus_core_(var, config) await setup_canbus_core_(var, config)
# Actions # Actions
@ -122,16 +120,16 @@ def register_canbus(var, config):
key=CONF_DATA, key=CONF_DATA,
), ),
) )
def canbus_action_to_code(config, action_id, template_arg, args): async def canbus_action_to_code(config, action_id, template_arg, args):
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID]) validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_CANBUS_ID]) await cg.register_parented(var, config[CONF_CANBUS_ID])
if CONF_CAN_ID in config: if CONF_CAN_ID in config:
can_id = yield cg.templatable(config[CONF_CAN_ID], args, cg.uint32) can_id = await cg.templatable(config[CONF_CAN_ID], args, cg.uint32)
cg.add(var.set_can_id(can_id)) cg.add(var.set_can_id(can_id))
use_extended_id = yield cg.templatable( use_extended_id = await cg.templatable(
config[CONF_USE_EXTENDED_ID], args, cg.uint32 config[CONF_USE_EXTENDED_ID], args, cg.uint32
) )
cg.add(var.set_use_extended_id(use_extended_id)) cg.add(var.set_use_extended_id(use_extended_id))
@ -140,8 +138,8 @@ def canbus_action_to_code(config, action_id, template_arg, args):
if isinstance(data, bytes): if isinstance(data, bytes):
data = [int(x) for x in data] data = [int(x) for x in data]
if cg.is_template(data): if cg.is_template(data):
templ = yield cg.templatable(data, args, cg.std_vector.template(cg.uint8)) templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
cg.add(var.set_data_template(templ)) cg.add(var.set_data_template(templ))
else: else:
cg.add(var.set_data_static(data)) cg.add(var.set_data_static(data))
yield var return var

View file

@ -23,9 +23,9 @@ CONFIG_SCHEMA = cv.Schema(
@coroutine_with_priority(64.0) @coroutine_with_priority(64.0)
def to_code(config): async def to_code(config):
paren = yield cg.get_variable(config[CONF_WEB_SERVER_BASE_ID]) paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
var = cg.new_Pvariable(config[CONF_ID], paren) var = cg.new_Pvariable(config[CONF_ID], paren)
yield cg.register_component(var, config) await cg.register_component(var, config)
cg.add_define("USE_CAPTIVE_PORTAL") cg.add_define("USE_CAPTIVE_PORTAL")

View file

@ -64,32 +64,11 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
ESP_LOGI(TAG, "Captive Portal Requested WiFi Settings Change:"); ESP_LOGI(TAG, "Captive Portal Requested WiFi Settings Change:");
ESP_LOGI(TAG, " SSID='%s'", ssid.c_str()); ESP_LOGI(TAG, " SSID='%s'", ssid.c_str());
ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str()); ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str());
this->override_sta_(ssid, psk); wifi::global_wifi_component->save_wifi_sta(ssid, psk);
request->redirect("/?save=true"); request->redirect("/?save=true");
} }
void CaptivePortal::override_sta_(const std::string &ssid, const std::string &password) {
CaptivePortalSettings save{};
strcpy(save.ssid, ssid.c_str());
strcpy(save.password, password.c_str());
this->pref_.save(&save);
wifi::WiFiAP sta{}; void CaptivePortal::setup() {}
sta.set_ssid(ssid);
sta.set_password(password);
wifi::global_wifi_component->set_sta(sta);
}
void CaptivePortal::setup() {
// Hash with compilation time
// This ensures the AP override is not applied for OTA
uint32_t hash = fnv1_hash(App.get_compilation_time());
this->pref_ = global_preferences.make_preference<CaptivePortalSettings>(hash, true);
CaptivePortalSettings save{};
if (this->pref_.load(&save)) {
this->override_sta_(save.ssid, save.password);
}
}
void CaptivePortal::start() { void CaptivePortal::start() {
this->base_->init(); this->base_->init();
if (!this->initialized_) { if (!this->initialized_) {

View file

@ -10,11 +10,6 @@ namespace esphome {
namespace captive_portal { namespace captive_portal {
struct CaptivePortalSettings {
char ssid[33];
char password[65];
} PACKED; // NOLINT
class CaptivePortal : public AsyncWebHandler, public Component { class CaptivePortal : public AsyncWebHandler, public Component {
public: public:
CaptivePortal(web_server_base::WebServerBase *base); CaptivePortal(web_server_base::WebServerBase *base);
@ -67,12 +62,9 @@ class CaptivePortal : public AsyncWebHandler, public Component {
void handleRequest(AsyncWebServerRequest *req) override; void handleRequest(AsyncWebServerRequest *req) override;
protected: protected:
void override_sta_(const std::string &ssid, const std::string &password);
web_server_base::WebServerBase *base_; web_server_base::WebServerBase *base_;
bool initialized_{false}; bool initialized_{false};
bool active_{false}; bool active_{false};
ESPPreferenceObject pref_;
DNSServer *dns_server_{nullptr}; DNSServer *dns_server_{nullptr};
}; };

View file

@ -5,6 +5,7 @@ from esphome.const import (
CONF_ID, CONF_ID,
DEVICE_CLASS_EMPTY, DEVICE_CLASS_EMPTY,
ICON_RADIATOR, ICON_RADIATOR,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_MILLION, UNIT_PARTS_PER_MILLION,
UNIT_PARTS_PER_BILLION, UNIT_PARTS_PER_BILLION,
CONF_TEMPERATURE, CONF_TEMPERATURE,
@ -28,10 +29,18 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(CCS811Component), cv.GenerateID(): cv.declare_id(CCS811Component),
cv.Required(CONF_ECO2): sensor.sensor_schema( cv.Required(CONF_ECO2): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY UNIT_PARTS_PER_MILLION,
ICON_MOLECULE_CO2,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
), ),
cv.Required(CONF_TVOC): sensor.sensor_schema( cv.Required(CONF_TVOC): sensor.sensor_schema(
UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, DEVICE_CLASS_EMPTY UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
0,
DEVICE_CLASS_EMPTY,
STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_BASELINE): cv.hex_uint16_t, cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor), cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
@ -43,22 +52,22 @@ CONFIG_SCHEMA = (
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config) await cg.register_component(var, config)
yield i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
sens = yield sensor.new_sensor(config[CONF_ECO2]) sens = await sensor.new_sensor(config[CONF_ECO2])
cg.add(var.set_co2(sens)) cg.add(var.set_co2(sens))
sens = yield sensor.new_sensor(config[CONF_TVOC]) sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens)) cg.add(var.set_tvoc(sens))
if CONF_BASELINE in config: if CONF_BASELINE in config:
cg.add(var.set_baseline(config[CONF_BASELINE])) cg.add(var.set_baseline(config[CONF_BASELINE]))
if CONF_TEMPERATURE in config: if CONF_TEMPERATURE in config:
sens = yield cg.get_variable(config[CONF_TEMPERATURE]) sens = await cg.get_variable(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens)) cg.add(var.set_temperature(sens))
if CONF_HUMIDITY in config: if CONF_HUMIDITY in config:
sens = yield cg.get_variable(config[CONF_HUMIDITY]) sens = await cg.get_variable(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens)) cg.add(var.set_humidity(sens))

View file

@ -4,11 +4,14 @@ from esphome import automation
from esphome.components import mqtt from esphome.components import mqtt
from esphome.const import ( from esphome.const import (
CONF_AWAY, CONF_AWAY,
CONF_CUSTOM_FAN_MODE,
CONF_CUSTOM_PRESET,
CONF_ID, CONF_ID,
CONF_INTERNAL, CONF_INTERNAL,
CONF_MAX_TEMPERATURE, CONF_MAX_TEMPERATURE,
CONF_MIN_TEMPERATURE, CONF_MIN_TEMPERATURE,
CONF_MODE, CONF_MODE,
CONF_PRESET,
CONF_TARGET_TEMPERATURE, CONF_TARGET_TEMPERATURE,
CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_HIGH,
CONF_TARGET_TEMPERATURE_LOW, CONF_TARGET_TEMPERATURE_LOW,
@ -19,7 +22,7 @@ from esphome.const import (
CONF_FAN_MODE, CONF_FAN_MODE,
CONF_SWING_MODE, CONF_SWING_MODE,
) )
from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@ -33,11 +36,12 @@ ClimateTraits = climate_ns.class_("ClimateTraits")
ClimateMode = climate_ns.enum("ClimateMode") ClimateMode = climate_ns.enum("ClimateMode")
CLIMATE_MODES = { CLIMATE_MODES = {
"OFF": ClimateMode.CLIMATE_MODE_OFF, "OFF": ClimateMode.CLIMATE_MODE_OFF,
"AUTO": ClimateMode.CLIMATE_MODE_AUTO, "HEAT_COOL": ClimateMode.CLIMATE_HEAT_COOL,
"COOL": ClimateMode.CLIMATE_MODE_COOL, "COOL": ClimateMode.CLIMATE_MODE_COOL,
"HEAT": ClimateMode.CLIMATE_MODE_HEAT, "HEAT": ClimateMode.CLIMATE_MODE_HEAT,
"DRY": ClimateMode.CLIMATE_MODE_DRY, "DRY": ClimateMode.CLIMATE_MODE_DRY,
"FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY, "FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
"AUTO": ClimateMode.CLIMATE_MODE_AUTO,
} }
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True) validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
@ -56,6 +60,19 @@ CLIMATE_FAN_MODES = {
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True) validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
ClimatePreset = climate_ns.enum("ClimatePreset")
CLIMATE_PRESETS = {
"ECO": ClimatePreset.CLIMATE_PRESET_ECO,
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
"COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT,
"HOME": ClimatePreset.CLIMATE_PRESET_HOME,
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
"ACTIVITY": ClimatePreset.CLIMATE_PRESET_ACTIVITY,
}
validate_climate_preset = cv.enum(CLIMATE_PRESETS, upper=True)
ClimateSwingMode = climate_ns.enum("ClimateSwingMode") ClimateSwingMode = climate_ns.enum("ClimateSwingMode")
CLIMATE_SWING_MODES = { CLIMATE_SWING_MODES = {
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF, "OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
@ -85,8 +102,7 @@ CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
) )
@coroutine async def setup_climate_core_(var, config):
def setup_climate_core_(var, config):
cg.add(var.set_name(config[CONF_NAME])) cg.add(var.set_name(config[CONF_NAME]))
if CONF_INTERNAL in config: if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL])) cg.add(var.set_internal(config[CONF_INTERNAL]))
@ -100,15 +116,14 @@ def setup_climate_core_(var, config):
if CONF_MQTT_ID in config: if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
yield mqtt.register_mqtt_component(mqtt_, config) await mqtt.register_mqtt_component(mqtt_, config)
@coroutine async def register_climate(var, config):
def register_climate(var, config):
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var) var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_climate(var)) cg.add(cg.App.register_climate(var))
yield setup_climate_core_(var, config) await setup_climate_core_(var, config)
CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema( CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
@ -119,7 +134,12 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature), cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature), cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
cv.Optional(CONF_AWAY): cv.templatable(cv.boolean), cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode), cv.Exclusive(CONF_FAN_MODE, "fan_mode"): cv.templatable(
validate_climate_fan_mode
),
cv.Exclusive(CONF_CUSTOM_FAN_MODE, "fan_mode"): cv.string_strict,
cv.Exclusive(CONF_PRESET, "preset"): cv.templatable(validate_climate_preset),
cv.Exclusive(CONF_CUSTOM_PRESET, "preset"): cv.string_strict,
cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode), cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode),
} }
) )
@ -128,40 +148,49 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
@automation.register_action( @automation.register_action(
"climate.control", ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA "climate.control", ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA
) )
def climate_control_to_code(config, action_id, template_arg, args): async def climate_control_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren) var = cg.new_Pvariable(action_id, template_arg, paren)
if CONF_MODE in config: if CONF_MODE in config:
template_ = yield cg.templatable(config[CONF_MODE], args, ClimateMode) template_ = await cg.templatable(config[CONF_MODE], args, ClimateMode)
cg.add(var.set_mode(template_)) cg.add(var.set_mode(template_))
if CONF_TARGET_TEMPERATURE in config: if CONF_TARGET_TEMPERATURE in config:
template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float) template_ = await cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float)
cg.add(var.set_target_temperature(template_)) cg.add(var.set_target_temperature(template_))
if CONF_TARGET_TEMPERATURE_LOW in config: if CONF_TARGET_TEMPERATURE_LOW in config:
template_ = yield cg.templatable( template_ = await cg.templatable(
config[CONF_TARGET_TEMPERATURE_LOW], args, float config[CONF_TARGET_TEMPERATURE_LOW], args, float
) )
cg.add(var.set_target_temperature_low(template_)) cg.add(var.set_target_temperature_low(template_))
if CONF_TARGET_TEMPERATURE_HIGH in config: if CONF_TARGET_TEMPERATURE_HIGH in config:
template_ = yield cg.templatable( template_ = await cg.templatable(
config[CONF_TARGET_TEMPERATURE_HIGH], args, float config[CONF_TARGET_TEMPERATURE_HIGH], args, float
) )
cg.add(var.set_target_temperature_high(template_)) cg.add(var.set_target_temperature_high(template_))
if CONF_AWAY in config: if CONF_AWAY in config:
template_ = yield cg.templatable(config[CONF_AWAY], args, bool) template_ = await cg.templatable(config[CONF_AWAY], args, bool)
cg.add(var.set_away(template_)) cg.add(var.set_away(template_))
if CONF_FAN_MODE in config: if CONF_FAN_MODE in config:
template_ = yield cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode) template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
cg.add(var.set_fan_mode(template_)) cg.add(var.set_fan_mode(template_))
if CONF_CUSTOM_FAN_MODE in config:
template_ = await cg.templatable(config[CONF_CUSTOM_FAN_MODE], args, str)
cg.add(var.set_custom_fan_mode(template_))
if CONF_PRESET in config:
template_ = await cg.templatable(config[CONF_PRESET], args, ClimatePreset)
cg.add(var.set_preset(template_))
if CONF_CUSTOM_PRESET in config:
template_ = await cg.templatable(config[CONF_CUSTOM_PRESET], args, str)
cg.add(var.set_custom_preset(template_))
if CONF_SWING_MODE in config: if CONF_SWING_MODE in config:
template_ = yield cg.templatable( template_ = await cg.templatable(
config[CONF_SWING_MODE], args, ClimateSwingMode config[CONF_SWING_MODE], args, ClimateSwingMode
) )
cg.add(var.set_swing_mode(template_)) cg.add(var.set_swing_mode(template_))
yield var return var
@coroutine_with_priority(100.0) @coroutine_with_priority(100.0)
def to_code(config): async def to_code(config):
cg.add_define("USE_CLIMATE") cg.add_define("USE_CLIMATE")
cg.add_global(climate_ns.using) cg.add_global(climate_ns.using)

View file

@ -16,6 +16,9 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
TEMPLATABLE_VALUE(float, target_temperature_high) TEMPLATABLE_VALUE(float, target_temperature_high)
TEMPLATABLE_VALUE(bool, away) TEMPLATABLE_VALUE(bool, away)
TEMPLATABLE_VALUE(ClimateFanMode, fan_mode) TEMPLATABLE_VALUE(ClimateFanMode, fan_mode)
TEMPLATABLE_VALUE(std::string, custom_fan_mode)
TEMPLATABLE_VALUE(ClimatePreset, preset)
TEMPLATABLE_VALUE(std::string, custom_preset)
TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode) TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode)
void play(Ts... x) override { void play(Ts... x) override {
@ -26,6 +29,9 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
call.set_target_temperature_high(this->target_temperature_high_.optional_value(x...)); call.set_target_temperature_high(this->target_temperature_high_.optional_value(x...));
call.set_away(this->away_.optional_value(x...)); call.set_away(this->away_.optional_value(x...));
call.set_fan_mode(this->fan_mode_.optional_value(x...)); call.set_fan_mode(this->fan_mode_.optional_value(x...));
call.set_fan_mode(this->custom_fan_mode_.optional_value(x...));
call.set_preset(this->preset_.optional_value(x...));
call.set_preset(this->custom_preset_.optional_value(x...));
call.set_swing_mode(this->swing_mode_.optional_value(x...)); call.set_swing_mode(this->swing_mode_.optional_value(x...));
call.perform(); call.perform();
} }

View file

@ -1,5 +1,4 @@
#include "climate.h" #include "climate.h"
#include "esphome/core/log.h"
namespace esphome { namespace esphome {
namespace climate { namespace climate {
@ -13,10 +12,24 @@ void ClimateCall::perform() {
const char *mode_s = climate_mode_to_string(*this->mode_); const char *mode_s = climate_mode_to_string(*this->mode_);
ESP_LOGD(TAG, " Mode: %s", mode_s); ESP_LOGD(TAG, " Mode: %s", mode_s);
} }
if (this->custom_fan_mode_.has_value()) {
this->fan_mode_.reset();
ESP_LOGD(TAG, " Custom Fan: %s", this->custom_fan_mode_.value().c_str());
}
if (this->fan_mode_.has_value()) { if (this->fan_mode_.has_value()) {
this->custom_fan_mode_.reset();
const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_); const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
ESP_LOGD(TAG, " Fan: %s", fan_mode_s); ESP_LOGD(TAG, " Fan: %s", fan_mode_s);
} }
if (this->custom_preset_.has_value()) {
this->preset_.reset();
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_.value().c_str());
}
if (this->preset_.has_value()) {
this->custom_preset_.reset();
const char *preset_s = climate_preset_to_string(*this->preset_);
ESP_LOGD(TAG, " Preset: %s", preset_s);
}
if (this->swing_mode_.has_value()) { if (this->swing_mode_.has_value()) {
const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_); const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
ESP_LOGD(TAG, " Swing: %s", swing_mode_s); ESP_LOGD(TAG, " Swing: %s", swing_mode_s);
@ -44,13 +57,32 @@ void ClimateCall::validate_() {
this->mode_.reset(); this->mode_.reset();
} }
} }
if (this->fan_mode_.has_value()) { if (this->custom_fan_mode_.has_value()) {
auto custom_fan_mode = *this->custom_fan_mode_;
if (!traits.supports_custom_fan_mode(custom_fan_mode)) {
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", custom_fan_mode.c_str());
this->custom_fan_mode_.reset();
}
} else if (this->fan_mode_.has_value()) {
auto fan_mode = *this->fan_mode_; auto fan_mode = *this->fan_mode_;
if (!traits.supports_fan_mode(fan_mode)) { if (!traits.supports_fan_mode(fan_mode)) {
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode)); ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode));
this->fan_mode_.reset(); this->fan_mode_.reset();
} }
} }
if (this->custom_preset_.has_value()) {
auto custom_preset = *this->custom_preset_;
if (!traits.supports_custom_preset(custom_preset)) {
ESP_LOGW(TAG, " Preset %s is not supported by this device!", custom_preset.c_str());
this->custom_preset_.reset();
}
} else if (this->preset_.has_value()) {
auto preset = *this->preset_;
if (!traits.supports_preset(preset)) {
ESP_LOGW(TAG, " Preset %s is not supported by this device!", climate_preset_to_string(preset));
this->preset_.reset();
}
}
if (this->swing_mode_.has_value()) { if (this->swing_mode_.has_value()) {
auto swing_mode = *this->swing_mode_; auto swing_mode = *this->swing_mode_;
if (!traits.supports_swing_mode(swing_mode)) { if (!traits.supports_swing_mode(swing_mode)) {
@ -117,6 +149,8 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
this->set_mode(CLIMATE_MODE_FAN_ONLY); this->set_mode(CLIMATE_MODE_FAN_ONLY);
} else if (str_equals_case_insensitive(mode, "DRY")) { } else if (str_equals_case_insensitive(mode, "DRY")) {
this->set_mode(CLIMATE_MODE_DRY); this->set_mode(CLIMATE_MODE_DRY);
} else if (str_equals_case_insensitive(mode, "HEAT_COOL")) {
this->set_mode(CLIMATE_MODE_HEAT_COOL);
} else { } else {
ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str()); ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str());
} }
@ -124,6 +158,7 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
} }
ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) { ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) {
this->fan_mode_ = fan_mode; this->fan_mode_ = fan_mode;
this->custom_fan_mode_.reset();
return *this; return *this;
} }
ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) { ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
@ -145,12 +180,60 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
this->set_fan_mode(CLIMATE_FAN_FOCUS); this->set_fan_mode(CLIMATE_FAN_FOCUS);
} else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) { } else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
this->set_fan_mode(CLIMATE_FAN_DIFFUSE); this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
} else {
auto custom_fan_modes = this->parent_->get_traits().get_supported_custom_fan_modes();
if (std::find(custom_fan_modes.begin(), custom_fan_modes.end(), fan_mode) != custom_fan_modes.end()) {
this->custom_fan_mode_ = fan_mode;
this->fan_mode_.reset();
} else { } else {
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str()); ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
} }
}
return *this;
}
ClimateCall &ClimateCall::set_fan_mode(optional<std::string> fan_mode) {
if (fan_mode.has_value()) {
this->set_fan_mode(fan_mode.value());
}
return *this;
}
ClimateCall &ClimateCall::set_preset(ClimatePreset preset) {
this->preset_ = preset;
this->custom_preset_.reset();
return *this;
}
ClimateCall &ClimateCall::set_preset(const std::string &preset) {
if (str_equals_case_insensitive(preset, "ECO")) {
this->set_preset(CLIMATE_PRESET_ECO);
} else if (str_equals_case_insensitive(preset, "AWAY")) {
this->set_preset(CLIMATE_PRESET_AWAY);
} else if (str_equals_case_insensitive(preset, "BOOST")) {
this->set_preset(CLIMATE_PRESET_BOOST);
} else if (str_equals_case_insensitive(preset, "COMFORT")) {
this->set_preset(CLIMATE_PRESET_COMFORT);
} else if (str_equals_case_insensitive(preset, "HOME")) {
this->set_preset(CLIMATE_PRESET_HOME);
} else if (str_equals_case_insensitive(preset, "SLEEP")) {
this->set_preset(CLIMATE_PRESET_SLEEP);
} else if (str_equals_case_insensitive(preset, "ACTIVITY")) {
this->set_preset(CLIMATE_PRESET_ACTIVITY);
} else {
auto custom_presets = this->parent_->get_traits().get_supported_custom_presets();
if (std::find(custom_presets.begin(), custom_presets.end(), preset) != custom_presets.end()) {
this->custom_preset_ = preset;
this->preset_.reset();
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), preset.c_str());
}
}
return *this;
}
ClimateCall &ClimateCall::set_preset(optional<std::string> preset) {
if (preset.has_value()) {
this->set_preset(preset.value());
}
return *this; return *this;
} }
ClimateCall &ClimateCall::set_swing_mode(ClimateSwingMode swing_mode) { ClimateCall &ClimateCall::set_swing_mode(ClimateSwingMode swing_mode) {
this->swing_mode_ = swing_mode; this->swing_mode_ = swing_mode;
return *this; return *this;
@ -188,6 +271,9 @@ const optional<float> &ClimateCall::get_target_temperature_low() const { return
const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; } const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; }
const optional<bool> &ClimateCall::get_away() const { return this->away_; } const optional<bool> &ClimateCall::get_away() const { return this->away_; }
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; } const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; }
const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; }
const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; }
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; } const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
ClimateCall &ClimateCall::set_away(bool away) { ClimateCall &ClimateCall::set_away(bool away) {
this->away_ = away; this->away_ = away;
@ -215,6 +301,12 @@ ClimateCall &ClimateCall::set_mode(optional<ClimateMode> mode) {
} }
ClimateCall &ClimateCall::set_fan_mode(optional<ClimateFanMode> fan_mode) { ClimateCall &ClimateCall::set_fan_mode(optional<ClimateFanMode> fan_mode) {
this->fan_mode_ = fan_mode; this->fan_mode_ = fan_mode;
this->custom_fan_mode_.reset();
return *this;
}
ClimateCall &ClimateCall::set_preset(optional<ClimatePreset> preset) {
this->preset_ = preset;
this->custom_preset_.reset();
return *this; return *this;
} }
ClimateCall &ClimateCall::set_swing_mode(optional<ClimateSwingMode> swing_mode) { ClimateCall &ClimateCall::set_swing_mode(optional<ClimateSwingMode> swing_mode) {
@ -249,8 +341,31 @@ void Climate::save_state_() {
if (traits.get_supports_away()) { if (traits.get_supports_away()) {
state.away = this->away; state.away = this->away;
} }
if (traits.get_supports_fan_modes()) { if (traits.get_supports_fan_modes() && fan_mode.has_value()) {
state.fan_mode = this->fan_mode; state.uses_custom_fan_mode = false;
state.fan_mode = this->fan_mode.value();
}
if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) {
state.uses_custom_fan_mode = true;
auto &custom_fan_modes = traits.get_supported_custom_fan_modes();
auto it = std::find(custom_fan_modes.begin(), custom_fan_modes.end(), this->custom_fan_mode.value());
// only set custom fan mode if value exists, otherwise leave it as is
if (it != custom_fan_modes.cend()) {
state.custom_fan_mode = std::distance(custom_fan_modes.begin(), it);
}
}
if (traits.get_supports_presets() && preset.has_value()) {
state.uses_custom_preset = false;
state.preset = this->preset.value();
}
if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) {
state.uses_custom_preset = true;
auto custom_presets = traits.get_supported_custom_presets();
auto it = std::find(custom_presets.begin(), custom_presets.end(), this->custom_preset.value());
// only set custom preset if value exists, otherwise leave it as is
if (it != custom_presets.cend()) {
state.custom_preset = std::distance(custom_presets.begin(), it);
}
} }
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
state.swing_mode = this->swing_mode; state.swing_mode = this->swing_mode;
@ -266,8 +381,17 @@ void Climate::publish_state() {
if (traits.get_supports_action()) { if (traits.get_supports_action()) {
ESP_LOGD(TAG, " Action: %s", climate_action_to_string(this->action)); ESP_LOGD(TAG, " Action: %s", climate_action_to_string(this->action));
} }
if (traits.get_supports_fan_modes()) { if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) {
ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode)); ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode.value()));
}
if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) {
ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode.value().c_str());
}
if (traits.get_supports_presets() && this->preset.has_value()) {
ESP_LOGD(TAG, " Preset: %s", climate_preset_to_string(this->preset.value()));
}
if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) {
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset.value().c_str());
} }
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
ESP_LOGD(TAG, " Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode)); ESP_LOGD(TAG, " Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode));
@ -332,9 +456,12 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
if (traits.get_supports_away()) { if (traits.get_supports_away()) {
call.set_away(this->away); call.set_away(this->away);
} }
if (traits.get_supports_fan_modes()) { if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
call.set_fan_mode(this->fan_mode); call.set_fan_mode(this->fan_mode);
} }
if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
call.set_preset(this->preset);
}
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
call.set_swing_mode(this->swing_mode); call.set_swing_mode(this->swing_mode);
} }
@ -352,9 +479,21 @@ void ClimateDeviceRestoreState::apply(Climate *climate) {
if (traits.get_supports_away()) { if (traits.get_supports_away()) {
climate->away = this->away; climate->away = this->away;
} }
if (traits.get_supports_fan_modes()) { if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) {
climate->fan_mode = this->fan_mode; climate->fan_mode = this->fan_mode;
} }
if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) {
climate->custom_fan_mode = traits.get_supported_custom_fan_modes()[this->custom_fan_mode];
}
if (traits.get_supports_presets() && !this->uses_custom_preset) {
climate->preset = this->preset;
}
if (!traits.get_supported_custom_presets().empty() && this->uses_custom_preset) {
climate->custom_preset = traits.get_supported_custom_presets()[this->custom_preset];
}
if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) {
climate->custom_preset = traits.get_supported_custom_presets()[this->preset];
}
if (traits.get_supports_swing_modes()) { if (traits.get_supports_swing_modes()) {
climate->swing_mode = this->swing_mode; climate->swing_mode = this->swing_mode;
} }

View file

@ -3,6 +3,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/preferences.h" #include "esphome/core/preferences.h"
#include "esphome/core/log.h"
#include "climate_mode.h" #include "climate_mode.h"
#include "climate_traits.h" #include "climate_traits.h"
@ -70,12 +71,22 @@ class ClimateCall {
ClimateCall &set_fan_mode(optional<ClimateFanMode> fan_mode); ClimateCall &set_fan_mode(optional<ClimateFanMode> fan_mode);
/// Set the fan mode of the climate device based on a string. /// Set the fan mode of the climate device based on a string.
ClimateCall &set_fan_mode(const std::string &fan_mode); ClimateCall &set_fan_mode(const std::string &fan_mode);
/// Set the fan mode of the climate device based on a string.
ClimateCall &set_fan_mode(optional<std::string> fan_mode);
/// Set the swing mode of the climate device. /// Set the swing mode of the climate device.
ClimateCall &set_swing_mode(ClimateSwingMode swing_mode); ClimateCall &set_swing_mode(ClimateSwingMode swing_mode);
/// Set the swing mode of the climate device. /// Set the swing mode of the climate device.
ClimateCall &set_swing_mode(optional<ClimateSwingMode> swing_mode); ClimateCall &set_swing_mode(optional<ClimateSwingMode> swing_mode);
/// Set the swing mode of the climate device based on a string. /// Set the swing mode of the climate device based on a string.
ClimateCall &set_swing_mode(const std::string &swing_mode); ClimateCall &set_swing_mode(const std::string &swing_mode);
/// Set the preset of the climate device.
ClimateCall &set_preset(ClimatePreset preset);
/// Set the preset of the climate device.
ClimateCall &set_preset(optional<ClimatePreset> preset);
/// Set the preset of the climate device based on a string.
ClimateCall &set_preset(const std::string &preset);
/// Set the preset of the climate device based on a string.
ClimateCall &set_preset(optional<std::string> preset);
void perform(); void perform();
@ -86,6 +97,9 @@ class ClimateCall {
const optional<bool> &get_away() const; const optional<bool> &get_away() const;
const optional<ClimateFanMode> &get_fan_mode() const; const optional<ClimateFanMode> &get_fan_mode() const;
const optional<ClimateSwingMode> &get_swing_mode() const; const optional<ClimateSwingMode> &get_swing_mode() const;
const optional<std::string> &get_custom_fan_mode() const;
const optional<ClimatePreset> &get_preset() const;
const optional<std::string> &get_custom_preset() const;
protected: protected:
void validate_(); void validate_();
@ -98,13 +112,25 @@ class ClimateCall {
optional<bool> away_; optional<bool> away_;
optional<ClimateFanMode> fan_mode_; optional<ClimateFanMode> fan_mode_;
optional<ClimateSwingMode> swing_mode_; optional<ClimateSwingMode> swing_mode_;
optional<std::string> custom_fan_mode_;
optional<ClimatePreset> preset_;
optional<std::string> custom_preset_;
}; };
/// Struct used to save the state of the climate device in restore memory. /// Struct used to save the state of the climate device in restore memory.
struct ClimateDeviceRestoreState { struct ClimateDeviceRestoreState {
ClimateMode mode; ClimateMode mode;
bool away; bool away;
bool uses_custom_fan_mode{false};
union {
ClimateFanMode fan_mode; ClimateFanMode fan_mode;
uint8_t custom_fan_mode;
};
bool uses_custom_preset{false};
union {
ClimatePreset preset;
uint8_t custom_preset;
};
ClimateSwingMode swing_mode; ClimateSwingMode swing_mode;
union { union {
float target_temperature; float target_temperature;
@ -168,11 +194,20 @@ class Climate : public Nameable {
bool away{false}; bool away{false};
/// The active fan mode of the climate device. /// The active fan mode of the climate device.
ClimateFanMode fan_mode; optional<ClimateFanMode> fan_mode;
/// The active swing mode of the climate device. /// The active swing mode of the climate device.
ClimateSwingMode swing_mode; ClimateSwingMode swing_mode;
/// The active custom fan mode of the climate device.
optional<std::string> custom_fan_mode;
/// The active preset of the climate device.
optional<ClimatePreset> preset;
/// The active custom preset mode of the climate device.
optional<std::string> custom_preset;
/** Add a callback for the climate device state, each time the state of the climate device is updated /** Add a callback for the climate device state, each time the state of the climate device is updated
* (using publish_state), this callback will be called. * (using publish_state), this callback will be called.
* *

View file

@ -17,6 +17,8 @@ const char *climate_mode_to_string(ClimateMode mode) {
return "FAN_ONLY"; return "FAN_ONLY";
case CLIMATE_MODE_DRY: case CLIMATE_MODE_DRY:
return "DRY"; return "DRY";
case CLIMATE_MODE_HEAT_COOL:
return "HEAT_COOL";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
@ -80,5 +82,26 @@ const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
} }
} }
const char *climate_preset_to_string(ClimatePreset preset) {
switch (preset) {
case climate::CLIMATE_PRESET_ECO:
return "ECO";
case climate::CLIMATE_PRESET_AWAY:
return "AWAY";
case climate::CLIMATE_PRESET_BOOST:
return "BOOST";
case climate::CLIMATE_PRESET_COMFORT:
return "COMFORT";
case climate::CLIMATE_PRESET_HOME:
return "HOME";
case climate::CLIMATE_PRESET_SLEEP:
return "SLEEP";
case climate::CLIMATE_PRESET_ACTIVITY:
return "ACTIVITY";
default:
return "UNKNOWN";
}
}
} // namespace climate } // namespace climate
} // namespace esphome } // namespace esphome

View file

@ -10,7 +10,7 @@ enum ClimateMode : uint8_t {
/// The climate device is off (not in auto, heat or cool mode) /// The climate device is off (not in auto, heat or cool mode)
CLIMATE_MODE_OFF = 0, CLIMATE_MODE_OFF = 0,
/// The climate device is set to automatically change the heating/cooling cycle /// The climate device is set to automatically change the heating/cooling cycle
CLIMATE_MODE_AUTO = 1, CLIMATE_MODE_HEAT_COOL = 1,
/// The climate device is manually set to cool mode (not in auto mode!) /// The climate device is manually set to cool mode (not in auto mode!)
CLIMATE_MODE_COOL = 2, CLIMATE_MODE_COOL = 2,
/// The climate device is manually set to heat mode (not in auto mode!) /// The climate device is manually set to heat mode (not in auto mode!)
@ -19,6 +19,8 @@ enum ClimateMode : uint8_t {
CLIMATE_MODE_FAN_ONLY = 4, CLIMATE_MODE_FAN_ONLY = 4,
/// The climate device is manually set to dry mode /// The climate device is manually set to dry mode
CLIMATE_MODE_DRY = 5, CLIMATE_MODE_DRY = 5,
/// The climate device is manually set to heat-cool mode
CLIMATE_MODE_AUTO = 6
}; };
/// Enum for the current action of the climate device. Values match those of ClimateMode. /// Enum for the current action of the climate device. Values match those of ClimateMode.
@ -61,7 +63,7 @@ enum ClimateFanMode : uint8_t {
/// Enum for all modes a climate swing can be in /// Enum for all modes a climate swing can be in
enum ClimateSwingMode : uint8_t { enum ClimateSwingMode : uint8_t {
/// The sing mode is set to Off /// The swing mode is set to Off
CLIMATE_SWING_OFF = 0, CLIMATE_SWING_OFF = 0,
/// The fan mode is set to Both /// The fan mode is set to Both
CLIMATE_SWING_BOTH = 1, CLIMATE_SWING_BOTH = 1,
@ -71,6 +73,24 @@ enum ClimateSwingMode : uint8_t {
CLIMATE_SWING_HORIZONTAL = 3, CLIMATE_SWING_HORIZONTAL = 3,
}; };
/// Enum for all modes a climate swing can be in
enum ClimatePreset : uint8_t {
/// Preset is set to ECO
CLIMATE_PRESET_ECO = 0,
/// Preset is set to AWAY
CLIMATE_PRESET_AWAY = 1,
/// Preset is set to BOOST
CLIMATE_PRESET_BOOST = 2,
/// Preset is set to COMFORT
CLIMATE_PRESET_COMFORT = 3,
/// Preset is set to HOME
CLIMATE_PRESET_HOME = 4,
/// Preset is set to SLEEP
CLIMATE_PRESET_SLEEP = 5,
/// Preset is set to ACTIVITY
CLIMATE_PRESET_ACTIVITY = 6,
};
/// Convert the given ClimateMode to a human-readable string. /// Convert the given ClimateMode to a human-readable string.
const char *climate_mode_to_string(ClimateMode mode); const char *climate_mode_to_string(ClimateMode mode);
@ -83,5 +103,8 @@ const char *climate_fan_mode_to_string(ClimateFanMode mode);
/// Convert the given ClimateSwingMode to a human-readable string. /// Convert the given ClimateSwingMode to a human-readable string.
const char *climate_swing_mode_to_string(ClimateSwingMode mode); const char *climate_swing_mode_to_string(ClimateSwingMode mode);
/// Convert the given ClimateSwingMode to a human-readable string.
const char *climate_preset_to_string(ClimatePreset preset);
} // namespace climate } // namespace climate
} // namespace esphome } // namespace esphome

View file

@ -119,6 +119,71 @@ bool ClimateTraits::get_supports_fan_modes() const {
this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ || this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_; this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_;
} }
void ClimateTraits::set_supported_custom_fan_modes(std::vector<std::string> &supported_custom_fan_modes) {
this->supported_custom_fan_modes_ = supported_custom_fan_modes;
}
const std::vector<std::string> ClimateTraits::get_supported_custom_fan_modes() const {
return this->supported_custom_fan_modes_;
}
bool ClimateTraits::supports_custom_fan_mode(std::string &custom_fan_mode) const {
return std::count(this->supported_custom_fan_modes_.begin(), this->supported_custom_fan_modes_.end(),
custom_fan_mode);
}
bool ClimateTraits::supports_preset(ClimatePreset preset) const {
switch (preset) {
case climate::CLIMATE_PRESET_ECO:
return this->supports_preset_eco_;
case climate::CLIMATE_PRESET_AWAY:
return this->supports_preset_away_;
case climate::CLIMATE_PRESET_BOOST:
return this->supports_preset_boost_;
case climate::CLIMATE_PRESET_COMFORT:
return this->supports_preset_comfort_;
case climate::CLIMATE_PRESET_HOME:
return this->supports_preset_home_;
case climate::CLIMATE_PRESET_SLEEP:
return this->supports_preset_sleep_;
case climate::CLIMATE_PRESET_ACTIVITY:
return this->supports_preset_activity_;
default:
return false;
}
}
void ClimateTraits::set_supports_preset_eco(bool supports_preset_eco) {
this->supports_preset_eco_ = supports_preset_eco;
}
void ClimateTraits::set_supports_preset_away(bool supports_preset_away) {
this->supports_preset_away_ = supports_preset_away;
}
void ClimateTraits::set_supports_preset_boost(bool supports_preset_boost) {
this->supports_preset_boost_ = supports_preset_boost;
}
void ClimateTraits::set_supports_preset_comfort(bool supports_preset_comfort) {
this->supports_preset_comfort_ = supports_preset_comfort;
}
void ClimateTraits::set_supports_preset_home(bool supports_preset_home) {
this->supports_preset_home_ = supports_preset_home;
}
void ClimateTraits::set_supports_preset_sleep(bool supports_preset_sleep) {
this->supports_preset_sleep_ = supports_preset_sleep;
}
void ClimateTraits::set_supports_preset_activity(bool supports_preset_activity) {
this->supports_preset_activity_ = supports_preset_activity;
}
bool ClimateTraits::get_supports_presets() const {
return this->supports_preset_eco_ || this->supports_preset_away_ || this->supports_preset_boost_ ||
this->supports_preset_comfort_ || this->supports_preset_home_ || this->supports_preset_sleep_ ||
this->supports_preset_activity_;
}
void ClimateTraits::set_supported_custom_presets(std::vector<std::string> &supported_custom_presets) {
this->supported_custom_presets_ = supported_custom_presets;
}
const std::vector<std::string> ClimateTraits::get_supported_custom_presets() const {
return this->supported_custom_presets_;
}
bool ClimateTraits::supports_custom_preset(std::string &custom_preset) const {
return std::count(this->supported_custom_presets_.begin(), this->supported_custom_presets_.end(), custom_preset);
}
void ClimateTraits::set_supports_swing_mode_off(bool supports_swing_mode_off) { void ClimateTraits::set_supports_swing_mode_off(bool supports_swing_mode_off) {
this->supports_swing_mode_off_ = supports_swing_mode_off; this->supports_swing_mode_off_ = supports_swing_mode_off;
} }

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "esphome/core/helpers.h"
#include "climate_mode.h" #include "climate_mode.h"
namespace esphome { namespace esphome {
@ -65,6 +66,21 @@ class ClimateTraits {
void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse); void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse);
bool supports_fan_mode(ClimateFanMode fan_mode) const; bool supports_fan_mode(ClimateFanMode fan_mode) const;
bool get_supports_fan_modes() const; bool get_supports_fan_modes() const;
void set_supported_custom_fan_modes(std::vector<std::string> &supported_custom_fan_modes);
const std::vector<std::string> get_supported_custom_fan_modes() const;
bool supports_custom_fan_mode(std::string &custom_fan_mode) const;
bool supports_preset(ClimatePreset preset) const;
void set_supports_preset_eco(bool supports_preset_eco);
void set_supports_preset_away(bool supports_preset_away);
void set_supports_preset_boost(bool supports_preset_boost);
void set_supports_preset_comfort(bool supports_preset_comfort);
void set_supports_preset_home(bool supports_preset_home);
void set_supports_preset_sleep(bool supports_preset_sleep);
void set_supports_preset_activity(bool supports_preset_activity);
bool get_supports_presets() const;
void set_supported_custom_presets(std::vector<std::string> &supported_custom_presets);
const std::vector<std::string> get_supported_custom_presets() const;
bool supports_custom_preset(std::string &custom_preset) const;
void set_supports_swing_mode_off(bool supports_swing_mode_off); void set_supports_swing_mode_off(bool supports_swing_mode_off);
void set_supports_swing_mode_both(bool supports_swing_mode_both); void set_supports_swing_mode_both(bool supports_swing_mode_both);
void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical); void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
@ -103,6 +119,15 @@ class ClimateTraits {
bool supports_swing_mode_both_{false}; bool supports_swing_mode_both_{false};
bool supports_swing_mode_vertical_{false}; bool supports_swing_mode_vertical_{false};
bool supports_swing_mode_horizontal_{false}; bool supports_swing_mode_horizontal_{false};
bool supports_preset_eco_{false};
bool supports_preset_away_{false};
bool supports_preset_boost_{false};
bool supports_preset_comfort_{false};
bool supports_preset_home_{false};
bool supports_preset_sleep_{false};
bool supports_preset_activity_{false};
std::vector<std::string> supported_custom_fan_modes_;
std::vector<std::string> supported_custom_presets_;
float visual_min_temperature_{10}; float visual_min_temperature_{10};
float visual_max_temperature_{30}; float visual_max_temperature_{30};

View file

@ -9,7 +9,6 @@ from esphome.components import (
) )
from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID
from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR
from esphome.core import coroutine
AUTO_LOAD = ["sensor", "remote_base"] AUTO_LOAD = ["sensor", "remote_base"]
CODEOWNERS = ["@glmnet"] CODEOWNERS = ["@glmnet"]
@ -39,19 +38,18 @@ CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend(
) )
@coroutine async def register_climate_ir(var, config):
def register_climate_ir(var, config): await cg.register_component(var, config)
yield cg.register_component(var, config) await climate.register_climate(var, config)
yield climate.register_climate(var, config)
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL])) cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT])) cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
if CONF_SENSOR in config: if CONF_SENSOR in config:
sens = yield cg.get_variable(config[CONF_SENSOR]) sens = await cg.get_variable(config[CONF_SENSOR])
cg.add(var.set_sensor(sens)) cg.add(var.set_sensor(sens))
if CONF_RECEIVER_ID in config: if CONF_RECEIVER_ID in config:
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID]) receiver = await cg.get_variable(config[CONF_RECEIVER_ID])
cg.add(receiver.register_listener(var)) cg.add(receiver.register_listener(var))
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID]) transmitter = await cg.get_variable(config[CONF_TRANSMITTER_ID])
cg.add(var.set_transmitter(transmitter)) cg.add(var.set_transmitter(transmitter))

View file

@ -36,9 +36,9 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield climate_ir.register_climate_ir(var, config) await climate_ir.register_climate_ir(var, config)
cg.add(var.set_header_high(config[CONF_HEADER_HIGH])) cg.add(var.set_header_high(config[CONF_HEADER_HIGH]))
cg.add(var.set_header_low(config[CONF_HEADER_LOW])) cg.add(var.set_header_low(config[CONF_HEADER_LOW]))

View file

@ -72,7 +72,7 @@ void LgIrClimate::transmit_state() {
remote_state |= FAN_AUTO; remote_state |= FAN_AUTO;
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY || } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY ||
this->mode == climate::CLIMATE_MODE_HEAT) { this->mode == climate::CLIMATE_MODE_HEAT) {
switch (this->fan_mode) { switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_HIGH: case climate::CLIMATE_FAN_HIGH:
remote_state |= FAN_MAX; remote_state |= FAN_MAX;
break; break;

View file

@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.Schema(
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
def to_code(config): async def to_code(config):
r = 0 r = 0
if CONF_RED in config: if CONF_RED in config:
r = int(config[CONF_RED] * 255) r = int(config[CONF_RED] * 255)

View file

@ -16,6 +16,6 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
) )
def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield climate_ir.register_climate_ir(var, config) await climate_ir.register_climate_ir(var, config)

View file

@ -93,7 +93,7 @@ void CoolixClimate::transmit_state() {
this->fan_mode = climate::CLIMATE_FAN_AUTO; this->fan_mode = climate::CLIMATE_FAN_AUTO;
remote_state |= COOLIX_FAN_MODE_AUTO_DRY; remote_state |= COOLIX_FAN_MODE_AUTO_DRY;
} else { } else {
switch (this->fan_mode) { switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_HIGH: case climate::CLIMATE_FAN_HIGH:
remote_state |= COOLIX_FAN_MAX; remote_state |= COOLIX_FAN_MAX;
break; break;

View file

@ -14,7 +14,7 @@ from esphome.const import (
CONF_MQTT_ID, CONF_MQTT_ID,
CONF_NAME, CONF_NAME,
) )
from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@ -73,8 +73,7 @@ COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
) )
@coroutine async def setup_cover_core_(var, config):
def setup_cover_core_(var, config):
cg.add(var.set_name(config[CONF_NAME])) cg.add(var.set_name(config[CONF_NAME]))
if CONF_INTERNAL in config: if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL])) cg.add(var.set_internal(config[CONF_INTERNAL]))
@ -83,15 +82,14 @@ def setup_cover_core_(var, config):
if CONF_MQTT_ID in config: if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
yield mqtt.register_mqtt_component(mqtt_, config) await mqtt.register_mqtt_component(mqtt_, config)
@coroutine async def register_cover(var, config):
def register_cover(var, config):
if not CORE.has_id(config[CONF_ID]): if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var) var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_cover(var)) cg.add(cg.App.register_cover(var))
yield setup_cover_core_(var, config) await setup_cover_core_(var, config)
COVER_ACTION_SCHEMA = maybe_simple_id( COVER_ACTION_SCHEMA = maybe_simple_id(
@ -102,21 +100,21 @@ COVER_ACTION_SCHEMA = maybe_simple_id(
@automation.register_action("cover.open", OpenAction, COVER_ACTION_SCHEMA) @automation.register_action("cover.open", OpenAction, COVER_ACTION_SCHEMA)
def cover_open_to_code(config, action_id, template_arg, args): async def cover_open_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action("cover.close", CloseAction, COVER_ACTION_SCHEMA) @automation.register_action("cover.close", CloseAction, COVER_ACTION_SCHEMA)
def cover_close_to_code(config, action_id, template_arg, args): async def cover_close_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action("cover.stop", StopAction, COVER_ACTION_SCHEMA) @automation.register_action("cover.stop", StopAction, COVER_ACTION_SCHEMA)
def cover_stop_to_code(config, action_id, template_arg, args): async def cover_stop_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren) return cg.new_Pvariable(action_id, template_arg, paren)
COVER_CONTROL_ACTION_SCHEMA = cv.Schema( COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
@ -131,25 +129,25 @@ COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
@automation.register_action("cover.control", ControlAction, COVER_CONTROL_ACTION_SCHEMA) @automation.register_action("cover.control", ControlAction, COVER_CONTROL_ACTION_SCHEMA)
def cover_control_to_code(config, action_id, template_arg, args): async def cover_control_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren) var = cg.new_Pvariable(action_id, template_arg, paren)
if CONF_STOP in config: if CONF_STOP in config:
template_ = yield cg.templatable(config[CONF_STOP], args, bool) template_ = await cg.templatable(config[CONF_STOP], args, bool)
cg.add(var.set_stop(template_)) cg.add(var.set_stop(template_))
if CONF_STATE in config: if CONF_STATE in config:
template_ = yield cg.templatable(config[CONF_STATE], args, float) template_ = await cg.templatable(config[CONF_STATE], args, float)
cg.add(var.set_position(template_)) cg.add(var.set_position(template_))
if CONF_POSITION in config: if CONF_POSITION in config:
template_ = yield cg.templatable(config[CONF_POSITION], args, float) template_ = await cg.templatable(config[CONF_POSITION], args, float)
cg.add(var.set_position(template_)) cg.add(var.set_position(template_))
if CONF_TILT in config: if CONF_TILT in config:
template_ = yield cg.templatable(config[CONF_TILT], args, float) template_ = await cg.templatable(config[CONF_TILT], args, float)
cg.add(var.set_tilt(template_)) cg.add(var.set_tilt(template_))
yield var return var
@coroutine_with_priority(100.0) @coroutine_with_priority(100.0)
def to_code(config): async def to_code(config):
cg.add_define("USE_COVER") cg.add_define("USE_COVER")
cg.add_global(cover_ns.using) cg.add_global(cover_ns.using)

View file

View file

@ -0,0 +1,342 @@
#include "cs5460a.h"
#include "esphome/core/log.h"
namespace esphome {
namespace cs5460a {
static const char *TAG = "cs5460a";
void CS5460AComponent::write_register_(enum CS5460ARegister addr, uint32_t value) {
this->write_byte(CMD_WRITE | (addr << 1));
this->write_byte(value >> 16);
this->write_byte(value >> 8);
this->write_byte(value >> 0);
}
uint32_t CS5460AComponent::read_register_(uint8_t addr) {
uint32_t value;
this->write_byte(CMD_READ | (addr << 1));
value = (uint32_t) this->transfer_byte(CMD_SYNC0) << 16;
value |= (uint32_t) this->transfer_byte(CMD_SYNC0) << 8;
value |= this->transfer_byte(CMD_SYNC0) << 0;
return value;
}
bool CS5460AComponent::softreset_() {
uint32_t pc = ((uint8_t) phase_offset_ & 0x3f) | (phase_offset_ < 0 ? 0x40 : 0);
uint32_t config = (1 << 0) | /* K = 0b0001 */
(current_hpf_ ? 1 << 5 : 0) | /* IHPF */
(voltage_hpf_ ? 1 << 6 : 0) | /* VHPF */
(pga_gain_ << 16) | /* Gi */
(pc << 17); /* PC */
int cnt = 0;
/* Serial resynchronization */
this->write_byte(CMD_SYNC1);
this->write_byte(CMD_SYNC1);
this->write_byte(CMD_SYNC1);
this->write_byte(CMD_SYNC0);
/* Reset */
this->write_register_(REG_CONFIG, 1 << 7);
delay(10);
while (cnt++ < 50 && (this->read_register_(REG_CONFIG) & 0x81) != 0x000001)
;
if (cnt > 50)
return false;
this->write_register_(REG_CONFIG, config);
return true;
}
void CS5460AComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up CS5460A...");
float current_full_scale = (pga_gain_ == CS5460A_PGA_GAIN_10X) ? 0.25 : 0.10;
float voltage_full_scale = 0.25;
current_multiplier_ = current_full_scale / (fabsf(current_gain_) * 0x1000000);
voltage_multiplier_ = voltage_full_scale / (voltage_gain_ * 0x1000000);
/*
* Calculate power from the Energy register because the Power register
* stores instantaneous power which varies a lot in each AC cycle,
* while the Energy value is accumulated over the "computation cycle"
* which should be an integer number of AC cycles.
*/
power_multiplier_ =
(current_full_scale * voltage_full_scale * 4096) / (current_gain_ * voltage_gain_ * samples_ * 0x800000);
pulse_freq_ =
(current_full_scale * voltage_full_scale) / (fabsf(current_gain_) * voltage_gain_ * pulse_energy_wh_ * 3600);
hw_init_();
}
void CS5460AComponent::hw_init_() {
this->spi_setup();
this->enable();
if (!this->softreset_()) {
this->disable();
ESP_LOGE(TAG, "CS5460A reset failed!");
this->mark_failed();
return;
}
uint32_t status = this->read_register_(REG_STATUS);
ESP_LOGCONFIG(TAG, " Version: %x", (status >> 6) & 7);
this->write_register_(REG_CYCLE_COUNT, samples_);
this->write_register_(REG_PULSE_RATE, lroundf(pulse_freq_ * 32.0f));
/* Use one of the power saving features (assuming external oscillator), reset other CONTROL bits,
* sometimes softreset_() is not enough */
this->write_register_(REG_CONTROL, 0x000004);
this->restart_();
this->disable();
ESP_LOGCONFIG(TAG, " Init ok");
}
/* Doesn't reset the register values etc., just restarts the "computation cycle" */
void CS5460AComponent::restart_() {
int cnt;
this->enable();
/* Stop running conversion, wake up if needed */
this->write_byte(CMD_POWER_UP);
/* Start continuous conversion */
this->write_byte(CMD_START_CONT);
this->disable();
this->started_();
}
void CS5460AComponent::started_() {
/*
* Try to guess when the next batch of results is going to be ready and
* schedule next STATUS check some time before that moment. This assumes
* two things:
* * a new "computation cycle" started just now. If it started some
* time ago we may be a late next time, but hopefully less late in each
* iteration -- that's why we schedule the next check in some 0.8 of
* the time we actually expect the next reading ready.
* * MCLK rate is 4.096MHz and K == 1. If there's a CS5460A module in
* use with a different clock this will need to be parametrised.
*/
expect_data_ts_ = millis() + samples_ * 1024 / 4096;
schedule_next_check_();
}
void CS5460AComponent::schedule_next_check_() {
int32_t time_left = expect_data_ts_ - millis();
/* First try at 0.8 of the actual expected time (if it's in the future) */
if (time_left > 0)
time_left -= time_left / 5;
if (time_left > -500) {
/* But not sooner than in 30ms from now */
if (time_left < 30)
time_left = 30;
} else {
/*
* If the measurement is more than 0.5s overdue start worrying. The
* device may be stuck because of an overcurrent error or similar,
* from now on just retry every 1s. After 15s try a reset, if it
* fails we give up and mark the component "failed".
*/
if (time_left > -15000) {
time_left = 1000;
this->status_momentary_warning("warning", 1000);
} else {
ESP_LOGCONFIG(TAG, "Device officially stuck, resetting");
this->cancel_timeout("status-check");
this->hw_init_();
return;
}
}
this->set_timeout("status-check", time_left, [this]() {
if (!this->check_status_())
this->schedule_next_check_();
});
}
bool CS5460AComponent::check_status_() {
this->enable();
uint32_t status = this->read_register_(REG_STATUS);
if (!(status & 0xcbf83c)) {
this->disable();
return false;
}
uint32_t clear = 1 << 20;
/* TODO: Report if IC=0 but only once as it can't be cleared */
if (status & (1 << 2)) {
clear |= 1 << 2;
ESP_LOGE(TAG, "Low supply detected");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 3)) {
clear |= 1 << 3;
ESP_LOGE(TAG, "Modulator oscillation on current channel");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 4)) {
clear |= 1 << 4;
ESP_LOGE(TAG, "Modulator oscillation on voltage channel");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 5)) {
clear |= 1 << 5;
ESP_LOGE(TAG, "Watch-dog timeout");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 11)) {
clear |= 1 << 11;
ESP_LOGE(TAG, "EOUT Energy Accumulation Register out of range");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 12)) {
clear |= 1 << 12;
ESP_LOGE(TAG, "Energy out of range");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 13)) {
clear |= 1 << 13;
ESP_LOGE(TAG, "RMS voltage out of range");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 14)) {
clear |= 1 << 14;
ESP_LOGE(TAG, "RMS current out of range");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 15)) {
clear |= 1 << 15;
ESP_LOGE(TAG, "Power calculation out of range");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 16)) {
clear |= 1 << 16;
ESP_LOGE(TAG, "Voltage out of range");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 17)) {
clear |= 1 << 17;
ESP_LOGE(TAG, "Current out of range");
this->status_momentary_warning("warning", 500);
}
if (status & (1 << 19)) {
clear |= 1 << 19;
ESP_LOGE(TAG, "Divide overflowed");
}
if (status & (1 << 22)) {
bool dir = status & (1 << 21);
if (current_gain_ < 0)
dir = !dir;
ESP_LOGI(TAG, "Energy counter %s pulse", dir ? "negative" : "positive");
clear |= 1 << 22;
}
uint32_t raw_current = 0; /* Calm the validators */
uint32_t raw_voltage = 0;
uint32_t raw_energy = 0;
if (status & (1 << 23)) {
clear |= 1 << 23;
if (current_sensor_ != nullptr)
raw_current = this->read_register_(REG_IRMS);
if (voltage_sensor_ != nullptr)
raw_voltage = this->read_register_(REG_VRMS);
}
if (status & ((1 << 23) | (1 << 5))) {
/* Read to clear the WDT bit */
raw_energy = this->read_register_(REG_E);
}
this->write_register_(REG_STATUS, clear);
this->disable();
/*
* Schedule the next STATUS check assuming that DRDY was asserted very
* recently, then publish the new values. Do this last for reentrancy in
* case the publish triggers a restart() or for whatever reason needs to
* cancel the timeout set in schedule_next_check_(), or needs to use SPI.
* If the current or power values haven't changed one bit it may be that
* the chip somehow forgot to update the registers -- seen happening very
* rarely. In that case don't publish them because the user may have
* the input connected to a multiplexer and may have switched channels
* since the previous reading and we'd be publishing the stale value for
* the new channel. If the value *was* updated it's very unlikely that
* it wouldn't have changed, especially power/energy which are affected
* by the noise on both the current and value channels (in case of energy,
* accumulated over many conversion cycles.)
*/
if (status & (1 << 23)) {
this->started_();
if (current_sensor_ != nullptr && raw_current != prev_raw_current_) {
current_sensor_->publish_state(raw_current * current_multiplier_);
prev_raw_current_ = raw_current;
}
if (voltage_sensor_ != nullptr)
voltage_sensor_->publish_state(raw_voltage * voltage_multiplier_);
if (power_sensor_ != nullptr && raw_energy != prev_raw_energy_) {
int32_t raw = (int32_t)(raw_energy << 8) >> 8; /* Sign-extend */
power_sensor_->publish_state(raw * power_multiplier_);
prev_raw_energy_ = raw_energy;
}
return true;
}
return false;
}
void CS5460AComponent::dump_config() {
uint32_t state = this->get_component_state();
ESP_LOGCONFIG(TAG, "CS5460A:");
ESP_LOGCONFIG(TAG, " Init status: %s",
state == COMPONENT_STATE_LOOP ? "OK" : (state == COMPONENT_STATE_FAILED ? "failed" : "other"));
LOG_PIN(" CS Pin: ", cs_);
ESP_LOGCONFIG(TAG, " Samples / cycle: %u", samples_);
ESP_LOGCONFIG(TAG, " Phase offset: %i", phase_offset_);
ESP_LOGCONFIG(TAG, " PGA Gain: %s", pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x");
ESP_LOGCONFIG(TAG, " Current gain: %.5f", current_gain_);
ESP_LOGCONFIG(TAG, " Voltage gain: %.5f", voltage_gain_);
ESP_LOGCONFIG(TAG, " Current HPF: %s", current_hpf_ ? "enabled" : "disabled");
ESP_LOGCONFIG(TAG, " Voltage HPF: %s", voltage_hpf_ ? "enabled" : "disabled");
ESP_LOGCONFIG(TAG, " Pulse energy: %.2f Wh", pulse_energy_wh_);
LOG_SENSOR(" ", "Voltage", voltage_sensor_);
LOG_SENSOR(" ", "Current", current_sensor_);
LOG_SENSOR(" ", "Power", power_sensor_);
}
} // namespace cs5460a
} // namespace esphome

View file

@ -0,0 +1,123 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/spi/spi.h"
namespace esphome {
namespace cs5460a {
enum CS5460ACommand {
CMD_SYNC0 = 0xfe,
CMD_SYNC1 = 0xff,
CMD_START_SINGLE = 0xe0,
CMD_START_CONT = 0xe8,
CMD_POWER_UP = 0xa0,
CMD_POWER_STANDBY = 0x88,
CMD_POWER_SLEEP = 0x90,
CMD_CALIBRATION = 0xc0,
CMD_READ = 0x00,
CMD_WRITE = 0x40,
};
enum CS5460ARegister {
REG_CONFIG = 0x00,
REG_IDCOFF = 0x01,
REG_IGN = 0x02,
REG_VDCOFF = 0x03,
REG_VGN = 0x04,
REG_CYCLE_COUNT = 0x05,
REG_PULSE_RATE = 0x06,
REG_I = 0x07,
REG_V = 0x08,
REG_P = 0x09,
REG_E = 0x0a,
REG_IRMS = 0x0b,
REG_VRMS = 0x0c,
REG_TBC = 0x0d,
REG_POFF = 0x0e,
REG_STATUS = 0x0f,
REG_IACOFF = 0x10,
REG_VACOFF = 0x11,
REG_MASK = 0x1a,
REG_CONTROL = 0x1c,
};
/** Enum listing the current channel aplifiergain settings for the CS5460A.
*/
enum CS5460APGAGain {
CS5460A_PGA_GAIN_10X = 0b0,
CS5460A_PGA_GAIN_50X = 0b1,
};
class CS5460AComponent : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
public:
void set_samples(uint32_t samples) { samples_ = samples; }
void set_phase_offset(int8_t phase_offset) { phase_offset_ = phase_offset; }
void set_pga_gain(CS5460APGAGain pga_gain) { pga_gain_ = pga_gain; }
void set_gains(float current_gain, float voltage_gain) {
current_gain_ = current_gain;
voltage_gain_ = voltage_gain;
}
void set_hpf_enable(bool current_hpf, bool voltage_hpf) {
current_hpf_ = current_hpf;
voltage_hpf_ = voltage_hpf;
}
void set_pulse_energy_wh(float pulse_energy_wh) { pulse_energy_wh_ = pulse_energy_wh; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
void restart() { restart_(); }
void setup() override;
void loop() override {}
float get_setup_priority() const override { return setup_priority::DATA; }
void dump_config() override;
protected:
uint32_t samples_;
int8_t phase_offset_;
CS5460APGAGain pga_gain_;
float current_gain_;
float voltage_gain_;
bool current_hpf_;
bool voltage_hpf_;
float pulse_energy_wh_;
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *power_sensor_{nullptr};
void write_register_(enum CS5460ARegister addr, uint32_t value);
uint32_t read_register_(uint8_t addr);
bool softreset_();
void hw_init_();
void restart_();
void started_();
void schedule_next_check_();
bool check_status_();
float current_multiplier_;
float voltage_multiplier_;
float power_multiplier_;
float pulse_freq_;
uint32_t expect_data_ts_;
uint32_t prev_raw_current_{0};
uint32_t prev_raw_energy_{0};
};
template<typename... Ts> class CS5460ARestartAction : public Action<Ts...> {
public:
CS5460ARestartAction(CS5460AComponent *cs5460a) : cs5460a_(cs5460a) {}
void play(Ts... x) override { cs5460a_->restart(); }
protected:
CS5460AComponent *cs5460a_;
};
} // namespace cs5460a
} // namespace esphome

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