1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-04 09:01:49 +00:00

Compare commits

..

80 Commits

Author SHA1 Message Date
Jesse Hills
d72ab25d46 Merge pull request #6804 from esphome/bump-2024.5.3
2024.5.3
2024-05-25 08:24:38 +12:00
Jesse Hills
af755380b7 Bump version to 2024.5.3 2024-05-25 08:14:39 +12:00
Jesse Hills
04db724295 [voice_assistant] Don't allocate buffers until starting the microphone for the first time (#6800) 2024-05-25 08:14:39 +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
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
Jesse Hills
caa8c820de Merge pull request #6753 from esphome/bump-2024.5.0
2024.5.0
2024-05-15 18:27:28 +12:00
Jesse Hills
0d3adc8f0c Bump version to 2024.5.0 2024-05-15 17:08:43 +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
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
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
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
0883f0efd7 Merge pull request #6708 from esphome/bump-2024.5.0b2
2024.5.0b2
2024-05-09 17:29:33 +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
Jesse Hills
e027c6248a Merge pull request #6697 from esphome/bump-2024.5.0b1
2024.5.0b1
2024-05-08 14:08:15 +12:00
Jesse Hills
bd8ccde862 Bump version to 2024.5.0b1 2024-05-08 13:22:04 +12:00
Jesse Hills
24aac10abe Merge branch 'release' into dev 2024-05-08 13:21:39 +12:00
esphomebot
d9fca585a2 Update webserver local assets to 20240507-231331 (#6696) 2024-05-08 11:57:03 +12:00
Trent Houliston
b545d57236 Make pulse_meter PULSE filter report the pulse as soon as it can (#6014) 2024-05-08 10:13:15 +12:00
Keith Burzinski
f6a3784eba Consolidate test files where all tests are identical (#6690) 2024-05-08 07:33:37 +12:00
Clyde Stubbs
829bfbdaa4 Migrate some constants to core code (#6692) 2024-05-08 07:26:04 +12:00
Samuel Sieb
5edf4970bd proceed if AP mode is set up (#6631) 2024-05-06 20:44:36 -07:00
RFDarter
1e196bac98 fix date_time validation (#6688)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-07 12:47:07 +12:00
Jesse Hills
7b0536fda3 Bump esphome/ESPAsyncWebServer-esphome to 3.2.0 (#6687) 2024-05-07 11:54:01 +12:00
RFDarter
5ee2a5f935 Fix Datetime-Datetime compiler error (#6686)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-07 10:44:49 +12:00
dependabot[bot]
594769be3c Bump actions/checkout from 4.1.1 to 4.1.5 (#6685)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 09:09:04 +12:00
tomaszduda23
8463f897e1 fix conflict with EMPTY macro in zephyr (#6679) 2024-05-07 07:20:01 +12:00
Markus
d1758a46bd Use clang-apply-replacements when clang-apply-replacements-14 does not exist (#6684) 2024-05-07 07:17:03 +12:00
Tomek Wasilczyk
f2caaf85c8 External components: optional configurable path for git source (#6677)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-06 15:19:13 +12:00
Anton Viktorov
599dbf27e0 Minor tidy up of BME280 code (#6672)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-06 14:19:25 +12:00
mkmer
833d31ef7a Add fast update to HMC5883L (#6669) 2024-05-06 10:48:09 +12:00
Edward Firmo
f78397c77e Fix recent definitions into defines.h (#6667)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2024-05-06 10:12:09 +12:00
tomaszduda23
8796a4c1a7 print task name if logger is called from other than main thread (#6630) 2024-05-06 10:10:49 +12:00
tomaszduda23
f1584205af [core] Rename ALWAYS_INLINE to ESPHOME_ALWAYS_INLINE (#6636) 2024-05-06 07:52:47 +12:00
Jesse Hills
ccbf5148aa Set "CONF_" CI counter to fail on 3 or more definitions (#6668) 2024-05-05 00:32:47 -05:00
Anton Viktorov
c7c0d97a5e SPI and I2C for BMP390 and BMP380 (#6652)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-02 13:49:01 +12:00
tronikos
bc65e6e914 Make fast update intervals in qmc5883l work (#6647)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-02 13:24:18 +12:00
Mat931
1b9a30e921 Remote receiver improvements (#4642)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-02 13:21:57 +12:00
tronikos
539c369eea Add a function to return the loop_interval (#6666) 2024-05-02 12:39:15 +12:00
Edward Firmo
a4a23d73b3 [nextion] Use persistent http connection for TFT upload (ESP-IDF) (#6576)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-02 10:05:37 +12:00
Keith Burzinski
5ddad26476 Extend and consolidate script tests (#6663) 2024-05-02 07:17:11 +12:00
Keith Burzinski
c69cdec052 Extend MQTT tests (#6648) 2024-05-01 16:49:20 +12:00
Edward Firmo
c299dff124 [nextion] Use persistent http connection for TFT upload (Arduino) (#6582)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-05-01 09:43:49 +12:00
Anton Sergunov
6fe328ef2b [TM1637] Let turn off the display (#6656)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-04-30 22:35:41 +12:00
Edward Firmo
74fd52e05f [nextion] Set alternative TFT update baud rate (#6587) 2024-04-30 21:29:57 +12:00
Jesse Hills
48fa549042 Merge pull request #6660 from esphome/bump-2024.4.2
2024.4.2
2024-04-30 16:30:03 +12:00
Jesse Hills
516971a255 Bump version to 2024.4.2 2024-04-30 15:47:40 +12:00
Jesse Hills
4936cbec0d [i2s_audio.microphone] Fixing adc bug (#6654) 2024-04-30 15:47:40 +12:00
tronikos
9832fa4d76 Revert #6458 (#6650)
Reading the z-axis register is required.
2024-04-30 15:47:40 +12:00
Samuel Sieb
5838af646b allow defaults with no include vars (#6613) 2024-04-30 15:47:40 +12:00
mrtoy-me
33e9881830 Fix SHT3xd fails sometimes in 2024.4.0 (#6592)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2024-04-30 15:47:39 +12:00
990 changed files with 6464 additions and 14262 deletions

View File

@@ -57,14 +57,6 @@ runs:
digest="${{ steps.build-ghcr.outputs.digest }}" digest="${{ steps.build-ghcr.outputs.digest }}"
touch "/tmp/digests/${{ inputs.target }}/ghcr/${digest#sha256:}" 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 - name: Build and push to dockerhub by digest
id: build-dockerhub id: build-dockerhub
uses: docker/build-push-action@v5.3.0 uses: docker/build-push-action@v5.3.0
@@ -87,11 +79,3 @@ runs:
mkdir -p /tmp/digests/${{ inputs.target }}/dockerhub mkdir -p /tmp/digests/${{ inputs.target }}/dockerhub
digest="${{ steps.build-dockerhub.outputs.digest }}" digest="${{ steps.build-dockerhub.outputs.digest }}"
touch "/tmp/digests/${{ inputs.target }}/dockerhub/${digest#sha256:}" 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 runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.1.0 uses: actions/setup-python@v5.1.0
with: with:

View File

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

View File

@@ -34,7 +34,7 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }} cache-key: ${{ steps.cache-key.outputs.key }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Generate cache-key - name: Generate cache-key
id: cache-key id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
@@ -66,7 +66,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -87,7 +87,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -108,7 +108,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -129,7 +129,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -150,7 +150,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -199,7 +199,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -229,7 +229,7 @@ jobs:
- common - common
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -254,7 +254,7 @@ jobs:
matrix: ${{ steps.set-matrix.outputs.matrix }} matrix: ${{ steps.set-matrix.outputs.matrix }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Find all YAML test files - name: Find all YAML test files
id: set-matrix id: set-matrix
run: echo "matrix=$(ls tests/test*.yaml | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT 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) }} file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -303,7 +303,7 @@ jobs:
file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }} file: ${{ fromJson(needs.compile-tests-list.outputs.matrix) }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -358,7 +358,7 @@ jobs:
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -402,7 +402,7 @@ jobs:
count: ${{ steps.list-components.outputs.count }} count: ${{ steps.list-components.outputs.count }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
with: with:
# Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works. # Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
fetch-depth: 500 fetch-depth: 500
@@ -450,7 +450,7 @@ jobs:
run: sudo apt-get install libsodium-dev run: sudo apt-get install libsodium-dev
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -476,7 +476,7 @@ jobs:
matrix: ${{ steps.split.outputs.components }} matrix: ${{ steps.split.outputs.components }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Split components into 20 groups - name: Split components into 20 groups
id: split id: split
run: | run: |
@@ -504,7 +504,7 @@ jobs:
run: sudo apt-get install libsodium-dev run: sudo apt-get install libsodium-dev
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.5
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:

View File

@@ -19,7 +19,7 @@ jobs:
tag: ${{ steps.tag.outputs.tag }} tag: ${{ steps.tag.outputs.tag }}
branch_build: ${{ steps.tag.outputs.branch_build }} branch_build: ${{ steps.tag.outputs.branch_build }}
steps: steps:
- uses: actions/checkout@v4.1.1 - uses: actions/checkout@v4.1.5
- name: Get tag - name: Get tag
id: tag id: tag
# yamllint disable rule:line-length # yamllint disable rule:line-length
@@ -51,7 +51,7 @@ jobs:
contents: read contents: read
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v4.1.1 - uses: actions/checkout@v4.1.5
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.1.0 uses: actions/setup-python@v5.1.0
with: with:
@@ -81,7 +81,7 @@ jobs:
- linux/arm/v7 - linux/arm/v7
- linux/arm64 - linux/arm64
steps: steps:
- uses: actions/checkout@v4.1.1 - uses: actions/checkout@v4.1.5
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.1.0 uses: actions/setup-python@v5.1.0
with: with:
@@ -132,6 +132,19 @@ jobs:
suffix: lint suffix: lint
version: ${{ needs.init.outputs.tag }} 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: deploy-manifest:
name: Publish ESPHome ${{ matrix.image.title }} to ${{ matrix.registry }} name: Publish ESPHome ${{ matrix.image.title }} to ${{ matrix.registry }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -159,12 +172,15 @@ jobs:
- ghcr - ghcr
- dockerhub - dockerhub
steps: steps:
- uses: actions/checkout@v4.1.1 - uses: actions/checkout@v4.1.5
- name: Download digests - name: Download digests
uses: actions/download-artifact@v3.0.2 uses: actions/download-artifact@v4.1.7
with: with:
name: digests-${{ matrix.image.target }}-${{ matrix.registry }} pattern: digests-*
path: /tmp/digests path: /tmp/digests
merge-multiple: true
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.3.0 uses: docker/setup-buildx-action@v3.3.0
@@ -195,7 +211,7 @@ jobs:
done done
- name: Create manifest list and push - name: Create manifest list and push
working-directory: /tmp/digests working-directory: /tmp/digests/${{ matrix.image.target }}/${{ matrix.registry }}
run: | run: |
docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \ docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *) $(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)

View File

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

View File

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

View File

@@ -63,7 +63,10 @@ esphome/components/bme280_base/* @esphome/core
esphome/components/bme280_spi/* @apbodrov esphome/components/bme280_spi/* @apbodrov
esphome/components/bme680_bsec/* @trvrnrth esphome/components/bme680_bsec/* @trvrnrth
esphome/components/bmi160/* @flaviut esphome/components/bmi160/* @flaviut
esphome/components/bmp3xx/* @martgras esphome/components/bmp3xx/* @latonita
esphome/components/bmp3xx_base/* @latonita @martgras
esphome/components/bmp3xx_i2c/* @latonita
esphome/components/bmp3xx_spi/* @latonita
esphome/components/bmp581/* @kahrendt esphome/components/bmp581/* @kahrendt
esphome/components/bp1658cj/* @Cossid esphome/components/bp1658cj/* @Cossid
esphome/components/bp5758d/* @Cossid esphome/components/bp5758d/* @Cossid
@@ -242,7 +245,7 @@ esphome/components/mpl3115a2/* @kbickar
esphome/components/mpu6886/* @fabaff esphome/components/mpu6886/* @fabaff
esphome/components/ms8607/* @e28eta esphome/components/ms8607/* @e28eta
esphome/components/network/* @esphome/core esphome/components/network/* @esphome/core
esphome/components/nextion/* @senexcrenshaw esphome/components/nextion/* @edwardtfn @senexcrenshaw
esphome/components/nextion/binary_sensor/* @senexcrenshaw esphome/components/nextion/binary_sensor/* @senexcrenshaw
esphome/components/nextion/sensor/* @senexcrenshaw esphome/components/nextion/sensor/* @senexcrenshaw
esphome/components/nextion/switch/* @senexcrenshaw esphome/components/nextion/switch/* @senexcrenshaw

View File

@@ -346,7 +346,7 @@ def upload_program(config, args, host):
not is_ip_address(CORE.address) # pylint: disable=too-many-boolean-expressions 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 (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED])
and CONF_MQTT in config 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 from esphome import mqtt

View File

@@ -18,11 +18,23 @@ from esphome.components.esp32.const import (
CODEOWNERS = ["@esphome/core"] 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 = { ATTENUATION_MODES = {
"0db": cg.global_ns.ADC_ATTEN_DB_0, "0db": cg.global_ns.ADC_ATTEN_DB_0,
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5, "2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
"6db": cg.global_ns.ADC_ATTEN_DB_6, "6db": cg.global_ns.ADC_ATTEN_DB_6,
"11db": cg.global_ns.ADC_ATTEN_DB_11, "11db": adc_ns.ADC_ATTEN_DB_12_COMPAT,
"12db": adc_ns.ADC_ATTEN_DB_12_COMPAT,
"auto": "auto", "auto": "auto",
} }

View File

@@ -62,7 +62,7 @@ extern "C"
} }
// load characteristics for each attenuation // load characteristics for each attenuation
for (int32_t i = 0; i <= ADC_ATTEN_DB_11; i++) { for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
auto adc_unit = channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2; auto adc_unit = 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, auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
1100, // default vref 1100, // default vref
@@ -118,8 +118,8 @@ void ADCSensor::dump_config() {
case ADC_ATTEN_DB_6: case ADC_ATTEN_DB_6:
ESP_LOGCONFIG(TAG, " Attenuation: 6db"); ESP_LOGCONFIG(TAG, " Attenuation: 6db");
break; break;
case ADC_ATTEN_DB_11: case ADC_ATTEN_DB_12_COMPAT:
ESP_LOGCONFIG(TAG, " Attenuation: 11db"); ESP_LOGCONFIG(TAG, " Attenuation: 12db");
break; break;
default: // This is to satisfy the unused ADC_ATTEN_MAX default: // This is to satisfy the unused ADC_ATTEN_MAX
break; break;
@@ -183,12 +183,12 @@ float ADCSensor::sample() {
return mv / 1000.0f; 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) { if (channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_11); adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_12_COMPAT);
raw11 = adc1_get_raw(channel1_); raw12 = adc1_get_raw(channel1_);
if (raw11 < ADC_MAX) { if (raw12 < ADC_MAX) {
adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6); adc1_config_channel_atten(channel1_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(channel1_); raw6 = adc1_get_raw(channel1_);
if (raw6 < ADC_MAX) { if (raw6 < ADC_MAX) {
@@ -201,9 +201,9 @@ float ADCSensor::sample() {
} }
} }
} else if (channel2_ != ADC2_CHANNEL_MAX) { } else if (channel2_ != ADC2_CHANNEL_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_11); adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_12_COMPAT);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw11); adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12);
if (raw11 < ADC_MAX) { if (raw12 < ADC_MAX) {
adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6); adc2_config_channel_atten(channel2_, ADC_ATTEN_DB_6);
adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6); adc2_get_raw(channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
if (raw6 < ADC_MAX) { if (raw6 < ADC_MAX) {
@@ -217,25 +217,25 @@ float ADCSensor::sample() {
} }
} }
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) { if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw12 == -1) {
return NAN; return NAN;
} }
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_11]); uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]); 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 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 mv0 = esp_adc_cal_raw_to_voltage(raw0, &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) // 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 c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF); uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF); uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
// max theoretical csum value is 4096*4 = 16384 // 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 // 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); return mv_scaled / (float) (csum * 1000U);
} }
#endif // USE_ESP32 #endif // USE_ESP32

View File

@@ -1,19 +1,34 @@
#pragma once #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/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.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 #ifdef USE_ESP32
#include "driver/adc.h"
#include <esp_adc_cal.h> #include <esp_adc_cal.h>
#include "driver/adc.h"
#endif #endif
namespace esphome { namespace esphome {
namespace adc { 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 { class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
public: public:
#ifdef USE_ESP32 #ifdef USE_ESP32

View File

@@ -1,3 +1,5 @@
import logging
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
import esphome.final_validate as fv import esphome.final_validate as fv
@@ -19,16 +21,29 @@ from . import (
ATTENUATION_MODES, ATTENUATION_MODES,
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL, ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
adc_ns,
validate_adc_pin, validate_adc_pin,
) )
_LOGGER = logging.getLogger(__name__)
AUTO_LOAD = ["voltage_sampler"] AUTO_LOAD = ["voltage_sampler"]
_attenuation = cv.enum(ATTENUATION_MODES, lower=True)
def validate_config(config): def validate_config(config):
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto": if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set") raise cv.Invalid("Automatic attenuation cannot be used when raw output 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 return config
@@ -47,7 +62,6 @@ def final_validate_config(config):
return config return config
adc_ns = cg.esphome_ns.namespace("adc")
ADCSensor = adc_ns.class_( ADCSensor = adc_ns.class_(
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
) )
@@ -65,7 +79,7 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_PIN): validate_adc_pin, cv.Required(CONF_PIN): validate_adc_pin,
cv.Optional(CONF_RAW, default=False): cv.boolean, cv.Optional(CONF_RAW, default=False): cv.boolean,
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All( cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True) cv.only_on_esp32, _attenuation
), ),
} }
) )

View File

@@ -19,6 +19,7 @@ from esphome.const import (
CONF_RESET_PIN, CONF_RESET_PIN,
CONF_REVERSE_ACTIVE_ENERGY, CONF_REVERSE_ACTIVE_ENERGY,
CONF_VOLTAGE, CONF_VOLTAGE,
CONF_VOLTAGE_GAIN,
DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY, DEVICE_CLASS_ENERGY,
@@ -47,7 +48,6 @@ CONF_CURRENT_GAIN = "current_gain"
CONF_IRQ0_PIN = "irq0_pin" CONF_IRQ0_PIN = "irq0_pin"
CONF_IRQ1_PIN = "irq1_pin" CONF_IRQ1_PIN = "irq1_pin"
CONF_POWER_GAIN = "power_gain" CONF_POWER_GAIN = "power_gain"
CONF_VOLTAGE_GAIN = "voltage_gain"
CONF_NEUTRAL = "neutral" CONF_NEUTRAL = "neutral"

View File

@@ -6,6 +6,7 @@ from esphome.const import (
CONF_IRQ_PIN, CONF_IRQ_PIN,
CONF_VOLTAGE, CONF_VOLTAGE,
CONF_FREQUENCY, CONF_FREQUENCY,
CONF_VOLTAGE_GAIN,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_POWER, DEVICE_CLASS_POWER,
@@ -36,7 +37,6 @@ CONF_POWER_FACTOR_B = "power_factor_b"
CONF_VOLTAGE_PGA_GAIN = "voltage_pga_gain" CONF_VOLTAGE_PGA_GAIN = "voltage_pga_gain"
CONF_CURRENT_PGA_GAIN_A = "current_pga_gain_a" CONF_CURRENT_PGA_GAIN_A = "current_pga_gain_a"
CONF_CURRENT_PGA_GAIN_B = "current_pga_gain_b" CONF_CURRENT_PGA_GAIN_B = "current_pga_gain_b"
CONF_VOLTAGE_GAIN = "voltage_gain"
CONF_CURRENT_GAIN_A = "current_gain_a" CONF_CURRENT_GAIN_A = "current_gain_a"
CONF_CURRENT_GAIN_B = "current_gain_b" CONF_CURRENT_GAIN_B = "current_gain_b"
CONF_ACTIVE_POWER_GAIN_A = "active_power_gain_a" CONF_ACTIVE_POWER_GAIN_A = "active_power_gain_a"

View File

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

View File

@@ -1002,7 +1002,11 @@ bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_pla
MediaPlayerStateResponse resp{}; MediaPlayerStateResponse resp{};
resp.key = media_player->get_object_id_hash(); 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.volume = media_player->volume;
resp.muted = media_player->is_muted(); resp.muted = media_player->is_muted();
return this->send_media_player_state_response(resp); 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) { if (msg.has_media_url) {
call.set_media_url(msg.media_url); call.set_media_url(msg.media_url);
} }
if (msg.has_announcement) {
call.set_announcement(msg.announcement);
}
call.perform(); call.perform();
} }
#endif #endif

View File

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

View File

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

View File

@@ -54,7 +54,6 @@ FAST_FILTER = {
"LSB10": 7, "LSB10": 7,
} }
CONF_ANGLE = "angle"
CONF_RAW_ANGLE = "raw_angle" CONF_RAW_ANGLE = "raw_angle"
CONF_RAW_POSITION = "raw_position" CONF_RAW_POSITION = "raw_position"
CONF_WATCHDOG = "watchdog" CONF_WATCHDOG = "watchdog"

View File

@@ -11,6 +11,7 @@ from esphome.const import (
CONF_MAGNITUDE, CONF_MAGNITUDE,
CONF_STATUS, CONF_STATUS,
CONF_POSITION, CONF_POSITION,
CONF_ANGLE,
) )
from .. import as5600_ns, AS5600Component from .. import as5600_ns, AS5600Component
@@ -19,7 +20,6 @@ DEPENDENCIES = ["as5600"]
AS5600Sensor = as5600_ns.class_("AS5600Sensor", sensor.Sensor, cg.PollingComponent) AS5600Sensor = as5600_ns.class_("AS5600Sensor", sensor.Sensor, cg.PollingComponent)
CONF_ANGLE = "angle"
CONF_RAW_ANGLE = "raw_angle" CONF_RAW_ANGLE = "raw_angle"
CONF_RAW_POSITION = "raw_position" CONF_RAW_POSITION = "raw_position"
CONF_WATCHDOG = "watchdog" CONF_WATCHDOG = "watchdog"

View File

@@ -22,7 +22,6 @@ CONF_AT581X_ID = "at581x_id"
CONF_SENSING_DISTANCE = "sensing_distance" CONF_SENSING_DISTANCE = "sensing_distance"
CONF_SENSITIVITY = "sensitivity"
CONF_POWERON_SELFCHECK_TIME = "poweron_selfcheck_time" CONF_POWERON_SELFCHECK_TIME = "poweron_selfcheck_time"
CONF_PROTECT_TIME = "protect_time" CONF_PROTECT_TIME = "protect_time"
CONF_TRIGGER_BASE = "trigger_base" CONF_TRIGGER_BASE = "trigger_base"

View File

@@ -6,6 +6,7 @@ from esphome.const import (
CONF_ENERGY, CONF_ENERGY,
CONF_EXTERNAL_TEMPERATURE, CONF_EXTERNAL_TEMPERATURE,
CONF_ID, CONF_ID,
CONF_INTERNAL_TEMPERATURE,
CONF_POWER, CONF_POWER,
CONF_VOLTAGE, CONF_VOLTAGE,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
@@ -24,7 +25,6 @@ from esphome.const import (
DEPENDENCIES = ["uart"] DEPENDENCIES = ["uart"]
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
bl0940_ns = cg.esphome_ns.namespace("bl0940") bl0940_ns = cg.esphome_ns.namespace("bl0940")
BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice) BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice)

View File

@@ -1 +1,108 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
UNIT_PERCENT,
)
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
bme280_ns = cg.esphome_ns.namespace("bme280_base")
BME280Oversampling = bme280_ns.enum("BME280Oversampling")
OVERSAMPLING_OPTIONS = {
"NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE,
"1X": BME280Oversampling.BME280_OVERSAMPLING_1X,
"2X": BME280Oversampling.BME280_OVERSAMPLING_2X,
"4X": BME280Oversampling.BME280_OVERSAMPLING_4X,
"8X": BME280Oversampling.BME280_OVERSAMPLING_8X,
"16X": BME280Oversampling.BME280_OVERSAMPLING_16X,
}
BME280IIRFilter = bme280_ns.enum("BME280IIRFilter")
IIR_FILTER_OPTIONS = {
"OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF,
"2X": BME280IIRFilter.BME280_IIR_FILTER_2X,
"4X": BME280IIRFilter.BME280_IIR_FILTER_4X,
"8X": BME280IIRFilter.BME280_IIR_FILTER_8X,
"16X": BME280IIRFilter.BME280_IIR_FILTER_16X,
}
CONFIG_SCHEMA_BASE = cv.Schema(
{
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
).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)
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))
cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
if pressure_config := config.get(CONF_PRESSURE):
sens = await sensor.new_sensor(pressure_config)
cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
if humidity_config := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humidity_config)
cg.add(var.set_humidity_sensor(sens))
cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING]))
cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))
return var

View File

@@ -1,106 +0,0 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
UNIT_PERCENT,
)
bme280_ns = cg.esphome_ns.namespace("bme280_base")
BME280Oversampling = bme280_ns.enum("BME280Oversampling")
OVERSAMPLING_OPTIONS = {
"NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE,
"1X": BME280Oversampling.BME280_OVERSAMPLING_1X,
"2X": BME280Oversampling.BME280_OVERSAMPLING_2X,
"4X": BME280Oversampling.BME280_OVERSAMPLING_4X,
"8X": BME280Oversampling.BME280_OVERSAMPLING_8X,
"16X": BME280Oversampling.BME280_OVERSAMPLING_16X,
}
BME280IIRFilter = bme280_ns.enum("BME280IIRFilter")
IIR_FILTER_OPTIONS = {
"OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF,
"2X": BME280IIRFilter.BME280_IIR_FILTER_2X,
"4X": BME280IIRFilter.BME280_IIR_FILTER_4X,
"8X": BME280IIRFilter.BME280_IIR_FILTER_8X,
"16X": BME280IIRFilter.BME280_IIR_FILTER_16X,
}
CONFIG_SCHEMA_BASE = cv.Schema(
{
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
).extend(cv.polling_component_schema("60s"))
async def to_code(config, func=None):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
if func is not None:
await func(var, config)
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))
cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING]))
if pressure_config := config.get(CONF_PRESSURE):
sens = await sensor.new_sensor(pressure_config)
cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING]))
if humidity_config := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humidity_config)
cg.add(var.set_humidity_sensor(sens))
cg.add(var.set_humidity_oversampling(humidity_config[CONF_OVERSAMPLING]))
cg.add(var.set_iir_filter(config[CONF_IIR_FILTER]))

View File

@@ -1,9 +1,10 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c from esphome.components import i2c
from ..bme280_base.sensor import to_code as to_code_base, cv, CONFIG_SCHEMA_BASE from ..bme280_base import to_code_base, CONFIG_SCHEMA_BASE
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["bme280_base"] AUTO_LOAD = ["bme280_base"]
DEPENDENCIES = ["i2c"]
bme280_ns = cg.esphome_ns.namespace("bme280_i2c") bme280_ns = cg.esphome_ns.namespace("bme280_i2c")
BME280I2CComponent = bme280_ns.class_( BME280I2CComponent = bme280_ns.class_(
@@ -16,4 +17,5 @@ CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(
async def to_code(config): async def to_code(config):
await to_code_base(config, func=i2c.register_i2c_device) var = await to_code_base(config)
await i2c.register_i2c_device(var, config)

View File

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

View File

@@ -4,19 +4,19 @@
#include "bme280_spi.h" #include "bme280_spi.h"
#include <esphome/components/bme280_base/bme280_base.h> #include <esphome/components/bme280_base/bme280_base.h>
int set_bit(uint8_t num, int position) { namespace esphome {
namespace bme280_spi {
uint8_t set_bit(uint8_t num, int position) {
int mask = 1 << position; int mask = 1 << position;
return num | mask; return num | mask;
} }
int clear_bit(uint8_t num, int position) { uint8_t clear_bit(uint8_t num, int position) {
int mask = 1 << position; int mask = 1 << position;
return num & ~mask; return num & ~mask;
} }
namespace esphome {
namespace bme280_spi {
void BME280SPIComponent::setup() { void BME280SPIComponent::setup() {
this->spi_setup(); this->spi_setup();
BME280Component::setup(); BME280Component::setup();
@@ -30,34 +30,33 @@ void BME280SPIComponent::setup() {
bool BME280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) { bool BME280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) {
this->enable(); this->enable();
// cause: *data = this->delegate_->transfer(tmp) doesnt work this->transfer_byte(set_bit(a_register, 7));
this->delegate_->transfer(set_bit(a_register, 7)); *data = this->transfer_byte(0);
*data = this->delegate_->transfer(0);
this->disable(); this->disable();
return true; return true;
} }
bool BME280SPIComponent::write_byte(uint8_t a_register, uint8_t data) { bool BME280SPIComponent::write_byte(uint8_t a_register, uint8_t data) {
this->enable(); this->enable();
this->delegate_->transfer(clear_bit(a_register, 7)); this->transfer_byte(clear_bit(a_register, 7));
this->delegate_->transfer(data); this->transfer_byte(data);
this->disable(); this->disable();
return true; return true;
} }
bool BME280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { bool BME280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
this->enable(); this->enable();
this->delegate_->transfer(set_bit(a_register, 7)); this->transfer_byte(set_bit(a_register, 7));
this->delegate_->read_array(data, len); this->read_array(data, len);
this->disable(); this->disable();
return true; return true;
} }
bool BME280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) { bool BME280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) {
this->enable(); this->enable();
this->delegate_->transfer(set_bit(a_register, 7)); this->transfer_byte(set_bit(a_register, 7));
((uint8_t *) data)[1] = this->delegate_->transfer(0); ((uint8_t *) data)[1] = this->transfer_byte(0);
((uint8_t *) data)[0] = this->delegate_->transfer(0); ((uint8_t *) data)[0] = this->transfer_byte(0);
this->disable(); this->disable();
return true; return true;
} }

View File

@@ -1,13 +1,11 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import spi from esphome.components import spi
from esphome.components.bme280_base.sensor import ( from ..bme280_base import to_code_base, CONFIG_SCHEMA_BASE
to_code as to_code_base,
cv,
CONFIG_SCHEMA_BASE,
)
DEPENDENCIES = ["spi"]
AUTO_LOAD = ["bme280_base"] AUTO_LOAD = ["bme280_base"]
CODEOWNERS = ["@apbodrov"]
DEPENDENCIES = ["spi"]
bme280_spi_ns = cg.esphome_ns.namespace("bme280_spi") bme280_spi_ns = cg.esphome_ns.namespace("bme280_spi")
@@ -21,4 +19,5 @@ CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(spi.spi_device_schema()).extend(
async def to_code(config): async def to_code(config):
await to_code_base(config, func=spi.register_spi_device) var = await to_code_base(config)
await spi.register_spi_device(var, config)

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, esp32 from esphome.components import i2c, esp32
from esphome.const import CONF_ID from esphome.const import CONF_ID, CONF_TEMPERATURE_OFFSET
CODEOWNERS = ["@trvrnrth"] CODEOWNERS = ["@trvrnrth"]
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
@@ -9,7 +9,6 @@ AUTO_LOAD = ["sensor", "text_sensor"]
MULTI_CONF = True MULTI_CONF = True
CONF_BME680_BSEC_ID = "bme680_bsec_id" CONF_BME680_BSEC_ID = "bme680_bsec_id"
CONF_TEMPERATURE_OFFSET = "temperature_offset"
CONF_IAQ_MODE = "iaq_mode" CONF_IAQ_MODE = "iaq_mode"
CONF_SUPPLY_VOLTAGE = "supply_voltage" CONF_SUPPLY_VOLTAGE = "supply_voltage"
CONF_SAMPLE_RATE = "sample_rate" CONF_SAMPLE_RATE = "sample_rate"

View File

@@ -1,102 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import ( CODEOWNERS = ["@latonita"]
CONF_ID,
CONF_IIR_FILTER, CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
CONF_OVERSAMPLING, "The bmp3xx sensor component has been renamed to bmp3xx_i2c."
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
) )
CODEOWNERS = ["@martgras"]
DEPENDENCIES = ["i2c"]
bmp3xx_ns = cg.esphome_ns.namespace("bmp3xx")
Oversampling = bmp3xx_ns.enum("Oversampling")
OVERSAMPLING_OPTIONS = {
"NONE": Oversampling.OVERSAMPLING_NONE,
"2X": Oversampling.OVERSAMPLING_X2,
"4X": Oversampling.OVERSAMPLING_X4,
"8X": Oversampling.OVERSAMPLING_X8,
"16X": Oversampling.OVERSAMPLING_X16,
"32X": Oversampling.OVERSAMPLING_X32,
}
IIRFilter = bmp3xx_ns.enum("IIRFilter")
IIR_FILTER_OPTIONS = {
"OFF": IIRFilter.IIR_FILTER_OFF,
"2X": IIRFilter.IIR_FILTER_2,
"4X": IIRFilter.IIR_FILTER_4,
"8X": IIRFilter.IIR_FILTER_8,
"16X": IIRFilter.IIR_FILTER_16,
"32X": IIRFilter.IIR_FILTER_32,
"64X": IIRFilter.IIR_FILTER_64,
"128X": IIRFilter.IIR_FILTER_128,
}
BMP3XXComponent = bmp3xx_ns.class_(
"BMP3XXComponent", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BMP3XXComponent),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="2X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x77))
)
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)
cg.add(var.set_iir_filter_config(config[CONF_IIR_FILTER]))
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))
cg.add(
var.set_temperature_oversampling_config(
temperature_config[CONF_OVERSAMPLING]
)
)
if pressure_config := config.get(CONF_PRESSURE):
sens = await sensor.new_sensor(pressure_config)
cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING]))

View File

@@ -0,0 +1,95 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_ID,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
)
CODEOWNERS = ["@martgras", "@latonita"]
bmp3xx_ns = cg.esphome_ns.namespace("bmp3xx_base")
Oversampling = bmp3xx_ns.enum("Oversampling")
OVERSAMPLING_OPTIONS = {
"NONE": Oversampling.OVERSAMPLING_NONE,
"2X": Oversampling.OVERSAMPLING_X2,
"4X": Oversampling.OVERSAMPLING_X4,
"8X": Oversampling.OVERSAMPLING_X8,
"16X": Oversampling.OVERSAMPLING_X16,
"32X": Oversampling.OVERSAMPLING_X32,
}
IIRFilter = bmp3xx_ns.enum("IIRFilter")
IIR_FILTER_OPTIONS = {
"OFF": IIRFilter.IIR_FILTER_OFF,
"2X": IIRFilter.IIR_FILTER_2,
"4X": IIRFilter.IIR_FILTER_4,
"8X": IIRFilter.IIR_FILTER_8,
"16X": IIRFilter.IIR_FILTER_16,
"32X": IIRFilter.IIR_FILTER_32,
"64X": IIRFilter.IIR_FILTER_64,
"128X": IIRFilter.IIR_FILTER_128,
}
CONFIG_SCHEMA_BASE = cv.Schema(
{
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="2X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
).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)
cg.add(var.set_iir_filter_config(config[CONF_IIR_FILTER]))
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))
cg.add(
var.set_temperature_oversampling_config(
temperature_config[CONF_OVERSAMPLING]
)
)
if pressure_config := config.get(CONF_PRESSURE):
sens = await sensor.new_sensor(pressure_config)
cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING]))
return var

View File

@@ -5,13 +5,13 @@
http://github.com/MartinL1/BMP388_DEV http://github.com/MartinL1/BMP388_DEV
*/ */
#include "bmp3xx.h" #include "bmp3xx_base.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include <cinttypes> #include <cinttypes>
namespace esphome { namespace esphome {
namespace bmp3xx { namespace bmp3xx_base {
static const char *const TAG = "bmp3xx.sensor"; static const char *const TAG = "bmp3xx.sensor";
@@ -150,7 +150,6 @@ void BMP3XXComponent::setup() {
void BMP3XXComponent::dump_config() { void BMP3XXComponent::dump_config() {
ESP_LOGCONFIG(TAG, "BMP3XX:"); ESP_LOGCONFIG(TAG, "BMP3XX:");
ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
LOG_I2C_DEVICE(this);
switch (this->error_code_) { switch (this->error_code_) {
case NONE: case NONE:
break; break;
@@ -386,5 +385,5 @@ float BMP3XXComponent::bmp388_compensate_pressure_(float uncomp_press, float t_l
return partial_out1 + partial_out2 + partial_data4; return partial_out1 + partial_out2 + partial_data4;
} }
} // namespace bmp3xx } // namespace bmp3xx_base
} // namespace esphome } // namespace esphome

View File

@@ -9,10 +9,9 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome { namespace esphome {
namespace bmp3xx { namespace bmp3xx_base {
static const uint8_t BMP388_ID = 0x50; // The BMP388 device ID static const uint8_t BMP388_ID = 0x50; // The BMP388 device ID
static const uint8_t BMP390_ID = 0x60; // The BMP390 device ID static const uint8_t BMP390_ID = 0x60; // The BMP390 device ID
@@ -69,8 +68,8 @@ enum IIRFilter {
IIR_FILTER_128 = 0x07 IIR_FILTER_128 = 0x07
}; };
/// This class implements support for the BMP3XX Temperature+Pressure i2c sensor. /// This class implements support for the BMP3XX Temperature+Pressure sensor.
class BMP3XXComponent : public PollingComponent, public i2c::I2CDevice { class BMP3XXComponent : public PollingComponent {
public: public:
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
@@ -231,7 +230,13 @@ class BMP3XXComponent : public PollingComponent, public i2c::I2CDevice {
float bmp388_compensate_temperature_(float uncomp_temp); float bmp388_compensate_temperature_(float uncomp_temp);
// Bosch pressure compensation function // Bosch pressure compensation function
float bmp388_compensate_pressure_(float uncomp_press, float t_lin); float bmp388_compensate_pressure_(float uncomp_press, float t_lin);
// interface specific functions
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;
}; };
} // namespace bmp3xx } // namespace bmp3xx_base
} // namespace esphome } // namespace esphome

View File

@@ -0,0 +1,29 @@
#include "esphome/components/i2c/i2c.h"
#include "bmp3xx_i2c.h"
#include <cinttypes>
namespace esphome {
namespace bmp3xx_i2c {
static const char *const TAG = "bmp3xx_i2c.sensor";
bool BMP3XXI2CComponent::read_byte(uint8_t a_register, uint8_t *data) {
return I2CDevice::read_byte(a_register, data);
};
bool BMP3XXI2CComponent::write_byte(uint8_t a_register, uint8_t data) {
return I2CDevice::write_byte(a_register, data);
};
bool BMP3XXI2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
return I2CDevice::read_bytes(a_register, data, len);
};
bool BMP3XXI2CComponent::write_bytes(uint8_t a_register, uint8_t *data, size_t len) {
return I2CDevice::write_bytes(a_register, data, len);
};
void BMP3XXI2CComponent::dump_config() {
LOG_I2C_DEVICE(this);
BMP3XXComponent::dump_config();
}
} // namespace bmp3xx_i2c
} // namespace esphome

View File

@@ -0,0 +1,17 @@
#pragma once
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/bmp3xx_base/bmp3xx_base.h"
namespace esphome {
namespace bmp3xx_i2c {
class BMP3XXI2CComponent : public bmp3xx_base::BMP3XXComponent, public i2c::I2CDevice {
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;
void dump_config() override;
};
} // namespace bmp3xx_i2c
} // namespace esphome

View File

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

View File

@@ -0,0 +1,57 @@
#include "bmp3xx_spi.h"
#include <cinttypes>
namespace esphome {
namespace bmp3xx_spi {
static const char *const TAG = "bmp3xx_spi.sensor";
uint8_t set_bit(uint8_t num, int position) {
int mask = 1 << position;
return num | mask;
}
uint8_t clear_bit(uint8_t num, int position) {
int mask = 1 << position;
return num & ~mask;
}
void BMP3XXSPIComponent::setup() {
this->spi_setup();
BMP3XXComponent::setup();
}
bool BMP3XXSPIComponent::read_byte(uint8_t a_register, uint8_t *data) {
this->enable();
this->transfer_byte(set_bit(a_register, 7));
*data = this->transfer_byte(0);
this->disable();
return true;
}
bool BMP3XXSPIComponent::write_byte(uint8_t a_register, uint8_t data) {
this->enable();
this->transfer_byte(clear_bit(a_register, 7));
this->transfer_byte(data);
this->disable();
return true;
}
bool BMP3XXSPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) {
this->enable();
this->transfer_byte(set_bit(a_register, 7));
this->read_array(data, len);
this->disable();
return true;
}
bool BMP3XXSPIComponent::write_bytes(uint8_t a_register, uint8_t *data, size_t len) {
this->enable();
this->transfer_byte(clear_bit(a_register, 7));
this->transfer_array(data, len);
this->disable();
return true;
}
} // namespace bmp3xx_spi
} // namespace esphome

View File

@@ -0,0 +1,19 @@
#pragma once
#include "esphome/components/bmp3xx_base/bmp3xx_base.h"
#include "esphome/components/spi/spi.h"
namespace esphome {
namespace bmp3xx_spi {
class BMP3XXSPIComponent : public bmp3xx_base::BMP3XXComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
void setup() 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 bmp3xx_spi
} // namespace esphome

View File

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

View File

@@ -14,15 +14,41 @@ CONF_HEX = "hex"
def hex_color(value): 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: if len(value) != 6:
raise cv.Invalid("Color must have six digits") raise cv.Invalid("Hex color must have six digits")
try: 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: except ValueError as exc:
raise cv.Invalid("Color must be hexadecimal") from 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.Schema(
{ {
cv.Required(CONF_ID): cv.declare_id(ColorStruct), 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_BLUE_INT, "blue"): cv.uint8_t,
cv.Exclusive(CONF_WHITE, "white"): cv.percentage, cv.Exclusive(CONF_WHITE, "white"): cv.percentage,
cv.Exclusive(CONF_WHITE_INT, "white"): cv.uint8_t, cv.Exclusive(CONF_WHITE_INT, "white"): cv.uint8_t,
cv.Optional(CONF_HEX): hex_color,
} }
).extend(cv.COMPONENT_SCHEMA), ).extend(cv.COMPONENT_SCHEMA),
cv.Schema( validate_color,
{
cv.Required(CONF_ID): cv.declare_id(ColorStruct),
cv.Required(CONF_HEX): hex_color,
}
).extend(cv.COMPONENT_SCHEMA),
) )

View File

@@ -6,6 +6,7 @@ from esphome.const import (
CONF_ID, CONF_ID,
CONF_POWER, CONF_POWER,
CONF_VOLTAGE, CONF_VOLTAGE,
CONF_VOLTAGE_GAIN,
UNIT_VOLT, UNIT_VOLT,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_WATT, UNIT_WATT,
@@ -33,7 +34,6 @@ CONF_SAMPLES = "samples"
CONF_PHASE_OFFSET = "phase_offset" CONF_PHASE_OFFSET = "phase_offset"
CONF_PGA_GAIN = "pga_gain" CONF_PGA_GAIN = "pga_gain"
CONF_CURRENT_GAIN = "current_gain" CONF_CURRENT_GAIN = "current_gain"
CONF_VOLTAGE_GAIN = "voltage_gain"
CONF_CURRENT_HPF = "current_hpf" CONF_CURRENT_HPF = "current_hpf"
CONF_VOLTAGE_HPF = "voltage_hpf" CONF_VOLTAGE_HPF = "voltage_hpf"
CONF_PULSE_ENERGY = "pulse_energy" CONF_PULSE_ENERGY = "pulse_energy"

View File

@@ -1,14 +1,13 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import climate_ir from esphome.components import climate_ir
from esphome.const import CONF_ID from esphome.const import CONF_ID, CONF_USE_FAHRENHEIT
AUTO_LOAD = ["climate_ir"] AUTO_LOAD = ["climate_ir"]
daikin_brc_ns = cg.esphome_ns.namespace("daikin_brc") daikin_brc_ns = cg.esphome_ns.namespace("daikin_brc")
DaikinBrcClimate = daikin_brc_ns.class_("DaikinBrcClimate", climate_ir.ClimateIR) DaikinBrcClimate = daikin_brc_ns.class_("DaikinBrcClimate", climate_ir.ClimateIR)
CONF_USE_FAHRENHEIT = "use_fahrenheit"
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
{ {

View File

@@ -169,7 +169,7 @@ async def to_code(config):
{ {
cv.Required(CONF_ID): cv.use_id(DateEntity), cv.Required(CONF_ID): cv.use_id(DateEntity),
cv.Required(CONF_DATE): cv.Any( cv.Required(CONF_DATE): cv.Any(
cv.returning_lambda, cv.date_time(allowed_time=False) cv.returning_lambda, cv.date_time(date=True, time=False)
), ),
} }
), ),
@@ -200,7 +200,7 @@ async def datetime_date_set_to_code(config, action_id, template_arg, args):
{ {
cv.Required(CONF_ID): cv.use_id(TimeEntity), cv.Required(CONF_ID): cv.use_id(TimeEntity),
cv.Required(CONF_TIME): cv.Any( cv.Required(CONF_TIME): cv.Any(
cv.returning_lambda, cv.date_time(allowed_date=False) cv.returning_lambda, cv.date_time(date=False, time=True)
), ),
} }
), ),
@@ -230,7 +230,9 @@ async def datetime_time_set_to_code(config, action_id, template_arg, args):
cv.Schema( cv.Schema(
{ {
cv.Required(CONF_ID): cv.use_id(DateTimeEntity), cv.Required(CONF_ID): cv.use_id(DateTimeEntity),
cv.Required(CONF_DATETIME): cv.Any(cv.returning_lambda, cv.date_time()), cv.Required(CONF_DATETIME): cv.Any(
cv.returning_lambda, cv.date_time(date=True, time=True)
),
}, },
), ),
) )

View File

@@ -2,7 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
from esphome.const import CONF_ID from esphome.const import CONF_FACTORY_RESET, CONF_ID, CONF_SENSITIVITY
from esphome.components import uart from esphome.components import uart
CODEOWNERS = ["@niklasweber"] CODEOWNERS = ["@niklasweber"]
@@ -28,8 +28,6 @@ CONF_DELAY_AFTER_DETECT = "delay_after_detect"
CONF_DELAY_AFTER_DISAPPEAR = "delay_after_disappear" CONF_DELAY_AFTER_DISAPPEAR = "delay_after_disappear"
CONF_DETECTION_SEGMENTS = "detection_segments" CONF_DETECTION_SEGMENTS = "detection_segments"
CONF_OUTPUT_LATENCY = "output_latency" CONF_OUTPUT_LATENCY = "output_latency"
CONF_FACTORY_RESET = "factory_reset"
CONF_SENSITIVITY = "sensitivity"
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(

View File

@@ -15,11 +15,11 @@ class Rect {
int16_t h; ///< Height of region int16_t h; ///< Height of region
Rect() : x(VALUE_NO_SET), y(VALUE_NO_SET), w(VALUE_NO_SET), h(VALUE_NO_SET) {} // NOLINT Rect() : x(VALUE_NO_SET), y(VALUE_NO_SET), w(VALUE_NO_SET), h(VALUE_NO_SET) {} // NOLINT
inline Rect(int16_t x, int16_t y, int16_t w, int16_t h) ALWAYS_INLINE : x(x), y(y), w(w), h(h) {} inline Rect(int16_t x, int16_t y, int16_t w, int16_t h) ESPHOME_ALWAYS_INLINE : x(x), y(y), w(w), h(h) {}
inline int16_t x2() const { return this->x + this->w; }; ///< X coordinate of corner inline int16_t x2() const { return this->x + this->w; }; ///< X coordinate of corner
inline int16_t y2() const { return this->y + this->h; }; ///< Y coordinate of corner inline int16_t y2() const { return this->y + this->h; }; ///< Y coordinate of corner
inline bool is_set() const ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); } inline bool is_set() const ESPHOME_ALWAYS_INLINE { return (this->h != VALUE_NO_SET) && (this->w != VALUE_NO_SET); }
void expand(int16_t horizontal, int16_t vertical); void expand(int16_t horizontal, int16_t vertical);

View File

@@ -23,7 +23,6 @@ CODEOWNERS = ["@numo68"]
display_menu_base_ns = cg.esphome_ns.namespace("display_menu_base") display_menu_base_ns = cg.esphome_ns.namespace("display_menu_base")
CONF_DISPLAY_ID = "display_id"
CONF_ROTARY = "rotary" CONF_ROTARY = "rotary"
CONF_JOYSTICK = "joystick" CONF_JOYSTICK = "joystick"

View File

@@ -4,6 +4,7 @@ from esphome.components import sensor
from esphome.const import ( from esphome.const import (
CONF_EXTERNAL_TEMPERATURE, CONF_EXTERNAL_TEMPERATURE,
CONF_ID, CONF_ID,
CONF_INTERNAL_TEMPERATURE,
CONF_SPEED, CONF_SPEED,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
@@ -16,7 +17,6 @@ from .. import EMC2101_COMPONENT_SCHEMA, CONF_EMC2101_ID, emc2101_ns
DEPENDENCIES = ["emc2101"] DEPENDENCIES = ["emc2101"]
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
CONF_DUTY_CYCLE = "duty_cycle" CONF_DUTY_CYCLE = "duty_cycle"
EMC2101Sensor = emc2101_ns.class_("EMC2101Sensor", cg.PollingComponent) EMC2101Sensor = emc2101_ns.class_("EMC2101Sensor", cg.PollingComponent)

View File

@@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import ( from esphome.const import (
CONF_COMPENSATION,
CONF_ECO2, CONF_ECO2,
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_ID, CONF_ID,
@@ -27,7 +28,6 @@ ENS160Component = ens160_ns.class_(
) )
CONF_AQI = "aqi" CONF_AQI = "aqi"
CONF_COMPENSATION = "compensation"
UNIT_INDEX = "index" UNIT_INDEX = "index"
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (

View File

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

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.const import CONF_ID from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID
from esphome.core import CORE from esphome.core import CORE
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const
@@ -11,7 +11,6 @@ CONFLICTS_WITH = ["esp32_ble_beacon"]
CONF_BLE_ID = "ble_id" CONF_BLE_ID = "ble_id"
CONF_IO_CAPABILITY = "io_capability" CONF_IO_CAPABILITY = "io_capability"
CONF_ENABLE_ON_BOOT = "enable_on_boot"
NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]

View File

@@ -14,6 +14,7 @@ from esphome.const import (
CONF_BRIGHTNESS, CONF_BRIGHTNESS,
CONF_CONTRAST, CONF_CONTRAST,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_VSYNC_PIN,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.esp32 import add_idf_sdkconfig_option
@@ -112,7 +113,6 @@ ENUM_SPECIAL_EFFECT = {
} }
# pin assignment # pin assignment
CONF_VSYNC_PIN = "vsync_pin"
CONF_HREF_PIN = "href_pin" CONF_HREF_PIN = "href_pin"
CONF_PIXEL_CLOCK_PIN = "pixel_clock_pin" CONF_PIXEL_CLOCK_PIN = "pixel_clock_pin"
CONF_EXTERNAL_CLOCK = "external_clock" CONF_EXTERNAL_CLOCK = "external_clock"

View File

@@ -1,12 +1,12 @@
#include "ethernet_component.h" #include "ethernet_component.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/util.h" #include "esphome/core/util.h"
#include "esphome/core/application.h"
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <cinttypes>
#include <lwip/dns.h> #include <lwip/dns.h>
#include <cinttypes>
#include "esp_event.h" #include "esp_event.h"
#ifdef USE_ETHERNET_SPI #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. // KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide.
this->ksz8081_set_clock_reference_(mac); 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 #endif
// use ESP internal eth mac // use ESP internal eth mac
@@ -554,9 +558,10 @@ bool EthernetComponent::powerdown() {
} }
#ifndef USE_ETHERNET_SPI #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; esp_err_t err;
uint32_t phy_control_2; 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"); 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()); 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 #endif
} // namespace ethernet } // namespace ethernet

View File

@@ -86,6 +86,8 @@ class EthernetComponent : public Component {
void dump_connect_params_(); void dump_connect_params_();
/// @brief Set `RMII Reference Clock Select` bit for KSZ8081. /// @brief Set `RMII Reference Clock Select` bit for KSZ8081.
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac); 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_; std::string use_address_;
#ifdef USE_ETHERNET_SPI #ifdef USE_ETHERNET_SPI

View File

@@ -49,7 +49,16 @@ def _process_git_config(config: dict, refresh) -> str:
password=config.get(CONF_PASSWORD), password=config.get(CONF_PASSWORD),
) )
if (repo_dir / "esphome" / "components").is_dir(): if path := config.get(CONF_PATH):
if (repo_dir / path).is_dir():
components_dir = repo_dir / path
else:
raise cv.Invalid(
"Could not find components folder for source. Please check the source contains a '"
+ path
+ "' folder"
)
elif (repo_dir / "esphome" / "components").is_dir():
components_dir = repo_dir / "esphome" / "components" components_dir = repo_dir / "esphome" / "components"
elif (repo_dir / "components").is_dir(): elif (repo_dir / "components").is_dir():
components_dir = repo_dir / "components" components_dir = repo_dir / "components"

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import display, font, color from esphome.components import display, font, color
from esphome.const import CONF_ID, CONF_TRIGGER_ID from esphome.const import CONF_DISPLAY, CONF_ID, CONF_TRIGGER_ID
from esphome import automation, core from esphome import automation, core
from esphome.components.display_menu_base import ( from esphome.components.display_menu_base import (
@@ -10,7 +10,6 @@ from esphome.components.display_menu_base import (
display_menu_to_code, display_menu_to_code,
) )
CONF_DISPLAY = "display"
CONF_FONT = "font" CONF_FONT = "font"
CONF_MENU_ITEM_VALUE = "menu_item_value" CONF_MENU_ITEM_VALUE = "menu_item_value"
CONF_FOREGROUND_COLOR = "foreground_color" CONF_FOREGROUND_COLOR = "foreground_color"

View File

@@ -6,12 +6,14 @@ from esphome.components import uart, climate, logger
from esphome import automation from esphome import automation
from esphome.const import ( from esphome.const import (
CONF_BEEPER, CONF_BEEPER,
CONF_DISPLAY,
CONF_ID, CONF_ID,
CONF_LEVEL, CONF_LEVEL,
CONF_LOGGER, CONF_LOGGER,
CONF_LOGS, CONF_LOGS,
CONF_MAX_TEMPERATURE, CONF_MAX_TEMPERATURE,
CONF_MIN_TEMPERATURE, CONF_MIN_TEMPERATURE,
CONF_OUTDOOR_TEMPERATURE,
CONF_PROTOCOL, CONF_PROTOCOL,
CONF_SUPPORTED_MODES, CONF_SUPPORTED_MODES,
CONF_SUPPORTED_PRESETS, CONF_SUPPORTED_PRESETS,
@@ -43,11 +45,9 @@ CONF_ALTERNATIVE_SWING_CONTROL = "alternative_swing_control"
CONF_ANSWER_TIMEOUT = "answer_timeout" CONF_ANSWER_TIMEOUT = "answer_timeout"
CONF_CONTROL_METHOD = "control_method" CONF_CONTROL_METHOD = "control_method"
CONF_CONTROL_PACKET_SIZE = "control_packet_size" CONF_CONTROL_PACKET_SIZE = "control_packet_size"
CONF_DISPLAY = "display"
CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow" CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow"
CONF_ON_ALARM_START = "on_alarm_start" CONF_ON_ALARM_START = "on_alarm_start"
CONF_ON_ALARM_END = "on_alarm_end" CONF_ON_ALARM_END = "on_alarm_end"
CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature"
CONF_VERTICAL_AIRFLOW = "vertical_airflow" CONF_VERTICAL_AIRFLOW = "vertical_airflow"
CONF_WIFI_SIGNAL = "wifi_signal" CONF_WIFI_SIGNAL = "wifi_signal"

View File

@@ -2,6 +2,7 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
from esphome.const import ( from esphome.const import (
CONF_OUTDOOR_TEMPERATURE,
CONF_POWER, CONF_POWER,
CONF_HUMIDITY, CONF_HUMIDITY,
DEVICE_CLASS_CURRENT, DEVICE_CLASS_CURRENT,
@@ -41,7 +42,6 @@ CONF_OUTDOOR_COIL_TEMPERATURE = "outdoor_coil_temperature"
CONF_OUTDOOR_DEFROST_TEMPERATURE = "outdoor_defrost_temperature" CONF_OUTDOOR_DEFROST_TEMPERATURE = "outdoor_defrost_temperature"
CONF_OUTDOOR_IN_AIR_TEMPERATURE = "outdoor_in_air_temperature" CONF_OUTDOOR_IN_AIR_TEMPERATURE = "outdoor_in_air_temperature"
CONF_OUTDOOR_OUT_AIR_TEMPERATURE = "outdoor_out_air_temperature" CONF_OUTDOOR_OUT_AIR_TEMPERATURE = "outdoor_out_air_temperature"
CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature"
# Additional icons # Additional icons
ICON_SNOWFLAKE_THERMOMETER = "mdi:snowflake-thermometer" ICON_SNOWFLAKE_THERMOMETER = "mdi:snowflake-thermometer"

View File

@@ -1,5 +1,6 @@
#include "hmc5883l.h" #include "hmc5883l.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/application.h"
namespace esphome { namespace esphome {
namespace hmc5883l { namespace hmc5883l {
@@ -31,6 +32,10 @@ void HMC5883LComponent::setup() {
return; return;
} }
if (this->get_update_interval() < App.get_loop_interval()) {
high_freq_.start();
}
if (id[0] != 0x48 || id[1] != 0x34 || id[2] != 0x33) { if (id[0] != 0x48 || id[1] != 0x34 || id[2] != 0x33) {
this->error_code_ = ID_REGISTERS; this->error_code_ = ID_REGISTERS;
this->mark_failed(); this->mark_failed();

View File

@@ -63,6 +63,7 @@ class HMC5883LComponent : public PollingComponent, public i2c::I2CDevice {
COMMUNICATION_FAILED, COMMUNICATION_FAILED,
ID_REGISTERS, ID_REGISTERS,
} error_code_; } error_code_;
HighFrequencyLoopRequester high_freq_;
}; };
} // namespace hmc5883l } // namespace hmc5883l

View File

@@ -6,6 +6,7 @@ from esphome.const import (
CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_X,
CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Y,
CONF_FIELD_STRENGTH_Z, CONF_FIELD_STRENGTH_Z,
CONF_HEADING,
CONF_ID, CONF_ID,
CONF_OVERSAMPLING, CONF_OVERSAMPLING,
CONF_RANGE, CONF_RANGE,
@@ -21,7 +22,6 @@ DEPENDENCIES = ["i2c"]
hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l") hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l")
CONF_HEADING = "heading"
HMC5883LComponent = hmc5883l_ns.class_( HMC5883LComponent = hmc5883l_ns.class_(
"HMC5883LComponent", cg.PollingComponent, i2c.I2CDevice "HMC5883LComponent", cg.PollingComponent, i2c.I2CDevice

View File

@@ -10,6 +10,11 @@ namespace i2s_audio {
static const char *const TAG = "audio"; static const char *const TAG = "audio";
void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) { void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
media_player::MediaPlayerState play_state = media_player::MEDIA_PLAYER_STATE_PLAYING;
if (call.get_announcement().has_value()) {
play_state = call.get_announcement().value() ? media_player::MEDIA_PLAYER_STATE_ANNOUNCING
: media_player::MEDIA_PLAYER_STATE_PLAYING;
}
if (call.get_media_url().has_value()) { if (call.get_media_url().has_value()) {
this->current_url_ = call.get_media_url(); this->current_url_ = call.get_media_url();
if (this->i2s_state_ != I2S_STATE_STOPPED && this->audio_ != nullptr) { if (this->i2s_state_ != I2S_STATE_STOPPED && this->audio_ != nullptr) {
@@ -17,7 +22,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
this->audio_->stopSong(); this->audio_->stopSong();
} }
this->audio_->connecttohost(this->current_url_.value().c_str()); this->audio_->connecttohost(this->current_url_.value().c_str());
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; this->state = play_state;
} else { } else {
this->start(); this->start();
} }
@@ -35,7 +40,7 @@ void I2SAudioMediaPlayer::control(const media_player::MediaPlayerCall &call) {
case media_player::MEDIA_PLAYER_COMMAND_PLAY: case media_player::MEDIA_PLAYER_COMMAND_PLAY:
if (!this->audio_->isRunning()) if (!this->audio_->isRunning())
this->audio_->pauseResume(); this->audio_->pauseResume();
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; this->state = play_state;
break; break;
case media_player::MEDIA_PLAYER_COMMAND_PAUSE: case media_player::MEDIA_PLAYER_COMMAND_PAUSE:
if (this->audio_->isRunning()) if (this->audio_->isRunning())
@@ -126,7 +131,9 @@ void I2SAudioMediaPlayer::loop() {
void I2SAudioMediaPlayer::play_() { void I2SAudioMediaPlayer::play_() {
this->audio_->loop(); this->audio_->loop();
if (this->state == media_player::MEDIA_PLAYER_STATE_PLAYING && !this->audio_->isRunning()) { if ((this->state == media_player::MEDIA_PLAYER_STATE_PLAYING ||
this->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING) &&
!this->audio_->isRunning()) {
this->stop(); this->stop();
} }
} }
@@ -164,6 +171,10 @@ void I2SAudioMediaPlayer::start_() {
if (this->current_url_.has_value()) { if (this->current_url_.has_value()) {
this->audio_->connecttohost(this->current_url_.value().c_str()); this->audio_->connecttohost(this->current_url_.value().c_str());
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
if (this->is_announcement_.has_value()) {
this->state = this->is_announcement_.value() ? media_player::MEDIA_PLAYER_STATE_ANNOUNCING
: media_player::MEDIA_PLAYER_STATE_PLAYING;
}
this->publish_state(); this->publish_state();
} }
} }

View File

@@ -78,6 +78,7 @@ class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer,
HighFrequencyLoopRequester high_freq_; HighFrequencyLoopRequester high_freq_;
optional<std::string> current_url_{}; optional<std::string> current_url_{};
optional<bool> is_announcement_{};
}; };
} // namespace i2s_audio } // namespace i2s_audio

View File

@@ -7,6 +7,7 @@ from esphome.const import (
CONF_ID, CONF_ID,
CONF_LAMBDA, CONF_LAMBDA,
CONF_MODEL, CONF_MODEL,
CONF_OE_PIN,
CONF_PAGES, CONF_PAGES,
CONF_WAKEUP_PIN, CONF_WAKEUP_PIN,
) )
@@ -29,7 +30,6 @@ CONF_GREYSCALE = "greyscale"
CONF_GMOD_PIN = "gmod_pin" CONF_GMOD_PIN = "gmod_pin"
CONF_GPIO0_ENABLE_PIN = "gpio0_enable_pin" CONF_GPIO0_ENABLE_PIN = "gpio0_enable_pin"
CONF_LE_PIN = "le_pin" CONF_LE_PIN = "le_pin"
CONF_OE_PIN = "oe_pin"
CONF_PARTIAL_UPDATING = "partial_updating" CONF_PARTIAL_UPDATING = "partial_updating"
CONF_POWERUP_PIN = "powerup_pin" CONF_POWERUP_PIN = "powerup_pin"
CONF_SPH_PIN = "sph_pin" CONF_SPH_PIN = "sph_pin"

View File

@@ -3,6 +3,7 @@ import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_INTERNAL_TEMPERATURE,
CONF_TEMPERATURE, CONF_TEMPERATURE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
@@ -10,7 +11,6 @@ from esphome.const import (
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
) )
CONF_INTERNAL_TEMPERATURE = "internal_temperature"
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
kmeteriso_ns = cg.esphome_ns.namespace("kmeteriso") kmeteriso_ns = cg.esphome_ns.namespace("kmeteriso")

View File

@@ -3,6 +3,7 @@ import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_DIMENSIONS, CONF_DIMENSIONS,
CONF_DISPLAY_ID,
) )
from esphome.core.entity_helpers import inherit_property_from from esphome.core.entity_helpers import inherit_property_from
from esphome.components import lcd_base from esphome.components import lcd_base
@@ -18,8 +19,6 @@ AUTO_LOAD = ["display_menu_base"]
lcd_menu_ns = cg.esphome_ns.namespace("lcd_menu") lcd_menu_ns = cg.esphome_ns.namespace("lcd_menu")
CONF_DISPLAY_ID = "display_id"
CONF_MARK_SELECTED = "mark_selected" CONF_MARK_SELECTED = "mark_selected"
CONF_MARK_EDITING = "mark_editing" CONF_MARK_EDITING = "mark_editing"
CONF_MARK_SUBMENU = "mark_submenu" CONF_MARK_SUBMENU = "mark_submenu"

View File

@@ -8,13 +8,13 @@ from esphome.const import (
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
ICON_MOTION_SENSOR, ICON_MOTION_SENSOR,
ICON_ACCOUNT, ICON_ACCOUNT,
CONF_HAS_TARGET,
CONF_HAS_MOVING_TARGET,
CONF_HAS_STILL_TARGET,
) )
from . import CONF_LD2410_ID, LD2410Component from . import CONF_LD2410_ID, LD2410Component
DEPENDENCIES = ["ld2410"] DEPENDENCIES = ["ld2410"]
CONF_HAS_TARGET = "has_target"
CONF_HAS_MOVING_TARGET = "has_moving_target"
CONF_HAS_STILL_TARGET = "has_still_target"
CONF_OUT_PIN_PRESENCE_STATUS = "out_pin_presence_status" CONF_OUT_PIN_PRESENCE_STATUS = "out_pin_presence_status"
CONFIG_SCHEMA = { CONFIG_SCHEMA = {

View File

@@ -2,6 +2,8 @@ import esphome.codegen as cg
from esphome.components import button from esphome.components import button
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_FACTORY_RESET,
CONF_RESTART,
DEVICE_CLASS_RESTART, DEVICE_CLASS_RESTART,
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_CONFIG,
@@ -15,8 +17,6 @@ QueryButton = ld2410_ns.class_("QueryButton", button.Button)
ResetButton = ld2410_ns.class_("ResetButton", button.Button) ResetButton = ld2410_ns.class_("ResetButton", button.Button)
RestartButton = ld2410_ns.class_("RestartButton", button.Button) RestartButton = ld2410_ns.class_("RestartButton", button.Button)
CONF_FACTORY_RESET = "factory_reset"
CONF_RESTART = "restart"
CONF_QUERY_PARAMS = "query_params" CONF_QUERY_PARAMS = "query_params"
CONFIG_SCHEMA = { CONFIG_SCHEMA = {

View File

@@ -1,14 +1,13 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import binary_sensor from esphome.components import binary_sensor
from esphome.const import CONF_ID, DEVICE_CLASS_OCCUPANCY from esphome.const import CONF_ID, DEVICE_CLASS_OCCUPANCY, CONF_HAS_TARGET
from .. import ld2420_ns, LD2420Component, CONF_LD2420_ID from .. import ld2420_ns, LD2420Component, CONF_LD2420_ID
LD2420BinarySensor = ld2420_ns.class_( LD2420BinarySensor = ld2420_ns.class_(
"LD2420BinarySensor", binary_sensor.BinarySensor, cg.Component "LD2420BinarySensor", binary_sensor.BinarySensor, cg.Component
) )
CONF_HAS_TARGET = "has_target"
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.COMPONENT_SCHEMA.extend( cv.COMPONENT_SCHEMA.extend(

View File

@@ -2,6 +2,7 @@ import esphome.codegen as cg
from esphome.components import button from esphome.components import button
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_FACTORY_RESET,
DEVICE_CLASS_RESTART, DEVICE_CLASS_RESTART,
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_CONFIG,
@@ -19,7 +20,6 @@ LD2420FactoryResetButton = ld2420_ns.class_("LD2420FactoryResetButton", button.B
CONF_APPLY_CONFIG = "apply_config" CONF_APPLY_CONFIG = "apply_config"
CONF_REVERT_CONFIG = "revert_config" CONF_REVERT_CONFIG = "revert_config"
CONF_RESTART_MODULE = "restart_module" CONF_RESTART_MODULE = "restart_module"
CONF_FACTORY_RESET = "factory_reset"
CONFIG_SCHEMA = { CONFIG_SCHEMA = {

View File

@@ -11,54 +11,54 @@ class ESPColorCorrection {
void set_max_brightness(const Color &max_brightness) { this->max_brightness_ = max_brightness; } void set_max_brightness(const Color &max_brightness) { this->max_brightness_ = max_brightness; }
void set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; } void set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; }
void calculate_gamma_table(float gamma); void calculate_gamma_table(float gamma);
inline Color color_correct(Color color) const ALWAYS_INLINE { inline Color color_correct(Color color) const ESPHOME_ALWAYS_INLINE {
// corrected = (uncorrected * max_brightness * local_brightness) ^ gamma // corrected = (uncorrected * max_brightness * local_brightness) ^ gamma
return Color(this->color_correct_red(color.red), this->color_correct_green(color.green), return Color(this->color_correct_red(color.red), this->color_correct_green(color.green),
this->color_correct_blue(color.blue), this->color_correct_white(color.white)); this->color_correct_blue(color.blue), this->color_correct_white(color.white));
} }
inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE { inline uint8_t color_correct_red(uint8_t red) const ESPHOME_ALWAYS_INLINE {
uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_); uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_);
return this->gamma_table_[res]; return this->gamma_table_[res];
} }
inline uint8_t color_correct_green(uint8_t green) const ALWAYS_INLINE { inline uint8_t color_correct_green(uint8_t green) const ESPHOME_ALWAYS_INLINE {
uint8_t res = esp_scale8(esp_scale8(green, this->max_brightness_.green), this->local_brightness_); uint8_t res = esp_scale8(esp_scale8(green, this->max_brightness_.green), this->local_brightness_);
return this->gamma_table_[res]; return this->gamma_table_[res];
} }
inline uint8_t color_correct_blue(uint8_t blue) const ALWAYS_INLINE { inline uint8_t color_correct_blue(uint8_t blue) const ESPHOME_ALWAYS_INLINE {
uint8_t res = esp_scale8(esp_scale8(blue, this->max_brightness_.blue), this->local_brightness_); uint8_t res = esp_scale8(esp_scale8(blue, this->max_brightness_.blue), this->local_brightness_);
return this->gamma_table_[res]; return this->gamma_table_[res];
} }
inline uint8_t color_correct_white(uint8_t white) const ALWAYS_INLINE { inline uint8_t color_correct_white(uint8_t white) const ESPHOME_ALWAYS_INLINE {
uint8_t res = esp_scale8(esp_scale8(white, this->max_brightness_.white), this->local_brightness_); uint8_t res = esp_scale8(esp_scale8(white, this->max_brightness_.white), this->local_brightness_);
return this->gamma_table_[res]; return this->gamma_table_[res];
} }
inline Color color_uncorrect(Color color) const ALWAYS_INLINE { inline Color color_uncorrect(Color color) const ESPHOME_ALWAYS_INLINE {
// uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness) // uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness)
return Color(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green), return Color(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green),
this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white)); this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white));
} }
inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE { inline uint8_t color_uncorrect_red(uint8_t red) const ESPHOME_ALWAYS_INLINE {
if (this->max_brightness_.red == 0 || this->local_brightness_ == 0) if (this->max_brightness_.red == 0 || this->local_brightness_ == 0)
return 0; return 0;
uint16_t uncorrected = this->gamma_reverse_table_[red] * 255UL; uint16_t uncorrected = this->gamma_reverse_table_[red] * 255UL;
uint8_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_; uint8_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_;
return res; return res;
} }
inline uint8_t color_uncorrect_green(uint8_t green) const ALWAYS_INLINE { inline uint8_t color_uncorrect_green(uint8_t green) const ESPHOME_ALWAYS_INLINE {
if (this->max_brightness_.green == 0 || this->local_brightness_ == 0) if (this->max_brightness_.green == 0 || this->local_brightness_ == 0)
return 0; return 0;
uint16_t uncorrected = this->gamma_reverse_table_[green] * 255UL; uint16_t uncorrected = this->gamma_reverse_table_[green] * 255UL;
uint8_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_; uint8_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_;
return res; return res;
} }
inline uint8_t color_uncorrect_blue(uint8_t blue) const ALWAYS_INLINE { inline uint8_t color_uncorrect_blue(uint8_t blue) const ESPHOME_ALWAYS_INLINE {
if (this->max_brightness_.blue == 0 || this->local_brightness_ == 0) if (this->max_brightness_.blue == 0 || this->local_brightness_ == 0)
return 0; return 0;
uint16_t uncorrected = this->gamma_reverse_table_[blue] * 255UL; uint16_t uncorrected = this->gamma_reverse_table_[blue] * 255UL;
uint8_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_; uint8_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_;
return res; return res;
} }
inline uint8_t color_uncorrect_white(uint8_t white) const ALWAYS_INLINE { inline uint8_t color_uncorrect_white(uint8_t white) const ESPHOME_ALWAYS_INLINE {
if (this->max_brightness_.white == 0 || this->local_brightness_ == 0) if (this->max_brightness_.white == 0 || this->local_brightness_ == 0)
return 0; return 0;
uint16_t uncorrected = this->gamma_reverse_table_[white] * 255UL; uint16_t uncorrected = this->gamma_reverse_table_[white] * 255UL;

View File

@@ -24,11 +24,11 @@ struct ESPHSVColor {
}; };
uint8_t raw[3]; uint8_t raw[3];
}; };
inline ESPHSVColor() ALWAYS_INLINE : h(0), s(0), v(0) { // NOLINT inline ESPHSVColor() ESPHOME_ALWAYS_INLINE : h(0), s(0), v(0) { // NOLINT
} }
inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue), inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ESPHOME_ALWAYS_INLINE : hue(hue),
saturation(saturation), saturation(saturation),
value(value) {} value(value) {}
Color to_rgb() const; Color to_rgb() const;
}; };

View File

@@ -39,7 +39,23 @@ void Logger::write_header_(int level, const char *tag, int line) {
const char *color = LOG_LEVEL_COLORS[level]; const char *color = LOG_LEVEL_COLORS[level];
const char *letter = LOG_LEVEL_LETTERS[level]; const char *letter = LOG_LEVEL_LETTERS[level];
this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line); #if defined(USE_ESP32) || defined(USE_LIBRETINY)
TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
#else
void *current_task = nullptr;
#endif
if (current_task == main_task_) {
this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line);
} else {
const char *thread_name = "";
#if defined(USE_ESP32)
thread_name = pcTaskGetName(current_task);
#elif defined(USE_LIBRETINY)
thread_name = pcTaskGetTaskName(current_task);
#endif
this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line,
ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), thread_name, color);
}
} }
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
@@ -127,6 +143,9 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) {
Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) { Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
// add 1 to buffer size for null terminator // add 1 to buffer size for null terminator
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
this->main_task_ = xTaskGetCurrentTaskHandle();
#endif
} }
#ifdef USE_LOGGER_USB_CDC #ifdef USE_LOGGER_USB_CDC

View File

@@ -167,6 +167,7 @@ class Logger : public Component {
CallbackManager<void(int, const char *, const char *)> log_callback_{}; CallbackManager<void(int, const char *, const char *)> log_callback_{};
/// Prevents recursive log calls, if true a log message is already being processed. /// Prevents recursive log calls, if true a log message is already being processed.
bool recursion_guard_ = false; bool recursion_guard_ = false;
void *main_task_ = nullptr;
}; };
extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -8,6 +8,9 @@ namespace ltr390 {
static const char *const TAG = "ltr390"; static const char *const TAG = "ltr390";
static const uint8_t LTR390_WAKEUP_TIME = 10;
static const uint8_t LTR390_SETTLE_TIME = 5;
static const uint8_t LTR390_MAIN_CTRL = 0x00; static const uint8_t LTR390_MAIN_CTRL = 0x00;
static const uint8_t LTR390_MEAS_RATE = 0x04; static const uint8_t LTR390_MEAS_RATE = 0x04;
static const uint8_t LTR390_GAIN = 0x05; static const uint8_t LTR390_GAIN = 0x05;
@@ -101,21 +104,27 @@ void LTR390Component::read_mode_(int mode_index) {
std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get();
ctrl[LTR390_CTRL_MODE] = mode; ctrl[LTR390_CTRL_MODE] = mode;
ctrl[LTR390_CTRL_EN] = true;
this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong();
// After the sensor integration time do the following // After the sensor integration time do the following
this->set_timeout(((uint32_t) RESOLUTIONVALUE[this->res_]) * 100, [this, mode_index]() { this->set_timeout(((uint32_t) RESOLUTIONVALUE[this->res_]) * 100 + LTR390_WAKEUP_TIME + LTR390_SETTLE_TIME,
// Read from the sensor [this, mode_index]() {
std::get<1>(this->mode_funcs_[mode_index])(); // Read from the sensor
std::get<1>(this->mode_funcs_[mode_index])();
// If there are more modes to read then begin the next // If there are more modes to read then begin the next
// otherwise stop // otherwise stop
if (mode_index + 1 < (int) this->mode_funcs_.size()) { if (mode_index + 1 < (int) this->mode_funcs_.size()) {
this->read_mode_(mode_index + 1); this->read_mode_(mode_index + 1);
} else { } else {
this->reading_ = false; // put sensor in standby
} std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get();
}); ctrl[LTR390_CTRL_EN] = false;
this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong();
this->reading_ = false;
}
});
} }
void LTR390Component::setup() { void LTR390Component::setup() {

View File

@@ -1,12 +1,9 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import binary_sensor from esphome.components import binary_sensor
from esphome.const import CONF_ID, CONF_KEY from esphome.const import CONF_ID, CONF_KEY, CONF_ROW, CONF_COL
from .. import MatrixKeypad, matrix_keypad_ns, CONF_KEYPAD_ID from .. import MatrixKeypad, matrix_keypad_ns, CONF_KEYPAD_ID
CONF_ROW = "row"
CONF_COL = "col"
DEPENDENCIES = ["matrix_keypad"] DEPENDENCIES = ["matrix_keypad"]
MatrixKeypadBinarySensor = matrix_keypad_ns.class_( MatrixKeypadBinarySensor = matrix_keypad_ns.class_(

View File

@@ -4,6 +4,7 @@ from esphome.components import sensor, voltage_sampler
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_NUMBER, CONF_NUMBER,
CONF_REFERENCE_VOLTAGE,
UNIT_VOLT, UNIT_VOLT,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
@@ -22,7 +23,6 @@ MCP3008Sensor = mcp3008_ns.class_(
voltage_sampler.VoltageSampler, voltage_sampler.VoltageSampler,
cg.Parented.template(MCP3008), cg.Parented.template(MCP3008),
) )
CONF_REFERENCE_VOLTAGE = "reference_voltage"
CONF_MCP3008_ID = "mcp3008_id" CONF_MCP3008_ID = "mcp3008_id"
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import spi from esphome.components import spi
from esphome.const import CONF_ID from esphome.const import CONF_ID, CONF_REFERENCE_VOLTAGE
DEPENDENCIES = ["spi"] DEPENDENCIES = ["spi"]
MULTI_CONF = True MULTI_CONF = True
@@ -10,7 +10,6 @@ CODEOWNERS = ["@rsumner"]
mcp3204_ns = cg.esphome_ns.namespace("mcp3204") mcp3204_ns = cg.esphome_ns.namespace("mcp3204")
MCP3204 = mcp3204_ns.class_("MCP3204", cg.Component, spi.SPIDevice) MCP3204 = mcp3204_ns.class_("MCP3204", cg.Component, spi.SPIDevice)
CONF_REFERENCE_VOLTAGE = "reference_voltage"
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {

View File

@@ -3,7 +3,13 @@ import esphome.config_validation as cv
import esphome.codegen as cg import esphome.codegen as cg
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID, CONF_VOLUME from esphome.const import (
CONF_ID,
CONF_ON_STATE,
CONF_TRIGGER_ID,
CONF_VOLUME,
CONF_ON_IDLE,
)
from esphome.core import CORE from esphome.core import CORE
from esphome.coroutine import coroutine_with_priority from esphome.coroutine import coroutine_with_priority
from esphome.cpp_helpers import setup_entity from esphome.cpp_helpers import setup_entity
@@ -43,15 +49,18 @@ VolumeSetAction = media_player_ns.class_(
) )
CONF_ON_IDLE = "on_idle"
CONF_ON_PLAY = "on_play" CONF_ON_PLAY = "on_play"
CONF_ON_PAUSE = "on_pause" CONF_ON_PAUSE = "on_pause"
CONF_ON_ANNOUNCEMENT = "on_announcement"
CONF_MEDIA_URL = "media_url" CONF_MEDIA_URL = "media_url"
StateTrigger = media_player_ns.class_("StateTrigger", automation.Trigger.template()) StateTrigger = media_player_ns.class_("StateTrigger", automation.Trigger.template())
IdleTrigger = media_player_ns.class_("IdleTrigger", automation.Trigger.template()) IdleTrigger = media_player_ns.class_("IdleTrigger", automation.Trigger.template())
PlayTrigger = media_player_ns.class_("PlayTrigger", automation.Trigger.template()) PlayTrigger = media_player_ns.class_("PlayTrigger", automation.Trigger.template())
PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.template()) PauseTrigger = media_player_ns.class_("PauseTrigger", automation.Trigger.template())
AnnoucementTrigger = media_player_ns.class_(
"AnnouncementTrigger", automation.Trigger.template()
)
IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition) IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition)
IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition) IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition)
@@ -70,6 +79,9 @@ async def setup_media_player_core_(var, config):
for conf in config.get(CONF_ON_PAUSE, []): for conf in config.get(CONF_ON_PAUSE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_ANNOUNCEMENT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
async def register_media_player(var, config): async def register_media_player(var, config):
@@ -101,6 +113,11 @@ MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger),
} }
), ),
cv.Optional(CONF_ON_ANNOUNCEMENT): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(AnnoucementTrigger),
}
),
} }
) )

View File

@@ -52,6 +52,7 @@ class StateTrigger : public Trigger<> {
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(IdleTrigger, IDLE) MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(IdleTrigger, IDLE)
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PlayTrigger, PLAYING) MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PlayTrigger, PLAYING)
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PauseTrigger, PAUSED) MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PauseTrigger, PAUSED)
MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(AnnouncementTrigger, ANNOUNCING)
template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> { template<typename... Ts> class IsIdleCondition : public Condition<Ts...>, public Parented<MediaPlayer> {
public: public:

View File

@@ -15,6 +15,8 @@ const char *media_player_state_to_string(MediaPlayerState state) {
return "PLAYING"; return "PLAYING";
case MEDIA_PLAYER_STATE_PAUSED: case MEDIA_PLAYER_STATE_PAUSED:
return "PAUSED"; return "PAUSED";
case MEDIA_PLAYER_STATE_ANNOUNCING:
return "ANNOUNCING";
case MEDIA_PLAYER_STATE_NONE: case MEDIA_PLAYER_STATE_NONE:
default: default:
return "UNKNOWN"; return "UNKNOWN";
@@ -68,6 +70,9 @@ void MediaPlayerCall::perform() {
if (this->volume_.has_value()) { if (this->volume_.has_value()) {
ESP_LOGD(TAG, " Volume: %.2f", this->volume_.value()); ESP_LOGD(TAG, " Volume: %.2f", this->volume_.value());
} }
if (this->announcement_.has_value()) {
ESP_LOGD(TAG, " Announcement: %s", this->announcement_.value() ? "yes" : "no");
}
this->parent_->control(*this); this->parent_->control(*this);
} }
@@ -108,6 +113,11 @@ MediaPlayerCall &MediaPlayerCall::set_volume(float volume) {
return *this; return *this;
} }
MediaPlayerCall &MediaPlayerCall::set_announcement(bool announce) {
this->announcement_ = announce;
return *this;
}
void MediaPlayer::add_on_state_callback(std::function<void()> &&callback) { void MediaPlayer::add_on_state_callback(std::function<void()> &&callback) {
this->state_callback_.add(std::move(callback)); this->state_callback_.add(std::move(callback));
} }

View File

@@ -10,7 +10,8 @@ enum MediaPlayerState : uint8_t {
MEDIA_PLAYER_STATE_NONE = 0, MEDIA_PLAYER_STATE_NONE = 0,
MEDIA_PLAYER_STATE_IDLE = 1, MEDIA_PLAYER_STATE_IDLE = 1,
MEDIA_PLAYER_STATE_PLAYING = 2, MEDIA_PLAYER_STATE_PLAYING = 2,
MEDIA_PLAYER_STATE_PAUSED = 3 MEDIA_PLAYER_STATE_PAUSED = 3,
MEDIA_PLAYER_STATE_ANNOUNCING = 4
}; };
const char *media_player_state_to_string(MediaPlayerState state); const char *media_player_state_to_string(MediaPlayerState state);
@@ -51,12 +52,14 @@ class MediaPlayerCall {
MediaPlayerCall &set_media_url(const std::string &url); MediaPlayerCall &set_media_url(const std::string &url);
MediaPlayerCall &set_volume(float volume); MediaPlayerCall &set_volume(float volume);
MediaPlayerCall &set_announcement(bool announce);
void perform(); void perform();
const optional<MediaPlayerCommand> &get_command() const { return command_; } const optional<MediaPlayerCommand> &get_command() const { return command_; }
const optional<std::string> &get_media_url() const { return media_url_; } const optional<std::string> &get_media_url() const { return media_url_; }
const optional<float> &get_volume() const { return volume_; } const optional<float> &get_volume() const { return volume_; }
const optional<bool> &get_announcement() const { return announcement_; }
protected: protected:
void validate_(); void validate_();
@@ -64,6 +67,7 @@ class MediaPlayerCall {
optional<MediaPlayerCommand> command_; optional<MediaPlayerCommand> command_;
optional<std::string> media_url_; optional<std::string> media_url_;
optional<float> volume_; optional<float> volume_;
optional<bool> announcement_;
}; };
class MediaPlayer : public EntityBase { class MediaPlayer : public EntityBase {

View File

@@ -11,6 +11,7 @@ from esphome.const import (
CONF_CUSTOM_PRESETS, CONF_CUSTOM_PRESETS,
CONF_ID, CONF_ID,
CONF_NUM_ATTEMPTS, CONF_NUM_ATTEMPTS,
CONF_OUTDOOR_TEMPERATURE,
CONF_PERIOD, CONF_PERIOD,
CONF_SUPPORTED_MODES, CONF_SUPPORTED_MODES,
CONF_SUPPORTED_PRESETS, CONF_SUPPORTED_PRESETS,
@@ -37,7 +38,6 @@ from esphome.components.climate import (
CODEOWNERS = ["@dudanov"] CODEOWNERS = ["@dudanov"]
DEPENDENCIES = ["climate", "uart"] DEPENDENCIES = ["climate", "uart"]
AUTO_LOAD = ["sensor"] AUTO_LOAD = ["sensor"]
CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature"
CONF_POWER_USAGE = "power_usage" CONF_POWER_USAGE = "power_usage"
CONF_HUMIDITY_SETPOINT = "humidity_setpoint" CONF_HUMIDITY_SETPOINT = "humidity_setpoint"
midea_ac_ns = cg.esphome_ns.namespace("midea").namespace("ac") midea_ac_ns = cg.esphome_ns.namespace("midea").namespace("ac")

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import climate_ir from esphome.components import climate_ir
from esphome.const import CONF_ID from esphome.const import CONF_ID, CONF_USE_FAHRENHEIT
AUTO_LOAD = ["climate_ir", "coolix"] AUTO_LOAD = ["climate_ir", "coolix"]
CODEOWNERS = ["@dudanov"] CODEOWNERS = ["@dudanov"]
@@ -9,7 +9,6 @@ CODEOWNERS = ["@dudanov"]
midea_ir_ns = cg.esphome_ns.namespace("midea_ir") midea_ir_ns = cg.esphome_ns.namespace("midea_ir")
MideaIR = midea_ir_ns.class_("MideaIR", climate_ir.ClimateIR) MideaIR = midea_ir_ns.class_("MideaIR", climate_ir.ClimateIR)
CONF_USE_FAHRENHEIT = "use_fahrenheit"
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
{ {

View File

@@ -6,6 +6,7 @@ from esphome.const import (
CONF_FIELD_STRENGTH_X, CONF_FIELD_STRENGTH_X,
CONF_FIELD_STRENGTH_Y, CONF_FIELD_STRENGTH_Y,
CONF_FIELD_STRENGTH_Z, CONF_FIELD_STRENGTH_Z,
CONF_HEADING,
CONF_ID, CONF_ID,
ICON_MAGNET, ICON_MAGNET,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
@@ -19,8 +20,6 @@ DEPENDENCIES = ["i2c"]
mmc5603_ns = cg.esphome_ns.namespace("mmc5603") mmc5603_ns = cg.esphome_ns.namespace("mmc5603")
CONF_HEADING = "heading"
MMC5603Component = mmc5603_ns.class_( MMC5603Component = mmc5603_ns.class_(
"MMC5603Component", cg.PollingComponent, i2c.I2CDevice "MMC5603Component", cg.PollingComponent, i2c.I2CDevice
) )

View File

@@ -54,7 +54,12 @@ from esphome.components.esp32 import add_idf_sdkconfig_option
DEPENDENCIES = ["network"] DEPENDENCIES = ["network"]
AUTO_LOAD = ["json"]
def AUTO_LOAD():
if CORE.is_esp8266 or CORE.is_libretiny:
return ["async_tcp", "json"]
return ["json"]
CONF_IDF_SEND_ASYNC = "idf_send_async" CONF_IDF_SEND_ASYNC = "idf_send_async"
CONF_SKIP_CERT_CN_CHECK = "skip_cert_cn_check" CONF_SKIP_CERT_CN_CHECK = "skip_cert_cn_check"

View File

@@ -25,7 +25,7 @@ from .base_component import (
CONF_EXIT_REPARSE_ON_START, CONF_EXIT_REPARSE_ON_START,
) )
CODEOWNERS = ["@senexcrenshaw"] CODEOWNERS = ["@senexcrenshaw", "@edwardtfn"]
DEPENDENCIES = ["uart"] DEPENDENCIES = ["uart"]
AUTO_LOAD = ["binary_sensor", "switch", "sensor", "text_sensor"] AUTO_LOAD = ["binary_sensor", "switch", "sensor", "text_sensor"]

View File

@@ -12,7 +12,7 @@
#include "esphome/components/display/display_color_utils.h" #include "esphome/components/display/display_color_utils.h"
#ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_NEXTION_TFT_UPLOAD
#ifdef ARDUINO #ifdef USE_ARDUINO
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <HTTPClient.h> #include <HTTPClient.h>
#endif // USE_ESP32 #endif // USE_ESP32
@@ -22,7 +22,7 @@
#endif // USE_ESP8266 #endif // USE_ESP8266
#elif defined(USE_ESP_IDF) #elif defined(USE_ESP_IDF)
#include <esp_http_client.h> #include <esp_http_client.h>
#endif // ARDUINO vs ESP-IDF #endif // ARDUINO vs USE_ESP_IDF
#endif // USE_NEXTION_TFT_UPLOAD #endif // USE_NEXTION_TFT_UPLOAD
namespace esphome { namespace esphome {
@@ -976,6 +976,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* @return Whether the send was successful. * @return Whether the send was successful.
*/ */
bool send_command(const char *command); bool send_command(const char *command);
/** /**
* Manually send a raw formatted command to the display. * Manually send a raw formatted command to the display.
* @param format The printf-style command format, like "vis %s,0" * @param format The printf-style command format, like "vis %s,0"
@@ -986,20 +987,33 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
#ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_NEXTION_TFT_UPLOAD
/** /**
* Set the tft file URL. https seems problematic with arduino.. * Set the tft file URL. https seems problematic with Arduino..
*/ */
void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; } void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; }
#endif
/** /**
* Upload the tft file and soft reset Nextion * @brief Uploads the TFT file to the Nextion display.
*
* This function initiates the upload of a TFT file to the Nextion display. Users can specify a target baud rate for
* the transfer. If the provided baud rate is not supported by Nextion, the function defaults to using the current
* baud rate set for the display. If no baud rate is specified (or if 0 is passed), the current baud rate is used.
*
* Supported baud rates are: 2400, 4800, 9600, 19200, 31250, 38400, 57600, 115200, 230400, 250000, 256000, 512000
* and 921600. Selecting a baud rate supported by both the Nextion display and the host hardware is essential for
* ensuring a successful upload process.
*
* @param baud_rate The desired baud rate for the TFT file transfer, specified as an unsigned 32-bit integer.
* If the specified baud rate is not supported, or if 0 is passed, the function will use the current baud rate.
* The default value is 0, which implies using the current baud rate.
* @param exit_reparse If true, the function exits reparse mode before uploading the TFT file. This parameter * @param exit_reparse If true, the function exits reparse mode before uploading the TFT file. This parameter
* defaults to true, ensuring that the display is ready to receive and apply the new TFT file without needing * defaults to true, ensuring that the display is ready to receive and apply the new TFT file without needing
* to manually reset or reconfigure. Exiting reparse mode is recommended for most upload scenarios to ensure * to manually reset or reconfigure. Exiting reparse mode is recommended for most upload scenarios to ensure
* the display properly processes the uploaded file command. * the display properly processes the uploaded file command.
* @return bool True: Transfer completed successfuly, False: Transfer failed. * @return bool True: Transfer completed successfuly, False: Transfer failed.
*/ */
bool upload_tft(bool exit_reparse = true); bool upload_tft(uint32_t baud_rate = 0, bool exit_reparse = true);
#endif // USE_NEXTION_TFT_UPLOAD
void dump_config() override; void dump_config() override;
@@ -1122,11 +1136,13 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
void all_components_send_state_(bool force_update = false); void all_components_send_state_(bool force_update = false);
uint64_t comok_sent_ = 0; uint64_t comok_sent_ = 0;
bool remove_from_q_(bool report_empty = true); bool remove_from_q_(bool report_empty = true);
/** /**
* @brief * @brief
* Sends commands ignoring of the Nextion has been setup. * Sends commands ignoring of the Nextion has been setup.
*/ */
bool ignore_is_setup_ = false; bool ignore_is_setup_ = false;
bool nextion_reports_is_setup_ = false; bool nextion_reports_is_setup_ = false;
uint8_t nextion_event_; uint8_t nextion_event_;
@@ -1163,41 +1179,37 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
void check_pending_waveform_(); void check_pending_waveform_();
#ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_NEXTION_TFT_UPLOAD
#ifdef USE_ESP8266
WiFiClient *wifi_client_{nullptr};
BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr};
WiFiClient *get_wifi_client_();
#endif // USE_ESP8266
std::string tft_url_;
uint32_t content_length_ = 0; uint32_t content_length_ = 0;
int tft_size_ = 0; int tft_size_ = 0;
uint32_t original_baud_rate_ = 0; uint32_t original_baud_rate_ = 0;
bool upload_first_chunk_sent_ = false; bool upload_first_chunk_sent_ = false;
std::string tft_url_; #ifdef USE_ARDUINO
uint8_t *transfer_buffer_{nullptr};
size_t transfer_buffer_size_;
#ifdef USE_ESP8266
WiFiClient *wifi_client_{nullptr};
BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr};
WiFiClient *get_wifi_client_();
#endif
#ifdef ARDUINO
/** /**
* will request chunk_size chunks from the web server * will request chunk_size chunks from the web server
* and send each to the nextion * and send each to the nextion
* @param HTTPClient http HTTP client handler. * @param HTTPClient http_client HTTP client handler.
* @param int range_start Position of next byte to transfer. * @param int range_start Position of next byte to transfer.
* @return position of last byte transferred, -1 for failure. * @return position of last byte transferred, -1 for failure.
*/ */
int upload_by_chunks_(HTTPClient *http, int range_start); int upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start);
#elif defined(USE_ESP_IDF)
bool upload_with_range_(uint32_t range_start, uint32_t range_end);
/** /**
* start update tft file to nextion. * will request 4096 bytes chunks from the web server
* * and send each to Nextion
* @param const uint8_t *file_buf * @param esp_http_client_handle_t http_client HTTP client handler.
* @param size_t buf_size * @param int range_start Position of next byte to transfer.
* @return true if success, false for failure. * @return position of last byte transferred, -1 for failure.
*/ */
bool upload_from_buffer_(const uint8_t *file_buf, size_t buf_size); int upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &range_start);
#endif // USE_ARDUINO vs USE_ESP_IDF
/** /**
* Ends the upload process, restart Nextion and, if successful, * Ends the upload process, restart Nextion and, if successful,
* restarts ESP * restarts ESP
@@ -1205,23 +1217,12 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* @return bool True: Transfer completed successfuly, False: Transfer failed. * @return bool True: Transfer completed successfuly, False: Transfer failed.
*/ */
bool upload_end_(bool successful); bool upload_end_(bool successful);
#elif defined(USE_ESP_IDF)
/** /**
* will request 4096 bytes chunks from the web server * Returns the ESP Free Heap memory. This is framework independent.
* and send each to Nextion * @return Free Heap in bytes.
* @param std::string url Full url for download.
* @param int range_start Position of next byte to transfer.
* @return position of last byte transferred, -1 for failure.
*/ */
int upload_range(const std::string &url, int range_start); uint32_t get_free_heap_();
/**
* Ends the upload process, restart Nextion and, if successful,
* restarts ESP
* @param bool url successful True: Transfer completed successfuly, False: Transfer failed.
* @return bool True: Transfer completed successfuly, False: Transfer failed.
*/
bool upload_end(bool successful);
#endif // ARDUINO vs ESP-IDF
#endif // USE_NEXTION_TFT_UPLOAD #endif // USE_NEXTION_TFT_UPLOAD
@@ -1252,7 +1253,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
#ifdef NEXTION_PROTOCOL_LOG #ifdef NEXTION_PROTOCOL_LOG
void print_queue_members_(); void print_queue_members_();
#endif #endif // NEXTION_PROTOCOL_LOG
void reset_(bool reset_nextion = true); void reset_(bool reset_nextion = true);
std::string command_data_; std::string command_data_;

View File

@@ -1,13 +1,14 @@
#include "nextion.h" #include "nextion.h"
#ifdef ARDUINO
#ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_NEXTION_TFT_UPLOAD
#ifdef USE_ARDUINO
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/util.h" #include "esphome/core/util.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/components/network/util.h" #include "esphome/components/network/util.h"
#include <cinttypes>
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <esp_heap_caps.h> #include <esp_heap_caps.h>
@@ -20,139 +21,147 @@ static const char *const TAG = "nextion.upload.arduino";
// Followed guide // Followed guide
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2 // https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { inline uint32_t Nextion::get_free_heap_() {
int range_end = 0; #if defined(USE_ESP32)
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#elif defined(USE_ESP8266)
return EspClass::getFreeHeap();
#endif // USE_ESP32 vs USE_ESP8266
}
if (range_start == 0 && this->transfer_buffer_size_ > 16384) { // Start small at the first run in case of a big skip int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
range_end = 16384 - 1; uint32_t range_size = this->tft_size_ - range_start;
} else { ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
range_end = range_start + this->transfer_buffer_size_ - 1; uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1;
ESP_LOGD(TAG, "Range start: %" PRIu32, range_start);
if (range_size <= 0 or range_end <= range_start) {
ESP_LOGD(TAG, "Range end: %" PRIu32, range_end);
ESP_LOGD(TAG, "Range size: %" PRIu32, range_size);
ESP_LOGE(TAG, "Invalid range");
return -1;
} }
if (range_end > this->tft_size_) char range_header[32];
range_end = this->tft_size_; sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end);
ESP_LOGV(TAG, "Requesting range: %s", range_header);
#ifdef USE_ESP8266 http_client.addHeader("Range", range_header);
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) int code = http_client.GET();
http->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); if (code != HTTP_CODE_OK and code != HTTP_CODE_PARTIAL_CONTENT) {
#elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) ESP_LOGW(TAG, "HTTP Request failed; Error: %s", HTTPClient::errorToString(code).c_str());
http->setFollowRedirects(true); return -1;
#endif
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
http->setRedirectLimit(3);
#endif
#endif // USE_ESP8266
char range_header[64];
sprintf(range_header, "bytes=%d-%d", range_start, range_end);
ESP_LOGD(TAG, "Requesting range: %s", range_header);
int tries = 1;
int code = 0;
bool begin_status = false;
while (tries <= 5) {
#ifdef USE_ESP32
begin_status = http->begin(this->tft_url_.c_str());
#endif
#ifdef USE_ESP8266
begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str());
#endif
++tries;
if (!begin_status) {
ESP_LOGD(TAG, "upload_by_chunks_: connection failed");
delay(500); // NOLINT
continue;
}
http->addHeader("Range", range_header);
code = http->GET();
if (code == 200 || code == 206) {
break;
}
ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s, retries(%d/5)", this->tft_url_.c_str(),
HTTPClient::errorToString(code).c_str(), tries);
http->end();
App.feed_wdt();
delay(500); // NOLINT
} }
if (tries > 5) { // Allocate the buffer dynamically
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
uint8_t *buffer = allocator.allocate(4096);
if (!buffer) {
ESP_LOGE(TAG, "Failed to allocate upload buffer");
return -1; return -1;
} }
std::string recv_string; std::string recv_string;
size_t size = 0; while (true) {
int fetched = 0;
int range = range_end - range_start;
while (fetched < range) {
size = http->getStreamPtr()->available();
if (!size) {
App.feed_wdt();
delay(0);
continue;
}
int c = http->getStreamPtr()->readBytes(
&this->transfer_buffer_[fetched], ((size > this->transfer_buffer_size_) ? this->transfer_buffer_size_ : size));
fetched += c;
}
http->end();
ESP_LOGN(TAG, "Fetched %d of %d bytes", fetched, this->content_length_);
// upload fetched segments to the display in 4KB chunks
int write_len;
for (int i = 0; i < range; i += 4096) {
App.feed_wdt(); App.feed_wdt();
write_len = this->content_length_ < 4096 ? this->content_length_ : 4096; const uint16_t buffer_size =
this->write_array(&this->transfer_buffer_[i], write_len); this->content_length_ < 4096 ? this->content_length_ : 4096; // Limits buffer to the remaining data
this->content_length_ -= write_len; ESP_LOGV(TAG, "Fetching %" PRIu16 " bytes from HTTP", buffer_size);
ESP_LOGD(TAG, "Uploaded %0.2f %%; %d bytes remaining", uint16_t read_len = 0;
100.0 * (this->tft_size_ - this->content_length_) / this->tft_size_, this->content_length_); int partial_read_len = 0;
const uint32_t start_time = millis();
if (!this->upload_first_chunk_sent_) { while (read_len < buffer_size && millis() - start_time < 5000) {
this->upload_first_chunk_sent_ = true; if (http_client.getStreamPtr()->available() > 0) {
delay(500); // NOLINT partial_read_len =
} http_client.getStreamPtr()->readBytes(reinterpret_cast<char *>(buffer) + read_len, buffer_size - read_len);
read_len += partial_read_len;
this->recv_ret_string_(recv_string, 4096, true); if (partial_read_len > 0) {
if (recv_string[0] != 0x05) { // 0x05 == "ok" App.feed_wdt();
ESP_LOGD(TAG, "recv_string [%s]", delay(2);
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str()); }
}
// handle partial upload request
if (recv_string[0] == 0x08 && recv_string.size() == 5) {
uint32_t result = 0;
for (int j = 0; j < 4; ++j) {
result += static_cast<uint8_t>(recv_string[j + 1]) << (8 * j);
}
if (result > 0) {
ESP_LOGD(TAG, "Nextion reported new range %d", result);
this->content_length_ = this->tft_size_ - result;
return result;
} }
} }
recv_string.clear(); if (read_len != buffer_size) {
// Did not receive the full package within the timeout period
ESP_LOGE(TAG, "Failed to read full package, received only %" PRIu16 " of %" PRIu16 " bytes", read_len,
buffer_size);
// Deallocate buffer
allocator.deallocate(buffer, 4096);
buffer = nullptr;
return -1;
}
ESP_LOGV(TAG, "%d bytes fetched, writing it to UART", read_len);
if (read_len > 0) {
recv_string.clear();
this->write_array(buffer, buffer_size);
App.feed_wdt();
this->recv_ret_string_(recv_string, upload_first_chunk_sent_ ? 500 : 5000, true);
this->content_length_ -= read_len;
const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_;
#if defined(USE_ESP32) && defined(USE_PSRAM)
ESP_LOGD(
TAG,
"Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " (DRAM) + %" PRIu32 " (PSRAM) bytes",
upload_percentage, this->content_length_, static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)),
static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)));
#else
ESP_LOGD(TAG, "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " bytes", upload_percentage,
this->content_length_, this->get_free_heap_());
#endif
upload_first_chunk_sent_ = true;
if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request
ESP_LOGD(TAG, "recv_string [%s]",
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
uint32_t result = 0;
for (int j = 0; j < 4; ++j) {
result += static_cast<uint8_t>(recv_string[j + 1]) << (8 * j);
}
if (result > 0) {
ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result);
this->content_length_ = this->tft_size_ - result;
range_start = result;
} else {
range_start = range_end + 1;
}
// Deallocate buffer
allocator.deallocate(buffer, 4096);
buffer = nullptr;
return range_end + 1;
} else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) { // 0x05 == "ok"
ESP_LOGE(TAG, "Invalid response from Nextion: [%s]",
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
// Deallocate buffer
allocator.deallocate(buffer, 4096);
buffer = nullptr;
return -1;
}
recv_string.clear();
} else if (read_len == 0) {
ESP_LOGV(TAG, "End of HTTP response reached");
break; // Exit the loop if there is no more data to read
} else {
ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %d", read_len);
break; // Exit the loop on error
}
} }
range_start = range_end + 1;
// Deallocate buffer
allocator.deallocate(buffer, 4096);
buffer = nullptr;
return range_end + 1; return range_end + 1;
} }
bool Nextion::upload_tft(bool exit_reparse) { bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
ESP_LOGD(TAG, "Nextion TFT upload requested"); ESP_LOGD(TAG, "Nextion TFT upload requested");
ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse)); ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse));
ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str()); ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str());
if (this->is_updating_) { if (this->is_updating_) {
ESP_LOGD(TAG, "Currently updating"); ESP_LOGW(TAG, "Currently uploading");
return false; return false;
} }
if (!network::is_connected()) { if (!network::is_connected()) {
ESP_LOGD(TAG, "network is not connected"); ESP_LOGE(TAG, "Network is not connected");
return false; return false;
} }
@@ -166,43 +175,51 @@ bool Nextion::upload_tft(bool exit_reparse) {
} }
} }
HTTPClient http; // Check if baud rate is supported
http.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along this->original_baud_rate_ = this->parent_->get_baud_rate();
static const std::vector<uint32_t> SUPPORTED_BAUD_RATES = {2400, 4800, 9600, 19200, 31250, 38400, 57600,
115200, 230400, 250000, 256000, 512000, 921600};
if (std::find(SUPPORTED_BAUD_RATES.begin(), SUPPORTED_BAUD_RATES.end(), baud_rate) == SUPPORTED_BAUD_RATES.end()) {
baud_rate = this->original_baud_rate_;
}
ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate);
// Define the configuration for the HTTP client
ESP_LOGV(TAG, "Initializing HTTP client");
ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
HTTPClient http_client;
http_client.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along
bool begin_status = false; bool begin_status = false;
#ifdef USE_ESP32 #ifdef USE_ESP32
begin_status = http.begin(this->tft_url_.c_str()); begin_status = http_client.begin(this->tft_url_.c_str());
#endif #endif
#ifdef USE_ESP8266 #ifdef USE_ESP8266
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http_client.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
#elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) #elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
http.setFollowRedirects(true); http_client.setFollowRedirects(true);
#endif #endif
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0)
http.setRedirectLimit(3); http_client.setRedirectLimit(3);
#endif #endif
begin_status = http.begin(*this->get_wifi_client_(), this->tft_url_.c_str()); begin_status = http_client.begin(*this->get_wifi_client_(), this->tft_url_.c_str());
#endif #endif // USE_ESP8266
if (!begin_status) { if (!begin_status) {
this->is_updating_ = false; this->is_updating_ = false;
ESP_LOGD(TAG, "Connection failed"); ESP_LOGD(TAG, "Connection failed");
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
allocator.deallocate(this->transfer_buffer_, this->transfer_buffer_size_);
return false; return false;
} else { } else {
ESP_LOGD(TAG, "Connected"); ESP_LOGD(TAG, "Connected");
} }
http_client.addHeader("Range", "bytes=0-255");
http.addHeader("Range", "bytes=0-255");
const char *header_names[] = {"Content-Range"}; const char *header_names[] = {"Content-Range"};
http.collectHeaders(header_names, 1); http_client.collectHeaders(header_names, 1);
ESP_LOGD(TAG, "Requesting URL: %s", this->tft_url_.c_str()); ESP_LOGD(TAG, "Requesting URL: %s", this->tft_url_.c_str());
http_client.setReuse(true);
http.setReuse(true);
// try up to 5 times. DNS sometimes needs a second try or so // try up to 5 times. DNS sometimes needs a second try or so
int tries = 1; int tries = 1;
int code = http.GET(); int code = http_client.GET();
delay(100); // NOLINT delay(100); // NOLINT
App.feed_wdt(); App.feed_wdt();
@@ -212,135 +229,133 @@ bool Nextion::upload_tft(bool exit_reparse) {
delay(250); // NOLINT delay(250); // NOLINT
App.feed_wdt(); App.feed_wdt();
code = http.GET(); code = http_client.GET();
++tries; ++tries;
} }
if ((code != 200 && code != 206) || tries > 5) { if (code != 200 and code != 206) {
return this->upload_end_(false); return this->upload_end_(false);
} }
String content_range_string = http.header("Content-Range"); String content_range_string = http_client.header("Content-Range");
content_range_string.remove(0, 12); content_range_string.remove(0, 12);
this->content_length_ = content_range_string.toInt(); this->tft_size_ = content_range_string.toInt();
this->tft_size_ = content_length_;
http.end();
if (this->content_length_ < 4096) { ESP_LOGD(TAG, "TFT file size: %zu bytes", this->tft_size_);
ESP_LOGE(TAG, "Failed to get file size"); if (this->tft_size_ < 4096) {
ESP_LOGE(TAG, "File size check failed.");
ESP_LOGD(TAG, "Close HTTP connection");
http_client.end();
ESP_LOGV(TAG, "Connection closed");
return this->upload_end_(false); return this->upload_end_(false);
} else {
ESP_LOGV(TAG, "File size check passed. Proceeding...");
} }
this->content_length_ = this->tft_size_;
ESP_LOGD(TAG, "Updating Nextion %s...", this->device_model_.c_str()); ESP_LOGD(TAG, "Uploading Nextion");
// The Nextion will ignore the update command if it is sleeping
// The Nextion will ignore the upload command if it is sleeping
ESP_LOGV(TAG, "Wake-up Nextion");
this->ignore_is_setup_ = true;
this->send_command_("sleep=0"); this->send_command_("sleep=0");
this->set_backlight_brightness(1.0); this->send_command_("dim=100");
delay(250); // NOLINT delay(250); // NOLINT
ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
App.feed_wdt(); App.feed_wdt();
char command[128]; char command[128];
// Tells the Nextion the content length of the tft file and baud rate it will be sent at // Tells the Nextion the content length of the tft file and baud rate it will be sent at
// Once the Nextion accepts the command it will wait until the file is successfully uploaded // Once the Nextion accepts the command it will wait until the file is successfully uploaded
// If it fails for any reason a power cycle of the display will be needed // If it fails for any reason a power cycle of the display will be needed
sprintf(command, "whmi-wris %d,%d,1", this->content_length_, this->parent_->get_baud_rate()); sprintf(command, "whmi-wris %d,%d,1", this->content_length_, baud_rate);
// Clear serial receive buffer // Clear serial receive buffer
uint8_t d; ESP_LOGV(TAG, "Clear serial receive buffer");
while (this->available()) { this->reset_(false);
this->read_byte(&d); delay(250); // NOLINT
}; ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
ESP_LOGV(TAG, "Send upload instruction: %s", command);
this->send_command_(command); this->send_command_(command);
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Changing baud rate from %" PRIu32 " to %" PRIu32 " bps", this->original_baud_rate_, baud_rate);
this->parent_->set_baud_rate(baud_rate);
this->parent_->load_settings();
}
App.feed_wdt(); App.feed_wdt();
std::string response; std::string response;
ESP_LOGD(TAG, "Waiting for upgrade response"); ESP_LOGV(TAG, "Waiting for upgrade response");
this->recv_ret_string_(response, 2000, true); // This can take some time to return this->recv_ret_string_(response, 5000, true); // This can take some time to return
// The Nextion display will, if it's ready to accept data, send a 0x05 byte. // The Nextion display will, if it's ready to accept data, send a 0x05 byte.
ESP_LOGD(TAG, "Upgrade response is [%s] - %zu bytes", ESP_LOGD(TAG, "Upgrade response is [%s] - %zu byte(s)",
format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str(), format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str(),
response.length()); response.length());
ESP_LOGV(TAG, "Free heap: %" PRIu32, this->get_free_heap_());
for (size_t i = 0; i < response.length(); i++) {
ESP_LOGD(TAG, "Available %d : 0x%02X", i, response[i]);
}
if (response.find(0x05) != std::string::npos) { if (response.find(0x05) != std::string::npos) {
ESP_LOGD(TAG, "preparation for tft update done"); ESP_LOGV(TAG, "Preparation for TFT upload done");
} else { } else {
ESP_LOGD(TAG, "preparation for tft update failed %d \"%s\"", response[0], response.c_str()); ESP_LOGE(TAG, "Preparation for TFT upload failed %d \"%s\"", response[0], response.c_str());
ESP_LOGD(TAG, "Close HTTP connection");
http_client.end();
ESP_LOGV(TAG, "Connection closed");
return this->upload_end_(false); return this->upload_end_(false);
} }
// Nextion wants 4096 bytes at a time. Make chunk_size a multiple of 4096 ESP_LOGD(TAG, "Uploading TFT to Nextion:");
#ifdef USE_ESP32 ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str());
uint32_t chunk_size = 8192; ESP_LOGD(TAG, " File size: %d bytes", this->content_length_);
if (heap_caps_get_free_size(MALLOC_CAP_SPIRAM) > 0) { ESP_LOGD(TAG, " Free heap: %" PRIu32, this->get_free_heap_());
chunk_size = this->content_length_;
} else {
if (ESP.getFreeHeap() > 81920) { // Ensure some FreeHeap to other things and limit chunk size
chunk_size = ESP.getFreeHeap() - 65536;
chunk_size = int(chunk_size / 4096) * 4096;
chunk_size = chunk_size > 65536 ? 65536 : chunk_size;
} else if (ESP.getFreeHeap() < 32768) {
chunk_size = 4096;
}
}
#else
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
uint32_t chunk_size = ESP.getFreeHeap() < 16384 ? 4096 : 8192;
#endif
if (this->transfer_buffer_ == nullptr) { // Proceed with the content download as before
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
ESP_LOGD(TAG, "Allocating buffer size %d, Heap size is %u", chunk_size, ESP.getFreeHeap());
this->transfer_buffer_ = allocator.allocate(chunk_size);
if (this->transfer_buffer_ == nullptr) { // Try a smaller size
ESP_LOGD(TAG, "Could not allocate buffer size: %d trying 4096 instead", chunk_size);
chunk_size = 4096;
ESP_LOGD(TAG, "Allocating %d buffer", chunk_size);
this->transfer_buffer_ = allocator.allocate(chunk_size);
if (!this->transfer_buffer_) ESP_LOGV(TAG, "Starting transfer by chunks loop");
return this->upload_end_(false);
}
this->transfer_buffer_size_ = chunk_size; uint32_t position = 0;
}
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
ESP_LOGD(TAG, "Updating tft from \"%s\" with a file size of %d using %zu chunksize, Heap Size %d",
this->tft_url_.c_str(), this->content_length_, this->transfer_buffer_size_, ESP.getFreeHeap());
int result = 0;
while (this->content_length_ > 0) { while (this->content_length_ > 0) {
result = this->upload_by_chunks_(&http, result); int upload_result = upload_by_chunks_(http_client, position);
if (result < 0) { if (upload_result < 0) {
ESP_LOGD(TAG, "Error updating Nextion!"); ESP_LOGE(TAG, "Error uploading TFT to Nextion!");
ESP_LOGD(TAG, "Close HTTP connection");
http_client.end();
ESP_LOGV(TAG, "Connection closed");
return this->upload_end_(false); return this->upload_end_(false);
} }
App.feed_wdt(); App.feed_wdt();
// NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGV(TAG, "Free heap: %" PRIu32 ", Bytes left: %" PRIu32, this->get_free_heap_(), this->content_length_);
ESP_LOGD(TAG, "Heap Size %d, Bytes left %d", ESP.getFreeHeap(), this->content_length_);
} }
ESP_LOGD(TAG, "Successfully updated Nextion!");
return this->upload_end_(true); ESP_LOGD(TAG, "Successfully uploaded TFT to Nextion!");
ESP_LOGD(TAG, "Close HTTP connection");
http_client.end();
ESP_LOGV(TAG, "Connection closed");
return upload_end_(true);
} }
bool Nextion::upload_end_(bool successful) { bool Nextion::upload_end_(bool successful) {
ESP_LOGD(TAG, "Nextion TFT upload finished: %s", YESNO(successful));
this->is_updating_ = false; this->is_updating_ = false;
ESP_LOGD(TAG, "Restarting Nextion"); this->ignore_is_setup_ = false;
this->soft_reset();
uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Changing baud rate back from %" PRIu32 " to %" PRIu32 " bps", baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
if (successful) { if (successful) {
ESP_LOGD(TAG, "Restarting ESPHome");
delay(1500); // NOLINT delay(1500); // NOLINT
ESP_LOGD(TAG, "Restarting esphome"); arch_restart();
ESP.restart(); // NOLINT(readability-static-accessed-through-instance) } else {
ESP_LOGE(TAG, "Nextion TFT upload failed");
} }
return successful; return successful;
} }
@@ -363,9 +378,10 @@ WiFiClient *Nextion::get_wifi_client_() {
} }
return this->wifi_client_; return this->wifi_client_;
} }
#endif #endif // USE_ESP8266
} // namespace nextion } // namespace nextion
} // namespace esphome } // namespace esphome
#endif // USE_ARDUINO
#endif // USE_NEXTION_TFT_UPLOAD #endif // USE_NEXTION_TFT_UPLOAD
#endif // ARDUINO

View File

@@ -1,17 +1,16 @@
#include "nextion.h" #include "nextion.h"
#ifdef USE_ESP_IDF
#ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_NEXTION_TFT_UPLOAD
#ifdef USE_ESP_IDF
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/util.h" #include "esphome/core/util.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/components/network/util.h" #include "esphome/components/network/util.h"
#include <cinttypes>
#include <esp_heap_caps.h> #include <esp_heap_caps.h>
#include <esp_http_client.h> #include <esp_http_client.h>
#include <cinttypes>
namespace esphome { namespace esphome {
namespace nextion { namespace nextion {
@@ -20,153 +19,147 @@ static const char *const TAG = "nextion.upload.idf";
// Followed guide // Followed guide
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2 // https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
int Nextion::upload_range(const std::string &url, int range_start) { int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &range_start) {
ESP_LOGVV(TAG, "url: %s", url.c_str()); uint32_t range_size = this->tft_size_ - range_start;
uint range_size = this->tft_size_ - range_start; ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size());
ESP_LOGVV(TAG, "tft_size_: %i", this->tft_size_); uint32_t range_end = ((upload_first_chunk_sent_ or this->tft_size_ < 4096) ? this->tft_size_ : 4096) - 1;
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); ESP_LOGD(TAG, "Range start: %" PRIu32, range_start);
int range_end = (range_start == 0) ? std::min(this->tft_size_, 16383) : this->tft_size_;
if (range_size <= 0 or range_end <= range_start) { if (range_size <= 0 or range_end <= range_start) {
ESP_LOGD(TAG, "Range end: %" PRIu32, range_end);
ESP_LOGD(TAG, "Range size: %" PRIu32, range_size);
ESP_LOGE(TAG, "Invalid range"); ESP_LOGE(TAG, "Invalid range");
ESP_LOGD(TAG, "Range start: %i", range_start);
ESP_LOGD(TAG, "Range end: %i", range_end);
ESP_LOGD(TAG, "Range size: %i", range_size);
return -1; return -1;
} }
esp_http_client_config_t config = { char range_header[32];
.url = url.c_str(), sprintf(range_header, "bytes=%" PRIu32 "-%" PRIu32, range_start, range_end);
.cert_pem = nullptr,
.disable_auto_redirect = false,
.max_redirection_count = 10,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
char range_header[64];
sprintf(range_header, "bytes=%d-%d", range_start, range_end);
ESP_LOGV(TAG, "Requesting range: %s", range_header); ESP_LOGV(TAG, "Requesting range: %s", range_header);
esp_http_client_set_header(client, "Range", range_header); esp_http_client_set_header(http_client, "Range", range_header);
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); ESP_LOGV(TAG, "Opening HTTP connetion");
ESP_LOGV(TAG, "Opening http connetion");
esp_err_t err; esp_err_t err;
if ((err = esp_http_client_open(client, 0)) != ESP_OK) { if ((err = esp_http_client_open(http_client, 0)) != ESP_OK) {
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
esp_http_client_cleanup(client);
return -1; return -1;
} }
ESP_LOGV(TAG, "Fetch content length"); ESP_LOGV(TAG, "Fetch content length");
int content_length = esp_http_client_fetch_headers(client); const int chunk_size = esp_http_client_fetch_headers(http_client);
ESP_LOGV(TAG, "content_length = %d", content_length); ESP_LOGV(TAG, "content_length = %d", chunk_size);
if (content_length <= 0) { if (chunk_size <= 0) {
ESP_LOGE(TAG, "Failed to get content length: %d", content_length); ESP_LOGE(TAG, "Failed to get chunk's content length: %d", chunk_size);
esp_http_client_cleanup(client);
return -1; return -1;
} }
int total_read_len = 0, read_len; // Allocate the buffer dynamically
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); uint8_t *buffer = allocator.allocate(4096);
ESP_LOGV(TAG, "Allocate buffer"); if (!buffer) {
uint8_t *buffer = new uint8_t[4096]; ESP_LOGE(TAG, "Failed to allocate upload buffer");
std::string recv_string; return -1;
if (buffer == nullptr) {
ESP_LOGE(TAG, "Failed to allocate memory for buffer");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
} else {
ESP_LOGV(TAG, "Memory for buffer allocated successfully");
while (true) {
App.feed_wdt();
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
int read_len = esp_http_client_read(client, reinterpret_cast<char *>(buffer), 4096);
ESP_LOGVV(TAG, "Read %d bytes from HTTP client, writing to UART", read_len);
if (read_len > 0) {
this->write_array(buffer, read_len);
ESP_LOGVV(TAG, "Write to UART successful");
this->recv_ret_string_(recv_string, 5000, true);
this->content_length_ -= read_len;
ESP_LOGD(TAG, "Uploaded %0.2f %%, remaining %d bytes, heap is %" PRIu32 " bytes",
100.0 * (this->tft_size_ - this->content_length_) / this->tft_size_, this->content_length_,
esp_get_free_heap_size());
if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request
ESP_LOGD(
TAG, "recv_string [%s]",
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
uint32_t result = 0;
for (int j = 0; j < 4; ++j) {
result += static_cast<uint8_t>(recv_string[j + 1]) << (8 * j);
}
if (result > 0) {
ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result);
this->content_length_ = this->tft_size_ - result;
// Deallocate the buffer when done
ESP_LOGV(TAG, "Deallocate buffer");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
delete[] buffer;
ESP_LOGVV(TAG, "Memory for buffer deallocated");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
ESP_LOGV(TAG, "Close http client");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
esp_http_client_close(client);
esp_http_client_cleanup(client);
ESP_LOGVV(TAG, "Client closed");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
return result;
}
} else if (recv_string[0] != 0x05) { // 0x05 == "ok"
ESP_LOGE(
TAG, "Invalid response from Nextion: [%s]",
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
ESP_LOGV(TAG, "Deallocate buffer");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
delete[] buffer;
ESP_LOGVV(TAG, "Memory for buffer deallocated");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
ESP_LOGV(TAG, "Close http client");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
esp_http_client_close(client);
esp_http_client_cleanup(client);
ESP_LOGVV(TAG, "Client closed");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
return -1;
}
recv_string.clear();
} else if (read_len == 0) {
ESP_LOGV(TAG, "End of HTTP response reached");
break; // Exit the loop if there is no more data to read
} else {
ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %d", read_len);
break; // Exit the loop on error
}
}
// Deallocate the buffer when done
ESP_LOGV(TAG, "Deallocate buffer");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
delete[] buffer;
ESP_LOGVV(TAG, "Memory for buffer deallocated");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
} }
ESP_LOGV(TAG, "Close http client");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); std::string recv_string;
esp_http_client_close(client); while (true) {
esp_http_client_cleanup(client); App.feed_wdt();
ESP_LOGVV(TAG, "Client closed"); const uint16_t buffer_size =
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); this->content_length_ < 4096 ? this->content_length_ : 4096; // Limits buffer to the remaining data
ESP_LOGV(TAG, "Fetching %" PRIu16 " bytes from HTTP", buffer_size);
uint16_t read_len = 0;
int partial_read_len = 0;
uint8_t retries = 0;
// Attempt to read the chunk with retries.
while (retries < 5 && read_len < buffer_size) {
partial_read_len =
esp_http_client_read(http_client, reinterpret_cast<char *>(buffer) + read_len, buffer_size - read_len);
if (partial_read_len > 0) {
read_len += partial_read_len; // Accumulate the total read length.
// Reset retries on successful read.
retries = 0;
} else {
// If no data was read, increment retries.
retries++;
vTaskDelay(pdMS_TO_TICKS(2)); // NOLINT
}
App.feed_wdt(); // Feed the watchdog timer.
}
if (read_len != buffer_size) {
// Did not receive the full package within the timeout period
ESP_LOGE(TAG, "Failed to read full package, received only %" PRIu16 " of %" PRIu16 " bytes", read_len,
buffer_size);
// Deallocate buffer
allocator.deallocate(buffer, 4096);
buffer = nullptr;
return -1;
}
ESP_LOGV(TAG, "%d bytes fetched, writing it to UART", read_len);
if (read_len > 0) {
recv_string.clear();
this->write_array(buffer, buffer_size);
App.feed_wdt();
this->recv_ret_string_(recv_string, upload_first_chunk_sent_ ? 500 : 5000, true);
this->content_length_ -= read_len;
const float upload_percentage = 100.0f * (this->tft_size_ - this->content_length_) / this->tft_size_;
#ifdef USE_PSRAM
ESP_LOGD(
TAG,
"Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " (DRAM) + %" PRIu32 " (PSRAM) bytes",
upload_percentage, this->content_length_, static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)),
static_cast<uint32_t>(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)));
#else
ESP_LOGD(TAG, "Uploaded %0.2f%%, remaining %" PRIu32 " bytes, free heap: %" PRIu32 " bytes", upload_percentage,
this->content_length_, static_cast<uint32_t>(esp_get_free_heap_size()));
#endif
upload_first_chunk_sent_ = true;
if (recv_string[0] == 0x08 && recv_string.size() == 5) { // handle partial upload request
ESP_LOGD(TAG, "recv_string [%s]",
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
uint32_t result = 0;
for (int j = 0; j < 4; ++j) {
result += static_cast<uint8_t>(recv_string[j + 1]) << (8 * j);
}
if (result > 0) {
ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result);
this->content_length_ = this->tft_size_ - result;
range_start = result;
} else {
range_start = range_end + 1;
}
// Deallocate buffer
allocator.deallocate(buffer, 4096);
buffer = nullptr;
return range_end + 1;
} else if (recv_string[0] != 0x05 and recv_string[0] != 0x08) { // 0x05 == "ok"
ESP_LOGE(TAG, "Invalid response from Nextion: [%s]",
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
// Deallocate buffer
allocator.deallocate(buffer, 4096);
buffer = nullptr;
return -1;
}
recv_string.clear();
} else if (read_len == 0) {
ESP_LOGV(TAG, "End of HTTP response reached");
break; // Exit the loop if there is no more data to read
} else {
ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %" PRIu16, read_len);
break; // Exit the loop on error
}
}
range_start = range_end + 1;
// Deallocate buffer
allocator.deallocate(buffer, 4096);
buffer = nullptr;
return range_end + 1; return range_end + 1;
} }
bool Nextion::upload_tft(bool exit_reparse) { bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
ESP_LOGD(TAG, "Nextion TFT upload requested"); ESP_LOGD(TAG, "Nextion TFT upload requested");
ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse)); ESP_LOGD(TAG, "Exit reparse: %s", YESNO(exit_reparse));
ESP_LOGD(TAG, "url: %s", this->tft_url_.c_str()); ESP_LOGD(TAG, "URL: %s", this->tft_url_.c_str());
if (this->is_updating_) { if (this->is_updating_) {
ESP_LOGW(TAG, "Currently updating"); ESP_LOGW(TAG, "Currently uploading");
return false; return false;
} }
@@ -185,9 +178,18 @@ bool Nextion::upload_tft(bool exit_reparse) {
} }
} }
// Check if baud rate is supported
this->original_baud_rate_ = this->parent_->get_baud_rate();
static const std::vector<uint32_t> SUPPORTED_BAUD_RATES = {2400, 4800, 9600, 19200, 31250, 38400, 57600,
115200, 230400, 250000, 256000, 512000, 921600};
if (std::find(SUPPORTED_BAUD_RATES.begin(), SUPPORTED_BAUD_RATES.end(), baud_rate) == SUPPORTED_BAUD_RATES.end()) {
baud_rate = this->original_baud_rate_;
}
ESP_LOGD(TAG, "Baud rate: %" PRIu32, baud_rate);
// Define the configuration for the HTTP client // Define the configuration for the HTTP client
ESP_LOGV(TAG, "Establishing connection to HTTP server"); ESP_LOGV(TAG, "Initializing HTTP client");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size());
esp_http_client_config_t config = { esp_http_client_config_t config = {
.url = this->tft_url_.c_str(), .url = this->tft_url_.c_str(),
.cert_pem = nullptr, .cert_pem = nullptr,
@@ -196,124 +198,164 @@ bool Nextion::upload_tft(bool exit_reparse) {
.disable_auto_redirect = false, .disable_auto_redirect = false,
.max_redirection_count = 10, .max_redirection_count = 10,
}; };
// Initialize the HTTP client with the configuration // Initialize the HTTP client with the configuration
ESP_LOGV(TAG, "Initializing HTTP client"); esp_http_client_handle_t http_client = esp_http_client_init(&config);
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); if (!http_client) {
esp_http_client_handle_t http = esp_http_client_init(&config);
if (!http) {
ESP_LOGE(TAG, "Failed to initialize HTTP client."); ESP_LOGE(TAG, "Failed to initialize HTTP client.");
return this->upload_end(false); return this->upload_end_(false);
}
esp_err_t err = esp_http_client_set_header(http_client, "Connection", "keep-alive");
if (err != ESP_OK) {
ESP_LOGE(TAG, "HTTP set header failed: %s", esp_err_to_name(err));
esp_http_client_cleanup(http_client);
return this->upload_end_(false);
} }
// Perform the HTTP request // Perform the HTTP request
ESP_LOGV(TAG, "Check if the client could connect"); ESP_LOGV(TAG, "Check if the client could connect");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size());
esp_err_t err = esp_http_client_perform(http); err = esp_http_client_perform(http_client);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
esp_http_client_cleanup(http); esp_http_client_cleanup(http_client);
return this->upload_end(false); return this->upload_end_(false);
} }
// Check the HTTP Status Code // Check the HTTP Status Code
ESP_LOGV(TAG, "Check the HTTP Status Code"); ESP_LOGV(TAG, "Check the HTTP Status Code");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size());
int status_code = esp_http_client_get_status_code(http); int status_code = esp_http_client_get_status_code(http_client);
ESP_LOGV(TAG, "HTTP Status Code: %d", status_code); if (status_code != 200 && status_code != 206) {
size_t tft_file_size = esp_http_client_get_content_length(http); return this->upload_end_(false);
ESP_LOGD(TAG, "TFT file size: %zu", tft_file_size); }
ESP_LOGD(TAG, "Close HTTP connection"); this->tft_size_ = esp_http_client_get_content_length(http_client);
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
esp_http_client_close(http);
esp_http_client_cleanup(http);
ESP_LOGVV(TAG, "Connection closed");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
if (tft_file_size < 4096) { ESP_LOGD(TAG, "TFT file size: %zu bytes", this->tft_size_);
ESP_LOGE(TAG, "File size check failed. Size: %zu", tft_file_size); if (this->tft_size_ < 4096 || this->tft_size_ > 134217728) {
return this->upload_end(false); ESP_LOGE(TAG, "File size check failed.");
ESP_LOGD(TAG, "Close HTTP connection");
esp_http_client_close(http_client);
esp_http_client_cleanup(http_client);
ESP_LOGV(TAG, "Connection closed");
return this->upload_end_(false);
} else { } else {
ESP_LOGV(TAG, "File size check passed. Proceeding..."); ESP_LOGV(TAG, "File size check passed. Proceeding...");
} }
this->content_length_ = tft_file_size; this->content_length_ = this->tft_size_;
this->tft_size_ = tft_file_size;
ESP_LOGD(TAG, "Updating Nextion"); ESP_LOGD(TAG, "Uploading Nextion");
// The Nextion will ignore the update command if it is sleeping // The Nextion will ignore the upload command if it is sleeping
ESP_LOGV(TAG, "Wake-up Nextion"); ESP_LOGV(TAG, "Wake-up Nextion");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); this->ignore_is_setup_ = true;
this->send_command_("sleep=0"); this->send_command_("sleep=0");
this->set_backlight_brightness(1.0); this->send_command_("dim=100");
vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT
ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size());
App.feed_wdt(); App.feed_wdt();
char command[128]; char command[128];
// Tells the Nextion the content length of the tft file and baud rate it will be sent at // Tells the Nextion the content length of the tft file and baud rate it will be sent at
// Once the Nextion accepts the command it will wait until the file is successfully uploaded // Once the Nextion accepts the command it will wait until the file is successfully uploaded
// If it fails for any reason a power cycle of the display will be needed // If it fails for any reason a power cycle of the display will be needed
sprintf(command, "whmi-wris %d,%" PRIu32 ",1", this->content_length_, this->parent_->get_baud_rate()); sprintf(command, "whmi-wris %d,%" PRIu32 ",1", this->content_length_, baud_rate);
// Clear serial receive buffer // Clear serial receive buffer
ESP_LOGV(TAG, "Clear serial receive buffer"); ESP_LOGV(TAG, "Clear serial receive buffer");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); this->reset_(false);
uint8_t d; vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT
while (this->available()) { ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size());
this->read_byte(&d);
};
ESP_LOGV(TAG, "Send update instruction: %s", command); ESP_LOGV(TAG, "Send upload instruction: %s", command);
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
this->send_command_(command); this->send_command_(command);
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Changing baud rate from %" PRIu32 " to %" PRIu32 " bps", this->original_baud_rate_, baud_rate);
this->parent_->set_baud_rate(baud_rate);
this->parent_->load_settings();
}
std::string response; std::string response;
ESP_LOGV(TAG, "Waiting for upgrade response"); ESP_LOGV(TAG, "Waiting for upgrade response");
this->recv_ret_string_(response, 5000, true); // This can take some time to return this->recv_ret_string_(response, 5000, true); // This can take some time to return
// The Nextion display will, if it's ready to accept data, send a 0x05 byte. // The Nextion display will, if it's ready to accept data, send a 0x05 byte.
ESP_LOGD(TAG, "Upgrade response is [%s] - %zu bytes", ESP_LOGD(TAG, "Upgrade response is [%s] - %zu byte(s)",
format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str(), format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str(),
response.length()); response.length());
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size()); ESP_LOGV(TAG, "Free heap: %" PRIu32, esp_get_free_heap_size());
if (response.find(0x05) != std::string::npos) { if (response.find(0x05) != std::string::npos) {
ESP_LOGV(TAG, "Preparation for tft update done"); ESP_LOGV(TAG, "Preparation for TFT upload done");
} else { } else {
ESP_LOGE(TAG, "Preparation for tft update failed %d \"%s\"", response[0], response.c_str()); ESP_LOGE(TAG, "Preparation for TFT upload failed %d \"%s\"", response[0], response.c_str());
return this->upload_end(false); ESP_LOGD(TAG, "Close HTTP connection");
esp_http_client_close(http_client);
esp_http_client_cleanup(http_client);
ESP_LOGV(TAG, "Connection closed");
return this->upload_end_(false);
} }
ESP_LOGD(TAG, "Updating tft from \"%s\" with a file size of %d, Heap Size %" PRIu32, this->tft_url_.c_str(), ESP_LOGV(TAG, "Change the method to GET before starting the download");
content_length_, esp_get_free_heap_size()); esp_err_t set_method_result = esp_http_client_set_method(http_client, HTTP_METHOD_GET);
if (set_method_result != ESP_OK) {
ESP_LOGE(TAG, "Failed to set HTTP method to GET: %s", esp_err_to_name(set_method_result));
return this->upload_end_(false);
}
ESP_LOGD(TAG, "Uploading TFT to Nextion:");
ESP_LOGD(TAG, " URL: %s", this->tft_url_.c_str());
ESP_LOGD(TAG, " File size: %" PRIu32 " bytes", this->content_length_);
ESP_LOGD(TAG, " Free heap: %" PRIu32, esp_get_free_heap_size());
// Proceed with the content download as before
ESP_LOGV(TAG, "Starting transfer by chunks loop"); ESP_LOGV(TAG, "Starting transfer by chunks loop");
ESP_LOGVV(TAG, "Available heap: %" PRIu32, esp_get_free_heap_size());
int result = 0; uint32_t position = 0;
while (content_length_ > 0) { while (this->content_length_ > 0) {
result = upload_range(this->tft_url_.c_str(), result); int upload_result = upload_by_chunks_(http_client, position);
if (result < 0) { if (upload_result < 0) {
ESP_LOGE(TAG, "Error updating Nextion!"); ESP_LOGE(TAG, "Error uploading TFT to Nextion!");
return this->upload_end(false); ESP_LOGD(TAG, "Close HTTP connection");
esp_http_client_close(http_client);
esp_http_client_cleanup(http_client);
ESP_LOGV(TAG, "Connection closed");
return this->upload_end_(false);
} }
App.feed_wdt(); App.feed_wdt();
ESP_LOGV(TAG, "Heap Size %" PRIu32 ", Bytes left %d", esp_get_free_heap_size(), content_length_); ESP_LOGV(TAG, "Free heap: %" PRIu32 ", Bytes left: %" PRIu32, esp_get_free_heap_size(), this->content_length_);
} }
ESP_LOGD(TAG, "Successfully updated Nextion!"); ESP_LOGD(TAG, "Successfully uploaded TFT to Nextion!");
return upload_end(true); ESP_LOGD(TAG, "Close HTTP connection");
esp_http_client_close(http_client);
esp_http_client_cleanup(http_client);
ESP_LOGV(TAG, "Connection closed");
return this->upload_end_(true);
} }
bool Nextion::upload_end(bool successful) { bool Nextion::upload_end_(bool successful) {
ESP_LOGD(TAG, "Nextion TFT upload finished: %s", YESNO(successful));
this->is_updating_ = false; this->is_updating_ = false;
ESP_LOGD(TAG, "Restarting Nextion"); this->ignore_is_setup_ = false;
this->soft_reset();
vTaskDelay(pdMS_TO_TICKS(1500)); // NOLINT uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Changing baud rate back from %" PRIu32 " to %" PRIu32 " bps", baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
if (successful) { if (successful) {
ESP_LOGD(TAG, "Restarting ESPHome"); ESP_LOGD(TAG, "Restarting ESPHome");
esp_restart(); // NOLINT(readability-static-accessed-through-instance) delay(1500); // NOLINT
arch_restart();
} else {
ESP_LOGE(TAG, "Nextion TFT upload failed");
} }
return successful; return successful;
} }
@@ -321,5 +363,5 @@ bool Nextion::upload_end(bool successful) {
} // namespace nextion } // namespace nextion
} // namespace esphome } // namespace esphome
#endif // USE_NEXTION_TFT_UPLOAD
#endif // USE_ESP_IDF #endif // USE_ESP_IDF
#endif // USE_NEXTION_TFT_UPLOAD

View File

@@ -2,14 +2,19 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.components import nfc from esphome.components import nfc
from esphome.const import CONF_ID, CONF_ON_TAG_REMOVED, CONF_ON_TAG, CONF_TRIGGER_ID from esphome.const import (
CONF_ID,
CONF_ON_FINISHED_WRITE,
CONF_ON_TAG_REMOVED,
CONF_ON_TAG,
CONF_TRIGGER_ID,
)
CODEOWNERS = ["@OttoWinter", "@jesserockz"] CODEOWNERS = ["@OttoWinter", "@jesserockz"]
AUTO_LOAD = ["binary_sensor", "nfc"] AUTO_LOAD = ["binary_sensor", "nfc"]
MULTI_CONF = True MULTI_CONF = True
CONF_PN532_ID = "pn532_id" CONF_PN532_ID = "pn532_id"
CONF_ON_FINISHED_WRITE = "on_finished_write"
pn532_ns = cg.esphome_ns.namespace("pn532") pn532_ns = cg.esphome_ns.namespace("pn532")
PN532 = pn532_ns.class_("PN532", cg.PollingComponent) PN532 = pn532_ns.class_("PN532", cg.PollingComponent)

View File

@@ -6,6 +6,8 @@ from esphome.components import nfc
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_IRQ_PIN, CONF_IRQ_PIN,
CONF_MESSAGE,
CONF_ON_FINISHED_WRITE,
CONF_ON_TAG_REMOVED, CONF_ON_TAG_REMOVED,
CONF_ON_TAG, CONF_ON_TAG,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
@@ -18,8 +20,6 @@ CONF_EMULATION_MESSAGE = "emulation_message"
CONF_EMULATION_OFF = "emulation_off" CONF_EMULATION_OFF = "emulation_off"
CONF_EMULATION_ON = "emulation_on" CONF_EMULATION_ON = "emulation_on"
CONF_INCLUDE_ANDROID_APP_RECORD = "include_android_app_record" CONF_INCLUDE_ANDROID_APP_RECORD = "include_android_app_record"
CONF_MESSAGE = "message"
CONF_ON_FINISHED_WRITE = "on_finished_write"
CONF_ON_EMULATED_TAG_SCAN = "on_emulated_tag_scan" CONF_ON_EMULATED_TAG_SCAN = "on_emulated_tag_scan"
CONF_PN7150_ID = "pn7150_id" CONF_PN7150_ID = "pn7150_id"
CONF_POLLING_OFF = "polling_off" CONF_POLLING_OFF = "polling_off"

View File

@@ -6,6 +6,8 @@ from esphome.components import nfc
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
CONF_IRQ_PIN, CONF_IRQ_PIN,
CONF_MESSAGE,
CONF_ON_FINISHED_WRITE,
CONF_ON_TAG_REMOVED, CONF_ON_TAG_REMOVED,
CONF_ON_TAG, CONF_ON_TAG,
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
@@ -19,8 +21,6 @@ CONF_EMULATION_MESSAGE = "emulation_message"
CONF_EMULATION_OFF = "emulation_off" CONF_EMULATION_OFF = "emulation_off"
CONF_EMULATION_ON = "emulation_on" CONF_EMULATION_ON = "emulation_on"
CONF_INCLUDE_ANDROID_APP_RECORD = "include_android_app_record" CONF_INCLUDE_ANDROID_APP_RECORD = "include_android_app_record"
CONF_MESSAGE = "message"
CONF_ON_FINISHED_WRITE = "on_finished_write"
CONF_ON_EMULATED_TAG_SCAN = "on_emulated_tag_scan" CONF_ON_EMULATED_TAG_SCAN = "on_emulated_tag_scan"
CONF_PN7160_ID = "pn7160_id" CONF_PN7160_ID = "pn7160_id"
CONF_POLLING_OFF = "polling_off" CONF_POLLING_OFF = "polling_off"

View File

@@ -1,15 +1,19 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN from esphome.const import (
CONF_ENABLE_ON_BOOT,
CONF_ENABLE_TIME,
CONF_ID,
CONF_KEEP_ON_TIME,
CONF_PIN,
)
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
power_supply_ns = cg.esphome_ns.namespace("power_supply") power_supply_ns = cg.esphome_ns.namespace("power_supply")
PowerSupply = power_supply_ns.class_("PowerSupply", cg.Component) PowerSupply = power_supply_ns.class_("PowerSupply", cg.Component)
MULTI_CONF = True MULTI_CONF = True
CONF_ENABLE_ON_BOOT = "enable_on_boot"
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.Required(CONF_ID): cv.declare_id(PowerSupply), cv.Required(CONF_ID): cv.declare_id(PowerSupply),

View File

@@ -0,0 +1,61 @@
# PULSE Filter
The PULSE filter filters noisy pulses by ensuring that the pulse is in a steady state for at least `filter_length` before allowing the state change to occur.
It counts the pulse time from the rising edge that stayed high for at least `filter_length`, so noise before this won't be considered the start of a pulse.
It then must see a low pulse that is at least `filter_length` long before a subsequent rising edge is considered a new pulse start.
It's operation should be the same as delayed_on_off from the Binary Sensor component.
There are three moving parts in the algorithm that are used to determine the state of the filter.
1. The time between interrupts, measured from the last interrupt, this is compared to filter_length to determine if the pulse has been in a steady state for long enough.
2. A latch variable that is set true when a high pulse is long enough to be considered a valid pulse and is reset when a low pulse is long enough to allow for another pulse to begin.
3. The previous and current state of the pin used to determine if the pulse is rising or falling.
## Ghost interrupts
Observations from the devices show that even though the interrupt should trigger on every rising or falling edge, sometimes interrupts show the same state twice in a row.
The current theory is an interprets occurs, but then the pin changing back faster than the ISR can be called and read the value, meaning it sees the same state twice in a row.
The algorithm interprets these when it sees them as two edges in a row, so will potentially reset a pulse if
## Pulse Filter Truth table
The following is all of the possible states of the filter along with the new inputs.
It also shows a diagram of a possible series of interrupts that would cause the filter to enter that state.
It then has the action that the filter should take and a description of what is happening.
Diagram legend
- `/` rising edge
- `\` falling edge
- `‾` high steady state of at least `filter_length`
- `_` low steady state of at least `filter_length`
- `¦` ghost interrupt
| Length | Latch | From | To | Diagram | Action | Description |
| ------ | ----- | ---- | --- | ------- | ------------------ | ---------------------------------------------------------------------------------------------------- |
| T | 1 | 0 | 0 | `‾\_¦ ` | Reset | `filter_length` low, reset the latch |
| T | 1 | 0 | 1 | `‾\_/ ` | Reset, Rising Edge | `filter_length` low, reset the latch, rising edge could be a new pulse |
| T | 1 | 1 | 0 | `‾\/‾\` | - | Already latched from a previous `filter_length` high |
| T | 1 | 1 | 1 | `‾\/‾¦` | - | Already latched from a previous `filter_length` high |
| T | 0 | 1 | 1 | `_/‾¦` | Set and Publish | `filter_length` high, set the latch and publish the pulse |
| T | 0 | 1 | 0 | `_/‾\ ` | Set and Publish | `filter_length` high, set the latch and publish the pulse |
| T | 0 | 0 | 1 | `_/\_/` | Rising Edge | Already unlatched from a previous `filter_length` low |
| T | 0 | 0 | 0 | `_/\_¦` | - | Already unlatched from a previous `filter_length` low |
| F | 1 | 0 | 0 | `‾\¦ ` | - | Low was not long enough to reset the latch |
| F | 1 | 0 | 1 | `‾\/ ` | - | Low was not long enough to reset the latch |
| F | 1 | 1 | 0 | `‾\/\ ` | - | Low was not long enough to reset the latch |
| F | 1 | 1 | 1 | `‾¦ ` | - | Ghost of 0 length definitely was not long was not long enough to reset the latch |
| F | 0 | 1 | 1 | `_/¦ ` | Rising Edge | High was not long enough to set the latch, but the second half of the ghost can be a new rising edge |
| F | 0 | 1 | 0 | `_/\ ` | - | High was not long enough to set the latch |
| F | 0 | 0 | 1 | `_/\/ ` | Rising Edge | High was not long enough to set the latch, but this may be a rising edge |
| F | 0 | 0 | 0 | `_¦ ` | - | Ghost of 0 length definitely was not long was not long enough to set the latch |
## Startup
On startup the filter should not consider whatever state it is in as valid so it does not count a strange pulse.
There are two possible starting configurations, either the pin is high or the pin is low.
If the pin is high, the subsequent falling edge should not count as a pulse as we never saw the rising edge.
Therefore we start in the latched state.
If the pin is low, the subsequent rising edge can be counted as the first pulse.
Therefore we start in the unlatched state.

View File

@@ -24,11 +24,16 @@ void PulseMeterSensor::setup() {
if (this->filter_mode_ == FILTER_EDGE) { if (this->filter_mode_ == FILTER_EDGE) {
this->pin_->attach_interrupt(PulseMeterSensor::edge_intr, this, gpio::INTERRUPT_RISING_EDGE); this->pin_->attach_interrupt(PulseMeterSensor::edge_intr, this, gpio::INTERRUPT_RISING_EDGE);
} else if (this->filter_mode_ == FILTER_PULSE) { } else if (this->filter_mode_ == FILTER_PULSE) {
// Set the pin value to the current value to avoid a false edge
this->pulse_state_.last_pin_val_ = this->isr_pin_.digital_read();
this->pulse_state_.latched_ = this->pulse_state_.last_pin_val_;
this->pin_->attach_interrupt(PulseMeterSensor::pulse_intr, this, gpio::INTERRUPT_ANY_EDGE); this->pin_->attach_interrupt(PulseMeterSensor::pulse_intr, this, gpio::INTERRUPT_ANY_EDGE);
} }
} }
void PulseMeterSensor::loop() { void PulseMeterSensor::loop() {
const uint32_t now = micros();
// Reset the count in get before we pass it back to the ISR as set // Reset the count in get before we pass it back to the ISR as set
this->get_->count_ = 0; this->get_->count_ = 0;
@@ -38,6 +43,20 @@ void PulseMeterSensor::loop() {
this->set_ = this->get_; this->set_ = this->get_;
this->get_ = temp; this->get_ = temp;
// If an edge was peeked, repay the debt
if (this->peeked_edge_ && this->get_->count_ > 0) {
this->peeked_edge_ = false;
this->get_->count_--;
}
// If there is an unprocessed edge, and filter_us_ has passed since, count this edge early
if (this->get_->last_rising_edge_us_ != this->get_->last_detected_edge_us_ &&
now - this->get_->last_rising_edge_us_ >= this->filter_us_) {
this->peeked_edge_ = true;
this->get_->last_detected_edge_us_ = this->get_->last_rising_edge_us_;
this->get_->count_++;
}
// Check if we detected a pulse this loop // Check if we detected a pulse this loop
if (this->get_->count_ > 0) { if (this->get_->count_ > 0) {
// Keep a running total of pulses if a total sensor is configured // Keep a running total of pulses if a total sensor is configured
@@ -64,7 +83,6 @@ void PulseMeterSensor::loop() {
} }
// No detected edges this loop // No detected edges this loop
else { else {
const uint32_t now = micros();
const uint32_t time_since_valid_edge_us = now - this->last_processed_edge_us_; const uint32_t time_since_valid_edge_us = now - this->last_processed_edge_us_;
switch (this->meter_state_) { switch (this->meter_state_) {
@@ -102,11 +120,14 @@ void IRAM_ATTR PulseMeterSensor::edge_intr(PulseMeterSensor *sensor) {
// This is an interrupt handler - we can't call any virtual method from this method // This is an interrupt handler - we can't call any virtual method from this method
// Get the current time before we do anything else so the measurements are consistent // Get the current time before we do anything else so the measurements are consistent
const uint32_t now = micros(); const uint32_t now = micros();
auto &state = sensor->edge_state_;
auto &set = *sensor->set_;
if ((now - sensor->last_edge_candidate_us_) >= sensor->filter_us_) { if ((now - state.last_sent_edge_us_) >= sensor->filter_us_) {
sensor->last_edge_candidate_us_ = now; state.last_sent_edge_us_ = now;
sensor->set_->last_detected_edge_us_ = now; set.last_detected_edge_us_ = now;
sensor->set_->count_++; set.last_rising_edge_us_ = now;
set.count_++;
} }
} }
@@ -115,33 +136,27 @@ void IRAM_ATTR PulseMeterSensor::pulse_intr(PulseMeterSensor *sensor) {
// Get the current time before we do anything else so the measurements are consistent // Get the current time before we do anything else so the measurements are consistent
const uint32_t now = micros(); const uint32_t now = micros();
const bool pin_val = sensor->isr_pin_.digital_read(); const bool pin_val = sensor->isr_pin_.digital_read();
auto &state = sensor->pulse_state_;
auto &set = *sensor->set_;
// A pulse occurred faster than we can detect // Filter length has passed since the last interrupt
if (sensor->last_pin_val_ == pin_val) { const bool length = now - state.last_intr_ >= sensor->filter_us_;
// If we haven't reached the filter length yet we need to reset our last_intr_ to now
// otherwise we can consider this noise as the "pulse" was certainly less than filter_us_
if (now - sensor->last_intr_ < sensor->filter_us_) {
sensor->last_intr_ = now;
}
} else {
// Check if the last interrupt was long enough in the past
if (now - sensor->last_intr_ > sensor->filter_us_) {
// High pulse of filter length now falling (therefore last_intr_ was the rising edge)
if (!sensor->in_pulse_ && sensor->last_pin_val_) {
sensor->last_edge_candidate_us_ = sensor->last_intr_;
sensor->in_pulse_ = true;
}
// Low pulse of filter length now rising (therefore last_intr_ was the falling edge)
else if (sensor->in_pulse_ && !sensor->last_pin_val_) {
sensor->set_->last_detected_edge_us_ = sensor->last_edge_candidate_us_;
sensor->set_->count_++;
sensor->in_pulse_ = false;
}
}
sensor->last_intr_ = now; if (length && state.latched_ && !state.last_pin_val_) { // Long enough low edge
sensor->last_pin_val_ = pin_val; state.latched_ = false;
} else if (length && !state.latched_ && state.last_pin_val_) { // Long enough high edge
state.latched_ = true;
set.last_detected_edge_us_ = state.last_intr_;
set.count_++;
} }
// Due to order of operations this includes
// length && latched && rising (just reset from a long low edge)
// !latched && (rising || high) (noise on the line resetting the potential rising edge)
set.last_rising_edge_us_ = !state.latched_ && pin_val ? now : set.last_detected_edge_us_;
state.last_intr_ = now;
state.last_pin_val_ = pin_val;
} }
} // namespace pulse_meter } // namespace pulse_meter

View File

@@ -43,6 +43,7 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
// Variables used in the loop // Variables used in the loop
enum class MeterState { INITIAL, RUNNING, TIMED_OUT }; enum class MeterState { INITIAL, RUNNING, TIMED_OUT };
MeterState meter_state_ = MeterState::INITIAL; MeterState meter_state_ = MeterState::INITIAL;
bool peeked_edge_ = false;
uint32_t total_pulses_ = 0; uint32_t total_pulses_ = 0;
uint32_t last_processed_edge_us_ = 0; uint32_t last_processed_edge_us_ = 0;
@@ -53,6 +54,7 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
// (except for resetting the values) // (except for resetting the values)
struct State { struct State {
uint32_t last_detected_edge_us_ = 0; uint32_t last_detected_edge_us_ = 0;
uint32_t last_rising_edge_us_ = 0;
uint32_t count_ = 0; uint32_t count_ = 0;
}; };
State state_[2]; State state_[2];
@@ -61,10 +63,20 @@ class PulseMeterSensor : public sensor::Sensor, public Component {
// Only use these variables in the ISR // Only use these variables in the ISR
ISRInternalGPIOPin isr_pin_; ISRInternalGPIOPin isr_pin_;
uint32_t last_edge_candidate_us_ = 0;
uint32_t last_intr_ = 0; /// Filter state for edge mode
bool in_pulse_ = false; struct EdgeState {
bool last_pin_val_ = false; uint32_t last_sent_edge_us_ = 0;
};
EdgeState edge_state_{};
/// Filter state for pulse mode
struct PulseState {
uint32_t last_intr_ = 0;
bool latched_ = false;
bool last_pin_val_ = false;
};
PulseState pulse_state_{};
}; };
} // namespace pulse_meter } // namespace pulse_meter

View File

@@ -1,4 +1,5 @@
#include "qmc5883l.h" #include "qmc5883l.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include <cmath> #include <cmath>
@@ -59,6 +60,10 @@ void QMC5883LComponent::setup() {
this->mark_failed(); this->mark_failed();
return; return;
} }
if (this->get_update_interval() < App.get_loop_interval()) {
high_freq_.start();
}
} }
void QMC5883LComponent::dump_config() { void QMC5883LComponent::dump_config() {
ESP_LOGCONFIG(TAG, "QMC5883L:"); ESP_LOGCONFIG(TAG, "QMC5883L:");

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