mirror of
https://github.com/esphome/esphome.git
synced 2026-05-27 20:37:44 +08:00
Merge pull request #9394 from esphome/bump-2025.7.0b1
CI for docker images / Build docker containers (docker, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (docker, ubuntu-24.04-arm) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04-arm) (push) Has been cancelled
CI / Create common environment (push) Has been cancelled
YAML lint / yamllint (push) Has been cancelled
CI / Check ruff (push) Has been cancelled
CI / Check flake8 (push) Has been cancelled
CI / Check pylint (push) Has been cancelled
CI / Check pyupgrade (push) Has been cancelled
CI / Run script/ci-custom (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.10) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.12) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.13) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.11) (push) Has been cancelled
CI / Run integration tests (push) Has been cancelled
CI / Check clang-format (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF (push) Has been cancelled
CI / Run script/clang-tidy for ESP8266 (push) Has been cancelled
CI / Run script/clang-tidy for ZEPHYR (push) Has been cancelled
CI / list-components (push) Has been cancelled
CI / Component test ${{ matrix.file }} (push) Has been cancelled
CI / Split components for testing into 20 groups maximum (push) Has been cancelled
CI / Test split components (push) Has been cancelled
CI / CI Status (push) Has been cancelled
CI for docker images / Build docker containers (docker, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (docker, ubuntu-24.04-arm) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04) (push) Has been cancelled
CI for docker images / Build docker containers (ha-addon, ubuntu-24.04-arm) (push) Has been cancelled
CI / Create common environment (push) Has been cancelled
YAML lint / yamllint (push) Has been cancelled
CI / Check ruff (push) Has been cancelled
CI / Check flake8 (push) Has been cancelled
CI / Check pylint (push) Has been cancelled
CI / Check pyupgrade (push) Has been cancelled
CI / Run script/ci-custom (push) Has been cancelled
CI / Run pytest (macOS-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.10) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.11) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.12) (push) Has been cancelled
CI / Run pytest (ubuntu-latest, 3.13) (push) Has been cancelled
CI / Run pytest (windows-latest, 3.11) (push) Has been cancelled
CI / Run integration tests (push) Has been cancelled
CI / Check clang-format (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 1/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 2/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 3/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 Arduino 4/4 (push) Has been cancelled
CI / Run script/clang-tidy for ESP32 IDF (push) Has been cancelled
CI / Run script/clang-tidy for ESP8266 (push) Has been cancelled
CI / Run script/clang-tidy for ZEPHYR (push) Has been cancelled
CI / list-components (push) Has been cancelled
CI / Component test ${{ matrix.file }} (push) Has been cancelled
CI / Split components for testing into 20 groups maximum (push) Has been cancelled
CI / Test split components (push) Has been cancelled
CI / CI Status (push) Has been cancelled
2025.7.0b1
This commit is contained in:
@@ -49,7 +49,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.11.1
|
||||||
|
|
||||||
- name: Set TAG
|
- name: Set TAG
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -214,17 +214,51 @@ jobs:
|
|||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
./venv/Scripts/activate
|
./venv/Scripts/activate
|
||||||
pytest -vv --cov-report=xml --tb=native -n auto tests
|
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/
|
||||||
- name: Run pytest
|
- name: Run pytest
|
||||||
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest'
|
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest'
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pytest -vv --cov-report=xml --tb=native -n auto tests
|
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v5.4.3
|
uses: codecov/codecov-action@v5.4.3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
|
integration-tests:
|
||||||
|
name: Run integration tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- common
|
||||||
|
steps:
|
||||||
|
- name: Check out code from GitHub
|
||||||
|
uses: actions/checkout@v4.2.2
|
||||||
|
- name: Set up Python 3.13
|
||||||
|
id: python
|
||||||
|
uses: actions/setup-python@v5.6.0
|
||||||
|
with:
|
||||||
|
python-version: "3.13"
|
||||||
|
- name: Restore Python virtual environment
|
||||||
|
id: cache-venv
|
||||||
|
uses: actions/cache@v4.2.3
|
||||||
|
with:
|
||||||
|
path: venv
|
||||||
|
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
|
||||||
|
- name: Create Python virtual environment
|
||||||
|
if: steps.cache-venv.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
python -m venv venv
|
||||||
|
. venv/bin/activate
|
||||||
|
python --version
|
||||||
|
pip install -r requirements.txt -r requirements_test.txt
|
||||||
|
pip install -e .
|
||||||
|
- name: Register matcher
|
||||||
|
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||||
|
- name: Run integration tests
|
||||||
|
run: |
|
||||||
|
. venv/bin/activate
|
||||||
|
pytest -vv --no-cov --tb=native -n auto tests/integration/
|
||||||
|
|
||||||
clang-format:
|
clang-format:
|
||||||
name: Check clang-format
|
name: Check clang-format
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@@ -494,6 +528,7 @@ jobs:
|
|||||||
- flake8
|
- flake8
|
||||||
- pylint
|
- pylint
|
||||||
- pytest
|
- pytest
|
||||||
|
- integration-tests
|
||||||
- pyupgrade
|
- pyupgrade
|
||||||
- clang-tidy
|
- clang-tidy
|
||||||
- list-components
|
- list-components
|
||||||
|
|||||||
@@ -1,28 +1,11 @@
|
|||||||
---
|
---
|
||||||
name: Lock
|
name: Lock closed issues and PRs
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "30 0 * * *"
|
- cron: "30 0 * * *" # Run daily at 00:30 UTC
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: lock
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
uses: esphome/workflows/.github/workflows/lock.yml@main
|
||||||
steps:
|
|
||||||
- uses: dessant/lock-threads@v5.0.1
|
|
||||||
with:
|
|
||||||
pr-inactive-days: "1"
|
|
||||||
pr-lock-reason: ""
|
|
||||||
exclude-any-pr-labels: keep-open
|
|
||||||
|
|
||||||
issue-inactive-days: "7"
|
|
||||||
issue-lock-reason: ""
|
|
||||||
exclude-any-issue-labels: keep-open
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ jobs:
|
|||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.11.1
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v3.4.0
|
uses: docker/login-action@v3.4.0
|
||||||
@@ -178,7 +178,7 @@ jobs:
|
|||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.11.1
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
if: matrix.registry == 'dockerhub'
|
if: matrix.registry == 'dockerhub'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.11.10
|
rev: v0.12.2
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff
|
- id: ruff
|
||||||
@@ -12,7 +12,7 @@ repos:
|
|||||||
# Run the formatter.
|
# Run the formatter.
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 7.2.0
|
rev: 7.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
|
|||||||
+12
@@ -87,6 +87,7 @@ esphome/components/bp1658cj/* @Cossid
|
|||||||
esphome/components/bp5758d/* @Cossid
|
esphome/components/bp5758d/* @Cossid
|
||||||
esphome/components/button/* @esphome/core
|
esphome/components/button/* @esphome/core
|
||||||
esphome/components/bytebuffer/* @clydebarrow
|
esphome/components/bytebuffer/* @clydebarrow
|
||||||
|
esphome/components/camera/* @DT-art1 @bdraco
|
||||||
esphome/components/canbus/* @danielschramm @mvturnho
|
esphome/components/canbus/* @danielschramm @mvturnho
|
||||||
esphome/components/cap1188/* @mreditor97
|
esphome/components/cap1188/* @mreditor97
|
||||||
esphome/components/captive_portal/* @OttoWinter
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
@@ -124,6 +125,7 @@ esphome/components/dht/* @OttoWinter
|
|||||||
esphome/components/display_menu_base/* @numo68
|
esphome/components/display_menu_base/* @numo68
|
||||||
esphome/components/dps310/* @kbx81
|
esphome/components/dps310/* @kbx81
|
||||||
esphome/components/ds1307/* @badbadc0ffee
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
|
esphome/components/ds2484/* @mrk-its
|
||||||
esphome/components/dsmr/* @glmnet @zuidwijk
|
esphome/components/dsmr/* @glmnet @zuidwijk
|
||||||
esphome/components/duty_time/* @dudanov
|
esphome/components/duty_time/* @dudanov
|
||||||
esphome/components/ee895/* @Stock-M
|
esphome/components/ee895/* @Stock-M
|
||||||
@@ -146,6 +148,7 @@ esphome/components/esp32_ble_client/* @jesserockz
|
|||||||
esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
|
esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
|
||||||
esphome/components/esp32_camera_web_server/* @ayufan
|
esphome/components/esp32_camera_web_server/* @ayufan
|
||||||
esphome/components/esp32_can/* @Sympatron
|
esphome/components/esp32_can/* @Sympatron
|
||||||
|
esphome/components/esp32_hosted/* @swoboda1337
|
||||||
esphome/components/esp32_improv/* @jesserockz
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
esphome/components/esp32_rmt/* @jesserockz
|
esphome/components/esp32_rmt/* @jesserockz
|
||||||
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
esphome/components/esp32_rmt_led_strip/* @jesserockz
|
||||||
@@ -167,6 +170,7 @@ esphome/components/ft5x06/* @clydebarrow
|
|||||||
esphome/components/ft63x6/* @gpambrozio
|
esphome/components/ft63x6/* @gpambrozio
|
||||||
esphome/components/gcja5/* @gcormier
|
esphome/components/gcja5/* @gcormier
|
||||||
esphome/components/gdk101/* @Szewcson
|
esphome/components/gdk101/* @Szewcson
|
||||||
|
esphome/components/gl_r01_i2c/* @pkejval
|
||||||
esphome/components/globals/* @esphome/core
|
esphome/components/globals/* @esphome/core
|
||||||
esphome/components/gp2y1010au0f/* @zry98
|
esphome/components/gp2y1010au0f/* @zry98
|
||||||
esphome/components/gp8403/* @jesserockz
|
esphome/components/gp8403/* @jesserockz
|
||||||
@@ -247,9 +251,11 @@ esphome/components/libretiny_pwm/* @kuba2k2
|
|||||||
esphome/components/light/* @esphome/core
|
esphome/components/light/* @esphome/core
|
||||||
esphome/components/lightwaverf/* @max246
|
esphome/components/lightwaverf/* @max246
|
||||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||||
|
esphome/components/ln882x/* @lamauny
|
||||||
esphome/components/lock/* @esphome/core
|
esphome/components/lock/* @esphome/core
|
||||||
esphome/components/logger/* @esphome/core
|
esphome/components/logger/* @esphome/core
|
||||||
esphome/components/logger/select/* @clydebarrow
|
esphome/components/logger/select/* @clydebarrow
|
||||||
|
esphome/components/lps22/* @nagisa
|
||||||
esphome/components/ltr390/* @latonita @sjtrny
|
esphome/components/ltr390/* @latonita @sjtrny
|
||||||
esphome/components/ltr501/* @latonita
|
esphome/components/ltr501/* @latonita
|
||||||
esphome/components/ltr_als_ps/* @latonita
|
esphome/components/ltr_als_ps/* @latonita
|
||||||
@@ -323,6 +329,7 @@ esphome/components/one_wire/* @ssieb
|
|||||||
esphome/components/online_image/* @clydebarrow @guillempages
|
esphome/components/online_image/* @clydebarrow @guillempages
|
||||||
esphome/components/opentherm/* @olegtarasov
|
esphome/components/opentherm/* @olegtarasov
|
||||||
esphome/components/openthread/* @mrene
|
esphome/components/openthread/* @mrene
|
||||||
|
esphome/components/opt3001/* @ccutrer
|
||||||
esphome/components/ota/* @esphome/core
|
esphome/components/ota/* @esphome/core
|
||||||
esphome/components/output/* @esphome/core
|
esphome/components/output/* @esphome/core
|
||||||
esphome/components/packet_transport/* @clydebarrow
|
esphome/components/packet_transport/* @clydebarrow
|
||||||
@@ -330,6 +337,7 @@ esphome/components/pca6416a/* @Mat931
|
|||||||
esphome/components/pca9554/* @clydebarrow @hwstar
|
esphome/components/pca9554/* @clydebarrow @hwstar
|
||||||
esphome/components/pcf85063/* @brogon
|
esphome/components/pcf85063/* @brogon
|
||||||
esphome/components/pcf8563/* @KoenBreeman
|
esphome/components/pcf8563/* @KoenBreeman
|
||||||
|
esphome/components/pi4ioe5v6408/* @jesserockz
|
||||||
esphome/components/pid/* @OttoWinter
|
esphome/components/pid/* @OttoWinter
|
||||||
esphome/components/pipsolar/* @andreashergert1984
|
esphome/components/pipsolar/* @andreashergert1984
|
||||||
esphome/components/pm1006/* @habbie
|
esphome/components/pm1006/* @habbie
|
||||||
@@ -436,6 +444,8 @@ esphome/components/sun/* @OttoWinter
|
|||||||
esphome/components/sun_gtil2/* @Mat931
|
esphome/components/sun_gtil2/* @Mat931
|
||||||
esphome/components/switch/* @esphome/core
|
esphome/components/switch/* @esphome/core
|
||||||
esphome/components/switch/binary_sensor/* @ssieb
|
esphome/components/switch/binary_sensor/* @ssieb
|
||||||
|
esphome/components/sx126x/* @swoboda1337
|
||||||
|
esphome/components/sx127x/* @swoboda1337
|
||||||
esphome/components/syslog/* @clydebarrow
|
esphome/components/syslog/* @clydebarrow
|
||||||
esphome/components/t6615/* @tylermenezes
|
esphome/components/t6615/* @tylermenezes
|
||||||
esphome/components/tc74/* @sethgirvan
|
esphome/components/tc74/* @sethgirvan
|
||||||
@@ -494,6 +504,7 @@ esphome/components/voice_assistant/* @jesserockz @kahrendt
|
|||||||
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
|
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
|
||||||
esphome/components/watchdog/* @oarcher
|
esphome/components/watchdog/* @oarcher
|
||||||
esphome/components/waveshare_epaper/* @clydebarrow
|
esphome/components/waveshare_epaper/* @clydebarrow
|
||||||
|
esphome/components/web_server/ota/* @esphome/core
|
||||||
esphome/components/web_server_base/* @OttoWinter
|
esphome/components/web_server_base/* @OttoWinter
|
||||||
esphome/components/web_server_idf/* @dentra
|
esphome/components/web_server_idf/* @dentra
|
||||||
esphome/components/weikai/* @DrCoolZic
|
esphome/components/weikai/* @DrCoolZic
|
||||||
@@ -520,6 +531,7 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
|||||||
esphome/components/xiaomi_mhoc303/* @drug123
|
esphome/components/xiaomi_mhoc303/* @drug123
|
||||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
||||||
|
esphome/components/xiaomi_xmwsdj04mmc/* @medusalix
|
||||||
esphome/components/xl9535/* @mreditor97
|
esphome/components/xl9535/* @mreditor97
|
||||||
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
|
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
|
||||||
esphome/components/xxtea/* @clydebarrow
|
esphome/components/xxtea/* @clydebarrow
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = 2025.6.3
|
PROJECT_NUMBER = 2025.7.0b1
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
|||||||
+1
-3
@@ -34,11 +34,9 @@ from esphome.const import (
|
|||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_SUBSTITUTIONS,
|
CONF_SUBSTITUTIONS,
|
||||||
CONF_TOPIC,
|
CONF_TOPIC,
|
||||||
PLATFORM_BK72XX,
|
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
PLATFORM_RTL87XX,
|
|
||||||
SECRETS_FILES,
|
SECRETS_FILES,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, EsphomeError, coroutine
|
from esphome.core import CORE, EsphomeError, coroutine
|
||||||
@@ -354,7 +352,7 @@ def upload_program(config, args, host):
|
|||||||
if CORE.target_platform in (PLATFORM_RP2040):
|
if CORE.target_platform in (PLATFORM_RP2040):
|
||||||
return upload_using_platformio(config, args.device)
|
return upload_using_platformio(config, args.device)
|
||||||
|
|
||||||
if CORE.target_platform in (PLATFORM_BK72XX, PLATFORM_RTL87XX):
|
if CORE.is_libretiny:
|
||||||
return upload_using_platformio(config, host)
|
return upload_using_platformio(config, host)
|
||||||
|
|
||||||
return 1 # Unknown target platform
|
return 1 # Unknown target platform
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
|||||||
TemplateArguments,
|
TemplateArguments,
|
||||||
add,
|
add,
|
||||||
add_build_flag,
|
add_build_flag,
|
||||||
|
add_build_unflag,
|
||||||
add_define,
|
add_define,
|
||||||
add_global,
|
add_global,
|
||||||
add_library,
|
add_library,
|
||||||
@@ -34,6 +35,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
|||||||
process_lambda,
|
process_lambda,
|
||||||
progmem_array,
|
progmem_array,
|
||||||
safe_exp,
|
safe_exp,
|
||||||
|
set_cpp_standard,
|
||||||
statement,
|
statement,
|
||||||
static_const_array,
|
static_const_array,
|
||||||
templatable,
|
templatable,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <numbers>
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#include <core_esp8266_waveform.h>
|
#include <core_esp8266_waveform.h>
|
||||||
@@ -193,18 +194,17 @@ void AcDimmer::setup() {
|
|||||||
setTimer1Callback(&timer_interrupt);
|
setTimer1Callback(&timer_interrupt);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
// 80 Divider -> 1 count=1µs
|
// timer frequency of 1mhz
|
||||||
dimmer_timer = timerBegin(0, 80, true);
|
dimmer_timer = timerBegin(1000000);
|
||||||
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr);
|
||||||
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
||||||
// are not callable from ISR (placed in flash storage).
|
// are not callable from ISR (placed in flash storage).
|
||||||
// Here we just use an interrupt firing every 50 µs.
|
// Here we just use an interrupt firing every 50 µs.
|
||||||
timerAlarmWrite(dimmer_timer, 50, true);
|
timerAlarm(dimmer_timer, 50, true, 0);
|
||||||
timerAlarmEnable(dimmer_timer);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void AcDimmer::write_state(float state) {
|
void AcDimmer::write_state(float state) {
|
||||||
state = std::acos(1 - (2 * state)) / 3.14159; // RMS power compensation
|
state = std::acos(1 - (2 * state)) / std::numbers::pi; // RMS power compensation
|
||||||
auto new_value = static_cast<uint16_t>(roundf(state * 65535));
|
auto new_value = static_cast<uint16_t>(roundf(state * 65535));
|
||||||
if (new_value != 0 && this->store_.value == 0)
|
if (new_value != 0 && this->store_.value == 0)
|
||||||
this->store_.init_cycle = this->init_with_half_cycle_;
|
this->store_.init_cycle = this->init_with_half_cycle_;
|
||||||
|
|||||||
@@ -10,8 +10,15 @@ from esphome.components.esp32.const import (
|
|||||||
VARIANT_ESP32S2,
|
VARIANT_ESP32S2,
|
||||||
VARIANT_ESP32S3,
|
VARIANT_ESP32S3,
|
||||||
)
|
)
|
||||||
|
from esphome.config_helpers import filter_source_files_from_platform
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266
|
from esphome.const import (
|
||||||
|
CONF_ANALOG,
|
||||||
|
CONF_INPUT,
|
||||||
|
CONF_NUMBER,
|
||||||
|
PLATFORM_ESP8266,
|
||||||
|
PlatformFramework,
|
||||||
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
@@ -229,3 +236,20 @@ def validate_adc_pin(value):
|
|||||||
)(value)
|
)(value)
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||||
|
{
|
||||||
|
"adc_sensor_esp32.cpp": {
|
||||||
|
PlatformFramework.ESP32_ARDUINO,
|
||||||
|
PlatformFramework.ESP32_IDF,
|
||||||
|
},
|
||||||
|
"adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO},
|
||||||
|
"adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO},
|
||||||
|
"adc_sensor_libretiny.cpp": {
|
||||||
|
PlatformFramework.BK72XX_ARDUINO,
|
||||||
|
PlatformFramework.RTL87XX_ARDUINO,
|
||||||
|
PlatformFramework.LN882X_ARDUINO,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ namespace adc {
|
|||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 7)) || \
|
#if (ESP_IDF_VERSION_MAJOR == 5 && \
|
||||||
(ESP_IDF_VERSION_MAJOR == 5 && \
|
|
||||||
((ESP_IDF_VERSION_MINOR == 0 && ESP_IDF_VERSION_PATCH >= 5) || \
|
((ESP_IDF_VERSION_MINOR == 0 && ESP_IDF_VERSION_PATCH >= 5) || \
|
||||||
(ESP_IDF_VERSION_MINOR == 1 && ESP_IDF_VERSION_PATCH >= 3) || \
|
(ESP_IDF_VERSION_MINOR == 1 && ESP_IDF_VERSION_PATCH >= 3) || \
|
||||||
(ESP_IDF_VERSION_MINOR >= 2)) \
|
(ESP_IDF_VERSION_MINOR >= 2)) \
|
||||||
@@ -28,19 +27,24 @@ static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_11;
|
|||||||
#endif
|
#endif
|
||||||
#endif // USE_ESP32
|
#endif // USE_ESP32
|
||||||
|
|
||||||
enum class SamplingMode : uint8_t { AVG = 0, MIN = 1, MAX = 2 };
|
enum class SamplingMode : uint8_t {
|
||||||
|
AVG = 0,
|
||||||
|
MIN = 1,
|
||||||
|
MAX = 2,
|
||||||
|
};
|
||||||
|
|
||||||
const LogString *sampling_mode_to_str(SamplingMode mode);
|
const LogString *sampling_mode_to_str(SamplingMode mode);
|
||||||
|
|
||||||
class Aggregator {
|
class Aggregator {
|
||||||
public:
|
public:
|
||||||
|
Aggregator(SamplingMode mode);
|
||||||
void add_sample(uint32_t value);
|
void add_sample(uint32_t value);
|
||||||
uint32_t aggregate();
|
uint32_t aggregate();
|
||||||
Aggregator(SamplingMode mode);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SamplingMode mode_{SamplingMode::AVG};
|
|
||||||
uint32_t aggr_{0};
|
uint32_t aggr_{0};
|
||||||
uint32_t samples_{0};
|
uint32_t samples_{0};
|
||||||
|
SamplingMode mode_{SamplingMode::AVG};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
||||||
@@ -81,9 +85,9 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
#endif // USE_RP2040
|
#endif // USE_RP2040
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
InternalGPIOPin *pin_;
|
|
||||||
bool output_raw_{false};
|
|
||||||
uint8_t sample_count_{1};
|
uint8_t sample_count_{1};
|
||||||
|
bool output_raw_{false};
|
||||||
|
InternalGPIOPin *pin_;
|
||||||
SamplingMode sampling_mode_{SamplingMode::AVG};
|
SamplingMode sampling_mode_{SamplingMode::AVG};
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
@@ -95,11 +99,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
|
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
|
||||||
adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
|
adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
|
||||||
bool autorange_{false};
|
bool autorange_{false};
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
|
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
|
||||||
#else
|
|
||||||
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
|
|
||||||
#endif // ESP_IDF_VERSION_MAJOR
|
|
||||||
#endif // USE_ESP32
|
#endif // USE_ESP32
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ uint32_t Aggregator::aggregate() {
|
|||||||
|
|
||||||
void ADCSensor::update() {
|
void ADCSensor::update() {
|
||||||
float value_v = this->sample();
|
float value_v = this->sample();
|
||||||
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
|
ESP_LOGV(TAG, "'%s': Voltage=%.4fV", this->get_name().c_str(), value_v);
|
||||||
this->publish_state(value_v);
|
this->publish_state(value_v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,32 +55,40 @@ void ADCSensor::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ADCSensor::dump_config() {
|
void ADCSensor::dump_config() {
|
||||||
|
static const char *const ATTEN_AUTO_STR = "auto";
|
||||||
|
static const char *const ATTEN_0DB_STR = "0 db";
|
||||||
|
static const char *const ATTEN_2_5DB_STR = "2.5 db";
|
||||||
|
static const char *const ATTEN_6DB_STR = "6 db";
|
||||||
|
static const char *const ATTEN_12DB_STR = "12 db";
|
||||||
|
const char *atten_str = ATTEN_AUTO_STR;
|
||||||
|
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
LOG_PIN(" Pin: ", this->pin_);
|
||||||
if (this->autorange_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
if (!this->autorange_) {
|
||||||
} else {
|
|
||||||
switch (this->attenuation_) {
|
switch (this->attenuation_) {
|
||||||
case ADC_ATTEN_DB_0:
|
case ADC_ATTEN_DB_0:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
|
atten_str = ATTEN_0DB_STR;
|
||||||
break;
|
break;
|
||||||
case ADC_ATTEN_DB_2_5:
|
case ADC_ATTEN_DB_2_5:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
|
atten_str = ATTEN_2_5DB_STR;
|
||||||
break;
|
break;
|
||||||
case ADC_ATTEN_DB_6:
|
case ADC_ATTEN_DB_6:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
|
atten_str = ATTEN_6DB_STR;
|
||||||
break;
|
break;
|
||||||
case ADC_ATTEN_DB_12_COMPAT:
|
case ADC_ATTEN_DB_12_COMPAT:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 12db");
|
atten_str = ATTEN_12DB_STR;
|
||||||
break;
|
break;
|
||||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
|
" Attenuation: %s\n"
|
||||||
" Samples: %i\n"
|
" Samples: %i\n"
|
||||||
" Sampling mode: %s",
|
" Sampling mode: %s",
|
||||||
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
atten_str, this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ class ADE7880 : public i2c::I2CDevice, public PollingComponent {
|
|||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ADE7880Store store_{};
|
ADE7880Store store_{};
|
||||||
InternalGPIOPin *irq0_pin_{nullptr};
|
InternalGPIOPin *irq0_pin_{nullptr};
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
|
|||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
/// HARDWARE_LATE setup priority
|
/// HARDWARE_LATE setup priority
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
|
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
|
||||||
|
|
||||||
/// Helper method to request a measurement from a sensor.
|
/// Helper method to request a measurement from a sensor.
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ class ADS1118 : public Component,
|
|||||||
ADS1118() = default;
|
ADS1118() = default;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
/// Helper method to request a measurement from a sensor.
|
/// Helper method to request a measurement from a sensor.
|
||||||
float request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain gain, bool temperature_mode);
|
float request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain gain, bool temperature_mode);
|
||||||
|
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ class AGS10Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies target address of AGS10.
|
* Modifies target address of AGS10.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ class AIC3204 : public audio_dac::AudioDac, public Component, public i2c::I2CDev
|
|||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
bool set_mute_off() override;
|
bool set_mute_off() override;
|
||||||
bool set_mute_on() override;
|
bool set_mute_on() override;
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ from esphome.const import (
|
|||||||
CONF_WEB_SERVER,
|
CONF_WEB_SERVER,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||||
from esphome.cpp_generator import MockObjClass
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.cpp_helpers import setup_entity
|
|
||||||
|
|
||||||
CODEOWNERS = ["@grahambrown11", "@hwstar"]
|
CODEOWNERS = ["@grahambrown11", "@hwstar"]
|
||||||
IS_PLATFORM_COMPONENT = True
|
IS_PLATFORM_COMPONENT = True
|
||||||
@@ -149,6 +149,9 @@ _ALARM_CONTROL_PANEL_SCHEMA = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_ALARM_CONTROL_PANEL_SCHEMA.add_extra(entity_duplicate_validator("alarm_control_panel"))
|
||||||
|
|
||||||
|
|
||||||
def alarm_control_panel_schema(
|
def alarm_control_panel_schema(
|
||||||
class_: MockObjClass,
|
class_: MockObjClass,
|
||||||
*,
|
*,
|
||||||
@@ -190,7 +193,7 @@ ALARM_CONTROL_PANEL_CONDITION_SCHEMA = maybe_simple_id(
|
|||||||
|
|
||||||
|
|
||||||
async def setup_alarm_control_panel_core_(var, config):
|
async def setup_alarm_control_panel_core_(var, config):
|
||||||
await setup_entity(var, config)
|
await setup_entity(var, config, "alarm_control_panel")
|
||||||
for conf in config.get(CONF_ON_STATE, []):
|
for conf in config.get(CONF_ON_STATE, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
await automation.build_automation(trigger, [], conf)
|
await automation.build_automation(trigger, [], conf)
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ class Alpha3 : public esphome::ble_client::BLEClientNode, public PollingComponen
|
|||||||
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;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
void set_flow_sensor(sensor::Sensor *sensor) { this->flow_sensor_ = sensor; }
|
void set_flow_sensor(sensor::Sensor *sensor) { this->flow_sensor_ = sensor; }
|
||||||
void set_head_sensor(sensor::Sensor *sensor) { this->head_sensor_ = sensor; }
|
void set_head_sensor(sensor::Sensor *sensor) { this->head_sensor_ = sensor; }
|
||||||
void set_power_sensor(sensor::Sensor *sensor) { this->power_sensor_ = sensor; }
|
void set_power_sensor(sensor::Sensor *sensor) { this->power_sensor_ = sensor; }
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class Am43Component : public cover::Cover, public esphome::ble_client::BLEClient
|
|||||||
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;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
cover::CoverTraits get_traits() override;
|
cover::CoverTraits get_traits() override;
|
||||||
void set_pin(uint16_t pin) { this->pin_ = pin; }
|
void set_pin(uint16_t pin) { this->pin_ = pin; }
|
||||||
void set_invert_position(bool invert_position) { this->invert_position_ = invert_position; }
|
void set_invert_position(bool invert_position) { this->invert_position_ = invert_position; }
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent
|
|||||||
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;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
void set_battery(sensor::Sensor *battery) { battery_ = battery; }
|
void set_battery(sensor::Sensor *battery) { battery_ = battery; }
|
||||||
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ class AnalogThresholdBinarySensor : public Component, public binary_sensor::Bina
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
void set_sensor(sensor::Sensor *analog_sensor);
|
void set_sensor(sensor::Sensor *analog_sensor);
|
||||||
template<typename T> void set_upper_threshold(T upper_threshold) { this->upper_threshold_ = upper_threshold; }
|
template<typename T> void set_upper_threshold(T upper_threshold) { this->upper_threshold_ = upper_threshold; }
|
||||||
template<typename T> void set_lower_threshold(T lower_threshold) { this->lower_threshold_ = lower_threshold; }
|
template<typename T> void set_lower_threshold(T lower_threshold) { this->lower_threshold_ = lower_threshold; }
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ void Anova::setup() {
|
|||||||
this->current_request_ = 0;
|
this->current_request_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Anova::loop() {}
|
void Anova::loop() {
|
||||||
|
// Parent BLEClientNode has a loop() method, but this component uses
|
||||||
|
// polling via update() and BLE callbacks so loop isn't needed
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
|
|
||||||
void Anova::control(const ClimateCall &call) {
|
void Anova::control(const ClimateCall &call) {
|
||||||
if (call.get_mode().has_value()) {
|
if (call.get_mode().has_value()) {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
|
|||||||
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;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
climate::ClimateTraits traits() override {
|
climate::ClimateTraits traits() override {
|
||||||
auto traits = climate::ClimateTraits();
|
auto traits = climate::ClimateTraits();
|
||||||
traits.set_supports_current_temperature(true);
|
traits.set_supports_current_temperature(true);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ void APDS9960::setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id != 0xAB && id != 0x9C && id != 0xA8) { // APDS9960 all should have one of these IDs
|
if (id != 0xAB && id != 0x9C && id != 0xA8 && id != 0x9E) { // APDS9960 all should have one of these IDs
|
||||||
this->error_code_ = WRONG_ID;
|
this->error_code_ = WRONG_ID;
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import base64
|
|||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import Condition
|
from esphome.automation import Condition
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.config_helpers import get_logger_level
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ACTION,
|
CONF_ACTION,
|
||||||
@@ -110,9 +111,10 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
): ACTIONS_SCHEMA,
|
): ACTIONS_SCHEMA,
|
||||||
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
|
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
|
||||||
cv.Optional(CONF_ENCRYPTION): _encryption_schema,
|
cv.Optional(CONF_ENCRYPTION): _encryption_schema,
|
||||||
cv.Optional(
|
cv.Optional(CONF_BATCH_DELAY, default="100ms"): cv.All(
|
||||||
CONF_BATCH_DELAY, default="100ms"
|
cv.positive_time_period_milliseconds,
|
||||||
): cv.positive_time_period_milliseconds,
|
cv.Range(max=cv.TimePeriod(milliseconds=65535)),
|
||||||
|
),
|
||||||
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
|
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
|
||||||
single=True
|
single=True
|
||||||
),
|
),
|
||||||
@@ -131,27 +133,32 @@ async def to_code(config):
|
|||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
cg.add(var.set_port(config[CONF_PORT]))
|
cg.add(var.set_port(config[CONF_PORT]))
|
||||||
cg.add(var.set_password(config[CONF_PASSWORD]))
|
if config[CONF_PASSWORD]:
|
||||||
|
cg.add_define("USE_API_PASSWORD")
|
||||||
|
cg.add(var.set_password(config[CONF_PASSWORD]))
|
||||||
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
||||||
cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))
|
cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))
|
||||||
|
|
||||||
for conf in config.get(CONF_ACTIONS, []):
|
if actions := config.get(CONF_ACTIONS, []):
|
||||||
template_args = []
|
cg.add_define("USE_API_YAML_SERVICES")
|
||||||
func_args = []
|
for conf in actions:
|
||||||
service_arg_names = []
|
template_args = []
|
||||||
for name, var_ in conf[CONF_VARIABLES].items():
|
func_args = []
|
||||||
native = SERVICE_ARG_NATIVE_TYPES[var_]
|
service_arg_names = []
|
||||||
template_args.append(native)
|
for name, var_ in conf[CONF_VARIABLES].items():
|
||||||
func_args.append((native, name))
|
native = SERVICE_ARG_NATIVE_TYPES[var_]
|
||||||
service_arg_names.append(name)
|
template_args.append(native)
|
||||||
templ = cg.TemplateArguments(*template_args)
|
func_args.append((native, name))
|
||||||
trigger = cg.new_Pvariable(
|
service_arg_names.append(name)
|
||||||
conf[CONF_TRIGGER_ID], templ, conf[CONF_ACTION], service_arg_names
|
templ = cg.TemplateArguments(*template_args)
|
||||||
)
|
trigger = cg.new_Pvariable(
|
||||||
cg.add(var.register_user_service(trigger))
|
conf[CONF_TRIGGER_ID], templ, conf[CONF_ACTION], service_arg_names
|
||||||
await automation.build_automation(trigger, func_args, conf)
|
)
|
||||||
|
cg.add(var.register_user_service(trigger))
|
||||||
|
await automation.build_automation(trigger, func_args, conf)
|
||||||
|
|
||||||
if CONF_ON_CLIENT_CONNECTED in config:
|
if CONF_ON_CLIENT_CONNECTED in config:
|
||||||
|
cg.add_define("USE_API_CLIENT_CONNECTED_TRIGGER")
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
var.get_client_connected_trigger(),
|
var.get_client_connected_trigger(),
|
||||||
[(cg.std_string, "client_info"), (cg.std_string, "client_address")],
|
[(cg.std_string, "client_info"), (cg.std_string, "client_address")],
|
||||||
@@ -159,6 +166,7 @@ async def to_code(config):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if CONF_ON_CLIENT_DISCONNECTED in config:
|
if CONF_ON_CLIENT_DISCONNECTED in config:
|
||||||
|
cg.add_define("USE_API_CLIENT_DISCONNECTED_TRIGGER")
|
||||||
await automation.build_automation(
|
await automation.build_automation(
|
||||||
var.get_client_disconnected_trigger(),
|
var.get_client_disconnected_trigger(),
|
||||||
[(cg.std_string, "client_info"), (cg.std_string, "client_address")],
|
[(cg.std_string, "client_info"), (cg.std_string, "client_address")],
|
||||||
@@ -177,7 +185,7 @@ async def to_code(config):
|
|||||||
# and plaintext disabled. Only a factory reset can remove it.
|
# and plaintext disabled. Only a factory reset can remove it.
|
||||||
cg.add_define("USE_API_PLAINTEXT")
|
cg.add_define("USE_API_PLAINTEXT")
|
||||||
cg.add_define("USE_API_NOISE")
|
cg.add_define("USE_API_NOISE")
|
||||||
cg.add_library("esphome/noise-c", "0.1.6")
|
cg.add_library("esphome/noise-c", "0.1.10")
|
||||||
else:
|
else:
|
||||||
cg.add_define("USE_API_PLAINTEXT")
|
cg.add_define("USE_API_PLAINTEXT")
|
||||||
|
|
||||||
@@ -306,3 +314,17 @@ async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, arg
|
|||||||
@automation.register_condition("api.connected", APIConnectedCondition, {})
|
@automation.register_condition("api.connected", APIConnectedCondition, {})
|
||||||
async def api_connected_to_code(config, condition_id, template_arg, args):
|
async def api_connected_to_code(config, condition_id, template_arg, args):
|
||||||
return cg.new_Pvariable(condition_id, template_arg)
|
return cg.new_Pvariable(condition_id, template_arg)
|
||||||
|
|
||||||
|
|
||||||
|
def FILTER_SOURCE_FILES() -> list[str]:
|
||||||
|
"""Filter out api_pb2_dump.cpp when proto message dumping is not enabled."""
|
||||||
|
# api_pb2_dump.cpp is only needed when HAS_PROTO_MESSAGE_DUMP is defined
|
||||||
|
# This is a particularly large file that still needs to be opened and read
|
||||||
|
# all the way to the end even when ifdef'd out
|
||||||
|
#
|
||||||
|
# HAS_PROTO_MESSAGE_DUMP is defined when ESPHOME_LOG_HAS_VERY_VERBOSE is set,
|
||||||
|
# which happens when the logger level is VERY_VERBOSE
|
||||||
|
if get_logger_level() != "VERY_VERBOSE":
|
||||||
|
return ["api_pb2_dump.cpp"]
|
||||||
|
|
||||||
|
return []
|
||||||
|
|||||||
@@ -188,6 +188,17 @@ message DeviceInfoRequest {
|
|||||||
// Empty
|
// Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message AreaInfo {
|
||||||
|
uint32 area_id = 1;
|
||||||
|
string name = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeviceInfo {
|
||||||
|
uint32 device_id = 1;
|
||||||
|
string name = 2;
|
||||||
|
uint32 area_id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message DeviceInfoResponse {
|
message DeviceInfoResponse {
|
||||||
option (id) = 10;
|
option (id) = 10;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
@@ -236,6 +247,12 @@ message DeviceInfoResponse {
|
|||||||
|
|
||||||
// Supports receiving and saving api encryption key
|
// Supports receiving and saving api encryption key
|
||||||
bool api_encryption_supported = 19;
|
bool api_encryption_supported = 19;
|
||||||
|
|
||||||
|
repeated DeviceInfo devices = 20;
|
||||||
|
repeated AreaInfo areas = 21;
|
||||||
|
|
||||||
|
// Top-level area info to phase out suggested_area
|
||||||
|
AreaInfo area = 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListEntitiesRequest {
|
message ListEntitiesRequest {
|
||||||
@@ -280,6 +297,7 @@ message ListEntitiesBinarySensorResponse {
|
|||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
string icon = 8;
|
string icon = 8;
|
||||||
EntityCategory entity_category = 9;
|
EntityCategory entity_category = 9;
|
||||||
|
uint32 device_id = 10;
|
||||||
}
|
}
|
||||||
message BinarySensorStateResponse {
|
message BinarySensorStateResponse {
|
||||||
option (id) = 21;
|
option (id) = 21;
|
||||||
@@ -293,6 +311,7 @@ message BinarySensorStateResponse {
|
|||||||
// If the binary sensor does not have a valid state yet.
|
// If the binary sensor does not have a valid state yet.
|
||||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
bool missing_state = 3;
|
bool missing_state = 3;
|
||||||
|
uint32 device_id = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== COVER ====================
|
// ==================== COVER ====================
|
||||||
@@ -315,6 +334,7 @@ message ListEntitiesCoverResponse {
|
|||||||
string icon = 10;
|
string icon = 10;
|
||||||
EntityCategory entity_category = 11;
|
EntityCategory entity_category = 11;
|
||||||
bool supports_stop = 12;
|
bool supports_stop = 12;
|
||||||
|
uint32 device_id = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LegacyCoverState {
|
enum LegacyCoverState {
|
||||||
@@ -341,6 +361,7 @@ message CoverStateResponse {
|
|||||||
float position = 3;
|
float position = 3;
|
||||||
float tilt = 4;
|
float tilt = 4;
|
||||||
CoverOperation current_operation = 5;
|
CoverOperation current_operation = 5;
|
||||||
|
uint32 device_id = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LegacyCoverCommand {
|
enum LegacyCoverCommand {
|
||||||
@@ -388,6 +409,7 @@ message ListEntitiesFanResponse {
|
|||||||
string icon = 10;
|
string icon = 10;
|
||||||
EntityCategory entity_category = 11;
|
EntityCategory entity_category = 11;
|
||||||
repeated string supported_preset_modes = 12;
|
repeated string supported_preset_modes = 12;
|
||||||
|
uint32 device_id = 13;
|
||||||
}
|
}
|
||||||
enum FanSpeed {
|
enum FanSpeed {
|
||||||
FAN_SPEED_LOW = 0;
|
FAN_SPEED_LOW = 0;
|
||||||
@@ -412,6 +434,7 @@ message FanStateResponse {
|
|||||||
FanDirection direction = 5;
|
FanDirection direction = 5;
|
||||||
int32 speed_level = 6;
|
int32 speed_level = 6;
|
||||||
string preset_mode = 7;
|
string preset_mode = 7;
|
||||||
|
uint32 device_id = 8;
|
||||||
}
|
}
|
||||||
message FanCommandRequest {
|
message FanCommandRequest {
|
||||||
option (id) = 31;
|
option (id) = 31;
|
||||||
@@ -471,6 +494,7 @@ message ListEntitiesLightResponse {
|
|||||||
bool disabled_by_default = 13;
|
bool disabled_by_default = 13;
|
||||||
string icon = 14;
|
string icon = 14;
|
||||||
EntityCategory entity_category = 15;
|
EntityCategory entity_category = 15;
|
||||||
|
uint32 device_id = 16;
|
||||||
}
|
}
|
||||||
message LightStateResponse {
|
message LightStateResponse {
|
||||||
option (id) = 24;
|
option (id) = 24;
|
||||||
@@ -492,6 +516,7 @@ message LightStateResponse {
|
|||||||
float cold_white = 12;
|
float cold_white = 12;
|
||||||
float warm_white = 13;
|
float warm_white = 13;
|
||||||
string effect = 9;
|
string effect = 9;
|
||||||
|
uint32 device_id = 14;
|
||||||
}
|
}
|
||||||
message LightCommandRequest {
|
message LightCommandRequest {
|
||||||
option (id) = 32;
|
option (id) = 32;
|
||||||
@@ -563,6 +588,7 @@ message ListEntitiesSensorResponse {
|
|||||||
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;
|
EntityCategory entity_category = 13;
|
||||||
|
uint32 device_id = 14;
|
||||||
}
|
}
|
||||||
message SensorStateResponse {
|
message SensorStateResponse {
|
||||||
option (id) = 25;
|
option (id) = 25;
|
||||||
@@ -576,6 +602,7 @@ message SensorStateResponse {
|
|||||||
// If the sensor does not have a valid state yet.
|
// If the sensor does not have a valid state yet.
|
||||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
bool missing_state = 3;
|
bool missing_state = 3;
|
||||||
|
uint32 device_id = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== SWITCH ====================
|
// ==================== SWITCH ====================
|
||||||
@@ -595,6 +622,7 @@ message ListEntitiesSwitchResponse {
|
|||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
EntityCategory entity_category = 8;
|
EntityCategory entity_category = 8;
|
||||||
string device_class = 9;
|
string device_class = 9;
|
||||||
|
uint32 device_id = 10;
|
||||||
}
|
}
|
||||||
message SwitchStateResponse {
|
message SwitchStateResponse {
|
||||||
option (id) = 26;
|
option (id) = 26;
|
||||||
@@ -605,6 +633,7 @@ message SwitchStateResponse {
|
|||||||
|
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool state = 2;
|
bool state = 2;
|
||||||
|
uint32 device_id = 3;
|
||||||
}
|
}
|
||||||
message SwitchCommandRequest {
|
message SwitchCommandRequest {
|
||||||
option (id) = 33;
|
option (id) = 33;
|
||||||
@@ -632,6 +661,7 @@ message ListEntitiesTextSensorResponse {
|
|||||||
bool disabled_by_default = 6;
|
bool disabled_by_default = 6;
|
||||||
EntityCategory entity_category = 7;
|
EntityCategory entity_category = 7;
|
||||||
string device_class = 8;
|
string device_class = 8;
|
||||||
|
uint32 device_id = 9;
|
||||||
}
|
}
|
||||||
message TextSensorStateResponse {
|
message TextSensorStateResponse {
|
||||||
option (id) = 27;
|
option (id) = 27;
|
||||||
@@ -645,6 +675,7 @@ message TextSensorStateResponse {
|
|||||||
// If the text sensor does not have a valid state yet.
|
// If the text sensor does not have a valid state yet.
|
||||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
bool missing_state = 3;
|
bool missing_state = 3;
|
||||||
|
uint32 device_id = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== SUBSCRIBE LOGS ====================
|
// ==================== SUBSCRIBE LOGS ====================
|
||||||
@@ -805,7 +836,7 @@ message ListEntitiesCameraResponse {
|
|||||||
option (id) = 43;
|
option (id) = 43;
|
||||||
option (base_class) = "InfoResponseProtoMessage";
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_ESP32_CAMERA";
|
option (ifdef) = "USE_CAMERA";
|
||||||
|
|
||||||
string object_id = 1;
|
string object_id = 1;
|
||||||
fixed32 key = 2;
|
fixed32 key = 2;
|
||||||
@@ -814,12 +845,13 @@ message ListEntitiesCameraResponse {
|
|||||||
bool disabled_by_default = 5;
|
bool disabled_by_default = 5;
|
||||||
string icon = 6;
|
string icon = 6;
|
||||||
EntityCategory entity_category = 7;
|
EntityCategory entity_category = 7;
|
||||||
|
uint32 device_id = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CameraImageResponse {
|
message CameraImageResponse {
|
||||||
option (id) = 44;
|
option (id) = 44;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_ESP32_CAMERA";
|
option (ifdef) = "USE_CAMERA";
|
||||||
|
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bytes data = 2;
|
bytes data = 2;
|
||||||
@@ -828,7 +860,7 @@ message CameraImageResponse {
|
|||||||
message CameraImageRequest {
|
message CameraImageRequest {
|
||||||
option (id) = 45;
|
option (id) = 45;
|
||||||
option (source) = SOURCE_CLIENT;
|
option (source) = SOURCE_CLIENT;
|
||||||
option (ifdef) = "USE_ESP32_CAMERA";
|
option (ifdef) = "USE_CAMERA";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
|
|
||||||
bool single = 1;
|
bool single = 1;
|
||||||
@@ -916,6 +948,7 @@ message ListEntitiesClimateResponse {
|
|||||||
bool supports_target_humidity = 23;
|
bool supports_target_humidity = 23;
|
||||||
float visual_min_humidity = 24;
|
float visual_min_humidity = 24;
|
||||||
float visual_max_humidity = 25;
|
float visual_max_humidity = 25;
|
||||||
|
uint32 device_id = 26;
|
||||||
}
|
}
|
||||||
message ClimateStateResponse {
|
message ClimateStateResponse {
|
||||||
option (id) = 47;
|
option (id) = 47;
|
||||||
@@ -940,6 +973,7 @@ message ClimateStateResponse {
|
|||||||
string custom_preset = 13;
|
string custom_preset = 13;
|
||||||
float current_humidity = 14;
|
float current_humidity = 14;
|
||||||
float target_humidity = 15;
|
float target_humidity = 15;
|
||||||
|
uint32 device_id = 16;
|
||||||
}
|
}
|
||||||
message ClimateCommandRequest {
|
message ClimateCommandRequest {
|
||||||
option (id) = 48;
|
option (id) = 48;
|
||||||
@@ -999,6 +1033,7 @@ message ListEntitiesNumberResponse {
|
|||||||
string unit_of_measurement = 11;
|
string unit_of_measurement = 11;
|
||||||
NumberMode mode = 12;
|
NumberMode mode = 12;
|
||||||
string device_class = 13;
|
string device_class = 13;
|
||||||
|
uint32 device_id = 14;
|
||||||
}
|
}
|
||||||
message NumberStateResponse {
|
message NumberStateResponse {
|
||||||
option (id) = 50;
|
option (id) = 50;
|
||||||
@@ -1012,6 +1047,7 @@ message NumberStateResponse {
|
|||||||
// If the number does not have a valid state yet.
|
// If the number does not have a valid state yet.
|
||||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
bool missing_state = 3;
|
bool missing_state = 3;
|
||||||
|
uint32 device_id = 4;
|
||||||
}
|
}
|
||||||
message NumberCommandRequest {
|
message NumberCommandRequest {
|
||||||
option (id) = 51;
|
option (id) = 51;
|
||||||
@@ -1039,6 +1075,7 @@ message ListEntitiesSelectResponse {
|
|||||||
repeated string options = 6;
|
repeated string options = 6;
|
||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
EntityCategory entity_category = 8;
|
EntityCategory entity_category = 8;
|
||||||
|
uint32 device_id = 9;
|
||||||
}
|
}
|
||||||
message SelectStateResponse {
|
message SelectStateResponse {
|
||||||
option (id) = 53;
|
option (id) = 53;
|
||||||
@@ -1052,6 +1089,7 @@ message SelectStateResponse {
|
|||||||
// If the select does not have a valid state yet.
|
// If the select does not have a valid state yet.
|
||||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
bool missing_state = 3;
|
bool missing_state = 3;
|
||||||
|
uint32 device_id = 4;
|
||||||
}
|
}
|
||||||
message SelectCommandRequest {
|
message SelectCommandRequest {
|
||||||
option (id) = 54;
|
option (id) = 54;
|
||||||
@@ -1081,6 +1119,7 @@ message ListEntitiesSirenResponse {
|
|||||||
bool supports_duration = 8;
|
bool supports_duration = 8;
|
||||||
bool supports_volume = 9;
|
bool supports_volume = 9;
|
||||||
EntityCategory entity_category = 10;
|
EntityCategory entity_category = 10;
|
||||||
|
uint32 device_id = 11;
|
||||||
}
|
}
|
||||||
message SirenStateResponse {
|
message SirenStateResponse {
|
||||||
option (id) = 56;
|
option (id) = 56;
|
||||||
@@ -1091,6 +1130,7 @@ message SirenStateResponse {
|
|||||||
|
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool state = 2;
|
bool state = 2;
|
||||||
|
uint32 device_id = 3;
|
||||||
}
|
}
|
||||||
message SirenCommandRequest {
|
message SirenCommandRequest {
|
||||||
option (id) = 57;
|
option (id) = 57;
|
||||||
@@ -1144,6 +1184,7 @@ message ListEntitiesLockResponse {
|
|||||||
|
|
||||||
// Not yet implemented:
|
// Not yet implemented:
|
||||||
string code_format = 11;
|
string code_format = 11;
|
||||||
|
uint32 device_id = 12;
|
||||||
}
|
}
|
||||||
message LockStateResponse {
|
message LockStateResponse {
|
||||||
option (id) = 59;
|
option (id) = 59;
|
||||||
@@ -1153,6 +1194,7 @@ message LockStateResponse {
|
|||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
LockState state = 2;
|
LockState state = 2;
|
||||||
|
uint32 device_id = 3;
|
||||||
}
|
}
|
||||||
message LockCommandRequest {
|
message LockCommandRequest {
|
||||||
option (id) = 60;
|
option (id) = 60;
|
||||||
@@ -1183,6 +1225,7 @@ message ListEntitiesButtonResponse {
|
|||||||
bool disabled_by_default = 6;
|
bool disabled_by_default = 6;
|
||||||
EntityCategory entity_category = 7;
|
EntityCategory entity_category = 7;
|
||||||
string device_class = 8;
|
string device_class = 8;
|
||||||
|
uint32 device_id = 9;
|
||||||
}
|
}
|
||||||
message ButtonCommandRequest {
|
message ButtonCommandRequest {
|
||||||
option (id) = 62;
|
option (id) = 62;
|
||||||
@@ -1238,6 +1281,8 @@ message ListEntitiesMediaPlayerResponse {
|
|||||||
bool supports_pause = 8;
|
bool supports_pause = 8;
|
||||||
|
|
||||||
repeated MediaPlayerSupportedFormat supported_formats = 9;
|
repeated MediaPlayerSupportedFormat supported_formats = 9;
|
||||||
|
|
||||||
|
uint32 device_id = 10;
|
||||||
}
|
}
|
||||||
message MediaPlayerStateResponse {
|
message MediaPlayerStateResponse {
|
||||||
option (id) = 64;
|
option (id) = 64;
|
||||||
@@ -1249,6 +1294,7 @@ message MediaPlayerStateResponse {
|
|||||||
MediaPlayerState state = 2;
|
MediaPlayerState state = 2;
|
||||||
float volume = 3;
|
float volume = 3;
|
||||||
bool muted = 4;
|
bool muted = 4;
|
||||||
|
uint32 device_id = 5;
|
||||||
}
|
}
|
||||||
message MediaPlayerCommandRequest {
|
message MediaPlayerCommandRequest {
|
||||||
option (id) = 65;
|
option (id) = 65;
|
||||||
@@ -1778,6 +1824,7 @@ message ListEntitiesAlarmControlPanelResponse {
|
|||||||
uint32 supported_features = 8;
|
uint32 supported_features = 8;
|
||||||
bool requires_code = 9;
|
bool requires_code = 9;
|
||||||
bool requires_code_to_arm = 10;
|
bool requires_code_to_arm = 10;
|
||||||
|
uint32 device_id = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AlarmControlPanelStateResponse {
|
message AlarmControlPanelStateResponse {
|
||||||
@@ -1788,6 +1835,7 @@ message AlarmControlPanelStateResponse {
|
|||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
AlarmControlPanelState state = 2;
|
AlarmControlPanelState state = 2;
|
||||||
|
uint32 device_id = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AlarmControlPanelCommandRequest {
|
message AlarmControlPanelCommandRequest {
|
||||||
@@ -1823,6 +1871,7 @@ message ListEntitiesTextResponse {
|
|||||||
uint32 max_length = 9;
|
uint32 max_length = 9;
|
||||||
string pattern = 10;
|
string pattern = 10;
|
||||||
TextMode mode = 11;
|
TextMode mode = 11;
|
||||||
|
uint32 device_id = 12;
|
||||||
}
|
}
|
||||||
message TextStateResponse {
|
message TextStateResponse {
|
||||||
option (id) = 98;
|
option (id) = 98;
|
||||||
@@ -1836,6 +1885,7 @@ message TextStateResponse {
|
|||||||
// If the Text does not have a valid state yet.
|
// If the Text does not have a valid state yet.
|
||||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
bool missing_state = 3;
|
bool missing_state = 3;
|
||||||
|
uint32 device_id = 4;
|
||||||
}
|
}
|
||||||
message TextCommandRequest {
|
message TextCommandRequest {
|
||||||
option (id) = 99;
|
option (id) = 99;
|
||||||
@@ -1863,6 +1913,7 @@ message ListEntitiesDateResponse {
|
|||||||
string icon = 5;
|
string icon = 5;
|
||||||
bool disabled_by_default = 6;
|
bool disabled_by_default = 6;
|
||||||
EntityCategory entity_category = 7;
|
EntityCategory entity_category = 7;
|
||||||
|
uint32 device_id = 8;
|
||||||
}
|
}
|
||||||
message DateStateResponse {
|
message DateStateResponse {
|
||||||
option (id) = 101;
|
option (id) = 101;
|
||||||
@@ -1878,6 +1929,7 @@ message DateStateResponse {
|
|||||||
uint32 year = 3;
|
uint32 year = 3;
|
||||||
uint32 month = 4;
|
uint32 month = 4;
|
||||||
uint32 day = 5;
|
uint32 day = 5;
|
||||||
|
uint32 device_id = 6;
|
||||||
}
|
}
|
||||||
message DateCommandRequest {
|
message DateCommandRequest {
|
||||||
option (id) = 102;
|
option (id) = 102;
|
||||||
@@ -1906,6 +1958,7 @@ message ListEntitiesTimeResponse {
|
|||||||
string icon = 5;
|
string icon = 5;
|
||||||
bool disabled_by_default = 6;
|
bool disabled_by_default = 6;
|
||||||
EntityCategory entity_category = 7;
|
EntityCategory entity_category = 7;
|
||||||
|
uint32 device_id = 8;
|
||||||
}
|
}
|
||||||
message TimeStateResponse {
|
message TimeStateResponse {
|
||||||
option (id) = 104;
|
option (id) = 104;
|
||||||
@@ -1921,6 +1974,7 @@ message TimeStateResponse {
|
|||||||
uint32 hour = 3;
|
uint32 hour = 3;
|
||||||
uint32 minute = 4;
|
uint32 minute = 4;
|
||||||
uint32 second = 5;
|
uint32 second = 5;
|
||||||
|
uint32 device_id = 6;
|
||||||
}
|
}
|
||||||
message TimeCommandRequest {
|
message TimeCommandRequest {
|
||||||
option (id) = 105;
|
option (id) = 105;
|
||||||
@@ -1952,6 +2006,7 @@ message ListEntitiesEventResponse {
|
|||||||
string device_class = 8;
|
string device_class = 8;
|
||||||
|
|
||||||
repeated string event_types = 9;
|
repeated string event_types = 9;
|
||||||
|
uint32 device_id = 10;
|
||||||
}
|
}
|
||||||
message EventResponse {
|
message EventResponse {
|
||||||
option (id) = 108;
|
option (id) = 108;
|
||||||
@@ -1961,6 +2016,7 @@ message EventResponse {
|
|||||||
|
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
string event_type = 2;
|
string event_type = 2;
|
||||||
|
uint32 device_id = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== VALVE ====================
|
// ==================== VALVE ====================
|
||||||
@@ -1983,6 +2039,7 @@ message ListEntitiesValveResponse {
|
|||||||
bool assumed_state = 9;
|
bool assumed_state = 9;
|
||||||
bool supports_position = 10;
|
bool supports_position = 10;
|
||||||
bool supports_stop = 11;
|
bool supports_stop = 11;
|
||||||
|
uint32 device_id = 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ValveOperation {
|
enum ValveOperation {
|
||||||
@@ -2000,6 +2057,7 @@ message ValveStateResponse {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
float position = 2;
|
float position = 2;
|
||||||
ValveOperation current_operation = 3;
|
ValveOperation current_operation = 3;
|
||||||
|
uint32 device_id = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ValveCommandRequest {
|
message ValveCommandRequest {
|
||||||
@@ -2029,6 +2087,7 @@ message ListEntitiesDateTimeResponse {
|
|||||||
string icon = 5;
|
string icon = 5;
|
||||||
bool disabled_by_default = 6;
|
bool disabled_by_default = 6;
|
||||||
EntityCategory entity_category = 7;
|
EntityCategory entity_category = 7;
|
||||||
|
uint32 device_id = 8;
|
||||||
}
|
}
|
||||||
message DateTimeStateResponse {
|
message DateTimeStateResponse {
|
||||||
option (id) = 113;
|
option (id) = 113;
|
||||||
@@ -2042,6 +2101,7 @@ message DateTimeStateResponse {
|
|||||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
bool missing_state = 2;
|
bool missing_state = 2;
|
||||||
fixed32 epoch_seconds = 3;
|
fixed32 epoch_seconds = 3;
|
||||||
|
uint32 device_id = 4;
|
||||||
}
|
}
|
||||||
message DateTimeCommandRequest {
|
message DateTimeCommandRequest {
|
||||||
option (id) = 114;
|
option (id) = 114;
|
||||||
@@ -2069,6 +2129,7 @@ message ListEntitiesUpdateResponse {
|
|||||||
bool disabled_by_default = 6;
|
bool disabled_by_default = 6;
|
||||||
EntityCategory entity_category = 7;
|
EntityCategory entity_category = 7;
|
||||||
string device_class = 8;
|
string device_class = 8;
|
||||||
|
uint32 device_id = 9;
|
||||||
}
|
}
|
||||||
message UpdateStateResponse {
|
message UpdateStateResponse {
|
||||||
option (id) = 117;
|
option (id) = 117;
|
||||||
@@ -2087,6 +2148,7 @@ message UpdateStateResponse {
|
|||||||
string title = 8;
|
string title = 8;
|
||||||
string release_summary = 9;
|
string release_summary = 9;
|
||||||
string release_url = 10;
|
string release_url = 10;
|
||||||
|
uint32 device_id = 11;
|
||||||
}
|
}
|
||||||
enum UpdateCommand {
|
enum UpdateCommand {
|
||||||
UPDATE_COMMAND_NONE = 0;
|
UPDATE_COMMAND_NONE = 0;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -66,6 +66,17 @@ const char *api_error_to_str(APIError err) {
|
|||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default implementation for loop - handles sending buffered data
|
||||||
|
APIError APIFrameHelper::loop() {
|
||||||
|
if (!this->tx_buf_.empty()) {
|
||||||
|
APIError err = try_send_tx_buf_();
|
||||||
|
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
|
||||||
|
}
|
||||||
|
|
||||||
// Helper method to buffer data from IOVs
|
// Helper method to buffer data from IOVs
|
||||||
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
|
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
|
||||||
SendBuffer buffer;
|
SendBuffer buffer;
|
||||||
@@ -214,6 +225,22 @@ APIError APIFrameHelper::init_common_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->info_.c_str(), ##__VA_ARGS__)
|
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->info_.c_str(), ##__VA_ARGS__)
|
||||||
|
|
||||||
|
APIError APIFrameHelper::handle_socket_read_result_(ssize_t received) {
|
||||||
|
if (received == -1) {
|
||||||
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||||
|
return APIError::WOULD_BLOCK;
|
||||||
|
}
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||||
|
return APIError::SOCKET_READ_FAILED;
|
||||||
|
} else if (received == 0) {
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Connection closed");
|
||||||
|
return APIError::CONNECTION_CLOSED;
|
||||||
|
}
|
||||||
|
return APIError::OK;
|
||||||
|
}
|
||||||
// uncomment to log raw packets
|
// uncomment to log raw packets
|
||||||
//#define HELPER_LOG_PACKETS
|
//#define HELPER_LOG_PACKETS
|
||||||
|
|
||||||
@@ -274,17 +301,21 @@ APIError APINoiseFrameHelper::init() {
|
|||||||
}
|
}
|
||||||
/// Run through handshake messages (if in that phase)
|
/// Run through handshake messages (if in that phase)
|
||||||
APIError APINoiseFrameHelper::loop() {
|
APIError APINoiseFrameHelper::loop() {
|
||||||
APIError err = state_action_();
|
// During handshake phase, process as many actions as possible until we can't progress
|
||||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
// socket_->ready() stays true until next main loop, but state_action() will return
|
||||||
return err;
|
// WOULD_BLOCK when no more data is available to read
|
||||||
}
|
while (state_ != State::DATA && this->socket_->ready()) {
|
||||||
if (!this->tx_buf_.empty()) {
|
APIError err = state_action_();
|
||||||
err = try_send_tx_buf_();
|
|
||||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
if (err == APIError::WOULD_BLOCK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
|
|
||||||
|
// Use base class implementation for buffer sending
|
||||||
|
return APIFrameHelper::loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
||||||
@@ -312,17 +343,9 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
// no header information yet
|
// no header information yet
|
||||||
uint8_t to_read = 3 - rx_header_buf_len_;
|
uint8_t to_read = 3 - rx_header_buf_len_;
|
||||||
ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
||||||
if (received == -1) {
|
APIError err = handle_socket_read_result_(received);
|
||||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
if (err != APIError::OK) {
|
||||||
return APIError::WOULD_BLOCK;
|
return err;
|
||||||
}
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
|
||||||
return APIError::SOCKET_READ_FAILED;
|
|
||||||
} else if (received == 0) {
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Connection closed");
|
|
||||||
return APIError::CONNECTION_CLOSED;
|
|
||||||
}
|
}
|
||||||
rx_header_buf_len_ += static_cast<uint8_t>(received);
|
rx_header_buf_len_ += static_cast<uint8_t>(received);
|
||||||
if (static_cast<uint8_t>(received) != to_read) {
|
if (static_cast<uint8_t>(received) != to_read) {
|
||||||
@@ -330,17 +353,15 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rx_header_buf_[0] != 0x01) {
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
|
||||||
|
return APIError::BAD_INDICATOR;
|
||||||
|
}
|
||||||
// header reading done
|
// header reading done
|
||||||
}
|
}
|
||||||
|
|
||||||
// read body
|
// read body
|
||||||
uint8_t indicator = rx_header_buf_[0];
|
|
||||||
if (indicator != 0x01) {
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Bad indicator byte %u", indicator);
|
|
||||||
return APIError::BAD_INDICATOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
|
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
|
||||||
|
|
||||||
if (state_ != State::DATA && msg_size > 128) {
|
if (state_ != State::DATA && msg_size > 128) {
|
||||||
@@ -359,17 +380,9 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
// more data to read
|
// more data to read
|
||||||
uint16_t to_read = msg_size - rx_buf_len_;
|
uint16_t to_read = msg_size - rx_buf_len_;
|
||||||
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
||||||
if (received == -1) {
|
APIError err = handle_socket_read_result_(received);
|
||||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
if (err != APIError::OK) {
|
||||||
return APIError::WOULD_BLOCK;
|
return err;
|
||||||
}
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
|
||||||
return APIError::SOCKET_READ_FAILED;
|
|
||||||
} else if (received == 0) {
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Connection closed");
|
|
||||||
return APIError::CONNECTION_CLOSED;
|
|
||||||
}
|
}
|
||||||
rx_buf_len_ += static_cast<uint16_t>(received);
|
rx_buf_len_ += static_cast<uint16_t>(received);
|
||||||
if (static_cast<uint16_t>(received) != to_read) {
|
if (static_cast<uint16_t>(received) != to_read) {
|
||||||
@@ -586,10 +599,6 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|||||||
return APIError::BAD_DATA_PACKET;
|
return APIError::BAD_DATA_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
// uint16_t type;
|
|
||||||
// uint16_t data_len;
|
|
||||||
// uint8_t *data;
|
|
||||||
// uint8_t *padding; zero or more bytes to fill up the rest of the packet
|
|
||||||
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
|
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
|
||||||
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
|
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
|
||||||
if (data_len > msg_size - 4) {
|
if (data_len > msg_size - 4) {
|
||||||
@@ -605,20 +614,14 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
|
APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
|
||||||
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
|
||||||
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
|
|
||||||
|
|
||||||
// Resize to include MAC space (required for Noise encryption)
|
// Resize to include MAC space (required for Noise encryption)
|
||||||
raw_buffer->resize(raw_buffer->size() + frame_footer_size_);
|
buffer.get_buffer()->resize(buffer.get_buffer()->size() + frame_footer_size_);
|
||||||
|
PacketInfo packet{type, 0,
|
||||||
// Use write_protobuf_packets with a single packet
|
static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_ - frame_footer_size_)};
|
||||||
std::vector<PacketInfo> packets;
|
return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
|
||||||
packets.emplace_back(type, 0, payload_len);
|
|
||||||
|
|
||||||
return write_protobuf_packets(buffer, packets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) {
|
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
|
||||||
APIError aerr = state_action_();
|
APIError aerr = state_action_();
|
||||||
if (aerr != APIError::OK) {
|
if (aerr != APIError::OK) {
|
||||||
return aerr;
|
return aerr;
|
||||||
@@ -633,18 +636,15 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
||||||
|
uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
|
||||||
|
|
||||||
this->reusable_iovs_.clear();
|
this->reusable_iovs_.clear();
|
||||||
this->reusable_iovs_.reserve(packets.size());
|
this->reusable_iovs_.reserve(packets.size());
|
||||||
|
|
||||||
// We need to encrypt each packet in place
|
// We need to encrypt each packet in place
|
||||||
for (const auto &packet : packets) {
|
for (const auto &packet : packets) {
|
||||||
uint16_t type = packet.message_type;
|
|
||||||
uint16_t offset = packet.offset;
|
|
||||||
uint16_t payload_len = packet.payload_size;
|
|
||||||
uint16_t msg_len = 4 + payload_len; // type(2) + data_len(2) + payload
|
|
||||||
|
|
||||||
// The buffer already has padding at offset
|
// The buffer already has padding at offset
|
||||||
uint8_t *buf_start = raw_buffer->data() + offset;
|
uint8_t *buf_start = buffer_data + packet.offset;
|
||||||
|
|
||||||
// Write noise header
|
// Write noise header
|
||||||
buf_start[0] = 0x01; // indicator
|
buf_start[0] = 0x01; // indicator
|
||||||
@@ -652,10 +652,10 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, co
|
|||||||
|
|
||||||
// Write message header (to be encrypted)
|
// Write message header (to be encrypted)
|
||||||
const uint8_t msg_offset = 3;
|
const uint8_t msg_offset = 3;
|
||||||
buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte
|
buf_start[msg_offset] = static_cast<uint8_t>(packet.message_type >> 8); // type high byte
|
||||||
buf_start[msg_offset + 1] = (uint8_t) type; // type low byte
|
buf_start[msg_offset + 1] = static_cast<uint8_t>(packet.message_type); // type low byte
|
||||||
buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte
|
buf_start[msg_offset + 2] = static_cast<uint8_t>(packet.payload_size >> 8); // data_len high byte
|
||||||
buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte
|
buf_start[msg_offset + 3] = static_cast<uint8_t>(packet.payload_size); // data_len low byte
|
||||||
// payload data is already in the buffer starting at offset + 7
|
// payload data is already in the buffer starting at offset + 7
|
||||||
|
|
||||||
// Make sure we have space for MAC
|
// Make sure we have space for MAC
|
||||||
@@ -664,7 +664,8 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, co
|
|||||||
// Encrypt the message in place
|
// Encrypt the message in place
|
||||||
NoiseBuffer mbuf;
|
NoiseBuffer mbuf;
|
||||||
noise_buffer_init(mbuf);
|
noise_buffer_init(mbuf);
|
||||||
noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_);
|
noise_buffer_set_inout(mbuf, buf_start + msg_offset, 4 + packet.payload_size,
|
||||||
|
4 + packet.payload_size + frame_footer_size_);
|
||||||
|
|
||||||
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
|
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
@@ -674,14 +675,12 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fill in the encrypted size
|
// Fill in the encrypted size
|
||||||
buf_start[1] = (uint8_t) (mbuf.size >> 8);
|
buf_start[1] = static_cast<uint8_t>(mbuf.size >> 8);
|
||||||
buf_start[2] = (uint8_t) mbuf.size;
|
buf_start[2] = static_cast<uint8_t>(mbuf.size);
|
||||||
|
|
||||||
// Add iovec for this encrypted packet
|
// Add iovec for this encrypted packet
|
||||||
struct iovec iov;
|
this->reusable_iovs_.push_back(
|
||||||
iov.iov_base = buf_start;
|
{buf_start, static_cast<size_t>(3 + mbuf.size)}); // indicator + size + encrypted data
|
||||||
iov.iov_len = 3 + mbuf.size; // indicator + size + encrypted data
|
|
||||||
this->reusable_iovs_.push_back(iov);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send all encrypted packets in one writev call
|
// Send all encrypted packets in one writev call
|
||||||
@@ -822,18 +821,12 @@ APIError APIPlaintextFrameHelper::init() {
|
|||||||
state_ = State::DATA;
|
state_ = State::DATA;
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
/// Not used for plaintext
|
|
||||||
APIError APIPlaintextFrameHelper::loop() {
|
APIError APIPlaintextFrameHelper::loop() {
|
||||||
if (state_ != State::DATA) {
|
if (state_ != State::DATA) {
|
||||||
return APIError::BAD_STATE;
|
return APIError::BAD_STATE;
|
||||||
}
|
}
|
||||||
if (!this->tx_buf_.empty()) {
|
// Use base class implementation for buffer sending
|
||||||
APIError err = try_send_tx_buf_();
|
return APIFrameHelper::loop();
|
||||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
||||||
@@ -862,17 +855,9 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
// Try to get to at least 3 bytes total (indicator + 2 varint bytes), then read one byte at a time
|
// Try to get to at least 3 bytes total (indicator + 2 varint bytes), then read one byte at a time
|
||||||
ssize_t received =
|
ssize_t received =
|
||||||
this->socket_->read(&rx_header_buf_[rx_header_buf_pos_], rx_header_buf_pos_ < 3 ? 3 - rx_header_buf_pos_ : 1);
|
this->socket_->read(&rx_header_buf_[rx_header_buf_pos_], rx_header_buf_pos_ < 3 ? 3 - rx_header_buf_pos_ : 1);
|
||||||
if (received == -1) {
|
APIError err = handle_socket_read_result_(received);
|
||||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
if (err != APIError::OK) {
|
||||||
return APIError::WOULD_BLOCK;
|
return err;
|
||||||
}
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
|
||||||
return APIError::SOCKET_READ_FAILED;
|
|
||||||
} else if (received == 0) {
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Connection closed");
|
|
||||||
return APIError::CONNECTION_CLOSED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this was the first read, validate the indicator byte
|
// If this was the first read, validate the indicator byte
|
||||||
@@ -956,17 +941,9 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
// more data to read
|
// more data to read
|
||||||
uint16_t to_read = rx_header_parsed_len_ - rx_buf_len_;
|
uint16_t to_read = rx_header_parsed_len_ - rx_buf_len_;
|
||||||
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
||||||
if (received == -1) {
|
APIError err = handle_socket_read_result_(received);
|
||||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
if (err != APIError::OK) {
|
||||||
return APIError::WOULD_BLOCK;
|
return err;
|
||||||
}
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
|
||||||
return APIError::SOCKET_READ_FAILED;
|
|
||||||
} else if (received == 0) {
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Connection closed");
|
|
||||||
return APIError::CONNECTION_CLOSED;
|
|
||||||
}
|
}
|
||||||
rx_buf_len_ += static_cast<uint16_t>(received);
|
rx_buf_len_ += static_cast<uint16_t>(received);
|
||||||
if (static_cast<uint16_t>(received) != to_read) {
|
if (static_cast<uint16_t>(received) != to_read) {
|
||||||
@@ -1026,18 +1003,11 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
|
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
|
||||||
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
PacketInfo packet{type, 0, static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_)};
|
||||||
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
|
return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
|
||||||
|
|
||||||
// Use write_protobuf_packets with a single packet
|
|
||||||
std::vector<PacketInfo> packets;
|
|
||||||
packets.emplace_back(type, 0, payload_len);
|
|
||||||
|
|
||||||
return write_protobuf_packets(buffer, packets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer,
|
APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
|
||||||
const std::vector<PacketInfo> &packets) {
|
|
||||||
if (state_ != State::DATA) {
|
if (state_ != State::DATA) {
|
||||||
return APIError::BAD_STATE;
|
return APIError::BAD_STATE;
|
||||||
}
|
}
|
||||||
@@ -1047,17 +1017,15 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
|
||||||
|
uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
|
||||||
|
|
||||||
this->reusable_iovs_.clear();
|
this->reusable_iovs_.clear();
|
||||||
this->reusable_iovs_.reserve(packets.size());
|
this->reusable_iovs_.reserve(packets.size());
|
||||||
|
|
||||||
for (const auto &packet : packets) {
|
for (const auto &packet : packets) {
|
||||||
uint16_t type = packet.message_type;
|
|
||||||
uint16_t offset = packet.offset;
|
|
||||||
uint16_t payload_len = packet.payload_size;
|
|
||||||
|
|
||||||
// Calculate varint sizes for header layout
|
// Calculate varint sizes for header layout
|
||||||
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len));
|
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.payload_size));
|
||||||
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type));
|
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.message_type));
|
||||||
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
|
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
|
||||||
|
|
||||||
// Calculate where to start writing the header
|
// Calculate where to start writing the header
|
||||||
@@ -1085,23 +1053,20 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer
|
|||||||
//
|
//
|
||||||
// The message starts at offset + frame_header_padding_
|
// The message starts at offset + frame_header_padding_
|
||||||
// So we write the header starting at offset + frame_header_padding_ - total_header_len
|
// So we write the header starting at offset + frame_header_padding_ - total_header_len
|
||||||
uint8_t *buf_start = raw_buffer->data() + offset;
|
uint8_t *buf_start = buffer_data + packet.offset;
|
||||||
uint32_t header_offset = frame_header_padding_ - total_header_len;
|
uint32_t header_offset = frame_header_padding_ - total_header_len;
|
||||||
|
|
||||||
// Write the plaintext header
|
// Write the plaintext header
|
||||||
buf_start[header_offset] = 0x00; // indicator
|
buf_start[header_offset] = 0x00; // indicator
|
||||||
|
|
||||||
// Encode size varint directly into buffer
|
// Encode varints directly into buffer
|
||||||
ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
|
ProtoVarInt(packet.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
|
||||||
|
ProtoVarInt(packet.message_type)
|
||||||
// Encode type varint directly into buffer
|
.encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
|
||||||
ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
|
|
||||||
|
|
||||||
// Add iovec for this packet (header + payload)
|
// Add iovec for this packet (header + payload)
|
||||||
struct iovec iov;
|
this->reusable_iovs_.push_back(
|
||||||
iov.iov_base = buf_start + header_offset;
|
{buf_start + header_offset, static_cast<size_t>(total_header_len + packet.payload_size)});
|
||||||
iov.iov_len = total_header_len + payload_len;
|
|
||||||
this->reusable_iovs_.push_back(iov);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send all packets in one writev call
|
// Send all packets in one writev call
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <span>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ struct PacketInfo {
|
|||||||
: message_type(type), offset(off), payload_size(size), padding(0) {}
|
: message_type(type), offset(off), payload_size(size), padding(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class APIError : int {
|
enum class APIError : uint16_t {
|
||||||
OK = 0,
|
OK = 0,
|
||||||
WOULD_BLOCK = 1001,
|
WOULD_BLOCK = 1001,
|
||||||
BAD_HANDSHAKE_PACKET_LEN = 1002,
|
BAD_HANDSHAKE_PACKET_LEN = 1002,
|
||||||
@@ -74,7 +75,7 @@ class APIFrameHelper {
|
|||||||
}
|
}
|
||||||
virtual ~APIFrameHelper() = default;
|
virtual ~APIFrameHelper() = default;
|
||||||
virtual APIError init() = 0;
|
virtual APIError init() = 0;
|
||||||
virtual APIError loop() = 0;
|
virtual APIError loop();
|
||||||
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
|
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
|
||||||
bool can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
|
bool can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
|
||||||
std::string getpeername() { return socket_->getpeername(); }
|
std::string getpeername() { return socket_->getpeername(); }
|
||||||
@@ -101,7 +102,7 @@ class APIFrameHelper {
|
|||||||
// Write multiple protobuf packets in a single operation
|
// Write multiple protobuf packets in a single operation
|
||||||
// packets contains (message_type, offset, length) for each message in the buffer
|
// packets contains (message_type, offset, length) for each message in the buffer
|
||||||
// The buffer contains all messages with appropriate padding before each
|
// The buffer contains all messages with appropriate padding before each
|
||||||
virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) = 0;
|
virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) = 0;
|
||||||
// Get the frame header padding required by this protocol
|
// Get the frame header padding required by this protocol
|
||||||
virtual uint8_t frame_header_padding() = 0;
|
virtual uint8_t frame_header_padding() = 0;
|
||||||
// Get the frame footer size required by this protocol
|
// Get the frame footer size required by this protocol
|
||||||
@@ -125,38 +126,6 @@ class APIFrameHelper {
|
|||||||
const uint8_t *current_data() const { return data.data() + offset; }
|
const uint8_t *current_data() const { return data.data() + offset; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Queue of data buffers to be sent
|
|
||||||
std::deque<SendBuffer> tx_buf_;
|
|
||||||
|
|
||||||
// Common state enum for all frame helpers
|
|
||||||
// Note: Not all states are used by all implementations
|
|
||||||
// - INITIALIZE: Used by both Noise and Plaintext
|
|
||||||
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
|
|
||||||
// - DATA: Used by both Noise and Plaintext
|
|
||||||
// - CLOSED: Used by both Noise and Plaintext
|
|
||||||
// - FAILED: Used by both Noise and Plaintext
|
|
||||||
// - EXPLICIT_REJECT: Only used by Noise protocol
|
|
||||||
enum class State {
|
|
||||||
INITIALIZE = 1,
|
|
||||||
CLIENT_HELLO = 2, // Noise only
|
|
||||||
SERVER_HELLO = 3, // Noise only
|
|
||||||
HANDSHAKE = 4, // Noise only
|
|
||||||
DATA = 5,
|
|
||||||
CLOSED = 6,
|
|
||||||
FAILED = 7,
|
|
||||||
EXPLICIT_REJECT = 8, // Noise only
|
|
||||||
};
|
|
||||||
|
|
||||||
// Current state of the frame helper
|
|
||||||
State state_{State::INITIALIZE};
|
|
||||||
|
|
||||||
// Helper name for logging
|
|
||||||
std::string info_;
|
|
||||||
|
|
||||||
// Socket for communication
|
|
||||||
socket::Socket *socket_{nullptr};
|
|
||||||
std::unique_ptr<socket::Socket> socket_owned_;
|
|
||||||
|
|
||||||
// Common implementation for writing raw data to socket
|
// Common implementation for writing raw data to socket
|
||||||
APIError write_raw_(const struct iovec *iov, int iovcnt);
|
APIError write_raw_(const struct iovec *iov, int iovcnt);
|
||||||
|
|
||||||
@@ -169,18 +138,47 @@ class APIFrameHelper {
|
|||||||
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
|
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
|
||||||
const std::string &info, StateEnum &state, StateEnum failed_state);
|
const std::string &info, StateEnum &state, StateEnum failed_state);
|
||||||
|
|
||||||
|
// Pointers first (4 bytes each)
|
||||||
|
socket::Socket *socket_{nullptr};
|
||||||
|
std::unique_ptr<socket::Socket> socket_owned_;
|
||||||
|
|
||||||
|
// Common state enum for all frame helpers
|
||||||
|
// Note: Not all states are used by all implementations
|
||||||
|
// - INITIALIZE: Used by both Noise and Plaintext
|
||||||
|
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
|
||||||
|
// - DATA: Used by both Noise and Plaintext
|
||||||
|
// - CLOSED: Used by both Noise and Plaintext
|
||||||
|
// - FAILED: Used by both Noise and Plaintext
|
||||||
|
// - EXPLICIT_REJECT: Only used by Noise protocol
|
||||||
|
enum class State : uint8_t {
|
||||||
|
INITIALIZE = 1,
|
||||||
|
CLIENT_HELLO = 2, // Noise only
|
||||||
|
SERVER_HELLO = 3, // Noise only
|
||||||
|
HANDSHAKE = 4, // Noise only
|
||||||
|
DATA = 5,
|
||||||
|
CLOSED = 6,
|
||||||
|
FAILED = 7,
|
||||||
|
EXPLICIT_REJECT = 8, // Noise only
|
||||||
|
};
|
||||||
|
|
||||||
|
// Containers (size varies, but typically 12+ bytes on 32-bit)
|
||||||
|
std::deque<SendBuffer> tx_buf_;
|
||||||
|
std::string info_;
|
||||||
|
std::vector<struct iovec> reusable_iovs_;
|
||||||
|
std::vector<uint8_t> rx_buf_;
|
||||||
|
|
||||||
|
// Group smaller types together
|
||||||
|
uint16_t rx_buf_len_ = 0;
|
||||||
|
State state_{State::INITIALIZE};
|
||||||
uint8_t frame_header_padding_{0};
|
uint8_t frame_header_padding_{0};
|
||||||
uint8_t frame_footer_size_{0};
|
uint8_t frame_footer_size_{0};
|
||||||
|
// 5 bytes total, 3 bytes padding
|
||||||
// Reusable IOV array for write_protobuf_packets to avoid repeated allocations
|
|
||||||
std::vector<struct iovec> reusable_iovs_;
|
|
||||||
|
|
||||||
// Receive buffer for reading frame data
|
|
||||||
std::vector<uint8_t> rx_buf_;
|
|
||||||
uint16_t rx_buf_len_ = 0;
|
|
||||||
|
|
||||||
// Common initialization for both plaintext and noise protocols
|
// Common initialization for both plaintext and noise protocols
|
||||||
APIError init_common_();
|
APIError init_common_();
|
||||||
|
|
||||||
|
// Helper method to handle socket read results
|
||||||
|
APIError handle_socket_read_result_(ssize_t received);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
@@ -200,7 +198,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
|||||||
APIError loop() override;
|
APIError loop() override;
|
||||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||||
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
|
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
|
||||||
APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) override;
|
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
|
||||||
// Get the frame header padding required by this protocol
|
// Get the frame header padding required by this protocol
|
||||||
uint8_t frame_header_padding() override { return frame_header_padding_; }
|
uint8_t frame_header_padding() override { return frame_header_padding_; }
|
||||||
// Get the frame footer size required by this protocol
|
// Get the frame footer size required by this protocol
|
||||||
@@ -213,19 +211,28 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
|||||||
APIError init_handshake_();
|
APIError init_handshake_();
|
||||||
APIError check_handshake_finished_();
|
APIError check_handshake_finished_();
|
||||||
void send_explicit_handshake_reject_(const std::string &reason);
|
void send_explicit_handshake_reject_(const std::string &reason);
|
||||||
|
|
||||||
|
// Pointers first (4 bytes each)
|
||||||
|
NoiseHandshakeState *handshake_{nullptr};
|
||||||
|
NoiseCipherState *send_cipher_{nullptr};
|
||||||
|
NoiseCipherState *recv_cipher_{nullptr};
|
||||||
|
|
||||||
|
// Shared pointer (8 bytes on 32-bit = 4 bytes control block pointer + 4 bytes object pointer)
|
||||||
|
std::shared_ptr<APINoiseContext> ctx_;
|
||||||
|
|
||||||
|
// Vector (12 bytes on 32-bit)
|
||||||
|
std::vector<uint8_t> prologue_;
|
||||||
|
|
||||||
|
// NoiseProtocolId (size depends on implementation)
|
||||||
|
NoiseProtocolId nid_;
|
||||||
|
|
||||||
|
// Group small types together
|
||||||
// Fixed-size header buffer for noise protocol:
|
// Fixed-size header buffer for noise protocol:
|
||||||
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
|
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
|
||||||
// Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
|
// Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
|
||||||
uint8_t rx_header_buf_[3];
|
uint8_t rx_header_buf_[3];
|
||||||
uint8_t rx_header_buf_len_ = 0;
|
uint8_t rx_header_buf_len_ = 0;
|
||||||
|
// 4 bytes total, no padding
|
||||||
std::vector<uint8_t> prologue_;
|
|
||||||
|
|
||||||
std::shared_ptr<APINoiseContext> ctx_;
|
|
||||||
NoiseHandshakeState *handshake_{nullptr};
|
|
||||||
NoiseCipherState *send_cipher_{nullptr};
|
|
||||||
NoiseCipherState *recv_cipher_{nullptr};
|
|
||||||
NoiseProtocolId nid_;
|
|
||||||
};
|
};
|
||||||
#endif // USE_API_NOISE
|
#endif // USE_API_NOISE
|
||||||
|
|
||||||
@@ -245,13 +252,19 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
|
|||||||
APIError loop() override;
|
APIError loop() override;
|
||||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||||
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
|
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
|
||||||
APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) override;
|
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
|
||||||
uint8_t frame_header_padding() override { return frame_header_padding_; }
|
uint8_t frame_header_padding() override { return frame_header_padding_; }
|
||||||
// Get the frame footer size required by this protocol
|
// Get the frame footer size required by this protocol
|
||||||
uint8_t frame_footer_size() override { return frame_footer_size_; }
|
uint8_t frame_footer_size() override { return frame_footer_size_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
APIError try_read_frame_(ParsedFrame *frame);
|
APIError try_read_frame_(ParsedFrame *frame);
|
||||||
|
|
||||||
|
// Group 2-byte aligned types
|
||||||
|
uint16_t rx_header_parsed_type_ = 0;
|
||||||
|
uint16_t rx_header_parsed_len_ = 0;
|
||||||
|
|
||||||
|
// Group 1-byte types together
|
||||||
// Fixed-size header buffer for plaintext protocol:
|
// Fixed-size header buffer for plaintext protocol:
|
||||||
// We now store the indicator byte + the two varints.
|
// We now store the indicator byte + the two varints.
|
||||||
// To match noise protocol's maximum message size (UINT16_MAX = 65535), we need:
|
// To match noise protocol's maximum message size (UINT16_MAX = 65535), we need:
|
||||||
@@ -263,8 +276,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
|
|||||||
uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type)
|
uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type)
|
||||||
uint8_t rx_header_buf_pos_ = 0;
|
uint8_t rx_header_buf_pos_ = 0;
|
||||||
bool rx_header_parsed_ = false;
|
bool rx_header_parsed_ = false;
|
||||||
uint16_t rx_header_parsed_type_ = 0;
|
// 8 bytes total, no padding needed
|
||||||
uint16_t rx_header_parsed_len_ = 0;
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+391
-4327
File diff suppressed because it is too large
Load Diff
+287
-198
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,10 @@
|
|||||||
// See script/api_protobuf/api_protobuf.py
|
// See script/api_protobuf/api_protobuf.py
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "api_pb2.h"
|
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
|
#include "api_pb2.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
|
|
||||||
template<typename T> bool send_message(const T &msg) {
|
template<typename T> bool send_message(const T &msg) {
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
this->log_send_message_(T::message_name(), msg.dump());
|
this->log_send_message_(msg.message_name(), msg.dump());
|
||||||
#endif
|
#endif
|
||||||
return this->send_message_(msg, T::MESSAGE_TYPE);
|
return this->send_message_(msg, T::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
@@ -70,7 +71,7 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
|
|
||||||
virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
|
virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
virtual void on_camera_image_request(const CameraImageRequest &value){};
|
virtual void on_camera_image_request(const CameraImageRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -199,7 +200,7 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
virtual void on_update_command_request(const UpdateCommandRequest &value){};
|
virtual void on_update_command_request(const UpdateCommandRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class APIServerConnection : public APIServerConnectionBase {
|
class APIServerConnection : public APIServerConnectionBase {
|
||||||
@@ -222,7 +223,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
virtual void camera_image(const CameraImageRequest &msg) = 0;
|
virtual void camera_image(const CameraImageRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
@@ -339,7 +340,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
void on_button_command_request(const ButtonCommandRequest &msg) override;
|
void on_button_command_request(const ButtonCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
void on_camera_image_request(const CameraImageRequest &msg) override;
|
void on_camera_image_request(const CameraImageRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
|
|||||||
@@ -316,15 +316,13 @@ class ProtoSize {
|
|||||||
/**
|
/**
|
||||||
* @brief Calculates and adds the size of a nested message field to the total message size
|
* @brief Calculates and adds the size of a nested message field to the total message size
|
||||||
*
|
*
|
||||||
* This templated version directly takes a message object, calculates its size internally,
|
* This version takes a ProtoMessage object, calculates its size internally,
|
||||||
* and updates the total_size reference. This eliminates the need for a temporary variable
|
* and updates the total_size reference. This eliminates the need for a temporary variable
|
||||||
* at the call site.
|
* at the call site.
|
||||||
*
|
*
|
||||||
* @tparam MessageType The type of the nested message (inferred from parameter)
|
|
||||||
* @param message The nested message object
|
* @param message The nested message object
|
||||||
*/
|
*/
|
||||||
template<typename MessageType>
|
static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message,
|
||||||
static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const MessageType &message,
|
|
||||||
bool force = false) {
|
bool force = false) {
|
||||||
uint32_t nested_size = 0;
|
uint32_t nested_size = 0;
|
||||||
message.calculate_size(nested_size);
|
message.calculate_size(nested_size);
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ static const char *const TAG = "api";
|
|||||||
// APIServer
|
// APIServer
|
||||||
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
|
#ifndef USE_API_YAML_SERVICES
|
||||||
|
// Global empty vector to avoid guard variables (saves 8 bytes)
|
||||||
|
// This is initialized at program startup before any threads
|
||||||
|
static const std::vector<UserServiceDescriptor *> empty_user_services{};
|
||||||
|
|
||||||
|
const std::vector<UserServiceDescriptor *> &get_empty_user_services_instance() { return empty_user_services; }
|
||||||
|
#endif
|
||||||
|
|
||||||
APIServer::APIServer() {
|
APIServer::APIServer() {
|
||||||
global_api_server = this;
|
global_api_server = this;
|
||||||
// Pre-allocate shared write buffer
|
// Pre-allocate shared write buffer
|
||||||
@@ -47,6 +55,11 @@ void APIServer::setup() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Schedule reboot if no clients connect within timeout
|
||||||
|
if (this->reboot_timeout_ != 0) {
|
||||||
|
this->schedule_reboot_timeout_();
|
||||||
|
}
|
||||||
|
|
||||||
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
|
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
|
||||||
if (this->socket_ == nullptr) {
|
if (this->socket_ == nullptr) {
|
||||||
ESP_LOGW(TAG, "Could not create socket");
|
ESP_LOGW(TAG, "Could not create socket");
|
||||||
@@ -91,34 +104,42 @@ void APIServer::setup() {
|
|||||||
|
|
||||||
#ifdef USE_LOGGER
|
#ifdef USE_LOGGER
|
||||||
if (logger::global_logger != nullptr) {
|
if (logger::global_logger != nullptr) {
|
||||||
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
logger::global_logger->add_on_log_callback(
|
||||||
if (this->shutting_down_) {
|
[this](int level, const char *tag, const char *message, size_t message_len) {
|
||||||
// Don't try to send logs during shutdown
|
if (this->shutting_down_) {
|
||||||
// as it could result in a recursion and
|
// Don't try to send logs during shutdown
|
||||||
// we would be filling a buffer we are trying to clear
|
// as it could result in a recursion and
|
||||||
return;
|
// we would be filling a buffer we are trying to clear
|
||||||
}
|
return;
|
||||||
for (auto &c : this->clients_) {
|
}
|
||||||
if (!c->remove_)
|
|
||||||
c->try_send_log_message(level, tag, message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this->last_connected_ = millis();
|
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
|
||||||
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
|
|
||||||
esp32_camera::global_esp32_camera->add_image_callback(
|
|
||||||
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
|
|
||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
if (!c->remove_)
|
if (!c->flags_.remove)
|
||||||
c->set_camera_state(image);
|
c->try_send_log_message(level, tag, message, message_len);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_CAMERA
|
||||||
|
if (camera::Camera::instance() != nullptr && !camera::Camera::instance()->is_internal()) {
|
||||||
|
camera::Camera::instance()->add_image_callback([this](const std::shared_ptr<camera::CameraImage> &image) {
|
||||||
|
for (auto &c : this->clients_) {
|
||||||
|
if (!c->flags_.remove)
|
||||||
|
c->set_camera_state(image);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIServer::schedule_reboot_timeout_() {
|
||||||
|
this->status_set_warning();
|
||||||
|
this->set_timeout("api_reboot", this->reboot_timeout_, []() {
|
||||||
|
if (!global_api_server->is_connected()) {
|
||||||
|
ESP_LOGE(TAG, "No clients; rebooting");
|
||||||
|
App.reboot();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIServer::loop() {
|
void APIServer::loop() {
|
||||||
@@ -130,51 +151,63 @@ void APIServer::loop() {
|
|||||||
auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len);
|
auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len);
|
||||||
if (!sock)
|
if (!sock)
|
||||||
break;
|
break;
|
||||||
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
|
ESP_LOGD(TAG, "Accept %s", sock->getpeername().c_str());
|
||||||
|
|
||||||
auto *conn = new APIConnection(std::move(sock), this);
|
auto *conn = new APIConnection(std::move(sock), this);
|
||||||
this->clients_.emplace_back(conn);
|
this->clients_.emplace_back(conn);
|
||||||
conn->start();
|
conn->start();
|
||||||
|
|
||||||
|
// Clear warning status and cancel reboot when first client connects
|
||||||
|
if (this->clients_.size() == 1 && this->reboot_timeout_ != 0) {
|
||||||
|
this->status_clear_warning();
|
||||||
|
this->cancel_timeout("api_reboot");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->clients_.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Process clients and remove disconnected ones in a single pass
|
// Process clients and remove disconnected ones in a single pass
|
||||||
if (!this->clients_.empty()) {
|
// Check network connectivity once for all clients
|
||||||
size_t client_index = 0;
|
if (!network::is_connected()) {
|
||||||
while (client_index < this->clients_.size()) {
|
// Network is down - disconnect all clients
|
||||||
auto &client = this->clients_[client_index];
|
for (auto &client : this->clients_) {
|
||||||
|
client->on_fatal_error();
|
||||||
if (client->remove_) {
|
ESP_LOGW(TAG, "%s: Network down; disconnect", client->get_client_combined_info().c_str());
|
||||||
// Handle disconnection
|
|
||||||
this->client_disconnected_trigger_->trigger(client->client_info_, client->client_peername_);
|
|
||||||
ESP_LOGV(TAG, "Removing connection to %s", client->client_info_.c_str());
|
|
||||||
|
|
||||||
// Swap with the last element and pop (avoids expensive vector shifts)
|
|
||||||
if (client_index < this->clients_.size() - 1) {
|
|
||||||
std::swap(this->clients_[client_index], this->clients_.back());
|
|
||||||
}
|
|
||||||
this->clients_.pop_back();
|
|
||||||
// Don't increment client_index since we need to process the swapped element
|
|
||||||
} else {
|
|
||||||
// Process active client
|
|
||||||
client->loop();
|
|
||||||
client_index++; // Move to next client
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Continue to process and clean up the clients below
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->reboot_timeout_ != 0) {
|
size_t client_index = 0;
|
||||||
const uint32_t now = millis();
|
while (client_index < this->clients_.size()) {
|
||||||
if (!this->is_connected()) {
|
auto &client = this->clients_[client_index];
|
||||||
if (now - this->last_connected_ > this->reboot_timeout_) {
|
|
||||||
ESP_LOGE(TAG, "No client connected; rebooting");
|
if (!client->flags_.remove) {
|
||||||
App.reboot();
|
// Common case: process active client
|
||||||
}
|
client->loop();
|
||||||
this->status_set_warning();
|
client_index++;
|
||||||
} else {
|
continue;
|
||||||
this->last_connected_ = now;
|
|
||||||
this->status_clear_warning();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rare case: handle disconnection
|
||||||
|
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||||
|
this->client_disconnected_trigger_->trigger(client->client_info_, client->client_peername_);
|
||||||
|
#endif
|
||||||
|
ESP_LOGV(TAG, "Remove connection %s", client->client_info_.c_str());
|
||||||
|
|
||||||
|
// Swap with the last element and pop (avoids expensive vector shifts)
|
||||||
|
if (client_index < this->clients_.size() - 1) {
|
||||||
|
std::swap(this->clients_[client_index], this->clients_.back());
|
||||||
|
}
|
||||||
|
this->clients_.pop_back();
|
||||||
|
|
||||||
|
// Schedule reboot when last client disconnects
|
||||||
|
if (this->clients_.empty() && this->reboot_timeout_ != 0) {
|
||||||
|
this->schedule_reboot_timeout_();
|
||||||
|
}
|
||||||
|
// Don't increment client_index since we need to process the swapped element
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +226,7 @@ void APIServer::dump_config() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_API_PASSWORD
|
||||||
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 {
|
||||||
@@ -223,192 +257,129 @@ bool APIServer::check_password(const std::string &password) const {
|
|||||||
|
|
||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void APIServer::handle_disconnect(APIConnection *conn) {}
|
void APIServer::handle_disconnect(APIConnection *conn) {}
|
||||||
|
|
||||||
|
// Macro for entities without extra parameters
|
||||||
|
#define API_DISPATCH_UPDATE(entity_type, entity_name) \
|
||||||
|
void APIServer::on_##entity_name##_update(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||||
|
if (obj->is_internal()) \
|
||||||
|
return; \
|
||||||
|
for (auto &c : this->clients_) \
|
||||||
|
c->send_##entity_name##_state(obj); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Macro for entities with extra parameters (but parameters not used in send)
|
||||||
|
#define API_DISPATCH_UPDATE_IGNORE_PARAMS(entity_type, entity_name, ...) \
|
||||||
|
void APIServer::on_##entity_name##_update(entity_type *obj, __VA_ARGS__) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||||
|
if (obj->is_internal()) \
|
||||||
|
return; \
|
||||||
|
for (auto &c : this->clients_) \
|
||||||
|
c->send_##entity_name##_state(obj); \
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
|
API_DISPATCH_UPDATE(binary_sensor::BinarySensor, binary_sensor)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_binary_sensor_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
void APIServer::on_cover_update(cover::Cover *obj) {
|
API_DISPATCH_UPDATE(cover::Cover, cover)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_cover_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
void APIServer::on_fan_update(fan::Fan *obj) {
|
API_DISPATCH_UPDATE(fan::Fan, fan)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_fan_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
void APIServer::on_light_update(light::LightState *obj) {
|
API_DISPATCH_UPDATE(light::LightState, light)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_light_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
API_DISPATCH_UPDATE_IGNORE_PARAMS(sensor::Sensor, sensor, float state)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_sensor_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
API_DISPATCH_UPDATE_IGNORE_PARAMS(switch_::Switch, switch, bool state)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_switch_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
API_DISPATCH_UPDATE_IGNORE_PARAMS(text_sensor::TextSensor, text_sensor, const std::string &state)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_text_sensor_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
void APIServer::on_climate_update(climate::Climate *obj) {
|
API_DISPATCH_UPDATE(climate::Climate, climate)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_climate_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
void APIServer::on_number_update(number::Number *obj, float state) {
|
API_DISPATCH_UPDATE_IGNORE_PARAMS(number::Number, number, float state)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_number_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
void APIServer::on_date_update(datetime::DateEntity *obj) {
|
API_DISPATCH_UPDATE(datetime::DateEntity, date)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_date_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
void APIServer::on_time_update(datetime::TimeEntity *obj) {
|
API_DISPATCH_UPDATE(datetime::TimeEntity, time)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_time_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
void APIServer::on_datetime_update(datetime::DateTimeEntity *obj) {
|
API_DISPATCH_UPDATE(datetime::DateTimeEntity, datetime)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_datetime_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void APIServer::on_text_update(text::Text *obj, const std::string &state) {
|
API_DISPATCH_UPDATE_IGNORE_PARAMS(text::Text, text, const std::string &state)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_text_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
void APIServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
|
API_DISPATCH_UPDATE_IGNORE_PARAMS(select::Select, select, const std::string &state, size_t index)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_select_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
void APIServer::on_lock_update(lock::Lock *obj) {
|
API_DISPATCH_UPDATE(lock::Lock, lock)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_lock_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
void APIServer::on_valve_update(valve::Valve *obj) {
|
API_DISPATCH_UPDATE(valve::Valve, valve)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_valve_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
void APIServer::on_media_player_update(media_player::MediaPlayer *obj) {
|
API_DISPATCH_UPDATE(media_player::MediaPlayer, media_player)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_media_player_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
|
// Event is a special case - it's the only entity that passes extra parameters to the send method
|
||||||
void APIServer::on_event(event::Event *obj, const std::string &event_type) {
|
void APIServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||||
|
if (obj->is_internal())
|
||||||
|
return;
|
||||||
for (auto &c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_event(obj, event_type);
|
c->send_event(obj, event_type);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
|
// Update is a special case - the method is called on_update, not on_update_update
|
||||||
void APIServer::on_update(update::UpdateEntity *obj) {
|
void APIServer::on_update(update::UpdateEntity *obj) {
|
||||||
|
if (obj->is_internal())
|
||||||
|
return;
|
||||||
for (auto &c : this->clients_)
|
for (auto &c : this->clients_)
|
||||||
c->send_update_state(obj);
|
c->send_update_state(obj);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
API_DISPATCH_UPDATE(alarm_control_panel::AlarmControlPanel, alarm_control_panel)
|
||||||
if (obj->is_internal())
|
|
||||||
return;
|
|
||||||
for (auto &c : this->clients_)
|
|
||||||
c->send_alarm_control_panel_state(obj);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
||||||
|
|
||||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
||||||
|
|
||||||
|
#ifdef USE_API_PASSWORD
|
||||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
||||||
|
#endif
|
||||||
|
|
||||||
void APIServer::set_batch_delay(uint32_t batch_delay) { this->batch_delay_ = batch_delay; }
|
void APIServer::set_batch_delay(uint16_t batch_delay) { this->batch_delay_ = batch_delay; }
|
||||||
|
|
||||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
for (auto &client : this->clients_) {
|
for (auto &client : this->clients_) {
|
||||||
@@ -479,7 +450,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
|
|||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
void APIServer::request_time() {
|
void APIServer::request_time() {
|
||||||
for (auto &client : this->clients_) {
|
for (auto &client : this->clients_) {
|
||||||
if (!client->remove_ && client->is_authenticated())
|
if (!client->flags_.remove && client->is_authenticated())
|
||||||
client->send_time_request();
|
client->send_time_request();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,8 +474,8 @@ void APIServer::on_shutdown() {
|
|||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
if (!c->send_message(DisconnectRequest())) {
|
if (!c->send_message(DisconnectRequest())) {
|
||||||
// If we can't send the disconnect request directly (tx_buffer full),
|
// If we can't send the disconnect request directly (tx_buffer full),
|
||||||
// schedule it in the batch so it will be sent with the 5ms timer
|
// schedule it at the front of the batch so it will be sent with priority
|
||||||
c->schedule_message_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE);
|
c->schedule_message_front_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ struct SavedNoisePsk {
|
|||||||
} PACKED; // NOLINT
|
} PACKED; // NOLINT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_API_YAML_SERVICES
|
||||||
|
// Forward declaration of helper function
|
||||||
|
const std::vector<UserServiceDescriptor *> &get_empty_user_services_instance();
|
||||||
|
#endif
|
||||||
|
|
||||||
class APIServer : public Component, public Controller {
|
class APIServer : public Component, public Controller {
|
||||||
public:
|
public:
|
||||||
APIServer();
|
APIServer();
|
||||||
@@ -35,13 +40,15 @@ class APIServer : public Component, public Controller {
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void on_shutdown() override;
|
void on_shutdown() override;
|
||||||
bool teardown() override;
|
bool teardown() override;
|
||||||
|
#ifdef USE_API_PASSWORD
|
||||||
bool check_password(const std::string &password) const;
|
bool check_password(const std::string &password) const;
|
||||||
bool uses_password() const;
|
bool uses_password() const;
|
||||||
void set_port(uint16_t port);
|
|
||||||
void set_password(const std::string &password);
|
void set_password(const std::string &password);
|
||||||
|
#endif
|
||||||
|
void set_port(uint16_t port);
|
||||||
void set_reboot_timeout(uint32_t reboot_timeout);
|
void set_reboot_timeout(uint32_t reboot_timeout);
|
||||||
void set_batch_delay(uint32_t batch_delay);
|
void set_batch_delay(uint16_t batch_delay);
|
||||||
uint32_t get_batch_delay() const { return batch_delay_; }
|
uint16_t get_batch_delay() const { return batch_delay_; }
|
||||||
|
|
||||||
// Get reference to shared buffer for API connections
|
// Get reference to shared buffer for API connections
|
||||||
std::vector<uint8_t> &get_shared_buffer_ref() { return shared_write_buffer_; }
|
std::vector<uint8_t> &get_shared_buffer_ref() { return shared_write_buffer_; }
|
||||||
@@ -54,7 +61,7 @@ class APIServer : public Component, public Controller {
|
|||||||
|
|
||||||
void handle_disconnect(APIConnection *conn);
|
void handle_disconnect(APIConnection *conn);
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
|
void on_binary_sensor_update(binary_sensor::BinarySensor *obj) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
void on_cover_update(cover::Cover *obj) override;
|
void on_cover_update(cover::Cover *obj) override;
|
||||||
@@ -105,7 +112,18 @@ class APIServer : public Component, public Controller {
|
|||||||
void on_media_player_update(media_player::MediaPlayer *obj) override;
|
void on_media_player_update(media_player::MediaPlayer *obj) override;
|
||||||
#endif
|
#endif
|
||||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
||||||
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
void register_user_service(UserServiceDescriptor *descriptor) {
|
||||||
|
#ifdef USE_API_YAML_SERVICES
|
||||||
|
// Vector is pre-allocated when services are defined in YAML
|
||||||
|
this->user_services_.push_back(descriptor);
|
||||||
|
#else
|
||||||
|
// Lazy allocate vector on first use for CustomAPIDevice
|
||||||
|
if (!this->user_services_) {
|
||||||
|
this->user_services_ = std::make_unique<std::vector<UserServiceDescriptor *>>();
|
||||||
|
}
|
||||||
|
this->user_services_->push_back(descriptor);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
void request_time();
|
void request_time();
|
||||||
#endif
|
#endif
|
||||||
@@ -134,27 +152,63 @@ class APIServer : public Component, public Controller {
|
|||||||
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||||
std::function<void(std::string)> f);
|
std::function<void(std::string)> f);
|
||||||
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
|
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
|
||||||
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
|
const std::vector<UserServiceDescriptor *> &get_user_services() const {
|
||||||
|
#ifdef USE_API_YAML_SERVICES
|
||||||
|
return this->user_services_;
|
||||||
|
#else
|
||||||
|
if (this->user_services_) {
|
||||||
|
return *this->user_services_;
|
||||||
|
}
|
||||||
|
// Return reference to global empty instance (no guard needed)
|
||||||
|
return get_empty_user_services_instance();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
||||||
Trigger<std::string, std::string> *get_client_connected_trigger() const { return this->client_connected_trigger_; }
|
Trigger<std::string, std::string> *get_client_connected_trigger() const { return this->client_connected_trigger_; }
|
||||||
|
#endif
|
||||||
|
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||||
Trigger<std::string, std::string> *get_client_disconnected_trigger() const {
|
Trigger<std::string, std::string> *get_client_disconnected_trigger() const {
|
||||||
return this->client_disconnected_trigger_;
|
return this->client_disconnected_trigger_;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool shutting_down_ = false;
|
void schedule_reboot_timeout_();
|
||||||
|
// Pointers and pointer-like types first (4 bytes each)
|
||||||
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
||||||
uint16_t port_{6053};
|
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
|
||||||
|
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
|
||||||
|
#endif
|
||||||
|
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
|
||||||
|
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 4-byte aligned types
|
||||||
uint32_t reboot_timeout_{300000};
|
uint32_t reboot_timeout_{300000};
|
||||||
uint32_t batch_delay_{100};
|
|
||||||
uint32_t last_connected_{0};
|
// Vectors and strings (12 bytes each on 32-bit)
|
||||||
std::vector<std::unique_ptr<APIConnection>> clients_;
|
std::vector<std::unique_ptr<APIConnection>> clients_;
|
||||||
|
#ifdef USE_API_PASSWORD
|
||||||
std::string password_;
|
std::string password_;
|
||||||
|
#endif
|
||||||
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
|
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
|
||||||
std::vector<HomeAssistantStateSubscription> state_subs_;
|
std::vector<HomeAssistantStateSubscription> state_subs_;
|
||||||
|
#ifdef USE_API_YAML_SERVICES
|
||||||
|
// When services are defined in YAML, we know at compile time that services will be registered
|
||||||
std::vector<UserServiceDescriptor *> user_services_;
|
std::vector<UserServiceDescriptor *> user_services_;
|
||||||
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
|
#else
|
||||||
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
|
// Services can still be registered at runtime by CustomAPIDevice components even when not
|
||||||
|
// defined in YAML. Using unique_ptr allows lazy allocation, saving 12 bytes in the common
|
||||||
|
// case where no services (YAML or custom) are used.
|
||||||
|
std::unique_ptr<std::vector<UserServiceDescriptor *>> user_services_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Group smaller types together
|
||||||
|
uint16_t port_{6053};
|
||||||
|
uint16_t batch_delay_{100};
|
||||||
|
bool shutting_down_ = false;
|
||||||
|
// 5 bytes used, 3 bytes padding
|
||||||
|
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
|
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ import asyncio
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
import warnings
|
||||||
|
|
||||||
from aioesphomeapi import APIClient, parse_log_message
|
# Suppress protobuf version warnings
|
||||||
from aioesphomeapi.log_runner import async_run
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings(
|
||||||
|
"ignore", category=UserWarning, message=".*Protobuf gencode version.*"
|
||||||
|
)
|
||||||
|
from aioesphomeapi import APIClient, parse_log_message
|
||||||
|
from aioesphomeapi.log_runner import async_run
|
||||||
|
|
||||||
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
|
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "list_entities.h"
|
#include "list_entities.h"
|
||||||
#ifdef USE_API
|
#ifdef USE_API
|
||||||
#include "api_connection.h"
|
#include "api_connection.h"
|
||||||
|
#include "api_pb2.h"
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/util.h"
|
#include "esphome/core/util.h"
|
||||||
@@ -8,155 +9,85 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
// Generate entity handler implementations using macros
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
LIST_ENTITIES_HANDLER(binary_sensor, binary_sensor::BinarySensor, ListEntitiesBinarySensorResponse)
|
||||||
this->client_->send_binary_sensor_info(binary_sensor);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool ListEntitiesIterator::on_cover(cover::Cover *cover) {
|
LIST_ENTITIES_HANDLER(cover, cover::Cover, ListEntitiesCoverResponse)
|
||||||
this->client_->send_cover_info(cover);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool ListEntitiesIterator::on_fan(fan::Fan *fan) {
|
LIST_ENTITIES_HANDLER(fan, fan::Fan, ListEntitiesFanResponse)
|
||||||
this->client_->send_fan_info(fan);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool ListEntitiesIterator::on_light(light::LightState *light) {
|
LIST_ENTITIES_HANDLER(light, light::LightState, ListEntitiesLightResponse)
|
||||||
this->client_->send_light_info(light);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) {
|
LIST_ENTITIES_HANDLER(sensor, sensor::Sensor, ListEntitiesSensorResponse)
|
||||||
this->client_->send_sensor_info(sensor);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
|
LIST_ENTITIES_HANDLER(switch, switch_::Switch, ListEntitiesSwitchResponse)
|
||||||
this->client_->send_switch_info(a_switch);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
bool ListEntitiesIterator::on_button(button::Button *button) {
|
LIST_ENTITIES_HANDLER(button, button::Button, ListEntitiesButtonResponse)
|
||||||
this->client_->send_button_info(button);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
LIST_ENTITIES_HANDLER(text_sensor, text_sensor::TextSensor, ListEntitiesTextSensorResponse)
|
||||||
this->client_->send_text_sensor_info(text_sensor);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) {
|
LIST_ENTITIES_HANDLER(lock, lock::Lock, ListEntitiesLockResponse)
|
||||||
this->client_->send_lock_info(a_lock);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
bool ListEntitiesIterator::on_valve(valve::Valve *valve) {
|
LIST_ENTITIES_HANDLER(valve, valve::Valve, ListEntitiesValveResponse)
|
||||||
this->client_->send_valve_info(valve);
|
#endif
|
||||||
return true;
|
#ifdef USE_CAMERA
|
||||||
}
|
LIST_ENTITIES_HANDLER(camera, camera::Camera, ListEntitiesCameraResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_CLIMATE
|
||||||
|
LIST_ENTITIES_HANDLER(climate, climate::Climate, ListEntitiesClimateResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
LIST_ENTITIES_HANDLER(number, number::Number, ListEntitiesNumberResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATE
|
||||||
|
LIST_ENTITIES_HANDLER(date, datetime::DateEntity, ListEntitiesDateResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
LIST_ENTITIES_HANDLER(time, datetime::TimeEntity, ListEntitiesTimeResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_DATETIME
|
||||||
|
LIST_ENTITIES_HANDLER(datetime, datetime::DateTimeEntity, ListEntitiesDateTimeResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_TEXT
|
||||||
|
LIST_ENTITIES_HANDLER(text, text::Text, ListEntitiesTextResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
LIST_ENTITIES_HANDLER(select, select::Select, ListEntitiesSelectResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_MEDIA_PLAYER
|
||||||
|
LIST_ENTITIES_HANDLER(media_player, media_player::MediaPlayer, ListEntitiesMediaPlayerResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
|
LIST_ENTITIES_HANDLER(alarm_control_panel, alarm_control_panel::AlarmControlPanel,
|
||||||
|
ListEntitiesAlarmControlPanelResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EVENT
|
||||||
|
LIST_ENTITIES_HANDLER(event, event::Event, ListEntitiesEventResponse)
|
||||||
|
#endif
|
||||||
|
#ifdef USE_UPDATE
|
||||||
|
LIST_ENTITIES_HANDLER(update, update::UpdateEntity, ListEntitiesUpdateResponse)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Special cases that don't follow the pattern
|
||||||
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
|
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
|
||||||
|
|
||||||
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
|
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
|
||||||
|
|
||||||
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
|
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
|
||||||
auto resp = service->encode_list_service_response();
|
auto resp = service->encode_list_service_response();
|
||||||
return this->client_->send_message(resp);
|
return this->client_->send_message(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
|
||||||
bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
|
|
||||||
this->client_->send_camera_info(camera);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_CLIMATE
|
|
||||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
|
|
||||||
this->client_->send_climate_info(climate);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_NUMBER
|
|
||||||
bool ListEntitiesIterator::on_number(number::Number *number) {
|
|
||||||
this->client_->send_number_info(number);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_DATETIME_DATE
|
|
||||||
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) {
|
|
||||||
this->client_->send_date_info(date);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_DATETIME_TIME
|
|
||||||
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) {
|
|
||||||
this->client_->send_time_info(time);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_DATETIME_DATETIME
|
|
||||||
bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
|
||||||
this->client_->send_datetime_info(datetime);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
|
||||||
bool ListEntitiesIterator::on_text(text::Text *text) {
|
|
||||||
this->client_->send_text_info(text);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_SELECT
|
|
||||||
bool ListEntitiesIterator::on_select(select::Select *select) {
|
|
||||||
this->client_->send_select_info(select);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_MEDIA_PLAYER
|
|
||||||
bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) {
|
|
||||||
this->client_->send_media_player_info(media_player);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
|
||||||
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
|
||||||
this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_EVENT
|
|
||||||
bool ListEntitiesIterator::on_event(event::Event *event) {
|
|
||||||
this->client_->send_event_info(event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_UPDATE
|
|
||||||
bool ListEntitiesIterator::on_update(update::UpdateEntity *update) {
|
|
||||||
this->client_->send_update_info(update);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,75 +9,83 @@ namespace api {
|
|||||||
|
|
||||||
class APIConnection;
|
class APIConnection;
|
||||||
|
|
||||||
|
// Macro for generating ListEntitiesIterator handlers
|
||||||
|
// Calls schedule_message_ with try_send_*_info
|
||||||
|
#define LIST_ENTITIES_HANDLER(entity_type, EntityClass, ResponseType) \
|
||||||
|
bool ListEntitiesIterator::on_##entity_type(EntityClass *entity) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||||
|
return this->client_->schedule_message_(entity, &APIConnection::try_send_##entity_type##_info, \
|
||||||
|
ResponseType::MESSAGE_TYPE); \
|
||||||
|
}
|
||||||
|
|
||||||
class ListEntitiesIterator : public ComponentIterator {
|
class ListEntitiesIterator : public ComponentIterator {
|
||||||
public:
|
public:
|
||||||
ListEntitiesIterator(APIConnection *client);
|
ListEntitiesIterator(APIConnection *client);
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
|
bool on_binary_sensor(binary_sensor::BinarySensor *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool on_cover(cover::Cover *cover) override;
|
bool on_cover(cover::Cover *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool on_fan(fan::Fan *fan) override;
|
bool on_fan(fan::Fan *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool on_light(light::LightState *light) override;
|
bool on_light(light::LightState *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool on_sensor(sensor::Sensor *sensor) override;
|
bool on_sensor(sensor::Sensor *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool on_switch(switch_::Switch *a_switch) override;
|
bool on_switch(switch_::Switch *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
bool on_button(button::Button *button) override;
|
bool on_button(button::Button *entity) override;
|
||||||
#endif
|
#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 *entity) override;
|
||||||
#endif
|
#endif
|
||||||
bool on_service(UserServiceDescriptor *service) override;
|
bool on_service(UserServiceDescriptor *service) override;
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_CAMERA
|
||||||
bool on_camera(esp32_camera::ESP32Camera *camera) override;
|
bool on_camera(camera::Camera *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool on_climate(climate::Climate *climate) override;
|
bool on_climate(climate::Climate *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool on_number(number::Number *number) override;
|
bool on_number(number::Number *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool on_date(datetime::DateEntity *date) override;
|
bool on_date(datetime::DateEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
bool on_time(datetime::TimeEntity *time) override;
|
bool on_time(datetime::TimeEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
bool on_datetime(datetime::DateTimeEntity *datetime) override;
|
bool on_datetime(datetime::DateTimeEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool on_text(text::Text *text) override;
|
bool on_text(text::Text *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
bool on_select(select::Select *select) override;
|
bool on_select(select::Select *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool on_lock(lock::Lock *a_lock) override;
|
bool on_lock(lock::Lock *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
bool on_valve(valve::Valve *valve) override;
|
bool on_valve(valve::Valve *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool on_media_player(media_player::MediaPlayer *media_player) override;
|
bool on_media_player(media_player::MediaPlayer *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
bool on_event(event::Event *event) override;
|
bool on_event(event::Event *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
bool on_update(update::UpdateEntity *update) override;
|
bool on_update(update::UpdateEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
bool on_end() override;
|
bool on_end() override;
|
||||||
bool completed() { return this->state_ == IteratorState::NONE; }
|
bool completed() { return this->state_ == IteratorState::NONE; }
|
||||||
|
|||||||
@@ -327,12 +327,15 @@ class ProtoWriteBuffer {
|
|||||||
class ProtoMessage {
|
class ProtoMessage {
|
||||||
public:
|
public:
|
||||||
virtual ~ProtoMessage() = default;
|
virtual ~ProtoMessage() = default;
|
||||||
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
// Default implementation for messages with no fields
|
||||||
|
virtual void encode(ProtoWriteBuffer buffer) const {}
|
||||||
void decode(const uint8_t *buffer, size_t length);
|
void decode(const uint8_t *buffer, size_t length);
|
||||||
virtual void calculate_size(uint32_t &total_size) const = 0;
|
// Default implementation for messages with no fields
|
||||||
|
virtual void calculate_size(uint32_t &total_size) const {}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
std::string dump() const;
|
std::string dump() const;
|
||||||
virtual void dump_to(std::string &out) const = 0;
|
virtual void dump_to(std::string &out) const = 0;
|
||||||
|
virtual const char *message_name() const { return "unknown"; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -361,7 +364,7 @@ class ProtoService {
|
|||||||
*/
|
*/
|
||||||
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
|
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
|
||||||
virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0;
|
virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0;
|
||||||
virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
|
virtual void read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
|
||||||
|
|
||||||
// Optimized method that pre-allocates buffer based on message size
|
// Optimized method that pre-allocates buffer based on message size
|
||||||
bool send_message_(const ProtoMessage &msg, uint16_t message_type) {
|
bool send_message_(const ProtoMessage &msg, uint16_t message_type) {
|
||||||
@@ -377,6 +380,26 @@ class ProtoService {
|
|||||||
// Send the buffer
|
// Send the buffer
|
||||||
return this->send_buffer(buffer, message_type);
|
return this->send_buffer(buffer, message_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Authentication helper methods
|
||||||
|
bool check_connection_setup_() {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_authenticated_() {
|
||||||
|
if (!this->check_connection_setup_()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|||||||
@@ -6,73 +6,67 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
// Generate entity handler implementations using macros
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
INITIAL_STATE_HANDLER(binary_sensor, binary_sensor::BinarySensor)
|
||||||
return this->client_->send_binary_sensor_state(binary_sensor);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); }
|
INITIAL_STATE_HANDLER(cover, cover::Cover)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_state(fan); }
|
INITIAL_STATE_HANDLER(fan, fan::Fan)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
|
INITIAL_STATE_HANDLER(light, light::LightState)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); }
|
INITIAL_STATE_HANDLER(sensor, sensor::Sensor)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); }
|
INITIAL_STATE_HANDLER(switch, switch_::Switch)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
INITIAL_STATE_HANDLER(text_sensor, text_sensor::TextSensor)
|
||||||
return this->client_->send_text_sensor_state(text_sensor);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
|
INITIAL_STATE_HANDLER(climate, climate::Climate)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); }
|
INITIAL_STATE_HANDLER(number, number::Number)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
|
INITIAL_STATE_HANDLER(date, datetime::DateEntity)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
bool InitialStateIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_state(time); }
|
INITIAL_STATE_HANDLER(time, datetime::TimeEntity)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
INITIAL_STATE_HANDLER(datetime, datetime::DateTimeEntity)
|
||||||
return this->client_->send_datetime_state(datetime);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text); }
|
INITIAL_STATE_HANDLER(text, text::Text)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); }
|
INITIAL_STATE_HANDLER(select, select::Select)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock); }
|
INITIAL_STATE_HANDLER(lock, lock::Lock)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); }
|
INITIAL_STATE_HANDLER(valve, valve::Valve)
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool InitialStateIterator::on_media_player(media_player::MediaPlayer *media_player) {
|
INITIAL_STATE_HANDLER(media_player, media_player::MediaPlayer)
|
||||||
return this->client_->send_media_player_state(media_player);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool InitialStateIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
INITIAL_STATE_HANDLER(alarm_control_panel, alarm_control_panel::AlarmControlPanel)
|
||||||
return this->client_->send_alarm_control_panel_state(a_alarm_control_panel);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
bool InitialStateIterator::on_update(update::UpdateEntity *update) { return this->client_->send_update_state(update); }
|
INITIAL_STATE_HANDLER(update, update::UpdateEntity)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Special cases (button and event) are already defined inline in subscribe_state.h
|
||||||
|
|
||||||
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
|
InitialStateIterator::InitialStateIterator(APIConnection *client) : client_(client) {}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|||||||
@@ -10,71 +10,78 @@ namespace api {
|
|||||||
|
|
||||||
class APIConnection;
|
class APIConnection;
|
||||||
|
|
||||||
|
// Macro for generating InitialStateIterator handlers
|
||||||
|
// Calls send_*_state
|
||||||
|
#define INITIAL_STATE_HANDLER(entity_type, EntityClass) \
|
||||||
|
bool InitialStateIterator::on_##entity_type(EntityClass *entity) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||||
|
return this->client_->send_##entity_type##_state(entity); \
|
||||||
|
}
|
||||||
|
|
||||||
class InitialStateIterator : public ComponentIterator {
|
class InitialStateIterator : public ComponentIterator {
|
||||||
public:
|
public:
|
||||||
InitialStateIterator(APIConnection *client);
|
InitialStateIterator(APIConnection *client);
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) override;
|
bool on_binary_sensor(binary_sensor::BinarySensor *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool on_cover(cover::Cover *cover) override;
|
bool on_cover(cover::Cover *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool on_fan(fan::Fan *fan) override;
|
bool on_fan(fan::Fan *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool on_light(light::LightState *light) override;
|
bool on_light(light::LightState *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool on_sensor(sensor::Sensor *sensor) override;
|
bool on_sensor(sensor::Sensor *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool on_switch(switch_::Switch *a_switch) override;
|
bool on_switch(switch_::Switch *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
bool on_button(button::Button *button) override { return true; };
|
bool on_button(button::Button *button) override { return true; };
|
||||||
#endif
|
#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 *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool on_climate(climate::Climate *climate) override;
|
bool on_climate(climate::Climate *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool on_number(number::Number *number) override;
|
bool on_number(number::Number *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool on_date(datetime::DateEntity *date) override;
|
bool on_date(datetime::DateEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
bool on_time(datetime::TimeEntity *time) override;
|
bool on_time(datetime::TimeEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
bool on_datetime(datetime::DateTimeEntity *datetime) override;
|
bool on_datetime(datetime::DateTimeEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool on_text(text::Text *text) override;
|
bool on_text(text::Text *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
bool on_select(select::Select *select) override;
|
bool on_select(select::Select *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool on_lock(lock::Lock *a_lock) override;
|
bool on_lock(lock::Lock *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
bool on_valve(valve::Valve *valve) override;
|
bool on_valve(valve::Valve *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool on_media_player(media_player::MediaPlayer *media_player) override;
|
bool on_media_player(media_player::MediaPlayer *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) override;
|
bool on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *entity) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
bool on_event(event::Event *event) override { return true; };
|
bool on_event(event::Event *event) override { return true; };
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
bool on_update(update::UpdateEntity *update) override;
|
bool on_update(update::UpdateEntity *entity) override;
|
||||||
#endif
|
#endif
|
||||||
bool completed() { return this->state_ == IteratorState::NONE; }
|
bool completed() { return this->state_ == IteratorState::NONE; }
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ class AS5600Component : public Component, public i2c::I2CDevice {
|
|||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
/// HARDWARE_LATE setup priority
|
/// HARDWARE_LATE setup priority
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
|
|
||||||
// configuration setters
|
// configuration setters
|
||||||
void set_dir_pin(InternalGPIOPin *pin) { this->dir_pin_ = pin; }
|
void set_dir_pin(InternalGPIOPin *pin) { this->dir_pin_ = pin; }
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from esphome.const import (
|
|||||||
PLATFORM_BK72XX,
|
PLATFORM_BK72XX,
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
|
PLATFORM_LN882X,
|
||||||
PLATFORM_RTL87XX,
|
PLATFORM_RTL87XX,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
@@ -14,15 +15,23 @@ CODEOWNERS = ["@OttoWinter"]
|
|||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema({}),
|
cv.Schema({}),
|
||||||
cv.only_with_arduino,
|
cv.only_with_arduino,
|
||||||
cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]),
|
cv.only_on(
|
||||||
|
[
|
||||||
|
PLATFORM_ESP32,
|
||||||
|
PLATFORM_ESP8266,
|
||||||
|
PLATFORM_BK72XX,
|
||||||
|
PLATFORM_LN882X,
|
||||||
|
PLATFORM_RTL87XX,
|
||||||
|
]
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(200.0)
|
@coroutine_with_priority(200.0)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
if CORE.is_esp32 or CORE.is_libretiny:
|
if CORE.is_esp32 or CORE.is_libretiny:
|
||||||
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
# https://github.com/ESP32Async/AsyncTCP
|
||||||
cg.add_library("esphome/AsyncTCP-esphome", "2.1.4")
|
cg.add_library("ESP32Async/AsyncTCP", "3.4.4")
|
||||||
elif CORE.is_esp8266:
|
elif CORE.is_esp8266:
|
||||||
# https://github.com/esphome/ESPAsyncTCP
|
# https://github.com/ESP32Async/ESPAsyncTCP
|
||||||
cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")
|
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
|
|||||||
|
|
||||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
|
||||||
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_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
|
void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user