Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"permissions": {
"deny": [
"Read(./devices/secrets.yaml)"
]
}
}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ jobs:
path: |
~/.platformio
~/.esphome-idf
key: build-${{ matrix.platform }}-${{ hashFiles('devices/packages/device_base*.yaml') }}
key: build-${{ matrix.platform }}-${{ hashFiles('devices/packages/device_*.yaml') }}
restore-keys: build-${{ matrix.platform }}-
- name: Install ESPHome
run: pip install esphome
Expand Down
13 changes: 8 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Installed via `pipx install esphome`, which creates its own isolated virtualenv
```
On a fresh machine ESPHome downloads roughly 6 GB into that prefix on the first compile. To migrate an existing install instead of re-downloading, move `%LOCALAPPDATA%\esphome\Cache\idf` to the new prefix, then delete `tools\`, `penvs\` and `idf-env.json` inside it — those re-extract from the `dist\` archive cache that moves along with them.

**2. Build to a local drive, not the UNC share.** This repo lives on `\\nas\docker\esphome\config`, and `devices/packages/device_base.yaml` sets `build_path: ./build/${device_id}`, which resolves (relative to the device yaml) to `devices/build/${device_id}` on that share. `cmd.exe` cannot use a UNC path as a working directory, so ninja's *link* step runs from `C:\Windows` and fails with `ld.exe: cannot open output file ...: Permission denied`. The compile steps succeed first, which makes this read as a linker bug rather than a path problem. Override `build_path` to a local path (e.g. `C:/eh/bld/<name>`) when compiling from Windows. Source files can stay on the share — gcc handles UNC paths fine; only the working directory is the problem.
**2. Build to a local drive, not the UNC share.** This repo lives on `\\nas\docker\esphome\config`, and `devices/packages/device_common.yaml` sets `build_path: ./build/${device_id}`, which resolves (relative to the device yaml) to `devices/build/${device_id}` on that share. `cmd.exe` cannot use a UNC path as a working directory, so ninja's *link* step runs from `C:\Windows` and fails with `ld.exe: cannot open output file ...: Permission denied`. The compile steps succeed first, which makes this read as a linker bug rather than a path problem. Override `build_path` to a local path (e.g. `C:/eh/bld/<name>`) when compiling from Windows. Source files can stay on the share — gcc handles UNC paths fine; only the working directory is the problem.

**3. Don't run any other `esphome` command while a compile is in progress.** Even `esphome config` rewrites state under `devices/.esphome/`, and because that directory is shared with the build, cmake sees a changed input and re-runs configure mid-build. The initial configure succeeds off the resolved dependency lock, but a *re-run* re-resolves component manifests and dies on `devices/.esphome/pio_components/idf/*/esphome/noise-c/idf_component.yml`, which contains `override_path: /config/.esphome/...` — a container path, written by the Docker ESPHome where `devices/` is mounted at `/config`. Windows resolves it against the UNC root as `\\nas\docker\config\...`, which doesn't exist, and the build dies with `The "override_path" field ... does not point to a directory`. That cache is gitignored and regenerable, so leave it alone rather than "fixing" it — the container needs those paths. Just validate before you build, then stay off the config directory until it finishes.

Expand All @@ -98,10 +98,12 @@ The `no-commit-to-branch` pre-commit hook blocks commits directly on `master`. A
`devices/` is the directory mounted as `/config` in the ESPHome Device Builder container — the dashboard only scans the top level of its config dir, which is why the device yamls, their `packages/`, and `secrets.yaml` all live together there while everything repo-level (`components/`, `docs/`, `scripts/`, lint config) stays at the repo root. `components/` is bind-mounted alongside it so that `path: ../components` resolves identically inside the container and from a host `esphome` CLI run.

**`devices/packages/`** holds shared YAML fragments included by devices via the `packages:` key, layered like this:
- `device_base.yaml` — the common core every device gets: wifi, api, ota, logger, status/uptime/wifi sensors, restart button, version/wifi-info text sensors, time.
- `device_base_esp32.yaml` / `device_base_esp8266.yaml` — platform block (`esp32:`/`esp8266:`) plus an include of `device_base.yaml`. Devices include one of these, not `device_base.yaml` directly.
- `econet_base.yaml` — extends the esp32 base for Econet-driven HVAC boards, internalizing a couple of entities via `!extend`.
- `wifi/wifi_dhcp.yaml` vs `wifi/wifi.yaml` — DHCP vs static-IP wifi variants.
- Devices include exactly one *combo* package — `device_esp32_wifi.yaml`, `device_esp8266_wifi.yaml` or `device_esp32_eth.yaml` — never the layers below it directly. Each combo is nothing but a `packages:` block pulling in one common, one chip and one network layer.
- `device_common.yaml` — the core every device gets regardless of chip or network: `esphome:`/`preferences:` boilerplate plus api, ota, logger, status/uptime sensors, restart button, version text sensor, time.
- `device_esp32.yaml` / `device_esp8266.yaml` — just the platform block (`esp32:`/`esp8266:`) and its substitutions.
- `device_wifi.yaml` / `device_eth.yaml` — the network layer and its diagnostics: wifi pulls in `wifi/wifi.yaml`, the wifi-signal sensor and the wifi-info text sensor; eth pulls in the ethernet-info text sensor (the `ethernet:` block itself is board-specific and stays in the device yaml).
- There is no `device_esp8266_eth.yaml` — no such device exists here.
- `econet.yaml` — extends `device_esp32_wifi.yaml` for Econet-driven HVAC boards, internalizing a couple of entities via `!extend`.
- `devices/secrets.yaml` (untracked, gitignored) holds actual secret values; every device/package references secrets with `!secret key_name`. `devices/packages/secrets.yaml` and the various per-device `secrets.yaml` files just include that one.

The `!extend` tag is used throughout to modify entities defined by an included package/component (e.g. marking a base sensor `internal: true`, or attaching an `on_value`/`on_time` trigger to `wifi_rssi`/`time_source` in `pool.yaml`) instead of redefining them.
Expand All @@ -125,3 +127,4 @@ Components are consumed either from disk (`external_components: - source: {type:

- YAML: 2-space indent, double-quoted strings, LF line endings (`.gitattributes` forces LF for `*.yaml`/`*.yml` even on this Windows checkout).
- Python (`components/**/*.py`): standard ESPHome component style; flake8 ignores docstring rules (`D1xx`) since ESPHome components aren't expected to have docstrings, and line length is capped at 120.
- Bash filesystem commands must be scoped. `devices/.esphome/` holds ~38k ESP-IDF toolchain/build-cache files, and this checkout lives on an SMB share. Grep/Glob honor `.gitignore` and skip it, but Bash does not — a bare `find`/`du`/`ls -R` from the repo root walks all of it and times out. Target a specific subdirectory, or use `-maxdepth` / `-prune`, instead of recursing from the root.
2 changes: 1 addition & 1 deletion devices/basement-bath-shower-light-heat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ substitutions:
pwd: !secret basement_bathroom_shower_light_heat_pwd

packages:
device_base: !include ./packages/device_base_esp8266.yaml
device: !include ./packages/device_esp8266_wifi.yaml

binary_sensor:
- platform: gpio
Expand Down
2 changes: 1 addition & 1 deletion devices/basement-fridge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ substitutions:
pwd: !secret basement_fridge_pwd

packages:
device_base: !include ./packages/topgreener_smart_plug.yaml
device: !include ./packages/topgreener_smart_plug.yaml
2 changes: 1 addition & 1 deletion devices/basement-tv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ substitutions:
pwd: !secret basement_tv_pwd

packages:
device_base: !include ./packages/topgreener_smart_plug.yaml
device: !include ./packages/topgreener_smart_plug.yaml
4 changes: 2 additions & 2 deletions devices/bt-proxy-basement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ substitutions:
pwd: !secret bt_proxy_basement_pwd

packages:
device_base: !include ./packages/device_base_esp32.yaml
device: !include ./packages/device_esp32_wifi.yaml

bluetooth_proxy:
active: true
cache_services: true
connection_slots: 4
connection_slots: 3
2 changes: 1 addition & 1 deletion devices/coffee-maker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ substitutions:
pwd: !secret coffee_maker_pwd

packages:
device_base: !include ./packages/device_base_esp8266.yaml
device: !include ./packages/device_esp8266_wifi.yaml

binary_sensor:
- platform: gpio
Expand Down
2 changes: 1 addition & 1 deletion devices/emporia-vue2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ substitutions:
pwd: !secret emporia_vue2_pwd

packages:
device_base: !include ./packages/device_base_esp32.yaml
device: !include ./packages/device_esp32_wifi.yaml

api:
services:
Expand Down
2 changes: 1 addition & 1 deletion devices/family-room-tv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ substitutions:
pwd: !secret family_room_tv_pwd

packages:
device_base: !include ./packages/topgreener_smart_plug.yaml
device: !include ./packages/topgreener_smart_plug.yaml
2 changes: 1 addition & 1 deletion devices/fire-pit-fountain.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ substitutions:
pwd: !secret fire_pit_fountain_pwd

packages:
device_base: !include ./packages/device_base_esp8266.yaml
device: !include ./packages/device_esp8266_wifi.yaml

binary_sensor:
- platform: gpio
Expand Down
2 changes: 1 addition & 1 deletion devices/garage-fridge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ substitutions:
log_baud_rate: '0'

packages:
device_base: !include ./packages/device_base_esp32.yaml
device: !include ./packages/device_esp32_wifi.yaml

esp32:
cpu_frequency: 160MHZ
Expand Down
2 changes: 1 addition & 1 deletion devices/garage-lights.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ substitutions:
light_wattage: '378'

packages:
device_base: !include ./packages/shelly_1l.yaml
device: !include ./packages/shelly_1l.yaml
2 changes: 1 addition & 1 deletion devices/hvac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ packages:
hvac-zone-yaml/econet_hvac_zone2.yaml,
hvac-zone-yaml/econet_hvac_zone3.yaml
]
device_base: !include ./packages/econet_base.yaml
device: !include ./packages/econet.yaml

climate:
- platform: econet_zone_control
Expand Down
2 changes: 1 addition & 1 deletion devices/kitchen-fridge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ substitutions:
pwd: !secret kitchen_fridge_pwd

packages:
device_base: !include ./packages/topgreener_smart_plug.yaml
device: !include ./packages/topgreener_smart_plug.yaml
2 changes: 1 addition & 1 deletion devices/main-garage-door.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ substitutions:

packages:
ratgdo: github://ratgdo/esphome-ratgdo/v25iboard.yaml@main
device_base: !include ./packages/device_base_esp8266.yaml
device: !include ./packages/device_esp8266_wifi.yaml

button:
- id: !remove ${id_prefix}_restart
Expand Down
2 changes: 1 addition & 1 deletion devices/master-bed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ substitutions:
pwd: !secret master_bed_pwd

packages:
device_base: !include ./packages/device_base_esp8266.yaml
device: !include ./packages/device_esp8266_wifi.yaml

external_components:
- source:
Expand Down
2 changes: 1 addition & 1 deletion devices/network-equipment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ substitutions:
pwd: !secret network_equipment_pwd

packages:
device_base: !include ./packages/topgreener_smart_plug.yaml
device: !include ./packages/topgreener_smart_plug.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
substitutions:
log_level: none
log_baud_rate: '115200'
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password
flash_write_interval: 10min

esphome:
Expand All @@ -15,14 +13,11 @@ preferences:
flash_write_interval: ${flash_write_interval}

packages:
wifi: !include wifi/wifi_dhcp.yaml
api: !include api/api.yaml
ota: !include ota/ota.yaml
logger: !include logger/logger.yaml
status_sensor: !include binary_sensor/status.yaml
uptime_sensor: !include sensor/uptime.yaml
wifi_sensor: !include sensor/wifi.yaml
restart_button: !include button/restart.yaml
version_sensor: !include text_sensor/version.yaml
wifi_info_sensor: !include text_sensor/wifi_info.yaml
time: !include time/time.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,3 @@ esp32:
variant: ${esp32_variant}
framework:
type: ${framework_type}

packages:
base: !include device_base.yaml
4 changes: 4 additions & 0 deletions devices/packages/device_esp32_eth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
packages:
common: !include device_common.yaml
esp32: !include device_esp32.yaml
eth: !include device_eth.yaml
4 changes: 4 additions & 0 deletions devices/packages/device_esp32_wifi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
packages:
common: !include device_common.yaml
esp32: !include device_esp32.yaml
wifi: !include device_wifi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,3 @@ substitutions:
esp8266:
board: ${board}
restore_from_flash: true

packages:
base: !include device_base.yaml
4 changes: 4 additions & 0 deletions devices/packages/device_esp8266_wifi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
packages:
common: !include device_common.yaml
esp8266: !include device_esp8266.yaml
wifi: !include device_wifi.yaml
2 changes: 2 additions & 0 deletions devices/packages/device_eth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages:
eth_info_sensor: !include text_sensor/ethernet_info.yaml
4 changes: 4 additions & 0 deletions devices/packages/device_wifi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
packages:
wifi: !include wifi/wifi.yaml
wifi_sensor: !include sensor/wifi.yaml
wifi_info_sensor: !include text_sensor/wifi_info.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
packages:
device_base: !include ./device_base_esp32.yaml
device: !include ./device_esp32_wifi.yaml

button:
- id: !extend restart_esp
Expand Down
1 change: 0 additions & 1 deletion devices/packages/ota/ota.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
ota:
- platform: esphome
id: esphome_ota
password: ${pwd}
2 changes: 1 addition & 1 deletion devices/packages/shelly_1l.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ substitutions:
light_wattage: '0'

packages:
base: !include device_base_esp8266.yaml
device: !include device_esp8266_wifi.yaml

binary_sensor:
- platform: gpio
Expand Down
5 changes: 5 additions & 0 deletions devices/packages/text_sensor/ethernet_info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
text_sensor:
- platform: ethernet_info
ip_address:
name: IP Address
icon: mdi:ip-network
2 changes: 1 addition & 1 deletion devices/packages/topgreener_smart_plug.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ substitutions:
log_baud_rate: '0'

packages:
base: !include device_base_esp8266.yaml
device: !include device_esp8266_wifi.yaml

binary_sensor:
- platform: gpio
Expand Down
10 changes: 4 additions & 6 deletions devices/packages/wifi/wifi.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
substitutions:
wifi_ssid: !secret wifi_ssid
wifi_password: !secret wifi_password

wifi:
ssid: ${wifi_ssid}
password: ${wifi_password}
fast_connect: true
power_save_mode: none
min_auth_mode: WPA2
manual_ip:
static_ip: ${ip_address}
subnet: ${ip_subnet}
gateway: ${ip_gateway}
ap:
ssid: ${device_id}
password: ${pwd}
ap_timeout: 5min

captive_portal:
10 changes: 0 additions & 10 deletions devices/packages/wifi/wifi_dhcp.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion devices/pool-lights.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ substitutions:
pwd: !secret pool_lights_pwd

packages:
device_base: !include ./packages/device_base_esp8266.yaml
device: !include ./packages/device_esp8266_wifi.yaml

external_components:
- source:
Expand Down
13 changes: 10 additions & 3 deletions devices/pool.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@ substitutions:
log_level: DEBUG

packages:
device_base: !include ./packages/device_base_esp32.yaml
device: !include ./packages/device_esp32_wifi.yaml

external_components:
- source:
type: local
path: ../components
components: [pool_controller]
- source: github://pr#17849
# Pinned to the last pzem6l24 commit before upstream switched reset_energy to
# automation.register_apply_action (esphome/esphome#19469, merged 2026-09-23).
# That helper landed after the 2026.9 branch cut, so the PR head no longer
# validates against any released ESPHome. Unpin once a release carries it.
- source:
type: git
url: https://github.com/esphome/esphome.git
ref: 36d39416f0a7cdabb536d5a7434222de462c3e89
components: [pzem6l24]
refresh: 1h
refresh: never

# esphome:
# on_boot:
Expand Down
2 changes: 1 addition & 1 deletion devices/second-garage-door.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ packages:
ref: main
files: [base_drycontact.yaml]
refresh: 1s
device_base: !include ./packages/device_base_esp8266.yaml
device: !include ./packages/device_esp8266_wifi.yaml

button:
- id: !remove ${id_prefix}_restart
Expand Down
2 changes: 1 addition & 1 deletion devices/sump-pump.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ substitutions:
pwd: !secret sump_pump_pwd

packages:
device_base: !include ./packages/topgreener_smart_plug.yaml
device: !include ./packages/topgreener_smart_plug.yaml
2 changes: 1 addition & 1 deletion devices/washing-machine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ substitutions:
pwd: !secret washing_machine_pwd

packages:
device_base: !include ./packages/topgreener_smart_plug.yaml
device: !include ./packages/topgreener_smart_plug.yaml
2 changes: 1 addition & 1 deletion devices/water-heater.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ packages:
url: https://github.com/esphome-econet/esphome-econet
ref: main
file: econet_heatpump_water_heater.yaml
device_base: !include ./packages/econet_base.yaml
device: !include ./packages/econet.yaml

climate:
- id: !extend econet_climate
Expand Down
Loading