Anatomy of a Configuration
Scope: one ESPHome configuration, read
top to bottom — what each section declares,
and what happens when ESPHome checks the
file before believing it. The specimen is
examples/greenhouse.yaml, and every claim
about validation below is a capture from
running the real tool against it.
The shape of the file
A configuration is a YAML document whose top-level keys name components — the building blocks ESPHome will assemble into firmware. The greenhouse file, in skeleton:
esphome:
name: greenhouse
friendly_name: Greenhouse
esp32:
board: esp32dev
framework:
type: esp-idf
logger:
api:
ota:
wifi:
captive_portal:
i2c:
sensor:
binary_sensor:
switch:
interval:
Read it as a parts list with opinions.
Some components appear once (wifi:);
some are domains holding a list of
individual things (sensor: holds three
sensors in the full file). Each entry
under a domain names a platform — which
kind of thing it is — and its settings:
sensor:
- platform: bme280_i2c
address: 0x76
temperature:
name: "Greenhouse Temperature"
id: air_temp
oversampling: 2x
update_interval: 60s
Two fields recur everywhere and are worth
learning once. name: is what the
outside world will call the thing — it is
how the sensor appears in Home Assistant
and in the logs. id: is the name the
rest of the file uses: automations refer
to air_temp, never to the friendly name.
Confusing the two is the beginner's most
common knot; chapter 5 unties it in place.
The sections, in order
esphome: names the device itself.
name becomes its network identity
(greenhouse.local on your LAN, via mDNS)
and must be lowercase letters, numbers,
and underscores. friendly_name is the
display label. Everything else in the file
belongs to this name.
The board section — esp32: here —
says what the firmware is for. It names
the board (hence the pin map) and the
framework, the underlying C++ library
the generated code stands on: esp-idf
(Espressif's own) or arduino (the hobby
ecosystem's). Other chips have their own
section names (esp8266:, rp2040:), and
the host build of chapter 1 replaces this
whole section with a bare host:. A
configuration targets exactly one board;
moving a project between chips is an edit
here, and usually only here.
logger: is the device's voice:
serial and network logging, with a level
(DEBUG, INFO, up through ERROR).
Chapter 9 is devoted to it. The greenhouse
runs INFO; the simulator runs DEBUG,
deliberately, so the guide could watch it
think.
api: and ota: are the two
doors back into the device once it is
deployed — the native API it speaks to
Home Assistant and tools (chapter 8), and
over-the-air updating (chapter 7). Both
carry credentials in the full file; both
are fed by !secret references rather
than literal values (chapter 6).
wifi: joins the network, with a
fallback of its own if it can't
(chapter 6). Ethernet exists for some
boards as an ethernet: component
instead; Wi-Fi is the common case and
the only one this guide builds.
captive_portal:, present as a bare
key, enables the little sign-in page the
fallback hotspot serves. A component with
no settings still has to be named to
exist — an empty section is a statement,
not an omission.
i2c: sets up the bus the sensor
rides: which pins are data and clock, and
scan: true, which makes the device report
what it finds on the bus at boot — the
single most useful line when a sensor
"doesn't work" (chapter 12).
The domains — sensor:,
binary_sensor:, switch: — are the
device's senses and hands; chapter 5.
interval: is one of ESPHome's
automation engines: do something every so
often. Others exist (on_... triggers on
nearly every component). Automations are
where a parts list becomes behaviour.
Validation: the file is checked before it is believed
None of this reaches a compiler until
ESPHome has checked the file against every
component's schema — every required field,
every pin, every cross-reference between
ids. That check is a command, it needs
no hardware, and it is the most useful
habit this guide can teach:
$ esphome config greenhouse.yaml
INFO ESPHome 2026.10.0
INFO Reading configuration greenhouse.yaml...
...
INFO Configuration is valid!
(The full output — 238 lines, the whole
configuration echoed back as ESPHome
understood it, secrets still shown as
!secret references — is transcript t01.)
Run config after every edit. It is the
difference between arguing with a compiler
for an afternoon and being told, in
seconds, exactly which line is wrong.
Being told looks like this. The guide planted two faults in a copy of the file; validation stopped at the first, an out-of-date platform name:
$ esphome config broken.yaml
INFO ESPHome 2026.10.0
INFO Reading configuration broken.yaml...
INFO Unable to import component bme280.sensor: No module named 'esphome.components.bme280'
Failed config
Platform not found: 'sensor.bme280'
(Transcript t06; the command exits with
code 2.) Three lessons ride on those six
lines. Platform names are exact — there
is no bme280 in ESPHome 2026.10, because
the combined component was split into
bme280_i2c and bme280_spi, so old
examples copied from the internet fail
exactly here. Validation is complete but
sequential — it found the platform fault
and stopped; the second planted fault (a
door switch assigned to a pin the I2C bus
already owns) waits behind it, and fixing
the first is how you get to be told about
the second. And the check is offline:
no board, no network, nothing but the file
and the tool. Everything chapter 4 does
before the flash rests on that.