Skip to content

Add ESP-NOW communication for multi-device relay control#160

Open
jakub-k-dev wants to merge 40 commits intoedwardtfn:mainfrom
jakub-k-dev:feature/espnow-communication
Open

Add ESP-NOW communication for multi-device relay control#160
jakub-k-dev wants to merge 40 commits intoedwardtfn:mainfrom
jakub-k-dev:feature/espnow-communication

Conversation

@jakub-k-dev
Copy link
Contributor

@jakub-k-dev jakub-k-dev commented Feb 7, 2026

Switches can toggle relays on other TX Ultimate Easy devices over ESP-NOW (no Home Assistant).
Per-button target MAC and relay via YAML substitutions; omit for local-only.
MAC shown in HA via “ESP-NOW MAC Address” text sensor; send/receive toggles in HA.
Docs in docs/espnow.md, linked from README.

Summary by CodeRabbit

  • New Features

    • ESP‑NOW peer-to-peer relay control with per-button target MAC/relay mapping, local fallback, enable/disable controls, queued sends, and support for up to four relays.
  • Documentation

    • Added ESP‑NOW setup guide, README updates (including new video/resource), and ESP‑NOW docs.
  • Examples

    • New ESP‑NOW example YAML demonstrating multi-device mappings and usage.
  • Diagnostics

    • Added ESP‑NOW MAC address sensor and enhanced logging/validation.
  • Tests / CI

    • Added ESP‑NOW example builds to validation workflow (Arduino & ESP‑IDF).
  • Style

    • Minor formatting/comment cleanups in button/relay configs.

- Send ESP-NOW only on single click (align with relay behavior)
- Validate on_receive payload size >= 2 before reading command/relay
- Use boolean initial_value for espnow_send/receive_enabled globals
- Replace ESPHome/ESP_NOW_README.md with short docs/espnow.md
- Add ESP-NOW to Key Features and Optional Features in README
- Document: omit substitutions for buttons that stay local-only
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 7, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an ESP‑NOW peer‑to‑peer relay control package and example: new espnow_component receiver, queued espnow_send(button) script, per‑button MAC/relay mappings, send/receive enable switches, example/test YAMLs, docs/README updates, minor button/relay comment tweaks, and new CI jobs for ESP‑NOW builds.

Changes

Cohort / File(s) Summary
Core ESP‑NOW implementation
ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml
New espnow component with on_receive validation/logging and gang_count‑gated relay toggle logic; queued espnow_send(button) script; per‑button MAC/relay resolution; sw_espnow_send_enabled / sw_espnow_receive_enabled template switches; wifi MAC text sensor; config validation and dump_config output.
Button & relay glue
ESPHome/TX-Ultimate-Easy-ESPHome_hw_buttons.yaml, ESPHome/TX-Ultimate-Easy-ESPHome_hw_relays.yaml
Minor formatting/comment tweaks; button actions trigger espnow_send when sending enabled; relay handler comments note ESP‑NOW interactions.
Examples & tests
TX-Ultimate-Easy-ESPHome_espnow_example.yaml, .test/esphome_ard_espnow_example.yaml, .test/esphome_idf_espnow_example.yaml
New example and CI test fixtures demonstrating substitutions (device_format, gang_count), per‑button ESP‑NOW MAC/relay mapping, package includes, and external component wiring.
Docs & README
docs/espnow.md, README.md
Adds ESP‑NOW documentation, README entries and usage guidance, MAC discovery notes, topology example, and links to ESP‑NOW resources.
CI / Workflow
.github/workflows/validate_esphome.yml
Adds four ESP‑NOW example build jobs (Arduino/IDF × latest/dev), updates summary job dependencies and aggregated totals/success calculations.

Sequence Diagram(s)

sequenceDiagram
    participant Button as Button
    participant Script as espnow_send
    participant Network as ESP-NOW Network
    participant Receiver as Remote Device
    participant Relay as Relay Control

    Button->>Script: trigger (if sw_espnow_send_enabled)
    Script->>Script: resolve target MAC & relay mapping
    Script->>Script: validate MAC (non-empty, not all zeros) and clamp relay
    Script->>Network: send ESP-NOW packet (cmd=0x01, relay)
    Network->>Receiver: deliver message
    Receiver->>Receiver: validate data pointer & size, parse payload
    alt command == 0x01 and relay valid
        Receiver->>Relay: toggle specified relay
    else
        Receiver->>Receiver: log invalid command/format
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • v0.0.4 - Bug fixes #36: Modifies the same CI workflow and appears related to adding/updating ESP‑NOW build jobs.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main feature added: ESP-NOW communication for controlling relays across multiple devices.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Fix all issues with AI agents
In `@docs/espnow.md`:
- Around line 10-13: The remote_package block currently sets ref: stable which
conflicts with README examples using ref: main; update the remote_package -> ref
value from "stable" to "main" (or add a short comment documenting when to use
"stable" vs "main") so examples are consistent; look for the remote_package
stanza and the keys url and ref to make the change in docs/espnow.md (and mirror
the same change in any example file that uses ref: stable).

In `@ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml`:
- Around line 200-206: The lambda attached to the id: !extend button_click_event
currently sends ESP-NOW for all click counts; update it to only send on
single-clicks by early-returning when the button's click_count is not 1: after
checking id(espnow_send_enabled), add a guard like if (button->click_count != 1)
return; then call espnow_send->execute(button); reference the existing symbols
button_click_event, espnow_send_enabled, espnow_send and button in your change.
- Line 252: Add the YAML document end marker (...) at the end of this YAML file
to match project convention; locate the end of the file (near the last entry
such as the "icon: mdi:radio" mapping) and append a single line containing three
dots (...) on its own line.
- Around line 85-90: The espnow_send script currently uses "mode: single" with a
100ms delay which will drop concurrent calls; change the script "id:
espnow_send" to use "mode: queued" and set a sensible "max_runs" (e.g., 3-10
depending on throughput) so concurrent button presses are queued instead of
ignored, keeping the existing delay and parameter list intact. Ensure you update
only the mode and add max_runs in the espnow_send block so queued runs can
execute sequentially without silent drops.
- Around line 64-79: The receive handler for command == 0x01 toggles
id(sw_relay_1)..id(sw_relay_4) based only on relay_num and does not check the
device's configured gang count, so commands for non-existent relays still toggle
GPIOs; update the block that handles "if (command == 0x01 && relay_num >= 1 &&
relay_num <= 4)" to first read the device's gang/configured relay count (the
variable or config that denotes how many gangs are active) and abort/ignore the
command if relay_num is greater than that count, then only call
id(sw_relay_1)->toggle() ... id(sw_relay_4)->toggle() for relays that are
actually present.
- Around line 28-29: Remove the unused include by deleting "esp_mac.h" from the
includes list in the YAML platformio options (the includes block that currently
contains esp_mac.h); the code parses MAC addresses using sscanf (no esp_mac or
ESP-IDF MAC APIs are referenced), so simply remove the esp_mac.h entry to avoid
an unnecessary dependency.

In `@README.md`:
- Line 278: The long Markdown sentence that begins "Add the ESP-NOW package so
one switch can toggle relays on another switch directly, without Home
Assistant..." exceeds the 200-char limit; split it into multiple shorter lines
(e.g., break after the first clause and again before the parenthetical) so each
line is under 200 characters while preserving the meaning and the ESP-NOW docs
link (docs/espnow.md) and the YAML substitution details (target MAC and relay
per button) intact.

In `@TX-Ultimate-Easy-ESPHome_espnow_example.yaml`:
- Around line 9-11: The substitutions block is missing the required
device_format and gang_count keys; add them to the substitutions section
(alongside name and friendly_name) with appropriate values used elsewhere (e.g.,
device_format: "sonoff_basic" or your device family and gang_count: "1" or the
number of switches) so the template compiles under v2025.12.2+; ensure the
symbol names device_format and gang_count exactly match any references in the
YAML templates or esphome includes.
- Around line 27-48: The user instructions incorrectly reference Home Assistant
UI entities; update the text to tell users to set the YAML substitution
variables espnow_target_mac_button_X and espnow_target_relay_button_X (and
document the espnow_enabled substitution if present) instead of "Button X
ESP-NOW Target MAC Address" and "Button X ESP-NOW Target Relay", and add a short
example substitutions block showing how to override espnow_target_mac_button_1,
espnow_target_relay_button_1, etc. so readers can copy/paste concrete examples
for each switch.
🧹 Nitpick comments (2)
ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml (2)

84-197: MAC and relay resolution is duplicated 3–4 times across separate lambdas.

The switch(button) block resolving mac_str appears in the condition (Line 97), the log lambda (Line 118), and the espnow.send address lambda (Line 148). The relay resolution appears in the log lambda (Line 119) and the data lambda (Line 176). Any change to the mapping requires updating all copies.

Consider consolidating by storing the resolved MAC string and relay number into global variables in the condition lambda, then reading them in the subsequent action lambdas. This eliminates all duplicates.


31-40: The espnow_send_enabled / espnow_receive_enabled globals duplicate the switch states.

The globals and switches both persist and restore independently. You could read the switch state directly (e.g., id(sw_espnow_send_enabled)->state) in the lambdas instead of maintaining separate globals, eliminating the dual-state tracking. This would remove the globals entirely.

Also applies to: 208-245

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@ESPHome/TX-Ultimate-Easy-ESPHome_addon_espnow.yaml`:
- Around line 106-109: Remove the stray preprocessor closing directives that
have no matching `#if`: delete the dangling "#endif" immediately after the case 1
block that sets mac_str = "${espnow_target_mac_button_1}" (and likewise remove
the other unmatched "#endif" noted around line 139) so the switch/case for
mac_str is not broken by unmatched preprocessor tokens.

In `@ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml`:
- Line 65: Replace the incorrect substitution ${gangs} with the defined
substitution ${gang_count} throughout
ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml (this includes all preprocessor
directives and runtime checks like the relay validation expression `if (command
!= 0x01 or relay_num < 1 or relay_num > ${gangs})`); update every occurrence of
`${gangs}` to `${gang_count}` so the YAML uses the documented substitution name
and the relay_num bounds checks and other directives reference the correct
variable.
- Around line 106-109: Remove the stray unmatched “#endif” directives that
follow the always-compiled switch cases: delete the `#endif` after the switch
`case 1` block where `mac_str = "${espnow_target_mac_button_1}";` and the
similar stray `#endif` in the second lambda (the block assigning `mac_str =
"${espnow_target_mac_button_2}";`), so the switch/case blocks are not closed by
an unmatched preprocessor directive.
🧹 Nitpick comments (2)
ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml (2)

91-165: Massive duplication: MAC-lookup and relay-lookup switch blocks are repeated 4 times.

The same switch (button) block resolving MAC and relay is duplicated across:

  1. Condition lambda (lines 100–129)
  2. Logging lambda (lines 131–165)
  3. address lambda (lines 168–201)
  4. data lambda (lines 202–231)

This makes maintenance error-prone (the stray #endif bug above is evidence). Consider extracting the MAC and relay resolution into a helper (e.g., a C++ function in an included header, or a single lambda that sets shared variables) and referencing it from each action.


241-278: Dual state management: template switches + globals are redundant.

Both the restore_value: true globals and the restore_mode: restore_default_off template switches persist the same boolean. On boot, they could disagree if one restores before the other. Since the template switches already have optimistic: true and restore_mode, you could drop the globals entirely and read switch state via id(sw_espnow_send_enabled).state in lambdas instead.

This project is already on the limits of NVS space and saving a couple of settings more could be the last drop, so I'm saving a few bytes here.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@ESPHome/TX-Ultimate-Easy-ESPHome_addon_espnow.yaml`:
- Around line 182-190: The MAC parsing block using sscanf currently returns a
zeroed MAC on failure without notifying the user; modify the else branch (the
path that returns {0x00,...}) to emit a warning log including the offending
mac_str (and optionally TAG/context) before returning zeros — locate the code
around the sscanf call that references mac_str and values[] and add an
ESP_LOGW/LOGW-style warning indicating the malformed MAC string so users can
debug malformed inputs.
- Around line 25-30: Remove the unused include entry "esp_mac.h" from the
esphome -> platformio_options -> includes list (the includes block that
currently contains esp_mac.h) since the code parses MACs manually with
sscanf/string methods and the espnow component manages MAC handling; simply
delete the "esp_mac.h" line from that includes array.
🧹 Nitpick comments (3)
ESPHome/TX-Ultimate-Easy-ESPHome_addon_espnow.yaml (3)

54-54: Minor: inconsistent use of or vs ||.

Line 54 uses the C++ alternative token or, while every other boolean expression in this file uses || (e.g., lines 151, 216). Consider using || here for consistency.

-      if (command != 0x01 or relay_num < 1 or relay_num > ${gang_count} ) {
+      if (command != 0x01 || relay_num < 1 || relay_num > ${gang_count}) {

120-154: Lambda computes mac_str and relay_num as local variables, then discards them — only the log line has any effect.

The espnow.send action at lines 155–220 cannot access the locals from this lambda, so it re-derives mac_str and relay_num from scratch. This lambda is effectively dead code apart from the ESP_LOGI call. Either move the log into one of the espnow.send lambdas, or remove this block entirely to avoid the duplicated switch logic.


87-219: The button → MAC / relay switch-case is duplicated four times.

The same switch (button) mapping appears in:

  1. Condition lambda (lines 93–116) — resolves MAC
  2. Logging lambda (lines 123–149) — resolves MAC + relay
  3. Address lambda (lines 159–180) — resolves MAC again
  4. Data lambda (lines 193–214) — resolves relay again

Each copy must be kept in sync manually and each carries its own #if/#endif guards. Consider extracting two helper lambdas or globals (one for MAC, one for relay) that are computed once and shared across the action steps, or restructuring as a single lambda that builds and sends the message in one place.

Changed the mode of espnow_send script to queued and set max_runs to 10.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml`:
- Around line 128-129: The current lambda that decides whether to skip ESP-NOW
only checks mac_str != "00:00:00:00:00:00" and allows malformed MACs like
"XX:YY:..." which later cause sscanf to fail and fallback to all-zero MAC;
update the lambda that uses mac_str to validate format by attempting to parse
the hex bytes (use sscanf or equivalent) and require that sscanf returns 6 (or
otherwise confirm 6 hex pairs) before returning true, so malformed strings are
treated as empty/invalid and the log near where the MAC is processed (sscanf)
remains accurate.
- Around line 160-163: The sender-side relay validation incorrectly uses the
sender's ${gang_count}; change the validation in the block that checks relay_num
(and the similar block around line 226) to clamp against the protocol maximum 4
instead of ${gang_count} so it validates 1..4 (e.g., replace uses of
${gang_count} in the relay_num bounds check with 4); keep the receiver-side
validation as-is.
🧹 Nitpick comments (2)
ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml (2)

32-41: Consider removing redundant globals in favor of reading switch state directly.

The espnow_send_enabled / espnow_receive_enabled globals mirror the template switch states and are kept in sync via on_turn_on/on_turn_off. You could simplify by reading id(sw_espnow_send_enabled).state directly, eliminating the extra restore-value globals. That said, this is a common ESPHome micro-optimization pattern, so feel free to keep if preferred.


91-230: Significant DRY violation: the same switch(button) MAC/relay resolution is repeated 4 times.

The button→MAC and button→relay mapping logic is duplicated across:

  1. Condition lambda (lines 101–129)
  2. Then-log lambda (lines 131–163)
  3. Address lambda (lines 167–200)
  4. Data lambda (lines 201–229)

Any mapping change requires updating all four blocks. Consider consolidating into a single lambda that resolves the target MAC + relay, validates, and then calls espnow.send directly from C++ (using id(espnow_component).send(...)) — or at minimum, store the resolved values in local script-scoped variables that the subsequent actions can reference.

I'm moving the changes applied to the other file by mistake. I will remove that other file.
@edwardtfn
Copy link
Owner

@jakub-k-dev, which files/modules are required to run this? I mean, you are extend things from buttons and relays, right, so I need those two files (apart of common base file). Anything else?

@edwardtfn
Copy link
Owner

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@edwardtfn
Copy link
Owner

@jakub-k-dev, have you thought about the possibility to assign one button to multiple target devices?
Something like this:

substitutions:
  espnow_button_1_target:
    - mac: "AA:BB:CC:DD:EE:FF"
      relay: 1
    - max: "FF:EE:DD:CC:BB:AA"
      relay: 4
  espnow_button_2_target:
    - mac: "AB:BC:CD:DE:EF:F0"
      relay: 3
  espnow_button_3_target:
    - mac: "01:12:23:34:45:56"
      relay: 2

Too complex for now?

@edwardtfn
Copy link
Owner

@coderabbitai resume

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 9, 2026

✅ Actions performed

Reviews resumed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@README.md`:
- Line 378: The markdown link "🇬🇧 [Let's
Automate](https://https://youtu.be/fIOgWQXndhI?si=O3j7sjwn7PvH1vxn)" contains a
duplicated "https://" prefix; edit the README entry to remove the extra protocol
so the href becomes "https://youtu.be/fIOgWQXndhI?si=O3j7sjwn7PvH1vxn" (update
the link text/URL where it appears in the README).

In `@TX-Ultimate-Easy-ESPHome_espnow_example.yaml`:
- Around line 38-44: The numbered steps in the README block contain a duplicate
"Step 3" — change the second occurrence (the line starting "3. To find MAC
addresses:") to "4." so the sequence reads 1, 2, 3, 4; update the literal string
"3. To find MAC addresses:" to "4. To find MAC addresses:" in the YAML comment
block to fix the duplicate numbering.
🧹 Nitpick comments (1)
TX-Ultimate-Easy-ESPHome_espnow_example.yaml (1)

40-42: Instructions still reference entity-style names rather than substitution keys.

The config section above (lines 14–19) correctly uses substitution keys like espnow_target_mac_button_1, but the instructions on lines 41–42 refer to "Button X ESP-NOW Target MAC Address" and "Button X ESP-NOW Target Relay" as if they were HA entities or UI labels. Since the user configures these in YAML, referencing the actual substitution variable names would be clearer.

Proposed fix
-##### 3. On the device's YAML in ESPHome, configure:                                            #####
-#####      * "Button X ESP-NOW Target MAC Address" - Enter the MAC address of the target device #####
-#####      * "Button X ESP-NOW Target Relay" - Select which relay to toggle (1-4)               #####
+##### 3. On the device's YAML in ESPHome, configure these substitutions:                        #####
+#####      * espnow_target_mac_button_X  - MAC address of the target device                    #####
+#####      * espnow_target_relay_button_X - Which relay to toggle (1-4)                        #####

edwardtfn and others added 2 commits February 10, 2026 01:24
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@TX-Ultimate-Easy-ESPHome_espnow_example.yaml`:
- Around line 9-13: The substitutions block uses generic values for name and
friendly_name which will cause mDNS and Home Assistant entity conflicts when
multiple devices use the same values; update the substitutions (specifically the
name and friendly_name entries) to require a unique identifier per device and
add a short comment above them stating "MUST be unique per device — change for
each unit" (or similar) so users know to set distinct names for each physical
device.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/validate_esphome.yml (1)

513-541: ⚠️ Potential issue | 🔴 Critical

summary_dev is missing a dependency and has wrong total count.

Two issues:

  1. build_ard_espnow_example_dev is not listed in needs (lines 516-522), yet its result is checked on line 535. GitHub Actions will evaluate the expression as an empty string, so it will never count as success.
  2. TOTAL=6 (line 529) should be TOTAL=8 — there are now 8 dev builds being checked (4 Arduino + 4 IDF).
Proposed fix
     needs:
       - build_ard_core_dev
       - build_ard_basic_dev
       - build_ard_ble_proxy_dev
+      - build_ard_espnow_example_dev
       - build_idf_core_dev
       - build_idf_basic_dev
       - build_idf_ble_proxy_dev
       - build_idf_espnow_example_dev
     if: ${{ always() && (github.event_name != 'workflow_dispatch' || github.event.inputs.skip_dev != 'true') }}
     runs-on: ubuntu-latest
     steps:
       - name: Dev ESPHome Results
         run: |
           SUCCESS=0
-          TOTAL=6
+          TOTAL=8
🤖 Fix all issues with AI agents
In @.test/esphome_ard_espnow_example.yaml:
- Line 13: In the .test/esphome_ard_espnow_example.yaml file fix the typo in the
include path: update the include value referencing
"../TX-Ultimate-Easy-ESPHome_espnow_example.yam" (under the espnow_example key)
to use the correct ".yaml" extension so it reads
"../TX-Ultimate-Easy-ESPHome_espnow_example.yaml".

In @.test/esphome_idf_espnow_example.yaml:
- Line 13: The include path has a typo: change the reference in the
espnow_example entry from "../TX-Ultimate-Easy-ESPHome_espnow_example.yam" to
the correct "../TX-Ultimate-Easy-ESPHome_espnow_example.yaml" so ESPHome can
find the file; update the string in the espnow_example include to have the
trailing "l".

@edwardtfn
Copy link
Owner

@coderabbitai resume

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 10, 2026

✅ Actions performed

Reviews resumed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@TX-Ultimate-Easy-ESPHome_espnow_example.yaml`:
- Around line 38-39: Update the README instruction that tells users to enable a
switch named "ESP-NOW Enabled" to instead refer to the actual switch entities
defined in espnow.yaml: instruct users to enable "ESP-NOW Send Enabled" and
"ESP-NOW Receive Enabled" (or specify which of those two is needed per device)
so they can find the correct switches in Home Assistant; mention espnow.yaml and
the exact switch names "ESP-NOW Send Enabled" and "ESP-NOW Receive Enabled" to
make locating them unambiguous.
🧹 Nitpick comments (3)
TX-Ultimate-Easy-ESPHome_espnow_example.yaml (2)

25-33: Remote package includes look complete.

Core, standard, and espnow are all included. Note that ref: main means this example won't work until the PR is merged — consider mentioning this in the instructions or temporarily using the feature branch ref during development.


53-54: "Fallback behavior" wording is slightly misleading.

When the MAC is 00:00:00:00:00:00, ESP-NOW sending is simply skipped — the button's normal local relay toggle still fires through the standard button/relay wiring, not as a "fallback" within ESP-NOW itself. Consider rewording to: "the button will only toggle the local relay (no ESP-NOW message is sent)."

ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml (1)

88-219: Consider extracting button-to-config lookup into helper functions.

The same switch (button) pattern with MAC/relay lookup is duplicated four times across the condition lambda (line 95), then lambda (line 123), address lambda (line 158), and data lambda (line 193). This duplication adds maintenance burden — adding a 5th button or changing the lookup logic requires updating all four copies.

Consider extracting helper functions (e.g., getMacForButton(button), getRelayForButton(button)) to consolidate the repeated case/break blocks, even if ESPHome YAML lambda constraints make this approach challenging.

Comment on lines +38 to +39
##### 1. Flash this configuration to all 3 devices #####
##### 2. In Home Assistant, for each device, enable the control "ESP-NOW Enabled" #####
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Instruction references incorrect switch name.

Step 2 says "ESP-NOW Enabled" but the actual switches defined in espnow.yaml are named "ESP-NOW Send Enabled" and "ESP-NOW Receive Enabled". Users won't find a single "ESP-NOW Enabled" entity.

Proposed fix
-##### 2. In Home Assistant, for each device, enable the control "ESP-NOW Enabled"         #####
+##### 2. In Home Assistant, for each device, enable "ESP-NOW Send Enabled" and             #####
+#####    "ESP-NOW Receive Enabled" controls                                                #####
🤖 Prompt for AI Agents
In `@TX-Ultimate-Easy-ESPHome_espnow_example.yaml` around lines 38 - 39, Update
the README instruction that tells users to enable a switch named "ESP-NOW
Enabled" to instead refer to the actual switch entities defined in espnow.yaml:
instruct users to enable "ESP-NOW Send Enabled" and "ESP-NOW Receive Enabled"
(or specify which of those two is needed per device) so they can find the
correct switches in Home Assistant; mention espnow.yaml and the exact switch
names "ESP-NOW Send Enabled" and "ESP-NOW Receive Enabled" to make locating them
unambiguous.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml`:
- Around line 313-315: The closing preprocessor comment after the `#endif` is
incorrect: the guard uses TX_ULTIMATE_EASY_HW_BUTTONS (in the `#ifndef`) but the
`#endif` comment currently reads TX_ULTIMATE_EASY_HW_LEDS; update the comment to
match the actual guard by changing the `#endif` trailing comment to //
TX_ULTIMATE_EASY_HW_BUTTONS so it correctly reflects the checked symbol and
avoids confusion.
- Around line 21-29: Remove trailing whitespace from the file: strip trailing
spaces on the ESP-NOW config lines (espnow_target_mac_button_2,
espnow_target_relay_button_2, espnow_target_mac_button_3,
espnow_target_relay_button_3) and from all flagged blank lines, and also remove
trailing spaces inside the hex-validation blocks after the character class
string '0123456789ABCDEFabcdef:' (lines referenced in CI). Ensure no lines end
with spaces so the YAML linter passes.
🧹 Nitpick comments (3)
.test/esphome_ard_espnow_example.yaml (1)

5-10: Consider quoting integer substitution values for consistency.

espnow_target_relay_button_* values are bare integers while the MAC addresses are quoted strings. ESPHome substitutions are always strings internally, so this works, but quoting them (e.g., "1") would be more consistent with how other substitutions are expressed and avoids any YAML-type ambiguity.

Suggested diff
-  espnow_target_relay_button_1: 1
+  espnow_target_relay_button_1: "1"
   espnow_target_mac_button_2: "AB:BC:CD:DE:EF:F0"
-  espnow_target_relay_button_2: 3
+  espnow_target_relay_button_2: "3"
   espnow_target_mac_button_3: "01:12:23:34:45:56"
-  espnow_target_relay_button_3: 2
+  espnow_target_relay_button_3: "2"
ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml (2)

127-129: Consider documenting the security posture of auto_add_peer: true.

With auto_add_peer: true and no encryption, any ESP-NOW device on the same Wi-Fi channel can send toggle commands to this device (when receive is enabled). This is standard for ESP-NOW home automation setups and mitigated by the receive switch defaulting to OFF, but it would be good to note in docs/espnow.md that ESP-NOW operates without authentication so users understand the trust boundary.


195-264: Compile-time validation doesn't warn when ESP-NOW is configured for buttons beyond gang_count.

If a user configures espnow_target_mac_button_3 with a valid MAC on a 2-gang device, validation passes (MAC format is valid) but the button 3 send case is compiled out by the #if ${gang_count} >= 3 guard. The configuration is silently ignored with no compile-time or runtime feedback. Consider adding a #if guard that emits a #warning or #error when a non-null MAC is configured for a button beyond the gang count.

Example addition after the existing validation blocks
          // Warn if ESP-NOW is configured for buttons beyond gang_count
          `#if` ${gang_count} < 2 && ${ESPNOW_BUTTON_2_IS_NULL} == 0
            `#error` "espnow_target_mac_button_2 is configured but gang_count < 2"
          `#endif`
          `#if` ${gang_count} < 3 && ${ESPNOW_BUTTON_3_IS_NULL} == 0
            `#error` "espnow_target_mac_button_3 is configured but gang_count < 3"
          `#endif`
          `#if` ${gang_count} < 4 && ${ESPNOW_BUTTON_4_IS_NULL} == 0
            `#error` "espnow_target_mac_button_4 is configured but gang_count < 4"
          `#endif`

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml`:
- Around line 190-201: The validation block for button 3 incorrectly uses
TX_ULTIMATE_EASY_ESPNOW_BUTTON_2_MAC_INVALID; update the MAC-format check to use
TX_ULTIMATE_EASY_ESPNOW_BUTTON_3_MAC_INVALID inside the button 3 validation
block (the section guarded by TX_ULTIMATE_EASY_ESPNOW_BUTTON_3_MAC_DEFINED) so
that invalid MACs for button 3 trigger the intended `#error` and button 3
validation is independent of button 2.
- Around line 90-93: The build flags for
TX_ULTIMATE_EASY_ESPNOW_BUTTON_X_MAC_INVALID are wrong because
ESPNOW_BUTTON_X_MAC_VALID is a string ('0' or '1') and Jinja treats any
non-empty string as truthy; update each expression to perform an explicit string
or integer comparison (e.g., use ESPNOW_BUTTON_1_MAC_VALID == "1" or
ESPNOW_BUTTON_1_MAC_VALID|int) so that
TX_ULTIMATE_EASY_ESPNOW_BUTTON_1_MAC_INVALID,
TX_ULTIMATE_EASY_ESPNOW_BUTTON_2_MAC_INVALID,
TX_ULTIMATE_EASY_ESPNOW_BUTTON_3_MAC_INVALID and
TX_ULTIMATE_EASY_ESPNOW_BUTTON_4_MAC_INVALID are set to 1 when the corresponding
ESPNOW_BUTTON_X_MAC_VALID is "0" and 0 when it is "1".
🧹 Nitpick comments (1)
ESPHome/TX-Ultimate-Easy-ESPHome_espnow.yaml (1)

95-97: No encryption on ESP-NOW — any nearby device can toggle relays.

auto_add_peer: true with no PMK/LMK encryption means any ESP32 in radio range can craft a 2-byte payload and toggle relays. This may be acceptable for a V1 home automation feature, but consider documenting the threat model and adding encryption support in a follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants