Merge remote-tracking branch 'origin/dev' into jesserockz-2023-284

This commit is contained in:
Jesse Hills 2023-10-11 16:32:26 +13:00
commit 89b3af8be4
No known key found for this signature in database
GPG key ID: BEAAE804EFD8E83A
287 changed files with 13703 additions and 1660 deletions

View file

@ -17,12 +17,12 @@ runs:
steps: steps:
- name: Set up Python ${{ inputs.python-version }} - name: Set up Python ${{ inputs.python-version }}
id: python id: python
uses: actions/setup-python@v4.6.0 uses: actions/setup-python@v4.7.0
with: with:
python-version: ${{ inputs.python-version }} python-version: ${{ inputs.python-version }}
- name: Restore Python virtual environment - name: Restore Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache/restore@v3.3.1 uses: actions/cache/restore@v3.3.2
with: with:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length

View file

@ -8,7 +8,7 @@ on:
branches: [dev, beta, release] branches: [dev, beta, release]
paths: paths:
- "docker/**" - "docker/**"
- ".github/workflows/**" - ".github/workflows/ci-docker.yml"
- "requirements*.txt" - "requirements*.txt"
- "platformio.ini" - "platformio.ini"
- "script/platformio_install_deps.py" - "script/platformio_install_deps.py"
@ -16,7 +16,7 @@ on:
pull_request: pull_request:
paths: paths:
- "docker/**" - "docker/**"
- ".github/workflows/**" - ".github/workflows/ci-docker.yml"
- "requirements*.txt" - "requirements*.txt"
- "platformio.ini" - "platformio.ini"
- "script/platformio_install_deps.py" - "script/platformio_install_deps.py"
@ -40,15 +40,15 @@ jobs:
arch: [amd64, armv7, aarch64] arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker", "lint"] build_type: ["ha-addon", "docker", "lint"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4.1.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4.7.1
with: with:
python-version: "3.9" python-version: "3.9"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3.0.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3.0.0
- name: Set TAG - name: Set TAG
run: | run: |

View file

@ -7,6 +7,10 @@ on:
branches: [dev, beta, release] branches: [dev, beta, release]
pull_request: pull_request:
paths:
- "**"
- "!.github/workflows/*.yml"
- ".github/workflows/ci.yml"
merge_group: merge_group:
permissions: permissions:
@ -30,18 +34,18 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }} cache-key: ${{ steps.cache-key.outputs.key }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Generate cache-key - name: Generate cache-key
id: cache-key id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v4.6.0 uses: actions/setup-python@v4.7.1
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment - name: Restore Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.3.1 uses: actions/cache@v3.3.2
with: with:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
@ -55,15 +59,6 @@ jobs:
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
pip install -e . pip install -e .
yamllint:
name: yamllint
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.2
- name: Run yamllint
uses: frenck/action-yamllint@v1.4.1
black: black:
name: Check black name: Check black
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -71,7 +66,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -92,7 +87,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -113,7 +108,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -134,7 +129,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -155,7 +150,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -176,7 +171,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -196,7 +191,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -215,6 +210,17 @@ jobs:
run: script/ci-suggest-changes run: script/ci-suggest-changes
if: always() if: always()
compile-tests-list:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.0
- name: Find all YAML test files
id: set-matrix
run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
compile-tests: compile-tests:
name: Run YAML test ${{ matrix.file }} name: Run YAML test ${{ matrix.file }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -227,24 +233,24 @@ jobs:
- pylint - pylint
- pytest - pytest
- pyupgrade - pyupgrade
- yamllint - compile-tests-list
strategy: strategy:
fail-fast: false fail-fast: false
max-parallel: 2 max-parallel: 2
matrix: matrix:
file: [1, 2, 3, 3.1, 4, 5, 6, 7, 8] file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run esphome compile tests/test${{ matrix.file }}.yaml - name: Run esphome compile ${{ matrix.file }}
run: | run: |
. venv/bin/activate . venv/bin/activate
esphome compile tests/test${{ matrix.file }}.yaml esphome compile ${{ matrix.file }}
clang-tidy: clang-tidy:
name: ${{ matrix.name }} name: ${{ matrix.name }}
@ -258,7 +264,6 @@ jobs:
- pylint - pylint
- pytest - pytest
- pyupgrade - pyupgrade
- yamllint
strategy: strategy:
fail-fast: false fail-fast: false
max-parallel: 2 max-parallel: 2
@ -291,14 +296,14 @@ jobs:
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.1.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio - name: Cache platformio
uses: actions/cache@v3.3.1 uses: actions/cache@v3.3.2
with: with:
path: ~/.platformio path: ~/.platformio
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
@ -337,7 +342,6 @@ jobs:
- pylint - pylint
- pytest - pytest
- pyupgrade - pyupgrade
- yamllint
- compile-tests - compile-tests
- clang-tidy - clang-tidy
if: always() if: always()

View file

@ -18,7 +18,7 @@ jobs:
lock: lock:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v4 - uses: dessant/lock-threads@v4.0.1
with: with:
pr-inactive-days: "1" pr-inactive-days: "1"
pr-lock-reason: "" pr-lock-reason: ""

View file

@ -19,7 +19,7 @@ jobs:
outputs: outputs:
tag: ${{ steps.tag.outputs.tag }} tag: ${{ steps.tag.outputs.tag }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4.1.0
- name: Get tag - name: Get tag
id: tag id: tag
# yamllint disable rule:line-length # yamllint disable rule:line-length
@ -43,9 +43,9 @@ jobs:
if: github.repository == 'esphome/esphome' && github.event_name == 'release' if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4.1.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4.7.1
with: with:
python-version: "3.x" python-version: "3.x"
- name: Set up python environment - name: Set up python environment
@ -88,24 +88,24 @@ jobs:
target: "lint" target: "lint"
baseimg: "docker" baseimg: "docker"
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4.1.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4.7.1
with: with:
python-version: "3.9" python-version: "3.9"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v3.0.0
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v3.0.0
- name: Log in to docker hub - name: Log in to docker hub
uses: docker/login-action@v2 uses: docker/login-action@v3.0.0
with: with:
username: ${{ secrets.DOCKER_USER }} username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry - name: Log in to the GitHub container registry
uses: docker/login-action@v2 uses: docker/login-action@v3.0.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@ -119,7 +119,7 @@ jobs:
--suffix "${{ matrix.image.suffix }}" --suffix "${{ matrix.image.suffix }}"
- name: Build and push - name: Build and push
uses: docker/build-push-action@v4 uses: docker/build-push-action@v5.0.0
with: with:
context: . context: .
file: ./docker/Dockerfile file: ./docker/Dockerfile
@ -141,7 +141,7 @@ jobs:
needs: [deploy-docker] needs: [deploy-docker]
steps: steps:
- name: Trigger Workflow - name: Trigger Workflow
uses: actions/github-script@v6 uses: actions/github-script@v6.4.1
with: with:
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }} github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
script: | script: |

View file

@ -18,7 +18,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v8 - uses: actions/stale@v8.0.0
with: with:
days-before-pr-stale: 90 days-before-pr-stale: 90
days-before-pr-close: 7 days-before-pr-close: 7
@ -38,7 +38,7 @@ jobs:
close-issues: close-issues:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v8 - uses: actions/stale@v8.0.0
with: with:
days-before-pr-stale: -1 days-before-pr-stale: -1
days-before-pr-close: -1 days-before-pr-close: -1

View file

@ -4,8 +4,7 @@ name: Synchronise Device Classes from Home Assistant
on: on:
workflow_dispatch: workflow_dispatch:
schedule: schedule:
- cron: '45 6 * * *' - cron: "45 6 * * *"
jobs: jobs:
sync: sync:
@ -14,16 +13,16 @@ jobs:
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4.1.0
- name: Checkout Home Assistant - name: Checkout Home Assistant
uses: actions/checkout@v3 uses: actions/checkout@v4.1.0
with: with:
repository: home-assistant/core repository: home-assistant/core
path: lib/home-assistant path: lib/home-assistant
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4.7.1
with: with:
python-version: 3.11 python-version: 3.11
@ -37,7 +36,7 @@ jobs:
python ./script/sync-device_class.py python ./script/sync-device_class.py
- name: Commit changes - name: Commit changes
uses: peter-evans/create-pull-request@v5 uses: peter-evans/create-pull-request@v5.0.2
with: with:
commit-message: "Synchronise Device Classes from Home Assistant" commit-message: "Synchronise Device Classes from Home Assistant"
committer: esphomebot <esphome@nabucasa.com> committer: esphomebot <esphome@nabucasa.com>

22
.github/workflows/yaml-lint.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: YAML lint
on:
push:
branches: [dev, beta, release]
paths:
- "**.yaml"
- "**.yml"
pull_request:
paths:
- "**.yaml"
- "**.yml"
jobs:
yamllint:
name: yamllint
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.0
- name: Run yamllint
uses: frenck/action-yamllint@v1.4.1

6
.gitignore vendored
View file

@ -13,6 +13,12 @@ __pycache__/
# Intellij Idea # Intellij Idea
.idea .idea
# Eclipse
.project
.cproject
.pydevproject
.settings/
# Vim # Vim
*.swp *.swp

View file

@ -2,8 +2,8 @@
# See https://pre-commit.com for more information # See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks # See https://pre-commit.com/hooks.html for more hooks
repos: repos:
- repo: https://github.com/psf/black - repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.7.0 rev: 23.9.1
hooks: hooks:
- id: black - id: black
args: args:

View file

@ -42,12 +42,14 @@ esphome/components/bedjet/climate/* @jhansche
esphome/components/bedjet/fan/* @jhansche esphome/components/bedjet/fan/* @jhansche
esphome/components/bh1750/* @OttoWinter esphome/components/bh1750/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core esphome/components/binary_sensor/* @esphome/core
esphome/components/bk72xx/* @kuba2k2
esphome/components/bl0939/* @ziceva esphome/components/bl0939/* @ziceva
esphome/components/bl0940/* @tobias- esphome/components/bl0940/* @tobias-
esphome/components/bl0942/* @dbuezas esphome/components/bl0942/* @dbuezas
esphome/components/ble_client/* @buxtronix esphome/components/ble_client/* @buxtronix
esphome/components/bluetooth_proxy/* @jesserockz esphome/components/bluetooth_proxy/* @jesserockz
esphome/components/bme680_bsec/* @trvrnrth esphome/components/bme680_bsec/* @trvrnrth
esphome/components/bmi160/* @flaviut
esphome/components/bmp3xx/* @martgras esphome/components/bmp3xx/* @martgras
esphome/components/bmp581/* @kahrendt esphome/components/bmp581/* @kahrendt
esphome/components/bp1658cj/* @Cossid esphome/components/bp1658cj/* @Cossid
@ -130,7 +132,7 @@ esphome/components/i2s_audio/* @jesserockz
esphome/components/i2s_audio/media_player/* @jesserockz esphome/components/i2s_audio/media_player/* @jesserockz
esphome/components/i2s_audio/microphone/* @jesserockz esphome/components/i2s_audio/microphone/* @jesserockz
esphome/components/i2s_audio/speaker/* @jesserockz esphome/components/i2s_audio/speaker/* @jesserockz
esphome/components/ili9xxx/* @nielsnl68 esphome/components/ili9xxx/* @clydebarrow @nielsnl68
esphome/components/improv_base/* @esphome/core esphome/components/improv_base/* @esphome/core
esphome/components/improv_serial/* @esphome/core esphome/components/improv_serial/* @esphome/core
esphome/components/ina260/* @mreditor97 esphome/components/ina260/* @mreditor97
@ -147,7 +149,10 @@ esphome/components/kuntze/* @ssieb
esphome/components/lcd_menu/* @numo68 esphome/components/lcd_menu/* @numo68
esphome/components/ld2410/* @regevbr @sebcaps esphome/components/ld2410/* @regevbr @sebcaps
esphome/components/ledc/* @OttoWinter esphome/components/ledc/* @OttoWinter
esphome/components/libretiny/* @kuba2k2
esphome/components/libretiny_pwm/* @kuba2k2
esphome/components/light/* @esphome/core esphome/components/light/* @esphome/core
esphome/components/lightwaverf/* @max246
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
esphome/components/lock/* @esphome/core esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core esphome/components/logger/* @esphome/core
@ -182,6 +187,7 @@ esphome/components/mitsubishi/* @RubyBailey
esphome/components/mlx90393/* @functionpointer esphome/components/mlx90393/* @functionpointer
esphome/components/mlx90614/* @jesserockz esphome/components/mlx90614/* @jesserockz
esphome/components/mmc5603/* @benhoff esphome/components/mmc5603/* @benhoff
esphome/components/mmc5983/* @agoode
esphome/components/modbus_controller/* @martgras esphome/components/modbus_controller/* @martgras
esphome/components/modbus_controller/binary_sensor/* @martgras esphome/components/modbus_controller/binary_sensor/* @martgras
esphome/components/modbus_controller/number/* @martgras esphome/components/modbus_controller/number/* @martgras
@ -235,6 +241,7 @@ esphome/components/rgbct/* @jesserockz
esphome/components/rp2040/* @jesserockz esphome/components/rp2040/* @jesserockz
esphome/components/rp2040_pio_led_strip/* @Papa-DMan esphome/components/rp2040_pio_led_strip/* @Papa-DMan
esphome/components/rp2040_pwm/* @jesserockz esphome/components/rp2040_pwm/* @jesserockz
esphome/components/rtl87xx/* @kuba2k2
esphome/components/rtttl/* @glmnet esphome/components/rtttl/* @glmnet
esphome/components/safe_mode/* @jsuanet @paulmonigatti esphome/components/safe_mode/* @jsuanet @paulmonigatti
esphome/components/scd4x/* @martgras @sjtrny esphome/components/scd4x/* @martgras @sjtrny
@ -243,6 +250,7 @@ esphome/components/sdm_meter/* @jesserockz @polyfaces
esphome/components/sdp3x/* @Azimath esphome/components/sdp3x/* @Azimath
esphome/components/selec_meter/* @sourabhjaiswal esphome/components/selec_meter/* @sourabhjaiswal
esphome/components/select/* @esphome/core esphome/components/select/* @esphome/core
esphome/components/sen0321/* @notjj
esphome/components/sen21231/* @shreyaskarnik esphome/components/sen21231/* @shreyaskarnik
esphome/components/sen5x/* @martgras esphome/components/sen5x/* @martgras
esphome/components/sensirion_common/* @martgras esphome/components/sensirion_common/* @martgras
@ -264,7 +272,9 @@ esphome/components/sn74hc165/* @jesserockz
esphome/components/socket/* @esphome/core esphome/components/socket/* @esphome/core
esphome/components/sonoff_d1/* @anatoly-savchenkov esphome/components/sonoff_d1/* @anatoly-savchenkov
esphome/components/speaker/* @jesserockz esphome/components/speaker/* @jesserockz
esphome/components/spi/* @esphome/core esphome/components/spi/* @clydebarrow @esphome/core
esphome/components/spi_device/* @clydebarrow
esphome/components/spi_led_strip/* @clydebarrow
esphome/components/sprinkler/* @kbx81 esphome/components/sprinkler/* @kbx81
esphome/components/sps30/* @martgras esphome/components/sps30/* @martgras
esphome/components/ssd1322_base/* @kbx81 esphome/components/ssd1322_base/* @kbx81
@ -325,6 +335,7 @@ esphome/components/web_server_idf/* @dentra
esphome/components/whirlpool/* @glmnet esphome/components/whirlpool/* @glmnet
esphome/components/whynter/* @aeonsablaze esphome/components/whynter/* @aeonsablaze
esphome/components/wiegand/* @ssieb esphome/components/wiegand/* @ssieb
esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
esphome/components/wl_134/* @hobbypunk90 esphome/components/wl_134/* @hobbypunk90
esphome/components/x9c/* @EtienneMD esphome/components/x9c/* @EtienneMD
esphome/components/xiaomi_lywsd03mmc/* @ahpohl esphome/components/xiaomi_lywsd03mmc/* @ahpohl

View file

@ -26,10 +26,11 @@ RUN \
python3-venv=3.9.2-3 \ python3-venv=3.9.2-3 \
iputils-ping=3:20210202-1 \ iputils-ping=3:20210202-1 \
git=1:2.30.2-1+deb11u2 \ git=1:2.30.2-1+deb11u2 \
curl=7.74.0-1.3+deb11u7 \ curl=7.74.0-1.3+deb11u9 \
openssh-client=1:8.4p1-5+deb11u1 \ openssh-client=1:8.4p1-5+deb11u2 \
python3-cffi=1.14.5-1 \ python3-cffi=1.14.5-1 \
libcairo2=1.16.0-5; \ libcairo2=1.16.0-5 \
patch=2.7.6-7; \
if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
build-essential=12.9 \ build-essential=12.9 \
@ -61,7 +62,7 @@ RUN \
# Ubuntu python3-pip is missing wheel # Ubuntu python3-pip is missing wheel
pip3 install --no-cache-dir \ pip3 install --no-cache-dir \
wheel==0.37.1 \ wheel==0.37.1 \
platformio==6.1.10 \ platformio==6.1.11 \
# Change some platformio settings # Change some platformio settings
&& platformio settings set enable_telemetry No \ && platformio settings set enable_telemetry No \
&& platformio settings set check_platformio_interval 1000000 \ && platformio settings set check_platformio_interval 1000000 \

View file

@ -35,11 +35,23 @@ if bashio::config.has_value 'default_compile_process_limit'; then
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit') export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit')
else else
if grep -q 'Raspberry Pi 3' /proc/cpuinfo; then if grep -q 'Raspberry Pi 3' /proc/cpuinfo; then
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=1; export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=1
fi fi
fi fi
mkdir -p "${pio_cache_base}" mkdir -p "${pio_cache_base}"
mkdir -p /config/esphome
if bashio::fs.directory_exists '/config/esphome/.esphome'; then
bashio::log.info "Migrating old .esphome directory..."
if bashio::fs.file_exists '/config/esphome/.esphome/esphome.json'; then
mv /config/esphome/.esphome/esphome.json /data/esphome.json
fi
mkdir -p "/data/storage"
mv /config/esphome/.esphome/*.json /data/storage/ || true
rm -rf /config/esphome/.esphome
fi
bashio::log.info "Starting ESPHome dashboard..." bashio::log.info "Starting ESPHome dashboard..."
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon

View file

@ -26,6 +26,8 @@ from esphome.const import (
CONF_ESPHOME, CONF_ESPHOME,
CONF_PLATFORMIO_OPTIONS, CONF_PLATFORMIO_OPTIONS,
CONF_SUBSTITUTIONS, CONF_SUBSTITUTIONS,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
PLATFORM_ESP32, PLATFORM_ESP32,
PLATFORM_ESP8266, PLATFORM_ESP8266,
PLATFORM_RP2040, PLATFORM_RP2040,
@ -83,6 +85,8 @@ def choose_upload_log_host(
options = [] options = []
for port in get_serial_ports(): for port in get_serial_ports():
options.append((f"{port.path} ({port.description})", port.path)) options.append((f"{port.path} ({port.description})", port.path))
if default == "SERIAL":
return choose_prompt(options, purpose=purpose)
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config): if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
options.append((f"Over The Air ({CORE.address})", CORE.address)) options.append((f"Over The Air ({CORE.address})", CORE.address))
if default == "OTA": if default == "OTA":
@ -216,14 +220,16 @@ def compile_program(args, config):
return 0 if idedata is not None else 1 return 0 if idedata is not None else 1
def upload_using_esptool(config, port): def upload_using_esptool(config, port, file):
from esphome import platformio_api from esphome import platformio_api
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get( first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
"upload_speed", 460800 "upload_speed", 460800
) )
def run_esptool(baud_rate): if file is not None:
flash_images = [platformio_api.FlashImage(path=file, offset="0x0")]
else:
idedata = platformio_api.get_idedata(config) idedata = platformio_api.get_idedata(config)
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0" firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
@ -240,6 +246,7 @@ def upload_using_esptool(config, port):
mcu = get_esp32_variant().lower() mcu = get_esp32_variant().lower()
def run_esptool(baud_rate):
cmd = [ cmd = [
"esptool.py", "esptool.py",
"--before", "--before",
@ -278,20 +285,26 @@ def upload_using_esptool(config, port):
return run_esptool(115200) return run_esptool(115200)
def upload_using_platformio(config, port):
from esphome import platformio_api
upload_args = ["-t", "upload", "-t", "nobuild"]
if port is not None:
upload_args += ["--upload-port", port]
return platformio_api.run_platformio_cli_run(config, CORE.verbose, *upload_args)
def upload_program(config, args, host): def upload_program(config, args, host):
if get_port_type(host) == "SERIAL": if get_port_type(host) == "SERIAL":
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266): if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
return upload_using_esptool(config, host) file = getattr(args, "file", None)
return upload_using_esptool(config, host, file)
if CORE.target_platform in (PLATFORM_RP2040): if CORE.target_platform in (PLATFORM_RP2040):
from esphome import platformio_api return upload_using_platformio(config, args.device)
upload_args = ["-t", "upload"] if CORE.target_platform in (PLATFORM_BK72XX, PLATFORM_RTL87XX):
if args.device is not None: return upload_using_platformio(config, host)
upload_args += ["--upload-port", args.device]
return platformio_api.run_platformio_cli_run(
config, CORE.verbose, *upload_args
)
return 1 # Unknown target platform return 1 # Unknown target platform
@ -371,7 +384,7 @@ def command_config(args, config):
# add the console decoration so the front-end can hide the secrets # add the console decoration so the front-end can hide the secrets
if not args.show_secrets: if not args.show_secrets:
output = re.sub( output = re.sub(
r"(password|key|psk|ssid)\:\s(.*)", r"\1: \\033[5m\2\\033[6m", output r"(password|key|psk|ssid)\: (.+)", r"\1: \\033[5m\2\\033[6m", output
) )
safe_print(output) safe_print(output)
_LOGGER.info("Configuration is valid!") _LOGGER.info("Configuration is valid!")

View file

@ -11,6 +11,7 @@ from esphome.const import (
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_TYPE_ID, CONF_TYPE_ID,
CONF_TIME, CONF_TIME,
CONF_UPDATE_INTERVAL,
) )
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.util import Registry from esphome.util import Registry
@ -69,6 +70,8 @@ WhileAction = cg.esphome_ns.class_("WhileAction", Action)
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action) RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component) WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action) UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
SuspendComponentAction = cg.esphome_ns.class_("SuspendComponentAction", Action)
ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action)
Automation = cg.esphome_ns.class_("Automation") Automation = cg.esphome_ns.class_("Automation")
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition) LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
@ -138,6 +141,7 @@ AUTOMATION_SCHEMA = cv.Schema(
AndCondition = cg.esphome_ns.class_("AndCondition", Condition) AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
OrCondition = cg.esphome_ns.class_("OrCondition", Condition) OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
NotCondition = cg.esphome_ns.class_("NotCondition", Condition) NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
XorCondition = cg.esphome_ns.class_("XorCondition", Condition)
@register_condition("and", AndCondition, validate_condition_list) @register_condition("and", AndCondition, validate_condition_list)
@ -158,6 +162,12 @@ async def not_condition_to_code(config, condition_id, template_arg, args):
return cg.new_Pvariable(condition_id, template_arg, condition) return cg.new_Pvariable(condition_id, template_arg, condition)
@register_condition("xor", XorCondition, validate_condition_list)
async def xor_condition_to_code(config, condition_id, template_arg, args):
conditions = await build_condition_list(config, template_arg, args)
return cg.new_Pvariable(condition_id, template_arg, conditions)
@register_condition("lambda", LambdaCondition, cv.returning_lambda) @register_condition("lambda", LambdaCondition, cv.returning_lambda)
async def lambda_condition_to_code(config, condition_id, template_arg, args): async def lambda_condition_to_code(config, condition_id, template_arg, args):
lambda_ = await cg.process_lambda(config, args, return_type=bool) lambda_ = await cg.process_lambda(config, args, return_type=bool)
@ -303,6 +313,41 @@ async def component_update_action_to_code(config, action_id, template_arg, args)
return cg.new_Pvariable(action_id, template_arg, comp) return cg.new_Pvariable(action_id, template_arg, comp)
@register_action(
"component.suspend",
SuspendComponentAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
}
),
)
async def component_suspend_action_to_code(config, action_id, template_arg, args):
comp = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, comp)
@register_action(
"component.resume",
ResumeComponentAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
cv.Optional(CONF_UPDATE_INTERVAL): cv.templatable(
cv.positive_time_period_milliseconds
),
}
),
)
async def component_resume_action_to_code(config, action_id, template_arg, args):
comp = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, comp)
if CONF_UPDATE_INTERVAL in config:
template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, int)
cg.add(var.set_update_interval(template_))
return var
async def build_action(full_config, template_arg, args): async def build_action(full_config, template_arg, args):
registry_entry, config = cg.extract_registry_entry_config( registry_entry, config = cg.extract_registry_entry_config(
ACTION_REGISTRY, full_config ACTION_REGISTRY, full_config

View file

@ -1,10 +1,11 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.const import CONF_INPUT from esphome.const import CONF_ANALOG, CONF_INPUT
from esphome.core import CORE from esphome.core import CORE
from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32 import get_esp32_variant
from esphome.const import PLATFORM_ESP8266
from esphome.components.esp32.const import ( from esphome.components.esp32.const import (
VARIANT_ESP32, VARIANT_ESP32,
VARIANT_ESP32C2, VARIANT_ESP32C2,
@ -143,7 +144,9 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
def validate_adc_pin(value): def validate_adc_pin(value):
if str(value).upper() == "VCC": if str(value).upper() == "VCC":
return cv.only_on_esp8266("VCC") if CORE.is_rp2040:
return pins.internal_gpio_input_pin_schema(29)
return cv.only_on([PLATFORM_ESP8266])("VCC")
if str(value).upper() == "TEMPERATURE": if str(value).upper() == "TEMPERATURE":
return cv.only_on_rp2040("TEMPERATURE") return cv.only_on_rp2040("TEMPERATURE")
@ -166,8 +169,6 @@ def validate_adc_pin(value):
return pins.internal_gpio_input_pin_schema(value) return pins.internal_gpio_input_pin_schema(value)
if CORE.is_esp8266: if CORE.is_esp8266:
from esphome.components.esp8266.gpio import CONF_ANALOG
value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})( value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
value value
) )
@ -184,4 +185,9 @@ def validate_adc_pin(value):
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC") raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC")
return pins.internal_gpio_input_pin_schema(value) return pins.internal_gpio_input_pin_schema(value)
if CORE.is_libretiny:
return pins.gpio_pin_schema(
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
)(value)
raise NotImplementedError raise NotImplementedError

View file

@ -1,6 +1,6 @@
#include "adc_sensor.h" #include "adc_sensor.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#ifdef USE_ESP8266 #ifdef USE_ESP8266
#ifdef USE_ADC_SENSOR_VCC #ifdef USE_ADC_SENSOR_VCC
@ -12,6 +12,9 @@ ADC_MODE(ADC_VCC)
#endif #endif
#ifdef USE_RP2040 #ifdef USE_RP2040
#ifdef CYW43_USES_VSYS_PIN
#include "pico/cyw43_arch.h"
#endif
#include <hardware/adc.h> #include <hardware/adc.h>
#endif #endif
@ -92,13 +95,13 @@ extern "C"
void ADCSensor::dump_config() { void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this); LOG_SENSOR("", "ADC Sensor", this);
#ifdef USE_ESP8266 #if defined(USE_ESP8266) || defined(USE_LIBRETINY)
#ifdef USE_ADC_SENSOR_VCC #ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC"); ESP_LOGCONFIG(TAG, " Pin: VCC");
#else #else
LOG_PIN(" Pin: ", pin_); LOG_PIN(" Pin: ", pin_);
#endif #endif
#endif // USE_ESP8266 #endif // USE_ESP8266 || USE_LIBRETINY
#ifdef USE_ESP32 #ifdef USE_ESP32
LOG_PIN(" Pin: ", pin_); LOG_PIN(" Pin: ", pin_);
@ -123,13 +126,19 @@ void ADCSensor::dump_config() {
} }
} }
#endif // USE_ESP32 #endif // USE_ESP32
#ifdef USE_RP2040 #ifdef USE_RP2040
if (this->is_temperature_) { if (this->is_temperature_) {
ESP_LOGCONFIG(TAG, " Pin: Temperature"); ESP_LOGCONFIG(TAG, " Pin: Temperature");
} else { } else {
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", pin_); LOG_PIN(" Pin: ", pin_);
#endif // USE_ADC_SENSOR_VCC
} }
#endif #endif // USE_RP2040
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }
@ -237,23 +246,54 @@ float ADCSensor::sample() {
adc_set_temp_sensor_enabled(true); adc_set_temp_sensor_enabled(true);
delay(1); delay(1);
adc_select_input(4); adc_select_input(4);
} else {
uint8_t pin = this->pin_->get_pin();
adc_gpio_init(pin);
adc_select_input(pin - 26);
}
int32_t raw = adc_read(); int32_t raw = adc_read();
if (this->is_temperature_) {
adc_set_temp_sensor_enabled(false); adc_set_temp_sensor_enabled(false);
} if (this->output_raw_) {
if (output_raw_) {
return raw; return raw;
} }
return raw * 3.3f / 4096.0f; return raw * 3.3f / 4096.0f;
} else {
uint8_t pin = this->pin_->get_pin();
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
// VSYS ADC both share GPIO29
cyw43_thread_enter();
}
#endif // CYW43_USES_VSYS_PIN
adc_gpio_init(pin);
adc_select_input(pin - 26);
int32_t raw = adc_read();
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
cyw43_thread_exit();
}
#endif // CYW43_USES_VSYS_PIN
if (output_raw_) {
return raw;
}
float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
return raw * 3.3f / 4096.0f * coeff;
}
} }
#endif #endif
#ifdef USE_LIBRETINY
float ADCSensor::sample() {
if (output_raw_) {
return analogRead(this->pin_->get_pin()); // NOLINT
}
return analogReadVoltage(this->pin_->get_pin()) / 1000.0f; // NOLINT
}
#endif // USE_LIBRETINY
#ifdef USE_ESP8266 #ifdef USE_ESP8266
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
#endif #endif

View file

@ -62,8 +62,12 @@ 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] = {};
#else
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {}; esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
#endif #endif
#endif
}; };
} // namespace adc } // namespace adc

View file

@ -4,13 +4,14 @@ from esphome.components import sensor, i2c
from esphome import pins from esphome import pins
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_IRQ_PIN,
CONF_VOLTAGE, CONF_VOLTAGE,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_VOLT,
UNIT_WATT, UNIT_WATT,
) )
@ -19,7 +20,6 @@ DEPENDENCIES = ["i2c"]
ade7953_ns = cg.esphome_ns.namespace("ade7953") ade7953_ns = cg.esphome_ns.namespace("ade7953")
ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice) ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice)
CONF_IRQ_PIN = "irq_pin"
CONF_CURRENT_A = "current_a" CONF_CURRENT_A = "current_a"
CONF_CURRENT_B = "current_b" CONF_CURRENT_B = "current_b"
CONF_ACTIVE_POWER_A = "active_power_a" CONF_ACTIVE_POWER_A = "active_power_a"

View file

@ -151,7 +151,7 @@ async def to_code(config):
pos = 0 pos = 0
for frameIndex in range(frames): for frameIndex in range(frames):
image.seek(frameIndex) image.seek(frameIndex)
frame = image.convert("LA", dither=Image.NONE) frame = image.convert("LA", dither=Image.Dither.NONE)
if CONF_RESIZE in config: if CONF_RESIZE in config:
frame = frame.resize([width, height]) frame = frame.resize([width, height])
pixels = list(frame.getdata()) pixels = list(frame.getdata())
@ -259,7 +259,7 @@ async def to_code(config):
if transparent: if transparent:
alpha = image.split()[-1] alpha = image.split()[-1]
has_alpha = alpha.getextrema()[0] < 0xFF has_alpha = alpha.getextrema()[0] < 0xFF
frame = image.convert("1", dither=Image.NONE) frame = image.convert("1", dither=Image.Dither.NONE)
if CONF_RESIZE in config: if CONF_RESIZE in config:
frame = frame.resize([width, height]) frame = frame.resize([width, height])
if transparent: if transparent:

View file

@ -1413,6 +1413,18 @@ message SubscribeVoiceAssistantRequest {
bool subscribe = 1; bool subscribe = 1;
} }
enum VoiceAssistantRequestFlag {
VOICE_ASSISTANT_REQUEST_NONE = 0;
VOICE_ASSISTANT_REQUEST_USE_VAD = 1;
VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2;
}
message VoiceAssistantAudioSettings {
uint32 noise_suppression_level = 1;
uint32 auto_gain = 2;
float volume_multiplier = 3;
}
message VoiceAssistantRequest { message VoiceAssistantRequest {
option (id) = 90; option (id) = 90;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@ -1420,7 +1432,8 @@ message VoiceAssistantRequest {
bool start = 1; bool start = 1;
string conversation_id = 2; string conversation_id = 2;
bool use_vad = 3; uint32 flags = 3;
VoiceAssistantAudioSettings audio_settings = 4;
} }
message VoiceAssistantResponse { message VoiceAssistantResponse {
@ -1442,6 +1455,10 @@ enum VoiceAssistantEvent {
VOICE_ASSISTANT_INTENT_END = 6; VOICE_ASSISTANT_INTENT_END = 6;
VOICE_ASSISTANT_TTS_START = 7; VOICE_ASSISTANT_TTS_START = 7;
VOICE_ASSISTANT_TTS_END = 8; VOICE_ASSISTANT_TTS_END = 8;
VOICE_ASSISTANT_WAKE_WORD_START = 9;
VOICE_ASSISTANT_WAKE_WORD_END = 10;
VOICE_ASSISTANT_STT_VAD_START = 11;
VOICE_ASSISTANT_STT_VAD_END = 12;
} }
message VoiceAssistantEventData { message VoiceAssistantEventData {

View file

@ -907,21 +907,22 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool APIConnection::request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad) { bool APIConnection::request_voice_assistant(const VoiceAssistantRequest &msg) {
if (!this->voice_assistant_subscription_) if (!this->voice_assistant_subscription_)
return false; return false;
VoiceAssistantRequest msg;
msg.start = start;
msg.conversation_id = conversation_id;
msg.use_vad = use_vad;
return this->send_voice_assistant_request(msg); return this->send_voice_assistant_request(msg);
} }
void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) { void APIConnection::on_voice_assistant_response(const VoiceAssistantResponse &msg) {
if (voice_assistant::global_voice_assistant != nullptr) { if (voice_assistant::global_voice_assistant != nullptr) {
if (msg.error) {
voice_assistant::global_voice_assistant->failed_to_start();
return;
}
struct sockaddr_storage storage; struct sockaddr_storage storage;
socklen_t len = sizeof(storage); socklen_t len = sizeof(storage);
this->helper_->getpeername((struct sockaddr *) &storage, &len); this->helper_->getpeername((struct sockaddr *) &storage, &len);
voice_assistant::global_voice_assistant->start(&storage, msg.port); voice_assistant::global_voice_assistant->start_streaming(&storage, msg.port);
} }
}; };
void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) { void APIConnection::on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) {
@ -1051,6 +1052,10 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
resp.manufacturer = "Espressif"; resp.manufacturer = "Espressif";
#elif defined(USE_RP2040) #elif defined(USE_RP2040)
resp.manufacturer = "Raspberry Pi"; resp.manufacturer = "Raspberry Pi";
#elif defined(USE_BK72XX)
resp.manufacturer = "Beken";
#elif defined(USE_RTL87XX)
resp.manufacturer = "Realtek";
#elif defined(USE_HOST) #elif defined(USE_HOST)
resp.manufacturer = "Host"; resp.manufacturer = "Host";
#endif #endif

View file

@ -124,7 +124,7 @@ class APIConnection : public APIServerConnection {
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override { void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override {
this->voice_assistant_subscription_ = msg.subscribe; this->voice_assistant_subscription_ = msg.subscribe;
} }
bool request_voice_assistant(bool start, const std::string &conversation_id, bool use_vad); bool request_voice_assistant(const VoiceAssistantRequest &msg);
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override; void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override; void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
#endif #endif

View file

@ -3,6 +3,8 @@
#include "api_pb2.h" #include "api_pb2.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <cinttypes>
namespace esphome { namespace esphome {
namespace api { namespace api {
@ -408,6 +410,20 @@ const char *proto_enum_to_string<enums::BluetoothDeviceRequestType>(enums::Bluet
} }
#endif #endif
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
template<> const char *proto_enum_to_string<enums::VoiceAssistantRequestFlag>(enums::VoiceAssistantRequestFlag value) {
switch (value) {
case enums::VOICE_ASSISTANT_REQUEST_NONE:
return "VOICE_ASSISTANT_REQUEST_NONE";
case enums::VOICE_ASSISTANT_REQUEST_USE_VAD:
return "VOICE_ASSISTANT_REQUEST_USE_VAD";
case enums::VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD:
return "VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD";
default:
return "UNKNOWN";
}
}
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::VoiceAssistantEvent value) { template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::VoiceAssistantEvent value) {
switch (value) { switch (value) {
case enums::VOICE_ASSISTANT_ERROR: case enums::VOICE_ASSISTANT_ERROR:
@ -428,6 +444,14 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
return "VOICE_ASSISTANT_TTS_START"; return "VOICE_ASSISTANT_TTS_START";
case enums::VOICE_ASSISTANT_TTS_END: case enums::VOICE_ASSISTANT_TTS_END:
return "VOICE_ASSISTANT_TTS_END"; return "VOICE_ASSISTANT_TTS_END";
case enums::VOICE_ASSISTANT_WAKE_WORD_START:
return "VOICE_ASSISTANT_WAKE_WORD_START";
case enums::VOICE_ASSISTANT_WAKE_WORD_END:
return "VOICE_ASSISTANT_WAKE_WORD_END";
case enums::VOICE_ASSISTANT_STT_VAD_START:
return "VOICE_ASSISTANT_STT_VAD_START";
case enums::VOICE_ASSISTANT_STT_VAD_END:
return "VOICE_ASSISTANT_STT_VAD_END";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
@ -522,12 +546,12 @@ void HelloRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" api_version_major: "); out.append(" api_version_major: ");
sprintf(buffer, "%u", this->api_version_major); sprintf(buffer, "%" PRIu32, this->api_version_major);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" api_version_minor: "); out.append(" api_version_minor: ");
sprintf(buffer, "%u", this->api_version_minor); sprintf(buffer, "%" PRIu32, this->api_version_minor);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -572,12 +596,12 @@ void HelloResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("HelloResponse {\n"); out.append("HelloResponse {\n");
out.append(" api_version_major: "); out.append(" api_version_major: ");
sprintf(buffer, "%u", this->api_version_major); sprintf(buffer, "%" PRIu32, this->api_version_major);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" api_version_minor: "); out.append(" api_version_minor: ");
sprintf(buffer, "%u", this->api_version_minor); sprintf(buffer, "%" PRIu32, this->api_version_minor);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -783,17 +807,17 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" webserver_port: "); out.append(" webserver_port: ");
sprintf(buffer, "%u", this->webserver_port); sprintf(buffer, "%" PRIu32, this->webserver_port);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" legacy_bluetooth_proxy_version: "); out.append(" legacy_bluetooth_proxy_version: ");
sprintf(buffer, "%u", this->legacy_bluetooth_proxy_version); sprintf(buffer, "%" PRIu32, this->legacy_bluetooth_proxy_version);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" bluetooth_proxy_feature_flags: "); out.append(" bluetooth_proxy_feature_flags: ");
sprintf(buffer, "%u", this->bluetooth_proxy_feature_flags); sprintf(buffer, "%" PRIu32, this->bluetooth_proxy_feature_flags);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -806,7 +830,7 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" voice_assistant_version: "); out.append(" voice_assistant_version: ");
sprintf(buffer, "%u", this->voice_assistant_version); sprintf(buffer, "%" PRIu32, this->voice_assistant_version);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -898,7 +922,7 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -966,7 +990,7 @@ void BinarySensorStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("BinarySensorStateResponse {\n"); out.append("BinarySensorStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1069,7 +1093,7 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1159,7 +1183,7 @@ void CoverStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("CoverStateResponse {\n"); out.append("CoverStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1242,7 +1266,7 @@ void CoverCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("CoverCommandRequest {\n"); out.append("CoverCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1362,7 +1386,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1387,7 +1411,7 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" supported_speed_count: "); out.append(" supported_speed_count: ");
sprintf(buffer, "%d", this->supported_speed_count); sprintf(buffer, "%" PRId32, this->supported_speed_count);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1454,7 +1478,7 @@ void FanStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("FanStateResponse {\n"); out.append("FanStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1475,7 +1499,7 @@ void FanStateResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" speed_level: "); out.append(" speed_level: ");
sprintf(buffer, "%d", this->speed_level); sprintf(buffer, "%" PRId32, this->speed_level);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -1555,7 +1579,7 @@ void FanCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("FanCommandRequest {\n"); out.append("FanCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1596,7 +1620,7 @@ void FanCommandRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" speed_level: "); out.append(" speed_level: ");
sprintf(buffer, "%d", this->speed_level); sprintf(buffer, "%" PRId32, this->speed_level);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -1710,7 +1734,7 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -1864,7 +1888,7 @@ void LightStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("LightStateResponse {\n"); out.append("LightStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2087,7 +2111,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("LightCommandRequest {\n"); out.append("LightCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2185,7 +2209,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" transition_length: "); out.append(" transition_length: ");
sprintf(buffer, "%u", this->transition_length); sprintf(buffer, "%" PRIu32, this->transition_length);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2194,7 +2218,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" flash_length: "); out.append(" flash_length: ");
sprintf(buffer, "%u", this->flash_length); sprintf(buffer, "%" PRIu32, this->flash_length);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2302,7 +2326,7 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2323,7 +2347,7 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" accuracy_decimals: "); out.append(" accuracy_decimals: ");
sprintf(buffer, "%d", this->accuracy_decimals); sprintf(buffer, "%" PRId32, this->accuracy_decimals);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2387,7 +2411,7 @@ void SensorStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SensorStateResponse {\n"); out.append("SensorStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2476,7 +2500,7 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2539,7 +2563,7 @@ void SwitchStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SwitchStateResponse {\n"); out.append("SwitchStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2578,7 +2602,7 @@ void SwitchCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SwitchCommandRequest {\n"); out.append("SwitchCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2652,7 +2676,7 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -2718,7 +2742,7 @@ void TextSensorStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("TextSensorStateResponse {\n"); out.append("TextSensorStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3025,7 +3049,7 @@ void GetTimeResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("GetTimeResponse {\n"); out.append("GetTimeResponse {\n");
out.append(" epoch_seconds: "); out.append(" epoch_seconds: ");
sprintf(buffer, "%u", this->epoch_seconds); sprintf(buffer, "%" PRIu32, this->epoch_seconds);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -3109,7 +3133,7 @@ void ListEntitiesServicesResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3203,7 +3227,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" legacy_int: "); out.append(" legacy_int: ");
sprintf(buffer, "%d", this->legacy_int); sprintf(buffer, "%" PRId32, this->legacy_int);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3217,7 +3241,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" int_: "); out.append(" int_: ");
sprintf(buffer, "%d", this->int_); sprintf(buffer, "%" PRId32, this->int_);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3229,7 +3253,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
for (const auto &it : this->int_array) { for (const auto &it : this->int_array) {
out.append(" int_array: "); out.append(" int_array: ");
sprintf(buffer, "%d", it); sprintf(buffer, "%" PRId32, it);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
} }
@ -3280,7 +3304,7 @@ void ExecuteServiceRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ExecuteServiceRequest {\n"); out.append("ExecuteServiceRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3356,7 +3380,7 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3422,7 +3446,7 @@ void CameraImageResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("CameraImageResponse {\n"); out.append("CameraImageResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3614,7 +3638,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3802,7 +3826,7 @@ void ClimateStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ClimateStateResponse {\n"); out.append("ClimateStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -3990,7 +4014,7 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ClimateCommandRequest {\n"); out.append("ClimateCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4173,7 +4197,7 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4260,7 +4284,7 @@ void NumberStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("NumberStateResponse {\n"); out.append("NumberStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4298,7 +4322,7 @@ void NumberCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("NumberCommandRequest {\n"); out.append("NumberCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4380,7 +4404,7 @@ void ListEntitiesSelectResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4452,7 +4476,7 @@ void SelectStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SelectStateResponse {\n"); out.append("SelectStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4495,7 +4519,7 @@ void SelectCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SelectCommandRequest {\n"); out.append("SelectCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4589,7 +4613,7 @@ void ListEntitiesLockResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4660,7 +4684,7 @@ void LockStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("LockStateResponse {\n"); out.append("LockStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4715,7 +4739,7 @@ void LockCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("LockCommandRequest {\n"); out.append("LockCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4802,7 +4826,7 @@ void ListEntitiesButtonResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4848,7 +4872,7 @@ void ButtonCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ButtonCommandRequest {\n"); out.append("ButtonCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -4923,7 +4947,7 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -4992,7 +5016,7 @@ void MediaPlayerStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("MediaPlayerStateResponse {\n"); out.append("MediaPlayerStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5071,7 +5095,7 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("MediaPlayerCommandRequest {\n"); out.append("MediaPlayerCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5120,7 +5144,7 @@ void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("SubscribeBluetoothLEAdvertisementsRequest {\n"); out.append("SubscribeBluetoothLEAdvertisementsRequest {\n");
out.append(" flags: "); out.append(" flags: ");
sprintf(buffer, "%u", this->flags); sprintf(buffer, "%" PRIu32, this->flags);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -5167,7 +5191,7 @@ void BluetoothServiceData::dump_to(std::string &out) const {
for (const auto &it : this->legacy_data) { for (const auto &it : this->legacy_data) {
out.append(" legacy_data: "); out.append(" legacy_data: ");
sprintf(buffer, "%u", it); sprintf(buffer, "%" PRIu32, it);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
} }
@ -5247,7 +5271,7 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" rssi: "); out.append(" rssi: ");
sprintf(buffer, "%d", this->rssi); sprintf(buffer, "%" PRId32, this->rssi);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5270,7 +5294,7 @@ void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const {
} }
out.append(" address_type: "); out.append(" address_type: ");
sprintf(buffer, "%u", this->address_type); sprintf(buffer, "%" PRIu32, this->address_type);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -5320,12 +5344,12 @@ void BluetoothLERawAdvertisement::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" rssi: "); out.append(" rssi: ");
sprintf(buffer, "%d", this->rssi); sprintf(buffer, "%" PRId32, this->rssi);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" address_type: "); out.append(" address_type: ");
sprintf(buffer, "%u", this->address_type); sprintf(buffer, "%" PRIu32, this->address_type);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5408,7 +5432,7 @@ void BluetoothDeviceRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" address_type: "); out.append(" address_type: ");
sprintf(buffer, "%u", this->address_type); sprintf(buffer, "%" PRIu32, this->address_type);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -5456,12 +5480,12 @@ void BluetoothDeviceConnectionResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" mtu: "); out.append(" mtu: ");
sprintf(buffer, "%u", this->mtu); sprintf(buffer, "%" PRIu32, this->mtu);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" error: "); out.append(" error: ");
sprintf(buffer, "%d", this->error); sprintf(buffer, "%" PRId32, this->error);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -5521,7 +5545,7 @@ void BluetoothGATTDescriptor::dump_to(std::string &out) const {
} }
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -5577,12 +5601,12 @@ void BluetoothGATTCharacteristic::dump_to(std::string &out) const {
} }
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" properties: "); out.append(" properties: ");
sprintf(buffer, "%u", this->properties); sprintf(buffer, "%" PRIu32, this->properties);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5639,7 +5663,7 @@ void BluetoothGATTService::dump_to(std::string &out) const {
} }
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5746,7 +5770,7 @@ void BluetoothGATTReadRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -5791,7 +5815,7 @@ void BluetoothGATTReadResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5845,7 +5869,7 @@ void BluetoothGATTWriteRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5887,7 +5911,7 @@ void BluetoothGATTReadDescriptorRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -5932,7 +5956,7 @@ void BluetoothGATTWriteDescriptorRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -5975,7 +5999,7 @@ void BluetoothGATTNotifyRequest::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -6024,7 +6048,7 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -6063,12 +6087,12 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("BluetoothConnectionsFreeResponse {\n"); out.append("BluetoothConnectionsFreeResponse {\n");
out.append(" free: "); out.append(" free: ");
sprintf(buffer, "%u", this->free); sprintf(buffer, "%" PRIu32, this->free);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" limit: "); out.append(" limit: ");
sprintf(buffer, "%u", this->limit); sprintf(buffer, "%" PRIu32, this->limit);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -6107,12 +6131,12 @@ void BluetoothGATTErrorResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" error: "); out.append(" error: ");
sprintf(buffer, "%d", this->error); sprintf(buffer, "%" PRId32, this->error);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -6146,7 +6170,7 @@ void BluetoothGATTWriteResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -6180,7 +6204,7 @@ void BluetoothGATTNotifyResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" handle: "); out.append(" handle: ");
sprintf(buffer, "%u", this->handle); sprintf(buffer, "%" PRIu32, this->handle);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -6223,7 +6247,7 @@ void BluetoothDevicePairingResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" error: "); out.append(" error: ");
sprintf(buffer, "%d", this->error); sprintf(buffer, "%" PRId32, this->error);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -6266,7 +6290,7 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" error: "); out.append(" error: ");
sprintf(buffer, "%d", this->error); sprintf(buffer, "%" PRId32, this->error);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -6315,7 +6339,7 @@ void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" error: "); out.append(" error: ");
sprintf(buffer, "%d", this->error); sprintf(buffer, "%" PRId32, this->error);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
@ -6342,6 +6366,56 @@ void SubscribeVoiceAssistantRequest::dump_to(std::string &out) const {
out.append("}"); out.append("}");
} }
#endif #endif
bool VoiceAssistantAudioSettings::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->noise_suppression_level = value.as_uint32();
return true;
}
case 2: {
this->auto_gain = value.as_uint32();
return true;
}
default:
return false;
}
}
bool VoiceAssistantAudioSettings::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 3: {
this->volume_multiplier = value.as_float();
return true;
}
default:
return false;
}
}
void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->noise_suppression_level);
buffer.encode_uint32(2, this->auto_gain);
buffer.encode_float(3, this->volume_multiplier);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantAudioSettings::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("VoiceAssistantAudioSettings {\n");
out.append(" noise_suppression_level: ");
sprintf(buffer, "%" PRIu32, this->noise_suppression_level);
out.append(buffer);
out.append("\n");
out.append(" auto_gain: ");
sprintf(buffer, "%" PRIu32, this->auto_gain);
out.append(buffer);
out.append("\n");
out.append(" volume_multiplier: ");
sprintf(buffer, "%g", this->volume_multiplier);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) { switch (field_id) {
case 1: { case 1: {
@ -6349,7 +6423,7 @@ bool VoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
return true; return true;
} }
case 3: { case 3: {
this->use_vad = value.as_bool(); this->flags = value.as_uint32();
return true; return true;
} }
default: default:
@ -6362,6 +6436,10 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
this->conversation_id = value.as_string(); this->conversation_id = value.as_string();
return true; return true;
} }
case 4: {
this->audio_settings = value.as_message<VoiceAssistantAudioSettings>();
return true;
}
default: default:
return false; return false;
} }
@ -6369,7 +6447,8 @@ bool VoiceAssistantRequest::decode_length(uint32_t field_id, ProtoLengthDelimite
void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const { void VoiceAssistantRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(1, this->start); buffer.encode_bool(1, this->start);
buffer.encode_string(2, this->conversation_id); buffer.encode_string(2, this->conversation_id);
buffer.encode_bool(3, this->use_vad); buffer.encode_uint32(3, this->flags);
buffer.encode_message<VoiceAssistantAudioSettings>(4, this->audio_settings);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void VoiceAssistantRequest::dump_to(std::string &out) const { void VoiceAssistantRequest::dump_to(std::string &out) const {
@ -6383,8 +6462,13 @@ void VoiceAssistantRequest::dump_to(std::string &out) const {
out.append("'").append(this->conversation_id).append("'"); out.append("'").append(this->conversation_id).append("'");
out.append("\n"); out.append("\n");
out.append(" use_vad: "); out.append(" flags: ");
out.append(YESNO(this->use_vad)); sprintf(buffer, "%" PRIu32, this->flags);
out.append(buffer);
out.append("\n");
out.append(" audio_settings: ");
this->audio_settings.dump_to(out);
out.append("\n"); out.append("\n");
out.append("}"); out.append("}");
} }
@ -6412,7 +6496,7 @@ void VoiceAssistantResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("VoiceAssistantResponse {\n"); out.append("VoiceAssistantResponse {\n");
out.append(" port: "); out.append(" port: ");
sprintf(buffer, "%u", this->port); sprintf(buffer, "%" PRIu32, this->port);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -6575,7 +6659,7 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -6600,7 +6684,7 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const {
out.append("\n"); out.append("\n");
out.append(" supported_features: "); out.append(" supported_features: ");
sprintf(buffer, "%u", this->supported_features); sprintf(buffer, "%" PRIu32, this->supported_features);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -6643,7 +6727,7 @@ void AlarmControlPanelStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("AlarmControlPanelStateResponse {\n"); out.append("AlarmControlPanelStateResponse {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
@ -6693,7 +6777,7 @@ void AlarmControlPanelCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("AlarmControlPanelCommandRequest {\n"); out.append("AlarmControlPanelCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%" PRIu32, this->key);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");

View file

@ -165,6 +165,11 @@ enum BluetoothDeviceRequestType : uint32_t {
BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5,
BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6, BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6,
}; };
enum VoiceAssistantRequestFlag : uint32_t {
VOICE_ASSISTANT_REQUEST_NONE = 0,
VOICE_ASSISTANT_REQUEST_USE_VAD = 1,
VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD = 2,
};
enum VoiceAssistantEvent : uint32_t { enum VoiceAssistantEvent : uint32_t {
VOICE_ASSISTANT_ERROR = 0, VOICE_ASSISTANT_ERROR = 0,
VOICE_ASSISTANT_RUN_START = 1, VOICE_ASSISTANT_RUN_START = 1,
@ -175,6 +180,10 @@ enum VoiceAssistantEvent : uint32_t {
VOICE_ASSISTANT_INTENT_END = 6, VOICE_ASSISTANT_INTENT_END = 6,
VOICE_ASSISTANT_TTS_START = 7, VOICE_ASSISTANT_TTS_START = 7,
VOICE_ASSISTANT_TTS_END = 8, VOICE_ASSISTANT_TTS_END = 8,
VOICE_ASSISTANT_WAKE_WORD_START = 9,
VOICE_ASSISTANT_WAKE_WORD_END = 10,
VOICE_ASSISTANT_STT_VAD_START = 11,
VOICE_ASSISTANT_STT_VAD_END = 12,
}; };
enum AlarmControlPanelState : uint32_t { enum AlarmControlPanelState : uint32_t {
ALARM_STATE_DISARMED = 0, ALARM_STATE_DISARMED = 0,
@ -1651,11 +1660,26 @@ class SubscribeVoiceAssistantRequest : public ProtoMessage {
protected: protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
}; };
class VoiceAssistantAudioSettings : public ProtoMessage {
public:
uint32_t noise_suppression_level{0};
uint32_t auto_gain{0};
float volume_multiplier{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantRequest : public ProtoMessage { class VoiceAssistantRequest : public ProtoMessage {
public: public:
bool start{false}; bool start{false};
std::string conversation_id{}; std::string conversation_id{};
bool use_vad{false}; uint32_t flags{0};
VoiceAssistantAudioSettings audio_settings{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;

View file

@ -1,13 +1,13 @@
#include "api_server.h" #include "api_server.h"
#include <cerrno>
#include "api_connection.h" #include "api_connection.h"
#include "esphome/components/network/util.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/util.h" #include "esphome/core/util.h"
#include "esphome/core/version.h" #include "esphome/core/version.h"
#include "esphome/core/hal.h"
#include "esphome/components/network/util.h"
#include <cerrno>
#ifdef USE_LOGGER #ifdef USE_LOGGER
#include "esphome/components/logger/logger.h" #include "esphome/components/logger/logger.h"
@ -323,16 +323,24 @@ void APIServer::on_shutdown() {
} }
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool APIServer::start_voice_assistant(const std::string &conversation_id, bool use_vad) { bool APIServer::start_voice_assistant(const std::string &conversation_id, uint32_t flags,
const api::VoiceAssistantAudioSettings &audio_settings) {
VoiceAssistantRequest msg;
msg.start = true;
msg.conversation_id = conversation_id;
msg.flags = flags;
msg.audio_settings = audio_settings;
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (c->request_voice_assistant(true, conversation_id, use_vad)) if (c->request_voice_assistant(msg))
return true; return true;
} }
return false; return false;
} }
void APIServer::stop_voice_assistant() { void APIServer::stop_voice_assistant() {
VoiceAssistantRequest msg;
msg.start = false;
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (c->request_voice_assistant(false, "", false)) if (c->request_voice_assistant(msg))
return; return;
} }
} }

View file

@ -1,16 +1,16 @@
#pragma once #pragma once
#include "api_noise_context.h"
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "esphome/components/socket/socket.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/controller.h" #include "esphome/core/controller.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/components/socket/socket.h"
#include "api_pb2.h"
#include "api_pb2_service.h"
#include "list_entities.h" #include "list_entities.h"
#include "subscribe_state.h" #include "subscribe_state.h"
#include "user_services.h" #include "user_services.h"
#include "api_noise_context.h"
#include <vector> #include <vector>
@ -81,7 +81,8 @@ class APIServer : public Component, public Controller {
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
bool start_voice_assistant(const std::string &conversation_id, bool use_vad); bool start_voice_assistant(const std::string &conversation_id, uint32_t flags,
const api::VoiceAssistantAudioSettings &audio_settings);
void stop_voice_assistant(); void stop_voice_assistant();
#endif #endif

View file

@ -2,14 +2,15 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.const import ( from esphome.const import (
CONF_CAPACITANCE,
CONF_DIV_RATIO,
CONF_INDOOR, CONF_INDOOR,
CONF_WATCHDOG_THRESHOLD, CONF_IRQ_PIN,
CONF_NOISE_LEVEL,
CONF_SPIKE_REJECTION,
CONF_LIGHTNING_THRESHOLD, CONF_LIGHTNING_THRESHOLD,
CONF_MASK_DISTURBER, CONF_MASK_DISTURBER,
CONF_DIV_RATIO, CONF_NOISE_LEVEL,
CONF_CAPACITANCE, CONF_SPIKE_REJECTION,
CONF_WATCHDOG_THRESHOLD,
) )
MULTI_CONF = True MULTI_CONF = True
@ -19,7 +20,6 @@ CONF_AS3935_ID = "as3935_id"
as3935_ns = cg.esphome_ns.namespace("as3935") as3935_ns = cg.esphome_ns.namespace("as3935")
AS3935 = as3935_ns.class_("AS3935Component", cg.Component) AS3935 = as3935_ns.class_("AS3935Component", cg.Component)
CONF_IRQ_PIN = "irq_pin"
AS3935_SCHEMA = cv.Schema( AS3935_SCHEMA = cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(AS3935), cv.GenerateID(): cv.declare_id(AS3935),

View file

@ -2,21 +2,27 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
)
CODEOWNERS = ["@OttoWinter"] 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(["esp32", "esp8266"]), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, 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: if CORE.is_esp32 or CORE.is_libretiny:
# https://github.com/esphome/AsyncTCP/blob/master/library.json # https://github.com/esphome/AsyncTCP/blob/master/library.json
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2") cg.add_library("esphome/AsyncTCP-esphome", "2.0.1")
elif CORE.is_esp8266: elif CORE.is_esp8266:
# https://github.com/esphome/ESPAsyncTCP # https://github.com/esphome/ESPAsyncTCP
cg.add_library("esphome/ESPAsyncTCP-esphome", "1.2.3") cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")

View file

@ -6,6 +6,9 @@ from esphome.const import (
CONF_REACTIVE_POWER, CONF_REACTIVE_POWER,
CONF_VOLTAGE, CONF_VOLTAGE,
CONF_CURRENT, CONF_CURRENT,
CONF_PHASE_A,
CONF_PHASE_B,
CONF_PHASE_C,
CONF_POWER, CONF_POWER,
CONF_POWER_FACTOR, CONF_POWER_FACTOR,
CONF_FREQUENCY, CONF_FREQUENCY,
@ -31,10 +34,6 @@ from esphome.const import (
UNIT_WATT_HOURS, UNIT_WATT_HOURS,
) )
CONF_PHASE_A = "phase_a"
CONF_PHASE_B = "phase_b"
CONF_PHASE_C = "phase_c"
CONF_LINE_FREQUENCY = "line_frequency" CONF_LINE_FREQUENCY = "line_frequency"
CONF_CHIP_TEMPERATURE = "chip_temperature" CONF_CHIP_TEMPERATURE = "chip_temperature"
CONF_GAIN_PGA = "gain_pga" CONF_GAIN_PGA = "gain_pga"

View file

@ -0,0 +1,51 @@
# This file was auto-generated by libretiny/generate_components.py
# Do not modify its contents.
# For custom pin validators, put validate_pin() or validate_usage()
# in gpio.py file in this directory.
# For changing schema/pin schema, put COMPONENT_SCHEMA or COMPONENT_PIN_SCHEMA
# in schema.py file in this directory.
from esphome import pins
from esphome.components import libretiny
from esphome.components.libretiny.const import (
COMPONENT_BK72XX,
KEY_COMPONENT_DATA,
KEY_LIBRETINY,
LibreTinyComponent,
)
from esphome.core import CORE
from .boards import BK72XX_BOARDS, BK72XX_BOARD_PINS
CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["libretiny"]
COMPONENT_DATA = LibreTinyComponent(
name=COMPONENT_BK72XX,
boards=BK72XX_BOARDS,
board_pins=BK72XX_BOARD_PINS,
pin_validation=None,
usage_validation=None,
)
def _set_core_data(config):
CORE.data[KEY_LIBRETINY] = {}
CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA] = COMPONENT_DATA
return config
CONFIG_SCHEMA = libretiny.BASE_SCHEMA
PIN_SCHEMA = libretiny.gpio.BASE_PIN_SCHEMA
CONFIG_SCHEMA.prepend_extra(_set_core_data)
async def to_code(config):
return await libretiny.component_to_code(config)
@pins.PIN_SCHEMA_REGISTRY.register("bk72xx", PIN_SCHEMA)
async def pin_to_code(config):
return await libretiny.gpio.component_pin_to_code(config)

File diff suppressed because it is too large Load diff

View file

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

View file

@ -0,0 +1,270 @@
#include "bmi160.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace bmi160 {
static const char *const TAG = "bmi160";
const uint8_t BMI160_REGISTER_CHIPID = 0x00;
const uint8_t BMI160_REGISTER_CMD = 0x7E;
enum class Cmd : uint8_t {
START_FOC = 0x03,
ACCL_SET_PMU_MODE = 0b00010000, // last 2 bits are mode
GYRO_SET_PMU_MODE = 0b00010100, // last 2 bits are mode
MAG_SET_PMU_MODE = 0b00011000, // last 2 bits are mode
PROG_NVM = 0xA0,
FIFO_FLUSH = 0xB0,
INT_RESET = 0xB1,
SOFT_RESET = 0xB6,
STEP_CNT_CLR = 0xB2,
};
enum class GyroPmuMode : uint8_t {
SUSPEND = 0b00,
NORMAL = 0b01,
LOW_POWER = 0b10,
};
enum class AcclPmuMode : uint8_t {
SUSPEND = 0b00,
NORMAL = 0b01,
FAST_STARTUP = 0b11,
};
enum class MagPmuMode : uint8_t {
SUSPEND = 0b00,
NORMAL = 0b01,
LOW_POWER = 0b10,
};
const uint8_t BMI160_REGISTER_ACCEL_CONFIG = 0x40;
enum class AcclFilterMode : uint8_t {
POWER_SAVING = 0b00000000,
PERF = 0b10000000,
};
enum class AcclBandwidth : uint8_t {
OSR4_AVG1 = 0b00000000,
OSR2_AVG2 = 0b00010000,
NORMAL_AVG4 = 0b00100000,
RES_AVG8 = 0b00110000,
RES_AVG16 = 0b01000000,
RES_AVG32 = 0b01010000,
RES_AVG64 = 0b01100000,
RES_AVG128 = 0b01110000,
};
enum class AccelOutputDataRate : uint8_t {
HZ_25_32 = 0b0001, // 25/32 Hz
HZ_25_16 = 0b0010, // 25/16 Hz
HZ_25_8 = 0b0011, // 25/8 Hz
HZ_25_4 = 0b0100, // 25/4 Hz
HZ_25_2 = 0b0101, // 25/2 Hz
HZ_25 = 0b0110, // 25 Hz
HZ_50 = 0b0111, // 50 Hz
HZ_100 = 0b1000, // 100 Hz
HZ_200 = 0b1001, // 200 Hz
HZ_400 = 0b1010, // 400 Hz
HZ_800 = 0b1011, // 800 Hz
HZ_1600 = 0b1100, // 1600 Hz
};
const uint8_t BMI160_REGISTER_ACCEL_RANGE = 0x41;
enum class AccelRange : uint8_t {
RANGE_2G = 0b0011,
RANGE_4G = 0b0101,
RANGE_8G = 0b1000,
RANGE_16G = 0b1100,
};
const uint8_t BMI160_REGISTER_GYRO_CONFIG = 0x42;
enum class GyroBandwidth : uint8_t {
OSR4 = 0x00,
OSR2 = 0x10,
NORMAL = 0x20,
};
enum class GyroOuputDataRate : uint8_t {
HZ_25 = 0x06,
HZ_50 = 0x07,
HZ_100 = 0x08,
HZ_200 = 0x09,
HZ_400 = 0x0A,
HZ_800 = 0x0B,
HZ_1600 = 0x0C,
HZ_3200 = 0x0D,
};
const uint8_t BMI160_REGISTER_GYRO_RANGE = 0x43;
enum class GyroRange : uint8_t {
RANGE_2000_DPS = 0x0, // ±2000 °/s
RANGE_1000_DPS = 0x1,
RANGE_500_DPS = 0x2,
RANGE_250_DPS = 0x3,
RANGE_125_DPS = 0x4,
};
const uint8_t BMI160_REGISTER_DATA_GYRO_X_LSB = 0x0C;
const uint8_t BMI160_REGISTER_DATA_GYRO_X_MSB = 0x0D;
const uint8_t BMI160_REGISTER_DATA_GYRO_Y_LSB = 0x0E;
const uint8_t BMI160_REGISTER_DATA_GYRO_Y_MSB = 0x0F;
const uint8_t BMI160_REGISTER_DATA_GYRO_Z_LSB = 0x10;
const uint8_t BMI160_REGISTER_DATA_GYRO_Z_MSB = 0x11;
const uint8_t BMI160_REGISTER_DATA_ACCEL_X_LSB = 0x12;
const uint8_t BMI160_REGISTER_DATA_ACCEL_X_MSB = 0x13;
const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_LSB = 0x14;
const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_MSB = 0x15;
const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_LSB = 0x16;
const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_MSB = 0x17;
const uint8_t BMI160_REGISTER_DATA_TEMP_LSB = 0x20;
const uint8_t BMI160_REGISTER_DATA_TEMP_MSB = 0x21;
const float GRAVITY_EARTH = 9.80665f;
void BMI160Component::internal_setup_(int stage) {
switch (stage) {
case 0:
ESP_LOGCONFIG(TAG, "Setting up BMI160...");
uint8_t chipid;
if (!this->read_byte(BMI160_REGISTER_CHIPID, &chipid) || (chipid != 0b11010001)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Bringing accelerometer out of sleep...");
if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::ACCL_SET_PMU_MODE | (uint8_t) AcclPmuMode::NORMAL)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Waiting for accelerometer to wake up...");
// need to wait (max delay in datasheet) because we can't send commands while another is in progress
// min 5ms, 10ms
this->set_timeout(10, [this]() { this->internal_setup_(1); });
break;
case 1:
ESP_LOGV(TAG, " Bringing gyroscope out of sleep...");
if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::GYRO_SET_PMU_MODE | (uint8_t) GyroPmuMode::NORMAL)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Waiting for gyroscope to wake up...");
// wait between 51 & 81ms, doing 100 to be safe
this->set_timeout(10, [this]() { this->internal_setup_(2); });
break;
case 2:
ESP_LOGV(TAG, " Setting up Gyro Config...");
uint8_t gyro_config = (uint8_t) GyroBandwidth::OSR4 | (uint8_t) GyroOuputDataRate::HZ_25;
ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
if (!this->write_byte(BMI160_REGISTER_GYRO_CONFIG, gyro_config)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Setting up Gyro Range...");
uint8_t gyro_range = (uint8_t) GyroRange::RANGE_2000_DPS;
ESP_LOGV(TAG, " Output gyro_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_range));
if (!this->write_byte(BMI160_REGISTER_GYRO_RANGE, gyro_range)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Setting up Accel Config...");
uint8_t accel_config =
(uint8_t) AcclFilterMode::PERF | (uint8_t) AcclBandwidth::RES_AVG16 | (uint8_t) AccelOutputDataRate::HZ_25;
ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
if (!this->write_byte(BMI160_REGISTER_ACCEL_CONFIG, accel_config)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Setting up Accel Range...");
uint8_t accel_range = (uint8_t) AccelRange::RANGE_16G;
ESP_LOGV(TAG, " Output accel_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_range));
if (!this->write_byte(BMI160_REGISTER_ACCEL_RANGE, accel_range)) {
this->mark_failed();
return;
}
this->setup_complete_ = true;
}
}
void BMI160Component::setup() { this->internal_setup_(0); }
void BMI160Component::dump_config() {
ESP_LOGCONFIG(TAG, "BMI160:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with BMI160 failed!");
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_);
LOG_SENSOR(" ", "Acceleration Y", this->accel_y_sensor_);
LOG_SENSOR(" ", "Acceleration Z", this->accel_z_sensor_);
LOG_SENSOR(" ", "Gyro X", this->gyro_x_sensor_);
LOG_SENSOR(" ", "Gyro Y", this->gyro_y_sensor_);
LOG_SENSOR(" ", "Gyro Z", this->gyro_z_sensor_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
}
i2c::ErrorCode BMI160Component::read_le_int16_(uint8_t reg, int16_t *value, uint8_t len) {
uint8_t raw_data[len * 2];
// read using read_register because we have little-endian data, and read_bytes_16 will swap it
i2c::ErrorCode err = this->read_register(reg, raw_data, len * 2, true);
if (err != i2c::ERROR_OK) {
return err;
}
for (int i = 0; i < len; i++) {
value[i] = (int16_t) ((uint16_t) raw_data[i * 2] | ((uint16_t) raw_data[i * 2 + 1] << 8));
}
return err;
}
void BMI160Component::update() {
if (!this->setup_complete_) {
return;
}
ESP_LOGV(TAG, " Updating BMI160...");
int16_t data[6];
if (this->read_le_int16_(BMI160_REGISTER_DATA_GYRO_X_LSB, data, 6) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
float gyro_x = (float) data[0] / (float) INT16_MAX * 2000.f;
float gyro_y = (float) data[1] / (float) INT16_MAX * 2000.f;
float gyro_z = (float) data[2] / (float) INT16_MAX * 2000.f;
float accel_x = (float) data[3] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
float accel_y = (float) data[4] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
float accel_z = (float) data[5] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
int16_t raw_temperature;
if (this->read_le_int16_(BMI160_REGISTER_DATA_TEMP_LSB, &raw_temperature, 1) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
float temperature = (float) raw_temperature / (float) INT16_MAX * 64.5f + 23.f;
ESP_LOGD(TAG,
"Got accel={x=%.3f m/s², y=%.3f m/s², z=%.3f m/s²}, "
"gyro={x=%.3f °/s, y=%.3f °/s, z=%.3f °/s}, temp=%.3f°C",
accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z, temperature);
if (this->accel_x_sensor_ != nullptr)
this->accel_x_sensor_->publish_state(accel_x);
if (this->accel_y_sensor_ != nullptr)
this->accel_y_sensor_->publish_state(accel_y);
if (this->accel_z_sensor_ != nullptr)
this->accel_z_sensor_->publish_state(accel_z);
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature);
if (this->gyro_x_sensor_ != nullptr)
this->gyro_x_sensor_->publish_state(gyro_x);
if (this->gyro_y_sensor_ != nullptr)
this->gyro_y_sensor_->publish_state(gyro_y);
if (this->gyro_z_sensor_ != nullptr)
this->gyro_z_sensor_->publish_state(gyro_z);
this->status_clear_warning();
}
float BMI160Component::get_setup_priority() const { return setup_priority::DATA; }
} // namespace bmi160
} // namespace esphome

View file

@ -0,0 +1,44 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace bmi160 {
class BMI160Component : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
void update() override;
float get_setup_priority() const override;
void set_accel_x_sensor(sensor::Sensor *accel_x_sensor) { accel_x_sensor_ = accel_x_sensor; }
void set_accel_y_sensor(sensor::Sensor *accel_y_sensor) { accel_y_sensor_ = accel_y_sensor; }
void set_accel_z_sensor(sensor::Sensor *accel_z_sensor) { accel_z_sensor_ = accel_z_sensor; }
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
void set_gyro_x_sensor(sensor::Sensor *gyro_x_sensor) { gyro_x_sensor_ = gyro_x_sensor; }
void set_gyro_y_sensor(sensor::Sensor *gyro_y_sensor) { gyro_y_sensor_ = gyro_y_sensor; }
void set_gyro_z_sensor(sensor::Sensor *gyro_z_sensor) { gyro_z_sensor_ = gyro_z_sensor; }
protected:
sensor::Sensor *accel_x_sensor_{nullptr};
sensor::Sensor *accel_y_sensor_{nullptr};
sensor::Sensor *accel_z_sensor_{nullptr};
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *gyro_x_sensor_{nullptr};
sensor::Sensor *gyro_y_sensor_{nullptr};
sensor::Sensor *gyro_z_sensor_{nullptr};
void internal_setup_(int stage);
bool setup_complete_{false};
/** reads `len` 16-bit little-endian integers from the given i2c register */
i2c::ErrorCode read_le_int16_(uint8_t reg, int16_t *value, uint8_t len);
};
} // namespace bmi160
} // namespace esphome

View file

@ -0,0 +1,102 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_ID,
CONF_TEMPERATURE,
CONF_ACCELERATION_X,
CONF_ACCELERATION_Y,
CONF_ACCELERATION_Z,
CONF_GYROSCOPE_X,
CONF_GYROSCOPE_Y,
CONF_GYROSCOPE_Z,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_METER_PER_SECOND_SQUARED,
ICON_ACCELERATION_X,
ICON_ACCELERATION_Y,
ICON_ACCELERATION_Z,
ICON_GYROSCOPE_X,
ICON_GYROSCOPE_Y,
ICON_GYROSCOPE_Z,
UNIT_DEGREE_PER_SECOND,
UNIT_CELSIUS,
)
DEPENDENCIES = ["i2c"]
bmi160_ns = cg.esphome_ns.namespace("bmi160")
BMI160Component = bmi160_ns.class_(
"BMI160Component", cg.PollingComponent, i2c.I2CDevice
)
accel_schema = {
"unit_of_measurement": UNIT_METER_PER_SECOND_SQUARED,
"accuracy_decimals": 2,
"state_class": STATE_CLASS_MEASUREMENT,
}
gyro_schema = {
"unit_of_measurement": UNIT_DEGREE_PER_SECOND,
"accuracy_decimals": 2,
"state_class": STATE_CLASS_MEASUREMENT,
}
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BMI160Component),
cv.Optional(CONF_ACCELERATION_X): sensor.sensor_schema(
icon=ICON_ACCELERATION_X,
**accel_schema,
),
cv.Optional(CONF_ACCELERATION_Y): sensor.sensor_schema(
icon=ICON_ACCELERATION_Y,
**accel_schema,
),
cv.Optional(CONF_ACCELERATION_Z): sensor.sensor_schema(
icon=ICON_ACCELERATION_Z,
**accel_schema,
),
cv.Optional(CONF_GYROSCOPE_X): sensor.sensor_schema(
icon=ICON_GYROSCOPE_X,
**gyro_schema,
),
cv.Optional(CONF_GYROSCOPE_Y): sensor.sensor_schema(
icon=ICON_GYROSCOPE_Y,
**gyro_schema,
),
cv.Optional(CONF_GYROSCOPE_Z): sensor.sensor_schema(
icon=ICON_GYROSCOPE_Z,
**gyro_schema,
),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x68))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
for d in ["x", "y", "z"]:
accel_key = f"acceleration_{d}"
if accel_key in config:
sens = await sensor.new_sensor(config[accel_key])
cg.add(getattr(var, f"set_accel_{d}_sensor")(sens))
accel_key = f"gyroscope_{d}"
if accel_key in config:
sens = await sensor.new_sensor(config[accel_key])
cg.add(getattr(var, f"set_gyro_{d}_sensor")(sens))
if CONF_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature_sensor(sens))

View file

@ -12,6 +12,8 @@ static const uint8_t BP1658CJ_ADDR_START_3CH = 0x10;
static const uint8_t BP1658CJ_ADDR_START_2CH = 0x20; static const uint8_t BP1658CJ_ADDR_START_2CH = 0x20;
static const uint8_t BP1658CJ_ADDR_START_5CH = 0x30; static const uint8_t BP1658CJ_ADDR_START_5CH = 0x30;
static const uint8_t BP1658CJ_DELAY = 2;
void BP1658CJ::setup() { void BP1658CJ::setup() {
ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component..."); ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component...");
this->data_pin_->setup(); this->data_pin_->setup();
@ -81,27 +83,41 @@ void BP1658CJ::set_channel_value_(uint8_t channel, uint16_t value) {
} }
this->pwm_amounts_[channel] = value; this->pwm_amounts_[channel] = value;
} }
void BP1658CJ::write_bit_(bool value) { void BP1658CJ::write_bit_(bool value) {
this->clock_pin_->digital_write(false);
this->data_pin_->digital_write(value); this->data_pin_->digital_write(value);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
delayMicroseconds(BP1658CJ_DELAY);
this->clock_pin_->digital_write(false);
} }
void BP1658CJ::write_byte_(uint8_t data) { void BP1658CJ::write_byte_(uint8_t data) {
for (uint8_t mask = 0x80; mask; mask >>= 1) { for (uint8_t mask = 0x80; mask; mask >>= 1) {
this->write_bit_(data & mask); this->write_bit_(data & mask);
delayMicroseconds(BP1658CJ_DELAY);
} }
this->clock_pin_->digital_write(false);
this->data_pin_->digital_write(true); // ack bit
this->data_pin_->pin_mode(gpio::FLAG_INPUT);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
delayMicroseconds(BP1658CJ_DELAY);
this->clock_pin_->digital_write(false);
this->data_pin_->pin_mode(gpio::FLAG_OUTPUT);
} }
void BP1658CJ::write_buffer_(uint8_t *buffer, uint8_t size) { void BP1658CJ::write_buffer_(uint8_t *buffer, uint8_t size) {
this->data_pin_->digital_write(false); this->data_pin_->digital_write(false);
this->clock_pin_->digital_write(false);
for (uint32_t i = 0; i < size; i++) { for (uint32_t i = 0; i < size; i++) {
this->write_byte_(buffer[i]); this->write_byte_(buffer[i]);
delayMicroseconds(BP1658CJ_DELAY);
} }
this->clock_pin_->digital_write(false);
this->clock_pin_->digital_write(true); this->clock_pin_->digital_write(true);
this->data_pin_->digital_write(true); this->data_pin_->digital_write(true);
} }

View file

@ -39,10 +39,14 @@ void BP5758D::loop() {
uint8_t data[17]; uint8_t data[17];
if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) { this->pwm_amounts_[3] == 0 && this->pwm_amounts_[4] == 0) {
// Off / Sleep
data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY;
for (int i = 1; i < 16; i++) for (int i = 1; i < 16; i++)
data[i] = 0; data[i] = 0;
// First turn all channels off
data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_START_3CH;
this->write_buffer_(data, 17);
// Then sleep
data[0] = BP5758D_MODEL_ID + BP5758D_ADDR_STANDBY;
this->write_buffer_(data, 17); this->write_buffer_(data, 17);
} else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 && } else if (this->pwm_amounts_[0] == 0 && this->pwm_amounts_[1] == 0 && this->pwm_amounts_[2] == 0 &&
(this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) { (this->pwm_amounts_[3] > 0 || this->pwm_amounts_[4] > 0)) {

View file

@ -45,9 +45,13 @@ CanbusTrigger = canbus_ns.class_(
CanSpeed = canbus_ns.enum("CAN_SPEED") CanSpeed = canbus_ns.enum("CAN_SPEED")
CAN_SPEEDS = { CAN_SPEEDS = {
"1KBPS": CanSpeed.CAN_1KBPS,
"5KBPS": CanSpeed.CAN_5KBPS, "5KBPS": CanSpeed.CAN_5KBPS,
"10KBPS": CanSpeed.CAN_10KBPS, "10KBPS": CanSpeed.CAN_10KBPS,
"12K5BPS": CanSpeed.CAN_12K5BPS,
"16KBPS": CanSpeed.CAN_16KBPS,
"20KBPS": CanSpeed.CAN_20KBPS, "20KBPS": CanSpeed.CAN_20KBPS,
"25KBPS": CanSpeed.CAN_25KBPS,
"31K25BPS": CanSpeed.CAN_31K25BPS, "31K25BPS": CanSpeed.CAN_31K25BPS,
"33KBPS": CanSpeed.CAN_33KBPS, "33KBPS": CanSpeed.CAN_33KBPS,
"40KBPS": CanSpeed.CAN_40KBPS, "40KBPS": CanSpeed.CAN_40KBPS,
@ -60,9 +64,9 @@ CAN_SPEEDS = {
"200KBPS": CanSpeed.CAN_200KBPS, "200KBPS": CanSpeed.CAN_200KBPS,
"250KBPS": CanSpeed.CAN_250KBPS, "250KBPS": CanSpeed.CAN_250KBPS,
"500KBPS": CanSpeed.CAN_500KBPS, "500KBPS": CanSpeed.CAN_500KBPS,
"800KBPS": CanSpeed.CAN_800KBPS,
"1000KBPS": CanSpeed.CAN_1000KBPS, "1000KBPS": CanSpeed.CAN_1000KBPS,
} }
CANBUS_SCHEMA = cv.Schema( CANBUS_SCHEMA = cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(CanbusComponent), cv.GenerateID(): cv.declare_id(CanbusComponent),

View file

@ -19,9 +19,13 @@ enum Error : uint8_t {
}; };
enum CanSpeed : uint8_t { enum CanSpeed : uint8_t {
CAN_1KBPS,
CAN_5KBPS, CAN_5KBPS,
CAN_10KBPS, CAN_10KBPS,
CAN_12K5BPS,
CAN_16KBPS,
CAN_20KBPS, CAN_20KBPS,
CAN_25KBPS,
CAN_31K25BPS, CAN_31K25BPS,
CAN_33KBPS, CAN_33KBPS,
CAN_40KBPS, CAN_40KBPS,
@ -34,6 +38,7 @@ enum CanSpeed : uint8_t {
CAN_200KBPS, CAN_200KBPS,
CAN_250KBPS, CAN_250KBPS,
CAN_500KBPS, CAN_500KBPS,
CAN_800KBPS,
CAN_1000KBPS CAN_1000KBPS
}; };

View file

@ -2,7 +2,13 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import web_server_base from esphome.components import web_server_base
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
from esphome.const import CONF_ID from esphome.const import (
CONF_ID,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
)
from esphome.core import coroutine_with_priority, CORE from esphome.core import coroutine_with_priority, CORE
AUTO_LOAD = ["web_server_base"] AUTO_LOAD = ["web_server_base"]
@ -21,7 +27,7 @@ CONFIG_SCHEMA = cv.All(
), ),
} }
).extend(cv.COMPONENT_SCHEMA), ).extend(cv.COMPONENT_SCHEMA),
cv.only_on(["esp32", "esp8266"]), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]),
) )
@ -39,3 +45,5 @@ async def to_code(config):
cg.add_library("WiFi", None) cg.add_library("WiFi", None)
if CORE.is_esp8266: if CORE.is_esp8266:
cg.add_library("DNSServer", None) cg.add_library("DNSServer", None)
if CORE.is_libretiny:
cg.add_library("DNSServer", None)

View file

@ -48,7 +48,7 @@ void CaptivePortal::start() {
this->dns_server_ = make_unique<DNSServer>(); this->dns_server_ = make_unique<DNSServer>();
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError); this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip(); network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
this->dns_server_->start(53, "*", (uint32_t) ip); this->dns_server_->start(53, "*", IPAddress(ip));
#endif #endif
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) { this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {

View file

@ -24,7 +24,7 @@ CONFIG_SCHEMA = cv.All(
).extend( ).extend(
{ {
cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent), cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent),
cv.Optional(CONF_ADDRESS): cv.hex_int, cv.Optional(CONF_ADDRESS): cv.hex_uint64_t,
cv.Optional(CONF_INDEX): cv.positive_int, cv.Optional(CONF_INDEX): cv.positive_int,
cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12), cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12),
} }

View file

@ -17,6 +17,8 @@
#include <esp32/rom/rtc.h> #include <esp32/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32C3) #elif defined(USE_ESP32_VARIANT_ESP32C3)
#include <esp32c3/rom/rtc.h> #include <esp32c3/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32C6)
#include <esp32c6/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32S2) #elif defined(USE_ESP32_VARIANT_ESP32S2)
#include <esp32s2/rom/rtc.h> #include <esp32s2/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32S3) #elif defined(USE_ESP32_VARIANT_ESP32S3)
@ -28,7 +30,7 @@
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
#ifdef USE_RP2040 #ifdef USE_RP2040
#include <Arduino.h> #include <Arduino.h>
#else #elif defined(USE_ESP32) || defined(USE_ESP8266)
#include <Esp.h> #include <Esp.h>
#endif #endif
#endif #endif
@ -45,6 +47,8 @@ static uint32_t get_free_heap() {
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#elif defined(USE_RP2040) #elif defined(USE_RP2040)
return rp2040.getFreeHeap(); return rp2040.getFreeHeap();
#elif defined(USE_LIBRETINY)
return lt_heap_get_free();
#endif #endif
} }
@ -75,7 +79,7 @@ void DebugComponent::dump_config() {
this->free_heap_ = get_free_heap(); this->free_heap_ = get_free_heap();
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_); ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
#if defined(USE_ARDUINO) && !defined(USE_RP2040) #if defined(USE_ARDUINO) && (defined(USE_ESP32) || defined(USE_ESP8266))
const char *flash_mode; const char *flash_mode;
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance) switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
case FM_QIO: case FM_QIO:
@ -107,7 +111,7 @@ void DebugComponent::dump_config() {
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT "kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
device_info += flash_mode; device_info += flash_mode;
#endif // USE_ARDUINO #endif // USE_ARDUINO && (USE_ESP32 || USE_ESP8266)
#ifdef USE_ESP32 #ifdef USE_ESP32
esp_chip_info_t info; esp_chip_info_t info;
@ -117,6 +121,8 @@ void DebugComponent::dump_config() {
model = "ESP32"; model = "ESP32";
#elif defined(USE_ESP32_VARIANT_ESP32C3) #elif defined(USE_ESP32_VARIANT_ESP32C3)
model = "ESP32-C3"; model = "ESP32-C3";
#elif defined(USE_ESP32_VARIANT_ESP32C6)
model = "ESP32-C6";
#elif defined(USE_ESP32_VARIANT_ESP32S2) #elif defined(USE_ESP32_VARIANT_ESP32S2)
model = "ESP32-S2"; model = "ESP32-S2";
#elif defined(USE_ESP32_VARIANT_ESP32S3) #elif defined(USE_ESP32_VARIANT_ESP32S3)
@ -143,6 +149,10 @@ void DebugComponent::dump_config() {
features += "BT,"; features += "BT,";
info.features &= ~CHIP_FEATURE_BT; info.features &= ~CHIP_FEATURE_BT;
} }
if (info.features & CHIP_FEATURE_EMB_PSRAM) {
features += "EMB_PSRAM,";
info.features &= ~CHIP_FEATURE_EMB_PSRAM;
}
if (info.features) if (info.features)
features += "Other:" + format_hex(info.features); features += "Other:" + format_hex(info.features);
ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores, ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
@ -196,9 +206,11 @@ void DebugComponent::dump_config() {
case RTCWDT_SYS_RESET: case RTCWDT_SYS_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core"; reset_reason = "RTC Watch Dog Reset Digital Core";
break; break;
#if !defined(USE_ESP32_VARIANT_ESP32C6)
case INTRUSION_RESET: case INTRUSION_RESET:
reset_reason = "Intrusion Reset CPU"; reset_reason = "Intrusion Reset CPU";
break; break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32) #if defined(USE_ESP32_VARIANT_ESP32)
case TGWDT_CPU_RESET: case TGWDT_CPU_RESET:
reset_reason = "Timer Group Reset CPU"; reset_reason = "Timer Group Reset CPU";
@ -340,6 +352,27 @@ void DebugComponent::dump_config() {
device_info += "CPU Frequency: " + to_string(rp2040.f_cpu()); device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
#endif // USE_RP2040 #endif // USE_RP2040
#ifdef USE_LIBRETINY
ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
ESP_LOGD(TAG, "Board: %s", lt_get_board_code());
ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024);
ESP_LOGD(TAG, "Reset Reason: %s", lt_get_reboot_reason_name(lt_get_reboot_reason()));
device_info += "|Version: ";
device_info += LT_BANNER_STR + 10;
device_info += "|Reset Reason: ";
device_info += lt_get_reboot_reason_name(lt_get_reboot_reason());
device_info += "|Chip Name: ";
device_info += lt_cpu_get_model_name();
device_info += "|Chip ID: 0x" + format_hex(lt_cpu_get_mac_id());
device_info += "|Flash: " + to_string(lt_flash_get_size() / 1024) + " KiB";
device_info += "|RAM: " + to_string(lt_ram_get_size() / 1024) + " KiB";
reset_reason = lt_get_reboot_reason_name(lt_get_reboot_reason());
#endif // USE_LIBRETINY
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->device_info_ != nullptr) { if (this->device_info_ != nullptr) {
if (device_info.length() > 255) if (device_info.length() > 255)
@ -384,6 +417,8 @@ void DebugComponent::update() {
this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize()); this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
#elif defined(USE_ESP32) #elif defined(USE_ESP32)
this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL)); this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
#elif defined(USE_LIBRETINY)
this->block_sensor_->publish_state(lt_heap_get_max_alloc());
#endif #endif
} }
@ -398,6 +433,12 @@ void DebugComponent::update() {
this->loop_time_sensor_->publish_state(this->max_loop_time_); this->loop_time_sensor_->publish_state(this->max_loop_time_);
this->max_loop_time_ = 0; this->max_loop_time_ = 0;
} }
#ifdef USE_ESP32
if (this->psram_sensor_ != nullptr) {
this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
}
#endif // USE_ESP32
#endif // USE_SENSOR #endif // USE_SENSOR
} }

View file

@ -33,6 +33,9 @@ class DebugComponent : public PollingComponent {
void set_fragmentation_sensor(sensor::Sensor *fragmentation_sensor) { fragmentation_sensor_ = fragmentation_sensor; } void set_fragmentation_sensor(sensor::Sensor *fragmentation_sensor) { fragmentation_sensor_ = fragmentation_sensor; }
#endif #endif
void set_loop_time_sensor(sensor::Sensor *loop_time_sensor) { loop_time_sensor_ = loop_time_sensor; } void set_loop_time_sensor(sensor::Sensor *loop_time_sensor) { loop_time_sensor_ = loop_time_sensor; }
#ifdef USE_ESP32
void set_psram_sensor(sensor::Sensor *psram_sensor) { this->psram_sensor_ = psram_sensor; }
#endif // USE_ESP32
#endif // USE_SENSOR #endif // USE_SENSOR
protected: protected:
uint32_t free_heap_{}; uint32_t free_heap_{};
@ -47,6 +50,9 @@ class DebugComponent : public PollingComponent {
sensor::Sensor *fragmentation_sensor_{nullptr}; sensor::Sensor *fragmentation_sensor_{nullptr};
#endif #endif
sensor::Sensor *loop_time_sensor_{nullptr}; sensor::Sensor *loop_time_sensor_{nullptr};
#ifdef USE_ESP32
sensor::Sensor *psram_sensor_{nullptr};
#endif // USE_ESP32
#endif // USE_SENSOR #endif // USE_SENSOR
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR

View file

@ -17,6 +17,8 @@ from . import CONF_DEBUG_ID, DebugComponent
DEPENDENCIES = ["debug"] DEPENDENCIES = ["debug"]
CONF_PSRAM = "psram"
CONFIG_SCHEMA = { CONFIG_SCHEMA = {
cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent), cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent),
cv.Optional(CONF_FREE): sensor.sensor_schema( cv.Optional(CONF_FREE): sensor.sensor_schema(
@ -47,24 +49,38 @@ CONFIG_SCHEMA = {
accuracy_decimals=0, accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
), ),
cv.Optional(CONF_PSRAM): cv.All(
cv.only_on_esp32,
cv.requires_component("psram"),
sensor.sensor_schema(
unit_of_measurement=UNIT_BYTES,
icon=ICON_COUNTER,
accuracy_decimals=0,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
),
} }
async def to_code(config): async def to_code(config):
debug_component = await cg.get_variable(config[CONF_DEBUG_ID]) debug_component = await cg.get_variable(config[CONF_DEBUG_ID])
if CONF_FREE in config: if free_conf := config.get(CONF_FREE):
sens = await sensor.new_sensor(config[CONF_FREE]) sens = await sensor.new_sensor(free_conf)
cg.add(debug_component.set_free_sensor(sens)) cg.add(debug_component.set_free_sensor(sens))
if CONF_BLOCK in config: if block_conf := config.get(CONF_BLOCK):
sens = await sensor.new_sensor(config[CONF_BLOCK]) sens = await sensor.new_sensor(block_conf)
cg.add(debug_component.set_block_sensor(sens)) cg.add(debug_component.set_block_sensor(sens))
if CONF_FRAGMENTATION in config: if fragmentation_conf := config.get(CONF_FRAGMENTATION):
sens = await sensor.new_sensor(config[CONF_FRAGMENTATION]) sens = await sensor.new_sensor(fragmentation_conf)
cg.add(debug_component.set_fragmentation_sensor(sens)) cg.add(debug_component.set_fragmentation_sensor(sens))
if CONF_LOOP_TIME in config: if loop_time_conf := config.get(CONF_LOOP_TIME):
sens = await sensor.new_sensor(config[CONF_LOOP_TIME]) sens = await sensor.new_sensor(loop_time_conf)
cg.add(debug_component.set_loop_time_sensor(sens)) cg.add(debug_component.set_loop_time_sensor(sens))
if psram_conf := config.get(CONF_PSRAM):
sens = await sensor.new_sensor(psram_conf)
cg.add(debug_component.set_psram_sensor(sens))

View file

@ -14,6 +14,8 @@ from esphome.const import (
CONF_SLEEP_DURATION, CONF_SLEEP_DURATION,
CONF_TIME_ID, CONF_TIME_ID,
CONF_WAKEUP_PIN, CONF_WAKEUP_PIN,
PLATFORM_ESP32,
PLATFORM_ESP8266,
) )
from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32 import get_esp32_variant
@ -24,6 +26,7 @@ from esphome.components.esp32.const import (
VARIANT_ESP32S3, VARIANT_ESP32S3,
VARIANT_ESP32C2, VARIANT_ESP32C2,
VARIANT_ESP32C6, VARIANT_ESP32C6,
VARIANT_ESP32H2,
) )
WAKEUP_PINS = { WAKEUP_PINS = {
@ -98,6 +101,7 @@ WAKEUP_PINS = {
], ],
VARIANT_ESP32C2: [0, 1, 2, 3, 4, 5], VARIANT_ESP32C2: [0, 1, 2, 3, 4, 5],
VARIANT_ESP32C6: [0, 1, 2, 3, 4, 5, 6, 7], VARIANT_ESP32C6: [0, 1, 2, 3, 4, 5, 6, 7],
VARIANT_ESP32H2: [7, 8, 9, 10, 11, 12, 13, 14],
} }
@ -163,7 +167,8 @@ WAKEUP_CAUSES_SCHEMA = cv.Schema(
} }
) )
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.All(
cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(DeepSleepComponent), cv.GenerateID(): cv.declare_id(DeepSleepComponent),
cv.Optional(CONF_RUN_DURATION): cv.Any( cv.Optional(CONF_RUN_DURATION): cv.Any(
@ -172,7 +177,9 @@ CONFIG_SCHEMA = cv.Schema(
), ),
cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_WAKEUP_PIN): cv.All( cv.Optional(CONF_WAKEUP_PIN): cv.All(
cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number cv.only_on_esp32,
pins.internal_gpio_input_pin_schema,
validate_pin_number,
), ),
cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All( cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(
cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True
@ -190,7 +197,9 @@ CONFIG_SCHEMA = cv.Schema(
), ),
cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean), cv.Optional(CONF_TOUCH_WAKEUP): cv.All(cv.only_on_esp32, cv.boolean),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA),
cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]),
)
async def to_code(config): async def to_code(config):

View file

@ -101,6 +101,11 @@ void DFPlayer::loop() {
ESP_LOGV(TAG, "Nack"); ESP_LOGV(TAG, "Nack");
this->ack_set_is_playing_ = false; this->ack_set_is_playing_ = false;
this->ack_reset_is_playing_ = false; this->ack_reset_is_playing_ = false;
if (argument == 6) {
ESP_LOGV(TAG, "File not found");
this->is_playing_ = false;
}
break;
case 0x41: case 0x41:
ESP_LOGV(TAG, "Ack ok"); ESP_LOGV(TAG, "Ack ok");
this->is_playing_ |= this->ack_set_is_playing_; this->is_playing_ |= this->ack_set_is_playing_;

View file

@ -40,7 +40,6 @@ void E131Component::setup() {
this->mark_failed(); this->mark_failed();
return; return;
} }
server.ss_family = AF_INET;
err = this->socket_->bind((struct sockaddr *) &server, sizeof(server)); err = this->socket_->bind((struct sockaddr *) &server, sizeof(server));
if (err != 0) { if (err != 0) {

View file

@ -67,8 +67,8 @@ bool E131Component::join_igmp_groups_() {
if (!universe.second) if (!universe.second)
continue; continue;
ip4_addr_t multicast_addr = {static_cast<uint32_t>( ip4_addr_t multicast_addr =
network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff)))}; network::IPAddress(239, 255, ((universe.first >> 8) & 0xff), ((universe.first >> 0) & 0xff));
auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr); auto err = igmp_joingroup(IP4_ADDR_ANY4, &multicast_addr);
@ -101,8 +101,7 @@ void E131Component::leave_(int universe) {
} }
if (listen_method_ == E131_MULTICAST) { if (listen_method_ == E131_MULTICAST) {
ip4_addr_t multicast_addr = { ip4_addr_t multicast_addr = network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff));
static_cast<uint32_t>(network::IPAddress(239, 255, ((universe >> 8) & 0xff), ((universe >> 0) & 0xff)))};
igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr); igmp_leavegroup(IP4_ADDR_ANY4, &multicast_addr);
} }

View file

@ -22,8 +22,10 @@ from esphome.const import (
CONF_IGNORE_EFUSE_MAC_CRC, CONF_IGNORE_EFUSE_MAC_CRC,
KEY_CORE, KEY_CORE,
KEY_FRAMEWORK_VERSION, KEY_FRAMEWORK_VERSION,
KEY_NAME,
KEY_TARGET_FRAMEWORK, KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
PLATFORM_ESP32,
TYPE_GIT, TYPE_GIT,
TYPE_LOCAL, TYPE_LOCAL,
__version__, __version__,
@ -37,6 +39,7 @@ from .const import ( # noqa
KEY_BOARD, KEY_BOARD,
KEY_COMPONENTS, KEY_COMPONENTS,
KEY_ESP32, KEY_ESP32,
KEY_EXTRA_BUILD_FILES,
KEY_PATH, KEY_PATH,
KEY_REF, KEY_REF,
KEY_REFRESH, KEY_REFRESH,
@ -60,7 +63,7 @@ AUTO_LOAD = ["preferences"]
def set_core_data(config): def set_core_data(config):
CORE.data[KEY_ESP32] = {} CORE.data[KEY_ESP32] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp32" CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP32
conf = config[CONF_FRAMEWORK] conf = config[CONF_FRAMEWORK]
if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF:
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf"
@ -73,6 +76,8 @@ def set_core_data(config):
) )
CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD] CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD]
CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT] CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT]
CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES] = {}
return config return config
@ -84,6 +89,23 @@ def get_board(core_obj=None):
return (core_obj or CORE).data[KEY_ESP32][KEY_BOARD] return (core_obj or CORE).data[KEY_ESP32][KEY_BOARD]
def get_download_types(storage_json):
return [
{
"title": "Modern format",
"description": "For use with ESPHome Web and other tools.",
"file": "firmware-factory.bin",
"download": f"{storage_json.name}-factory.bin",
},
{
"title": "Legacy format",
"description": "For use with ESPHome Flasher.",
"file": "firmware.bin",
"download": f"{storage_json.name}.bin",
},
]
def only_on_variant(*, supported=None, unsupported=None): def only_on_variant(*, supported=None, unsupported=None):
"""Config validator for features only available on some ESP32 variants.""" """Config validator for features only available on some ESP32 variants."""
if supported is not None and not isinstance(supported, list): if supported is not None and not isinstance(supported, list):
@ -149,6 +171,24 @@ def add_idf_component(
} }
def add_extra_script(stage: str, filename: str, path: str):
"""Add an extra script to the project."""
key = f"{stage}:{filename}"
if add_extra_build_file(filename, path):
cg.add_platformio_option("extra_scripts", [key])
def add_extra_build_file(filename: str, path: str) -> bool:
"""Add an extra build file to the project."""
if filename not in CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES]:
CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES][filename] = {
KEY_NAME: filename,
KEY_PATH: path,
}
return True
return False
def _format_framework_arduino_version(ver: cv.Version) -> str: def _format_framework_arduino_version(ver: cv.Version) -> str:
# format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to # format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to
# a PIO platformio/framework-arduinoespressif32 value # a PIO platformio/framework-arduinoespressif32 value
@ -373,7 +413,11 @@ async def to_code(config):
conf = config[CONF_FRAMEWORK] conf = config[CONF_FRAMEWORK]
cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION]) cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
cg.add_platformio_option("extra_scripts", ["post:post_build.py"]) add_extra_script(
"post",
"post_build2.py",
os.path.join(os.path.dirname(__file__), "post_build.py.script"),
)
if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF: if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF:
cg.add_platformio_option("framework", "espidf") cg.add_platformio_option("framework", "espidf")
@ -587,9 +631,15 @@ def copy_files():
ignore_dangling_symlinks=True, ignore_dangling_symlinks=True,
) )
dir = os.path.dirname(__file__) for _, file in CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES].items():
post_build_file = os.path.join(dir, "post_build.py.script") if file[KEY_PATH].startswith("http"):
import requests
mkdir_p(CORE.relative_build_path(os.path.dirname(file[KEY_NAME])))
with open(CORE.relative_build_path(file[KEY_NAME]), "wb") as f:
f.write(requests.get(file[KEY_PATH], timeout=30).content)
else:
copy_file_if_changed( copy_file_if_changed(
post_build_file, file[KEY_PATH],
CORE.relative_build_path("post_build.py"), CORE.relative_build_path(file[KEY_NAME]),
) )

View file

@ -235,6 +235,7 @@ ESP32_BOARD_PINS = {
"SDA": 5, "SDA": 5,
"SS": 15, "SS": 15,
}, },
"denky_d4": {"RX": 8, "LED": 14},
"esp-wrover-kit": {}, "esp-wrover-kit": {},
"esp32-devkitlipo": {}, "esp32-devkitlipo": {},
"esp32-evb": { "esp32-evb": {

View file

@ -10,6 +10,7 @@ KEY_REF = "ref"
KEY_REFRESH = "refresh" KEY_REFRESH = "refresh"
KEY_PATH = "path" KEY_PATH = "path"
KEY_SUBMODULES = "submodules" KEY_SUBMODULES = "submodules"
KEY_EXTRA_BUILD_FILES = "extra_build_files"
VARIANT_ESP32 = "ESP32" VARIANT_ESP32 = "ESP32"
VARIANT_ESP32S2 = "ESP32S2" VARIANT_ESP32S2 = "ESP32S2"

View file

@ -53,7 +53,11 @@ void arch_init() {
void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); } void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); }
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; } uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
#if ESP_IDF_VERSION_MAJOR >= 5
uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); }
#else
uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_cycle_count(); } uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_cycle_count(); }
#endif
uint32_t arch_get_cpu_freq_hz() { return rtc_clk_apb_freq_get(); } uint32_t arch_get_cpu_freq_hz() { return rtc_clk_apb_freq_get(); }
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF

View file

@ -1,11 +1,53 @@
import logging
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
import esphome.config_validation as cv import esphome.config_validation as cv
_ESP32H2_SPI_FLASH_PINS = {6, 7, 15, 16, 17, 18, 19, 20, 21}
_ESP32H2_USB_JTAG_PINS = {26, 27}
_ESP32H2_STRAPPING_PINS = {2, 3, 8, 9, 25}
_LOGGER = logging.getLogger(__name__)
def esp32_h2_validate_gpio_pin(value): def esp32_h2_validate_gpio_pin(value):
# ESP32-H2 not yet supported if value < 0 or value > 27:
raise cv.Invalid("ESP32-H2 isn't supported yet") raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)")
if value in _ESP32H2_STRAPPING_PINS:
_LOGGER.warning(
"GPIO%d is a Strapping PIN and should be avoided.\n"
"Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n"
"See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins",
value,
)
if value in _ESP32H2_SPI_FLASH_PINS:
_LOGGER.warning(
"GPIO%d is reserved for SPI Flash communication on some ESP32-H2 chip variants.\n"
"Utilizing SPI-reserved pins could cause unexpected failures.\n"
"See https://docs.espressif.com/projects/esp-idf/en/latest/esp32h2/api-reference/peripherals/gpio.html",
value,
)
if value in _ESP32H2_USB_JTAG_PINS:
_LOGGER.warning(
"GPIO%d is reserved for the USB-Serial-JTAG interface.\n"
"To use this pin as GPIO, USB-Serial-JTAG will be disabled.",
value,
)
return value
def esp32_h2_validate_supports(value): def esp32_h2_validate_supports(value):
# ESP32-H2 not yet supported num = value[CONF_NUMBER]
raise cv.Invalid("ESP32-H2 isn't supported yet") mode = value[CONF_MODE]
is_input = mode[CONF_INPUT]
if num < 0 or num > 27:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)")
if is_input:
# All ESP32 pins support input mode
pass
return value

View file

@ -5,6 +5,15 @@ from esphome.components import canbus
from esphome.const import CONF_ID, CONF_RX_PIN, CONF_TX_PIN from esphome.const import CONF_ID, CONF_RX_PIN, CONF_TX_PIN
from esphome.components.canbus import CanbusComponent, CanSpeed, CONF_BIT_RATE from esphome.components.canbus import CanbusComponent, CanSpeed, CONF_BIT_RATE
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import (
VARIANT_ESP32,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
VARIANT_ESP32C3,
VARIANT_ESP32H2,
)
CODEOWNERS = ["@Sympatron"] CODEOWNERS = ["@Sympatron"]
DEPENDENCIES = ["esp32"] DEPENDENCIES = ["esp32"]
@ -12,19 +21,57 @@ esp32_can_ns = cg.esphome_ns.namespace("esp32_can")
esp32_can = esp32_can_ns.class_("ESP32Can", CanbusComponent) esp32_can = esp32_can_ns.class_("ESP32Can", CanbusComponent)
# Currently the driver only supports a subset of the bit rates defined in canbus # Currently the driver only supports a subset of the bit rates defined in canbus
CAN_SPEEDS = { # The supported bit rates differ between ESP32 variants.
# See ESP-IDF Programming Guide --> API Reference --> Two-Wire Automotive Interface (TWAI)
CAN_SPEEDS_ESP32 = {
"25KBPS": CanSpeed.CAN_25KBPS,
"50KBPS": CanSpeed.CAN_50KBPS, "50KBPS": CanSpeed.CAN_50KBPS,
"100KBPS": CanSpeed.CAN_100KBPS, "100KBPS": CanSpeed.CAN_100KBPS,
"125KBPS": CanSpeed.CAN_125KBPS, "125KBPS": CanSpeed.CAN_125KBPS,
"250KBPS": CanSpeed.CAN_250KBPS, "250KBPS": CanSpeed.CAN_250KBPS,
"500KBPS": CanSpeed.CAN_500KBPS, "500KBPS": CanSpeed.CAN_500KBPS,
"800KBPS": CanSpeed.CAN_800KBPS,
"1000KBPS": CanSpeed.CAN_1000KBPS, "1000KBPS": CanSpeed.CAN_1000KBPS,
} }
CAN_SPEEDS_ESP32_S2 = {
"1KBPS": CanSpeed.CAN_1KBPS,
"5KBPS": CanSpeed.CAN_5KBPS,
"10KBPS": CanSpeed.CAN_10KBPS,
"12K5BPS": CanSpeed.CAN_12K5BPS,
"16KBPS": CanSpeed.CAN_16KBPS,
"20KBPS": CanSpeed.CAN_20KBPS,
**CAN_SPEEDS_ESP32,
}
CAN_SPEEDS_ESP32_S3 = {**CAN_SPEEDS_ESP32_S2}
CAN_SPEEDS_ESP32_C3 = {**CAN_SPEEDS_ESP32_S2}
CAN_SPEEDS_ESP32_H2 = {**CAN_SPEEDS_ESP32_S2}
CAN_SPEEDS = {
VARIANT_ESP32: CAN_SPEEDS_ESP32,
VARIANT_ESP32S2: CAN_SPEEDS_ESP32_S2,
VARIANT_ESP32S3: CAN_SPEEDS_ESP32_S3,
VARIANT_ESP32C3: CAN_SPEEDS_ESP32_C3,
VARIANT_ESP32H2: CAN_SPEEDS_ESP32_H2,
}
def validate_bit_rate(value):
variant = get_esp32_variant()
if variant not in CAN_SPEEDS:
raise cv.Invalid(f"{variant} is not supported by component {esp32_can_ns}")
value = value.upper()
if value not in CAN_SPEEDS[variant]:
raise cv.Invalid(f"Bit rate {value} is not supported on {variant}")
return cv.enum(CAN_SPEEDS[variant])(value)
CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend( CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend(
{ {
cv.GenerateID(): cv.declare_id(esp32_can), cv.GenerateID(): cv.declare_id(esp32_can),
cv.Optional(CONF_BIT_RATE, default="125KBPS"): cv.enum(CAN_SPEEDS, upper=True), cv.Optional(CONF_BIT_RATE, default="125KBPS"): validate_bit_rate,
cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_number, cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_number,
} }

View file

@ -16,6 +16,30 @@ static const char *const TAG = "esp32_can";
static bool get_bitrate(canbus::CanSpeed bitrate, twai_timing_config_t *t_config) { static bool get_bitrate(canbus::CanSpeed bitrate, twai_timing_config_t *t_config) {
switch (bitrate) { switch (bitrate) {
#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C3) || \
defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32H6)
case canbus::CAN_1KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_1KBITS();
return true;
case canbus::CAN_5KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_5KBITS();
return true;
case canbus::CAN_10KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_10KBITS();
return true;
case canbus::CAN_12K5BPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_12_5KBITS();
return true;
case canbus::CAN_16KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_16KBITS();
return true;
case canbus::CAN_20KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_20KBITS();
return true;
#endif
case canbus::CAN_25KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_25KBITS();
return true;
case canbus::CAN_50KBPS: case canbus::CAN_50KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_50KBITS(); *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_50KBITS();
return true; return true;
@ -31,6 +55,9 @@ static bool get_bitrate(canbus::CanSpeed bitrate, twai_timing_config_t *t_config
case canbus::CAN_500KBPS: case canbus::CAN_500KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_500KBITS(); *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_500KBITS();
return true; return true;
case canbus::CAN_800KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_800KBITS();
return true;
case canbus::CAN_1000KBPS: case canbus::CAN_1000KBPS:
*t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_1MBITS(); *t_config = (twai_timing_config_t) TWAI_TIMING_CONFIG_1MBITS();
return true; return true;

View file

@ -64,6 +64,7 @@ RMT_CHANNELS = {
esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3], esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32C3: [0, 1], esp32.const.VARIANT_ESP32C3: [0, 1],
esp32.const.VARIANT_ESP32C6: [0, 1], esp32.const.VARIANT_ESP32C6: [0, 1],
esp32.const.VARIANT_ESP32H2: [0, 1],
} }

View file

@ -11,6 +11,7 @@ from esphome.const import (
KEY_FRAMEWORK_VERSION, KEY_FRAMEWORK_VERSION,
KEY_TARGET_FRAMEWORK, KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
PLATFORM_ESP8266,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
import esphome.config_validation as cv import esphome.config_validation as cv
@ -38,7 +39,7 @@ AUTO_LOAD = ["preferences"]
def set_core_data(config): def set_core_data(config):
CORE.data[KEY_ESP8266] = {} CORE.data[KEY_ESP8266] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp8266" CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_ESP8266
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse( CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
config[CONF_FRAMEWORK][CONF_VERSION] config[CONF_FRAMEWORK][CONF_VERSION]
@ -50,6 +51,17 @@ def set_core_data(config):
return config return config
def get_download_types(storage_json):
return [
{
"title": "Standard format",
"description": "For flashing ESP8266.",
"file": "firmware.bin",
"download": f"{storage_json.name}.bin",
},
]
def _format_framework_arduino_version(ver: cv.Version) -> str: def _format_framework_arduino_version(ver: cv.Version) -> str:
# format the given arduino (https://github.com/esp8266/Arduino/releases) version to # format the given arduino (https://github.com/esp8266/Arduino/releases) version to
# a PIO platformio/framework-arduinoespressif8266 value # a PIO platformio/framework-arduinoespressif8266 value

View file

@ -2,6 +2,7 @@ import logging
from dataclasses import dataclass from dataclasses import dataclass
from esphome.const import ( from esphome.const import (
CONF_ANALOG,
CONF_ID, CONF_ID,
CONF_INPUT, CONF_INPUT,
CONF_INVERTED, CONF_INVERTED,
@ -140,7 +141,6 @@ def validate_supports(value):
return value return value
CONF_ANALOG = "analog"
ESP8266_PIN_SCHEMA = cv.All( ESP8266_PIN_SCHEMA = cv.All(
{ {
cv.GenerateID(): cv.declare_id(ESP8266GPIOPin), cv.GenerateID(): cv.declare_id(ESP8266GPIOPin),

View file

@ -118,10 +118,10 @@ void EthernetComponent::setup() {
ESPHL_ERROR_CHECK(err, "ETH event handler register error"); ESPHL_ERROR_CHECK(err, "ETH event handler register error");
err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &EthernetComponent::got_ip_event_handler, nullptr); err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &EthernetComponent::got_ip_event_handler, nullptr);
ESPHL_ERROR_CHECK(err, "GOT IP event handler register error"); ESPHL_ERROR_CHECK(err, "GOT IP event handler register error");
#if LWIP_IPV6 #if ENABLE_IPV6
err = esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &EthernetComponent::got_ip6_event_handler, nullptr); err = esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &EthernetComponent::got_ip6_event_handler, nullptr);
ESPHL_ERROR_CHECK(err, "GOT IP6 event handler register error"); ESPHL_ERROR_CHECK(err, "GOT IP6 event handler register error");
#endif /* LWIP_IPV6 */ #endif /* ENABLE_IPV6 */
/* start Ethernet driver state machine */ /* start Ethernet driver state machine */
err = esp_eth_start(this->eth_handle_); err = esp_eth_start(this->eth_handle_);
@ -164,7 +164,7 @@ void EthernetComponent::loop() {
this->state_ = EthernetComponentState::CONNECTING; this->state_ = EthernetComponentState::CONNECTING;
this->start_connect_(); this->start_connect_();
} }
#if LWIP_IPV6 #if ENABLE_IPV6
else if (this->got_ipv6_) { else if (this->got_ipv6_) {
esp_ip6_addr_t ip6_addr; esp_ip6_addr_t ip6_addr;
if (esp_netif_get_ip6_global(this->eth_netif_, &ip6_addr) == 0 && if (esp_netif_get_ip6_global(this->eth_netif_, &ip6_addr) == 0 &&
@ -177,7 +177,7 @@ void EthernetComponent::loop() {
this->got_ipv6_ = false; this->got_ipv6_ = false;
} }
#endif /* LWIP_IPV6 */ #endif /* ENABLE_IPV6 */
break; break;
} }
} }
@ -236,7 +236,7 @@ bool EthernetComponent::can_proceed() { return this->is_connected(); }
network::IPAddress EthernetComponent::get_ip_address() { network::IPAddress EthernetComponent::get_ip_address() {
esp_netif_ip_info_t ip; esp_netif_ip_info_t ip;
esp_netif_get_ip_info(this->eth_netif_, &ip); esp_netif_get_ip_info(this->eth_netif_, &ip);
return {ip.ip.addr}; return network::IPAddress(&ip.ip);
} }
void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) { void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event, void *event_data) {
@ -272,14 +272,14 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP (num=%" PRId32 ")", event_id); ESP_LOGV(TAG, "[Ethernet event] ETH Got IP (num=%" PRId32 ")", event_id);
} }
#if LWIP_IPV6 #if ENABLE_IPV6
void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
void *event_data) { void *event_data) {
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP6 (num=%d)", event_id); ESP_LOGV(TAG, "[Ethernet event] ETH Got IP6 (num=%d)", event_id);
global_eth_component->got_ipv6_ = true; global_eth_component->got_ipv6_ = true;
global_eth_component->ipv6_count_ += 1; global_eth_component->ipv6_count_ += 1;
} }
#endif /* LWIP_IPV6 */ #endif /* ENABLE_IPV6 */
void EthernetComponent::start_connect_() { void EthernetComponent::start_connect_() {
this->connect_begin_ = millis(); this->connect_begin_ = millis();
@ -293,9 +293,9 @@ void EthernetComponent::start_connect_() {
esp_netif_ip_info_t info; esp_netif_ip_info_t info;
if (this->manual_ip_.has_value()) { if (this->manual_ip_.has_value()) {
info.ip.addr = static_cast<uint32_t>(this->manual_ip_->static_ip); info.ip = this->manual_ip_->static_ip;
info.gw.addr = static_cast<uint32_t>(this->manual_ip_->gateway); info.gw = this->manual_ip_->gateway;
info.netmask.addr = static_cast<uint32_t>(this->manual_ip_->subnet); info.netmask = this->manual_ip_->subnet;
} else { } else {
info.ip.addr = 0; info.ip.addr = 0;
info.gw.addr = 0; info.gw.addr = 0;
@ -318,24 +318,14 @@ void EthernetComponent::start_connect_() {
ESPHL_ERROR_CHECK(err, "DHCPC set IP info error"); ESPHL_ERROR_CHECK(err, "DHCPC set IP info error");
if (this->manual_ip_.has_value()) { if (this->manual_ip_.has_value()) {
if (uint32_t(this->manual_ip_->dns1) != 0) { if (this->manual_ip_->dns1.is_set()) {
ip_addr_t d; ip_addr_t d;
#if LWIP_IPV6 d = this->manual_ip_->dns1;
d.type = IPADDR_TYPE_V4;
d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
#else
d.addr = static_cast<uint32_t>(this->manual_ip_->dns1);
#endif
dns_setserver(0, &d); dns_setserver(0, &d);
} }
if (uint32_t(this->manual_ip_->dns2) != 0) { if (this->manual_ip_->dns2.is_set()) {
ip_addr_t d; ip_addr_t d;
#if LWIP_IPV6 d = this->manual_ip_->dns2;
d.type = IPADDR_TYPE_V4;
d.u_addr.ip4.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
#else
d.addr = static_cast<uint32_t>(this->manual_ip_->dns2);
#endif
dns_setserver(1, &d); dns_setserver(1, &d);
} }
} else { } else {
@ -343,12 +333,12 @@ void EthernetComponent::start_connect_() {
if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) { if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) {
ESPHL_ERROR_CHECK(err, "DHCPC start error"); ESPHL_ERROR_CHECK(err, "DHCPC start error");
} }
#if LWIP_IPV6 #if ENABLE_IPV6
err = esp_netif_create_ip6_linklocal(this->eth_netif_); err = esp_netif_create_ip6_linklocal(this->eth_netif_);
if (err != ESP_OK) { if (err != ESP_OK) {
ESPHL_ERROR_CHECK(err, "IPv6 local failed"); ESPHL_ERROR_CHECK(err, "IPv6 local failed");
} }
#endif /* LWIP_IPV6 */ #endif /* ENABLE_IPV6 */
} }
this->connect_begin_ = millis(); this->connect_begin_ = millis();
@ -360,23 +350,18 @@ bool EthernetComponent::is_connected() { return this->state_ == EthernetComponen
void EthernetComponent::dump_connect_params_() { void EthernetComponent::dump_connect_params_() {
esp_netif_ip_info_t ip; esp_netif_ip_info_t ip;
esp_netif_get_ip_info(this->eth_netif_, &ip); esp_netif_get_ip_info(this->eth_netif_, &ip);
ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(ip.ip.addr).str().c_str()); ESP_LOGCONFIG(TAG, " IP Address: %s", network::IPAddress(&ip.ip).str().c_str());
ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str()); ESP_LOGCONFIG(TAG, " Hostname: '%s'", App.get_name().c_str());
ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(ip.netmask.addr).str().c_str()); ESP_LOGCONFIG(TAG, " Subnet: %s", network::IPAddress(&ip.netmask).str().c_str());
ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(ip.gw.addr).str().c_str()); ESP_LOGCONFIG(TAG, " Gateway: %s", network::IPAddress(&ip.gw).str().c_str());
const ip_addr_t *dns_ip1 = dns_getserver(0); const ip_addr_t *dns_ip1 = dns_getserver(0);
const ip_addr_t *dns_ip2 = dns_getserver(1); const ip_addr_t *dns_ip2 = dns_getserver(1);
#if LWIP_IPV6 ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1).str().c_str());
ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->u_addr.ip4.addr).str().c_str()); ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2).str().c_str());
ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->u_addr.ip4.addr).str().c_str());
#else
ESP_LOGCONFIG(TAG, " DNS1: %s", network::IPAddress(dns_ip1->addr).str().c_str());
ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str());
#endif
#if LWIP_IPV6 #if ENABLE_IPV6
if (this->ipv6_count_ > 0) { if (this->ipv6_count_ > 0) {
esp_ip6_addr_t ip6_addr; esp_ip6_addr_t ip6_addr;
esp_netif_get_ip6_linklocal(this->eth_netif_, &ip6_addr); esp_netif_get_ip6_linklocal(this->eth_netif_, &ip6_addr);
@ -387,7 +372,7 @@ void EthernetComponent::dump_connect_params_() {
ESP_LOGCONFIG(TAG, "IPv6 Addr (Global): " IPV6STR, IPV62STR(ip6_addr)); ESP_LOGCONFIG(TAG, "IPv6 Addr (Global): " IPV6STR, IPV62STR(ip6_addr));
} }
} }
#endif /* LWIP_IPV6 */ #endif /* ENABLE_IPV6 */
esp_err_t err; esp_err_t err;

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/components/network/ip_address.h" #include "esphome/components/network/ip_address.h"

View file

@ -67,18 +67,13 @@ def validate_pillow_installed(value):
except ImportError as err: except ImportError as err:
raise cv.Invalid( raise cv.Invalid(
"Please install the pillow python package to use this feature. " "Please install the pillow python package to use this feature. "
'(pip install pillow">4.0.0,<10.0.0")' '(pip install "pillow==10.0.1")'
) from err ) from err
if version.parse(PIL.__version__) < version.parse("4.0.0"): if version.parse(PIL.__version__) != version.parse("10.0.1"):
raise cv.Invalid( raise cv.Invalid(
"Please update your pillow installation to at least 4.0.x. " "Please update your pillow installation to 10.0.1. "
'(pip install pillow">4.0.0,<10.0.0")' '(pip install "pillow==10.0.1")'
)
if version.parse(PIL.__version__) >= version.parse("10.0.0"):
raise cv.Invalid(
"Please downgrade your pillow installation to below 10.0.0. "
'(pip install pillow">4.0.0,<10.0.0")'
) )
return value return value
@ -98,10 +93,9 @@ def validate_truetype_file(value):
def _compute_local_font_dir(name) -> Path: def _compute_local_font_dir(name) -> Path:
base_dir = Path(CORE.config_dir) / ".esphome" / DOMAIN
h = hashlib.new("sha256") h = hashlib.new("sha256")
h.update(name.encode()) h.update(name.encode())
return base_dir / h.hexdigest()[:8] return Path(CORE.data_dir) / DOMAIN / h.hexdigest()[:8]
def _compute_gfonts_local_path(value) -> Path: def _compute_gfonts_local_path(value) -> Path:

View file

@ -15,8 +15,14 @@ CODEOWNERS = ["@esphome/core"]
globals_ns = cg.esphome_ns.namespace("globals") globals_ns = cg.esphome_ns.namespace("globals")
GlobalsComponent = globals_ns.class_("GlobalsComponent", cg.Component) GlobalsComponent = globals_ns.class_("GlobalsComponent", cg.Component)
RestoringGlobalsComponent = globals_ns.class_("RestoringGlobalsComponent", cg.Component) RestoringGlobalsComponent = globals_ns.class_("RestoringGlobalsComponent", cg.Component)
RestoringGlobalStringComponent = globals_ns.class_(
"RestoringGlobalStringComponent", cg.Component
)
GlobalVarSetAction = globals_ns.class_("GlobalVarSetAction", automation.Action) GlobalVarSetAction = globals_ns.class_("GlobalVarSetAction", automation.Action)
CONF_MAX_RESTORE_DATA_LENGTH = "max_restore_data_length"
MULTI_CONF = True MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
@ -24,6 +30,7 @@ CONFIG_SCHEMA = cv.Schema(
cv.Required(CONF_TYPE): cv.string_strict, cv.Required(CONF_TYPE): cv.string_strict,
cv.Optional(CONF_INITIAL_VALUE): cv.string_strict, cv.Optional(CONF_INITIAL_VALUE): cv.string_strict,
cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean,
cv.Optional(CONF_MAX_RESTORE_DATA_LENGTH): cv.int_range(0, 254),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@ -32,12 +39,19 @@ CONFIG_SCHEMA = cv.Schema(
@coroutine_with_priority(-100.0) @coroutine_with_priority(-100.0)
async def to_code(config): async def to_code(config):
type_ = cg.RawExpression(config[CONF_TYPE]) type_ = cg.RawExpression(config[CONF_TYPE])
template_args = cg.TemplateArguments(type_)
restore = config[CONF_RESTORE_VALUE] restore = config[CONF_RESTORE_VALUE]
# Special casing the strings to their own class with a different save/restore mechanism
if str(type_) == "std::string" and restore:
template_args = cg.TemplateArguments(
type_, config.get(CONF_MAX_RESTORE_DATA_LENGTH, 63) + 1
)
type = RestoringGlobalStringComponent
else:
template_args = cg.TemplateArguments(type_)
type = RestoringGlobalsComponent if restore else GlobalsComponent type = RestoringGlobalsComponent if restore else GlobalsComponent
res_type = type.template(template_args)
res_type = type.template(template_args)
initial_value = None initial_value = None
if CONF_INITIAL_VALUE in config: if CONF_INITIAL_VALUE in config:
initial_value = cg.RawExpression(config[CONF_INITIAL_VALUE]) initial_value = cg.RawExpression(config[CONF_INITIAL_VALUE])

View file

@ -65,6 +65,64 @@ template<typename T> class RestoringGlobalsComponent : public Component {
ESPPreferenceObject rtc_; ESPPreferenceObject rtc_;
}; };
// Use with string or subclasses of strings
template<typename T, uint8_t SZ> class RestoringGlobalStringComponent : public Component {
public:
using value_type = T;
explicit RestoringGlobalStringComponent() = default;
explicit RestoringGlobalStringComponent(T initial_value) { this->value_ = initial_value; }
explicit RestoringGlobalStringComponent(
std::array<typename std::remove_extent<T>::type, std::extent<T>::value> initial_value) {
memcpy(this->value_, initial_value.data(), sizeof(T));
}
T &value() { return this->value_; }
void setup() override {
char temp[SZ];
this->rtc_ = global_preferences->make_preference<uint8_t[SZ]>(1944399030U ^ this->name_hash_);
bool hasdata = this->rtc_.load(&temp);
if (hasdata) {
this->value_.assign(temp + 1, temp[0]);
}
this->prev_value_.assign(this->value_);
}
float get_setup_priority() const override { return setup_priority::HARDWARE; }
void loop() override { store_value_(); }
void on_shutdown() override { store_value_(); }
void set_name_hash(uint32_t name_hash) { this->name_hash_ = name_hash; }
protected:
void store_value_() {
int diff = this->value_.compare(this->prev_value_);
if (diff != 0) {
// Make it into a length prefixed thing
unsigned char temp[SZ];
// If string is bigger than the allocation, do not save it.
// We don't need to waste ram setting prev_value either.
int size = this->value_.size();
// Less than, not less than or equal, SZ includes the length byte.
if (size < SZ) {
memcpy(temp + 1, this->value_.c_str(), size);
// SZ should be pre checked at the schema level, it can't go past the char range.
temp[0] = ((unsigned char) size);
this->rtc_.save(&temp);
this->prev_value_.assign(this->value_);
}
}
}
T value_{};
T prev_value_{};
uint32_t name_hash_{};
ESPPreferenceObject rtc_;
};
template<class C, typename... Ts> class GlobalVarSetAction : public Action<Ts...> { template<class C, typename... Ts> class GlobalVarSetAction : public Action<Ts...> {
public: public:
explicit GlobalVarSetAction(C *parent) : parent_(parent) {} explicit GlobalVarSetAction(C *parent) : parent_(parent) {}
@ -81,6 +139,7 @@ template<class C, typename... Ts> class GlobalVarSetAction : public Action<Ts...
template<typename T> T &id(GlobalsComponent<T> *value) { return value->value(); } template<typename T> T &id(GlobalsComponent<T> *value) { return value->value(); }
template<typename T> T &id(RestoringGlobalsComponent<T> *value) { return value->value(); } template<typename T> T &id(RestoringGlobalsComponent<T> *value) { return value->value(); }
template<typename T, uint8_t SZ> T &id(RestoringGlobalStringComponent<T, SZ> *value) { return value->value(); }
} // namespace globals } // namespace globals
} // namespace esphome } // namespace esphome

View file

@ -6,6 +6,9 @@ from esphome.const import (
CONF_CURRENT, CONF_CURRENT,
CONF_FREQUENCY, CONF_FREQUENCY,
CONF_ID, CONF_ID,
CONF_PHASE_A,
CONF_PHASE_B,
CONF_PHASE_C,
CONF_VOLTAGE, CONF_VOLTAGE,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,
@ -21,10 +24,6 @@ from esphome.const import (
UNIT_WATT, UNIT_WATT,
) )
CONF_PHASE_A = "phase_a"
CONF_PHASE_B = "phase_b"
CONF_PHASE_C = "phase_c"
CONF_ENERGY_PRODUCTION_DAY = "energy_production_day" CONF_ENERGY_PRODUCTION_DAY = "energy_production_day"
CONF_TOTAL_ENERGY_PRODUCTION = "total_energy_production" CONF_TOTAL_ENERGY_PRODUCTION = "total_energy_production"
CONF_TOTAL_GENERATION_TIME = "total_generation_time" CONF_TOTAL_GENERATION_TIME = "total_generation_time"

View file

@ -136,12 +136,10 @@ def validate_visual(config):
f"Configured visual temperature step {temp_step} is wrong, it should be a multiple of 0.5" f"Configured visual temperature step {temp_step} is wrong, it should be a multiple of 0.5"
) )
else: else:
config[CONF_VISUAL][CONF_TEMPERATURE_STEP] = ( config[CONF_VISUAL][CONF_TEMPERATURE_STEP] = {
{
CONF_TARGET_TEMPERATURE: PROTOCOL_TARGET_TEMPERATURE_STEP, CONF_TARGET_TEMPERATURE: PROTOCOL_TARGET_TEMPERATURE_STEP,
CONF_CURRENT_TEMPERATURE: PROTOCOL_CURRENT_TEMPERATURE_STEP, CONF_CURRENT_TEMPERATURE: PROTOCOL_CURRENT_TEMPERATURE_STEP,
}, }
)
else: else:
config[CONF_VISUAL] = { config[CONF_VISUAL] = {
CONF_MIN_TEMPERATURE: PROTOCOL_MIN_TEMPERATURE, CONF_MIN_TEMPERATURE: PROTOCOL_MIN_TEMPERATURE,

View file

@ -6,6 +6,9 @@ from esphome.const import (
CONF_CURRENT, CONF_CURRENT,
CONF_FREQUENCY, CONF_FREQUENCY,
CONF_ID, CONF_ID,
CONF_PHASE_A,
CONF_PHASE_B,
CONF_PHASE_C,
CONF_REACTIVE_POWER, CONF_REACTIVE_POWER,
CONF_VOLTAGE, CONF_VOLTAGE,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
@ -24,9 +27,6 @@ from esphome.const import (
UNIT_WATT, UNIT_WATT,
) )
CONF_PHASE_A = "phase_a"
CONF_PHASE_B = "phase_b"
CONF_PHASE_C = "phase_c"
CONF_ENERGY_PRODUCTION_DAY = "energy_production_day" CONF_ENERGY_PRODUCTION_DAY = "energy_production_day"
CONF_TOTAL_ENERGY_PRODUCTION = "total_energy_production" CONF_TOTAL_ENERGY_PRODUCTION = "total_energy_production"
CONF_TOTAL_GENERATION_TIME = "total_generation_time" CONF_TOTAL_GENERATION_TIME = "total_generation_time"

View file

@ -3,6 +3,9 @@ import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import ( from esphome.const import (
CONF_ADDRESS, CONF_ADDRESS,
CONF_FIELD_STRENGTH_X,
CONF_FIELD_STRENGTH_Y,
CONF_FIELD_STRENGTH_Z,
CONF_ID, CONF_ID,
CONF_OVERSAMPLING, CONF_OVERSAMPLING,
CONF_RANGE, CONF_RANGE,
@ -18,9 +21,6 @@ DEPENDENCIES = ["i2c"]
hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l") hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l")
CONF_FIELD_STRENGTH_X = "field_strength_x"
CONF_FIELD_STRENGTH_Y = "field_strength_y"
CONF_FIELD_STRENGTH_Z = "field_strength_z"
CONF_HEADING = "heading" CONF_HEADING = "heading"
HMC5883LComponent = hmc5883l_ns.class_( HMC5883LComponent = hmc5883l_ns.class_(

View file

@ -3,6 +3,7 @@ from esphome.const import (
KEY_FRAMEWORK_VERSION, KEY_FRAMEWORK_VERSION,
KEY_TARGET_FRAMEWORK, KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
PLATFORM_HOST,
) )
from esphome.core import CORE from esphome.core import CORE
import esphome.config_validation as cv import esphome.config_validation as cv
@ -20,7 +21,7 @@ AUTO_LOAD = ["network"]
def set_core_data(config): def set_core_data(config):
CORE.data[KEY_HOST] = {} CORE.data[KEY_HOST] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "host" CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_HOST
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host" CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host"
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(1, 0, 0) CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(1, 0, 0)
return config return config

View file

@ -1,9 +1,10 @@
// Official Datasheet: // Official Datasheet:
// https://www.maxbotix.com/documents/HRXL-MaxSonar-WR_Datasheet.pdf // HRXL: https://www.maxbotix.com/documents/HRXL-MaxSonar-WR_Datasheet.pdf
// XL: https://www.maxbotix.com/documents/XL-MaxSonar-WR_Datasheet.pdf
// //
// This implementation is designed to work with the TTL Versions of the // This implementation is designed to work with the TTL Versions of the
// MaxBotix HRXL MaxSonar WR sensor series. The sensor's TTL Pin (5) should be // MaxBotix HRXL and XL MaxSonar WR sensor series. The sensor's TTL Pin (5)
// wired to one of the ESP's input pins and configured as uart rx_pin. // should be wired to one of the ESP's input pins and configured as uart rx_pin.
#include "hrxl_maxsonar_wr.h" #include "hrxl_maxsonar_wr.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@ -17,8 +18,10 @@ static const uint8_t ASCII_NBSP = 0xFF;
static const int MAX_DATA_LENGTH_BYTES = 6; static const int MAX_DATA_LENGTH_BYTES = 6;
/** /**
* The sensor outputs something like "R1234\r" at a fixed rate of 6 Hz. Where * HRXL sensors output the format "R1234\r" at 6Hz
* 1234 means a distance of 1,234 m. * The 1234 means 1234mm
* XL sensors output the format "R123\r" at 5 to 10Hz
* The 123 means 123cm
*/ */
void HrxlMaxsonarWrComponent::loop() { void HrxlMaxsonarWrComponent::loop() {
uint8_t data; uint8_t data;
@ -42,9 +45,17 @@ void HrxlMaxsonarWrComponent::check_buffer_() {
if (this->buffer_.back() == static_cast<char>(ASCII_CR) || this->buffer_.length() >= MAX_DATA_LENGTH_BYTES) { if (this->buffer_.back() == static_cast<char>(ASCII_CR) || this->buffer_.length() >= MAX_DATA_LENGTH_BYTES) {
ESP_LOGV(TAG, "Read from serial: %s", this->buffer_.c_str()); ESP_LOGV(TAG, "Read from serial: %s", this->buffer_.c_str());
if (this->buffer_.length() == MAX_DATA_LENGTH_BYTES && this->buffer_[0] == 'R' && size_t rpos = this->buffer_.find(static_cast<char>(ASCII_CR));
this->buffer_.back() == static_cast<char>(ASCII_CR)) {
int millimeters = parse_number<int>(this->buffer_.substr(1, MAX_DATA_LENGTH_BYTES - 2)).value_or(0); if (this->buffer_.length() <= MAX_DATA_LENGTH_BYTES && this->buffer_[0] == 'R' && rpos != std::string::npos) {
std::string distance = this->buffer_.substr(1, rpos - 1);
int millimeters = parse_number<int>(distance).value_or(0);
// XL reports in cm instead of mm and reports 3 digits instead of 4
if (distance.length() == 3) {
millimeters = millimeters * 10;
}
float meters = float(millimeters) / 1000.0; float meters = float(millimeters) / 1000.0;
ESP_LOGV(TAG, "Distance from sensor: %d mm, %f m", millimeters, meters); ESP_LOGV(TAG, "Distance from sensor: %d mm, %f m", millimeters, meters);
this->publish_state(meters); this->publish_state(meters);

View file

@ -80,8 +80,6 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(std::string, url)
TEMPLATABLE_VALUE(const char *, method) TEMPLATABLE_VALUE(const char *, method)
TEMPLATABLE_VALUE(std::string, body) TEMPLATABLE_VALUE(std::string, body)
TEMPLATABLE_VALUE(const char *, useragent)
TEMPLATABLE_VALUE(uint16_t, timeout)
void add_header(const char *key, TemplatableValue<const char *, Ts...> value) { this->headers_.insert({key, value}); } void add_header(const char *key, TemplatableValue<const char *, Ts...> value) { this->headers_.insert({key, value}); }
@ -105,13 +103,6 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1); auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1);
this->parent_->set_body(json::build_json(f)); this->parent_->set_body(json::build_json(f));
} }
if (this->useragent_.has_value()) {
this->parent_->set_useragent(this->useragent_.value(x...));
}
if (this->timeout_.has_value()) {
this->parent_->set_timeout(this->timeout_.value(x...));
}
if (!this->headers_.empty()) {
std::list<Header> headers; std::list<Header> headers;
for (const auto &item : this->headers_) { for (const auto &item : this->headers_) {
auto val = item.second; auto val = item.second;
@ -121,9 +112,9 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
headers.push_back(header); headers.push_back(header);
} }
this->parent_->set_headers(headers); this->parent_->set_headers(headers);
}
this->parent_->send(this->response_triggers_); this->parent_->send(this->response_triggers_);
this->parent_->close(); this->parent_->close();
this->parent_->set_body("");
} }
protected: protected:

View file

@ -11,7 +11,11 @@ static const uint8_t HTU21D_ADDRESS = 0x40;
static const uint8_t HTU21D_REGISTER_RESET = 0xFE; static const uint8_t HTU21D_REGISTER_RESET = 0xFE;
static const uint8_t HTU21D_REGISTER_TEMPERATURE = 0xF3; static const uint8_t HTU21D_REGISTER_TEMPERATURE = 0xF3;
static const uint8_t HTU21D_REGISTER_HUMIDITY = 0xF5; static const uint8_t HTU21D_REGISTER_HUMIDITY = 0xF5;
static const uint8_t HTU21D_WRITERHT_REG_CMD = 0xE6; /**< Write RH/T User Register 1 */
static const uint8_t HTU21D_REGISTER_STATUS = 0xE7; static const uint8_t HTU21D_REGISTER_STATUS = 0xE7;
static const uint8_t HTU21D_WRITEHEATER_REG_CMD = 0x51; /**< Write Heater Control Register */
static const uint8_t HTU21D_READHEATER_REG_CMD = 0x11; /**< Read Heater Control Register */
static const uint8_t HTU21D_REG_HTRE_BIT = 0x02; /**< Control Register Heater Bit */
void HTU21DComponent::setup() { void HTU21DComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up HTU21D..."); ESP_LOGCONFIG(TAG, "Setting up HTU21D...");
@ -62,14 +66,66 @@ void HTU21DComponent::update() {
raw_humidity = i2c::i2ctohs(raw_humidity); raw_humidity = i2c::i2ctohs(raw_humidity);
float humidity = (float(raw_humidity & 0xFFFC)) * 125.0f / 65536.0f - 6.0f; float humidity = (float(raw_humidity & 0xFFFC)) * 125.0f / 65536.0f - 6.0f;
ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity);
int8_t heater_level = this->get_heater_level();
ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%% Heater Level=%d", temperature, humidity, heater_level);
if (this->temperature_ != nullptr) if (this->temperature_ != nullptr)
this->temperature_->publish_state(temperature); this->temperature_->publish_state(temperature);
if (this->humidity_ != nullptr) if (this->humidity_ != nullptr)
this->humidity_->publish_state(humidity); this->humidity_->publish_state(humidity);
if (this->heater_ != nullptr)
this->heater_->publish_state(humidity);
this->status_clear_warning(); this->status_clear_warning();
} }
bool HTU21DComponent::is_heater_enabled() {
uint8_t raw_heater;
if (this->read_register(HTU21D_REGISTER_STATUS, reinterpret_cast<uint8_t *>(&raw_heater), 2) != i2c::ERROR_OK) {
this->status_set_warning();
return false;
}
raw_heater = i2c::i2ctohs(raw_heater);
return (bool) (((raw_heater) >> (HTU21D_REG_HTRE_BIT)) & 0x01);
}
void HTU21DComponent::set_heater(bool status) {
uint8_t raw_heater;
if (this->read_register(HTU21D_REGISTER_STATUS, reinterpret_cast<uint8_t *>(&raw_heater), 2) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
raw_heater = i2c::i2ctohs(raw_heater);
if (status) {
raw_heater |= (1 << (HTU21D_REG_HTRE_BIT));
} else {
raw_heater &= ~(1 << (HTU21D_REG_HTRE_BIT));
}
if (this->write_register(HTU21D_WRITERHT_REG_CMD, &raw_heater, 1) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
}
void HTU21DComponent::set_heater_level(uint8_t level) {
if (this->write_register(HTU21D_WRITEHEATER_REG_CMD, &level, 1) != i2c::ERROR_OK) {
this->status_set_warning();
return;
}
}
int8_t HTU21DComponent::get_heater_level() {
int8_t raw_heater;
if (this->read_register(HTU21D_READHEATER_REG_CMD, reinterpret_cast<uint8_t *>(&raw_heater), 2) != i2c::ERROR_OK) {
this->status_set_warning();
return 0;
}
raw_heater = i2c::i2ctohs(raw_heater);
return raw_heater;
}
float HTU21DComponent::get_setup_priority() const { return setup_priority::DATA; } float HTU21DComponent::get_setup_priority() const { return setup_priority::DATA; }
} // namespace htu21d } // namespace htu21d

View file

@ -3,6 +3,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h" #include "esphome/components/i2c/i2c.h"
#include "esphome/core/automation.h"
namespace esphome { namespace esphome {
namespace htu21d { namespace htu21d {
@ -11,6 +12,7 @@ class HTU21DComponent : public PollingComponent, public i2c::I2CDevice {
public: public:
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_heater(sensor::Sensor *heater) { heater_ = heater; }
/// Setup (reset) the sensor and check connection. /// Setup (reset) the sensor and check connection.
void setup() override; void setup() override;
@ -18,11 +20,39 @@ class HTU21DComponent : public PollingComponent, public i2c::I2CDevice {
/// Update the sensor values (temperature+humidity). /// Update the sensor values (temperature+humidity).
void update() override; void update() override;
bool is_heater_enabled();
void set_heater(bool status);
void set_heater_level(uint8_t level);
int8_t get_heater_level();
float get_setup_priority() const override; float get_setup_priority() const override;
protected: protected:
sensor::Sensor *temperature_{nullptr}; sensor::Sensor *temperature_{nullptr};
sensor::Sensor *humidity_{nullptr}; sensor::Sensor *humidity_{nullptr};
sensor::Sensor *heater_{nullptr};
};
template<typename... Ts> class SetHeaterLevelAction : public Action<Ts...>, public Parented<HTU21DComponent> {
public:
TEMPLATABLE_VALUE(uint8_t, level)
void play(Ts... x) override {
auto level = this->level_.value(x...);
this->parent_->set_heater_level(level);
}
};
template<typename... Ts> class SetHeaterAction : public Action<Ts...>, public Parented<HTU21DComponent> {
public:
TEMPLATABLE_VALUE(bool, status)
void play(Ts... x) override {
auto status = this->status_.value(x...);
this->parent_->set_heater(status);
}
}; };
} // namespace htu21d } // namespace htu21d

View file

@ -1,6 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome import automation
from esphome.const import ( from esphome.const import (
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_ID, CONF_ID,
@ -10,6 +11,10 @@ from esphome.const import (
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_PERCENT, UNIT_PERCENT,
CONF_HEATER,
UNIT_EMPTY,
CONF_LEVEL,
CONF_STATUS,
) )
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
@ -19,6 +24,10 @@ HTU21DComponent = htu21d_ns.class_(
"HTU21DComponent", cg.PollingComponent, i2c.I2CDevice "HTU21DComponent", cg.PollingComponent, i2c.I2CDevice
) )
SetHeaterLevelAction = htu21d_ns.class_("SetHeaterLevelAction", automation.Action)
SetHeaterAction = htu21d_ns.class_("SetHeaterAction", automation.Action)
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
cv.Schema( cv.Schema(
{ {
@ -35,6 +44,11 @@ CONFIG_SCHEMA = (
device_class=DEVICE_CLASS_HUMIDITY, device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_HEATER): sensor.sensor_schema(
unit_of_measurement=UNIT_EMPTY,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
} }
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s"))
@ -54,3 +68,45 @@ async def to_code(config):
if CONF_HUMIDITY in config: if CONF_HUMIDITY in config:
sens = await sensor.new_sensor(config[CONF_HUMIDITY]) sens = await sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens)) cg.add(var.set_humidity(sens))
if CONF_HEATER in config:
sens = await sensor.new_sensor(config[CONF_HEATER])
cg.add(var.set_heater(sens))
@automation.register_action(
"htu21d.set_heater_level",
SetHeaterLevelAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(HTU21DComponent),
cv.Required(CONF_LEVEL): cv.templatable(cv.int_),
},
key=CONF_LEVEL,
),
)
async def set_heater_level_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
level_ = await cg.templatable(config[CONF_LEVEL], args, int)
cg.add(var.set_level(level_))
return var
@automation.register_action(
"htu21d.set_heater",
SetHeaterAction,
cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(HTU21DComponent),
cv.Required(CONF_STATUS): cv.templatable(cv.boolean),
},
key=CONF_STATUS,
),
)
async def set_heater_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
status_ = await cg.templatable(config[CONF_LEVEL], args, bool)
cg.add(var.set_status(status_))
return var

View file

@ -12,6 +12,9 @@ from esphome.const import (
CONF_SDA, CONF_SDA,
CONF_ADDRESS, CONF_ADDRESS,
CONF_I2C_ID, CONF_I2C_ID,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
) )
from esphome.core import coroutine_with_priority, CORE from esphome.core import coroutine_with_priority, CORE
@ -42,7 +45,8 @@ pin_with_input_and_output_support = cv.All(
) )
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.All(
cv.Schema(
{ {
cv.GenerateID(): _bus_declare_type, cv.GenerateID(): _bus_declare_type,
cv.Optional(CONF_SDA, default="SDA"): pin_with_input_and_output_support, cv.Optional(CONF_SDA, default="SDA"): pin_with_input_and_output_support,
@ -58,7 +62,9 @@ CONFIG_SCHEMA = cv.Schema(
), ),
cv.Optional(CONF_SCAN, default=True): cv.boolean, cv.Optional(CONF_SCAN, default=True): cv.boolean,
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA),
cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]),
)
@coroutine_with_priority(1.0) @coroutine_with_priority(1.0)

View file

@ -66,8 +66,9 @@ void I2SAudioMicrophone::start_() {
i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_); i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_);
i2s_adc_enable(this->parent_->get_port()); i2s_adc_enable(this->parent_->get_port());
} else { } else
#endif #endif
{
if (this->pdm_) if (this->pdm_)
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM); config.mode = (i2s_mode_t) (config.mode | I2S_MODE_PDM);
@ -77,9 +78,7 @@ void I2SAudioMicrophone::start_() {
pin_config.data_in_num = this->din_pin_; pin_config.data_in_num = this->din_pin_;
i2s_set_pin(this->parent_->get_port(), &pin_config); i2s_set_pin(this->parent_->get_port(), &pin_config);
#if SOC_I2S_SUPPORTS_ADC
} }
#endif
this->state_ = microphone::STATE_RUNNING; this->state_ = microphone::STATE_RUNNING;
this->high_freq_.start(); this->high_freq_.start();
} }
@ -110,6 +109,10 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
this->status_set_warning(); this->status_set_warning();
return 0; return 0;
} }
if (bytes_read == 0) {
this->status_set_warning();
return 0;
}
this->status_clear_warning(); this->status_clear_warning();
if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) { if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) {
return bytes_read; return bytes_read;

View file

@ -158,10 +158,10 @@ void I2SAudioSpeaker::watch_() {
this->status_clear_warning(); this->status_clear_warning();
break; break;
case TaskEventType::STOPPED: case TaskEventType::STOPPED:
this->parent_->unlock();
this->state_ = speaker::STATE_STOPPED; this->state_ = speaker::STATE_STOPPED;
vTaskDelete(this->player_task_handle_); vTaskDelete(this->player_task_handle_);
this->player_task_handle_ = nullptr; this->player_task_handle_ = nullptr;
this->parent_->unlock();
break; break;
case TaskEventType::WARNING: case TaskEventType::WARNING:
ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err)); ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err));
@ -177,9 +177,9 @@ void I2SAudioSpeaker::loop() {
this->start_(); this->start_();
break; break;
case speaker::STATE_RUNNING: case speaker::STATE_RUNNING:
case speaker::STATE_STOPPING:
this->watch_(); this->watch_();
break; break;
case speaker::STATE_STOPPING:
case speaker::STATE_STOPPED: case speaker::STATE_STOPPED:
break; break;
} }

View file

@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import core, pins from esphome import core, pins
from esphome.components import display, spi from esphome.components import display, spi, font
from esphome.core import CORE, HexInt from esphome.core import CORE, HexInt
from esphome.const import ( from esphome.const import (
CONF_COLOR_PALETTE, CONF_COLOR_PALETTE,
@ -13,7 +13,6 @@ from esphome.const import (
CONF_PAGES, CONF_PAGES,
CONF_RESET_PIN, CONF_RESET_PIN,
CONF_DIMENSIONS, CONF_DIMENSIONS,
CONF_DATA_RATE,
) )
DEPENDENCIES = ["spi"] DEPENDENCIES = ["spi"]
@ -25,7 +24,7 @@ def AUTO_LOAD():
return [] return []
CODEOWNERS = ["@nielsnl68"] CODEOWNERS = ["@nielsnl68", "@clydebarrow"]
ili9XXX_ns = cg.esphome_ns.namespace("ili9xxx") ili9XXX_ns = cg.esphome_ns.namespace("ili9xxx")
ili9XXXSPI = ili9XXX_ns.class_( ili9XXXSPI = ili9XXX_ns.class_(
@ -42,6 +41,7 @@ MODELS = {
"ILI9341": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI), "ILI9341": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI),
"ILI9342": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI), "ILI9342": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI),
"ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI), "ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI),
"ILI9481-18": ili9XXX_ns.class_("ILI9XXXILI948118", ili9XXXSPI),
"ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI), "ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI),
"ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI), "ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI),
"ILI9488_A": ili9XXX_ns.class_("ILI9XXXILI9488A", ili9XXXSPI), "ILI9488_A": ili9XXX_ns.class_("ILI9XXXILI9488A", ili9XXXSPI),
@ -84,6 +84,7 @@ def _validate(config):
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
font.validate_pillow_installed,
display.FULL_DISPLAY_SCHEMA.extend( display.FULL_DISPLAY_SCHEMA.extend(
{ {
cv.GenerateID(): cv.declare_id(ili9XXXSPI), cv.GenerateID(): cv.declare_id(ili9XXXSPI),
@ -99,11 +100,10 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list( cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list(
cv.file_ cv.file_
), ),
cv.Optional(CONF_DATA_RATE, default="40MHz"): spi.SPI_DATA_RATE_SCHEMA,
} }
) )
.extend(cv.polling_component_schema("1s")) .extend(cv.polling_component_schema("1s"))
.extend(spi.spi_device_schema(False)), .extend(spi.spi_device_schema(False, "40MHz")),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
_validate, _validate,
) )
@ -140,8 +140,6 @@ async def to_code(config):
rhs = [] rhs = []
for x in range(256): for x in range(256):
rhs.extend([HexInt(x), HexInt(x), HexInt(x)]) rhs.extend([HexInt(x), HexInt(x), HexInt(x)])
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.add(var.set_palette(prog_arr))
elif config[CONF_COLOR_PALETTE] == "IMAGE_ADAPTIVE": elif config[CONF_COLOR_PALETTE] == "IMAGE_ADAPTIVE":
cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8_INDEXED)) cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8_INDEXED))
from PIL import Image from PIL import Image
@ -165,7 +163,7 @@ async def to_code(config):
x = x + i.width x = x + i.width
# reduce the colors on combined image to 256. # reduce the colors on combined image to 256.
converted = ref_image.convert("P", palette=Image.ADAPTIVE, colors=256) converted = ref_image.convert("P", palette=Image.Palette.ADAPTIVE, colors=256)
# if you want to verify how the images look use # if you want to verify how the images look use
# ref_image.save("ref_in.png") # ref_image.save("ref_in.png")
# converted.save("ref_out.png") # converted.save("ref_out.png")
@ -178,6 +176,3 @@ async def to_code(config):
if rhs is not None: if rhs is not None:
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.add(var.set_palette(prog_arr)) cg.add(var.set_palette(prog_arr))
spi_data_rate = str(spi.SPI_DATA_RATE_OPTIONS[config[CONF_DATA_RATE]])
cg.add_define("ILI9XXXDisplay_DATA_RATE", cg.RawExpression(spi_data_rate))

View file

@ -59,6 +59,7 @@ void ILI9XXXDisplay::dump_config() {
if (this->is_18bitdisplay_) { if (this->is_18bitdisplay_) {
ESP_LOGCONFIG(TAG, " 18-Bit Mode: YES"); ESP_LOGCONFIG(TAG, " 18-Bit Mode: YES");
} }
ESP_LOGCONFIG(TAG, " Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000));
LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_);
@ -387,6 +388,17 @@ void ILI9XXXILI9481::initialize() {
} }
} }
void ILI9XXXILI948118::initialize() {
this->init_lcd_(INITCMD_ILI9481_18);
if (this->width_ == 0) {
this->width_ = 320;
}
if (this->height_ == 0) {
this->height_ = 480;
}
this->is_18bitdisplay_ = true;
}
// 35_TFT display // 35_TFT display
void ILI9XXXILI9486::initialize() { void ILI9XXXILI9486::initialize() {
this->init_lcd_(INITCMD_ILI9486); this->init_lcd_(INITCMD_ILI9486);

View file

@ -120,6 +120,12 @@ class ILI9XXXILI9481 : public ILI9XXXDisplay {
void initialize() override; void initialize() override;
}; };
//----------- ILI9481 in 18 bit mode --------------
class ILI9XXXILI948118 : public ILI9XXXDisplay {
protected:
void initialize() override;
};
//----------- ILI9XXX_35_TFT rotated display -------------- //----------- ILI9XXX_35_TFT rotated display --------------
class ILI9XXXILI9486 : public ILI9XXXDisplay { class ILI9XXXILI9486 : public ILI9XXXDisplay {
protected: protected:

View file

@ -94,8 +94,32 @@ static const uint8_t PROGMEM INITCMD_ILI9481[] = {
ILI9XXX_IFCTR , 1, 0x83, ILI9XXX_IFCTR , 1, 0x83,
ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00, ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00,
ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect
ILI9XXX_PTLAR , 4, 0, 0, 1, 0xDF,
0xE4 , 1, 0xA0, 0xE4 , 1, 0xA0,
ILI9XXX_MADCTL , 1, MADCTL_MV | MADCTL_BGR, // Memory Access Control
ILI9XXX_CSCON , 1, 0x01, ILI9XXX_CSCON , 1, 0x01,
ILI9XXX_PIXFMT, 1, 0x55, // 16 bit mode
ILI9XXX_INVON, 0,
ILI9XXX_DISPON, 0x80, // Set display on
0x00 // end
};
static const uint8_t PROGMEM INITCMD_ILI9481_18[] = {
ILI9XXX_SLPOUT , 0x80, // Exit sleep mode
ILI9XXX_PWSET , 3, 0x07, 0x41, 0x1D,
ILI9XXX_VMCTR , 3, 0x00, 0x1C, 0x1F,
ILI9XXX_PWSETN , 2, 0x01, 0x11,
ILI9XXX_PWCTR1 , 5, 0x10, 0x3B, 0x00, 0x02, 0x11,
ILI9XXX_VMCTR1 , 1, 0x03,
ILI9XXX_IFCTR , 1, 0x83,
ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00,
ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect
ILI9XXX_PTLAR , 4, 0, 0, 1, 0xDF,
0xE4 , 1, 0xA0,
ILI9XXX_MADCTL , 1, MADCTL_MX| MADCTL_BGR, // Memory Access Control
ILI9XXX_CSCON , 1, 0x01,
ILI9XXX_PIXFMT, 1, 0x66, // 18 bit mode
ILI9XXX_INVON, 0,
ILI9XXX_DISPON, 0x80, // Set display on ILI9XXX_DISPON, 0x80, // Set display on
0x00 // end 0x00 // end
}; };

View file

@ -52,7 +52,7 @@ Image_ = image_ns.class_("Image")
def _compute_local_icon_path(value) -> Path: def _compute_local_icon_path(value) -> Path:
base_dir = Path(CORE.config_dir) / ".esphome" / DOMAIN / "mdi" base_dir = Path(CORE.data_dir) / DOMAIN / "mdi"
return base_dir / f"{value[CONF_ICON]}.svg" return base_dir / f"{value[CONF_ICON]}.svg"
@ -255,7 +255,11 @@ async def to_code(config):
transparent = config[CONF_USE_TRANSPARENCY] transparent = config[CONF_USE_TRANSPARENCY]
dither = Image.NONE if config[CONF_DITHER] == "NONE" else Image.FLOYDSTEINBERG dither = (
Image.Dither.NONE
if config[CONF_DITHER] == "NONE"
else Image.Dither.FLOYDSTEINBERG
)
if config[CONF_TYPE] == "GRAYSCALE": if config[CONF_TYPE] == "GRAYSCALE":
image = image.convert("LA", dither=dither) image = image.convert("LA", dither=dither)
pixels = list(image.getdata()) pixels = list(image.getdata())

View file

@ -48,7 +48,7 @@ uint8_t ImprovSerialComponent::read_byte_() {
this->hw_serial_->readBytes(&data, 1); this->hw_serial_->readBytes(&data, 1);
#endif #endif
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_RATE_MS); uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_PERIOD_MS);
#endif #endif
return data; return data;
} }

View file

@ -12,6 +12,8 @@ from esphome.const import (
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
KEY_CORE, KEY_CORE,
KEY_FRAMEWORK_VERSION, KEY_FRAMEWORK_VERSION,
PLATFORM_ESP32,
PLATFORM_RP2040,
) )
from esphome.core import CORE from esphome.core import CORE
@ -49,7 +51,7 @@ CONFIG_SCHEMA = cv.All(
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
).extend(cv.polling_component_schema("60s")), ).extend(cv.polling_component_schema("60s")),
cv.only_on(["esp32", "rp2040"]), cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040]),
validate_config, validate_config,
) )

View file

@ -29,6 +29,8 @@ std::string build_json(const json_build_t &f) {
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
#elif defined(USE_RP2040) #elif defined(USE_RP2040)
const size_t free_heap = rp2040.getFreeHeap(); const size_t free_heap = rp2040.getFreeHeap();
#elif defined(USE_LIBRETINY)
const size_t free_heap = lt_heap_get_free();
#endif #endif
size_t request_size = std::min(free_heap, (size_t) 512); size_t request_size = std::min(free_heap, (size_t) 512);
@ -71,6 +73,8 @@ void parse_json(const std::string &data, const json_parse_t &f) {
const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
#elif defined(USE_RP2040) #elif defined(USE_RP2040)
const size_t free_heap = rp2040.getFreeHeap(); const size_t free_heap = rp2040.getFreeHeap();
#elif defined(USE_LIBRETINY)
const size_t free_heap = lt_heap_get_free();
#endif #endif
bool pass = false; bool pass = false;
size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5));

View file

@ -0,0 +1,336 @@
import json
import logging
from os.path import dirname, isfile, join
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import (
CONF_BOARD,
CONF_COMPONENT_ID,
CONF_DEBUG,
CONF_FAMILY,
CONF_FRAMEWORK,
CONF_ID,
CONF_NAME,
CONF_OPTIONS,
CONF_PROJECT,
CONF_SOURCE,
CONF_VERSION,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM,
__version__,
)
from esphome.core import CORE
from . import gpio # noqa
from .const import (
CONF_GPIO_RECOVER,
CONF_LOGLEVEL,
CONF_SDK_SILENT,
CONF_UART_PORT,
FAMILIES,
FAMILY_COMPONENT,
FAMILY_FRIENDLY,
KEY_BOARD,
KEY_COMPONENT,
KEY_COMPONENT_DATA,
KEY_FAMILY,
KEY_LIBRETINY,
LT_DEBUG_MODULES,
LT_LOGLEVELS,
LibreTinyComponent,
LTComponent,
)
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = []
def _detect_variant(value):
if KEY_LIBRETINY not in CORE.data:
raise cv.Invalid("Family component didn't populate core data properly!")
component: LibreTinyComponent = CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA]
board = value[CONF_BOARD]
# read board-default family if not specified
if CONF_FAMILY not in value:
if board not in component.boards:
raise cv.Invalid(
"This board is unknown, please set the family manually. "
"Also, make sure the chosen chip component is correct.",
path=[CONF_BOARD],
)
value = value.copy()
value[CONF_FAMILY] = component.boards[board][KEY_FAMILY]
# read component name matching this family
value[CONF_COMPONENT_ID] = FAMILY_COMPONENT[value[CONF_FAMILY]]
# make sure the chosen component matches the family
if value[CONF_COMPONENT_ID] != component.name:
raise cv.Invalid(
f"The chosen family doesn't belong to '{component.name}' component. The correct component is '{value[CONF_COMPONENT_ID]}'",
path=[CONF_FAMILY],
)
# warn anyway if the board wasn't found
if board not in component.boards:
_LOGGER.warning(
"This board is unknown. Make sure the chosen chip component is correct.",
)
return value
def _update_core_data(config):
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = config[CONF_COMPONENT_ID]
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
config[CONF_FRAMEWORK][CONF_VERSION]
)
CORE.data[KEY_LIBRETINY][KEY_BOARD] = config[CONF_BOARD]
CORE.data[KEY_LIBRETINY][KEY_COMPONENT] = config[CONF_COMPONENT_ID]
CORE.data[KEY_LIBRETINY][KEY_FAMILY] = config[CONF_FAMILY]
return config
def get_libretiny_component(core_obj=None):
return (core_obj or CORE).data[KEY_LIBRETINY][KEY_COMPONENT]
def get_libretiny_family(core_obj=None):
return (core_obj or CORE).data[KEY_LIBRETINY][KEY_FAMILY]
def only_on_family(*, supported=None, unsupported=None):
"""Config validator for features only available on some LibreTiny families."""
if supported is not None and not isinstance(supported, list):
supported = [supported]
if unsupported is not None and not isinstance(unsupported, list):
unsupported = [unsupported]
def validator_(obj):
family = get_libretiny_family()
if supported is not None and family not in supported:
raise cv.Invalid(
f"This feature is only available on {', '.join(supported)}"
)
if unsupported is not None and family in unsupported:
raise cv.Invalid(
f"This feature is not available on {', '.join(unsupported)}"
)
return obj
return validator_
def get_download_types(storage_json=None):
types = [
{
"title": "UF2 package (recommended)",
"description": "For flashing via web_server OTA or with ltchiptool (UART)",
"file": "firmware.uf2",
"download": f"{storage_json.name}.uf2",
},
]
build_dir = dirname(storage_json.firmware_bin_path)
outputs = join(build_dir, "firmware.json")
if not isfile(outputs):
return types
with open(outputs, encoding="utf-8") as f:
outputs = json.load(f)
for output in outputs:
if not output["public"]:
continue
suffix = output["filename"].partition(".")[2]
suffix = f"-{suffix}" if "." in suffix else f".{suffix}"
types.append(
{
"title": output["title"],
"description": output["description"],
"file": output["filename"],
"download": storage_json.name + suffix,
}
)
return types
def _notify_old_style(config):
if config:
raise cv.Invalid(
"The LibreTiny component is now split between supported chip families.\n"
"Migrate your config file to include a chip-based configuration, "
"instead of the 'libretiny:' block.\n"
"For example 'bk72xx:' or 'rtl87xx:'."
)
return config
# NOTE: Keep this in mind when updating the recommended version:
# * For all constants below, update platformio.ini (in this repo)
ARDUINO_VERSIONS = {
"dev": (cv.Version(0, 0, 0), "https://github.com/libretiny-eu/libretiny.git"),
"latest": (cv.Version(0, 0, 0), None),
"recommended": (cv.Version(1, 4, 1), None),
}
def _check_framework_version(value):
value = value.copy()
if value[CONF_VERSION] in ARDUINO_VERSIONS:
if CONF_SOURCE in value:
raise cv.Invalid(
"Framework version needs to be explicitly specified when custom source is used."
)
version, source = ARDUINO_VERSIONS[value[CONF_VERSION]]
else:
version = cv.Version.parse(cv.version_number(value[CONF_VERSION]))
source = value.get(CONF_SOURCE, None)
value[CONF_VERSION] = str(version)
value[CONF_SOURCE] = source
return value
def _check_debug_order(value):
debug = value[CONF_DEBUG]
if "NONE" in debug and "NONE" in debug[1:]:
raise cv.Invalid(
"'none' has to be specified before other modules, and only once",
path=[CONF_DEBUG],
)
return value
FRAMEWORK_SCHEMA = cv.All(
cv.Schema(
{
cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict,
cv.Optional(CONF_SOURCE): cv.string_strict,
cv.Optional(CONF_LOGLEVEL, default="warn"): (
cv.one_of(*LT_LOGLEVELS, upper=True)
),
cv.Optional(CONF_DEBUG, default=[]): cv.ensure_list(
cv.one_of("NONE", *LT_DEBUG_MODULES, upper=True)
),
cv.Optional(CONF_SDK_SILENT, default="all"): (
cv.one_of("all", "auto", "none", lower=True)
),
cv.Optional(CONF_UART_PORT): cv.one_of(0, 1, 2, int=True),
cv.Optional(CONF_GPIO_RECOVER, default=True): cv.boolean,
cv.Optional(CONF_OPTIONS, default={}): {
cv.string_strict: cv.string,
},
}
),
_check_framework_version,
_check_debug_order,
)
CONFIG_SCHEMA = cv.All(_notify_old_style)
BASE_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(LTComponent),
cv.Required(CONF_BOARD): cv.string_strict,
cv.Optional(CONF_FAMILY): cv.one_of(*FAMILIES, upper=True),
cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA,
},
)
BASE_SCHEMA.add_extra(_detect_variant)
BASE_SCHEMA.add_extra(_update_core_data)
# pylint: disable=use-dict-literal
async def component_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
# setup board config
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_build_flag("-DUSE_LIBRETINY")
cg.add_build_flag(f"-DUSE_{config[CONF_COMPONENT_ID]}")
cg.add_build_flag(f"-DUSE_LIBRETINY_VARIANT_{config[CONF_FAMILY]}")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_define("ESPHOME_VARIANT", FAMILY_FRIENDLY[config[CONF_FAMILY]])
# force using arduino framework
cg.add_platformio_option("framework", "arduino")
cg.add_build_flag("-DUSE_ARDUINO")
# disable library compatibility checks
cg.add_platformio_option("lib_ldf_mode", "off")
# include <Arduino.h> in every file
cg.add_platformio_option("build_src_flags", "-include Arduino.h")
# dummy version code
cg.add_define("USE_ARDUINO_VERSION_CODE", cg.RawExpression("VERSION_CODE(0, 0, 0)"))
# decrease web server stack size (16k words -> 4k words)
cg.add_build_flag("-DCONFIG_ASYNC_TCP_STACK_SIZE=4096")
# build framework version
# if platform version is a valid version constraint, prefix the default package
framework = config[CONF_FRAMEWORK]
cv.platformio_version_constraint(framework[CONF_VERSION])
if str(framework[CONF_VERSION]) != "0.0.0":
cg.add_platformio_option("platform", f"libretiny @ {framework[CONF_VERSION]}")
elif framework[CONF_SOURCE]:
cg.add_platformio_option("platform", framework[CONF_SOURCE])
else:
cg.add_platformio_option("platform", "libretiny")
# apply LibreTiny options from framework: block
# setup LT logger to work nicely with ESPHome logger
lt_options = dict(
LT_LOGLEVEL="LT_LEVEL_" + framework[CONF_LOGLEVEL],
LT_LOGGER_CALLER=0,
LT_LOGGER_TASK=0,
LT_LOGGER_COLOR=1,
LT_USE_TIME=1,
)
# enable/disable per-module debugging
for module in framework[CONF_DEBUG]:
if module == "NONE":
# disable all modules
for module in LT_DEBUG_MODULES:
lt_options[f"LT_DEBUG_{module}"] = 0
else:
# enable one module
lt_options[f"LT_DEBUG_{module}"] = 1
# set SDK silencing mode
if framework[CONF_SDK_SILENT] == "all":
lt_options["LT_UART_SILENT_ENABLED"] = 1
lt_options["LT_UART_SILENT_ALL"] = 1
elif framework[CONF_SDK_SILENT] == "auto":
lt_options["LT_UART_SILENT_ENABLED"] = 1
lt_options["LT_UART_SILENT_ALL"] = 0
else:
lt_options["LT_UART_SILENT_ENABLED"] = 0
lt_options["LT_UART_SILENT_ALL"] = 0
# set default UART port
if uart_port := framework.get(CONF_UART_PORT, None) is not None:
lt_options["LT_UART_DEFAULT_PORT"] = uart_port
# add custom options
lt_options.update(framework[CONF_OPTIONS])
# apply ESPHome options from framework: block
cg.add_define("LT_GPIO_RECOVER", int(framework[CONF_GPIO_RECOVER]))
# build PlatformIO compiler flags
for name, value in sorted(lt_options.items()):
cg.add_build_flag(f"-D{name}={value}")
# custom output firmware name and version
if CONF_PROJECT in config:
cg.add_platformio_option(
"custom_fw_name", "esphome." + config[CONF_PROJECT][CONF_NAME]
)
cg.add_platformio_option(
"custom_fw_version", config[CONF_PROJECT][CONF_VERSION]
)
else:
cg.add_platformio_option("custom_fw_name", "esphome")
cg.add_platformio_option("custom_fw_version", __version__)
await cg.register_component(var, config)

View file

@ -0,0 +1,90 @@
from dataclasses import dataclass
from typing import Callable
import esphome.codegen as cg
@dataclass
class LibreTinyComponent:
name: str
boards: dict[str, dict[str, str]]
board_pins: dict[str, dict[str, int]]
pin_validation: Callable[[int], int]
usage_validation: Callable[[dict], dict]
CONF_LIBRETINY = "libretiny"
CONF_LOGLEVEL = "loglevel"
CONF_SDK_SILENT = "sdk_silent"
CONF_GPIO_RECOVER = "gpio_recover"
CONF_UART_PORT = "uart_port"
LT_LOGLEVELS = [
"VERBOSE",
"TRACE",
"DEBUG",
"INFO",
"WARN",
"ERROR",
"FATAL",
"NONE",
]
LT_DEBUG_MODULES = [
"WIFI",
"CLIENT",
"SERVER",
"SSL",
"OTA",
"FDB",
"MDNS",
"LWIP",
"LWIP_ASSERT",
]
KEY_LIBRETINY = "libretiny"
KEY_BOARD = "board"
KEY_COMPONENT = "component"
KEY_COMPONENT_DATA = "component_data"
KEY_FAMILY = "family"
# COMPONENTS - auto-generated! Do not modify this block.
COMPONENT_BK72XX = "bk72xx"
COMPONENT_RTL87XX = "rtl87xx"
# COMPONENTS - end
# FAMILIES - auto-generated! Do not modify this block.
FAMILY_BK7231N = "BK7231N"
FAMILY_BK7231Q = "BK7231Q"
FAMILY_BK7231T = "BK7231T"
FAMILY_BK7251 = "BK7251"
FAMILY_RTL8710B = "RTL8710B"
FAMILY_RTL8720C = "RTL8720C"
FAMILIES = [
FAMILY_BK7231N,
FAMILY_BK7231Q,
FAMILY_BK7231T,
FAMILY_BK7251,
FAMILY_RTL8710B,
FAMILY_RTL8720C,
]
FAMILY_FRIENDLY = {
FAMILY_BK7231N: "BK7231N",
FAMILY_BK7231Q: "BK7231Q",
FAMILY_BK7231T: "BK7231T",
FAMILY_BK7251: "BK7251",
FAMILY_RTL8710B: "RTL8710B",
FAMILY_RTL8720C: "RTL8720C",
}
FAMILY_COMPONENT = {
FAMILY_BK7231N: COMPONENT_BK72XX,
FAMILY_BK7231Q: COMPONENT_BK72XX,
FAMILY_BK7231T: COMPONENT_BK72XX,
FAMILY_BK7251: COMPONENT_BK72XX,
FAMILY_RTL8710B: COMPONENT_RTL87XX,
FAMILY_RTL8720C: COMPONENT_RTL87XX,
}
# FAMILIES - end
libretiny_ns = cg.esphome_ns.namespace("libretiny")
LTComponent = libretiny_ns.class_("LTComponent", cg.PollingComponent)

View file

@ -0,0 +1,40 @@
#ifdef USE_LIBRETINY
#include "core.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "preferences.h"
void setup();
void loop();
namespace esphome {
void IRAM_ATTR HOT yield() { ::yield(); }
uint32_t IRAM_ATTR HOT millis() { return ::millis(); }
uint32_t IRAM_ATTR HOT micros() { return ::micros(); }
void IRAM_ATTR HOT delay(uint32_t ms) { ::delay(ms); }
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { ::delayMicroseconds(us); }
void arch_init() {
libretiny::setup_preferences();
lt_wdt_enable(10000L);
#if LT_GPIO_RECOVER
lt_gpio_recover();
#endif
}
void arch_restart() {
lt_reboot();
while (1) {
}
}
void IRAM_ATTR HOT arch_feed_wdt() { lt_wdt_feed(); }
uint32_t arch_get_cpu_cycle_count() { return lt_cpu_get_cycle_count(); }
uint32_t arch_get_cpu_freq_hz() { return lt_cpu_get_freq(); }
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
} // namespace esphome
#endif // USE_LIBRETINY

View file

@ -0,0 +1,11 @@
#pragma once
#ifdef USE_LIBRETINY
#include <Arduino.h>
namespace esphome {
namespace libretiny {} // namespace libretiny
} // namespace esphome
#endif // USE_LIBRETINY

View file

@ -0,0 +1,329 @@
# Copyright (c) Kuba Szczodrzyński 2023-06-01.
# pylint: skip-file
# flake8: noqa
import json
import re
from pathlib import Path
from black import FileMode, format_str
from ltchiptool import Board, Family
from ltchiptool.util.lvm import LVM
BASE_CODE_INIT = """
# This file was auto-generated by libretiny/generate_components.py
# Do not modify its contents.
# For custom pin validators, put validate_pin() or validate_usage()
# in gpio.py file in this directory.
# For changing schema/pin schema, put COMPONENT_SCHEMA or COMPONENT_PIN_SCHEMA
# in schema.py file in this directory.
from esphome import pins
from esphome.components import libretiny
from esphome.components.libretiny.const import (
COMPONENT_{COMPONENT},
KEY_COMPONENT_DATA,
KEY_LIBRETINY,
LibreTinyComponent,
)
from esphome.core import CORE
{IMPORTS}
CODEOWNERS = ["@kuba2k2"]
AUTO_LOAD = ["libretiny"]
COMPONENT_DATA = LibreTinyComponent(
name=COMPONENT_{COMPONENT},
boards={COMPONENT}_BOARDS,
board_pins={COMPONENT}_BOARD_PINS,
pin_validation={PIN_VALIDATION},
usage_validation={USAGE_VALIDATION},
)
def _set_core_data(config):
CORE.data[KEY_LIBRETINY] = {}
CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA] = COMPONENT_DATA
return config
CONFIG_SCHEMA = {SCHEMA}
PIN_SCHEMA = {PIN_SCHEMA}
CONFIG_SCHEMA.prepend_extra(_set_core_data)
async def to_code(config):
return await libretiny.component_to_code(config)
@pins.PIN_SCHEMA_REGISTRY.register("{COMPONENT_LOWER}", PIN_SCHEMA)
async def pin_to_code(config):
return await libretiny.gpio.component_pin_to_code(config)
"""
BASE_CODE_BOARDS = """
# This file was auto-generated by libretiny/generate_components.py
# Do not modify its contents.
from esphome.components.libretiny.const import {FAMILIES}
{COMPONENT}_BOARDS = {BOARDS_JSON}
{COMPONENT}_BOARD_PINS = {PINS_JSON}
BOARDS = {COMPONENT}_BOARDS
"""
# variable names in component extension code
VAR_SCHEMA = "COMPONENT_SCHEMA"
VAR_PIN_SCHEMA = "COMPONENT_PIN_SCHEMA"
VAR_GPIO_PIN = "validate_pin"
VAR_GPIO_USAGE = "validate_usage"
# lines for code snippets
SCHEMA_BASE = "libretiny.BASE_SCHEMA"
SCHEMA_EXTRA = f"libretiny.BASE_SCHEMA.extend({VAR_SCHEMA})"
PIN_SCHEMA_BASE = "libretiny.gpio.BASE_PIN_SCHEMA"
PIN_SCHEMA_EXTRA = f"libretiny.BASE_PIN_SCHEMA.extend({VAR_PIN_SCHEMA})"
# supported root components
COMPONENT_MAP = {
"rtl87xx": "realtek-amb",
"bk72xx": "beken-72xx",
}
def subst(code: str, key: str, value: str) -> str:
return code.replace(f"{{{key}}}", value)
def subst_all(code: str, value: str) -> str:
return re.sub(r"{.+?}", value, code)
def subst_many(code: str, *templates: tuple[str, str]) -> str:
while True:
prev_code = code
for key, value in templates:
code = subst(code, key, value)
if code == prev_code:
break
return code
def check_base_code(code: str) -> None:
code = subst_all(code, "DUMMY")
formatted = format_str(code, mode=FileMode())
if code.strip() != formatted.strip():
print(formatted)
raise RuntimeError("Base code is not formatted properly")
def write_component_code(
component_dir: Path,
component: str,
) -> None:
code = BASE_CODE_INIT
gpio_py = component_dir.joinpath("gpio.py")
schema_py = component_dir.joinpath("schema.py")
init_py = component_dir.joinpath("__init__.py")
# gather all imports
imports = {
"gpio": set(),
"schema": set(),
"boards": {"{COMPONENT}_BOARDS", "{COMPONENT}_BOARD_PINS"},
}
# substitution values
values = dict(
COMPONENT=component.upper(),
COMPONENT_LOWER=component.lower(),
SCHEMA=SCHEMA_BASE,
PIN_SCHEMA=PIN_SCHEMA_BASE,
PIN_VALIDATION="None",
USAGE_VALIDATION="None",
)
# parse gpio.py file to find custom validators
if gpio_py.is_file():
gpio_code = gpio_py.read_text()
if VAR_GPIO_PIN in gpio_code:
values["PIN_VALIDATION"] = VAR_GPIO_PIN
imports["gpio"].add(VAR_GPIO_PIN)
# parse schema.py file to find schema extension
if schema_py.is_file():
schema_code = schema_py.read_text()
if VAR_SCHEMA in schema_code:
values["SCHEMA"] = SCHEMA_EXTRA
imports["schema"].add(VAR_SCHEMA)
if VAR_PIN_SCHEMA in schema_code:
values["PIN_SCHEMA"] = PIN_SCHEMA_EXTRA
imports["schema"].add(VAR_PIN_SCHEMA)
# add import lines if needed
import_lines = "\n".join(
f"from .{m} import {', '.join(sorted(v))}" for m, v in imports.items() if v
)
code = subst_many(
code,
("IMPORTS", import_lines),
*values.items(),
)
# format with black
code = format_str(code, mode=FileMode())
# write back to file
init_py.write_text(code)
def write_component_boards(
component_dir: Path,
component: str,
boards: list[Board],
) -> list[Family]:
code = BASE_CODE_BOARDS
variants_dir = Path(LVM.path(), "boards", "variants")
boards_py = component_dir.joinpath("boards.py")
pin_regex = r"#define PIN_(\w+)\s+(\d+)"
pin_number_regex = r"0*(\d+)$"
# families to import
families = set()
# found root families
root_families = []
# substitution values
values = dict(
COMPONENT=component.upper(),
)
# resulting JSON objects
boards_json = {}
pins_json = {}
# go through all boards found for this root family
for board in boards:
family = "FAMILY_" + board.family.short_name
boards_json[board.name] = {
"name": board.title,
"family": family,
}
families.add(family)
if board.family not in root_families:
root_families.append(board.family)
board_h = variants_dir.joinpath(f"{board.name}.h")
board_code = board_h.read_text()
board_pins = {}
for match in re.finditer(pin_regex, board_code):
pin_name = match[1]
pin_value = match[2]
board_pins[pin_name] = int(pin_value)
# trim leading zeroes in GPIO numbers
pin_name = re.sub(pin_number_regex, r"\1", pin_name)
board_pins[pin_name] = int(pin_value)
pins_json[board.name] = board_pins
# make the JSONs format as non-inline
boards_json = json.dumps(boards_json).replace("}", ",}")
pins_json = json.dumps(pins_json).replace("}", ",}")
# remove quotes from family constants
for family in families:
boards_json = boards_json.replace(f'"{family}"', family)
code = subst_many(
code,
("FAMILIES", ", ".join(sorted(families))),
("BOARDS_JSON", boards_json),
("PINS_JSON", pins_json),
*values.items(),
)
# format with black
code = format_str(code, mode=FileMode())
# write back to file
boards_py.write_text(code)
return root_families
def write_const(
components_dir: Path,
components: set[str],
families: dict[str, str],
) -> None:
const_py = components_dir.joinpath("libretiny").joinpath("const.py")
if not const_py.is_file():
raise FileNotFoundError(const_py)
code = const_py.read_text()
components = sorted(components)
v2f = families
families = sorted(families)
# regex for finding the component list block
comp_regex = r"(# COMPONENTS.+?\n)(.*?)(\n# COMPONENTS)"
# build component constants
comp_str = "\n".join(f'COMPONENT_{f} = "{f.lower()}"' for f in components)
# replace the 2nd regex group only
repl = lambda m: m.group(1) + comp_str + m.group(3)
code = re.sub(comp_regex, repl, code, flags=re.DOTALL | re.MULTILINE)
# regex for finding the family list block
fam_regex = r"(# FAMILIES.+?\n)(.*?)(\n# FAMILIES)"
# build family constants
fam_defs = "\n".join(f'FAMILY_{v} = "{v}"' for v in families)
fam_list = ", ".join(f"FAMILY_{v}" for v in families)
fam_friendly = ", ".join(f'FAMILY_{v}: "{v}"' for v in families)
fam_component = ", ".join(f"FAMILY_{v}: COMPONENT_{v2f[v]}" for v in families)
fam_lines = [
fam_defs,
"FAMILIES = [",
fam_list,
",]",
"FAMILY_FRIENDLY = {",
fam_friendly,
",}",
"FAMILY_COMPONENT = {",
fam_component,
",}",
]
var_str = "\n".join(fam_lines)
# replace the 2nd regex group only
repl = lambda m: m.group(1) + var_str + m.group(3)
code = re.sub(fam_regex, repl, code, flags=re.DOTALL | re.MULTILINE)
# format with black
code = format_str(code, mode=FileMode())
# write back to file
const_py.write_text(code)
if __name__ == "__main__":
# safety check if code is properly formatted
check_base_code(BASE_CODE_INIT)
# list all boards from ltchiptool
components_dir = Path(__file__).parent.parent
boards = [Board(b) for b in Board.get_list()]
# keep track of all supported root- and chip-families
components = set()
families = {}
# loop through supported components
for component, family_name in COMPONENT_MAP.items():
family = Family.get(name=family_name)
# make family component directory
component_dir = components_dir.joinpath(component)
component_dir.mkdir(exist_ok=True)
# filter boards list
family_boards = [b for b in boards if family in b.family.inheritance]
# write __init__.py
write_component_code(component_dir, component)
# write boards.py
component_families = write_component_boards(
component_dir, component, family_boards
)
# store current root component name
components.add(component.upper())
# add all chip families
for family in component_families:
families[family.short_name] = component.upper()
# update libretiny/const.py
write_const(components_dir, components, families)

View file

@ -0,0 +1,216 @@
import logging
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import (
CONF_ANALOG,
CONF_ID,
CONF_INPUT,
CONF_INVERTED,
CONF_MODE,
CONF_NUMBER,
CONF_OPEN_DRAIN,
CONF_OUTPUT,
CONF_PULLDOWN,
CONF_PULLUP,
)
from esphome.core import CORE
from .const import (
KEY_BOARD,
KEY_COMPONENT_DATA,
KEY_LIBRETINY,
LibreTinyComponent,
libretiny_ns,
)
_LOGGER = logging.getLogger(__name__)
ArduinoInternalGPIOPin = libretiny_ns.class_(
"ArduinoInternalGPIOPin", cg.InternalGPIOPin
)
def _is_name_deprecated(value):
return value[0] in "DA" and value[1:].isnumeric()
def _lookup_board_pins(board):
component: LibreTinyComponent = CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA]
board_pins = component.board_pins.get(board, {})
# Resolve aliased board pins (shorthand when two boards have the same pin configuration)
while isinstance(board_pins, str):
board_pins = board_pins[board_pins]
return board_pins
def _lookup_pin(value):
board: str = CORE.data[KEY_LIBRETINY][KEY_BOARD]
board_pins = _lookup_board_pins(board)
# check numeric pin values
if isinstance(value, int):
if value in board_pins.values() or not board_pins:
# accept if pin number present in board pins
# if board is not found, just accept all numeric values
return value
raise cv.Invalid(f"Pin number '{value}' is not usable for board {board}.")
# check textual pin names
if isinstance(value, str):
if not board_pins:
# can't remap without known pin name
raise cv.Invalid(
f"Board {board} wasn't found. "
f"Use 'GPIO#' (numeric value) instead of '{value}'."
)
if value in board_pins:
# pin name found, remap to numeric value
if _is_name_deprecated(value):
number = board_pins[value]
# find all alternative pin names (except the deprecated)
names = (
k
for k, v in board_pins.items()
if v == number and not _is_name_deprecated(k)
)
# sort by shortest
# favor P# or PA# names
names = sorted(
names,
key=lambda x: len(x) - 99 if x[0] == "P" else len(x),
)
_LOGGER.warning(
"Using D# and A# pin numbering is deprecated. "
"Please replace '%s' with one of: %s",
value,
", ".join(names),
)
return board_pins[value]
# pin name not found and not numeric
raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {board}.")
# unknown type of the value
raise cv.Invalid(f"Unrecognized pin value '{value}'.")
def _translate_pin(value):
if isinstance(value, dict) or value is None:
raise cv.Invalid(
"This variable only supports pin numbers, not full pin schemas "
"(with inverted and mode)."
)
if isinstance(value, int):
return value
try:
return int(value)
except ValueError:
pass
# translate GPIO* and P* to a number, if possible
# otherwise return unchanged value (i.e. pin PA05)
try:
if value.startswith("GPIO"):
value = int(value[4:])
elif value.startswith("P"):
value = int(value[1:])
except ValueError:
pass
return value
def validate_gpio_pin(value):
value = _translate_pin(value)
value = _lookup_pin(value)
component: LibreTinyComponent = CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA]
if component.pin_validation:
value = component.pin_validation(value)
return value
def validate_gpio_usage(value):
mode = value[CONF_MODE]
is_analog = mode[CONF_ANALOG]
is_input = mode[CONF_INPUT]
is_output = mode[CONF_OUTPUT]
is_open_drain = mode[CONF_OPEN_DRAIN]
is_pullup = mode[CONF_PULLUP]
is_pulldown = mode[CONF_PULLDOWN]
if is_open_drain and not is_output:
raise cv.Invalid(
"Open-drain only works with output mode", [CONF_MODE, CONF_OPEN_DRAIN]
)
if is_analog and not is_input:
raise cv.Invalid("Analog pins must be an input", [CONF_MODE, CONF_ANALOG])
if is_analog:
# expect analog pin numbers to be available as either ADC# or A#
number = value[CONF_NUMBER]
board: str = CORE.data[KEY_LIBRETINY][KEY_BOARD]
board_pins = _lookup_board_pins(board)
analog_pins = [
v
for k, v in board_pins.items()
if k[0] == "A" and k[1:].isnumeric() or k[0:3] == "ADC"
]
if number not in analog_pins:
raise cv.Invalid(
f"Pin '{number}' is not an analog pin", [CONF_MODE, CONF_ANALOG]
)
# (input, output, open_drain, pullup, pulldown)
supported_modes = {
# INPUT
(True, False, False, False, False),
# OUTPUT
(False, True, False, False, False),
# INPUT_PULLUP
(True, False, False, True, False),
# INPUT_PULLDOWN
(True, False, False, False, True),
# OUTPUT_OPEN_DRAIN
(False, True, True, False, False),
}
key = (is_input, is_output, is_open_drain, is_pullup, is_pulldown)
if key not in supported_modes:
raise cv.Invalid("This pin mode is not supported", [CONF_MODE])
component: LibreTinyComponent = CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA]
if component.usage_validation:
value = component.usage_validation(value)
return value
BASE_PIN_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(ArduinoInternalGPIOPin),
cv.Required(CONF_NUMBER): validate_gpio_pin,
cv.Optional(CONF_MODE, default={}): cv.Schema(
{
cv.Optional(CONF_ANALOG, default=False): cv.boolean,
cv.Optional(CONF_INPUT, default=False): cv.boolean,
cv.Optional(CONF_OUTPUT, default=False): cv.boolean,
cv.Optional(CONF_OPEN_DRAIN, default=False): cv.boolean,
cv.Optional(CONF_PULLUP, default=False): cv.boolean,
cv.Optional(CONF_PULLDOWN, default=False): cv.boolean,
}
),
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
},
)
BASE_PIN_SCHEMA.add_extra(validate_gpio_usage)
async def component_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
num = config[CONF_NUMBER]
cg.add(var.set_pin(num))
cg.add(var.set_inverted(config[CONF_INVERTED]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
return var

View file

@ -0,0 +1,105 @@
#ifdef USE_LIBRETINY
#include "gpio_arduino.h"
#include "esphome/core/log.h"
namespace esphome {
namespace libretiny {
static const char *const TAG = "lt.gpio";
static int IRAM_ATTR flags_to_mode(gpio::Flags flags) {
if (flags == gpio::FLAG_INPUT) {
return INPUT;
} else if (flags == gpio::FLAG_OUTPUT) {
return OUTPUT;
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) {
return INPUT_PULLUP;
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) {
return INPUT_PULLDOWN;
} else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) {
return OUTPUT_OPEN_DRAIN;
} else {
return 0;
}
}
struct ISRPinArg {
uint8_t pin;
bool inverted;
};
ISRInternalGPIOPin ArduinoInternalGPIOPin::to_isr() const {
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
arg->pin = pin_;
arg->inverted = inverted_;
return ISRInternalGPIOPin((void *) arg);
}
void ArduinoInternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
PinStatus arduino_mode = (PinStatus) 255;
switch (type) {
case gpio::INTERRUPT_RISING_EDGE:
arduino_mode = inverted_ ? FALLING : RISING;
break;
case gpio::INTERRUPT_FALLING_EDGE:
arduino_mode = inverted_ ? RISING : FALLING;
break;
case gpio::INTERRUPT_ANY_EDGE:
arduino_mode = CHANGE;
break;
case gpio::INTERRUPT_LOW_LEVEL:
arduino_mode = inverted_ ? HIGH : LOW;
break;
case gpio::INTERRUPT_HIGH_LEVEL:
arduino_mode = inverted_ ? LOW : HIGH;
break;
}
attachInterruptParam(pin_, func, arduino_mode, arg);
}
void ArduinoInternalGPIOPin::pin_mode(gpio::Flags flags) {
pinMode(pin_, flags_to_mode(flags)); // NOLINT
}
std::string ArduinoInternalGPIOPin::dump_summary() const {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%u", pin_);
return buffer;
}
bool ArduinoInternalGPIOPin::digital_read() {
return bool(digitalRead(pin_)) ^ inverted_; // NOLINT
}
void ArduinoInternalGPIOPin::digital_write(bool value) {
digitalWrite(pin_, value ^ inverted_); // NOLINT
}
void ArduinoInternalGPIOPin::detach_interrupt() const {
detachInterrupt(pin_); // NOLINT
}
} // namespace libretiny
using namespace libretiny;
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
return bool(digitalRead(arg->pin)) ^ arg->inverted; // NOLINT
}
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
digitalWrite(arg->pin, value ^ arg->inverted); // NOLINT
}
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
detachInterrupt(arg->pin);
}
void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
pinMode(arg->pin, flags_to_mode(flags)); // NOLINT
}
} // namespace esphome
#endif // USE_LIBRETINY

View file

@ -0,0 +1,36 @@
#pragma once
#ifdef USE_LIBRETINY
#include "esphome/core/hal.h"
namespace esphome {
namespace libretiny {
class ArduinoInternalGPIOPin : public InternalGPIOPin {
public:
void set_pin(uint8_t pin) { pin_ = pin; }
void set_inverted(bool inverted) { inverted_ = inverted; }
void set_flags(gpio::Flags flags) { flags_ = flags; }
void setup() override { pin_mode(flags_); }
void pin_mode(gpio::Flags flags) override;
bool digital_read() override;
void digital_write(bool value) override;
std::string dump_summary() const override;
void detach_interrupt() const override;
ISRInternalGPIOPin to_isr() const override;
uint8_t get_pin() const override { return pin_; }
bool is_inverted() const override { return inverted_; }
protected:
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
uint8_t pin_;
bool inverted_;
gpio::Flags flags_;
};
} // namespace libretiny
} // namespace esphome
#endif // USE_LIBRETINY

View file

@ -0,0 +1,29 @@
#include "lt_component.h"
#ifdef USE_LIBRETINY
#include "esphome/core/log.h"
namespace esphome {
namespace libretiny {
static const char *const TAG = "lt.component";
void LTComponent::dump_config() {
ESP_LOGCONFIG(TAG, "LibreTiny:");
ESP_LOGCONFIG(TAG, " Version: %s", LT_BANNER_STR + 10);
ESP_LOGCONFIG(TAG, " Loglevel: %u", LT_LOGLEVEL);
#ifdef USE_TEXT_SENSOR
if (this->version_ != nullptr) {
this->version_->publish_state(LT_BANNER_STR + 10);
}
#endif // USE_TEXT_SENSOR
}
float LTComponent::get_setup_priority() const { return setup_priority::LATE; }
} // namespace libretiny
} // namespace esphome
#endif // USE_LIBRETINY

View file

@ -0,0 +1,36 @@
#pragma once
#ifdef USE_LIBRETINY
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif
#ifdef USE_TEXT_SENSOR
#include "esphome/components/text_sensor/text_sensor.h"
#endif
namespace esphome {
namespace libretiny {
class LTComponent : public Component {
public:
float get_setup_priority() const override;
void dump_config() override;
#ifdef USE_TEXT_SENSOR
void set_version_sensor(text_sensor::TextSensor *version) { version_ = version; }
#endif // USE_TEXT_SENSOR
protected:
#ifdef USE_TEXT_SENSOR
text_sensor::TextSensor *version_{nullptr};
#endif // USE_TEXT_SENSOR
};
} // namespace libretiny
} // namespace esphome
#endif // USE_LIBRETINY

View file

@ -0,0 +1,182 @@
#ifdef USE_LIBRETINY
#include "esphome/core/preferences.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <flashdb.h>
#include <cstring>
#include <vector>
#include <string>
namespace esphome {
namespace libretiny {
static const char *const TAG = "lt.preferences";
struct NVSData {
std::string key;
std::vector<uint8_t> data;
};
static std::vector<NVSData> s_pending_save; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
class LibreTinyPreferenceBackend : public ESPPreferenceBackend {
public:
std::string key;
fdb_kvdb_t db;
fdb_blob_t blob;
bool save(const uint8_t *data, size_t len) override {
// try find in pending saves and update that
for (auto &obj : s_pending_save) {
if (obj.key == key) {
obj.data.assign(data, data + len);
return true;
}
}
NVSData save{};
save.key = key;
save.data.assign(data, data + len);
s_pending_save.emplace_back(save);
ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %d", key.c_str(), len);
return true;
}
bool load(uint8_t *data, size_t len) override {
// try find in pending saves and load from that
for (auto &obj : s_pending_save) {
if (obj.key == key) {
if (obj.data.size() != len) {
// size mismatch
return false;
}
memcpy(data, obj.data.data(), len);
return true;
}
}
fdb_blob_make(blob, data, len);
size_t actual_len = fdb_kv_get_blob(db, key.c_str(), blob);
if (actual_len != len) {
ESP_LOGVV(TAG, "NVS length does not match (%u!=%u)", actual_len, len);
return false;
} else {
ESP_LOGVV(TAG, "fdb_kv_get_blob: key: %s, len: %d", key.c_str(), len);
}
return true;
}
};
class LibreTinyPreferences : public ESPPreferences {
public:
struct fdb_kvdb db;
struct fdb_blob blob;
void open() {
//
fdb_err_t err = fdb_kvdb_init(&db, "esphome", "kvs", NULL, NULL);
if (err != FDB_NO_ERR) {
LT_E("fdb_kvdb_init(...) failed: %d", err);
} else {
LT_I("Preferences initialized");
}
}
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
return make_preference(length, type);
}
ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
auto *pref = new LibreTinyPreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
pref->db = &db;
pref->blob = &blob;
uint32_t keyval = type;
pref->key = str_sprintf("%u", keyval);
return ESPPreferenceObject(pref);
}
bool sync() override {
if (s_pending_save.empty())
return true;
ESP_LOGD(TAG, "Saving %d preferences to flash...", s_pending_save.size());
// goal try write all pending saves even if one fails
int cached = 0, written = 0, failed = 0;
fdb_err_t last_err = FDB_NO_ERR;
std::string last_key{};
// go through vector from back to front (makes erase easier/more efficient)
for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) {
const auto &save = s_pending_save[i];
ESP_LOGVV(TAG, "Checking if FDB data %s has changed", save.key.c_str());
if (is_changed(&db, save)) {
ESP_LOGV(TAG, "sync: key: %s, len: %d", save.key.c_str(), save.data.size());
fdb_blob_make(&blob, save.data.data(), save.data.size());
fdb_err_t err = fdb_kv_set_blob(&db, save.key.c_str(), &blob);
if (err != FDB_NO_ERR) {
ESP_LOGV(TAG, "fdb_kv_set_blob('%s', len=%u) failed: %d", save.key.c_str(), save.data.size(), err);
failed++;
last_err = err;
last_key = save.key;
continue;
}
written++;
} else {
ESP_LOGD(TAG, "FDB data not changed; skipping %s len=%u", save.key.c_str(), save.data.size());
cached++;
}
s_pending_save.erase(s_pending_save.begin() + i);
}
ESP_LOGD(TAG, "Saving %d preferences to flash: %d cached, %d written, %d failed", cached + written + failed, cached,
written, failed);
if (failed > 0) {
ESP_LOGE(TAG, "Error saving %d preferences to flash. Last error=%d for key=%s", failed, last_err,
last_key.c_str());
}
return failed == 0;
}
bool is_changed(const fdb_kvdb_t db, const NVSData &to_save) {
NVSData stored_data{};
struct fdb_kv kv;
fdb_kv_t kvp = fdb_kv_get_obj(db, to_save.key.c_str(), &kv);
if (kvp == nullptr) {
ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", to_save.key.c_str());
return true;
}
stored_data.data.reserve(kv.value_len);
fdb_blob_make(&blob, stored_data.data.data(), kv.value_len);
size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &blob);
if (actual_len != kv.value_len) {
ESP_LOGV(TAG, "fdb_kv_get_blob('%s') len mismatch: %u != %u", to_save.key.c_str(), actual_len, kv.value_len);
return true;
}
return to_save.data != stored_data.data;
}
bool reset() override {
ESP_LOGD(TAG, "Cleaning up preferences in flash...");
s_pending_save.clear();
fdb_kv_set_default(&db);
fdb_kvdb_deinit(&db);
return true;
}
};
void setup_preferences() {
auto *prefs = new LibreTinyPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
prefs->open();
global_preferences = prefs;
}
} // namespace libretiny
ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace esphome
#endif // USE_LIBRETINY

View file

@ -0,0 +1,13 @@
#pragma once
#ifdef USE_LIBRETINY
namespace esphome {
namespace libretiny {
void setup_preferences();
} // namespace libretiny
} // namespace esphome
#endif // USE_LIBRETINY

View file

@ -0,0 +1,31 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import text_sensor
from esphome.const import (
CONF_VERSION,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_CELLPHONE_ARROW_DOWN,
)
from .const import CONF_LIBRETINY, LTComponent
DEPENDENCIES = ["libretiny"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_LIBRETINY): cv.use_id(LTComponent),
cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema(
icon=ICON_CELLPHONE_ARROW_DOWN,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)
async def to_code(config):
lt_component = await cg.get_variable(config[CONF_LIBRETINY])
if CONF_VERSION in config:
sens = await text_sensor.new_text_sensor(config[CONF_VERSION])
cg.add(lt_component.set_version_sensor(sens))

View file

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

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