Skip to content

feat: multi-device support via ESPHome packages#22

Open
flowcool wants to merge 10 commits into
JGAguado:V2R1from
flowcool:V2R1
Open

feat: multi-device support via ESPHome packages#22
flowcool wants to merge 10 commits into
JGAguado:V2R1from
flowcool:V2R1

Conversation

@flowcool

Copy link
Copy Markdown
Contributor

Summary

Implements the multi-device architecture proposed in #21, now validated in production on 8 devices.

  • examples/multi-device/packages/smart_plant_base.yaml — shared base package (~320 lines): all firmware in one place, every OTA push propagates to all devices automatically
  • examples/multi-device/my-lemon-tree.yaml — minimal per-device file (~20 lines): plant-specific substitutions only

Includes all fixes accumulated since the RFC:

  • SNTP: NTP servers as direct IPs (bypass DNS failures on ESP32)
  • on_time_sync: display refreshes as soon as time is available, no wait_until
  • use_address substitution: reliable OTA/log connections with static IP override
  • cpu_frequency: 160MHz: prevents ~3 mAh/day extra drain from ESPHome 2026.4 new 240MHz default
  • power_save_mode: LIGHT: WiFi savings during wake window
  • MQTT with topic_prefix substitution, log_topic disabled (bandwidth), birth/will disabled (no "unavailable" flash on wake)

No changes to existing docs/source/files/configuration.yaml — fully additive.

Validation

Running on 8 ESP32-S2 Saola boards in production for 2+ days:

  • All sensors reporting correctly to Home Assistant via MQTT discovery
  • SNTP time display working on all devices
  • OTA flashing reliable via static IP

Closes #21

🤖 Assisted with Claude Code

flowcool and others added 8 commits June 16, 2026 09:36
This is a discussion proposal — not a merge request for the current
single-device configuration.yaml.

## Context

I'm running 8 Smart Plant boards at home. With the current flat
configuration.yaml, maintaining 8 copies means:
- every bug fix or improvement must be applied 8 times
- calibration values are scattered across 8 files
- inconsistencies creep in over time

## Proposed structure

    examples/multi-device/
    ├── packages/
    │   └── smart_plant_base.yaml   # all shared config (~250 lines)
    └── my-lemon-tree.yaml          # per-plant overrides (~20 lines)

The base package contains everything that is currently in
configuration.yaml. Each device file only defines its substitutions
(name, timezone, label image, soil calibration, gauge ranges) and
includes the package with a single !include.

Benefits:
- A bug fix in smart_plant_base.yaml propagates to all devices on the
  next OTA — no copy-paste
- Soil calibration is clearly visible per device, not buried in a
  shared file
- New plant = 15-line file, not a 400-line copy

Also included in the base:
- All fixes from PRs JGAguado#18 (SNTP), JGAguado#19 (power_save_mode / timezone)
- draw_gauge refactored as a C++ lambda to eliminate the 4×50-line
  repeated block
- device_class: battery on the battery_level sensor

## Questions for @JGAguado

1. Would you consider adding this as an `examples/` directory rather
   than replacing configuration.yaml? The single-file config would
   remain the default for users with one plant; this would be an
   opt-in for multi-plant setups.
2. The draw_gauge lambda refactor inside the display lambda — is that
   acceptable in terms of ESPHome compatibility across versions?
3. Are there plans to migrate the project to esp-idf framework? That
   would affect some of the deep sleep optimisations.

Note: I've been working on this refactoring with AI assistance
(Claude Code) for my personal multi-plant setup. The PRs in this
series are the result of a careful diff between the upstream config
and my production setup. Happy to iterate on any of this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace api: with mqtt: (MQTT required for deep-sleep compatibility)
- Add substitutions: device_comment, mqtt_topic_prefix, ota_password,
  ap_ssid — all previously hardcoded per-device
- Improve on_boot: Wire.begin() + wait for mqtt.connected + wait for
  valid SNTP time (replaces fixed 20s delay, fixes 1970 display bug)
- Update my-lemon-tree.yaml to show static IP override pattern and
  github:// package URL for zero-local-copy deployment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… init

Wire is not in scope in ESPHome lambdas without explicit include; the i2c:
component already initializes the bus before on_boot runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bypasses mDNS resolution (which fails with name_add_mac_suffix: true).
Device files set use_address to their static IP; default falls back to
device_name for setups without static IP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes the 30s blocking wait for SNTP in on_boot — display already
handles the missing-time case gracefully with dashes. Adds on_time_sync
callback instead: display refreshes immediately the moment SNTP syncs,
regardless of network latency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pool.ntp.org alone can be slow on some networks (ESP32 UDP stack init
latency + retry cycles). Adding cloudflare and google as fallbacks
increases the chance of a fast first sync without any local IP dependency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DNS resolution can fail on ESP32 depending on router/DHCP config.
Using Cloudflare (162.159.200.1) and Google (216.239.35.0) stable
anycast IPs as primary servers with pool.ntp.org as hostname fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ESP32-S2 default changed to 240MHz in ESPHome 2026.4 — cap at 160MHz
to avoid ~3 mAh/day extra drain during wake windows on battery devices.
Add power_save_mode: LIGHT during wake for additional WiFi savings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
flowcool and others added 2 commits June 18, 2026 11:31
Both api: and mqtt: can coexist — MQTT keeps deep-sleep retained
values, api: adds mDNS advertisement (_esphomelib._tcp.local.) so
the ESPHome dashboard discovers devices via mDNS instead of ping only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant