Compare commits

..
Author SHA1 Message Date
J. Nick Koston a2f765a733 Keep a gdb failure from replacing the test's own error
Decode gdb's output with replacement characters and treat an OSError like
a timeout.
2026-10-26 13:58:52 -10:00
J. Nick Koston 3a62bd2a4e Give the debug re-run symbols for custom-flag fixtures and stripped OTA binaries
Fixtures with their own build_flags now get -g too, and the OTA tests pass
their unstripped build so gdb reads its symbols.
2026-10-26 13:33:09 -10:00
J. Nick Koston 7dd3a6f451 Backtrace an early crash, keep the re-run going without core dumps
The died-immediately path now prints the backtrace before raising, the
core setup only warns when it fails, and the re-run's per-test timeout
sits under the step limit.
2026-10-26 13:22:49 -10:00
J. Nick Koston a7b5429032 Revert "Temporary: crash a fixture at boot to check the CI backtrace"
This reverts commit 3f31aeeccf.
2026-10-26 13:20:17 -10:00
J. Nick Koston 3f31aeeccf Temporary: crash a fixture at boot to check the CI backtrace 2026-10-26 13:05:35 -10:00
J. Nick Koston b8c26bea20 [ci] Build integration tests without debug symbols, re-run failures with them
Debug symbols double the object size and nothing reads them on a passing
run. A failed bucket re-runs its failed tests with -g, core dumps and gdb,
so a crash prints a backtrace.
2026-10-26 13:03:43 -10:00
1a50e1394a [vnc] Add input actions and touchscreen forwarding (#20627)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-26 12:48:25 -10:00
luar123andJ. Nick Koston ae1f912a5f [esp32] fix rtcio handling (#21576)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-26 22:42:56 +00:00
916798184d [nextion] Keep a queued command when an unrelated response arrives (#19019)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-26 12:41:48 -10:00
Adam Matthews 1e49616bc5 [api] Fix compile of responding actions with array args (#21672) 2026-10-26 12:32:11 -10:00
esphome[bot] c66f71bb96 Bump aioesphomeapi from 46.22.0 to 46.23.0 (#21700) 2026-10-26 22:31:36 +00:00
8db0774d67 [integration] Test state_class on the integration sensor (#21489)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-26 12:28:18 -10:00
Jonathan Swoboda f735c4634e [esp32] Split and quote build flags in native ESP-IDF builds (#21680) 2026-10-26 17:54:37 -04:00
fe125726ee [template] Stop a running position action on the next cover command (#21599)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-26 11:39:25 -10:00
e05f2fc74e [copy] Forward cover toggle and lock open to the source (#21598)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-26 11:22:58 -10:00
PanoplianandJ. Nick Koston 4e68f1d324 [display] Warn when show_test_card is used with update_interval: never (#20887)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-26 11:12:41 -10:00
Bascht74andJ. Nick Koston 98706c594b [demo] Set the cover to idle when it is stopped (#21600)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-26 11:09:08 -10:00
Jonathan Swoboda 633c2b7f71 [core] Load !include_dir_* files in a fixed order (#21678) 2026-10-26 16:44:24 -04:00
dependabot[bot] 516511b346 Bump actions/setup-python from 7.0.0 to 7.1.0 (#21694)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 20:27:44 +00:00
dependabot[bot] f72dc7c329 Bump astral-sh/setup-uv from 10.3.0 to 10.3.2 (#21695)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:27:07 -10:00
dependabot[bot] f1695ac0b0 Bump CodSpeedHQ/action from 5.5.0 to 5.6.0 (#21692)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:27:01 -10:00
dependabot[bot] 24c64a8efd Bump prek from 0.5.6 to 0.5.7 (#21689)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:54 -10:00
dependabot[bot]andesphome[bot] d3638336ae Bump ruff from 0.17.1 to 0.17.2 (#21683)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:47 -10:00
dependabot[bot] 53bffb560b Bump tzlocal from 5.4.4 to 5.4.5 (#21686)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:44 -10:00
dependabot[bot] 2af39e6643 Bump smp from 4.1.0 to 4.2.0 (#21685)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:40 -10:00
dependabot[bot] 533a3afebd Bump actions/setup-python from 7.0.0 to 7.1.0 in /.github/actions/restore-python (#21690)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:34 -10:00
dependabot[bot] ddb7a6d9aa Bump astral-sh/setup-uv from 10.3.0 to 10.3.2 in /.github/actions/restore-python (#21688)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:28 -10:00
dependabot[bot] ae99accace Bump cryptography from 48.0.1 to 50.0.2 (#21684)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:15 -10:00
dependabot[bot] 474667d1a8 Bump github/codeql-action/init from 4.39.0 to 4.39.1 (#21693)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:08 -10:00
dependabot[bot] dbc93bb133 Bump docker/login-action from 4.6.0 to 4.7.0 in the docker-actions group (#21687)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:26:01 -10:00
dependabot[bot] 5486ef400f Bump github/codeql-action/analyze from 4.39.0 to 4.39.1 (#21691)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-26 10:25:54 -10:00
J. Nick Koston bd723f75af [pipsolar] Test the switch commands (#21655) 2026-10-26 16:17:27 -04:00
J. Nick Koston efba19ea3a [ld2420] Switch the module to simple reporting when the select picks Simple (#21651) 2026-10-26 08:29:45 -10:00
Jonathan Swoboda 6c68985c14 [core] Report missing dependencies of a component as an import error (#21670) 2026-10-26 14:05:19 -04:00
Jonathan Swoboda 7be8a7c390 [core][esp32] Reject git refs and github:// shorthands with unmatched text (#21656) 2026-10-26 14:05:11 -04:00
PolarGoose eb8cf139d7 [dlms_meter] Update parser to latest version to fix bogus error message on some smart meters (#21657) 2026-10-26 07:45:58 -10:00
Kevin Ahrendt ab6ebac628 [microphone] Zero muted audio in place before publishing (#21646) 2026-10-26 13:27:22 -04:00
6cb622417a [tas58xx] Add fault_output and modulation options (#21242)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-26 17:18:18 +00:00
Jonathan Swoboda 960316abd6 [core] Percent-encode "/" in git credentials (#21653) 2026-10-26 07:17:44 -10:00
Jonathan Swoboda 87341dc136 [logger] Enable Log V2 only on the native ESP-IDF toolchain (#21652) 2026-10-26 13:03:15 -04:00
J. Nick Koston cfe5809654 [wifi] Hand the DPP bootstrapping key to the supplicant DER encoded (#20882) 2026-10-26 16:37:25 +00:00
J. Nick Koston 9c54f96520 [libretiny] Send only the UF2 images the device writes over OTA (#21642) 2026-10-26 06:28:54 -10:00
J. Nick Kostonandpre-commit-ci-lite[bot] 0cc5b372fb [core] Defer integration imports in ir_rf_base and media_player (#21601)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-26 06:26:00 -10:00
J. Nick Koston f7c8cba8b7 [components] Use pulse_reset for blocking reset pin pulses (#21472) 2026-10-26 06:22:11 -10:00
J. Nick Koston 79a22e9a9a [core] Add pulse_reset for active-low reset pins (#21470) 2026-10-26 06:22:10 -10:00
J. Nick Koston 103023737d [openthread] Resolve IPv6 literal addresses when the DNS64 client is enabled (#19269) 2026-10-26 16:18:12 +00:00
J. Nick Koston 6b6b70a8b6 [dfrobot_sen0395] Collect lines with LineReader (#20979) 2026-10-26 12:16:51 -04:00
J. Nick Koston d312bf1b73 [core] Let the yaml editor insert lines, write with a rollback and restore (#20602) 2026-10-26 06:16:26 -10:00
J. Nick Koston 0d2f8c0e88 [components] Set or clear entity, socket and sensor bits with assign_bits (#21188) 2026-10-26 12:10:29 -04:00
J. Nick Koston 2f8b50c5af [components] Build the remaining enum option dicts with options() (#20972) 2026-10-26 12:10:08 -04:00
Kevin Ahrendt c4b598be3e [microphone] Pass each audio buffer as an AudioCapture (#21645) 2026-10-26 06:09:53 -10:00
J. Nick Koston 0cd16df16f [components] Write constant codec register tables with write_registers (#20946) 2026-10-26 12:09:47 -04:00
J. Nick Koston a680974da2 [ld6002b] Use Apply entities and find the setting by entity pointer (#20937) 2026-10-26 12:09:03 -04:00
J. Nick Koston 78f0b1ceba [esp32_rmt_led_strip] Stop blocking the main loop while waiting for the previous frame (#19337) 2026-10-26 06:08:13 -10:00
J. Nick Koston c5a841e11e [seeed_mr60fda2] Keep the parameter value tables in flash (#20720) 2026-10-26 12:07:24 -04:00
J. Nick Koston 365965f9bb [veml7700] Keep the gain and integration time tables in flash (#20719) 2026-10-26 12:07:06 -04:00
J. Nick Koston 16011d7fce [nextion] Keep the queue type name table in flash (#20658) 2026-10-26 12:06:31 -04:00
J. Nick Koston 4486dc53d8 [tx20] Keep the wind direction table in flash (#20657) 2026-10-26 12:05:43 -04:00
8d5e3ce534 [components] Use Apply selects for hub forwarding selects (#20966)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-26 16:05:20 +00:00
J. Nick Koston 9ea4d35a50 [uart] Keep debug delimiters and event patterns in flash (#20109) 2026-10-26 12:05:06 -04:00
J. Nick Koston 5cceddfbf4 [kamstrup_kmp] Keep the unit name table in flash (#20642) 2026-10-26 11:58:43 -04:00
J. Nick Koston ff6a37a052 [micronova] Keep the stove state table in flash (#20645) 2026-10-26 11:58:15 -04:00
J. Nick Koston 6990092be0 [ezo] Keep the command and calibration type tables in flash (#20655) 2026-10-26 11:57:41 -04:00
J. Nick Koston 9220c79a51 [bme680_bsec] Keep the IAQ accuracy names in flash (#20656) 2026-10-26 11:57:23 -04:00
J. Nick Koston 215fcd4f5d [pulse_counter] Keep the edge mode name table in flash (#20660) 2026-10-26 11:57:03 -04:00
J. Nick Koston 918e3b3a58 [haier] Keep the protocol phase names in flash (#20710) 2026-10-26 11:56:43 -04:00
J. Nick Koston 347eb3902e [seeed_mr24hpc1] Keep the text sensor state tables in flash (#20711) 2026-10-26 11:55:18 -04:00
J. Nick Koston 177820083f [epaper_spi] Keep the state name table in flash (#20713) 2026-10-26 11:54:07 -04:00
J. Nick Koston b0a0524091 [cc1101] Keep the modulation name table in flash (#20716) 2026-10-26 11:53:10 -04:00
306 changed files with 6704 additions and 2403 deletions

No files matched your search

+2 -2
View File
@@ -17,7 +17,7 @@ runs:
steps:
- name: Set up Python ${{ inputs.python-version }}
id: python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: ${{ inputs.python-version }}
- name: Restore Python virtual environment
@@ -32,7 +32,7 @@ runs:
# detects the activated venv via ``VIRTUAL_ENV`` so the venv layout
# downstream jobs rely on is preserved.
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
+2 -2
View File
@@ -23,13 +23,13 @@ jobs:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.12"
- name: Set up uv
# ``--system`` (below) installs into the setup-python interpreter;
# no venv is created or restored by this workflow.
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
enable-cache: true
# Pull-request-only workflow: a save could never be shared and
+4 -4
View File
@@ -62,7 +62,7 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.12"
- name: Set up Docker Buildx
@@ -97,7 +97,7 @@ jobs:
- name: Log in to the GitHub container registry
if: steps.tag.outputs.push == 'true'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -154,14 +154,14 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Log in to the GitHub container registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
+34 -7
View File
@@ -34,7 +34,7 @@ jobs:
run: echo key="${{ hashFiles('requirements.txt', 'requirements_dev.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
@@ -49,7 +49,7 @@ jobs:
# detects the activated venv via ``VIRTUAL_ENV`` so downstream jobs
# that ``. venv/bin/activate`` see an identical layout.
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -422,7 +422,7 @@ jobs:
version: 1.2
- name: Set up Python 3.13
id: python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.13"
- name: Restore integration library cache
@@ -441,7 +441,7 @@ jobs:
- name: Set up uv
# Only needed on cache miss to populate the venv.
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -462,6 +462,7 @@ jobs:
- name: Register matcher
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
- name: Run integration tests
id: integration
env:
# JSON array of test paths; parsed into a bash array below to avoid
# shell word-splitting / glob hazards.
@@ -477,6 +478,32 @@ jobs:
# A hung test fails after 30 min: the 15 min shared build lock wait plus a cold compile
pytest -vv --no-cov --tb=native --durations=30 -n auto --dist worksteal --timeout=1800 \
--junitxml=junit-integration.xml "${test_files[@]}"
- name: Re-run the failed tests with debug symbols
# The first run builds without -g. A crash in the re-run prints a gdb
# backtrace of its core; exit 1 keeps the job red when the re-run passes.
if: failure() && steps.integration.outcome == 'failure'
timeout-minutes: 30
env:
ESPHOME_INTEGRATION_DEBUG_SYMBOLS: "1"
ESPHOME_INTEGRATION_CORE_DIR: /tmp/esphome-cores
run: |
. venv/bin/activate
# The image's package lists usually suffice; refresh them only if not
if ! command -v gdb >/dev/null &&
! sudo DEBIAN_FRONTEND=noninteractive timeout -k 15 90 apt-get install -y -qq gdb; then
sudo timeout -k 15 300 apt-get update -qq &&
sudo DEBIAN_FRONTEND=noninteractive timeout -k 15 300 apt-get install -y -qq gdb ||
echo "::warning::gdb install failed; no backtraces"
fi
{
mkdir -p "$ESPHOME_INTEGRATION_CORE_DIR" &&
echo "$ESPHOME_INTEGRATION_CORE_DIR/core.%p" | sudo tee /proc/sys/kernel/core_pattern &&
ulimit -c unlimited
} || echo "::warning::core dumps not enabled; no backtraces"
# Under the step limit, so a hung test still reports before Actions kills the step
pytest -vv --no-cov --tb=native -n auto --dist worksteal --timeout=600 \
--lf --lfnf=none tests/integration || true
exit 1
- name: Upload test artifacts
# Tests that compare rendered output write the image they actually got here, so a
# failure can be looked at without reproducing the whole build locally.
@@ -630,7 +657,7 @@ jobs:
apt-get install -y libc6-dbg
- name: Run CodSpeed benchmarks
uses: CodSpeedHQ/action@821a40cd6c80e3cfe387caf55668287addd808da # v5.5.0
uses: CodSpeedHQ/action@78102a468f96d8e0ccb92d0ec49425753fc42062 # v5.6.0
with:
run: |
. venv/bin/activate
@@ -1457,7 +1484,7 @@ jobs:
ref: main
path: device-builder
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.13"
- name: Set up uv
@@ -1466,7 +1493,7 @@ jobs:
# install step (order-of-magnitude faster on cold boots,
# with its own wheel cache). actions/setup-python still
# provides the interpreter.
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
+2 -2
View File
@@ -56,7 +56,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -84,6 +84,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
category: "/language:${{matrix.language}}"
+6 -6
View File
@@ -86,7 +86,7 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.x"
- name: Build
@@ -118,7 +118,7 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.12"
@@ -126,12 +126,12 @@ jobs:
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Log in to docker hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -206,13 +206,13 @@ jobs:
- name: Log in to docker hub
if: matrix.registry == 'dockerhub'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
if: matrix.registry == 'ghcr'
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -71,7 +71,7 @@ jobs:
token: ${{ steps.generate-token.outputs.token }}
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.12"
+2 -2
View File
@@ -37,7 +37,7 @@ jobs:
path: lib/home-assistant
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.14"
@@ -47,7 +47,7 @@ jobs:
# setup-python interpreter so subsequent ``prek`` /
# ``script/run-in-env.py`` steps find the deps without a
# ``uv run`` prefix.
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
with:
enable-cache: true
# Pin uv version so the action does not have to fetch the
@@ -31,7 +31,7 @@ jobs:
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: "3.13"
+1 -1
View File
@@ -10,7 +10,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.17.1
rev: v0.17.2
hooks:
# Run the linter.
- id: ruff
+17 -10
View File
@@ -1305,10 +1305,7 @@ def upload_program(
f"{option_string} is only supported with the esphome OTA platform; "
"the web_server OTA path can only update the firmware image."
)
binary = CORE.firmware_bin
if getattr(args, "file", None) is not None:
binary = Path(args.file)
return _upload_via_web_server(config, network_devices, binary)
return _upload_via_web_server(config, network_devices, _app_ota_image(args))
return _upload_via_native_api(config, network_devices, args)
@@ -1364,6 +1361,16 @@ def _esphome_ota_conf(config: ConfigType) -> ConfigType | None:
return next(iter(esphome_ota_items(config)), None)
def _app_ota_image(args: ArgsProtocol) -> Path:
"""The file an app update sends: ``--file`` or the build output, as the
target platform's ota_image hook picks it."""
binary = CORE.firmware_bin
if (file := getattr(args, "file", None)) is not None:
binary = Path(file)
ota_image = platform_hooks.get_platform_hook(CORE.target_platform, "ota_image")
return ota_image(binary) if ota_image is not None else binary
def _upload_via_native_api(
config: ConfigType, network_devices: list[str], args: ArgsProtocol
) -> tuple[int, str | None]:
@@ -1413,27 +1420,27 @@ def _upload_via_native_api(
f"flash a build with the option enabled, and then retry {option_string}."
)
binary = CORE.firmware_bin
ota_type = espota2.OTA_TYPE_UPDATE_APP
file = getattr(args, "file", None)
if getattr(args, "partition_table", False):
# Fail fast if the resolved ESPHome OTA config does not enable allow_partition_access.
# The device-side handshake also rejects this with "Device only supports app updates",
# but checking here surfaces the misconfiguration before opening a network connection.
check_partition_access("--partition-table")
binary = CORE.partition_table_bin
binary = Path(file) if file is not None else CORE.partition_table_bin
ota_type = espota2.OTA_TYPE_UPDATE_PARTITION_TABLE
elif getattr(args, "bootloader", False):
check_partition_access("--bootloader")
if (
getattr(args, "file", None) is None
file is None
and (native := native_backend())
and (hint := getattr(native, "missing_image_hint", lambda: None)())
):
raise EsphomeError(hint)
binary = CORE.bootloader_bin
binary = Path(file) if file is not None else CORE.bootloader_bin
ota_type = espota2.OTA_TYPE_UPDATE_BOOTLOADER
if getattr(args, "file", None) is not None:
binary = Path(args.file)
else:
binary = _app_ota_image(args)
if ota_type == espota2.OTA_TYPE_UPDATE_PARTITION_TABLE:
_validate_partition_table_binary(binary)
+54 -6
View File
@@ -25,6 +25,7 @@ from esphome.framework_helpers import (
get_project_link_flags,
)
from esphome.helpers import get_bool_env, mkdir_p, write_file_if_changed
from esphome.platformio.library import lex_build_flags
_LOGGER = logging.getLogger(__name__)
@@ -154,6 +155,47 @@ def has_discovered_components() -> bool:
return bool(get_available_components())
_KEY_WARNED_BUILD_FLAGS = "warned_build_flags"
def _lexed_build_flags() -> list[str]:
"""Shell-lex the build flag entries as the other generators do, so an
entry holding several flags is classified flag by flag. Warns once per
entry about a flag that native ESP-IDF builds do not pass on."""
warned: set[tuple[str, str]] = CORE.data.setdefault(KEY_ESP32, {}).setdefault(
_KEY_WARNED_BUILD_FLAGS, set()
)
flags: list[str] = []
for entry in sorted(CORE.build_flags):
tokens = lex_build_flags(entry, "esphome build_flags")
for token in tokens:
if (
(token, entry) not in warned
and not get_project_compile_flags([token])
and not get_project_link_flags([token])
):
warned.add((token, entry))
_LOGGER.warning(
"Build flag '%s'%s is ignored: native ESP-IDF builds only "
"pass -D, -W and -Wl, flags",
token,
"" if token == entry else f" in '{entry}'",
)
flags += tokens
return flags
def _cmake_args(flags: list[str]) -> list[str]:
"""Quote each flag as a CMake argument so quotes, backslashes and $
survive."""
return [
'"{}"'.format(
flag.replace("\\", "\\\\").replace('"', '\\"').replace("$", "\\$")
)
for flag in flags
]
def _cmake_quote(value: str) -> str:
"""Quote a cmake arg value for a set() line. add_cmake_arg rejects
whitespace, quotes, and '$', so only backslashes need escaping."""
@@ -294,18 +336,24 @@ def get_project_cmakelists(
# esphome__micro-mp3) rather than just src/. Required so suppressions
# like ``-Wno-error=maybe-uninitialized`` actually silence warnings in
# third-party components we don't author.
project_compile_opts = get_project_compile_flags()
# CMake keeps only the first copy of a repeated option; drop all but the
# last copy instead so the last definition wins, as on the command line.
project_compile_opts = list(
reversed(
dict.fromkeys(reversed(get_project_compile_flags(_lexed_build_flags())))
)
)
extra_compile_options = "\n".join(
f'idf_build_set_property(COMPILE_OPTIONS "{flag}" APPEND)'
for flag in project_compile_opts
f"idf_build_set_property(COMPILE_OPTIONS {flag} APPEND)"
for flag in _cmake_args(project_compile_opts)
)
# Flags registered via cg.add_cxx_build_flag() go on CXX_COMPILE_OPTIONS
# (not COMPILE_OPTIONS) because GCC warns when a C++-only flag such as
# -Wno-volatile is passed on a C compile.
cxx_compile_options = "\n".join(
f'idf_build_set_property(CXX_COMPILE_OPTIONS "{flag}" APPEND)'
for flag in get_project_cxx_compile_flags()
f"idf_build_set_property(CXX_COMPILE_OPTIONS {flag} APPEND)"
for flag in _cmake_args(get_project_cxx_compile_flags())
)
cpp_standard_options = (
@@ -461,7 +509,7 @@ def get_component_cmakelists() -> str:
# Extract linker options (-Wl, flags). Compile flags (-D, -W) are
# emitted project-wide via idf_build_set_property in
# get_project_cmakelists so they reach every component, not just src/.
link_opts = get_project_link_flags()
link_opts = _cmake_args(get_project_link_flags(_lexed_build_flags()))
link_opts_str = "\n ".join(link_opts) if link_opts else ""
src_cmake = "\n".join(CORE.data[KEY_ESP32].get(KEY_SRC_CMAKE, []))
+27 -1
View File
@@ -33,9 +33,35 @@ _CACHE_VERSION = 1
_LAMBDA_KEY = "__esphome_lambda__"
_CACHE_SUFFIX = ".validated.json"
def compiled_config_path(config_filename: str) -> Path:
"""Path to the cached validated config alongside the storage sidecar."""
return CORE.data_dir / "storage" / f"{config_filename}.validated.json"
return CORE.data_dir / "storage" / f"{config_filename}{_CACHE_SUFFIX}"
def invalidate_compiled_config() -> None:
"""Drop every cache in the storage dir after a change to a file the
mtime check cannot see, such as secrets.yaml or an include; other
configurations may share that file."""
storage = compiled_config_path(CORE.config_filename).parent
try:
caches = [p for p in storage.iterdir() if p.name.endswith(_CACHE_SUFFIX)]
except FileNotFoundError:
return
except OSError as err:
raise EsphomeError(
f"Could not list the validated config caches in {storage}: {err}"
) from err
for path in caches:
try:
path.unlink(missing_ok=True)
except OSError as err:
# A stale cache would present the old key on the next upload
raise EsphomeError(
f"Could not remove the validated config cache {path}: {err}"
) from err
def save_compiled_config(config: ConfigType) -> None:
+1 -3
View File
@@ -297,9 +297,7 @@ void ADE7880::init_device_() {
void ADE7880::reset_device_() {
if (this->reset_pin_ != nullptr) {
ESP_LOGD(TAG, "Reset device using RESET pin");
this->reset_pin_->digital_write(false);
delay(1);
this->reset_pin_->digital_write(true);
pulse_reset(this->reset_pin_, {.low_ms = 1});
} else {
ESP_LOGD(TAG, "Reset device using SWRST command");
this->write_u16_register16_(CONFIG, CONFIG_SWRST);
+73 -73
View File
@@ -14,80 +14,80 @@ ESPHOME_LOG_TAG(TAG, "aic3204");
return; \
}
static constexpr i2c::I2CRegValue AIC3204_INIT[] PROGMEM = {
// Set register page to 0
{AIC3204_PAGE_CTRL, 0x00},
// Initiate SW reset (PLL is powered off as part of reset)
{AIC3204_SW_RST, 0x01},
// *** Program clock settings ***
// Default is CODEC_CLKIN is from MCLK pin. Don't need to change this.
// MDAC*NDAC*FOSR*48Khz = mClk (24.576 MHz when the XMOS is expecting 48kHz audio)
// (See page 51 of https://www.ti.com/lit/ml/slaa557/slaa557.pdf)
// We do need MDAC*DOSR/32 >= the resource compute level for the processing block
// So here 2*128/32 = 8, which is equal to processing block 1 's resource compute
// See page 5 of https://www.ti.com/lit/an/slaa404c/slaa404c.pdf for the workflow
// for determining these settings.
// Power up NDAC and set to 2
{AIC3204_NDAC, 0x82},
// Power up MDAC and set to 2
{AIC3204_MDAC, 0x82},
// Program DOSR = 128
{AIC3204_DOSR, 0x80},
// Set Audio Interface Config: I2S, 32 bits, DOUT always driving
{AIC3204_CODEC_IF, 0x30},
// For I2S Firmware only, set SCLK/MFP3 pin as Audio Data In
{AIC3204_SCLK_MFP3, 0x02},
{AIC3204_AUDIO_IF_4, 0x01},
{AIC3204_AUDIO_IF_5, 0x01},
// Program the DAC processing block to be used - PRB_P1
{AIC3204_DAC_SIG_PROC, 0x01},
// *** Select Page 1 ***
{AIC3204_PAGE_CTRL, 0x01},
// Enable the internal AVDD_LDO:
{AIC3204_LDO_CTRL, 0x09},
// *** Program Analog Blocks ***
// Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO
{AIC3204_PWR_CFG, 0x08},
// Enable Master Analog Power Control
{AIC3204_LDO_CTRL, 0x01},
// Page 125: Common mode control register, set d6 to 1 to make the full chip common mode = 0.75 v
// We are using the internal AVdd regulator with a nominal output of 1.72 V (see LDO_CTRL_REGISTER on page 123)
// Page 86 says to only set the common mode voltage to 0.9 v if AVdd >= 1.8... but it isn't on our hardware
// We also adjust the HPL and HPR gains to -2dB gian later in this config flow compensate (see page 47)
// (All pages refer to the TLV320AIC3204 Application Reference Guide)
{AIC3204_CM_CTRL, 0x40},
// *** Set PowerTune Modes ***
// Set the Left & Right DAC PowerTune mode to PTM_P3/4. Use Class-AB driver.
{AIC3204_PLAY_CFG1, 0x00},
{AIC3204_PLAY_CFG2, 0x00},
// Set the REF charging time to 40ms
{AIC3204_REF_STARTUP, 0x01},
// HP soft stepping settings for optimal pop performance at power up
// Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling
// capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound.
{AIC3204_HP_START, 0x25},
// Route Left DAC to HPL
{AIC3204_HPL_ROUTE, 0x08},
// Route Right DAC to HPR
{AIC3204_HPR_ROUTE, 0x08},
// Route Left DAC to LOL
{AIC3204_LOL_ROUTE, 0x08},
// Route Right DAC to LOR
{AIC3204_LOR_ROUTE, 0x08},
// Unmute HPL and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
{AIC3204_HPL_GAIN, 0x3e},
// Unmute HPR and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
{AIC3204_HPR_GAIN, 0x3e},
// Unmute LOL and set gain to 0dB
{AIC3204_LOL_DRV_GAIN, 0x00},
// Unmute LOR and set gain to 0dB
{AIC3204_LOR_DRV_GAIN, 0x00},
// Power up HPL and HPR, LOL and LOR drivers
{AIC3204_OP_PWR_CTRL, 0x3C},
};
void AIC3204::setup() {
// Set register page to 0
ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set page 0 failed");
// Initiate SW reset (PLL is powered off as part of reset)
ERROR_CHECK(this->write_byte(AIC3204_SW_RST, 0x01), "Software reset failed");
// *** Program clock settings ***
// Default is CODEC_CLKIN is from MCLK pin. Don't need to change this.
// MDAC*NDAC*FOSR*48Khz = mClk (24.576 MHz when the XMOS is expecting 48kHz audio)
// (See page 51 of https://www.ti.com/lit/ml/slaa557/slaa557.pdf)
// We do need MDAC*DOSR/32 >= the resource compute level for the processing block
// So here 2*128/32 = 8, which is equal to processing block 1 's resource compute
// See page 5 of https://www.ti.com/lit/an/slaa404c/slaa404c.pdf for the workflow
// for determining these settings.
// Power up NDAC and set to 2
ERROR_CHECK(this->write_byte(AIC3204_NDAC, 0x82), "Set NDAC failed");
// Power up MDAC and set to 2
ERROR_CHECK(this->write_byte(AIC3204_MDAC, 0x82), "Set MDAC failed");
// Program DOSR = 128
ERROR_CHECK(this->write_byte(AIC3204_DOSR, 0x80), "Set DOSR failed");
// Set Audio Interface Config: I2S, 32 bits, DOUT always driving
ERROR_CHECK(this->write_byte(AIC3204_CODEC_IF, 0x30), "Set CODEC_IF failed");
// For I2S Firmware only, set SCLK/MFP3 pin as Audio Data In
ERROR_CHECK(this->write_byte(AIC3204_SCLK_MFP3, 0x02), "Set SCLK/MFP3 failed");
ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_4, 0x01), "Set AUDIO_IF_4 failed");
ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_5, 0x01), "Set AUDIO_IF_5 failed");
// Program the DAC processing block to be used - PRB_P1
ERROR_CHECK(this->write_byte(AIC3204_DAC_SIG_PROC, 0x01), "Set DAC_SIG_PROC failed");
// *** Select Page 1 ***
ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x01), "Set page 1 failed");
// Enable the internal AVDD_LDO:
ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x09), "Set LDO_CTRL failed");
// *** Program Analog Blocks ***
// Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO
ERROR_CHECK(this->write_byte(AIC3204_PWR_CFG, 0x08), "Set PWR_CFG failed");
// Enable Master Analog Power Control
ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x01), "Set LDO_CTRL failed");
// Page 125: Common mode control register, set d6 to 1 to make the full chip common mode = 0.75 v
// We are using the internal AVdd regulator with a nominal output of 1.72 V (see LDO_CTRL_REGISTER on page 123)
// Page 86 says to only set the common mode voltage to 0.9 v if AVdd >= 1.8... but it isn't on our hardware
// We also adjust the HPL and HPR gains to -2dB gian later in this config flow compensate (see page 47)
// (All pages refer to the TLV320AIC3204 Application Reference Guide)
ERROR_CHECK(this->write_byte(AIC3204_CM_CTRL, 0x40), "Set CM_CTRL failed");
// *** Set PowerTune Modes ***
// Set the Left & Right DAC PowerTune mode to PTM_P3/4. Use Class-AB driver.
ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG1, 0x00), "Set PLAY_CFG1 failed");
ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG2, 0x00), "Set PLAY_CFG2 failed");
// Set the REF charging time to 40ms
ERROR_CHECK(this->write_byte(AIC3204_REF_STARTUP, 0x01), "Set REF_STARTUP failed");
// HP soft stepping settings for optimal pop performance at power up
// Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling
// capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound.
ERROR_CHECK(this->write_byte(AIC3204_HP_START, 0x25), "Set HP_START failed");
// Route Left DAC to HPL
ERROR_CHECK(this->write_byte(AIC3204_HPL_ROUTE, 0x08), "Set HPL_ROUTE failed");
// Route Right DAC to HPR
ERROR_CHECK(this->write_byte(AIC3204_HPR_ROUTE, 0x08), "Set HPR_ROUTE failed");
// Route Left DAC to LOL
ERROR_CHECK(this->write_byte(AIC3204_LOL_ROUTE, 0x08), "Set LOL_ROUTE failed");
// Route Right DAC to LOR
ERROR_CHECK(this->write_byte(AIC3204_LOR_ROUTE, 0x08), "Set LOR_ROUTE failed");
// Unmute HPL and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
ERROR_CHECK(this->write_byte(AIC3204_HPL_GAIN, 0x3e), "Set HPL_GAIN failed");
// Unmute HPR and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register)
ERROR_CHECK(this->write_byte(AIC3204_HPR_GAIN, 0x3e), "Set HPR_GAIN failed");
// Unmute LOL and set gain to 0dB
ERROR_CHECK(this->write_byte(AIC3204_LOL_DRV_GAIN, 0x00), "Set LOL_DRV_GAIN failed");
// Unmute LOR and set gain to 0dB
ERROR_CHECK(this->write_byte(AIC3204_LOR_DRV_GAIN, 0x00), "Set LOR_DRV_GAIN failed");
// Power up HPL and HPR, LOL and LOR drivers
ERROR_CHECK(this->write_byte(AIC3204_OP_PWR_CTRL, 0x3C), "Set OP_PWR_CTRL failed");
ERROR_CHECK(this->write_registers(AIC3204_INIT), "Failed to initialize");
// Wait for 2.5 sec for soft stepping to take effect before attempting power-up
this->set_timeout(2500, [this]() {
+4 -8
View File
@@ -174,14 +174,10 @@ def _register_provisioning_source(config: ConfigType) -> ConfigType:
CONF_SUPPORTS_RESPONSE = "supports_response"
# Enum values in api::enums namespace
enums_ns = api_ns.namespace("enums")
SUPPORTS_RESPONSE_OPTIONS = {
"none": enums_ns.SUPPORTS_RESPONSE_NONE,
"optional": enums_ns.SUPPORTS_RESPONSE_OPTIONAL,
"only": enums_ns.SUPPORTS_RESPONSE_ONLY,
"status": enums_ns.SUPPORTS_RESPONSE_STATUS,
}
SupportsResponseType = api_ns.namespace("enums").enum("SupportsResponseType")
SUPPORTS_RESPONSE_OPTIONS = SupportsResponseType.options(
"none", "optional", "only", "status", prefix="SUPPORTS_RESPONSE_"
)
def _auto_detect_supports_response(config: ConfigType) -> ConfigType:
+4 -3
View File
@@ -253,8 +253,9 @@ template<typename... Ts> class APIRespondAction final : public Action<Ts...> {
#endif
void play(const Ts &...x) override {
// Extract call_id from first argument - it's always first for optional/only/status modes
auto args = std::make_tuple(x...);
// Extract call_id from first argument - it's always first for optional/only/status modes.
// By reference: array args are non-copyable FixedVector views.
auto args = std::forward_as_tuple(x...);
uint32_t call_id = std::get<0>(args);
bool success = this->success_.value(x...);
@@ -308,7 +309,7 @@ template<typename... Ts> class APIUnregisterServiceCallAction final : public Act
void play(const Ts &...x) override {
// Extract call_id from first argument - same convention as APIRespondAction
auto args = std::make_tuple(x...);
auto args = std::forward_as_tuple(x...);
uint32_t call_id = std::get<0>(args);
if (call_id != 0) {
this->parent_->unregister_active_action_call(call_id);
+7 -6
View File
@@ -54,7 +54,8 @@ static const char AS7341_NAME[] PROGMEM = "AS7341";
static constexpr uint8_t AS7341_ADC_SLOTS = 6;
static constexpr uint8_t AS7341_SMUX_LEN = 20;
static constexpr uint8_t AS7341_SMUX_STEPS = 2;
static const RegValue AS7341_INIT[] PROGMEM = {{0x70, 0x00}}; // CONFIG: SPM mode, INT_SEL and LED_SEL cleared
static constexpr i2c::I2CRegValue AS7341_INIT[] PROGMEM = {
{0x70, 0x00}}; // CONFIG: SPM mode, INT_SEL and LED_SEL cleared
static const uint8_t AS7341_SMUX[] PROGMEM = {
0x30, 0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x50, 0x00, // step 0: F1 to F4, NIR, Clear
0x00, 0x00, 0x20, 0x04, 0x00, 0x30, 0x01, 0x50, 0x00, 0x06, // step 0, continued
@@ -150,8 +151,8 @@ static constexpr uint8_t AS7343_ADC_SLOTS = 18;
static constexpr uint8_t AS7343_SMUX_LEN = 10;
static const char TCS3448_NAME[] PROGMEM = "TCS3448";
// CFG6, FD_CFG0, CFG10, CFG8, CFG20 (auto_smux, 18 channels in 3 cycles), AGC_GAIN_MAX, FD_TIME_1, FD_TIME_2
static const RegValue AS7343_INIT[] PROGMEM = {{0xF5, 0x00}, {0xDF, 0xA1}, {0x65, 0xF2}, {0xC9, 0xC8},
{0xD6, 0x62}, {0xD7, 0x99}, {0xE0, 0x64}, {0xE2, 0x21}};
static constexpr i2c::I2CRegValue AS7343_INIT[] PROGMEM = {{0xF5, 0x00}, {0xDF, 0xA1}, {0x65, 0xF2}, {0xC9, 0xC8},
{0xD6, 0x62}, {0xD7, 0x99}, {0xE0, 0x64}, {0xE2, 0x21}};
static const uint8_t AS7343_SMUX[] PROGMEM = {0x00, 0x04, 0x65, 0x02, 0x00, 0x05, 0x00, 0x01, 0x00, 0x30,
0x00, 0x00, 0x60, 0x20, 0x04, 0x50, 0x03, 0x00, 0x01, 0x05,
0x05, 0x00, 0x60, 0x00, 0x30, 0x00, 0x40, 0x10, 0x20, 0x00};
@@ -230,7 +231,7 @@ static constexpr ChipInfo as7343_chip(const char *name) {
.gain_correction = AS7343_GAIN_CORRECTION,
.contributions = AS7343_CONTRIBUTIONS,
.tristimulus = AS7343_TRISTIMULUS,
.init_count = sizeof(AS7343_INIT) / sizeof(RegValue),
.init_count = sizeof(AS7343_INIT) / sizeof(i2c::I2CRegValue),
.astep_reg = 0xD4,
.cfg0_reg = 0xBF,
.cfg1_reg = 0xC6,
@@ -298,9 +299,9 @@ void AS734XComponent::setup() {
this->mark_failed(LOG_STR("Wrong chip ID"));
return;
}
const RegValue *init = pgm(chip->init);
const i2c::I2CRegValue *init = pgm(chip->init);
for (uint8_t i = 0, n = pgm(chip->init_count); ok && i < n; i++) {
RegValue rv;
i2c::I2CRegValue rv;
progmem_memcpy(&rv, &init[i], sizeof(rv));
ok = this->select_bank_(rv.reg) && this->write_byte(rv.reg, rv.value);
}
+1 -6
View File
@@ -68,15 +68,10 @@ struct ChannelTristimulus {
float z;
};
struct RegValue {
uint8_t reg;
uint8_t value;
};
/// Per-chip constants, stored in flash. The pointed-to tables are in flash too.
struct ChipInfo {
const char *name;
const RegValue *init; // written in order; the bank is selected per register address
const i2c::I2CRegValue *init; // written in order; the bank is selected per register address
const uint8_t *smux; // AS7341: SMUX RAM image per step; AS7343: the three chain images
const uint8_t *readout; // per step, the ADC slot of each band (0xFF: not read in this step)
const uint16_t *wavelengths; // per band, WIDEBAND_NM for clear and ABSENT_NM for a missing band
@@ -16,13 +16,7 @@ constexpr static const uint8_t AXS_READ_TOUCHPAD[11] = {0xb5, 0xab, 0xa5, 0x5a,
}
void AXS15231Touchscreen::setup() {
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(false);
delay(5);
this->reset_pin_->digital_write(true);
delay(10);
}
pulse_reset(this->reset_pin_, {.low_ms = 5, .high_after_ms = 10});
if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT);
this->interrupt_pin_->setup();
+4
View File
@@ -78,3 +78,7 @@ async def pin_to_code(config: ConfigType) -> MockObj:
# Called by writer.py; delegates to the shared libretiny implementation.
def copy_files() -> None:
libretiny.copy_files()
# Called by the OTA upload; the shared libretiny implementation.
ota_image = libretiny.device_ota_image
@@ -1,7 +1,7 @@
#include "bme680_bsec.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <string>
#include "esphome/core/progmem.h"
namespace esphome::bme680_bsec {
#ifdef USE_BSEC
@@ -9,7 +9,7 @@ ESPHOME_LOG_TAG(TAG, "bme680_bsec.sensor");
static constexpr uint32_t READ_TIMEOUT_ID = 0;
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
PROGMEM_STRING_TABLE(IaqAccuracyStrings, "Stabilizing", "Uncertain", "Calibrating", "Calibrated");
std::vector<BME680BSECComponent *>
BME680BSECComponent::instances; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
@@ -386,10 +386,11 @@ void BME680BSECComponent::publish_(const bsec_output_t *outputs, uint8_t num_out
switch (outputs[i].sensor_id) {
case BSEC_OUTPUT_IAQ:
case BSEC_OUTPUT_STATIC_IAQ: {
uint8_t accuracy = std::min<uint8_t>(outputs[i].accuracy, std::size(IAQ_ACCURACY_STATES) - 1);
uint8_t accuracy = std::min<uint8_t>(outputs[i].accuracy, IaqAccuracyStrings::LAST_INDEX);
this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_sensor_, signal); });
this->queue_push_([this, accuracy]() {
this->publish_sensor_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[accuracy]);
this->publish_sensor_(this->iaq_accuracy_text_sensor_,
IaqAccuracyStrings::get_progmem_str(accuracy, IaqAccuracyStrings::LAST_INDEX));
});
this->queue_push_([this, accuracy]() { this->publish_sensor_(this->iaq_accuracy_sensor_, accuracy, true); });
@@ -435,8 +436,9 @@ void BME680BSECComponent::publish_sensor_(sensor::Sensor *sensor, float value, b
sensor->publish_state(value);
}
void BME680BSECComponent::publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value) {
if (!sensor || (sensor->has_state() && sensor->state == value)) {
void BME680BSECComponent::publish_sensor_(text_sensor::TextSensor *sensor, ProgmemStr value) {
if (!sensor ||
(sensor->has_state() && ESPHOME_strcmp_P(sensor->state.c_str(), reinterpret_cast<ESPHOME_PGM_P>(value)) == 0)) {
return;
}
sensor->publish_state(value);
+1 -1
View File
@@ -77,7 +77,7 @@ class BME680BSECComponent final : public Component, public i2c::I2CDevice {
int64_t get_time_ns_();
void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only = false);
void publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value);
void publish_sensor_(text_sensor::TextSensor *sensor, ProgmemStr value);
void snapshot_state_(); // Fetch the current BSEC library state and save it in the bsec_state_data_ member (volatile
// memory)
+7 -4
View File
@@ -2,6 +2,7 @@
#include "cc1101pa.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include <cmath>
#include <cstring>
@@ -14,6 +15,9 @@ namespace esphome::cc1101 {
ESPHOME_LOG_TAG(TAG, "cc1101");
static constexpr size_t TX_FIFO_SIZE = 64;
// MDMCFG2 MOD_FORMAT values
PROGMEM_STRING_TABLE(ModulationStrings, "2-FSK", "GFSK", "UNUSED", "ASK/OOK", "4-FSK", "UNUSED", "UNUSED", "MSK");
static void split_float(float value, int mbits, uint8_t &e, uint32_t &m) {
int e_tmp;
float m_tmp = std::frexp(value, &e_tmp);
@@ -238,8 +242,6 @@ void CC1101Component::loop() {
}
void CC1101Component::dump_config() {
static const char *const MODULATION_NAMES[] = {"2-FSK", "GFSK", "UNUSED", "ASK/OOK",
"4-FSK", "UNUSED", "UNUSED", "MSK"};
int32_t freq = static_cast<int32_t>(this->state_.FREQ2 << 16 | this->state_.FREQ1 << 8 | this->state_.FREQ0) *
XTAL_FREQUENCY / (1 << 16);
float symbol_rate = (((256.0f + this->state_.DRATE_M) * (1 << this->state_.DRATE_E)) / (1 << 28)) * XTAL_FREQUENCY;
@@ -253,8 +255,9 @@ void CC1101Component::dump_config() {
" Symbol Rate: %.0f baud\n"
" Filter Bandwidth: %.1f Hz\n"
" Output Power: %.1f dBm",
this->chip_id_, freq, this->state_.CHANNR, MODULATION_NAMES[this->state_.MOD_FORMAT & 0x07],
symbol_rate, bw, this->output_power_effective_);
this->chip_id_, freq, this->state_.CHANNR,
LOG_STR_ARG(ModulationStrings::get_log_str(this->state_.MOD_FORMAT & 0x07, 0)), symbol_rate, bw,
this->output_power_effective_);
LOG_PIN(" CS Pin: ", this->cs_);
}
+2 -3
View File
@@ -45,9 +45,8 @@ void CopyCover::control(const cover::CoverCall &call) {
auto position = call.get_position();
if (position.has_value())
call2.set_position(*position);
auto tilt2 = call.get_tilt();
if (tilt2.has_value())
call2.set_tilt(*tilt2);
if (call.get_toggle().has_value())
call2.set_command_toggle();
call2.perform();
}
@@ -21,4 +21,6 @@ void CopyLock::control(const lock::LockCall &call) {
call2.perform();
}
void CopyLock::open_latch() { this->source_->open(); }
} // namespace esphome::copy
+1
View File
@@ -13,6 +13,7 @@ class CopyLock final : public lock::Lock, public Component {
protected:
void control(const lock::LockCall &call) override;
void open_latch() override;
lock::Lock *source_;
};
@@ -6,12 +6,7 @@ ESPHOME_LOG_TAG(TAG, "cst226.touchscreen");
void CST226Touchscreen::setup() {
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);
delay(5);
this->reset_pin_->digital_write(false);
delay(5);
this->reset_pin_->digital_write(true);
pulse_reset(this->reset_pin_, {.high_before_ms = 5, .low_ms = 5});
this->set_timeout(30, [this] { this->continue_setup_(); });
} else {
this->continue_setup_();
@@ -43,9 +43,7 @@ void CST328Touchscreen::setup() {
}
void CST328Touchscreen::reset_device_() {
this->reset_pin_->digital_write(false);
delay(5);
this->reset_pin_->digital_write(true);
pulse_reset(this->reset_pin_, {.low_ms = 5});
this->set_timeout(CST328_TRANSITION_TIMEOUT, [this] { this->continue_setup_(); });
}
@@ -52,12 +52,7 @@ void CST816Touchscreen::continue_setup_() {
void CST816Touchscreen::setup() {
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);
delay(5);
this->reset_pin_->digital_write(false);
delay(5);
this->reset_pin_->digital_write(true);
pulse_reset(this->reset_pin_, {.high_before_ms = 5, .low_ms = 5});
this->set_timeout(30, [this] { this->continue_setup_(); });
} else {
this->continue_setup_();
@@ -10,14 +10,7 @@ static constexpr uint32_t REREAD_TIMEOUT_ID = 1;
static constexpr uint32_t REREAD_DELAY_MS = 20;
void CST9220Touchscreen::setup() {
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);
delay(5);
this->reset_pin_->digital_write(false);
delay(10);
this->reset_pin_->digital_write(true);
}
pulse_reset(this->reset_pin_, {.high_before_ms = 5, .low_ms = 10});
// Wait for the controller to leave its bootloader before talking to it.
this->set_timeout(30, [this] { this->continue_setup_(); });
}
+1
View File
@@ -57,6 +57,7 @@ class DemoCover final : public cover::Cover, public Component {
}
if (call.get_stop()) {
this->cancel_timeout(MOVE_TIMEOUT_ID);
this->current_operation = cover::COVER_OPERATION_IDLE;
}
this->publish_state();
@@ -42,6 +42,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config: ConfigType) -> None:
cg.add_define("USE_LINE_READER")
await uart.new_uart_component(config)
@@ -11,10 +11,10 @@
namespace esphome::dfrobot_sen0395 {
ESPHOME_LOG_TAG(TAG, "dfrobot_sen0395");
static constexpr char ASCII_CR = 0x0D;
static constexpr char ASCII_LF = 0x0A;
static constexpr float DET_RANGE_STEP_M = 0.15f;
static constexpr float LATENCY_STEP_S = 0.025f;
// Sent after a reset with no line end
static constexpr char PROMPT[] PROGMEM = "leapMMW:/>";
// What each CommandType cannot do while the sensor runs; empty for commands that do not need it stopped
PROGMEM_STRING_TABLE(BusyActions, "", "configure range config", "configure output latency",
@@ -121,9 +121,9 @@ bool DfrobotSen0395Component::run_command_(const Command &cmd, uint32_t now) {
return false; // Command not done yet
}
if (this->read_message_()) {
if (ESPHOME_strstr_P(this->read_buffer_, ESPHOME_PSTR("is not recognized as a CLI command")) == nullptr) {
if (!this->handle_reply_(cmd))
if (const char *message = this->read_message_()) {
if (ESPHOME_strstr_P(message, ESPHOME_PSTR("is not recognized as a CLI command")) == nullptr) {
if (!this->handle_reply_(cmd, message))
return false; // Command not done yet
this->read_message_(); // Skip the prompt
return true;
@@ -146,12 +146,13 @@ bool DfrobotSen0395Component::run_command_(const Command &cmd, uint32_t now) {
}
void DfrobotSen0395Component::read_state_() {
if (!this->read_message_())
const char *message = this->read_message_();
if (message == nullptr)
return;
if (ESPHOME_strstr_P(this->read_buffer_, ESPHOME_PSTR("$JYBSS,0, , , *")) != nullptr) {
if (ESPHOME_strstr_P(message, ESPHOME_PSTR("$JYBSS,0, , , *")) != nullptr) {
this->set_detected_(false);
this->set_active(true);
} else if (ESPHOME_strstr_P(this->read_buffer_, ESPHOME_PSTR("$JYBSS,1, , , *")) != nullptr) {
} else if (ESPHOME_strstr_P(message, ESPHOME_PSTR("$JYBSS,1, , , *")) != nullptr) {
this->set_detected_(true);
this->set_active(true);
}
@@ -206,8 +207,7 @@ void DfrobotSen0395Component::format_command(const Command &cmd, char *buf, size
}
}
bool DfrobotSen0395Component::handle_reply_(const Command &cmd) {
const char *message = this->read_buffer_;
bool DfrobotSen0395Component::handle_reply_(const Command &cmd, const char *message) {
auto is = [message](ESPHOME_PGM_P text) { return ESPHOME_strcmp_P(message, text) == 0; };
// Commands with their own replies
@@ -230,7 +230,7 @@ bool DfrobotSen0395Component::handle_reply_(const Command &cmd) {
}
return true;
case CommandType::COMMAND_TYPE_RESET_SYSTEM:
if (!is(ESPHOME_PSTR("leapMMW:/>")))
if (!is(PROMPT))
return false;
ESP_LOGI(TAG, "Restarted sensor.");
return true;
@@ -300,34 +300,24 @@ bool DfrobotSen0395Component::handle_reply_(const Command &cmd) {
return true;
}
bool DfrobotSen0395Component::read_message_() {
const char *DfrobotSen0395Component::read_message_() {
while (this->available()) {
uint8_t byte;
this->read_byte(&byte);
if (this->read_pos_ == MMWAVE_READ_BUFFER_LENGTH)
this->read_pos_ = 0;
ESP_LOGVV(TAG, "Buffer pos: %u %d", this->read_pos_, byte);
if (byte == ASCII_CR)
continue;
if (byte >= 0x7F)
byte = '?'; // needs to be valid utf8 string for log functions.
this->read_buffer_[this->read_pos_] = byte;
if (this->read_pos_ == 9 && byte == '>')
this->read_buffer_[++this->read_pos_] = ASCII_LF;
if (this->read_buffer_[this->read_pos_] == ASCII_LF) {
this->read_buffer_[this->read_pos_] = 0;
this->read_pos_ = 0;
ESP_LOGV(TAG, "Message: %s", this->read_buffer_);
return true; // Full message in buffer
const char *line = this->line_reader_.feed(byte);
if (line == nullptr && byte == '>' && this->line_reader_.length() == sizeof(PROMPT) - 1) {
line = this->line_reader_.partial();
this->line_reader_.reset();
}
this->read_pos_++;
if (line != nullptr) {
ESP_LOGV(TAG, "Message: %s", line);
return line;
}
this->line_reader_.log_if_overflowed(TAG);
}
return false; // No full message yet
return nullptr;
}
void DfrobotSen0395Component::set_detected_(bool detected) {
@@ -4,6 +4,7 @@
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/line_reader.h"
#ifdef USE_BINARY_SENSOR
#include "esphome/components/binary_sensor/binary_sensor.h"
@@ -14,7 +15,7 @@
namespace esphome::dfrobot_sen0395 {
static constexpr uint8_t MMWAVE_READ_BUFFER_LENGTH = 255;
static constexpr uint8_t MMWAVE_READ_BUFFER_LENGTH = 253; // largest LineReader size with a one byte position
static constexpr uint8_t COMMAND_QUEUE_SIZE = 20;
// Keep in sync with the BusyActions table in dfrobot_sen0395.cpp
@@ -121,8 +122,7 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component
bool led_active_{false};
bool uart_presence_active_{false};
bool start_after_boot_{false};
char read_buffer_[MMWAVE_READ_BUFFER_LENGTH];
size_t read_pos_{0};
LineReader<MMWAVE_READ_BUFFER_LENGTH> line_reader_;
bool cmd_sent_{false};
uint8_t retries_left_{2};
StaticRingBuffer<Command, COMMAND_QUEUE_SIZE> cmd_queue_;
@@ -131,12 +131,13 @@ class DfrobotSen0395Component final : public uart::UARTDevice, public Component
int16_t det_range_steps_[8]{};
uint16_t latency_steps_[2]{};
bool read_message_();
/// The next complete line, valid until the next call, or nullptr
const char *read_message_();
void read_state_();
/// Run the command at the front of the queue; true when it is done
bool run_command_(const Command &cmd, uint32_t now);
/// Handle a reply to a sent command; true when the command is done
bool handle_reply_(const Command &cmd);
bool handle_reply_(const Command &cmd, const char *message);
void set_detected_(bool detected);
};
+15 -4
View File
@@ -1,4 +1,5 @@
from dataclasses import dataclass
import logging
from typing import Any, Protocol
from esphome import automation, core, pins
@@ -34,6 +35,8 @@ from esphome.types import ConfigType
DOMAIN = "display"
IS_PLATFORM_COMPONENT = True
_LOGGER = logging.getLogger(__name__)
display_ns = cg.esphome_ns.namespace("display")
Display = display_ns.class_("Display", cg.PollingComponent)
DisplayBuffer = display_ns.class_("DisplayBuffer", Display)
@@ -102,13 +105,21 @@ BASIC_DISPLAY_SCHEMA = cv.Schema(
).extend(cv.polling_component_schema("1s"))
def _validate_test_card(config):
def _validate_test_card(config: ConfigType) -> ConfigType:
if (
config.get(CONF_SHOW_TEST_CARD, False)
and config.get(CONF_UPDATE_INTERVAL, False) == SCHEDULER_DONT_RUN
and (update_interval := config.get(CONF_UPDATE_INTERVAL)) is not None
and update_interval.total_milliseconds == SCHEDULER_DONT_RUN
):
raise cv.Invalid(
f"`{CONF_SHOW_TEST_CARD}: True` cannot be used with `{CONF_UPDATE_INTERVAL}: never` because this combination will not show a test_card."
# Only a warning: something else can still update the display, for example
# component.update, and the test card is drawn then.
display_id = config.get(CONF_ID)
_LOGGER.warning(
"%s`%s: True` is used with `%s: never`, so the test card is only drawn "
"when something updates the display (for example component.update)",
f"{display_id.id}: " if display_id is not None and display_id.id else "",
CONF_SHOW_TEST_CARD,
CONF_UPDATE_INTERVAL,
)
return config
+1 -1
View File
@@ -22,7 +22,7 @@ CONF_CUSTOM_PATTERNS = "custom_patterns"
CONF_SKIP_CRC = "skip_crc"
CONF_DEFAULT_OBIS = "default_obis"
DLMS_PARSER_VERSION = "2.2.0"
DLMS_PARSER_VERSION = "2.2.2"
dlms_meter_component_ns = cg.esphome_ns.namespace("dlms_meter")
DlmsMeterComponent = dlms_meter_component_ns.class_(
@@ -1,5 +1,6 @@
#include "ds248x_one_wire_bus.h"
#include "ds248x.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::ds248x {
@@ -151,11 +152,7 @@ uint64_t DS248xOneWireBus::search_int() {
}
// Update address based on direction taken
if (dir_taken) {
address |= bit_mask;
} else {
address &= ~bit_mask;
}
assign_bits(address, bit_mask, dir_taken);
}
// Search successful
+4 -6
View File
@@ -18,13 +18,11 @@ E131AddressableLightEffect = e131_ns.class_(
)
E131Component = e131_ns.class_("E131Component", cg.Component)
METHODS = {"UNICAST": e131_ns.E131_UNICAST, "MULTICAST": e131_ns.E131_MULTICAST}
E131ListenMethod = e131_ns.enum("E131ListenMethod")
METHODS = E131ListenMethod.options("UNICAST", "MULTICAST", prefix="E131_")
CHANNELS = {
"MONO": e131_ns.E131_MONO,
"RGB": e131_ns.E131_RGB,
"RGBW": e131_ns.E131_RGBW,
}
E131LightChannels = e131_ns.enum("E131LightChannels")
CHANNELS = E131LightChannels.options("MONO", "RGB", "RGBW", prefix="E131_")
CONF_UNIVERSE = "universe"
CONF_E131_ID = "e131_id"
@@ -20,9 +20,7 @@ void EKTF2232Touchscreen::setup() {
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
this->reset_pin_->setup();
this->hard_reset_();
pulse_reset(this->reset_pin_, {.low_ms = 15, .high_after_ms = 15});
if (!this->soft_reset_()) {
ESP_LOGE(TAG, "Failed to soft reset EKT2232!");
this->interrupt_pin_->detach_interrupt();
@@ -96,13 +94,6 @@ bool EKTF2232Touchscreen::get_power_state() {
return (received[1] >> 3) & 1;
}
void EKTF2232Touchscreen::hard_reset_() {
this->reset_pin_->digital_write(false);
delay(15);
this->reset_pin_->digital_write(true);
delay(15);
}
bool EKTF2232Touchscreen::soft_reset_() {
auto err = this->write(SOFT_RESET_CMD, 4);
if (err != i2c::ERROR_OK)
@@ -22,7 +22,6 @@ class EKTF2232Touchscreen final : public Touchscreen, public i2c::I2CDevice {
bool get_power_state();
protected:
void hard_reset_();
bool soft_reset_();
void update_touches() override;
+12 -15
View File
@@ -4,21 +4,18 @@
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
namespace esphome::epaper_spi {
ESPHOME_LOG_TAG(TAG, "epaper_spi");
static constexpr size_t EPAPER_MAX_CMD_LOG_BYTES = 128;
static constexpr const char *const EPAPER_STATE_STRINGS[] = {
"IDLE", "UPDATE", "RESET", "RESET_END", "SHOULD_WAIT", "INITIALISE",
"TRANSFER_DATA", "POWER_ON", "REFRESH_SCREEN", "POWER_OFF", "DEEP_SLEEP",
};
PROGMEM_STRING_TABLE(EPaperStateStrings, "IDLE", "UPDATE", "RESET", "RESET_END", "SHOULD_WAIT", "INITIALISE",
"TRANSFER_DATA", "POWER_ON", "REFRESH_SCREEN", "POWER_OFF", "DEEP_SLEEP", "Unknown");
const char *EPaperBase::epaper_state_to_string_() {
if (auto idx = static_cast<unsigned>(this->state_); idx < std::size(EPAPER_STATE_STRINGS))
return EPAPER_STATE_STRINGS[idx];
return "Unknown";
const LogString *EPaperBase::epaper_state_to_string_() {
return EPaperStateStrings::get_log_str(static_cast<uint8_t>(this->state_), EPaperStateStrings::LAST_INDEX);
}
void EPaperBase::setup() {
@@ -159,7 +156,7 @@ void EPaperBase::update_effective_transform_() {
void EPaperBase::update() {
if (this->state_ != EPaperState::IDLE) {
ESP_LOGE(TAG, "Display already in state %s", epaper_state_to_string_());
ESP_LOGE(TAG, "Display already in state %s", LOG_STR_ARG(epaper_state_to_string_()));
return;
}
this->set_state_(EPaperState::UPDATE);
@@ -212,7 +209,7 @@ void EPaperBase::loop() {
this->high_frequency_loop_.stop();
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
if (now - this->waiting_for_idle_last_print_ >= 1000) {
ESP_LOGV(TAG, "Waiting for idle in state %s", this->epaper_state_to_string_());
ESP_LOGV(TAG, "Waiting for idle in state %s", LOG_STR_ARG(this->epaper_state_to_string_()));
this->waiting_for_idle_last_print_ = millis();
}
#endif
@@ -227,7 +224,7 @@ void EPaperBase::loop() {
// The busy pin never cleared: give up on this update so later ones are not refused, and make the next one full
void EPaperBase::abandon_update_() {
ESP_LOGE(TAG, "Display stayed busy for %u s in state %s; check the busy pin", (unsigned) this->busy_timeout_seconds_,
this->epaper_state_to_string_());
LOG_STR_ARG(this->epaper_state_to_string_()));
this->status_set_warning(LOG_STR("Busy pin timeout"));
if (this->state_ == EPaperState::TRANSFER_DATA)
this->abort_transfer();
@@ -247,10 +244,10 @@ void EPaperBase::abandon_update_() {
* Should a subclassed class need to override this, the method will need to be made virtual.
*/
void EPaperBase::process_state_() {
ESP_LOGV(TAG, "Process state entered in state %s", epaper_state_to_string_());
ESP_LOGV(TAG, "Process state entered in state %s", LOG_STR_ARG(epaper_state_to_string_()));
switch (this->state_) {
default:
ESP_LOGE(TAG, "Display is in unhandled state %s", epaper_state_to_string_());
ESP_LOGE(TAG, "Display is in unhandled state %s", LOG_STR_ARG(epaper_state_to_string_()));
this->set_state_(EPaperState::IDLE);
break;
case EPaperState::IDLE:
@@ -342,7 +339,7 @@ void EPaperBase::process_state_() {
}
void EPaperBase::set_state_(EPaperState state, uint16_t delay) {
ESP_LOGV(TAG, "Exit state %s", this->epaper_state_to_string_());
ESP_LOGV(TAG, "Exit state %s", LOG_STR_ARG(this->epaper_state_to_string_()));
this->state_ = state;
// allow subclasses to nominate delays
if (delay == 0)
@@ -350,7 +347,7 @@ void EPaperBase::set_state_(EPaperState state, uint16_t delay) {
this->next_delay_ = 0;
this->delay_until_ = millis() + delay;
this->wait_for_idle_(state > EPaperState::SHOULD_WAIT);
ESP_LOGV(TAG, "Enter state %s, delay %u, wait_for_idle=%s", this->epaper_state_to_string_(), delay,
ESP_LOGV(TAG, "Enter state %s, delay %u, wait_for_idle=%s", LOG_STR_ARG(this->epaper_state_to_string_()), delay,
TRUEFALSE(this->waiting_for_idle_));
// Each step of an update otherwise waits for the next loop iteration; loop() slows down again for long waits
if (state == EPaperState::IDLE) {
+1 -1
View File
@@ -125,7 +125,7 @@ class EPaperBase : public Display,
bool is_using_partial_update_() const { return this->full_update_every_ > 1; }
void process_state_();
const char *epaper_state_to_string_();
const LogString *epaper_state_to_string_();
bool is_idle_() const;
void setup_pins_() const;
virtual bool reset();
@@ -72,9 +72,7 @@ bool EPaperJD79660::reset() {
// As commented on SLEEP_MS_RESET1: Reset pulse must happen within time window.
// So do not use FSM loop, and avoid other calls/logs during pulse below.
this->reset_pin_->digital_write(false);
delay(SLEEP_MS_RESET1);
this->reset_pin_->digital_write(true);
pulse_reset(this->reset_pin_, {.low_ms = SLEEP_MS_RESET1});
this->reset_duration_ = SLEEP_MS_RESET2;
this->step_ = FSMState::RESET_STEP2_IDLECHECK;
+42 -36
View File
@@ -33,53 +33,59 @@ void ES7210::dump_config() {
this->channel_gains_[2], this->channel_gains_[3]);
}
static constexpr i2c::I2CRegValue ES7210_RESET[] PROGMEM = {
// Software reset
{ES7210_RESET_REG00, 0xff},
{ES7210_RESET_REG00, 0x32},
{ES7210_CLOCK_OFF_REG01, 0x3f},
// Set initialization time when device powers up
{ES7210_TIME_CONTROL0_REG09, 0x30},
{ES7210_TIME_CONTROL1_REG0A, 0x30},
// Configure HFP for all ADC channels
{ES7210_ADC12_HPF2_REG23, 0x2a},
{ES7210_ADC12_HPF1_REG22, 0x0a},
{ES7210_ADC34_HPF2_REG20, 0x0a},
{ES7210_ADC34_HPF1_REG21, 0x2a},
};
static constexpr i2c::I2CRegValue ES7210_ANALOG[] PROGMEM = {
// Configure analog power
{ES7210_ANALOG_REG40, 0xC3},
// Set mic bias
{ES7210_MIC12_BIAS_REG41, 0x70},
{ES7210_MIC34_BIAS_REG42, 0x70},
};
static constexpr i2c::I2CRegValue ES7210_POWER_ON[] PROGMEM = {
// Power on mics 1 through 4
{ES7210_MIC1_POWER_REG47, 0x08},
{ES7210_MIC2_POWER_REG48, 0x08},
{ES7210_MIC3_POWER_REG49, 0x08},
{ES7210_MIC4_POWER_REG4A, 0x08},
// Power down DLL
{ES7210_POWER_DOWN_REG06, 0x04},
// Power on MIC1-4 bias & ADC1-4 & PGA1-4 Power
{ES7210_MIC12_POWER_REG4B, 0x0F},
{ES7210_MIC34_POWER_REG4C, 0x0F},
// Enable device
{ES7210_RESET_REG00, 0x71},
{ES7210_RESET_REG00, 0x41},
};
void ES7210::setup() {
// Software reset
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0xff));
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x32));
ES7210_ERROR_FAILED(this->write_byte(ES7210_CLOCK_OFF_REG01, 0x3f));
// Set initialization time when device powers up
ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL0_REG09, 0x30));
ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL1_REG0A, 0x30));
// Configure HFP for all ADC channels
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF2_REG23, 0x2a));
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF1_REG22, 0x0a));
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF2_REG20, 0x0a));
ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF1_REG21, 0x2a));
ES7210_ERROR_FAILED(this->write_registers(ES7210_RESET));
// Secondary I2S mode settings
ES7210_ERROR_FAILED(this->update_register(ES7210_MODE_CONFIG_REG08, 0x01, 0x00));
// Configure analog power
ES7210_ERROR_FAILED(this->write_byte(ES7210_ANALOG_REG40, 0xC3));
// Set mic bias
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_BIAS_REG41, 0x70));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_BIAS_REG42, 0x70));
ES7210_ERROR_FAILED(this->write_registers(ES7210_ANALOG));
// Configure I2S settings, sample rate, and microphone gains
ES7210_ERROR_FAILED(this->configure_i2s_format_());
ES7210_ERROR_FAILED(this->configure_sample_rate_());
ES7210_ERROR_FAILED(this->configure_mic_gain_());
// Power on mics 1 through 4
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC1_POWER_REG47, 0x08));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC2_POWER_REG48, 0x08));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC3_POWER_REG49, 0x08));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC4_POWER_REG4A, 0x08));
// Power down DLL
ES7210_ERROR_FAILED(this->write_byte(ES7210_POWER_DOWN_REG06, 0x04));
// Power on MIC1-4 bias & ADC1-4 & PGA1-4 Power
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x0F));
ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x0F));
// Enable device
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x71));
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x41));
ES7210_ERROR_FAILED(this->write_registers(ES7210_POWER_ON));
this->setup_complete_ = true;
}
+54 -48
View File
@@ -25,56 +25,62 @@ ESPHOME_LOG_TAG(TAG, "es7243e");
void ES7243E::dump_config() { ESP_LOGCONFIG(TAG, "ES7243E audio ADC:"); }
// clang-format off
static constexpr i2c::I2CRegValue ES7243E_INIT[] PROGMEM = {
{ES7243E_CLOCK_MGR_REG01, 0x3A},
{ES7243E_RESET_REG00, 0x80},
{ES7243E_TEST_MODE_REGF9, 0x00},
{ES7243E_CLOCK_MGR_REG04, 0x02},
{ES7243E_CLOCK_MGR_REG04, 0x01},
{ES7243E_TEST_MODE_REGF9, 0x01},
{ES7243E_RESET_REG00, 0x1E},
{ES7243E_CLOCK_MGR_REG01, 0x00},
{ES7243E_CLOCK_MGR_REG02, 0x00},
{ES7243E_CLOCK_MGR_REG03, 0x20},
{ES7243E_CLOCK_MGR_REG04, 0x01},
{ES7243E_ADC_CTRL_REG0D, 0x00},
{ES7243E_CLOCK_MGR_REG05, 0x00},
{ES7243E_CLOCK_MGR_REG06, 0x03}, // SCLK=MCLK/4
{ES7243E_CLOCK_MGR_REG07, 0x00}, // LRCK=MCLK/256
{ES7243E_CLOCK_MGR_REG08, 0xFF}, // LRCK=MCLK/256
{ES7243E_CLOCK_MGR_REG09, 0xCA},
{ES7243E_SDP_REG0A, 0x85},
{ES7243E_SDP_REG0B, 0x00},
{ES7243E_ADC_CTRL_REG0E, 0xBF},
{ES7243E_ADC_CTRL_REG0F, 0x80},
{ES7243E_ADC_CTRL_REG14, 0x0C},
{ES7243E_ADC_CTRL_REG15, 0x0C},
{ES7243E_ANALOG_REG17, 0x02},
{ES7243E_ANALOG_REG18, 0x26},
{ES7243E_ANALOG_REG19, 0x77},
{ES7243E_ANALOG_REG1A, 0xF4},
{ES7243E_ANALOG_REG1B, 0x66},
{ES7243E_ANALOG_REG1C, 0x44},
{ES7243E_ANALOG_REG1E, 0x00},
{ES7243E_ANALOG_REG1F, 0x0C},
{ES7243E_ANALOG_REG20, 0x1A}, // PGA gain +30dB
{ES7243E_ANALOG_REG21, 0x1A},
{ES7243E_RESET_REG00, 0x80},
{ES7243E_CLOCK_MGR_REG01, 0x3A},
{ES7243E_ANALOG_REG16, 0x3F},
{ES7243E_ANALOG_REG16, 0x00},
{ES7243E_TEST_MODE_REGF9, 0x00},
{ES7243E_CLOCK_MGR_REG04, 0x01},
{ES7243E_ANALOG_REG17, 0x01},
};
// clang-format on
static constexpr i2c::I2CRegValue ES7243E_START[] PROGMEM = {
{ES7243E_RESET_REG00, 0x80},
{ES7243E_CLOCK_MGR_REG01, 0x3A},
{ES7243E_ANALOG_REG16, 0x3F},
{ES7243E_ANALOG_REG16, 0x00},
};
void ES7243E::setup() {
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x02));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x01));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x1E));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG02, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG03, 0x20));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0D, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG05, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG06, 0x03)); // SCLK=MCLK/4
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG07, 0x00)); // LRCK=MCLK/256
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG08, 0xFF)); // LRCK=MCLK/256
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG09, 0xCA));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_SDP_REG0A, 0x85));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_SDP_REG0B, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0E, 0xBF));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0F, 0x80));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG14, 0x0C));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG15, 0x0C));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG17, 0x02));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG18, 0x26));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG19, 0x77));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1A, 0xF4));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1B, 0x66));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1C, 0x44));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1E, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1F, 0x0C));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG20, 0x1A)); // PGA gain +30dB
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG21, 0x1A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x3F));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x00));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG17, 0x01));
ES7243E_ERROR_FAILED(this->write_registers(ES7243E_INIT));
ES7243E_ERROR_FAILED(this->configure_mic_gain_());
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x3F));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x00));
ES7243E_ERROR_FAILED(this->write_registers(ES7243E_START));
this->setup_complete_ = true;
}
+46 -62
View File
@@ -9,70 +9,54 @@ namespace esphome::es8156 {
ESPHOME_LOG_TAG(TAG, "es8156");
// Mark the component as failed; use only in setup
#define ES8156_ERROR_FAILED(func) \
if (!(func)) { \
this->mark_failed(LOG_STR("Failed to initialize")); \
return; \
}
static constexpr i2c::I2CRegValue ES8156_INIT[] PROGMEM = {
// REG02 MODE CONFIG 1: Enable software mode for I2C control of volume/mute
// Bit 2: SOFT_MODE_SEL=1 (software mode enabled)
{ES8156_REG02_SCLK_MODE, 0x04},
// Analog system configuration (active-low power down bits, active-high enables)
// REG20 ANALOG SYSTEM: Configure analog signal path
{ES8156_REG20_ANALOG_SYS1, 0x2A},
// REG21 ANALOG SYSTEM: VSEL=0x1C (bias level ~120%), normal VREF ramp speed
{ES8156_REG21_ANALOG_SYS2, 0x3C},
// REG22 ANALOG SYSTEM: Line out mode (HPSW=0), OUT_MUTE=0 (not muted)
{ES8156_REG22_ANALOG_SYS3, 0x00},
// REG24 ANALOG SYSTEM: Low power mode for VREFBUF, HPCOM, DACVRP; DAC normal power
// Bits 2:0 = 0x07: LPVREFBUF=1, LPHPCOM=1, LPDACVRP=1, LPDAC=0
{ES8156_REG24_ANALOG_LP, 0x07},
// REG23 ANALOG SYSTEM: Lowest bias (IBIAS_SW=0), VMIDLVL=VDDA/2, normal impedance
{ES8156_REG23_ANALOG_SYS4, 0x00},
// Timing and interface configuration
// REG0A/0B TIME CONTROL: Fast state machine transitions
{ES8156_REG0A_TIME_CONTROL1, 0x01},
{ES8156_REG0B_TIME_CONTROL2, 0x01},
// REG11 SDP INTERFACE CONFIG: Default I2S format (24-bit, I2S mode)
{ES8156_REG11_DAC_SDP, 0x00},
// REG19 EQ CONTROL 1: EQ disabled (EQ_ON=0), EQ_BAND_NUM=2
{ES8156_REG19_EQ_CONTROL1, 0x20},
// REG0D P2S CONTROL: Parallel-to-serial converter settings
{ES8156_REG0D_P2S_CONTROL, 0x14},
// REG09 MISC CONTROL 2: Default settings
{ES8156_REG09_MISC_CONTROL2, 0x00},
// REG18 MISC CONTROL 3: Stereo channel routing, no inversion
// Bits 5:4 CHN_CROSS: 0=L→L/R→R, 1=L to both, 2=R to both, 3=swap L/R
// Bits 3:2: LCH_INV/RCH_INV channel inversion
{ES8156_REG18_MISC_CONTROL3, 0x00},
// REG08 CLOCK OFF: Enable all internal clocks (0x3F = all clock gates open)
{ES8156_REG08_CLOCK_ON_OFF, 0x3F},
// REG00 RESET CONTROL: Reset sequence
// First: RST_DIG=1 (assert digital reset)
{ES8156_REG00_RESET, 0x02},
// Then: CSM_ON=1 (enable chip state machine), RST_DIG=1
{ES8156_REG00_RESET, 0x03},
// REG25 ANALOG SYSTEM: Power up analog blocks
// VMIDSEL=2 (normal VMID operation), PDN_ANA=0, ENREFR=0, ENHPCOM=0
// PDN_DACVREFGEN=0, PDN_VREFBUF=0, PDN_DAC=0 (all enabled)
{ES8156_REG25_ANALOG_SYS5, 0x20},
};
void ES8156::setup() {
// REG02 MODE CONFIG 1: Enable software mode for I2C control of volume/mute
// Bit 2: SOFT_MODE_SEL=1 (software mode enabled)
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG02_SCLK_MODE, 0x04));
// Analog system configuration (active-low power down bits, active-high enables)
// REG20 ANALOG SYSTEM: Configure analog signal path
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG20_ANALOG_SYS1, 0x2A));
// REG21 ANALOG SYSTEM: VSEL=0x1C (bias level ~120%), normal VREF ramp speed
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG21_ANALOG_SYS2, 0x3C));
// REG22 ANALOG SYSTEM: Line out mode (HPSW=0), OUT_MUTE=0 (not muted)
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG22_ANALOG_SYS3, 0x00));
// REG24 ANALOG SYSTEM: Low power mode for VREFBUF, HPCOM, DACVRP; DAC normal power
// Bits 2:0 = 0x07: LPVREFBUF=1, LPHPCOM=1, LPDACVRP=1, LPDAC=0
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG24_ANALOG_LP, 0x07));
// REG23 ANALOG SYSTEM: Lowest bias (IBIAS_SW=0), VMIDLVL=VDDA/2, normal impedance
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG23_ANALOG_SYS4, 0x00));
// Timing and interface configuration
// REG0A/0B TIME CONTROL: Fast state machine transitions
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0A_TIME_CONTROL1, 0x01));
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0B_TIME_CONTROL2, 0x01));
// REG11 SDP INTERFACE CONFIG: Default I2S format (24-bit, I2S mode)
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG11_DAC_SDP, 0x00));
// REG19 EQ CONTROL 1: EQ disabled (EQ_ON=0), EQ_BAND_NUM=2
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG19_EQ_CONTROL1, 0x20));
// REG0D P2S CONTROL: Parallel-to-serial converter settings
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0D_P2S_CONTROL, 0x14));
// REG09 MISC CONTROL 2: Default settings
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG09_MISC_CONTROL2, 0x00));
// REG18 MISC CONTROL 3: Stereo channel routing, no inversion
// Bits 5:4 CHN_CROSS: 0=L→L/R→R, 1=L to both, 2=R to both, 3=swap L/R
// Bits 3:2: LCH_INV/RCH_INV channel inversion
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG18_MISC_CONTROL3, 0x00));
// REG08 CLOCK OFF: Enable all internal clocks (0x3F = all clock gates open)
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG08_CLOCK_ON_OFF, 0x3F));
// REG00 RESET CONTROL: Reset sequence
// First: RST_DIG=1 (assert digital reset)
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG00_RESET, 0x02));
// Then: CSM_ON=1 (enable chip state machine), RST_DIG=1
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG00_RESET, 0x03));
// REG25 ANALOG SYSTEM: Power up analog blocks
// VMIDSEL=2 (normal VMID operation), PDN_ANA=0, ENREFR=0, ENHPCOM=0
// PDN_DACVREFGEN=0, PDN_VREFBUF=0, PDN_DAC=0 (all enabled)
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG25_ANALOG_SYS5, 0x20));
if (!this->write_registers(ES8156_INIT))
this->mark_failed(LOG_STR("Failed to initialize"));
}
void ES8156::dump_config() { ESP_LOGCONFIG(TAG, "ES8156 Audio Codec:"); }
+69 -78
View File
@@ -22,79 +22,76 @@ ESPHOME_LOG_TAG(TAG, "es8388");
return false; \
}
static constexpr i2c::I2CRegValue ES8388_INIT[] PROGMEM = {
// I2S worker mode
{ES8388_MASTERMODE, 0x00},
/* Chip Control and Power Management */
{ES8388_CONTROL2, 0x50},
// normal all and power up all
{ES8388_CHIPPOWER, 0x00},
// vmidsel/500k
// EnRef=0,Play&Record Mode,(0x17-both of mic&play)
{ES8388_CONTROL1, 0x12},
// i2s 16 bits
{ES8388_DACCONTROL1, 0x18},
// sample freq 256
// DACFsMode,SINGLE SPEED; DACFsRatio,256
{ES8388_DACCONTROL2, 0x02},
// 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
{ES8388_DACCONTROL16, 0x00},
// only left DAC to left mixer enable 0db
{ES8388_DACCONTROL17, 0x90},
// only right DAC to right mixer enable 0db
{ES8388_DACCONTROL20, 0x90},
// set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
{ES8388_DACCONTROL21, 0x80},
// vroi=0 - 1.5k VREF to analog output resistance (default)
{ES8388_DACCONTROL23, 0x00},
// power down adc and line in
{ES8388_ADCPOWER, 0xFF},
//@nightdav
{ES8388_ADCCONTROL1, 0x00}, // +21dB : recommended value for ALC & voice recording
// set to Mono Right
{ES8388_ADCCONTROL3, 0x02},
// I2S 16 Bits length and I2S serial audio data format
{ES8388_ADCCONTROL4, 0x0d},
// ADCFsMode,singel SPEED,RATIO=256
{ES8388_ADCCONTROL5, 0x02},
// ADC Volume
{ES8388_ADCCONTROL8, 0x00},
{ES8388_ADCCONTROL9, 0x00},
//@nightDav
// ALC Config (as recommended by ES8388 user guide for voice recording)
// Reg 0x12 = 0xe2 (ALC enable, PGA Max. Gain=23.5dB, Min. Gain=0dB)
{ES8388_ADCCONTROL10, 0xe2},
// Reg 0x13 = 0xa0 (ALC Target=-1.5dB, ALC Hold time =0 mS)
{ES8388_ADCCONTROL11, 0xa0},
// Reg 0x14 = 0x12(Decay time =820uS , Attack time = 416 uS)
{ES8388_ADCCONTROL12, 0x12},
// Reg 0x15 = 0x06(ALC mode)
{ES8388_ADCCONTROL13, 0x06},
// Reg 0x16 = 0xc3(nose gate = -40.5dB, NGG = 0x01(mute ADC))
{ES8388_ADCCONTROL14, 0xc3},
// Power on ADC
{ES8388_DACCONTROL21, 0x80},
// Start state machine
{ES8388_CHIPPOWER, 0xF0},
};
static constexpr i2c::I2CRegValue ES8388_UNMUTE[] PROGMEM = {
// unmute ADC with fade in
{ES8388_ADCCONTROL7, 0x60},
// unmute DAC with fade in
{ES8388_DACCONTROL3, 0x20},
// Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode
{ES8388_ADCPOWER, 0x09},
};
void ES8388::setup() {
// mute DAC
this->set_mute_state_(true);
// I2S worker mode
ES8388_ERROR_FAILED(this->write_byte(ES8388_MASTERMODE, 0x00));
/* Chip Control and Power Management */
ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL2, 0x50));
// normal all and power up all
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00));
// vmidsel/500k
// EnRef=0,Play&Record Mode,(0x17-both of mic&play)
ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL1, 0x12));
// i2s 16 bits
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL1, 0x18));
// sample freq 256
// DACFsMode,SINGLE SPEED; DACFsRatio,256
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL2, 0x02));
// 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL16, 0x00));
// only left DAC to left mixer enable 0db
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL17, 0x90));
// only right DAC to right mixer enable 0db
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL20, 0x90));
// set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80));
// vroi=0 - 1.5k VREF to analog output resistance (default)
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL23, 0x00));
// power down adc and line in
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0xFF));
//@nightdav
ES8388_ERROR_FAILED(
this->write_byte(ES8388_ADCCONTROL1, 0x00)); // +21dB : recommended value for ALC & voice recording
// set to Mono Right
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL3, 0x02));
// I2S 16 Bits length and I2S serial audio data format
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL4, 0x0d));
// ADCFsMode,singel SPEED,RATIO=256
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL5, 0x02));
// ADC Volume
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL8, 0x00));
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL9, 0x00));
//@nightDav
// ALC Config (as recommended by ES8388 user guide for voice recording)
// Reg 0x12 = 0xe2 (ALC enable, PGA Max. Gain=23.5dB, Min. Gain=0dB)
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL10, 0xe2));
// Reg 0x13 = 0xa0 (ALC Target=-1.5dB, ALC Hold time =0 mS)
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL11, 0xa0));
// Reg 0x14 = 0x12(Decay time =820uS , Attack time = 416 uS)
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL12, 0x12));
// Reg 0x15 = 0x06(ALC mode)
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL13, 0x06));
// Reg 0x16 = 0xc3(nose gate = -40.5dB, NGG = 0x01(mute ADC))
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL14, 0xc3));
// Power on ADC
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80));
// Start state machine
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0xF0));
ES8388_ERROR_FAILED(this->write_registers(ES8388_INIT));
delay(1);
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00));
@@ -104,13 +101,7 @@ void ES8388::setup() {
this->set_mute_state_(false);
// unmute ADC with fade in
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL7, 0x60));
// unmute DAC with fade in
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL3, 0x20));
// Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0x09));
ES8388_ERROR_FAILED(this->write_registers(ES8388_UNMUTE));
#ifdef USE_ES8388_SELECT
if (this->dac_output_select_ != nullptr) {
@@ -152,8 +143,8 @@ void ES8388::dump_config() {
ESP_LOGCONFIG(TAG, "ES8388 Audio Codec:");
LOG_I2C_DEVICE(this);
#ifdef USE_ES8388_SELECT
LOG_SELECT(" ", "DacOutputSelect", this->dac_output_select_);
LOG_SELECT(" ", "ADCInputMicSelect", this->adc_input_mic_select_);
LOG_SELECT(" ", "DAC Output", this->dac_output_select_);
LOG_SELECT(" ", "ADC Input Mic", this->adc_input_mic_select_);
#endif
}
+9 -5
View File
@@ -24,14 +24,12 @@ ADC_INPUT_MIC_OPTIONS = AdcInputMicLine.options(
)
ADC_INPUT_MIC_OPTION_NAMES = list(ADC_INPUT_MIC_OPTIONS)
DacOutputSelect = es8388_ns.class_("DacOutputSelect", select.Select)
ADCInputMicSelect = es8388_ns.class_("ADCInputMicSelect", select.Select)
CONFIG_SCHEMA = cv.All(
{
cv.GenerateID(CONF_ES8388_ID): cv.use_id(ES8388),
cv.Optional(CONF_DAC_OUTPUT): select.select_schema(
DacOutputSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_CHIP,
).extend(
@@ -42,7 +40,7 @@ CONFIG_SCHEMA = cv.All(
}
),
cv.Optional(CONF_ADC_INPUT_MIC): select.select_schema(
ADCInputMicSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_CHIP,
).extend(
@@ -61,12 +59,18 @@ async def to_code(config: ConfigType) -> None:
hub = await cg.get_variable(config[CONF_ES8388_ID])
selects = select.sub_selects(config, parent=hub)
await selects(
CONF_DAC_OUTPUT, hub.set_dac_output_select, options=DAC_OUTPUT_OPTION_NAMES
CONF_DAC_OUTPUT,
hub.set_dac_output_select,
options=DAC_OUTPUT_OPTION_NAMES,
apply="set_dac_output(static_cast<es8388::DacOutputLine>(index))",
publish="before",
)
await selects(
CONF_ADC_INPUT_MIC,
hub.set_adc_input_mic_select,
options=ADC_INPUT_MIC_OPTION_NAMES,
apply="set_adc_input_mic(static_cast<es8388::AdcInputMicLine>(index))",
publish="before",
)
if (
initial := config.get(CONF_DAC_OUTPUT, {}).get(CONF_INITIAL_OPTION)
@@ -1,10 +0,0 @@
#include "adc_input_mic_select.h"
namespace esphome::es8388 {
void ADCInputMicSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_adc_input_mic(static_cast<AdcInputMicLine>(index));
}
} // namespace esphome::es8388
@@ -1,13 +0,0 @@
#pragma once
#include "esphome/components/es8388/es8388.h"
#include "esphome/components/select/select.h"
namespace esphome::es8388 {
class ADCInputMicSelect final : public select::Select, public Parented<ES8388> {
protected:
void control(size_t index) override;
};
} // namespace esphome::es8388
@@ -1,10 +0,0 @@
#include "dac_output_select.h"
namespace esphome::es8388 {
void DacOutputSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_dac_output(static_cast<DacOutputLine>(index));
}
} // namespace esphome::es8388
@@ -1,13 +0,0 @@
#pragma once
#include "esphome/components/es8388/es8388.h"
#include "esphome/components/select/select.h"
namespace esphome::es8388 {
class DacOutputSelect final : public select::Select, public Parented<ES8388> {
protected:
void control(size_t index) override;
};
} // namespace esphome::es8388
+3 -1
View File
@@ -2389,7 +2389,9 @@ FRAMEWORK_SCHEMA = cv.Schema(
cv.Schema(
{
cv.Required(CONF_NAME): cv.string_strict,
cv.Optional(CONF_SOURCE): cv.git_ref,
cv.Optional(CONF_SOURCE): cv.All(
cv.string_strict, cv.Length(min=1)
),
cv.Optional(CONF_REF): cv.string,
cv.Optional(CONF_PATH): cv.string,
}
+19 -11
View File
@@ -17,8 +17,13 @@
#include "hal/rtc_io_hal.h"
#endif
#ifndef SOC_GPIO_SUPPORT_RTC_INDEPENDENT
#define SOC_GPIO_SUPPORT_RTC_INDEPENDENT 0 // NOLINT
// some variants don't map rtcio to gpio 1:1
#if defined(USE_ESP32_VARIANT_ESP32) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32H21)
#define RTCIO_NEED_MAPPING 1 // NOLINT
#endif
#if defined(USE_ESP32_VARIANT_ESP32) || (SOC_RTCIO_PIN_COUNT > 0 && defined(USE_GPIO_HOLD))
#define ISR_PIN_USE_RTC 1 // NOLINT
#endif
namespace esphome {
@@ -53,10 +58,12 @@ struct ISRPinArg {
#ifdef USE_GPIO_HOLD
bool hold;
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
#ifdef ISR_PIN_USE_RTC
bool use_rtc;
#ifdef RTCIO_NEED_MAPPING
int rtc_pin;
#endif
#endif
};
#ifdef USE_GPIO_HOLD
@@ -69,17 +76,15 @@ static inline void refresh_hold(const ESP32InternalGPIOPin &pin) {
static inline void IRAM_ATTR isr_refresh_hold(const ISRPinArg *arg) {
if (!arg->hold)
return;
#if defined(USE_ESP32_VARIANT_ESP32)
#if defined(ISR_PIN_USE_RTC) && defined(SOC_RTCIO_HOLD_SUPPORTED)
if (arg->use_rtc) {
#ifdef RTCIO_NEED_MAPPING
rtcio_hal_hold_disable(arg->rtc_pin);
rtcio_hal_hold_enable(arg->rtc_pin);
return;
}
#elif defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
// GPIO0-21 are held from the RTC domain; the digital hold mask is zero for them
if (arg->pin < SOC_RTCIO_PIN_COUNT) {
#else
rtcio_hal_hold_disable(arg->pin);
rtcio_hal_hold_enable(arg->pin);
#endif
return;
}
#endif
@@ -96,10 +101,12 @@ ISRInternalGPIOPin ESP32InternalGPIOPin::to_isr() const {
#ifdef USE_GPIO_HOLD
arg->hold = this->get_hold();
#endif
#if defined(USE_ESP32_VARIANT_ESP32)
#ifdef ISR_PIN_USE_RTC
arg->use_rtc = rtc_gpio_is_valid_gpio(this->get_pin_num());
#ifdef RTCIO_NEED_MAPPING
if (arg->use_rtc)
arg->rtc_pin = rtc_io_number_get(this->get_pin_num());
#endif
#endif
return ISRInternalGPIOPin((void *) arg);
}
@@ -251,7 +258,8 @@ void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
if (diff & gpio::FLAG_INPUT) {
if (flags & gpio::FLAG_INPUT) {
gpio_hal_input_enable(&GPIO_HAL, arg->pin);
#if defined(USE_ESP32_VARIANT_ESP32)
#ifdef USE_ESP32_VARIANT_ESP32
// Only ESP32 uses the RTC registers for pullup/pulldown
if (arg->use_rtc) {
if (flags & gpio::FLAG_PULLUP) {
rtcio_hal_pullup_enable(arg->rtc_pin);
@@ -114,7 +114,8 @@ BLECharacteristicNotifyAction = esp32_ble_server_automations_ns.class_(
"BLECharacteristicNotifyAction", automation.Action
)
bytebuffer_ns = cg.esphome_ns.namespace("bytebuffer")
Endianness_ns = bytebuffer_ns.namespace("Endian")
# Unscoped in C++; is_class keeps the generated Endian::LITTLE spelling
Endian = bytebuffer_ns.enum("Endian", is_class=True)
PROPERTY_MAP = {
@@ -410,10 +411,7 @@ def value_schema(default_type=UNDEFINED, templatable=True):
),
cv.Optional(CONF_STRING_ENCODING, default="utf_8"): validate_encoding,
cv.Optional(CONF_ENDIANNESS, default="LITTLE"): cv.enum(
{
"LITTLE": Endianness_ns.LITTLE,
"BIG": Endianness_ns.BIG,
}
Endian.options("LITTLE", "BIG")
),
},
validate_value_type,
+13 -17
View File
@@ -54,25 +54,21 @@ CAN_MODES = {
# The supported bit rates differ between ESP32 variants.
# See ESP-IDF Programming Guide --> API Reference --> Two-Wire Automotive Interface (TWAI)
CAN_SPEEDS_ESP32 = {
"20KBPS": CanSpeed.CAN_20KBPS,
"25KBPS": CanSpeed.CAN_25KBPS,
"50KBPS": CanSpeed.CAN_50KBPS,
"100KBPS": CanSpeed.CAN_100KBPS,
"125KBPS": CanSpeed.CAN_125KBPS,
"250KBPS": CanSpeed.CAN_250KBPS,
"500KBPS": CanSpeed.CAN_500KBPS,
"800KBPS": CanSpeed.CAN_800KBPS,
"1000KBPS": CanSpeed.CAN_1000KBPS,
}
CAN_SPEEDS_ESP32 = CanSpeed.options(
"20KBPS",
"25KBPS",
"50KBPS",
"100KBPS",
"125KBPS",
"250KBPS",
"500KBPS",
"800KBPS",
"1000KBPS",
prefix="CAN_",
)
CAN_SPEEDS_ESP32_S2 = {
"1KBPS": CanSpeed.CAN_1KBPS,
"5KBPS": CanSpeed.CAN_5KBPS,
"10KBPS": CanSpeed.CAN_10KBPS,
"12K5BPS": CanSpeed.CAN_12K5BPS,
"16KBPS": CanSpeed.CAN_16KBPS,
"20KBPS": CanSpeed.CAN_20KBPS,
**CanSpeed.options("1KBPS", "5KBPS", "10KBPS", "12K5BPS", "16KBPS", prefix="CAN_"),
**CAN_SPEEDS_ESP32,
}
@@ -1,4 +1,5 @@
#include "led_strip.h"
#include <algorithm>
#include <cinttypes>
#ifdef USE_ESP32
@@ -14,6 +15,8 @@ namespace esphome::esp32_rmt_led_strip {
ESPHOME_LOG_TAG(TAG, "esp32_rmt_led_strip");
static const size_t RMT_SYMBOLS_PER_BYTE = 8;
// Idle time the line must stay low between frames so the strip latches
static constexpr uint32_t RESET_GAP_US = 50;
// Query the RMT default clock source frequency. This varies by variant:
// APB (80MHz) on ESP32/S2/S3/C3, PLL_F80M (80MHz) on C6/P4, XTAL (32MHz) on H2.
@@ -91,10 +94,20 @@ void ESP32RMTLEDStripLightOutput::setup() {
this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1);
#endif
const uint32_t resolution_hz = rmt_resolution_hz();
// Longest wire time of one frame; write_state() defers instead of blocking until it has elapsed
uint32_t bit_ticks = std::max(this->params_.bit0.duration0 + this->params_.bit0.duration1,
this->params_.bit1.duration0 + this->params_.bit1.duration1);
uint64_t frame_ticks = (uint64_t) bit_ticks * buffer_size * RMT_SYMBOLS_PER_BYTE + this->params_.reset.duration0 +
this->params_.reset.duration1;
this->frame_time_us_ = frame_ticks * 1000000 / resolution_hz + RESET_GAP_US;
// Seed with the interval write_state() gates on, so the first frame is not deferred
this->last_refresh_ = micros() - std::max(this->frame_time_us_, this->max_refresh_rate_);
rmt_tx_channel_config_t channel;
memset(&channel, 0, sizeof(channel));
channel.clk_src = RMT_CLK_SRC_DEFAULT;
channel.resolution_hz = rmt_resolution_hz();
channel.resolution_hz = resolution_hz;
channel.gpio_num = gpio_num_t(this->pin_);
channel.mem_block_symbols = this->rmt_symbols_;
channel.trans_queue_depth = 1;
@@ -157,22 +170,21 @@ void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bi
}
void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
uint32_t now = micros();
if (this->throttle_show_(now, this->last_refresh_, this->max_refresh_rate_))
// Previous frame still on the wire, inside its reset gap, or refreshing too often: try again next loop
if (this->throttle_show_(micros(), this->last_refresh_, std::max(this->frame_time_us_, this->max_refresh_rate_)))
return;
this->last_refresh_ = now;
// The gate above should always cover the previous frame; never overwrite rmt_buf_ while the RMT still reads it
if (rmt_tx_wait_all_done(this->channel_, 0) != ESP_OK) {
ESP_LOGE(TAG, "RMT TX still busy");
this->status_set_warning();
this->schedule_show();
return;
}
this->mark_shown_();
ESP_LOGVV(TAG, "Writing RGB values to bus");
esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000);
if (error != ESP_OK) {
ESP_LOGE(TAG, "RMT TX timeout");
this->status_set_warning();
return;
}
delayMicroseconds(50);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
memcpy(this->rmt_buf_, this->buf_, this->get_buffer_size_());
#else
@@ -203,13 +215,19 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
rmt_transmit_config_t config;
memset(&config, 0, sizeof(config));
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, this->get_buffer_size_(), &config);
esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, this->get_buffer_size_(), &config);
#else
error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
esp_err_t error =
rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
#endif
if (!this->status_comm_result(error == ESP_OK, nullptr)) {
ESP_LOGE(TAG, "RMT TX error");
// retry next loop iteration so the frame is not lost
this->schedule_show();
return;
}
// Stamped after the transfer started so the gate above stays conservative
this->last_refresh_ = micros();
}
void ESP32RMTLEDStripLightOutput::dump_config() {
@@ -81,6 +81,8 @@ class ESP32RMTLEDStripLightOutput final : public light::AddressableLight {
uint32_t last_refresh_{0};
uint32_t max_refresh_rate_{0};
// Worst-case wire time of one frame plus the reset gap, computed in setup()
uint32_t frame_time_us_{0};
};
} // namespace esphome::esp32_rmt_led_strip
@@ -9,24 +9,23 @@ from .. import (
CONF_ESPECTRE_ID,
DOMAIN,
ESPectreComponent,
espectre_ns,
supported_traffic_generator_modes,
)
DEPENDENCIES = ["espectre"]
TrafficModeSelect = espectre_ns.class_(
"TrafficModeSelect", select.Select, cg.Parented.template(ESPectreComponent)
)
CONFIG_SCHEMA = select.select_schema(
TrafficModeSelect, entity_category=ENTITY_CATEGORY_CONFIG
select.ApplySelect, entity_category=ENTITY_CATEGORY_CONFIG
).extend({cv.GenerateID(CONF_ESPECTRE_ID): cv.use_id(ESPectreComponent)})
async def to_code(config: ConfigType) -> None:
options = supported_traffic_generator_modes(CORE.config[DOMAIN])
var = await select.new_select(config, options=options)
await cg.register_parented(var, config[CONF_ESPECTRE_ID])
parent = await cg.get_variable(config[CONF_ESPECTRE_ID])
cg.add(parent.set_traffic_mode_select(var))
hub = await cg.get_variable(config[CONF_ESPECTRE_ID])
await select.sub_selects(config, parent=hub).create(
config,
hub.set_traffic_mode_select,
options=options,
apply="request_traffic_generator_mode(option)",
)
@@ -1,11 +0,0 @@
#include "espectre_select.h"
#ifdef USE_ESPECTRE
namespace esphome::espectre {
void TrafficModeSelect::control(size_t index) { this->parent_->request_traffic_generator_mode(this->option_at(index)); }
} // namespace esphome::espectre
#endif // USE_ESPECTRE
@@ -1,19 +0,0 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_ESPECTRE
#include "esphome/components/select/select.h"
#include "../espectre.h"
namespace esphome::espectre {
class TrafficModeSelect final : public select::Select, public Parented<ESPectreComponent> {
protected:
void control(size_t index) override;
};
} // namespace esphome::espectre
#endif // USE_ESPECTRE
+12 -9
View File
@@ -1,14 +1,14 @@
#include "ezo.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/progmem.h"
namespace esphome::ezo {
static const char *const EZO_COMMAND_TYPE_STRINGS[] = {"EZO_READ", "EZO_LED", "EZO_DEVICE_INFORMATION",
"EZO_SLOPE", "EZO_CALIBRATION", "EZO_SLEEP",
"EZO_I2C", "EZO_T", "EZO_CUSTOM"};
PROGMEM_STRING_TABLE(EzoCommandTypeStrings, "EZO_READ", "EZO_LED", "EZO_DEVICE_INFORMATION", "EZO_SLOPE",
"EZO_CALIBRATION", "EZO_SLEEP", "EZO_I2C", "EZO_T", "EZO_CUSTOM");
static const char *const EZO_CALIBRATION_TYPE_STRINGS[] = {"LOW", "MID", "HIGH"};
PROGMEM_STRING_TABLE(EzoCalibrationTypeStrings, "LOW", "MID", "HIGH");
void EZOSensor::dump_config() {
LOG_SENSOR("", "EZO", this);
@@ -103,7 +103,8 @@ void EZOSensor::loop() {
break;
}
ESP_LOGV(TAG, "Received buffer \"%s\" for command type %s", &buf[1], EZO_COMMAND_TYPE_STRINGS[to_run->command_type]);
ESP_LOGV(TAG, "Received buffer \"%s\" for command type %s", &buf[1],
LOG_STR_ARG(EzoCommandTypeStrings::get_log_str(to_run->command_type, EzoCommandTypeStrings::LAST_INDEX)));
if (buf[0] == 1) {
std::string payload = reinterpret_cast<char *>(&buf[1]);
@@ -168,7 +169,9 @@ void EZOSensor::add_command_(const char *command, EzoCommandType command_type, u
void EZOSensor::set_calibration_point_(EzoCalibrationType type, float value) {
// max 21: "Cal,"(4) + type(4) + ","(1) + float(11) + null; use 24 for safety
char payload[24];
snprintf(payload, sizeof(payload), "Cal,%s,%0.2f", EZO_CALIBRATION_TYPE_STRINGS[type], value);
ESPHOME_snprintf_P(payload, sizeof(payload), ESPHOME_PSTR("Cal,%s,%0.2f"),
LOG_STR_ARG(EzoCalibrationTypeStrings::get_log_str(type, EzoCalibrationTypeStrings::LAST_INDEX)),
value);
this->add_command_(payload, EzoCommandType::EZO_CALIBRATION, 900);
}
@@ -176,7 +179,7 @@ void EZOSensor::set_address(uint8_t address) {
if (address > 0 && address < 128) {
// max 8: "I2C,"(4) + uint8(3) + null
char payload[8];
snprintf(payload, sizeof(payload), "I2C,%u", address);
ESPHOME_snprintf_P(payload, sizeof(payload), ESPHOME_PSTR("I2C,%u"), address);
this->new_address_ = address;
this->add_command_(payload, EzoCommandType::EZO_I2C);
} else {
@@ -197,7 +200,7 @@ void EZOSensor::get_t() { this->add_command_("T,?", EzoCommandType::EZO_T); }
void EZOSensor::set_t(float value) {
// max 14 bytes: "T,"(2) + float with "%0.2f" (up to 11 chars) + null(1); use 16 for alignment
char payload[16];
snprintf(payload, sizeof(payload), "T,%0.2f", value);
ESPHOME_snprintf_P(payload, sizeof(payload), ESPHOME_PSTR("T,%0.2f"), value);
this->add_command_(payload, EzoCommandType::EZO_T);
}
@@ -220,7 +223,7 @@ void EZOSensor::set_calibration_point_high(float value) {
void EZOSensor::set_calibration_generic(float value) {
// exact 16 bytes: "Cal," (4) + float with "%0.2f" (up to 11 chars, e.g. "-9999999.99") + null (1) = 16
char payload[16];
snprintf(payload, sizeof(payload), "Cal,%0.2f", value);
ESPHOME_snprintf_P(payload, sizeof(payload), ESPHOME_PSTR("Cal,%0.2f"), value);
this->add_command_(payload, EzoCommandType::EZO_CALIBRATION, 900);
}
+1 -12
View File
@@ -34,10 +34,7 @@ void FT63X6Touchscreen::setup() {
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_ANY_EDGE);
}
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->hard_reset_();
}
pulse_reset(this->reset_pin_, {.low_ms = 10});
// Get touch resolution
if (this->x_raw_max_ == this->x_raw_min_) {
@@ -57,14 +54,6 @@ void FT63X6Touchscreen::setup() {
this->write_byte(FT6X36_ADDR_TOUCHRATE_ACTIVE, 0x0E);
}
void FT63X6Touchscreen::hard_reset_() {
if (this->reset_pin_ != nullptr) {
this->reset_pin_->digital_write(false);
delay(10);
this->reset_pin_->digital_write(true);
}
}
void FT63X6Touchscreen::dump_config() {
ESP_LOGCONFIG(TAG, "FT63X6 Touchscreen:");
LOG_I2C_DEVICE(this);
-1
View File
@@ -27,7 +27,6 @@ class FT63X6Touchscreen final : public Touchscreen, public i2c::I2CDevice {
void set_threshold(uint8_t threshold) { this->threshold_ = threshold; }
protected:
void hard_reset_();
void update_touches() override;
InternalGPIOPin *interrupt_pin_{nullptr};
@@ -83,13 +83,7 @@ void GSL3670Touchscreen::update_touches() {
void GSL3670Touchscreen::clear_reg_() {
ESP_LOGD(TAG, "clear_reg");
// GPIO reset pulse
if (this->reset_pin_ != nullptr) {
this->reset_pin_->digital_write(false);
delay(1);
this->reset_pin_->digital_write(true);
delay(5);
}
pulse_reset(this->reset_pin_, {.low_ms = 1, .high_after_ms = 5});
this->write_reg8_(0x88, 0x01);
// delay(5);
@@ -106,12 +100,7 @@ void GSL3670Touchscreen::clear_reg_() {
void GSL3670Touchscreen::reset_() {
ESP_LOGD(TAG, "reset");
if (this->reset_pin_ != nullptr) {
this->reset_pin_->digital_write(false);
delay(1);
this->reset_pin_->digital_write(true);
delay(5);
}
pulse_reset(this->reset_pin_, {.low_ms = 1, .high_after_ms = 5});
this->write_reg8_(0xe4, 0x04);
+9 -17
View File
@@ -3,7 +3,11 @@ import logging
from esphome import automation
import esphome.codegen as cg
from esphome.components import climate, logger, uart
from esphome.components.climate import ClimateMode, ClimatePreset, ClimateSwingMode
from esphome.components.climate import (
CLIMATE_MODES,
CLIMATE_PRESETS,
CLIMATE_SWING_MODES,
)
from esphome.components.logger import DOMAIN as LOGGER_DOMAIN
from esphome.components.wifi import DOMAIN as WIFI_DOMAIN
import esphome.config_validation as cv
@@ -77,31 +81,19 @@ AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS = AirflowHorizontalDirection.options(
)
SUPPORTED_SWING_MODES_OPTIONS = {
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
"VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL,
"HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
"BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH,
k: CLIMATE_SWING_MODES[k] for k in ("OFF", "VERTICAL", "HORIZONTAL", "BOTH")
}
SUPPORTED_CLIMATE_MODES_OPTIONS = {
"OFF": ClimateMode.CLIMATE_MODE_OFF, # always available
"HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL, # always available
"COOL": ClimateMode.CLIMATE_MODE_COOL,
"HEAT": ClimateMode.CLIMATE_MODE_HEAT,
"DRY": ClimateMode.CLIMATE_MODE_DRY,
"FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
k: CLIMATE_MODES[k] for k in ("OFF", "HEAT_COOL", "COOL", "HEAT", "DRY", "FAN_ONLY")
}
SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = {
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
"COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT,
k: CLIMATE_PRESETS[k] for k in ("AWAY", "BOOST", "COMFORT")
}
SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
k: CLIMATE_PRESETS[k] for k in ("AWAY", "BOOST", "SLEEP")
}
HonControlMethod = haier_ns.enum("HonControlMethod", True)
+14 -24
View File
@@ -6,6 +6,7 @@
#include "esphome/components/wifi/wifi_component.h"
#endif
#include "haier_base.h"
#include "esphome/core/progmem.h"
using namespace esphome::climate;
using namespace esphome::uart;
@@ -19,28 +20,16 @@ constexpr size_t PROTOCOL_INITIALIZATION_INTERVAL = 10000;
constexpr size_t DEFAULT_MESSAGES_INTERVAL_MS = 2000;
constexpr size_t CONTROL_MESSAGES_INTERVAL_MS = 400;
const char *HaierClimateBase::phase_to_string_(ProtocolPhases phase) {
static const char *phase_names[] = {
"SENDING_INIT_1",
"SENDING_INIT_2",
"SENDING_FIRST_STATUS_REQUEST",
"SENDING_FIRST_ALARM_STATUS_REQUEST",
"IDLE",
"SENDING_STATUS_REQUEST",
"SENDING_UPDATE_SIGNAL_REQUEST",
"SENDING_SIGNAL_LEVEL",
"SENDING_CONTROL",
"SENDING_ACTION_COMMAND",
"SENDING_ALARM_STATUS_REQUEST",
"UNKNOWN" // Should be the last!
};
static_assert(
(sizeof(phase_names) / sizeof(char *)) == (((int) ProtocolPhases::NUM_PROTOCOL_PHASES) + 1),
"Wrong phase_names array size. Please, make sure that this array is aligned with the enum ProtocolPhases");
int phase_index = (int) phase;
if ((phase_index > (int) ProtocolPhases::NUM_PROTOCOL_PHASES) || (phase_index < 0))
phase_index = (int) ProtocolPhases::NUM_PROTOCOL_PHASES;
return phase_names[phase_index];
PROGMEM_STRING_TABLE(ProtocolPhaseStrings, "SENDING_INIT_1", "SENDING_INIT_2", "SENDING_FIRST_STATUS_REQUEST",
"SENDING_FIRST_ALARM_STATUS_REQUEST", "IDLE", "SENDING_STATUS_REQUEST",
"SENDING_UPDATE_SIGNAL_REQUEST", "SENDING_SIGNAL_LEVEL", "SENDING_CONTROL",
"SENDING_ACTION_COMMAND", "SENDING_ALARM_STATUS_REQUEST", "UNKNOWN");
const LogString *HaierClimateBase::phase_to_string_(ProtocolPhases phase) {
static_assert(ProtocolPhaseStrings::COUNT == ((int) ProtocolPhases::NUM_PROTOCOL_PHASES) + 1,
"ProtocolPhaseStrings must match the enum ProtocolPhases, with UNKNOWN last");
// UNKNOWN (-1) wraps past the table and reads as the last entry, "UNKNOWN"
return ProtocolPhaseStrings::get_log_str(static_cast<uint8_t>(phase), ProtocolPhaseStrings::LAST_INDEX);
}
bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::steady_clock::time_point tpoint,
@@ -71,7 +60,8 @@ HaierClimateBase::~HaierClimateBase() {}
void HaierClimateBase::set_phase(ProtocolPhases phase) {
if (this->protocol_phase_ != phase) {
ESP_LOGV(TAG, "Phase transition: %s => %s", phase_to_string_(this->protocol_phase_), phase_to_string_(phase));
ESP_LOGV(TAG, "Phase transition: %s => %s", LOG_STR_ARG(phase_to_string_(this->protocol_phase_)),
LOG_STR_ARG(phase_to_string_(phase)));
this->protocol_phase_ = phase;
}
}
@@ -225,7 +215,7 @@ haier_protocol::HandlerError HaierClimateBase::report_network_status_answer_hand
haier_protocol::HandlerError HaierClimateBase::timeout_default_handler_(haier_protocol::FrameType request_type) {
ESP_LOGW(TAG, "Answer timeout for command %02X, phase %s", (uint8_t) request_type,
phase_to_string_(this->protocol_phase_));
LOG_STR_ARG(phase_to_string_(this->protocol_phase_)));
if (this->protocol_phase_ > ProtocolPhases::IDLE) {
this->set_phase(ProtocolPhases::IDLE);
} else {
+1 -1
View File
@@ -95,7 +95,7 @@ class HaierClimateBase : public esphome::Component,
SENDING_ALARM_STATUS_REQUEST,
NUM_PROTOCOL_PHASES
};
const char *phase_to_string_(ProtocolPhases phase);
const LogString *phase_to_string_(ProtocolPhases phase);
virtual void set_handlers() = 0;
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming)
+1 -1
View File
@@ -564,7 +564,7 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) {
default:
// Shouldn't get here
ESP_LOGE(TAG, "Wrong protocol handler state: %s (%d), resetting communication",
phase_to_string_(this->protocol_phase_), (int) this->protocol_phase_);
LOG_STR_ARG(phase_to_string_(this->protocol_phase_)), (int) this->protocol_phase_);
this->set_phase(ProtocolPhases::SENDING_INIT_1);
break;
}
+6 -5
View File
@@ -9,12 +9,9 @@ from ..climate import (
CONF_HORIZONTAL_AIRFLOW,
CONF_VERTICAL_AIRFLOW,
HonClimate,
haier_ns,
)
CODEOWNERS = ["@paveldn"]
VerticalAirflowSelect = haier_ns.class_("VerticalAirflowSelect", select.Select)
HorizontalAirflowSelect = haier_ns.class_("HorizontalAirflowSelect", select.Select)
# Same order as VERTICAL_SWING_MODES_ORDER and HORIZONTAL_SWING_MODES_ORDER in hon_climate.h
AIRFLOW_VERTICAL_DIRECTION_OPTIONS = [
@@ -39,12 +36,12 @@ CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_HAIER_ID): cv.use_id(HonClimate),
cv.Optional(CONF_VERTICAL_AIRFLOW): select.select_schema(
VerticalAirflowSelect,
select.ApplySelect,
icon="mdi:arrow-expand-vertical",
entity_category=ENTITY_CATEGORY_CONFIG,
),
cv.Optional(CONF_HORIZONTAL_AIRFLOW): select.select_schema(
HorizontalAirflowSelect,
select.ApplySelect,
icon="mdi:arrow-expand-horizontal",
entity_category=ENTITY_CATEGORY_CONFIG,
),
@@ -59,9 +56,13 @@ async def to_code(config: ConfigType) -> None:
CONF_VERTICAL_AIRFLOW,
hub.set_vertical_airflow_select,
options=AIRFLOW_VERTICAL_DIRECTION_OPTIONS,
apply="set_vertical_airflow("
"haier::airflow_order_at(haier::VERTICAL_SWING_MODES_ORDER, index))",
)
await selects(
CONF_HORIZONTAL_AIRFLOW,
hub.set_horizontal_airflow_select,
options=AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS,
apply="set_horizontal_airflow("
"haier::airflow_order_at(haier::HORIZONTAL_SWING_MODES_ORDER, index))",
)
@@ -1,15 +0,0 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../hon_climate.h"
namespace esphome::haier {
class HorizontalAirflowSelect : public select::Select, public Parented<HonClimate> {
protected:
void control(size_t index) override {
this->parent_->set_horizontal_airflow(airflow_order_at(HORIZONTAL_SWING_MODES_ORDER, index));
}
};
} // namespace esphome::haier
@@ -1,15 +0,0 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../hon_climate.h"
namespace esphome::haier {
class VerticalAirflowSelect : public select::Select, public Parented<HonClimate> {
protected:
void control(size_t index) override {
this->parent_->set_vertical_airflow(airflow_order_at(VERTICAL_SWING_MODES_ORDER, index));
}
};
} // namespace esphome::haier
@@ -93,7 +93,7 @@ haier_protocol::HandlerError Smartair2Climate::messages_timeout_handler_with_cyc
if (this->protocol_phase_ >= ProtocolPhases::IDLE)
return HaierClimateBase::timeout_default_handler_(message_type);
ESP_LOGI(TAG, "Answer timeout for command %02X, phase %s", (uint8_t) message_type,
phase_to_string_(this->protocol_phase_));
LOG_STR_ARG(phase_to_string_(this->protocol_phase_)));
ProtocolPhases new_phase = (ProtocolPhases) ((int) this->protocol_phase_ + 1);
if (new_phase >= ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST)
new_phase = ProtocolPhases::SENDING_INIT_1;
@@ -217,7 +217,7 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now)
default:
// Shouldn't get here
ESP_LOGE(TAG, "Wrong protocol handler state: %s (%d), resetting communication",
phase_to_string_(this->protocol_phase_), (int) this->protocol_phase_);
LOG_STR_ARG(phase_to_string_(this->protocol_phase_)), (int) this->protocol_phase_);
this->set_phase(ProtocolPhases::SENDING_INIT_1);
break;
}
+3 -6
View File
@@ -1,6 +1,7 @@
#include "htu21d.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::htu21d {
@@ -112,11 +113,7 @@ void HTU21DComponent::set_heater(bool status) {
this->status_set_warning();
return;
}
if (status) {
raw_heater |= (1 << HTU21D_REG_HTRE_BIT);
} else {
raw_heater &= ~(1 << HTU21D_REG_HTRE_BIT);
}
assign_bits(raw_heater, 1 << HTU21D_REG_HTRE_BIT, status);
if (this->write_register(HTU21D_WRITERHT_REG_CMD, &raw_heater, 1) != i2c::ERROR_OK) {
this->status_set_warning();
return;
+1 -4
View File
@@ -40,10 +40,7 @@ RG_MODELS = {
# 1.200 - https://rainsensors.com/wp-content/uploads/sites/3/2022/03/2022.02.17-rev-1.200-rg-9_instructions.pdf
}
RG15_RESOLUTION = {
"low": RG15Resolution.FORCE_LOW,
"high": RG15Resolution.FORCE_HIGH,
}
RG15_RESOLUTION = RG15Resolution.options("low", "high", prefix="FORCE_")
SUPPORTED_OPTIONS = {
CONF_ACC: ["RG_15"],
@@ -270,21 +270,23 @@ void I2SAudioMicrophone::mic_task(void *params) {
{ // Ensures the samples vector is freed when the task stops
const size_t bytes_to_read = this_microphone->audio_stream_info_.ms_to_bytes(READ_DURATION_MS);
// Copied so the format cannot change while the task runs
const audio::AudioStreamInfo stream_info = this_microphone->audio_stream_info_;
const size_t bytes_to_read = stream_info.ms_to_bytes(READ_DURATION_MS);
std::vector<uint8_t> samples;
samples.reserve(bytes_to_read);
xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
while (!(xEventGroupGetBits(this_microphone->event_group_) & MicrophoneEventGroupBits::COMMAND_STOP)) {
if (this_microphone->data_callbacks_.size() > 0) {
if (this_microphone->has_data_callbacks_()) {
samples.resize(bytes_to_read);
size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * READ_DURATION_MS);
samples.resize(bytes_read);
if (this_microphone->correct_dc_offset_) {
this_microphone->fix_dc_offset_(samples);
}
this_microphone->data_callbacks_.call(samples);
this_microphone->publish_audio_(samples, stream_info);
} else {
vTaskDelay(pdMS_TO_TICKS(READ_DURATION_MS));
}
+1 -6
View File
@@ -19,12 +19,7 @@ void ICNT86Touchscreen::setup() {
}
// Perform reset if necessary
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(false);
delay(10);
this->reset_pin_->digital_write(true);
}
pulse_reset(this->reset_pin_, {.low_ms = 10});
if (this->x_raw_max_ == this->x_raw_min_) {
this->x_raw_max_ = this->display_->get_native_width();
+4 -3
View File
@@ -1,8 +1,7 @@
"""Shared base for the infrared and radio_frequency entity components."""
import esphome.codegen as cg
from esphome.components import remote_base
from esphome.components.api import DOMAIN as API_DOMAIN
from esphome.const import CONF_API
from esphome.core import CORE
from esphome.core.entity_helpers import queue_entity_register
from esphome.types import ConfigType
@@ -24,8 +23,10 @@ async def attach_transmitter(var: cg.MockObj, config: ConfigType, key: str) -> N
does. Without this call anywhere in the build a request is answered as
soon as the frame is handed over.
"""
from esphome.components import remote_base
await remote_base.register_transmittable(var, config, key)
if API_DOMAIN in CORE.config:
if CONF_API in CORE.config:
cg.add_define("USE_IR_RF_TRANSMIT_COMPLETE")
@@ -2,11 +2,20 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
namespace esphome::kamstrup_kmp {
ESPHOME_LOG_TAG(TAG, "kamstrup_kmp");
// KMP units indexed by unit code; the empty first entry doubles as the fallback for unknown codes
PROGMEM_STRING_TABLE(KmpUnits, "", "Wh", "kWh", "MWh", "GWh", "J", "kJ", "MJ", "GJ", "Cal", "kCal", "Mcal", "Gcal",
"varh", "kvarh", "Mvarh", "Gvarh", "VAh", "kVAh", "MVAh", "GVAh", "kW", "kW", "MW", "GW", "kvar",
"kvar", "Mvar", "Gvar", "VA", "kVA", "MVA", "GVA", "V", "A", "kV", "kA", "C", "K", "l", "m3",
"l/h", "m3/h", "m3xC", "ton", "ton/h", "h", "hh:mm:ss", "yy:mm:dd", "yyyy:mm:dd", "mm:dd", "",
"bar", "RTC", "ASCII", "m3 x 10", "ton x 10", "GJ x 10", "minutes", "Bitfield", "s", "ms", "days",
"RTC-Q", "Datetime");
void KamstrupKMPComponent::dump_config() {
ESP_LOGCONFIG(TAG, "kamstrup_kmp:");
LOG_UPDATE_INTERVAL(this);
@@ -237,8 +246,6 @@ void KamstrupKMPComponent::parse_command_message_(uint16_t command, const uint8_
}
void KamstrupKMPComponent::set_sensor_value_(uint16_t command, float value, uint8_t unit_idx) {
const char *unit = unit_idx < sizeof(UNITS) / sizeof(UNITS[0]) ? UNITS[unit_idx] : "";
// Standard sensors
if (command == CMD_HEAT_ENERGY && this->heat_energy_sensor_ != nullptr) {
this->heat_energy_sensor_->publish_state(value);
@@ -263,7 +270,8 @@ void KamstrupKMPComponent::set_sensor_value_(uint16_t command, float value, uint
}
}
ESP_LOGD(TAG, "Received value for command 0x%04X: %.3f [%s]", command, value, unit);
ESP_LOGD(TAG, "Received value for command 0x%04X: %.3f [%s]", command, value,
LOG_STR_ARG(KmpUnits::get_log_str(unit_idx, 0)));
}
} // namespace esphome::kamstrup_kmp
@@ -63,16 +63,6 @@ static const uint16_t CMD_TEMP_DIFF = 0x0059;
static const uint16_t CMD_FLOW = 0x004A;
static const uint16_t CMD_VOLUME = 0x0044;
// KMP units
static const char *const UNITS[] = {
"", "Wh", "kWh", "MWh", "GWh", "J", "kJ", "MJ", "GJ", "Cal",
"kCal", "Mcal", "Gcal", "varh", "kvarh", "Mvarh", "Gvarh", "VAh", "kVAh", "MVAh",
"GVAh", "kW", "kW", "MW", "GW", "kvar", "kvar", "Mvar", "Gvar", "VA",
"kVA", "MVA", "GVA", "V", "A", "kV", "kA", "C", "K", "l",
"m3", "l/h", "m3/h", "m3xC", "ton", "ton/h", "h", "hh:mm:ss", "yy:mm:dd", "yyyy:mm:dd",
"mm:dd", "", "bar", "RTC", "ASCII", "m3 x 10", "ton x 10", "GJ x 10", "minutes", "Bitfield",
"s", "ms", "days", "RTC-Q", "Datetime"};
class KamstrupKMPComponent final : public PollingComponent, public uart::UARTDevice {
public:
void set_heat_energy_sensor(sensor::Sensor *sensor) { this->heat_energy_sensor_ = sensor; }
+18 -10
View File
@@ -13,11 +13,7 @@ from esphome.const import (
)
from esphome.types import ConfigType
from .. import CONF_LD2410_ID, LD2410Component, ld2410_ns
BaudRateSelect = ld2410_ns.class_("BaudRateSelect", select.Select)
DistanceResolutionSelect = ld2410_ns.class_("DistanceResolutionSelect", select.Select)
LightOutControlSelect = ld2410_ns.class_("LightOutControlSelect", select.Select)
from .. import CONF_LD2410_ID, LD2410Component
CONF_DISTANCE_RESOLUTION = "distance_resolution"
CONF_LIGHT_FUNCTION = "light_function"
@@ -27,22 +23,22 @@ CONFIG_SCHEMA = {
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
cv.Optional(CONF_DISTANCE_RESOLUTION): select.select_schema(
DistanceResolutionSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_RULER,
),
cv.Optional(CONF_LIGHT_FUNCTION): select.select_schema(
LightOutControlSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_LIGHTBULB,
),
cv.Optional(CONF_OUT_PIN_LEVEL): select.select_schema(
LightOutControlSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_SCALE,
),
cv.Optional(CONF_BAUD_RATE): select.select_schema(
BaudRateSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_THERMOMETER,
),
@@ -57,18 +53,30 @@ async def to_code(config: ConfigType) -> None:
CONF_DISTANCE_RESOLUTION,
hub.set_distance_resolution_select,
options=["0.2m", "0.75m"],
apply=(
"set_distance_resolution(static_cast<ld2410::DistanceResolution>("
"progmem_read_byte(&ld2410::DISTANCE_RESOLUTIONS[index])))"
),
publish="before",
)
light = {"apply": "set_light_out_control()", "publish": "before"}
await selects(
CONF_OUT_PIN_LEVEL, hub.set_out_pin_level_select, options=["low", "high"]
CONF_OUT_PIN_LEVEL,
hub.set_out_pin_level_select,
options=["low", "high"],
**light,
)
await selects(
CONF_LIGHT_FUNCTION,
hub.set_light_function_select,
options=["off", "below", "above"],
**light,
)
await selects(
CONF_BAUD_RATE,
hub.set_baud_rate_select,
apply="set_baud_rate(static_cast<ld2410::BaudRate>(index + 1))",
publish="before",
options=[
"9600",
"19200",
@@ -1,10 +0,0 @@
#include "baud_rate_select.h"
namespace esphome::ld2410 {
void BaudRateSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_baud_rate(static_cast<BaudRate>(index + 1));
}
} // namespace esphome::ld2410
@@ -1,16 +0,0 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../ld2410.h"
namespace esphome::ld2410 {
class BaudRateSelect final : public select::Select, public Parented<LD2410Component> {
public:
BaudRateSelect() = default;
protected:
void control(size_t index) override;
};
} // namespace esphome::ld2410
@@ -1,11 +0,0 @@
#include "distance_resolution_select.h"
namespace esphome::ld2410 {
void DistanceResolutionSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_distance_resolution(
static_cast<DistanceResolution>(progmem_read_byte(&DISTANCE_RESOLUTIONS[index])));
}
} // namespace esphome::ld2410
@@ -1,16 +0,0 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../ld2410.h"
namespace esphome::ld2410 {
class DistanceResolutionSelect final : public select::Select, public Parented<LD2410Component> {
public:
DistanceResolutionSelect() = default;
protected:
void control(size_t index) override;
};
} // namespace esphome::ld2410
@@ -1,10 +0,0 @@
#include "light_out_control_select.h"
namespace esphome::ld2410 {
void LightOutControlSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_light_out_control();
}
} // namespace esphome::ld2410
@@ -1,16 +0,0 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../ld2410.h"
namespace esphome::ld2410 {
class LightOutControlSelect final : public select::Select, public Parented<LD2410Component> {
public:
LightOutControlSelect() = default;
protected:
void control(size_t index) override;
};
} // namespace esphome::ld2410
+13 -8
View File
@@ -13,11 +13,7 @@ from esphome.const import (
)
from esphome.types import ConfigType
from .. import CONF_LD2412_ID, LD2412_ns, LD2412Component
BaudRateSelect = LD2412_ns.class_("BaudRateSelect", select.Select)
DistanceResolutionSelect = LD2412_ns.class_("DistanceResolutionSelect", select.Select)
LightOutControlSelect = LD2412_ns.class_("LightOutControlSelect", select.Select)
from .. import CONF_LD2412_ID, LD2412Component
CONF_DISTANCE_RESOLUTION = "distance_resolution"
CONF_LIGHT_FUNCTION = "light_function"
@@ -27,17 +23,17 @@ CONFIG_SCHEMA = {
cv.GenerateID(CONF_ID): cv.declare_id(cg.EntityBase),
cv.GenerateID(CONF_LD2412_ID): cv.use_id(LD2412Component),
cv.Optional(CONF_BAUD_RATE): select.select_schema(
BaudRateSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_THERMOMETER,
),
cv.Optional(CONF_DISTANCE_RESOLUTION): select.select_schema(
DistanceResolutionSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_RULER,
),
cv.Optional(CONF_LIGHT_FUNCTION): select.select_schema(
LightOutControlSelect,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_LIGHTBULB,
),
@@ -66,16 +62,25 @@ async def to_code(config: ConfigType) -> None:
"256000",
"460800",
],
apply="set_baud_rate(static_cast<ld2412::BaudRate>(index + 1))",
publish="before",
)
await selects(
CONF_DISTANCE_RESOLUTION,
hub.set_distance_resolution_select,
options=["0.2m", "0.5m", "0.75m"],
apply=(
"set_distance_resolution(static_cast<ld2412::DistanceResolution>("
"progmem_read_byte(&ld2412::DISTANCE_RESOLUTIONS[index])))"
),
publish="before",
)
await selects(
CONF_LIGHT_FUNCTION,
hub.set_light_function_select,
options=["off", "below", "above"],
apply="set_light_out_control()",
publish="before",
)
# The pin level travels in the basic configuration command, not the light control one
await selects(
@@ -1,10 +0,0 @@
#include "baud_rate_select.h"
namespace esphome::ld2412 {
void BaudRateSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_baud_rate(static_cast<BaudRate>(index + 1));
}
} // namespace esphome::ld2412
@@ -1,17 +0,0 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../ld2412.h"
namespace esphome::ld2412 {
class BaudRateSelect final : public select::Select, public Parented<LD2412Component> {
public:
// User provided, not "= default": `new(p) BaudRateSelect()` would zero-fill .bss that is already zero.
BaudRateSelect() {}
protected:
void control(size_t index) override;
};
} // namespace esphome::ld2412
@@ -1,11 +0,0 @@
#include "distance_resolution_select.h"
namespace esphome::ld2412 {
void DistanceResolutionSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_distance_resolution(
static_cast<DistanceResolution>(progmem_read_byte(&DISTANCE_RESOLUTIONS[index])));
}
} // namespace esphome::ld2412
@@ -1,17 +0,0 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../ld2412.h"
namespace esphome::ld2412 {
class DistanceResolutionSelect final : public select::Select, public Parented<LD2412Component> {
public:
// User provided, not "= default": `new(p) DistanceResolutionSelect()` would zero-fill .bss that is already zero.
DistanceResolutionSelect() {}
protected:
void control(size_t index) override;
};
} // namespace esphome::ld2412
@@ -1,10 +0,0 @@
#include "light_out_control_select.h"
namespace esphome::ld2412 {
void LightOutControlSelect::control(size_t index) {
this->publish_state(index);
this->parent_->set_light_out_control();
}
} // namespace esphome::ld2412
@@ -1,17 +0,0 @@
#pragma once
#include "esphome/components/select/select.h"
#include "../ld2412.h"
namespace esphome::ld2412 {
class LightOutControlSelect final : public select::Select, public Parented<LD2412Component> {
public:
// User provided, not "= default": `new(p) LightOutControlSelect()` would zero-fill .bss that is already zero.
LightOutControlSelect() {}
protected:
void control(size_t index) override;
};
} // namespace esphome::ld2412
+26 -6
View File
@@ -138,6 +138,11 @@ static uint8_t calc_checksum(const void *data, size_t size) {
return xor_bytes(static_cast<const uint8_t *>(data), size);
}
// Simple mode needs simple reporting from the module; Normal and Calibrate read energy frames
static uint16_t system_mode_for(uint8_t mode) {
return mode == OP_SIMPLE_MODE ? CMD_SYSTEM_MODE_SIMPLE : CMD_SYSTEM_MODE_ENERGY;
}
static int32_t get_firmware_int(const char *version_string) {
// Convert "v1.5.4" -> 154 by skipping 'v' and '.', accumulating digits
const char *p = (*version_string == 'v') ? version_string + 1 : version_string;
@@ -207,13 +212,10 @@ void LD2420Component::setup() {
memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
if (ld2420::get_firmware_int(this->firmware_ver_) < CALIBRATE_VERSION_MIN) {
this->set_operating_mode(OP_SIMPLE_MODE);
this->set_mode_(CMD_SYSTEM_MODE_SIMPLE);
ESP_LOGW(TAG, "Firmware version %s and older supports Simple Mode only", this->firmware_ver_);
} else {
this->set_operating_mode(OP_NORMAL_MODE);
this->set_mode_(CMD_SYSTEM_MODE_ENERGY);
}
this->set_operating_mode(OP_NORMAL_MODE); // older firmware is held in Simple
this->set_mode_(system_mode_for(this->current_operating_mode));
#ifdef USE_NUMBER
this->init_gate_config_numbers();
#endif
@@ -244,7 +246,9 @@ void LD2420Component::apply_config_action() {
#endif
this->set_system_mode(this->system_mode_);
this->set_config_mode(false); // Disable config mode to save new values in LD2420 nvm
this->set_operating_mode(OP_NORMAL_MODE);
// Applying the calibrated thresholds ends calibration; Normal and Simple stay as they are
if (this->current_operating_mode == OP_CALIBRATE_MODE)
this->set_operating_mode(OP_NORMAL_MODE);
}
void LD2420Component::factory_reset_action() {
@@ -356,6 +360,22 @@ void LD2420Component::set_operating_mode(const char *state) {
}
}
void LD2420Component::change_operating_mode(OpMode mode) {
this->set_operating_mode(mode);
const uint16_t system_mode = system_mode_for(this->current_operating_mode);
if (system_mode == this->get_mode_())
return;
if (this->set_config_mode(true) != LD2420_ERROR_TIMEOUT) {
this->set_system_mode(system_mode);
this->set_config_mode(false);
}
if (this->get_mode_() != system_mode) {
// The module kept its reporting mode; show the mode that matches it
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->set_operating_mode(this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE ? OP_SIMPLE_MODE : OP_NORMAL_MODE);
}
}
void LD2420Component::set_operating_mode(OpMode mode) {
// Older firmware only supports simple mode
if (ld2420::get_firmware_int(this->firmware_ver_) < CALIBRATE_VERSION_MIN) {
+1
View File
@@ -119,6 +119,7 @@ class LD2420Component final : public Component, public uart::UARTDevice {
void handle_cmd_error(uint16_t error);
void set_operating_mode(const char *state);
void set_operating_mode(OpMode mode);
void change_operating_mode(OpMode mode);
void auto_calibrate_sensitivity();
void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number);
uint8_t set_config_mode(bool enable);
+6 -4
View File
@@ -4,7 +4,7 @@ import esphome.config_validation as cv
from esphome.const import ENTITY_CATEGORY_CONFIG
from esphome.types import ConfigType
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
from .. import CONF_LD2420_ID, LD2420Component
CONF_OPERATING_MODE = "operating_mode"
CONF_SELECTS = [
@@ -13,12 +13,11 @@ CONF_SELECTS = [
"Simple",
]
LD2420Select = ld2420_ns.class_("LD2420Select", select.Select, cg.Component)
CONFIG_SCHEMA = {
cv.GenerateID(CONF_LD2420_ID): cv.use_id(LD2420Component),
cv.Required(CONF_OPERATING_MODE): select.select_schema(
LD2420Select,
select.ApplySelect,
entity_category=ENTITY_CATEGORY_CONFIG,
),
}
@@ -28,5 +27,8 @@ async def to_code(config: ConfigType) -> None:
hub = await cg.get_variable(config[CONF_LD2420_ID])
selects = select.sub_selects(config, parent=hub)
await selects(
CONF_OPERATING_MODE, hub.set_operating_mode_select, options=CONF_SELECTS
CONF_OPERATING_MODE,
hub.set_operating_mode_select,
options=CONF_SELECTS,
apply="change_operating_mode(static_cast<ld2420::OpMode>(index + 1))",
)
@@ -1,14 +0,0 @@
#include "operating_mode_select.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome::ld2420 {
ESPHOME_LOG_TAG(TAG, "ld2420.select");
void LD2420Select::control(size_t index) {
this->publish_state(index);
this->parent_->set_operating_mode(static_cast<OpMode>(index + 1));
}
} // namespace esphome::ld2420
@@ -1,16 +0,0 @@
#pragma once
#include "../ld2420.h"
#include "esphome/components/select/select.h"
namespace esphome::ld2420 {
class LD2420Select final : public Component, public select::Select, public Parented<LD2420Component> {
public:
LD2420Select() = default;
protected:
void control(size_t index) override;
};
} // namespace esphome::ld2420
Loaded 100 of 306 files, more files were not shown because too many files have changed in this diff. Show more