Guide for developing ESPHome components with YAML config and C++ code generation. Covers configuration schemas, platform support, coding standards, and testing workflows.
A comprehensive guide for AI assistants working with the ESPHome project. ESPHome configures microcontrollers (ESP32, ESP8266, RP2040, LibreTiny chips) using YAML files that generate C++ firmware for home automation.
**Purpose:** ESPHome generates C++ firmware from YAML configurations for microcontrollers, enabling remote control through home automation systems.
**Core Technologies:**
**Architecture:** Code-generation pattern - Python parses YAML → generates C++ → PlatformIO compiles → flashed to device
1. **ESP32** (`components/esp32/`): Multiple variants (Original, C2, C3, C5, C6, H2, P4, S2, S3). ESP-IDF or Arduino framework.
2. **ESP8266** (`components/esp8266/`): Arduino only, memory-constrained.
3. **RP2040** (`components/rp2040/`): Raspberry Pi Pico, Arduino with PIO support.
4. **LibreTiny** (`components/libretiny/`): Realtek and Beken chips.
- Functions/methods/variables: `lower_snake_case`
- Classes/structs/enums: `UpperCamelCase`
- Top-level constants: `UPPER_SNAKE_CASE`
- Local constants: `lower_snake_case`
- Protected/private fields: `lower_snake_case_with_trailing_underscore_`
- **Prefer `protected`** for extensibility
- Use `private` only for safety-critical cases (pointer lifetime, invariant coupling, resource management)
- Provide `protected` accessor methods when needed
```
components/[component_name]/
├── __init__.py # Schema and code generation
├── [component].h # C++ header
├── [component].cpp # C++ implementation
└── [platform]/ # Platform-specific code
├── __init__.py
├── [platform].h
└── [platform].cpp
```
```python
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_KEY, CONF_ID
CONF_PARAM = "param" # New constant not in esphome/const.py
my_component_ns = cg.esphome_ns.namespace("my_component")
MyComponent = my_component_ns.class_("MyComponent", cg.Component)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(MyComponent),
cv.Required(CONF_KEY): cv.string,
cv.Optional(CONF_PARAM, default=42): cv.int_,
}).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add(var.set_key(config[CONF_KEY]))
cg.add(var.set_param(config[CONF_PARAM]))
```
```cpp
namespace esphome::my_component {
class MyComponent : public Component {
public:
void setup() override;
void loop() override;
void dump_config() override;
void set_key(const std::string &key) { this->key_ = key; }
void set_param(int param) { this->param_ = param; }
protected:
std::string key_;
int param_{0};
};
} // namespace esphome::my_component
```
**Sensor:**
```python
from esphome.components import sensor
CONFIG_SCHEMA = sensor.sensor_schema(MySensor).extend(
cv.polling_component_schema("60s")
)
async def to_code(config):
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
```
**Binary Sensor:**
```python
from esphome.components import binary_sensor
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend({ ... })
async def to_code(config):
var = await binary_sensor.new_binary_sensor(config)
```
**Switch:**
```python
from esphome.components import switch
CONFIG_SCHEMA = switch.switch_schema().extend({ ... })
async def to_code(config):
var = await switch.new_switch(config)
```
**Common Validators:**
**Platform/Framework Filters:**
**Schema Extensions:**
```python
CONFIG_SCHEMA = cv.Schema({ ... })
.extend(cv.COMPONENT_SCHEMA)
.extend(uart.UART_DEVICE_SCHEMA)
.extend(i2c.i2c_device_schema(0x48))
.extend(spi.spi_device_schema(cs_pin_required=True))
```
**Setup:**
1. Use Docker container or create Python venv
2. Install dependencies from `requirements_dev.txt`
3. Run commands via `script/run-in-env.py`
**Testing:**
- Run: `script/test_build_components -c <component> -t <target>`
- Test all together: `./script/test_component_grouping.py -e config --all`
**Key Files:**
1. **Never use `#define` for constants** - use `const` or enums
2. **Always prefix member access with `this->`**
3. **Prefer `protected` fields** for extensibility; use `private` only for safety-critical invariants
4. **Follow schema patterns** for consistent configuration interfaces
5. **Test components both individually and together** to catch conflicts
6. **Add new defines to `esphome/core/defines.h`** for static analysis support
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/esphome-component-development-smf4x7/raw