Merge pull request #5435 from esphome/bump-2023.9.0

2023.9.0
This commit is contained in:
Jesse Hills 2023-09-27 17:19:58 +13:00 committed by GitHub
commit 5751e9ec59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
266 changed files with 12414 additions and 1378 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.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4.7.0
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.0.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.0
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.0.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.0.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.0.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.0.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.0.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.0.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.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -227,15 +222,14 @@ jobs:
- pylint - pylint
- pytest - pytest
- pyupgrade - pyupgrade
- yamllint
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: [1, 2, 3, 3.1, 4, 5, 6, 7, 8, 10, 11.5]
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v3.5.2 uses: actions/checkout@v4.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@ -258,7 +252,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 +284,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.0.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 +330,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.0.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.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4.7.0
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.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4.7.0
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.0.0
- name: Checkout Home Assistant - name: Checkout Home Assistant
uses: actions/checkout@v3 uses: actions/checkout@v4.0.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.0
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.0.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,19 +42,21 @@ 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
esphome/components/bp5758d/* @Cossid esphome/components/bp5758d/* @Cossid
esphome/components/button/* @esphome/core esphome/components/button/* @esphome/core
esphome/components/canbus/* @danielschramm @mvturnho esphome/components/canbus/* @danielschramm @mvturnho
esphome/components/cap1188/* @MrEditor97 esphome/components/cap1188/* @mreditor97
esphome/components/captive_portal/* @OttoWinter esphome/components/captive_portal/* @OttoWinter
esphome/components/ccs811/* @habbie esphome/components/ccs811/* @habbie
esphome/components/cd74hc4067/* @asoehlke esphome/components/cd74hc4067/* @asoehlke
@ -87,7 +89,7 @@ esphome/components/ens210/* @itn3rd77
esphome/components/esp32/* @esphome/core esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @jesserockz esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble_client/* @jesserockz esphome/components/esp32_ble_client/* @jesserockz
esphome/components/esp32_ble_server/* @jesserockz esphome/components/esp32_ble_server/* @clydebarrow @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron esphome/components/esp32_can/* @Sympatron
esphome/components/esp32_improv/* @jesserockz esphome/components/esp32_improv/* @jesserockz
@ -129,10 +131,10 @@ 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
esphome/components/inkbird_ibsth1_mini/* @fkirill esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter esphome/components/integration/* @OttoWinter
@ -146,7 +148,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
@ -168,7 +173,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp3204/* @rsumner esphome/components/mcp3204/* @rsumner
esphome/components/mcp4728/* @berfenger esphome/components/mcp4728/* @berfenger
esphome/components/mcp47a1/* @jesserockz esphome/components/mcp47a1/* @jesserockz
esphome/components/mcp9600/* @MrEditor97 esphome/components/mcp9600/* @mreditor97
esphome/components/mcp9808/* @k7hpn esphome/components/mcp9808/* @k7hpn
esphome/components/md5/* @esphome/core esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core esphome/components/mdns/* @esphome/core
@ -212,6 +217,7 @@ esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984 esphome/components/pipsolar/* @andreashergert1984
esphome/components/pm1006/* @habbie esphome/components/pm1006/* @habbie
esphome/components/pmsa003i/* @sjtrny esphome/components/pmsa003i/* @sjtrny
esphome/components/pmwcs3/* @SeByDocKy
esphome/components/pn532/* @OttoWinter @jesserockz esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz esphome/components/pn532_spi/* @OttoWinter @jesserockz
@ -233,6 +239,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
@ -241,6 +248,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
@ -263,6 +271,8 @@ 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/* @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
@ -323,6 +333,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

@ -29,7 +29,8 @@ RUN \
curl=7.74.0-1.3+deb11u7 \ curl=7.74.0-1.3+deb11u7 \
openssh-client=1:8.4p1-5+deb11u1 \ openssh-client=1:8.4p1-5+deb11u1 \
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.7 \ 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,16 @@ 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}"
if bashio::fs.directory_exists '/config/esphome/.esphome'; then
bashio::log.info "Removing old .esphome directory..."
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"
@ -234,12 +240,13 @@ def upload_using_esptool(config, port):
*idedata.extra_flash_images, *idedata.extra_flash_images,
] ]
mcu = "esp8266" mcu = "esp8266"
if CORE.is_esp32: if CORE.is_esp32:
from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32 import get_esp32_variant
mcu = get_esp32_variant().lower() mcu = get_esp32_variant().lower()
def run_esptool(baud_rate):
cmd = [ cmd = [
"esptool.py", "esptool.py",
"--before", "--before",
@ -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

View file

@ -1,13 +1,16 @@
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_ESP32C3, VARIANT_ESP32C3,
VARIANT_ESP32C6,
VARIANT_ESP32H2, VARIANT_ESP32H2,
VARIANT_ESP32S2, VARIANT_ESP32S2,
VARIANT_ESP32S3, VARIANT_ESP32S3,
@ -70,6 +73,22 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
3: adc1_channel_t.ADC1_CHANNEL_3, 3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4, 4: adc1_channel_t.ADC1_CHANNEL_4,
}, },
VARIANT_ESP32C2: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
VARIANT_ESP32C6: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
5: adc1_channel_t.ADC1_CHANNEL_5,
6: adc1_channel_t.ADC1_CHANNEL_6,
},
VARIANT_ESP32H2: { VARIANT_ESP32H2: {
0: adc1_channel_t.ADC1_CHANNEL_0, 0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1, 1: adc1_channel_t.ADC1_CHANNEL_1,
@ -125,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")
@ -148,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
) )
@ -166,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);
int32_t raw = adc_read();
adc_set_temp_sensor_enabled(false);
if (this->output_raw_) {
return raw;
}
return raw * 3.3f / 4096.0f;
} else { } else {
uint8_t pin = this->pin_->get_pin(); 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_gpio_init(pin);
adc_select_input(pin - 26); 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); #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;
} }
if (output_raw_) {
return raw;
}
return raw * 3.3f / 4096.0f;
} }
#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

@ -1051,6 +1051,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

@ -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 {
@ -522,12 +524,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 +574,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 +785,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 +808,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 +900,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 +968,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 +1071,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 +1161,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 +1244,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 +1364,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 +1389,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 +1456,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 +1477,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 +1557,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 +1598,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 +1712,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 +1866,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 +2089,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 +2187,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 +2196,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 +2304,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 +2325,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 +2389,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 +2478,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 +2541,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 +2580,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 +2654,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 +2720,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 +3027,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 +3111,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 +3205,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 +3219,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 +3231,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 +3282,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 +3358,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 +3424,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 +3616,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 +3804,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 +3992,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 +4175,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 +4262,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 +4300,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 +4382,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 +4454,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 +4497,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 +4591,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 +4662,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 +4717,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 +4804,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 +4850,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 +4925,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 +4994,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 +5073,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 +5122,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 +5169,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 +5249,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 +5272,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 +5322,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 +5410,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 +5458,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 +5523,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 +5579,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 +5641,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 +5748,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 +5793,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 +5847,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 +5889,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 +5934,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 +5977,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 +6026,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 +6065,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 +6109,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 +6148,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 +6182,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 +6225,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 +6268,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 +6317,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("}");
@ -6412,7 +6414,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 +6577,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 +6602,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 +6645,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 +6695,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

@ -8,15 +8,15 @@ 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(["esp32", "esp8266", "bk72xx", "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

@ -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

@ -9,7 +9,7 @@ CONF_ALLOW_MULTIPLE_TOUCHES = "allow_multiple_touches"
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["binary_sensor", "output"] AUTO_LOAD = ["binary_sensor", "output"]
CODEOWNERS = ["@MrEditor97"] CODEOWNERS = ["@mreditor97"]
cap1188_ns = cg.esphome_ns.namespace("cap1188") cap1188_ns = cg.esphome_ns.namespace("cap1188")
CONF_CAP1188_ID = "cap1188_id" CONF_CAP1188_ID = "cap1188_id"

View file

@ -21,7 +21,7 @@ CONFIG_SCHEMA = cv.All(
), ),
} }
).extend(cv.COMPONENT_SCHEMA), ).extend(cv.COMPONENT_SCHEMA),
cv.only_on(["esp32", "esp8266"]), cv.only_on(["esp32", "esp8266", "bk72xx", "rtl87xx"]),
) )
@ -39,3 +39,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

@ -38,7 +38,7 @@ void CurrentBasedCover::control(const CoverCall &call) {
} }
if (call.get_position().has_value()) { if (call.get_position().has_value()) {
auto pos = *call.get_position(); auto pos = *call.get_position();
if (pos == this->position) { if (fabsf(this->position - pos) < 0.01) {
// already at target // already at target
} else { } else {
auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING; auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;

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

@ -12,11 +12,17 @@
#include <esp_heap_caps.h> #include <esp_heap_caps.h>
#include <esp_system.h> #include <esp_system.h>
#if ESP_IDF_VERSION_MAJOR >= 4
#include <esp32/rom/rtc.h>
#include <esp_chip_info.h> #include <esp_chip_info.h>
#else #if defined(USE_ESP32_VARIANT_ESP32)
#include <rom/rtc.h> #include <esp32/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32C3)
#include <esp32c3/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32C6)
#include <esp32c6/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32S2)
#include <esp32s2/rom/rtc.h>
#elif defined(USE_ESP32_VARIANT_ESP32S3)
#include <esp32s3/rom/rtc.h>
#endif #endif
#endif // USE_ESP32 #endif // USE_ESP32
@ -24,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
@ -41,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
} }
@ -71,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:
@ -103,19 +111,27 @@ 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;
esp_chip_info(&info); esp_chip_info(&info);
const char *model; const char *model;
switch (info.model) { #if defined(USE_ESP32_VARIANT_ESP32)
case CHIP_ESP32: model = "ESP32";
model = "ESP32"; #elif defined(USE_ESP32_VARIANT_ESP32C3)
break; model = "ESP32-C3";
default: #elif defined(USE_ESP32_VARIANT_ESP32C6)
model = "UNKNOWN"; model = "ESP32-C6";
} #elif defined(USE_ESP32_VARIANT_ESP32S2)
model = "ESP32-S2";
#elif defined(USE_ESP32_VARIANT_ESP32S3)
model = "ESP32-S3";
#elif defined(USE_ESP32_VARIANT_ESP32H2)
model = "ESP32-H2";
#else
model = "UNKNOWN";
#endif
std::string features; std::string features;
if (info.features & CHIP_FEATURE_EMB_FLASH) { if (info.features & CHIP_FEATURE_EMB_FLASH) {
features += "EMB_FLASH,"; features += "EMB_FLASH,";
@ -133,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,
@ -157,18 +177,26 @@ void DebugComponent::dump_config() {
case POWERON_RESET: case POWERON_RESET:
reset_reason = "Power On Reset"; reset_reason = "Power On Reset";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32)
case SW_RESET: case SW_RESET:
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case RTC_SW_SYS_RESET:
#endif
reset_reason = "Software Reset Digital Core"; reset_reason = "Software Reset Digital Core";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32)
case OWDT_RESET: case OWDT_RESET:
reset_reason = "Watch Dog Reset Digital Core"; reset_reason = "Watch Dog Reset Digital Core";
break; break;
#endif
case DEEPSLEEP_RESET: case DEEPSLEEP_RESET:
reset_reason = "Deep Sleep Reset Digital Core"; reset_reason = "Deep Sleep Reset Digital Core";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32)
case SDIO_RESET: case SDIO_RESET:
reset_reason = "SLC Module Reset Digital Core"; reset_reason = "SLC Module Reset Digital Core";
break; break;
#endif
case TG0WDT_SYS_RESET: case TG0WDT_SYS_RESET:
reset_reason = "Timer Group 0 Watch Dog Reset Digital Core"; reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
break; break;
@ -178,27 +206,66 @@ 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)
case TGWDT_CPU_RESET: case TGWDT_CPU_RESET:
reset_reason = "Timer Group Reset CPU"; reset_reason = "Timer Group Reset CPU";
break; break;
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case TG0WDT_CPU_RESET:
reset_reason = "Timer Group 0 Reset CPU";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
case SW_CPU_RESET: case SW_CPU_RESET:
#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case RTC_SW_CPU_RESET:
#endif
reset_reason = "Software Reset CPU"; reset_reason = "Software Reset CPU";
break; break;
case RTCWDT_CPU_RESET: case RTCWDT_CPU_RESET:
reset_reason = "RTC Watch Dog Reset CPU"; reset_reason = "RTC Watch Dog Reset CPU";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32)
case EXT_CPU_RESET: case EXT_CPU_RESET:
reset_reason = "External CPU Reset"; reset_reason = "External CPU Reset";
break; break;
#endif
case RTCWDT_BROWN_OUT_RESET: case RTCWDT_BROWN_OUT_RESET:
reset_reason = "Voltage Unstable Reset"; reset_reason = "Voltage Unstable Reset";
break; break;
case RTCWDT_RTC_RESET: case RTCWDT_RTC_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module"; reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
break; break;
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
case TG1WDT_CPU_RESET:
reset_reason = "Timer Group 1 Reset CPU";
break;
case SUPER_WDT_RESET:
reset_reason = "Super Watchdog Reset Digital Core And RTC Module";
break;
case GLITCH_RTC_RESET:
reset_reason = "Glitch Reset Digital Core And RTC Module";
break;
case EFUSE_RESET:
reset_reason = "eFuse Reset Digital Core";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3)
case USB_UART_CHIP_RESET:
reset_reason = "USB UART Reset Digital Core";
break;
case USB_JTAG_CHIP_RESET:
reset_reason = "USB JTAG Reset Digital Core";
break;
case POWER_GLITCH_RESET:
reset_reason = "Power Glitch Reset Digital Core And RTC Module";
break;
#endif
default: default:
reset_reason = "Unknown Reset Reason"; reset_reason = "Unknown Reset Reason";
} }
@ -285,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)
@ -329,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
} }
@ -343,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

@ -22,6 +22,8 @@ from esphome.components.esp32.const import (
VARIANT_ESP32C3, VARIANT_ESP32C3,
VARIANT_ESP32S2, VARIANT_ESP32S2,
VARIANT_ESP32S3, VARIANT_ESP32S3,
VARIANT_ESP32C2,
VARIANT_ESP32C6,
) )
WAKEUP_PINS = { WAKEUP_PINS = {
@ -94,6 +96,8 @@ WAKEUP_PINS = {
20, 20,
21, 21,
], ],
VARIANT_ESP32C2: [0, 1, 2, 3, 4, 5],
VARIANT_ESP32C6: [0, 1, 2, 3, 4, 5, 6, 7],
} }

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

@ -22,6 +22,7 @@ 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,
TYPE_GIT, TYPE_GIT,
@ -37,6 +38,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,
@ -44,7 +46,6 @@ from .const import ( # noqa
KEY_SDKCONFIG_OPTIONS, KEY_SDKCONFIG_OPTIONS,
KEY_SUBMODULES, KEY_SUBMODULES,
KEY_VARIANT, KEY_VARIANT,
VARIANT_ESP32C3,
VARIANT_FRIENDLY, VARIANT_FRIENDLY,
VARIANTS, VARIANTS,
) )
@ -74,6 +75,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
@ -85,6 +88,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):
@ -150,6 +170,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
@ -374,7 +412,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")
@ -588,9 +630,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"):
copy_file_if_changed( import requests
post_build_file,
CORE.relative_build_path("post_build.py"), 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(
file[KEY_PATH],
CORE.relative_build_path(file[KEY_NAME]),
)

View file

@ -10,17 +10,22 @@ 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"
VARIANT_ESP32S3 = "ESP32S3" VARIANT_ESP32S3 = "ESP32S3"
VARIANT_ESP32C2 = "ESP32C2"
VARIANT_ESP32C3 = "ESP32C3" VARIANT_ESP32C3 = "ESP32C3"
VARIANT_ESP32C6 = "ESP32C6"
VARIANT_ESP32H2 = "ESP32H2" VARIANT_ESP32H2 = "ESP32H2"
VARIANTS = [ VARIANTS = [
VARIANT_ESP32, VARIANT_ESP32,
VARIANT_ESP32S2, VARIANT_ESP32S2,
VARIANT_ESP32S3, VARIANT_ESP32S3,
VARIANT_ESP32C2,
VARIANT_ESP32C3, VARIANT_ESP32C3,
VARIANT_ESP32C6,
VARIANT_ESP32H2, VARIANT_ESP32H2,
] ]
@ -28,7 +33,9 @@ VARIANT_FRIENDLY = {
VARIANT_ESP32: "ESP32", VARIANT_ESP32: "ESP32",
VARIANT_ESP32S2: "ESP32-S2", VARIANT_ESP32S2: "ESP32-S2",
VARIANT_ESP32S3: "ESP32-S3", VARIANT_ESP32S3: "ESP32-S3",
VARIANT_ESP32C2: "ESP32-C2",
VARIANT_ESP32C3: "ESP32-C3", VARIANT_ESP32C3: "ESP32-C3",
VARIANT_ESP32C6: "ESP32-C6",
VARIANT_ESP32H2: "ESP32-H2", VARIANT_ESP32H2: "ESP32-H2",
} }

View file

@ -10,9 +10,7 @@
#include <esp_timer.h> #include <esp_timer.h>
#include <soc/rtc.h> #include <soc/rtc.h>
#if ESP_IDF_VERSION_MAJOR >= 4
#include <hal/cpu_hal.h> #include <hal/cpu_hal.h>
#endif
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
#include <esp32-hal.h> #include <esp32-hal.h>
@ -55,15 +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; }
uint32_t arch_get_cpu_cycle_count() { #if ESP_IDF_VERSION_MAJOR >= 5
#if ESP_IDF_VERSION_MAJOR >= 4 uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); }
return cpu_hal_get_cycle_count();
#else #else
uint32_t ccount; uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_cycle_count(); }
__asm__ __volatile__("esync; rsr %0,ccount" : "=a"(ccount));
return ccount;
#endif #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

@ -26,6 +26,8 @@ from .const import (
VARIANT_ESP32C3, VARIANT_ESP32C3,
VARIANT_ESP32S2, VARIANT_ESP32S2,
VARIANT_ESP32S3, VARIANT_ESP32S3,
VARIANT_ESP32C2,
VARIANT_ESP32C6,
VARIANT_ESP32H2, VARIANT_ESP32H2,
esp32_ns, esp32_ns,
) )
@ -35,6 +37,8 @@ from .gpio_esp32 import esp32_validate_gpio_pin, esp32_validate_supports
from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports
from .gpio_esp32_c3 import esp32_c3_validate_gpio_pin, esp32_c3_validate_supports from .gpio_esp32_c3 import esp32_c3_validate_gpio_pin, esp32_c3_validate_supports
from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports
from .gpio_esp32_c2 import esp32_c2_validate_gpio_pin, esp32_c2_validate_supports
from .gpio_esp32_c6 import esp32_c6_validate_gpio_pin, esp32_c6_validate_supports
from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_supports from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_supports
@ -95,6 +99,14 @@ _esp32_validations = {
pin_validation=esp32_s3_validate_gpio_pin, pin_validation=esp32_s3_validate_gpio_pin,
usage_validation=esp32_s3_validate_supports, usage_validation=esp32_s3_validate_supports,
), ),
VARIANT_ESP32C2: ESP32ValidationFunctions(
pin_validation=esp32_c2_validate_gpio_pin,
usage_validation=esp32_c2_validate_supports,
),
VARIANT_ESP32C6: ESP32ValidationFunctions(
pin_validation=esp32_c6_validate_gpio_pin,
usage_validation=esp32_c6_validate_supports,
),
VARIANT_ESP32H2: ESP32ValidationFunctions( VARIANT_ESP32H2: ESP32ValidationFunctions(
pin_validation=esp32_h2_validate_gpio_pin, pin_validation=esp32_h2_validate_gpio_pin,
usage_validation=esp32_h2_validate_supports, usage_validation=esp32_h2_validate_supports,

View file

@ -0,0 +1,37 @@
import logging
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
import esphome.config_validation as cv
_ESP32C2_STRAPPING_PINS = {8, 9}
_LOGGER = logging.getLogger(__name__)
def esp32_c2_validate_gpio_pin(value):
if value < 0 or value > 20:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-20)")
if value in _ESP32C2_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,
)
return value
def esp32_c2_validate_supports(value):
num = value[CONF_NUMBER]
mode = value[CONF_MODE]
is_input = mode[CONF_INPUT]
if num < 0 or num > 20:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-20)")
if is_input:
# All ESP32 pins support input mode
pass
return value

View file

@ -0,0 +1,50 @@
import logging
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
import esphome.config_validation as cv
_ESP32C6_SPI_PSRAM_PINS = {
24: "SPICS0",
25: "SPIQ",
26: "SPIWP",
27: "VDD_SPI",
28: "SPIHD",
29: "SPICLK",
30: "SPID",
}
_ESP32C6_STRAPPING_PINS = {8, 9, 15}
_LOGGER = logging.getLogger(__name__)
def esp32_c6_validate_gpio_pin(value):
if value < 0 or value > 23:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-23)")
if value in _ESP32C6_SPI_PSRAM_PINS:
raise cv.Invalid(
f"This pin cannot be used on ESP32-C6s and is already used by the SPI/PSRAM interface (function: {_ESP32C6_SPI_PSRAM_PINS[value]})"
)
if value in _ESP32C6_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,
)
return value
def esp32_c6_validate_supports(value):
num = value[CONF_NUMBER]
mode = value[CONF_MODE]
is_input = mode[CONF_INPUT]
if num < 0 or num > 23:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-23)")
if is_input:
# All ESP32 pins support input mode
pass
return value

View file

@ -42,9 +42,15 @@ void BLEAdvertising::remove_service_uuid(ESPBTUUID uuid) {
this->advertising_uuids_.end()); this->advertising_uuids_.end());
} }
void BLEAdvertising::set_manufacturer_data(uint8_t *data, uint16_t size) { void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
this->advertising_data_.p_manufacturer_data = data; delete[] this->advertising_data_.p_manufacturer_data;
this->advertising_data_.manufacturer_len = size; this->advertising_data_.p_manufacturer_data = nullptr;
this->advertising_data_.manufacturer_len = data.size();
if (!data.empty()) {
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
this->advertising_data_.p_manufacturer_data = new uint8_t[data.size()];
memcpy(this->advertising_data_.p_manufacturer_data, data.data(), data.size());
}
} }
void BLEAdvertising::start() { void BLEAdvertising::start() {
@ -74,16 +80,21 @@ void BLEAdvertising::start() {
return; return;
} }
memcpy(&this->scan_response_data_, &this->advertising_data_, sizeof(esp_ble_adv_data_t)); if (this->scan_response_) {
this->scan_response_data_.set_scan_rsp = true; memcpy(&this->scan_response_data_, &this->advertising_data_, sizeof(esp_ble_adv_data_t));
this->scan_response_data_.include_name = true; this->scan_response_data_.set_scan_rsp = true;
this->scan_response_data_.include_txpower = true; this->scan_response_data_.include_name = true;
this->scan_response_data_.appearance = 0; this->scan_response_data_.include_txpower = true;
this->scan_response_data_.flag = 0; this->scan_response_data_.min_interval = 0;
err = esp_ble_gap_config_adv_data(&this->scan_response_data_); this->scan_response_data_.max_interval = 0;
if (err != ESP_OK) { this->scan_response_data_.manufacturer_len = 0;
ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %d", err); this->scan_response_data_.appearance = 0;
return; this->scan_response_data_.flag = 0;
err = esp_ble_gap_config_adv_data(&this->scan_response_data_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %d", err);
return;
}
} }
if (this->advertising_data_.service_uuid_len > 0) { if (this->advertising_data_.service_uuid_len > 0) {

View file

@ -20,7 +20,7 @@ class BLEAdvertising {
void remove_service_uuid(ESPBTUUID uuid); void remove_service_uuid(ESPBTUUID uuid);
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; } void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; } void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
void set_manufacturer_data(uint8_t *data, uint16_t size); void set_manufacturer_data(const std::vector<uint8_t> &data);
void start(); void start();
void stop(); void stop();

View file

@ -6,11 +6,12 @@ from esphome.core import CORE
from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.esp32 import add_idf_sdkconfig_option
AUTO_LOAD = ["esp32_ble"] AUTO_LOAD = ["esp32_ble"]
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz", "@clydebarrow"]
CONFLICTS_WITH = ["esp32_ble_beacon"] CONFLICTS_WITH = ["esp32_ble_beacon"]
DEPENDENCIES = ["esp32"] DEPENDENCIES = ["esp32"]
CONF_MANUFACTURER = "manufacturer" CONF_MANUFACTURER = "manufacturer"
CONF_MANUFACTURER_DATA = "manufacturer_data"
esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server") esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server")
BLEServer = esp32_ble_server_ns.class_( BLEServer = esp32_ble_server_ns.class_(
@ -27,6 +28,7 @@ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(BLEServer), cv.GenerateID(): cv.declare_id(BLEServer),
cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE), cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE),
cv.Optional(CONF_MANUFACTURER, default="ESPHome"): cv.string, cv.Optional(CONF_MANUFACTURER, default="ESPHome"): cv.string,
cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.hex_uint8_t]),
cv.Optional(CONF_MODEL): cv.string, cv.Optional(CONF_MODEL): cv.string,
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@ -42,6 +44,8 @@ async def to_code(config):
cg.add(var.set_parent(parent)) cg.add(var.set_parent(parent))
cg.add(var.set_manufacturer(config[CONF_MANUFACTURER])) cg.add(var.set_manufacturer(config[CONF_MANUFACTURER]))
if CONF_MANUFACTURER_DATA in config:
cg.add(var.set_manufacturer_data(config[CONF_MANUFACTURER_DATA]))
if CONF_MODEL in config: if CONF_MODEL in config:
cg.add(var.set_model(config[CONF_MODEL])) cg.add(var.set_model(config[CONF_MODEL]))
cg.add_define("USE_ESP32_BLE_SERVER") cg.add_define("USE_ESP32_BLE_SERVER")

View file

@ -68,6 +68,7 @@ void BLEServer::loop() {
if (this->device_information_service_->is_running()) { if (this->device_information_service_->is_running()) {
this->state_ = RUNNING; this->state_ = RUNNING;
this->can_proceed_ = true; this->can_proceed_ = true;
this->restart_advertising_();
ESP_LOGD(TAG, "BLE server setup successfully"); ESP_LOGD(TAG, "BLE server setup successfully");
} else if (!this->device_information_service_->is_starting()) { } else if (!this->device_information_service_->is_starting()) {
this->device_information_service_->start(); this->device_information_service_->start();
@ -77,6 +78,13 @@ void BLEServer::loop() {
} }
} }
void BLEServer::restart_advertising_() {
if (this->state_ == RUNNING) {
esp32_ble::global_ble->get_advertising()->set_manufacturer_data(this->manufacturer_data_);
esp32_ble::global_ble->get_advertising()->start();
}
}
bool BLEServer::create_device_characteristics_() { bool BLEServer::create_device_characteristics_() {
if (this->model_.has_value()) { if (this->model_.has_value()) {
BLECharacteristic *model = BLECharacteristic *model =

View file

@ -45,6 +45,10 @@ class BLEServer : public Component, public GATTsEventHandler, public Parented<ES
void set_manufacturer(const std::string &manufacturer) { this->manufacturer_ = manufacturer; } void set_manufacturer(const std::string &manufacturer) { this->manufacturer_ = manufacturer; }
void set_model(const std::string &model) { this->model_ = model; } void set_model(const std::string &model) { this->model_ = model; }
void set_manufacturer_data(const std::vector<uint8_t> &data) {
this->manufacturer_data_ = data;
this->restart_advertising_();
}
std::shared_ptr<BLEService> create_service(const uint8_t *uuid, bool advertise = false); std::shared_ptr<BLEService> create_service(const uint8_t *uuid, bool advertise = false);
std::shared_ptr<BLEService> create_service(uint16_t uuid, bool advertise = false); std::shared_ptr<BLEService> create_service(uint16_t uuid, bool advertise = false);
@ -63,6 +67,7 @@ class BLEServer : public Component, public GATTsEventHandler, public Parented<ES
protected: protected:
bool create_device_characteristics_(); bool create_device_characteristics_();
void restart_advertising_();
void add_client_(uint16_t conn_id, void *client) { void add_client_(uint16_t conn_id, void *client) {
this->clients_.insert(std::pair<uint16_t, void *>(conn_id, client)); this->clients_.insert(std::pair<uint16_t, void *>(conn_id, client));
@ -73,6 +78,7 @@ class BLEServer : public Component, public GATTsEventHandler, public Parented<ES
std::string manufacturer_; std::string manufacturer_;
optional<std::string> model_; optional<std::string> model_;
std::vector<uint8_t> manufacturer_data_;
esp_gatt_if_t gatts_if_{0}; esp_gatt_if_t gatts_if_{0};
bool registered_{false}; bool registered_{false};

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

@ -63,6 +63,7 @@ RMT_CHANNELS = {
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3], esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
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],
} }

View file

@ -8,11 +8,7 @@
#include <vector> #include <vector>
#if ESP_IDF_VERSION_MAJOR >= 4
#include <driver/touch_sensor.h> #include <driver/touch_sensor.h>
#else
#include <driver/touch_pad.h>
#endif
namespace esphome { namespace esphome {
namespace esp32_touch { namespace esp32_touch {

View file

@ -50,6 +50,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;
} }
} }
@ -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();
@ -343,12 +343,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();
@ -376,7 +376,7 @@ void EthernetComponent::dump_connect_params_() {
ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str()); ESP_LOGCONFIG(TAG, " DNS2: %s", network::IPAddress(dns_ip2->addr).str().c_str());
#endif #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 +387,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

@ -98,10 +98,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]
type = RestoringGlobalsComponent if restore else GlobalsComponent # Special casing the strings to their own class with a different save/restore mechanism
res_type = type.template(template_args) 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
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

@ -33,6 +33,7 @@ PROTOCOLS = {
"greeya": Protocol.PROTOCOL_GREEYAA, "greeya": Protocol.PROTOCOL_GREEYAA,
"greeyan": Protocol.PROTOCOL_GREEYAN, "greeyan": Protocol.PROTOCOL_GREEYAN,
"greeyac": Protocol.PROTOCOL_GREEYAC, "greeyac": Protocol.PROTOCOL_GREEYAC,
"greeyt": Protocol.PROTOCOL_GREEYT,
"hisense_aud": Protocol.PROTOCOL_HISENSE_AUD, "hisense_aud": Protocol.PROTOCOL_HISENSE_AUD,
"hitachi": Protocol.PROTOCOL_HITACHI, "hitachi": Protocol.PROTOCOL_HITACHI,
"hyundai": Protocol.PROTOCOL_HYUNDAI, "hyundai": Protocol.PROTOCOL_HYUNDAI,
@ -115,7 +116,7 @@ def to_code(config):
cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE])) cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE]))
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE])) cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
cg.add_library("tonia/HeatpumpIR", "1.0.20") cg.add_library("tonia/HeatpumpIR", "1.0.23")
if CORE.is_esp8266 or CORE.is_esp32: if CORE.is_esp8266 or CORE.is_esp32:
cg.add_library("crankyoldgit/IRremoteESP8266", "2.7.12") cg.add_library("crankyoldgit/IRremoteESP8266", "2.7.12")

View file

@ -27,6 +27,7 @@ const std::map<Protocol, std::function<HeatpumpIR *()>> PROTOCOL_CONSTRUCTOR_MAP
{PROTOCOL_GREEYAA, []() { return new GreeYAAHeatpumpIR(); }}, // NOLINT {PROTOCOL_GREEYAA, []() { return new GreeYAAHeatpumpIR(); }}, // NOLINT
{PROTOCOL_GREEYAN, []() { return new GreeYANHeatpumpIR(); }}, // NOLINT {PROTOCOL_GREEYAN, []() { return new GreeYANHeatpumpIR(); }}, // NOLINT
{PROTOCOL_GREEYAC, []() { return new GreeYACHeatpumpIR(); }}, // NOLINT {PROTOCOL_GREEYAC, []() { return new GreeYACHeatpumpIR(); }}, // NOLINT
{PROTOCOL_GREEYT, []() { return new GreeYTHeatpumpIR(); }}, // NOLINT
{PROTOCOL_HISENSE_AUD, []() { return new HisenseHeatpumpIR(); }}, // NOLINT {PROTOCOL_HISENSE_AUD, []() { return new HisenseHeatpumpIR(); }}, // NOLINT
{PROTOCOL_HITACHI, []() { return new HitachiHeatpumpIR(); }}, // NOLINT {PROTOCOL_HITACHI, []() { return new HitachiHeatpumpIR(); }}, // NOLINT
{PROTOCOL_HYUNDAI, []() { return new HyundaiHeatpumpIR(); }}, // NOLINT {PROTOCOL_HYUNDAI, []() { return new HyundaiHeatpumpIR(); }}, // NOLINT

View file

@ -27,6 +27,7 @@ enum Protocol {
PROTOCOL_GREEYAA, PROTOCOL_GREEYAA,
PROTOCOL_GREEYAN, PROTOCOL_GREEYAN,
PROTOCOL_GREEYAC, PROTOCOL_GREEYAC,
PROTOCOL_GREEYT,
PROTOCOL_HISENSE_AUD, PROTOCOL_HISENSE_AUD,
PROTOCOL_HITACHI, PROTOCOL_HITACHI,
PROTOCOL_HYUNDAI, PROTOCOL_HYUNDAI,

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,25 +103,18 @@ 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()) { std::list<Header> headers;
this->parent_->set_useragent(this->useragent_.value(x...)); for (const auto &item : this->headers_) {
} auto val = item.second;
if (this->timeout_.has_value()) { Header header;
this->parent_->set_timeout(this->timeout_.value(x...)); header.name = item.first;
} header.value = val.value(x...);
if (!this->headers_.empty()) { headers.push_back(header);
std::list<Header> headers;
for (const auto &item : this->headers_) {
auto val = item.second;
Header header;
header.name = item.first;
header.value = val.value(x...);
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

@ -42,23 +42,26 @@ pin_with_input_and_output_support = cv.All(
) )
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.All(
{ cv.Schema(
cv.GenerateID(): _bus_declare_type, {
cv.Optional(CONF_SDA, default="SDA"): pin_with_input_and_output_support, cv.GenerateID(): _bus_declare_type,
cv.SplitDefault(CONF_SDA_PULLUP_ENABLED, esp32_idf=True): cv.All( cv.Optional(CONF_SDA, default="SDA"): pin_with_input_and_output_support,
cv.only_with_esp_idf, cv.boolean cv.SplitDefault(CONF_SDA_PULLUP_ENABLED, esp32_idf=True): cv.All(
), cv.only_with_esp_idf, cv.boolean
cv.Optional(CONF_SCL, default="SCL"): pin_with_input_and_output_support, ),
cv.SplitDefault(CONF_SCL_PULLUP_ENABLED, esp32_idf=True): cv.All( cv.Optional(CONF_SCL, default="SCL"): pin_with_input_and_output_support,
cv.only_with_esp_idf, cv.boolean cv.SplitDefault(CONF_SCL_PULLUP_ENABLED, esp32_idf=True): cv.All(
), cv.only_with_esp_idf, cv.boolean
cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All( ),
cv.frequency, cv.Range(min=0, min_included=False) cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All(
), cv.frequency, cv.Range(min=0, min_included=False)
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(["esp32", "esp8266", "rp2040"]),
)
@coroutine_with_priority(1.0) @coroutine_with_priority(1.0)

View file

@ -25,7 +25,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 +42,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),
@ -140,8 +141,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
@ -178,6 +177,4 @@ 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))
cg.add(var.set_data_rate(config[CONF_DATA_RATE]))
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,12 +94,36 @@ 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 ILI9XXX_DISPON, 0x80, // Set display on
0x00 // end 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
0x00 // end
};
static const uint8_t PROGMEM INITCMD_ILI9486[] = { static const uint8_t PROGMEM INITCMD_ILI9486[] = {
ILI9XXX_SLPOUT, 0x80, ILI9XXX_SLPOUT, 0x80,
ILI9XXX_PIXFMT, 1, 0x55, ILI9XXX_PIXFMT, 1, 0x55,

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"

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

@ -16,7 +16,7 @@ from esphome.const import (
) )
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
CODEOWNERS = ["@MrEditor97"] CODEOWNERS = ["@mreditor97"]
ina260_ns = cg.esphome_ns.namespace("ina260") ina260_ns = cg.esphome_ns.namespace("ina260")
INA260Component = ina260_ns.class_( INA260Component = ina260_ns.class_(

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"]

View file

@ -0,0 +1,53 @@
#include "libretiny_pwm.h"
#include "esphome/core/log.h"
#ifdef USE_LIBRETINY
namespace esphome {
namespace libretiny_pwm {
static const char *const TAG = "libretiny.pwm";
void LibreTinyPWM::write_state(float state) {
if (!this->initialized_) {
ESP_LOGW(TAG, "LibreTinyPWM output hasn't been initialized yet!");
return;
}
if (this->pin_->is_inverted())
state = 1.0f - state;
this->duty_ = state;
const uint32_t max_duty = (uint32_t(1) << this->bit_depth_) - 1;
const float duty_rounded = roundf(state * max_duty);
auto duty = static_cast<uint32_t>(duty_rounded);
analogWrite(this->pin_->get_pin(), duty); // NOLINT
}
void LibreTinyPWM::setup() {
this->update_frequency(this->frequency_);
this->turn_off();
}
void LibreTinyPWM::dump_config() {
ESP_LOGCONFIG(TAG, "PWM Output:");
LOG_PIN(" Pin ", this->pin_);
ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_);
}
void LibreTinyPWM::update_frequency(float frequency) {
this->frequency_ = frequency;
// force changing the frequency by removing PWM mode
this->pin_->pin_mode(gpio::FLAG_INPUT);
analogWriteResolution(this->bit_depth_); // NOLINT
analogWriteFrequency(frequency); // NOLINT
this->initialized_ = true;
// re-apply duty
this->write_state(this->duty_);
}
} // namespace libretiny_pwm
} // namespace esphome
#endif

View file

@ -0,0 +1,55 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/automation.h"
#include "esphome/components/output/float_output.h"
#ifdef USE_LIBRETINY
namespace esphome {
namespace libretiny_pwm {
class LibreTinyPWM : public output::FloatOutput, public Component {
public:
explicit LibreTinyPWM(InternalGPIOPin *pin) : pin_(pin) {}
void set_frequency(float frequency) { this->frequency_ = frequency; }
/// Dynamically change frequency at runtime
void update_frequency(float frequency) override;
/// Setup LibreTinyPWM.
void setup() override;
void dump_config() override;
/// HARDWARE setup priority
float get_setup_priority() const override { return setup_priority::HARDWARE; }
/// Override FloatOutput's write_state.
void write_state(float state) override;
protected:
InternalGPIOPin *pin_;
uint8_t bit_depth_{10};
float frequency_{};
float duty_{0.0f};
bool initialized_ = false;
};
template<typename... Ts> class SetFrequencyAction : public Action<Ts...> {
public:
SetFrequencyAction(LibreTinyPWM *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(float, frequency);
void play(Ts... x) {
float freq = this->frequency_.value(x...);
this->parent_->update_frequency(freq);
}
protected:
LibreTinyPWM *parent_;
};
} // namespace libretiny_pwm
} // namespace esphome
#endif

View file

@ -0,0 +1,49 @@
from esphome import pins, automation
from esphome.components import output
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import (
CONF_FREQUENCY,
CONF_ID,
CONF_PIN,
)
DEPENDENCIES = ["libretiny"]
libretinypwm_ns = cg.esphome_ns.namespace("libretiny_pwm")
LibreTinyPWM = libretinypwm_ns.class_("LibreTinyPWM", output.FloatOutput, cg.Component)
SetFrequencyAction = libretinypwm_ns.class_("SetFrequencyAction", automation.Action)
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(LibreTinyPWM),
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_FREQUENCY, default="1kHz"): cv.frequency,
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
gpio = await cg.gpio_pin_expression(config[CONF_PIN])
var = cg.new_Pvariable(config[CONF_ID], gpio)
await cg.register_component(var, config)
await output.register_output(var, config)
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
@automation.register_action(
"output.libretiny_pwm.set_frequency",
SetFrequencyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(LibreTinyPWM),
cv.Required(CONF_FREQUENCY): cv.templatable(cv.int_),
}
),
)
async def libretiny_pwm_set_frequency_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config[CONF_FREQUENCY], args, float)
cg.add(var.set_frequency(template_))
return var

View file

@ -0,0 +1,427 @@
// LwRx.cpp
//
// LightwaveRF 434MHz receiver interface for Arduino
//
// Author: Bob Tidey (robert@tideys.net)
#ifdef USE_ESP8266
#include "LwRx.h"
#include <cstring>
namespace esphome {
namespace lightwaverf {
/**
Pin change interrupt routine that identifies 1 and 0 LightwaveRF bits
and constructs a message when a valid packet of data is received.
**/
void IRAM_ATTR LwRx::rx_process_bits(LwRx *args) {
uint8_t event = args->rx_pin_isr_.digital_read(); // start setting event to the current value
uint32_t curr = micros(); // the current time in microseconds
uint16_t dur = (curr - args->rx_prev); // unsigned int
args->rx_prev = curr;
// set event based on input and duration of previous pulse
if (dur < 120) { // 120 very short
} else if (dur < 500) { // normal short pulse
event += 2;
} else if (dur < 2000) { // normal long pulse
event += 4;
} else if (dur > 5000) { // gap between messages
event += 6;
} else { // 2000 > 5000
event = 8; // illegal gap
}
// state machine transitions
switch (args->rx_state) {
case RX_STATE_IDLE:
switch (event) {
case 7: // 1 after a message gap
args->rx_state = RX_STATE_MSGSTARTFOUND;
break;
}
break;
case RX_STATE_MSGSTARTFOUND:
switch (event) {
case 2: // 0 160->500
// nothing to do wait for next positive edge
break;
case 3: // 1 160->500
args->rx_num_bytes = 0;
args->rx_state = RX_STATE_BYTESTARTFOUND;
break;
default:
// not good start again
args->rx_state = RX_STATE_IDLE;
break;
}
break;
case RX_STATE_BYTESTARTFOUND:
switch (event) {
case 2: // 0 160->500
// nothing to do wait for next positive edge
break;
case 3: // 1 160->500
args->rx_state = RX_STATE_GETBYTE;
args->rx_num_bits = 0;
break;
case 5: // 0 500->1500
args->rx_state = RX_STATE_GETBYTE;
// Starts with 0 so put this into uint8_t
args->rx_num_bits = 1;
args->rx_buf[args->rx_num_bytes] = 0;
break;
default:
// not good start again
args->rx_state = RX_STATE_IDLE;
break;
}
break;
case RX_STATE_GETBYTE:
switch (event) {
case 2: // 0 160->500
// nothing to do wait for next positive edge but do stats
if (args->lwrx_stats_enable) {
args->lwrx_stats[RX_STAT_HIGH_MAX] = std::max(args->lwrx_stats[RX_STAT_HIGH_MAX], dur);
args->lwrx_stats[RX_STAT_HIGH_MIN] = std::min(args->lwrx_stats[RX_STAT_HIGH_MIN], dur);
args->lwrx_stats[RX_STAT_HIGH_AVE] =
args->lwrx_stats[RX_STAT_HIGH_AVE] - (args->lwrx_stats[RX_STAT_HIGH_AVE] >> 4) + dur;
}
break;
case 3: // 1 160->500
// a single 1
args->rx_buf[args->rx_num_bytes] = args->rx_buf[args->rx_num_bytes] << 1 | 1;
args->rx_num_bits++;
if (args->lwrx_stats_enable) {
args->lwrx_stats[RX_STAT_LOW1_MAX] = std::max(args->lwrx_stats[RX_STAT_LOW1_MAX], dur);
args->lwrx_stats[RX_STAT_LOW1_MIN] = std::min(args->lwrx_stats[RX_STAT_LOW1_MIN], dur);
args->lwrx_stats[RX_STAT_LOW1_AVE] =
args->lwrx_stats[RX_STAT_LOW1_AVE] - (args->lwrx_stats[RX_STAT_LOW1_AVE] >> 4) + dur;
}
break;
case 5: // 1 500->1500
// a 1 followed by a 0
args->rx_buf[args->rx_num_bytes] = args->rx_buf[args->rx_num_bytes] << 2 | 2;
args->rx_num_bits++;
args->rx_num_bits++;
if (args->lwrx_stats_enable) {
args->lwrx_stats[RX_STAT_LOW0_MAX] = std::max(args->lwrx_stats[RX_STAT_LOW0_MAX], dur);
args->lwrx_stats[RX_STAT_LOW0_MIN] = std::min(args->lwrx_stats[RX_STAT_LOW0_MIN], dur);
args->lwrx_stats[RX_STAT_LOW0_AVE] =
args->lwrx_stats[RX_STAT_LOW0_AVE] - (args->lwrx_stats[RX_STAT_LOW0_AVE] >> 4) + dur;
}
break;
default:
// not good start again
args->rx_state = RX_STATE_IDLE;
break;
}
if (args->rx_num_bits >= 8) {
args->rx_num_bytes++;
args->rx_num_bits = 0;
if (args->rx_num_bytes >= RX_MSGLEN) {
uint32_t curr_millis = millis();
if (args->rx_repeats > 0) {
if ((curr_millis - args->rx_prevpkttime) / 100 > args->rx_timeout) {
args->rx_repeatcount = 1;
} else {
// Test message same as last one
int16_t i = RX_MSGLEN; // int
do {
i--;
} while ((i >= 0) && (args->rx_msg[i] == args->rx_buf[i]));
if (i < 0) {
args->rx_repeatcount++;
} else {
args->rx_repeatcount = 1;
}
}
} else {
args->rx_repeatcount = 0;
}
args->rx_prevpkttime = curr_millis;
// If last message hasn't been read it gets overwritten
memcpy(args->rx_msg, args->rx_buf, RX_MSGLEN);
if (args->rx_repeats == 0 || args->rx_repeatcount == args->rx_repeats) {
if (args->rx_pairtimeout != 0) {
if ((curr_millis - args->rx_pairstarttime) / 100 <= args->rx_pairtimeout) {
if (args->rx_msg[3] == RX_CMD_ON) {
args->rx_addpairfrommsg_();
} else if (args->rx_msg[3] == RX_CMD_OFF) {
args->rx_remove_pair_(&args->rx_msg[2]);
}
}
}
if (args->rx_report_message_()) {
args->rx_msgcomplete = true;
}
args->rx_pairtimeout = 0;
}
// And cycle round for next one
args->rx_state = RX_STATE_IDLE;
} else {
args->rx_state = RX_STATE_BYTESTARTFOUND;
}
}
break;
}
}
/**
Test if a message has arrived
**/
bool LwRx::lwrx_message() { return (this->rx_msgcomplete); }
/**
Set translate mode
**/
void LwRx::lwrx_settranslate(bool rxtranslate) { this->rx_translate = rxtranslate; }
/**
Transfer a message to user buffer
**/
bool LwRx::lwrx_getmessage(uint8_t *buf, uint8_t len) {
bool ret = true;
int16_t j = 0; // int
if (this->rx_msgcomplete && len <= RX_MSGLEN) {
for (uint8_t i = 0; ret && i < RX_MSGLEN; i++) {
if (this->rx_translate || (len != RX_MSGLEN)) {
j = this->rx_find_nibble_(this->rx_msg[i]);
if (j < 0)
ret = false;
} else {
j = this->rx_msg[i];
}
switch (len) {
case 4:
if (i == 9)
buf[2] = j;
if (i == 2)
buf[3] = j;
case 2:
if (i == 3)
buf[0] = j;
if (i == 0)
buf[1] = j << 4;
if (i == 1)
buf[1] += j;
break;
case 10:
buf[i] = j;
break;
}
}
this->rx_msgcomplete = false;
} else {
ret = false;
}
return ret;
}
/**
Return time in milliseconds since last packet received
**/
uint32_t LwRx::lwrx_packetinterval() { return millis() - this->rx_prevpkttime; }
/**
Set up repeat filtering of received messages
**/
void LwRx::lwrx_setfilter(uint8_t repeats, uint8_t timeout) {
this->rx_repeats = repeats;
this->rx_timeout = timeout;
}
/**
Add a pair to filter received messages
pairdata is device,dummy,5*addr,room
pairdata is held in translated form to make comparisons quicker
**/
uint8_t LwRx::lwrx_addpair(const uint8_t *pairdata) {
if (this->rx_paircount < RX_MAXPAIRS) {
for (uint8_t i = 0; i < 8; i++) {
this->rx_pairs[rx_paircount][i] = RX_NIBBLE[pairdata[i]];
}
this->rx_paircommit_();
}
return this->rx_paircount;
}
/**
Make a pair from next message successfully received
**/
void LwRx::lwrx_makepair(uint8_t timeout) {
this->rx_pairtimeout = timeout;
this->rx_pairstarttime = millis();
}
/**
Get pair data (translated back to nibble form
**/
uint8_t LwRx::lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber) {
if (pairnumber < this->rx_paircount) {
int16_t j; // int
for (uint8_t i = 0; i < 8; i++) {
j = this->rx_find_nibble_(this->rx_pairs[pairnumber][i]);
if (j >= 0)
pairdata[i] = j;
}
}
return this->rx_paircount;
}
/**
Clear all pairing
**/
void LwRx::lwrx_clearpairing_() { rx_paircount = 0; }
/**
Return stats on high and low pulses
**/
bool LwRx::lwrx_getstats_(uint16_t *stats) { // unsigned int
if (this->lwrx_stats_enable) {
memcpy(stats, this->lwrx_stats, 2 * RX_STAT_COUNT);
return true;
} else {
return false;
}
}
/**
Set stats mode
**/
void LwRx::lwrx_setstatsenable_(bool rx_stats_enable) {
this->lwrx_stats_enable = rx_stats_enable;
if (!this->lwrx_stats_enable) {
// clear down stats when disabling
memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
}
}
/**
Set pairs behaviour
**/
void LwRx::lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only) {
this->rx_pairEnforce = pair_enforce;
this->rx_pairBaseOnly = pair_base_only;
}
/**
Set things up to receive LightWaveRF 434Mhz messages
pin must be 2 or 3 to trigger interrupts
!!! For Spark, any pin will work
**/
void LwRx::lwrx_setup(InternalGPIOPin *pin) {
// rx_pin = pin;
pin->setup();
this->rx_pin_isr_ = pin->to_isr();
pin->attach_interrupt(&LwRx::rx_process_bits, this, gpio::INTERRUPT_ANY_EDGE);
memcpy(this->lwrx_stats, LWRX_STATSDFLT, sizeof(LWRX_STATSDFLT));
}
/**
Check a message to see if it should be reported under pairing / mood / all off rules
returns -1 if none found
**/
bool LwRx::rx_report_message_() {
if (this->rx_pairEnforce && this->rx_paircount == 0) {
return false;
} else {
bool all_devices;
// True if mood to device 15 or Off cmd with Allof paramater
all_devices = ((this->rx_msg[3] == RX_CMD_MOOD && this->rx_msg[2] == RX_DEV_15) ||
(this->rx_msg[3] == RX_CMD_OFF && this->rx_msg[0] == RX_PAR0_ALLOFF));
return (rx_check_pairs_(&this->rx_msg[2], all_devices) != -1);
}
}
/**
Find nibble from byte
returns -1 if none found
**/
int16_t LwRx::rx_find_nibble_(uint8_t data) { // int
int16_t i = 15; // int
do {
if (RX_NIBBLE[i] == data)
break;
i--;
} while (i >= 0);
return i;
}
/**
add pair from message buffer
**/
void LwRx::rx_addpairfrommsg_() {
if (this->rx_paircount < RX_MAXPAIRS) {
memcpy(this->rx_pairs[this->rx_paircount], &this->rx_msg[2], 8);
this->rx_paircommit_();
}
}
/**
check and commit pair
**/
void LwRx::rx_paircommit_() {
if (this->rx_paircount == 0 || this->rx_check_pairs_(this->rx_pairs[this->rx_paircount], false) < 0) {
this->rx_paircount++;
}
}
/**
Check to see if message matches one of the pairs
if mode is pairBase only then ignore device and room
if allDevices is true then ignore the device number
Returns matching pair number, -1 if not found, -2 if no pairs defined
**/
int16_t LwRx::rx_check_pairs_(const uint8_t *buf, bool all_devices) { // int
if (this->rx_paircount == 0) {
return -2;
} else {
int16_t pair = this->rx_paircount; // int
int16_t j = -1; // int
int16_t jstart, jend; // int
if (this->rx_pairBaseOnly) {
// skip room(8) and dev/cmd (0,1)
jstart = 7;
jend = 2;
} else {
// include room in comparison
jstart = 8;
// skip device comparison if allDevices true
jend = (all_devices) ? 2 : 0;
}
while (pair > 0 && j < 0) {
pair--;
j = jstart;
while (j > jend) {
j--;
if (j != 1) {
if (this->rx_pairs[pair][j] != buf[j]) {
j = -1;
}
}
}
}
return (j >= 0) ? pair : -1;
}
}
/**
Remove an existing pair matching the buffer
**/
void LwRx::rx_remove_pair_(uint8_t *buf) {
int16_t pair = this->rx_check_pairs_(buf, false); // int
if (pair >= 0) {
while (pair < this->rx_paircount - 1) {
for (uint8_t j = 0; j < 8; j++) {
this->rx_pairs[pair][j] = this->rx_pairs[pair + 1][j];
}
pair++;
}
this->rx_paircount--;
}
}
} // namespace lightwaverf
} // namespace esphome
#endif

View file

@ -0,0 +1,142 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace lightwaverf {
// LwRx.h
//
// LightwaveRF 434MHz receiver for Arduino
//
// Author: Bob Tidey (robert@tideys.net)
static const uint8_t RX_STAT_HIGH_AVE = 0;
static const uint8_t RX_STAT_HIGH_MAX = 1;
static const uint8_t RX_STAT_HIGH_MIN = 2;
static const uint8_t RX_STAT_LOW0_AVE = 3;
static const uint8_t RX_STAT_LOW0_MAX = 4;
static const uint8_t RX_STAT_LOW0_MIN = 5;
static const uint8_t RX_STAT_LOW1_AVE = 6;
static const uint8_t RX_STAT_LOW1_MAX = 7;
static const uint8_t RX_STAT_LOW1_MIN = 8;
static const uint8_t RX_STAT_COUNT = 9;
// sets maximum number of pairings which can be held
static const uint8_t RX_MAXPAIRS = 10;
static const uint8_t RX_NIBBLE[] = {0xF6, 0xEE, 0xED, 0xEB, 0xDE, 0xDD, 0xDB, 0xBE,
0xBD, 0xBB, 0xB7, 0x7E, 0x7D, 0x7B, 0x77, 0x6F};
static const uint8_t RX_CMD_OFF = 0xF6; // raw 0
static const uint8_t RX_CMD_ON = 0xEE; // raw 1
static const uint8_t RX_CMD_MOOD = 0xED; // raw 2
static const uint8_t RX_PAR0_ALLOFF = 0x7D; // param 192-255 all off (12 in msb)
static const uint8_t RX_DEV_15 = 0x6F; // device 15
static const uint8_t RX_MSGLEN = 10; // expected length of rx message
static const uint8_t RX_STATE_IDLE = 0;
static const uint8_t RX_STATE_MSGSTARTFOUND = 1;
static const uint8_t RX_STATE_BYTESTARTFOUND = 2;
static const uint8_t RX_STATE_GETBYTE = 3;
// Gather stats for pulse widths (ave is x 16)
static const uint16_t LWRX_STATSDFLT[RX_STAT_COUNT] = {5000, 0, 5000, 20000, 0, 2500, 4000, 0, 500}; // usigned int
class LwRx {
public:
// Seup must be called once, set up pin used to receive data
void lwrx_setup(InternalGPIOPin *pin);
// Set translate to determine whether translating from nibbles to bytes in message
// Translate off only applies to 10char message returns
void lwrx_settranslate(bool translate);
// Check to see whether message available
bool lwrx_message();
// Get a message, len controls format (2 cmd+param, 4 cmd+param+room+device),10 full message
bool lwrx_getmessage(uint8_t *buf, uint8_t len);
// Setup repeat filter
void lwrx_setfilter(uint8_t repeats, uint8_t timeout);
// Add pair, if no pairing set then all messages are received, returns number of pairs
uint8_t lwrx_addpair(const uint8_t *pairdata);
// Get pair data into buffer for the pairnumber. Returns current paircount
// Use pairnumber 255 to just get current paircount
uint8_t lwrx_getpair(uint8_t *pairdata, uint8_t pairnumber);
// Make a pair from next message received within timeout 100mSec
// This call returns immediately whilst message checking continues
void lwrx_makepair(uint8_t timeout);
// Set pair mode controls
void lwrx_set_pair_mode(bool pair_enforce, bool pair_base_only);
// Returns time from last packet received in msec
// Can be used to determine if Rx may be still receiving repeats
uint32_t lwrx_packetinterval();
static void rx_process_bits(LwRx *arg);
// Pairing data
uint8_t rx_paircount = 0;
uint8_t rx_pairs[RX_MAXPAIRS][8];
// set false to responds to all messages if no pairs set up
bool rx_pairEnforce = false;
// set false to use Address, Room and Device in pairs, true just the Address part
bool rx_pairBaseOnly = false;
uint8_t rx_pairtimeout = 0; // 100msec units
// Repeat filters
uint8_t rx_repeats = 2; // msg must be repeated at least this number of times
uint8_t rx_repeatcount = 0;
uint8_t rx_timeout = 20; // reset repeat window after this in 100mSecs
uint32_t rx_prevpkttime = 0; // last packet time in milliseconds
uint32_t rx_pairstarttime = 0; // last msg time in milliseconds
// Receive mode constants and variables
uint8_t rx_msg[RX_MSGLEN]; // raw message received
uint8_t rx_buf[RX_MSGLEN]; // message buffer during reception
uint32_t rx_prev; // time of previous interrupt in microseconds
bool rx_msgcomplete = false; // set high when message available
bool rx_translate = true; // Set false to get raw data
uint8_t rx_state = 0;
uint8_t rx_num_bits = 0; // number of bits in the current uint8_t
uint8_t rx_num_bytes = 0; // number of bytes received
uint16_t lwrx_stats[RX_STAT_COUNT]; // unsigned int
bool lwrx_stats_enable = true;
protected:
void lwrx_clearpairing_();
// Return stats on pulse timings
bool lwrx_getstats_(uint16_t *stats);
// Enable collection of stats on pulse timings
void lwrx_setstatsenable_(bool rx_stats_enable);
// internal support functions
bool rx_report_message_();
int16_t rx_find_nibble_(uint8_t data); // int
void rx_addpairfrommsg_();
void rx_paircommit_();
void rx_remove_pair_(uint8_t *buf);
int16_t rx_check_pairs_(const uint8_t *buf, bool all_devices); // int
ISRInternalGPIOPin rx_pin_isr_;
InternalGPIOPin *rx_pin_;
};
} // namespace lightwaverf
} // namespace esphome

View file

@ -0,0 +1,208 @@
// LwTx.cpp
//
// LightwaveRF 434MHz tx interface for Arduino
//
// Author: Bob Tidey (robert@tideys.net)
#ifdef USE_ESP8266
#include "LwTx.h"
#include <cstring>
#include <Arduino.h>
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace lightwaverf {
static const uint8_t TX_NIBBLE[] = {0xF6, 0xEE, 0xED, 0xEB, 0xDE, 0xDD, 0xDB, 0xBE,
0xBD, 0xBB, 0xB7, 0x7E, 0x7D, 0x7B, 0x77, 0x6F};
static const uint8_t TX_STATE_IDLE = 0;
static const uint8_t TX_STATE_MSGSTART = 1;
static const uint8_t TX_STATE_BYTESTART = 2;
static const uint8_t TX_STATE_SENDBYTE = 3;
static const uint8_t TX_STATE_MSGEND = 4;
static const uint8_t TX_STATE_GAPSTART = 5;
static const uint8_t TX_STATE_GAPEND = 6;
/**
Set translate mode
**/
void LwTx::lwtx_settranslate(bool txtranslate) { tx_translate = txtranslate; }
static void IRAM_ATTR isr_t_xtimer(LwTx *arg) {
// Set low after toggle count interrupts
arg->tx_toggle_count--;
if (arg->tx_toggle_count == arg->tx_trail_count) {
// ESP_LOGD("lightwaverf.sensor", "timer")
arg->tx_pin->digital_write(arg->txoff);
} else if (arg->tx_toggle_count == 0) {
arg->tx_toggle_count = arg->tx_high_count; // default high pulse duration
switch (arg->tx_state) {
case TX_STATE_IDLE:
if (arg->tx_msg_active) {
arg->tx_repeat = 0;
arg->tx_state = TX_STATE_MSGSTART;
}
break;
case TX_STATE_MSGSTART:
arg->tx_pin->digital_write(arg->txon);
arg->tx_num_bytes = 0;
arg->tx_state = TX_STATE_BYTESTART;
break;
case TX_STATE_BYTESTART:
arg->tx_pin->digital_write(arg->txon);
arg->tx_bit_mask = 0x80;
arg->tx_state = TX_STATE_SENDBYTE;
break;
case TX_STATE_SENDBYTE:
if (arg->tx_buf[arg->tx_num_bytes] & arg->tx_bit_mask) {
arg->tx_pin->digital_write(arg->txon);
} else {
// toggle count for the 0 pulse
arg->tx_toggle_count = arg->tx_low_count;
}
arg->tx_bit_mask >>= 1;
if (arg->tx_bit_mask == 0) {
arg->tx_num_bytes++;
if (arg->tx_num_bytes >= esphome::lightwaverf::LwTx::TX_MSGLEN) {
arg->tx_state = TX_STATE_MSGEND;
} else {
arg->tx_state = TX_STATE_BYTESTART;
}
}
break;
case TX_STATE_MSGEND:
arg->tx_pin->digital_write(arg->txon);
arg->tx_state = TX_STATE_GAPSTART;
arg->tx_gap_repeat = arg->tx_gap_multiplier;
break;
case TX_STATE_GAPSTART:
arg->tx_toggle_count = arg->tx_gap_count;
if (arg->tx_gap_repeat == 0) {
arg->tx_state = TX_STATE_GAPEND;
} else {
arg->tx_gap_repeat--;
}
break;
case TX_STATE_GAPEND:
arg->tx_repeat++;
if (arg->tx_repeat >= arg->tx_repeats) {
// disable timer nterrupt
arg->lw_timer_stop();
arg->tx_msg_active = false;
arg->tx_state = TX_STATE_IDLE;
} else {
arg->tx_state = TX_STATE_MSGSTART;
}
break;
}
}
}
/**
Check for send free
**/
bool LwTx::lwtx_free() { return !this->tx_msg_active; }
/**
Send a LightwaveRF message (10 nibbles in bytes)
**/
void LwTx::lwtx_send(const std::vector<uint8_t> &msg) {
if (this->tx_translate) {
for (uint8_t i = 0; i < TX_MSGLEN; i++) {
this->tx_buf[i] = TX_NIBBLE[msg[i] & 0xF];
ESP_LOGD("lightwaverf.sensor", "%x ", msg[i]);
}
} else {
// memcpy(tx_buf, msg, tx_msglen);
}
this->lw_timer_start();
this->tx_msg_active = true;
}
/**
Set 5 char address for future messages
**/
void LwTx::lwtx_setaddr(const uint8_t *addr) {
for (uint8_t i = 0; i < 5; i++) {
this->tx_buf[i + 4] = TX_NIBBLE[addr[i] & 0xF];
}
}
/**
Send a LightwaveRF message (10 nibbles in bytes)
**/
void LwTx::lwtx_cmd(uint8_t command, uint8_t parameter, uint8_t room, uint8_t device) {
// enable timer 2 interrupts
this->tx_buf[0] = TX_NIBBLE[parameter >> 4];
this->tx_buf[1] = TX_NIBBLE[parameter & 0xF];
this->tx_buf[2] = TX_NIBBLE[device & 0xF];
this->tx_buf[3] = TX_NIBBLE[command & 0xF];
this->tx_buf[9] = TX_NIBBLE[room & 0xF];
this->lw_timer_start();
this->tx_msg_active = true;
}
/**
Set things up to transmit LightWaveRF 434Mhz messages
**/
void LwTx::lwtx_setup(InternalGPIOPin *pin, uint8_t repeats, bool inverted, int u_sec) {
pin->setup();
tx_pin = pin;
tx_pin->pin_mode(gpio::FLAG_OUTPUT);
tx_pin->digital_write(txoff);
if (repeats > 0 && repeats < 40) {
this->tx_repeats = repeats;
}
if (inverted) {
this->txon = 0;
this->txoff = 1;
} else {
this->txon = 1;
this->txoff = 0;
}
int period1 = 330;
/*
if (period > 32 && period < 1000) {
period1 = period;
} else {
// default 330 uSec
period1 = 330;
}*/
this->espPeriod = 5 * period1;
timer1_isr_init();
}
void LwTx::lwtx_set_tick_counts(uint8_t low_count, uint8_t high_count, uint8_t trail_count, uint8_t gap_count) {
this->tx_low_count = low_count;
this->tx_high_count = high_count;
this->tx_trail_count = trail_count;
this->tx_gap_count = gap_count;
}
void LwTx::lwtx_set_gap_multiplier(uint8_t gap_multiplier) { this->tx_gap_multiplier = gap_multiplier; }
void LwTx::lw_timer_start() {
{
InterruptLock lock;
static LwTx *arg = this; // NOLINT
timer1_attachInterrupt([] { isr_t_xtimer(arg); });
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
timer1_write(this->espPeriod);
}
}
void LwTx::lw_timer_stop() {
{
InterruptLock lock;
timer1_disable();
timer1_detachInterrupt();
}
}
} // namespace lightwaverf
} // namespace esphome
#endif

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