Build and configure ESPHome firmware for ESP32, ESP8266, RP2040, and LibreTiny microcontrollers using YAML configuration files and C++ code generation.
Develop firmware for ESPHome, a system to configure microcontrollers (ESP32, ESP8266, RP2040, LibreTiny) using YAML configuration files that generate C++ firmware for home automation.
This skill helps you work with the ESPHome project, which follows a code-generation architecture: Python parses YAML configs and generates C++ source code, which is compiled and flashed to microcontrollers via PlatformIO.
**Primary Goal:** Configure microcontrollers using simple yet powerful YAML files. Generate C++ firmware for remote control through home automation systems.
**Core Technologies:**
**Architecture:** Code-generation pattern. Python → YAML parsing → C++ generation → PlatformIO compilation → Firmware flashing.
```
/esphome # Core Python source
/esphome/components # Individual components (self-contained C++/Python)
/tests # Unit and integration tests
/docker # Docker build files
/script # Helper scripts
```
1. **Configuration System** (`esphome/config*.py`): YAML parsing, Voluptuous validation, schema definitions, multi-platform configurations
2. **Code Generation** (`esphome/codegen.py`, `esphome/cpp_generator.py`): Python → C++ generation, template processing, build flags
3. **Component System** (`esphome/components/`): Modular hardware/software components with platform-specific implementations
4. **Core Framework** (`esphome/core/`): App lifecycle, hardware abstraction, component registration
5. **Dashboard** (`esphome/dashboard/`): Web interface for device config, management, OTA updates
- Functions/methods/variables: `lower_snake_case`
- Classes/structs/enums: `UpperCamelCase`
- Top-level constants: `UPPER_SNAKE_CASE`
- Function-local constants: `lower_snake_case`
- Protected/private fields: `lower_snake_case_with_trailing_underscore_`
**Prefer `protected`** for most fields to enable extensibility and testing.
**Use `private` only for safety-critical cases:**
1. **Pointer lifetime issues** (setters validate pointers from known lists)
2. **Invariant coupling** (multiple fields must stay synchronized)
3. **Resource management** (setters perform cleanup/registration)
Provide `protected` accessor methods when derived classes need controlled access to `private` members.
**Avoid `#define` for constants.** Use `const` variables or enums instead.
**Use `#define` only for:**
**Static Analysis:** All defines added via `cg.add_define()` must be documented in `esphome/core/defines.h` (used exclusively for IDE support/static analysis, not runtime compilation).
```
components/[component_name]/
├── __init__.py # Config schema and code generation
├── [component].h # C++ header (if needed)
├── [component].cpp # C++ implementation (if needed)
└── [platform]/ # Platform-specific implementations
├── __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 yet 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:** `cv.int_`, `cv.float_`, `cv.string`, `cv.boolean`, `cv.int_range(min=0, max=100)`, `cv.positive_int`, `cv.percentage`
**Complex Validation:** `cv.All(cv.string, cv.Length(min=1, max=50))`, `cv.Any(cv.int_, cv.string)`
**Platform-Specific:** `cv.only_on(["esp32", "esp8266"])`, `esp32.only_on_variant(...)`, `cv.only_on_esp32`, `cv.only_on_esp8266`, `cv.only_on_rp2040`
**Framework-Specific:** `cv.only_with_framework(...)`, `cv.only_with_arduino`, `cv.only_with_esp_idf`
**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))
```
Use the provided Docker container or create a Python virtual environment and install dependencies from `requirements_dev.txt`.
Use `script/run-in-env.py` to execute commands within the project's virtual environment:
```bash
python3 script/run-in-env.py pre-commit run
```
**Python:** Run unit tests with `pytest`.
**C++:** Use `clang-tidy` for static analysis.
**Component Tests:** YAML-based compilation tests in `tests/`:
```
tests/
├── test_build_components/ # Base test configs
└── components/[component]/ # Component-specific tests
```
Run tests: `script/test_build_components`
**Test All Components Together:**
```bash
./script/test_component_grouping.py -e config --all
```
This catches ID conflicts and configuration issues that won't appear in isolated tests.
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/esphome-ai-collaboration-guide/raw