Expert assistant for developing ESPHome components, YAML configurations, and C++ firmware for ESP32/ESP8266/RP2040 microcontrollers in home automation systems.
Expert assistant for developing ESPHome components and configurations for IoT home automation devices.
Provides expert guidance for ESPHome development, including:
When working with ESPHome projects, follow these guidelines:
ESPHome is a system to configure microcontrollers using YAML files that generate C++ firmware. The architecture follows:
Key directories:
**Python (≥3.11):**
**C++ (gnu++20):**
- 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_`
- Prefix all member access with `this->` (e.g., `this->value_`)
- Use spaces (2 per level), never tabs
- Max line length: 120 characters
- Prefer `using type_t = int;` over `typedef`
**Field Visibility:**
1. Pointer lifetime issues (validated setters)
2. Invariant coupling (synchronized fields)
3. Resource management (cleanup operations)
**Preprocessor Guidelines:**
- Conditional compilation (`#ifdef`, `#ifndef`)
- Compile-time sizes from Python code generation
Create components with this standard structure:
```
components/[component_name]/
├── __init__.py # Schema and code generation
├── [component].h # C++ header (if needed)
├── [component].cpp # C++ implementation (if needed)
└── [platform]/ # Platform-specific code
├── __init__.py
├── [platform].h
└── [platform].cpp
```
**Component Metadata:**
```python
DEPENDENCIES = ["required_component"]
AUTO_LOAD = ["auto_loaded_component"]
CONFLICTS_WITH = ["conflicting_component"]
CODEOWNERS = ["@github_username"]
MULTI_CONF = True # Allow multiple instances
```
```python
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_NAME
CONF_CUSTOM_PARAM = "custom_param"
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_NAME): cv.string,
cv.Optional(CONF_CUSTOM_PARAM, default=42): cv.int_range(min=0, max=100),
}).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_name(config[CONF_NAME]))
cg.add(var.set_custom_param(config[CONF_CUSTOM_PARAM]))
```
```cpp
namespace esphome::my_component {
class MyComponent : public Component {
public:
void setup() override;
void loop() override;
void dump_config() override;
void set_name(const std::string &name) { this->name_ = name; }
void set_custom_param(int param) { this->custom_param_ = param; }
protected:
std::string name_;
int custom_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 common validators:
Platform-specific validation:
```python
cv.only_on(["esp32", "esp8266"])
esp32.only_on_variant([esp32.const.VARIANT_ESP32S3])
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))
```
**ESP32** (`components/esp32/`):
**ESP8266** (`components/esp8266/`):
**RP2040** (`components/rp2040/`):
**LibreTiny** (`components/libretiny/`):
**Python Tests:**
```bash
pytest
```
**C++ Static Analysis:**
```bash
clang-tidy
```
**Component Tests:**
```bash
./script/test_build_components -c <component> -t <platform>
```
**Test All Components Together:**
```bash
./script/test_component_grouping.py -e config --all
```
**Linting:**
```bash
python3 script/run-in-env.py pre-commit run
```
Run commands in the project environment:
```bash
python3 script/run-in-env.py <command>
```
Main entrypoint:
```bash
python esphome/__main__.py
```
When adding new defines via `cg.add_define()`, add them to `esphome/core/defines.h` for IDE support and static analysis. This file is not used during runtime compilation.
Creating a new temperature sensor component:
1. Create directory structure under `components/my_temp_sensor/`
2. Define Python schema in `__init__.py` with sensor validation
3. Implement C++ class inheriting from `sensor::Sensor`
4. Add platform-specific implementations if needed
5. Write YAML tests in `tests/components/my_temp_sensor/`
6. Run component tests and linting before committing
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/esphome-component-development-ve9ujk/raw