Merge branch 'dev' into Xiaomi-MCCGQ02HL

This commit is contained in:
Jesse Hills 2022-01-16 22:41:14 +13:00 committed by GitHub
commit d5c1a154fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
787 changed files with 26025 additions and 5581 deletions

View file

@ -5,11 +5,8 @@ Checks: >-
-altera-*, -altera-*,
-android-*, -android-*,
-boost-*, -boost-*,
-bugprone-branch-clone,
-bugprone-easily-swappable-parameters,
-bugprone-narrowing-conversions, -bugprone-narrowing-conversions,
-bugprone-signed-char-misuse, -bugprone-signed-char-misuse,
-bugprone-too-small-loop-variable,
-cert-dcl50-cpp, -cert-dcl50-cpp,
-cert-err58-cpp, -cert-err58-cpp,
-cert-oop57-cpp, -cert-oop57-cpp,
@ -19,12 +16,10 @@ Checks: >-
-clang-diagnostic-delete-abstract-non-virtual-dtor, -clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor, -clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-shadow-field, -clang-diagnostic-shadow-field,
-clang-diagnostic-sign-compare,
-clang-diagnostic-unused-variable,
-clang-diagnostic-unused-const-variable, -clang-diagnostic-unused-const-variable,
-clang-diagnostic-unused-parameter,
-concurrency-*, -concurrency-*,
-cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-init-variables, -cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage, -cppcoreguidelines-macro-usage,
@ -41,7 +36,6 @@ Checks: >-
-cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions, -cppcoreguidelines-special-member-functions,
-fuchsia-default-arguments,
-fuchsia-multiple-inheritance, -fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator, -fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects, -fuchsia-statically-constructed-objects,
@ -51,6 +45,7 @@ Checks: >-
-google-explicit-constructor, -google-explicit-constructor,
-google-readability-braces-around-statements, -google-readability-braces-around-statements,
-google-readability-casting, -google-readability-casting,
-google-readability-namespace-comments,
-google-readability-todo, -google-readability-todo,
-google-runtime-references, -google-runtime-references,
-hicpp-*, -hicpp-*,
@ -97,9 +92,11 @@ CheckOptions:
value: '1' value: '1'
- key: google-readability-function-size.StatementThreshold - key: google-readability-function-size.StatementThreshold
value: '800' value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines - key: google-runtime-int.TypeSuffix
value: '_t'
- key: llvm-namespace-comment.ShortNamespaceLines
value: '10' value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments - key: llvm-namespace-comment.SpacesBeforeComments
value: '2' value: '2'
- key: modernize-loop-convert.MaxCopySize - key: modernize-loop-convert.MaxCopySize
value: '16' value: '16'
@ -135,10 +132,14 @@ CheckOptions:
value: 'UPPER_CASE' value: 'UPPER_CASE'
- key: readability-identifier-naming.ParameterCase - key: readability-identifier-naming.ParameterCase
value: 'lower_case' value: 'lower_case'
- key: readability-identifier-naming.PrivateMemberPrefix - key: readability-identifier-naming.PrivateMemberCase
value: 'NO_PRIVATE_MEMBERS_ALWAYS_USE_PROTECTED' value: 'lower_case'
- key: readability-identifier-naming.PrivateMethodPrefix - key: readability-identifier-naming.PrivateMemberSuffix
value: 'NO_PRIVATE_METHODS_ALWAYS_USE_PROTECTED' value: '_'
- key: readability-identifier-naming.PrivateMethodCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMethodSuffix
value: '_'
- key: readability-identifier-naming.ClassMemberCase - key: readability-identifier-naming.ClassMemberCase
value: 'lower_case' value: 'lower_case'
- key: readability-identifier-naming.ClassMemberCase - key: readability-identifier-naming.ClassMemberCase

3
.gitattributes vendored
View file

@ -1 +1,2 @@
* text=auto eol=lf # Normalize line endings to LF in the repository
* text eol=lf

View file

@ -16,6 +16,7 @@ Quick description and explanation of changes
## Test Environment ## Test Environment
- [ ] ESP32 - [ ] ESP32
- [ ] ESP32 IDF
- [ ] ESP8266 - [ ] ESP8266
## Example entry for `config.yaml`: ## Example entry for `config.yaml`:

View file

@ -1,7 +0,0 @@
comment: >-
https://github.com/esphome/esphome/issues/430
issueConfigs:
- content:
- "OTHERWISE THE ISSUE WILL BE CLOSED AUTOMATICALLY"
caseInsensitive: false

36
.github/lock.yml vendored
View file

@ -1,36 +0,0 @@
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 7
# Skip issues and pull requests created before a given timestamp. Timestamp must
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
skipCreatedBefore: false
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
exemptLabels:
- keep-open
# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false
# Comment to post before locking. Set to `false` to disable
lockComment: false
# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: false
# Limit to only `issues` or `pulls`
# only: issues
# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
# exemptLabels:
# - help-wanted
# lockLabel: outdated
# pulls:
# daysUntilLock: 30
# Repository to extend settings from
# _extends: repo

View file

@ -17,6 +17,10 @@ on:
- 'requirements*.txt' - 'requirements*.txt'
- 'platformio.ini' - 'platformio.ini'
permissions:
contents: read
packages: read
jobs: jobs:
check-docker: check-docker:
name: Build docker containers name: Build docker containers

View file

@ -1,5 +1,3 @@
# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
name: CI name: CI
on: on:
@ -8,6 +6,13 @@ on:
pull_request: pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
ci: ci:
name: ${{ matrix.name }} name: ${{ matrix.name }}
@ -31,7 +36,7 @@ jobs:
- id: test - id: test
file: tests/test3.yaml file: tests/test3.yaml
name: Test tests/test3.yaml name: Test tests/test3.yaml
pio_cache_key: test1 pio_cache_key: test3
- id: test - id: test
file: tests/test4.yaml file: tests/test4.yaml
name: Test tests/test4.yaml name: Test tests/test4.yaml
@ -46,26 +51,26 @@ jobs:
name: Run script/clang-format name: Run script/clang-format
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP8266 name: Run script/clang-tidy for ESP8266
options: --environment esp8266-tidy --grep USE_ESP8266 options: --environment esp8266-arduino-tidy --grep USE_ESP8266
pio_cache_key: tidyesp8266 pio_cache_key: tidyesp8266
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 1/4 name: Run script/clang-tidy for ESP32 Arduino 1/4
options: --environment esp32-tidy --split-num 4 --split-at 1 options: --environment esp32-arduino-tidy --split-num 4 --split-at 1
pio_cache_key: tidyesp32 pio_cache_key: tidyesp32
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 2/4 name: Run script/clang-tidy for ESP32 Arduino 2/4
options: --environment esp32-tidy --split-num 4 --split-at 2 options: --environment esp32-arduino-tidy --split-num 4 --split-at 2
pio_cache_key: tidyesp32 pio_cache_key: tidyesp32
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 3/4 name: Run script/clang-tidy for ESP32 Arduino 3/4
options: --environment esp32-tidy --split-num 4 --split-at 3 options: --environment esp32-arduino-tidy --split-num 4 --split-at 3
pio_cache_key: tidyesp32 pio_cache_key: tidyesp32
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 4/4 name: Run script/clang-tidy for ESP32 Arduino 4/4
options: --environment esp32-tidy --split-num 4 --split-at 4 options: --environment esp32-arduino-tidy --split-num 4 --split-at 4
pio_cache_key: tidyesp32 pio_cache_key: tidyesp32
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 esp-idf name: Run script/clang-tidy for ESP32 IDF
options: --environment esp32-idf-tidy --grep USE_ESP_IDF options: --environment esp32-idf-tidy --grep USE_ESP_IDF
pio_cache_key: tidyesp32-idf pio_cache_key: tidyesp32-idf
@ -77,18 +82,23 @@ jobs:
with: with:
python-version: '3.7' python-version: '3.7'
- name: Cache pip modules - name: Cache virtualenv
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/.cache/pip path: .venv
key: pip-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }} key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
restore-keys: | restore-keys: |
pip-${{ steps.python.outputs.python-version }}- venv-${{ steps.python.outputs.python-version }}-
- name: Set up python environment - name: Set up virtualenv
run: | run: |
pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt python -m venv .venv
pip3 install -e . source .venv/bin/activate
pip install -U pip
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
pip install -e .
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
# Use per check platformio cache because checks use different parts # Use per check platformio cache because checks use different parts
- name: Cache platformio - name: Cache platformio

View file

@ -9,13 +9,19 @@ permissions:
issues: write issues: write
pull-requests: write pull-requests: write
concurrency:
group: lock
jobs: jobs:
lock: lock:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v2 - uses: dessant/lock-threads@v3
with: with:
github-token: ${{ github.token }} pr-inactive-days: "1"
pr-lock-inactive-days: "1"
pr-lock-reason: "" pr-lock-reason: ""
process-only: prs exclude-any-pr-labels: keep-open
issue-inactive-days: "7"
issue-lock-reason: ""
exclude-any-issue-labels: keep-open

View file

@ -4,7 +4,7 @@
"owner": "ci-custom", "owner": "ci-custom",
"pattern": [ "pattern": [
{ {
"regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$", "regexp": "^(.*):(\\d+):(\\d+):\\s+lint:\\s+(.*)$",
"file": 1, "file": 1,
"line": 2, "line": 2,
"column": 3, "column": 3,

View file

@ -5,7 +5,7 @@
"severity": "error", "severity": "error",
"pattern": [ "pattern": [
{ {
"regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", "regexp": "^src/(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1, "file": 1,
"line": 2, "line": 2,
"column": 3, "column": 3,

View file

@ -1,11 +1,22 @@
{ {
"problemMatcher": [ "problemMatcher": [
{
"owner": "black",
"severity": "error",
"pattern": [
{
"regexp": "^(.*): (Please format this file with the black formatter)",
"file": 1,
"message": 2
}
]
},
{ {
"owner": "flake8", "owner": "flake8",
"severity": "error", "severity": "error",
"pattern": [ "pattern": [
{ {
"regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$", "regexp": "^(.*):(\\d+): ([EFCDNW]\\d{3}.*)$",
"file": 1, "file": 1,
"line": 2, "line": 2,
"message": 3 "message": 3
@ -17,7 +28,7 @@
"severity": "error", "severity": "error",
"pattern": [ "pattern": [
{ {
"regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$", "regexp": "^(.*):(\\d+): (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
"file": 1, "file": 1,
"line": 2, "line": 2,
"message": 3 "message": 3

View file

@ -7,6 +7,9 @@ on:
schedule: schedule:
- cron: "0 2 * * *" - cron: "0 2 * * *"
permissions:
contents: read
jobs: jobs:
init: init:
name: Initialize build name: Initialize build
@ -52,6 +55,9 @@ jobs:
deploy-docker: deploy-docker:
name: Build and publish docker containers name: Build and publish docker containers
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
permissions:
contents: read
packages: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [init] needs: [init]
strategy: strategy:
@ -93,6 +99,9 @@ jobs:
deploy-docker-manifest: deploy-docker-manifest:
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
permissions:
contents: read
packages: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [init, deploy-docker] needs: [init, deploy-docker]
strategy: strategy:

View file

@ -9,13 +9,15 @@ permissions:
issues: write issues: write
pull-requests: write pull-requests: write
concurrency:
group: lock
jobs: jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v4 - uses: actions/stale@v4
with: with:
repo-token: ${{ github.token }}
days-before-pr-stale: 90 days-before-pr-stale: 90
days-before-pr-close: 7 days-before-pr-close: 7
days-before-issue-stale: -1 days-before-issue-stale: -1
@ -28,3 +30,19 @@ jobs:
pull request has been automatically marked as stale because of that pull request has been automatically marked as stale because of that
and will be closed if no further activity occurs within 7 days. and will be closed if no further activity occurs within 7 days.
Thank you for your contributions. Thank you for your contributions.
# Use stale to automatically close issues with a reference to the issue tracker
close-issues:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v4
with:
days-before-pr-stale: -1
days-before-pr-close: -1
days-before-issue-stale: 1
days-before-issue-close: 1
remove-stale-when-updated: true
stale-issue-label: "stale"
exempt-issue-labels: "not-stale"
stale-issue-message: >
https://github.com/esphome/esphome/issues/430

View file

@ -15,6 +15,7 @@ esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core esphome/components/adc/* @esphome/core
esphome/components/addressable_light/* @justfalter esphome/components/addressable_light/* @justfalter
esphome/components/airthings_ble/* @jeromelaban esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_mini/* @ncareau
esphome/components/airthings_wave_plus/* @jeromelaban esphome/components/airthings_wave_plus/* @jeromelaban
esphome/components/am43/* @buxtronix esphome/components/am43/* @buxtronix
esphome/components/am43/cover/* @buxtronix esphome/components/am43/cover/* @buxtronix
@ -27,18 +28,27 @@ esphome/components/b_parasite/* @rbaron
esphome/components/ballu/* @bazuchan esphome/components/ballu/* @bazuchan
esphome/components/bang_bang/* @OttoWinter esphome/components/bang_bang/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core esphome/components/binary_sensor/* @esphome/core
esphome/components/bl0940/* @tobias-
esphome/components/ble_client/* @buxtronix esphome/components/ble_client/* @buxtronix
esphome/components/bme680_bsec/* @trvrnrth esphome/components/bme680_bsec/* @trvrnrth
esphome/components/bmp3xx/* @martgras
esphome/components/button/* @esphome/core
esphome/components/canbus/* @danielschramm @mvturnho esphome/components/canbus/* @danielschramm @mvturnho
esphome/components/cap1188/* @MrEditor97
esphome/components/captive_portal/* @OttoWinter esphome/components/captive_portal/* @OttoWinter
esphome/components/ccs811/* @habbie esphome/components/ccs811/* @habbie
esphome/components/cd74hc4067/* @asoehlke
esphome/components/climate/* @esphome/core esphome/components/climate/* @esphome/core
esphome/components/climate_ir/* @glmnet esphome/components/climate_ir/* @glmnet
esphome/components/color_temperature/* @jesserockz esphome/components/color_temperature/* @jesserockz
esphome/components/coolix/* @glmnet esphome/components/coolix/* @glmnet
esphome/components/cover/* @esphome/core esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
esphome/components/ct_clamp/* @jesserockz esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/daly_bms/* @s1lvi0
esphome/components/dashboard_import/* @esphome/core
esphome/components/debug/* @OttoWinter esphome/components/debug/* @OttoWinter
esphome/components/dfplayer/* @glmnet esphome/components/dfplayer/* @glmnet
esphome/components/dht/* @OttoWinter esphome/components/dht/* @OttoWinter
@ -47,6 +57,8 @@ esphome/components/dsmr/* @glmnet @zuidwijk
esphome/components/esp32/* @esphome/core esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @jesserockz esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble_server/* @jesserockz esphome/components/esp32_ble_server/* @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron
esphome/components/esp32_improv/* @jesserockz esphome/components/esp32_improv/* @jesserockz
esphome/components/esp8266/* @esphome/core esphome/components/esp8266/* @esphome/core
esphome/components/exposure_notifications/* @OttoWinter esphome/components/exposure_notifications/* @OttoWinter
@ -57,22 +69,27 @@ esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle esphome/components/gps/* @coogle
esphome/components/graph/* @synco esphome/components/graph/* @synco
esphome/components/growatt_solar/* @leeuwte
esphome/components/havells_solar/* @sourabhjaiswal esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann esphome/components/hbridge/light/* @DotNetDann
esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hitachi_ac424/* @sourabhjaiswal
esphome/components/homeassistant/* @OttoWinter esphome/components/homeassistant/* @OttoWinter
esphome/components/hrxl_maxsonar_wr/* @netmikey esphome/components/hrxl_maxsonar_wr/* @netmikey
esphome/components/i2c/* @esphome/core esphome/components/i2c/* @esphome/core
esphome/components/improv/* @jesserockz esphome/components/improv_serial/* @esphome/core
esphome/components/ina260/* @MrEditor97
esphome/components/inkbird_ibsth1_mini/* @fkirill esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter esphome/components/integration/* @OttoWinter
esphome/components/interval/* @esphome/core esphome/components/interval/* @esphome/core
esphome/components/json/* @OttoWinter esphome/components/json/* @OttoWinter
esphome/components/kalman_combinator/* @Cat-Ion
esphome/components/ledc/* @OttoWinter esphome/components/ledc/* @OttoWinter
esphome/components/light/* @esphome/core esphome/components/light/* @esphome/core
esphome/components/logger/* @esphome/core esphome/components/logger/* @esphome/core
esphome/components/ltr390/* @sjtrny
esphome/components/max7219digit/* @rspaargaren esphome/components/max7219digit/* @rspaargaren
esphome/components/mcp23008/* @jesserockz esphome/components/mcp23008/* @jesserockz
esphome/components/mcp23017/* @jesserockz esphome/components/mcp23017/* @jesserockz
@ -82,10 +99,21 @@ esphome/components/mcp23x08_base/* @jesserockz
esphome/components/mcp23x17_base/* @jesserockz esphome/components/mcp23x17_base/* @jesserockz
esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp23xxx_base/* @jesserockz
esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp3204/* @rsumner
esphome/components/mcp47a1/* @jesserockz
esphome/components/mcp9808/* @k7hpn esphome/components/mcp9808/* @k7hpn
esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core esphome/components/mdns/* @esphome/core
esphome/components/midea/* @dudanov esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov
esphome/components/mitsubishi/* @RubyBailey esphome/components/mitsubishi/* @RubyBailey
esphome/components/modbus_controller/* @martgras
esphome/components/modbus_controller/binary_sensor/* @martgras
esphome/components/modbus_controller/number/* @martgras
esphome/components/modbus_controller/output/* @martgras
esphome/components/modbus_controller/sensor/* @martgras
esphome/components/modbus_controller/switch/* @martgras
esphome/components/modbus_controller/text_sensor/* @martgras
esphome/components/network/* @esphome/core esphome/components/network/* @esphome/core
esphome/components/nextion/* @senexcrenshaw esphome/components/nextion/* @senexcrenshaw
esphome/components/nextion/binary_sensor/* @senexcrenshaw esphome/components/nextion/binary_sensor/* @senexcrenshaw
@ -105,6 +133,7 @@ esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz esphome/components/pn532_spi/* @OttoWinter @jesserockz
esphome/components/power_supply/* @esphome/core esphome/components/power_supply/* @esphome/core
esphome/components/preferences/* @esphome/core esphome/components/preferences/* @esphome/core
esphome/components/psram/* @esphome/core
esphome/components/pulse_meter/* @stevebaxter esphome/components/pulse_meter/* @stevebaxter
esphome/components/pvvx_mithermometer/* @pasiz esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/rc522/* @glmnet esphome/components/rc522/* @glmnet
@ -114,6 +143,8 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz esphome/components/rgbct/* @jesserockz
esphome/components/rtttl/* @glmnet esphome/components/rtttl/* @glmnet
esphome/components/safe_mode/* @jsuanet @paulmonigatti
esphome/components/scd4x/* @sjtrny
esphome/components/script/* @esphome/core esphome/components/script/* @esphome/core
esphome/components/sdm_meter/* @jesserockz @polyfaces esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath esphome/components/sdp3x/* @Azimath
@ -122,7 +153,7 @@ esphome/components/select/* @esphome/core
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
esphome/components/shutdown/* @esphome/core esphome/components/shutdown/* @esphome/core @jsuanet
esphome/components/sim800l/* @glmnet esphome/components/sim800l/* @glmnet
esphome/components/sm2135/* @BoukeHaarsma23 esphome/components/sm2135/* @BoukeHaarsma23
esphome/components/socket/* @esphome/core esphome/components/socket/* @esphome/core
@ -159,8 +190,10 @@ esphome/components/toshiba/* @kbx81
esphome/components/tsl2591/* @wjcarpenter esphome/components/tsl2591/* @wjcarpenter
esphome/components/tuya/binary_sensor/* @jesserockz esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz esphome/components/tuya/climate/* @jesserockz
esphome/components/tuya/number/* @frankiboy1
esphome/components/tuya/sensor/* @jesserockz esphome/components/tuya/sensor/* @jesserockz
esphome/components/tuya/switch/* @jesserockz esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core esphome/components/uart/* @esphome/core
esphome/components/ultrasonic/* @OttoWinter esphome/components/ultrasonic/* @OttoWinter
esphome/components/version/* @esphome/core esphome/components/version/* @esphome/core

View file

@ -5,12 +5,12 @@
# One of "docker", "hassio" # One of "docker", "hassio"
ARG BASEIMGTYPE=docker ARG BASEIMGTYPE=docker
FROM ghcr.io/hassio-addons/debian-base/amd64:5.0.0 AS base-hassio-amd64 FROM ghcr.io/hassio-addons/debian-base/amd64:5.2.3 AS base-hassio-amd64
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.0.0 AS base-hassio-arm64 FROM ghcr.io/hassio-addons/debian-base/aarch64:5.2.3 AS base-hassio-arm64
FROM ghcr.io/hassio-addons/debian-base/armv7:5.0.0 AS base-hassio-armv7 FROM ghcr.io/hassio-addons/debian-base/armv7:5.2.3 AS base-hassio-armv7
FROM debian:bullseye-20210816-slim AS base-docker-amd64 FROM debian:bullseye-20211220-slim AS base-docker-amd64
FROM debian:bullseye-20210816-slim AS base-docker-arm64 FROM debian:bullseye-20211220-slim AS base-docker-arm64
FROM debian:bullseye-20210816-slim AS base-docker-armv7 FROM debian:bullseye-20211220-slim AS base-docker-armv7
# Use TARGETARCH/TARGETVARIANT defined by docker # Use TARGETARCH/TARGETVARIANT defined by docker
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope # https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
@ -27,7 +27,7 @@ RUN \
python3-cryptography=3.3.2-1 \ python3-cryptography=3.3.2-1 \
iputils-ping=3:20210202-1 \ iputils-ping=3:20210202-1 \
git=1:2.30.2-1 \ git=1:2.30.2-1 \
curl=7.74.0-1.3+b1 \ curl=7.74.0-1.3+deb11u1 \
&& rm -rf \ && rm -rf \
/tmp/* \ /tmp/* \
/var/{cache,log}/* \ /var/{cache,log}/* \
@ -42,8 +42,8 @@ ENV \
RUN \ RUN \
# Ubuntu python3-pip is missing wheel # Ubuntu python3-pip is missing wheel
pip3 install --no-cache-dir \ pip3 install --no-cache-dir \
wheel==0.36.2 \ wheel==0.37.1 \
platformio==5.2.0 \ platformio==5.2.4 \
# Change some platformio settings # Change some platformio settings
&& platformio settings set enable_telemetry No \ && platformio settings set enable_telemetry No \
&& platformio settings set check_libraries_interval 1000000 \ && platformio settings set check_libraries_interval 1000000 \
@ -64,7 +64,7 @@ RUN \
# Copy esphome and install # Copy esphome and install
COPY . /esphome COPY . /esphome
RUN pip3 install --no-cache-dir -e /esphome RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
# Settings for dashboard # Settings for dashboard
ENV USERNAME="" PASSWORD="" ENV USERNAME="" PASSWORD=""
@ -112,7 +112,7 @@ RUN \
# Copy esphome and install # Copy esphome and install
COPY . /esphome COPY . /esphome
RUN pip3 install --no-cache-dir -e /esphome RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
# Labels # Labels
LABEL \ LABEL \
@ -147,9 +147,9 @@ RUN \
/var/{cache,log}/* \ /var/{cache,log}/* \
/var/lib/apt/lists/* /var/lib/apt/lists/*
COPY requirements.txt requirements_optional.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_optional.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

@ -32,6 +32,7 @@ parser.add_argument("--dry-run", action="store_true", help="Don't run any comman
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True) subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
build_parser = subparsers.add_parser("build", help="Build the image") build_parser = subparsers.add_parser("build", help="Build the image")
build_parser.add_argument("--push", help="Also push the images", action="store_true") build_parser.add_argument("--push", help="Also push the images", action="store_true")
build_parser.add_argument("--load", help="Load the docker image locally", action="store_true")
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images") manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
@ -132,6 +133,8 @@ def main():
cmd += ["--tag", img] cmd += ["--tag", img]
if args.push: if args.push:
cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"] cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"]
if args.load:
cmd += ["--load"]
run_command(*cmd, ".") run_command(*cmd, ".")
elif args.command == "manifest": elif args.command == "manifest":

View file

@ -8,6 +8,23 @@ import sys
config = configparser.ConfigParser(inline_comment_prefixes=(';', )) config = configparser.ConfigParser(inline_comment_prefixes=(';', ))
config.read(sys.argv[1]) config.read(sys.argv[1])
libs = [x for x in config['common']['lib_deps'].splitlines() if len(x) != 0]
libs = []
# Extract from every lib_deps key in all sections
for section in config.sections():
conf = config[section]
if "lib_deps" not in conf:
continue
for lib_dep in conf["lib_deps"].splitlines():
if not lib_dep:
# Empty line or comment
continue
if lib_dep.startswith("${"):
# Extending from another section
continue
if "@" not in lib_dep:
# No version pinned, this is an internal lib
continue
libs.append(lib_dep)
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs]) subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])

View file

@ -18,6 +18,7 @@ from esphome.const import (
CONF_PORT, CONF_PORT,
CONF_ESPHOME, CONF_ESPHOME,
CONF_PLATFORMIO_OPTIONS, CONF_PLATFORMIO_OPTIONS,
SECRETS_FILES,
) )
from esphome.core import CORE, EsphomeError, coroutine from esphome.core import CORE, EsphomeError, coroutine
from esphome.helpers import indent from esphome.helpers import indent
@ -144,6 +145,8 @@ def wrap_to_code(name, comp):
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("//", "")
# remove tailing \ to avoid multi-line comment warning
conf_str = conf_str.replace("\\\n", "\n")
cg.add(cg.LineComment(indent(conf_str))) cg.add(cg.LineComment(indent(conf_str)))
await coro(conf) await coro(conf)
@ -180,16 +183,37 @@ def compile_program(args, config):
from esphome import platformio_api from esphome import platformio_api
_LOGGER.info("Compiling app...") _LOGGER.info("Compiling app...")
return platformio_api.run_compile(config, CORE.verbose) rc = platformio_api.run_compile(config, CORE.verbose)
if rc != 0:
return rc
idedata = platformio_api.get_idedata(config)
return 0 if idedata is not None else 1
def upload_using_esptool(config, port): def upload_using_esptool(config, port):
path = CORE.firmware_bin from esphome import platformio_api
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get( first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
"upload_speed", 460800 "upload_speed", 460800
) )
def run_esptool(baud_rate): def run_esptool(baud_rate):
idedata = platformio_api.get_idedata(config)
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
flash_images = [
platformio_api.FlashImage(
path=idedata.firmware_bin_path, offset=firmware_offset
),
*idedata.extra_flash_images,
]
mcu = "esp8266"
if CORE.is_esp32:
from esphome.components.esp32 import get_esp32_variant
mcu = get_esp32_variant().lower()
cmd = [ cmd = [
"esptool.py", "esptool.py",
"--before", "--before",
@ -198,14 +222,17 @@ def upload_using_esptool(config, port):
"hard_reset", "hard_reset",
"--baud", "--baud",
str(baud_rate), str(baud_rate),
"--chip",
"esp8266",
"--port", "--port",
port, port,
"--chip",
mcu,
"write_flash", "write_flash",
"0x0", "-z",
path, "--flash_size",
"detect",
] ]
for img in flash_images:
cmd += [img.offset, img.path]
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None: if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
import esptool import esptool
@ -229,11 +256,7 @@ def upload_using_esptool(config, port):
def upload_program(config, args, host): def upload_program(config, args, host):
# if upload is to a serial port use platformio, otherwise assume ota # if upload is to a serial port use platformio, otherwise assume ota
if get_port_type(host) == "SERIAL": if get_port_type(host) == "SERIAL":
from esphome import platformio_api return upload_using_esptool(config, host)
if CORE.is_esp8266:
return upload_using_esptool(config, host)
return platformio_api.run_upload(config, CORE.verbose, host)
from esphome import espota2 from esphome import espota2
@ -443,6 +466,21 @@ def command_update_all(args):
return failed return failed
def command_idedata(args, config):
from esphome import platformio_api
import json
logging.disable(logging.INFO)
logging.disable(logging.WARNING)
idedata = platformio_api.get_idedata(config)
if idedata is None:
return 1
print(json.dumps(idedata.raw, indent=2) + "\n")
return 0
PRE_CONFIG_ACTIONS = { PRE_CONFIG_ACTIONS = {
"wizard": command_wizard, "wizard": command_wizard,
"version": command_version, "version": command_version,
@ -460,6 +498,7 @@ POST_CONFIG_ACTIONS = {
"clean-mqtt": command_clean_mqtt, "clean-mqtt": command_clean_mqtt,
"mqtt-fingerprint": command_mqtt_fingerprint, "mqtt-fingerprint": command_mqtt_fingerprint,
"clean": command_clean, "clean": command_clean,
"idedata": command_idedata,
} }
@ -570,10 +609,7 @@ def parse_args(argv):
"wizard", "wizard",
help="A helpful setup wizard that will guide you through setting up ESPHome.", help="A helpful setup wizard that will guide you through setting up ESPHome.",
) )
parser_wizard.add_argument( parser_wizard.add_argument("configuration", help="Your YAML configuration file.")
"configuration",
help="Your YAML configuration file.",
)
parser_fingerprint = 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."
@ -595,8 +631,7 @@ def parse_args(argv):
"dashboard", help="Create a simple web server for a dashboard." "dashboard", help="Create a simple web server for a dashboard."
) )
parser_dashboard.add_argument( parser_dashboard.add_argument(
"configuration", "configuration", help="Your YAML configuration file directory."
help="Your YAML configuration file directory.",
) )
parser_dashboard.add_argument( parser_dashboard.add_argument(
"--port", "--port",
@ -604,6 +639,12 @@ def parse_args(argv):
type=int, type=int,
default=6052, default=6052,
) )
parser_dashboard.add_argument(
"--address",
help="The address to bind to.",
type=str,
default="0.0.0.0",
)
parser_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.",
@ -635,6 +676,11 @@ def parse_args(argv):
"configuration", help="Your YAML configuration file directories.", nargs="+" "configuration", help="Your YAML configuration file directories.", nargs="+"
) )
parser_idedata = subparsers.add_parser("idedata")
parser_idedata.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs=1
)
# Keep backward compatibility with the old command line format of # Keep backward compatibility with the old command line format of
# esphome <config> <command>. # esphome <config> <command>.
# #
@ -718,7 +764,12 @@ def run_esphome(argv):
args = parse_args(argv) args = parse_args(argv)
CORE.dashboard = args.dashboard CORE.dashboard = args.dashboard
setup_log(args.verbose, args.quiet) setup_log(
args.verbose,
args.quiet,
# Show timestamp for dashboard access logs
args.command == "dashboard",
)
if args.deprecated_argv_suggestion is not None and args.command != "vscode": if args.deprecated_argv_suggestion is not None and args.command != "vscode":
_LOGGER.warning( _LOGGER.warning(
"Calling ESPHome with the configuration before the command is deprecated " "Calling ESPHome with the configuration before the command is deprecated "
@ -742,12 +793,16 @@ def run_esphome(argv):
return 1 return 1
for conf_path in args.configuration: for conf_path in args.configuration:
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
_LOGGER.warning("Skipping secrets file %s", conf_path)
continue
CORE.config_path = conf_path CORE.config_path = conf_path
CORE.dashboard = args.dashboard CORE.dashboard = args.dashboard
config = read_config(dict(args.substitution) if args.substitution else {}) config = read_config(dict(args.substitution) if args.substitution else {})
if config is None: if config is None:
return 1 return 2
CORE.config = config CORE.config = config
if args.command not in POST_CONFIG_ACTIONS: if args.command not in POST_CONFIG_ACTIONS:

View file

@ -3,9 +3,11 @@ import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_AUTOMATION_ID, CONF_AUTOMATION_ID,
CONF_CONDITION, CONF_CONDITION,
CONF_COUNT,
CONF_ELSE, CONF_ELSE,
CONF_ID, CONF_ID,
CONF_THEN, CONF_THEN,
CONF_TIMEOUT,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_TYPE_ID, CONF_TYPE_ID,
CONF_TIME, CONF_TIME,
@ -65,6 +67,7 @@ DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action) LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
IfAction = cg.esphome_ns.class_("IfAction", Action) IfAction = cg.esphome_ns.class_("IfAction", Action)
WhileAction = cg.esphome_ns.class_("WhileAction", Action) WhileAction = cg.esphome_ns.class_("WhileAction", Action)
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component) WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action) UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
Automation = cg.esphome_ns.class_("Automation") Automation = cg.esphome_ns.class_("Automation")
@ -240,10 +243,32 @@ async def while_action_to_code(config, action_id, template_arg, args):
return var return var
@register_action(
"repeat",
RepeatAction,
cv.Schema(
{
cv.Required(CONF_COUNT): cv.templatable(cv.positive_not_null_int),
cv.Required(CONF_THEN): validate_action_list,
}
),
)
async def repeat_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
count_template = await cg.templatable(config[CONF_COUNT], args, cg.uint32)
cg.add(var.set_count(count_template))
actions = await build_action_list(config[CONF_THEN], template_arg, args)
cg.add(var.add_then(actions))
return var
def validate_wait_until(value): def validate_wait_until(value):
schema = cv.Schema( schema = cv.Schema(
{ {
cv.Required(CONF_CONDITION): validate_potentially_and_condition, cv.Required(CONF_CONDITION): validate_potentially_and_condition,
cv.Optional(CONF_TIMEOUT): cv.templatable(
cv.positive_time_period_milliseconds
),
} }
) )
if isinstance(value, dict) and CONF_CONDITION in value: if isinstance(value, dict) and CONF_CONDITION in value:
@ -255,6 +280,9 @@ def validate_wait_until(value):
async def wait_until_action_to_code(config, action_id, template_arg, args): async def wait_until_action_to_code(config, action_id, template_arg, args):
conditions = await build_condition(config[CONF_CONDITION], template_arg, args) conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
var = cg.new_Pvariable(action_id, template_arg, conditions) var = cg.new_Pvariable(action_id, template_arg, conditions)
if CONF_TIMEOUT in config:
template_ = await cg.templatable(config[CONF_TIMEOUT], args, cg.uint32)
cg.add(var.set_timeout_value(template_))
await cg.register_component(var, {}) await cg.register_component(var, {})
return var return var

View file

@ -67,7 +67,7 @@ from esphome.cpp_types import ( # noqa
NAN, NAN,
esphome_ns, esphome_ns,
App, App,
Nameable, EntityBase,
Component, Component,
ComponentPtr, ComponentPtr,
PollingComponent, PollingComponent,
@ -75,10 +75,10 @@ from esphome.cpp_types import ( # noqa
optional, optional,
arduino_json_ns, arduino_json_ns,
JsonObject, JsonObject,
JsonObjectRef, JsonObjectConst,
JsonObjectConstRef,
Controller, Controller,
GPIOPin, GPIOPin,
InternalGPIOPin, InternalGPIOPin,
gpio_Flags, gpio_Flags,
EntityCategory,
) )

View file

@ -25,7 +25,7 @@ void AdalightLightEffect::stop() {
AddressableLightEffect::stop(); AddressableLightEffect::stop();
} }
int AdalightLightEffect::get_frame_size_(int led_count) const { unsigned int AdalightLightEffect::get_frame_size_(int led_count) const {
// 3 bytes: Ada // 3 bytes: Ada
// 2 bytes: LED count // 2 bytes: LED count
// 1 byte: checksum // 1 byte: checksum

View file

@ -25,7 +25,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
CONSUMED, CONSUMED,
}; };
int get_frame_size_(int led_count) const; unsigned int get_frame_size_(int led_count) const;
void reset_frame_(light::AddressableLight &it); void reset_frame_(light::AddressableLight &it);
void blank_all_leds_(light::AddressableLight &it); void blank_all_leds_(light::AddressableLight &it);
Frame parse_frame_(light::AddressableLight &it); Frame parse_frame_(light::AddressableLight &it);

View file

@ -1,9 +1,14 @@
#include "adc_sensor.h" #include "adc_sensor.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#ifdef USE_ESP8266
#ifdef USE_ADC_SENSOR_VCC #ifdef USE_ADC_SENSOR_VCC
#include <Esp.h> #include <Esp.h>
ADC_MODE(ADC_VCC) ADC_MODE(ADC_VCC)
#else
#include <Arduino.h>
#endif
#endif #endif
namespace esphome { namespace esphome {
@ -11,50 +16,6 @@ namespace adc {
static const char *const TAG = "adc"; static const char *const TAG = "adc";
#ifdef USE_ESP32
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
#if CONFIG_IDF_TARGET_ESP32
switch (pin) {
case 36:
return ADC1_CHANNEL_0;
case 37:
return ADC1_CHANNEL_1;
case 38:
return ADC1_CHANNEL_2;
case 39:
return ADC1_CHANNEL_3;
case 32:
return ADC1_CHANNEL_4;
case 33:
return ADC1_CHANNEL_5;
case 34:
return ADC1_CHANNEL_6;
case 35:
return ADC1_CHANNEL_7;
default:
return ADC1_CHANNEL_MAX;
}
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
switch (pin) {
case 0:
return ADC1_CHANNEL_0;
case 1:
return ADC1_CHANNEL_1;
case 2:
return ADC1_CHANNEL_2;
case 3:
return ADC1_CHANNEL_3;
case 4:
return ADC1_CHANNEL_4;
default:
return ADC1_CHANNEL_MAX;
}
#endif
}
#endif
void ADCSensor::setup() { void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC #ifndef USE_ADC_SENSOR_VCC
@ -62,13 +23,36 @@ void ADCSensor::setup() {
#endif #endif
#ifdef USE_ESP32 #ifdef USE_ESP32
adc1_config_channel_atten(gpio_to_adc1(pin_->get_pin()), attenuation_);
adc1_config_width(ADC_WIDTH_BIT_12); adc1_config_width(ADC_WIDTH_BIT_12);
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 if (!autorange_) {
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_->get_pin())); adc1_config_channel_atten(channel_, attenuation_);
#endif }
// load characteristics for each attenuation
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_BIT_12,
1100, // default vref
&cal_characteristics_[i]);
switch (cal_value) {
case ESP_ADC_CAL_VAL_EFUSE_VREF:
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
break;
case ESP_ADC_CAL_VAL_EFUSE_TP:
ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
break;
case ESP_ADC_CAL_VAL_DEFAULT_VREF:
default:
break;
}
}
// adc_gpio_init doesn't exist on ESP32-C3 or ESP32-H2
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32H2)
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) channel_);
#endif #endif
#endif // USE_ESP32
} }
void ADCSensor::dump_config() { void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this); LOG_SENSOR("", "ADC Sensor", this);
#ifdef USE_ESP8266 #ifdef USE_ESP8266
@ -77,84 +61,107 @@ void ADCSensor::dump_config() {
#else #else
LOG_PIN(" Pin: ", pin_); LOG_PIN(" Pin: ", pin_);
#endif #endif
#endif #endif // USE_ESP8266
#ifdef USE_ESP32 #ifdef USE_ESP32
LOG_PIN(" Pin: ", pin_); LOG_PIN(" Pin: ", pin_);
switch (this->attenuation_) { if (autorange_)
case ADC_ATTEN_DB_0: ESP_LOGCONFIG(TAG, " Attenuation: auto");
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)"); else
break; switch (this->attenuation_) {
case ADC_ATTEN_DB_2_5: case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)"); ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
break; break;
case ADC_ATTEN_DB_6: case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)"); ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
break; break;
case ADC_ATTEN_DB_11: case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)"); ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
break; break;
default: // This is to satisfy the unused ADC_ATTEN_MAX case ADC_ATTEN_DB_11:
break; ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
} break;
#endif default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
#endif // USE_ESP32
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; } float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
void ADCSensor::update() { void ADCSensor::update() {
float value_v = this->sample(); float value_v = this->sample();
ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v); ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v); this->publish_state(value_v);
} }
float ADCSensor::sample() {
#ifdef USE_ESP32
int raw = adc1_get_raw(gpio_to_adc1(pin_->get_pin()));
float value_v = raw / 4095.0f;
#if CONFIG_IDF_TARGET_ESP32
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
value_v *= 1.1;
break;
case ADC_ATTEN_DB_2_5:
value_v *= 1.5;
break;
case ADC_ATTEN_DB_6:
value_v *= 2.2;
break;
case ADC_ATTEN_DB_11:
value_v *= 3.9;
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
value_v *= 0.84;
break;
case ADC_ATTEN_DB_2_5:
value_v *= 1.13;
break;
case ADC_ATTEN_DB_6:
value_v *= 1.56;
break;
case ADC_ATTEN_DB_11:
value_v *= 3.0;
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
}
#endif
return value_v;
#endif
#ifdef USE_ESP8266 #ifdef USE_ESP8266
float ADCSensor::sample() {
#ifdef USE_ADC_SENSOR_VCC #ifdef USE_ADC_SENSOR_VCC
return ESP.getVcc() / 1024.0f; // NOLINT(readability-static-accessed-through-instance) int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else #else
return analogRead(this->pin_->get_pin()) / 1024.0f; // NOLINT int raw = analogRead(this->pin_->get_pin()); // NOLINT
#endif
#endif #endif
if (output_raw_) {
return raw;
}
return raw / 1024.0f;
} }
#endif
#ifdef USE_ESP32
float ADCSensor::sample() {
if (!autorange_) {
int raw = adc1_get_raw(channel_);
if (raw == -1) {
return NAN;
}
if (output_raw_) {
return raw;
}
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
return mv / 1000.0f;
}
int raw11, raw6 = 4095, raw2 = 4095, raw0 = 4095;
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel_);
if (raw11 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel_);
if (raw6 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel_);
if (raw2 < 4095) {
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel_);
}
}
}
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) {
return NAN;
}
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
// Contribution of each value, in range 0-2048
uint32_t c11 = std::min(raw11, 2048);
uint32_t c6 = 2048 - std::abs(raw6 - 2048);
uint32_t c2 = 2048 - std::abs(raw2 - 2048);
uint32_t c0 = std::min(4095 - raw0, 2048);
// max theoretical csum value is 2048*4 = 8192
uint32_t csum = c11 + c6 + c2 + c0;
// each mv is max 3900; so max value is 3900*2048*4, fits in unsigned
uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U);
}
#endif // USE_ESP32
#ifdef USE_ESP8266 #ifdef USE_ESP8266
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
#endif #endif

View file

@ -8,6 +8,7 @@
#ifdef USE_ESP32 #ifdef USE_ESP32
#include "driver/adc.h" #include "driver/adc.h"
#include <esp_adc_cal.h>
#endif #endif
namespace esphome { namespace esphome {
@ -17,7 +18,9 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
public: public:
#ifdef USE_ESP32 #ifdef USE_ESP32
/// Set the attenuation for this pin. Only available on the ESP32. /// Set the attenuation for this pin. Only available on the ESP32.
void set_attenuation(adc_atten_t attenuation); void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
void set_channel(adc1_channel_t channel) { channel_ = channel; }
void set_autorange(bool autorange) { autorange_ = autorange; }
#endif #endif
/// Update adc values. /// Update adc values.
@ -28,6 +31,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
/// `HARDWARE_LATE` setup priority. /// `HARDWARE_LATE` setup priority.
float get_setup_priority() const override; float get_setup_priority() const override;
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
float sample() override; float sample() override;
#ifdef USE_ESP8266 #ifdef USE_ESP8266
@ -36,9 +40,13 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
protected: protected:
InternalGPIOPin *pin_; InternalGPIOPin *pin_;
bool output_raw_{false};
#ifdef USE_ESP32 #ifdef USE_ESP32
adc_atten_t attenuation_{ADC_ATTEN_DB_0}; adc_atten_t attenuation_{ADC_ATTEN_DB_0};
adc1_channel_t channel_{};
bool autorange_{false};
esp_adc_cal_characteristics_t cal_characteristics_[(int) ADC_ATTEN_MAX] = {};
#endif #endif
}; };

View file

@ -4,14 +4,24 @@ from esphome import pins
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
from esphome.const import ( from esphome.const import (
CONF_ATTENUATION, CONF_ATTENUATION,
CONF_RAW,
CONF_ID, CONF_ID,
CONF_INPUT, CONF_INPUT,
CONF_NUMBER,
CONF_PIN, CONF_PIN,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32,
VARIANT_ESP32C3,
VARIANT_ESP32H2,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
AUTO_LOAD = ["voltage_sampler"] AUTO_LOAD = ["voltage_sampler"]
@ -21,6 +31,62 @@ ATTENUATION_MODES = {
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5, "2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
"6db": cg.global_ns.ADC_ATTEN_DB_6, "6db": cg.global_ns.ADC_ATTEN_DB_6,
"11db": cg.global_ns.ADC_ATTEN_DB_11, "11db": cg.global_ns.ADC_ATTEN_DB_11,
"auto": "auto",
}
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
# pin to adc1 channel mapping
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
VARIANT_ESP32: {
36: adc1_channel_t.ADC1_CHANNEL_0,
37: adc1_channel_t.ADC1_CHANNEL_1,
38: adc1_channel_t.ADC1_CHANNEL_2,
39: adc1_channel_t.ADC1_CHANNEL_3,
32: adc1_channel_t.ADC1_CHANNEL_4,
33: adc1_channel_t.ADC1_CHANNEL_5,
34: adc1_channel_t.ADC1_CHANNEL_6,
35: adc1_channel_t.ADC1_CHANNEL_7,
},
VARIANT_ESP32S2: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
},
VARIANT_ESP32S3: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
},
VARIANT_ESP32C3: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
VARIANT_ESP32H2: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
} }
@ -29,15 +95,16 @@ def validate_adc_pin(value):
return cv.only_on_esp8266("VCC") return cv.only_on_esp8266("VCC")
if CORE.is_esp32: if CORE.is_esp32:
from esphome.components.esp32 import is_esp32c3
value = pins.internal_gpio_input_pin_number(value) value = pins.internal_gpio_input_pin_number(value)
if is_esp32c3(): variant = get_esp32_variant()
if not (0 <= value <= 4): # ADC1 if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.") raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
if not (32 <= value <= 39): # ADC1
raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.") if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
elif CORE.is_esp8266: raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
return pins.internal_gpio_input_pin_schema(value)
if CORE.is_esp8266:
from esphome.components.esp8266.gpio import CONF_ANALOG from esphome.components.esp8266.gpio import CONF_ANALOG
value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})( value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
@ -49,10 +116,14 @@ def validate_adc_pin(value):
return pins.gpio_pin_schema( return pins.gpio_pin_schema(
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True {CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value) )(value)
else:
raise NotImplementedError
return pins.internal_gpio_input_pin_schema(value) raise NotImplementedError
def validate_config(config):
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set.")
return config
adc_ns = cg.esphome_ns.namespace("adc") adc_ns = cg.esphome_ns.namespace("adc")
@ -60,7 +131,7 @@ ADCSensor = adc_ns.class_(
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
) )
CONFIG_SCHEMA = ( CONFIG_SCHEMA = cv.All(
sensor.sensor_schema( sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT, unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2, accuracy_decimals=2,
@ -71,12 +142,14 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(ADCSensor), cv.GenerateID(): cv.declare_id(ADCSensor),
cv.Required(CONF_PIN): validate_adc_pin, cv.Required(CONF_PIN): validate_adc_pin,
cv.Optional(CONF_RAW, default=False): cv.boolean,
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All( cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True) cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
), ),
} }
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s")),
validate_config,
) )
@ -91,5 +164,17 @@ async def to_code(config):
pin = await cg.gpio_pin_expression(config[CONF_PIN]) pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin)) cg.add(var.set_pin(pin))
if CONF_RAW in config:
cg.add(var.set_output_raw(config[CONF_RAW]))
if CONF_ATTENUATION in config: if CONF_ATTENUATION in config:
cg.add(var.set_attenuation(config[CONF_ATTENUATION])) if config[CONF_ATTENUATION] == "auto":
cg.add(var.set_autorange(cg.global_ns.true))
else:
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
if CORE.is_esp32:
variant = get_esp32_variant()
pin_num = config[CONF_PIN][CONF_NUMBER]
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel(chan))

View file

@ -76,9 +76,9 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
return err; return err;
*value = 0; *value = 0;
*value |= ((uint32_t) recv[0]) << 24; *value |= ((uint32_t) recv[0]) << 24;
*value |= ((uint32_t) recv[1]) << 24; *value |= ((uint32_t) recv[1]) << 16;
*value |= ((uint32_t) recv[2]) << 24; *value |= ((uint32_t) recv[2]) << 8;
*value |= ((uint32_t) recv[3]) << 24; *value |= ((uint32_t) recv[3]);
return i2c::ERROR_OK; return i2c::ERROR_OK;
} }

View file

@ -73,13 +73,6 @@ void AHT10Component::update() {
bool success = false; bool success = false;
for (int i = 0; i < AHT10_ATTEMPTS; ++i) { for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis()); ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis());
delay_microseconds_accurate(4);
uint8_t reg = 0;
if (this->write(&reg, 1) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
continue;
}
delay(delay_ms); delay(delay_ms);
if (this->read(data, 6) != i2c::ERROR_OK) { if (this->read(data, 6) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting..."); ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
@ -117,12 +110,12 @@ void AHT10Component::update() {
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4; uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0; float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
float humidity; float humidity;
if (raw_humidity == 0) { // unrealistic value if (raw_humidity == 0) { // unrealistic value
humidity = NAN; humidity = NAN;
} else { } else {
humidity = (float) raw_humidity * 100.0 / 1048576.0; humidity = (float) raw_humidity * 100.0f / 1048576.0f;
} }
if (this->temperature_sensor_ != nullptr) { if (this->temperature_sensor_ != nullptr) {

View file

@ -0,0 +1 @@
CODEOWNERS = ["@ncareau"]

View file

@ -0,0 +1,113 @@
#include "airthings_wave_mini.h"
#ifdef USE_ESP32
namespace esphome {
namespace airthings_wave_mini {
static const char *const TAG = "airthings_wave_mini";
void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "Connected successfully!");
}
break;
}
case ESP_GATTC_DISCONNECT_EVT: {
ESP_LOGW(TAG, "Disconnected!");
break;
}
case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle_ = 0;
auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
sensors_data_characteristic_uuid_.to_string().c_str());
break;
}
this->handle_ = chr->handle;
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
request_read_values_();
break;
}
case ESP_GATTC_READ_CHAR_EVT: {
if (param->read.conn_id != this->parent()->conn_id)
break;
if (param->read.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
break;
}
if (param->read.handle == this->handle_) {
read_sensors_(param->read.value, param->read.value_len);
}
break;
}
default:
break;
}
}
void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
auto value = (WaveMiniReadings *) raw_value;
if (sizeof(WaveMiniReadings) <= value_len) {
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
if (is_valid_voc_value_(value->voc)) {
this->tvoc_sensor_->publish_state(value->voc);
}
// This instance must not stay connected
// so other clients can connect to it (e.g. the
// mobile app).
parent()->set_enabled(false);
}
}
bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
void AirthingsWaveMini::update() {
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device");
parent()->set_enabled(true);
parent()->connect();
} else {
ESP_LOGW(TAG, "Connection in progress");
}
}
}
void AirthingsWaveMini::request_read_values_() {
auto status =
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
}
}
void AirthingsWaveMini::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
}
AirthingsWaveMini::AirthingsWaveMini()
: PollingComponent(10000),
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
} // namespace airthings_wave_mini
} // namespace esphome
#endif // USE_ESP32

View file

@ -0,0 +1,65 @@
#pragma once
#ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm>
#include <iterator>
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
namespace esphome {
namespace airthings_wave_mini {
static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode {
public:
AirthingsWaveMini();
void dump_config() override;
void update() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
protected:
bool is_valid_voc_value_(uint16_t voc);
void read_sensors_(uint8_t *value, uint16_t value_len);
void request_read_values_();
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
sensor::Sensor *tvoc_sensor_{nullptr};
uint16_t handle_;
esp32_ble_tracker::ESPBTUUID service_uuid_;
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
struct WaveMiniReadings {
uint16_t unused01;
uint16_t temperature;
uint16_t pressure;
uint16_t humidity;
uint16_t voc;
uint16_t unused02;
uint32_t unused03;
uint32_t unused04;
};
};
} // namespace airthings_wave_mini
} // namespace esphome
#endif // USE_ESP32

View file

@ -0,0 +1,82 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
STATE_CLASS_MEASUREMENT,
UNIT_PERCENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
CONF_ID,
CONF_HUMIDITY,
CONF_TVOC,
CONF_PRESSURE,
CONF_TEMPERATURE,
UNIT_PARTS_PER_BILLION,
ICON_RADIATOR,
)
DEPENDENCIES = ["ble_client"]
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
AirthingsWaveMini = airthings_wave_mini_ns.class_(
"AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
accuracy_decimals=2,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=2,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA),
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await ble_client.register_ble_node(var, config)
if CONF_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
if CONF_PRESSURE in config:
sens = await sensor.new_sensor(config[CONF_PRESSURE])
cg.add(var.set_pressure(sens))
if CONF_TVOC in config:
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))

View file

@ -1,6 +1,6 @@
#include "airthings_wave_plus.h" #include "airthings_wave_plus.h"
#ifdef USE_ESP32_FRAMEWORK_ARDUINO #ifdef USE_ESP32
namespace esphome { namespace esphome {
namespace airthings_wave_plus { namespace airthings_wave_plus {
@ -31,7 +31,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
break; break;
} }
this->handle_ = chr->handle; this->handle_ = chr->handle;
this->node_state = esp32_ble_tracker::ClientState::Established; this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
request_read_values_(); request_read_values_();
break; break;
@ -96,10 +96,8 @@ bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && v
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; } bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
void AirthingsWavePlus::loop() {}
void AirthingsWavePlus::update() { void AirthingsWavePlus::update() {
if (this->node_state != esp32_ble_tracker::ClientState::Established) { if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
if (!parent()->enabled) { if (!parent()->enabled) {
ESP_LOGW(TAG, "Reconnecting to device"); ESP_LOGW(TAG, "Reconnecting to device");
parent()->set_enabled(true); parent()->set_enabled(true);
@ -128,17 +126,12 @@ void AirthingsWavePlus::dump_config() {
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
} }
AirthingsWavePlus::AirthingsWavePlus() : PollingComponent(10000) { AirthingsWavePlus::AirthingsWavePlus()
auto service_bt = *BLEUUID::fromString(std::string("b42e1c08-ade7-11e4-89d3-123b93f75cba")).getNative(); : PollingComponent(10000),
auto characteristic_bt = *BLEUUID::fromString(std::string("b42e2a68-ade7-11e4-89d3-123b93f75cba")).getNative(); service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
service_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(service_bt);
sensors_data_characteristic_uuid_ = esp32_ble_tracker::ESPBTUUID::from_uuid(characteristic_bt);
}
void AirthingsWavePlus::setup() {}
} // namespace airthings_wave_plus } // namespace airthings_wave_plus
} // namespace esphome } // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO #endif // USE_ESP32

View file

@ -1,28 +1,28 @@
#pragma once #pragma once
#ifdef USE_ESP32_FRAMEWORK_ARDUINO #ifdef USE_ESP32
#include <esp_gattc_api.h>
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <esp_gattc_api.h>
#include <BLEDevice.h>
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/components/ble_client/ble_client.h" #include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/log.h"
namespace esphome { namespace esphome {
namespace airthings_wave_plus { namespace airthings_wave_plus {
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode { class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
public: public:
AirthingsWavePlus(); AirthingsWavePlus();
void setup() override;
void dump_config() override; void dump_config() override;
void update() override; void update() override;
void loop() override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override; esp_ble_gattc_cb_param_t *param) override;
@ -72,4 +72,4 @@ class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientN
} // namespace airthings_wave_plus } // namespace airthings_wave_plus
} // namespace esphome } // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO #endif // USE_ESP32

View file

@ -82,10 +82,8 @@ CONFIG_SCHEMA = cv.All(
), ),
} }
) )
.extend(cv.polling_component_schema("5mins")) .extend(cv.polling_component_schema("5min"))
.extend(ble_client.BLE_CLIENT_SCHEMA), .extend(ble_client.BLE_CLIENT_SCHEMA),
# Until BLEUUID reference removed
cv.only_with_arduino,
) )

View file

@ -38,9 +38,9 @@ void AM2320Component::update() {
return; return;
} }
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0; float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0f;
temperature = (data[4] & 0x80) ? -temperature : temperature; temperature = (data[4] & 0x80) ? -temperature : temperature;
float humidity = ((data[2] << 8) + data[3]) / 10.0; float humidity = ((data[2] << 8) + data[3]) / 10.0f;
ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity); ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity);
if (this->temperature_sensor_ != nullptr) if (this->temperature_sensor_ != nullptr)

View file

@ -31,7 +31,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
} }
case ESP_GATTC_DISCONNECT_EVT: { case ESP_GATTC_DISCONNECT_EVT: {
this->logged_in_ = false; this->logged_in_ = false;
this->node_state = espbt::ClientState::Idle; this->node_state = espbt::ClientState::IDLE;
if (this->battery_ != nullptr) if (this->battery_ != nullptr)
this->battery_->publish_state(NAN); this->battery_->publish_state(NAN);
if (this->illuminance_ != nullptr) if (this->illuminance_ != nullptr)
@ -54,7 +54,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
break; break;
} }
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established; this->node_state = espbt::ClientState::ESTABLISHED;
this->update(); this->update();
break; break;
} }
@ -93,7 +93,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
} }
void Am43::update() { void Am43::update() {
if (this->node_state != espbt::ClientState::Established) { if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str()); ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
return; return;
} }

View file

@ -24,7 +24,7 @@ void Am43Component::setup() {
} }
void Am43Component::loop() { void Am43Component::loop() {
if (this->node_state == espbt::ClientState::Established && !this->logged_in_) { if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) {
auto packet = this->encoder_->get_send_pin_request(this->pin_); auto packet = this->encoder_->get_send_pin_request(this->pin_);
auto status = auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
@ -46,7 +46,7 @@ CoverTraits Am43Component::get_traits() {
} }
void Am43Component::control(const CoverCall &call) { void Am43Component::control(const CoverCall &call) {
if (this->node_state != espbt::ClientState::Established) { if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str()); ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
return; return;
} }
@ -98,7 +98,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break; break;
} }
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established; this->node_state = espbt::ClientState::ESTABLISHED;
break; break;
} }
case ESP_GATTC_NOTIFY_EVT: { case ESP_GATTC_NOTIFY_EVT: {

View file

@ -4,7 +4,8 @@ from esphome.components import sensor, ble_client
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_BATTERY_LEVEL, CONF_BATTERY_LEVEL,
ICON_BATTERY, DEVICE_CLASS_BATTERY,
ENTITY_CATEGORY_DIAGNOSTIC,
CONF_ILLUMINANCE, CONF_ILLUMINANCE,
ICON_BRIGHTNESS_5, ICON_BRIGHTNESS_5,
UNIT_PERCENT, UNIT_PERCENT,
@ -20,10 +21,15 @@ CONFIG_SCHEMA = (
{ {
cv.GenerateID(): cv.declare_id(Am43), cv.GenerateID(): cv.declare_id(Am43),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT, ICON_BATTERY, 0 unit_of_measurement=UNIT_PERCENT,
device_class=DEVICE_CLASS_BATTERY,
accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
), ),
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema( cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
UNIT_PERCENT, ICON_BRIGHTNESS_5, 0 unit_of_measurement=UNIT_PERCENT,
icon=ICON_BRIGHTNESS_5,
accuracy_decimals=0,
), ),
} }
) )

View file

@ -5,7 +5,7 @@ from esphome.components import display, font
import esphome.components.image as espImage import esphome.components.image as espImage
import esphome.config_validation as cv import esphome.config_validation as cv
import esphome.codegen as cg import esphome.codegen as cg
from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE from esphome.const import CONF_FILE, CONF_ID, CONF_RAW_DATA_ID, CONF_RESIZE, CONF_TYPE
from esphome.core import CORE, HexInt from esphome.core import CORE, HexInt
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -15,8 +15,6 @@ MULTI_CONF = True
Animation_ = display.display_ns.class_("Animation") Animation_ = display.display_ns.class_("Animation")
CONF_RAW_DATA_ID = "raw_data_id"
ANIMATION_SCHEMA = cv.Schema( ANIMATION_SCHEMA = cv.Schema(
{ {
cv.Required(CONF_ID): cv.declare_id(Animation_), cv.Required(CONF_ID): cv.declare_id(Animation_),
@ -46,8 +44,9 @@ async def to_code(config):
width, height = image.size width, height = image.size
frames = image.n_frames frames = image.n_frames
if CONF_RESIZE in config: if CONF_RESIZE in config:
image.thumbnail(config[CONF_RESIZE]) new_width_max, new_height_max = config[CONF_RESIZE]
width, height = image.size ratio = min(new_width_max / width, new_height_max / height)
width, height = int(width * ratio), int(height * ratio)
else: else:
if width > 500 or height > 500: if width > 500 or height > 500:
_LOGGER.warning( _LOGGER.warning(
@ -61,7 +60,13 @@ async def to_code(config):
for frameIndex in range(frames): for frameIndex in range(frames):
image.seek(frameIndex) image.seek(frameIndex)
frame = image.convert("L", dither=Image.NONE) frame = image.convert("L", dither=Image.NONE)
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata()) pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
)
for pix in pixels: for pix in pixels:
data[pos] = pix data[pos] = pix
pos += 1 pos += 1
@ -72,7 +77,13 @@ async def to_code(config):
for frameIndex in range(frames): for frameIndex in range(frames):
image.seek(frameIndex) image.seek(frameIndex)
frame = image.convert("RGB") frame = image.convert("RGB")
if CONF_RESIZE in config:
frame = frame.resize([width, height])
pixels = list(frame.getdata()) pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
)
for pix in pixels: for pix in pixels:
data[pos] = pix[0] data[pos] = pix[0]
pos += 1 pos += 1
@ -87,6 +98,8 @@ async def to_code(config):
for frameIndex in range(frames): for frameIndex in range(frames):
image.seek(frameIndex) image.seek(frameIndex)
frame = image.convert("1", dither=Image.NONE) frame = image.convert("1", dither=Image.NONE)
if CONF_RESIZE in config:
frame = frame.resize([width, height])
for y in range(height): for y in range(height):
for x in range(width): for x in range(width):
if frame.getpixel((x, y)): if frame.getpixel((x, y)):

View file

@ -72,7 +72,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
break; break;
} }
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
this->node_state = espbt::ClientState::Established; this->node_state = espbt::ClientState::ESTABLISHED;
this->current_request_ = 0; this->current_request_ = 0;
this->update(); this->update();
break; break;
@ -129,7 +129,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); } void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
void Anova::update() { void Anova::update() {
if (this->node_state != espbt::ClientState::Established) if (this->node_state != espbt::ClientState::ESTABLISHED)
return; return;
if (this->current_request_ < 2) { if (this->current_request_ < 2) {

View file

@ -27,10 +27,10 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
esp_ble_gattc_cb_param_t *param) override; esp_ble_gattc_cb_param_t *param) override;
void dump_config() override; void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; } float get_setup_priority() const override { return setup_priority::DATA; }
climate::ClimateTraits traits() { climate::ClimateTraits traits() override {
auto traits = climate::ClimateTraits(); auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true); traits.set_supports_current_temperature(true);
traits.set_supports_heat_mode(true); traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::ClimateMode::CLIMATE_MODE_HEAT});
traits.set_visual_min_temperature(25.0); traits.set_visual_min_temperature(25.0);
traits.set_visual_max_temperature(100.0); traits.set_visual_max_temperature(100.0);
traits.set_visual_temperature_step(0.1); traits.set_visual_temperature_step(0.1);

View file

@ -73,51 +73,46 @@ AnovaPacket *AnovaCodec::get_stop_request() {
} }
void AnovaCodec::decode(const uint8_t *data, uint16_t length) { void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
memset(this->buf_, 0, 32); char buf[32];
strncpy(this->buf_, (char *) data, length); memset(buf, 0, sizeof(buf));
strncpy(buf, (char *) data, std::min<uint16_t>(length, sizeof(buf) - 1));
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false; this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
switch (this->current_query_) { switch (this->current_query_) {
case READ_DEVICE_STATUS: { case READ_DEVICE_STATUS: {
if (!strncmp(this->buf_, "stopped", 7)) { if (!strncmp(buf, "stopped", 7)) {
this->has_running_ = true; this->has_running_ = true;
this->running_ = false; this->running_ = false;
} }
if (!strncmp(this->buf_, "running", 7)) { if (!strncmp(buf, "running", 7)) {
this->has_running_ = true; this->has_running_ = true;
this->running_ = true; this->running_ = true;
} }
break; break;
} }
case START: { case START: {
if (!strncmp(this->buf_, "start", 5)) { if (!strncmp(buf, "start", 5)) {
this->has_running_ = true; this->has_running_ = true;
this->running_ = true; this->running_ = true;
} }
break; break;
} }
case STOP: { case STOP: {
if (!strncmp(this->buf_, "stop", 4)) { if (!strncmp(buf, "stop", 4)) {
this->has_running_ = true; this->has_running_ = true;
this->running_ = false; this->running_ = false;
} }
break; break;
} }
case READ_TARGET_TEMPERATURE: { case READ_TARGET_TEMPERATURE:
this->target_temp_ = strtof(this->buf_, nullptr);
if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
case SET_TARGET_TEMPERATURE: { case SET_TARGET_TEMPERATURE: {
this->target_temp_ = strtof(this->buf_, nullptr); this->target_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
if (this->fahrenheit_) if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_); this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true; this->has_target_temp_ = true;
break; break;
} }
case READ_CURRENT_TEMPERATURE: { case READ_CURRENT_TEMPERATURE: {
this->current_temp_ = strtof(this->buf_, nullptr); this->current_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
if (this->fahrenheit_) if (this->fahrenheit_)
this->current_temp_ = ftoc(this->current_temp_); this->current_temp_ = ftoc(this->current_temp_);
this->has_current_temp_ = true; this->has_current_temp_ = true;
@ -125,8 +120,8 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
} }
case SET_UNIT: case SET_UNIT:
case READ_UNIT: { case READ_UNIT: {
this->unit_ = this->buf_[0]; this->unit_ = buf[0];
this->fahrenheit_ = this->buf_[0] == 'f'; this->fahrenheit_ = buf[0] == 'f';
this->has_unit_ = true; this->has_unit_ = true;
break; break;
} }

View file

@ -70,7 +70,6 @@ class AnovaCodec {
bool has_current_temp_; bool has_current_temp_;
bool has_unit_; bool has_unit_;
bool has_running_; bool has_running_;
char buf_[32];
bool fahrenheit_; bool fahrenheit_;
CurrentQuery current_query_; CurrentQuery current_query_;

View file

@ -121,7 +121,7 @@ async def to_code(config):
decoded = base64.b64decode(conf[CONF_KEY]) decoded = base64.b64decode(conf[CONF_KEY])
cg.add(var.set_noise_psk(list(decoded))) cg.add(var.set_noise_psk(list(decoded)))
cg.add_define("USE_API_NOISE") cg.add_define("USE_API_NOISE")
cg.add_library("esphome/noise-c", "0.1.1") cg.add_library("esphome/noise-c", "0.1.4")
else: else:
cg.add_define("USE_API_PLAINTEXT") cg.add_define("USE_API_PLAINTEXT")

View file

@ -40,6 +40,7 @@ service APIConnection {
rpc climate_command (ClimateCommandRequest) returns (void) {} rpc climate_command (ClimateCommandRequest) returns (void) {}
rpc number_command (NumberCommandRequest) returns (void) {} rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {} rpc select_command (SelectCommandRequest) returns (void) {}
rpc button_command (ButtonCommandRequest) returns (void) {}
} }
@ -182,6 +183,8 @@ message DeviceInfoResponse {
// The esphome project details if set // The esphome project details if set
string project_name = 8; string project_name = 8;
string project_version = 9; string project_version = 9;
uint32 webserver_port = 10;
} }
message ListEntitiesRequest { message ListEntitiesRequest {
@ -201,6 +204,14 @@ message SubscribeStatesRequest {
// Empty // Empty
} }
// ==================== COMMON =====================
enum EntityCategory {
ENTITY_CATEGORY_NONE = 0;
ENTITY_CATEGORY_CONFIG = 1;
ENTITY_CATEGORY_DIAGNOSTIC = 2;
}
// ==================== BINARY SENSOR ==================== // ==================== BINARY SENSOR ====================
message ListEntitiesBinarySensorResponse { message ListEntitiesBinarySensorResponse {
option (id) = 12; option (id) = 12;
@ -215,6 +226,8 @@ message ListEntitiesBinarySensorResponse {
string device_class = 5; string device_class = 5;
bool is_status_binary_sensor = 6; bool is_status_binary_sensor = 6;
bool disabled_by_default = 7; bool disabled_by_default = 7;
string icon = 8;
EntityCategory entity_category = 9;
} }
message BinarySensorStateResponse { message BinarySensorStateResponse {
option (id) = 21; option (id) = 21;
@ -245,6 +258,8 @@ message ListEntitiesCoverResponse {
bool supports_tilt = 7; bool supports_tilt = 7;
string device_class = 8; string device_class = 8;
bool disabled_by_default = 9; bool disabled_by_default = 9;
string icon = 10;
EntityCategory entity_category = 11;
} }
enum LegacyCoverState { enum LegacyCoverState {
@ -313,6 +328,8 @@ message ListEntitiesFanResponse {
bool supports_direction = 7; bool supports_direction = 7;
int32 supported_speed_count = 8; int32 supported_speed_count = 8;
bool disabled_by_default = 9; bool disabled_by_default = 9;
string icon = 10;
EntityCategory entity_category = 11;
} }
enum FanSpeed { enum FanSpeed {
FAN_SPEED_LOW = 0; FAN_SPEED_LOW = 0;
@ -388,6 +405,8 @@ message ListEntitiesLightResponse {
float max_mireds = 10; float max_mireds = 10;
repeated string effects = 11; repeated string effects = 11;
bool disabled_by_default = 13; bool disabled_by_default = 13;
string icon = 14;
EntityCategory entity_category = 15;
} }
message LightStateResponse { message LightStateResponse {
option (id) = 24; option (id) = 24;
@ -476,6 +495,7 @@ message ListEntitiesSensorResponse {
// Last reset type removed in 2021.9.0 // Last reset type removed in 2021.9.0
SensorLastResetType legacy_last_reset_type = 11; SensorLastResetType legacy_last_reset_type = 11;
bool disabled_by_default = 12; bool disabled_by_default = 12;
EntityCategory entity_category = 13;
} }
message SensorStateResponse { message SensorStateResponse {
option (id) = 25; option (id) = 25;
@ -504,6 +524,7 @@ message ListEntitiesSwitchResponse {
string icon = 5; string icon = 5;
bool assumed_state = 6; bool assumed_state = 6;
bool disabled_by_default = 7; bool disabled_by_default = 7;
EntityCategory entity_category = 8;
} }
message SwitchStateResponse { message SwitchStateResponse {
option (id) = 26; option (id) = 26;
@ -537,6 +558,7 @@ message ListEntitiesTextSensorResponse {
string icon = 5; string icon = 5;
bool disabled_by_default = 6; bool disabled_by_default = 6;
EntityCategory entity_category = 7;
} }
message TextSensorStateResponse { message TextSensorStateResponse {
option (id) = 27; option (id) = 27;
@ -697,6 +719,8 @@ message ListEntitiesCameraResponse {
string name = 3; string name = 3;
string unique_id = 4; string unique_id = 4;
bool disabled_by_default = 5; bool disabled_by_default = 5;
string icon = 6;
EntityCategory entity_category = 7;
} }
message CameraImageResponse { message CameraImageResponse {
@ -790,6 +814,8 @@ message ListEntitiesClimateResponse {
repeated ClimatePreset supported_presets = 16; repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17; repeated string supported_custom_presets = 17;
bool disabled_by_default = 18; bool disabled_by_default = 18;
string icon = 19;
EntityCategory entity_category = 20;
} }
message ClimateStateResponse { message ClimateStateResponse {
option (id) = 47; option (id) = 47;
@ -843,6 +869,11 @@ message ClimateCommandRequest {
} }
// ==================== NUMBER ==================== // ==================== NUMBER ====================
enum NumberMode {
NUMBER_MODE_AUTO = 0;
NUMBER_MODE_BOX = 1;
NUMBER_MODE_SLIDER = 2;
}
message ListEntitiesNumberResponse { message ListEntitiesNumberResponse {
option (id) = 49; option (id) = 49;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@ -858,6 +889,9 @@ message ListEntitiesNumberResponse {
float max_value = 7; float max_value = 7;
float step = 8; float step = 8;
bool disabled_by_default = 9; bool disabled_by_default = 9;
EntityCategory entity_category = 10;
string unit_of_measurement = 11;
NumberMode mode = 12;
} }
message NumberStateResponse { message NumberStateResponse {
option (id) = 50; option (id) = 50;
@ -895,6 +929,7 @@ message ListEntitiesSelectResponse {
string icon = 5; string icon = 5;
repeated string options = 6; repeated string options = 6;
bool disabled_by_default = 7; bool disabled_by_default = 7;
EntityCategory entity_category = 8;
} }
message SelectStateResponse { message SelectStateResponse {
option (id) = 53; option (id) = 53;
@ -917,3 +952,28 @@ message SelectCommandRequest {
fixed32 key = 1; fixed32 key = 1;
string state = 2; string state = 2;
} }
// ==================== BUTTON ====================
message ListEntitiesButtonResponse {
option (id) = 61;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BUTTON";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
}
message ButtonCommandRequest {
option (id) = 62;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BUTTON";
option (no_delay) = true;
fixed32 key = 1;
}

View file

@ -1,4 +1,5 @@
#include "api_connection.h" #include "api_connection.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/components/network/util.h" #include "esphome/components/network/util.h"
#include "esphome/core/version.h" #include "esphome/core/version.h"
@ -19,6 +20,7 @@ namespace esphome {
namespace api { namespace api {
static const char *const TAG = "api.connection"; static const char *const TAG = "api.connection";
static const int ESP32_CAMERA_STOP_STREAM = 5000;
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent) APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
: parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { : parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
@ -77,6 +79,8 @@ void APIConnection::loop() {
on_fatal_error(); on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) { if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str()); ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str());
} else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", client_info_.c_str());
} else { } else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno); ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
} }
@ -129,7 +133,7 @@ void APIConnection::loop() {
if (state_subs_at_ != -1) { if (state_subs_at_ != -1) {
const auto &subs = this->parent_->get_state_subs(); const auto &subs = this->parent_->get_state_subs();
if (state_subs_at_ >= subs.size()) { if (state_subs_at_ >= (int) subs.size()) {
state_subs_at_ = -1; state_subs_at_ = -1;
} else { } else {
auto &it = subs[state_subs_at_]; auto &it = subs[state_subs_at_];
@ -143,8 +147,8 @@ void APIConnection::loop() {
} }
} }
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) { std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
return App.get_name() + component_type + nameable->get_object_id(); return App.get_name() + component_type + entity->get_object_id();
} }
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) { DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
@ -180,6 +184,8 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
msg.device_class = binary_sensor->get_device_class(); msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor(); msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
msg.disabled_by_default = binary_sensor->is_disabled_by_default(); msg.disabled_by_default = binary_sensor->is_disabled_by_default();
msg.icon = binary_sensor->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(binary_sensor->get_entity_category());
return this->send_list_entities_binary_sensor_response(msg); return this->send_list_entities_binary_sensor_response(msg);
} }
#endif #endif
@ -212,6 +218,8 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
msg.supports_tilt = traits.get_supports_tilt(); msg.supports_tilt = traits.get_supports_tilt();
msg.device_class = cover->get_device_class(); msg.device_class = cover->get_device_class();
msg.disabled_by_default = cover->is_disabled_by_default(); msg.disabled_by_default = cover->is_disabled_by_default();
msg.icon = cover->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(cover->get_entity_category());
return this->send_list_entities_cover_response(msg); return this->send_list_entities_cover_response(msg);
} }
void APIConnection::cover_command(const CoverCommandRequest &msg) { void APIConnection::cover_command(const CoverCommandRequest &msg) {
@ -277,6 +285,8 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
msg.supports_direction = traits.supports_direction(); msg.supports_direction = traits.supports_direction();
msg.supported_speed_count = traits.supported_speed_count(); msg.supported_speed_count = traits.supported_speed_count();
msg.disabled_by_default = fan->is_disabled_by_default(); msg.disabled_by_default = fan->is_disabled_by_default();
msg.icon = fan->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(fan->get_entity_category());
return this->send_list_entities_fan_response(msg); return this->send_list_entities_fan_response(msg);
} }
void APIConnection::fan_command(const FanCommandRequest &msg) { void APIConnection::fan_command(const FanCommandRequest &msg) {
@ -339,6 +349,8 @@ bool APIConnection::send_light_info(light::LightState *light) {
msg.unique_id = get_default_unique_id("light", light); msg.unique_id = get_default_unique_id("light", light);
msg.disabled_by_default = light->is_disabled_by_default(); msg.disabled_by_default = light->is_disabled_by_default();
msg.icon = light->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(light->get_entity_category());
for (auto mode : traits.get_supported_color_modes()) for (auto mode : traits.get_supported_color_modes())
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode)); msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
@ -425,7 +437,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
msg.device_class = sensor->get_device_class(); msg.device_class = sensor->get_device_class();
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class()); msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
msg.disabled_by_default = sensor->is_disabled_by_default(); msg.disabled_by_default = sensor->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(sensor->get_entity_category());
return this->send_list_entities_sensor_response(msg); return this->send_list_entities_sensor_response(msg);
} }
#endif #endif
@ -449,6 +461,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
msg.icon = a_switch->get_icon(); msg.icon = a_switch->get_icon();
msg.assumed_state = a_switch->assumed_state(); msg.assumed_state = a_switch->assumed_state();
msg.disabled_by_default = a_switch->is_disabled_by_default(); msg.disabled_by_default = a_switch->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
return this->send_list_entities_switch_response(msg); return this->send_list_entities_switch_response(msg);
} }
void APIConnection::switch_command(const SwitchCommandRequest &msg) { void APIConnection::switch_command(const SwitchCommandRequest &msg) {
@ -484,6 +497,7 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
msg.unique_id = get_default_unique_id("text_sensor", text_sensor); msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
msg.icon = text_sensor->get_icon(); msg.icon = text_sensor->get_icon();
msg.disabled_by_default = text_sensor->is_disabled_by_default(); msg.disabled_by_default = text_sensor->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
return this->send_list_entities_text_sensor_response(msg); return this->send_list_entities_text_sensor_response(msg);
} }
#endif #endif
@ -529,6 +543,8 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.unique_id = get_default_unique_id("climate", climate); msg.unique_id = get_default_unique_id("climate", climate);
msg.disabled_by_default = climate->is_disabled_by_default(); msg.disabled_by_default = climate->is_disabled_by_default();
msg.icon = climate->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(climate->get_entity_category());
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();
@ -601,8 +617,11 @@ bool APIConnection::send_number_info(number::Number *number) {
msg.object_id = number->get_object_id(); msg.object_id = number->get_object_id();
msg.name = number->get_name(); msg.name = number->get_name();
msg.unique_id = get_default_unique_id("number", number); msg.unique_id = get_default_unique_id("number", number);
msg.icon = number->traits.get_icon(); msg.icon = number->get_icon();
msg.disabled_by_default = number->is_disabled_by_default(); msg.disabled_by_default = number->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(number->get_entity_category());
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
msg.min_value = number->traits.get_min_value(); msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value(); msg.max_value = number->traits.get_max_value();
@ -638,8 +657,9 @@ bool APIConnection::send_select_info(select::Select *select) {
msg.object_id = select->get_object_id(); msg.object_id = select->get_object_id();
msg.name = select->get_name(); msg.name = select->get_name();
msg.unique_id = get_default_unique_id("select", select); msg.unique_id = get_default_unique_id("select", select);
msg.icon = select->traits.get_icon(); msg.icon = select->get_icon();
msg.disabled_by_default = select->is_disabled_by_default(); msg.disabled_by_default = select->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(select->get_entity_category());
for (const auto &option : select->traits.get_options()) for (const auto &option : select->traits.get_options())
msg.options.push_back(option); msg.options.push_back(option);
@ -657,13 +677,37 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
} }
#endif #endif
#ifdef USE_BUTTON
bool APIConnection::send_button_info(button::Button *button) {
ListEntitiesButtonResponse msg;
msg.key = button->get_object_id_hash();
msg.object_id = button->get_object_id();
msg.name = button->get_name();
msg.unique_id = get_default_unique_id("button", button);
msg.icon = button->get_icon();
msg.disabled_by_default = button->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(button->get_entity_category());
msg.device_class = button->get_device_class();
return this->send_list_entities_button_response(msg);
}
void APIConnection::button_command(const ButtonCommandRequest &msg) {
button::Button *button = App.get_button_by_key(msg.key);
if (button == nullptr)
return;
button->press();
}
#endif
#ifdef USE_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) { void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_) if (!this->state_subscription_)
return; return;
if (this->image_reader_.available()) if (this->image_reader_.available())
return; return;
this->image_reader_.set_image(std::move(image)); if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) ||
image->was_requested_by(esphome::esp32_camera::IDLE))
this->image_reader_.set_image(std::move(image));
} }
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
ListEntitiesCameraResponse msg; ListEntitiesCameraResponse msg;
@ -672,6 +716,8 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
msg.name = camera->get_name(); msg.name = camera->get_name();
msg.unique_id = get_default_unique_id("camera", camera); msg.unique_id = get_default_unique_id("camera", camera);
msg.disabled_by_default = camera->is_disabled_by_default(); msg.disabled_by_default = camera->is_disabled_by_default();
msg.icon = camera->get_icon();
msg.entity_category = static_cast<enums::EntityCategory>(camera->get_entity_category());
return this->send_list_entities_camera_response(msg); return this->send_list_entities_camera_response(msg);
} }
void APIConnection::camera_image(const CameraImageRequest &msg) { void APIConnection::camera_image(const CameraImageRequest &msg) {
@ -679,9 +725,14 @@ void APIConnection::camera_image(const CameraImageRequest &msg) {
return; return;
if (msg.single) if (msg.single)
esp32_camera::global_esp32_camera->request_image(); esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER);
if (msg.stream) if (msg.stream) {
esp32_camera::global_esp32_camera->request_stream(); esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::API_REQUESTER);
App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, []() {
esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER);
});
}
} }
#endif #endif
@ -750,6 +801,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
#ifdef ESPHOME_PROJECT_NAME #ifdef ESPHOME_PROJECT_NAME
resp.project_name = ESPHOME_PROJECT_NAME; resp.project_name = ESPHOME_PROJECT_NAME;
resp.project_version = ESPHOME_PROJECT_VERSION; resp.project_version = ESPHOME_PROJECT_VERSION;
#endif
#ifdef USE_WEBSERVER
resp.webserver_port = WEBSERVER_PORT;
#endif #endif
return resp; return resp;
} }

View file

@ -73,6 +73,10 @@ class APIConnection : public APIServerConnection {
bool send_select_state(select::Select *select, std::string state); bool send_select_state(select::Select *select, std::string state);
bool send_select_info(select::Select *select); bool send_select_info(select::Select *select);
void select_command(const SelectCommandRequest &msg) override; void select_command(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_BUTTON
bool send_button_info(button::Button *button);
void button_command(const ButtonCommandRequest &msg) override;
#endif #endif
bool send_log_message(int level, const char *tag, const char *line); bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

View file

@ -10,7 +10,7 @@ namespace api {
static const char *const TAG = "api.socket"; static const char *const TAG = "api.socket";
/// Is the given return value (from read/write syscalls) a wouldblock error? /// Is the given return value (from write syscalls) a wouldblock error?
bool is_would_block(ssize_t ret) { bool is_would_block(ssize_t ret) {
if (ret == -1) { if (ret == -1) {
return errno == EWOULDBLOCK || errno == EAGAIN; return errno == EWOULDBLOCK || errno == EAGAIN;
@ -64,6 +64,8 @@ const char *api_error_to_str(APIError err) {
return "HANDSHAKESTATE_SPLIT_FAILED"; return "HANDSHAKESTATE_SPLIT_FAILED";
} else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) { } else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
return "BAD_HANDSHAKE_ERROR_BYTE"; return "BAD_HANDSHAKE_ERROR_BYTE";
} else if (err == APIError::CONNECTION_CLOSED) {
return "CONNECTION_CLOSED";
} }
return "UNKNOWN"; return "UNKNOWN";
} }
@ -127,6 +129,14 @@ APIError APINoiseFrameHelper::init() {
return APIError::TCP_NONBLOCKING_FAILED; return APIError::TCP_NONBLOCKING_FAILED;
} }
int enable = 1;
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nodelay failed with errno %d", errno);
return APIError::TCP_NODELAY_FAILED;
}
// init prologue // init prologue
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT)); prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
@ -164,9 +174,6 @@ APIError APINoiseFrameHelper::loop() {
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase. * errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
*/ */
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) { APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
int err;
APIError aerr;
if (frame == nullptr) { if (frame == nullptr) {
HELPER_LOG("Bad argument for try_read_frame_"); HELPER_LOG("Bad argument for try_read_frame_");
return APIError::BAD_ARG; return APIError::BAD_ARG;
@ -177,15 +184,20 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
// no header information yet // no header information yet
size_t to_read = 3 - rx_header_buf_len_; size_t to_read = 3 - rx_header_buf_len_;
ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read); ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
if (is_would_block(received)) { if (received == -1) {
return APIError::WOULD_BLOCK; if (errno == EWOULDBLOCK || errno == EAGAIN) {
} else if (received == -1) { return APIError::WOULD_BLOCK;
}
state_ = State::FAILED; state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno); HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED; return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
} }
rx_header_buf_len_ += received; rx_header_buf_len_ += received;
if (received != to_read) { if ((size_t) received != to_read) {
// not a full read // not a full read
return APIError::WOULD_BLOCK; return APIError::WOULD_BLOCK;
} }
@ -219,15 +231,20 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
// more data to read // more data to read
size_t to_read = msg_size - rx_buf_len_; size_t to_read = msg_size - rx_buf_len_;
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read); ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
if (is_would_block(received)) { if (received == -1) {
return APIError::WOULD_BLOCK; if (errno == EWOULDBLOCK || errno == EAGAIN) {
} else if (received == -1) { return APIError::WOULD_BLOCK;
}
state_ = State::FAILED; state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno); HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED; return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
} }
rx_buf_len_ += received; rx_buf_len_ += received;
if (received != to_read) { if ((size_t) received != to_read) {
// not all read // not all read
return APIError::WOULD_BLOCK; return APIError::WOULD_BLOCK;
} }
@ -235,7 +252,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
// uncomment for even more debugging // uncomment for even more debugging
#ifdef HELPER_LOG_PACKETS #ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str()); ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
#endif #endif
frame->msg = std::move(rx_buf_); frame->msg = std::move(rx_buf_);
// consume msg // consume msg
@ -524,13 +541,13 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() {
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (iovcnt == 0) if (iovcnt == 0)
return APIError::OK; return APIError::OK;
int err;
APIError aerr; APIError aerr;
size_t total_write_len = 0; size_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS #ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str()); ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif #endif
total_write_len += iov[i].iov_len; total_write_len += iov[i].iov_len;
} }
@ -564,7 +581,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
state_ = State::FAILED; state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno); HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED; return APIError::SOCKET_WRITE_FAILED;
} else if (sent != total_write_len) { } else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf // partially sent, add end to tx_buf
size_t to_consume = sent; size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
@ -704,7 +721,7 @@ APIError APINoiseFrameHelper::shutdown(int how) {
} }
extern "C" { extern "C" {
// declare how noise generates random bytes (here with a good HWRNG based on the RF system) // declare how noise generates random bytes (here with a good HWRNG based on the RF system)
void noise_rand_bytes(void *output, size_t len) { esphome::fill_random(reinterpret_cast<uint8_t *>(output), len); } void noise_rand_bytes(void *output, size_t len) { esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len); }
} }
#endif // USE_API_NOISE #endif // USE_API_NOISE
@ -722,6 +739,13 @@ APIError APIPlaintextFrameHelper::init() {
HELPER_LOG("Setting nonblocking failed with errno %d", errno); HELPER_LOG("Setting nonblocking failed with errno %d", errno);
return APIError::TCP_NONBLOCKING_FAILED; return APIError::TCP_NONBLOCKING_FAILED;
} }
int enable = 1;
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nodelay failed with errno %d", errno);
return APIError::TCP_NODELAY_FAILED;
}
state_ = State::DATA; state_ = State::DATA;
return APIError::OK; return APIError::OK;
@ -751,9 +775,6 @@ APIError APIPlaintextFrameHelper::loop() {
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame. * error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
*/ */
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) { APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
int err;
APIError aerr;
if (frame == nullptr) { if (frame == nullptr) {
HELPER_LOG("Bad argument for try_read_frame_"); HELPER_LOG("Bad argument for try_read_frame_");
return APIError::BAD_ARG; return APIError::BAD_ARG;
@ -763,12 +784,17 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
while (!rx_header_parsed_) { while (!rx_header_parsed_) {
uint8_t data; uint8_t data;
ssize_t received = socket_->read(&data, 1); ssize_t received = socket_->read(&data, 1);
if (is_would_block(received)) { if (received == -1) {
return APIError::WOULD_BLOCK; if (errno == EWOULDBLOCK || errno == EAGAIN) {
} else if (received == -1) { return APIError::WOULD_BLOCK;
}
state_ = State::FAILED; state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno); HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED; return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
} }
rx_header_buf_.push_back(data); rx_header_buf_.push_back(data);
@ -809,15 +835,20 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
// more data to read // more data to read
size_t to_read = rx_header_parsed_len_ - rx_buf_len_; size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read); ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
if (is_would_block(received)) { if (received == -1) {
return APIError::WOULD_BLOCK; if (errno == EWOULDBLOCK || errno == EAGAIN) {
} else if (received == -1) { return APIError::WOULD_BLOCK;
}
state_ = State::FAILED; state_ = State::FAILED;
HELPER_LOG("Socket read failed with errno %d", errno); HELPER_LOG("Socket read failed with errno %d", errno);
return APIError::SOCKET_READ_FAILED; return APIError::SOCKET_READ_FAILED;
} else if (received == 0) {
state_ = State::FAILED;
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
} }
rx_buf_len_ += received; rx_buf_len_ += received;
if (received != to_read) { if ((size_t) received != to_read) {
// not all read // not all read
return APIError::WOULD_BLOCK; return APIError::WOULD_BLOCK;
} }
@ -825,7 +856,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
// uncomment for even more debugging // uncomment for even more debugging
#ifdef HELPER_LOG_PACKETS #ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str()); ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
#endif #endif
frame->msg = std::move(rx_buf_); frame->msg = std::move(rx_buf_);
// consume msg // consume msg
@ -837,7 +868,6 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
} }
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) { APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
int err;
APIError aerr; APIError aerr;
if (state_ != State::DATA) { if (state_ != State::DATA) {
@ -857,9 +887,6 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
} }
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); } bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) { APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
int err;
APIError aerr;
if (state_ != State::DATA) { if (state_ != State::DATA) {
return APIError::BAD_STATE; return APIError::BAD_STATE;
} }
@ -903,13 +930,13 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) { APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (iovcnt == 0) if (iovcnt == 0)
return APIError::OK; return APIError::OK;
int err;
APIError aerr; APIError aerr;
size_t total_write_len = 0; size_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS #ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str()); ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif #endif
total_write_len += iov[i].iov_len; total_write_len += iov[i].iov_len;
} }
@ -943,7 +970,7 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
state_ = State::FAILED; state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno); HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED; return APIError::SOCKET_WRITE_FAILED;
} else if (sent != total_write_len) { } else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf // partially sent, add end to tx_buf
size_t to_consume = sent; size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {

View file

@ -1,7 +1,8 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <vector>
#include <deque> #include <deque>
#include <utility>
#include <vector>
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
@ -52,6 +53,7 @@ enum class APIError : int {
HANDSHAKESTATE_SETUP_FAILED = 1019, HANDSHAKESTATE_SETUP_FAILED = 1019,
HANDSHAKESTATE_SPLIT_FAILED = 1020, HANDSHAKESTATE_SPLIT_FAILED = 1020,
BAD_HANDSHAKE_ERROR_BYTE = 1021, BAD_HANDSHAKE_ERROR_BYTE = 1021,
CONNECTION_CLOSED = 1022,
}; };
const char *api_error_to_str(APIError err); const char *api_error_to_str(APIError err);
@ -75,8 +77,8 @@ class APIFrameHelper {
class APINoiseFrameHelper : public APIFrameHelper { class APINoiseFrameHelper : public APIFrameHelper {
public: public:
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx) APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
: socket_(std::move(socket)), ctx_(ctx) {} : socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {}
~APINoiseFrameHelper(); ~APINoiseFrameHelper() override;
APIError init() override; APIError init() override;
APIError loop() override; APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override; APIError read_packet(ReadPacketBuffer *buffer) override;
@ -136,7 +138,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
class APIPlaintextFrameHelper : public APIFrameHelper { class APIPlaintextFrameHelper : public APIFrameHelper {
public: public:
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {} APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
~APIPlaintextFrameHelper() = default; ~APIPlaintextFrameHelper() override = default;
APIError init() override; APIError init() override;
APIError loop() override; APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override; APIError read_packet(ReadPacketBuffer *buffer) override;

View file

@ -11,7 +11,7 @@ using psk_t = std::array<uint8_t, 32>;
class APINoiseContext { class APINoiseContext {
public: public:
void set_psk(psk_t psk) { psk_ = std::move(psk); } void set_psk(psk_t psk) { psk_ = psk; }
const psk_t &get_psk() const { return psk_; } const psk_t &get_psk() const { return psk_; }
protected: protected:

View file

@ -2,11 +2,22 @@
// See scripts/api_protobuf/api_protobuf.py // See scripts/api_protobuf/api_protobuf.py
#include "api_pb2.h" #include "api_pb2.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <cstdio>
namespace esphome { namespace esphome {
namespace api { namespace api {
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
switch (value) {
case enums::ENTITY_CATEGORY_NONE:
return "ENTITY_CATEGORY_NONE";
case enums::ENTITY_CATEGORY_CONFIG:
return "ENTITY_CATEGORY_CONFIG";
case enums::ENTITY_CATEGORY_DIAGNOSTIC:
return "ENTITY_CATEGORY_DIAGNOSTIC";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::LegacyCoverState>(enums::LegacyCoverState value) { template<> const char *proto_enum_to_string<enums::LegacyCoverState>(enums::LegacyCoverState value) {
switch (value) { switch (value) {
case enums::LEGACY_COVER_STATE_OPEN: case enums::LEGACY_COVER_STATE_OPEN:
@ -255,6 +266,18 @@ template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::Climate
return "UNKNOWN"; return "UNKNOWN";
} }
} }
template<> const char *proto_enum_to_string<enums::NumberMode>(enums::NumberMode value) {
switch (value) {
case enums::NUMBER_MODE_AUTO:
return "NUMBER_MODE_AUTO";
case enums::NUMBER_MODE_BOX:
return "NUMBER_MODE_BOX";
case enums::NUMBER_MODE_SLIDER:
return "NUMBER_MODE_SLIDER";
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: {
@ -268,7 +291,7 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value)
void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); } void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void HelloRequest::dump_to(std::string &out) const { void HelloRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("HelloRequest {\n"); out.append("HelloRequest {\n");
out.append(" client_info: "); out.append(" client_info: ");
out.append("'").append(this->client_info).append("'"); out.append("'").append(this->client_info).append("'");
@ -307,7 +330,7 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void HelloResponse::dump_to(std::string &out) const { void HelloResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("HelloResponse {\n"); out.append("HelloResponse {\n");
out.append(" api_version_major: "); out.append(" api_version_major: ");
sprintf(buffer, "%u", this->api_version_major); sprintf(buffer, "%u", this->api_version_major);
@ -338,7 +361,7 @@ bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value
void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); } void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ConnectRequest::dump_to(std::string &out) const { void ConnectRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ConnectRequest {\n"); out.append("ConnectRequest {\n");
out.append(" password: "); out.append(" password: ");
out.append("'").append(this->password).append("'"); out.append("'").append(this->password).append("'");
@ -359,7 +382,7 @@ bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); } void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ConnectResponse::dump_to(std::string &out) const { void ConnectResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ConnectResponse {\n"); out.append("ConnectResponse {\n");
out.append(" invalid_password: "); out.append(" invalid_password: ");
out.append(YESNO(this->invalid_password)); out.append(YESNO(this->invalid_password));
@ -397,6 +420,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->has_deep_sleep = value.as_bool(); this->has_deep_sleep = value.as_bool();
return true; return true;
} }
case 10: {
this->webserver_port = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@ -445,10 +472,11 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
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(8, this->project_name);
buffer.encode_string(9, this->project_version); buffer.encode_string(9, this->project_version);
buffer.encode_uint32(10, this->webserver_port);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoResponse::dump_to(std::string &out) const { void DeviceInfoResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("DeviceInfoResponse {\n"); out.append("DeviceInfoResponse {\n");
out.append(" uses_password: "); out.append(" uses_password: ");
out.append(YESNO(this->uses_password)); out.append(YESNO(this->uses_password));
@ -485,6 +513,11 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append(" project_version: "); out.append(" project_version: ");
out.append("'").append(this->project_version).append("'"); out.append("'").append(this->project_version).append("'");
out.append("\n"); out.append("\n");
out.append(" webserver_port: ");
sprintf(buffer, "%u", this->webserver_port);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -510,6 +543,10 @@ bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVar
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 9: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -532,6 +569,10 @@ bool ListEntitiesBinarySensorResponse::decode_length(uint32_t field_id, ProtoLen
this->device_class = value.as_string(); this->device_class = value.as_string();
return true; return true;
} }
case 8: {
this->icon = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -554,10 +595,12 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->device_class); buffer.encode_string(5, this->device_class);
buffer.encode_bool(6, this->is_status_binary_sensor); buffer.encode_bool(6, this->is_status_binary_sensor);
buffer.encode_bool(7, this->disabled_by_default); buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_string(8, this->icon);
buffer.encode_enum<enums::EntityCategory>(9, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const { void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesBinarySensorResponse {\n"); out.append("ListEntitiesBinarySensorResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -587,6 +630,14 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -621,7 +672,7 @@ void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void BinarySensorStateResponse::dump_to(std::string &out) const { void BinarySensorStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("BinarySensorStateResponse {\n"); out.append("BinarySensorStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -656,6 +707,10 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 11: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -678,6 +733,10 @@ bool ListEntitiesCoverResponse::decode_length(uint32_t field_id, ProtoLengthDeli
this->device_class = value.as_string(); this->device_class = value.as_string();
return true; return true;
} }
case 10: {
this->icon = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -702,10 +761,12 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->supports_tilt); buffer.encode_bool(7, this->supports_tilt);
buffer.encode_string(8, this->device_class); buffer.encode_string(8, this->device_class);
buffer.encode_bool(9, this->disabled_by_default); buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_string(10, this->icon);
buffer.encode_enum<enums::EntityCategory>(11, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCoverResponse::dump_to(std::string &out) const { void ListEntitiesCoverResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesCoverResponse {\n"); out.append("ListEntitiesCoverResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -743,6 +804,14 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -787,7 +856,7 @@ void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void CoverStateResponse::dump_to(std::string &out) const { void CoverStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("CoverStateResponse {\n"); out.append("CoverStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -870,7 +939,7 @@ void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void CoverCommandRequest::dump_to(std::string &out) const { void CoverCommandRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("CoverCommandRequest {\n"); out.append("CoverCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -931,6 +1000,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 11: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -949,6 +1022,10 @@ bool ListEntitiesFanResponse::decode_length(uint32_t field_id, ProtoLengthDelimi
this->unique_id = value.as_string(); this->unique_id = value.as_string();
return true; return true;
} }
case 10: {
this->icon = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -973,10 +1050,12 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->supports_direction); buffer.encode_bool(7, this->supports_direction);
buffer.encode_int32(8, this->supported_speed_count); buffer.encode_int32(8, this->supported_speed_count);
buffer.encode_bool(9, this->disabled_by_default); buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_string(10, this->icon);
buffer.encode_enum<enums::EntityCategory>(11, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesFanResponse::dump_to(std::string &out) const { void ListEntitiesFanResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesFanResponse {\n"); out.append("ListEntitiesFanResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -1015,6 +1094,14 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -1064,7 +1151,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void FanStateResponse::dump_to(std::string &out) const { void FanStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("FanStateResponse {\n"); out.append("FanStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -1165,7 +1252,7 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void FanCommandRequest::dump_to(std::string &out) const { void FanCommandRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("FanCommandRequest {\n"); out.append("FanCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -1241,6 +1328,10 @@ bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 15: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -1263,6 +1354,10 @@ bool ListEntitiesLightResponse::decode_length(uint32_t field_id, ProtoLengthDeli
this->effects.push_back(value.as_string()); this->effects.push_back(value.as_string());
return true; return true;
} }
case 14: {
this->icon = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -1303,10 +1398,12 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(11, it, true); buffer.encode_string(11, it, true);
} }
buffer.encode_bool(13, this->disabled_by_default); buffer.encode_bool(13, this->disabled_by_default);
buffer.encode_string(14, this->icon);
buffer.encode_enum<enums::EntityCategory>(15, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLightResponse::dump_to(std::string &out) const { void ListEntitiesLightResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesLightResponse {\n"); out.append("ListEntitiesLightResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -1366,6 +1463,14 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -1456,7 +1561,7 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void LightStateResponse::dump_to(std::string &out) const { void LightStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("LightStateResponse {\n"); out.append("LightStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -1679,7 +1784,7 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void LightCommandRequest::dump_to(std::string &out) const { void LightCommandRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("LightCommandRequest {\n"); out.append("LightCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -1825,6 +1930,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 13: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -1882,10 +1991,11 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::SensorStateClass>(10, this->state_class); buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
buffer.encode_enum<enums::SensorLastResetType>(11, this->legacy_last_reset_type); buffer.encode_enum<enums::SensorLastResetType>(11, this->legacy_last_reset_type);
buffer.encode_bool(12, this->disabled_by_default); buffer.encode_bool(12, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(13, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSensorResponse::dump_to(std::string &out) const { void ListEntitiesSensorResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesSensorResponse {\n"); out.append("ListEntitiesSensorResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -1936,6 +2046,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -1970,7 +2084,7 @@ void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SensorStateResponse::dump_to(std::string &out) const { void SensorStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SensorStateResponse {\n"); out.append("SensorStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -1998,6 +2112,10 @@ bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 8: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -2042,10 +2160,11 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->icon); buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->assumed_state); buffer.encode_bool(6, this->assumed_state);
buffer.encode_bool(7, this->disabled_by_default); buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSwitchResponse::dump_to(std::string &out) const { void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesSwitchResponse {\n"); out.append("ListEntitiesSwitchResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -2075,6 +2194,10 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -2104,7 +2227,7 @@ void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SwitchStateResponse::dump_to(std::string &out) const { void SwitchStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SwitchStateResponse {\n"); out.append("SwitchStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -2143,7 +2266,7 @@ void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SwitchCommandRequest::dump_to(std::string &out) const { void SwitchCommandRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SwitchCommandRequest {\n"); out.append("SwitchCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -2162,6 +2285,10 @@ bool ListEntitiesTextSensorResponse::decode_varint(uint32_t field_id, ProtoVarIn
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -2205,10 +2332,11 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(4, this->unique_id); buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon); buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default); buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const { void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesTextSensorResponse {\n"); out.append("ListEntitiesTextSensorResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -2234,6 +2362,10 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -2274,7 +2406,7 @@ void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void TextSensorStateResponse::dump_to(std::string &out) const { void TextSensorStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("TextSensorStateResponse {\n"); out.append("TextSensorStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -2311,7 +2443,7 @@ void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeLogsRequest::dump_to(std::string &out) const { void SubscribeLogsRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SubscribeLogsRequest {\n"); out.append("SubscribeLogsRequest {\n");
out.append(" level: "); out.append(" level: ");
out.append(proto_enum_to_string<enums::LogLevel>(this->level)); out.append(proto_enum_to_string<enums::LogLevel>(this->level));
@ -2354,7 +2486,7 @@ void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeLogsResponse::dump_to(std::string &out) const { void SubscribeLogsResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SubscribeLogsResponse {\n"); out.append("SubscribeLogsResponse {\n");
out.append(" level: "); out.append(" level: ");
out.append(proto_enum_to_string<enums::LogLevel>(this->level)); out.append(proto_enum_to_string<enums::LogLevel>(this->level));
@ -2396,7 +2528,7 @@ void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void HomeassistantServiceMap::dump_to(std::string &out) const { void HomeassistantServiceMap::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("HomeassistantServiceMap {\n"); out.append("HomeassistantServiceMap {\n");
out.append(" key: "); out.append(" key: ");
out.append("'").append(this->key).append("'"); out.append("'").append(this->key).append("'");
@ -2455,7 +2587,7 @@ void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void HomeassistantServiceResponse::dump_to(std::string &out) const { void HomeassistantServiceResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("HomeassistantServiceResponse {\n"); out.append("HomeassistantServiceResponse {\n");
out.append(" service: "); out.append(" service: ");
out.append("'").append(this->service).append("'"); out.append("'").append(this->service).append("'");
@ -2511,7 +2643,7 @@ void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const { void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SubscribeHomeAssistantStateResponse {\n"); out.append("SubscribeHomeAssistantStateResponse {\n");
out.append(" entity_id: "); out.append(" entity_id: ");
out.append("'").append(this->entity_id).append("'"); out.append("'").append(this->entity_id).append("'");
@ -2548,7 +2680,7 @@ void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void HomeAssistantStateResponse::dump_to(std::string &out) const { void HomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("HomeAssistantStateResponse {\n"); out.append("HomeAssistantStateResponse {\n");
out.append(" entity_id: "); out.append(" entity_id: ");
out.append("'").append(this->entity_id).append("'"); out.append("'").append(this->entity_id).append("'");
@ -2581,7 +2713,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); } void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void GetTimeResponse::dump_to(std::string &out) const { void GetTimeResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("GetTimeResponse {\n"); out.append("GetTimeResponse {\n");
out.append(" epoch_seconds: "); out.append(" epoch_seconds: ");
sprintf(buffer, "%u", this->epoch_seconds); sprintf(buffer, "%u", this->epoch_seconds);
@ -2616,7 +2748,7 @@ void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesServicesArgument::dump_to(std::string &out) const { void ListEntitiesServicesArgument::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesServicesArgument {\n"); out.append("ListEntitiesServicesArgument {\n");
out.append(" name: "); out.append(" name: ");
out.append("'").append(this->name).append("'"); out.append("'").append(this->name).append("'");
@ -2661,7 +2793,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesServicesResponse::dump_to(std::string &out) const { void ListEntitiesServicesResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesServicesResponse {\n"); out.append("ListEntitiesServicesResponse {\n");
out.append(" name: "); out.append(" name: ");
out.append("'").append(this->name).append("'"); out.append("'").append(this->name).append("'");
@ -2755,7 +2887,7 @@ void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ExecuteServiceArgument::dump_to(std::string &out) const { void ExecuteServiceArgument::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ExecuteServiceArgument {\n"); out.append("ExecuteServiceArgument {\n");
out.append(" bool_: "); out.append(" bool_: ");
out.append(YESNO(this->bool_)); out.append(YESNO(this->bool_));
@ -2836,7 +2968,7 @@ void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ExecuteServiceRequest::dump_to(std::string &out) const { void ExecuteServiceRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ExecuteServiceRequest {\n"); out.append("ExecuteServiceRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -2857,6 +2989,10 @@ bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -2875,6 +3011,10 @@ bool ListEntitiesCameraResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->unique_id = value.as_string(); this->unique_id = value.as_string();
return true; return true;
} }
case 6: {
this->icon = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -2895,10 +3035,12 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(3, this->name); buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id); buffer.encode_string(4, this->unique_id);
buffer.encode_bool(5, this->disabled_by_default); buffer.encode_bool(5, this->disabled_by_default);
buffer.encode_string(6, this->icon);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCameraResponse::dump_to(std::string &out) const { void ListEntitiesCameraResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesCameraResponse {\n"); out.append("ListEntitiesCameraResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -2920,6 +3062,14 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -2960,7 +3110,7 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void CameraImageResponse::dump_to(std::string &out) const { void CameraImageResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("CameraImageResponse {\n"); out.append("CameraImageResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -2997,7 +3147,7 @@ void CameraImageRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void CameraImageRequest::dump_to(std::string &out) const { void CameraImageRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("CameraImageRequest {\n"); out.append("CameraImageRequest {\n");
out.append(" single: "); out.append(" single: ");
out.append(YESNO(this->single)); out.append(YESNO(this->single));
@ -3047,6 +3197,10 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 20: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -3073,6 +3227,10 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
this->supported_custom_presets.push_back(value.as_string()); this->supported_custom_presets.push_back(value.as_string());
return true; return true;
} }
case 19: {
this->icon = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -3130,10 +3288,12 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(17, it, true); buffer.encode_string(17, it, true);
} }
buffer.encode_bool(18, this->disabled_by_default); buffer.encode_bool(18, this->disabled_by_default);
buffer.encode_string(19, this->icon);
buffer.encode_enum<enums::EntityCategory>(20, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesClimateResponse::dump_to(std::string &out) const { void ListEntitiesClimateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesClimateResponse {\n"); out.append("ListEntitiesClimateResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -3222,6 +3382,14 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -3312,7 +3480,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ClimateStateResponse::dump_to(std::string &out) const { void ClimateStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ClimateStateResponse {\n"); out.append("ClimateStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -3500,7 +3668,7 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ClimateCommandRequest::dump_to(std::string &out) const { void ClimateCommandRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ClimateCommandRequest {\n"); out.append("ClimateCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -3598,6 +3766,14 @@ bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 10: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
case 12: {
this->mode = value.as_enum<enums::NumberMode>();
return true;
}
default: default:
return false; return false;
} }
@ -3620,6 +3796,10 @@ bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->icon = value.as_string(); this->icon = value.as_string();
return true; return true;
} }
case 11: {
this->unit_of_measurement = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -3656,10 +3836,13 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(7, this->max_value); buffer.encode_float(7, this->max_value);
buffer.encode_float(8, this->step); buffer.encode_float(8, this->step);
buffer.encode_bool(9, this->disabled_by_default); buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(10, this->entity_category);
buffer.encode_string(11, this->unit_of_measurement);
buffer.encode_enum<enums::NumberMode>(12, this->mode);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesNumberResponse::dump_to(std::string &out) const { void ListEntitiesNumberResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesNumberResponse {\n"); out.append("ListEntitiesNumberResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -3700,6 +3883,18 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" unit_of_measurement: ");
out.append("'").append(this->unit_of_measurement).append("'");
out.append("\n");
out.append(" mode: ");
out.append(proto_enum_to_string<enums::NumberMode>(this->mode));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -3734,7 +3929,7 @@ void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void NumberStateResponse::dump_to(std::string &out) const { void NumberStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("NumberStateResponse {\n"); out.append("NumberStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -3772,7 +3967,7 @@ void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void NumberCommandRequest::dump_to(std::string &out) const { void NumberCommandRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("NumberCommandRequest {\n"); out.append("NumberCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -3792,6 +3987,10 @@ bool ListEntitiesSelectResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->disabled_by_default = value.as_bool(); this->disabled_by_default = value.as_bool();
return true; return true;
} }
case 8: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default: default:
return false; return false;
} }
@ -3842,10 +4041,11 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(6, it, true); buffer.encode_string(6, it, true);
} }
buffer.encode_bool(7, this->disabled_by_default); buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSelectResponse::dump_to(std::string &out) const { void ListEntitiesSelectResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesSelectResponse {\n"); out.append("ListEntitiesSelectResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -3877,6 +4077,10 @@ void ListEntitiesSelectResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: "); out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default)); out.append(YESNO(this->disabled_by_default));
out.append("\n"); out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -3917,7 +4121,7 @@ void SelectStateResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SelectStateResponse::dump_to(std::string &out) const { void SelectStateResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SelectStateResponse {\n"); out.append("SelectStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -3960,7 +4164,7 @@ void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void SelectCommandRequest::dump_to(std::string &out) const { void SelectCommandRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SelectCommandRequest {\n"); out.append("SelectCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);
@ -3973,6 +4177,127 @@ void SelectCommandRequest::dump_to(std::string &out) const {
out.append("}"); out.append("}");
} }
#endif #endif
bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
}
bool ListEntitiesButtonResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
case 8: {
this->device_class = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesButtonResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_string(8, this->device_class);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesButtonResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesButtonResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
out.append("\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" device_class: ");
out.append("'").append(this->device_class).append("'");
out.append("\n");
out.append("}");
}
#endif
bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ButtonCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("ButtonCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View file

@ -9,6 +9,11 @@ namespace api {
namespace enums { namespace enums {
enum EntityCategory : uint32_t {
ENTITY_CATEGORY_NONE = 0,
ENTITY_CATEGORY_CONFIG = 1,
ENTITY_CATEGORY_DIAGNOSTIC = 2,
};
enum LegacyCoverState : uint32_t { enum LegacyCoverState : uint32_t {
LEGACY_COVER_STATE_OPEN = 0, LEGACY_COVER_STATE_OPEN = 0,
LEGACY_COVER_STATE_CLOSED = 1, LEGACY_COVER_STATE_CLOSED = 1,
@ -118,6 +123,11 @@ enum ClimatePreset : uint32_t {
CLIMATE_PRESET_SLEEP = 6, CLIMATE_PRESET_SLEEP = 6,
CLIMATE_PRESET_ACTIVITY = 7, CLIMATE_PRESET_ACTIVITY = 7,
}; };
enum NumberMode : uint32_t {
NUMBER_MODE_AUTO = 0,
NUMBER_MODE_BOX = 1,
NUMBER_MODE_SLIDER = 2,
};
} // namespace enums } // namespace enums
@ -224,6 +234,7 @@ class DeviceInfoResponse : public ProtoMessage {
bool has_deep_sleep{false}; bool has_deep_sleep{false};
std::string project_name{}; std::string project_name{};
std::string project_version{}; std::string project_version{};
uint32_t webserver_port{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -269,6 +280,8 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
std::string device_class{}; std::string device_class{};
bool is_status_binary_sensor{false}; bool is_status_binary_sensor{false};
bool disabled_by_default{false}; bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -304,6 +317,8 @@ class ListEntitiesCoverResponse : public ProtoMessage {
bool supports_tilt{false}; bool supports_tilt{false};
std::string device_class{}; std::string device_class{};
bool disabled_by_default{false}; bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -360,6 +375,8 @@ class ListEntitiesFanResponse : public ProtoMessage {
bool supports_direction{false}; bool supports_direction{false};
int32_t supported_speed_count{0}; int32_t supported_speed_count{0};
bool disabled_by_default{false}; bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -424,6 +441,8 @@ class ListEntitiesLightResponse : public ProtoMessage {
float max_mireds{0.0f}; float max_mireds{0.0f};
std::vector<std::string> effects{}; std::vector<std::string> effects{};
bool disabled_by_default{false}; bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -512,6 +531,7 @@ class ListEntitiesSensorResponse : public ProtoMessage {
enums::SensorStateClass state_class{}; enums::SensorStateClass state_class{};
enums::SensorLastResetType legacy_last_reset_type{}; enums::SensorLastResetType legacy_last_reset_type{};
bool disabled_by_default{false}; bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -545,6 +565,7 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
std::string icon{}; std::string icon{};
bool assumed_state{false}; bool assumed_state{false};
bool disabled_by_default{false}; bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -589,6 +610,7 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
std::string unique_id{}; std::string unique_id{};
std::string icon{}; std::string icon{};
bool disabled_by_default{false}; bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -799,6 +821,8 @@ class ListEntitiesCameraResponse : public ProtoMessage {
std::string name{}; std::string name{};
std::string unique_id{}; std::string unique_id{};
bool disabled_by_default{false}; bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -856,6 +880,8 @@ class ListEntitiesClimateResponse : public ProtoMessage {
std::vector<enums::ClimatePreset> supported_presets{}; std::vector<enums::ClimatePreset> supported_presets{};
std::vector<std::string> supported_custom_presets{}; std::vector<std::string> supported_custom_presets{};
bool disabled_by_default{false}; bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -935,6 +961,9 @@ class ListEntitiesNumberResponse : public ProtoMessage {
float max_value{0.0f}; float max_value{0.0f};
float step{0.0f}; float step{0.0f};
bool disabled_by_default{false}; bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string unit_of_measurement{};
enums::NumberMode mode{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -980,6 +1009,7 @@ class ListEntitiesSelectResponse : public ProtoMessage {
std::string icon{}; std::string icon{};
std::vector<std::string> options{}; std::vector<std::string> options{};
bool disabled_by_default{false}; bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -1018,6 +1048,37 @@ class SelectCommandRequest : public ProtoMessage {
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_length(uint32_t field_id, ProtoLengthDelimited value) override;
}; };
class ListEntitiesButtonResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string device_class{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ButtonCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View file

@ -282,6 +282,16 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
#endif #endif
#ifdef USE_BUTTON
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesButtonResponse>(msg, 61);
}
#endif
#ifdef USE_BUTTON
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) { switch (msg_type) {
case 1: { case 1: {
@ -513,6 +523,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
#endif #endif
this->on_select_command_request(msg); this->on_select_command_request(msg);
#endif
break;
}
case 62: {
#ifdef USE_BUTTON
ButtonCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
#endif
this->on_button_command_request(msg);
#endif #endif
break; break;
} }
@ -737,6 +758,19 @@ void APIServerConnection::on_select_command_request(const SelectCommandRequest &
this->select_command(msg); this->select_command(msg);
} }
#endif #endif
#ifdef USE_BUTTON
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->button_command(msg);
}
#endif
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View file

@ -129,6 +129,12 @@ class APIServerConnectionBase : public ProtoService {
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){}; virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif
#ifdef USE_BUTTON
bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
#endif
#ifdef USE_BUTTON
virtual void on_button_command_request(const ButtonCommandRequest &value){};
#endif #endif
protected: protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@ -171,6 +177,9 @@ class APIServerConnection : public APIServerConnectionBase {
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
virtual void select_command(const SelectCommandRequest &msg) = 0; virtual void select_command(const SelectCommandRequest &msg) = 0;
#endif
#ifdef USE_BUTTON
virtual void button_command(const ButtonCommandRequest &msg) = 0;
#endif #endif
protected: protected:
void on_hello_request(const HelloRequest &msg) override; void on_hello_request(const HelloRequest &msg) override;
@ -209,6 +218,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_SELECT #ifdef USE_SELECT
void on_select_command_request(const SelectCommandRequest &msg) override; void on_select_command_request(const SelectCommandRequest &msg) override;
#endif #endif
#ifdef USE_BUTTON
void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif
}; };
} // namespace api } // namespace api

View file

@ -77,7 +77,7 @@ void APIServer::setup() {
this->last_connected_ = millis(); this->last_connected_ = millis();
#ifdef USE_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
if (esp32_camera::global_esp32_camera != nullptr) { if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
esp32_camera::global_esp32_camera->add_image_callback( esp32_camera::global_esp32_camera->add_image_callback(
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) { [this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
for (auto &c : this->clients_) for (auto &c : this->clients_)
@ -133,6 +133,11 @@ void APIServer::loop() {
void APIServer::dump_config() { void APIServer::dump_config() {
ESP_LOGCONFIG(TAG, "API Server:"); ESP_LOGCONFIG(TAG, "API Server:");
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
#ifdef USE_API_NOISE
ESP_LOGCONFIG(TAG, " Using noise encryption: YES");
#else
ESP_LOGCONFIG(TAG, " Using noise encryption: NO");
#endif
} }
bool APIServer::uses_password() const { return !this->password_.empty(); } bool APIServer::uses_password() const { return !this->password_.empty(); }
bool APIServer::check_password(const std::string &password) const { bool APIServer::check_password(const std::string &password) const {

View file

@ -32,7 +32,7 @@ class APIServer : public Component, public Controller {
void set_reboot_timeout(uint32_t reboot_timeout); void set_reboot_timeout(uint32_t reboot_timeout);
#ifdef USE_API_NOISE #ifdef USE_API_NOISE
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(std::move(psk)); } void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; } std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
#endif // USE_API_NOISE #endif // USE_API_NOISE

View file

@ -23,7 +23,6 @@ async def async_run_logs(config, address):
_LOGGER.info("Starting log output from %s using esphome API", address) _LOGGER.info("Starting log output from %s using esphome API", address)
zc = zeroconf.Zeroconf() zc = zeroconf.Zeroconf()
cli = APIClient( cli = APIClient(
asyncio.get_event_loop(),
address, address,
port, port,
password, password,

View file

@ -8,6 +8,18 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
template<typename... X> class TemplatableStringValue : public TemplatableValue<std::string, X...> {
public:
TemplatableStringValue() : TemplatableValue<std::string, X...>() {}
template<typename F, enable_if_t<!is_invocable<F, X...>::value, int> = 0>
TemplatableStringValue(F value) : TemplatableValue<std::string, X...>(value) {}
template<typename F, enable_if_t<is_invocable<F, X...>::value, int> = 0>
TemplatableStringValue(F f)
: TemplatableValue<std::string, X...>([f](X... x) -> std::string { return to_string(f(x...)); }) {}
};
template<typename... Ts> class TemplatableKeyValuePair { template<typename... Ts> class TemplatableKeyValuePair {
public: public:
template<typename T> TemplatableKeyValuePair(std::string key, T value) : key(std::move(key)), value(value) {} template<typename T> TemplatableKeyValuePair(std::string key, T value) : key(std::move(key)), value(value) {}
@ -19,7 +31,8 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
public: public:
explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent), is_event_(is_event) {} explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent), is_event_(is_event) {}
TEMPLATABLE_STRING_VALUE(service); template<typename T> void set_service(T service) { this->service_ = service; }
template<typename T> void add_data(std::string key, T value) { template<typename T> void add_data(std::string key, T value) {
this->data_.push_back(TemplatableKeyValuePair<Ts...>(key, value)); this->data_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
} }
@ -58,6 +71,7 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
protected: protected:
APIServer *parent_; APIServer *parent_;
bool is_event_; bool is_event_;
TemplatableStringValue<Ts...> service_{};
std::vector<TemplatableKeyValuePair<Ts...>> data_; std::vector<TemplatableKeyValuePair<Ts...>> data_;
std::vector<TemplatableKeyValuePair<Ts...>> data_template_; std::vector<TemplatableKeyValuePair<Ts...>> data_template_;
std::vector<TemplatableKeyValuePair<Ts...>> variables_; std::vector<TemplatableKeyValuePair<Ts...>> variables_;

View file

@ -27,6 +27,9 @@ bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->clie
#ifdef USE_SWITCH #ifdef USE_SWITCH
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); } bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); }
#endif #endif
#ifdef USE_BUTTON
bool ListEntitiesIterator::on_button(button::Button *button) { return this->client_->send_button_info(button); }
#endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
return this->client_->send_text_sensor_info(text_sensor); return this->client_->send_text_sensor_info(text_sensor);

View file

@ -30,6 +30,9 @@ class ListEntitiesIterator : public ComponentIterator {
#ifdef USE_SWITCH #ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override; bool on_switch(switch_::Switch *a_switch) override;
#endif #endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override;
#endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override; bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif #endif

View file

@ -31,6 +31,9 @@ class InitialStateIterator : public ComponentIterator {
#ifdef USE_SWITCH #ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override; bool on_switch(switch_::Switch *a_switch) override;
#endif #endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override { return true; };
#endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override; bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif #endif

View file

@ -116,6 +116,21 @@ void ComponentIterator::advance() {
} }
break; break;
#endif #endif
#ifdef USE_BUTTON
case IteratorState::BUTTON:
if (this->at_ >= App.get_buttons().size()) {
advance_platform = true;
} else {
auto *button = App.get_buttons()[this->at_];
if (button->is_internal()) {
success = true;
break;
} else {
success = this->on_button(button);
}
}
break;
#endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
case IteratorState::TEXT_SENSOR: case IteratorState::TEXT_SENSOR:
if (this->at_ >= App.get_text_sensors().size()) { if (this->at_ >= App.get_text_sensors().size()) {

View file

@ -38,6 +38,9 @@ class ComponentIterator {
#ifdef USE_SWITCH #ifdef USE_SWITCH
virtual bool on_switch(switch_::Switch *a_switch) = 0; virtual bool on_switch(switch_::Switch *a_switch) = 0;
#endif #endif
#ifdef USE_BUTTON
virtual bool on_button(button::Button *button) = 0;
#endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0; virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
#endif #endif
@ -78,6 +81,9 @@ class ComponentIterator {
#ifdef USE_SWITCH #ifdef USE_SWITCH
SWITCH, SWITCH,
#endif #endif
#ifdef USE_BUTTON
BUTTON,
#endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
TEXT_SENSOR, TEXT_SENSOR,
#endif #endif

View file

@ -25,14 +25,14 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
bool success = false; bool success = false;
for (auto &service_data : device.get_service_datas()) { for (auto &service_data : device.get_service_datas()) {
auto res = parse_header(service_data); auto res = parse_header_(service_data);
if (!res.has_value()) { if (!res.has_value()) {
continue; continue;
} }
if (!(parse_message(service_data.data, *res))) { if (!(parse_message_(service_data.data, *res))) {
continue; continue;
} }
if (!(report_results(res, device.address_str()))) { if (!(report_results_(res, device.address_str()))) {
continue; continue;
} }
if (res->temperature.has_value() && this->temperature_ != nullptr) if (res->temperature.has_value() && this->temperature_ != nullptr)
@ -45,11 +45,13 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
this->battery_voltage_->publish_state(*res->battery_voltage); this->battery_voltage_->publish_state(*res->battery_voltage);
success = true; success = true;
} }
if (this->signal_strength_ != nullptr)
this->signal_strength_->publish_state(device.get_rssi());
return success; return success;
} }
optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) { optional<ParseResult> ATCMiThermometer::parse_header_(const esp32_ble_tracker::ServiceData &service_data) {
ParseResult result; ParseResult result;
if (!service_data.uuid.contains(0x1A, 0x18)) { if (!service_data.uuid.contains(0x1A, 0x18)) {
ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes."); ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
@ -68,7 +70,7 @@ optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::Se
return result; return result;
} }
bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseResult &result) { bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, ParseResult &result) {
// Byte 0-5 mac in correct order // Byte 0-5 mac in correct order
// Byte 6-7 Temperature in uint16 // Byte 6-7 Temperature in uint16
// Byte 8 Humidity in percent // Byte 8 Humidity in percent
@ -101,7 +103,7 @@ bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseR
return true; return true;
} }
bool ATCMiThermometer::report_results(const optional<ParseResult> &result, const std::string &address) { bool ATCMiThermometer::report_results_(const optional<ParseResult> &result, const std::string &address) {
if (!result.has_value()) { if (!result.has_value()) {
ESP_LOGVV(TAG, "report_results(): no results available."); ESP_LOGVV(TAG, "report_results(): no results available.");
return false; return false;

View file

@ -28,6 +28,7 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; } void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
void set_battery_voltage(sensor::Sensor *battery_voltage) { battery_voltage_ = battery_voltage; } void set_battery_voltage(sensor::Sensor *battery_voltage) { battery_voltage_ = battery_voltage; }
void set_signal_strength(sensor::Sensor *signal_strength) { signal_strength_ = signal_strength; }
protected: protected:
uint64_t address_; uint64_t address_;
@ -35,10 +36,11 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
sensor::Sensor *humidity_{nullptr}; sensor::Sensor *humidity_{nullptr};
sensor::Sensor *battery_level_{nullptr}; sensor::Sensor *battery_level_{nullptr};
sensor::Sensor *battery_voltage_{nullptr}; sensor::Sensor *battery_voltage_{nullptr};
sensor::Sensor *signal_strength_{nullptr};
optional<ParseResult> parse_header(const esp32_ble_tracker::ServiceData &service_data); optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
bool parse_message(const std::vector<uint8_t> &message, ParseResult &result); bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
bool report_results(const optional<ParseResult> &result, const std::string &address); bool report_results_(const optional<ParseResult> &result, const std::string &address);
}; };
} // namespace atc_mithermometer } // namespace atc_mithermometer

View file

@ -6,14 +6,18 @@ from esphome.const import (
CONF_BATTERY_VOLTAGE, CONF_BATTERY_VOLTAGE,
CONF_MAC_ADDRESS, CONF_MAC_ADDRESS,
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_SIGNAL_STRENGTH,
CONF_TEMPERATURE, CONF_TEMPERATURE,
CONF_ID, CONF_ID,
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_SIGNAL_STRENGTH,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_DECIBEL_MILLIWATT,
UNIT_PERCENT, UNIT_PERCENT,
UNIT_VOLT, UNIT_VOLT,
) )
@ -49,12 +53,21 @@ CONFIG_SCHEMA = (
accuracy_decimals=0, accuracy_decimals=0,
device_class=DEVICE_CLASS_BATTERY, device_class=DEVICE_CLASS_BATTERY,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
), ),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema( cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT, unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3, accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE, device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_SIGNAL_STRENGTH): sensor.sensor_schema(
unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
), ),
} }
) )
@ -82,3 +95,6 @@ async def to_code(config):
if CONF_BATTERY_VOLTAGE in config: if CONF_BATTERY_VOLTAGE in config:
sens = await 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))
if CONF_SIGNAL_STRENGTH in config:
sens = await sensor.new_sensor(config[CONF_SIGNAL_STRENGTH])
cg.add(var.set_signal_strength(sens))

View file

@ -265,27 +265,57 @@ float ATM90E32Component::get_power_factor_c_() {
} }
float ATM90E32Component::get_forward_active_energy_a_() { float ATM90E32Component::get_forward_active_energy_a_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA); uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA);
return (float) val * 10 / 3200; // convert register value to WattHours if ((UINT32_MAX - this->phase_[0].cumulative_forward_active_energy_) > val) {
this->phase_[0].cumulative_forward_active_energy_ += val;
} else {
this->phase_[0].cumulative_forward_active_energy_ = val;
}
return ((float) this->phase_[0].cumulative_forward_active_energy_ * 10 / 3200);
} }
float ATM90E32Component::get_forward_active_energy_b_() { float ATM90E32Component::get_forward_active_energy_b_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB); uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB);
return (float) val * 10 / 3200; if (UINT32_MAX - this->phase_[1].cumulative_forward_active_energy_ > val) {
this->phase_[1].cumulative_forward_active_energy_ += val;
} else {
this->phase_[1].cumulative_forward_active_energy_ = val;
}
return ((float) this->phase_[1].cumulative_forward_active_energy_ * 10 / 3200);
} }
float ATM90E32Component::get_forward_active_energy_c_() { float ATM90E32Component::get_forward_active_energy_c_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC); uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC);
return (float) val * 10 / 3200; if (UINT32_MAX - this->phase_[2].cumulative_forward_active_energy_ > val) {
this->phase_[2].cumulative_forward_active_energy_ += val;
} else {
this->phase_[2].cumulative_forward_active_energy_ = val;
}
return ((float) this->phase_[2].cumulative_forward_active_energy_ * 10 / 3200);
} }
float ATM90E32Component::get_reverse_active_energy_a_() { float ATM90E32Component::get_reverse_active_energy_a_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA); uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA);
return (float) val * 10 / 3200; if (UINT32_MAX - this->phase_[0].cumulative_reverse_active_energy_ > val) {
this->phase_[0].cumulative_reverse_active_energy_ += val;
} else {
this->phase_[0].cumulative_reverse_active_energy_ = val;
}
return ((float) this->phase_[0].cumulative_reverse_active_energy_ * 10 / 3200);
} }
float ATM90E32Component::get_reverse_active_energy_b_() { float ATM90E32Component::get_reverse_active_energy_b_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB); uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB);
return (float) val * 10 / 3200; if (UINT32_MAX - this->phase_[1].cumulative_reverse_active_energy_ > val) {
this->phase_[1].cumulative_reverse_active_energy_ += val;
} else {
this->phase_[1].cumulative_reverse_active_energy_ = val;
}
return ((float) this->phase_[1].cumulative_reverse_active_energy_ * 10 / 3200);
} }
float ATM90E32Component::get_reverse_active_energy_c_() { float ATM90E32Component::get_reverse_active_energy_c_() {
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC); uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC);
return (float) val * 10 / 3200; if (UINT32_MAX - this->phase_[2].cumulative_reverse_active_energy_ > val) {
this->phase_[2].cumulative_reverse_active_energy_ += val;
} else {
this->phase_[2].cumulative_reverse_active_energy_ = val;
}
return ((float) this->phase_[2].cumulative_reverse_active_energy_ * 10 / 3200);
} }
float ATM90E32Component::get_frequency_() { float ATM90E32Component::get_frequency_() {
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ); uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);

View file

@ -77,6 +77,8 @@ class ATM90E32Component : public PollingComponent,
sensor::Sensor *power_factor_sensor_{nullptr}; sensor::Sensor *power_factor_sensor_{nullptr};
sensor::Sensor *forward_active_energy_sensor_{nullptr}; sensor::Sensor *forward_active_energy_sensor_{nullptr};
sensor::Sensor *reverse_active_energy_sensor_{nullptr}; sensor::Sensor *reverse_active_energy_sensor_{nullptr};
uint32_t cumulative_forward_active_energy_{0};
uint32_t cumulative_reverse_active_energy_{0};
} phase_[3]; } phase_[3];
sensor::Sensor *freq_sensor_{nullptr}; sensor::Sensor *freq_sensor_{nullptr};
sensor::Sensor *chip_temperature_sensor_{nullptr}; sensor::Sensor *chip_temperature_sensor_{nullptr};

View file

@ -17,6 +17,7 @@ from esphome.const import (
DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_LIGHTBULB, ICON_LIGHTBULB,
ICON_CURRENT_AC, ICON_CURRENT_AC,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
@ -125,6 +126,7 @@ CONFIG_SCHEMA = (
accuracy_decimals=1, accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE, device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
), ),
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(

View file

@ -14,6 +14,7 @@ void BParasite::dump_config() {
LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Temperature", this->temperature_);
LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Humidity", this->humidity_);
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_); LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
} }
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
@ -36,6 +37,15 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
const auto &data = service_data.data; const auto &data = service_data.data;
const uint8_t protocol_version = data[0] >> 4;
if (protocol_version != 1) {
ESP_LOGE(TAG, "Unsupported protocol version: %u", protocol_version);
return false;
}
// Some b-parasite versions have an (optional) illuminance sensor.
bool has_illuminance = data[0] & 0x1;
// Counter for deduplicating messages. // Counter for deduplicating messages.
uint8_t counter = data[1] & 0x0f; uint8_t counter = data[1] & 0x0f;
if (last_processed_counter_ == counter) { if (last_processed_counter_ == counter) {
@ -59,6 +69,9 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
uint16_t soil_moisture = data[8] << 8 | data[9]; uint16_t soil_moisture = data[8] << 8 | data[9];
float moisture_percent = (100.0f * soil_moisture) / (1 << 16); float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
// Ambient light in lux.
float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f;
if (battery_voltage_ != nullptr) { if (battery_voltage_ != nullptr) {
battery_voltage_->publish_state(battery_voltage); battery_voltage_->publish_state(battery_voltage);
} }
@ -71,6 +84,13 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (soil_moisture_ != nullptr) { if (soil_moisture_ != nullptr) {
soil_moisture_->publish_state(moisture_percent); soil_moisture_->publish_state(moisture_percent);
} }
if (illuminance_ != nullptr) {
if (has_illuminance) {
illuminance_->publish_state(illuminance);
} else {
ESP_LOGE(TAG, "No lux information is present in the BLE packet");
}
}
last_processed_counter_ = counter; last_processed_counter_ = counter;
return true; return true;

View file

@ -22,6 +22,7 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
void set_soil_moisture(sensor::Sensor *soil_moisture) { soil_moisture_ = soil_moisture; } void set_soil_moisture(sensor::Sensor *soil_moisture) { soil_moisture_ = soil_moisture; }
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
protected: protected:
// The received advertisement packet contains an unsigned 4 bits wrap-around counter // The received advertisement packet contains an unsigned 4 bits wrap-around counter
@ -32,6 +33,7 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene
sensor::Sensor *temperature_{nullptr}; sensor::Sensor *temperature_{nullptr};
sensor::Sensor *humidity_{nullptr}; sensor::Sensor *humidity_{nullptr};
sensor::Sensor *soil_moisture_{nullptr}; sensor::Sensor *soil_moisture_{nullptr};
sensor::Sensor *illuminance_{nullptr};
}; };
} // namespace b_parasite } // namespace b_parasite

View file

@ -5,14 +5,18 @@ from esphome.const import (
CONF_BATTERY_VOLTAGE, CONF_BATTERY_VOLTAGE,
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_ID, CONF_ID,
CONF_ILLUMINANCE,
CONF_MOISTURE, CONF_MOISTURE,
CONF_MAC_ADDRESS, CONF_MAC_ADDRESS,
CONF_TEMPERATURE, CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_LUX,
UNIT_PERCENT, UNIT_PERCENT,
UNIT_VOLT, UNIT_VOLT,
) )
@ -48,6 +52,7 @@ CONFIG_SCHEMA = (
accuracy_decimals=3, accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE, device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
), ),
cv.Optional(CONF_MOISTURE): sensor.sensor_schema( cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT, unit_of_measurement=UNIT_PERCENT,
@ -55,6 +60,12 @@ CONFIG_SCHEMA = (
device_class=DEVICE_CLASS_HUMIDITY, device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
unit_of_measurement=UNIT_LUX,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
),
} }
) )
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
@ -74,6 +85,7 @@ async def to_code(config):
(CONF_HUMIDITY, var.set_humidity), (CONF_HUMIDITY, var.set_humidity),
(CONF_BATTERY_VOLTAGE, var.set_battery_voltage), (CONF_BATTERY_VOLTAGE, var.set_battery_voltage),
(CONF_MOISTURE, var.set_soil_moisture), (CONF_MOISTURE, var.set_soil_moisture),
(CONF_ILLUMINANCE, var.set_illuminance),
]: ]:
if config_key in config: if config_key in config:
sens = await sensor.new_sensor(config[config_key]) sens = await sensor.new_sensor(config[config_key])

View file

@ -80,21 +80,23 @@ void BangBangClimate::compute_state_() {
climate::ClimateAction target_action; climate::ClimateAction target_action;
if (too_cold) { if (too_cold) {
// too cold -> enable heating if possible, else idle // too cold -> enable heating if possible and enabled, else idle
if (this->supports_heat_) if (this->supports_heat_ &&
(this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_HEAT))
target_action = climate::CLIMATE_ACTION_HEATING; target_action = climate::CLIMATE_ACTION_HEATING;
else else
target_action = climate::CLIMATE_ACTION_IDLE; target_action = climate::CLIMATE_ACTION_IDLE;
} else if (too_hot) { } else if (too_hot) {
// too hot -> enable cooling if possible, else idle // too hot -> enable cooling if possible and enabled, else idle
if (this->supports_cool_) if (this->supports_cool_ &&
(this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_COOL))
target_action = climate::CLIMATE_ACTION_COOLING; target_action = climate::CLIMATE_ACTION_COOLING;
else else
target_action = climate::CLIMATE_ACTION_IDLE; target_action = climate::CLIMATE_ACTION_IDLE;
} else { } else {
// neither too hot nor too cold -> in range // neither too hot nor too cold -> in range
if (this->supports_cool_ && this->supports_heat_) { if (this->supports_cool_ && this->supports_heat_ && this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
// if supports both ends, go to idle action // if supports both ends and both cooling and heating enabled, go to idle action
target_action = climate::CLIMATE_ACTION_IDLE; target_action = climate::CLIMATE_ACTION_IDLE;
} else { } else {
// else use current mode and don't change (hysteresis) // else use current mode and don't change (hysteresis)

View file

@ -71,7 +71,7 @@ void BH1750Sensor::update() {
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; } float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
void BH1750Sensor::read_data_() { void BH1750Sensor::read_data_() {
uint16_t raw_value; uint16_t raw_value;
if (!this->read(reinterpret_cast<uint8_t *>(&raw_value), 2)) { if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
this->status_set_warning(); this->status_set_warning();
return; return;
} }
@ -79,6 +79,9 @@ void BH1750Sensor::read_data_() {
float lx = float(raw_value) / 1.2f; float lx = float(raw_value) / 1.2f;
lx *= 69.0f / this->measurement_duration_; lx *= 69.0f / this->measurement_duration_;
if (this->resolution_ == BH1750_RESOLUTION_0P5_LX) {
lx /= 2.0f;
}
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx); ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx);
this->publish_state(lx); this->publish_state(lx);
this->status_clear_warning(); this->status_clear_warning();

View file

@ -1,15 +1,14 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.cpp_helpers import setup_entity
from esphome import automation, core from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt from esphome.components import mqtt
from esphome.const import ( from esphome.const import (
CONF_DELAY, CONF_DELAY,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_DISABLED_BY_DEFAULT,
CONF_FILTERS, CONF_FILTERS,
CONF_ID, CONF_ID,
CONF_INTERNAL,
CONF_INVALID_COOLDOWN, CONF_INVALID_COOLDOWN,
CONF_INVERTED, CONF_INVERTED,
CONF_MAX_LENGTH, CONF_MAX_LENGTH,
@ -45,9 +44,11 @@ from esphome.const import (
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE, DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM, DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_SAFETY, DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE, DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND, DEVICE_CLASS_SOUND,
DEVICE_CLASS_TAMPER,
DEVICE_CLASS_UPDATE, DEVICE_CLASS_UPDATE,
DEVICE_CLASS_VIBRATION, DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW, DEVICE_CLASS_WINDOW,
@ -77,9 +78,11 @@ DEVICE_CLASSES = [
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE, DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM, DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
DEVICE_CLASS_SAFETY, DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE, DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND, DEVICE_CLASS_SOUND,
DEVICE_CLASS_TAMPER,
DEVICE_CLASS_UPDATE, DEVICE_CLASS_UPDATE,
DEVICE_CLASS_VIBRATION, DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW, DEVICE_CLASS_WINDOW,
@ -88,7 +91,7 @@ DEVICE_CLASSES = [
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor") binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable) BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
BinarySensorInitiallyOff = binary_sensor_ns.class_( BinarySensorInitiallyOff = binary_sensor_ns.class_(
"BinarySensorInitiallyOff", BinarySensor "BinarySensorInitiallyOff", BinarySensor
) )
@ -314,7 +317,8 @@ def validate_multi_click_timing(value):
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{ {
cv.GenerateID(): cv.declare_id(BinarySensor), cv.GenerateID(): cv.declare_id(BinarySensor),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
@ -375,10 +379,8 @@ BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).exten
async def setup_binary_sensor_core_(var, config): async def setup_binary_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME])) await setup_entity(var, config)
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
if CONF_DEVICE_CLASS in config: if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_INVERTED in config: if CONF_INVERTED in config:

View file

@ -42,13 +42,16 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
} }
} }
std::string BinarySensor::device_class() { return ""; } std::string BinarySensor::device_class() { return ""; }
BinarySensor::BinarySensor(const std::string &name) : Nameable(name), state(false) {} BinarySensor::BinarySensor(const std::string &name) : EntityBase(name), state(false) {}
BinarySensor::BinarySensor() : BinarySensor("") {} BinarySensor::BinarySensor() : BinarySensor("") {}
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; } void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
std::string BinarySensor::get_device_class() { std::string BinarySensor::get_device_class() {
if (this->device_class_.has_value()) if (this->device_class_.has_value())
return *this->device_class_; return *this->device_class_;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
return this->device_class(); return this->device_class();
#pragma GCC diagnostic pop
} }
void BinarySensor::add_filter(Filter *filter) { void BinarySensor::add_filter(Filter *filter) {
filter->parent_ = this; filter->parent_ = this;

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/filter.h" #include "esphome/components/binary_sensor/filter.h"
@ -22,7 +23,7 @@ namespace binary_sensor {
* The sub classes should notify the front-end of new states via the publish_state() method which * The sub classes should notify the front-end of new states via the publish_state() method which
* handles inverted inputs for you. * handles inverted inputs for you.
*/ */
class BinarySensor : public Nameable { class BinarySensor : public EntityBase {
public: public:
explicit BinarySensor(); explicit BinarySensor();
/** Construct a binary sensor with the specified name /** Construct a binary sensor with the specified name
@ -74,6 +75,7 @@ class BinarySensor : public Nameable {
// ========== OVERRIDE METHODS ========== // ========== OVERRIDE METHODS ==========
// (You'll only need this when creating your own custom binary sensor) // (You'll only need this when creating your own custom binary sensor)
/// Get the default device class for this sensor, or empty string for no default. /// Get the default device class for this sensor, or empty string for no default.
ESPDEPRECATED("device_class() is deprecated, set property during config validation instead.", "2022.01")
virtual std::string device_class(); virtual std::string device_class();
protected: protected:

View file

@ -0,0 +1 @@
CODEOWNERS = ["@tobias-"]

View file

@ -0,0 +1,137 @@
#include "bl0940.h"
#include "esphome/core/log.h"
namespace esphome {
namespace bl0940 {
static const char *const TAG = "bl0940";
static const uint8_t BL0940_READ_COMMAND = 0x50; // 0x58 according to documentation
static const uint8_t BL0940_FULL_PACKET = 0xAA;
static const uint8_t BL0940_PACKET_HEADER = 0x55; // 0x58 according to documentation
static const uint8_t BL0940_WRITE_COMMAND = 0xA0; // 0xA8 according to documentation
static const uint8_t BL0940_REG_I_FAST_RMS_CTRL = 0x10;
static const uint8_t BL0940_REG_MODE = 0x18;
static const uint8_t BL0940_REG_SOFT_RESET = 0x19;
static const uint8_t BL0940_REG_USR_WRPROT = 0x1A;
static const uint8_t BL0940_REG_TPS_CTRL = 0x1B;
const uint8_t BL0940_INIT[5][6] = {
// Reset to default
{BL0940_WRITE_COMMAND, BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
// Enable User Operation Write
{BL0940_WRITE_COMMAND, BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
// 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
{BL0940_WRITE_COMMAND, BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37},
// 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
{BL0940_WRITE_COMMAND, BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
// 0x181C = Half cycle, Fast RMS threshold 6172
{BL0940_WRITE_COMMAND, BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
void BL0940::loop() {
DataPacket buffer;
if (!this->available()) {
return;
}
if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
if (validate_checksum(&buffer)) {
received_package_(&buffer);
}
} else {
ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
while (read() >= 0)
;
}
}
bool BL0940::validate_checksum(const DataPacket *data) {
uint8_t checksum = BL0940_READ_COMMAND;
// Whole package but checksum
for (uint32_t i = 0; i < sizeof(data->raw) - 1; i++) {
checksum += data->raw[i];
}
checksum ^= 0xFF;
if (checksum != data->checksum) {
ESP_LOGW(TAG, "BL0940 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
}
return checksum == data->checksum;
}
void BL0940::update() {
this->flush();
this->write_byte(BL0940_READ_COMMAND);
this->write_byte(BL0940_FULL_PACKET);
}
void BL0940::setup() {
for (auto i : BL0940_INIT) {
this->write_array(i, 6);
delay(1);
}
this->flush();
}
float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const {
auto tb = (float) (temperature.h << 8 | temperature.l);
float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
if (sensor != nullptr) {
if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
ESP_LOGD("bl0940", "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
sensor->get_name().c_str(), sensor->get_state(), converted_temp);
return 0.0f;
}
sensor->publish_state(converted_temp);
}
return converted_temp;
}
void BL0940::received_package_(const DataPacket *data) const {
// Bad header
if (data->frame_header != BL0940_PACKET_HEADER) {
ESP_LOGI("bl0940", "Invalid data. Header mismatch: %d", data->frame_header);
return;
}
float v_rms = (float) to_uint32_t(data->v_rms) / voltage_reference_;
float i_rms = (float) to_uint32_t(data->i_rms) / current_reference_;
float watt = (float) to_int32_t(data->watt) / power_reference_;
uint32_t cf_cnt = to_uint32_t(data->cf_cnt);
float total_energy_consumption = (float) cf_cnt / energy_reference_;
float tps1 = update_temp_(internal_temperature_sensor_, data->tps1);
float tps2 = update_temp_(external_temperature_sensor_, data->tps2);
if (voltage_sensor_ != nullptr) {
voltage_sensor_->publish_state(v_rms);
}
if (current_sensor_ != nullptr) {
current_sensor_->publish_state(i_rms);
}
if (power_sensor_ != nullptr) {
power_sensor_->publish_state(watt);
}
if (energy_sensor_ != nullptr) {
energy_sensor_->publish_state(total_energy_consumption);
}
ESP_LOGV("bl0940", "BL0940: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
total_energy_consumption, tps1, tps2);
}
void BL0940::dump_config() { // NOLINT(readability-function-cognitive-complexity)
ESP_LOGCONFIG(TAG, "BL0940:");
LOG_SENSOR("", "Voltage", this->voltage_sensor_);
LOG_SENSOR("", "Current", this->current_sensor_);
LOG_SENSOR("", "Power", this->power_sensor_);
LOG_SENSOR("", "Energy", this->energy_sensor_);
LOG_SENSOR("", "Internal temperature", this->internal_temperature_sensor_);
LOG_SENSOR("", "External temperature", this->external_temperature_sensor_);
}
uint32_t BL0940::to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
int32_t BL0940::to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; }
} // namespace bl0940
} // namespace esphome

View file

@ -0,0 +1,109 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace bl0940 {
static const float BL0940_PREF = 1430;
static const float BL0940_UREF = 33000;
static const float BL0940_IREF = 275000; // 2750 from tasmota. Seems to generate values 100 times too high
// Measured to 297J per click according to power consumption of 5 minutes
// Converted to kWh (3.6MJ per kwH). Used to be 256 * 1638.4
static const float BL0940_EREF = 3.6e6 / 297;
struct ube24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
uint8_t l;
uint8_t m;
uint8_t h;
} __attribute__((packed));
struct ube16_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
uint8_t l;
uint8_t h;
} __attribute__((packed));
struct sbe24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
uint8_t l;
uint8_t m;
int8_t h;
} __attribute__((packed));
// Caveat: All these values are big endian (low - middle - high)
union DataPacket { // NOLINT(altera-struct-pack-align)
uint8_t raw[35];
struct {
uint8_t frame_header; // value of 0x58 according to docs. 0x55 according to Tasmota real world tests. Reality wins.
ube24_t i_fast_rms; // 0x00
ube24_t i_rms; // 0x04
ube24_t RESERVED0; // reserved
ube24_t v_rms; // 0x06
ube24_t RESERVED1; // reserved
sbe24_t watt; // 0x08
ube24_t RESERVED2; // reserved
ube24_t cf_cnt; // 0x0A
ube24_t RESERVED3; // reserved
ube16_t tps1; // 0x0c
uint8_t RESERVED4; // value of 0x00
ube16_t tps2; // 0x0c
uint8_t RESERVED5; // value of 0x00
uint8_t checksum; // checksum
};
} __attribute__((packed));
class BL0940 : public PollingComponent, public uart::UARTDevice {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
void set_internal_temperature_sensor(sensor::Sensor *internal_temperature_sensor) {
internal_temperature_sensor_ = internal_temperature_sensor;
}
void set_external_temperature_sensor(sensor::Sensor *external_temperature_sensor) {
external_temperature_sensor_ = external_temperature_sensor;
}
void loop() override;
void update() override;
void setup() override;
void dump_config() override;
protected:
sensor::Sensor *voltage_sensor_;
sensor::Sensor *current_sensor_;
// NB This may be negative as the circuits is seemingly able to measure
// power in both directions
sensor::Sensor *power_sensor_;
sensor::Sensor *energy_sensor_;
sensor::Sensor *internal_temperature_sensor_;
sensor::Sensor *external_temperature_sensor_;
// Max difference between two measurements of the temperature. Used to avoid noise.
float max_temperature_diff_{0};
// Divide by this to turn into Watt
float power_reference_ = BL0940_PREF;
// Divide by this to turn into Volt
float voltage_reference_ = BL0940_UREF;
// Divide by this to turn into Ampere
float current_reference_ = BL0940_IREF;
// Divide by this to turn into kWh
float energy_reference_ = BL0940_EREF;
float update_temp_(sensor::Sensor *sensor, ube16_t packed_temperature) const;
static uint32_t to_uint32_t(ube24_t input);
static int32_t to_int32_t(sbe24_t input);
static bool validate_checksum(const DataPacket *data);
void received_package_(const DataPacket *data) const;
};
} // namespace bl0940
} // namespace esphome

View file

@ -0,0 +1,106 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, uart
from esphome.const import (
CONF_CURRENT,
CONF_ENERGY,
CONF_ID,
CONF_POWER,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_NONE,
UNIT_AMPERE,
UNIT_CELSIUS,
UNIT_KILOWATT_HOURS,
UNIT_VOLT,
UNIT_WATT,
)
DEPENDENCIES = ["uart"]
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
CONF_EXTERNAL_TEMPERATURE = "external_temperature"
bl0940_ns = cg.esphome_ns.namespace("bl0940")
BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BL0940),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE,
ICON_EMPTY,
2,
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
),
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
UNIT_KILOWATT_HOURS,
ICON_EMPTY,
0,
DEVICE_CLASS_ENERGY,
STATE_CLASS_NONE,
),
cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
0,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_NONE,
),
cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_EMPTY,
0,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_NONE,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(uart.UART_DEVICE_SCHEMA)
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
if CONF_VOLTAGE in config:
conf = config[CONF_VOLTAGE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_voltage_sensor(sens))
if CONF_CURRENT in config:
conf = config[CONF_CURRENT]
sens = await sensor.new_sensor(conf)
cg.add(var.set_current_sensor(sens))
if CONF_POWER in config:
conf = config[CONF_POWER]
sens = await sensor.new_sensor(conf)
cg.add(var.set_power_sensor(sens))
if CONF_ENERGY in config:
conf = config[CONF_ENERGY]
sens = await sensor.new_sensor(conf)
cg.add(var.set_energy_sensor(sens))
if CONF_INTERNAL_TEMPERATURE in config:
conf = config[CONF_INTERNAL_TEMPERATURE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_internal_temperature_sensor(sens))
if CONF_EXTERNAL_TEMPERATURE in config:
conf = config[CONF_EXTERNAL_TEMPERATURE]
sens = await sensor.new_sensor(conf)
cg.add(var.set_external_temperature_sensor(sens))

View file

@ -11,11 +11,12 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode {
public: public:
explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); } explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
void loop() override {} void loop() override {}
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override {
if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK) if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK)
this->trigger(); this->trigger();
if (event == ESP_GATTC_SEARCH_CMPL_EVT) if (event == ESP_GATTC_SEARCH_CMPL_EVT)
this->node_state = espbt::ClientState::Established; this->node_state = espbt::ClientState::ESTABLISHED;
} }
}; };
@ -23,11 +24,12 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
public: public:
explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); } explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
void loop() override {} void loop() override {}
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override {
if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0) if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0)
this->trigger(); this->trigger();
if (event == ESP_GATTC_SEARCH_CMPL_EVT) if (event == ESP_GATTC_SEARCH_CMPL_EVT)
this->node_state = espbt::ClientState::Established; this->node_state = espbt::ClientState::ESTABLISHED;
} }
}; };

View file

@ -11,18 +11,20 @@ namespace ble_client {
static const char *const TAG = "ble_client"; static const char *const TAG = "ble_client";
float BLEClient::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
void BLEClient::setup() { void BLEClient::setup() {
auto ret = esp_ble_gattc_app_register(this->app_id); auto ret = esp_ble_gattc_app_register(this->app_id);
if (ret) { if (ret) {
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret); ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
this->mark_failed(); this->mark_failed();
} }
this->set_states(espbt::ClientState::Idle); this->set_states_(espbt::ClientState::IDLE);
this->enabled = true; this->enabled = true;
} }
void BLEClient::loop() { void BLEClient::loop() {
if (this->state() == espbt::ClientState::Discovered) { if (this->state() == espbt::ClientState::DISCOVERED) {
this->connect(); this->connect();
} }
for (auto *node : this->nodes_) for (auto *node : this->nodes_)
@ -39,11 +41,11 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
return false; return false;
if (device.address_uint64() != this->address) if (device.address_uint64() != this->address)
return false; return false;
if (this->state() != espbt::ClientState::Idle) if (this->state() != espbt::ClientState::IDLE)
return false; return false;
ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str()); ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str());
this->set_states(espbt::ClientState::Discovered); this->set_states_(espbt::ClientState::DISCOVERED);
auto addr = device.address_uint64(); auto addr = device.address_uint64();
this->remote_bda[0] = (addr >> 40) & 0xFF; this->remote_bda[0] = (addr >> 40) & 0xFF;
@ -69,7 +71,7 @@ std::string BLEClient::address_str() const {
void BLEClient::set_enabled(bool enabled) { void BLEClient::set_enabled(bool enabled) {
if (enabled == this->enabled) if (enabled == this->enabled)
return; return;
if (!enabled && this->state() != espbt::ClientState::Idle) { if (!enabled && this->state() != espbt::ClientState::IDLE) {
ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str()); ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str());
auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id); auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id);
if (ret) { if (ret) {
@ -84,9 +86,9 @@ void BLEClient::connect() {
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true); auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true);
if (ret) { if (ret) {
ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret); ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
this->set_states(espbt::ClientState::Idle); this->set_states_(espbt::ClientState::IDLE);
} else { } else {
this->set_states(espbt::ClientState::Connecting); this->set_states_(espbt::ClientState::CONNECTING);
} }
} }
@ -97,7 +99,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if) if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
return; return;
bool all_established = this->all_nodes_established(); bool all_established = this->all_nodes_established_();
switch (event) { switch (event) {
case ESP_GATTC_REG_EVT: { case ESP_GATTC_REG_EVT: {
@ -113,7 +115,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str()); ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
if (param->open.status != ESP_GATT_OK) { if (param->open.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status); ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
this->set_states(espbt::ClientState::Idle); this->set_states_(espbt::ClientState::IDLE);
break; break;
} }
this->conn_id = param->open.conn_id; this->conn_id = param->open.conn_id;
@ -126,7 +128,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
case ESP_GATTC_CFG_MTU_EVT: { case ESP_GATTC_CFG_MTU_EVT: {
if (param->cfg_mtu.status != ESP_GATT_OK) { if (param->cfg_mtu.status != ESP_GATT_OK) {
ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status); ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
this->set_states(espbt::ClientState::Idle); this->set_states_(espbt::ClientState::IDLE);
break; break;
} }
ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu); ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
@ -141,7 +143,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
for (auto &svc : this->services_) for (auto &svc : this->services_)
delete svc; // NOLINT(cppcoreguidelines-owning-memory) delete svc; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.clear(); this->services_.clear();
this->set_states(espbt::ClientState::Idle); this->set_states_(espbt::ClientState::IDLE);
break; break;
} }
case ESP_GATTC_SEARCH_RES_EVT: { case ESP_GATTC_SEARCH_RES_EVT: {
@ -160,8 +162,8 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle); ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle);
svc->parse_characteristics(); svc->parse_characteristics();
} }
this->set_states(espbt::ClientState::Connected); this->set_states_(espbt::ClientState::CONNECTED);
this->set_state(espbt::ClientState::Established); this->set_state(espbt::ClientState::ESTABLISHED);
break; break;
} }
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
@ -192,7 +194,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
node->gattc_event_handler(event, esp_gattc_if, param); node->gattc_event_handler(event, esp_gattc_if, param);
// Delete characteristics after clients have used them to save RAM. // Delete characteristics after clients have used them to save RAM.
if (!all_established && this->all_nodes_established()) { if (!all_established && this->all_nodes_established_()) {
for (auto &svc : this->services_) for (auto &svc : this->services_)
delete svc; // NOLINT(cppcoreguidelines-owning-memory) delete svc; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.clear(); this->services_.clear();
@ -386,6 +388,15 @@ BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid)); return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
} }
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
auto client = this->service->client;
auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val,
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
}
}
} // namespace ble_client } // namespace ble_client
} // namespace esphome } // namespace esphome

View file

@ -59,7 +59,7 @@ class BLECharacteristic {
void parse_descriptors(); void parse_descriptors();
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid); BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
BLEDescriptor *get_descriptor(uint16_t uuid); BLEDescriptor *get_descriptor(uint16_t uuid);
void write_value(uint8_t *new_val, int16_t new_val_size);
BLEService *service; BLEService *service;
}; };
@ -81,11 +81,13 @@ class BLEClient : public espbt::ESPBTClient, public Component {
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
void loop() override; void loop() override;
float get_setup_priority() const override;
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param); void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
bool parse_device(const espbt::ESPBTDevice &device) override; bool parse_device(const espbt::ESPBTDevice &device) override;
void on_scan_end() override {} void on_scan_end() override {}
void connect(); void connect() override;
void set_address(uint64_t address) { this->address = address; } void set_address(uint64_t address) { this->address = address; }
@ -116,16 +118,16 @@ class BLEClient : public espbt::ESPBTClient, public Component {
std::string address_str() const; std::string address_str() const;
protected: protected:
void set_states(espbt::ClientState st) { void set_states_(espbt::ClientState st) {
this->set_state(st); this->set_state(st);
for (auto &node : nodes_) for (auto &node : nodes_)
node->node_state = st; node->node_state = st;
} }
bool all_nodes_established() { bool all_nodes_established_() {
if (this->state() != espbt::ClientState::Established) if (this->state() != espbt::ClientState::ESTABLISHED)
return false; return false;
for (auto &node : nodes_) for (auto &node : nodes_)
if (node->node_state != espbt::ClientState::Established) if (node->node_state != espbt::ClientState::ESTABLISHED)
return false; return false;
return true; return true;
} }

View file

@ -0,0 +1,67 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import output, ble_client, esp32_ble_tracker
from esphome.const import CONF_ID, CONF_SERVICE_UUID
from .. import ble_client_ns
DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
BLEBinaryOutput = ble_client_ns.class_(
"BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component
)
CONFIG_SCHEMA = cv.All(
output.BINARY_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(ble_client.BLE_CLIENT_SCHEMA)
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
cg.add(
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(
var.set_char_uuid16(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid32_format
):
cg.add(
var.set_char_uuid32(
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
)
)
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format
):
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128))
yield output.register_output(var, config)
yield ble_client.register_ble_node(var, config)
yield cg.register_component(var, config)

View file

@ -0,0 +1,71 @@
#include "ble_binary_output.h"
#include "esphome/core/log.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef USE_ESP32
namespace esphome {
namespace ble_client {
static const char *const TAG = "ble_binary_output";
void BLEBinaryOutput::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Binary Output:");
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str());
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
LOG_BINARY_OUTPUT(this);
}
void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
switch (event) {
case ESP_GATTC_OPEN_EVT:
this->client_state_ = espbt::ClientState::ESTABLISHED;
ESP_LOGW(TAG, "[%s] Connected successfully!", this->char_uuid_.to_string().c_str());
break;
case ESP_GATTC_DISCONNECT_EVT:
ESP_LOGW(TAG, "[%s] Disconnected", this->char_uuid_.to_string().c_str());
this->client_state_ = espbt::ClientState::IDLE;
break;
case ESP_GATTC_WRITE_CHAR_EVT: {
if (param->write.status == 0) {
break;
}
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str());
break;
}
if (param->write.handle == chr->handle) {
ESP_LOGW(TAG, "[%s] Write error, status=%d", this->char_uuid_.to_string().c_str(), param->write.status);
}
break;
}
default:
break;
}
}
void BLEBinaryOutput::write_state(bool state) {
if (this->client_state_ != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Not connected to BLE client. State update can not be written.",
this->char_uuid_.to_string().c_str());
return;
}
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] Characteristic not found. State update can not be written.",
this->char_uuid_.to_string().c_str());
return;
}
uint8_t state_as_uint = (uint8_t) state;
ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint);
chr->write_value(&state_as_uint, sizeof(state_as_uint));
}
} // namespace ble_client
} // namespace esphome
#endif

View file

@ -0,0 +1,39 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/output/binary_output.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
namespace esphome {
namespace ble_client {
namespace espbt = esphome::esp32_ble_tracker;
class BLEBinaryOutput : public output::BinaryOutput, public BLEClientNode, public Component {
public:
void dump_config() override;
void loop() override {}
float get_setup_priority() const override { return setup_priority::DATA; }
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
protected:
void write_state(bool state) override;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;
espbt::ClientState client_state_;
};
} // namespace ble_client
} // namespace esphome
#endif

View file

@ -67,7 +67,7 @@ async def to_code(config):
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])) var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
) )
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128)) cg.add(var.set_service_uuid128(uuid128))
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
@ -87,7 +87,9 @@ async def to_code(config):
elif len(config[CONF_CHARACTERISTIC_UUID]) == len( elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format esp32_ble_tracker.bt_uuid128_format
): ):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_CHARACTERISTIC_UUID]) uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_CHARACTERISTIC_UUID]
)
cg.add(var.set_char_uuid128(uuid128)) cg.add(var.set_char_uuid128(uuid128))
if CONF_DESCRIPTOR_UUID in config: if CONF_DESCRIPTOR_UUID in config:
@ -108,7 +110,9 @@ async def to_code(config):
elif len(config[CONF_DESCRIPTOR_UUID]) == len( elif len(config[CONF_DESCRIPTOR_UUID]) == len(
esp32_ble_tracker.bt_uuid128_format esp32_ble_tracker.bt_uuid128_format
): ):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID]) uuid128 = esp32_ble_tracker.as_reversed_hex_array(
config[CONF_DESCRIPTOR_UUID]
)
cg.add(var.set_descr_uuid128(uuid128)) cg.add(var.set_descr_uuid128(uuid128))
if CONF_LAMBDA in config: if CONF_LAMBDA in config:

View file

@ -11,10 +11,11 @@ namespace ble_client {
class BLESensorNotifyTrigger : public Trigger<float>, public BLESensor { class BLESensorNotifyTrigger : public Trigger<float>, public BLESensor {
public: public:
explicit BLESensorNotifyTrigger(BLESensor *sensor) { sensor_ = sensor; } explicit BLESensorNotifyTrigger(BLESensor *sensor) { sensor_ = sensor; }
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override {
switch (event) { switch (event) {
case ESP_GATTC_SEARCH_CMPL_EVT: { case ESP_GATTC_SEARCH_CMPL_EVT: {
this->sensor_->node_state = espbt::ClientState::Established; this->sensor_->node_state = espbt::ClientState::ESTABLISHED;
break; break;
} }
case ESP_GATTC_NOTIFY_EVT: { case ESP_GATTC_NOTIFY_EVT: {

View file

@ -71,7 +71,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status); ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
} }
} else { } else {
this->node_state = espbt::ClientState::Established; this->node_state = espbt::ClientState::ESTABLISHED;
} }
break; break;
} }
@ -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(this->parse_data(param->read.value, param->read.value_len)); this->publish_state(this->parse_data_(param->read.value, param->read.value_len));
} }
break; break;
} }
@ -93,11 +93,11 @@ 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(this->parse_data(param->notify.value, param->notify.value_len)); 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: {
this->node_state = espbt::ClientState::Established; this->node_state = espbt::ClientState::ESTABLISHED;
break; break;
} }
default: default:
@ -105,7 +105,7 @@ 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) { float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
if (this->data_to_value_func_.has_value()) { if (this->data_to_value_func_.has_value()) {
std::vector<uint8_t> data(value, value + value_len); std::vector<uint8_t> data(value, value + value_len);
return (*this->data_to_value_func_)(data); return (*this->data_to_value_func_)(data);
@ -115,7 +115,7 @@ float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
} }
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());
return; return;
} }

View file

@ -32,13 +32,13 @@ 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_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); float parse_data_(uint8_t *value, uint16_t value_len);
optional<data_to_value_t> data_to_value_func_{}; optional<data_to_value_t> data_to_value_func_{};
bool notify_; bool notify_;
espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID service_uuid_;

View file

@ -21,10 +21,10 @@ void BLEClientSwitch::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
this->publish_state(this->parent_->enabled); this->publish_state(this->parent_->enabled);
break; break;
case ESP_GATTC_OPEN_EVT: case ESP_GATTC_OPEN_EVT:
this->node_state = espbt::ClientState::Established; this->node_state = espbt::ClientState::ESTABLISHED;
break; break;
case ESP_GATTC_DISCONNECT_EVT: case ESP_GATTC_DISCONNECT_EVT:
this->node_state = espbt::ClientState::Idle; this->node_state = espbt::ClientState::IDLE;
this->publish_state(this->parent_->enabled); this->publish_state(this->parent_->enabled);
break; break;
default: default:

View file

@ -93,8 +93,8 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
float get_setup_priority() const override { return setup_priority::DATA; } float get_setup_priority() const override { return setup_priority::DATA; }
protected: protected:
enum MATCH_TYPE { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID }; enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
MATCH_TYPE match_by_; MatchType match_by_;
bool found_{false}; bool found_{false};

View file

@ -15,7 +15,7 @@ namespace ble_scanner {
class BLEScanner : public text_sensor::TextSensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { class BLEScanner : public text_sensor::TextSensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component {
public: public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
this->publish_state("{\"timestamp\":" + to_string(::time(NULL)) + this->publish_state("{\"timestamp\":" + to_string(::time(nullptr)) +
"," ","
"\"address\":\"" + "\"address\":\"" +
device.address_str() + device.address_str() +

View file

@ -33,6 +33,7 @@ static const uint8_t BME280_REGISTER_CONTROLHUMID = 0xF2;
static const uint8_t BME280_REGISTER_STATUS = 0xF3; static const uint8_t BME280_REGISTER_STATUS = 0xF3;
static const uint8_t BME280_REGISTER_CONTROL = 0xF4; static const uint8_t BME280_REGISTER_CONTROL = 0xF4;
static const uint8_t BME280_REGISTER_CONFIG = 0xF5; static const uint8_t BME280_REGISTER_CONFIG = 0xF5;
static const uint8_t BME280_REGISTER_MEASUREMENTS = 0xF7;
static const uint8_t BME280_REGISTER_PRESSUREDATA = 0xF7; static const uint8_t BME280_REGISTER_PRESSUREDATA = 0xF7;
static const uint8_t BME280_REGISTER_TEMPDATA = 0xFA; static const uint8_t BME280_REGISTER_TEMPDATA = 0xFA;
static const uint8_t BME280_REGISTER_HUMIDDATA = 0xFD; static const uint8_t BME280_REGISTER_HUMIDDATA = 0xFD;
@ -178,23 +179,29 @@ void BME280Component::update() {
return; return;
} }
float meas_time = 1.5; float meas_time = 1.5f;
meas_time += 2.3f * oversampling_to_time(this->temperature_oversampling_); meas_time += 2.3f * oversampling_to_time(this->temperature_oversampling_);
meas_time += 2.3f * oversampling_to_time(this->pressure_oversampling_) + 0.575f; meas_time += 2.3f * oversampling_to_time(this->pressure_oversampling_) + 0.575f;
meas_time += 2.3f * oversampling_to_time(this->humidity_oversampling_) + 0.575f; meas_time += 2.3f * oversampling_to_time(this->humidity_oversampling_) + 0.575f;
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() { this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
uint8_t data[8];
if (!this->read_bytes(BME280_REGISTER_MEASUREMENTS, data, 8)) {
ESP_LOGW(TAG, "Error reading registers.");
this->status_set_warning();
return;
}
int32_t t_fine = 0; int32_t t_fine = 0;
float temperature = this->read_temperature_(&t_fine); float temperature = this->read_temperature_(data, &t_fine);
if (std::isnan(temperature)) { if (std::isnan(temperature)) {
ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values."); ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values.");
this->status_set_warning(); this->status_set_warning();
return; return;
} }
float pressure = this->read_pressure_(t_fine); float pressure = this->read_pressure_(data, t_fine);
float humidity = this->read_humidity_(t_fine); float humidity = this->read_humidity_(data, t_fine);
ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity); ESP_LOGV(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
if (this->temperature_sensor_ != nullptr) if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature); this->temperature_sensor_->publish_state(temperature);
if (this->pressure_sensor_ != nullptr) if (this->pressure_sensor_ != nullptr)
@ -204,11 +211,8 @@ void BME280Component::update() {
this->status_clear_warning(); this->status_clear_warning();
}); });
} }
float BME280Component::read_temperature_(int32_t *t_fine) { float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
uint8_t data[3]; int32_t adc = ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
if (!this->read_bytes(BME280_REGISTER_TEMPDATA, data, 3))
return NAN;
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
adc >>= 4; adc >>= 4;
if (adc == 0x80000) if (adc == 0x80000)
// temperature was disabled // temperature was disabled
@ -226,10 +230,7 @@ float BME280Component::read_temperature_(int32_t *t_fine) {
return temperature / 100.0f; return temperature / 100.0f;
} }
float BME280Component::read_pressure_(int32_t t_fine) { float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
uint8_t data[3];
if (!this->read_bytes(BME280_REGISTER_PRESSUREDATA, data, 3))
return NAN;
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
adc >>= 4; adc >>= 4;
if (adc == 0x80000) if (adc == 0x80000)
@ -265,9 +266,9 @@ float BME280Component::read_pressure_(int32_t t_fine) {
return (p / 256.0f) / 100.0f; return (p / 256.0f) / 100.0f;
} }
float BME280Component::read_humidity_(int32_t t_fine) { float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) {
uint16_t raw_adc; uint16_t raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
if (!this->read_byte_16(BME280_REGISTER_HUMIDDATA, &raw_adc) || raw_adc == 0x8000) if (raw_adc == 0x8000)
return NAN; return NAN;
int32_t adc = raw_adc; int32_t adc = raw_adc;

View file

@ -82,11 +82,11 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice {
protected: protected:
/// Read the temperature value and store the calculated ambient temperature in t_fine. /// Read the temperature value and store the calculated ambient temperature in t_fine.
float read_temperature_(int32_t *t_fine); float read_temperature_(const uint8_t *data, int32_t *t_fine);
/// Read the pressure value in hPa using the provided t_fine value. /// Read the pressure value in hPa using the provided t_fine value.
float read_pressure_(int32_t t_fine); float read_pressure_(const uint8_t *data, int32_t t_fine);
/// Read the humidity value in % using the provided t_fine value. /// Read the humidity value in % using the provided t_fine value.
float read_humidity_(int32_t t_fine); float read_humidity_(const uint8_t *data, int32_t t_fine);
uint8_t read_u8_(uint8_t a_register); uint8_t read_u8_(uint8_t a_register);
uint16_t read_u16_le_(uint8_t a_register); uint16_t read_u16_le_(uint8_t a_register);
int16_t read_s16_le_(uint8_t a_register); int16_t read_s16_le_(uint8_t a_register);

View file

@ -44,7 +44,8 @@ CONFIG_SCHEMA = cv.Schema(
cv.Optional( cv.Optional(
CONF_STATE_SAVE_INTERVAL, default="6hours" CONF_STATE_SAVE_INTERVAL, default="6hours"
): cv.positive_time_period_minutes, ): cv.positive_time_period_minutes,
} },
cv.only_with_arduino,
).extend(i2c.i2c_device_schema(0x76)) ).extend(i2c.i2c_device_schema(0x76))
@ -60,5 +61,8 @@ async 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)
) )
# Although this component does not use SPI, the BSEC library requires the SPI library
cg.add_library("SPI", None)
cg.add_define("USE_BSEC") cg.add_define("USE_BSEC")
cg.add_library("BSEC Software Library", "1.6.1480") cg.add_library("boschsensortec/BSEC Software Library", "1.6.1480")

View file

@ -5,6 +5,7 @@
#include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/text_sensor/text_sensor.h"
#include "esphome/components/i2c/i2c.h" #include "esphome/components/i2c/i2c.h"
#include "esphome/core/preferences.h" #include "esphome/core/preferences.h"
#include "esphome/core/defines.h"
#include <map> #include <map>
#ifdef USE_BSEC #ifdef USE_BSEC

View file

View file

@ -0,0 +1,388 @@
/*
based on BMP388_DEV by Martin Lindupp
under MIT License (MIT)
Copyright (C) Martin Lindupp 2020
http://github.com/MartinL1/BMP388_DEV
*/
#include "bmp3xx.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace bmp3xx {
static const char *const TAG = "bmp3xx.sensor";
static const LogString *chip_type_to_str(uint8_t chip_type) {
switch (chip_type) {
case BMP388_ID:
return LOG_STR("BMP 388");
case BMP390_ID:
return LOG_STR("BMP 390");
default:
return LOG_STR("Unknown Chip Type");
}
}
static const LogString *oversampling_to_str(Oversampling oversampling) {
switch (oversampling) {
case Oversampling::OVERSAMPLING_NONE:
return LOG_STR("None");
case Oversampling::OVERSAMPLING_X2:
return LOG_STR("2x");
case Oversampling::OVERSAMPLING_X4:
return LOG_STR("4x");
case Oversampling::OVERSAMPLING_X8:
return LOG_STR("8x");
case Oversampling::OVERSAMPLING_X16:
return LOG_STR("16x");
case Oversampling::OVERSAMPLING_X32:
return LOG_STR("32x");
default:
return LOG_STR("");
}
}
static const LogString *iir_filter_to_str(IIRFilter filter) {
switch (filter) {
case IIRFilter::IIR_FILTER_OFF:
return LOG_STR("OFF");
case IIRFilter::IIR_FILTER_2:
return LOG_STR("2x");
case IIRFilter::IIR_FILTER_4:
return LOG_STR("4x");
case IIRFilter::IIR_FILTER_8:
return LOG_STR("8x");
case IIRFilter::IIR_FILTER_16:
return LOG_STR("16x");
case IIRFilter::IIR_FILTER_32:
return LOG_STR("32x");
case IIRFilter::IIR_FILTER_64:
return LOG_STR("64x");
case IIRFilter::IIR_FILTER_128:
return LOG_STR("128x");
default:
return LOG_STR("");
}
}
void BMP3XXComponent::setup() {
this->error_code_ = NONE;
ESP_LOGCONFIG(TAG, "Setting up BMP3XX...");
// Call the Device base class "initialise" function
if (!reset()) {
ESP_LOGE(TAG, "Failed to reset BMP3XX...");
this->error_code_ = ERROR_SENSOR_RESET;
this->mark_failed();
}
if (!read_byte(BMP388_CHIP_ID, &this->chip_id_.reg)) {
ESP_LOGE(TAG, "Can't read chip id");
this->error_code_ = ERROR_COMMUNICATION_FAILED;
this->mark_failed();
return;
}
ESP_LOGCONFIG(TAG, "Chip %s Id 0x%X", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
if (chip_id_.reg != BMP388_ID && chip_id_.reg != BMP390_ID) {
ESP_LOGE(TAG, "Unknown chip id - is this really a BMP388 or BMP390?");
this->error_code_ = ERROR_WRONG_CHIP_ID;
this->mark_failed();
return;
}
// set sensor in sleep mode
stop_conversion();
// Read the calibration parameters into the params structure
if (!read_bytes(BMP388_TRIM_PARAMS, (uint8_t *) &compensation_params_, sizeof(compensation_params_))) {
ESP_LOGE(TAG, "Can't read calibration data");
this->error_code_ = ERROR_COMMUNICATION_FAILED;
this->mark_failed();
return;
}
compensation_float_params_.param_T1 =
(float) compensation_params_.param_T1 / powf(2.0f, -8.0f); // Calculate the floating point trim parameters
compensation_float_params_.param_T2 = (float) compensation_params_.param_T2 / powf(2.0f, 30.0f);
compensation_float_params_.param_T3 = (float) compensation_params_.param_T3 / powf(2.0f, 48.0f);
compensation_float_params_.param_P1 = ((float) compensation_params_.param_P1 - powf(2.0f, 14.0f)) / powf(2.0f, 20.0f);
compensation_float_params_.param_P2 = ((float) compensation_params_.param_P2 - powf(2.0f, 14.0f)) / powf(2.0f, 29.0f);
compensation_float_params_.param_P3 = (float) compensation_params_.param_P3 / powf(2.0f, 32.0f);
compensation_float_params_.param_P4 = (float) compensation_params_.param_P4 / powf(2.0f, 37.0f);
compensation_float_params_.param_P5 = (float) compensation_params_.param_P5 / powf(2.0f, -3.0f);
compensation_float_params_.param_P6 = (float) compensation_params_.param_P6 / powf(2.0f, 6.0f);
compensation_float_params_.param_P7 = (float) compensation_params_.param_P7 / powf(2.0f, 8.0f);
compensation_float_params_.param_P8 = (float) compensation_params_.param_P8 / powf(2.0f, 15.0f);
compensation_float_params_.param_P9 = (float) compensation_params_.param_P9 / powf(2.0f, 48.0f);
compensation_float_params_.param_P10 = (float) compensation_params_.param_P10 / powf(2.0f, 48.0f);
compensation_float_params_.param_P11 = (float) compensation_params_.param_P11 / powf(2.0f, 65.0f);
// Initialise the BMP388 IIR filter register
if (!set_iir_filter(this->iir_filter_)) {
ESP_LOGE(TAG, "Failed to set IIR filter");
this->error_code_ = ERROR_COMMUNICATION_FAILED;
this->mark_failed();
return;
}
// Set power control registers
pwr_ctrl_.bit.press_en = 1;
pwr_ctrl_.bit.temp_en = 1;
// Disable pressure if no sensor defined
// keep temperature enabled since it's needed for compensation
if (this->pressure_sensor_ == nullptr) {
pwr_ctrl_.bit.press_en = 0;
this->pressure_oversampling_ = OVERSAMPLING_NONE;
}
// just disable oeversampling for temp if not used
if (this->temperature_sensor_ == nullptr) {
this->temperature_oversampling_ = OVERSAMPLING_NONE;
}
// Initialise the BMP388 oversampling register
if (!set_oversampling_register(this->pressure_oversampling_, this->temperature_oversampling_)) {
ESP_LOGE(TAG, "Failed to set oversampling register");
this->error_code_ = ERROR_COMMUNICATION_FAILED;
this->mark_failed();
return;
}
}
void BMP3XXComponent::dump_config() {
ESP_LOGCONFIG(TAG, "BMP3XX:");
ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
LOG_I2C_DEVICE(this);
switch (this->error_code_) {
case NONE:
break;
case ERROR_COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with BMP3XX failed!");
break;
case ERROR_WRONG_CHIP_ID:
ESP_LOGE(
TAG,
"BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390",
this->chip_id_.reg);
break;
case ERROR_SENSOR_RESET:
ESP_LOGE(TAG, "BMP3XX failed to reset");
break;
default:
ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_);
break;
}
ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_)));
LOG_UPDATE_INTERVAL(this);
if (this->temperature_sensor_) {
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
}
if (this->pressure_sensor_) {
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
}
}
float BMP3XXComponent::get_setup_priority() const { return setup_priority::DATA; }
inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }
void BMP3XXComponent::update() {
// Enable sensor
ESP_LOGV(TAG, "Sending conversion request...");
float meas_time = 1.0f;
// Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2
meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f;
meas_time += 2.02f * oversampling_to_time(this->pressure_oversampling_) + 0.392f;
meas_time += 0.234f;
if (!set_mode(FORCED_MODE)) {
ESP_LOGE(TAG, "Failed start forced mode");
this->mark_failed();
return;
}
ESP_LOGVV(TAG, "measurement time %d", uint32_t(ceilf(meas_time)));
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
float temperature = 0.0f;
float pressure = 0.0f;
if (this->pressure_sensor_ != nullptr) {
if (!get_measurements(temperature, pressure)) {
ESP_LOGW(TAG, "Failed to read pressure and temperature - skipping update");
this->status_set_warning();
return;
}
ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa", temperature, pressure);
} else {
if (!get_temperature(temperature)) {
ESP_LOGW(TAG, "Failed to read temperature - skipping update");
this->status_set_warning();
return;
}
ESP_LOGD(TAG, "Got temperature=%.1f°C", temperature);
}
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature);
if (this->pressure_sensor_ != nullptr)
this->pressure_sensor_->publish_state(pressure);
this->status_clear_warning();
set_mode(SLEEP_MODE);
});
}
// Reset the BMP3XX
uint8_t BMP3XXComponent::reset() {
write_byte(BMP388_CMD, RESET_CODE); // Write the reset code to the command register
// Wait for 10ms
delay(10);
this->read_byte(BMP388_EVENT, &event_.reg); // Read the BMP388's event register
return event_.bit.por_detected; // Return if device reset is complete
}
// Start a one shot measurement in FORCED_MODE
bool BMP3XXComponent::start_forced_conversion() {
// Only set FORCED_MODE if we're already in SLEEP_MODE
if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
return set_mode(FORCED_MODE);
}
return true;
}
// Stop the conversion and return to SLEEP_MODE
bool BMP3XXComponent::stop_conversion() { return set_mode(SLEEP_MODE); }
// Set the pressure oversampling rate
bool BMP3XXComponent::set_pressure_oversampling(Oversampling oversampling) {
osr_.bit.osr_p = oversampling;
return this->write_byte(BMP388_OSR, osr_.reg);
}
// Set the temperature oversampling rate
bool BMP3XXComponent::set_temperature_oversampling(Oversampling oversampling) {
osr_.bit.osr_t = oversampling;
return this->write_byte(BMP388_OSR, osr_.reg);
}
// Set the IIR filter setting
bool BMP3XXComponent::set_iir_filter(IIRFilter iir_filter) {
config_.bit.iir_filter = iir_filter;
return this->write_byte(BMP388_CONFIG, config_.reg);
}
// Get temperature
bool BMP3XXComponent::get_temperature(float &temperature) {
// Check if a measurement is ready
if (!data_ready()) {
return false;
}
uint8_t data[3];
// Read the temperature
if (!this->read_bytes(BMP388_DATA_3, &data[0], 3)) {
ESP_LOGE(TAG, "Failed to read temperature");
return false;
}
// Copy the temperature data into the adc variables
int32_t adc_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
// Temperature compensation (function from BMP388 datasheet)
temperature = bmp388_compensate_temperature_((float) adc_temp);
return true;
}
// Get the pressure
bool BMP3XXComponent::get_pressure(float &pressure) {
float temperature;
return get_measurements(temperature, pressure);
}
// Get temperature and pressure
bool BMP3XXComponent::get_measurements(float &temperature, float &pressure) {
// Check if a measurement is ready
if (!data_ready()) {
ESP_LOGD(TAG, "BMP3XX Get measurement - data not ready skipping update");
return false;
}
uint8_t data[6];
// Read the temperature and pressure data
if (!this->read_bytes(BMP388_DATA_0, &data[0], 6)) {
ESP_LOGE(TAG, "Failed to read measurements");
return false;
}
// Copy the temperature and pressure data into the adc variables
int32_t adc_pres = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
int32_t adc_temp = (int32_t) data[5] << 16 | (int32_t) data[4] << 8 | (int32_t) data[3];
// Temperature compensation (function from BMP388 datasheet)
temperature = bmp388_compensate_temperature_((float) adc_temp);
// Pressure compensation (function from BMP388 datasheet)
pressure = bmp388_compensate_pressure_((float) adc_pres, temperature);
// Calculate the pressure in millibar/hPa
pressure /= 100.0f;
return true;
}
// Set the BMP388's mode in the power control register
bool BMP3XXComponent::set_mode(OperationMode mode) {
pwr_ctrl_.bit.mode = mode;
return this->write_byte(BMP388_PWR_CTRL, pwr_ctrl_.reg);
}
// Set the BMP388 oversampling register
bool BMP3XXComponent::set_oversampling_register(Oversampling pressure_oversampling,
Oversampling temperature_oversampling) {
osr_.reg = temperature_oversampling << 3 | pressure_oversampling;
return this->write_byte(BMP388_OSR, osr_.reg);
}
// Check if measurement data is ready
bool BMP3XXComponent::data_ready() {
// If we're in SLEEP_MODE return immediately
if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
ESP_LOGD(TAG, "Not ready - sensor is in sleep mode");
return false;
}
// Read the interrupt status register
uint8_t status;
if (!this->read_byte(BMP388_INT_STATUS, &status)) {
ESP_LOGE(TAG, "Failed to read status register");
return false;
}
int_status_.reg = status;
ESP_LOGVV(TAG, "data ready status %d", status);
// If we're in FORCED_MODE switch back to SLEEP_MODE
if (int_status_.bit.drdy) {
if (pwr_ctrl_.bit.mode == FORCED_MODE) {
pwr_ctrl_.bit.mode = SLEEP_MODE;
}
return true; // The measurement is ready
}
return false; // The measurement is still pending
}
////////////////////////////////////////////////////////////////////////////////
// Bosch BMP3XXComponent (Private) Member Functions
////////////////////////////////////////////////////////////////////////////////
float BMP3XXComponent::bmp388_compensate_temperature_(float uncomp_temp) {
float partial_data1 = uncomp_temp - compensation_float_params_.param_T1;
float partial_data2 = partial_data1 * compensation_float_params_.param_T2;
return partial_data2 + partial_data1 * partial_data1 * compensation_float_params_.param_T3;
}
float BMP3XXComponent::bmp388_compensate_pressure_(float uncomp_press, float t_lin) {
float partial_data1 = compensation_float_params_.param_P6 * t_lin;
float partial_data2 = compensation_float_params_.param_P7 * t_lin * t_lin;
float partial_data3 = compensation_float_params_.param_P8 * t_lin * t_lin * t_lin;
float partial_out1 = compensation_float_params_.param_P5 + partial_data1 + partial_data2 + partial_data3;
partial_data1 = compensation_float_params_.param_P2 * t_lin;
partial_data2 = compensation_float_params_.param_P3 * t_lin * t_lin;
partial_data3 = compensation_float_params_.param_P4 * t_lin * t_lin * t_lin;
float partial_out2 =
uncomp_press * (compensation_float_params_.param_P1 + partial_data1 + partial_data2 + partial_data3);
partial_data1 = uncomp_press * uncomp_press;
partial_data2 = compensation_float_params_.param_P9 + compensation_float_params_.param_P10 * t_lin;
partial_data3 = partial_data1 * partial_data2;
float partial_data4 =
partial_data3 + uncomp_press * uncomp_press * uncomp_press * compensation_float_params_.param_P11;
return partial_out1 + partial_out2 + partial_data4;
}
} // namespace bmp3xx
} // namespace esphome

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