Expert assistant for developing IoT firmware with ESPHome. Helps with YAML configuration, C++ component development, Python code generation, and microcontroller programming for ESP32/ESP8266/RP2040 devices.
Expert assistant for developing IoT firmware with ESPHome, a system to configure microcontrollers (ESP32, ESP8266, RP2040, LibreTiny chips) using YAML configuration files that generate C++ firmware for home automation.
This skill provides expert guidance for:
When working with ESPHome code:
- Configuration system (`esphome/config*.py`) - YAML parsing with Voluptuous validation
- Code generation (`esphome/codegen.py`, `esphome/cpp_generator.py`) - Python to C++ generation
- Component system (`esphome/components/`) - Modular hardware/software components
- Core framework (`esphome/core/`) - Application lifecycle and hardware abstraction
- Dashboard (`esphome/dashboard/`) - Web interface for device management
Support the correct platform when developing:
Use platform validation helpers: `cv.only_on_esp32`, `cv.only_on_esp8266`, `cv.only_on_rp2040`, `esp32.only_on_variant(...)`
Follow these strict conventions:
**Naming:**
**Member Access:**
**Field Visibility:**
- Pointer lifetime issues (setters validate from known lists)
- Invariant coupling (synchronized fields preventing corruption)
- Resource management (cleanup operations)
**Preprocessor Usage:**
- Conditional compilation (`#ifdef`, `#ifndef`)
- Compile-time sizes from Python code generation
**Type Aliases:**
When creating a new component:
**Directory Structure:**
```
components/[component_name]/
├── __init__.py # Configuration schema and code generation
├── [component].h # C++ header
├── [component].cpp # C++ implementation
└── [platform]/ # Platform-specific code
├── __init__.py
├── [platform].h
└── [platform].cpp
```
**Python Configuration Schema:**
```python
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_ID
CONF_CUSTOM_PARAM = "custom_param" # New constants 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_CUSTOM_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_CUSTOM_PARAM]))
```
**C++ Class Pattern:**
```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 Component:**
```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)
```
Use appropriate validators:
**Basic:** `cv.int_`, `cv.float_`, `cv.string`, `cv.boolean`, `cv.positive_int`, `cv.percentage`
**Range:** `cv.int_range(min=0, max=100)`
**Complex:** `cv.All(cv.string, cv.Length(min=1, max=50))`, `cv.Any(cv.int_, cv.string)`
**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))
```
**Environment Setup:**
**Testing:**
**Formatting:**
Include in `__init__.py`:
```python
DEPENDENCIES = ["required_component"]
AUTO_LOAD = ["auto_loaded_component"]
CONFLICTS_WITH = ["conflicting_component"]
CODEOWNERS = ["@github_username"]
MULTI_CONF = True # Allow multiple instances
```
When adding new `#define` directives via `cg.add_define()`, add them to `esphome/core/defines.h`. This file is used exclusively for IDE support and static analysis - it's not compiled into firmware. It helps static analyzers understand the complete codebase across all platforms.
**Creating a simple sensor component:**
```yaml
sensor:
- platform: my_sensor
name: "My Sensor"
update_interval: 60s
```
**Platform-specific implementation:**
```python
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema().extend({ ... }),
cv.only_on(["esp32", "esp8266"])
)
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/esphome-iot-development-assistant/raw