1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-02 16:11:53 +00:00

Compare commits

..

130 Commits

Author SHA1 Message Date
Jesse Hills
01899d70bc [esp32_ble] Attempt to manage memory better with shared_ptr 2024-05-24 16:48:23 +12:00
Edward Firmo
9d03f47233 [nextion] Add basic functions to Intelligent series (#6791) 2024-05-24 09:11:34 +12:00
polyfloyd
c2d67659f3 mpr121: Add GPIO support (#6776)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-24 09:08:28 +12:00
Pavlo Dudnytskyi
aed0593793 [haier] `text_sensor and button` platforms (#6780) 2024-05-24 09:07:39 +12:00
Jesse Hills
4ab7a5d964 [ledc] Change some logging lines from debug to verbose (#6796) 2024-05-23 00:04:33 -05:00
Penny Wood
7f9383c83b [sx1509] Output open drain pin mode (#6788) 2024-05-23 09:31:56 +12:00
Keith Burzinski
9a6fde21ee Add on_safe_mode trigger (#6790) 2024-05-23 07:43:13 +12:00
Jeroen van Oort
1ca7c2d7dd Add support for acting as Modbus server (#4874)
Co-authored-by: Jeroen van Oort <jeroen.vanoort@webparking.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-22 16:17:32 +12:00
Keith Burzinski
76abf2200c Uncouple safe_mode from OTA (#6759) 2024-05-22 13:08:53 +12:00
Jesse Hills
83d3584173 Merge branch 'release' into dev 2024-05-21 12:30:00 +12:00
Jesse Hills
0ee4348777 Merge pull request #6786 from esphome/bump-2024.5.2
2024.5.2
2024-05-21 12:29:09 +12:00
Jesse Hills
fcdf36e991 Bump version to 2024.5.2 2024-05-21 11:07:20 +12:00
esphomebot
5eb8efd8b3 Update webserver local assets to 20240519-215627 (#6779) 2024-05-21 11:07:20 +12:00
Jesse Hills
cd0f557940 [remote_receiver] Add better error message for tolerance breaking change (#6784) 2024-05-21 11:07:20 +12:00
J. Nick Koston
efde677ca9 Fix DashboardEntries.all() call (#6783) 2024-05-21 11:07:20 +12:00
J. Nick Koston
2eebee1de7 Revert "Fix MQTT dashboard discovery (Exception in MqttStatusThread)." (#6782) 2024-05-21 11:07:20 +12:00
esphomebot
525c4891d5 Update webserver local assets to 20240519-215627 (#6779) 2024-05-21 10:55:56 +12:00
Anton Viktorov
ce6dc040da Tiny fix in automation.h - unused return value removed (#6760) 2024-05-21 10:54:38 +12:00
Jesse Hills
9de67feccd [remote_receiver] Add better error message for tolerance breaking change (#6784) 2024-05-21 10:53:16 +12:00
Clyde Stubbs
bad400e1cd [ili9xxx] Add 18bit mode selection and custom init sequence (#6745) 2024-05-21 09:18:13 +12:00
J. Nick Koston
59b1e9c1b0 Fix DashboardEntries.all() call (#6783) 2024-05-20 11:52:24 +00:00
J. Nick Koston
25ee24299a Revert "Fix MQTT dashboard discovery (Exception in MqttStatusThread)." (#6782) 2024-05-20 11:49:00 +00:00
Jesse Hills
81ef67cfbb Merge branch 'release' into dev 2024-05-20 19:50:39 +12:00
Jesse Hills
f235dcc096 Merge pull request #6781 from esphome/bump-2024.5.1
2024.5.1
2024-05-20 19:48:55 +12:00
Jesse Hills
d2d3db4b8c Bump version to 2024.5.1 2024-05-20 17:14:17 +12:00
Markus
ec6d86c8f5 Fix MQTT dashboard discovery (Exception in MqttStatusThread). (#6775) 2024-05-20 17:14:17 +12:00
Markus
7452879fb1 Fix Upload from Dashboard with MQTT discovery. (#6774)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-20 17:14:17 +12:00
esphomebot
4fc2f2284a Synchronise Device Classes from Home Assistant (#6768) 2024-05-20 17:14:17 +12:00
acshef
840f69ffe6 Add device_class to valve core config (#6765) 2024-05-20 17:14:17 +12:00
fodfodfod
b9bb3cd4be add rp2040 support to the wizard (#6239) 2024-05-20 12:42:30 +12:00
Markus
91e7a44c31 Fix MQTT dashboard discovery (Exception in MqttStatusThread). (#6775) 2024-05-20 11:52:14 +12:00
Markus
080f8bc86e Fix Upload from Dashboard with MQTT discovery. (#6774)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-19 20:33:20 +00:00
dependabot[bot]
a85d37a1cf Bump actions/checkout from 4.1.5 to 4.1.6 (#6764)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-20 08:12:32 +12:00
tomaszduda23
ba73187c1b separate deep_sleep component for each platform in different file (#6762)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-20 08:11:27 +12:00
tomaszduda23
4469ba4024 [tests] make test_build_components work with venv without installing esphome (#6761) 2024-05-19 11:47:23 +12:00
Sebastian Muszynski
70e0925f9a Fix pip3 install (#6771) 2024-05-19 11:31:36 +12:00
esphomebot
1164cb8610 Synchronise Device Classes from Home Assistant (#6768) 2024-05-18 07:15:52 +00:00
acshef
94b63d7bc2 Add device_class to valve core config (#6765) 2024-05-18 16:17:09 +12:00
Jesse Hills
df838b5788 [core] Remove references to deleted setup.py (#6757) 2024-05-16 22:33:33 +12:00
Edward Firmo
d410cc4f7b [nextion] Fix type on sprintf for IDF v5 (#6758) 2024-05-16 22:22:18 +12:00
Anton Viktorov
b06e0746f5 INA228/INA229, INA238/INA239, INA237 power/energy/charge monitor (I2C, SPI) (#6138)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-16 16:50:28 +12:00
Jesse Hills
034c196ad8 [core] Update some coroutine priorities (#6755) 2024-05-16 16:46:16 +12:00
Keith Burzinski
996f71c03c Fix wifi compile error on IDF 5.1+ (#6756) 2024-05-16 04:40:21 +00:00
Anton Viktorov
98cb6555df SPI and I2C for ENS160 (#6369) 2024-05-16 15:22:40 +12:00
Faidon Liambotis
0bb2773c64 Port wifi_component_esp32_arduino from tcpip_adapter to esp_netif (#6476)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-16 14:49:40 +12:00
Jesse Hills
7c243dafb3 [core] Fix some extends cases (#6748) 2024-05-16 14:11:54 +12:00
Mat931
247b2eee30 Add ADC multisampling (#6330) 2024-05-16 14:11:21 +12:00
Keith Burzinski
f46c499c4e Separate OTABackend from OTA component (#6459)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-16 14:01:09 +12:00
Jesse Hills
f91c31f093 [core] Fix running pylint via pre-commit from GUI apps (#6754) 2024-05-16 13:47:56 +12:00
Jesse Hills
a27c05483c [core] Move pylint config into pyproject.toml (#6739) 2024-05-16 13:47:36 +12:00
Jesse Hills
bf48ccaf22 [core] Move pytest config into pyproject.toml (#6740) 2024-05-16 13:20:12 +12:00
Jesse Hills
f2ef06d8b5 [core] Migrate to pyproject.toml (#6737) 2024-05-16 13:19:37 +12:00
Andrew McFague
f0ec900e48 Skip gpio validation (#5615) 2024-05-16 11:49:04 +12:00
Shawn Wilsher
7d804bf90f Fix Prometheus Output to Match Spec (#6032) 2024-05-16 11:39:57 +12:00
shxshxshxshx
2921831b55 WPA2 Enterprise - Explicitly set TTLS Phase 2 (#6436)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-16 11:37:53 +12:00
heggi
08509f7755 Mirage remote receiver & transmitter (#6479)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-16 11:35:17 +12:00
Daniel Mahaney
ebfccc64c7 fix rp2040_pio_led flicker and proper multi-strip support (#6194)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-16 11:33:52 +12:00
lbilger
8952719045 Allow one timing to cancel others (#6744)
Co-authored-by: Lars Bilger <lars.bilger@lht.dlh.de>
2024-05-16 11:33:27 +12:00
ius
073fb4c124 i2c: fix format string specifiers (#6746) 2024-05-16 11:33:15 +12:00
Mat931
46eee4a4f0 Add beken_spi_led_strip component (#6515)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-16 11:32:48 +12:00
Alex Boyd
773951d85e BedJet: expose the outlet temperature on the climate and as a sensor (#6633) 2024-05-16 11:31:08 +12:00
Jesse Hills
1f29023c92 Merge branch 'release' into dev 2024-05-15 18:29:15 +12:00
Jesse Hills
caa8c820de Merge pull request #6753 from esphome/bump-2024.5.0
2024.5.0
2024-05-15 18:27:28 +12:00
dependabot[bot]
9f1ba00b7c Bump esphome-dashboard from 20240412.0 to 20240429.1 (#6743)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-15 17:16:04 +12:00
Keith Burzinski
891f56b421 [tests] `test2.yaml` has become too large (#6750) 2024-05-15 17:14:19 +12:00
Jesse Hills
0d3adc8f0c Bump version to 2024.5.0 2024-05-15 17:08:43 +12:00
Jesse Hills
d7cb953994 Merge branch 'beta' into dev 2024-05-15 16:33:46 +12:00
Jesse Hills
ad0a1c5c35 Merge pull request #6751 from esphome/bump-2024.5.0b6
2024.5.0b6
2024-05-15 16:24:15 +12:00
Jesse Hills
5d2e3a7d8d Bump version to 2024.5.0b6 2024-05-15 13:14:15 +12:00
Jesse Hills
ebc3f0fe17 [adc] Fix 11db deprecation warning (#6749) 2024-05-15 13:14:15 +12:00
Jesse Hills
9a6e90af54 [adc] Fix 11db deprecation warning (#6749) 2024-05-15 12:51:01 +12:00
Jesse Hills
55e4532a88 Merge branch 'beta' into dev 2024-05-15 10:03:05 +12:00
Jesse Hills
bd8afa51cd Merge pull request #6741 from esphome/bump-2024.5.0b5
2024.5.0b5
2024-05-15 09:57:25 +12:00
Jesse Hills
db4aa0b679 Bump version to 2024.5.0b5 2024-05-15 07:37:22 +12:00
Mischa Siekmann
28a09cc0d0 Add ANNOUNCING state to media_player. (#6691) 2024-05-15 07:37:21 +12:00
Mischa Siekmann
128fad57b3 Voice-Assistant: Start-order change for VAD disabled: start va-pipeline when microphon… (#6391) 2024-05-15 07:37:21 +12:00
Mischa Siekmann
6f53607e5a Add ANNOUNCING state to media_player. (#6691) 2024-05-14 21:40:08 +12:00
Clyde Stubbs
d5eeab81d6 [config] Improve error reporting (#6736) 2024-05-14 21:31:03 +12:00
Jesse Hills
636037cec1 [core] Fix minor formatting issues (#6738) 2024-05-14 17:01:07 +12:00
Jesse Hills
7d791cbdfb [esp32_ble] Fix compilation error on esp32c6 (#6734) 2024-05-14 16:22:43 +12:00
Jesse Hills
036a666e36 [web_server] Minor python formatting (#6735) 2024-05-14 15:38:53 +12:00
Mischa Siekmann
921e56f2c6 Voice-Assistant: Start-order change for VAD disabled: start va-pipeline when microphon… (#6391) 2024-05-14 13:25:24 +12:00
Jesse Hills
c94f638c0b Merge branch 'beta' into dev 2024-05-14 11:49:01 +12:00
Jesse Hills
142c4a87d2 Merge pull request #6733 from esphome/bump-2024.5.0b4
2024.5.0b4
2024-05-14 11:38:00 +12:00
Jesse Hills
1e4d6ee344 Bump version to 2024.5.0b4 2024-05-14 10:02:22 +12:00
Jesse Hills
5afe0e5ec2 Fix ESPHOME_PROJECT_VERSION_30 (#6731) 2024-05-14 10:02:22 +12:00
dependabot[bot]
ba3fc4c5d0 Bump platformio from 6.1.13 to 6.1.15 (#6634)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-14 10:02:22 +12:00
Mischa Siekmann
694f75117e Set FEATURE_API_AUDIO flag also if the speaker component is not used (#6712) 2024-05-14 10:02:22 +12:00
Marcin Krasowski
4ec2ef27a8 fix(ltr390): stuck ALS values when configured for ALS+UV readings (#6723) 2024-05-14 10:02:21 +12:00
dependabot[bot]
2ac0821cab Bump pytest from 8.1.1 to 8.2.0 (#6732)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-14 09:58:43 +12:00
Jesse Hills
47b40505c2 Fix ESPHOME_PROJECT_VERSION_30 (#6731) 2024-05-14 09:42:53 +12:00
dependabot[bot]
eae97dbaa0 Bump platformio from 6.1.13 to 6.1.15 (#6634)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-14 07:11:59 +12:00
Ludovic BOUÉ
91007952e2 [CST816] Add support for Hynitron Microelectronics CST826 capacitive touch (#6682) 2024-05-13 20:21:06 +10:00
Mischa Siekmann
5ee4bf3802 Set FEATURE_API_AUDIO flag also if the speaker component is not used (#6712) 2024-05-13 16:05:13 +12:00
Jorge-Crespo-Celdran
a23d1631e1 time_based_cover.cpp with manual control fix (#6719) 2024-05-13 16:04:06 +12:00
Clyde Stubbs
dd81c83686 Typing hint and doc fixes (#6729) 2024-05-13 15:21:02 +12:00
Szewcson
13e3920c13 GDK101 support (#4703)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-13 13:36:10 +12:00
tomaszduda23
67ca60e2af separate debug component for each platform in different file (#6715)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-13 11:54:43 +12:00
Marcin Krasowski
61b65e2726 fix(ltr390): stuck ALS values when configured for ALS+UV readings (#6723) 2024-05-13 11:25:41 +12:00
Clyde Stubbs
1a45858904 Add pylint to git pre-commit hooks (#6726) 2024-05-13 09:56:55 +12:00
Jesse Hills
47a1710b1e Only cache docker images on dev branch (#6714) 2024-05-10 11:55:35 +12:00
Jesse Hills
ca5050d4a5 [github] Only save platformio cache for dev branch (#6711) 2024-05-10 11:04:32 +12:00
Jesse Hills
8280772b91 Add new Error type to skip prepending path (#6716) 2024-05-10 10:57:47 +12:00
Jesse Hills
026c3a69b8 Merge branch 'beta' into dev 2024-05-09 23:23:14 +12:00
Jesse Hills
448b4f5cb6 Merge pull request #6713 from esphome/bump-2024.5.0b3
2024.5.0b3
2024-05-09 23:04:30 +12:00
Jesse Hills
8ae8cd1168 Bump version to 2024.5.0b3 2024-05-09 21:55:34 +12:00
Jesse Hills
bd776baf8d [github] Fix digest artifact name (#6710) 2024-05-09 21:55:34 +12:00
Clyde Stubbs
819bb9f8bc [color] Fix crash when hex color parses as int, improve error reporting. (#6707) 2024-05-09 21:55:34 +12:00
Clyde Stubbs
26048d18ef [core] Ensure that a generated ID name is distinct from its type. (#6706) 2024-05-09 21:55:34 +12:00
Jesse Hills
78d1a50853 [github] Fix digest artifact name (#6710) 2024-05-09 21:25:48 +12:00
Jesse Hills
ca031287a1 Merge branch 'beta' into dev 2024-05-09 17:35:13 +12:00
Jesse Hills
0883f0efd7 Merge pull request #6708 from esphome/bump-2024.5.0b2
2024.5.0b2
2024-05-09 17:29:33 +12:00
Clyde Stubbs
5956bebcb7 [color] Fix crash when hex color parses as int, improve error reporting. (#6707) 2024-05-09 03:14:31 +00:00
Clyde Stubbs
afe81184a8 [core] Ensure that a generated ID name is distinct from its type. (#6706) 2024-05-09 15:08:30 +12:00
chbmuc
d0120cefd2 Add IRK support to ble_rssi (#6422)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-09 15:02:43 +12:00
Jesse Hills
0ca395e8d0 Bump version to 2024.5.0b2 2024-05-09 14:45:40 +12:00
J. Nick Koston
98dc9fde6c Bump recommended ESP-IDF to 4.4.7 (#6703) 2024-05-09 14:45:40 +12:00
Nate Clark
ed1344edd2 Add PHY register writes to enable external clock on Ethernet with RTL8201 (#6704)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-09 14:45:40 +12:00
Jesse Hills
2fbe80c1f7 [ethernet] Use constexpr instead of inline define for KSZ80XX_PC2R_REG_ADDR (#6705) 2024-05-09 14:45:40 +12:00
Mat931
879f404b48 [remote_receiver, remote_transmitter] Improve error messages on the ESP32 (#6701) 2024-05-09 14:45:40 +12:00
Edward Firmo
34585a6f15 [nextion] Replace flags to USE_ARDUINO (#6700) 2024-05-09 14:45:40 +12:00
Jesse Hills
054587c0e4 [github] Upgrade to actions/{upload,download}-artifact v4 (#6698) 2024-05-09 14:45:39 +12:00
J. Nick Koston
3ec4a66c9e Bump recommended ESP-IDF to 4.4.7 (#6703) 2024-05-09 13:45:10 +12:00
Nate Clark
819be76013 Add PHY register writes to enable external clock on Ethernet with RTL8201 (#6704)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-09 12:47:25 +12:00
Jesse Hills
72481006e4 [ethernet] Use constexpr instead of inline define for KSZ80XX_PC2R_REG_ADDR (#6705) 2024-05-09 12:25:57 +12:00
Mat931
487e171443 [remote_receiver, remote_transmitter] Improve error messages on the ESP32 (#6701) 2024-05-09 09:58:40 +12:00
Edward Firmo
e48d02495b [nextion] Replace flags to USE_ARDUINO (#6700) 2024-05-09 07:05:24 +12:00
Jesse Hills
7764ab2411 [github] Upgrade to actions/{upload,download}-artifact v4 (#6698) 2024-05-08 16:17:45 +12:00
Jesse Hills
225beb305d Merge branch 'beta' into dev 2024-05-08 14:09:30 +12:00
Jesse Hills
b7c6125a0b Bump version to 2024.6.0-dev 2024-05-08 13:22:05 +12:00
316 changed files with 11509 additions and 6419 deletions

View File

@@ -1,19 +1,3 @@
[metadata]
license = MIT
license_file = LICENSE
platforms = any
description = Make creating custom firmwares for ESP32/ESP8266 super easy.
long_description = file: README.md
keywords = home, automation
classifier =
Environment :: Console
Intended Audience :: Developers
Intended Audience :: End Users/Desktop
License :: OSI Approved :: MIT License
Programming Language :: C++
Programming Language :: Python :: 3
Topic :: Home Automation
[flake8]
max-line-length = 120
# Following 4 for black compatibility
@@ -37,25 +21,22 @@ max-line-length = 120
# D401 First line should be in imperative mood
ignore =
E501,
W503,
E203,
D202,
E501,
W503,
E203,
D202,
D100,
D101,
D102,
D103,
D104,
D105,
D107,
D200,
D205,
D209,
D400,
D401,
D100,
D101,
D102,
D103,
D104,
D105,
D107,
D200,
D205,
D209,
D400,
D401,
exclude = api_pb2.py
[bdist_wheel]
universal = 1

View File

@@ -34,6 +34,16 @@ runs:
echo $l >> $GITHUB_OUTPUT
done
# set cache-to only if dev branch
- id: cache-to
shell: bash
run: |-
if [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
echo "value=type=gha,mode=max" >> $GITHUB_OUTPUT
else
echo "value=" >> $GITHUB_OUTPUT
fi
- name: Build and push to ghcr by digest
id: build-ghcr
uses: docker/build-push-action@v5.3.0
@@ -43,7 +53,7 @@ runs:
platforms: ${{ inputs.platform }}
target: ${{ inputs.target }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-to: ${{ steps.cache-to.outputs.value }}
build-args: |
BASEIMGTYPE=${{ inputs.baseimg }}
BUILD_VERSION=${{ inputs.version }}
@@ -57,14 +67,6 @@ runs:
digest="${{ steps.build-ghcr.outputs.digest }}"
touch "/tmp/digests/${{ inputs.target }}/ghcr/${digest#sha256:}"
- name: Upload ghcr digest
uses: actions/upload-artifact@v3.1.3
with:
name: digests-${{ inputs.target }}-ghcr
path: /tmp/digests/${{ inputs.target }}/ghcr/*
if-no-files-found: error
retention-days: 1
- name: Build and push to dockerhub by digest
id: build-dockerhub
uses: docker/build-push-action@v5.3.0
@@ -74,7 +76,7 @@ runs:
platforms: ${{ inputs.platform }}
target: ${{ inputs.target }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-to: ${{ steps.cache-to.outputs.value }}
build-args: |
BASEIMGTYPE=${{ inputs.baseimg }}
BUILD_VERSION=${{ inputs.version }}
@@ -87,11 +89,3 @@ runs:
mkdir -p /tmp/digests/${{ inputs.target }}/dockerhub
digest="${{ steps.build-dockerhub.outputs.digest }}"
touch "/tmp/digests/${{ inputs.target }}/dockerhub/${digest#sha256:}"
- name: Upload dockerhub digest
uses: actions/upload-artifact@v3.1.3
with:
name: digests-${{ inputs.target }}-dockerhub
path: /tmp/digests/${{ inputs.target }}/dockerhub/*
if-no-files-found: error
retention-days: 1

View File

@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Set up Python
uses: actions/setup-python@v5.1.0
with:

View File

@@ -40,7 +40,7 @@ jobs:
arch: [amd64, armv7, aarch64]
build_type: ["ha-addon", "docker", "lint"]
steps:
- uses: actions/checkout@v4.1.5
- uses: actions/checkout@v4.1.6
- name: Set up Python
uses: actions/setup-python@v5.1.0
with:

View File

@@ -34,7 +34,7 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Generate cache-key
id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
@@ -66,7 +66,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -87,7 +87,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -108,7 +108,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -129,7 +129,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -150,7 +150,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -199,7 +199,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -229,7 +229,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -254,7 +254,7 @@ jobs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Find all YAML test files
id: set-matrix
run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
@@ -271,7 +271,7 @@ jobs:
file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -303,7 +303,7 @@ jobs:
file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -358,18 +358,26 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio
if: github.ref == 'refs/heads/dev'
uses: actions/cache@v4.0.2
with:
path: ~/.platformio
# yamllint disable-line rule:line-length
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
key: platformio-${{ matrix.pio_cache_key }}
- name: Cache platformio
if: github.ref != 'refs/heads/dev'
uses: actions/cache/restore@v4.0.2
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}
- name: Install clang-tidy
run: sudo apt-get install clang-tidy-14
@@ -402,7 +410,7 @@ jobs:
count: ${{ steps.list-components.outputs.count }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
with:
# Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
fetch-depth: 500
@@ -450,7 +458,7 @@ jobs:
run: sudo apt-get install libsodium-dev
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -476,7 +484,7 @@ jobs:
matrix: ${{ steps.split.outputs.components }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Split components into 20 groups
id: split
run: |
@@ -504,7 +512,7 @@ jobs:
run: sudo apt-get install libsodium-dev
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Restore Python
uses: ./.github/actions/restore-python
with:

View File

@@ -19,7 +19,7 @@ jobs:
tag: ${{ steps.tag.outputs.tag }}
branch_build: ${{ steps.tag.outputs.branch_build }}
steps:
- uses: actions/checkout@v4.1.5
- uses: actions/checkout@v4.1.6
- name: Get tag
id: tag
# yamllint disable rule:line-length
@@ -51,7 +51,7 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4.1.5
- uses: actions/checkout@v4.1.6
- name: Set up Python
uses: actions/setup-python@v5.1.0
with:
@@ -61,7 +61,9 @@ jobs:
ESPHOME_NO_VENV: 1
run: script/setup
- name: Build
run: python setup.py sdist bdist_wheel
run: |-
pip3 install build
python3 -m build
- name: Publish
uses: pypa/gh-action-pypi-publish@v1.8.14
@@ -81,7 +83,7 @@ jobs:
- linux/arm/v7
- linux/arm64
steps:
- uses: actions/checkout@v4.1.5
- uses: actions/checkout@v4.1.6
- name: Set up Python
uses: actions/setup-python@v5.1.0
with:
@@ -132,6 +134,19 @@ jobs:
suffix: lint
version: ${{ needs.init.outputs.tag }}
- name: Sanitize platform name
id: sanitize
run: |
echo "${{ matrix.platform }}" | sed 's|/|-|g' > /tmp/platform
echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
- name: Upload digests
uses: actions/upload-artifact@v4.3.3
with:
name: digests-${{ steps.sanitize.outputs.name }}
path: /tmp/digests
retention-days: 1
deploy-manifest:
name: Publish ESPHome ${{ matrix.image.title }} to ${{ matrix.registry }}
runs-on: ubuntu-latest
@@ -159,12 +174,15 @@ jobs:
- ghcr
- dockerhub
steps:
- uses: actions/checkout@v4.1.5
- uses: actions/checkout@v4.1.6
- name: Download digests
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.7
with:
name: digests-${{ matrix.image.target }}-${{ matrix.registry }}
pattern: digests-*
path: /tmp/digests
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.3.0
@@ -195,7 +213,7 @@ jobs:
done
- name: Create manifest list and push
working-directory: /tmp/digests
working-directory: /tmp/digests/${{ matrix.image.target }}/${{ matrix.registry }}
run: |
docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)

View File

@@ -13,10 +13,10 @@ jobs:
if: github.repository == 'esphome/esphome'
steps:
- name: Checkout
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Checkout Home Assistant
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
with:
repository: home-assistant/core
path: lib/home-assistant

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.5
uses: actions/checkout@v4.1.6
- name: Run yamllint
uses: frenck/action-yamllint@v1.5.0
with:

View File

@@ -40,3 +40,10 @@ repos:
hooks:
- id: clang-format
types_or: [c, c++]
- repo: local
hooks:
- id: pylint
name: pylint
entry: script/run-in-env.sh pylint
language: script
types: [python]

View File

@@ -6,7 +6,7 @@
# the integration's code owner is automatically notified.
# Core Code
setup.py @esphome/core
pyproject.toml @esphome/core
esphome/*.py @esphome/core
esphome/core/* @esphome/core
@@ -51,6 +51,8 @@ esphome/components/bang_bang/* @OttoWinter
esphome/components/bedjet/* @jhansche
esphome/components/bedjet/climate/* @jhansche
esphome/components/bedjet/fan/* @jhansche
esphome/components/bedjet/sensor/* @javawizard @jhansche
esphome/components/beken_spi_led_strip/* @Mat931
esphome/components/bh1750/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core
esphome/components/bk72xx/* @kuba2k2
@@ -109,7 +111,10 @@ esphome/components/ee895/* @Stock-M
esphome/components/ektf2232/touchscreen/* @jesserockz
esphome/components/emc2101/* @ellull
esphome/components/emmeti/* @E440QF
esphome/components/ens160/* @vincentscode
esphome/components/ens160/* @latonita
esphome/components/ens160_base/* @latonita @vincentscode
esphome/components/ens160_i2c/* @latonita
esphome/components/ens160_spi/* @latonita
esphome/components/ens210/* @itn3rd77
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @Rapsssito @jesserockz
@@ -135,6 +140,7 @@ esphome/components/fs3000/* @kahrendt
esphome/components/ft5x06/* @clydebarrow
esphome/components/ft63x6/* @gpambrozio
esphome/components/gcja5/* @gcormier
esphome/components/gdk101/* @Szewcson
esphome/components/globals/* @esphome/core
esphome/components/gp8403/* @jesserockz
esphome/components/gpio/* @esphome/core
@@ -146,6 +152,10 @@ esphome/components/grove_tb6612fng/* @max246
esphome/components/growatt_solar/* @leeuwte
esphome/components/gt911/* @clydebarrow @jesserockz
esphome/components/haier/* @paveldn
esphome/components/haier/binary_sensor/* @paveldn
esphome/components/haier/button/* @paveldn
esphome/components/haier/sensor/* @paveldn
esphome/components/haier/text_sensor/* @paveldn
esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann
@@ -174,6 +184,9 @@ esphome/components/improv_base/* @esphome/core
esphome/components/improv_serial/* @esphome/core
esphome/components/ina226/* @Sergio303 @latonita
esphome/components/ina260/* @mreditor97
esphome/components/ina2xx_base/* @latonita
esphome/components/ina2xx_i2c/* @latonita
esphome/components/ina2xx_spi/* @latonita
esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter
@@ -297,7 +310,7 @@ esphome/components/rp2040_pwm/* @jesserockz
esphome/components/rpi_dpi_rgb/* @clydebarrow
esphome/components/rtl87xx/* @kuba2k2
esphome/components/rtttl/* @glmnet
esphome/components/safe_mode/* @jsuanet @paulmonigatti
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
esphome/components/scd4x/* @martgras @sjtrny
esphome/components/script/* @esphome/core
esphome/components/sdm_meter/* @jesserockz @polyfaces

View File

@@ -110,7 +110,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
fi; \
pip3 install \
--break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
--break-system-packages --no-cache-dir -e /esphome
# Settings for dashboard
ENV USERNAME="" PASSWORD=""
@@ -160,7 +160,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
fi; \
pip3 install \
--break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
--break-system-packages --no-cache-dir -e /esphome
# Labels
LABEL \

View File

@@ -18,22 +18,23 @@ from esphome.const import (
CONF_BAUD_RATE,
CONF_BROKER,
CONF_DEASSERT_RTS_DTR,
CONF_DISABLED,
CONF_ESPHOME,
CONF_LOGGER,
CONF_MDNS,
CONF_MQTT,
CONF_NAME,
CONF_OTA,
CONF_MQTT,
CONF_MDNS,
CONF_DISABLED,
CONF_PASSWORD,
CONF_PORT,
CONF_ESPHOME,
CONF_PLATFORM,
CONF_PLATFORMIO_OPTIONS,
CONF_PORT,
CONF_SUBSTITUTIONS,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
SECRETS_FILES,
)
from esphome.core import CORE, EsphomeError, coroutine
@@ -65,7 +66,7 @@ def choose_prompt(options, purpose: str = None):
f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:'
)
for i, (desc, _) in enumerate(options):
safe_print(f" [{i+1}] {desc}")
safe_print(f" [{i + 1}] {desc}")
while True:
opt = input("(number): ")
@@ -330,15 +331,19 @@ def upload_program(config, args, host):
return 1 # Unknown target platform
if CONF_OTA not in config:
ota_conf = {}
for ota_item in config.get(CONF_OTA, []):
if ota_item[CONF_PLATFORM] == CONF_ESPHOME:
ota_conf = ota_item
break
if not ota_conf:
raise EsphomeError(
"Cannot upload Over the Air as the config does not include the ota: "
"component"
f"Cannot upload Over the Air as the {CONF_OTA} configuration is not present or does not include {CONF_PLATFORM}: {CONF_ESPHOME}"
)
from esphome import espota2
ota_conf = config[CONF_OTA]
remote_port = ota_conf[CONF_PORT]
password = ota_conf.get(CONF_PASSWORD, "")
@@ -346,7 +351,7 @@ def upload_program(config, args, host):
not is_ip_address(CORE.address) # pylint: disable=too-many-boolean-expressions
and (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED])
and CONF_MQTT in config
and (not args.device or args.device == "MQTT")
and (not args.device or args.device in ("MQTT", "OTA"))
):
from esphome import mqtt

View File

@@ -18,11 +18,23 @@ from esphome.components.esp32.const import (
CODEOWNERS = ["@esphome/core"]
adc_ns = cg.esphome_ns.namespace("adc")
"""
From the below patch versions (and 5.2+) ADC_ATTEN_DB_11 is deprecated and replaced with ADC_ATTEN_DB_12.
4.4.7
5.0.5
5.1.3
5.2+
"""
ATTENUATION_MODES = {
"0db": cg.global_ns.ADC_ATTEN_DB_0,
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
"6db": cg.global_ns.ADC_ATTEN_DB_6,
"11db": cg.global_ns.ADC_ATTEN_DB_11,
"11db": adc_ns.ADC_ATTEN_DB_12_COMPAT,
"12db": adc_ns.ADC_ATTEN_DB_12_COMPAT,
"auto": "auto",
}

View File

@@ -46,27 +46,27 @@ extern "C"
ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
pin_->setup();
this->pin_->setup();
#endif
#ifdef USE_ESP32
if (channel1_ != ADC1_CHANNEL_MAX) {
if (this->channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!autorange_) {
adc1_config_channel_atten(channel1_, attenuation_);
if (!this->autorange_) {
adc1_config_channel_atten(this->channel1_, this->attenuation_);
}
} else if (channel2_ != ADC2_CHANNEL_MAX) {
if (!autorange_) {
adc2_config_channel_atten(channel2_, attenuation_);
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
if (!this->autorange_) {
adc2_config_channel_atten(this->channel2_, this->attenuation_);
}
}
// load characteristics for each attenuation
for (int32_t i = 0; i <= ADC_ATTEN_DB_11; i++) {
auto adc_unit = channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
1100, // default vref
&cal_characteristics_[i]);
&this->cal_characteristics_[i]);
switch (cal_value) {
case ESP_ADC_CAL_VAL_EFUSE_VREF:
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
@@ -99,27 +99,27 @@ void ADCSensor::dump_config() {
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", pin_);
LOG_PIN(" Pin: ", this->pin_);
#endif
#endif // USE_ESP8266 || USE_LIBRETINY
#ifdef USE_ESP32
LOG_PIN(" Pin: ", pin_);
if (autorange_) {
ESP_LOGCONFIG(TAG, " Attenuation: auto");
LOG_PIN(" Pin: ", this->pin_);
if (this->autorange_) {
ESP_LOGCONFIG(TAG, " Attenuation: auto");
} else {
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
break;
case ADC_ATTEN_DB_2_5:
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
break;
case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
break;
case ADC_ATTEN_DB_11:
ESP_LOGCONFIG(TAG, " Attenuation: 11db");
case ADC_ATTEN_DB_12_COMPAT:
ESP_LOGCONFIG(TAG, " Attenuation: 12db");
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
@@ -134,11 +134,11 @@ void ADCSensor::dump_config() {
#ifdef USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Pin: VCC");
#else
LOG_PIN(" Pin: ", pin_);
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
}
#endif // USE_RP2040
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
LOG_UPDATE_INTERVAL(this);
}
@@ -149,14 +149,24 @@ void ADCSensor::update() {
this->publish_state(value_v);
}
void ADCSensor::set_sample_count(uint8_t sample_count) {
if (sample_count != 0) {
this->sample_count_ = sample_count;
}
}
#ifdef USE_ESP8266
float ADCSensor::sample() {
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
#ifdef USE_ADC_SENSOR_VCC
int32_t raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
#else
int32_t raw = analogRead(this->pin_->get_pin()); // NOLINT
raw += analogRead(this->pin_->get_pin()); // NOLINT
#endif
if (output_raw_) {
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
if (this->output_raw_) {
return raw;
}
return raw / 1024.0f;
@@ -165,77 +175,81 @@ float ADCSensor::sample() {
#ifdef USE_ESP32
float ADCSensor::sample() {
if (!autorange_) {
int raw = -1;
if (channel1_ != ADC1_CHANNEL_MAX) {
raw = adc1_get_raw(channel1_);
} else if (channel2_ != ADC2_CHANNEL_MAX) {
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
if (!this->autorange_) {
uint32_t sum = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
int raw = -1;
if (this->channel1_ != ADC1_CHANNEL_MAX) {
raw = adc1_get_raw(this->channel1_);
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
}
if (raw == -1) {
return NAN;
}
sum += raw;
}
if (raw == -1) {
return NAN;
sum = (sum + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
if (this->output_raw_) {
return sum;
}
if (output_raw_) {
return raw;
}
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int32_t) attenuation_]);
uint32_t mv = esp_adc_cal_raw_to_voltage(sum, &this->cal_characteristics_[(int32_t) this->attenuation_]);
return mv / 1000.0f;
}
int raw11 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
int raw12 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
if (channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_11);
raw11 = adc1_get_raw(channel1_);
if (raw11 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel1_);
if (this->channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_12_COMPAT);
raw12 = adc1_get_raw(this->channel1_);
if (raw12 < ADC_MAX) {
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(this->channel1_);
if (raw6 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(channel1_);
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(this->channel1_);
if (raw2 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(channel1_);
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(this->channel1_);
}
}
}
} else if (channel2_ != ADC2_CHANNEL_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_11);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw11);
if (raw11 < ADC_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_12_COMPAT);
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12);
if (raw12 < ADC_MAX) {
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_6);
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
if (raw6 < ADC_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_2_5);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_2_5);
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
if (raw2 < ADC_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_0);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_0);
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
}
}
}
}
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) {
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw12 == -1) {
return NAN;
}
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_11]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
uint32_t c11 = std::min(raw11, ADC_HALF);
uint32_t c12 = std::min(raw12, ADC_HALF);
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
// max theoretical csum value is 4096*4 = 16384
uint32_t csum = c11 + c6 + c2 + c0;
uint32_t csum = c12 + c6 + c2 + c0;
// each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U);
}
#endif // USE_ESP32
@@ -246,8 +260,11 @@ float ADCSensor::sample() {
adc_set_temp_sensor_enabled(true);
delay(1);
adc_select_input(4);
int32_t raw = adc_read();
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
adc_set_temp_sensor_enabled(false);
if (this->output_raw_) {
return raw;
@@ -268,7 +285,11 @@ float ADCSensor::sample() {
adc_gpio_init(pin);
adc_select_input(pin - 26);
int32_t raw = adc_read();
uint32_t raw = 0;
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += adc_read();
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
#ifdef CYW43_USES_VSYS_PIN
if (pin == PICO_VSYS_PIN) {
@@ -276,7 +297,7 @@ float ADCSensor::sample() {
}
#endif // CYW43_USES_VSYS_PIN
if (output_raw_) {
if (this->output_raw_) {
return raw;
}
float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
@@ -287,10 +308,19 @@ float ADCSensor::sample() {
#ifdef USE_LIBRETINY
float ADCSensor::sample() {
if (output_raw_) {
return analogRead(this->pin_->get_pin()); // NOLINT
uint32_t raw = 0;
if (this->output_raw_) {
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += analogRead(this->pin_->get_pin()); // NOLINT
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
return raw;
}
return analogReadVoltage(this->pin_->get_pin()) / 1000.0f; // NOLINT
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT
}
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
return raw / 1000.0f;
}
#endif // USE_LIBRETINY

View File

@@ -1,33 +1,48 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/defines.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#ifdef USE_ESP32
#include "driver/adc.h"
#include <esp_adc_cal.h>
#include "driver/adc.h"
#endif
namespace esphome {
namespace adc {
#ifdef USE_ESP32
// clang-format off
#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 7)) || \
(ESP_IDF_VERSION_MAJOR == 5 && \
((ESP_IDF_VERSION_MINOR == 0 && ESP_IDF_VERSION_PATCH >= 5) || \
(ESP_IDF_VERSION_MINOR == 1 && ESP_IDF_VERSION_PATCH >= 3) || \
(ESP_IDF_VERSION_MINOR >= 2)) \
)
// clang-format on
static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_12;
#else
static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_11;
#endif
#endif // USE_ESP32
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
public:
#ifdef USE_ESP32
/// Set the attenuation for this pin. Only available on the ESP32.
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
void set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
void set_channel1(adc1_channel_t channel) {
channel1_ = channel;
channel2_ = ADC2_CHANNEL_MAX;
this->channel1_ = channel;
this->channel2_ = ADC2_CHANNEL_MAX;
}
void set_channel2(adc2_channel_t channel) {
channel2_ = channel;
channel1_ = ADC1_CHANNEL_MAX;
this->channel2_ = channel;
this->channel1_ = ADC1_CHANNEL_MAX;
}
void set_autorange(bool autorange) { autorange_ = autorange; }
void set_autorange(bool autorange) { this->autorange_ = autorange; }
#endif
/// Update ADC values
@@ -38,7 +53,8 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
/// `HARDWARE_LATE` setup priority
float get_setup_priority() const override;
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; }
void set_sample_count(uint8_t sample_count);
float sample() override;
#ifdef USE_ESP8266
@@ -46,12 +62,13 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
#endif
#ifdef USE_RP2040
void set_is_temperature() { is_temperature_ = true; }
void set_is_temperature() { this->is_temperature_ = true; }
#endif
protected:
InternalGPIOPin *pin_;
bool output_raw_{false};
uint8_t sample_count_{1};
#ifdef USE_RP2040
bool is_temperature_{false};

View File

@@ -1,3 +1,5 @@
import logging
import esphome.codegen as cg
import esphome.config_validation as cv
import esphome.final_validate as fv
@@ -19,16 +21,35 @@ from . import (
ATTENUATION_MODES,
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
adc_ns,
validate_adc_pin,
)
_LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["voltage_sampler"]
CONF_SAMPLES = "samples"
_attenuation = cv.enum(ATTENUATION_MODES, lower=True)
def validate_config(config):
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set")
if config.get(CONF_ATTENUATION, None) == "auto" and config.get(CONF_SAMPLES, 1) > 1:
raise cv.Invalid(
"Automatic attenuation cannot be used when multisampling is set"
)
if config.get(CONF_ATTENUATION) == "11db":
_LOGGER.warning(
"`attenuation: 11db` is deprecated, use `attenuation: 12db` instead"
)
# Alter value here so `config` command prints the recommended change
config[CONF_ATTENUATION] = _attenuation("12db")
return config
@@ -47,7 +68,6 @@ def final_validate_config(config):
return config
adc_ns = cg.esphome_ns.namespace("adc")
ADCSensor = adc_ns.class_(
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
@@ -65,8 +85,9 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_PIN): validate_adc_pin,
cv.Optional(CONF_RAW, default=False): cv.boolean,
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
cv.only_on_esp32, _attenuation
),
cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255),
}
)
.extend(cv.polling_component_schema("60s")),
@@ -90,6 +111,7 @@ async def to_code(config):
cg.add(var.set_pin(pin))
cg.add(var.set_output_raw(config[CONF_RAW]))
cg.add(var.set_sample_count(config[CONF_SAMPLES]))
if attenuation := config.get(CONF_ATTENUATION):
if attenuation == "auto":

View File

@@ -157,7 +157,7 @@ async def to_code(config):
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
)
for pix, a in pixels:
if transparent:
@@ -180,7 +180,7 @@ async def to_code(config):
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
)
for pix in pixels:
data[pos] = pix[0]
@@ -203,7 +203,7 @@ async def to_code(config):
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
)
for r, g, b, a in pixels:
if transparent:
@@ -232,7 +232,7 @@ async def to_code(config):
pixels = list(frame.getdata())
if len(pixels) != height * width:
raise core.EsphomeError(
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
)
for r, g, b, a in pixels:
R = r >> 3

View File

@@ -1147,6 +1147,9 @@ message MediaPlayerCommandRequest {
bool has_media_url = 6;
string media_url = 7;
bool has_announcement = 8;
bool announcement = 9;
}
// ==================== BLUETOOTH ====================

View File

@@ -1002,7 +1002,11 @@ bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_pla
MediaPlayerStateResponse resp{};
resp.key = media_player->get_object_id_hash();
resp.state = static_cast<enums::MediaPlayerState>(media_player->state);
media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
? media_player::MEDIA_PLAYER_STATE_PLAYING
: media_player->state;
resp.state = static_cast<enums::MediaPlayerState>(report_state);
resp.volume = media_player->volume;
resp.muted = media_player->is_muted();
return this->send_media_player_state_response(resp);
@@ -1038,6 +1042,9 @@ void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
if (msg.has_media_url) {
call.set_media_url(msg.media_url);
}
if (msg.has_announcement) {
call.set_announcement(msg.announcement);
}
call.perform();
}
#endif

View File

@@ -5253,6 +5253,14 @@ bool MediaPlayerCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt val
this->has_media_url = value.as_bool();
return true;
}
case 8: {
this->has_announcement = value.as_bool();
return true;
}
case 9: {
this->announcement = value.as_bool();
return true;
}
default:
return false;
}
@@ -5289,6 +5297,8 @@ void MediaPlayerCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(5, this->volume);
buffer.encode_bool(6, this->has_media_url);
buffer.encode_string(7, this->media_url);
buffer.encode_bool(8, this->has_announcement);
buffer.encode_bool(9, this->announcement);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void MediaPlayerCommandRequest::dump_to(std::string &out) const {
@@ -5323,6 +5333,14 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
out.append(" media_url: ");
out.append("'").append(this->media_url).append("'");
out.append("\n");
out.append(" has_announcement: ");
out.append(YESNO(this->has_announcement));
out.append("\n");
out.append(" announcement: ");
out.append(YESNO(this->announcement));
out.append("\n");
out.append("}");
}
#endif

View File

@@ -1298,6 +1298,8 @@ class MediaPlayerCommandRequest : public ProtoMessage {
float volume{0.0f};
bool has_media_url{false};
std::string media_url{};
bool has_announcement{false};
bool announcement{false};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;

View File

@@ -31,7 +31,7 @@ CONFIG_SCHEMA = (
BEDJET_CLIENT_SCHEMA = cv.Schema(
{
cv.Required(CONF_BEDJET_ID): cv.use_id(BedJetHub),
cv.GenerateID(CONF_BEDJET_ID): cv.use_id(BedJetHub),
}
)

View File

@@ -157,5 +157,11 @@ bool BedjetCodec::compare(const uint8_t *data, uint16_t length) {
return explicit_fields_changed;
}
/// Converts a BedJet temp step into degrees Celsius.
float bedjet_temp_to_c(uint8_t temp) {
// BedJet temp is "C*2"; to get C, divide by 2.
return temp / 2.0f;
}
} // namespace bedjet
} // namespace esphome

View File

@@ -187,5 +187,8 @@ class BedjetCodec {
BedjetStatusPacket buf_;
};
/// Converts a BedJet temp step into degrees Celsius.
float bedjet_temp_to_c(uint8_t temp);
} // namespace bedjet
} // namespace esphome

View File

@@ -40,6 +40,14 @@ enum BedjetHeatMode {
HEAT_MODE_EXTENDED,
};
// Which temperature to use as the climate entity's current temperature reading
enum BedjetTemperatureSource {
// Use the temperature of the air the BedJet is putting out
TEMPERATURE_SOURCE_OUTLET,
// Use the ambient temperature of the room the BedJet is in
TEMPERATURE_SOURCE_AMBIENT
};
enum BedjetButton : uint8_t {
/// Turn BedJet off
BTN_OFF = 0x1,

View File

@@ -7,6 +7,7 @@ from esphome.const import (
CONF_HEAT_MODE,
CONF_ID,
CONF_RECEIVE_TIMEOUT,
CONF_TEMPERATURE_SOURCE,
CONF_TIME_ID,
)
from .. import (
@@ -21,10 +22,15 @@ DEPENDENCIES = ["bedjet"]
BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent)
BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode")
BedjetTemperatureSource = bedjet_ns.enum("BedjetTemperatureSource")
BEDJET_HEAT_MODES = {
"heat": BedjetHeatMode.HEAT_MODE_HEAT,
"extended": BedjetHeatMode.HEAT_MODE_EXTENDED,
}
BEDJET_TEMPERATURE_SOURCES = {
"outlet": BedjetTemperatureSource.TEMPERATURE_SOURCE_OUTLET,
"ambient": BedjetTemperatureSource.TEMPERATURE_SOURCE_AMBIENT,
}
CONFIG_SCHEMA = (
climate.CLIMATE_SCHEMA.extend(
@@ -33,6 +39,9 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_HEAT_MODE, default="heat"): cv.enum(
BEDJET_HEAT_MODES, lower=True
),
cv.Optional(CONF_TEMPERATURE_SOURCE, default="ambient"): cv.enum(
BEDJET_TEMPERATURE_SOURCES, lower=True
),
}
)
.extend(cv.polling_component_schema("60s"))
@@ -63,3 +72,4 @@ async def to_code(config):
await register_bedjet_child(var, config)
cg.add(var.set_heating_mode(config[CONF_HEAT_MODE]))
cg.add(var.set_temperature_source(config[CONF_TEMPERATURE_SOURCE]))

View File

@@ -8,12 +8,6 @@ namespace bedjet {
using namespace esphome::climate;
/// Converts a BedJet temp step into degrees Celsius.
float bedjet_temp_to_c(const uint8_t temp) {
// BedJet temp is "C*2"; to get C, divide by 2.
return temp / 2.0f;
}
static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) {
if (fan_step < BEDJET_FAN_SPEED_COUNT)
return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step];
@@ -236,9 +230,14 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
if (converted_temp > 0)
this->target_temperature = converted_temp;
converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
if (converted_temp > 0)
if (this->temperature_source_ == TEMPERATURE_SOURCE_OUTLET) {
converted_temp = bedjet_temp_to_c(data->actual_temp_step);
} else {
converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
}
if (converted_temp > 0) {
this->current_temperature = converted_temp;
}
const auto *fan_mode_name = bedjet_fan_step_to_fan_mode(data->fan_step);
if (fan_mode_name != nullptr) {

View File

@@ -28,6 +28,8 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli
/** Sets the default strategy to use for climate::CLIMATE_MODE_HEAT. */
void set_heating_mode(BedjetHeatMode mode) { this->heating_mode_ = mode; }
/** Sets the temperature source to use for the climate entity's current temperature */
void set_temperature_source(BedjetTemperatureSource source) { this->temperature_source_ = source; }
climate::ClimateTraits traits() override {
auto traits = climate::ClimateTraits();
@@ -74,6 +76,7 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli
void control(const climate::ClimateCall &call) override;
BedjetHeatMode heating_mode_ = HEAT_MODE_HEAT;
BedjetTemperatureSource temperature_source_ = TEMPERATURE_SOURCE_AMBIENT;
void reset_state_();
bool update_status_();

View File

@@ -0,0 +1,55 @@
import logging
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_ID,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
)
from .. import (
BEDJET_CLIENT_SCHEMA,
bedjet_ns,
register_bedjet_child,
)
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@jhansche", "@javawizard"]
DEPENDENCIES = ["bedjet"]
CONF_OUTLET_TEMPERATURE = "outlet_temperature"
CONF_AMBIENT_TEMPERATURE = "ambient_temperature"
BedjetSensor = bedjet_ns.class_("BedjetSensor", cg.Component)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(BedjetSensor),
cv.Optional(CONF_OUTLET_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_AMBIENT_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
).extend(BEDJET_CLIENT_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await register_bedjet_child(var, config)
if outlet_temperature_sensor := config.get(CONF_OUTLET_TEMPERATURE):
sensor_var = await sensor.new_sensor(outlet_temperature_sensor)
cg.add(var.set_outlet_temperature_sensor(sensor_var))
if ambient_temperature_sensor := config.get(CONF_AMBIENT_TEMPERATURE):
sensor_var = await sensor.new_sensor(ambient_temperature_sensor)
cg.add(var.set_ambient_temperature_sensor(sensor_var))

View File

@@ -0,0 +1,34 @@
#include "bedjet_sensor.h"
#include "esphome/core/log.h"
namespace esphome {
namespace bedjet {
std::string BedjetSensor::describe() { return "BedJet Sensor"; }
void BedjetSensor::dump_config() {
ESP_LOGCONFIG(TAG, "BedJet Sensor:");
LOG_SENSOR(" ", "Outlet Temperature", this->outlet_temperature_sensor_);
LOG_SENSOR(" ", "Ambient Temperature", this->ambient_temperature_sensor_);
}
void BedjetSensor::on_bedjet_state(bool is_ready) {}
void BedjetSensor::on_status(const BedjetStatusPacket *data) {
if (this->outlet_temperature_sensor_ != nullptr) {
float converted_temp = bedjet_temp_to_c(data->actual_temp_step);
if (converted_temp > 0) {
this->outlet_temperature_sensor_->publish_state(converted_temp);
}
}
if (this->ambient_temperature_sensor_ != nullptr) {
float converted_temp = bedjet_temp_to_c(data->ambient_temp_step);
if (converted_temp > 0) {
this->ambient_temperature_sensor_->publish_state(converted_temp);
}
}
}
} // namespace bedjet
} // namespace esphome

View File

@@ -0,0 +1,32 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/bedjet/bedjet_child.h"
#include "esphome/components/bedjet/bedjet_codec.h"
namespace esphome {
namespace bedjet {
class BedjetSensor : public BedJetClient, public Component {
public:
void dump_config() override;
void on_status(const BedjetStatusPacket *data) override;
void on_bedjet_state(bool is_ready) override;
std::string describe() override;
void set_outlet_temperature_sensor(sensor::Sensor *outlet_temperature_sensor) {
this->outlet_temperature_sensor_ = outlet_temperature_sensor;
}
void set_ambient_temperature_sensor(sensor::Sensor *ambient_temperature_sensor) {
this->ambient_temperature_sensor_ = ambient_temperature_sensor;
}
protected:
sensor::Sensor *outlet_temperature_sensor_{nullptr};
sensor::Sensor *ambient_temperature_sensor_{nullptr};
};
} // namespace bedjet
} // namespace esphome

View File

@@ -0,0 +1,384 @@
#include "led_strip.h"
#ifdef USE_BK72XX
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
extern "C" {
#include "rtos_pub.h"
#include "spi.h"
#include "arm_arch.h"
#include "general_dma_pub.h"
#include "gpio_pub.h"
#include "icu_pub.h"
#undef SPI_DAT
#undef SPI_BASE
};
static const uint32_t SPI_TX_DMA_CHANNEL = GDMA_CHANNEL_3;
// TODO: Check if SPI_PERI_CLK_DCO depends on the chip variant
static const uint32_t SPI_PERI_CLK_26M = 26000000;
static const uint32_t SPI_PERI_CLK_DCO = 120000000;
static const uint32_t SPI_BASE = 0x00802700;
static const uint32_t SPI_DAT = SPI_BASE + 3 * 4;
static const uint32_t SPI_CONFIG = SPI_BASE + 1 * 4;
static const uint32_t SPI_TX_EN = 1 << 0;
static const uint32_t CTRL_NSSMD_3 = 1 << 17;
static const uint32_t SPI_TX_FINISH_EN = 1 << 2;
static const uint32_t SPI_RX_FINISH_EN = 1 << 3;
namespace esphome {
namespace beken_spi_led_strip {
static const char *const TAG = "beken_spi_led_strip";
struct spi_data_t {
SemaphoreHandle_t dma_tx_semaphore;
volatile bool tx_in_progress;
bool first_run;
};
static spi_data_t *spi_data = nullptr;
static void set_spi_ctrl_register(unsigned long bit, bool val) {
uint32_t value = REG_READ(SPI_CTRL);
if (val == 0) {
value &= ~bit;
} else if (val == 1) {
value |= bit;
}
REG_WRITE(SPI_CTRL, value);
}
static void set_spi_config_register(unsigned long bit, bool val) {
uint32_t value = REG_READ(SPI_CONFIG);
if (val == 0) {
value &= ~bit;
} else if (val == 1) {
value |= bit;
}
REG_WRITE(SPI_CONFIG, value);
}
void spi_dma_tx_enable(bool enable) {
GDMA_CFG_ST en_cfg;
set_spi_config_register(SPI_TX_EN, enable ? 1 : 0);
en_cfg.channel = SPI_TX_DMA_CHANNEL;
en_cfg.param = enable ? 1 : 0;
sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_DMA_ENABLE, &en_cfg);
}
static void spi_set_clock(uint32_t max_hz) {
int source_clk = 0;
int spi_clk = 0;
int div = 0;
uint32_t param;
if (max_hz > 4333000) {
if (max_hz > 30000000) {
spi_clk = 30000000;
} else {
spi_clk = max_hz;
}
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_DOWN, &param);
source_clk = SPI_PERI_CLK_DCO;
param = PCLK_POSI_SPI;
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_DCO, &param);
param = PWD_SPI_CLK_BIT;
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, &param);
} else {
spi_clk = max_hz;
#if CFG_XTAL_FREQUENCE
source_clk = CFG_XTAL_FREQUENCE;
#else
source_clk = SPI_PERI_CLK_26M;
#endif
param = PCLK_POSI_SPI;
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, &param);
}
div = ((source_clk >> 1) / spi_clk);
if (div < 2) {
div = 2;
} else if (div >= 255) {
div = 255;
}
param = REG_READ(SPI_CTRL);
param &= ~(SPI_CKR_MASK << SPI_CKR_POSI);
param |= (div << SPI_CKR_POSI);
REG_WRITE(SPI_CTRL, param);
ESP_LOGD(TAG, "target frequency: %d, actual frequency: %d", max_hz, source_clk / 2 / div);
}
void spi_dma_tx_finish_callback(unsigned int param) {
spi_data->tx_in_progress = false;
xSemaphoreGive(spi_data->dma_tx_semaphore);
spi_dma_tx_enable(0);
}
void BekenSPILEDStripLightOutput::setup() {
ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip...");
size_t buffer_size = this->get_buffer_size_();
size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->buf_ = allocator.allocate(buffer_size);
if (this->buf_ == nullptr) {
ESP_LOGE(TAG, "Cannot allocate LED buffer!");
this->mark_failed();
return;
}
this->effect_data_ = allocator.allocate(this->num_leds_);
if (this->effect_data_ == nullptr) {
ESP_LOGE(TAG, "Cannot allocate effect data!");
this->mark_failed();
return;
}
this->dma_buf_ = allocator.allocate(dma_buffer_size);
if (this->dma_buf_ == nullptr) {
ESP_LOGE(TAG, "Cannot allocate DMA buffer!");
this->mark_failed();
return;
}
memset(this->buf_, 0, buffer_size);
memset(this->effect_data_, 0, this->num_leds_);
memset(this->dma_buf_, 0, dma_buffer_size);
uint32_t value = PCLK_POSI_SPI;
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, &value);
value = PWD_SPI_CLK_BIT;
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, &value);
if (spi_data != nullptr) {
ESP_LOGE(TAG, "SPI device already initialized!");
this->mark_failed();
return;
}
spi_data = (spi_data_t *) calloc(1, sizeof(spi_data_t));
if (spi_data == nullptr) {
ESP_LOGE(TAG, "Cannot allocate spi_data!");
this->mark_failed();
return;
}
spi_data->dma_tx_semaphore = xSemaphoreCreateBinary();
if (spi_data->dma_tx_semaphore == nullptr) {
ESP_LOGE(TAG, "TX Semaphore init faild!");
this->mark_failed();
return;
}
spi_data->first_run = true;
set_spi_ctrl_register(MSTEN, 0);
set_spi_ctrl_register(BIT_WDTH, 0);
spi_set_clock(this->spi_frequency_);
set_spi_ctrl_register(CKPOL, 0);
set_spi_ctrl_register(CKPHA, 0);
set_spi_ctrl_register(MSTEN, 1);
set_spi_ctrl_register(SPIEN, 1);
set_spi_ctrl_register(TXINT_EN, 0);
set_spi_ctrl_register(RXINT_EN, 0);
set_spi_config_register(SPI_TX_FINISH_EN, 1);
set_spi_config_register(SPI_RX_FINISH_EN, 1);
set_spi_ctrl_register(RXOVR_EN, 0);
set_spi_ctrl_register(TXOVR_EN, 0);
value = REG_READ(SPI_CTRL);
value &= ~CTRL_NSSMD_3;
value |= (1 << 17);
REG_WRITE(SPI_CTRL, value);
value = GFUNC_MODE_SPI_DMA;
sddev_control(GPIO_DEV_NAME, CMD_GPIO_ENABLE_SECOND, &value);
set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, 0);
GDMA_CFG_ST en_cfg;
GDMACFG_TPYES_ST init_cfg;
memset(&init_cfg, 0, sizeof(GDMACFG_TPYES_ST));
init_cfg.dstdat_width = 8;
init_cfg.srcdat_width = 32;
init_cfg.dstptr_incr = 0;
init_cfg.srcptr_incr = 1;
init_cfg.src_start_addr = this->dma_buf_;
init_cfg.dst_start_addr = (void *) SPI_DAT; // SPI_DMA_REG4_TXFIFO
init_cfg.channel = SPI_TX_DMA_CHANNEL;
init_cfg.prio = 0; // 10
init_cfg.u.type4.src_loop_start_addr = this->dma_buf_;
init_cfg.u.type4.src_loop_end_addr = this->dma_buf_ + dma_buffer_size;
init_cfg.half_fin_handler = nullptr;
init_cfg.fin_handler = spi_dma_tx_finish_callback;
init_cfg.src_module = GDMA_X_SRC_DTCM_RD_REQ;
init_cfg.dst_module = GDMA_X_DST_GSPI_TX_REQ; // GDMA_X_DST_HSSPI_TX_REQ
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_TYPE4, (void *) &init_cfg);
en_cfg.channel = SPI_TX_DMA_CHANNEL;
en_cfg.param = dma_buffer_size;
sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_TRANS_LENGTH, (void *) &en_cfg);
en_cfg.channel = SPI_TX_DMA_CHANNEL;
en_cfg.param = 0;
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_WORK_MODE, (void *) &en_cfg);
en_cfg.channel = SPI_TX_DMA_CHANNEL;
en_cfg.param = 0;
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_SRCADDR_LOOP, &en_cfg);
spi_dma_tx_enable(0);
value = REG_READ(SPI_CONFIG);
value &= ~(0xFFF << 8);
value |= ((dma_buffer_size & 0xFFF) << 8);
REG_WRITE(SPI_CONFIG, value);
}
void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency) {
this->bit0_ = bit0;
this->bit1_ = bit1;
this->spi_frequency_ = spi_frequency;
}
void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
// protect from refreshing too often
uint32_t now = micros();
if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
// try again next loop iteration, so that this change won't get lost
this->schedule_show();
return;
}
this->last_refresh_ = now;
this->mark_shown_();
ESP_LOGVV(TAG, "Writing RGB values to bus...");
if (spi_data == nullptr) {
ESP_LOGE(TAG, "SPI not initialized");
this->status_set_warning();
return;
}
if (!spi_data->first_run && !xSemaphoreTake(spi_data->dma_tx_semaphore, 10 / portTICK_PERIOD_MS)) {
ESP_LOGE(TAG, "Timed out waiting for semaphore");
return;
}
if (spi_data->tx_in_progress) {
ESP_LOGE(TAG, "tx_in_progress is set");
this->status_set_warning();
return;
}
spi_data->tx_in_progress = true;
size_t buffer_size = this->get_buffer_size_();
size_t size = 0;
uint8_t *psrc = this->buf_;
uint8_t *pdest = this->dma_buf_ + 64;
// The 64 byte padding is a workaround for a SPI DMA bug where the
// output doesn't exactly start at the beginning of dma_buf_
while (size < buffer_size) {
uint8_t b = *psrc;
for (int i = 0; i < 8; i++) {
*pdest++ = b & (1 << (7 - i)) ? this->bit1_ : this->bit0_;
}
size++;
psrc++;
}
spi_data->first_run = false;
spi_dma_tx_enable(1);
this->status_clear_warning();
}
light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index) const {
int32_t r = 0, g = 0, b = 0;
switch (this->rgb_order_) {
case ORDER_RGB:
r = 0;
g = 1;
b = 2;
break;
case ORDER_RBG:
r = 0;
g = 2;
b = 1;
break;
case ORDER_GRB:
r = 1;
g = 0;
b = 2;
break;
case ORDER_GBR:
r = 2;
g = 0;
b = 1;
break;
case ORDER_BGR:
r = 2;
g = 1;
b = 0;
break;
case ORDER_BRG:
r = 1;
g = 2;
b = 0;
break;
}
uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
uint8_t white = this->is_wrgb_ ? 0 : 3;
return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
this->buf_ + (index * multiplier) + g + this->is_wrgb_,
this->buf_ + (index * multiplier) + b + this->is_wrgb_,
this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
&this->effect_data_[index],
&this->correction_};
}
void BekenSPILEDStripLightOutput::dump_config() {
ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:");
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
const char *rgb_order;
switch (this->rgb_order_) {
case ORDER_RGB:
rgb_order = "RGB";
break;
case ORDER_RBG:
rgb_order = "RBG";
break;
case ORDER_GRB:
rgb_order = "GRB";
break;
case ORDER_GBR:
rgb_order = "GBR";
break;
case ORDER_BGR:
rgb_order = "BGR";
break;
case ORDER_BRG:
rgb_order = "BRG";
break;
default:
rgb_order = "UNKNOWN";
break;
}
ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_);
}
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
} // namespace beken_spi_led_strip
} // namespace esphome
#endif // USE_BK72XX

View File

@@ -0,0 +1,85 @@
#pragma once
#ifdef USE_BK72XX
#include "esphome/components/light/addressable_light.h"
#include "esphome/components/light/light_output.h"
#include "esphome/core/color.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace beken_spi_led_strip {
enum RGBOrder : uint8_t {
ORDER_RGB,
ORDER_RBG,
ORDER_GRB,
ORDER_GBR,
ORDER_BGR,
ORDER_BRG,
};
class BekenSPILEDStripLightOutput : public light::AddressableLight {
public:
void setup() override;
void write_state(light::LightState *state) override;
float get_setup_priority() const override;
int32_t size() const override { return this->num_leds_; }
light::LightTraits get_traits() override {
auto traits = light::LightTraits();
if (this->is_rgbw_ || this->is_wrgb_) {
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
} else {
traits.set_supported_color_modes({light::ColorMode::RGB});
}
return traits;
}
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
void set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency);
void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
void clear_effect_data() override {
for (int i = 0; i < this->size(); i++)
this->effect_data_[i] = 0;
}
void dump_config() override;
protected:
light::ESPColorView get_view_internal(int32_t index) const override;
size_t get_buffer_size_() const { return this->num_leds_ * (this->is_rgbw_ || this->is_wrgb_ ? 4 : 3); }
uint8_t *buf_{nullptr};
uint8_t *effect_data_{nullptr};
uint8_t *dma_buf_{nullptr};
uint8_t pin_;
uint16_t num_leds_;
bool is_rgbw_;
bool is_wrgb_;
uint32_t spi_frequency_{6666666};
uint8_t bit0_{0xE0};
uint8_t bit1_{0xFC};
RGBOrder rgb_order_;
uint32_t last_refresh_{0};
optional<uint32_t> max_refresh_rate_{};
};
} // namespace beken_spi_led_strip
} // namespace esphome
#endif // USE_BK72XX

View File

@@ -0,0 +1,134 @@
from dataclasses import dataclass
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import libretiny, light
from esphome.const import (
CONF_CHIPSET,
CONF_IS_RGBW,
CONF_MAX_REFRESH_RATE,
CONF_NUM_LEDS,
CONF_OUTPUT_ID,
CONF_PIN,
CONF_RGB_ORDER,
)
CODEOWNERS = ["@Mat931"]
DEPENDENCIES = ["libretiny"]
beken_spi_led_strip_ns = cg.esphome_ns.namespace("beken_spi_led_strip")
BekenSPILEDStripLightOutput = beken_spi_led_strip_ns.class_(
"BekenSPILEDStripLightOutput", light.AddressableLight
)
RGBOrder = beken_spi_led_strip_ns.enum("RGBOrder")
RGB_ORDERS = {
"RGB": RGBOrder.ORDER_RGB,
"RBG": RGBOrder.ORDER_RBG,
"GRB": RGBOrder.ORDER_GRB,
"GBR": RGBOrder.ORDER_GBR,
"BGR": RGBOrder.ORDER_BGR,
"BRG": RGBOrder.ORDER_BRG,
}
@dataclass
class LEDStripTimings:
bit0: int
bit1: int
spi_frequency: int
CHIPSETS = {
"WS2812": LEDStripTimings(
0b11100000, 0b11111100, 6666666
), # Clock divider: 9, Bit time: 1350ns
"SK6812": LEDStripTimings(
0b11000000, 0b11111000, 7500000
), # Clock divider: 8, Bit time: 1200ns
"APA106": LEDStripTimings(
0b11000000, 0b11111110, 5454545
), # Clock divider: 11, Bit time: 1650ns
"SM16703": LEDStripTimings(
0b11000000, 0b11111110, 7500000
), # Clock divider: 8, Bit time: 1200ns
}
CONF_IS_WRGB = "is_wrgb"
SUPPORTED_PINS = {
libretiny.const.FAMILY_BK7231N: [16],
libretiny.const.FAMILY_BK7231T: [16],
libretiny.const.FAMILY_BK7251: [16],
}
def _validate_pin(value):
family = libretiny.get_libretiny_family()
if family not in SUPPORTED_PINS:
raise cv.Invalid(f"Chip family {family} is not supported.")
if value not in SUPPORTED_PINS[family]:
supported_pin_info = ", ".join(f"{x}" for x in SUPPORTED_PINS[family])
raise cv.Invalid(
f"Pin {value} is not supported on the {family}. Supported pins: {supported_pin_info}"
)
return value
def _validate_num_leds(value):
max_num_leds = 165 # 170
if value[CONF_IS_RGBW] or value[CONF_IS_WRGB]:
max_num_leds = 123 # 127
if value[CONF_NUM_LEDS] > max_num_leds:
raise cv.Invalid(
f"The maximum number of LEDs for this configuration is {max_num_leds}.",
path=CONF_NUM_LEDS,
)
return value
CONFIG_SCHEMA = cv.All(
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BekenSPILEDStripLightOutput),
cv.Required(CONF_PIN): cv.All(
pins.internal_gpio_output_pin_number, _validate_pin
),
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
cv.Optional(CONF_IS_WRGB, default=False): cv.boolean,
}
),
_validate_num_leds,
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
await light.register_light(var, config)
await cg.register_component(var, config)
cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
cg.add(var.set_pin(config[CONF_PIN]))
if CONF_MAX_REFRESH_RATE in config:
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
chipset = CHIPSETS[config[CONF_CHIPSET]]
cg.add(
var.set_led_params(
chipset.bit0,
chipset.bit1,
chipset.spi_frequency,
)
)
cg.add(var.set_rgb_order(config[CONF_RGB_ORDER]))
cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))

View File

@@ -98,6 +98,11 @@ void binary_sensor::MultiClickTrigger::schedule_is_not_valid_(uint32_t max_lengt
this->schedule_cooldown_();
});
}
void binary_sensor::MultiClickTrigger::cancel() {
ESP_LOGV(TAG, "Multi Click: Sequence explicitly cancelled.");
this->is_valid_ = false;
this->schedule_cooldown_();
}
void binary_sensor::MultiClickTrigger::trigger_() {
ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!");
this->at_index_.reset();

View File

@@ -105,6 +105,8 @@ class MultiClickTrigger : public Trigger<>, public Component {
void set_invalid_cooldown(uint32_t invalid_cooldown) { this->invalid_cooldown_ = invalid_cooldown; }
void cancel();
protected:
void on_state_(bool state);
void schedule_cooldown_();

View File

@@ -6,16 +6,6 @@
#ifdef USE_ESP32
#ifdef USE_ARDUINO
#include "mbedtls/aes.h"
#include "mbedtls/base64.h"
#endif
#ifdef USE_ESP_IDF
#define MBEDTLS_AES_ALT
#include <aes_alt.h>
#endif
namespace esphome {
namespace ble_presence {
@@ -72,7 +62,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
}
break;
case MATCH_BY_IRK:
if (resolve_irk_(device.address_uint64(), this->irk_)) {
if (device.resolve_irk(this->irk_)) {
this->set_found_(true);
return true;
}
@@ -142,43 +132,6 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
bool check_ibeacon_minor_{false};
bool check_minimum_rssi_{false};
bool resolve_irk_(uint64_t addr64, const uint8_t *irk) {
uint8_t ecb_key[16];
uint8_t ecb_plaintext[16];
uint8_t ecb_ciphertext[16];
memcpy(&ecb_key, irk, 16);
memset(&ecb_plaintext, 0, 16);
ecb_plaintext[13] = (addr64 >> 40) & 0xff;
ecb_plaintext[14] = (addr64 >> 32) & 0xff;
ecb_plaintext[15] = (addr64 >> 24) & 0xff;
mbedtls_aes_context ctx = {0, 0, {0}};
mbedtls_aes_init(&ctx);
if (mbedtls_aes_setkey_enc(&ctx, ecb_key, 128) != 0) {
mbedtls_aes_free(&ctx);
return false;
}
if (mbedtls_aes_crypt_ecb(&ctx,
#ifdef USE_ARDUINO
MBEDTLS_AES_ENCRYPT,
#elif defined(USE_ESP_IDF)
ESP_AES_ENCRYPT,
#endif
ecb_plaintext, ecb_ciphertext) != 0) {
mbedtls_aes_free(&ctx);
return false;
}
mbedtls_aes_free(&ctx);
return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
}
bool found_{false};
uint32_t last_seen_{};
uint32_t timeout_{};

View File

@@ -15,6 +15,10 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
this->match_by_ = MATCH_BY_MAC_ADDRESS;
this->address_ = address;
}
void set_irk(uint8_t *irk) {
this->match_by_ = MATCH_BY_IRK;
this->irk_ = irk;
}
void set_service_uuid16(uint16_t uuid) {
this->match_by_ = MATCH_BY_SERVICE_UUID;
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid);
@@ -53,6 +57,13 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
return true;
}
break;
case MATCH_BY_IRK:
if (device.resolve_irk(this->irk_)) {
this->publish_state(device.get_rssi());
this->found_ = true;
return true;
}
break;
case MATCH_BY_SERVICE_UUID:
for (auto uuid : device.get_service_uuids()) {
if (this->uuid_ == uuid) {
@@ -91,12 +102,13 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
float get_setup_priority() const override { return setup_priority::DATA; }
protected:
enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_IRK, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
MatchType match_by_;
bool found_{false};
uint64_t address_;
uint8_t *irk_;
esp32_ble_tracker::ESPBTUUID uuid_;

View File

@@ -12,6 +12,8 @@ from esphome.const import (
UNIT_DECIBEL_MILLIWATT,
)
CONF_IRK = "irk"
DEPENDENCIES = ["esp32_ble_tracker"]
ble_rssi_ns = cg.esphome_ns.namespace("ble_rssi")
@@ -39,6 +41,7 @@ CONFIG_SCHEMA = cv.All(
.extend(
{
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_IRK): cv.uuid,
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t,
cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t,
@@ -47,7 +50,9 @@ CONFIG_SCHEMA = cv.All(
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA),
cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_IBEACON_UUID),
cv.has_exactly_one_key(
CONF_MAC_ADDRESS, CONF_IRK, CONF_SERVICE_UUID, CONF_IBEACON_UUID
),
_validate,
)
@@ -60,6 +65,10 @@ async def to_code(config):
if mac_address := config.get(CONF_MAC_ADDRESS):
cg.add(var.set_address(mac_address.as_hex))
if irk := config.get(CONF_IRK):
irk = esp32_ble_tracker.as_hex_array(str(irk))
cg.add(var.set_irk(irk))
if service_uuid := config.get(CONF_SERVICE_UUID):
if len(service_uuid) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(service_uuid)))

View File

@@ -14,15 +14,41 @@ CONF_HEX = "hex"
def hex_color(value):
if isinstance(value, int):
value = str(value)
if not isinstance(value, str):
raise cv.Invalid("Invalid value for hex color")
if len(value) != 6:
raise cv.Invalid("Color must have six digits")
raise cv.Invalid("Hex color must have six digits")
try:
return (int(value[0:2], 16), int(value[2:4], 16), int(value[4:6], 16))
return int(value[0:2], 16), int(value[2:4], 16), int(value[4:6], 16)
except ValueError as exc:
raise cv.Invalid("Color must be hexadecimal") from exc
CONFIG_SCHEMA = cv.Any(
components = {
CONF_RED,
CONF_RED_INT,
CONF_GREEN,
CONF_GREEN_INT,
CONF_BLUE,
CONF_BLUE_INT,
CONF_WHITE,
CONF_WHITE_INT,
}
def validate_color(config):
has_components = set(config) & components
has_hex = CONF_HEX in config
if has_hex and has_components:
raise cv.Invalid("Hex color value may not be combined with component values")
if not has_hex and not has_components:
raise cv.Invalid("Must provide at least one color option")
return config
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(ColorStruct),
@@ -34,14 +60,10 @@ CONFIG_SCHEMA = cv.Any(
cv.Exclusive(CONF_BLUE_INT, "blue"): cv.uint8_t,
cv.Exclusive(CONF_WHITE, "white"): cv.percentage,
cv.Exclusive(CONF_WHITE_INT, "white"): cv.uint8_t,
cv.Optional(CONF_HEX): hex_color,
}
).extend(cv.COMPONENT_SCHEMA),
cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(ColorStruct),
cv.Required(CONF_HEX): hex_color,
}
).extend(cv.COMPONENT_SCHEMA),
validate_color,
)

View File

@@ -15,6 +15,7 @@ void CST816Touchscreen::continue_setup_() {
}
switch (this->chip_id_) {
case CST820_CHIP_ID:
case CST826_CHIP_ID:
case CST716_CHIP_ID:
case CST816S_CHIP_ID:
case CST816D_CHIP_ID:
@@ -90,6 +91,9 @@ void CST816Touchscreen::dump_config() {
case CST820_CHIP_ID:
name = "CST820";
break;
case CST826_CHIP_ID:
name = "CST826";
break;
case CST816S_CHIP_ID:
name = "CST816S";
break;

View File

@@ -24,6 +24,7 @@ static const uint8_t REG_SLEEP = 0xE5;
static const uint8_t REG_IRQ_CTL = 0xFA;
static const uint8_t IRQ_EN_MOTION = 0x70;
static const uint8_t CST826_CHIP_ID = 0x11;
static const uint8_t CST820_CHIP_ID = 0xB7;
static const uint8_t CST816S_CHIP_ID = 0xB4;
static const uint8_t CST816D_CHIP_ID = 0xB6;

View File

@@ -156,7 +156,7 @@ async def new_datetime(config, *args):
return var
@coroutine_with_priority(40.0)
@coroutine_with_priority(100.0)
async def to_code(config):
cg.add_define("USE_DATETIME")
cg.add_global(datetime_ns.using)

View File

@@ -8,62 +8,16 @@
#include <cinttypes>
#include <climits>
#ifdef USE_ESP32
#include <esp_heap_caps.h>
#include <esp_system.h>
#include <esp_chip_info.h>
#if defined(USE_ESP32_VARIANT_ESP32)
#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 // USE_ESP32
#ifdef USE_ARDUINO
#ifdef USE_RP2040
#include <Arduino.h>
#elif defined(USE_ESP32) || defined(USE_ESP8266)
#include <Esp.h>
#endif
#endif
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
static uint32_t get_free_heap() {
#if defined(USE_ESP8266)
return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
#elif defined(USE_ESP32)
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#elif defined(USE_RP2040)
return rp2040.getFreeHeap();
#elif defined(USE_LIBRETINY)
return lt_heap_get_free();
#elif defined(USE_HOST)
return INT_MAX;
#endif
}
void DebugComponent::dump_config() {
#ifndef ESPHOME_LOG_HAS_DEBUG
return; // Can't log below if debug logging is disabled
#endif
std::string device_info;
std::string reset_reason;
device_info.reserve(256);
ESP_LOGCONFIG(TAG, "Debug component:");
#ifdef USE_TEXT_SENSOR
LOG_TEXT_SENSOR(" ", "Device info", this->device_info_);
@@ -76,305 +30,15 @@ void DebugComponent::dump_config() {
#endif // defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
#endif // USE_SENSOR
std::string device_info;
device_info.reserve(256);
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
device_info += ESPHOME_VERSION;
this->free_heap_ = get_free_heap();
this->free_heap_ = get_free_heap_();
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
#if defined(USE_ARDUINO) && (defined(USE_ESP32) || defined(USE_ESP8266))
const char *flash_mode;
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
case FM_QIO:
flash_mode = "QIO";
break;
case FM_QOUT:
flash_mode = "QOUT";
break;
case FM_DIO:
flash_mode = "DIO";
break;
case FM_DOUT:
flash_mode = "DOUT";
break;
#ifdef USE_ESP32
case FM_FAST_READ:
flash_mode = "FAST_READ";
break;
case FM_SLOW_READ:
flash_mode = "SLOW_READ";
break;
#endif
default:
flash_mode = "UNKNOWN";
}
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
ESP.getFlashChipSize() / 1024, // NOLINT
ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
device_info += flash_mode;
#endif // USE_ARDUINO && (USE_ESP32 || USE_ESP8266)
#ifdef USE_ESP32
esp_chip_info_t info;
esp_chip_info(&info);
const char *model;
#if defined(USE_ESP32_VARIANT_ESP32)
model = "ESP32";
#elif defined(USE_ESP32_VARIANT_ESP32C3)
model = "ESP32-C3";
#elif defined(USE_ESP32_VARIANT_ESP32C6)
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;
if (info.features & CHIP_FEATURE_EMB_FLASH) {
features += "EMB_FLASH,";
info.features &= ~CHIP_FEATURE_EMB_FLASH;
}
if (info.features & CHIP_FEATURE_WIFI_BGN) {
features += "WIFI_BGN,";
info.features &= ~CHIP_FEATURE_WIFI_BGN;
}
if (info.features & CHIP_FEATURE_BLE) {
features += "BLE,";
info.features &= ~CHIP_FEATURE_BLE;
}
if (info.features & CHIP_FEATURE_BT) {
features += "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)
features += "Other:" + format_hex(info.features);
ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
info.revision);
device_info += "|Chip: ";
device_info += model;
device_info += " Features:";
device_info += features;
device_info += " Cores:" + to_string(info.cores);
device_info += " Revision:" + to_string(info.revision);
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
device_info += "|ESP-IDF: ";
device_info += esp_get_idf_version();
std::string mac = get_mac_address_pretty();
ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
device_info += "|EFuse MAC: ";
device_info += mac;
switch (rtc_get_reset_reason(0)) {
case POWERON_RESET:
reset_reason = "Power On Reset";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
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";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case OWDT_RESET:
reset_reason = "Watch Dog Reset Digital Core";
break;
#endif
case DEEPSLEEP_RESET:
reset_reason = "Deep Sleep Reset Digital Core";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case SDIO_RESET:
reset_reason = "SLC Module Reset Digital Core";
break;
#endif
case TG0WDT_SYS_RESET:
reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
break;
case TG1WDT_SYS_RESET:
reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
break;
case RTCWDT_SYS_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core";
break;
#if !defined(USE_ESP32_VARIANT_ESP32C6)
case INTRUSION_RESET:
reset_reason = "Intrusion Reset CPU";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
case TGWDT_CPU_RESET:
reset_reason = "Timer Group Reset CPU";
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:
#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";
break;
case RTCWDT_CPU_RESET:
reset_reason = "RTC Watch Dog Reset CPU";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case EXT_CPU_RESET:
reset_reason = "External CPU Reset";
break;
#endif
case RTCWDT_BROWN_OUT_RESET:
reset_reason = "Voltage Unstable Reset";
break;
case RTCWDT_RTC_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
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:
reset_reason = "Unknown Reset Reason";
}
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
device_info += "|Reset: ";
device_info += reset_reason;
const char *wakeup_reason;
switch (rtc_get_wakeup_cause()) {
case NO_SLEEP:
wakeup_reason = "No Sleep";
break;
case EXT_EVENT0_TRIG:
wakeup_reason = "External Event 0";
break;
case EXT_EVENT1_TRIG:
wakeup_reason = "External Event 1";
break;
case GPIO_TRIG:
wakeup_reason = "GPIO";
break;
case TIMER_EXPIRE:
wakeup_reason = "Wakeup Timer";
break;
case SDIO_TRIG:
wakeup_reason = "SDIO";
break;
case MAC_TRIG:
wakeup_reason = "MAC";
break;
case UART0_TRIG:
wakeup_reason = "UART0";
break;
case UART1_TRIG:
wakeup_reason = "UART1";
break;
case TOUCH_TRIG:
wakeup_reason = "Touch";
break;
case SAR_TRIG:
wakeup_reason = "SAR";
break;
case BT_TRIG:
wakeup_reason = "BT";
break;
default:
wakeup_reason = "Unknown";
}
ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
device_info += "|Wakeup: ";
device_info += wakeup_reason;
#endif
#if defined(USE_ESP8266) && !defined(CLANG_TIDY)
ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
ESP_LOGD(TAG, "Reset Reason: %s", ESP.getResetReason().c_str());
ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
device_info += "|SDK: ";
device_info += ESP.getSdkVersion();
device_info += "|Core: ";
device_info += ESP.getCoreVersion().c_str();
device_info += "|Boot: ";
device_info += to_string(ESP.getBootVersion());
device_info += "|Mode: " + to_string(ESP.getBootMode());
device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
device_info += "|Reset: ";
device_info += ESP.getResetReason().c_str();
device_info += "|";
device_info += ESP.getResetInfo().c_str();
reset_reason = ESP.getResetReason().c_str();
#endif
#ifdef USE_RP2040
ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu());
device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
#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
get_device_info_(device_info);
#ifdef USE_TEXT_SENSOR
if (this->device_info_ != nullptr) {
@@ -383,14 +47,14 @@ void DebugComponent::dump_config() {
this->device_info_->publish_state(device_info);
}
if (this->reset_reason_ != nullptr) {
this->reset_reason_->publish_state(reset_reason);
this->reset_reason_->publish_state(get_reset_reason_());
}
#endif // USE_TEXT_SENSOR
}
void DebugComponent::loop() {
// log when free heap space has halved
uint32_t new_free_heap = get_free_heap();
uint32_t new_free_heap = get_free_heap_();
if (new_free_heap < this->free_heap_ / 2) {
this->free_heap_ = new_free_heap;
ESP_LOGD(TAG, "Free Heap Size: %" PRIu32 " bytes", this->free_heap_);
@@ -411,38 +75,16 @@ void DebugComponent::loop() {
void DebugComponent::update() {
#ifdef USE_SENSOR
if (this->free_sensor_ != nullptr) {
this->free_sensor_->publish_state(get_free_heap());
this->free_sensor_->publish_state(get_free_heap_());
}
if (this->block_sensor_ != nullptr) {
#if defined(USE_ESP8266)
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
#elif defined(USE_ESP32)
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
}
#if defined(USE_ESP8266) && USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
if (this->fragmentation_sensor_ != nullptr) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
}
#endif
if (this->loop_time_sensor_ != nullptr) {
this->loop_time_sensor_->publish_state(this->max_loop_time_);
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
update_platform_();
}
float DebugComponent::get_setup_priority() const { return setup_priority::LATE; }

View File

@@ -59,6 +59,11 @@ class DebugComponent : public PollingComponent {
text_sensor::TextSensor *device_info_{nullptr};
text_sensor::TextSensor *reset_reason_{nullptr};
#endif // USE_TEXT_SENSOR
std::string get_reset_reason_();
uint32_t get_free_heap_();
void get_device_info_(std::string &device_info);
void update_platform_();
};
} // namespace debug

View File

@@ -0,0 +1,287 @@
#include "debug_component.h"
#ifdef USE_ESP32
#include "esphome/core/log.h"
#include <esp_heap_caps.h>
#include <esp_system.h>
#include <esp_chip_info.h>
#if defined(USE_ESP32_VARIANT_ESP32)
#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
#ifdef USE_ARDUINO
#include <Esp.h>
#endif
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
std::string DebugComponent::get_reset_reason_() {
std::string reset_reason;
switch (rtc_get_reset_reason(0)) {
case POWERON_RESET:
reset_reason = "Power On Reset";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
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";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case OWDT_RESET:
reset_reason = "Watch Dog Reset Digital Core";
break;
#endif
case DEEPSLEEP_RESET:
reset_reason = "Deep Sleep Reset Digital Core";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case SDIO_RESET:
reset_reason = "SLC Module Reset Digital Core";
break;
#endif
case TG0WDT_SYS_RESET:
reset_reason = "Timer Group 0 Watch Dog Reset Digital Core";
break;
case TG1WDT_SYS_RESET:
reset_reason = "Timer Group 1 Watch Dog Reset Digital Core";
break;
case RTCWDT_SYS_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core";
break;
#if !defined(USE_ESP32_VARIANT_ESP32C6)
case INTRUSION_RESET:
reset_reason = "Intrusion Reset CPU";
break;
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
case TGWDT_CPU_RESET:
reset_reason = "Timer Group Reset CPU";
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:
#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";
break;
case RTCWDT_CPU_RESET:
reset_reason = "RTC Watch Dog Reset CPU";
break;
#if defined(USE_ESP32_VARIANT_ESP32)
case EXT_CPU_RESET:
reset_reason = "External CPU Reset";
break;
#endif
case RTCWDT_BROWN_OUT_RESET:
reset_reason = "Voltage Unstable Reset";
break;
case RTCWDT_RTC_RESET:
reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module";
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:
reset_reason = "Unknown Reset Reason";
}
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
return reset_reason;
}
uint32_t DebugComponent::get_free_heap_() { return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); }
void DebugComponent::get_device_info_(std::string &device_info) {
#if defined(USE_ARDUINO)
const char *flash_mode;
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
case FM_QIO:
flash_mode = "QIO";
break;
case FM_QOUT:
flash_mode = "QOUT";
break;
case FM_DIO:
flash_mode = "DIO";
break;
case FM_DOUT:
flash_mode = "DOUT";
break;
case FM_FAST_READ:
flash_mode = "FAST_READ";
break;
case FM_SLOW_READ:
flash_mode = "SLOW_READ";
break;
default:
flash_mode = "UNKNOWN";
}
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
ESP.getFlashChipSize() / 1024, // NOLINT
ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
device_info += flash_mode;
#endif
esp_chip_info_t info;
esp_chip_info(&info);
const char *model;
#if defined(USE_ESP32_VARIANT_ESP32)
model = "ESP32";
#elif defined(USE_ESP32_VARIANT_ESP32C3)
model = "ESP32-C3";
#elif defined(USE_ESP32_VARIANT_ESP32C6)
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;
if (info.features & CHIP_FEATURE_EMB_FLASH) {
features += "EMB_FLASH,";
info.features &= ~CHIP_FEATURE_EMB_FLASH;
}
if (info.features & CHIP_FEATURE_WIFI_BGN) {
features += "WIFI_BGN,";
info.features &= ~CHIP_FEATURE_WIFI_BGN;
}
if (info.features & CHIP_FEATURE_BLE) {
features += "BLE,";
info.features &= ~CHIP_FEATURE_BLE;
}
if (info.features & CHIP_FEATURE_BT) {
features += "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)
features += "Other:" + format_hex(info.features);
ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
info.revision);
device_info += "|Chip: ";
device_info += model;
device_info += " Features:";
device_info += features;
device_info += " Cores:" + to_string(info.cores);
device_info += " Revision:" + to_string(info.revision);
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
device_info += "|ESP-IDF: ";
device_info += esp_get_idf_version();
std::string mac = get_mac_address_pretty();
ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
device_info += "|EFuse MAC: ";
device_info += mac;
device_info += "|Reset: ";
device_info += get_reset_reason_();
const char *wakeup_reason;
switch (rtc_get_wakeup_cause()) {
case NO_SLEEP:
wakeup_reason = "No Sleep";
break;
case EXT_EVENT0_TRIG:
wakeup_reason = "External Event 0";
break;
case EXT_EVENT1_TRIG:
wakeup_reason = "External Event 1";
break;
case GPIO_TRIG:
wakeup_reason = "GPIO";
break;
case TIMER_EXPIRE:
wakeup_reason = "Wakeup Timer";
break;
case SDIO_TRIG:
wakeup_reason = "SDIO";
break;
case MAC_TRIG:
wakeup_reason = "MAC";
break;
case UART0_TRIG:
wakeup_reason = "UART0";
break;
case UART1_TRIG:
wakeup_reason = "UART1";
break;
case TOUCH_TRIG:
wakeup_reason = "Touch";
break;
case SAR_TRIG:
wakeup_reason = "SAR";
break;
case BT_TRIG:
wakeup_reason = "BT";
break;
default:
wakeup_reason = "Unknown";
}
ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
device_info += "|Wakeup: ";
device_info += wakeup_reason;
}
void DebugComponent::update_platform_() {
#ifdef USE_SENSOR
if (this->block_sensor_ != nullptr) {
this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
}
if (this->psram_sensor_ != nullptr) {
this->psram_sensor_->publish_state(heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
}
#endif
}
} // namespace debug
} // namespace esphome
#endif

View File

@@ -0,0 +1,94 @@
#include "debug_component.h"
#ifdef USE_ESP8266
#include "esphome/core/log.h"
#include <Esp.h>
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
std::string DebugComponent::get_reset_reason_() {
#if !defined(CLANG_TIDY)
return ESP.getResetReason().c_str();
#else
return "";
#endif
}
uint32_t DebugComponent::get_free_heap_() {
return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
}
void DebugComponent::get_device_info_(std::string &device_info) {
const char *flash_mode;
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
case FM_QIO:
flash_mode = "QIO";
break;
case FM_QOUT:
flash_mode = "QOUT";
break;
case FM_DIO:
flash_mode = "DIO";
break;
case FM_DOUT:
flash_mode = "DOUT";
break;
default:
flash_mode = "UNKNOWN";
}
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
ESP.getFlashChipSize() / 1024, // NOLINT
ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
device_info += flash_mode;
#if !defined(CLANG_TIDY)
auto reset_reason = get_reset_reason_();
ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
device_info += "|SDK: ";
device_info += ESP.getSdkVersion();
device_info += "|Core: ";
device_info += ESP.getCoreVersion().c_str();
device_info += "|Boot: ";
device_info += to_string(ESP.getBootVersion());
device_info += "|Mode: " + to_string(ESP.getBootMode());
device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
device_info += "|Reset: ";
device_info += reset_reason;
device_info += "|";
device_info += ESP.getResetInfo().c_str();
#endif
}
void DebugComponent::update_platform_() {
#ifdef USE_SENSOR
if (this->block_sensor_ != nullptr) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
}
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
if (this->fragmentation_sensor_ != nullptr) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
}
#endif
#endif
}
} // namespace debug
} // namespace esphome
#endif

View File

@@ -0,0 +1,18 @@
#include "debug_component.h"
#ifdef USE_HOST
#include <climits>
namespace esphome {
namespace debug {
std::string DebugComponent::get_reset_reason_() { return ""; }
uint32_t DebugComponent::get_free_heap_() { return INT_MAX; }
void DebugComponent::get_device_info_(std::string &device_info) {}
void DebugComponent::update_platform_() {}
} // namespace debug
} // namespace esphome
#endif

View File

@@ -0,0 +1,44 @@
#include "debug_component.h"
#ifdef USE_LIBRETINY
#include "esphome/core/log.h"
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
std::string DebugComponent::get_reset_reason_() { return lt_get_reboot_reason_name(lt_get_reboot_reason()); }
uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); }
void DebugComponent::get_device_info_(std::string &device_info) {
reset_reason = get_reset_reason_();
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", reset_reason.c_str());
device_info += "|Version: ";
device_info += LT_BANNER_STR + 10;
device_info += "|Reset Reason: ";
device_info += reset_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";
}
void DebugComponent::update_platform_() {
#ifdef USE_SENSOR
if (this->block_sensor_ != nullptr) {
this->block_sensor_->publish_state(lt_heap_get_max_alloc());
}
#endif
}
} // namespace debug
} // namespace esphome
#endif

View File

@@ -0,0 +1,23 @@
#include "debug_component.h"
#ifdef USE_RP2040
#include "esphome/core/log.h"
#include <Arduino.h>
namespace esphome {
namespace debug {
static const char *const TAG = "debug";
std::string DebugComponent::get_reset_reason_() { return ""; }
uint32_t DebugComponent::get_free_heap_() { return rp2040.getFreeHeap(); }
void DebugComponent::get_device_info_(std::string &device_info) {
ESP_LOGD(TAG, "CPU Frequency: %u", rp2040.f_cpu());
device_info += "CPU Frequency: " + to_string(rp2040.f_cpu());
}
void DebugComponent::update_platform_() {}
} // namespace debug
} // namespace esphome
#endif

View File

@@ -1,12 +1,7 @@
#include "deep_sleep_component.h"
#include <cinttypes>
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#ifdef USE_ESP8266
#include <Esp.h>
#endif
namespace esphome {
namespace deep_sleep {
@@ -14,25 +9,6 @@ static const char *const TAG = "deep_sleep";
bool global_has_deep_sleep = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
optional<uint32_t> DeepSleepComponent::get_run_duration_() const {
#ifdef USE_ESP32
if (this->wakeup_cause_to_run_duration_.has_value()) {
esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
switch (wakeup_cause) {
case ESP_SLEEP_WAKEUP_EXT0:
case ESP_SLEEP_WAKEUP_EXT1:
case ESP_SLEEP_WAKEUP_GPIO:
return this->wakeup_cause_to_run_duration_->gpio_cause;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
return this->wakeup_cause_to_run_duration_->touch_cause;
default:
return this->wakeup_cause_to_run_duration_->default_cause;
}
}
#endif
return this->run_duration_;
}
void DeepSleepComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
global_has_deep_sleep = true;
@@ -45,6 +21,7 @@ void DeepSleepComponent::setup() {
ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured.");
}
}
void DeepSleepComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
if (this->sleep_duration_.has_value()) {
@@ -54,65 +31,31 @@ void DeepSleepComponent::dump_config() {
if (this->run_duration_.has_value()) {
ESP_LOGCONFIG(TAG, " Run Duration: %" PRIu32 " ms", *this->run_duration_);
}
#ifdef USE_ESP32
if (wakeup_pin_ != nullptr) {
LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_);
}
if (this->wakeup_cause_to_run_duration_.has_value()) {
ESP_LOGCONFIG(TAG, " Default Wakeup Run Duration: %" PRIu32 " ms",
this->wakeup_cause_to_run_duration_->default_cause);
ESP_LOGCONFIG(TAG, " Touch Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->touch_cause);
ESP_LOGCONFIG(TAG, " GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause);
}
#endif
this->dump_config_platform_();
}
void DeepSleepComponent::loop() {
if (this->next_enter_deep_sleep_)
this->begin_sleep();
}
float DeepSleepComponent::get_loop_priority() const {
return -100.0f; // run after everything else is ready
}
void DeepSleepComponent::set_sleep_duration(uint32_t time_ms) { this->sleep_duration_ = uint64_t(time_ms) * 1000; }
#if defined(USE_ESP32)
void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) {
this->wakeup_pin_mode_ = wakeup_pin_mode;
}
#endif
#if defined(USE_ESP32)
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6)
void DeepSleepComponent::set_ext1_wakeup(Ext1Wakeup ext1_wakeup) { this->ext1_wakeup_ = ext1_wakeup; }
void DeepSleepComponent::set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; }
#endif
void DeepSleepComponent::set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration) {
wakeup_cause_to_run_duration_ = wakeup_cause_to_run_duration;
}
#endif
void DeepSleepComponent::set_run_duration(uint32_t time_ms) { this->run_duration_ = time_ms; }
void DeepSleepComponent::begin_sleep(bool manual) {
if (this->prevent_ && !manual) {
this->next_enter_deep_sleep_ = true;
return;
}
#ifdef USE_ESP32
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr &&
!this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) {
// Defer deep sleep until inactive
if (!this->next_enter_deep_sleep_) {
this->status_set_warning();
ESP_LOGW(TAG, "Waiting for pin_ to switch state to enter deep sleep...");
}
this->next_enter_deep_sleep_ = true;
if (!this->prepare_to_sleep_()) {
return;
}
#endif
ESP_LOGI(TAG, "Beginning Deep Sleep");
if (this->sleep_duration_.has_value()) {
@@ -120,47 +63,13 @@ void DeepSleepComponent::begin_sleep(bool manual) {
}
App.run_safe_shutdown_hooks();
#if defined(USE_ESP32)
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6)
if (this->sleep_duration_.has_value())
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
if (this->wakeup_pin_ != nullptr) {
bool level = !this->wakeup_pin_->is_inverted();
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
level = !level;
}
esp_sleep_enable_ext0_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);
}
if (this->ext1_wakeup_.has_value()) {
esp_sleep_enable_ext1_wakeup(this->ext1_wakeup_->mask, this->ext1_wakeup_->wakeup_mode);
}
if (this->touch_wakeup_.has_value() && *(this->touch_wakeup_)) {
esp_sleep_enable_touchpad_wakeup();
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
}
#endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6)
if (this->sleep_duration_.has_value())
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
if (this->wakeup_pin_ != nullptr) {
bool level = !this->wakeup_pin_->is_inverted();
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
level = !level;
}
esp_deep_sleep_enable_gpio_wakeup(1 << this->wakeup_pin_->get_pin(),
static_cast<esp_deepsleep_gpio_wake_up_mode_t>(level));
}
#endif
esp_deep_sleep_start();
#endif
#ifdef USE_ESP8266
ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance)
#endif
this->deep_sleep_();
}
float DeepSleepComponent::get_setup_priority() const { return setup_priority::LATE; }
void DeepSleepComponent::prevent_deep_sleep() { this->prevent_ = true; }
void DeepSleepComponent::allow_deep_sleep() { this->prevent_ = false; }
} // namespace deep_sleep

View File

@@ -106,6 +106,10 @@ class DeepSleepComponent : public Component {
// duration before entering deep sleep.
optional<uint32_t> get_run_duration_() const;
void dump_config_platform_();
bool prepare_to_sleep_();
void deep_sleep_();
optional<uint64_t> sleep_duration_;
#ifdef USE_ESP32
InternalGPIOPin *wakeup_pin_;

View File

@@ -0,0 +1,104 @@
#ifdef USE_ESP32
#include "deep_sleep_component.h"
#include "esphome/core/log.h"
namespace esphome {
namespace deep_sleep {
static const char *const TAG = "deep_sleep";
optional<uint32_t> DeepSleepComponent::get_run_duration_() const {
if (this->wakeup_cause_to_run_duration_.has_value()) {
esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
switch (wakeup_cause) {
case ESP_SLEEP_WAKEUP_EXT0:
case ESP_SLEEP_WAKEUP_EXT1:
case ESP_SLEEP_WAKEUP_GPIO:
return this->wakeup_cause_to_run_duration_->gpio_cause;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
return this->wakeup_cause_to_run_duration_->touch_cause;
default:
return this->wakeup_cause_to_run_duration_->default_cause;
}
}
return this->run_duration_;
}
void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) {
this->wakeup_pin_mode_ = wakeup_pin_mode;
}
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6)
void DeepSleepComponent::set_ext1_wakeup(Ext1Wakeup ext1_wakeup) { this->ext1_wakeup_ = ext1_wakeup; }
void DeepSleepComponent::set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; }
#endif
void DeepSleepComponent::set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration) {
wakeup_cause_to_run_duration_ = wakeup_cause_to_run_duration;
}
void DeepSleepComponent::dump_config_platform_() {
if (wakeup_pin_ != nullptr) {
LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_);
}
if (this->wakeup_cause_to_run_duration_.has_value()) {
ESP_LOGCONFIG(TAG, " Default Wakeup Run Duration: %" PRIu32 " ms",
this->wakeup_cause_to_run_duration_->default_cause);
ESP_LOGCONFIG(TAG, " Touch Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->touch_cause);
ESP_LOGCONFIG(TAG, " GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause);
}
}
bool DeepSleepComponent::prepare_to_sleep_() {
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr &&
!this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) {
// Defer deep sleep until inactive
if (!this->next_enter_deep_sleep_) {
this->status_set_warning();
ESP_LOGW(TAG, "Waiting for pin_ to switch state to enter deep sleep...");
}
this->next_enter_deep_sleep_ = true;
return false;
}
return true;
}
void DeepSleepComponent::deep_sleep_() {
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6)
if (this->sleep_duration_.has_value())
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
if (this->wakeup_pin_ != nullptr) {
bool level = !this->wakeup_pin_->is_inverted();
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
level = !level;
}
esp_sleep_enable_ext0_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);
}
if (this->ext1_wakeup_.has_value()) {
esp_sleep_enable_ext1_wakeup(this->ext1_wakeup_->mask, this->ext1_wakeup_->wakeup_mode);
}
if (this->touch_wakeup_.has_value() && *(this->touch_wakeup_)) {
esp_sleep_enable_touchpad_wakeup();
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
}
#endif
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6)
if (this->sleep_duration_.has_value())
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
if (this->wakeup_pin_ != nullptr) {
bool level = !this->wakeup_pin_->is_inverted();
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
level = !level;
}
esp_deep_sleep_enable_gpio_wakeup(1 << this->wakeup_pin_->get_pin(),
static_cast<esp_deepsleep_gpio_wake_up_mode_t>(level));
}
#endif
esp_deep_sleep_start();
}
} // namespace deep_sleep
} // namespace esphome
#endif

View File

@@ -0,0 +1,23 @@
#ifdef USE_ESP8266
#include "deep_sleep_component.h"
#include <Esp.h>
namespace esphome {
namespace deep_sleep {
static const char *const TAG = "deep_sleep";
optional<uint32_t> DeepSleepComponent::get_run_duration_() const { return this->run_duration_; }
void DeepSleepComponent::dump_config_platform_() {}
bool DeepSleepComponent::prepare_to_sleep_() { return true; }
void DeepSleepComponent::deep_sleep_() {
ESP.deepSleep(*this->sleep_duration_); // NOLINT(readability-static-accessed-through-instance)
}
} // namespace deep_sleep
} // namespace esphome
#endif

View File

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

View File

@@ -1,87 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_COMPENSATION,
CONF_ECO2,
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
CONF_TVOC,
DEVICE_CLASS_AQI,
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
ICON_CHEMICAL_WEAPON,
ICON_MOLECULE_CO2,
ICON_RADIATOR,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_BILLION,
UNIT_PARTS_PER_MILLION,
CODEOWNERS = ["@latonita"]
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
"The ens160 sensor component has been renamed to ens160_i2c."
)
CODEOWNERS = ["@vincentscode"]
DEPENDENCIES = ["i2c"]
ens160_ns = cg.esphome_ns.namespace("ens160")
ENS160Component = ens160_ns.class_(
"ENS160Component", cg.PollingComponent, i2c.I2CDevice, sensor.Sensor
)
CONF_AQI = "aqi"
UNIT_INDEX = "index"
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ENS160Component),
cv.Required(CONF_ECO2): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_MOLECULE_CO2,
accuracy_decimals=0,
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_AQI): sensor.sensor_schema(
icon=ICON_CHEMICAL_WEAPON,
accuracy_decimals=0,
device_class=DEVICE_CLASS_AQI,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_COMPENSATION): cv.Schema(
{
cv.Required(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
cv.Required(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
}
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x53))
)
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)
sens = await sensor.new_sensor(config[CONF_ECO2])
cg.add(var.set_co2(sens))
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))
sens = await sensor.new_sensor(config[CONF_AQI])
cg.add(var.set_aqi(sens))
if CONF_COMPENSATION in config:
compensation_config = config[CONF_COMPENSATION]
sens = await cg.get_variable(compensation_config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
sens = await cg.get_variable(compensation_config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))

View File

@@ -0,0 +1,78 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_COMPENSATION,
CONF_ECO2,
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
CONF_TVOC,
DEVICE_CLASS_AQI,
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
ICON_CHEMICAL_WEAPON,
ICON_MOLECULE_CO2,
ICON_RADIATOR,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_BILLION,
UNIT_PARTS_PER_MILLION,
)
CODEOWNERS = ["@vincentscode", "@latonita"]
ens160_ns = cg.esphome_ns.namespace("ens160_base")
CONF_AQI = "aqi"
UNIT_INDEX = "index"
CONFIG_SCHEMA_BASE = cv.Schema(
{
cv.Required(CONF_ECO2): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_MOLECULE_CO2,
accuracy_decimals=0,
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_AQI): sensor.sensor_schema(
icon=ICON_CHEMICAL_WEAPON,
accuracy_decimals=0,
device_class=DEVICE_CLASS_AQI,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_COMPENSATION): cv.Schema(
{
cv.Required(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
cv.Required(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
}
),
}
).extend(cv.polling_component_schema("60s"))
async def to_code_base(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
sens = await sensor.new_sensor(config[CONF_ECO2])
cg.add(var.set_co2(sens))
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))
sens = await sensor.new_sensor(config[CONF_AQI])
cg.add(var.set_aqi(sens))
if compensation_config := config.get(CONF_COMPENSATION):
sens = await cg.get_variable(compensation_config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
sens = await cg.get_variable(compensation_config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))
return var

View File

@@ -5,12 +5,12 @@
// Implementation based on:
// https://github.com/sciosense/ENS160_driver
#include "ens160.h"
#include "ens160_base.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace ens160 {
namespace ens160_base {
static const char *const TAG = "ens160";
@@ -303,7 +303,6 @@ void ENS160Component::dump_config() {
ESP_LOGI(TAG, "Firmware Version: %d.%d.%d", this->firmware_ver_major_, this->firmware_ver_minor_,
this->firmware_ver_build_);
LOG_I2C_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "CO2 Sensor:", this->co2_);
LOG_SENSOR(" ", "TVOC Sensor:", this->tvoc_);
@@ -317,5 +316,5 @@ void ENS160Component::dump_config() {
}
}
} // namespace ens160
} // namespace ens160_base
} // namespace esphome

View File

@@ -2,12 +2,11 @@
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace ens160 {
namespace ens160_base {
class ENS160Component : public PollingComponent, public i2c::I2CDevice, public sensor::Sensor {
class ENS160Component : public PollingComponent, public sensor::Sensor {
public:
void set_co2(sensor::Sensor *co2) { co2_ = co2; }
void set_tvoc(sensor::Sensor *tvoc) { tvoc_ = tvoc; }
@@ -44,6 +43,11 @@ class ENS160Component : public PollingComponent, public i2c::I2CDevice, public s
bool warming_up_{false};
bool initial_startup_{false};
virtual bool read_byte(uint8_t a_register, uint8_t *data) = 0;
virtual bool write_byte(uint8_t a_register, uint8_t data) = 0;
virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
virtual bool write_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
uint8_t firmware_ver_major_{0};
uint8_t firmware_ver_minor_{0};
uint8_t firmware_ver_build_{0};
@@ -56,5 +60,5 @@ class ENS160Component : public PollingComponent, public i2c::I2CDevice, public s
sensor::Sensor *temperature_{nullptr};
};
} // namespace ens160
} // namespace ens160_base
} // namespace esphome

View File

@@ -0,0 +1,32 @@
#include <cstddef>
#include <cstdint>
#include "ens160_i2c.h"
#include "esphome/components/i2c/i2c.h"
#include "../ens160_base/ens160_base.h"
namespace esphome {
namespace ens160_i2c {
static const char *const TAG = "ens160_i2c.sensor";
bool ENS160I2CComponent::read_byte(uint8_t a_register, uint8_t *data) {
return I2CDevice::read_byte(a_register, data);
};
bool ENS160I2CComponent::write_byte(uint8_t a_register, uint8_t data) {
return I2CDevice::write_byte(a_register, data);
};
bool ENS160I2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
return I2CDevice::read_bytes(a_register, data, len);
};
bool ENS160I2CComponent::write_bytes(uint8_t a_register, uint8_t *data, size_t len) {
return I2CDevice::write_bytes(a_register, data, len);
};
void ENS160I2CComponent::dump_config() {
ENS160Component::dump_config();
LOG_I2C_DEVICE(this);
}
} // namespace ens160_i2c
} // namespace esphome

View File

@@ -0,0 +1,19 @@
#pragma once
#include "esphome/components/ens160_base/ens160_base.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace ens160_i2c {
class ENS160I2CComponent : public esphome::ens160_base::ENS160Component, public i2c::I2CDevice {
void dump_config() override;
bool read_byte(uint8_t a_register, uint8_t *data) override;
bool write_byte(uint8_t a_register, uint8_t data) override;
bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
bool write_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
};
} // namespace ens160_i2c
} // namespace esphome

View File

@@ -0,0 +1,22 @@
import esphome.codegen as cg
from esphome.components import i2c
from ..ens160_base import to_code_base, cv, CONFIG_SCHEMA_BASE
AUTO_LOAD = ["ens160_base"]
CODEOWNERS = ["@latonita"]
DEPENDENCIES = ["i2c"]
ens160_ns = cg.esphome_ns.namespace("ens160_i2c")
ENS160I2CComponent = ens160_ns.class_(
"ENS160I2CComponent", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(
i2c.i2c_device_schema(default_address=0x52)
).extend({cv.GenerateID(): cv.declare_id(ENS160I2CComponent)})
async def to_code(config):
var = await to_code_base(config)
await i2c.register_i2c_device(var, config)

View File

@@ -0,0 +1,59 @@
#include <cstdint>
#include <cstddef>
#include "ens160_spi.h"
#include <esphome/components/ens160_base/ens160_base.h>
namespace esphome {
namespace ens160_spi {
static const char *const TAG = "ens160_spi.sensor";
inline uint8_t reg_read(uint8_t reg) { return (reg << 1) | 0x01; }
inline uint8_t reg_write(uint8_t reg) { return (reg << 1) & 0xFE; }
void ENS160SPIComponent::setup() {
this->spi_setup();
ENS160Component::setup();
};
void ENS160SPIComponent::dump_config() {
ENS160Component::dump_config();
LOG_PIN(" CS Pin: ", this->cs_);
}
bool ENS160SPIComponent::read_byte(uint8_t a_register, uint8_t *data) {
this->enable();
this->transfer_byte(reg_read(a_register));
*data = this->transfer_byte(0);
this->disable();
return true;
}
bool ENS160SPIComponent::write_byte(uint8_t a_register, uint8_t data) {
this->enable();
this->transfer_byte(reg_write(a_register));
this->transfer_byte(data);
this->disable();
return true;
}
bool ENS160SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
this->enable();
this->transfer_byte(reg_read(a_register));
this->read_array(data, len);
this->disable();
return true;
}
bool ENS160SPIComponent::write_bytes(uint8_t a_register, uint8_t *data, size_t len) {
this->enable();
this->transfer_byte(reg_write(a_register));
this->transfer_array(data, len);
this->disable();
return true;
}
} // namespace ens160_spi
} // namespace esphome

View File

@@ -0,0 +1,22 @@
#pragma once
#include "esphome/components/ens160_base/ens160_base.h"
#include "esphome/components/spi/spi.h"
namespace esphome {
namespace ens160_spi {
class ENS160SPIComponent : public esphome::ens160_base::ENS160Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> {
void setup() override;
void dump_config() override;
bool read_byte(uint8_t a_register, uint8_t *data) override;
bool write_byte(uint8_t a_register, uint8_t data) override;
bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
bool write_bytes(uint8_t a_register, uint8_t *data, size_t len) override;
};
} // namespace ens160_spi
} // namespace esphome

View File

@@ -0,0 +1,22 @@
import esphome.codegen as cg
from esphome.components import spi
from ..ens160_base import to_code_base, cv, CONFIG_SCHEMA_BASE
AUTO_LOAD = ["ens160_base"]
CODEOWNERS = ["@latonita"]
DEPENDENCIES = ["spi"]
ens160_spi_ns = cg.esphome_ns.namespace("ens160_spi")
ENS160SPIComponent = ens160_spi_ns.class_(
"ENS160SPIComponent", cg.PollingComponent, spi.SPIDevice
)
CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(spi.spi_device_schema()).extend(
{cv.GenerateID(): cv.declare_id(ENS160SPIComponent)}
)
async def to_code(config):
var = await to_code_base(config)
await spi.register_spi_device(var, config)

View File

@@ -227,7 +227,7 @@ ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0)
# The default/recommended esp-idf framework version
# - https://github.com/espressif/esp-idf/releases
# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 6)
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 7)
# The platformio/espressif32 version to use for esp-idf frameworks
# - https://github.com/platformio/platform-espressif32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32

View File

@@ -1,5 +1,6 @@
from dataclasses import dataclass
from typing import Any
import logging
from esphome.const import (
CONF_ID,
@@ -8,6 +9,7 @@ from esphome.const import (
CONF_NUMBER,
CONF_OPEN_DRAIN,
CONF_OUTPUT,
CONF_IGNORE_PIN_VALIDATION_ERROR,
CONF_IGNORE_STRAPPING_WARNING,
PLATFORM_ESP32,
)
@@ -42,6 +44,9 @@ from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_support
ESP32InternalGPIOPin = esp32_ns.class_("ESP32InternalGPIOPin", cg.InternalGPIOPin)
_LOGGER = logging.getLogger(__name__)
def _lookup_pin(value):
board = CORE.data[KEY_ESP32][KEY_BOARD]
board_pins = boards.ESP32_BOARD_PINS.get(board, {})
@@ -111,7 +116,7 @@ _esp32_validations = {
}
def validate_gpio_pin(value):
def gpio_pin_number_validator(value):
value = _translate_pin(value)
board = CORE.data[KEY_ESP32][KEY_BOARD]
board_pins = boards.ESP32_BOARD_PINS.get(board, {})
@@ -127,7 +132,33 @@ def validate_gpio_pin(value):
if variant not in _esp32_validations:
raise cv.Invalid(f"Unsupported ESP32 variant {variant}")
return _esp32_validations[variant].pin_validation(value)
return value
def validate_gpio_pin(pin):
variant = CORE.data[KEY_ESP32][KEY_VARIANT]
if variant not in _esp32_validations:
raise cv.Invalid(f"Unsupported ESP32 variant {variant}")
ignore_pin_validation_warning = pin[CONF_IGNORE_PIN_VALIDATION_ERROR]
try:
pin[CONF_NUMBER] = _esp32_validations[variant].pin_validation(pin[CONF_NUMBER])
except cv.Invalid as exc:
if not ignore_pin_validation_warning:
raise
_LOGGER.warning(
"Ignoring validation error on pin %d; error: %s",
pin[CONF_NUMBER],
exc,
)
else:
# Throw an exception if used for a pin that would not have resulted
# in a validation error anyway!
if ignore_pin_validation_warning:
raise cv.Invalid(f"GPIO{pin[CONF_NUMBER]} is not a reserved pin")
return pin
def validate_supports(value):
@@ -158,9 +189,11 @@ DRIVE_STRENGTHS = {
gpio_num_t = cg.global_ns.enum("gpio_num_t")
CONF_DRIVE_STRENGTH = "drive_strength"
ESP32_PIN_SCHEMA = cv.All(
pins.gpio_base_schema(ESP32InternalGPIOPin, validate_gpio_pin).extend(
pins.gpio_base_schema(ESP32InternalGPIOPin, gpio_pin_number_validator).extend(
{
cv.Optional(CONF_IGNORE_PIN_VALIDATION_ERROR, default=False): cv.boolean,
cv.Optional(CONF_IGNORE_STRAPPING_WARNING, default=False): cv.boolean,
cv.Optional(CONF_DRIVE_STRENGTH, default="20mA"): cv.All(
cv.float_with_unit("current", "mA", optional_unit=True),
@@ -168,6 +201,7 @@ ESP32_PIN_SCHEMA = cv.All(
),
}
),
validate_gpio_pin,
validate_supports,
)

View File

@@ -1,6 +1,11 @@
#ifdef USE_ESP32
#include "ble.h"
#ifdef USE_ESP32_VARIANT_ESP32C6
#include "const_esp32c6.h"
#endif // USE_ESP32_VARIANT_ESP32C6
#include "esphome/core/application.h"
#include "esphome/core/log.h"
@@ -114,7 +119,11 @@ bool ESP32BLE::ble_setup_() {
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
// start bt controller
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) {
#ifdef USE_ESP32_VARIANT_ESP32C6
esp_bt_controller_config_t cfg = BT_CONTROLLER_CONFIG;
#else
esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
#endif
err = esp_bt_controller_init(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err));

View File

@@ -2,9 +2,9 @@
#ifdef USE_ESP32
#include <cstring>
#include <cstdio>
#include <cinttypes>
#include <cstdio>
#include <cstring>
#include "esphome/core/log.h"
namespace esphome {
@@ -31,14 +31,15 @@ ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) {
memcpy(ret.uuid_.uuid.uuid128, data, ESP_UUID_LEN_128);
return ret;
}
ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { return ESPBTUUID::from_raw(data.data(), data.length()); }
ESPBTUUID ESPBTUUID::from_raw(const char *data, size_t len) {
ESPBTUUID ret;
if (data.length() == 4) {
if (len == 4) {
ret.uuid_.len = ESP_UUID_LEN_16;
ret.uuid_.uuid.uuid16 = 0;
for (int i = 0; i < data.length();) {
uint8_t msb = data.c_str()[i];
uint8_t lsb = data.c_str()[i + 1];
for (int i = 0; i < len;) {
uint8_t msb = data[i];
uint8_t lsb = data[i + 1];
if (msb > '9')
msb -= 7;
@@ -47,12 +48,12 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (2 - i) * 4;
i += 2;
}
} else if (data.length() == 8) {
} else if (len == 8) {
ret.uuid_.len = ESP_UUID_LEN_32;
ret.uuid_.uuid.uuid32 = 0;
for (int i = 0; i < data.length();) {
uint8_t msb = data.c_str()[i];
uint8_t lsb = data.c_str()[i + 1];
for (int i = 0; i < len;) {
uint8_t msb = data[i];
uint8_t lsb = data[i + 1];
if (msb > '9')
msb -= 7;
@@ -61,20 +62,20 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (6 - i) * 4;
i += 2;
}
} else if (data.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be
// investigated (lack of time)
} else if (len == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be
// investigated (lack of time)
ret.uuid_.len = ESP_UUID_LEN_128;
memcpy(ret.uuid_.uuid.uuid128, (uint8_t *) data.data(), 16);
} else if (data.length() == 36) {
memcpy(ret.uuid_.uuid.uuid128, (uint8_t *) data, 16);
} else if (len == 36) {
// If the length of the string is 36 bytes then we will assume it is a long hex string in
// UUID format.
ret.uuid_.len = ESP_UUID_LEN_128;
int n = 0;
for (int i = 0; i < data.length();) {
if (data.c_str()[i] == '-')
for (int i = 0; i < len;) {
if (data[i] == '-')
i++;
uint8_t msb = data.c_str()[i];
uint8_t lsb = data.c_str()[i + 1];
uint8_t msb = data[i];
uint8_t lsb = data[i + 1];
if (msb > '9')
msb -= 7;
@@ -84,7 +85,7 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) {
i += 2;
}
} else {
ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data.c_str());
ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data);
}
return ret;
}
@@ -162,14 +163,19 @@ bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const {
return false;
}
esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; }
std::string ESPBTUUID::to_string() const {
std::string ESPBTUUID::to_string() {
if (!this->string_.empty())
return this->string_;
switch (this->uuid_.len) {
case ESP_UUID_LEN_16:
return str_snprintf("0x%02X%02X", 6, this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
this->string_ = str_snprintf("0x%02X%02X", 6, this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
return this->string_;
case ESP_UUID_LEN_32:
return str_snprintf("0x%02" PRIX32 "%02" PRIX32 "%02" PRIX32 "%02" PRIX32, 10, (this->uuid_.uuid.uuid32 >> 24),
(this->uuid_.uuid.uuid32 >> 16 & 0xff), (this->uuid_.uuid.uuid32 >> 8 & 0xff),
this->uuid_.uuid.uuid32 & 0xff);
this->string_ = str_snprintf("0x%02" PRIX32 "%02" PRIX32 "%02" PRIX32 "%02" PRIX32, 10,
(this->uuid_.uuid.uuid32 >> 24), (this->uuid_.uuid.uuid32 >> 16 & 0xff),
(this->uuid_.uuid.uuid32 >> 8 & 0xff), this->uuid_.uuid.uuid32 & 0xff);
return this->string_;
default:
case ESP_UUID_LEN_128:
std::string buf;
@@ -178,6 +184,7 @@ std::string ESPBTUUID::to_string() const {
if (i == 6 || i == 8 || i == 10 || i == 12)
buf += "-";
}
this->string_ = buf;
return buf;
}
return "";

View File

@@ -1,12 +1,12 @@
#pragma once
#include "esphome/core/helpers.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#ifdef USE_ESP32
#include <string>
#include <esp_bt_defs.h>
#include <string>
namespace esphome {
namespace esp32_ble {
@@ -23,6 +23,8 @@ class ESPBTUUID {
static ESPBTUUID from_raw(const std::string &data);
static ESPBTUUID from_raw(const char *uuid, size_t len);
static ESPBTUUID from_uuid(esp_bt_uuid_t uuid);
ESPBTUUID as_128bit() const;
@@ -34,10 +36,11 @@ class ESPBTUUID {
esp_bt_uuid_t get_uuid() const;
std::string to_string() const;
std::string to_string();
protected:
esp_bt_uuid_t uuid_;
std::string string_;
};
} // namespace esp32_ble

View File

@@ -0,0 +1,67 @@
#pragma once
#ifdef USE_ESP32_VARIANT_ESP32C6
#include <esp_bt.h>
namespace esphome {
namespace esp32_ble {
static const esp_bt_controller_config_t BT_CONTROLLER_CONFIG = {
.config_version = CONFIG_VERSION,
.ble_ll_resolv_list_size = CONFIG_BT_LE_LL_RESOLV_LIST_SIZE,
.ble_hci_evt_hi_buf_count = DEFAULT_BT_LE_HCI_EVT_HI_BUF_COUNT,
.ble_hci_evt_lo_buf_count = DEFAULT_BT_LE_HCI_EVT_LO_BUF_COUNT,
.ble_ll_sync_list_cnt = DEFAULT_BT_LE_MAX_PERIODIC_ADVERTISER_LIST,
.ble_ll_sync_cnt = DEFAULT_BT_LE_MAX_PERIODIC_SYNCS,
.ble_ll_rsp_dup_list_count = CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT,
.ble_ll_adv_dup_list_count = CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT,
.ble_ll_tx_pwr_dbm = BLE_LL_TX_PWR_DBM_N,
.rtc_freq = RTC_FREQ_N,
.ble_ll_sca = CONFIG_BT_LE_LL_SCA,
.ble_ll_scan_phy_number = BLE_LL_SCAN_PHY_NUMBER_N,
.ble_ll_conn_def_auth_pyld_tmo = BLE_LL_CONN_DEF_AUTH_PYLD_TMO_N,
.ble_ll_jitter_usecs = BLE_LL_JITTER_USECS_N,
.ble_ll_sched_max_adv_pdu_usecs = BLE_LL_SCHED_MAX_ADV_PDU_USECS_N,
.ble_ll_sched_direct_adv_max_usecs = BLE_LL_SCHED_DIRECT_ADV_MAX_USECS_N,
.ble_ll_sched_adv_max_usecs = BLE_LL_SCHED_ADV_MAX_USECS_N,
.ble_scan_rsp_data_max_len = DEFAULT_BT_LE_SCAN_RSP_DATA_MAX_LEN_N,
.ble_ll_cfg_num_hci_cmd_pkts = BLE_LL_CFG_NUM_HCI_CMD_PKTS_N,
.ble_ll_ctrl_proc_timeout_ms = BLE_LL_CTRL_PROC_TIMEOUT_MS_N,
.nimble_max_connections = DEFAULT_BT_LE_MAX_CONNECTIONS,
.ble_whitelist_size = DEFAULT_BT_NIMBLE_WHITELIST_SIZE, // NOLINT
.ble_acl_buf_size = DEFAULT_BT_LE_ACL_BUF_SIZE,
.ble_acl_buf_count = DEFAULT_BT_LE_ACL_BUF_COUNT,
.ble_hci_evt_buf_size = DEFAULT_BT_LE_HCI_EVT_BUF_SIZE,
.ble_multi_adv_instances = DEFAULT_BT_LE_MAX_EXT_ADV_INSTANCES,
.ble_ext_adv_max_size = DEFAULT_BT_LE_EXT_ADV_MAX_SIZE,
.controller_task_stack_size = NIMBLE_LL_STACK_SIZE,
.controller_task_prio = ESP_TASK_BT_CONTROLLER_PRIO,
.controller_run_cpu = 0,
.enable_qa_test = RUN_QA_TEST,
.enable_bqb_test = RUN_BQB_TEST,
.enable_uart_hci = HCI_UART_EN,
.ble_hci_uart_port = DEFAULT_BT_LE_HCI_UART_PORT,
.ble_hci_uart_baud = DEFAULT_BT_LE_HCI_UART_BAUD,
.ble_hci_uart_data_bits = DEFAULT_BT_LE_HCI_UART_DATA_BITS,
.ble_hci_uart_stop_bits = DEFAULT_BT_LE_HCI_UART_STOP_BITS,
.ble_hci_uart_flow_ctrl = DEFAULT_BT_LE_HCI_UART_FLOW_CTRL,
.ble_hci_uart_uart_parity = DEFAULT_BT_LE_HCI_UART_PARITY,
.enable_tx_cca = DEFAULT_BT_LE_TX_CCA_ENABLED,
.cca_rssi_thresh = 256 - DEFAULT_BT_LE_CCA_RSSI_THRESH,
.sleep_en = NIMBLE_SLEEP_ENABLE,
.coex_phy_coded_tx_rx_time_limit = DEFAULT_BT_LE_COEX_PHY_CODED_TX_RX_TLIM_EFF,
.dis_scan_backoff = NIMBLE_DISABLE_SCAN_BACKOFF,
.ble_scan_classify_filter_enable = 1,
.main_xtal_freq = CONFIG_XTAL_FREQ,
.version_num = (uint8_t) efuse_hal_chip_revision(),
.cpu_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
.ignore_wl_for_direct_adv = 0,
.enable_pcl = DEFAULT_BT_LE_POWER_CONTROL_ENABLED,
.config_magic = CONFIG_MAGIC,
};
} // namespace esp32_ble
} // namespace esphome
#endif // USE_ESP32_VARIANT_ESP32C6

View File

@@ -8,6 +8,9 @@ namespace esp32_ble_server {
BLE2901::BLE2901(const std::string &value) : BLE2901((uint8_t *) value.data(), value.length()) {}
BLE2901::BLE2901(const uint8_t *data, size_t length) : BLEDescriptor(esp32_ble::ESPBTUUID::from_uint16(0x2901)) {
if (this->state_ == FAILED) {
return;
}
this->set_value(data, length);
this->permissions_ = ESP_GATT_PERM_READ;
}

View File

@@ -1,6 +1,8 @@
#include "ble_2902.h"
#include "esphome/components/esp32_ble/ble_uuid.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
#include <cstring>
@@ -9,6 +11,9 @@ namespace esphome {
namespace esp32_ble_server {
BLE2902::BLE2902() : BLEDescriptor(esp32_ble::ESPBTUUID::from_uint16(0x2902)) {
if (this->state_ == FAILED) {
return;
}
this->value_.attr_len = 2;
uint8_t data[2] = {0, 0};
memcpy(this->value_.attr_value, data, 2);

View File

@@ -1,4 +1,6 @@
#include "ble_characteristic.h"
#include "ble_2901.h"
#include "ble_2902.h"
#include "ble_server.h"
#include "ble_service.h"
@@ -12,14 +14,17 @@ namespace esp32_ble_server {
static const char *const TAG = "esp32_ble_server.characteristic";
BLECharacteristic::~BLECharacteristic() {
for (auto *descriptor : this->descriptors_) {
delete descriptor; // NOLINT(cppcoreguidelines-owning-memory)
}
this->descriptors_.clear();
vSemaphoreDelete(this->set_value_lock_);
}
BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) : uuid_(uuid) {
this->set_value_lock_ = xSemaphoreCreateBinary();
if (this->set_value_lock_ == nullptr) {
ESP_LOGE(TAG, "Failed to create set_value_lock_ semaphore");
this->state_ = FAILED;
return;
}
xSemaphoreGive(this->set_value_lock_);
this->properties_ = (esp_gatt_char_prop_t) 0;
@@ -103,14 +108,36 @@ void BLECharacteristic::notify(bool notification) {
}
}
void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { this->descriptors_.push_back(descriptor); }
void BLECharacteristic::add_descriptor(std::shared_ptr<BLEDescriptor> descriptor) {
this->descriptors_.push_back(descriptor);
}
void BLECharacteristic::remove_descriptor(BLEDescriptor *descriptor) {
void BLECharacteristic::remove_descriptor(std::shared_ptr<BLEDescriptor> descriptor) {
this->descriptors_.erase(std::remove(this->descriptors_.begin(), this->descriptors_.end(), descriptor),
this->descriptors_.end());
}
void BLECharacteristic::do_create(BLEService *service) {
std::shared_ptr<BLE2901> BLECharacteristic::make_2901_descriptor(const std::string &value) {
auto descriptor = std::make_shared<BLE2901>(value);
if (descriptor == nullptr) {
ESP_LOGE(TAG, "Failed to allocate BLE2901 descriptor");
return nullptr;
}
this->add_descriptor(descriptor);
return descriptor;
}
std::shared_ptr<BLE2902> BLECharacteristic::make_2902_descriptor() {
auto descriptor = std::make_shared<BLE2902>();
if (descriptor == nullptr) {
ESP_LOGE(TAG, "Failed to allocate BLE2902 descriptor");
return nullptr;
}
this->add_descriptor(descriptor);
return descriptor;
}
void BLECharacteristic::do_create(std::shared_ptr<BLEService> service) {
this->service_ = service;
esp_attr_control_t control;
control.auto_rsp = ESP_GATT_RSP_BY_APP;
@@ -137,7 +164,7 @@ bool BLECharacteristic::is_created() {
return false;
bool created = true;
for (auto *descriptor : this->descriptors_) {
for (auto descriptor : this->descriptors_) {
created &= descriptor->is_created();
}
if (created)
@@ -150,7 +177,7 @@ bool BLECharacteristic::is_failed() {
return true;
bool failed = false;
for (auto *descriptor : this->descriptors_) {
for (auto descriptor : this->descriptors_) {
failed |= descriptor->is_failed();
}
if (failed)
@@ -208,8 +235,8 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
if (this->uuid_ == ESPBTUUID::from_uuid(param->add_char.char_uuid)) {
this->handle_ = param->add_char.attr_handle;
for (auto *descriptor : this->descriptors_) {
descriptor->do_create(this);
for (auto descriptor : this->descriptors_) {
descriptor->do_create(shared_from_this());
}
this->state_ = CREATING_DEPENDENTS;
@@ -313,7 +340,7 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt
break;
}
for (auto *descriptor : this->descriptors_) {
for (auto descriptor : this->descriptors_) {
descriptor->gatts_event_handler(event, gatts_if, param);
}
}

View File

@@ -1,17 +1,21 @@
#pragma once
#include "ble_2901.h"
#include "ble_2902.h"
#include "ble_descriptor.h"
#include "esphome/components/esp32_ble/ble_uuid.h"
#include <memory>
#include <vector>
#ifdef USE_ESP32
#include <esp_bt_defs.h>
#include <esp_gap_ble_api.h>
#include <esp_gatt_defs.h>
#include <esp_gattc_api.h>
#include <esp_gatts_api.h>
#include <esp_bt_defs.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
@@ -22,7 +26,7 @@ using namespace esp32_ble;
class BLEService;
class BLECharacteristic {
class BLECharacteristic : public std::enable_shared_from_this<BLECharacteristic> {
public:
BLECharacteristic(ESPBTUUID uuid, uint32_t properties);
~BLECharacteristic();
@@ -47,15 +51,18 @@ class BLECharacteristic {
void notify(bool notification = true);
void do_create(BLEService *service);
void do_create(std::shared_ptr<BLEService> service);
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void on_write(const std::function<void(const std::vector<uint8_t> &)> &&func) { this->on_write_ = func; }
void add_descriptor(BLEDescriptor *descriptor);
void remove_descriptor(BLEDescriptor *descriptor);
void add_descriptor(std::shared_ptr<BLEDescriptor> descriptor);
void remove_descriptor(std::shared_ptr<BLEDescriptor> descriptor);
BLEService *get_service() { return this->service_; }
std::shared_ptr<BLE2901> make_2901_descriptor(const std::string &value);
std::shared_ptr<BLE2902> make_2902_descriptor();
std::shared_ptr<BLEService> get_service() { return this->service_; }
ESPBTUUID get_uuid() { return this->uuid_; }
std::vector<uint8_t> &get_value() { return this->value_; }
@@ -71,7 +78,7 @@ class BLECharacteristic {
protected:
bool write_event_{false};
BLEService *service_;
std::shared_ptr<BLEService> service_;
ESPBTUUID uuid_;
esp_gatt_char_prop_t properties_;
uint16_t handle_{0xFFFF};
@@ -80,7 +87,7 @@ class BLECharacteristic {
std::vector<uint8_t> value_;
SemaphoreHandle_t set_value_lock_;
std::vector<BLEDescriptor *> descriptors_;
std::vector<std::shared_ptr<BLEDescriptor>> descriptors_;
std::function<void(const std::vector<uint8_t> &)> on_write_;

View File

@@ -1,8 +1,12 @@
#include "ble_descriptor.h"
#include "ble_characteristic.h"
#include "ble_service.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include <cstring>
#ifdef USE_ESP32
@@ -16,12 +20,23 @@ BLEDescriptor::BLEDescriptor(ESPBTUUID uuid, uint16_t max_len) {
this->uuid_ = uuid;
this->value_.attr_len = 0;
this->value_.attr_max_len = max_len;
this->value_.attr_value = (uint8_t *) malloc(max_len); // NOLINT
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->value_.attr_value = allocator.allocate(max_len);
if (this->value_.attr_value == nullptr) {
ESP_LOGE(TAG, "Failed to allocate %d bytes for value", max_len);
this->state_ = FAILED;
return;
}
}
BLEDescriptor::~BLEDescriptor() { free(this->value_.attr_value); } // NOLINT
BLEDescriptor::~BLEDescriptor() {
ExternalRAMAllocator<uint8_t> deallocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
deallocator.deallocate(this->value_.attr_value, this->value_.attr_max_len);
this->value_.attr_value = nullptr;
}
void BLEDescriptor::do_create(BLECharacteristic *characteristic) {
void BLEDescriptor::do_create(std::shared_ptr<BLECharacteristic> characteristic) {
this->characteristic_ = characteristic;
esp_attr_control_t control;
control.auto_rsp = ESP_GATT_AUTO_RSP;

View File

@@ -18,7 +18,7 @@ class BLEDescriptor {
public:
BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100);
virtual ~BLEDescriptor();
void do_create(BLECharacteristic *characteristic);
void do_create(std::shared_ptr<BLECharacteristic> characteristic);
void set_value(const std::string &value);
void set_value(const uint8_t *data, size_t length);
@@ -29,7 +29,7 @@ class BLEDescriptor {
bool is_failed() { return this->state_ == FAILED; }
protected:
BLECharacteristic *characteristic_{nullptr};
std::shared_ptr<BLECharacteristic> characteristic_{nullptr};
ESPBTUUID uuid_;
uint16_t handle_{0xFFFF};

View File

@@ -1,18 +1,18 @@
#include "ble_server.h"
#include "esphome/components/esp32_ble/ble.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "esphome/core/version.h"
#ifdef USE_ESP32
#include <nvs_flash.h>
#include <freertos/FreeRTOSConfig.h>
#include <esp_bt_main.h>
#include <esp_bt.h>
#include <freertos/task.h>
#include <esp_bt_main.h>
#include <esp_gap_ble_api.h>
#include <freertos/FreeRTOSConfig.h>
#include <freertos/task.h>
#include <nvs_flash.h>
namespace esphome {
namespace esp32_ble_server {
@@ -58,9 +58,8 @@ void BLEServer::loop() {
pair.second->do_create(this);
}
if (this->device_information_service_ == nullptr) {
this->create_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID));
this->device_information_service_ =
this->get_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID));
this->create_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID));
this->create_device_characteristics_();
}
this->state_ = STARTING_SERVICE;
@@ -94,56 +93,58 @@ void BLEServer::restart_advertising_() {
}
bool BLEServer::create_device_characteristics_() {
std::shared_ptr<BLECharacteristic> model =
this->device_information_service_->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ);
if (this->model_.has_value()) {
BLECharacteristic *model =
this->device_information_service_->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ);
model->set_value(this->model_.value());
} else {
BLECharacteristic *model =
this->device_information_service_->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ);
model->set_value(ESPHOME_BOARD);
}
BLECharacteristic *version =
std::shared_ptr<BLECharacteristic> version =
this->device_information_service_->create_characteristic(VERSION_UUID, BLECharacteristic::PROPERTY_READ);
version->set_value("ESPHome " ESPHOME_VERSION);
BLECharacteristic *manufacturer =
std::shared_ptr<BLECharacteristic> manufacturer =
this->device_information_service_->create_characteristic(MANUFACTURER_UUID, BLECharacteristic::PROPERTY_READ);
manufacturer->set_value(this->manufacturer_);
return true;
}
void BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles, uint8_t inst_id) {
ESP_LOGV(TAG, "Creating BLE service - %s", uuid.to_string().c_str());
std::shared_ptr<BLEService> BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles,
uint8_t inst_id) {
std::string uuid_str = uuid.to_string();
// If the service already exists, do nothing
BLEService *service = this->get_service(uuid);
std::shared_ptr<BLEService> service = this->get_service(uuid);
if (service != nullptr) {
ESP_LOGW(TAG, "BLE service %s already exists", uuid.to_string().c_str());
return;
ESP_LOGW(TAG, "BLE service %s already exists", uuid_str.c_str());
return service;
}
service = new BLEService(uuid, num_handles, inst_id, advertise); // NOLINT(cppcoreguidelines-owning-memory)
this->services_.emplace(uuid.to_string(), service);
ESP_LOGV(TAG, "Creating BLE service - %s", uuid_str.c_str());
service = std::make_shared<BLEService>(uuid, num_handles, inst_id, advertise);
this->services_.emplace(uuid_str, service);
service->do_create(this);
return service;
}
void BLEServer::remove_service(ESPBTUUID uuid) {
ESP_LOGV(TAG, "Removing BLE service - %s", uuid.to_string().c_str());
BLEService *service = this->get_service(uuid);
std::string uuid_str = uuid.to_string();
std::shared_ptr<BLEService> service = this->get_service(uuid);
if (service == nullptr) {
ESP_LOGW(TAG, "BLE service %s not found", uuid.to_string().c_str());
ESP_LOGW(TAG, "BLE service %s not found", uuid_str.c_str());
return;
}
ESP_LOGV(TAG, "Removing BLE service - %s", uuid_str.c_str());
service->do_delete();
delete service; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.erase(uuid.to_string());
this->services_.erase(uuid_str);
}
BLEService *BLEServer::get_service(ESPBTUUID uuid) {
BLEService *service = nullptr;
if (this->services_.count(uuid.to_string()) > 0) {
service = this->services_.at(uuid.to_string());
std::shared_ptr<BLEService> BLEServer::get_service(ESPBTUUID uuid) {
std::string uuid_str = uuid.to_string();
std::shared_ptr<BLEService> service = nullptr;
if (this->services_.count(uuid_str) > 0) {
service = this->services_.at(uuid_str);
}
return service;
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include "ble_service.h"
#include "ble_characteristic.h"
#include "ble_service.h"
#include "esphome/components/esp32_ble/ble.h"
#include "esphome/components/esp32_ble/ble_advertising.h"
@@ -12,8 +12,8 @@
#include "esphome/core/preferences.h"
#include <memory>
#include <vector>
#include <unordered_map>
#include <vector>
#ifdef USE_ESP32
@@ -51,9 +51,10 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
this->restart_advertising_();
}
void create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15, uint8_t inst_id = 0);
std::shared_ptr<BLEService> create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15,
uint8_t inst_id = 0);
void remove_service(ESPBTUUID uuid);
BLEService *get_service(ESPBTUUID uuid);
std::shared_ptr<BLEService> get_service(ESPBTUUID uuid);
esp_gatt_if_t get_gatts_if() { return this->gatts_if_; }
uint32_t get_connected_client_count() { return this->connected_clients_; }
@@ -81,8 +82,8 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv
uint32_t connected_clients_{0};
std::unordered_map<uint16_t, void *> clients_;
std::unordered_map<std::string, BLEService *> services_;
BLEService *device_information_service_;
std::unordered_map<std::string, std::shared_ptr<BLEService>> services_;
std::shared_ptr<BLEService> device_information_service_;
std::vector<BLEServiceComponent *> service_components_;

View File

@@ -12,31 +12,33 @@ static const char *const TAG = "esp32_ble_server.service";
BLEService::BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id, bool advertise)
: uuid_(uuid), num_handles_(num_handles), inst_id_(inst_id), advertise_(advertise) {}
BLEService::~BLEService() {
for (auto &chr : this->characteristics_)
delete chr; // NOLINT(cppcoreguidelines-owning-memory)
}
BLECharacteristic *BLEService::get_characteristic(ESPBTUUID uuid) {
for (auto *chr : this->characteristics_) {
std::shared_ptr<BLECharacteristic> BLEService::get_characteristic(ESPBTUUID uuid) {
for (auto chr : this->characteristics_) {
if (chr->get_uuid() == uuid)
return chr;
}
return nullptr;
}
BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) {
std::shared_ptr<BLECharacteristic> BLEService::get_characteristic(uint16_t uuid) {
return this->get_characteristic(ESPBTUUID::from_uint16(uuid));
}
BLECharacteristic *BLEService::create_characteristic(uint16_t uuid, esp_gatt_char_prop_t properties) {
std::shared_ptr<BLECharacteristic> BLEService::create_characteristic(uint16_t uuid, esp_gatt_char_prop_t properties) {
return create_characteristic(ESPBTUUID::from_uint16(uuid), properties);
}
BLECharacteristic *BLEService::create_characteristic(const std::string &uuid, esp_gatt_char_prop_t properties) {
std::shared_ptr<BLECharacteristic> BLEService::create_characteristic(const std::string &uuid,
esp_gatt_char_prop_t properties) {
return create_characteristic(ESPBTUUID::from_raw(uuid), properties);
}
BLECharacteristic *BLEService::create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties) {
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
BLECharacteristic *characteristic = new BLECharacteristic(uuid, properties);
std::shared_ptr<BLECharacteristic> BLEService::create_characteristic(const char *uuid, size_t len,
esp_gatt_char_prop_t properties) {
return create_characteristic(ESPBTUUID::from_raw(uuid, len), properties);
}
std::shared_ptr<BLECharacteristic> BLEService::create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties) {
auto characteristic = std::make_shared<BLECharacteristic>(uuid, properties);
if (characteristic == nullptr) {
return nullptr;
}
this->characteristics_.push_back(characteristic);
return characteristic;
}
@@ -80,9 +82,9 @@ bool BLEService::do_create_characteristics_() {
if (this->last_created_characteristic_ != nullptr && !this->last_created_characteristic_->is_created())
return true; // Signifies that the previous characteristic is still being created.
auto *characteristic = this->characteristics_[this->created_characteristic_count_++];
auto characteristic = this->characteristics_[this->created_characteristic_count_++];
this->last_created_characteristic_ = characteristic;
characteristic->do_create(this);
characteristic->do_create(shared_from_this());
return true;
}
@@ -124,7 +126,7 @@ bool BLEService::is_failed() {
if (this->init_state_ == FAILED)
return true;
bool failed = false;
for (auto *characteristic : this->characteristics_)
for (auto characteristic : this->characteristics_)
failed |= characteristic->is_failed();
if (failed)
@@ -166,7 +168,7 @@ void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t g
break;
}
for (auto *characteristic : this->characteristics_) {
for (auto characteristic : this->characteristics_) {
characteristic->gatts_event_handler(event, gatts_if, param);
}
}

View File

@@ -3,6 +3,7 @@
#include "ble_characteristic.h"
#include "esphome/components/esp32_ble/ble_uuid.h"
#include <memory>
#include <vector>
#ifdef USE_ESP32
@@ -20,19 +21,21 @@ class BLEServer;
using namespace esp32_ble;
class BLEService {
class BLEService : public std::enable_shared_from_this<BLEService> {
public:
BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id, bool advertise);
~BLEService();
BLECharacteristic *get_characteristic(ESPBTUUID uuid);
BLECharacteristic *get_characteristic(uint16_t uuid);
BLECharacteristic *create_characteristic(const std::string &uuid, esp_gatt_char_prop_t properties);
BLECharacteristic *create_characteristic(uint16_t uuid, esp_gatt_char_prop_t properties);
BLECharacteristic *create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties);
std::shared_ptr<BLECharacteristic> get_characteristic(ESPBTUUID uuid);
std::shared_ptr<BLECharacteristic> get_characteristic(uint16_t uuid);
std::shared_ptr<BLECharacteristic> create_characteristic(const char *uuid, size_t len,
esp_gatt_char_prop_t properties);
std::shared_ptr<BLECharacteristic> create_characteristic(const std::string &uuid, esp_gatt_char_prop_t properties);
std::shared_ptr<BLECharacteristic> create_characteristic(uint16_t uuid, esp_gatt_char_prop_t properties);
std::shared_ptr<BLECharacteristic> create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties);
ESPBTUUID get_uuid() { return this->uuid_; }
BLECharacteristic *get_last_created_characteristic() { return this->last_created_characteristic_; }
std::shared_ptr<BLECharacteristic> get_last_created_characteristic() { return this->last_created_characteristic_; }
uint16_t get_handle() { return this->handle_; }
BLEServer *get_server() { return this->server_; }
@@ -52,8 +55,8 @@ class BLEService {
bool is_deleted() { return this->init_state_ == DELETED; }
protected:
std::vector<BLECharacteristic *> characteristics_;
BLECharacteristic *last_created_characteristic_{nullptr};
std::vector<std::shared_ptr<BLECharacteristic>> characteristics_;
std::shared_ptr<BLECharacteristic> last_created_characteristic_{nullptr};
uint32_t created_characteristic_count_{0};
BLEServer *server_;
ESPBTUUID uuid_;

View File

@@ -18,13 +18,16 @@
#include <cinttypes>
#ifdef USE_OTA
#include "esphome/components/ota/ota_component.h"
#include "esphome/components/ota/ota_backend.h"
#endif
#ifdef USE_ARDUINO
#include <esp32-hal-bt.h>
#endif
#define MBEDTLS_AES_ALT
#include <aes_alt.h>
// bt_trace.h
#undef TAG
@@ -58,11 +61,12 @@ void ESP32BLETracker::setup() {
this->scanner_idle_ = true;
#ifdef USE_OTA
ota::global_ota_component->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) {
if (state == ota::OTA_STARTED) {
this->stop_scan();
}
});
ota::get_global_ota_callback()->add_on_state_callback(
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
if (state == ota::OTA_STARTED) {
this->stop_scan();
}
});
#endif
}
@@ -692,6 +696,39 @@ void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
}
}
bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
uint8_t ecb_key[16];
uint8_t ecb_plaintext[16];
uint8_t ecb_ciphertext[16];
uint64_t addr64 = esp32_ble::ble_addr_to_uint64(this->address_);
memcpy(&ecb_key, irk, 16);
memset(&ecb_plaintext, 0, 16);
ecb_plaintext[13] = (addr64 >> 40) & 0xff;
ecb_plaintext[14] = (addr64 >> 32) & 0xff;
ecb_plaintext[15] = (addr64 >> 24) & 0xff;
mbedtls_aes_context ctx = {0, 0, {0}};
mbedtls_aes_init(&ctx);
if (mbedtls_aes_setkey_enc(&ctx, ecb_key, 128) != 0) {
mbedtls_aes_free(&ctx);
return false;
}
if (mbedtls_aes_crypt_ecb(&ctx, ESP_AES_ENCRYPT, ecb_plaintext, ecb_ciphertext) != 0) {
mbedtls_aes_free(&ctx);
return false;
}
mbedtls_aes_free(&ctx);
return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
}
} // namespace esp32_ble_tracker
} // namespace esphome

View File

@@ -86,6 +86,8 @@ class ESPBTDevice {
const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &get_scan_result() const { return scan_result_; }
bool resolve_irk(const uint8_t *irk) const;
optional<ESPBLEiBeacon> get_ibeacon() const {
for (auto &it : this->manufacturer_datas_) {
auto res = ESPBLEiBeacon::from_manufacturer_data(it);

View File

@@ -1,12 +1,13 @@
#include "esp32_improv_component.h"
#include "esphome/components/esp32_ble/ble.h"
#include "esphome/components/esp32_ble_server/ble_2902.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
static constexpr size_t MAX_UUID_LENGTH = 37;
namespace esphome {
namespace esp32_improv {
@@ -28,35 +29,72 @@ void ESP32ImprovComponent::setup() {
#endif
}
void ESP32ImprovComponent::setup_characteristics() {
this->status_ = this->service_->create_characteristic(
improv::STATUS_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor *status_descriptor = new BLE2902();
this->status_->add_descriptor(status_descriptor);
bool ESP32ImprovComponent::setup_characteristics() {
this->status_ =
this->service_->create_characteristic(improv::STATUS_UUID, strnlen(improv::STATUS_UUID, MAX_UUID_LENGTH),
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
if (this->status_ == nullptr) {
ESP_LOGE(TAG, "Failed to create status characteristic");
return false;
}
this->error_ = this->service_->create_characteristic(
improv::ERROR_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor *error_descriptor = new BLE2902();
this->error_->add_descriptor(error_descriptor);
if (!this->status_->make_2902_descriptor()) {
ESP_LOGE(TAG, "Failed to create status descriptor");
return false;
}
this->rpc_ = this->service_->create_characteristic(improv::RPC_COMMAND_UUID, BLECharacteristic::PROPERTY_WRITE);
this->error_ =
this->service_->create_characteristic(improv::ERROR_UUID, strnlen(improv::ERROR_UUID, MAX_UUID_LENGTH),
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
if (this->error_ == nullptr) {
ESP_LOGE(TAG, "Failed to create error characteristic");
return false;
}
if (!this->error_->make_2902_descriptor()) {
ESP_LOGE(TAG, "Failed to create error descriptor");
return false;
}
this->rpc_ = this->service_->create_characteristic(
improv::RPC_COMMAND_UUID, strnlen(improv::RPC_COMMAND_UUID, MAX_UUID_LENGTH), BLECharacteristic::PROPERTY_WRITE);
if (this->rpc_ == nullptr) {
ESP_LOGE(TAG, "Failed to create rpc characteristic");
return false;
}
this->rpc_->on_write([this](const std::vector<uint8_t> &data) {
if (!data.empty()) {
this->incoming_data_.insert(this->incoming_data_.end(), data.begin(), data.end());
}
});
BLEDescriptor *rpc_descriptor = new BLE2902();
this->rpc_->add_descriptor(rpc_descriptor);
if (this->rpc_->make_2902_descriptor() == nullptr) {
ESP_LOGE(TAG, "Failed to create rpc descriptor");
return false;
}
this->rpc_response_ = this->service_->create_characteristic(
improv::RPC_RESULT_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
BLEDescriptor *rpc_response_descriptor = new BLE2902();
this->rpc_response_->add_descriptor(rpc_response_descriptor);
this->rpc_response_ =
this->service_->create_characteristic(improv::RPC_RESULT_UUID, strnlen(improv::RPC_RESULT_UUID, MAX_UUID_LENGTH),
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
if (this->rpc_response_ == nullptr) {
ESP_LOGE(TAG, "Failed to create rpc response characteristic");
return false;
}
if (!this->rpc_response_->make_2902_descriptor()) {
ESP_LOGE(TAG, "Failed to create rpc response descriptor");
return false;
}
this->capabilities_ = this->service_->create_characteristic(
improv::CAPABILITIES_UUID, strnlen(improv::CAPABILITIES_UUID, MAX_UUID_LENGTH), BLECharacteristic::PROPERTY_READ);
if (this->capabilities_ == nullptr) {
ESP_LOGE(TAG, "Failed to create capabilities characteristic");
return false;
}
if (!this->capabilities_->make_2902_descriptor()) {
ESP_LOGE(TAG, "Failed to create capabilities descriptor");
return false;
}
this->capabilities_ =
this->service_->create_characteristic(improv::CAPABILITIES_UUID, BLECharacteristic::PROPERTY_READ);
BLEDescriptor *capabilities_descriptor = new BLE2902();
this->capabilities_->add_descriptor(capabilities_descriptor);
uint8_t capabilities = 0x00;
#ifdef USE_OUTPUT
if (this->status_indicator_ != nullptr)
@@ -64,6 +102,7 @@ void ESP32ImprovComponent::setup_characteristics() {
#endif
this->capabilities_->set_value(capabilities);
this->setup_complete_ = true;
return true;
}
void ESP32ImprovComponent::loop() {
@@ -75,9 +114,11 @@ void ESP32ImprovComponent::loop() {
if (this->service_ == nullptr) {
// Setup the service
ESP_LOGD(TAG, "Creating Improv service");
global_ble_server->create_service(ESPBTUUID::from_raw(improv::SERVICE_UUID), true);
this->service_ = global_ble_server->get_service(ESPBTUUID::from_raw(improv::SERVICE_UUID));
this->setup_characteristics();
this->service_ = global_ble_server->create_service(ESPBTUUID::from_raw(improv::SERVICE_UUID), true);
if (!this->setup_characteristics()) {
this->mark_failed();
return;
}
}
if (!this->incoming_data_.empty())

View File

@@ -34,7 +34,7 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
void dump_config() override;
void loop() override;
void setup() override;
void setup_characteristics();
bool setup_characteristics();
void on_client_disconnect() override;
float get_setup_priority() const override;
@@ -68,12 +68,12 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
std::vector<uint8_t> incoming_data_;
wifi::WiFiAP connecting_sta_;
BLEService *service_ = nullptr;
BLECharacteristic *status_;
BLECharacteristic *error_;
BLECharacteristic *rpc_;
BLECharacteristic *rpc_response_;
BLECharacteristic *capabilities_;
std::shared_ptr<BLEService> service_{nullptr};
std::shared_ptr<BLECharacteristic> status_;
std::shared_ptr<BLECharacteristic> error_;
std::shared_ptr<BLECharacteristic> rpc_;
std::shared_ptr<BLECharacteristic> rpc_response_;
std::shared_ptr<BLECharacteristic> capabilities_;
#ifdef USE_BINARY_SENSOR
binary_sensor::BinarySensor *authorizer_{nullptr};

View File

@@ -6,6 +6,7 @@ from esphome import pins
from esphome.components import esp32_rmt, light
from esphome.const import (
CONF_CHIPSET,
CONF_IS_RGBW,
CONF_MAX_REFRESH_RATE,
CONF_NUM_LEDS,
CONF_OUTPUT_ID,
@@ -52,7 +53,6 @@ CHIPSETS = {
}
CONF_IS_RGBW = "is_rgbw"
CONF_IS_WRGB = "is_wrgb"
CONF_BIT0_HIGH = "bit0_high"
CONF_BIT0_LOW = "bit0_low"

View File

@@ -150,7 +150,7 @@ TOUCH_PAD_WATERPROOF_SHIELD_DRIVER = {
def validate_touch_pad(value):
value = gpio.validate_gpio_pin(value)
value = gpio.gpio_pin_number_validator(value)
variant = get_esp32_variant()
if variant not in TOUCH_PADS:
raise cv.Invalid(f"ESP32 variant {variant} does not support touch pads.")

View File

@@ -0,0 +1,64 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code, OTAComponent
from esphome.const import (
CONF_ID,
CONF_NUM_ATTEMPTS,
CONF_PASSWORD,
CONF_PORT,
CONF_REBOOT_TIMEOUT,
CONF_SAFE_MODE,
CONF_VERSION,
)
from esphome.core import coroutine_with_priority
CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["md5", "socket"]
DEPENDENCIES = ["network"]
esphome = cg.esphome_ns.namespace("esphome")
ESPHomeOTAComponent = esphome.class_("ESPHomeOTAComponent", OTAComponent)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ESPHomeOTAComponent),
cv.Optional(CONF_VERSION, default=2): cv.one_of(1, 2, int=True),
cv.SplitDefault(
CONF_PORT,
esp8266=8266,
esp32=3232,
rp2040=2040,
bk72xx=8892,
rtl87xx=8892,
): cv.port,
cv.Optional(CONF_PASSWORD): cv.string,
cv.Optional(CONF_NUM_ATTEMPTS): cv.invalid(
f"'{CONF_SAFE_MODE}' (and its related configuration variables) has moved from 'ota' to its own component. See https://esphome.io/components/safe_mode"
),
cv.Optional(CONF_REBOOT_TIMEOUT): cv.invalid(
f"'{CONF_SAFE_MODE}' (and its related configuration variables) has moved from 'ota' to its own component. See https://esphome.io/components/safe_mode"
),
cv.Optional(CONF_SAFE_MODE): cv.invalid(
f"'{CONF_SAFE_MODE}' (and its related configuration variables) has moved from 'ota' to its own component. See https://esphome.io/components/safe_mode"
),
}
)
.extend(BASE_OTA_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
@coroutine_with_priority(52.0)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await ota_to_code(var, config)
cg.add(var.set_port(config[CONF_PORT]))
if CONF_PASSWORD in config:
cg.add(var.set_auth_password(config[CONF_PASSWORD]))
cg.add_define("USE_OTA_PASSWORD")
cg.add_define("USE_OTA_VERSION", config[CONF_VERSION])
await cg.register_component(var, config)

View File

@@ -1,55 +1,34 @@
#include "ota_component.h"
#include "ota_backend.h"
#include "ota_backend_arduino_esp32.h"
#include "ota_backend_arduino_esp8266.h"
#include "ota_backend_arduino_rp2040.h"
#include "ota_backend_arduino_libretiny.h"
#include "ota_backend_esp_idf.h"
#include "ota_esphome.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/util.h"
#include "esphome/components/md5/md5.h"
#include "esphome/components/network/util.h"
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_arduino_esp32.h"
#include "esphome/components/ota/ota_backend_arduino_esp8266.h"
#include "esphome/components/ota/ota_backend_arduino_libretiny.h"
#include "esphome/components/ota/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota/ota_backend_esp_idf.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include <cerrno>
#include <cstdio>
namespace esphome {
namespace ota {
static const char *const TAG = "ota";
static const char *const TAG = "esphome.ota";
static constexpr u_int16_t OTA_BLOCK_SIZE = 8192;
OTAComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
std::unique_ptr<OTABackend> make_ota_backend() {
#ifdef USE_ARDUINO
#ifdef USE_ESP8266
return make_unique<ArduinoESP8266OTABackend>();
#endif // USE_ESP8266
#ifdef USE_ESP32
return make_unique<ArduinoESP32OTABackend>();
#endif // USE_ESP32
#endif // USE_ARDUINO
#ifdef USE_ESP_IDF
return make_unique<IDFOTABackend>();
#endif // USE_ESP_IDF
#ifdef USE_RP2040
return make_unique<ArduinoRP2040OTABackend>();
#endif // USE_RP2040
#ifdef USE_LIBRETINY
return make_unique<ArduinoLibreTinyOTABackend>();
void ESPHomeOTAComponent::setup() {
#ifdef USE_OTA_STATE_CALLBACK
ota::register_ota_platform(this);
#endif
}
OTAComponent::OTAComponent() { global_ota_component = this; }
void OTAComponent::setup() {
server_ = socket::socket_ip(SOCK_STREAM, 0);
if (server_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket.");
ESP_LOGW(TAG, "Could not create socket");
this->mark_failed();
return;
}
@@ -88,41 +67,25 @@ void OTAComponent::setup() {
this->mark_failed();
return;
}
this->dump_config();
}
void OTAComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Over-The-Air Updates:");
void ESPHomeOTAComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Over-The-Air updates:");
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
ESP_LOGCONFIG(TAG, " Version: %d", USE_OTA_VERSION);
#ifdef USE_OTA_PASSWORD
if (!this->password_.empty()) {
ESP_LOGCONFIG(TAG, " Using Password.");
ESP_LOGCONFIG(TAG, " Password configured");
}
#endif
ESP_LOGCONFIG(TAG, " OTA version: %d.", USE_OTA_VERSION);
if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1 &&
this->safe_mode_rtc_value_ != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) {
ESP_LOGW(TAG, "Last Boot was an unhandled reset, will proceed to safe mode in %" PRIu32 " restarts",
this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_);
}
}
void OTAComponent::loop() {
this->handle_();
if (this->has_safe_mode_ && (millis() - this->safe_mode_start_time_) > this->safe_mode_enable_time_) {
this->has_safe_mode_ = false;
// successful boot, reset counter
ESP_LOGI(TAG, "Boot seems successful, resetting boot loop counter.");
this->clean_rtc();
}
}
void ESPHomeOTAComponent::loop() { this->handle_(); }
static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
void OTAComponent::handle_() {
OTAResponseTypes error_code = OTA_RESPONSE_ERROR_UNKNOWN;
void ESPHomeOTAComponent::handle_() {
ota::OTAResponseTypes error_code = ota::OTA_RESPONSE_ERROR_UNKNOWN;
bool update_started = false;
size_t total = 0;
uint32_t last_progress = 0;
@@ -130,7 +93,7 @@ void OTAComponent::handle_() {
char *sbuf = reinterpret_cast<char *>(buf);
size_t ota_size;
uint8_t ota_features;
std::unique_ptr<OTABackend> backend;
std::unique_ptr<ota::OTABackend> backend;
(void) ota_features;
#if USE_OTA_VERSION == 2
size_t size_acknowledged = 0;
@@ -147,54 +110,54 @@ void OTAComponent::handle_() {
int enable = 1;
int err = client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
ESP_LOGW(TAG, "Socket could not enable tcp nodelay, errno: %d", errno);
ESP_LOGW(TAG, "Socket could not enable TCP nodelay, errno %d", errno);
return;
}
ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_->getpeername().c_str());
ESP_LOGD(TAG, "Starting update from %s...", this->client_->getpeername().c_str());
this->status_set_warning();
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(OTA_STARTED, 0.0f, 0);
this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0);
#endif
if (!this->readall_(buf, 5)) {
ESP_LOGW(TAG, "Reading magic bytes failed!");
ESP_LOGW(TAG, "Reading magic bytes failed");
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
// 0x6C, 0x26, 0xF7, 0x5C, 0x45
if (buf[0] != 0x6C || buf[1] != 0x26 || buf[2] != 0xF7 || buf[3] != 0x5C || buf[4] != 0x45) {
ESP_LOGW(TAG, "Magic bytes do not match! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", buf[0], buf[1], buf[2], buf[3],
buf[4]);
error_code = OTA_RESPONSE_ERROR_MAGIC;
error_code = ota::OTA_RESPONSE_ERROR_MAGIC;
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
// Send OK and version - 2 bytes
buf[0] = OTA_RESPONSE_OK;
buf[0] = ota::OTA_RESPONSE_OK;
buf[1] = USE_OTA_VERSION;
this->writeall_(buf, 2);
backend = make_ota_backend();
backend = ota::make_ota_backend();
// Read features - 1 byte
if (!this->readall_(buf, 1)) {
ESP_LOGW(TAG, "Reading features failed!");
ESP_LOGW(TAG, "Reading features failed");
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
ota_features = buf[0]; // NOLINT
ESP_LOGV(TAG, "OTA features is 0x%02X", ota_features);
ESP_LOGV(TAG, "Features: 0x%02X", ota_features);
// Acknowledge header - 1 byte
buf[0] = OTA_RESPONSE_HEADER_OK;
buf[0] = ota::OTA_RESPONSE_HEADER_OK;
if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) {
buf[0] = OTA_RESPONSE_SUPPORTS_COMPRESSION;
buf[0] = ota::OTA_RESPONSE_SUPPORTS_COMPRESSION;
}
this->writeall_(buf, 1);
#ifdef USE_OTA_PASSWORD
if (!this->password_.empty()) {
buf[0] = OTA_RESPONSE_REQUEST_AUTH;
buf[0] = ota::OTA_RESPONSE_REQUEST_AUTH;
this->writeall_(buf, 1);
md5::MD5Digest md5{};
md5.init();
@@ -206,7 +169,7 @@ void OTAComponent::handle_() {
// Send nonce, 32 bytes hex MD5
if (!this->writeall_(reinterpret_cast<uint8_t *>(sbuf), 32)) {
ESP_LOGW(TAG, "Auth: Writing nonce failed!");
ESP_LOGW(TAG, "Auth: Writing nonce failed");
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
@@ -218,7 +181,7 @@ void OTAComponent::handle_() {
// Receive cnonce, 32 bytes hex MD5
if (!this->readall_(buf, 32)) {
ESP_LOGW(TAG, "Auth: Reading cnonce failed!");
ESP_LOGW(TAG, "Auth: Reading cnonce failed");
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
sbuf[32] = '\0';
@@ -233,7 +196,7 @@ void OTAComponent::handle_() {
// Receive result, 32 bytes hex MD5
if (!this->readall_(buf + 64, 32)) {
ESP_LOGW(TAG, "Auth: Reading response failed!");
ESP_LOGW(TAG, "Auth: Reading response failed");
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
sbuf[64 + 32] = '\0';
@@ -244,20 +207,20 @@ void OTAComponent::handle_() {
matches = matches && buf[i] == buf[64 + i];
if (!matches) {
ESP_LOGW(TAG, "Auth failed! Passwords do not match!");
error_code = OTA_RESPONSE_ERROR_AUTH_INVALID;
ESP_LOGW(TAG, "Auth failed! Passwords do not match");
error_code = ota::OTA_RESPONSE_ERROR_AUTH_INVALID;
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
}
#endif // USE_OTA_PASSWORD
// Acknowledge auth OK - 1 byte
buf[0] = OTA_RESPONSE_AUTH_OK;
buf[0] = ota::OTA_RESPONSE_AUTH_OK;
this->writeall_(buf, 1);
// Read size, 4 bytes MSB first
if (!this->readall_(buf, 4)) {
ESP_LOGW(TAG, "Reading size failed!");
ESP_LOGW(TAG, "Reading size failed");
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
ota_size = 0;
@@ -265,20 +228,20 @@ void OTAComponent::handle_() {
ota_size <<= 8;
ota_size |= buf[i];
}
ESP_LOGV(TAG, "OTA size is %u bytes", ota_size);
ESP_LOGV(TAG, "Size is %u bytes", ota_size);
error_code = backend->begin(ota_size);
if (error_code != OTA_RESPONSE_OK)
if (error_code != ota::OTA_RESPONSE_OK)
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
update_started = true;
// Acknowledge prepare OK - 1 byte
buf[0] = OTA_RESPONSE_UPDATE_PREPARE_OK;
buf[0] = ota::OTA_RESPONSE_UPDATE_PREPARE_OK;
this->writeall_(buf, 1);
// Read binary MD5, 32 bytes
if (!this->readall_(buf, 32)) {
ESP_LOGW(TAG, "Reading binary MD5 checksum failed!");
ESP_LOGW(TAG, "Reading binary MD5 checksum failed");
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
sbuf[32] = '\0';
@@ -286,7 +249,7 @@ void OTAComponent::handle_() {
backend->set_update_md5(sbuf);
// Acknowledge MD5 OK - 1 byte
buf[0] = OTA_RESPONSE_BIN_MD5_OK;
buf[0] = ota::OTA_RESPONSE_BIN_MD5_OK;
this->writeall_(buf, 1);
while (total < ota_size) {
@@ -299,7 +262,7 @@ void OTAComponent::handle_() {
delay(1);
continue;
}
ESP_LOGW(TAG, "Error receiving data for update, errno: %d", errno);
ESP_LOGW(TAG, "Error receiving data for update, errno %d", errno);
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
} else if (read == 0) {
// $ man recv
@@ -310,14 +273,14 @@ void OTAComponent::handle_() {
}
error_code = backend->write(buf, read);
if (error_code != OTA_RESPONSE_OK) {
if (error_code != ota::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Error writing binary data to flash!, error_code: %d", error_code);
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
total += read;
#if USE_OTA_VERSION == 2
while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) {
buf[0] = OTA_RESPONSE_CHUNK_OK;
buf[0] = ota::OTA_RESPONSE_CHUNK_OK;
this->writeall_(buf, 1);
size_acknowledged += OTA_BLOCK_SIZE;
}
@@ -327,9 +290,9 @@ void OTAComponent::handle_() {
if (now - last_progress > 1000) {
last_progress = now;
float percentage = (total * 100.0f) / ota_size;
ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage);
ESP_LOGD(TAG, "Progress: %0.1f%%", percentage);
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(OTA_IN_PROGRESS, percentage, 0);
this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0);
#endif
// feed watchdog and give other tasks a chance to run
App.feed_wdt();
@@ -338,32 +301,32 @@ void OTAComponent::handle_() {
}
// Acknowledge receive OK - 1 byte
buf[0] = OTA_RESPONSE_RECEIVE_OK;
buf[0] = ota::OTA_RESPONSE_RECEIVE_OK;
this->writeall_(buf, 1);
error_code = backend->end();
if (error_code != OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Error ending OTA!, error_code: %d", error_code);
if (error_code != ota::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Error ending update! error_code: %d", error_code);
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
// Acknowledge Update end OK - 1 byte
buf[0] = OTA_RESPONSE_UPDATE_END_OK;
buf[0] = ota::OTA_RESPONSE_UPDATE_END_OK;
this->writeall_(buf, 1);
// Read ACK
if (!this->readall_(buf, 1) || buf[0] != OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Reading back acknowledgement failed!");
if (!this->readall_(buf, 1) || buf[0] != ota::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Reading back acknowledgement failed");
// do not go to error, this is not fatal
}
this->client_->close();
this->client_ = nullptr;
delay(10);
ESP_LOGI(TAG, "OTA update finished!");
ESP_LOGI(TAG, "Update complete");
this->status_clear_warning();
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(OTA_COMPLETED, 100.0f, 0);
this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, 0);
#endif
delay(100); // NOLINT
App.safe_reboot();
@@ -380,11 +343,11 @@ error:
this->status_momentary_error("onerror", 5000);
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
this->state_callback_.call(ota::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
#endif
}
bool OTAComponent::readall_(uint8_t *buf, size_t len) {
bool ESPHomeOTAComponent::readall_(uint8_t *buf, size_t len) {
uint32_t start = millis();
uint32_t at = 0;
while (len - at > 0) {
@@ -401,7 +364,7 @@ bool OTAComponent::readall_(uint8_t *buf, size_t len) {
delay(1);
continue;
}
ESP_LOGW(TAG, "Failed to read %d bytes of data, errno: %d", len, errno);
ESP_LOGW(TAG, "Failed to read %d bytes of data, errno %d", len, errno);
return false;
} else if (read == 0) {
ESP_LOGW(TAG, "Remote closed connection");
@@ -415,7 +378,7 @@ bool OTAComponent::readall_(uint8_t *buf, size_t len) {
return true;
}
bool OTAComponent::writeall_(const uint8_t *buf, size_t len) {
bool ESPHomeOTAComponent::writeall_(const uint8_t *buf, size_t len) {
uint32_t start = millis();
uint32_t at = 0;
while (len - at > 0) {
@@ -432,7 +395,7 @@ bool OTAComponent::writeall_(const uint8_t *buf, size_t len) {
delay(1);
continue;
}
ESP_LOGW(TAG, "Failed to write %d bytes of data, errno: %d", len, errno);
ESP_LOGW(TAG, "Failed to write %d bytes of data, errno %d", len, errno);
return false;
} else {
at += written;
@@ -443,93 +406,7 @@ bool OTAComponent::writeall_(const uint8_t *buf, size_t len) {
return true;
}
float OTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
uint16_t OTAComponent::get_port() const { return this->port_; }
void OTAComponent::set_port(uint16_t port) { this->port_ = port; }
void OTAComponent::set_safe_mode_pending(const bool &pending) {
if (!this->has_safe_mode_)
return;
uint32_t current_rtc = this->read_rtc_();
if (pending && current_rtc != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) {
ESP_LOGI(TAG, "Device will enter safe mode on next boot.");
this->write_rtc_(esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC);
}
if (!pending && current_rtc == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) {
ESP_LOGI(TAG, "Safe mode pending has been cleared");
this->clean_rtc();
}
}
bool OTAComponent::get_safe_mode_pending() {
return this->has_safe_mode_ && this->read_rtc_() == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC;
}
bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time) {
this->has_safe_mode_ = true;
this->safe_mode_start_time_ = millis();
this->safe_mode_enable_time_ = enable_time;
this->safe_mode_num_attempts_ = num_attempts;
this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false);
this->safe_mode_rtc_value_ = this->read_rtc_();
bool is_manual_safe_mode = this->safe_mode_rtc_value_ == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC;
if (is_manual_safe_mode) {
ESP_LOGI(TAG, "Safe mode has been entered manually");
} else {
ESP_LOGCONFIG(TAG, "There have been %" PRIu32 " suspected unsuccessful boot attempts.", this->safe_mode_rtc_value_);
}
if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) {
this->clean_rtc();
if (!is_manual_safe_mode) {
ESP_LOGE(TAG, "Boot loop detected. Proceeding to safe mode.");
}
this->status_set_error();
this->set_timeout(enable_time, []() {
ESP_LOGE(TAG, "No OTA attempt made, restarting.");
App.reboot();
});
// Delay here to allow power to stabilise before Wi-Fi/Ethernet is initialised.
delay(300); // NOLINT
App.setup();
ESP_LOGI(TAG, "Waiting for OTA attempt.");
return true;
} else {
// increment counter
this->write_rtc_(this->safe_mode_rtc_value_ + 1);
return false;
}
}
void OTAComponent::write_rtc_(uint32_t val) {
this->rtc_.save(&val);
global_preferences->sync();
}
uint32_t OTAComponent::read_rtc_() {
uint32_t val;
if (!this->rtc_.load(&val))
return 0;
return val;
}
void OTAComponent::clean_rtc() { this->write_rtc_(0); }
void OTAComponent::on_safe_shutdown() {
if (this->has_safe_mode_ && this->read_rtc_() != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC)
this->clean_rtc();
}
#ifdef USE_OTA_STATE_CALLBACK
void OTAComponent::add_on_state_callback(std::function<void(OTAState, float, uint8_t)> &&callback) {
this->state_callback_.add(std::move(callback));
}
#endif
} // namespace ota
float ESPHomeOTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
uint16_t ESPHomeOTAComponent::get_port() const { return this->port_; }
void ESPHomeOTAComponent::set_port(uint16_t port) { this->port_ = port; }
} // namespace esphome

View File

@@ -0,0 +1,43 @@
#pragma once
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/socket/socket.h"
namespace esphome {
/// ESPHomeOTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA.
class ESPHomeOTAComponent : public ota::OTAComponent {
public:
#ifdef USE_OTA_PASSWORD
void set_auth_password(const std::string &password) { password_ = password; }
#endif // USE_OTA_PASSWORD
/// Manually set the port OTA should listen on
void set_port(uint16_t port);
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void loop() override;
uint16_t get_port() const;
protected:
void handle_();
bool readall_(uint8_t *buf, size_t len);
bool writeall_(const uint8_t *buf, size_t len);
#ifdef USE_OTA_PASSWORD
std::string password_;
#endif // USE_OTA_PASSWORD
uint16_t port_;
std::unique_ptr<socket::Socket> server_;
std::unique_ptr<socket::Socket> client_;
};
} // namespace esphome

View File

@@ -1,12 +1,12 @@
#include "ethernet_component.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
#include "esphome/core/application.h"
#ifdef USE_ESP32
#include <cinttypes>
#include <lwip/dns.h>
#include <cinttypes>
#include "esp_event.h"
#ifdef USE_ETHERNET_SPI
@@ -184,6 +184,10 @@ void EthernetComponent::setup() {
// KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide.
this->ksz8081_set_clock_reference_(mac);
}
if (this->type_ == ETHERNET_TYPE_RTL8201 && this->clk_mode_ == EMAC_CLK_EXT_IN) {
// Change in default behavior of RTL8201FI may require register setting to enable external clock
this->rtl8201_set_rmii_mode_(mac);
}
#endif
// use ESP internal eth mac
@@ -554,9 +558,10 @@ bool EthernetComponent::powerdown() {
}
#ifndef USE_ETHERNET_SPI
void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
#define KSZ80XX_PC2R_REG_ADDR (0x1F)
constexpr uint8_t KSZ80XX_PC2R_REG_ADDR = 0x1F;
void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
esp_err_t err;
uint32_t phy_control_2;
@@ -581,9 +586,47 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str());
}
#undef KSZ80XX_PC2R_REG_ADDR
}
constexpr uint8_t RTL8201_RMSR_REG_ADDR = 0x10;
void EthernetComponent::rtl8201_set_rmii_mode_(esp_eth_mac_t *mac) {
esp_err_t err;
uint32_t phy_rmii_mode;
err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x07);
ESPHL_ERROR_CHECK(err, "Setting Page 7 failed");
/*
* RTL8201 RMII Mode Setting Register (RMSR)
* Page 7 Register 16
*
* bit 0 Reserved 0
* bit 1 Rg_rmii_rxdsel 1 (default)
* bit 2 Rg_rmii_rxdv_sel: 0 (default)
* bit 3 RMII Mode: 1 (RMII Mode)
* bit 4~7 Rg_rmii_rx_offset: 1111 (default)
* bit 8~11 Rg_rmii_tx_offset: 1111 (default)
* bit 12 Rg_rmii_clkdir: 1 (Input)
* bit 13~15 Reserved 000
*
* Binary: 0001 1111 1111 1010
* Hex: 0x1FFA
*
*/
err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode));
ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed");
ESP_LOGV(TAG, "Hardware default RTL8201 RMII Mode Register is: 0x%04X", phy_rmii_mode);
err = mac->write_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, 0x1FFA);
ESPHL_ERROR_CHECK(err, "Setting Register 16 RMII Mode Setting failed");
err = mac->read_phy_reg(mac, this->phy_addr_, RTL8201_RMSR_REG_ADDR, &(phy_rmii_mode));
ESPHL_ERROR_CHECK(err, "Read PHY RMSR Register failed");
ESP_LOGV(TAG, "Setting RTL8201 RMII Mode Register to: 0x%04X", phy_rmii_mode);
err = mac->write_phy_reg(mac, this->phy_addr_, 0x1f, 0x0);
ESPHL_ERROR_CHECK(err, "Setting Page 0 failed");
}
#endif
} // namespace ethernet

View File

@@ -86,6 +86,8 @@ class EthernetComponent : public Component {
void dump_connect_params_();
/// @brief Set `RMII Reference Clock Select` bit for KSZ8081.
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
/// @brief Set `RMII Mode Setting Register` for RTL8201.
void rtl8201_set_rmii_mode_(esp_eth_mac_t *mac);
std::string use_address_;
#ifdef USE_ETHERNET_SPI

View File

@@ -0,0 +1,32 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c
from esphome.const import CONF_ID
CODEOWNERS = ["@Szewcson"]
DEPENDENCIES = ["i2c"]
MULTI_CONF = True
CONF_GDK101_ID = "gdk101_id"
gdk101_ns = cg.esphome_ns.namespace("gdk101")
GDK101Component = gdk101_ns.class_(
"GDK101Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(GDK101Component),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x18))
)
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)

View File

@@ -0,0 +1,29 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import (
CONF_VIBRATIONS,
DEVICE_CLASS_VIBRATION,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_VIBRATE,
)
from . import CONF_GDK101_ID, GDK101Component
DEPENDENCIES = ["gdk101"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component),
cv.Required(CONF_VIBRATIONS): binary_sensor.binary_sensor_schema(
device_class=DEVICE_CLASS_VIBRATION,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
icon=ICON_VIBRATE,
),
}
)
async def to_code(config):
hub = await cg.get_variable(config[CONF_GDK101_ID])
var = await binary_sensor.new_binary_sensor(config[CONF_VIBRATIONS])
cg.add(hub.set_vibration_binary_sensor(var))

View File

@@ -0,0 +1,189 @@
#include "gdk101.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace gdk101 {
static const char *const TAG = "gdk101";
static const uint8_t NUMBER_OF_READ_RETRIES = 5;
void GDK101Component::update() {
uint8_t data[2];
if (!this->read_dose_1m_(data)) {
this->status_set_warning("Failed to read dose 1m");
return;
}
if (!this->read_dose_10m_(data)) {
this->status_set_warning("Failed to read dose 10m");
return;
}
if (!this->read_status_(data)) {
this->status_set_warning("Failed to read status");
return;
}
if (!this->read_measurement_duration_(data)) {
this->status_set_warning("Failed to read measurement duration");
return;
}
this->status_clear_warning();
}
void GDK101Component::setup() {
uint8_t data[2];
ESP_LOGCONFIG(TAG, "Setting up GDK101...");
// first, reset the sensor
if (!this->reset_sensor_(data)) {
this->status_set_error("Reset failed!");
this->mark_failed();
return;
}
// sensor should acknowledge success of the reset procedure
if (data[0] != 1) {
this->status_set_error("Reset not acknowledged!");
this->mark_failed();
return;
}
delay(10);
// read firmware version
if (!this->read_fw_version_(data)) {
this->status_set_error("Failed to read firmware version");
this->mark_failed();
return;
}
}
void GDK101Component::dump_config() {
ESP_LOGCONFIG(TAG, "GDK101:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with GDK101 failed!");
}
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Firmware Version", this->fw_version_sensor_);
LOG_SENSOR(" ", "Average Radaition Dose per 1 minute", this->rad_1m_sensor_);
LOG_SENSOR(" ", "Average Radaition Dose per 10 minutes", this->rad_10m_sensor_);
LOG_SENSOR(" ", "Status", this->status_sensor_);
LOG_SENSOR(" ", "Measurement Duration", this->measurement_duration_sensor_);
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "Vibration Status", this->vibration_binary_sensor_);
#endif // USE_BINARY_SENSOR
}
float GDK101Component::get_setup_priority() const { return setup_priority::DATA; }
bool GDK101Component::read_bytes_with_retry_(uint8_t a_register, uint8_t *data, uint8_t len) {
uint8_t retry = NUMBER_OF_READ_RETRIES;
bool status = false;
while (!status && retry) {
status = this->read_bytes(a_register, data, len);
retry--;
}
return status;
}
bool GDK101Component::reset_sensor_(uint8_t *data) {
// It looks like reset is not so well designed in that sensor
// After sending reset command it looks that sensor start performing reset and is unresponsible during read
// after a while we can send another reset command and read "0x01" as confirmation
// Documentation not going in to such details unfortunately
if (!this->read_bytes_with_retry_(GDK101_REG_RESET, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
return true;
}
bool GDK101Component::read_dose_1m_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->rad_1m_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_1MIN_AVG, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float dose = data[0] + (data[1] / 100.0f);
this->rad_1m_sensor_->publish_state(dose);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_dose_10m_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->rad_10m_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_10MIN_AVG, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float dose = data[0] + (data[1] / 100.0f);
this->rad_10m_sensor_->publish_state(dose);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_status_(uint8_t *data) {
if (!this->read_bytes(GDK101_REG_READ_STATUS, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
#ifdef USE_SENSOR
if (this->status_sensor_ != nullptr) {
this->status_sensor_->publish_state(data[0]);
}
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
if (this->vibration_binary_sensor_ != nullptr) {
this->vibration_binary_sensor_->publish_state(data[1]);
}
#endif // USE_BINARY_SENSOR
return true;
}
bool GDK101Component::read_fw_version_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->fw_version_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_FIRMWARE, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float fw_version = data[0] + (data[1] / 10.0f);
this->fw_version_sensor_->publish_state(fw_version);
}
#endif // USE_SENSOR
return true;
}
bool GDK101Component::read_measurement_duration_(uint8_t *data) {
#ifdef USE_SENSOR
if (this->measurement_duration_sensor_ != nullptr) {
if (!this->read_bytes(GDK101_REG_READ_MEASURING_TIME, data, 2)) {
ESP_LOGE(TAG, "Updating GDK101 failed!");
return false;
}
const float meas_time = (data[0] * 60) + data[1];
this->measurement_duration_sensor_->publish_state(meas_time);
}
#endif // USE_SENSOR
return true;
}
} // namespace gdk101
} // namespace esphome

View File

@@ -0,0 +1,52 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
#endif // USE_BINARY_SENSOR
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace gdk101 {
static const uint8_t GDK101_REG_READ_FIRMWARE = 0xB4; // Firmware version
static const uint8_t GDK101_REG_RESET = 0xA0; // Reset register - reading its value triggers reset
static const uint8_t GDK101_REG_READ_STATUS = 0xB0; // Status register
static const uint8_t GDK101_REG_READ_MEASURING_TIME = 0xB1; // Mesuring time
static const uint8_t GDK101_REG_READ_10MIN_AVG = 0xB2; // Average radiation dose per 10 min
static const uint8_t GDK101_REG_READ_1MIN_AVG = 0xB3; // Average radiation dose per 1 min
class GDK101Component : public PollingComponent, public i2c::I2CDevice {
#ifdef USE_SENSOR
SUB_SENSOR(rad_1m)
SUB_SENSOR(rad_10m)
SUB_SENSOR(status)
SUB_SENSOR(fw_version)
SUB_SENSOR(measurement_duration)
#endif // USE_SENSOR
#ifdef USE_BINARY_SENSOR
SUB_BINARY_SENSOR(vibration)
#endif // USE_BINARY_SENSOR
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:
bool read_bytes_with_retry_(uint8_t a_register, uint8_t *data, uint8_t len);
bool reset_sensor_(uint8_t *data);
bool read_dose_1m_(uint8_t *data);
bool read_dose_10m_(uint8_t *data);
bool read_status_(uint8_t *data);
bool read_fw_version_(uint8_t *data);
bool read_measurement_duration_(uint8_t *data);
};
} // namespace gdk101
} // namespace esphome

View File

@@ -0,0 +1,83 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
DEVICE_CLASS_DURATION,
DEVICE_CLASS_EMPTY,
ENTITY_CATEGORY_DIAGNOSTIC,
CONF_MEASUREMENT_DURATION,
CONF_STATUS,
CONF_VERSION,
ICON_RADIOACTIVE,
ICON_TIMER,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_MICROSILVERTS_PER_HOUR,
UNIT_SECOND,
)
from . import CONF_GDK101_ID, GDK101Component
CONF_RADIATION_DOSE_PER_1M = "radiation_dose_per_1m"
CONF_RADIATION_DOSE_PER_10M = "radiation_dose_per_10m"
DEPENDENCIES = ["gdk101"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component),
cv.Optional(CONF_RADIATION_DOSE_PER_1M): sensor.sensor_schema(
icon=ICON_RADIOACTIVE,
unit_of_measurement=UNIT_MICROSILVERTS_PER_HOUR,
accuracy_decimals=2,
device_class=DEVICE_CLASS_EMPTY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_RADIATION_DOSE_PER_10M): sensor.sensor_schema(
icon=ICON_RADIOACTIVE,
unit_of_measurement=UNIT_MICROSILVERTS_PER_HOUR,
accuracy_decimals=2,
device_class=DEVICE_CLASS_EMPTY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_VERSION): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
accuracy_decimals=1,
),
cv.Optional(CONF_STATUS): sensor.sensor_schema(
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
accuracy_decimals=0,
),
cv.Optional(CONF_MEASUREMENT_DURATION): sensor.sensor_schema(
unit_of_measurement=UNIT_SECOND,
icon=ICON_TIMER,
accuracy_decimals=0,
state_class=STATE_CLASS_TOTAL_INCREASING,
device_class=DEVICE_CLASS_DURATION,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)
async def to_code(config):
hub = await cg.get_variable(config[CONF_GDK101_ID])
if radiation_dose_per_1m := config.get(CONF_RADIATION_DOSE_PER_1M):
sens = await sensor.new_sensor(radiation_dose_per_1m)
cg.add(hub.set_rad_1m_sensor(sens))
if radiation_dose_per_10m := config.get(CONF_RADIATION_DOSE_PER_10M):
sens = await sensor.new_sensor(radiation_dose_per_10m)
cg.add(hub.set_rad_10m_sensor(sens))
if version_config := config.get(CONF_VERSION):
sens = await sensor.new_sensor(version_config)
cg.add(hub.set_fw_version_sensor(sens))
if status_config := config.get(CONF_STATUS):
sens = await sensor.new_sensor(status_config)
cg.add(hub.set_status_sensor(sens))
if measurement_duration_config := config.get(CONF_MEASUREMENT_DURATION):
sens = await sensor.new_sensor(measurement_duration_config)
cg.add(hub.set_measurement_duration_sensor(sens))

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