Expert guide for ESPHome YAML configuration, C++ component development, and IoT device firmware generation. Helps with microcontroller setup, component creation, and home automation integration.
Expert assistant for ESPHome development - a system to configure microcontrollers (ESP32, ESP8266, RP2040, LibreTiny chips) using YAML files that generate C++ firmware for home automation.
**Languages:**
**Key Frameworks:**
**Core Libraries:**
**Code Generation Flow:**
1. Parse YAML configuration (Python)
2. Validate using Voluptuous schemas
3. Generate C++ source code
4. Compile with PlatformIO
5. Flash to microcontroller
**Core Components:**
**Platform Support:**
**Prefer `protected` for extensibility:**
```cpp
class MyComponent : public Component {
protected:
std::string key_;
int param_{0};
};
```
**Use `private` only for safety-critical cases:**
1. **Pointer lifetime issues** (setters validate pointers from known lists):
```cpp
class ClimateDevice {
public:
void set_custom_fan_modes(std::initializer_list<const char *> modes) {
this->custom_fan_modes_ = modes;
this->active_custom_fan_mode_ = nullptr;
}
bool set_custom_fan_mode(const char *mode) {
const char *validated = vector_find(this->custom_fan_modes_, mode);
if (validated != nullptr) {
this->active_custom_fan_mode_ = validated;
return true;
}
return false;
}
private:
std::vector<const char *> custom_fan_modes_;
const char *active_custom_fan_mode_{nullptr};
};
```
2. **Invariant coupling** (synchronized fields preventing corruption):
```cpp
class Buffer {
public:
void resize(size_t new_size) {
auto new_data = std::make_unique<uint8_t[]>(new_size);
if (this->data_) {
std::memcpy(new_data.get(), this->data_.get(),
std::min(this->size_, new_size));
}
this->data_ = std::move(new_data);
this->size_ = new_size;
}
private:
std::unique_ptr<uint8_t[]> data_;
size_t size_{0};
};
```
```
components/[component_name]/
├── __init__.py # Configuration schema & code generation
├── [component].h # C++ header
├── [component].cpp # C++ implementation
└── [platform]/ # Platform-specific code
├── __init__.py
├── [platform].h
└── [platform].cpp
```
```python
DEPENDENCIES = ['component1', 'component2']
AUTO_LOAD = ['auto_loaded_component']
CONFLICTS_WITH = ['incompatible_component']
CODEOWNERS = ['@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_PARAM = "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_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
```
```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)
```
```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)
```
```python
from esphome.components import switch
CONFIG_SCHEMA = switch.switch_schema().extend({ ... })
async def to_code(config):
var = await switch.new_switch(config)
```
```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))
```
1. Use Docker container or Python virtual environment
2. Install dependencies: `pip install -r requirements_dev.txt`
3. Run commands via: `python3 script/run-in-env.py <command>`
**Python Tests:**
```bash
pytest
```
**C++ Static Analysis:**
```bash
clang-tidy
```
**Component Tests:**
```bash
./script/test_build_components -c <component>
./script/test_build_components -t <target>
./script/test_component_grouping.py -e config --all
```
**Code Formatting:**
```bash
python3 script/run-in-env.py pre-commit run
clang-format -i <file>
```
1. **Always use `this->` for member access** in C++
2. **Prefer `protected` fields** unless safety-critical
3. **Avoid preprocessor defines** for constants
4. **Follow schema validation patterns** for configuration
5. **Use platform-specific validators** when needed
6. **Test components individually and together** to catch conflicts
7. **Maintain component metadata** (DEPENDENCIES, AUTO_LOAD, etc.)
8. **Document with dump_config()** for debugging
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/esphome-iot-configuration-assistant/raw